chore: upgrade to Vue3/Vuetify3

This commit is contained in:
argoyle committed 2024-02-05 16:49:33 +01:00
1 parent 171e1039a7
commit ef3b5460ad
65 files changed
+3137 -9016

No files matched your search

+24 -22
View File
@@ -1,36 +1,38 @@
module.exports = { module.exports = {
root: true,
env: {
browser: true,
node: true
},
parser: 'vue-eslint-parser', parser: 'vue-eslint-parser',
plugins: [
'@typescript-eslint'
],
parserOptions: { parserOptions: {
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
requireConfigFile: false sourceType: 'module',
}, },
extends: [ extends: ['@nuxtjs/eslint-config-typescript', 'eslint:recommended', 'plugin:vue/vue3-recommended'],
'@nuxtjs', rules: {
'@nuxtjs/eslint-config-typescript', 'vue/valid-v-slot': 'off',
'plugin:nuxt/recommended' 'arrow-parens': ['error', 'always'],
], 'comma-dangle': ['error', 'always-multiline'],
// add your custom rules here 'space-before-function-paren': ['error', {
rules: {}, anonymous: 'never',
named: 'never',
asyncArrow: 'always',
}],
'@typescript-eslint/consistent-type-imports': ['error', {
fixStyle: 'inline-type-imports',
}],
},
plugins: [],
ignorePatterns: ['nuxt.config.ts'],
overrides: [ overrides: [
{ {
files: ['pages/**/*.vue', 'layouts/*.vue'], files: ['pages/**/*.vue', 'layouts/*.vue'],
rules: { rules: {
'vue/multi-word-component-names': 'off' 'vue/multi-word-component-names': 'off',
} },
}, },
{ {
files: ['graphql/generated/*.ts'], files: ['graphql/generated/*.ts'],
rules: { rules: {
'no-use-before-define': 'off' 'no-use-before-define': 'off',
} 'no-unused-vars': 'off',
} },
] },
],
} }
+1 -1
View File
@@ -6,7 +6,7 @@ COPY ./package.json ./yarn.lock ./.snyk ./
RUN yarn install --frozen-lockfile RUN yarn install --frozen-lockfile
COPY ./ ./ COPY ./ ./
RUN yarn build && yarn lint RUN yarn run generate && yarn lint
#RUN yarn start:ci & yarn wait && yarn test:cypress #RUN yarn start:ci & yarn wait && yarn test:cypress
FROM nginx FROM nginx
@@ -1,34 +0,0 @@
<template>
<v-layout
row
wrap
>
<v-flex xs12 sm6>
<v-icon>mdi-home</v-icon>
<span><strong>{{ distance.origin }}</strong></span>
</v-flex>
<v-flex xs12 sm6>
<v-icon>mdi-car</v-icon>
<span>{{ numericDistance }} km</span>
<v-icon>mdi-clock-outline</v-icon>
<span>{{ distance.duration }}</span>
</v-flex>
</v-layout>
</template>
<script setup lang='ts'>
import { computed, PropType } from 'vue'
import { DanceHallDistance } from '~/graphql/generated/operations'
const props = defineProps({
distance: {
type: Object as PropType<DanceHallDistance>,
required: true
}
})
const numericDistance = computed(() =>
Number(props.distance.distance / 1000).toLocaleString('sv-SE', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}))
</script>
@@ -1,80 +1,81 @@
<template> <template>
<v-card flat outlined rounded class="mx-3 my-3 rounded-xl"> <v-card flat variant="outlined" rounded="xl">
<v-card-title primary-title> <v-card-title v-if="event.band" primary-title>
<h3 class="headline mb-0"> <h3 class="headline mb-0">
<v-icon <v-icon
v-if="hasUser" v-if="hasUser"
class="ml-1 mr-1" class="ml-1 mr-1 text-medium-emphasis"
medium size="small"
title="Dölj" title="Dölj"
@click="toggleIgnore('band', event.band.name)" @click="toggleIgnore('band', event.band.name)"
> >
mdi-eye-off mdi-eye-off
</v-icon>{{ event.band.name }} </v-icon>
{{ event.band.name }}
</h3> </h3>
</v-card-title> </v-card-title>
<v-container> <v-card-text>
<v-layout row wrap> <v-row wrap>
<v-flex <v-col
xs12 xs="12"
sm6 sm="6"
> >
<strong class="mr-1" v-text="$t('events.date')" />{{ <strong class="mr-1" v-text="$t('events.date')" />{{
event.date event.date
}} }}
({{ weekday }} {{ daysUntil }}) ({{ weekday }} {{ daysUntil }})
</v-flex> </v-col>
<v-flex v-if="event.time" xs12 sm6> <v-col v-if="event.time" xs="12" sm="6">
<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-col>
</v-layout> </v-row>
<v-layout row wrap> <v-row v-if="event.danceHall" wrap>
<v-flex <v-col
xs12 xs="12"
sm6 sm="6"
md3 md="3"
> >
<strong class="mr-1" v-text="$t('events.hall')" /> <strong class="mr-1" v-text="$t('events.hall')" />
<v-icon <v-icon
v-if="hasUser" v-if="hasUser"
class="ml-1 mr-1" class="ml-1 mr-1 text-medium-emphasis"
small size="small"
:title="$t('events.hide')" :title="$t('events.hide')"
@click="toggleIgnore('danceHall', event.danceHall.name)" @click="toggleIgnore('danceHall', event.danceHall.name)"
> >
mdi-eye-off mdi-eye-off
</v-icon> </v-icon>
{{ event.danceHall.name }} {{ event.danceHall.name }}
</v-flex> </v-col>
<v-flex <v-col
xs12 xs="12"
sm6 sm="6"
md3 md="3"
> >
<strong class="mr-1" v-text="$t('events.city')" /> <strong class="mr-1" v-text="$t('events.city')" />
<v-icon <v-icon
v-if="hasUser" v-if="hasUser"
class="ml-1 mr-1" class="ml-1 mr-1 text-medium-emphasis"
small size="small"
:title="$t('events.hide')" :title="$t('events.hide')"
@click="toggleIgnore('city', event.danceHall.city)" @click="toggleIgnore('city', event.danceHall.city)"
> >
mdi-eye-off mdi-eye-off
</v-icon> </v-icon>
{{ event.danceHall.city }} {{ event.danceHall.city }}
</v-flex> </v-col>
<v-flex <v-col
xs12 xs="12"
sm6 sm="6"
md3 md="3"
> >
<strong class="mr-1" v-text="$t('events.municipality')" /> <strong class="mr-1" v-text="$t('events.municipality')" />
<v-icon <v-icon
v-if="hasUser" v-if="hasUser"
class="ml-1 mr-1" class="ml-1 mr-1 text-medium-emphasis"
small size="small"
:title="$t('events.hide')" :title="$t('events.hide')"
@click=" @click="
toggleIgnore('municipality', event.danceHall.municipality) toggleIgnore('municipality', event.danceHall.municipality)
@@ -83,27 +84,27 @@
mdi-eye-off mdi-eye-off
</v-icon> </v-icon>
{{ event.danceHall.municipality }} {{ event.danceHall.municipality }}
</v-flex> </v-col>
<v-flex <v-col
xs12 xs="12"
sm6 sm="6"
md3 md="3"
> >
<strong class="mr-1" v-text="$t('events.state')" /> <strong class="mr-1" v-text="$t('events.state')" />
<v-icon <v-icon
v-if="hasUser" v-if="hasUser"
class="ml-1 mr-1" class="ml-1 mr-1 text-medium-emphasis"
small size="small"
:title="$t('events.hide')" :title="$t('events.hide')"
@click="toggleIgnore('state', event.danceHall.state)" @click="toggleIgnore('state', event.danceHall.state)"
> >
mdi-eye-off mdi-eye-off
</v-icon> </v-icon>
{{ event.danceHall.state }} {{ event.danceHall.state }}
</v-flex> </v-col>
</v-layout> </v-row>
<distance-display v-for="distance in event.distances" :key="distance.origin" :distance="distance" /> <distance-display v-for="distance in event.distances" :key="distance.origin" :distance="distance" />
</v-container> </v-card-text>
</v-card> </v-card>
</template> </template>
@@ -112,30 +113,31 @@
import { format, formatDistanceToNow, parseISO } from 'date-fns' import { format, formatDistanceToNow, parseISO } from 'date-fns'
import { enGB, sv } from 'date-fns/locale' import { enGB, sv } from 'date-fns/locale'
import { computed, getCurrentInstance, PropType } from 'vue' import { computed, type PropType } from 'vue'
import { Event } from '~/graphql/generated/operations' import { useI18n } from '#i18n'
import DistanceDisplay from '~/components/pages/events/Event/distance.vue' import { type Event } from '~/graphql/generated/operations'
import DistanceDisplay from '~/components/pages/events/event-distance.vue'
const props = defineProps({ const props = defineProps({
event: { event: {
type: Object as PropType<Event>, type: Object as PropType<Event>,
required: true required: true,
}, },
hasUser: { hasUser: {
type: Boolean, type: Boolean,
required: true required: true,
}, },
toggleIgnore: { toggleIgnore: {
type: Function, type: Function,
required: true required: true,
} },
}) })
const instance = getCurrentInstance() const { locale: currentLocale } = useI18n()
const locale = computed(() => (instance?.proxy.$i18n.locale ?? 'sv') === 'en' ? enGB : sv) const locale = computed(() => (currentLocale.value ?? 'sv') === 'en' ? enGB : sv)
const time = computed(() => (props.event.time || '').split('-')[0].replace('.', ':')) const time = computed(() => (props.event.time || '').split('-')[0].replace('.', ':'))
const weekday = computed(() => format(parseISO(props.event.date), 'EEEE', { locale: locale.value })) const weekday = computed(() => format(parseISO(props.event.date), 'EEEE', { locale: locale.value }))
const daysUntil = computed(() => formatDistanceToNow(parseISO(`${props.event.date}T${time.value}`), { const daysUntil = computed(() => formatDistanceToNow(parseISO(`${props.event.date}T${time.value}`), {
addSuffix: true, addSuffix: true,
locale: locale.value locale: locale.value,
})) }))
</script> </script>
@@ -0,0 +1,39 @@
<template>
<v-row
wrap
>
<v-col xs="12" sm="6">
<v-icon class="me-1">
mdi-home
</v-icon>
<span><strong>{{ distance.origin }}</strong></span>
</v-col>
<v-col xs="12" sm="6">
<v-icon class="me-1">
mdi-car
</v-icon>
<span>{{ numericDistance }} km</span>
<v-icon class="ms-2 me-1">
mdi-clock-outline
</v-icon>
<span>{{ distance.duration }}</span>
</v-col>
</v-row>
</template>
<script setup lang='ts'>
import { computed, type PropType } from 'vue'
import { type DanceHallDistance } from '~/graphql/generated/operations'
const props = defineProps({
distance: {
type: Object as PropType<DanceHallDistance>,
required: true,
},
})
const numericDistance = computed(() =>
Number(props.distance.distance / 1000).toLocaleString('sv-SE', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}))
</script>
@@ -1,34 +1,34 @@
<template> <template>
<div> <div>
<v-row v-for="event in events" :key="event.id" wrap> <v-row v-for="event in events" :key="event.id" wrap>
<v-flex xs12> <v-col xs="12">
<event-card <event-card
:event="event" :event="event"
:has-user="hasUser" :has-user="hasUser"
:toggle-ignore="toggleIgnore" :toggle-ignore="toggleIgnore"
/> />
</v-flex> </v-col>
</v-row> </v-row>
</div> </div>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { type PropType } from 'vue' import { type PropType } from 'vue'
import EventCard from '../Event/index.vue' import EventCard from './event-card.vue'
import { type Event } from '~/graphql/generated/operations' import { type Event } from '~/graphql/generated/operations'
defineProps({ defineProps({
hasUser: { hasUser: {
type: Boolean, type: Boolean,
required: true required: true,
}, },
toggleIgnore: { toggleIgnore: {
type: Function, type: Function,
required: true required: true,
}, },
events: { events: {
type: Array as PropType<Event[]>, type: Array as PropType<Event[]>,
required: true required: true,
} },
}) })
</script> </script>
@@ -10,14 +10,15 @@
<v-col xs="12"> <v-col xs="12">
<v-text-field <v-text-field
v-model="origin" v-model="origin"
variant="underlined"
:label="$t('origins.origin')" :label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')" :placeholder="$t('origins.geolocation')"
> >
<template #append-outer> <template #append>
<v-tooltip top> <v-tooltip top>
<template #activator="{ on }"> <template #activator="{ props }">
<v-icon <v-icon
v-on="on" v-bind="props"
@click="fetchAddressFn()" @click="fetchAddressFn()"
> >
mdi-crosshairs-gps mdi-crosshairs-gps
@@ -28,10 +29,10 @@
</template> </template>
<template #prepend> <template #prepend>
<v-tooltip v-if="isAuthenticated" top> <v-tooltip v-if="isAuthenticated" top>
<template #activator="{ on }"> <template #activator="{ props }">
<v-icon <v-icon
:disabled="!origin" :disabled="!origin"
v-on="on" v-bind="props"
@click="saveOriginFn(origin)" @click="saveOriginFn(origin)"
> >
mdi-bookmark-plus-outline mdi-bookmark-plus-outline
@@ -46,20 +47,20 @@
<v-row wrap> <v-row wrap>
<v-col> <v-col>
<v-btn-toggle <v-btn-toggle
v-if="$vuetify.breakpoint.smAndUp" v-if="smAndUp"
v-model="state.range" v-model="state.range"
mandatory mandatory
> >
<v-btn v-for="r in ranges" :key="r.value" text :value="r.value"> <v-btn v-for="r in ranges" :key="r.value" variant="outlined" :value="r.value">
<span v-text="$t(`events.range.${r.value}`)" /> <span v-text="$t(`events.range.${r.value}`)" />
</v-btn> </v-btn>
</v-btn-toggle> </v-btn-toggle>
<v-select <v-select
v-else v-else
v-model="state.range" v-model="state.range"
outline variant="outlined"
:items="ranges" :items="ranges"
item-text="name" item-title="name"
item-value="value" item-value="value"
/> />
</v-col> </v-col>
@@ -68,6 +69,7 @@
<v-col cols="12" sm="8"> <v-col cols="12" sm="8">
<v-text-field <v-text-field
v-model="state.search" v-model="state.search"
variant="underlined"
append-outer-icon="mdi-magnify" append-outer-icon="mdi-magnify"
:label="$t('events.filter')" :label="$t('events.filter')"
:placeholder="$t('events.filter')" :placeholder="$t('events.filter')"
@@ -95,11 +97,12 @@
<script setup lang='ts'> <script setup lang='ts'>
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import EventList from './List/index.vue' import { useAuth0 } from '@auth0/auth0-vue'
import { useAuth } from '~/plugins/auth' import { useDisplay } from 'vuetify'
import { useTranslation } from '~/plugins/i18n' import { useI18n } from '#i18n'
import EventList from './event-list.vue'
import { import {
FindEventsQueryVariables, type FindEventsQueryVariables,
useFetchAddressLazyQuery, useFetchAddressLazyQuery,
useFindEventsQuery, useFindEventsQuery,
useSaveOriginMutation, useSaveOriginMutation,
@@ -107,20 +110,21 @@ import {
useToggleIgnoreCityMutation, useToggleIgnoreCityMutation,
useToggleIgnoreDanceHallMutation, useToggleIgnoreDanceHallMutation,
useToggleIgnoreMunicipalityMutation, useToggleIgnoreMunicipalityMutation,
useToggleIgnoreStateMutation useToggleIgnoreStateMutation,
} from '~/graphql/generated/operations' } from '~/graphql/generated/operations'
import { useState } from '~/store' import { useState } from '~/store'
const state = useState() const state = useState()
const { smAndUp } = useDisplay()
const range = computed(() => state.range) const range = computed(() => state.range)
const { t } = useTranslation() const { t } = useI18n()
state.setTitle(t('app.links.events')) state.setTitle(t('app.links.events'))
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth0()
const variables = ref<FindEventsQueryVariables>({ const variables = ref<FindEventsQueryVariables>({
range: state.range, range: state.range,
includeOrigins: isAuthenticated.value || false, includeOrigins: isAuthenticated.value || false,
search: state.search, search: state.search,
includeHidden: state.includeHidden includeHidden: state.includeHidden,
}) })
const { result, refetch } = useFindEventsQuery(() => variables.value) const { result, refetch } = useFindEventsQuery(() => variables.value)
watch([state, isAuthenticated], () => { watch([state, isAuthenticated], () => {
@@ -137,7 +141,7 @@ const ranges = [
{ name: '2 veckor', value: 'TWO_WEEKS' }, { name: '2 veckor', value: 'TWO_WEEKS' },
{ name: '1 månad', value: 'ONE_MONTH' }, { name: '1 månad', value: 'ONE_MONTH' },
{ name: '1 kvartal', value: 'ONE_QUARTER' }, { name: '1 kvartal', value: 'ONE_QUARTER' },
{ name: '1 år', value: 'ONE_YEAR' } { name: '1 år', value: 'ONE_YEAR' },
] ]
const snackbar = ref({ active: false, color: 'success', text: '' }) const snackbar = ref({ active: false, color: 'success', text: '' })
@@ -151,7 +155,7 @@ const fetchEvents = () => {
...variables.value, ...variables.value,
range: state.range, range: state.range,
origins: originsTemp, origins: originsTemp,
includeOrigins: isAuthenticated.value || false includeOrigins: isAuthenticated.value || false,
} }
refetch(variables.value) refetch(variables.value)
} }
@@ -217,7 +221,7 @@ const fetchAddressFn = () => {
window.navigator.geolocation.getCurrentPosition((pos) => { window.navigator.geolocation.getCurrentPosition((pos) => {
loadFetchAddress() loadFetchAddress()
doFetchAddress({ doFetchAddress({
latlng: `${pos.coords.latitude},${pos.coords.longitude}` latlng: `${pos.coords.latitude},${pos.coords.longitude}`,
})?.then((result) => { })?.then((result) => {
origin.value = result.data.address origin.value = result.data.address
}) })
-43
View File
@@ -1,43 +0,0 @@
<template>
<v-card flat outlined class="mx-3 my-3 rounded-xl">
<v-card-title>
<span v-text="$tc(title, model.length)" />
</v-card-title>
<v-list>
<v-list-item v-for="item in model" :key="item">
<v-list-item-action @click="toggleIgnore(type, item)">
<v-tooltip top>
<template #activator="{ on }">
<v-icon v-on="on">
mdi-delete-outline
</v-icon>
</template>
<span v-text="$t('filters.remove')" />
</v-tooltip>
</v-list-item-action>
<v-list-item-title><span v-text="item" /></v-list-item-title>
</v-list-item>
</v-list>
</v-card>
</template>
<script setup lang='ts'>
defineProps({
model: {
type: Array,
required: true
},
title: {
type: String,
required: true
},
type: {
type: String,
required: true
},
toggleIgnore: {
type: Function,
required: true
}
})
</script>
+44
View File
@@ -0,0 +1,44 @@
<template>
<v-card flat variant="outlined" rounded="xl" class="mx-3 my-3">
<v-card-title>
<span v-text="$t(title, model.length)" />
</v-card-title>
<v-list>
<v-list-item v-for="item in model" :key="item" :title="item">
<template #prepend>
<v-list-item-action @click="toggleIgnore(type, item)">
<v-tooltip top>
<template #activator="{ props }">
<v-icon v-bind="props">
mdi-delete-outline
</v-icon>
</template>
<span v-text="$t('filters.remove')" />
</v-tooltip>
</v-list-item-action>
</template>
</v-list-item>
</v-list>
</v-card>
</template>
<script setup lang='ts'>
defineProps({
model: {
type: Array,
required: true,
},
title: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
toggleIgnore: {
type: Function,
required: true,
},
})
</script>
@@ -1,5 +1,5 @@
<template> <template>
<div :key="isAuthenticated"> <div :key="isAuthenticated ? 'true' : 'false'">
<v-container fluid grid-list-md class="app-fade-in"> <v-container fluid grid-list-md class="app-fade-in">
<v-row wrap> <v-row wrap>
<v-col cols="12"> <v-col cols="12">
@@ -62,23 +62,23 @@
<script setup lang='ts'> <script setup lang='ts'>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import List from './List/index.vue' import { useAuth0 } from '@auth0/auth0-vue'
import { useAuth } from '~/plugins/auth' import { useI18n } from '#i18n'
import { useTranslation } from '~/plugins/i18n' import List from './filter-list.vue'
import { import {
useFetchFiltersQuery, useFetchFiltersQuery,
useToggleIgnoreBandMutation, useToggleIgnoreBandMutation,
useToggleIgnoreCityMutation, useToggleIgnoreCityMutation,
useToggleIgnoreDanceHallMutation, useToggleIgnoreDanceHallMutation,
useToggleIgnoreMunicipalityMutation, useToggleIgnoreMunicipalityMutation,
useToggleIgnoreStateMutation useToggleIgnoreStateMutation,
} from '~/graphql/generated/operations' } from '~/graphql/generated/operations'
import { useState } from '~/store' import { useState } from '~/store'
const state = useState() const state = useState()
const { t } = useTranslation() const { t } = useI18n()
state.setTitle(t('app.links.filters')) state.setTitle(t('app.links.filters'))
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth0()
const { result, refetch } = useFetchFiltersQuery() const { result, refetch } = useFetchFiltersQuery()
const bands = computed(() => result.value?.bands ?? []) const bands = computed(() => result.value?.bands ?? [])
const cities = computed(() => result.value?.cities ?? []) const cities = computed(() => result.value?.cities ?? [])
@@ -2,17 +2,18 @@
<div :key="isAuthenticated ? 'true' : 'false'"> <div :key="isAuthenticated ? 'true' : 'false'">
<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-col xs="12">
<v-text-field <v-text-field
v-model="origin" v-model="origin"
variant="underlined"
:label="$t('origins.origin')" :label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')" :placeholder="$t('origins.geolocation')"
> >
<template #append-outer> <template #append>
<v-tooltip top> <v-tooltip top>
<template #activator="{ on }"> <template #activator="{ props }">
<v-icon <v-icon
v-on="on" v-bind="props"
@click="fetchAddressFn()" @click="fetchAddressFn()"
> >
mdi-crosshairs-gps mdi-crosshairs-gps
@@ -23,10 +24,10 @@
</template> </template>
<template #prepend> <template #prepend>
<v-tooltip top> <v-tooltip top>
<template #activator="{ on }"> <template #activator="{ props }">
<v-icon <v-icon
:disabled="!origin" :disabled="!origin"
v-on="on" v-bind="props"
@click="saveOriginFn(origin)" @click="saveOriginFn(origin)"
> >
mdi-bookmark-plus-outline mdi-bookmark-plus-outline
@@ -36,14 +37,14 @@
</v-tooltip> </v-tooltip>
</template> </template>
</v-text-field> </v-text-field>
</v-flex> </v-col>
</v-layout> </v-layout>
<v-layout v-for="o in origins" :key="o" row wrap> <v-layout v-for="o in origins" :key="o" row wrap>
<v-flex xs12> <v-col xs="12">
<v-tooltip top> <v-tooltip top>
<template #activator="{ on }"> <template #activator="{ props }">
<v-icon <v-icon
v-on="on" v-bind="props"
@click="removeOriginFn(o)" @click="removeOriginFn(o)"
> >
mdi-delete-outline mdi-delete-outline
@@ -52,7 +53,7 @@
<span v-text="$t('origins.remove')" /> <span v-text="$t('origins.remove')" />
</v-tooltip> </v-tooltip>
<span>{{ o }}</span> <span>{{ o }}</span>
</v-flex> </v-col>
</v-layout> </v-layout>
</v-container> </v-container>
<v-snackbar <v-snackbar
@@ -67,20 +68,20 @@
<script setup lang='ts'> <script setup lang='ts'>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useAuth } from '~/plugins/auth' import { useAuth0 } from '@auth0/auth0-vue'
import { useTranslation } from '~/plugins/i18n' import { useI18n } from '#i18n'
import { import {
useFetchAddressLazyQuery, useFetchAddressLazyQuery,
useFindOriginsQuery, useFindOriginsQuery,
useRemoveOriginMutation, useRemoveOriginMutation,
useSaveOriginMutation useSaveOriginMutation,
} from '~/graphql/generated/operations' } from '~/graphql/generated/operations'
import { useState } from '~/store' import { useState } from '~/store'
const state = useState() const state = useState()
const { t } = useTranslation() const { t } = useI18n()
state.setTitle(t('app.links.origins')) state.setTitle(t('app.links.origins'))
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth0()
const { result, refetch } = useFindOriginsQuery() const { result, refetch } = useFindOriginsQuery()
const origins = computed(() => result.value?.origins ?? []) const origins = computed(() => result.value?.origins ?? [])
const snackbar = ref({ active: false, color: 'success', text: '' }) const snackbar = ref({ active: false, color: 'success', text: '' })
@@ -93,7 +94,7 @@ const fetchAddressFn = () => {
window.navigator.geolocation.getCurrentPosition((pos) => { window.navigator.geolocation.getCurrentPosition((pos) => {
load() load()
doFetchAddress({ doFetchAddress({
latlng: `${pos.coords.latitude},${pos.coords.longitude}` latlng: `${pos.coords.latitude},${pos.coords.longitude}`,
})?.then((res) => { })?.then((res) => {
origin.value = res.data.address origin.value = res.data.address
}) })
+17 -17
View File
@@ -1,5 +1,5 @@
import * as VueApolloComposable from '@vue/apollo-composable' import * as VueApolloComposable from '@vue/apollo-composable'
import * as VueCompositionApi from 'vue' import type * as VueCompositionApi from 'vue'
import { gql } from '@/utils/gql' import { gql } from '@/utils/gql'
export type Maybe<T> = T | null | undefined; export type Maybe<T> = T | null | undefined;
export type InputMaybe<T> = T | null | undefined; export type InputMaybe<T> = T | null | undefined;
@@ -246,7 +246,7 @@ export const RemoveOriginDocument = gql`
* }, * },
* }); * });
*/ */
export function useRemoveOriginMutation (options: VueApolloComposable.UseMutationOptions<RemoveOriginMutation, RemoveOriginMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<RemoveOriginMutation, RemoveOriginMutationVariables>> = {}) { export function useRemoveOriginMutation(options: VueApolloComposable.UseMutationOptions<RemoveOriginMutation, RemoveOriginMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<RemoveOriginMutation, RemoveOriginMutationVariables>> = {}) {
return VueApolloComposable.useMutation<RemoveOriginMutation, RemoveOriginMutationVariables>(RemoveOriginDocument, options) return VueApolloComposable.useMutation<RemoveOriginMutation, RemoveOriginMutationVariables>(RemoveOriginDocument, options)
} }
export type RemoveOriginMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<RemoveOriginMutation, RemoveOriginMutationVariables>; export type RemoveOriginMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<RemoveOriginMutation, RemoveOriginMutationVariables>;
@@ -273,7 +273,7 @@ export const SaveOriginDocument = gql`
* }, * },
* }); * });
*/ */
export function useSaveOriginMutation (options: VueApolloComposable.UseMutationOptions<SaveOriginMutation, SaveOriginMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<SaveOriginMutation, SaveOriginMutationVariables>> = {}) { export function useSaveOriginMutation(options: VueApolloComposable.UseMutationOptions<SaveOriginMutation, SaveOriginMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<SaveOriginMutation, SaveOriginMutationVariables>> = {}) {
return VueApolloComposable.useMutation<SaveOriginMutation, SaveOriginMutationVariables>(SaveOriginDocument, options) return VueApolloComposable.useMutation<SaveOriginMutation, SaveOriginMutationVariables>(SaveOriginDocument, options)
} }
export type SaveOriginMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<SaveOriginMutation, SaveOriginMutationVariables>; export type SaveOriginMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<SaveOriginMutation, SaveOriginMutationVariables>;
@@ -300,7 +300,7 @@ export const ToggleIgnoreBandDocument = gql`
* }, * },
* }); * });
*/ */
export function useToggleIgnoreBandMutation (options: VueApolloComposable.UseMutationOptions<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>> = {}) { export function useToggleIgnoreBandMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>(ToggleIgnoreBandDocument, options) return VueApolloComposable.useMutation<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>(ToggleIgnoreBandDocument, options)
} }
export type ToggleIgnoreBandMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>; export type ToggleIgnoreBandMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreBandMutation, ToggleIgnoreBandMutationVariables>;
@@ -327,7 +327,7 @@ export const ToggleIgnoreCityDocument = gql`
* }, * },
* }); * });
*/ */
export function useToggleIgnoreCityMutation (options: VueApolloComposable.UseMutationOptions<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>> = {}) { export function useToggleIgnoreCityMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>(ToggleIgnoreCityDocument, options) return VueApolloComposable.useMutation<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>(ToggleIgnoreCityDocument, options)
} }
export type ToggleIgnoreCityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>; export type ToggleIgnoreCityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreCityMutation, ToggleIgnoreCityMutationVariables>;
@@ -354,7 +354,7 @@ export const ToggleIgnoreDanceHallDocument = gql`
* }, * },
* }); * });
*/ */
export function useToggleIgnoreDanceHallMutation (options: VueApolloComposable.UseMutationOptions<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>> = {}) { export function useToggleIgnoreDanceHallMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>(ToggleIgnoreDanceHallDocument, options) return VueApolloComposable.useMutation<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>(ToggleIgnoreDanceHallDocument, options)
} }
export type ToggleIgnoreDanceHallMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>; export type ToggleIgnoreDanceHallMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreDanceHallMutation, ToggleIgnoreDanceHallMutationVariables>;
@@ -381,7 +381,7 @@ export const ToggleIgnoreMunicipalityDocument = gql`
* }, * },
* }); * });
*/ */
export function useToggleIgnoreMunicipalityMutation (options: VueApolloComposable.UseMutationOptions<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>> = {}) { export function useToggleIgnoreMunicipalityMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>(ToggleIgnoreMunicipalityDocument, options) return VueApolloComposable.useMutation<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>(ToggleIgnoreMunicipalityDocument, options)
} }
export type ToggleIgnoreMunicipalityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>; export type ToggleIgnoreMunicipalityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreMunicipalityMutation, ToggleIgnoreMunicipalityMutationVariables>;
@@ -408,7 +408,7 @@ export const ToggleIgnoreStateDocument = gql`
* }, * },
* }); * });
*/ */
export function useToggleIgnoreStateMutation (options: VueApolloComposable.UseMutationOptions<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>> = {}) { export function useToggleIgnoreStateMutation(options: VueApolloComposable.UseMutationOptions<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>(ToggleIgnoreStateDocument, options) return VueApolloComposable.useMutation<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>(ToggleIgnoreStateDocument, options)
} }
export type ToggleIgnoreStateMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>; export type ToggleIgnoreStateMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>;
@@ -433,10 +433,10 @@ export const FetchAddressDocument = gql`
* latlng: // value for 'latlng' * 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>> = {}) { 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) 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>> = {}) { 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) return VueApolloComposable.useLazyQuery<FetchAddressQuery, FetchAddressQueryVariables>(FetchAddressDocument, variables, options)
} }
export type FetchAddressQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchAddressQuery, FetchAddressQueryVariables>; export type FetchAddressQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchAddressQuery, FetchAddressQueryVariables>;
@@ -462,10 +462,10 @@ export const FetchFiltersDocument = gql`
* @example * @example
* const { result, loading, error } = useFetchFiltersQuery(); * const { result, loading, error } = useFetchFiltersQuery();
*/ */
export function useFetchFiltersQuery (options: VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FetchFiltersQuery, FetchFiltersQueryVariables>> = {}) { 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) 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>> = {}) { 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) return VueApolloComposable.useLazyQuery<FetchFiltersQuery, FetchFiltersQueryVariables>(FetchFiltersDocument, {}, options)
} }
export type FetchFiltersQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchFiltersQuery, FetchFiltersQueryVariables>; export type FetchFiltersQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchFiltersQuery, FetchFiltersQueryVariables>;
@@ -518,10 +518,10 @@ export const FindEventsDocument = gql`
* includeHidden: // value for 'includeHidden' * includeHidden: // value for 'includeHidden'
* }); * });
*/ */
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>> = {}) { 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) 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>> = {}) { 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) return VueApolloComposable.useLazyQuery<FindEventsQuery, FindEventsQueryVariables>(FindEventsDocument, variables, options)
} }
export type FindEventsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindEventsQuery, FindEventsQueryVariables>; export type FindEventsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindEventsQuery, FindEventsQueryVariables>;
@@ -543,10 +543,10 @@ export const FindOriginsDocument = gql`
* @example * @example
* const { result, loading, error } = useFindOriginsQuery(); * const { result, loading, error } = useFindOriginsQuery();
*/ */
export function useFindOriginsQuery (options: VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<FindOriginsQuery, FindOriginsQueryVariables>> = {}) { 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) 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>> = {}) { 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) return VueApolloComposable.useLazyQuery<FindOriginsQuery, FindOriginsQueryVariables>(FindOriginsDocument, {}, options)
} }
export type FindOriginsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindOriginsQuery, FindOriginsQueryVariables>; export type FindOriginsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindOriginsQuery, FindOriginsQueryVariables>;
@@ -557,6 +557,6 @@ export interface PossibleTypesResultData {
} }
} }
const result: PossibleTypesResultData = { const result: PossibleTypesResultData = {
possibleTypes: {} possibleTypes: {},
} }
export default result export default result
+6
View File
@@ -0,0 +1,6 @@
import { defineI18nConfig } from '#i18n'
export default defineI18nConfig(() => ({
legacy: false,
fallbackLocale: 'sv',
}))
-16
View File
@@ -1,16 +0,0 @@
<template>
<div>
<slot />
</div>
</template>
<script setup lang='ts'>
import { getCurrentInstance } from 'vue'
import { useState } from '~/store'
const instance = getCurrentInstance()
const state = useState()
if (instance) {
instance.proxy.$vuetify.theme.dark = state.darkMode
}
</script>
+48 -97
View File
@@ -1,140 +1,91 @@
<template> <template>
<v-app :key="$i18n.locale + isAuthenticated"> <v-app :key="$i18n.locale + isAuthenticated">
<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-if="!user" link :title="$t('app.login')" @click="doLogin" />
<v-list-item v-if="isAuthenticated && user" :title="user.name" :prepend-avatar="user.picture" />
<v-list-item> <v-list-item>
<v-list-item-action> <v-switch v-model="darkMode" color="primary" :label="$t('app.darkMode')" hide-details class="ms-1" />
<v-switch v-model="darkMode" />
</v-list-item-action>
<v-list-item-title>{{ $t('app.darkMode') }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item @click="locale = 'en'"> <v-list-item title="In English" :to="switchLocalePath('en')">
<v-list-item-title>In English 🇬🇧</v-list-item-title> <template #prepend>
<div class="d-flex d-inline-flex text-h5 me-8">
🇬🇧
</div>
</template>
</v-list-item> </v-list-item>
<v-list-item @click="locale = 'sv'"> <v-list-item title="På Svenska" :to="switchLocalePath('sv')">
<v-list-item-title>På Svenska 🇸🇪</v-list-item-title> <template #prepend>
</v-list-item> <div class="d-flex d-inline-flex text-h5 me-8">
<v-list-item v-if="!user" link @click="doLogin"> 🇸🇪
<v-list-item-title>{{ $t('app.login') }}</v-list-item-title> </div>
</v-list-item> </template>
<v-list-item v-if="isAuthenticated && user">
<v-list-item-avatar>
<v-img :src="user.picture" :alt="user.name" />
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title><span v-text="user.name" /></v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item link to="/">
<v-list-item-action>
<v-icon>mdi-calendar-outline</v-icon>
</v-list-item-action>
<v-list-item-title>{{ $t('app.links.events') }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="isAuthenticated" link to="/origins/">
<v-list-item-action>
<v-icon>mdi-home</v-icon>
</v-list-item-action>
<v-list-item-title>{{ $t('app.links.origins') }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="isAuthenticated" link to="/filters/">
<v-list-item-action>
<v-icon>mdi-magnify</v-icon>
</v-list-item-action>
<v-list-item-title>{{ $t('app.links.filters') }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="isAuthenticated" link>
<v-list-item-action>
<v-icon>exit_to_app</v-icon>
</v-list-item-action>
<v-list-item-content @click="doLogout">
<v-list-item-title>{{ $t('app.logout') }}</v-list-item-title>
</v-list-item-content>
</v-list-item> </v-list-item>
<v-list-item to="/" :title="$t('app.links.events')" prepend-icon="mdi-calendar-outline" />
<v-list-item v-if="isAuthenticated" to="/origins/" :title="$t('app.links.origins')" prepend-icon="mdi-home" />
<v-list-item v-if="isAuthenticated" to="/filters/" :title="$t('app.links.filters')" prepend-icon="mdi-magnify" />
<v-list-item v-if="isAuthenticated" link :title="$t('app.logout')" prepend-icon="mdi-exit-to-app" @click="doLogout" />
</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-text="title" /></v-toolbar-title> <v-toolbar-title :title="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" :prepend-avatar="user.picture" :title="user.name" />
<v-list-item-avatar>
<v-img :src="user.picture" :alt="user.name" />
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title><span v-text="user.name" /></v-list-item-title>
</v-list-item-content>
</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="!isLoading" fluid>
<nuxt /> <slot />
</v-container> </v-container>
</v-main> </v-main>
</themed>
</v-app> </v-app>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { computed, onMounted, ref } from 'vue'
import { computed, getCurrentInstance, provide, ref } from 'vue' import { useTheme } from 'vuetify'
import { DefaultApolloClient } from '@vue/apollo-composable' import { useAuth0 } from '@auth0/auth0-vue'
import Themed from './components/themed.vue' import { useSwitchLocalePath } from '#i18n'
import { useAuth } from '~/plugins/auth'
import { useState } from '~/store' import { useState } from '~/store'
const instance = getCurrentInstance() const switchLocalePath = useSwitchLocalePath()
if (instance) {
provide(DefaultApolloClient, instance.proxy.$apollo)
}
const state = useState() const state = useState()
const router = useRouter() const { isLoading, isAuthenticated, user, loginWithRedirect, logout } =
const onRedirectCallback = (appState) => { useAuth0()
router.push(
appState && appState.targetUrl
? appState.targetUrl
: window.location.pathname
)
}
const { loading, isAuthenticated, user, loginWithRedirect, logout } =
useAuth(onRedirectCallback)
const doLogin = () => { const doLogin = () => {
loginWithRedirect({ loginWithRedirect({
appState: { targetUrl: window.location.pathname } appState: { targetUrl: window.location.pathname },
}) })
} }
const doLogout = () => { const doLogout = () => {
logout({ logout({
logoutParams: { logoutParams: {
returnTo: window.location.origin returnTo: window.location.origin,
} },
}) })
} }
const darkMode = computed({ const theme = useTheme()
get: () => (instance ? instance.proxy.$vuetify.theme.dark : false), onMounted(() => {
set: (val) => { if (state.darkMode) {
if (instance) { theme.global.name.value = 'dark'
instance.proxy.$vuetify.theme.dark = val } else {
state.setDarkMode(instance.proxy.$vuetify.theme.dark) theme.global.name.value = 'light'
}
} }
}) })
const locale = computed({ const darkMode = computed<boolean>({
get: () => (instance ? instance.proxy.$i18n.locale : 'sv'), get: () => theme.global.current.value.dark,
set: (newLocale) => { set: (value: boolean) => {
if (instance) { state.setDarkMode(value)
instance.proxy.$i18n.setLocaleCookie(newLocale) if (value) {
instance.proxy.$vuetify.lang.current = newLocale theme.global.name.value = 'dark'
instance.proxy.$i18n.locale = newLocale } else {
} theme.global.name.value = 'light'
state.setLocale(newLocale)
} }
},
}) })
const nav = ref(false) const nav = ref(false)
locale.value = instance.proxy.$i18n.locale
const title = computed(() => state.title) const title = computed(() => state.title)
</script> </script>
-12
View File
@@ -1,12 +0,0 @@
import { useAuth } from '~/plugins/auth'
export default ({ app: { router } }) => {
const onRedirectCallback = (appState) => {
router.push(
appState && appState.targetUrl
? appState.targetUrl
: window.location.pathname
)
}
useAuth(onRedirectCallback)
}
+42 -132
View File
@@ -1,151 +1,61 @@
import { defineNuxtConfig } from '@nuxt/bridge' import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
import translations from './translations' import { defineNuxtConfig } from 'nuxt/config'
import numberFormats from './translations/numberFormats'
export default defineNuxtConfig({ export default defineNuxtConfig({
alias: {
tslib: 'tslib/tslib.es6.js'
},
bridge: {
meta: true
},
build: { build: {
extend (config) { transpile: ['vuetify', 'date-fns'],
config.module.rules.push({
include: /node_modules/,
test: /\.mjs$/,
type: 'javascript/auto'
})
},
babel: {
presets ({ isServer }) {
return [
[
// require.resolve('@nuxt/babel-preset-app'),
require.resolve('@nuxt/babel-preset-app-edge'), // For nuxt-edge users
{
buildTarget: isServer ? 'server' : 'client',
corejs: { version: 3 }
}
]
]
}
},
loaders: {
vue: {
prettify: false
}
},
transpile: ['date-fns', '@unhead/vue', 'unhead']
},
buildModules: [
// 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'
],
css: ['vuetify/dist/vuetify.css', '~/assets/scss/global.scss'],
env: {
graphqlApi: process.env.GRAPHQL_API
},
head: {
link: [
{
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/apple-touch-icon.png'
},
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: '/favicon-32x32.png'
},
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: '/favicon-16x16.png'
},
{ rel: 'manifest', href: '/site.webmanifest' },
{ rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#7f0aff' },
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons'
},
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css?family=Material+Icons'
},
{
rel: 'stylesheet',
href: 'https://cdn.materialdesignicons.com/3.3.92/css/materialdesignicons.min.css'
}
],
meta: [
{
name: 'viewport',
content:
'width=device-width, initial-scale=1, user-scalable=no, minimal-ui'
}
]
}, },
devtools: { enabled: true },
i18n: { i18n: {
strategy: 'prefix_and_default',
detectBrowserLanguage: { detectBrowserLanguage: {
useCookie: true, useCookie: true,
cookieKey: 'nuxt_i18n_redirected', cookieKey: 'nuxt_i18n_redirected',
redirectOn: 'root', // recommended
alwaysRedirect: true, alwaysRedirect: true,
fallbackLocale: 'sv' fallbackLocale: 'sv',
}, },
langDir: 'translations',
lazy: true,
locales: [ locales: [
{ { code: 'sv', name: 'Svenska', file: 'sv.ts' },
code: 'en', { code: 'en', name: 'English', file: 'en.ts' },
iso: 'en-US'
},
{
code: 'sv',
iso: 'sv-SE'
}
], ],
defaultLocale: 'sv', defaultLocale: 'sv',
vueI18n: { vueI18n: './i18n.config.ts', // if you are using custom path, default
fallbackLocale: 'sv',
messages: translations,
numberFormats
}
}, },
modules: [ modules: [
'@nuxtjs/i18n', (_options, nuxt) => {
'@pinia/nuxt', nuxt.hooks.hook('vite:extendConfig', (config) => {
'@nuxtjs/sentry' // @ts-expect-error
], config.plugins.push(
plugins: [ vuetify({
'~/plugins/apollo', autoImport: true,
'~/plugins/i18n', // styles: { configFile: "assets/settings.scss" },
'~/plugins/pinia' }),
], )
router: { })
middleware: ['auth']
}, },
sentry: { '@nuxtjs/eslint-module',
dsn: 'https://da2e8d42185a4013909d49955432a116@o365290.ingest.sentry.io/5187660', '@pinia/nuxt',
config: { '@pinia-plugin-persistedstate/nuxt',
tracing: { '@nuxtjs/i18n',
tracesSampleRate: 1.0, '@nuxt/devtools',
browserTracing: {}, ],
vueOptions: { piniaPersistedstate: {
trackComponents: true cookieOptions: {
} sameSite: 'strict',
} },
} // Additional config storage: 'localStorage',
}, },
ssr: false, ssr: false,
target: 'static', vite: {
vuetify: { resolve: {
optionsPath: './vuetify.options.js' dedupe: ['pinia'],
} },
vue: {
template: {
transformAssetUrls,
},
},
},
}) })
+48 -57
View File
@@ -7,76 +7,67 @@
"author": "Joakim Olsson <joakim@unbound.se>", "author": "Joakim Olsson <joakim@unbound.se>",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "nuxi dev", "build": "nuxt build",
"build": "nuxi generate", "dev": "nuxt dev",
"start": "nuxi preview", "generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"lint:js": "eslint --ext \".ts,.js,.cjs,.vue\" --ignore-path .gitignore .", "lint:js": "eslint --ext \".ts,.js,.cjs,.vue\" --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{css,scss,sass,html,vue}\" --ignore-path .gitignore", "lint:style": "stylelint \"**/*.{css,scss,sass,html,vue}\" --ignore-path .gitignore",
"lint": "yarn lint:js && yarn lint:style", "lint": "yarn lint:js && yarn lint:style",
"lintfix": "yarn lint:js --fix && yarn lint:style --fix", "lintfix": "yarn lint:js --fix && yarn lint:style --fix",
"prepare": "husky install",
"test": "jest",
"codegen": "graphql-codegen && eslint --ext \".ts\" --ignore-path .gitignore graphql/generated/operations.ts --fix" "codegen": "graphql-codegen && eslint --ext \".ts\" --ignore-path .gitignore graphql/generated/operations.ts --fix"
}, },
"dependencies": { "devDependencies": {
"@apollo/client": "^3.9.2", "@commitlint/cli": "^18.6.0",
"@auth0/auth0-spa-js": "^2.1.3", "@commitlint/config-conventional": "^18.6.0",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/fragment-matcher": "^5.0.0",
"@graphql-codegen/typescript": "^4.0.1", "@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1", "@graphql-codegen/typescript-operations": "^4.0.1",
"@graphql-codegen/typescript-vue-apollo": "^4.1.0", "@graphql-codegen/typescript-vue-apollo": "^4.1.0",
"@nuxtjs/i18n": "^7.3.1", "@nuxt/devtools": "^1.0.8",
"@nuxtjs/sentry": "^8.0.6", "@nuxtjs/eslint-config-typescript": "^12.1.0",
"@nuxtjs/vuetify": "^1.12.3", "@nuxtjs/eslint-module": "^4.1.0",
"@nuxtjs/i18n": "^8.0.1",
"@pinia-plugin-persistedstate/nuxt": "^1.2.0",
"@types/vuelidate": "^0.7.21",
"@typescript-eslint/parser": "^6.20.0",
"@vue/test-utils": "^2.4.4",
"esbuild": "^0.20.0",
"eslint": "^8.56.0",
"eslint-plugin-nuxt": "^4.0.0",
"eslint-plugin-vue": "^9.21.1",
"nuxt": "^3.10.0",
"postcss-html": "^1.6.0",
"sass": "^1.70.0",
"stylelint": "^16.2.1",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^36.0.0",
"typescript": "^5.3.3",
"vite-plugin-vuetify": "^2.0.1",
"vue": "^3.4.15",
"vue-router": "^4.2.5",
"vuetify": "^3.5.2"
},
"dependencies": {
"@apollo/client": "^3.9.2",
"@auth0/auth0-vue": "^2.3.3",
"@mdi/font": "^7.4.47",
"@pinia/nuxt": "^0.5.1", "@pinia/nuxt": "^0.5.1",
"@snyk/protect": "^1.1276.0", "@sentry/browser": "^7.99.0",
"@snyk/graphlib": "^3.4.0",
"@snyk/lodash": "^4.17.15-patch",
"@vue/apollo-composable": "^4.0.1", "@vue/apollo-composable": "^4.0.1",
"@vueuse/core": "^10.7.2", "@vuelidate/core": "^2.0.3",
"core-js": "3", "@vuelidate/validators": "^2.0.4",
"@vuepic/vue-datepicker": "^7.4.1",
"apollo-link-sentry": "^3.3.0",
"date-fns": "^3.3.1", "date-fns": "^3.3.1",
"graphql": "^16.8.1", "graphql": "^16.8.1",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"node-sass": "^9.0.0",
"nuxt-edge": "latest",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1", "subscriptions-transport-ws": "^0.11.0",
"sass-loader": "^10.1.1", "vue-debounce": "^5.0.0"
"vue": "2.7.16", }
"vue-demi": "^0.14.7",
"vue-server-renderer": "2.7.16",
"vue-template-compiler": "2.7.16",
"vuetify": "^2.7.1"
},
"devDependencies": {
"@babel/runtime-corejs3": "^7.23.9",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/fragment-matcher": "^5.0.0",
"@nuxt/bridge": "npm:@nuxt/bridge-edge",
"@nuxt/types": "^2.17.3",
"@nuxtjs/eslint-config-typescript": "^12.1.0",
"@nuxtjs/eslint-module": "^4.1.0",
"@nuxtjs/stylelint-module": "^5.1.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"babel-eslint": "^10.0.3",
"cli-engine": "^4.7.6",
"cypress": "^13.6.4",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-nuxt": "^4.0.0",
"eslint-plugin-vue": "^9.21.1",
"husky": "^9.0.10",
"nuxi": "^3.10.0",
"postcss-html": "^1.6.0",
"stylelint": "^14.16.1",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^29.0.0",
"typescript": "^5.3.3",
"wait-on": "^7.2.0"
},
"resolutions": {
"eslint-webpack-plugin": "^2.0.0"
},
"snyk": true
} }
+4 -4
View File
@@ -4,11 +4,11 @@
<script setup lang='ts'> <script setup lang='ts'>
import { useHead } from '@unhead/vue' import { useHead } from '@unhead/vue'
import Filters from '../components/pages/filters/index.vue' import { useI18n } from '#i18n'
import { useTranslation } from '~/plugins/i18n' import Filters from '../components/pages/filters/filter-page.vue'
const { t } = useTranslation() const { t } = useI18n()
useHead({ useHead({
title: t('filters.title') title: t('filters.title'),
}) })
</script> </script>
+4 -4
View File
@@ -4,11 +4,11 @@
<script setup lang='ts'> <script setup lang='ts'>
import { useHead } from '@unhead/vue' import { useHead } from '@unhead/vue'
import Events from '~/components/pages/events/index.vue' import { useI18n } from '#i18n'
import { useTranslation } from '~/plugins/i18n' import Events from '~/components/pages/events/event-page.vue'
const { t } = useTranslation() const { t } = useI18n()
useHead({ useHead({
title: t('events.title') title: t('events.title'),
}) })
</script> </script>
+4 -4
View File
@@ -4,11 +4,11 @@
<script setup lang='ts'> <script setup lang='ts'>
import { useHead } from '@unhead/vue' import { useHead } from '@unhead/vue'
import Origins from '../components/pages/origins/index.vue' import { useI18n } from '#i18n'
import { useTranslation } from '~/plugins/i18n' import Origins from '../components/pages/origins/origin-page.vue'
const { t } = useTranslation() const { t } = useI18n()
useHead({ useHead({
title: t('origins.title') title: t('origins.title'),
}) })
</script> </script>
-48
View File
@@ -1,48 +0,0 @@
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { setContext } from '@apollo/client/link/context'
import { useAuth } from './auth'
const apiUrl = process.env.graphqlApi || '/query'
const cache = new InMemoryCache()
const httpLink = createHttpLink({
uri: apiUrl
})
const getToken = (options) => {
const { getTokenSilently } = useAuth()
return getTokenSilently(options)
}
const authLink = setContext((_, { headers }) => {
return getToken()
.then(token => ({
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ''
}
})).catch(() => {
return {}
})
})
const link = authLink.concat(httpLink)
const instance = new ApolloClient({
connectToDevTools: true,
link,
cache,
defaultOptions: {
query: {
fetchPolicy: 'cache-and-network'
},
watchQuery: {
fetchPolicy: 'cache-and-network'
}
}
})
export default function (_, inject) {
inject('apollo', instance)
}
+138
View File
@@ -0,0 +1,138 @@
import {
ApolloClient,
ApolloLink,
createHttpLink,
from,
InMemoryCache,
split,
} from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { setContext } from '@apollo/client/link/context'
import { getMainDefinition } from '@apollo/client/utilities'
import { SentryLink } from 'apollo-link-sentry'
import * as Sentry from '@sentry/browser'
import { type GetTokenSilentlyOptions } from '@auth0/auth0-spa-js'
import { DefaultApolloClient, provideApolloClient } from '@vue/apollo-composable'
import type { Auth0VueClient } from '@auth0/auth0-vue'
import { defineNuxtPlugin, useNuxtApp } from '#app'
import { envConfig } from '~/utils/environment'
// The side effect of patching fetch() has to occur before configuring Apollo Client
// https://github.com/getsentry/sentry-javascript/issues/2860#issuecomment-684514367
import './sentry'
const apiUrl = envConfig(window.location.hostname).apiUrl
const wsUrl = apiUrl.replace(/^http/, 'ws')
const cache = new InMemoryCache({
typePolicies: {
},
})
const getToken = async (options: GetTokenSilentlyOptions) => {
const nuxtApp = useNuxtApp()
const auth0: Auth0VueClient = nuxtApp.$auth0 as Auth0VueClient
return await auth0.getAccessTokenSilently(options).catch((err) => {
Sentry.captureException(err, {
extra: {
function: 'getTokenSilently',
},
})
return undefined
})
}
const httpLink = createHttpLink({
uri: apiUrl,
})
const wsLink = new WebSocketLink({
uri: wsUrl,
options: {
reconnect: true,
lazy: true,
connectionParams: () => {
return getToken({}).then((token) => ({
authToken: token,
}))
},
},
})
const authLink = setContext(async (_, { headers }) => {
return await getToken({}).then((token) => ({
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
}))
})
const traceHeaders = setContext((_, { headers }) => {
const th = Sentry.getCurrentHub().traceHeaders()
return {
headers: {
...headers,
...th,
},
}
})
const sentryTransactionLink = new ApolloLink((operation, forward) => {
const existingTransaction = Sentry.getCurrentHub()
.getScope()
?.getTransaction()
// Create a transaction if one does not exist in order to work around
// https://github.com/getsentry/sentry-javascript/issues/3169
// https://github.com/getsentry/sentry-javascript/issues/4072
const transaction =
existingTransaction ??
Sentry.startTransaction({
name: `Apollo Request: ${operation.operationName}`,
})
Sentry.getCurrentHub().configureScope((scope) => scope.setSpan(transaction))
operation.setContext({ tracing: { transaction } })
return forward(operation).map((data) => {
operation.getContext().tracing?.transaction?.finish()
return data
})
})
const link = sentryTransactionLink.concat(
traceHeaders.concat(
from([
new SentryLink({}),
split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
authLink.concat(wsLink),
authLink.concat(httpLink),
),
]),
),
)
const instance = new ApolloClient({
connectToDevTools: true,
link,
cache,
defaultOptions: {
query: {
fetchPolicy: 'cache-first',
},
watchQuery: {
fetchPolicy: 'cache-and-network',
},
},
})
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.provide(DefaultApolloClient[Symbol.toStringTag], instance)
provideApolloClient(instance)
})
-141
View File
@@ -1,141 +0,0 @@
import {
createAuth0Client,
Auth0Client,
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: () => 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,
clientId: 'orQfnvCPUR5C3mJkKoiWLQHOVQsBn60e',
authorizationParams: {
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: () => {
return instance.auth0Client!.then(client => client.getIdTokenClaims())
},
/** 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
}
+10
View File
@@ -0,0 +1,10 @@
import { createAuth0 } from '@auth0/auth0-vue'
import { defineNuxtPlugin } from '#app'
import { envConfig } from '~/utils/environment'
export default defineNuxtPlugin((nuxtApp) => {
const options = envConfig(window.location.hostname).auth
const auth0 = createAuth0(options)
nuxtApp.vueApp.use(auth0)
nuxtApp.provide('auth0', auth0)
})
-4
View File
@@ -1,4 +0,0 @@
import Vue from 'vue'
import hooks from '@u3u/vue-hooks'
Vue.use(hooks)
-14
View File
@@ -1,14 +0,0 @@
let i18n = null
let localePath = null
export const useTranslation = () => {
return {
t: i18n.t.bind(i18n),
localePath
}
}
export default ({ app }) => {
i18n = app.i18n
localePath = app.localePath
}
-10
View File
@@ -1,10 +0,0 @@
import { createPersistedState } from 'pinia-plugin-persistedstate'
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin(({ $pinia }) => {
$pinia.use(
createPersistedState({
key: id => `dancefinder_${id}`
})
)
})
+21
View File
@@ -0,0 +1,21 @@
import * as Sentry from '@sentry/browser'
import { defineNuxtPlugin } from '#app'
import { envConfig } from '~/utils/environment'
const env = envConfig(window.location.hostname)
Sentry.init({
enabled: env.sentryEnabled,
dsn: 'https://da2e8d42185a4013909d49955432a116@o365290.ingest.sentry.io/5187660',
integrations: [
new Sentry.BrowserTracing({ traceFetch: false }),
Sentry.replayIntegration(),
],
environment: env.name,
tracesSampleRate: env.tracesSampleRate,
replaysSessionSampleRate: env.replaysSessionSampleRate,
replaysOnErrorSampleRate: env.replaysOnErrorSampleRate,
})
export default defineNuxtPlugin(() => {
})
-4
View File
@@ -1,4 +0,0 @@
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
+14
View File
@@ -0,0 +1,14 @@
import { createVuetify } from 'vuetify'
import { defineNuxtPlugin } from '#app'
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
export default defineNuxtPlugin((app) => {
const vuetify = createVuetify({
theme: {
defaultTheme: 'light',
},
})
app.vueApp.use(vuetify)
})
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+6 -3
View File
@@ -1,4 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue'
import { Range } from '~/graphql/generated/operations' import { Range } from '~/graphql/generated/operations'
export const useState = defineStore( export const useState = defineStore(
@@ -40,10 +41,12 @@ export const useState = defineStore(
setLocale, setLocale,
setSearch, setSearch,
setIncludeHidden, setIncludeHidden,
setRange setRange,
} }
}, },
{ {
persist: true persist: {
} key: 'dancefinder_state',
},
},
) )
+2 -2
View File
@@ -2,9 +2,9 @@ module.exports = {
customSyntax: 'postcss-html', customSyntax: 'postcss-html',
extends: [ extends: [
'stylelint-config-standard', 'stylelint-config-standard',
'stylelint-config-recommended-vue' 'stylelint-config-recommended-vue',
], ],
// add your custom config here // add your custom config here
// https://stylelint.io/user-guide/configuration // https://stylelint.io/user-guide/configuration
rules: {} rules: {},
} }
+11
View File
@@ -0,0 +1,11 @@
export default {
title: 'Dancefinder',
login: 'Login',
logout: 'Log out',
darkMode: 'Dark mode',
links: {
events: 'Events',
filters: 'Filters',
origins: 'Origins',
},
}
+11
View File
@@ -0,0 +1,11 @@
export default {
title: 'Dancefinder',
login: 'Logga in',
logout: 'Logga ut',
darkMode: 'Mörkt läge',
links: {
events: 'Evenemang',
filters: 'Hantera filter',
origins: 'Hantera startpunkter',
},
}
-24
View File
@@ -1,24 +0,0 @@
export default {
en: {
title: 'Dancefinder',
login: 'Login',
logout: 'Log out',
darkMode: 'Dark mode',
links: {
events: 'Events',
filters: 'Filters',
origins: 'Origins'
}
},
sv: {
title: 'Dancefinder',
login: 'Logga in',
logout: 'Logga ut',
darkMode: 'Mörkt läge',
links: {
events: 'Evenemang',
filters: 'Hantera filter',
origins: 'Hantera startpunkter'
}
}
}
+14
View File
@@ -0,0 +1,14 @@
import { defineI18nLocale } from '#i18n'
import app from './app-en'
import events from './events-en'
import filters from './filters-en'
import origins from './origins-en'
export default defineI18nLocale(() => {
return {
app,
events,
filters,
origins,
}
})
+20
View File
@@ -0,0 +1,20 @@
export default {
title: 'Dancefinder - Events',
login: 'NB! Login to handle filtering of the list',
range: {
ONE_WEEK: '1 week',
TWO_WEEKS: '2 weeks',
ONE_MONTH: '1 month',
ONE_QUARTER: '1 quarter',
ONE_YEAR: '1 year',
},
date: 'Date:',
time: 'Time:',
hall: 'Where:',
city: 'City:',
municipality: 'Municipality:',
state: 'State:',
hide: 'Hide',
filter: 'Full text search',
includeHidden: 'Include hidden',
}
+20
View File
@@ -0,0 +1,20 @@
export default {
title: 'Dancefinder - Evenemang',
login: 'OBS! Logga in för att kunna filtrera listan',
range: {
ONE_WEEK: '1 vecka',
TWO_WEEKS: '2 veckor',
ONE_MONTH: '1 månad',
ONE_QUARTER: '1 kvartal',
ONE_YEAR: '1 år',
},
date: 'Datum:',
time: 'Tid:',
hall: 'Var:',
city: 'Stad:',
municipality: 'Kommun:',
state: 'Län:',
hide: 'Dölj',
filter: 'Fritext-sökning',
includeHidden: 'Inkludera dolda',
}
-42
View File
@@ -1,42 +0,0 @@
export default {
en: {
title: 'Dancefinder - Events',
login: 'NB! Login to handle filtering of the list',
range: {
ONE_WEEK: '1 week',
TWO_WEEKS: '2 weeks',
ONE_MONTH: '1 month',
ONE_QUARTER: '1 quarter',
ONE_YEAR: '1 year'
},
date: 'Date:',
time: 'Time:',
hall: 'Where:',
city: 'City:',
municipality: 'Municipality:',
state: 'State:',
hide: 'Hide',
filter: 'Full text search',
includeHidden: 'Include hidden'
},
sv: {
title: 'Dancefinder - Evenemang',
login: 'OBS! Logga in för att kunna filtrera listan',
range: {
ONE_WEEK: '1 vecka',
TWO_WEEKS: '2 veckor',
ONE_MONTH: '1 månad',
ONE_QUARTER: '1 kvartal',
ONE_YEAR: '1 år'
},
date: 'Datum:',
time: 'Tid:',
hall: 'Var:',
city: 'Stad:',
municipality: 'Kommun:',
state: 'Län:',
hide: 'Dölj',
filter: 'Fritext-sökning',
includeHidden: 'Inkludera dolda'
}
}
+11
View File
@@ -0,0 +1,11 @@
export default {
title: 'Dancefinder - Filters',
band: 'No bands | 1 band | {count} bands',
hall: 'No dance halls | 1 dance hall | {count} dance halls',
city: 'No cities | 1 city | {count} cities',
municipality: 'No municipalities | 1 municipality | {count} municipalities',
state: 'No states | 1 state | {count} states',
remove: 'Remove filter',
success: 'Filter for {name} is removed',
failure: 'Filter for {name} could not be removed',
}
+11
View File
@@ -0,0 +1,11 @@
export default {
title: 'Dancefinder - Filter',
band: 'Inga band | 1 band | {count} band',
hall: 'Inga danslokaler | 1 danslokal | {count} danslokaler',
city: 'Ingen stad | 1 stad | {count} städer',
municipality: 'Ingen kommun | 1 kommun | {count} kommuner',
state: 'Inget län | 1 län | {count} län',
remove: 'Ta bort filter',
success: 'Filtrering av {name} har tagits bort',
failure: 'Filtrering av {name} kunde inte tas bort',
}
-24
View File
@@ -1,24 +0,0 @@
export default {
en: {
title: 'Dancefinder - Filters',
band: 'No bands | 1 band | {count} bands',
hall: 'No dance halls | 1 dance hall | {count} dance halls',
city: 'No cities | 1 city | {count} cities',
municipality: 'No municipalities | 1 municipality | {count} municipalities',
state: 'No states | 1 state | {count} states',
remove: 'Remove filter',
success: 'Filter for {name} is removed',
failure: 'Filter for {name} could not be removed'
},
sv: {
title: 'Dancefinder - Filter',
band: 'Inga band | 1 band | {count} band',
hall: 'Inga danslokaler | 1 danslokal | {count} danslokaler',
city: 'Ingen stad | 1 stad | {count} städer',
municipality: 'Ingen kommun | 1 kommun | {count} kommuner',
state: 'Inget län | 1 län | {count} län',
remove: 'Ta bort filter',
success: 'Filtrering av {name} har tagits bort',
failure: 'Filtrering av {name} kunde inte tas bort'
}
}
-19
View File
@@ -1,19 +0,0 @@
import app from './app'
import events from './events'
import filters from './filters'
import origins from './origins'
export default {
en: {
app: app.en,
events: events.en,
filters: filters.en,
origins: origins.en
},
sv: {
app: app.sv,
events: events.sv,
filters: filters.sv,
origins: origins.sv
}
}
-14
View File
@@ -1,14 +0,0 @@
export default {
en: {
currency: {
style: 'currency',
currency: 'USD'
}
},
sv: {
currency: {
style: 'currency',
currency: 'SEK'
}
}
}
+8
View File
@@ -0,0 +1,8 @@
export default {
title: 'Dancefinder - Origins',
origin: 'Origin',
geolocation: 'Address/geolocation (WGS84)',
fetchAddress: 'Fetch current position',
save: 'Save origin',
remove: 'Remove origin',
}
+8
View File
@@ -0,0 +1,8 @@
export default {
title: 'Dancefinder - Startpunkter',
origin: 'Startpunkt',
geolocation: 'Address/geokoordinat (WGS84)',
fetchAddress: 'Hämta nuvarande position',
save: 'Spara startpunkt',
remove: 'Ta bort startpunkt',
}
-18
View File
@@ -1,18 +0,0 @@
export default {
en: {
title: 'Dancefinder - Origins',
origin: 'Origin',
geolocation: 'Address/geolocation (WGS84)',
fetchAddress: 'Fetch current position',
save: 'Save origin',
remove: 'Remove origin'
},
sv: {
title: 'Dancefinder - Startpunkter',
origin: 'Startpunkt',
geolocation: 'Address/geokoordinat (WGS84)',
fetchAddress: 'Hämta nuvarande position',
save: 'Spara startpunkt',
remove: 'Ta bort startpunkt'
}
}
+14
View File
@@ -0,0 +1,14 @@
import { defineI18nLocale } from '#i18n'
import app from './app-sv'
import events from './events-sv'
import filters from './filters-sv'
import origins from './origins-sv'
export default defineI18nLocale(() => {
return {
app,
events,
filters,
origins,
}
})
-38
View File
@@ -1,38 +0,0 @@
export default {
sv: {
close: 'Stäng',
dataIterator: {
pageText: '{0}-{1} av {2}',
noResultsText: 'Inga matchande resultat',
loadingText: 'Laddar...'
},
dataTable: {
itemsPerPageText: 'Rader per sida:',
ariaLabel: {
sortDescending:
': Sorterad minskande. Aktivera för att ta bort sortering.',
sortAscending: ': Sorterad ökande. Aktivera för att sortera minskande.',
sortNone: ': Ej sorterad. Aktivera för att sortera ökande.'
}
},
dataFooter: {
itemsPerPageText: 'Per sida:',
itemsPerPageAll: 'Alla',
nextPage: 'Nästa sida',
prevPage: 'Föregående sida',
firstPage: 'Första sidan',
lastPage: 'Sista sidan'
},
datePicker: {
itemsSelected: '{0} valda'
},
noDataText: 'Ingen data tillgänglig',
carousel: {
prev: 'Föregående yta',
next: 'Nästa yta'
},
calendar: {
moreEvents: '{0} fler'
}
}
}
+2 -26
View File
@@ -1,28 +1,4 @@
{ {
"extends": "./.nuxt/tsconfig.json", // https://nuxt.com/docs/guide/concepts/typescript
"compilerOptions": { "extends": "./.nuxt/tsconfig.json"
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ESNext",
"ESNext.AsyncIterable",
"DOM"
],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"baseUrl": ".",
"types": [
"@types/node",
"@nuxt/types",
"@nuxtjs/i18n",
"@nuxtjs/sentry"
]
},
"exclude": [
"node_modules"
]
} }
+55
View File
@@ -0,0 +1,55 @@
import { type Auth0ClientOptions, type CacheLocation } from '@auth0/auth0-spa-js'
interface EnvConfig {
name: string
apiUrl: string
auth: Auth0ClientOptions
dev: boolean
sentryEnabled: boolean
tracesSampleRate: number
replaysSessionSampleRate: number
replaysOnErrorSampleRate: number
}
export const envConfig = (host: string): EnvConfig => {
const options = {
useRefreshTokens: true,
cacheLocation: 'localstorage' as CacheLocation,
authorizationParams: {
audience: 'http://dancefinder.unbound.se',
redirect_uri: window.location.origin,
},
}
const prod = {
...options,
domain: 'unbound.eu.auth0.com',
clientId: 'orQfnvCPUR5C3mJkKoiWLQHOVQsBn60e',
}
switch (host) {
case 'localhost':
return {
name: 'development',
apiUrl: 'http://localhost:6080/query',
auth: prod,
dev: true,
sentryEnabled: false,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 1.0,
replaysOnErrorSampleRate: 1.0,
}
case 'dancefinder.unbound.se':
return {
name: 'production',
apiUrl: 'https://dancefinder.unbound.se/query',
auth: prod,
dev: false,
sentryEnabled: true,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
}
default:
throw new Error(`unexpected host: ${host}`)
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { gql as apolloGql } from '@apollo/client/core' import { gql as apolloGql } from '@apollo/client/core'
import { markRaw } from 'vue' import { markRaw } from 'vue'
export function gql (literals: string | readonly string[], ...args: any[]) { export function gql(literals: string | readonly string[], ...args: any[]) {
return markRaw(apolloGql(literals, ...args)) return markRaw(apolloGql(literals, ...args))
} }
+2366 -8035
View File
File diff suppressed because it is too large. Load diff