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
+3153 -9032

No files matched your search

+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>
+65 -114
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-list-item-action>
<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 @click="locale = 'en'">
<v-list-item-title>In English 🇬🇧</v-list-item-title>
</v-list-item>
<v-list-item @click="locale = 'sv'">
<v-list-item-title>På 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>
</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-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-toolbar-items>
</v-app-bar>
<v-main>
<v-container v-if="!loading" fluid>
<nuxt />
</v-container>
</v-main>
</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-switch v-model="darkMode" color="primary" :label="$t('app.darkMode')" hide-details class="ms-1" />
</v-list-item>
<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 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 :title="title" />
<v-spacer />
<v-toolbar-items>
<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="!isLoading" fluid>
<slot />
</v-container>
</v-main>
</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
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'
}
state.setLocale(newLocale)
}
},
})
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)
}
+41 -131
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',
(_options, nuxt) => {
nuxt.hooks.hook('vite:extendConfig', (config) => {
// @ts-expect-error
config.plugins.push(
vuetify({
autoImport: true,
// styles: { configFile: "assets/settings.scss" },
}),
)
})
},
'@nuxtjs/eslint-module',
'@pinia/nuxt',
'@nuxtjs/sentry'
'@pinia-plugin-persistedstate/nuxt',
'@nuxtjs/i18n',
'@nuxt/devtools',
],
plugins: [
'~/plugins/apollo',
'~/plugins/i18n',
'~/plugins/pinia'
],
router: {
middleware: ['auth']
},
sentry: {
dsn: 'https://da2e8d42185a4013909d49955432a116@o365290.ingest.sentry.io/5187660',
config: {
tracing: {
tracesSampleRate: 1.0,
browserTracing: {},
vueOptions: {
trackComponents: true
}
}
} // Additional config
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)
})
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 { 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