chore: upgrade to Vue3/Vuetify3

This commit is contained in:
2024-02-05 16:48:02 +01:00
parent 171e1039a7
commit ef3b5460ad
65 changed files with 3153 additions and 9032 deletions
+24 -22
View File
@@ -1,36 +1,38 @@
module.exports = {
root: true,
env: {
browser: true,
node: true
},
parser: 'vue-eslint-parser',
plugins: [
'@typescript-eslint'
],
parserOptions: {
parser: '@typescript-eslint/parser',
requireConfigFile: false
sourceType: 'module',
},
extends: [
'@nuxtjs',
'@nuxtjs/eslint-config-typescript',
'plugin:nuxt/recommended'
],
// add your custom rules here
rules: {},
extends: ['@nuxtjs/eslint-config-typescript', 'eslint:recommended', 'plugin:vue/vue3-recommended'],
rules: {
'vue/valid-v-slot': 'off',
'arrow-parens': ['error', 'always'],
'comma-dangle': ['error', 'always-multiline'],
'space-before-function-paren': ['error', {
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
}],
'@typescript-eslint/consistent-type-imports': ['error', {
fixStyle: 'inline-type-imports',
}],
},
plugins: [],
ignorePatterns: ['nuxt.config.ts'],
overrides: [
{
files: ['pages/**/*.vue', 'layouts/*.vue'],
rules: {
'vue/multi-word-component-names': 'off'
}
'vue/multi-word-component-names': 'off',
},
},
{
files: ['graphql/generated/*.ts'],
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
COPY ./ ./
RUN yarn build && yarn lint
RUN yarn run generate && yarn lint
#RUN yarn start:ci & yarn wait && yarn test:cypress
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>
<v-card flat outlined rounded class="mx-3 my-3 rounded-xl">
<v-card-title primary-title>
<v-card flat variant="outlined" rounded="xl">
<v-card-title v-if="event.band" primary-title>
<h3 class="headline mb-0">
<v-icon
v-if="hasUser"
class="ml-1 mr-1"
medium
class="ml-1 mr-1 text-medium-emphasis"
size="small"
title="Dölj"
@click="toggleIgnore('band', event.band.name)"
>
mdi-eye-off
</v-icon>{{ event.band.name }}
</v-icon>
{{ event.band.name }}
</h3>
</v-card-title>
<v-container>
<v-layout row wrap>
<v-flex
xs12
sm6
<v-card-text>
<v-row wrap>
<v-col
xs="12"
sm="6"
>
<strong class="mr-1" v-text="$t('events.date')" />{{
event.date
}}
({{ weekday }} {{ daysUntil }})
</v-flex>
<v-flex v-if="event.time" xs12 sm6>
</v-col>
<v-col v-if="event.time" xs="12" sm="6">
<strong class="mr-1" v-text="$t('events.time')" />{{
event.time
}}
</v-flex>
</v-layout>
<v-layout row wrap>
<v-flex
xs12
sm6
md3
</v-col>
</v-row>
<v-row v-if="event.danceHall" wrap>
<v-col
xs="12"
sm="6"
md="3"
>
<strong class="mr-1" v-text="$t('events.hall')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1"
small
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
@click="toggleIgnore('danceHall', event.danceHall.name)"
>
mdi-eye-off
</v-icon>
{{ event.danceHall.name }}
</v-flex>
<v-flex
xs12
sm6
md3
</v-col>
<v-col
xs="12"
sm="6"
md="3"
>
<strong class="mr-1" v-text="$t('events.city')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1"
small
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
@click="toggleIgnore('city', event.danceHall.city)"
>
mdi-eye-off
</v-icon>
{{ event.danceHall.city }}
</v-flex>
<v-flex
xs12
sm6
md3
</v-col>
<v-col
xs="12"
sm="6"
md="3"
>
<strong class="mr-1" v-text="$t('events.municipality')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1"
small
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
@click="
toggleIgnore('municipality', event.danceHall.municipality)
@@ -83,27 +84,27 @@
mdi-eye-off
</v-icon>
{{ event.danceHall.municipality }}
</v-flex>
<v-flex
xs12
sm6
md3
</v-col>
<v-col
xs="12"
sm="6"
md="3"
>
<strong class="mr-1" v-text="$t('events.state')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1"
small
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
@click="toggleIgnore('state', event.danceHall.state)"
>
mdi-eye-off
</v-icon>
{{ event.danceHall.state }}
</v-flex>
</v-layout>
</v-col>
</v-row>
<distance-display v-for="distance in event.distances" :key="distance.origin" :distance="distance" />
</v-container>
</v-card-text>
</v-card>
</template>
@@ -112,30 +113,31 @@
import { format, formatDistanceToNow, parseISO } from 'date-fns'
import { enGB, sv } from 'date-fns/locale'
import { computed, getCurrentInstance, PropType } from 'vue'
import { Event } from '~/graphql/generated/operations'
import DistanceDisplay from '~/components/pages/events/Event/distance.vue'
import { computed, type PropType } from 'vue'
import { useI18n } from '#i18n'
import { type Event } from '~/graphql/generated/operations'
import DistanceDisplay from '~/components/pages/events/event-distance.vue'
const props = defineProps({
event: {
type: Object as PropType<Event>,
required: true
required: true,
},
hasUser: {
type: Boolean,
required: true
required: true,
},
toggleIgnore: {
type: Function,
required: true
}
required: true,
},
})
const instance = getCurrentInstance()
const locale = computed(() => (instance?.proxy.$i18n.locale ?? 'sv') === 'en' ? enGB : sv)
const { locale: currentLocale } = useI18n()
const locale = computed(() => (currentLocale.value ?? 'sv') === 'en' ? enGB : sv)
const time = computed(() => (props.event.time || '').split('-')[0].replace('.', ':'))
const weekday = computed(() => format(parseISO(props.event.date), 'EEEE', { locale: locale.value }))
const daysUntil = computed(() => formatDistanceToNow(parseISO(`${props.event.date}T${time.value}`), {
addSuffix: true,
locale: locale.value
locale: locale.value,
}))
</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>
<div>
<v-row v-for="event in events" :key="event.id" wrap>
<v-flex xs12>
<v-col xs="12">
<event-card
:event="event"
:has-user="hasUser"
:toggle-ignore="toggleIgnore"
/>
</v-flex>
</v-col>
</v-row>
</div>
</template>
<script setup lang='ts'>
import { type PropType } from 'vue'
import EventCard from '../Event/index.vue'
import EventCard from './event-card.vue'
import { type Event } from '~/graphql/generated/operations'
defineProps({
hasUser: {
type: Boolean,
required: true
required: true,
},
toggleIgnore: {
type: Function,
required: true
required: true,
},
events: {
type: Array as PropType<Event[]>,
required: true
}
required: true,
},
})
</script>
@@ -10,14 +10,15 @@
<v-col xs="12">
<v-text-field
v-model="origin"
variant="underlined"
:label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')"
>
<template #append-outer>
<template #append>
<v-tooltip top>
<template #activator="{ on }">
<template #activator="{ props }">
<v-icon
v-on="on"
v-bind="props"
@click="fetchAddressFn()"
>
mdi-crosshairs-gps
@@ -28,10 +29,10 @@
</template>
<template #prepend>
<v-tooltip v-if="isAuthenticated" top>
<template #activator="{ on }">
<template #activator="{ props }">
<v-icon
:disabled="!origin"
v-on="on"
v-bind="props"
@click="saveOriginFn(origin)"
>
mdi-bookmark-plus-outline
@@ -46,20 +47,20 @@
<v-row wrap>
<v-col>
<v-btn-toggle
v-if="$vuetify.breakpoint.smAndUp"
v-if="smAndUp"
v-model="state.range"
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}`)" />
</v-btn>
</v-btn-toggle>
<v-select
v-else
v-model="state.range"
outline
variant="outlined"
:items="ranges"
item-text="name"
item-title="name"
item-value="value"
/>
</v-col>
@@ -68,6 +69,7 @@
<v-col cols="12" sm="8">
<v-text-field
v-model="state.search"
variant="underlined"
append-outer-icon="mdi-magnify"
:label="$t('events.filter')"
:placeholder="$t('events.filter')"
@@ -95,11 +97,12 @@
<script setup lang='ts'>
import { computed, ref, watch } from 'vue'
import EventList from './List/index.vue'
import { useAuth } from '~/plugins/auth'
import { useTranslation } from '~/plugins/i18n'
import { useAuth0 } from '@auth0/auth0-vue'
import { useDisplay } from 'vuetify'
import { useI18n } from '#i18n'
import EventList from './event-list.vue'
import {
FindEventsQueryVariables,
type FindEventsQueryVariables,
useFetchAddressLazyQuery,
useFindEventsQuery,
useSaveOriginMutation,
@@ -107,20 +110,21 @@ import {
useToggleIgnoreCityMutation,
useToggleIgnoreDanceHallMutation,
useToggleIgnoreMunicipalityMutation,
useToggleIgnoreStateMutation
useToggleIgnoreStateMutation,
} from '~/graphql/generated/operations'
import { useState } from '~/store'
const state = useState()
const { smAndUp } = useDisplay()
const range = computed(() => state.range)
const { t } = useTranslation()
const { t } = useI18n()
state.setTitle(t('app.links.events'))
const { isAuthenticated } = useAuth()
const { isAuthenticated } = useAuth0()
const variables = ref<FindEventsQueryVariables>({
range: state.range,
includeOrigins: isAuthenticated.value || false,
search: state.search,
includeHidden: state.includeHidden
includeHidden: state.includeHidden,
})
const { result, refetch } = useFindEventsQuery(() => variables.value)
watch([state, isAuthenticated], () => {
@@ -137,7 +141,7 @@ const ranges = [
{ name: '2 veckor', value: 'TWO_WEEKS' },
{ name: '1 månad', value: 'ONE_MONTH' },
{ 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: '' })
@@ -151,7 +155,7 @@ const fetchEvents = () => {
...variables.value,
range: state.range,
origins: originsTemp,
includeOrigins: isAuthenticated.value || false
includeOrigins: isAuthenticated.value || false,
}
refetch(variables.value)
}
@@ -217,7 +221,7 @@ const fetchAddressFn = () => {
window.navigator.geolocation.getCurrentPosition((pos) => {
loadFetchAddress()
doFetchAddress({
latlng: `${pos.coords.latitude},${pos.coords.longitude}`
latlng: `${pos.coords.latitude},${pos.coords.longitude}`,
})?.then((result) => {
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>
<div :key="isAuthenticated">
<div :key="isAuthenticated ? 'true' : 'false'">
<v-container fluid grid-list-md class="app-fade-in">
<v-row wrap>
<v-col cols="12">
@@ -62,23 +62,23 @@
<script setup lang='ts'>
import { computed, ref } from 'vue'
import List from './List/index.vue'
import { useAuth } from '~/plugins/auth'
import { useTranslation } from '~/plugins/i18n'
import { useAuth0 } from '@auth0/auth0-vue'
import { useI18n } from '#i18n'
import List from './filter-list.vue'
import {
useFetchFiltersQuery,
useToggleIgnoreBandMutation,
useToggleIgnoreCityMutation,
useToggleIgnoreDanceHallMutation,
useToggleIgnoreMunicipalityMutation,
useToggleIgnoreStateMutation
useToggleIgnoreStateMutation,
} from '~/graphql/generated/operations'
import { useState } from '~/store'
const state = useState()
const { t } = useTranslation()
const { t } = useI18n()
state.setTitle(t('app.links.filters'))
const { isAuthenticated } = useAuth()
const { isAuthenticated } = useAuth0()
const { result, refetch } = useFetchFiltersQuery()
const bands = computed(() => result.value?.bands ?? [])
const cities = computed(() => result.value?.cities ?? [])
@@ -2,17 +2,18 @@
<div :key="isAuthenticated ? 'true' : 'false'">
<v-container fluid grid-list-md class="app-fade-in">
<v-layout row wrap>
<v-flex xs12>
<v-col xs="12">
<v-text-field
v-model="origin"
variant="underlined"
:label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')"
>
<template #append-outer>
<template #append>
<v-tooltip top>
<template #activator="{ on }">
<template #activator="{ props }">
<v-icon
v-on="on"
v-bind="props"
@click="fetchAddressFn()"
>
mdi-crosshairs-gps
@@ -23,10 +24,10 @@
</template>
<template #prepend>
<v-tooltip top>
<template #activator="{ on }">
<template #activator="{ props }">
<v-icon
:disabled="!origin"
v-on="on"
v-bind="props"
@click="saveOriginFn(origin)"
>
mdi-bookmark-plus-outline
@@ -36,14 +37,14 @@
</v-tooltip>
</template>
</v-text-field>
</v-flex>
</v-col>
</v-layout>
<v-layout v-for="o in origins" :key="o" row wrap>
<v-flex xs12>
<v-col xs="12">
<v-tooltip top>
<template #activator="{ on }">
<template #activator="{ props }">
<v-icon
v-on="on"
v-bind="props"
@click="removeOriginFn(o)"
>
mdi-delete-outline
@@ -52,7 +53,7 @@
<span v-text="$t('origins.remove')" />
</v-tooltip>
<span>{{ o }}</span>
</v-flex>
</v-col>
</v-layout>
</v-container>
<v-snackbar
@@ -67,20 +68,20 @@
<script setup lang='ts'>
import { computed, ref } from 'vue'
import { useAuth } from '~/plugins/auth'
import { useTranslation } from '~/plugins/i18n'
import { useAuth0 } from '@auth0/auth0-vue'
import { useI18n } from '#i18n'
import {
useFetchAddressLazyQuery,
useFindOriginsQuery,
useRemoveOriginMutation,
useSaveOriginMutation
useSaveOriginMutation,
} from '~/graphql/generated/operations'
import { useState } from '~/store'
const state = useState()
const { t } = useTranslation()
const { t } = useI18n()
state.setTitle(t('app.links.origins'))
const { isAuthenticated } = useAuth()
const { isAuthenticated } = useAuth0()
const { result, refetch } = useFindOriginsQuery()
const origins = computed(() => result.value?.origins ?? [])
const snackbar = ref({ active: false, color: 'success', text: '' })
@@ -93,7 +94,7 @@ const fetchAddressFn = () => {
window.navigator.geolocation.getCurrentPosition((pos) => {
load()
doFetchAddress({
latlng: `${pos.coords.latitude},${pos.coords.longitude}`
latlng: `${pos.coords.latitude},${pos.coords.longitude}`,
})?.then((res) => {
origin.value = res.data.address
})
+17 -17
View File
@@ -1,5 +1,5 @@
import * as VueApolloComposable from '@vue/apollo-composable'
import * as VueCompositionApi from 'vue'
import type * as VueCompositionApi from 'vue'
import { gql } from '@/utils/gql'
export type Maybe<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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
export type ToggleIgnoreStateMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ToggleIgnoreStateMutation, ToggleIgnoreStateMutationVariables>;
@@ -433,10 +433,10 @@ export const FetchAddressDocument = gql`
* 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)
}
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)
}
export type FetchAddressQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FetchAddressQuery, FetchAddressQueryVariables>;
@@ -462,10 +462,10 @@ export const FetchFiltersDocument = gql`
* @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>> = {}) {
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>> = {}) {
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>;
@@ -518,10 +518,10 @@ export const FindEventsDocument = gql`
* 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)
}
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)
}
export type FindEventsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<FindEventsQuery, FindEventsQueryVariables>;
@@ -543,10 +543,10 @@ export const FindOriginsDocument = gql`
* @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>> = {}) {
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>> = {}) {
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>;
@@ -557,6 +557,6 @@ export interface PossibleTypesResultData {
}
}
const result: PossibleTypesResultData = {
possibleTypes: {}
possibleTypes: {},
}
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>
<v-app :key="$i18n.locale + isAuthenticated">
<themed>
<v-navigation-drawer v-model="nav" temporary app>
<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-action>
<v-switch v-model="darkMode" />
</v-list-item-action>
<v-list-item-title>{{ $t('app.darkMode') }}</v-list-item-title>
<v-switch v-model="darkMode" color="primary" :label="$t('app.darkMode')" hide-details class="ms-1" />
</v-list-item>
<v-list-item @click="locale = 'en'">
<v-list-item-title>In English 🇬🇧</v-list-item-title>
<v-list-item title="In English" :to="switchLocalePath('en')">
<template #prepend>
<div class="d-flex d-inline-flex text-h5 me-8">
🇬🇧
</div>
</template>
</v-list-item>
<v-list-item @click="locale = 'sv'">
<v-list-item-title> Svenska 🇸🇪</v-list-item-title>
</v-list-item>
<v-list-item v-if="!user" link @click="doLogin">
<v-list-item-title>{{ $t('app.login') }}</v-list-item-title>
</v-list-item>
<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 title="På Svenska" :to="switchLocalePath('sv')">
<template #prepend>
<div class="d-flex d-inline-flex text-h5 me-8">
🇸🇪
</div>
</template>
</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-navigation-drawer>
<v-app-bar app scroll-off-screen>
<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-toolbar-items>
<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 v-if="isAuthenticated && user" :prepend-avatar="user.picture" :title="user.name" />
</v-toolbar-items>
</v-app-bar>
<v-main>
<v-container v-if="!loading" fluid>
<nuxt />
<v-container v-if="!isLoading" fluid>
<slot />
</v-container>
</v-main>
</themed>
</v-app>
</template>
<script setup lang='ts'>
import { computed, getCurrentInstance, provide, ref } from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import Themed from './components/themed.vue'
import { useAuth } from '~/plugins/auth'
import { computed, onMounted, ref } from 'vue'
import { useTheme } from 'vuetify'
import { useAuth0 } from '@auth0/auth0-vue'
import { useSwitchLocalePath } from '#i18n'
import { useState } from '~/store'
const instance = getCurrentInstance()
if (instance) {
provide(DefaultApolloClient, instance.proxy.$apollo)
}
const switchLocalePath = useSwitchLocalePath()
const state = useState()
const router = useRouter()
const onRedirectCallback = (appState) => {
router.push(
appState && appState.targetUrl
? appState.targetUrl
: window.location.pathname
)
}
const { loading, isAuthenticated, user, loginWithRedirect, logout } =
useAuth(onRedirectCallback)
const { isLoading, isAuthenticated, user, loginWithRedirect, logout } =
useAuth0()
const doLogin = () => {
loginWithRedirect({
appState: { targetUrl: window.location.pathname }
appState: { targetUrl: window.location.pathname },
})
}
const doLogout = () => {
logout({
logoutParams: {
returnTo: window.location.origin
}
returnTo: window.location.origin,
},
})
}
const darkMode = computed({
get: () => (instance ? instance.proxy.$vuetify.theme.dark : false),
set: (val) => {
if (instance) {
instance.proxy.$vuetify.theme.dark = val
state.setDarkMode(instance.proxy.$vuetify.theme.dark)
}
const theme = useTheme()
onMounted(() => {
if (state.darkMode) {
theme.global.name.value = 'dark'
} else {
theme.global.name.value = 'light'
}
})
const locale = computed({
get: () => (instance ? instance.proxy.$i18n.locale : 'sv'),
set: (newLocale) => {
if (instance) {
instance.proxy.$i18n.setLocaleCookie(newLocale)
instance.proxy.$vuetify.lang.current = newLocale
instance.proxy.$i18n.locale = newLocale
}
state.setLocale(newLocale)
const darkMode = computed<boolean>({
get: () => theme.global.current.value.dark,
set: (value: boolean) => {
state.setDarkMode(value)
if (value) {
theme.global.name.value = 'dark'
} else {
theme.global.name.value = 'light'
}
},
})
const nav = ref(false)
locale.value = instance.proxy.$i18n.locale
const title = computed(() => state.title)
</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 translations from './translations'
import numberFormats from './translations/numberFormats'
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
import { defineNuxtConfig } from 'nuxt/config'
export default defineNuxtConfig({
alias: {
tslib: 'tslib/tslib.es6.js'
},
bridge: {
meta: true
},
build: {
extend (config) {
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'
}
]
transpile: ['vuetify', 'date-fns'],
},
devtools: { enabled: true },
i18n: {
strategy: 'prefix_and_default',
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'nuxt_i18n_redirected',
redirectOn: 'root', // recommended
alwaysRedirect: true,
fallbackLocale: 'sv'
fallbackLocale: 'sv',
},
langDir: 'translations',
lazy: true,
locales: [
{
code: 'en',
iso: 'en-US'
},
{
code: 'sv',
iso: 'sv-SE'
}
{ code: 'sv', name: 'Svenska', file: 'sv.ts' },
{ code: 'en', name: 'English', file: 'en.ts' },
],
defaultLocale: 'sv',
vueI18n: {
fallbackLocale: 'sv',
messages: translations,
numberFormats
}
vueI18n: './i18n.config.ts', // if you are using custom path, default
},
modules: [
'@nuxtjs/i18n',
'@pinia/nuxt',
'@nuxtjs/sentry'
],
plugins: [
'~/plugins/apollo',
'~/plugins/i18n',
'~/plugins/pinia'
],
router: {
middleware: ['auth']
(_options, nuxt) => {
nuxt.hooks.hook('vite:extendConfig', (config) => {
// @ts-expect-error
config.plugins.push(
vuetify({
autoImport: true,
// styles: { configFile: "assets/settings.scss" },
}),
)
})
},
sentry: {
dsn: 'https://da2e8d42185a4013909d49955432a116@o365290.ingest.sentry.io/5187660',
config: {
tracing: {
tracesSampleRate: 1.0,
browserTracing: {},
vueOptions: {
trackComponents: true
}
}
} // Additional config
'@nuxtjs/eslint-module',
'@pinia/nuxt',
'@pinia-plugin-persistedstate/nuxt',
'@nuxtjs/i18n',
'@nuxt/devtools',
],
piniaPersistedstate: {
cookieOptions: {
sameSite: 'strict',
},
storage: 'localStorage',
},
ssr: false,
target: 'static',
vuetify: {
optionsPath: './vuetify.options.js'
}
vite: {
resolve: {
dedupe: ['pinia'],
},
vue: {
template: {
transformAssetUrls,
},
},
},
})
+48 -57
View File
@@ -7,76 +7,67 @@
"author": "Joakim Olsson <joakim@unbound.se>",
"private": true,
"scripts": {
"dev": "nuxi dev",
"build": "nuxi generate",
"start": "nuxi preview",
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"lint:js": "eslint --ext \".ts,.js,.cjs,.vue\" --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{css,scss,sass,html,vue}\" --ignore-path .gitignore",
"lint": "yarn lint:js && yarn lint:style",
"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"
},
"dependencies": {
"@apollo/client": "^3.9.2",
"@auth0/auth0-spa-js": "^2.1.3",
"devDependencies": {
"@commitlint/cli": "^18.6.0",
"@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-operations": "^4.0.1",
"@graphql-codegen/typescript-vue-apollo": "^4.1.0",
"@nuxtjs/i18n": "^7.3.1",
"@nuxtjs/sentry": "^8.0.6",
"@nuxtjs/vuetify": "^1.12.3",
"@nuxt/devtools": "^1.0.8",
"@nuxtjs/eslint-config-typescript": "^12.1.0",
"@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",
"@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",
"@vueuse/core": "^10.7.2",
"core-js": "3",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
"@vuepic/vue-datepicker": "^7.4.1",
"apollo-link-sentry": "^3.3.0",
"date-fns": "^3.3.1",
"graphql": "^16.8.1",
"graphql-tag": "^2.12.6",
"node-sass": "^9.0.0",
"nuxt-edge": "latest",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"sass-loader": "^10.1.1",
"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
"subscriptions-transport-ws": "^0.11.0",
"vue-debounce": "^5.0.0"
}
}
+4 -4
View File
@@ -4,11 +4,11 @@
<script setup lang='ts'>
import { useHead } from '@unhead/vue'
import Filters from '../components/pages/filters/index.vue'
import { useTranslation } from '~/plugins/i18n'
import { useI18n } from '#i18n'
import Filters from '../components/pages/filters/filter-page.vue'
const { t } = useTranslation()
const { t } = useI18n()
useHead({
title: t('filters.title')
title: t('filters.title'),
})
</script>
+4 -4
View File
@@ -4,11 +4,11 @@
<script setup lang='ts'>
import { useHead } from '@unhead/vue'
import Events from '~/components/pages/events/index.vue'
import { useTranslation } from '~/plugins/i18n'
import { useI18n } from '#i18n'
import Events from '~/components/pages/events/event-page.vue'
const { t } = useTranslation()
const { t } = useI18n()
useHead({
title: t('events.title')
title: t('events.title'),
})
</script>
+4 -4
View File
@@ -4,11 +4,11 @@
<script setup lang='ts'>
import { useHead } from '@unhead/vue'
import Origins from '../components/pages/origins/index.vue'
import { useTranslation } from '~/plugins/i18n'
import { useI18n } from '#i18n'
import Origins from '../components/pages/origins/origin-page.vue'
const { t } = useTranslation()
const { t } = useI18n()
useHead({
title: t('origins.title')
title: t('origins.title'),
})
</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)
})

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+6 -3
View File
@@ -1,4 +1,5 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { Range } from '~/graphql/generated/operations'
export const useState = defineStore(
@@ -40,10 +41,12 @@ export const useState = defineStore(
setLocale,
setSearch,
setIncludeHidden,
setRange
setRange,
}
},
{
persist: true
}
persist: {
key: 'dancefinder_state',
},
},
)
+2 -2
View File
@@ -2,9 +2,9 @@ module.exports = {
customSyntax: 'postcss-html',
extends: [
'stylelint-config-standard',
'stylelint-config-recommended-vue'
'stylelint-config-recommended-vue',
],
// add your custom config here
// 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",
"compilerOptions": {
"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"
]
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}
+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 { 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))
}
+2366 -8035
View File
File diff suppressed because it is too large Load Diff