feat: add i18n support and implement Grafana plugin

Adds internationalization support in filters and origins pages by 
importing the useI18n function. Expands ESLint configuration to 
include new rules and plugins, ensuring improved code quality. 
Introduces Grafana monitoring plugin to enhance performance 
tracking capabilities in the application.
This commit is contained in:
2025-06-13 15:21:27 +02:00
parent 7bbd8f522a
commit c80fd0313c
28 changed files with 1989 additions and 1948 deletions
+24 -29
View File
@@ -6,11 +6,10 @@
v-if="hasUser"
class="ml-1 mr-1 text-medium-emphasis"
size="small"
title="Dölj"
:title="t('events.hide')"
icon='mdi-eye-off'
@click="toggleIgnore('band', event.band.name)"
>
mdi-eye-off
</v-icon>
/>
{{ event.band.name }}
</h3>
</v-card-title>
@@ -21,13 +20,13 @@
xs="12"
sm="6"
>
<strong class="mr-1" v-text="$t('events.date')" />{{
<strong class="mr-1" v-text="t('events.date')" />{{
event.date
}}
({{ weekday }} {{ daysUntil }})
</v-col>
<v-col v-if="event.time" cols="12" xs="12" sm="6">
<strong class="mr-1" v-text="$t('events.time')" />{{
<strong class="mr-1" v-text="t('events.time')" />{{
event.time
}}
</v-col>
@@ -38,16 +37,15 @@
xs="12"
sm="6"
>
<strong class="mr-1" v-text="$t('events.hall')" />
<strong class="mr-1" v-text="t('events.hall')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
:title="t('events.hide')"
icon='mdi-eye-off'
@click="toggleIgnore('danceHall', event.danceHall.name)"
>
mdi-eye-off
</v-icon>
/>
{{ event.danceHall.name }}
</v-col>
<v-col
@@ -55,16 +53,15 @@
xs="12"
sm="6"
>
<strong class="mr-1" v-text="$t('events.city')" />
<strong class="mr-1" v-text="t('events.city')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
:title="t('events.hide')"
icon='mdi-eye-off'
@click="toggleIgnore('city', event.danceHall.city)"
>
mdi-eye-off
</v-icon>
/>
{{ event.danceHall.city }}
</v-col>
<v-col
@@ -72,18 +69,17 @@
xs="12"
sm="6"
>
<strong class="mr-1" v-text="$t('events.municipality')" />
<strong class="mr-1" v-text="t('events.municipality')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
:title="t('events.hide')"
icon='mdi-eye-off'
@click="
toggleIgnore('municipality', event.danceHall.municipality)
"
>
mdi-eye-off
</v-icon>
/>
{{ event.danceHall.municipality }}
</v-col>
<v-col
@@ -91,16 +87,15 @@
xs="12"
sm="6"
>
<strong class="mr-1" v-text="$t('events.state')" />
<strong class="mr-1" v-text="t('events.state')" />
<v-icon
v-if="hasUser"
class="ml-1 mr-1 text-medium-emphasis"
size="small"
:title="$t('events.hide')"
:title="t('events.hide')"
icon='mdi-eye-off'
@click="toggleIgnore('state', event.danceHall.state)"
>
mdi-eye-off
</v-icon>
/>
{{ event.danceHall.state }}
</v-col>
</v-row>
@@ -112,12 +107,12 @@
<script setup lang='ts'>
import { format, formatDistanceToNow, parseISO } from 'date-fns'
import { enGB, sv } from 'date-fns/locale'
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'
import type { Event } from '~/graphql/generated/operations'
const props = defineProps({
event: {
@@ -133,7 +128,7 @@ const props = defineProps({
required: true,
},
})
const { locale: currentLocale } = useI18n()
const { t, 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 }))
+7 -12
View File
@@ -1,19 +1,13 @@
<template>
<v-row dense>
<v-col cols="12" sm="12" md="6">
<v-icon class="me-1">
mdi-home
</v-icon>
<v-icon class="me-1" icon='mdi-home' />
<span><strong>{{ distance.origin }}</strong></span>
</v-col>
<v-col cols="12" sm="12" md="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>
<v-icon class="me-1" icon='mdi-car' />
<span>{{ numericDistance }}</span>
<v-icon class="ms-2 me-1" icon='mdi-clock-outline' />
<span>{{ distance.duration }}</span>
</v-col>
</v-row>
@@ -21,6 +15,7 @@
<script setup lang='ts'>
import { computed, type PropType } from 'vue'
import type { DanceHallDistance } from '~/graphql/generated/operations'
const props = defineProps({
@@ -30,8 +25,8 @@ const props = defineProps({
},
})
const numericDistance = computed(() =>
Number(props.distance.distance / 1000).toLocaleString('sv-SE', {
`${Number(props.distance.distance / 1000).toLocaleString('sv-SE', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}))
})} km`)
</script>
+3 -1
View File
@@ -14,9 +14,11 @@
<script setup lang='ts'>
import type { PropType } from 'vue'
import EventCard from './event-card.vue'
import type { Event } from '~/graphql/generated/operations'
import EventCard from './event-card.vue'
defineProps({
hasUser: {
type: Boolean,
+17 -17
View File
@@ -3,7 +3,7 @@
<v-container :key="range" fluid grid-list-md class="app-fade-in">
<v-row v-if="!isAuthenticated" wrap>
<v-col xs="12">
<p><b v-text="$t('events.login')" /></p>
<p><b v-text="t('events.login')" /></p>
</v-col>
</v-row>
<v-row wrap>
@@ -12,34 +12,32 @@
v-model="origin"
variant="underlined"
hide-details
:label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')"
:label="t('origins.origin')"
:placeholder="t('origins.geolocation')"
>
<template #append>
<v-tooltip top>
<template #activator="{ props }">
<v-icon
icon='mdi-crosshairs-gps'
v-bind="props"
@click="fetchAddressFn()"
>
mdi-crosshairs-gps
</v-icon>
/>
</template>
<span v-text="$t('origins.fetchAddress')" />
<span v-text="t('origins.fetchAddress')" />
</v-tooltip>
</template>
<template #prepend>
<v-tooltip v-if="isAuthenticated" top>
<template #activator="{ props }">
<v-icon
icon='mdi-bookmark-plus-outline'
:disabled="!origin"
v-bind="props"
@click="saveOriginFn(origin)"
>
mdi-bookmark-plus-outline
</v-icon>
/>
</template>
<span v-text="$t('origins.save')" />
<span v-text="t('origins.save')" />
</v-tooltip>
</template>
</v-text-field>
@@ -53,7 +51,7 @@
mandatory
>
<v-btn v-for="r in ranges" :key="r.value" variant="outlined" :value="r.value">
<span v-text="$t(`events.range.${r.value}`)" />
<span v-text="t(`events.range.${r.value}`)" />
</v-btn>
</v-btn-toggle>
<v-select
@@ -73,13 +71,13 @@
v-model="state.search"
variant="underlined"
append-outer-icon="mdi-magnify"
:label="$t('events.filter')"
:placeholder="$t('events.filter')"
:label="t('events.filter')"
:placeholder="t('events.filter')"
hide-details
/>
</v-col>
<v-col cols="12" sm="4">
<v-checkbox v-model="state.includeHidden" :label="$t('events.includeHidden')" hide-details />
<v-checkbox v-model="state.includeHidden" :label="t('events.includeHidden')" hide-details />
</v-col>
</v-row>
<event-list
@@ -99,11 +97,11 @@
</template>
<script setup lang='ts'>
import { computed, ref, watch } from 'vue'
import { useAuth0 } from '@auth0/auth0-vue'
import { computed, ref, watch } from 'vue'
import { useDisplay } from 'vuetify'
import { useI18n } from '#i18n'
import EventList from './event-list.vue'
import {
type FindEventsQueryVariables,
useFetchAddressLazyQuery,
@@ -117,6 +115,8 @@ import {
} from '~/graphql/generated/operations'
import { useState } from '~/store'
import EventList from './event-list.vue'
const state = useState()
const { smAndUp } = useDisplay()
const range = computed(() => state.range)
+6 -6
View File
@@ -1,7 +1,7 @@
<template>
<v-card flat variant="outlined" rounded="xl" class="mx-3 my-3">
<v-card-title>
<span v-text="$t(title, model.length)" />
<span v-text="t(titleKey, model.length)" />
</v-card-title>
<v-list>
<v-list-item v-for="item in model" :key="item" :title="item">
@@ -9,11 +9,9 @@
<v-list-item-action @click="toggleIgnore(type, item)">
<v-tooltip top>
<template #activator="{ props }">
<v-icon v-bind="props">
mdi-delete-outline
</v-icon>
<v-icon icon='mdi-delete-outline' v-bind="props" />
</template>
<span v-text="$t('filters.remove')" />
<span v-text="t('filters.remove')" />
</v-tooltip>
</v-list-item-action>
</template>
@@ -28,7 +26,7 @@ defineProps({
type: Array,
required: true,
},
title: {
titleKey: {
type: String,
required: true,
},
@@ -41,4 +39,6 @@ defineProps({
required: true,
},
})
const { t } = useI18n()
</script>
+9 -7
View File
@@ -9,7 +9,7 @@
<v-col xs="12" sm="12" md="4" lg="4">
<list
:model="bands || []"
title="filters.band"
title-key="filters.band"
type="band"
:toggle-ignore="toggleIgnore"
/>
@@ -19,25 +19,25 @@
<v-col cols="12">
<list
:model="states || []"
title="filters.state"
title-key="filters.state"
type="state"
:toggle-ignore="toggleIgnore"
/>
<list
:model="municipalities || []"
title="filters.municipality"
title-key="filters.municipality"
type="municipality"
:toggle-ignore="toggleIgnore"
/>
<list
:model="cities || []"
title="filters.city"
title-key="filters.city"
type="city"
:toggle-ignore="toggleIgnore"
/>
<list
:model="danceHalls || []"
title="filters.hall"
title-key="filters.hall"
type="danceHall"
:toggle-ignore="toggleIgnore"
/>
@@ -61,10 +61,10 @@
</template>
<script setup lang='ts'>
import { computed, ref } from 'vue'
import { useAuth0 } from '@auth0/auth0-vue'
import { computed, ref } from 'vue'
import { useI18n } from '#i18n'
import List from './filter-list.vue'
import {
useFetchFiltersQuery,
useToggleIgnoreBandMutation,
@@ -75,6 +75,8 @@ import {
} from '~/graphql/generated/operations'
import { useState } from '~/store'
import List from './filter-list.vue'
const state = useState()
const { t } = useI18n()
state.setTitle(t('app.links.filters'))
+13 -15
View File
@@ -6,34 +6,32 @@
<v-text-field
v-model="origin"
variant="underlined"
:label="$t('origins.origin')"
:placeholder="$t('origins.geolocation')"
:label="t('origins.origin')"
:placeholder="t('origins.geolocation')"
>
<template #append>
<v-tooltip top>
<template #activator="{ props }">
<v-icon
icon='mdi-crosshairs-gps'
v-bind="props"
@click="fetchAddressFn()"
>
mdi-crosshairs-gps
</v-icon>
/>
</template>
<span v-text="$t('origins.fetchAddress')" />
<span v-text="t('origins.fetchAddress')" />
</v-tooltip>
</template>
<template #prepend>
<v-tooltip top>
<template #activator="{ props }">
<v-icon
icon='mdi-bookmark-plus-outline'
:disabled="!origin"
v-bind="props"
@click="saveOriginFn(origin)"
>
mdi-bookmark-plus-outline
</v-icon>
/>
</template>
<span v-text="$t('origins.save')" />
<span v-text="t('origins.save')" />
</v-tooltip>
</template>
</v-text-field>
@@ -44,13 +42,12 @@
<v-tooltip top>
<template #activator="{ props }">
<v-icon
icon='mdi-delete-outline'
v-bind="props"
@click="removeOriginFn(o)"
>
mdi-delete-outline
</v-icon>
/>
</template>
<span v-text="$t('origins.remove')" />
<span v-text="t('origins.remove')" />
</v-tooltip>
<span>{{ o }}</span>
</v-col>
@@ -67,8 +64,9 @@
</template>
<script setup lang='ts'>
import { computed, ref } from 'vue'
import { useAuth0 } from '@auth0/auth0-vue'
import { computed, ref } from 'vue'
import { useI18n } from '#i18n'
import {
useFetchAddressLazyQuery,