Merge branch 'feat/#22_AddLanguageSwitcher'
This commit is contained in:
@@ -231,6 +231,8 @@ legalconsenthub/
|
||||
│ │ ├── permissions.global.ts # Authorization check
|
||||
│ │ └── refreshToken.global.ts # Token refresh logic
|
||||
│ ├── pages/ # Route pages
|
||||
│ │ ├── settings.vue # User settings page (language, theme, appearance)
|
||||
│ │ ├── administration.vue # Admin template editor
|
||||
│ │ ├── application-forms/
|
||||
│ │ ├── callback.vue # OAuth callback
|
||||
│ │ ├── create.vue
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<UApp>
|
||||
<UApp :locale="locales[locale]">
|
||||
<NuxtLoadingIndicator />
|
||||
|
||||
<NuxtLayout>
|
||||
@@ -11,9 +11,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as locales from '@nuxt/ui/locale'
|
||||
|
||||
const { locale } = useI18n()
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const color = computed(() => (colorMode.value === 'dark' ? '#111827' : 'white'))
|
||||
const lang = computed(() => locales[locale.value].code)
|
||||
const dir = computed(() => locales[locale.value].dir)
|
||||
|
||||
useHead({
|
||||
meta: [
|
||||
@@ -23,7 +28,8 @@ useHead({
|
||||
],
|
||||
link: [{ rel: 'icon', href: '/favicon.ico' }],
|
||||
htmlAttrs: {
|
||||
lang: 'en'
|
||||
lang,
|
||||
dir
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<UModal :open="isOpen" title="Mitbestimmungsantrag löschen" @update:open="$emit('update:isOpen', $event)">
|
||||
<UModal :open="isOpen" :title="$t('applicationForms.deleteTitle')" @update:open="$emit('update:isOpen', $event)">
|
||||
<template #body>
|
||||
Möchten Sie wirklich den Mitbestimmungsantrag <strong>{{ applicationFormToDelete.name }}</strong> löschen?
|
||||
{{ $t('applicationForms.deleteConfirm', { name: applicationFormToDelete.name }) }}
|
||||
</template>
|
||||
<template #footer>
|
||||
<UButton label="Abbrechen" color="neutral" variant="outline" @click="$emit('update:isOpen', false)" />
|
||||
<UButton label="Löschen" color="neutral" @click="$emit('delete', applicationFormToDelete.id)" />
|
||||
<UButton :label="$t('common.cancel')" color="neutral" variant="outline" @click="$emit('update:isOpen', false)" />
|
||||
<UButton :label="$t('common.delete')" color="neutral" @click="$emit('delete', applicationFormToDelete.id)" />
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -92,18 +92,19 @@ function getResolvedComponent(formElement: FormElementDto) {
|
||||
}
|
||||
|
||||
function getDropdownItems(formElementId: string, formElementPosition: number): DropdownMenuItem[] {
|
||||
const { t: $t } = useI18n()
|
||||
const items = []
|
||||
|
||||
if (route.path !== '/create') {
|
||||
items.push({
|
||||
label: 'Comments',
|
||||
label: $t('applicationForms.formElements.comments'),
|
||||
icon: 'i-lucide-message-square-more',
|
||||
onClick: () => toggleComments(formElementId)
|
||||
})
|
||||
}
|
||||
|
||||
items.push({
|
||||
label: 'Add input field below',
|
||||
label: $t('applicationForms.formElements.addInputBelow'),
|
||||
icon: 'i-lucide-list-plus',
|
||||
onClick: () => emit('add:input-form', formElementPosition)
|
||||
})
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<div class="flex gap-2 justify-between">
|
||||
<UButton leading-icon="i-lucide-arrow-left" :disabled="!stepper?.hasPrev" @click="handleNavigate('backward')">
|
||||
Prev
|
||||
{{ $t('applicationForms.navigation.previous') }}
|
||||
</UButton>
|
||||
|
||||
<UButton
|
||||
@@ -40,15 +40,15 @@
|
||||
size="lg"
|
||||
@click="handleNavigate('forward')"
|
||||
>
|
||||
Next
|
||||
{{ $t('applicationForms.navigation.next') }}
|
||||
</UButton>
|
||||
|
||||
<div v-if="!stepper?.hasNext" class="flex flex-wrap items-center gap-1.5">
|
||||
<UButton trailing-icon="i-lucide-save" :disabled="disabled" variant="outline" size="lg" @click="emit('save')">
|
||||
Save
|
||||
{{ $t('applicationForms.navigation.save') }}
|
||||
</UButton>
|
||||
<UButton trailing-icon="i-lucide-send-horizontal" :disabled="disabled" size="lg" @click="emit('submit')">
|
||||
Submit
|
||||
{{ $t('applicationForms.navigation.submit') }}
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="relative flex items-center justify-center">
|
||||
<svg
|
||||
class="shield-ring"
|
||||
:class="ringClass"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
>
|
||||
<svg class="shield-ring" :class="ringClass" width="48" height="48" viewBox="0 0 48 48">
|
||||
<circle
|
||||
class="ring-background"
|
||||
cx="24"
|
||||
@@ -32,24 +26,13 @@
|
||||
transform="rotate(-90 24 24)"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
|
||||
<Transition name="shield-pulse" mode="out-in">
|
||||
<UIcon
|
||||
:key="status"
|
||||
:name="shieldIcon"
|
||||
class="absolute shield-icon"
|
||||
:class="iconClass"
|
||||
/>
|
||||
<UIcon :key="status" :name="shieldIcon" class="absolute shield-icon" :class="iconClass" />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<UBadge
|
||||
:label="statusLabel"
|
||||
:color="badgeColor"
|
||||
size="md"
|
||||
variant="subtle"
|
||||
class="status-badge"
|
||||
/>
|
||||
|
||||
<UBadge :label="statusLabel" :color="badgeColor" size="md" variant="subtle" class="status-badge" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -75,16 +58,18 @@ const shieldIcon = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
switch (props.status) {
|
||||
case ComplianceStatus.Critical:
|
||||
return 'Kritisch'
|
||||
return $t('compliance.critical')
|
||||
case ComplianceStatus.Warning:
|
||||
return 'Warnung'
|
||||
return $t('compliance.warning')
|
||||
case ComplianceStatus.NonCritical:
|
||||
return 'Unkritisch'
|
||||
return $t('compliance.nonCritical')
|
||||
default:
|
||||
return 'Unkritisch'
|
||||
return $t('compliance.nonCritical')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -144,15 +129,18 @@ const progressOffset = computed(() => {
|
||||
return circumference - (progressValue.value / 100) * circumference
|
||||
})
|
||||
|
||||
watch(() => props.status, () => {
|
||||
const element = document.querySelector('.shield-ring')
|
||||
if (element) {
|
||||
element.classList.add('status-change-pulse')
|
||||
setTimeout(() => {
|
||||
element.classList.remove('status-change-pulse')
|
||||
}, 600)
|
||||
watch(
|
||||
() => props.status,
|
||||
() => {
|
||||
const element = document.querySelector('.shield-ring')
|
||||
if (element) {
|
||||
element.classList.add('status-change-pulse')
|
||||
setTimeout(() => {
|
||||
element.classList.remove('status-change-pulse')
|
||||
}, 600)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -210,4 +198,3 @@ watch(() => props.status, () => {
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<USlideover v-model:open="isOpen" title="Benachrichtigungen">
|
||||
<USlideover v-model:open="isOpen" :title="$t('notifications.title')">
|
||||
<template #body>
|
||||
<div v-if="notifications.length === 0" class="text-center py-8 text-muted">
|
||||
<UIcon name="i-heroicons-bell-slash" class="h-8 w-8 mx-auto mb-2" />
|
||||
<p>Keine Benachrichtigungen</p>
|
||||
<p>{{ $t('notifications.empty') }}</p>
|
||||
</div>
|
||||
|
||||
<NuxtLink
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<template>
|
||||
<UModal :open="open" title="Version wiederherstellen" @update:open="$emit('update:open', $event)">
|
||||
<UModal :open="open" :title="$t('versions.restoreTitle')" @update:open="$emit('update:open', $event)">
|
||||
<template #body>
|
||||
<div class="space-y-2">
|
||||
<p>
|
||||
Möchten Sie Version <strong>v{{ versionNumber }}</strong> wirklich wiederherstellen?
|
||||
{{ $t('versions.restoreConfirm', { number: versionNumber }) }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-600">
|
||||
Dies erstellt eine neue Version mit dem Inhalt der ausgewählten Version. Die aktuelle Version und alle
|
||||
Änderungen bleiben in der Historie erhalten.
|
||||
{{ $t('versions.restoreDescription') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<UButton label="Abbrechen" color="neutral" variant="outline" @click="$emit('update:open', false)" />
|
||||
<UButton label="Wiederherstellen" color="primary" :loading="loading" @click="$emit('confirm')" />
|
||||
<UButton :label="$t('common.cancel')" color="neutral" variant="outline" @click="$emit('update:open', false)" />
|
||||
<UButton :label="$t('versions.restore')" color="primary" :loading="loading" @click="$emit('confirm')" />
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -23,9 +23,11 @@
|
||||
</UChatMessages>
|
||||
</template>
|
||||
<UTextarea v-model="commentTextAreaValue" class="w-full" />
|
||||
<UButton v-if="!isEditingComment" class="my-3 lg:my-4" @click="submitComment(formElementId)"> Submit </UButton>
|
||||
<UButton v-if="isEditingComment" class="my-3 lg:my-4" @click="updateEditComment"> Edit comment </UButton>
|
||||
<UButton v-if="isEditingComment" class="my-3 lg:my-4" @click="cancelEditComment"> Cancel </UButton>
|
||||
<UButton v-if="!isEditingComment" class="my-3 lg:my-4" @click="submitComment(formElementId)">
|
||||
{{ $t('comments.submit') }}
|
||||
</UButton>
|
||||
<UButton v-if="isEditingComment" class="my-3 lg:my-4" @click="updateEditComment"> {{ $t('comments.edit') }} </UButton>
|
||||
<UButton v-if="isEditingComment" class="my-3 lg:my-4" @click="cancelEditComment"> {{ $t('common.cancel') }} </UButton>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -50,11 +52,12 @@ const {
|
||||
} = commentActions
|
||||
|
||||
function createChatMessageActions(comment: CommentDto) {
|
||||
const { t: $t } = useI18n()
|
||||
const chatMessageActions = []
|
||||
|
||||
if (isCommentByUser(comment)) {
|
||||
chatMessageActions.push({
|
||||
label: 'Edit',
|
||||
label: $t('comments.editAction'),
|
||||
icon: 'i-lucide-pencil',
|
||||
onClick: () => editComment(comment)
|
||||
})
|
||||
|
||||
@@ -14,18 +14,11 @@
|
||||
variant="ghost"
|
||||
block
|
||||
:square="collapsed"
|
||||
class="data-[state=open]:bg-(--ui-bg-elevated)"
|
||||
class="data-[state=open]:bg-elevated"
|
||||
:ui="{
|
||||
trailingIcon: 'text-(--ui-text-dimmed)'
|
||||
}"
|
||||
/>
|
||||
|
||||
<template #chip-leading="{ item }">
|
||||
<span
|
||||
:style="{ '--chip': `var(--color-${(item as any).chip}-400)` }"
|
||||
class="ms-0.5 size-2 rounded-full bg-(--chip)"
|
||||
/>
|
||||
</template>
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -37,30 +30,6 @@ defineProps<{
|
||||
collapsed?: boolean
|
||||
}>()
|
||||
|
||||
const colorMode = useColorMode()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const colors = [
|
||||
'red',
|
||||
'orange',
|
||||
'amber',
|
||||
'yellow',
|
||||
'lime',
|
||||
'green',
|
||||
'emerald',
|
||||
'teal',
|
||||
'cyan',
|
||||
'sky',
|
||||
'blue',
|
||||
'indigo',
|
||||
'violet',
|
||||
'purple',
|
||||
'fuchsia',
|
||||
'pink',
|
||||
'rose'
|
||||
]
|
||||
const neutrals = ['slate', 'gray', 'zinc', 'neutral', 'stone']
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { user: keyCloakUser } = storeToRefs(userStore)
|
||||
|
||||
@@ -71,6 +40,8 @@ const user = ref({
|
||||
}
|
||||
})
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
const items = computed<DropdownMenuItem[][]>(() => [
|
||||
[
|
||||
{
|
||||
@@ -81,101 +52,19 @@ const items = computed<DropdownMenuItem[][]>(() => [
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user'
|
||||
},
|
||||
{
|
||||
label: 'Administration',
|
||||
label: $t('user.administration'),
|
||||
icon: 'i-lucide-shield',
|
||||
to: '/administration'
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
label: $t('user.settings'),
|
||||
icon: 'i-lucide-settings',
|
||||
to: '/settings'
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Theme',
|
||||
icon: 'i-lucide-palette',
|
||||
children: [
|
||||
{
|
||||
label: 'Primary',
|
||||
slot: 'chip',
|
||||
chip: appConfig.ui.colors.primary,
|
||||
content: {
|
||||
align: 'center',
|
||||
collisionPadding: 16
|
||||
},
|
||||
children: colors.map((color) => ({
|
||||
label: color,
|
||||
chip: color,
|
||||
slot: 'chip',
|
||||
checked: appConfig.ui.colors.primary === color,
|
||||
type: 'checkbox',
|
||||
onSelect: (e) => {
|
||||
e.preventDefault()
|
||||
appConfig.ui.colors.primary = color
|
||||
}
|
||||
}))
|
||||
},
|
||||
{
|
||||
label: 'Neutral',
|
||||
slot: 'chip',
|
||||
chip: appConfig.ui.colors.neutral,
|
||||
content: {
|
||||
align: 'end',
|
||||
collisionPadding: 16
|
||||
},
|
||||
children: neutrals.map((color) => ({
|
||||
label: color,
|
||||
chip: color,
|
||||
slot: 'chip',
|
||||
type: 'checkbox',
|
||||
checked: appConfig.ui.colors.neutral === color,
|
||||
onSelect: (e) => {
|
||||
e.preventDefault()
|
||||
appConfig.ui.colors.neutral = color
|
||||
}
|
||||
}))
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Appearance',
|
||||
icon: 'i-lucide-sun-moon',
|
||||
children: [
|
||||
{
|
||||
label: 'Light',
|
||||
icon: 'i-lucide-sun',
|
||||
type: 'checkbox',
|
||||
checked: colorMode.value === 'light',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
colorMode.preference = 'light'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Dark',
|
||||
icon: 'i-lucide-moon',
|
||||
type: 'checkbox',
|
||||
checked: colorMode.value === 'dark',
|
||||
onUpdateChecked(checked: boolean) {
|
||||
if (checked) {
|
||||
colorMode.preference = 'dark'
|
||||
}
|
||||
},
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Log out',
|
||||
label: $t('user.logout'),
|
||||
icon: 'i-lucide-log-out',
|
||||
async onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UModal :open="open" title="Vergleich mit Version" @update:open="$emit('update:open', $event)">
|
||||
<UModal :open="open" :title="$t('versions.compare')" @update:open="$emit('update:open', $event)">
|
||||
<template #header>
|
||||
<h3 class="text-lg font-semibold">Vergleich: Aktuelles Formular mit Version v{{ versionNumber }}</h3>
|
||||
<h3 class="text-lg font-semibold">{{ $t('versions.comparisonTitle', { number: versionNumber }) }}</h3>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
@@ -9,13 +9,13 @@
|
||||
<UIcon name="i-lucide-loader-circle" class="animate-spin h-8 w-8" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="text-red-500">Fehler beim Laden der Version: {{ error }}</div>
|
||||
<div v-else-if="error" class="text-red-500">{{ $t('versions.comparisonError') }}: {{ error }}</div>
|
||||
|
||||
<div v-else-if="diff && hasChanges" class="space-y-6">
|
||||
<div v-if="diff.elementsAdded.length > 0" class="space-y-2">
|
||||
<h4 class="font-semibold text-success flex items-center gap-2">
|
||||
<UIcon name="i-lucide-plus-circle" />
|
||||
Hinzugefügte Elemente ({{ diff.elementsAdded.length }})
|
||||
{{ $t('versions.elementsAdded', { count: diff.elementsAdded.length }) }}
|
||||
</h4>
|
||||
<UCard v-for="(element, index) in diff.elementsAdded" :key="`added-${index}`" variant="subtle">
|
||||
<div class="flex items-start gap-2">
|
||||
@@ -31,14 +31,17 @@
|
||||
<div v-if="diff.elementsRemoved.length > 0" class="space-y-2">
|
||||
<h4 class="font-semibold text-error flex items-center gap-2">
|
||||
<UIcon name="i-lucide-minus-circle" />
|
||||
Entfernte Elemente ({{ diff.elementsRemoved.length }})
|
||||
{{ $t('versions.elementsRemoved', { count: diff.elementsRemoved.length }) }}
|
||||
</h4>
|
||||
<UCard v-for="(element, index) in diff.elementsRemoved" :key="`removed-${index}`" variant="subtle">
|
||||
<div class="flex items-start gap-2">
|
||||
<UBadge color="error" variant="subtle">-</UBadge>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">{{ element.title || 'Ohne Titel' }}</div>
|
||||
<div class="text-sm text-gray-500">Typ: {{ element.type }} · Sektion: {{ element.sectionTitle }}</div>
|
||||
<div class="font-medium">{{ element.title || $t('versions.elementWithoutTitle') }}</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ $t('common.type') }}: {{ element.type }} · {{ $t('versions.elementIn') }}
|
||||
{{ element.sectionTitle }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
@@ -47,14 +50,14 @@
|
||||
<div v-if="diff.elementsModified.length > 0" class="space-y-2">
|
||||
<h4 class="font-semibold text-warning flex items-center gap-2">
|
||||
<UIcon name="i-lucide-pencil" />
|
||||
Geänderte Elemente ({{ diff.elementsModified.length }})
|
||||
{{ $t('versions.elementsModified', { count: diff.elementsModified.length }) }}
|
||||
</h4>
|
||||
<UCard v-for="(element, index) in diff.elementsModified" :key="`modified-${index}`" variant="subtle">
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-start gap-2">
|
||||
<UBadge color="warning" variant="subtle">~</UBadge>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">Element in {{ element.sectionTitle }}</div>
|
||||
<div class="font-medium">{{ $t('versions.elementIn') }} {{ element.sectionTitle }}</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
@@ -66,7 +69,7 @@
|
||||
>
|
||||
<div v-if="element.optionsAdded.length > 0">
|
||||
<div class="text-sm font-medium text-success">
|
||||
Optionen hinzugefügt ({{ element.optionsAdded.length }}):
|
||||
{{ $t('versions.optionsAdded', { count: element.optionsAdded.length }) }}:
|
||||
</div>
|
||||
<div
|
||||
v-for="(option, optIdx) in element.optionsAdded"
|
||||
@@ -80,7 +83,7 @@
|
||||
|
||||
<div v-if="element.optionsRemoved.length > 0">
|
||||
<div class="text-sm font-medium text-error">
|
||||
Optionen entfernt ({{ element.optionsRemoved.length }}):
|
||||
{{ $t('versions.optionsRemoved', { count: element.optionsRemoved.length }) }}:
|
||||
</div>
|
||||
<div
|
||||
v-for="(option, optIdx) in element.optionsRemoved"
|
||||
@@ -94,7 +97,7 @@
|
||||
|
||||
<div v-if="element.optionsModified.length > 0">
|
||||
<div class="text-sm font-medium text-warning">
|
||||
Optionen geändert ({{ element.optionsModified.length }}):
|
||||
{{ $t('versions.optionsModified', { count: element.optionsModified.length }) }}:
|
||||
</div>
|
||||
<div
|
||||
v-for="(option, optIdx) in element.optionsModified"
|
||||
@@ -117,12 +120,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-8 text-gray-500">Keine Unterschiede gefunden</div>
|
||||
<div v-else class="text-center py-8 text-gray-500">{{ $t('versions.noChanges') }}</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<UButton label="Schließen" color="neutral" variant="outline" @click="$emit('update:open', false)" />
|
||||
<UButton :label="$t('common.close')" color="neutral" variant="outline" @click="$emit('update:open', false)" />
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
@@ -145,6 +148,7 @@ defineEmits<{
|
||||
}>()
|
||||
|
||||
const { getVersion } = useApplicationFormVersion()
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
@@ -176,7 +180,7 @@ async function loadVersionAndCompare() {
|
||||
versionData.value = await getVersion(props.applicationFormId, props.versionNumber)
|
||||
diff.value = compareApplicationForms(props.currentForm, versionData.value.snapshot)
|
||||
} catch (e: unknown) {
|
||||
error.value = e instanceof Error ? e.message : 'Unbekannter Fehler'
|
||||
error.value = e instanceof Error ? e.message : $t('versions.unknownError')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<UIcon name="i-lucide-loader-circle" class="animate-spin h-8 w-8" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="text-red-500">Fehler beim Laden der Versionen: {{ error }}</div>
|
||||
<div v-else-if="error" class="text-red-500">{{ $t('versions.loadError') }}: {{ error }}</div>
|
||||
|
||||
<div v-else-if="versions.length === 0" class="text-center py-8 text-gray-500">Keine Versionen verfügbar</div>
|
||||
<div v-else-if="versions.length === 0" class="text-center py-8 text-gray-500">{{ $t('versions.empty') }}</div>
|
||||
|
||||
<div v-else class="space-y-3">
|
||||
<UCard v-for="version in versions" :key="version.id" class="hover:shadow-md transition-shadow">
|
||||
@@ -29,7 +29,7 @@
|
||||
size="sm"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
label="Vergleichen"
|
||||
:label="$t('versions.compare')"
|
||||
@click="openComparisonModal(version)"
|
||||
/>
|
||||
<UButton
|
||||
@@ -37,7 +37,7 @@
|
||||
size="sm"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
label="Wiederherstellen"
|
||||
:label="$t('versions.restore')"
|
||||
@click="openRestoreModal(version)"
|
||||
/>
|
||||
</div>
|
||||
@@ -76,6 +76,7 @@ const emit = defineEmits<{
|
||||
|
||||
const { getVersions, restoreVersion } = useApplicationFormVersion()
|
||||
const toast = useToast()
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
const versions = ref<ApplicationFormVersionListItemDto[]>([])
|
||||
const loading = ref(false)
|
||||
@@ -91,10 +92,10 @@ async function loadVersions() {
|
||||
try {
|
||||
versions.value = await getVersions(props.applicationFormId)
|
||||
} catch (e: unknown) {
|
||||
error.value = e instanceof Error ? e.message : 'Unbekannter Fehler'
|
||||
error.value = e instanceof Error ? e.message : $t('versions.unknownError')
|
||||
toast.add({
|
||||
title: 'Fehler',
|
||||
description: 'Versionen konnten nicht geladen werden',
|
||||
title: $t('common.error'),
|
||||
description: $t('versions.loadErrorDescription'),
|
||||
color: 'error'
|
||||
})
|
||||
} finally {
|
||||
@@ -119,8 +120,8 @@ async function handleRestore() {
|
||||
try {
|
||||
await restoreVersion(props.applicationFormId, selectedVersion.value.versionNumber)
|
||||
toast.add({
|
||||
title: 'Erfolg',
|
||||
description: `Version v${selectedVersion.value.versionNumber} wurde wiederhergestellt`,
|
||||
title: $t('common.success'),
|
||||
description: $t('versions.restored', { number: selectedVersion.value.versionNumber }),
|
||||
color: 'success'
|
||||
})
|
||||
isRestoreModalOpen.value = false
|
||||
@@ -128,8 +129,8 @@ async function handleRestore() {
|
||||
emit('restored')
|
||||
} catch {
|
||||
toast.add({
|
||||
title: 'Fehler',
|
||||
description: 'Version konnte nicht wiederhergestellt werden',
|
||||
title: $t('common.error'),
|
||||
description: $t('versions.restoreError'),
|
||||
color: 'error'
|
||||
})
|
||||
} finally {
|
||||
@@ -154,18 +155,20 @@ function getStatusColor(status: ApplicationFormStatus) {
|
||||
}
|
||||
}
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
function getStatusLabel(status: ApplicationFormStatus): string {
|
||||
switch (status) {
|
||||
case 'DRAFT':
|
||||
return 'Entwurf'
|
||||
return $t('applicationForms.status.draft')
|
||||
case 'SUBMITTED':
|
||||
return 'Eingereicht'
|
||||
return $t('applicationForms.status.submitted')
|
||||
case 'APPROVED':
|
||||
return 'Genehmigt'
|
||||
return $t('applicationForms.status.approved')
|
||||
case 'REJECTED':
|
||||
return 'Abgelehnt'
|
||||
return $t('applicationForms.status.rejected')
|
||||
case 'SIGNED':
|
||||
return 'Signiert'
|
||||
return $t('applicationForms.status.signed')
|
||||
default:
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<USelect v-model="modelValue" placeholder="Select status" :items="items" />
|
||||
<USelect v-model="modelValue" :placeholder="$t('applicationForms.formElements.selectPlaceholder')" :items="items" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<UFormField label="Titel">
|
||||
<UFormField :label="$t('applicationForms.formElements.title')">
|
||||
<UInput v-model="title" class="w-full" :disabled="props.disabled" />
|
||||
</UFormField>
|
||||
<UFormField label="Text">
|
||||
<UFormField :label="$t('applicationForms.formElements.text')">
|
||||
<UTextarea v-model="body" class="w-full" autoresize :disabled="props.disabled" />
|
||||
</UFormField>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<template>
|
||||
<div>Element unimplemented:</div>
|
||||
<div>{{ $t('applicationForms.formElements.unimplemented') }}</div>
|
||||
</template>
|
||||
|
||||
@@ -25,7 +25,12 @@ export function useFormElementManagement() {
|
||||
|
||||
if (applicationFormId) {
|
||||
try {
|
||||
return await applicationForm.addFormElementToSubSection(applicationFormId, subsectionId, inputFormElement, position + 1)
|
||||
return await applicationForm.addFormElementToSubSection(
|
||||
applicationFormId,
|
||||
subsectionId,
|
||||
inputFormElement,
|
||||
position + 1
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Failed to add form element:', error)
|
||||
throw error
|
||||
|
||||
@@ -149,4 +149,3 @@ export const usePermissions = () => {
|
||||
canDeleteComment
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@ defineProps<{
|
||||
error: NuxtError
|
||||
}>()
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
useSeoMeta({
|
||||
title: 'Page not found',
|
||||
description: 'We are sorry but this page could not be found.'
|
||||
title: $t('error.pageNotFound'),
|
||||
description: $t('error.pageNotFoundDescription')
|
||||
})
|
||||
|
||||
useHead({
|
||||
|
||||
@@ -36,10 +36,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
const links = [
|
||||
[
|
||||
{
|
||||
label: 'General',
|
||||
label: $t('common.general'),
|
||||
icon: 'i-lucide-user',
|
||||
to: '/settings',
|
||||
exact: true
|
||||
@@ -47,7 +49,7 @@ const links = [
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'General2',
|
||||
label: $t('common.general'),
|
||||
icon: 'i-lucide-user',
|
||||
to: '/settings',
|
||||
exact: true
|
||||
|
||||
@@ -5,4 +5,3 @@ export default defineNuxtRouteMiddleware((to) => {
|
||||
return navigateTo('/', { replace: true })
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UDashboardPanel id="administration">
|
||||
<template #header>
|
||||
<UDashboardNavbar title="Administration - JSON Template Editor" :ui="{ right: 'gap-3' }">
|
||||
<UDashboardNavbar :title="$t('templates.editorTitle')" :ui="{ right: 'gap-3' }">
|
||||
<template #leading>
|
||||
<UDashboardSidebarCollapse />
|
||||
</template>
|
||||
@@ -10,21 +10,21 @@
|
||||
<UButton
|
||||
v-if="hasUnsavedChanges"
|
||||
icon="i-lucide-rotate-ccw"
|
||||
label="Zurücksetzen"
|
||||
:label="$t('templates.reset')"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
@click="resetEditor"
|
||||
/>
|
||||
<UButton
|
||||
icon="i-lucide-file-plus"
|
||||
label="Neue Vorlage"
|
||||
:label="$t('templates.newTemplate')"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
@click="createNewTemplate"
|
||||
/>
|
||||
<UButton
|
||||
icon="i-lucide-save"
|
||||
label="Speichern"
|
||||
:label="$t('common.save')"
|
||||
:disabled="!hasUnsavedChanges || !isValidJson"
|
||||
:loading="isSaving"
|
||||
@click="saveTemplate"
|
||||
@@ -40,17 +40,17 @@
|
||||
<div>
|
||||
<h3 class="font-semibold text-lg">{{ currentTemplate.name }}</h3>
|
||||
<p class="text-sm text-muted mt-1">
|
||||
Zuletzt bearbeitet am {{ formatDate(new Date(currentTemplate.modifiedAt)) }}
|
||||
{{ $t('templates.lastModified') }} {{ formatDate(new Date(currentTemplate.modifiedAt)) }}
|
||||
</p>
|
||||
</div>
|
||||
<UBadge v-if="hasUnsavedChanges" label="Ungespeicherte Änderungen" color="warning" variant="subtle" />
|
||||
<UBadge v-if="hasUnsavedChanges" :label="$t('templates.unsavedChanges')" color="warning" variant="subtle" />
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard v-else class="mb-4">
|
||||
<div class="text-center py-4">
|
||||
<UIcon name="i-lucide-info" class="size-8 text-muted mx-auto mb-2" />
|
||||
<p class="text-muted">Keine Vorlage gefunden. Erstellen Sie eine neue Vorlage.</p>
|
||||
<p class="text-muted">{{ $t('templates.noTemplateFound') }}</p>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
@@ -70,8 +70,8 @@
|
||||
v-if="!isValidJson"
|
||||
color="error"
|
||||
icon="i-lucide-alert-circle"
|
||||
title="Ungültiges JSON"
|
||||
description="Das JSON-Format ist ungültig. Bitte korrigieren Sie die Syntax."
|
||||
:title="$t('templates.invalidJson')"
|
||||
:description="$t('templates.invalidJsonDescription')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -92,6 +92,7 @@ const colorMode = useColorMode()
|
||||
const { hasRole } = usePermissions()
|
||||
const { getAllApplicationFormTemplates, updateApplicationFormTemplate, createApplicationFormTemplate } =
|
||||
await useApplicationFormTemplate()
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
if (!hasRole('CHIEF_EXECUTIVE_OFFICER') && !hasRole('IT_DEPARTMENT')) {
|
||||
await navigateTo('/')
|
||||
@@ -194,8 +195,8 @@ function createNewTemplate() {
|
||||
async function saveTemplate() {
|
||||
if (!isValidJson.value) {
|
||||
toast.add({
|
||||
title: 'Fehler',
|
||||
description: 'Das JSON-Format ist ungültig.',
|
||||
title: $t('common.error'),
|
||||
description: $t('templates.invalidJsonDescription'),
|
||||
color: 'error'
|
||||
})
|
||||
return
|
||||
@@ -210,15 +211,15 @@ async function saveTemplate() {
|
||||
if (currentTemplate.value?.id) {
|
||||
currentTemplate.value = await updateApplicationFormTemplate(currentTemplate.value.id, dataWithDates)
|
||||
toast.add({
|
||||
title: 'Erfolg',
|
||||
description: 'Vorlage erfolgreich aktualisiert.',
|
||||
title: $t('common.success'),
|
||||
description: $t('templates.updated'),
|
||||
color: 'success'
|
||||
})
|
||||
} else {
|
||||
currentTemplate.value = await createApplicationFormTemplate(dataWithDates)
|
||||
toast.add({
|
||||
title: 'Erfolg',
|
||||
description: 'Vorlage erfolgreich erstellt.',
|
||||
title: $t('common.success'),
|
||||
description: $t('templates.created'),
|
||||
color: 'success'
|
||||
})
|
||||
}
|
||||
@@ -230,8 +231,8 @@ async function saveTemplate() {
|
||||
await refreshNuxtData('applicationFormTemplates')
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
title: 'Fehler',
|
||||
description: 'Fehler beim Speichern der Vorlage.',
|
||||
title: $t('common.error'),
|
||||
description: $t('templates.saveError'),
|
||||
color: 'error'
|
||||
})
|
||||
console.error('Error saving template:', error)
|
||||
@@ -242,7 +243,7 @@ async function saveTemplate() {
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
if (hasUnsavedChanges.value) {
|
||||
const answer = window.confirm('Sie haben ungespeicherte Änderungen. Möchten Sie die Seite wirklich verlassen?')
|
||||
const answer = window.confirm($t('templates.unsavedWarning'))
|
||||
if (answer) {
|
||||
next()
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UDashboardPanel id="home">
|
||||
<template #header>
|
||||
<UDashboardNavbar title="Home" :ui="{ right: 'gap-3' }">
|
||||
<UDashboardNavbar :title="$t('common.home')" :ui="{ right: 'gap-3' }">
|
||||
<template #leading>
|
||||
<UDashboardSidebarCollapse />
|
||||
</template>
|
||||
@@ -9,7 +9,7 @@
|
||||
<template #right>
|
||||
<UButton
|
||||
icon="i-lucide-circle-plus"
|
||||
label="Neuer Mitbestimmungsantrag"
|
||||
:label="$t('applicationForms.createNew')"
|
||||
to="/create"
|
||||
:disabled="!canWriteApplicationForms"
|
||||
size="xl"
|
||||
@@ -59,6 +59,7 @@ import { useUserStore } from '~~/stores/useUserStore'
|
||||
|
||||
const route = useRoute()
|
||||
const toast = useToast()
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
definePageMeta({
|
||||
// Prevent whole page from re-rendering when navigating between sections to keep state
|
||||
@@ -113,7 +114,7 @@ async function onSave() {
|
||||
const updated = await updateForm(applicationForm.value.id, applicationForm.value)
|
||||
if (updated) {
|
||||
updateApplicationForm(updated)
|
||||
toast.add({ title: 'Success', description: 'Application form saved', color: 'success' })
|
||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +123,7 @@ async function onSubmit() {
|
||||
if (applicationForm.value) {
|
||||
await submitApplicationForm(applicationForm.value.id)
|
||||
await navigateTo('/')
|
||||
toast.add({ title: 'Success', description: 'Application form submitted', color: 'success' })
|
||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.submitted'), color: 'success' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<UDashboardPanel id="versions">
|
||||
<template #header>
|
||||
<UDashboardNavbar :title="`Versionen: ${applicationForm?.name}`">
|
||||
<UDashboardNavbar :title="$t('versions.pageTitle', { name: applicationForm?.name })">
|
||||
<template #leading>
|
||||
<UDashboardSidebarCollapse />
|
||||
</template>
|
||||
@@ -15,7 +15,12 @@
|
||||
|
||||
<template #body>
|
||||
<div class="p-6">
|
||||
<VersionHistory v-if="applicationForm" :application-form-id="applicationForm.id" :current-form="applicationForm" @restored="handleRestored" />
|
||||
<VersionHistory
|
||||
v-if="applicationForm"
|
||||
:application-form-id="applicationForm.id"
|
||||
:current-form="applicationForm"
|
||||
@restored="handleRestored"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UDashboardPanel>
|
||||
@@ -25,6 +30,7 @@
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
definePageMeta({
|
||||
key: (route) => `${route.params.id}-versions`
|
||||
@@ -36,8 +42,8 @@ const { applicationForm, navigationLinks: links, refresh } = await useApplicatio
|
||||
async function handleRestored() {
|
||||
await refresh()
|
||||
toast.add({
|
||||
title: 'Version wiederhergestellt',
|
||||
description: 'Das Formular wurde auf die ausgewählte Version zurückgesetzt.',
|
||||
title: $t('versions.restored'),
|
||||
description: $t('versions.restoredDescription'),
|
||||
color: 'success'
|
||||
})
|
||||
router.push(`/application-forms/${applicationForm.value.id}/0`)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<UDashboardPanel id="home">
|
||||
<template #header>
|
||||
<UDashboardNavbar title="Home" :ui="{ right: 'gap-3' }">
|
||||
<UDashboardNavbar :title="$t('common.home')" :ui="{ right: 'gap-3' }">
|
||||
<template #leading>
|
||||
<UDashboardSidebarCollapse />
|
||||
</template>
|
||||
@@ -18,9 +18,9 @@
|
||||
<div class="flex flex-col gap-4 sm:gap-6 lg:gap-12 w-full lg:max-w-4xl mx-auto">
|
||||
<div v-if="!canWriteApplicationForms" class="text-center py-12">
|
||||
<UIcon name="i-lucide-shield-x" class="w-16 h-16 mx-auto text-red-400 mb-4" />
|
||||
<h2 class="text-2xl font-semibold text-gray-700 mb-2">Keine Berechtigung</h2>
|
||||
<p class="text-gray-500 mb-4">Sie haben keine Berechtigung zum Erstellen von Anträgen.</p>
|
||||
<UButton to="/" class="mt-4"> Zurück zur Übersicht </UButton>
|
||||
<h2 class="text-2xl font-semibold text-gray-700 mb-2">{{ $t('applicationForms.noPermission') }}</h2>
|
||||
<p class="text-gray-500 mb-4">{{ $t('applicationForms.noPermissionDescription') }}</p>
|
||||
<UButton to="/" class="mt-4"> {{ $t('applicationForms.backToOverview') }} </UButton>
|
||||
</div>
|
||||
<div v-else-if="applicationFormTemplate">
|
||||
<FormStepperWithNavigation
|
||||
@@ -29,7 +29,7 @@
|
||||
@submit="onSubmit"
|
||||
@add-input-form="handleAddInputForm"
|
||||
>
|
||||
<UFormField label="Name" class="mb-4">
|
||||
<UFormField :label="$t('common.name')" class="mb-4">
|
||||
<UInput v-model="applicationFormTemplate.name" class="w-full" />
|
||||
</UFormField>
|
||||
</FormStepperWithNavigation>
|
||||
@@ -57,6 +57,7 @@ const { canWriteApplicationForms } = usePermissions()
|
||||
const userStore = useUserStore()
|
||||
const { selectedOrganization } = storeToRefs(userStore)
|
||||
const toast = useToast()
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
const { data, error } = await useAsyncData<PagedApplicationFormDto>(
|
||||
'create-application-form',
|
||||
@@ -98,7 +99,7 @@ watch(
|
||||
async function onSave() {
|
||||
const applicationForm = await prepareAndCreateApplicationForm()
|
||||
if (applicationForm) {
|
||||
toast.add({ title: 'Success', description: 'Application form saved', color: 'success' })
|
||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ async function onSubmit() {
|
||||
if (applicationForm) {
|
||||
await submitApplicationForm(applicationForm.id)
|
||||
await navigateTo('/')
|
||||
toast.add({ title: 'Success', description: 'Application form submitted', color: 'success' })
|
||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.submitted'), color: 'success' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<UDashboardPanel id="home">
|
||||
<template #header>
|
||||
<UDashboardNavbar title="Home" :ui="{ right: 'gap-3' }">
|
||||
<UDashboardNavbar :title="$t('common.home')" :ui="{ right: 'gap-3' }">
|
||||
<template #leading>
|
||||
<UDashboardSidebarCollapse />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
Aktuelle Organisation
|
||||
{{ $t('organization.current') }}
|
||||
<USelect
|
||||
v-model="selectedOrganizationId"
|
||||
:items="organizations"
|
||||
@@ -20,7 +20,7 @@
|
||||
class="w-48"
|
||||
/>
|
||||
|
||||
<UTooltip text="Notifications" :shortcuts="['N']">
|
||||
<UTooltip :text="$t('notifications.tooltip')" :shortcuts="['N']">
|
||||
<UButton color="neutral" variant="ghost" square @click="isNotificationsSlideoverOpen = true">
|
||||
<UChip :show="unreadCount > 0" color="error" inset>
|
||||
<UIcon name="i-lucide-bell" class="size-5 shrink-0" />
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<UButton
|
||||
icon="i-lucide-circle-plus"
|
||||
label="Neuer Mitbestimmungsantrag"
|
||||
:label="$t('applicationForms.createNew')"
|
||||
to="/create"
|
||||
:disabled="!canWriteApplicationForms"
|
||||
size="xl"
|
||||
@@ -86,18 +86,18 @@
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<UIcon name="i-lucide-pencil" class="size-4 text-muted shrink-0" />
|
||||
<span class="text-muted">
|
||||
Zuletzt bearbeitet von
|
||||
{{ $t('applicationForms.lastEditedBy') }}
|
||||
<span class="font-medium text-highlighted">{{ applicationFormElem.lastModifiedBy.name }}</span>
|
||||
am {{ formatDate(applicationFormElem.modifiedAt) }}
|
||||
{{ $t('common.on') }} {{ formatDate(applicationFormElem.modifiedAt) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<UIcon name="i-lucide-user-plus" class="size-4 text-muted shrink-0" />
|
||||
<span class="text-muted">
|
||||
Erstellt von
|
||||
{{ $t('applicationForms.createdBy') }}
|
||||
<span class="font-medium text-highlighted">{{ applicationFormElem.createdBy.name }}</span>
|
||||
am {{ formatDate(applicationFormElem.createdAt) }}
|
||||
{{ $t('common.on') }} {{ formatDate(applicationFormElem.createdAt) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,6 +122,7 @@ const { getAllApplicationForms, deleteApplicationFormById } = useApplicationForm
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
const { organizations, selectedOrganization } = storeToRefs(userStore)
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
// Inject notification state from layout
|
||||
const { isNotificationsSlideoverOpen, unreadCount } = inject('notificationState', {
|
||||
@@ -178,7 +179,7 @@ const applicationForms = computed({
|
||||
function getLinksForApplicationForm(applicationForm: ApplicationFormDto) {
|
||||
return [
|
||||
{
|
||||
label: 'Löschen',
|
||||
label: $t('common.delete'),
|
||||
icon: 'i-lucide-trash',
|
||||
to: `?delete&id=${applicationForm.id}`,
|
||||
disabled: !canWriteApplicationForms.value
|
||||
|
||||
@@ -1,40 +1,37 @@
|
||||
<template>
|
||||
<UCard variant="subtle">
|
||||
<template #header>
|
||||
<div class="text-center">
|
||||
<UIcon name="i-lucide-lock" class="mx-auto h-16 w-16 text-primary-500 mb-6" />
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2">
|
||||
Welcome
|
||||
</h1>
|
||||
<p class="text-gray-600">
|
||||
You will be redirected to Keycloak to authenticate
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UCard variant="subtle">
|
||||
<template #header>
|
||||
<div class="text-center">
|
||||
<UButton
|
||||
color="primary"
|
||||
size="xl"
|
||||
icon="i-lucide-log-in"
|
||||
@click="handleSignIn"
|
||||
>
|
||||
Sign in with Keycloak
|
||||
</UButton>
|
||||
<UIcon name="i-lucide-lock" class="mx-auto h-16 w-16 text-primary-500 mb-6" />
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2">
|
||||
{{ $t('auth.welcome') }}
|
||||
</h1>
|
||||
<p class="text-gray-600">
|
||||
{{ $t('auth.redirectMessage') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="text-center text-xs text-gray-500">
|
||||
By signing in, you agree to our terms of service
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
<div class="text-center">
|
||||
<UButton color="primary" size="xl" icon="i-lucide-log-in" @click="handleSignIn">
|
||||
{{ $t('auth.signIn') }}
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="text-center text-xs text-gray-500">
|
||||
{{ $t('auth.termsAgreement') }}
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ auth: false, layout: 'auth' })
|
||||
|
||||
useSeoMeta({ title: 'Login' })
|
||||
const { t: $t } = useI18n()
|
||||
|
||||
useSeoMeta({ title: $t('auth.login') })
|
||||
|
||||
function handleSignIn() {
|
||||
navigateTo('/auth/keycloak', { external: true })
|
||||
|
||||
135
legalconsenthub/app/pages/settings.vue
Normal file
135
legalconsenthub/app/pages/settings.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<UDashboardPanel id="settings">
|
||||
<template #header>
|
||||
<UDashboardNavbar :title="$t('settings.title')">
|
||||
<template #leading>
|
||||
<UDashboardSidebarCollapse />
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<div class="flex flex-col gap-6 w-full lg:max-w-4xl mx-auto p-6">
|
||||
<!-- Language Section -->
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-highlighted">{{ $t('settings.language.title') }}</h3>
|
||||
<p class="text-sm text-muted mt-1">{{ $t('settings.language.description') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<ULocaleSelect
|
||||
:model-value="locale"
|
||||
:locales="[de, en]"
|
||||
class="w-full max-w-xs"
|
||||
size="md"
|
||||
@update:model-value="handleLocaleChange"
|
||||
/>
|
||||
</UCard>
|
||||
|
||||
<!-- Appearance Section -->
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-highlighted">{{ $t('settings.appearance.title') }}</h3>
|
||||
<p class="text-sm text-muted mt-1">{{ $t('settings.appearance.description') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<URadioGroup v-model="selectedColorMode" :items="colorModeOptions" class="gap-4" />
|
||||
</UCard>
|
||||
|
||||
<!-- Theme Colors Section -->
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-highlighted">{{ $t('settings.theme.title') }}</h3>
|
||||
<p class="text-sm text-muted mt-1">{{ $t('settings.theme.description') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Primary Color -->
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-highlighted mb-3">{{ $t('settings.theme.primary') }}</h4>
|
||||
<div class="grid grid-cols-10 gap-2">
|
||||
<button
|
||||
v-for="color in colors"
|
||||
:key="color"
|
||||
type="button"
|
||||
:class="[
|
||||
'w-10 h-10 rounded-md transition-all',
|
||||
appConfig.ui.colors.primary === color
|
||||
? 'ring-2 ring-offset-2 ring-offset-background'
|
||||
: 'hover:scale-110'
|
||||
]"
|
||||
:style="{
|
||||
backgroundColor: `var(--color-${color}-500)`,
|
||||
'--tw-ring-color': `var(--color-${color}-500)`
|
||||
}"
|
||||
@click="appConfig.ui.colors.primary = color"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
</UDashboardPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { de, en } from '@nuxt/ui/locale'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'default'
|
||||
})
|
||||
|
||||
const { t: $t, locale, setLocale } = useI18n()
|
||||
const colorMode = useColorMode()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const colors = [
|
||||
'red',
|
||||
'orange',
|
||||
'amber',
|
||||
'yellow',
|
||||
'lime',
|
||||
'green',
|
||||
'emerald',
|
||||
'teal',
|
||||
'cyan',
|
||||
'sky',
|
||||
'blue',
|
||||
'indigo',
|
||||
'violet',
|
||||
'purple',
|
||||
'fuchsia',
|
||||
'pink'
|
||||
]
|
||||
|
||||
function handleLocaleChange(newLocale: string | undefined) {
|
||||
if (newLocale) {
|
||||
setLocale(newLocale as 'de' | 'en')
|
||||
}
|
||||
}
|
||||
|
||||
const colorModeOptions = computed(() => [
|
||||
{
|
||||
value: 'light',
|
||||
label: $t('settings.appearance.light'),
|
||||
icon: 'i-lucide-sun'
|
||||
},
|
||||
{
|
||||
value: 'dark',
|
||||
label: $t('settings.appearance.dark'),
|
||||
icon: 'i-lucide-moon'
|
||||
}
|
||||
])
|
||||
|
||||
const selectedColorMode = computed({
|
||||
get: () => colorMode.value,
|
||||
set: (value) => {
|
||||
colorMode.preference = value as 'light' | 'dark'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -74,7 +74,7 @@ function flattenFormElements(form: ApplicationFormDto): Array<{ element: FormEle
|
||||
for (const section of form.formElementSections) {
|
||||
for (const subsection of section.formElementSubSections) {
|
||||
for (const element of subsection.formElements) {
|
||||
elements.push({ element, sectionTitle: section.title })
|
||||
elements.push({ element, sectionTitle: section.title })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ function flattenSnapshotElements(
|
||||
for (const section of snapshot.sections) {
|
||||
for (const subsection of section.subsections) {
|
||||
for (const element of subsection.elements) {
|
||||
elements.push({ element, sectionTitle: section.title })
|
||||
elements.push({ element, sectionTitle: section.title })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ function compareElements(
|
||||
position: number
|
||||
): ElementModification | null {
|
||||
const optionsDiff = compareOptions(current.options, version.options)
|
||||
|
||||
|
||||
if (optionsDiff.added.length > 0 || optionsDiff.removed.length > 0 || optionsDiff.modified.length > 0) {
|
||||
return {
|
||||
sectionTitle,
|
||||
|
||||
@@ -1,10 +1,160 @@
|
||||
{
|
||||
"roles": {
|
||||
"admin": "Administrator",
|
||||
"employee": "Arbeitnehmer",
|
||||
"employer": "Arbeitgeber",
|
||||
"worksCouncilMember": "Betriebsratsmitglied",
|
||||
"worksCouncilChair": "Betriebsratsvorsitzender"
|
||||
"applicationForms": {
|
||||
"title": "Mitbestimmungsanträge",
|
||||
"createNew": "Neuer Mitbestimmungsantrag",
|
||||
"noFormsAvailable": "Keine Anträge vorhanden",
|
||||
"noPermission": "Keine Berechtigung",
|
||||
"noPermissionDescription": "Sie haben keine Berechtigung zum Erstellen von Anträgen.",
|
||||
"backToOverview": "Zurück zur Übersicht",
|
||||
"deleteConfirm": "Möchten Sie wirklich den Mitbestimmungsantrag \"{name}\" löschen?",
|
||||
"deleteTitle": "Mitbestimmungsantrag löschen",
|
||||
"lastEditedBy": "Zuletzt bearbeitet von",
|
||||
"createdBy": "Erstellt von",
|
||||
"saved": "Antrag erfolgreich gespeichert",
|
||||
"submitted": "Antrag erfolgreich eingereicht",
|
||||
"deleted": "Antrag erfolgreich gelöscht",
|
||||
"formElements": {
|
||||
"comments": "Kommentare",
|
||||
"addInputBelow": "Eingabefeld hinzufügen",
|
||||
"selectPlaceholder": "Status auswählen",
|
||||
"title": "Titel",
|
||||
"text": "Text",
|
||||
"unimplemented": "Element nicht implementiert:"
|
||||
},
|
||||
"status": {
|
||||
"draft": "Entwurf",
|
||||
"submitted": "Eingereicht",
|
||||
"approved": "Genehmigt",
|
||||
"rejected": "Abgelehnt",
|
||||
"signed": "Signiert"
|
||||
},
|
||||
"navigation": {
|
||||
"previous": "Zurück",
|
||||
"next": "Weiter",
|
||||
"save": "Speichern",
|
||||
"submit": "Einreichen"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"title": "Vorlagen",
|
||||
"editorTitle": "Administration - JSON Template Editor",
|
||||
"newTemplate": "Neue Vorlage",
|
||||
"reset": "Zurücksetzen",
|
||||
"unsavedChanges": "Ungespeicherte Änderungen",
|
||||
"noTemplateFound": "Keine Vorlage gefunden. Erstellen Sie eine neue Vorlage.",
|
||||
"invalidJson": "Ungültiges JSON",
|
||||
"invalidJsonDescription": "Das JSON-Format ist ungültig. Bitte korrigieren Sie die Syntax.",
|
||||
"lastModified": "Zuletzt bearbeitet am",
|
||||
"created": "Vorlage erfolgreich erstellt",
|
||||
"updated": "Vorlage erfolgreich aktualisiert",
|
||||
"saveError": "Fehler beim Speichern der Vorlage",
|
||||
"unsavedWarning": "Sie haben ungespeicherte Änderungen. Möchten Sie die Seite wirklich verlassen?"
|
||||
},
|
||||
"versions": {
|
||||
"title": "Versionen",
|
||||
"pageTitle": "Versionen: {name}",
|
||||
"empty": "Keine Versionen verfügbar",
|
||||
"loading": "Versionen werden geladen...",
|
||||
"loadError": "Fehler beim Laden der Versionen",
|
||||
"loadErrorDescription": "Versionen konnten nicht geladen werden",
|
||||
"unknownError": "Unbekannter Fehler",
|
||||
"compare": "Vergleichen",
|
||||
"restore": "Wiederherstellen",
|
||||
"restored": "Erfolg",
|
||||
"restoredDescription": "Das Formular wurde auf die ausgewählte Version zurückgesetzt.",
|
||||
"restoreError": "Version konnte nicht wiederhergestellt werden",
|
||||
"restoreTitle": "Version wiederherstellen",
|
||||
"restoreConfirm": "Möchten Sie Version v{number} wirklich wiederherstellen?",
|
||||
"restoreDescription": "Dies erstellt eine neue Version mit dem Inhalt der ausgewählten Version. Die aktuelle Version und alle Änderungen bleiben in der Historie erhalten.",
|
||||
"comparisonTitle": "Vergleich: Aktuelles Formular mit Version v{number}",
|
||||
"comparisonError": "Fehler beim Laden der Version",
|
||||
"elementsAdded": "Hinzugefügte Elemente ({count})",
|
||||
"elementsRemoved": "Entfernte Elemente ({count})",
|
||||
"elementsModified": "Geänderte Elemente ({count})",
|
||||
"elementWithoutTitle": "Ohne Titel",
|
||||
"elementIn": "Element in",
|
||||
"optionsAdded": "Optionen hinzugefügt ({count})",
|
||||
"optionsRemoved": "Optionen entfernt ({count})",
|
||||
"optionsModified": "Optionen geändert ({count})",
|
||||
"noChanges": "Keine Unterschiede gefunden"
|
||||
},
|
||||
"comments": {
|
||||
"title": "Kommentare",
|
||||
"empty": "Keine Kommentare vorhanden",
|
||||
"submit": "Absenden",
|
||||
"edit": "Kommentar bearbeiten",
|
||||
"editAction": "Bearbeiten"
|
||||
},
|
||||
"compliance": {
|
||||
"title": "Compliance-Status",
|
||||
"critical": "Kritisch",
|
||||
"warning": "Warnung",
|
||||
"nonCritical": "Unkritisch"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Benachrichtigungen",
|
||||
"empty": "Keine Benachrichtigungen",
|
||||
"unreadCount": "{count} ungelesen",
|
||||
"tooltip": "Benachrichtigungen"
|
||||
},
|
||||
"administration": {
|
||||
"title": "Administration",
|
||||
"accessDenied": "Zugriff verweigert"
|
||||
},
|
||||
"user": {
|
||||
"administration": "Administration",
|
||||
"settings": "Einstellungen",
|
||||
"logout": "Abmelden"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
"language": {
|
||||
"title": "Sprache",
|
||||
"description": "Wählen Sie Ihre bevorzugte Sprache"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Erscheinungsbild",
|
||||
"description": "Wählen Sie zwischen hellem und dunklem Modus",
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Farbschema",
|
||||
"description": "Passen Sie die Farben der Anwendung an",
|
||||
"primary": "Primärfarbe",
|
||||
"neutral": "Neutralfarbe"
|
||||
}
|
||||
},
|
||||
"organization": {
|
||||
"current": "Aktuelle Organisation"
|
||||
},
|
||||
"auth": {
|
||||
"welcome": "Willkommen",
|
||||
"redirectMessage": "Sie werden zur Authentifizierung zu Keycloak weitergeleitet",
|
||||
"signIn": "Mit Keycloak anmelden",
|
||||
"termsAgreement": "Mit der Anmeldung stimmen Sie unseren Nutzungsbedingungen zu",
|
||||
"login": "Anmelden"
|
||||
},
|
||||
"error": {
|
||||
"pageNotFound": "Seite nicht gefunden",
|
||||
"pageNotFoundDescription": "Es tut uns leid, aber diese Seite konnte nicht gefunden werden."
|
||||
},
|
||||
"common": {
|
||||
"save": "Speichern",
|
||||
"cancel": "Abbrechen",
|
||||
"delete": "Löschen",
|
||||
"edit": "Bearbeiten",
|
||||
"close": "Schließen",
|
||||
"confirm": "Bestätigen",
|
||||
"back": "Zurück",
|
||||
"loading": "Laden...",
|
||||
"error": "Fehler",
|
||||
"success": "Erfolg",
|
||||
"name": "Name",
|
||||
"home": "Home",
|
||||
"type": "Typ",
|
||||
"general": "Allgemein",
|
||||
"on": "am"
|
||||
},
|
||||
"serverConnection": {
|
||||
"title": "Verbindung zum Server unterbrochen",
|
||||
@@ -13,5 +163,12 @@
|
||||
"lastCheck": "Letzte Überprüfung",
|
||||
"retryInfo": "Automatischer Wiederholungsversuch alle 60 Sekunden",
|
||||
"retryNow": "Jetzt erneut versuchen"
|
||||
},
|
||||
"roles": {
|
||||
"admin": "Administrator",
|
||||
"employee": "Arbeitnehmer",
|
||||
"employer": "Arbeitgeber",
|
||||
"worksCouncilMember": "Betriebsratsmitglied",
|
||||
"worksCouncilChair": "Betriebsratsvorsitzender"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,160 @@
|
||||
{
|
||||
"roles": {
|
||||
"admin": "Administrator",
|
||||
"employee": "Employee",
|
||||
"employer": "Employer",
|
||||
"worksCouncilMember": "Works Council Member",
|
||||
"worksCouncilChair": "Works Council Chair"
|
||||
"applicationForms": {
|
||||
"title": "Co-determination Applications",
|
||||
"createNew": "New Co-determination Application",
|
||||
"noFormsAvailable": "No applications available",
|
||||
"noPermission": "No Permission",
|
||||
"noPermissionDescription": "You do not have permission to create applications.",
|
||||
"backToOverview": "Back to Overview",
|
||||
"deleteConfirm": "Do you really want to delete the co-determination application \"{name}\"?",
|
||||
"deleteTitle": "Delete Co-determination Application",
|
||||
"lastEditedBy": "Last edited by",
|
||||
"createdBy": "Created by",
|
||||
"saved": "Application form saved",
|
||||
"submitted": "Application form submitted",
|
||||
"deleted": "Application form deleted",
|
||||
"formElements": {
|
||||
"comments": "Comments",
|
||||
"addInputBelow": "Add input field below",
|
||||
"selectPlaceholder": "Select status",
|
||||
"title": "Title",
|
||||
"text": "Text",
|
||||
"unimplemented": "Element unimplemented:"
|
||||
},
|
||||
"status": {
|
||||
"draft": "Draft",
|
||||
"submitted": "Submitted",
|
||||
"approved": "Approved",
|
||||
"rejected": "Rejected",
|
||||
"signed": "Signed"
|
||||
},
|
||||
"navigation": {
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"save": "Save",
|
||||
"submit": "Submit"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"title": "Templates",
|
||||
"editorTitle": "Administration - JSON Template Editor",
|
||||
"newTemplate": "New Template",
|
||||
"reset": "Reset",
|
||||
"unsavedChanges": "Unsaved Changes",
|
||||
"noTemplateFound": "No template found. Create a new template.",
|
||||
"invalidJson": "Invalid JSON",
|
||||
"invalidJsonDescription": "The JSON format is invalid. Please correct the syntax.",
|
||||
"lastModified": "Last edited on",
|
||||
"created": "Template successfully created",
|
||||
"updated": "Template successfully updated",
|
||||
"saveError": "Error saving template",
|
||||
"unsavedWarning": "You have unsaved changes. Do you really want to leave this page?"
|
||||
},
|
||||
"versions": {
|
||||
"title": "Versions",
|
||||
"pageTitle": "Versions: {name}",
|
||||
"empty": "No versions available",
|
||||
"loading": "Loading versions...",
|
||||
"loadError": "Error loading versions",
|
||||
"loadErrorDescription": "Versions could not be loaded",
|
||||
"unknownError": "Unknown error",
|
||||
"compare": "Compare",
|
||||
"restore": "Restore",
|
||||
"restored": "Success",
|
||||
"restoredDescription": "The form has been restored to the selected version.",
|
||||
"restoreError": "Version could not be restored",
|
||||
"restoreTitle": "Restore Version",
|
||||
"restoreConfirm": "Do you really want to restore version v{number}?",
|
||||
"restoreDescription": "This will create a new version with the content of the selected version. The current version and all changes will remain in the history.",
|
||||
"comparisonTitle": "Comparison: Current Form with Version v{number}",
|
||||
"comparisonError": "Error loading version",
|
||||
"elementsAdded": "Added Elements ({count})",
|
||||
"elementsRemoved": "Removed Elements ({count})",
|
||||
"elementsModified": "Modified Elements ({count})",
|
||||
"elementWithoutTitle": "Without Title",
|
||||
"elementIn": "Element in",
|
||||
"optionsAdded": "Options added ({count})",
|
||||
"optionsRemoved": "Options removed ({count})",
|
||||
"optionsModified": "Options modified ({count})",
|
||||
"noChanges": "No differences found"
|
||||
},
|
||||
"comments": {
|
||||
"title": "Comments",
|
||||
"empty": "No comments available",
|
||||
"submit": "Submit",
|
||||
"edit": "Edit Comment",
|
||||
"editAction": "Edit"
|
||||
},
|
||||
"compliance": {
|
||||
"title": "Compliance Status",
|
||||
"critical": "Critical",
|
||||
"warning": "Warning",
|
||||
"nonCritical": "Non-critical"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
"empty": "No notifications",
|
||||
"unreadCount": "{count} unread",
|
||||
"tooltip": "Notifications"
|
||||
},
|
||||
"administration": {
|
||||
"title": "Administration",
|
||||
"accessDenied": "Access denied"
|
||||
},
|
||||
"user": {
|
||||
"administration": "Administration",
|
||||
"settings": "Settings",
|
||||
"logout": "Log out"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"language": {
|
||||
"title": "Language",
|
||||
"description": "Choose your preferred language"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Appearance",
|
||||
"description": "Choose between light and dark mode",
|
||||
"light": "Light",
|
||||
"dark": "Dark"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Color Theme",
|
||||
"description": "Customize the application colors",
|
||||
"primary": "Primary Color",
|
||||
"neutral": "Neutral Color"
|
||||
}
|
||||
},
|
||||
"organization": {
|
||||
"current": "Current Organization"
|
||||
},
|
||||
"auth": {
|
||||
"welcome": "Welcome",
|
||||
"redirectMessage": "You will be redirected to Keycloak to authenticate",
|
||||
"signIn": "Sign in with Keycloak",
|
||||
"termsAgreement": "By signing in, you agree to our terms of service",
|
||||
"login": "Login"
|
||||
},
|
||||
"error": {
|
||||
"pageNotFound": "Page not found",
|
||||
"pageNotFoundDescription": "We are sorry but this page could not be found."
|
||||
},
|
||||
"common": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"close": "Close",
|
||||
"confirm": "Confirm",
|
||||
"back": "Back",
|
||||
"loading": "Loading...",
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
"name": "Name",
|
||||
"home": "Home",
|
||||
"type": "Type",
|
||||
"general": "General",
|
||||
"on": "on"
|
||||
},
|
||||
"serverConnection": {
|
||||
"title": "Server Connection Lost",
|
||||
@@ -13,5 +163,12 @@
|
||||
"lastCheck": "Last check",
|
||||
"retryInfo": "Automatic retry every 60 seconds",
|
||||
"retryNow": "Try again now"
|
||||
},
|
||||
"roles": {
|
||||
"admin": "Administrator",
|
||||
"employee": "Employee",
|
||||
"employer": "Employer",
|
||||
"worksCouncilMember": "Works Council Member",
|
||||
"worksCouncilChair": "Works Council Chair"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ export default defineNuxtConfig({
|
||||
],
|
||||
i18n: {
|
||||
defaultLocale: 'de',
|
||||
strategy: 'no_prefix',
|
||||
locales: [
|
||||
{ code: 'en', name: 'English', file: 'en.json' },
|
||||
{ code: 'de', name: 'Deutsch', file: 'de.json' }
|
||||
|
||||
@@ -28,4 +28,3 @@ export interface OptionModification {
|
||||
value: string
|
||||
labelChanged: { from: string; to: string }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user