chore: refactor a lot, add codegen and upgrade vue

This commit is contained in:
2022-08-03 18:40:05 +02:00
parent 0e1ce42af2
commit e24b65c85b
42 changed files with 3854 additions and 1073 deletions
+2
View File
@@ -0,0 +1,2 @@
graphql/generated
node_modules
+17 -28
View File
@@ -1,35 +1,24 @@
module.exports = { module.exports = {
root: true,
env: { env: {
browser: true, browser: true,
es6: true node: true
},
parser: 'vue-eslint-parser',
plugins: [
'@typescript-eslint'
],
parserOptions: {
"parser": "@typescript-eslint/parser",
requireConfigFile: false
}, },
extends: [ extends: [
'airbnb-base', '@nuxtjs',
'plugin:vue/recommended', '@nuxtjs/eslint-config-typescript',
'eslint:recommended', 'plugin:nuxt/recommended',
'prettier', 'prettier'
'plugin:prettier/recommended'
], ],
globals: { ignorePatterns: ["graphql/generated/*"],
Atomics: 'readonly', // add your custom rules here
SharedArrayBuffer: 'readonly' rules: {}
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
},
plugins: ['vue'],
rules: {
'import/extensions': 0,
'import/no-unresolved': 0,
'no-param-reassign': 0,
'prettier/prettier': [
'error',
{
trailingComma: 'none',
singleQuote: true,
semi: false
}
]
}
} }
+1
View File
@@ -1,3 +1,4 @@
node_modules node_modules
.nuxt .nuxt
dist dist
.eslintcache
+106
View File
@@ -0,0 +1,106 @@
###
# Place your Prettier ignore content here
###
# .gitignore content is duplicated here due to https://github.com/prettier/prettier/issues/8506
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp
.eslintrc.js
.gitlab-ci.yml
.gitlab/dependabot.yml
.prettierignore
codegen.yml
graphql/generated/*
k8s/*
nuxt.config.js
tsconfig.json
-2
View File
@@ -1,6 +1,4 @@
{ {
"trailingComma": "none",
"tabWidth": 2,
"semi": false, "semi": false,
"singleQuote": true "singleQuote": true
} }
+20 -4
View File
@@ -1,6 +1,22 @@
schema: ./schema.graphql schema: ./graphql/schema.graphql
overwrite: true documents: './graphql/**/*.graphql'
generates: generates:
./fragmentTypes.json: ./graphql/generated/operations.ts:
plugins: plugins:
- fragment-matcher - typescript
- typescript-operations
- typescript-vue-apollo
- fragment-matcher
config:
fetcher: fetch
avoidOptionals:
field: true
inputValue: true
object: false
defaultValue: true
maybeValue: T | null | undefined
strictScalars: true
scalars:
LocalDate: string
LocalDateTime: string
vueCompositionApiImportFrom: vue
+70 -45
View File
@@ -1,13 +1,13 @@
<template> <template>
<v-card xs12> <v-card xs12>
<v-card-title primary-title> <v-card-title primary-title>
<h3 class="headline mb-0"> <h3 class='headline mb-0'>
<v-icon <v-icon
class="ml-1 mr-1" v-if='hasUser'
v-if="hasUser" class='ml-1 mr-1'
v-on:click="toggleIgnore('band', event.band.name)"
medium medium
title="Dölj" title="Dölj"
@click="toggleIgnore('band', event.band.name)"
>mdi-eye-off</v-icon >mdi-eye-off</v-icon
>{{ event.band.name }} >{{ event.band.name }}
</h3> </h3>
@@ -15,71 +15,89 @@
<v-container> <v-container>
<v-layout row wrap> <v-layout row wrap>
<v-flex xs12 sm6 <v-flex xs12 sm6
><strong class="mr-1" v-text="$t('events.date')" />{{ ><strong class='mr-1' v-text="$t('events.date')" />{{
event.date event.date
}} }}
({{ weekday }} {{ daysUntil }})</v-flex ({{ weekday }} {{ daysUntil }})
</v-flex
> >
<v-flex xs12 sm6 v-if="event.time" <v-flex v-if='event.time' xs12 sm6
><strong class="mr-1" v-text="$t('events.time')" />{{ ><strong class='mr-1' v-text="$t('events.time')" />{{
event.time event.time
}}</v-flex }}
</v-flex
> >
</v-layout> </v-layout>
<v-layout row wrap> <v-layout row wrap>
<v-flex xs12 sm6 md3 <v-flex xs12 sm6 md3
><strong class="mr-1" v-text="$t('events.hall')" /><v-icon ><strong class='mr-1' v-text="$t('events.hall')" />
class="ml-1 mr-1" <v-icon
v-if="hasUser" v-if='hasUser'
v-on:click="toggleIgnore('danceHall', event.danceHall.name)" class='ml-1 mr-1'
small small
:title="$t('events.hide')" :title="$t('events.hide')"
>mdi-eye-off</v-icon @click="toggleIgnore('danceHall', event.danceHall.name)"
>{{ event.danceHall.name }}</v-flex >mdi-eye-off
</v-icon
>
{{ event.danceHall.name }}
</v-flex
> >
<v-flex xs12 sm6 md3 <v-flex xs12 sm6 md3
><strong class="mr-1" v-text="$t('events.city')" /><v-icon ><strong class='mr-1' v-text="$t('events.city')" />
class="ml-1 mr-1" <v-icon
v-if="hasUser" v-if='hasUser'
v-on:click="toggleIgnore('city', event.danceHall.city)" class='ml-1 mr-1'
small small
:title="$t('events.hide')" :title="$t('events.hide')"
>mdi-eye-off</v-icon @click="toggleIgnore('city', event.danceHall.city)"
>{{ event.danceHall.city }}</v-flex >mdi-eye-off
</v-icon
>
{{ event.danceHall.city }}
</v-flex
> >
<v-flex xs12 sm6 md3 <v-flex xs12 sm6 md3
><strong class="mr-1" v-text="$t('events.municipality')" /><v-icon ><strong class='mr-1' v-text="$t('events.municipality')" />
class="ml-1 mr-1" <v-icon
v-if="hasUser" v-if='hasUser'
v-on:click=" class='ml-1 mr-1'
small
:title="$t('events.hide')"
@click="
toggleIgnore('municipality', event.danceHall.municipality) toggleIgnore('municipality', event.danceHall.municipality)
" "
small >mdi-eye-off
:title="$t('events.hide')" </v-icon
>mdi-eye-off</v-icon >
>{{ event.danceHall.municipality }}</v-flex {{ event.danceHall.municipality }}
</v-flex
> >
<v-flex xs12 sm6 md3 <v-flex xs12 sm6 md3
><strong class="mr-1" v-text="$t('events.state')" /><v-icon ><strong class='mr-1' v-text="$t('events.state')" />
class="ml-1 mr-1" <v-icon
v-if="hasUser" v-if='hasUser'
v-on:click="toggleIgnore('state', event.danceHall.state)" class='ml-1 mr-1'
small small
:title="$t('events.hide')" :title="$t('events.hide')"
>mdi-eye-off</v-icon @click="toggleIgnore('state', event.danceHall.state)"
>{{ event.danceHall.state }}</v-flex >mdi-eye-off
</v-icon
>
{{ event.danceHall.state }}
</v-flex
> >
</v-layout> </v-layout>
<v-layout <v-layout
v-for='distance in event.distances'
:key='distance.origin'
row row
wrap wrap
v-for="distance in event.distances"
:key="distance.origin"
> >
<v-flex xs12 sm6> <v-flex xs12 sm6>
<v-icon>mdi-home</v-icon> <v-icon>mdi-home</v-icon>
<span <span
><strong>{{ distance.origin }}</strong></span ><strong>{{ distance.origin }}</strong></span
> >
</v-flex> </v-flex>
<v-flex xs12 sm6> <v-flex xs12 sm6>
@@ -93,14 +111,19 @@
</v-card> </v-card>
</template> </template>
<script> <script lang='ts'>
import dayjs from 'dayjs' // eslint-disable-next-line import/no-duplicates
import { format, formatDistanceToNow, parseISO } from 'date-fns'
// eslint-disable-next-line import/no-duplicates
import { enGB, sv } from 'date-fns/locale'
import { computed, defineComponent, getCurrentInstance, PropType } from 'vue'
import { Event } from '~/graphql/generated/operations'
export default { export default defineComponent({
name: 'EventDetail', name: 'EventDetail',
props: { props: {
event: { event: {
type: Object, type: Object as PropType<Event>,
required: true required: true
}, },
hasUser: { hasUser: {
@@ -113,13 +136,15 @@ export default {
} }
}, },
setup(props) { setup(props) {
const instance = getCurrentInstance()
const locale = computed(() => (instance?.proxy.$i18n.locale ?? 'sv') === 'en' ? enGB : sv )
const time = (props.event.time || '').split('-')[0].replace('.', ':') const time = (props.event.time || '').split('-')[0].replace('.', ':')
const weekday = dayjs(props.event.date).format('dddd') const weekday = format(parseISO(props.event.date), 'EEEE', { locale: locale.value })
const daysUntil = dayjs(`${props.event.date} ${time}`).fromNow() const daysUntil = formatDistanceToNow(parseISO(`${props.event.date}T${time}`), { addSuffix: true, locale: locale.value })
return { return {
weekday, weekday,
daysUntil daysUntil
} }
} }
} })
</script> </script>
+3 -3
View File
@@ -1,14 +1,14 @@
<template> <template>
<div> <div>
<v-layout row wrap v-for="event in events" :key="event.id"> <v-row v-for="event in events" :key="event.id" wrap>
<v-flex xs12> <v-flex xs12>
<Event <Event
:event="event" :event="event"
:has-user="hasUser" :has-user="hasUser"
:toggleIgnore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
</v-flex> </v-flex>
</v-layout> </v-row>
</div> </div>
</template> </template>
+96 -98
View File
@@ -1,42 +1,46 @@
<template> <template>
<div :key='isAuthenticated'> <div :key='isAuthenticated'>
<v-container :key='range' fluid grid-list-md class='app-fade-in'> <v-container :key='range' fluid grid-list-md class='app-fade-in'>
<v-layout v-if='!isAuthenticated' row wrap> <v-row v-if='!isAuthenticated' wrap>
<v-flex xs12> <v-col xs='12'>
<p><b v-text="$t('events.login')" /></p> <p><b v-text="$t('events.login')" /></p>
</v-flex> </v-col>
</v-layout> </v-row>
<v-layout row wrap> <v-row wrap>
<v-flex xs12> <v-col xs='12'>
<v-text-field <v-text-field
v-model='origin' v-model='origin'
:label="$t('origins.origin')" :label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')" :placeholder="$t('origins.geolocation')"
> >
<v-tooltip slot='append-outer' top> <template #append-outer>
<template #activator='{ on }'> <v-tooltip top>
<v-icon v-on='on' @click='fetchAddress()' <template #activator='{ on }'>
>mdi-crosshairs-gps <v-icon v-on='on' @click='fetchAddress()'
</v-icon> >mdi-crosshairs-gps
</template> </v-icon>
<span v-text="$t('origins.fetchAddress')" /> </template>
</v-tooltip> <span v-text="$t('origins.fetchAddress')" />
<v-tooltip v-if='isAuthenticated' slot='prepend' top> </v-tooltip>
<template #activator='{ on }'> </template>
<v-icon <template #prepend>
:disabled='!origin' <v-tooltip v-if='isAuthenticated' top>
v-on='on' <template #activator='{ on }'>
@click='saveOrigin(origin)' <v-icon
>mdi-bookmark-plus-outline :disabled='!origin'
</v-icon> v-on='on'
</template> @click='saveOrigin(origin)'
<span v-text="$t('origins.save')" /> >mdi-bookmark-plus-outline
</v-tooltip> </v-icon>
</template>
<span v-text="$t('origins.save')" />
</v-tooltip>
</template>
</v-text-field> </v-text-field>
</v-flex> </v-col>
</v-layout> </v-row>
<v-layout row wrap> <v-row wrap>
<v-flex> <v-col>
<v-btn-toggle <v-btn-toggle
v-if='$vuetify.breakpoint.smAndUp' v-if='$vuetify.breakpoint.smAndUp'
v-model='range' v-model='range'
@@ -47,17 +51,17 @@
/></v-btn> /></v-btn>
</v-btn-toggle> </v-btn-toggle>
<v-select <v-select
v-if='$vuetify.breakpoint.xsOnly' v-else
v-model='range' v-model='range'
outline outline
:items='ranges' :items='ranges'
item-text='name' item-text='name'
item-value='value' item-value='value'
/> />
</v-flex> </v-col>
</v-layout> </v-row>
<list <list
:events='events || []' :events='events'
:has-user='isAuthenticated' :has-user='isAuthenticated'
:toggle-ignore='toggleIgnore' :toggle-ignore='toggleIgnore'
/> />
@@ -72,58 +76,61 @@
</div> </div>
</template> </template>
<script> <script lang='ts'>
import { useMutations, useRouter } from '@u3u/vue-hooks' import { computed, defineComponent, ref, watch } from 'vue'
import { computed, ref, watch } from '@vue/composition-api' import { useRoute, useRouter, useStore } from '@nuxtjs/composition-api'
import { useMutation, useQuery, useResult } from '@vue/apollo-composable'
import List from './List/index.vue'
import { useAuth } from '~/plugins/auth' import { useAuth } from '~/plugins/auth'
import List from './List'
import {
fetchAddress,
findEvents,
saveOrigin,
toggleIgnoreBand,
toggleIgnoreCity,
toggleIgnoreDanceHall,
toggleIgnoreMunicipality,
toggleIgnoreState
} from '~/utils/graph-client'
import { useTranslation } from '~/plugins/i18n' import { useTranslation } from '~/plugins/i18n'
import {
FindEventsQueryVariables,
Range,
useFetchAddressLazyQuery,
useFindEventsLazyQuery,
useSaveOriginMutation,
useToggleIgnoreBandMutation,
useToggleIgnoreCityMutation,
useToggleIgnoreDanceHallMutation,
useToggleIgnoreMunicipalityMutation,
useToggleIgnoreStateMutation
} from '~/graphql/generated/operations'
export default { export default defineComponent({
name: 'EventsPage', name: 'EventsPage',
components: { components: {
List List
}, },
setup() { setup() {
const { setTitle } = useMutations(['setTitle']) const store = useStore()
const { t } = useTranslation() const { t } = useTranslation()
setTitle(t('app.links.events')) store.commit('setTitle', t('app.links.events'))
const { loading: authLoading, isAuthenticated } = useAuth() const { loading: authLoading, isAuthenticated } = useAuth()
const { route, router } = useRouter() const route = useRoute()
const router = useRouter()
const range = computed({ const range = computed({
get: () => route.value.query.range || 'ONE_WEEK', get: () => route.value.query.range ? route.value.query.range as Range : undefined,
set: (value) => router.push(`/?range=${value}`) set: (value) => {
router.push(`/?range=${value}`)
}
}) })
const enabled = ref(false) const variables = ref<FindEventsQueryVariables>({ includeOrigins: false})
const { result: data, refetch } = useQuery( const { result, refetch, load } = useFindEventsLazyQuery(variables)
findEvents, const events = computed(() => result.value?.events ?? [])
{ includeOrigins: false }, const origins = computed(() => result.value?.origins ?? [])
() => ({ enabled: enabled.value })
)
const events = useResult(data, [], (result) => result.events)
const origins = useResult(data, [], (result) => result.origins)
watch( watch(
range, () => route.value.query.range,
(r) => { (r) => {
enabled.value = true if (!authLoading.value) {
refetch({ variables.value = {
range: r, range: r ? r as Range : Range.OneWeek,
includeOrigins: isAuthenticated.value || false includeOrigins: isAuthenticated.value || false
}) }
load()
refetch(variables.value)
}
}, },
{ lazy: false } { immediate: true }
) )
const submitting = ref(true) const submitting = ref(true)
const ranges = [ const ranges = [
@@ -141,25 +148,22 @@ export default {
if (origin.value) { if (origin.value) {
originsTemp.push(origin.value) originsTemp.push(origin.value)
} }
enabled.value = true variables.value = {
refetch({
range: range.value, range: range.value,
origins: originsTemp, origins: originsTemp,
includeOrigins: isAuthenticated.value || false includeOrigins: isAuthenticated.value || false
}) }
load()
refetch(variables.value)
} }
const { mutate: doToggleIgnoreBand } = useMutation(toggleIgnoreBand) const { mutate: doToggleIgnoreBand } = useToggleIgnoreBandMutation({})
const { mutate: doToggleIgnoreDanceHall } = useMutation( const { mutate: doToggleIgnoreDanceHall } = useToggleIgnoreDanceHallMutation({})
toggleIgnoreDanceHall const { mutate: doToggleIgnoreCity } = useToggleIgnoreCityMutation({})
) const { mutate: doToggleIgnoreMunicipality } = useToggleIgnoreMunicipalityMutation({})
const { mutate: doToggleIgnoreCity } = useMutation(toggleIgnoreCity) const { mutate: doToggleIgnoreState } = useToggleIgnoreStateMutation({})
const { mutate: doToggleIgnoreMunicipality } = useMutation(
toggleIgnoreMunicipality
)
const { mutate: doToggleIgnoreState } = useMutation(toggleIgnoreState)
const toggleIgnoreSuccess = (name) => { const toggleIgnoreSuccess = (name: string) => {
return () => { return () => {
fetchEvents() fetchEvents()
snackbar.value.color = 'success' snackbar.value.color = 'success'
@@ -168,7 +172,7 @@ export default {
} }
} }
const toggleIgnoreFailed = (name) => { const toggleIgnoreFailed = (name: string) => {
return () => { return () => {
snackbar.value.color = 'error' snackbar.value.color = 'error'
snackbar.value.text = `${name} kunde inte döljas` snackbar.value.text = `${name} kunde inte döljas`
@@ -176,7 +180,7 @@ export default {
} }
} }
const toggleIgnore = (type, name) => { const toggleIgnore = (type: string, name: string) => {
switch (type) { switch (type) {
case 'band': case 'band':
doToggleIgnoreBand({ name }) doToggleIgnoreBand({ name })
@@ -207,26 +211,21 @@ export default {
} }
} }
const { mutate: doSaveOrigin } = useMutation(saveOrigin) const { mutate: doSaveOrigin } = useSaveOriginMutation({})
const addressEnabled = ref(false) const { refetch: doFetchAddress, load: loadFetchAddress } = useFetchAddressLazyQuery({ latlng: '' })
const { result: address, refetch: doFetchAddress } = useQuery(
fetchAddress,
{},
() => ({ enabled: addressEnabled.value })
)
const fetchAddressFn = () => { const fetchAddressFn = () => {
if (window.navigator) { if (window.navigator) {
window.navigator.geolocation.getCurrentPosition((pos) => { window.navigator.geolocation.getCurrentPosition((pos) => {
addressEnabled.value = true loadFetchAddress()
doFetchAddress({ doFetchAddress({
latlng: `${pos.coords.latitude},${pos.coords.longitude}` latlng: `${pos.coords.latitude},${pos.coords.longitude}`
}).then(() => { })?.then((result) => {
origin.value = address.value.address origin.value = result.data.address
}) })
}) })
} }
} }
const saveOriginFn = (o) => const saveOriginFn = (o: string) =>
doSaveOrigin({ origin: o }).then(() => { doSaveOrigin({ origin: o }).then(() => {
origin.value = '' origin.value = ''
fetchEvents() fetchEvents()
@@ -238,7 +237,6 @@ export default {
range, range,
events, events,
origins, origins,
query: fetchEvents,
submitting, submitting,
ranges, ranges,
snackbar, snackbar,
@@ -248,5 +246,5 @@ export default {
saveOrigin: saveOriginFn saveOrigin: saveOriginFn
} }
} }
} })
</script> </script>
+4 -4
View File
@@ -1,5 +1,5 @@
<template> <template>
<v-flex xs12 sm6 md4 lg3> <v-col xs='12' sm='6' md='4' lg='3'>
<v-card> <v-card>
<v-card-title> <v-card-title>
<span v-text="$tc(title, model.length)" /> <span v-text="$tc(title, model.length)" />
@@ -7,18 +7,18 @@
<v-list> <v-list>
<v-list-item v-for="item in model" :key="item"> <v-list-item v-for="item in model" :key="item">
<v-list-item-action @click="toggleIgnore(type, item)"> <v-list-item-action @click="toggleIgnore(type, item)">
<v-tooltip top slot="prepend"> <v-tooltip top>
<template #activator="{ on }"> <template #activator="{ on }">
<v-icon v-on="on">mdi-delete-outline</v-icon> <v-icon v-on="on">mdi-delete-outline</v-icon>
</template> </template>
<span v-text="$t('filters.remove')" /> <span v-text="$t('filters.remove')" />
</v-tooltip> </v-tooltip>
</v-list-item-action> </v-list-item-action>
<v-list-item-titl><span v-html="item" /></v-list-item-titl> <v-list-item-title><span v-text="item" /></v-list-item-title>
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-card> </v-card>
</v-flex> </v-col>
</template> </template>
<script> <script>
+32 -44
View File
@@ -10,7 +10,7 @@
:model="bands || []" :model="bands || []"
title="filters.band" title="filters.band"
type="band" type="band"
:toggleIgnore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
<v-flex xs12 sm6 md4 lg3> <v-flex xs12 sm6 md4 lg3>
<v-layout column> <v-layout column>
@@ -18,25 +18,25 @@
:model="states || []" :model="states || []"
title="filters.state" title="filters.state"
type="state" type="state"
:toggleIgnore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
<list <list
:model="municipalities || []" :model="municipalities || []"
title="filters.municipality" title="filters.municipality"
type="municipality" type="municipality"
:toggleIgnore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
<list <list
:model="cities || []" :model="cities || []"
title="filters.city" title="filters.city"
type="city" type="city"
:toggleIgnore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
<list <list
:model="danceHalls || []" :model="danceHalls || []"
title="filters.hall" title="filters.hall"
type="danceHall" type="danceHall"
:toggleIgnore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
</v-layout> </v-layout>
</v-flex> </v-flex>
@@ -56,22 +56,18 @@
</div> </div>
</template> </template>
<script> <script lang='ts'>
import { ref } from '@vue/composition-api' import { computed, ref } from 'vue'
import { useMutations } from '@u3u/vue-hooks' import { useStore } from '@nuxtjs/composition-api'
import { useMutation, useQuery, useResult } from '@vue/apollo-composable'
import {
fetchFilters,
toggleIgnoreBand,
toggleIgnoreCity,
toggleIgnoreDanceHall,
toggleIgnoreMunicipality,
toggleIgnoreState
} from '~/utils/graph-client'
import List from './List' import List from './List/index.vue'
import { useAuth } from '../../../plugins/auth' import { useAuth } from '~/plugins/auth'
import { useTranslation } from '../../../plugins/i18n' import { useTranslation } from '~/plugins/i18n'
import {
useFetchFiltersQuery,
useToggleIgnoreBandMutation, useToggleIgnoreCityMutation,
useToggleIgnoreDanceHallMutation, useToggleIgnoreMunicipalityMutation, useToggleIgnoreStateMutation
} from '~/graphql/generated/operations'
export default { export default {
name: 'FiltersPage', name: 'FiltersPage',
@@ -79,32 +75,24 @@ export default {
List List
}, },
setup() { setup() {
const { setTitle } = useMutations(['setTitle']) const store = useStore()
const { t } = useTranslation() const { t } = useTranslation()
setTitle(t('app.links.filters')) store.commit('setTitle', t('app.links.filters'))
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth()
const { result: data, loading, refetch } = useQuery(fetchFilters) const { result, loading, refetch } = useFetchFiltersQuery()
const bands = useResult(data, [], (result) => result.bands) const bands = computed(() => result.value?.bands ?? [])
const cities = useResult(data, [], (result) => result.cities) const cities = computed(() => result.value?.cities ?? [])
const danceHalls = useResult(data, [], (result) => result.danceHalls) const danceHalls = computed(() => result.value?.danceHalls ?? [])
const municipalities = useResult( const municipalities = computed(() => result.value?.municipalities ?? [])
data, const states = computed(() => result.value?.states ?? [])
[],
(result) => result.municipalities
)
const states = useResult(data, [], (result) => result.states)
const snackbar = ref({ active: false, color: 'success', text: '' }) const snackbar = ref({ active: false, color: 'success', text: '' })
const { mutate: doToggleIgnoreBand } = useMutation(toggleIgnoreBand) const { mutate: doToggleIgnoreBand } = useToggleIgnoreBandMutation({})
const { mutate: doToggleIgnoreDanceHall } = useMutation( const { mutate: doToggleIgnoreDanceHall } = useToggleIgnoreDanceHallMutation({})
toggleIgnoreDanceHall const { mutate: doToggleIgnoreCity } = useToggleIgnoreCityMutation({})
) const { mutate: doToggleIgnoreMunicipality } = useToggleIgnoreMunicipalityMutation({})
const { mutate: doToggleIgnoreCity } = useMutation(toggleIgnoreCity) const { mutate: doToggleIgnoreState } = useToggleIgnoreStateMutation({})
const { mutate: doToggleIgnoreMunicipality } = useMutation(
toggleIgnoreMunicipality
)
const { mutate: doToggleIgnoreState } = useMutation(toggleIgnoreState)
const toggleIgnoreSuccess = (name) => { const toggleIgnoreSuccess = (name: string) => {
return () => { return () => {
refetch() refetch()
snackbar.value.color = 'success' snackbar.value.color = 'success'
@@ -113,7 +101,7 @@ export default {
} }
} }
const toggleIgnoreFailed = (name) => { const toggleIgnoreFailed = (name: string) => {
return () => { return () => {
snackbar.value.color = 'error' snackbar.value.color = 'error'
snackbar.value.text = t('filters.failure', { name }) snackbar.value.text = t('filters.failure', { name })
@@ -121,7 +109,7 @@ export default {
} }
} }
const toggleIgnore = (type, name) => { const toggleIgnore = (type: string, name: string) => {
switch (type) { switch (type) {
case 'band': case 'band':
doToggleIgnoreBand({ name }) doToggleIgnoreBand({ name })
+64 -66
View File
@@ -1,109 +1,107 @@
<template> <template>
<div :key="isAuthenticated"> <div :key='isAuthenticated'>
<v-container fluid grid-list-md class="app-fade-in"> <v-container fluid grid-list-md class='app-fade-in'>
<v-layout row wrap> <v-layout row wrap>
<v-flex xs12> <v-flex xs12>
<v-text-field <v-text-field
v-model="origin" v-model='origin'
:label="$t('origins.origin')" :label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')" :placeholder="$t('origins.geolocation')"
> >
<v-tooltip top slot="append-outer"> <template #append-outer>
<template v-slot:activator="{ on }"> <v-tooltip top>
<v-icon v-on="on" v-on:click="fetchAddress()" <template #activator='{ on }'>
<v-icon v-on='on' @click='fetchAddress()'
>mdi-crosshairs-gps >mdi-crosshairs-gps
</v-icon> </v-icon>
</template> </template>
<span v-text="$t('origins.fetchAddress')" /> <span v-text="$t('origins.fetchAddress')" />
</v-tooltip> </v-tooltip>
<v-tooltip top slot="prepend"> </template>
<template v-slot:activator="{ on }"> <template #prepend>
<v-icon <v-tooltip top>
v-on="on" <template #activator='{ on }'>
:disabled="!origin" <v-icon
v-on:click="saveOrigin(origin)" :disabled='!origin'
v-on='on'
@click='saveOrigin(origin)'
>mdi-bookmark-plus-outline >mdi-bookmark-plus-outline
</v-icon> </v-icon>
</template> </template>
<span v-text="$t('origins.save')" /> <span v-text="$t('origins.save')" />
</v-tooltip> </v-tooltip>
</template>
</v-text-field> </v-text-field>
</v-flex> </v-flex>
</v-layout> </v-layout>
<template> <v-layout v-for='o in origins' :key='o' row wrap>
<v-layout row wrap v-for="origin in origins" :key="origin"> <v-flex xs12>
<v-flex xs12> <v-tooltip top>
<v-tooltip top slot="prepend"> <template #activator='{ on }'>
<template v-slot:activator="{ on }"> <v-icon v-on='on' @click='removeOrigin(o)'
<v-icon v-on="on" v-on:click="removeOrigin(origin)" >mdi-delete-outline
>mdi-delete-outline </v-icon>
</v-icon> </template>
</template> <span v-text="$t('origins.remove')" />
<span v-text="$t('origins.remove')" /> </v-tooltip>
</v-tooltip> <span>{{ o }}</span>
<span>{{ origin }}</span> </v-flex>
</v-flex> </v-layout>
</v-layout>
</template>
</v-container> </v-container>
<v-snackbar <v-snackbar
v-model="snackbar.active" v-model='snackbar.active'
:color="snackbar.color" :color='snackbar.color'
:timeout="5000" :timeout='5000'
> >
{{ snackbar.text }} {{ snackbar.text }}
</v-snackbar> </v-snackbar>
</div> </div>
</template> </template>
<script> <script lang='ts'>
import { ref } from '@vue/composition-api' import { computed, defineComponent, ref } from 'vue'
import { useMutations } from '@u3u/vue-hooks' import { useStore } from '@nuxtjs/composition-api'
import { useMutation, useQuery, useResult } from '@vue/apollo-composable' import { useAuth } from '~/plugins/auth'
import { useTranslation } from '~/plugins/i18n'
import { import {
fetchAddress, useFetchAddressLazyQuery,
findOrigins, useFindOriginsQuery,
removeOrigin, useRemoveOriginMutation,
saveOrigin useSaveOriginMutation
} from '../../../utils/graph-client' } from '~/graphql/generated/operations'
import { useAuth } from '../../../plugins/auth'
import { useTranslation } from '../../../plugins/i18n'
export default { export default defineComponent({
name: 'OriginsPage', name: 'OriginsPage',
setup() { setup() {
const { setTitle } = useMutations(['setTitle']) const store = useStore()
const { t } = useTranslation() const { t } = useTranslation()
setTitle(t('app.links.origins')) store.commit('setTitle', t('app.links.origins'))
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth()
const { result: data, loading, refetch } = useQuery(findOrigins) const { result, loading, refetch } = useFindOriginsQuery()
const origins = useResult(data, []) const origins = computed(() => result.value?.origins ?? [])
const snackbar = ref({ active: false, color: 'success', text: '' }) const snackbar = ref({ active: false, color: 'success', text: '' })
const { mutate: doSaveOrigin } = useMutation(saveOrigin) const { mutate: doSaveOrigin } = useSaveOriginMutation({})
const { mutate: doRemoveOrigin } = useMutation(removeOrigin) const { mutate: doRemoveOrigin } = useRemoveOriginMutation({})
const enabled = ref(false) const { refetch: doFetchAddress, load } = useFetchAddressLazyQuery({ latlng: '' })
const { refetch: doFetchAddress } = useQuery(fetchAddress, {}, () => ({
enabled: enabled.value
}))
const origin = ref('') const origin = ref('')
const fetchAddressFn = () => { const fetchAddressFn = () => {
if (window.navigator) { if (window.navigator) {
window.navigator.geolocation.getCurrentPosition((pos) => { window.navigator.geolocation.getCurrentPosition((pos) => {
enabled.value = true load()
doFetchAddress({ doFetchAddress({
latlng: `${pos.coords.latitude},${pos.coords.longitude}` latlng: `${pos.coords.latitude},${pos.coords.longitude}`
}).then((res) => { })?.then((res) => {
origin.value = res.address origin.value = res.data.address
}) })
}) })
} }
} }
const saveOriginFn = (o) => const saveOriginFn = (o: string) =>
doSaveOrigin({ origin: o }).then(() => { doSaveOrigin({ origin: o }).then(() => {
refetch() refetch()
origin.value = '' origin.value = ''
}) })
const removeOriginFn = (o) => const removeOriginFn = (o: string) =>
doRemoveOrigin({ origin: o }).then(() => refetch()) doRemoveOrigin({ origin: o }).then(() => refetch())
return { return {
isAuthenticated, isAuthenticated,
@@ -116,5 +114,5 @@ export default {
fetchAddress: fetchAddressFn fetchAddress: fetchAddressFn
} }
} }
} })
</script> </script>
-5
View File
@@ -1,5 +0,0 @@
{
"__schema": {
"types": []
}
}
+571
View File
@@ -0,0 +1,571 @@
import gql from 'graphql-tag';
import * as VueApolloComposable from '@vue/apollo-composable';
import * as VueCompositionApi from 'vue';
export type Maybe<T> = T | null | undefined;
export type InputMaybe<T> = T | null | undefined;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type ReactiveFunction<TParam> = () => TParam;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
LocalDate: string;
LocalDateTime: string;
};
/** Band */
export type Band = {
__typename?: 'Band';
created: Scalars['LocalDateTime'];
id: Maybe<Scalars['Int']>;
name: Scalars['String'];
};
/** DanceHall */
export type DanceHall = {
__typename?: 'DanceHall';
city: Maybe<Scalars['String']>;
created: Scalars['LocalDateTime'];
id: Maybe<Scalars['Int']>;
latitude: Maybe<Scalars['Float']>;
longitude: Maybe<Scalars['Float']>;
municipality: Maybe<Scalars['String']>;
name: Maybe<Scalars['String']>;
state: Maybe<Scalars['String']>;
};
/** DanceHallDistance */
export type DanceHallDistance = {
__typename?: 'DanceHallDistance';
distance: Scalars['Int'];
duration: Scalars['String'];
origin: Scalars['String'];
};
/** Event */
export type Event = {
__typename?: 'Event';
/** The band of the event */
band: Maybe<Band>;
created: Scalars['LocalDateTime'];
/** The place of the event */
danceHall: Maybe<DanceHall>;
/** The date of the event */
date: Scalars['LocalDate'];
/** The driving distances and driving durations to the event from the provided origins */
distances: Array<DanceHallDistance>;
/** Additional information regarding the event */
extraInfo: Maybe<Scalars['String']>;
id: Maybe<Scalars['Int']>;
/** The time of the event */
time: Maybe<Scalars['String']>;
};
export type Mutation = {
__typename?: 'Mutation';
/** Remove provided origin from authenticated user */
RemoveOrigin: Scalars['Boolean'];
/** Save provided origin for authenticated user */
SaveOrigin: Scalars['Boolean'];
/** Toggle band in ignore list */
ToggleIgnoreBand: Scalars['Boolean'];
/** Toggle city in ignore list */
ToggleIgnoreCity: Scalars['Boolean'];
/** Toggle dance hall in ignore list */
ToggleIgnoreDanceHall: Scalars['Boolean'];
/** Toggle municipality in ignore list */
ToggleIgnoreMunicipality: Scalars['Boolean'];
/** Toggle state in ignore list */
ToggleIgnoreState: Scalars['Boolean'];
};
export type MutationRemoveOriginArgs = {
origin: Scalars['String'];
};
export type MutationSaveOriginArgs = {
origin: Scalars['String'];
};
export type MutationToggleIgnoreBandArgs = {
name: Scalars['String'];
};
export type MutationToggleIgnoreCityArgs = {
name: Scalars['String'];
};
export type MutationToggleIgnoreDanceHallArgs = {
name: Scalars['String'];
};
export type MutationToggleIgnoreMunicipalityArgs = {
name: Scalars['String'];
};
export type MutationToggleIgnoreStateArgs = {
name: Scalars['String'];
};
export type Query = {
__typename?: 'Query';
/** Fetch address for provided lat/long */
AddressFromLatLng: Scalars['String'];
/** Find bands given provided criteria */
Bands: Array<Band>;
/** Find dance halls given provided criteria */
DanceHalls: Array<DanceHall>;
/** Find events given provided criteria */
Events: Array<Event>;
/** Fetch ignored bands for authenticated user */
IgnoredBands: Array<Scalars['String']>;
/** Fetch ignored cities for authenticated user */
IgnoredCities: Array<Scalars['String']>;
/** Fetch ignored dance halls for authenticated user */
IgnoredDanceHalls: Array<Scalars['String']>;
/** Fetch ignored municipalities for authenticated user */
IgnoredMunicipalities: Array<Scalars['String']>;
/** Fetch ignored states for authenticated user */
IgnoredStates: Array<Scalars['String']>;
/** Fetch origins for authenticated user */
Origins: Array<Scalars['String']>;
};
export type QueryAddressFromLatLngArgs = {
latlng: Scalars['String'];
};
export type QueryEventsArgs = {
origins?: InputMaybe<Array<Scalars['String']>>;
range?: InputMaybe<Range>;
};
export enum Range {
OneMonth = 'ONE_MONTH',
OneQuarter = 'ONE_QUARTER',
OneWeek = 'ONE_WEEK',
OneYear = 'ONE_YEAR',
TwoWeeks = 'TWO_WEEKS'
}
export type RemoveOriginMutationVariables = Exact<{
origin: Scalars['String'];
}>;
export type RemoveOriginMutation = { __typename?: 'Mutation', removed: boolean };
export type SaveOriginMutationVariables = Exact<{
origin: Scalars['String'];
}>;
export type SaveOriginMutation = { __typename?: 'Mutation', saved: boolean };
export type ToggleIgnoreBandMutationVariables = Exact<{
name: Scalars['String'];
}>;
export type ToggleIgnoreBandMutation = { __typename?: 'Mutation', ignore: boolean };
export type ToggleIgnoreCityMutationVariables = Exact<{
name: Scalars['String'];
}>;
export type ToggleIgnoreCityMutation = { __typename?: 'Mutation', ignore: boolean };
export type ToggleIgnoreDanceHallMutationVariables = Exact<{
name: Scalars['String'];
}>;
export type ToggleIgnoreDanceHallMutation = { __typename?: 'Mutation', ignore: boolean };
export type ToggleIgnoreMunicipalityMutationVariables = Exact<{
name: Scalars['String'];
}>;
export type ToggleIgnoreMunicipalityMutation = { __typename?: 'Mutation', ignore: boolean };
export type ToggleIgnoreStateMutationVariables = Exact<{
name: Scalars['String'];
}>;
export type ToggleIgnoreStateMutation = { __typename?: 'Mutation', ignore: boolean };
export type FetchAddressQueryVariables = Exact<{
latlng: Scalars['String'];
}>;
export type FetchAddressQuery = { __typename?: 'Query', address: string };
export type FetchFiltersQueryVariables = Exact<{ [key: string]: never; }>;
export type FetchFiltersQuery = { __typename?: 'Query', bands: Array<string>, cities: Array<string>, states: Array<string>, danceHalls: Array<string>, municipalities: Array<string> };
export type FindEventsQueryVariables = Exact<{
range?: InputMaybe<Range>;
origins?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
includeOrigins: Scalars['Boolean'];
}>;
export type FindEventsQuery = { __typename?: 'Query', origins: Array<string>, events: Array<{ __typename?: 'Event', date: string, time: string | null | undefined, extraInfo: string | null | undefined, band: { __typename?: 'Band', name: string } | null | undefined, danceHall: { __typename?: 'DanceHall', name: string | null | undefined, city: string | null | undefined, municipality: string | null | undefined, state: string | null | undefined } | null | undefined, distances: Array<{ __typename?: 'DanceHallDistance', origin: string, distance: number, duration: string }> }> };
export type FindOriginsQueryVariables = Exact<{ [key: string]: never; }>;
export type FindOriginsQuery = { __typename?: 'Query', origins: Array<string> };
export const RemoveOriginDocument = gql`
mutation RemoveOrigin($origin: String!) {
removed: RemoveOrigin(origin: $origin)
}
`;
/**
* __useRemoveOriginMutation__
*
* To run a mutation, you first call `useRemoveOriginMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useRemoveOriginMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useRemoveOriginMutation({
* variables: {
* origin: // value for 'origin'
* },
* });
*/
export function useRemoveOriginMutation(options: VueApolloComposable.UseMutationOptions<RemoveOriginMutation, RemoveOriginMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<RemoveOriginMutation, RemoveOriginMutationVariables>>) {
return VueApolloComposable.useMutation<RemoveOriginMutation, RemoveOriginMutationVariables>(RemoveOriginDocument, options);
}
export type RemoveOriginMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<RemoveOriginMutation, RemoveOriginMutationVariables>;
export const SaveOriginDocument = gql`
mutation SaveOrigin($origin: String!) {
saved: SaveOrigin(origin: $origin)
}
`;
/**
* __useSaveOriginMutation__
*
* To run a mutation, you first call `useSaveOriginMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useSaveOriginMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useSaveOriginMutation({
* variables: {
* origin: // value for 'origin'
* },
* });
*/
export function useSaveOriginMutation(options: VueApolloComposable.UseMutationOptions<SaveOriginMutation, SaveOriginMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<SaveOriginMutation, SaveOriginMutationVariables>>) {
return VueApolloComposable.useMutation<SaveOriginMutation, SaveOriginMutationVariables>(SaveOriginDocument, options);
}
export type SaveOriginMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<SaveOriginMutation, SaveOriginMutationVariables>;
export const ToggleIgnoreBandDocument = gql`
mutation ToggleIgnoreBand($name: String!) {
ignore: ToggleIgnoreBand(name: $name)
}
`;
/**
* __useToggleIgnoreBandMutation__
*
* To run a mutation, you first call `useToggleIgnoreBandMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useToggleIgnoreBandMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useToggleIgnoreBandMutation({
* variables: {
* name: // value for 'name'
* },
* });
*/
export function useToggleIgnoreBandMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>>) {
return VueApolloComposable.useMutation<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>(ToggleIgnoreBandDocument, options);
}
export type ToggleIgnoreBandMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>;
export const ToggleIgnoreCityDocument = gql`
mutation ToggleIgnoreCity($name: String!) {
ignore: ToggleIgnoreCity(name: $name)
}
`;
/**
* __useToggleIgnoreCityMutation__
*
* To run a mutation, you first call `useToggleIgnoreCityMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useToggleIgnoreCityMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useToggleIgnoreCityMutation({
* variables: {
* name: // value for 'name'
* },
* });
*/
export function useToggleIgnoreCityMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>>) {
return VueApolloComposable.useMutation<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>(ToggleIgnoreCityDocument, options);
}
export type ToggleIgnoreCityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>;
export const ToggleIgnoreDanceHallDocument = gql`
mutation ToggleIgnoreDanceHall($name: String!) {
ignore: ToggleIgnoreDanceHall(name: $name)
}
`;
/**
* __useToggleIgnoreDanceHallMutation__
*
* To run a mutation, you first call `useToggleIgnoreDanceHallMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useToggleIgnoreDanceHallMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useToggleIgnoreDanceHallMutation({
* variables: {
* name: // value for 'name'
* },
* });
*/
export function useToggleIgnoreDanceHallMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>>) {
return VueApolloComposable.useMutation<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>(ToggleIgnoreDanceHallDocument, options);
}
export type ToggleIgnoreDanceHallMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>;
export const ToggleIgnoreMunicipalityDocument = gql`
mutation ToggleIgnoreMunicipality($name: String!) {
ignore: ToggleIgnoreMunicipality(name: $name)
}
`;
/**
* __useToggleIgnoreMunicipalityMutation__
*
* To run a mutation, you first call `useToggleIgnoreMunicipalityMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useToggleIgnoreMunicipalityMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useToggleIgnoreMunicipalityMutation({
* variables: {
* name: // value for 'name'
* },
* });
*/
export function useToggleIgnoreMunicipalityMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>>) {
return VueApolloComposable.useMutation<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>(ToggleIgnoreMunicipalityDocument, options);
}
export type ToggleIgnoreMunicipalityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>;
export const ToggleIgnoreStateDocument = gql`
mutation ToggleIgnoreState($name: String!) {
ignore: ToggleIgnoreState(name: $name)
}
`;
/**
* __useToggleIgnoreStateMutation__
*
* To run a mutation, you first call `useToggleIgnoreStateMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useToggleIgnoreStateMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useToggleIgnoreStateMutation({
* variables: {
* name: // value for 'name'
* },
* });
*/
export function useToggleIgnoreStateMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>>) {
return VueApolloComposable.useMutation<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>(ToggleIgnoreStateDocument, options);
}
export type ToggleIgnoreStateMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>;
export const FetchAddressDocument = gql`
query FetchAddress($latlng: String!) {
address: AddressFromLatLng(latlng: $latlng)
}
`;
/**
* __useFetchAddressQuery__
*
* To run a query within a Vue component, call `useFetchAddressQuery` and pass it any options that fit your needs.
* When your component renders, `useFetchAddressQuery` returns an object from Apollo Client that contains result, loading and error properties
* you can use to render your UI.
*
* @param variables that will be passed into the query
* @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options;
*
* @example
* const { result, loading, error } = useFetchAddressQuery({
* latlng: // value for 'latlng'
* });
*/
export function useFetchAddressQuery(variables: FetchAddressQueryVariables | VueCompositionApi.Ref<FetchAddressQueryVariables> | ReactiveFunction<FetchAddressQueryVariables>, options: VueApolloComposable.UseQueryOptions<FetchAddressQuery, FetchAddressQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FetchAddressQuery, FetchAddressQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FetchAddressQuery, FetchAddressQueryVariables>> = {}) {
return VueApolloComposable.useQuery<FetchAddressQuery, FetchAddressQueryVariables>(FetchAddressDocument, variables, options);
}
export function useFetchAddressLazyQuery(variables: FetchAddressQueryVariables | VueCompositionApi.Ref<FetchAddressQueryVariables> | ReactiveFunction<FetchAddressQueryVariables>, options: VueApolloComposable.UseQueryOptions<FetchAddressQuery, FetchAddressQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FetchAddressQuery, FetchAddressQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FetchAddressQuery, FetchAddressQueryVariables>> = {}) {
return VueApolloComposable.useLazyQuery<FetchAddressQuery, FetchAddressQueryVariables>(FetchAddressDocument, variables, options);
}
export type FetchAddressQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchAddressQuery, FetchAddressQueryVariables>;
export const FetchFiltersDocument = gql`
query FetchFilters {
bands: IgnoredBands
cities: IgnoredCities
states: IgnoredStates
danceHalls: IgnoredDanceHalls
municipalities: IgnoredMunicipalities
}
`;
/**
* __useFetchFiltersQuery__
*
* To run a query within a Vue component, call `useFetchFiltersQuery` and pass it any options that fit your needs.
* When your component renders, `useFetchFiltersQuery` returns an object from Apollo Client that contains result, loading and error properties
* you can use to render your UI.
*
* @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options;
*
* @example
* const { result, loading, error } = useFetchFiltersQuery();
*/
export function useFetchFiltersQuery(options: VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables>> = {}) {
return VueApolloComposable.useQuery<FetchFiltersQuery, FetchFiltersQueryVariables>(FetchFiltersDocument, {}, options);
}
export function useFetchFiltersLazyQuery(options: VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables>> = {}) {
return VueApolloComposable.useLazyQuery<FetchFiltersQuery, FetchFiltersQueryVariables>(FetchFiltersDocument, {}, options);
}
export type FetchFiltersQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchFiltersQuery, FetchFiltersQueryVariables>;
export const FindEventsDocument = gql`
query FindEvents($range: Range, $origins: [String!], $includeOrigins: Boolean!) {
events: Events(range: $range, origins: $origins) {
date
time
band {
name
}
danceHall {
name
city
municipality
state
}
extraInfo
distances {
origin
distance
duration
}
}
origins: Origins @include(if: $includeOrigins)
}
`;
/**
* __useFindEventsQuery__
*
* To run a query within a Vue component, call `useFindEventsQuery` and pass it any options that fit your needs.
* When your component renders, `useFindEventsQuery` returns an object from Apollo Client that contains result, loading and error properties
* you can use to render your UI.
*
* @param variables that will be passed into the query
* @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options;
*
* @example
* const { result, loading, error } = useFindEventsQuery({
* range: // value for 'range'
* origins: // value for 'origins'
* includeOrigins: // value for 'includeOrigins'
* });
*/
export function useFindEventsQuery(variables: FindEventsQueryVariables | VueCompositionApi.Ref<FindEventsQueryVariables> | ReactiveFunction<FindEventsQueryVariables>, options: VueApolloComposable.UseQueryOptions<FindEventsQuery, FindEventsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FindEventsQuery, FindEventsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FindEventsQuery, FindEventsQueryVariables>> = {}) {
return VueApolloComposable.useQuery<FindEventsQuery, FindEventsQueryVariables>(FindEventsDocument, variables, options);
}
export function useFindEventsLazyQuery(variables: FindEventsQueryVariables | VueCompositionApi.Ref<FindEventsQueryVariables> | ReactiveFunction<FindEventsQueryVariables>, options: VueApolloComposable.UseQueryOptions<FindEventsQuery, FindEventsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FindEventsQuery, FindEventsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FindEventsQuery, FindEventsQueryVariables>> = {}) {
return VueApolloComposable.useLazyQuery<FindEventsQuery, FindEventsQueryVariables>(FindEventsDocument, variables, options);
}
export type FindEventsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindEventsQuery, FindEventsQueryVariables>;
export const FindOriginsDocument = gql`
query FindOrigins {
origins: Origins
}
`;
/**
* __useFindOriginsQuery__
*
* To run a query within a Vue component, call `useFindOriginsQuery` and pass it any options that fit your needs.
* When your component renders, `useFindOriginsQuery` returns an object from Apollo Client that contains result, loading and error properties
* you can use to render your UI.
*
* @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options;
*
* @example
* const { result, loading, error } = useFindOriginsQuery();
*/
export function useFindOriginsQuery(options: VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables>> = {}) {
return VueApolloComposable.useQuery<FindOriginsQuery, FindOriginsQueryVariables>(FindOriginsDocument, {}, options);
}
export function useFindOriginsLazyQuery(options: VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables>> = {}) {
return VueApolloComposable.useLazyQuery<FindOriginsQuery, FindOriginsQueryVariables>(FindOriginsDocument, {}, options);
}
export type FindOriginsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindOriginsQuery, FindOriginsQueryVariables>;
export interface PossibleTypesResultData {
possibleTypes: {
[key: string]: string[]
}
}
const result: PossibleTypesResultData = {
"possibleTypes": {}
};
export default result;
+3
View File
@@ -0,0 +1,3 @@
mutation RemoveOrigin($origin: String!) {
removed: RemoveOrigin(origin: $origin)
}
+3
View File
@@ -0,0 +1,3 @@
mutation SaveOrigin($origin: String!) {
saved: SaveOrigin(origin: $origin)
}
@@ -0,0 +1,3 @@
mutation ToggleIgnoreBand($name: String!) {
ignore: ToggleIgnoreBand(name: $name)
}
@@ -0,0 +1,3 @@
mutation ToggleIgnoreCity($name: String!) {
ignore: ToggleIgnoreCity(name: $name)
}
@@ -0,0 +1,3 @@
mutation ToggleIgnoreDanceHall($name: String!) {
ignore: ToggleIgnoreDanceHall(name: $name)
}
@@ -0,0 +1,3 @@
mutation ToggleIgnoreMunicipality($name: String!) {
ignore: ToggleIgnoreMunicipality(name: $name)
}
@@ -0,0 +1,3 @@
mutation ToggleIgnoreState($name: String!) {
ignore: ToggleIgnoreState(name: $name)
}
+3
View File
@@ -0,0 +1,3 @@
query FetchAddress($latlng: String!) {
address: AddressFromLatLng(latlng: $latlng)
}
+7
View File
@@ -0,0 +1,7 @@
query FetchFilters {
bands: IgnoredBands
cities: IgnoredCities
states: IgnoredStates
danceHalls: IgnoredDanceHalls
municipalities: IgnoredMunicipalities
}
+22
View File
@@ -0,0 +1,22 @@
query FindEvents($range: Range, $origins: [String!], $includeOrigins: Boolean!) {
events: Events(range: $range, origins: $origins) {
date
time
band {
name
}
danceHall {
name
city
municipality
state
}
extraInfo
distances {
origin
distance
duration
}
}
origins: Origins @include(if: $includeOrigins)
}
+3
View File
@@ -0,0 +1,3 @@
query FindOrigins {
origins: Origins
}
+6 -2
View File
@@ -5,12 +5,16 @@
</template> </template>
<script> <script>
import { getCurrentInstance } from 'vue'
import { getDarkMode } from '../../utils/localStorage' import { getDarkMode } from '../../utils/localStorage'
export default { export default {
name: 'ThemedLayout', name: 'ThemedLayout',
setup(props, context) { setup() {
context.root.$vuetify.theme.dark = getDarkMode() const instance = getCurrentInstance()
if (instance) {
instance.proxy.$vuetify.theme.dark = getDarkMode()
}
} }
} }
</script> </script>
+45 -57
View File
@@ -1,11 +1,11 @@
<template> <template>
<v-app :key="$i18n.locale + isAuthenticated"> <v-app :key='$i18n.locale + isAuthenticated'>
<themed> <themed>
<v-navigation-drawer v-model="nav" temporary app> <v-navigation-drawer v-model='nav' temporary app>
<v-list dense> <v-list dense>
<v-list-item> <v-list-item>
<v-list-item-action> <v-list-item-action>
<v-switch v-model="darkMode" /> <v-switch v-model='darkMode' />
</v-list-item-action> </v-list-item-action>
<v-list-item-title>{{ $t('app.darkMode') }}</v-list-item-title> <v-list-item-title>{{ $t('app.darkMode') }}</v-list-item-title>
</v-list-item> </v-list-item>
@@ -15,62 +15,62 @@
<v-list-item @click="locale = 'sv'"> <v-list-item @click="locale = 'sv'">
<v-list-item-title> Svenska 🇸🇪</v-list-item-title> <v-list-item-title> Svenska 🇸🇪</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item v-if="!user" link @click="doLogin"> <v-list-item v-if='!user' link @click='doLogin'>
<v-list-item-title>{{ $t('app.login') }}</v-list-item-title> <v-list-item-title>{{ $t('app.login') }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item v-if="isAuthenticated && user"> <v-list-item v-if='isAuthenticated && user'>
<v-list-item-avatar> <v-list-item-avatar>
<v-img :src="user.picture" :alt="user.name" /> <v-img :src='user.picture' :alt='user.name' />
</v-list-item-avatar> </v-list-item-avatar>
<v-list-item-content> <v-list-item-content>
<v-list-item-title><span v-html="user.name" /></v-list-item-title> <v-list-item-title><span v-text='user.name' /></v-list-item-title>
</v-list-item-content> </v-list-item-content>
</v-list-item> </v-list-item>
<v-list-item link to="/"> <v-list-item link to='/'>
<v-list-item-action> <v-list-item-action>
<v-icon>mdi-calendar-outline</v-icon> <v-icon>mdi-calendar-outline</v-icon>
</v-list-item-action> </v-list-item-action>
<v-list-item-title>{{ $t('app.links.events') }}</v-list-item-title> <v-list-item-title>{{ $t('app.links.events') }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item v-if="isAuthenticated" link to="/origins/"> <v-list-item v-if='isAuthenticated' link to='/origins/'>
<v-list-item-action> <v-list-item-action>
<v-icon>mdi-home</v-icon> <v-icon>mdi-home</v-icon>
</v-list-item-action> </v-list-item-action>
<v-list-item-title>{{ $t('app.links.origins') }}</v-list-item-title> <v-list-item-title>{{ $t('app.links.origins') }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item v-if="isAuthenticated" link to="/filters/"> <v-list-item v-if='isAuthenticated' link to='/filters/'>
<v-list-item-action> <v-list-item-action>
<v-icon>mdi-magnify</v-icon> <v-icon>mdi-magnify</v-icon>
</v-list-item-action> </v-list-item-action>
<v-list-item-title>{{ $t('app.links.filters') }}</v-list-item-title> <v-list-item-title>{{ $t('app.links.filters') }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item v-if="isAuthenticated" link> <v-list-item v-if='isAuthenticated' link>
<v-list-item-action> <v-list-item-action>
<v-icon>exit_to_app</v-icon> <v-icon>exit_to_app</v-icon>
</v-list-item-action> </v-list-item-action>
<v-list-item-content @click="doLogout"> <v-list-item-content @click='doLogout'>
<v-list-item-title>{{ $t('app.logout') }}</v-list-item-title> <v-list-item-title>{{ $t('app.logout') }}</v-list-item-title>
</v-list-item-content> </v-list-item-content>
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-navigation-drawer> </v-navigation-drawer>
<v-app-bar app scroll-off-screen> <v-app-bar app scroll-off-screen>
<v-app-bar-nav-icon @click="nav = !nav" /> <v-app-bar-nav-icon @click='nav = !nav' />
<v-toolbar-title><span v-html="title" /></v-toolbar-title> <v-toolbar-title><span v-text='title' /></v-toolbar-title>
<v-spacer /> <v-spacer />
<v-toolbar-items> <v-toolbar-items>
<v-list-item v-if="isAuthenticated && user"> <v-list-item v-if='isAuthenticated && user'>
<v-list-item-avatar> <v-list-item-avatar>
<v-img :src="user.picture" :alt="user.name" /> <v-img :src='user.picture' :alt='user.name' />
</v-list-item-avatar> </v-list-item-avatar>
<v-list-item-content> <v-list-item-content>
<v-list-item-title><span v-html="user.name" /></v-list-item-title> <v-list-item-title><span v-text='user.name' /></v-list-item-title>
</v-list-item-content> </v-list-item-content>
</v-list-item> </v-list-item>
</v-toolbar-items> </v-toolbar-items>
</v-app-bar> </v-app-bar>
<v-main> <v-main>
<v-container v-if="!loading" fluid> <v-container v-if='!loading' fluid>
<nuxt /> <nuxt />
</v-container> </v-container>
</v-main> </v-main>
@@ -78,38 +78,28 @@
</v-app> </v-app>
</template> </template>
<style lang="scss">
// We need this line to import all global styling
@import 'assets/scss/global.scss';
</style>
<style lang="scss" scoped>
.layout {
background-color: white;
.log-out {
cursor: pointer;
}
}
</style>
<script> <script>
import { computed, ref } from '@vue/composition-api'
import { useRouter, useState } from '@u3u/vue-hooks' import { computed, getCurrentInstance, provide, ref } from 'vue'
import dayjs from 'dayjs' import { useRouter, useStore } from '@nuxtjs/composition-api'
import sv from 'dayjs/locale/sv' import { DefaultApolloClient } from '@vue/apollo-composable'
import { setDarkMode } from '../utils/localStorage'
import Themed from './components/themed' import Themed from './components/themed'
import { useAuth } from '../plugins/auth' import { setDarkMode } from '~/utils/localStorage'
import { useAuth } from '~/plugins/auth'
export default { export default {
name: 'DefaultLayout', name: 'DefaultLayout',
components: { components: {
Themed Themed
}, },
setup(props, context) { setup() {
const { router } = useRouter() const instance = getCurrentInstance()
const { title } = useState(['title']) if (instance) {
provide(DefaultApolloClient, instance.proxy.$apollo)
}
const router = useRouter()
const store = useStore()
const { title } = store.state
const onRedirectCallback = (appState) => { const onRedirectCallback = (appState) => {
router.push( router.push(
appState && appState.targetUrl appState && appState.targetUrl
@@ -120,37 +110,35 @@ export default {
const { loading, isAuthenticated, user, loginWithRedirect, logout } = const { loading, isAuthenticated, user, loginWithRedirect, logout } =
useAuth(onRedirectCallback) useAuth(onRedirectCallback)
const doLogin = () => { const doLogin = () => {
loginWithRedirect.value() loginWithRedirect()
} }
const doLogout = () => { const doLogout = () => {
logout.value({ logout({
returnTo: window.location.origin returnTo: window.location.origin
}) })
} }
const darkMode = computed({ const darkMode = computed({
get: () => context.root.$vuetify.theme.dark, get: () => (instance ? instance.proxy.$vuetify.theme.dark : false),
set: (val) => { set: (val) => {
context.root.$vuetify.theme.dark = val if (instance) {
setDarkMode(context.root.$vuetify.theme.dark) instance.proxy.$vuetify.theme.dark = val
setDarkMode(instance.proxy.$vuetify.theme.dark)
}
} }
}) })
const locale = computed({ const locale = computed({
get: () => context.root.$i18n.locale, get: () => (instance ? instance.proxy.$i18n.locale : 'sv'),
set: (newLocale) => { set: (newLocale) => {
if (newLocale === 'en') { if (instance) {
dayjs.locale(newLocale) instance.proxy.$i18n.setLocaleCookie(newLocale)
} else if (newLocale === 'sv') { instance.proxy.$vuetify.lang.current = newLocale
dayjs.locale(sv) instance.proxy.$i18n.locale = newLocale
} }
context.root.$i18n.setLocaleCookie(newLocale)
context.root.$vuetify.lang.current = newLocale
context.root.$i18n.locale = newLocale
} }
}) })
const nav = ref(false) const nav = ref(false)
locale.value = context.root.$i18n.locale locale.value = instance.proxy.$i18n.locale
return { return {
title, title,
+2 -2
View File
@@ -1,6 +1,6 @@
import { useAuth } from '../plugins/auth' import { useAuth } from '~/plugins/auth'
export default async ({ app: { router } }) => { export default ({ app: { router } }) => {
const onRedirectCallback = (appState) => { const onRedirectCallback = (appState) => {
router.push( router.push(
appState && appState.targetUrl appState && appState.targetUrl
+18 -4
View File
@@ -3,6 +3,13 @@ import numberFormats from './translations/numberFormats'
export default { export default {
build: { build: {
extend(config) {
config.module.rules.push({
include: /node_modules/,
test: /\.mjs$/,
type: 'javascript/auto'
})
},
babel: { babel: {
presets({ isServer }) { presets({ isServer }) {
return [ return [
@@ -18,7 +25,17 @@ export default {
} }
} }
}, },
buildModules: ['@nuxtjs/composition-api/module'], buildModules: [
'@nuxt/typescript-build',
// https://go.nuxtjs.dev/eslint
['@nuxtjs/eslint-module', { exclude: ['graphql/generated', 'node_modules'] }],
// https://go.nuxtjs.dev/stylelint
'@nuxtjs/stylelint-module',
// https://go.nuxtjs.dev/vuetify
'@nuxtjs/vuetify',
'@nuxtjs/composition-api/module',
'@vueuse/nuxt'
],
css: ['vuetify/dist/vuetify.css', '~/assets/scss/global.scss'], css: ['vuetify/dist/vuetify.css', '~/assets/scss/global.scss'],
env: { env: {
graphqlApi: process.env.GRAPHQL_API graphqlApi: process.env.GRAPHQL_API
@@ -94,13 +111,10 @@ export default {
modules: [ modules: [
'@nuxtjs/i18n', '@nuxtjs/i18n',
'@nuxtjs/sentry', '@nuxtjs/sentry',
'@nuxtjs/vuetify',
['@nuxtjs/moment', { locales: ['sv'], defaultLocale: 'sv' }] ['@nuxtjs/moment', { locales: ['sv'], defaultLocale: 'sv' }]
], ],
plugins: [ plugins: [
'~/plugins/apollo', '~/plugins/apollo',
'~/plugins/composition',
'~/plugins/hooks',
'~/plugins/i18n', '~/plugins/i18n',
'~/plugins/vue-numeral-filter.js' '~/plugins/vue-numeral-filter.js'
], ],
+40 -25
View File
@@ -6,52 +6,60 @@
}, },
"author": "Joakim Olsson <joakim@unbound.se>", "author": "Joakim Olsson <joakim@unbound.se>",
"private": true, "private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"lint:js": "eslint --ext \".ts,.js,.vue\" --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{css,scss,sass,html,vue}\" --ignore-path .gitignore",
"lint:prettier": "prettier --check .",
"lint": "yarn lint:js && yarn lint:style # ignore prettier for now # && yarn lint:prettier",
"lintfix": "prettier --write --list-different . && yarn lint:js --fix && yarn lint:style --fix",
"prepare": "husky install",
"test": "jest",
"codegen": "graphql-codegen"
},
"dependencies": { "dependencies": {
"@apollo/client": "^3.6.9", "@apollo/client": "^3.6.9",
"@auth0/auth0-spa-js": "^1.22.2", "@auth0/auth0-spa-js": "^1.22.2",
"@nuxtjs/composition-api": "^0.32.0", "@graphql-codegen/typescript": "^2.7.2",
"@graphql-codegen/typescript-operations": "^2.5.2",
"@graphql-codegen/typescript-vue-apollo": "^3.3.2",
"@nuxtjs/composition-api": "^0.33.0",
"@nuxtjs/i18n": "^7.2.3", "@nuxtjs/i18n": "^7.2.3",
"@nuxtjs/moment": "^1.1.0", "@nuxtjs/moment": "^1.1.0",
"@nuxtjs/sentry": "^5.1.7", "@nuxtjs/sentry": "^5.1.7",
"@nuxtjs/vuetify": "^1.12.3", "@nuxtjs/vuetify": "^1.12.3",
"@snyk/protect": "^1.982.0", "@snyk/protect": "^1.982.0",
"@u3u/vue-hooks": "^2.0.1",
"@vue/apollo-composable": "^4.0.0-alpha.19", "@vue/apollo-composable": "^4.0.0-alpha.19",
"@vue/apollo-option": "^4.0.0-alpha.20", "@vueuse/core": "^9.0.2",
"@vue/composition-api": "^1.7.0", "@vueuse/nuxt": "^9.0.2",
"core-js": "3", "core-js": "3",
"dayjs": "^1.11.4", "date-fns": "^2.29.1",
"graphql": "^15.8.0", "graphql": "^15.8.0",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"moment": "^2.29.4", "moment": "^2.29.4",
"node-sass": "^7.0.1", "node-sass": "^7.0.1",
"nuxt": "^2.15.8", "nuxt": "^2.15.8",
"sass-loader": "^7.0.3", "sass-loader": "^10.1.1",
"vue": "^2.6.14", "vue": "2.7.8",
"vue-demi": "^0.13.6",
"vue-numeral-filter": "^2.2.0", "vue-numeral-filter": "^2.2.0",
"vue-server-renderer": "^2.6.14", "vue-server-renderer": "2.7.8",
"vue-template-compiler": "2.6.14", "vue-template-compiler": "2.7.8",
"vuetify": "^2.6.8" "vuetify": "^2.6.8",
}, "vuex": "3.6.2"
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"generate": "nuxt generate",
"lint": "eslint --quiet --fix --ext .js,.vue --ignore-path .gitignore .",
"generate-gql": "graphql-codegen",
"precommit": "yarn lint",
"prepush": "yarn test",
"prepare": "yarn run snyk-protect",
"snyk-protect": "snyk-protect",
"start": "node server/index.js",
"start:ci": "NODE_ENV=production CI=true node server/index.js",
"test:cypress": "cypress run",
"wait": "wait-on http://localhost:3000"
}, },
"devDependencies": { "devDependencies": {
"@babel/runtime-corejs3": "^7.18.9", "@babel/runtime-corejs3": "^7.18.9",
"@graphql-codegen/cli": "^2.11.3", "@graphql-codegen/cli": "^2.11.3",
"@graphql-codegen/fragment-matcher": "^3.3.0", "@graphql-codegen/fragment-matcher": "^3.3.0",
"@nuxt/types": "^2.15.8",
"@nuxt/typescript-build": "^2.1.0",
"@nuxtjs/eslint-config-typescript": "^10.0.0",
"@nuxtjs/eslint-module": "^3.1.0",
"@nuxtjs/stylelint-module": "^4.1.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"cli-engine": "^4.7.6", "cli-engine": "^4.7.6",
"cypress": "^10.4.0", "cypress": "^10.4.0",
@@ -60,9 +68,16 @@
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-loader": "^4.0.2", "eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-nuxt": "^3.2.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"husky": "^8.0.1",
"postcss-html": "^1.5.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"stylelint": "^14.9.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^26.0.0",
"wait-on": "^6.0.1" "wait-on": "^6.0.1"
}, },
"snyk": true "snyk": true
+1
View File
@@ -0,0 +1 @@
<template><p>Dummy</p></template>
+10 -10
View File
@@ -1,7 +1,9 @@
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core' import {
ApolloClient,
InMemoryCache,
createHttpLink
} from '@apollo/client/core'
import { setContext } from '@apollo/client/link/context' import { setContext } from '@apollo/client/link/context'
import { provide } from '@vue/composition-api'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { useAuth } from './auth' import { useAuth } from './auth'
const apiUrl = process.env.graphqlApi || '/query' const apiUrl = process.env.graphqlApi || '/query'
@@ -12,12 +14,12 @@ const httpLink = createHttpLink({
uri: apiUrl uri: apiUrl
}) })
const getToken = async (options) => { const getToken = (options) => {
const { getTokenSilently } = useAuth() const { getTokenSilently } = useAuth()
return getTokenSilently.value(options) return getTokenSilently(options)
} }
const authLink = setContext(async (_, { headers }) => { const authLink = setContext((_, { headers }) => {
return getToken().then((token) => ({ return getToken().then((token) => ({
headers: { headers: {
...headers, ...headers,
@@ -42,8 +44,6 @@ const instance = new ApolloClient({
} }
}) })
export default function ({ app }) { export default function (_, inject) {
app.setup = () => { inject('apollo', instance)
provide(DefaultApolloClient, instance)
}
} }
-138
View File
@@ -1,138 +0,0 @@
import createAuth0Client from '@auth0/auth0-spa-js'
import { reactive, toRefs } from '@vue/composition-api'
/** Define a default action to perform after authentication */
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname)
let instance
const params = new URL(window.location).searchParams
const domain = params.get('domain') || 'unbound.eu.auth0.com'
// eslint-disable-next-line import/prefer-default-export
export const useAuth = (onRedirectCallback = DEFAULT_REDIRECT_CALLBACK) => {
if (instance) {
return toRefs(instance)
}
const options = {
domain,
client_id: 'orQfnvCPUR5C3mJkKoiWLQHOVQsBn60e',
audience: 'http://dancefinder.unbound.se',
redirect_uri: window.location.origin
}
instance = reactive({
loading: false,
isAuthenticated: false,
user: {},
auth0Client: null,
popupOpen: false,
error: null,
/** Authenticates the user using a popup window */
loginWithPopup: async (o) => {
this.popupOpen = true
try {
await instance.auth0Client.then((client) => client.loginWithPopup(o))
} catch (e) {
// eslint-disable-next-line
console.error(e)
} finally {
instance.popupOpen = false
}
instance.user = await instance.auth0Client.then((client) =>
client.getUser()
)
instance.isAuthenticated = true
},
/** Handles the callback when logging in using a redirect */
handleRedirectCallback: async () => {
instance.loading = true
try {
await instance.auth0Client.then((client) =>
client.handleRedirectCallback()
)
instance.user = await instance.auth0Client.then((client) =>
client.getUser()
)
instance.isAuthenticated = true
} catch (e) {
instance.error = e
} finally {
instance.loading = false
}
},
/** Authenticates the user using the redirect method */
loginWithRedirect: (o) => {
return instance.auth0Client.then((client) => client.loginWithRedirect(o))
},
/** Returns all the claims present in the ID token */
getIdTokenClaims: (o) => {
return instance.auth0Client.then((client) => client.getIdTokenClaims(o))
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
getTokenSilently: (o) => {
return instance.auth0Client.then((client) => {
return client.getTokenSilently(o)
})
},
/** Gets the access token using a popup window */
getTokenWithPopup: (o) => {
return instance.auth0Client.then((client) => client.getTokenWithPopup(o))
},
/** Logs the user out and removes their session on the authorization server */
logout: (o) => {
return instance.auth0Client.then((client) => client.logout(o))
}
})
const fetchUser = () => {
instance.auth0Client
.then((client) => client.isAuthenticated())
.then((a) => {
instance.isAuthenticated = a
instance.auth0Client.then((client) =>
client.getUser().then((u) => {
instance.user = u
instance.loading = false
})
)
})
}
// Create a new instance of the SDK client using members of the given options object
instance.auth0Client = createAuth0Client(options)
instance.auth0Client.then((client) => {
instance.loading = true
try {
// If the user is returning to the app after authentication..
if (
window.location.search.includes('code=') &&
window.location.search.includes('state=')
) {
// handle the redirect and retrieve tokens
client
.handleRedirectCallback()
.then((appState) => {
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
onRedirectCallback(appState)
// Initialize our internal authentication state
fetchUser()
})
// eslint-disable-next-line no-console
.catch((e) => console.error('error handling redirect callback', e))
} else {
fetchUser()
}
} catch (e) {
instance.error = e
} finally {
instance.loading = false
}
})
return toRefs(instance)
}
+139
View File
@@ -0,0 +1,139 @@
import createAuth0Client, {
Auth0Client,
GetIdTokenClaimsOptions,
GetTokenSilentlyOptions,
IdToken,
LogoutOptions,
RedirectLoginOptions,
User
} from '@auth0/auth0-spa-js'
import { Ref, ref } from 'vue'
/** Define a default action to perform after authentication */
const DEFAULT_REDIRECT_CALLBACK = (_: any) =>
window.history.replaceState({}, document.title, window.location.pathname)
interface Client {
loading: Ref<boolean>
isAuthenticated: Ref<boolean>
user: Ref<User | undefined>
auth0Client?: Promise<Auth0Client>
error: Ref
handleRedirectCallback: () => Promise<void>
loginWithRedirect: (o: RedirectLoginOptions) => Promise<void>
getIdTokenClaims: (o: GetIdTokenClaimsOptions) => Promise<void | IdToken | undefined>
getTokenSilently: (o: GetTokenSilentlyOptions) => Promise<string | void>
logout: (o: LogoutOptions) => Promise<void>
}
let instance: Client
const params = (new URL(window.location.href)).searchParams
const domain = params.get('domain') || 'unbound.eu.auth0.com'
export const useAuth = (onRedirectCallback: (appState?: any) => void = DEFAULT_REDIRECT_CALLBACK) => {
if (instance) {
return instance
}
const options = {
domain,
client_id: 'orQfnvCPUR5C3mJkKoiWLQHOVQsBn60e',
audience: 'http://dancefinder.unbound.se',
redirect_uri: window.location.origin
}
instance = {
loading: ref(true),
isAuthenticated: ref(false),
user: ref(undefined),
auth0Client: undefined,
error: ref(null),
/** Handles the callback when logging in using a redirect */
handleRedirectCallback: () => {
instance.loading.value = true
return instance.auth0Client!.then(client => client.handleRedirectCallback())
.then(() => {
return instance.auth0Client?.then(client => client.getUser())
.then((user) => {
instance.user.value = user
instance.isAuthenticated.value = true
instance.error.value = null
}
)
})
.catch((e) => {
instance.error.value = e
})
.finally(() => {
instance.loading.value = false
})
},
/** Authenticates the user using the redirect method */
loginWithRedirect: (o: RedirectLoginOptions) => {
return instance.auth0Client!.then(client => client.loginWithRedirect(o))
.catch((e) => {
instance.error.value = e
})
},
/** Returns all the claims present in the ID token */
getIdTokenClaims: (o: GetIdTokenClaimsOptions) => {
return instance.auth0Client!.then(client => client.getIdTokenClaims(o))
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
getTokenSilently: (o: GetTokenSilentlyOptions) => {
return instance.auth0Client!.then(client => {
return client.getTokenSilently(o).catch(e => {
instance.error.value = e
})
})
},
/** Logs the user out and removes their session on the authorization server */
logout: (o: LogoutOptions) => {
return instance.auth0Client!.then(client => client.logout(o))
}
}
const fetchUser = () => {
return instance.auth0Client!.then(client => client.isAuthenticated())
.then(a => {
instance.auth0Client?.then(client => client.getUser()
.then(u => {
instance.isAuthenticated.value = a
instance.user.value = u
instance.error.value = null
}))
})
}
// Create a new instance of the SDK client using members of the given options object
instance.auth0Client = createAuth0Client(options)
instance.auth0Client
.then(client => {
instance.loading.value = true
// If the user is returning to the app after authentication..
if (
window.location.search.includes('state=') && (
window.location.search.includes('code=') ||
window.location.search.includes('error=')
)
) {
// handle the redirect and retrieve tokens
return client.handleRedirectCallback()
.then(result => {
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
onRedirectCallback(result.appState)
// Initialize our internal authentication state
return fetchUser()
})
} else {
return fetchUser()
}
})
.catch((e) => {
instance.error.value = e
})
.finally(() => {
instance.loading.value = false
})
return instance
}
-4
View File
@@ -1,4 +0,0 @@
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
customSyntax: 'postcss-html',
extends: [
'stylelint-config-standard',
'stylelint-config-recommended-vue',
'stylelint-config-prettier',
],
// add your custom config here
// https://stylelint.io/user-guide/configuration
rules: {},
}
+34
View File
@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ESNext",
"ESNext.AsyncIterable",
"DOM"
],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"~/*": [
"./*"
],
"@/*": [
"./*"
]
},
"types": [
"@types/node",
"@nuxt/types",
"@nuxtjs/i18n"
]
},
"exclude": [
"node_modules"
]
}
-11
View File
@@ -1,11 +0,0 @@
export { findEvents, findOrigins, fetchAddress, fetchFilters } from './queries'
export {
toggleIgnoreBand,
toggleIgnoreDanceHall,
toggleIgnoreCity,
toggleIgnoreMunicipality,
toggleIgnoreState,
saveOrigin,
removeOrigin
} from './mutations'
-37
View File
@@ -1,37 +0,0 @@
import gql from 'graphql-tag'
export const toggleIgnoreBand = gql`
mutation ToggleIgnoreBand($name: String!) {
ignore: ToggleIgnoreBand(name: $name)
}
`
export const toggleIgnoreDanceHall = gql`
mutation ToggleIgnoreDanceHall($name: String!) {
ignore: ToggleIgnoreDanceHall(name: $name)
}
`
export const toggleIgnoreCity = gql`
mutation ToggleIgnoreCity($name: String!) {
ignore: ToggleIgnoreCity(name: $name)
}
`
export const toggleIgnoreMunicipality = gql`
mutation ToggleIgnoreMunicipality($name: String!) {
ignore: ToggleIgnoreMunicipality(name: $name)
}
`
export const toggleIgnoreState = gql`
mutation ToggleIgnoreState($name: String!) {
ignore: ToggleIgnoreState(name: $name)
}
`
export const saveOrigin = gql`
mutation SaveOrigin($origin: String!) {
saved: SaveOrigin(origin: $origin)
}
`
export const removeOrigin = gql`
mutation RemoveOrigin($origin: String!) {
removed: RemoveOrigin(origin: $origin)
}
`
-48
View File
@@ -1,48 +0,0 @@
import gql from 'graphql-tag'
export const findEvents = gql`
query events($range: Range, $origins: [String!], $includeOrigins: Boolean!) {
events: Events(range: $range, origins: $origins) {
date
time
band {
name
}
danceHall {
name
city
municipality
state
}
extraInfo
distances {
origin
distance
duration
}
}
origins: Origins @include(if: $includeOrigins)
}
`
export const findOrigins = gql`
query origins {
origins: Origins
}
`
export const fetchAddress = gql`
query adressFromLatLng($latlng: String!) {
address: AddressFromLatLng(latlng: $latlng)
}
`
export const fetchFilters = gql`
query {
bands: IgnoredBands
cities: IgnoredCities
states: IgnoredStates
danceHalls: IgnoredDanceHalls
municipalities: IgnoredMunicipalities
}
`
+2506 -436
View File
File diff suppressed because it is too large Load Diff