Start UI Change
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
primary: 'green',
|
||||
gray: 'slate',
|
||||
colors: {
|
||||
primary: 'green',
|
||||
neutral: 'slate'
|
||||
},
|
||||
tooltip: {
|
||||
background: '!bg-background'
|
||||
},
|
||||
|
||||
26
frontend/assets/css/main.css
Normal file
26
frontend/assets/css/main.css
Normal file
@@ -0,0 +1,26 @@
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@theme static {
|
||||
--font-sans: "SF Pro Text", "SF Pro Display", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-mono: "SF Mono", "Cascadia Code", "JetBrains Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
--color-green-50: #f4fbf2;
|
||||
--color-green-100: #e7f7e1;
|
||||
--color-green-200: #cdeec4;
|
||||
--color-green-300: #a6e095;
|
||||
--color-green-400: #69c350;
|
||||
--color-green-500: #53ad3a;
|
||||
--color-green-600: #418e2b;
|
||||
--color-green-700: #357025;
|
||||
--color-green-800: #2d5922;
|
||||
--color-green-900: #254a1d;
|
||||
--color-green-950: #10280b;
|
||||
}
|
||||
|
||||
:root {
|
||||
--ui-container: 90rem;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
@@ -38,7 +38,7 @@ const emitConfirm = () => {
|
||||
>
|
||||
Archivieren
|
||||
</UButton>
|
||||
<UModal v-model="showModal">
|
||||
<UModal v-model:open="showModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<span class="text-md font-bold">Archivieren bestätigen</span>
|
||||
|
||||
@@ -140,7 +140,7 @@ loadAccounts()
|
||||
</InputGroup>
|
||||
</div>
|
||||
|
||||
<UModal v-model="showCreate">
|
||||
<UModal v-model:open="showCreate">
|
||||
<UCard>
|
||||
<template #header>Neue Bankverbindung erstellen</template>
|
||||
<div class="space-y-3">
|
||||
|
||||
@@ -31,33 +31,35 @@ const emitConfirm = () => {
|
||||
>
|
||||
<slot name="button"></slot>
|
||||
</UButton>
|
||||
<UModal v-model="showModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<slot name="header"></slot>
|
||||
</template>
|
||||
<slot/>
|
||||
<template #footer>
|
||||
<div class="text-right">
|
||||
<UButtonGroup>
|
||||
<UButton
|
||||
variant="outline"
|
||||
@click="showModal = false"
|
||||
>
|
||||
Abbrechen
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="emitConfirm"
|
||||
class="ml-2"
|
||||
color="rose"
|
||||
>
|
||||
Archivieren
|
||||
</UButton>
|
||||
</UButtonGroup>
|
||||
<UModal v-model:open="showModal">
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<slot name="header"></slot>
|
||||
</template>
|
||||
<slot/>
|
||||
<template #footer>
|
||||
<div class="text-right">
|
||||
<UButtonGroup>
|
||||
<UButton
|
||||
variant="outline"
|
||||
@click="showModal = false"
|
||||
>
|
||||
Abbrechen
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="emitConfirm"
|
||||
class="ml-2"
|
||||
color="rose"
|
||||
>
|
||||
Archivieren
|
||||
</UButton>
|
||||
</UButtonGroup>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -227,9 +227,14 @@ defineShortcuts({
|
||||
width: 4px;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
background: transparent;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
@apply bg-gray-200 dark:bg-gray-700 rounded-full;
|
||||
background: #e5e7eb;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
:global(.dark) .custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: #374151;
|
||||
}
|
||||
</style>
|
||||
@@ -156,7 +156,8 @@ const moveFile = async () => {
|
||||
|
||||
<template>
|
||||
<UModal fullscreen >
|
||||
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }" class="h-full">
|
||||
<template #content>
|
||||
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }" class="h-full">
|
||||
<template #header>
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -351,7 +352,8 @@ const moveFile = async () => {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -78,81 +78,83 @@ const fileNames = computed(() => {
|
||||
|
||||
<template>
|
||||
<UModal>
|
||||
<div ref="dropZoneRef" class="relative h-full flex flex-col">
|
||||
<template #content>
|
||||
<div ref="dropZoneRef" class="relative h-full flex flex-col">
|
||||
|
||||
<div
|
||||
v-if="isOverDropZone"
|
||||
class="absolute inset-0 z-50 flex items-center justify-center bg-primary-500/10 border-2 border-primary-500 border-dashed rounded-lg backdrop-blur-sm transition-all"
|
||||
>
|
||||
<span class="text-xl font-bold text-primary-600 bg-white/80 px-4 py-2 rounded shadow-sm">
|
||||
Dateien hier ablegen
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||
Datei hochladen
|
||||
</h3>
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
icon="i-heroicons-x-mark-20-solid"
|
||||
class="-my-1"
|
||||
@click="modal.close()"
|
||||
:disabled="uploadInProgress"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UFormGroup
|
||||
label="Datei:"
|
||||
:help="selectedFiles.length > 0 ? `${selectedFiles.length} Datei(en) ausgewählt` : 'Ziehen Sie Dateien hierher oder klicken Sie'"
|
||||
<div
|
||||
v-if="isOverDropZone"
|
||||
class="absolute inset-0 z-50 flex items-center justify-center bg-primary-500/10 border-2 border-primary-500 border-dashed rounded-lg backdrop-blur-sm transition-all"
|
||||
>
|
||||
<UInput
|
||||
v-if="selectedFiles.length === 0"
|
||||
type="file"
|
||||
id="fileUploadInput"
|
||||
multiple
|
||||
accept="image/jpeg, image/png, image/gif, application/pdf"
|
||||
@change="onFileInputChange"
|
||||
/>
|
||||
<span class="text-xl font-bold text-primary-600 bg-white/80 px-4 py-2 rounded shadow-sm">
|
||||
Dateien hier ablegen
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedFiles.length > 0" class="mt-2 text-sm text-gray-500">
|
||||
Ausgewählt: <span class="font-medium text-gray-700 dark:text-gray-300">{{ fileNames }}</span>
|
||||
</div>
|
||||
</UFormGroup>
|
||||
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||
Datei hochladen
|
||||
</h3>
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
icon="i-heroicons-x-mark-20-solid"
|
||||
class="-my-1"
|
||||
@click="modal.close()"
|
||||
:disabled="uploadInProgress"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UFormGroup
|
||||
label="Typ:"
|
||||
class="mt-3"
|
||||
>
|
||||
<USelectMenu
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
searchable
|
||||
searchable-placeholder="Suchen..."
|
||||
:options="availableFiletypes"
|
||||
v-model="props.fileData.type"
|
||||
:disabled="!props.fileData.typeEnabled"
|
||||
<UFormGroup
|
||||
label="Datei:"
|
||||
:help="selectedFiles.length > 0 ? `${selectedFiles.length} Datei(en) ausgewählt` : 'Ziehen Sie Dateien hierher oder klicken Sie'"
|
||||
>
|
||||
<template #label>
|
||||
<span v-if="availableFiletypes.find(x => x.id === props.fileData.type)">{{availableFiletypes.find(x => x.id === props.fileData.type).name}}</span>
|
||||
<span v-else>Kein Typ ausgewählt</span>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
<UInput
|
||||
v-if="selectedFiles.length === 0"
|
||||
type="file"
|
||||
id="fileUploadInput"
|
||||
multiple
|
||||
accept="image/jpeg, image/png, image/gif, application/pdf"
|
||||
@change="onFileInputChange"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<UButton
|
||||
@click="uploadFiles"
|
||||
:loading="uploadInProgress"
|
||||
:disabled="uploadInProgress || selectedFiles.length === 0"
|
||||
>Hochladen</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
<div v-if="selectedFiles.length > 0" class="mt-2 text-sm text-gray-500">
|
||||
Ausgewählt: <span class="font-medium text-gray-700 dark:text-gray-300">{{ fileNames }}</span>
|
||||
</div>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Typ:"
|
||||
class="mt-3"
|
||||
>
|
||||
<USelectMenu
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
searchable
|
||||
searchable-placeholder="Suchen..."
|
||||
:options="availableFiletypes"
|
||||
v-model="props.fileData.type"
|
||||
:disabled="!props.fileData.typeEnabled"
|
||||
>
|
||||
<template #label>
|
||||
<span v-if="availableFiletypes.find(x => x.id === props.fileData.type)">{{availableFiletypes.find(x => x.id === props.fileData.type).name}}</span>
|
||||
<span v-else>Kein Typ ausgewählt</span>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
<template #footer>
|
||||
<UButton
|
||||
@click="uploadFiles"
|
||||
:loading="uploadInProgress"
|
||||
:disabled="uploadInProgress || selectedFiles.length === 0"
|
||||
>Hochladen</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -191,14 +191,14 @@ const filteredRows = computed(() => {
|
||||
<EntityTableMobile
|
||||
v-if="platform === 'mobile'"
|
||||
:type="props.type"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
:rows="filteredRows"
|
||||
/>
|
||||
<EntityTable
|
||||
v-else
|
||||
@sort="(i) => emit('sort',i)"
|
||||
:type="props.type"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
:rows="filteredRows"
|
||||
:loading="props.loading"
|
||||
/>
|
||||
|
||||
@@ -114,7 +114,7 @@ setup()
|
||||
<div class="scroll" style="height: 70vh">
|
||||
<EntityTable
|
||||
:type="type"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
:rows="props.item[type]"
|
||||
style
|
||||
/>
|
||||
|
||||
@@ -249,7 +249,7 @@ const selectItem = (item) => {
|
||||
</Toolbar>
|
||||
<UTable
|
||||
:rows="props.item.createddocuments.filter(i => !i.archived)"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="selectItem"
|
||||
|
||||
@@ -94,7 +94,7 @@ function isImage(file) {
|
||||
</UCard>
|
||||
|
||||
<!-- 📱 PDF / IMG Viewer Slideover -->
|
||||
<UModal v-model="showViewer" side="bottom" class="h-[100dvh]" fullscreen>
|
||||
<UModal v-model:open="showViewer" side="bottom" class="h-[100dvh]" fullscreen>
|
||||
<!-- Header -->
|
||||
<div class="p-4 border-b flex justify-between items-center flex-shrink-0">
|
||||
<h3 class="font-bold truncate max-w-[70vw]">{{ activeFile?.path?.split("/").pop() }}</h3>
|
||||
|
||||
@@ -78,7 +78,7 @@ const renderedAllocations = computed(() => {
|
||||
<UTable
|
||||
v-if="props.item.statementallocations"
|
||||
:rows="renderedAllocations"
|
||||
:columns="[{key:'amount', label:'Betrag'},{key:'date', label:'Datum'},{key:'partner', label:'Partner'},{key:'description', label:'Beschreibung'}]"
|
||||
:columns="normalizeTableColumns([{key:'amount', label:'Betrag'},{key:'date', label:'Datum'},{key:'partner', label:'Partner'},{key:'description', label:'Beschreibung'}])"
|
||||
@select="(i) => selectAllocation(i)"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Buchungen anzuzeigen' }"
|
||||
>
|
||||
|
||||
@@ -95,26 +95,26 @@ const changeActivePhase = async (key) => {
|
||||
<UAccordion
|
||||
:items="renderedPhases"
|
||||
>
|
||||
<template #default="{item,index,open}">
|
||||
<template #default="slotProps">
|
||||
<UButton
|
||||
variant="ghost"
|
||||
:color="item.active ? 'primary' : 'white'"
|
||||
:color="slotProps.item.active ? 'primary' : 'white'"
|
||||
class="mb-1"
|
||||
:disabled="true"
|
||||
>
|
||||
<template #leading>
|
||||
<div class="w-6 h-6 flex items-center justify-center -my-1">
|
||||
<UIcon :name="item.icon" class="w-4 h-4 " />
|
||||
<UIcon :name="slotProps.item.icon" class="w-4 h-4 " />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<span class="truncate"> {{item.label}}</span>
|
||||
<span class="truncate"> {{ slotProps.item.label }}</span>
|
||||
|
||||
<template #trailing>
|
||||
<UIcon
|
||||
name="i-heroicons-chevron-right-20-solid"
|
||||
class="w-5 h-5 ms-auto transform transition-transform duration-200"
|
||||
:class="[open && 'rotate-90']"
|
||||
:class="[slotProps?.open && 'rotate-90']"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ const columns = [
|
||||
<UCard class="mt-5">
|
||||
<UTable
|
||||
class="mt-3"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
:rows="props.item.times"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
>
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
column: dataType.sortColumn || "date",
|
||||
direction: 'desc'
|
||||
})
|
||||
const normalizedColumns = computed(() => normalizeTableColumns(props.columns))
|
||||
|
||||
</script>
|
||||
|
||||
@@ -74,7 +75,7 @@
|
||||
@update:sort="emit('sort',{sort_column: sort.column, sort_direction: sort.direction})"
|
||||
v-if="dataType && columns"
|
||||
:rows="props.rows"
|
||||
:columns="props.columns"
|
||||
:columns="normalizedColumns"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => router.push(getShowRoute(type, i.id))"
|
||||
|
||||
@@ -55,17 +55,19 @@ setup()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UModal v-model="showMessageModal" prevent-close>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<span class="font-bold">{{messageToShow.title}}</span>
|
||||
</template>
|
||||
<p class=" my-2" v-html="messageToShow.description"></p>
|
||||
<UButton
|
||||
variant="outline"
|
||||
@click="markMessageAsRead"
|
||||
>Gelesen</UButton>
|
||||
</UCard>
|
||||
<UModal v-model:open="showMessageModal" prevent-close>
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<span class="font-bold">{{messageToShow.title}}</span>
|
||||
</template>
|
||||
<p class=" my-2" v-html="messageToShow.description"></p>
|
||||
<UButton
|
||||
variant="outline"
|
||||
@click="markMessageAsRead"
|
||||
>Gelesen</UButton>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<!-- <UCard
|
||||
@@ -79,7 +81,7 @@ setup()
|
||||
variant="ghost"
|
||||
@click="showMessage(globalMessages[0])"
|
||||
/>
|
||||
<UModal v-model="showMessageModal">
|
||||
<UModal v-model:open="showMessageModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<span class="font-bold">{{messageToShow.title}}</span>
|
||||
|
||||
@@ -125,13 +125,15 @@ function onSelect (option) {
|
||||
<UModal
|
||||
v-model="showCommandPalette"
|
||||
>
|
||||
<UCommandPalette
|
||||
v-model="selectedCommand"
|
||||
:groups="groups"
|
||||
:autoselect="false"
|
||||
@update:model-value="onSelect"
|
||||
ref="commandPaletteRef"
|
||||
/>
|
||||
<template #content>
|
||||
<UCommandPalette
|
||||
v-model="selectedCommand"
|
||||
:groups="groups"
|
||||
:autoselect="false"
|
||||
@update:model-value="onSelect"
|
||||
ref="commandPaletteRef"
|
||||
/>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,9 +2,16 @@
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const { isHelpSlideoverOpen } = useDashboard()
|
||||
const { metaSymbol } = useShortcuts()
|
||||
const { entries, pending, error, seenState, refresh, markAsSeen } = useChangelog()
|
||||
|
||||
const metaSymbol = computed(() => {
|
||||
if (import.meta.server) {
|
||||
return 'Ctrl'
|
||||
}
|
||||
|
||||
return /Mac|iPhone|iPad|iPod/i.test(navigator.platform) ? '⌘' : 'Ctrl'
|
||||
})
|
||||
|
||||
const shortcuts = ref(false)
|
||||
const query = ref('')
|
||||
const toast = useToast()
|
||||
@@ -154,7 +161,7 @@ watch(isHelpSlideoverOpen, async (isOpen) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardSlideover v-model="isHelpSlideoverOpen">
|
||||
<USlideover v-model:open="isHelpSlideoverOpen" side="right">
|
||||
<template #title>
|
||||
<UButton
|
||||
v-if="shortcuts"
|
||||
@@ -168,93 +175,94 @@ watch(isHelpSlideoverOpen, async (isOpen) => {
|
||||
{{ shortcuts ? 'Shortcuts' : 'Hilfe & Information' }}
|
||||
</template>
|
||||
|
||||
<div v-if="shortcuts" class="space-y-6">
|
||||
<UInput v-model="query" icon="i-heroicons-magnifying-glass" placeholder="Search..." autofocus color="gray" />
|
||||
<template #body>
|
||||
<div v-if="shortcuts" class="space-y-6">
|
||||
<UInput v-model="query" icon="i-heroicons-magnifying-glass" placeholder="Search..." autofocus color="gray" />
|
||||
|
||||
<div v-for="(category, index) in filteredCategories" :key="index">
|
||||
<p class="mb-3 text-sm text-gray-900 dark:text-white font-semibold">
|
||||
{{ category.title }}
|
||||
</p>
|
||||
<div v-for="(category, index) in filteredCategories" :key="index">
|
||||
<p class="mb-3 text-sm text-gray-900 dark:text-white font-semibold">
|
||||
{{ category.title }}
|
||||
</p>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div v-for="(item, i) in category.items" :key="i" class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">{{ item.name }}</span>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(item, i) in category.items" :key="i" class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">{{ item.name }}</span>
|
||||
|
||||
<div class="flex items-center justify-end flex-shrink-0 gap-0.5">
|
||||
<UKbd v-for="(shortcut, j) in item.shortcuts" :key="j">
|
||||
{{ shortcut }}
|
||||
</UKbd>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-y-6">
|
||||
<div class="flex flex-col gap-y-3">
|
||||
<UButton v-for="(link, index) in links" :key="index" color="white" v-bind="link" />
|
||||
</div>
|
||||
|
||||
<UCard>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-base font-semibold text-gray-900 dark:text-white">
|
||||
Changelog
|
||||
</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Zuletzt geöffnet: {{ lastOpenedLabel }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
icon="i-heroicons-arrow-path"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
:loading="pending"
|
||||
@click="refresh(true)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UAlert
|
||||
v-if="error"
|
||||
class="mt-4"
|
||||
color="red"
|
||||
variant="soft"
|
||||
title="Changelog konnte nicht geladen werden"
|
||||
:description="error"
|
||||
/>
|
||||
|
||||
<div v-else-if="pending && !changelogEntries.length" class="mt-4">
|
||||
<UProgress animation="carousel"/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="changelogEntries.length" class="mt-4 flex flex-col gap-3">
|
||||
<div
|
||||
v-for="entry in changelogEntries"
|
||||
:key="entry.hash"
|
||||
class="rounded-lg border border-gray-200 dark:border-gray-800 p-3"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="font-medium text-gray-900 dark:text-white break-words">
|
||||
{{ entry.subject }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ entry.authorName }} · {{ dayjs(entry.committedAt).format('DD.MM.YYYY HH:mm') }}
|
||||
</p>
|
||||
<div class="flex items-center justify-end flex-shrink-0 gap-0.5">
|
||||
<UKbd v-for="(shortcut, j) in item.shortcuts" :key="j">
|
||||
{{ shortcut }}
|
||||
</UKbd>
|
||||
</div>
|
||||
|
||||
<UBadge color="gray" variant="subtle">
|
||||
{{ entry.shortHash }}
|
||||
</UBadge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-y-6">
|
||||
<div class="flex flex-col gap-y-3">
|
||||
<UButton v-for="(link, index) in links" :key="index" color="white" v-bind="link" />
|
||||
</div>
|
||||
|
||||
<p v-else class="mt-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
Es sind noch keine Changelog-Einträge verfügbar.
|
||||
</p>
|
||||
</UCard>
|
||||
</div>
|
||||
<UCard>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-base font-semibold text-gray-900 dark:text-white">
|
||||
Changelog
|
||||
</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Zuletzt geöffnet: {{ lastOpenedLabel }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
icon="i-heroicons-arrow-path"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
:loading="pending"
|
||||
@click="refresh(true)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UAlert
|
||||
v-if="error"
|
||||
class="mt-4"
|
||||
color="red"
|
||||
variant="soft"
|
||||
title="Changelog konnte nicht geladen werden"
|
||||
:description="error"
|
||||
/>
|
||||
|
||||
<div v-else-if="pending && !changelogEntries.length" class="mt-4">
|
||||
<UProgress animation="carousel"/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="changelogEntries.length" class="mt-4 flex flex-col gap-3">
|
||||
<div
|
||||
v-for="entry in changelogEntries"
|
||||
:key="entry.hash"
|
||||
class="rounded-lg border border-gray-200 dark:border-gray-800 p-3"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="font-medium text-gray-900 dark:text-white break-words">
|
||||
{{ entry.subject }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
{{ entry.authorName }} · {{ dayjs(entry.committedAt).format('DD.MM.YYYY HH:mm') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UBadge color="gray" variant="subtle">
|
||||
{{ entry.shortHash }}
|
||||
</UBadge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-else class="mt-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
Es sind noch keine Changelog-Einträge verfügbar.
|
||||
</p>
|
||||
</UCard>
|
||||
</div>
|
||||
<!-- <div class="mt-5" v-if="!loadingContactRequest">
|
||||
<h1 class="font-semibold">Kontaktanfrage:</h1>
|
||||
<UForm
|
||||
@@ -305,5 +313,6 @@ watch(isHelpSlideoverOpen, async (isOpen) => {
|
||||
</UForm>
|
||||
</div>
|
||||
<UProgress class="mt-5" animation="carousel" v-else/>-->
|
||||
</UDashboardSlideover>
|
||||
</template>
|
||||
</USlideover>
|
||||
</template>
|
||||
|
||||
@@ -79,35 +79,37 @@ const renderText = (text) => {
|
||||
v-model="showAddHistoryItemModal"
|
||||
|
||||
>
|
||||
<UCard class="h-full">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||
Eintrag hinzufügen
|
||||
</h3>
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="showAddHistoryItemModal = false" />
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<UCard class="h-full">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||
Eintrag hinzufügen
|
||||
</h3>
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="showAddHistoryItemModal = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UFormGroup
|
||||
label="Text:"
|
||||
>
|
||||
<UTextarea
|
||||
v-model="addHistoryItemData.text"
|
||||
@keyup.meta.enter="addHistoryItem"
|
||||
/>
|
||||
<!-- TODO: Add Dropdown and Checking for Usernames -->
|
||||
<!-- <template #help>
|
||||
<UKbd>{{metaSymbol}}</UKbd> <UKbd>Enter</UKbd> Speichern
|
||||
</template>-->
|
||||
<UFormGroup
|
||||
label="Text:"
|
||||
>
|
||||
<UTextarea
|
||||
v-model="addHistoryItemData.text"
|
||||
@keyup.meta.enter="addHistoryItem"
|
||||
/>
|
||||
<!-- TODO: Add Dropdown and Checking for Usernames -->
|
||||
<!-- <template #help>
|
||||
<UKbd>{{metaSymbol}}</UKbd> <UKbd>Enter</UKbd> Speichern
|
||||
</template>-->
|
||||
|
||||
</UFormGroup>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<UButton @click="addHistoryItem">Speichern</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
<template #footer>
|
||||
<UButton @click="addHistoryItem">Speichern</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
<Toolbar
|
||||
v-if="!props.renderHeadline && props.elementId && props.type"
|
||||
|
||||
@@ -90,7 +90,8 @@ watch(() => labelPrinter.connected, (connected) => {
|
||||
|
||||
<template>
|
||||
<UModal :ui="{ width: 'sm:max-w-5xl' }">
|
||||
<UCard class="w-[92vw] max-w-5xl">
|
||||
<template #content>
|
||||
<UCard class="w-[92vw] max-w-5xl">
|
||||
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -133,6 +134,7 @@ watch(() => labelPrinter.connected, (connected) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</UCard>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -18,21 +18,23 @@ const handleClick = async () => {
|
||||
<template>
|
||||
<!-- Printer Button -->
|
||||
|
||||
<UModal v-model="showPrinterInfo">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold">Drucker Informationen</h3>
|
||||
<UButton icon="i-heroicons-x-mark" variant="ghost" @click="showPrinterInfo = false" />
|
||||
</div>
|
||||
</template>
|
||||
<p>Seriennummer: {{labelPrinter.info.serial}}</p>
|
||||
<p>MAC: {{labelPrinter.info.mac}}</p>
|
||||
<p>Modell: {{labelPrinter.info.modelId}}</p>
|
||||
<p>Charge: {{labelPrinter.info.charge}}</p>
|
||||
<p>Hardware Version: {{labelPrinter.info.hardwareVersion}}</p>
|
||||
<p>Software Version: {{labelPrinter.info.softwareVersion}}</p>
|
||||
</UCard>
|
||||
<UModal v-model:open="showPrinterInfo">
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold">Drucker Informationen</h3>
|
||||
<UButton icon="i-heroicons-x-mark" variant="ghost" @click="showPrinterInfo = false" />
|
||||
</div>
|
||||
</template>
|
||||
<p>Seriennummer: {{labelPrinter.info.serial}}</p>
|
||||
<p>MAC: {{labelPrinter.info.mac}}</p>
|
||||
<p>Modell: {{labelPrinter.info.modelId}}</p>
|
||||
<p>Charge: {{labelPrinter.info.charge}}</p>
|
||||
<p>Hardware Version: {{labelPrinter.info.hardwareVersion}}</p>
|
||||
<p>Software Version: {{labelPrinter.info.softwareVersion}}</p>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<UButton
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
const { has } = usePermission()
|
||||
|
||||
// Lokaler State für den Taschenrechner
|
||||
const showCalculator = ref(false)
|
||||
const tenantExtraModules = computed(() => {
|
||||
const modules = auth.activeTenantData?.extraModules
|
||||
return Array.isArray(modules) ? modules : []
|
||||
@@ -19,6 +23,17 @@ const isAdmin = computed(() => Boolean(auth.user?.is_admin))
|
||||
const tenantFeatures = computed(() => auth.activeTenantData?.features || {})
|
||||
const featureEnabled = (key) => tenantFeatures.value?.[key] !== false
|
||||
const visibleItems = (items) => items.filter(item => item && !item.disabled)
|
||||
const isRouteActive = (to) => {
|
||||
if (!to) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (to === '/') {
|
||||
return route.path === '/'
|
||||
}
|
||||
|
||||
return route.path === to || route.path.startsWith(`${to}/`)
|
||||
}
|
||||
|
||||
const links = computed(() => {
|
||||
const organisationChildren = [
|
||||
@@ -284,15 +299,13 @@ const links = computed(() => {
|
||||
label: pin.label,
|
||||
to: pin.link,
|
||||
icon: pin.icon,
|
||||
target: "_blank",
|
||||
pinned: true
|
||||
target: "_blank"
|
||||
}
|
||||
} else if (pin.type === "standardEntity") {
|
||||
return {
|
||||
label: pin.label,
|
||||
to: pin.datatype === "tasks" ? `/tasks/show/${pin.id}` : `/standardEntity/${pin.datatype}/show/${pin.id}`,
|
||||
icon: pin.icon,
|
||||
pinned: true
|
||||
icon: pin.icon
|
||||
}
|
||||
}
|
||||
}),
|
||||
@@ -382,81 +395,80 @@ const links = computed(() => {
|
||||
])
|
||||
})
|
||||
|
||||
const accordionItems = computed(() =>
|
||||
links.value.filter(item => Array.isArray(item.children) && item.children.length > 0)
|
||||
)
|
||||
const navItems = computed(() =>
|
||||
links.value
|
||||
.filter(Boolean)
|
||||
.map((item, index) => {
|
||||
const children = Array.isArray(item.children)
|
||||
? item.children.map((child, childIndex) => ({
|
||||
...child,
|
||||
value: child.id || child.label || `${index}-${childIndex}`,
|
||||
active: isRouteActive(child.to)
|
||||
}))
|
||||
: undefined
|
||||
|
||||
const buttonItems = computed(() =>
|
||||
links.value.filter(item => !item.children || item.children.length === 0)
|
||||
const active = item.active || isRouteActive(item.to) || Boolean(children?.some(child => child.active))
|
||||
|
||||
return {
|
||||
...item,
|
||||
children,
|
||||
value: item.id || item.label || String(index),
|
||||
defaultOpen: item.defaultOpen || active,
|
||||
active,
|
||||
tooltip: true,
|
||||
popover: true,
|
||||
trailingIcon: children?.length ? undefined : ''
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-1">
|
||||
<UButton
|
||||
v-for="item in buttonItems"
|
||||
:key="item.label"
|
||||
variant="ghost"
|
||||
:color="(item.to && route.path === item.to) ? 'primary' : (item.pinned ? 'amber' : 'gray')"
|
||||
:icon="item.pinned ? 'i-heroicons-star' : item.icon"
|
||||
class="w-full"
|
||||
:to="item.to"
|
||||
:target="item.target"
|
||||
@click="item.click ? item.click() : null"
|
||||
>
|
||||
<UIcon
|
||||
v-if="item.pinned"
|
||||
:name="item.icon"
|
||||
class="w-5 h-5 me-2"
|
||||
/>
|
||||
{{ item.label }}
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<UDivider class="my-2"/>
|
||||
|
||||
<UAccordion
|
||||
:items="accordionItems"
|
||||
:multiple="false"
|
||||
class="mt-2"
|
||||
<UNavigationMenu
|
||||
:items="navItems"
|
||||
orientation="vertical"
|
||||
:collapsed="props.collapsed"
|
||||
tooltip
|
||||
popover
|
||||
color="neutral"
|
||||
highlight
|
||||
highlight-color="primary"
|
||||
class="w-full"
|
||||
:ui="{
|
||||
root: 'w-full',
|
||||
list: 'space-y-1',
|
||||
link: 'min-w-0 rounded-lg px-2.5 py-2',
|
||||
linkLeadingIcon: 'size-5 shrink-0',
|
||||
linkLabel: 'truncate',
|
||||
childList: 'ms-0 space-y-1 border-l border-default ps-3',
|
||||
childLink: 'min-w-0 rounded-lg px-2 py-1.5',
|
||||
childLinkLabel: 'truncate'
|
||||
}"
|
||||
>
|
||||
<template #default="{ item, open }">
|
||||
<UButton
|
||||
variant="ghost"
|
||||
:color="(item.children?.some(c => route.path.includes(c.to))) ? 'primary' : 'gray'"
|
||||
:icon="item.icon"
|
||||
class="w-full"
|
||||
<template #item-leading="{ item, active }">
|
||||
<UIcon
|
||||
v-if="item.icon"
|
||||
:name="item.icon"
|
||||
class="size-5 shrink-0"
|
||||
:class="active ? 'text-primary' : 'text-muted'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item-trailing="{ item, active }">
|
||||
<UBadge
|
||||
v-if="item.badge && !props.collapsed"
|
||||
color="primary"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
>
|
||||
{{ item.label }}
|
||||
<template #trailing>
|
||||
<UIcon
|
||||
name="i-heroicons-chevron-right-20-solid"
|
||||
class="w-5 h-5 ms-auto transform transition-transform duration-200"
|
||||
:class="[open && 'rotate-90']"
|
||||
/>
|
||||
</template>
|
||||
</UButton>
|
||||
{{ item.badge }}
|
||||
</UBadge>
|
||||
<UIcon
|
||||
v-else-if="item.children?.length"
|
||||
name="i-heroicons-chevron-down-20-solid"
|
||||
class="size-4 shrink-0 transition-transform"
|
||||
:class="active ? 'text-primary' : 'text-muted'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item="{ item }">
|
||||
<div class="flex flex-col">
|
||||
<UButton
|
||||
v-for="child in item.children"
|
||||
:key="child.label"
|
||||
variant="ghost"
|
||||
:color="child.to === route.path ? 'primary' : 'gray'"
|
||||
:icon="child.icon"
|
||||
class="ml-4"
|
||||
:to="child.to"
|
||||
:target="child.target"
|
||||
:disabled="child.disabled"
|
||||
@click="child.click ? child.click() : null"
|
||||
>
|
||||
{{ child.label }}
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UAccordion>
|
||||
|
||||
<Calculator v-if="showCalculator" v-model="showCalculator"/>
|
||||
</UNavigationMenu>
|
||||
</template>
|
||||
|
||||
@@ -36,28 +36,30 @@ const setNotificationAsRead = async (notification) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardSlideover v-model="isNotificationsSlideoverOpen" title="Benachrichtigungen">
|
||||
<NuxtLink
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
:to="notification.link"
|
||||
class="p-3 rounded-md hover:bg-gray-50 dark:hover:bg-gray-800/50 cursor-pointer flex items-center gap-3 relative"
|
||||
@click="setNotificationAsRead(notification)"
|
||||
>
|
||||
<UChip color="primary" :show="!notification.read && !notification.readAt" inset>
|
||||
<UAvatar alt="FEDEO" size="md" />
|
||||
</UChip>
|
||||
<USlideover v-model:open="isNotificationsSlideoverOpen" title="Benachrichtigungen" side="right">
|
||||
<template #body>
|
||||
<NuxtLink
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
:to="notification.link"
|
||||
class="p-3 rounded-md hover:bg-gray-50 dark:hover:bg-gray-800/50 cursor-pointer flex items-center gap-3 relative"
|
||||
@click="setNotificationAsRead(notification)"
|
||||
>
|
||||
<UChip color="primary" :show="!notification.read && !notification.readAt" inset>
|
||||
<UAvatar alt="FEDEO" size="md" />
|
||||
</UChip>
|
||||
|
||||
<div class="text-sm flex-1">
|
||||
<p class="flex items-center justify-between">
|
||||
<span class="text-gray-900 dark:text-white font-medium">{{notification.title}}</span>
|
||||
<div class="text-sm flex-1">
|
||||
<p class="flex items-center justify-between">
|
||||
<span class="text-gray-900 dark:text-white font-medium">{{notification.title}}</span>
|
||||
|
||||
<time :datetime="notification.date || notification.createdAt || notification.created_at" class="text-gray-500 dark:text-gray-400 text-xs" v-text="formatTimeAgo(new Date(notification.createdAt || notification.created_at))" />
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">
|
||||
{{ notification.message }}
|
||||
</p>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</UDashboardSlideover>
|
||||
<time :datetime="notification.date || notification.createdAt || notification.created_at" class="text-gray-500 dark:text-gray-400 text-xs" v-text="formatTimeAgo(new Date(notification.createdAt || notification.created_at))" />
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">
|
||||
{{ notification.message }}
|
||||
</p>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</USlideover>
|
||||
</template>
|
||||
|
||||
@@ -16,28 +16,30 @@ const onLogout = async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UModal v-model="auth.sessionWarningVisible" prevent-close>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<h3 class="text-lg font-semibold">Sitzung läuft bald ab</h3>
|
||||
</template>
|
||||
<UModal v-model:open="auth.sessionWarningVisible" prevent-close>
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<h3 class="text-lg font-semibold">Sitzung läuft bald ab</h3>
|
||||
</template>
|
||||
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300">
|
||||
Deine Sitzung endet in
|
||||
<span class="font-semibold">{{ remainingTimeLabel }}</span>.
|
||||
Bitte bestätige, um eingeloggt zu bleiben.
|
||||
</p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300">
|
||||
Deine Sitzung endet in
|
||||
<span class="font-semibold">{{ remainingTimeLabel }}</span>.
|
||||
Bitte bestätige, um eingeloggt zu bleiben.
|
||||
</p>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-2">
|
||||
<UButton variant="outline" color="gray" @click="onLogout">
|
||||
Abmelden
|
||||
</UButton>
|
||||
<UButton color="primary" @click="onRefresh">
|
||||
Eingeloggt bleiben
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-2">
|
||||
<UButton variant="outline" color="gray" @click="onLogout">
|
||||
Abmelden
|
||||
</UButton>
|
||||
<UButton color="primary" @click="onRefresh">
|
||||
Eingeloggt bleiben
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -124,7 +124,7 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UModal v-model="isOpen">
|
||||
<UModal v-model:open="isOpen">
|
||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -61,33 +61,35 @@ setupPage()
|
||||
|
||||
<template>
|
||||
<UModal :fullscreen="props.mode === 'show'">
|
||||
<EntityShow
|
||||
v-if="loaded && props.mode === 'show'"
|
||||
:type="props.type"
|
||||
:item="item"
|
||||
@updateNeeded="setupPage"
|
||||
:key="item"
|
||||
:in-modal="true"
|
||||
/>
|
||||
<EntityEdit
|
||||
v-else-if="loaded && (props.mode === 'edit' || props.mode === 'create')"
|
||||
:type="props.type"
|
||||
:item="item"
|
||||
:inModal="true"
|
||||
@return-data="(data) => emit('return-data',data)"
|
||||
:createQuery="props.createQuery"
|
||||
:mode="props.mode"
|
||||
/>
|
||||
<!-- <EntityList
|
||||
v-else-if="loaded && props.mode === 'list'"
|
||||
:type="props.type"
|
||||
:items="items"
|
||||
/>-->
|
||||
<UProgress
|
||||
v-else
|
||||
animation="carousel"
|
||||
class="p-5 mt-10"
|
||||
/>
|
||||
<template #content>
|
||||
<EntityShow
|
||||
v-if="loaded && props.mode === 'show'"
|
||||
:type="props.type"
|
||||
:item="item"
|
||||
@updateNeeded="setupPage"
|
||||
:key="item"
|
||||
:in-modal="true"
|
||||
/>
|
||||
<EntityEdit
|
||||
v-else-if="loaded && (props.mode === 'edit' || props.mode === 'create')"
|
||||
:type="props.type"
|
||||
:item="item"
|
||||
:inModal="true"
|
||||
@return-data="(data) => emit('return-data',data)"
|
||||
:createQuery="props.createQuery"
|
||||
:mode="props.mode"
|
||||
/>
|
||||
<!-- <EntityList
|
||||
v-else-if="loaded && props.mode === 'list'"
|
||||
:type="props.type"
|
||||
:items="items"
|
||||
/>-->
|
||||
<UProgress
|
||||
v-else
|
||||
animation="carousel"
|
||||
class="p-5 mt-10"
|
||||
/>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,27 +1,59 @@
|
||||
<script setup>
|
||||
const auth = useAuthStore()
|
||||
|
||||
const selectedTenant = ref(auth.user.tenant_id)
|
||||
const activeTenantName = computed(() => {
|
||||
return auth.activeTenantData?.name || auth.tenants?.find((tenant) => tenant.id === auth.activeTenant)?.name || 'Mandant waehlen'
|
||||
})
|
||||
|
||||
const tenantInitials = computed(() => {
|
||||
return activeTenantName.value
|
||||
.split(' ')
|
||||
.filter(Boolean)
|
||||
.slice(0, 2)
|
||||
.map((part) => part[0]?.toUpperCase() || '')
|
||||
.join('') || 'M'
|
||||
})
|
||||
|
||||
const tenantItems = computed(() => [
|
||||
auth.tenants.map((tenant) => ({
|
||||
label: tenant.name,
|
||||
icon: tenant.id === auth.activeTenant ? 'i-heroicons-check' : undefined,
|
||||
disabled: Boolean(tenant.locked),
|
||||
onSelect: async (event) => {
|
||||
if (tenant.locked || tenant.id === auth.activeTenant) {
|
||||
event?.preventDefault?.()
|
||||
return
|
||||
}
|
||||
|
||||
await auth.switchTenant(tenant.id)
|
||||
}
|
||||
}))
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
:options="auth.tenants"
|
||||
value-attribute="id"
|
||||
class="w-40"
|
||||
@change="auth.switchTenant(selectedTenant)"
|
||||
v-model="selectedTenant"
|
||||
:items="tenantItems"
|
||||
:content="{ align: 'start', side: 'bottom', sideOffset: 6 }"
|
||||
:ui="{ content: 'w-[var(--reka-dropdown-menu-trigger-width)] max-w-[var(--reka-dropdown-menu-trigger-width)]' }"
|
||||
class="block w-40"
|
||||
:avatar="{
|
||||
alt: activeTenantName,
|
||||
text: tenantInitials,
|
||||
loading: 'lazy'
|
||||
}"
|
||||
>
|
||||
<UButton color="gray" variant="ghost" :class="[open && 'bg-gray-50 dark:bg-gray-800']" class="w-full">
|
||||
<UAvatar :alt="auth.activeTenantData?.name" size="md" />
|
||||
|
||||
<span class="truncate text-gray-900 dark:text-white font-semibold">{{auth.tenants.find(i => auth.activeTenant === i.id).name}}</span>
|
||||
</UButton>
|
||||
|
||||
<template #option="{option}">
|
||||
{{option.name}}
|
||||
<template #default="{ open }">
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
class="w-full min-w-0 max-w-full justify-start gap-2 rounded-lg px-2.5 py-2 text-left"
|
||||
:class="[open && 'bg-gray-100 dark:bg-gray-800']"
|
||||
>
|
||||
<span class="min-w-0 flex-1 truncate font-medium text-gray-900 dark:text-white">
|
||||
{{ activeTenantName }}
|
||||
</span>
|
||||
</UButton>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
@@ -1,8 +1,6 @@
|
||||
<script setup>
|
||||
|
||||
const { isHelpSlideoverOpen } = useDashboard()
|
||||
const { isDashboardSearchModalOpen } = useUIState()
|
||||
const { metaSymbol } = useShortcuts()
|
||||
const auth = useAuthStore()
|
||||
|
||||
const items = computed(() => [
|
||||
@@ -31,8 +29,8 @@ const items = computed(() => [
|
||||
|
||||
<template>
|
||||
<UDropdown mode="hover" :items="items" :ui="{ width: 'w-full', item: { disabled: 'cursor-text select-text' } }" :popper="{ strategy: 'absolute', placement: 'top' }" class="w-full">
|
||||
<template #default="{ open }">
|
||||
<UButton color="gray" variant="ghost" class="w-full" :label="auth.user.email" :class="[open && 'bg-gray-50 dark:bg-gray-800']">
|
||||
<template #default="slotProps">
|
||||
<UButton color="gray" variant="ghost" class="w-full" :label="auth.user.email" :class="[slotProps?.open && 'bg-gray-50 dark:bg-gray-800']">
|
||||
<!-- <template #leading>
|
||||
<UAvatar :alt="auth.user.email" size="xs" />
|
||||
</template>-->
|
||||
|
||||
@@ -67,6 +67,7 @@ const startImport = () => {
|
||||
|
||||
<template>
|
||||
<UModal :fullscreen="false">
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
Erstelltes Dokument Kopieren
|
||||
@@ -101,6 +102,7 @@ const startImport = () => {
|
||||
</template>
|
||||
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ setupPage()
|
||||
<UTable
|
||||
v-if="openTasks.length > 0"
|
||||
:rows="openTasks"
|
||||
:columns="[{key:'name',label:'Name'},{key:'categorie',label:'Kategorie'}]"
|
||||
:columns="normalizeTableColumns([{key:'name',label:'Name'},{key:'categorie',label:'Kategorie'}])"
|
||||
@select="(i) => router.push(`/tasks/show/${i.id}`)"
|
||||
/>
|
||||
<div v-else>
|
||||
|
||||
@@ -53,7 +53,10 @@ const emit = defineEmits(["click"])
|
||||
<style scoped>
|
||||
/* FAB Basis */
|
||||
.fab-base {
|
||||
@apply rounded-full px-5 py-4 text-lg font-semibold;
|
||||
border-radius: 9999px;
|
||||
padding: 1rem 1.25rem;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
|
||||
/* Wenn nur ein Icon vorhanden ist → runder Kreis */
|
||||
/* Wenn Label + Icon → Extended FAB */
|
||||
@@ -61,6 +64,12 @@ const emit = defineEmits(["click"])
|
||||
|
||||
/* Optional: Auto-Kreisen wenn kein Label */
|
||||
#fab:not([label]) {
|
||||
@apply w-14 h-14 p-0 flex items-center justify-center text-2xl;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -206,16 +206,62 @@ const addVideo = () => {
|
||||
<style scoped>
|
||||
/* Toolbar & Buttons */
|
||||
.toolbar-btn {
|
||||
@apply p-1.5 rounded text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors text-sm font-medium min-w-[28px] h-[28px] flex items-center justify-center;
|
||||
padding: 0.375rem;
|
||||
border-radius: 0.25rem;
|
||||
color: #4b5563;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.toolbar-btn.is-active {
|
||||
@apply bg-gray-200 dark:bg-gray-600 text-black dark:text-white shadow-inner;
|
||||
background: #e5e7eb;
|
||||
color: #000;
|
||||
box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
|
||||
}
|
||||
.bubble-btn {
|
||||
@apply px-2 py-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors text-sm min-w-[24px] h-[24px] flex items-center justify-center text-gray-700 dark:text-gray-200;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
font-size: 0.875rem;
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #374151;
|
||||
}
|
||||
.bubble-btn.is-active {
|
||||
@apply bg-gray-200 dark:bg-gray-600 text-black dark:text-white;
|
||||
background: #e5e7eb;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.toolbar-btn:hover,
|
||||
.bubble-btn:hover {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
:global(.dark) .toolbar-btn {
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
:global(.dark) .toolbar-btn:hover,
|
||||
:global(.dark) .bubble-btn:hover {
|
||||
background: #374151;
|
||||
}
|
||||
|
||||
:global(.dark) .toolbar-btn.is-active,
|
||||
:global(.dark) .bubble-btn.is-active {
|
||||
background: #4b5563;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
:global(.dark) .bubble-btn {
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* GLOBAL EDITOR STYLES */
|
||||
@@ -235,20 +281,48 @@ const addVideo = () => {
|
||||
/* MENTION */
|
||||
.wiki-mention {
|
||||
/* Pill-Shape, grau/neutral statt knallig blau */
|
||||
@apply bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-200 px-1.5 py-0.5 rounded-md text-sm font-medium no-underline inline-block mx-0.5 align-middle border border-gray-200 dark:border-gray-700;
|
||||
|
||||
background: #f3f4f6;
|
||||
color: #374151;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin-inline: 0.125rem;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #e5e7eb;
|
||||
box-decoration-break: clone;
|
||||
}
|
||||
|
||||
.wiki-mention::before {
|
||||
@apply text-gray-400 dark:text-gray-500 mr-0.5;
|
||||
color: #9ca3af;
|
||||
margin-right: 0.125rem;
|
||||
}
|
||||
|
||||
.wiki-mention:hover {
|
||||
@apply bg-primary-50 dark:bg-primary-900/30 border-primary-200 dark:border-primary-800 text-primary-700 dark:text-primary-400;
|
||||
background: #eefbf0;
|
||||
border-color: #bbf7d0;
|
||||
color: #15803d;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:global(.dark) .wiki-mention {
|
||||
background: #1f2937;
|
||||
color: #e5e7eb;
|
||||
border-color: #374151;
|
||||
}
|
||||
|
||||
:global(.dark) .wiki-mention::before {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
:global(.dark) .wiki-mention:hover {
|
||||
background: rgb(20 83 45 / 0.3);
|
||||
border-color: #166534;
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
/* TABLE */
|
||||
table { width: 100% !important; border-collapse: collapse; table-layout: fixed; margin: 1rem 0; }
|
||||
th, td { border: 1px solid #d1d5db; padding: 0.5rem; vertical-align: top; box-sizing: border-box; min-width: 1em; }
|
||||
@@ -258,7 +332,7 @@ const addVideo = () => {
|
||||
.column-resize-handle { background-color: #3b82f6; width: 4px; }
|
||||
|
||||
/* CODE */
|
||||
pre { background: #0d1117; color: #c9d1d9; font-family: 'JetBrains Mono', monospace; padding: 0.75rem 1rem; border-radius: 0.5rem; margin: 1rem 0; overflow-x: auto; }
|
||||
pre { background: #0d1117; color: #c9d1d9; font-family: var(--font-mono); padding: 0.75rem 1rem; border-radius: 0.5rem; margin: 1rem 0; overflow-x: auto; }
|
||||
code { color: inherit; padding: 0; background: none; font-size: 0.9rem; }
|
||||
|
||||
/* IMG */
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UModal v-model="isCreateModalOpen">
|
||||
<UModal v-model:open="isCreateModalOpen">
|
||||
<div class="p-5">
|
||||
<h3 class="font-bold mb-4">Neue Seite</h3>
|
||||
<form @submit.prevent="createPage">
|
||||
|
||||
28
frontend/composables/useTableColumns.ts
Normal file
28
frontend/composables/useTableColumns.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
type LegacyTableColumn = {
|
||||
id?: string
|
||||
key?: string
|
||||
label?: unknown
|
||||
header?: unknown
|
||||
accessorKey?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export const normalizeTableColumns = (columns: LegacyTableColumn[] = []) => {
|
||||
return columns.map((column, index) => {
|
||||
const accessorKey = typeof column.accessorKey === 'string'
|
||||
? column.accessorKey
|
||||
: typeof column.key === 'string'
|
||||
? column.key
|
||||
: undefined
|
||||
|
||||
const header = column.header ?? column.label ?? accessorKey ?? `column_${index}`
|
||||
const id = column.id ?? accessorKey ?? (typeof header === 'string' ? header : `column_${index}`)
|
||||
|
||||
return {
|
||||
...column,
|
||||
id,
|
||||
accessorKey,
|
||||
header
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -240,26 +240,42 @@ onMounted(() => {
|
||||
</UCard>
|
||||
</UContainer>
|
||||
</div>
|
||||
<UDashboardLayout class="safearea" v-else>
|
||||
<UDashboardPanel :width="250" :resizable="{ min: 200, max: 300 }" collapsible>
|
||||
<UDashboardNavbar style="margin-top: env(safe-area-inset-top, 10px) !important;"
|
||||
:class="['!border-transparent']" :ui="{ left: 'flex-1' }">
|
||||
<template #left>
|
||||
<div class="safearea flex min-h-screen w-full flex-col overflow-hidden" v-else>
|
||||
<!-- <div
|
||||
class="border-b border-default bg-default px-3 py-2"
|
||||
style="padding-top: max(env(safe-area-inset-top, 0px), 0.5rem);"
|
||||
>
|
||||
<TenantDropdown class="min-w-0 w-full max-w-sm" />
|
||||
</div>-->
|
||||
|
||||
<UDashboardGroup class="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
|
||||
|
||||
<UDashboardSidebar
|
||||
id="sidebar"
|
||||
collapsible
|
||||
resizable
|
||||
:default-size="18"
|
||||
:min-size="14"
|
||||
:max-size="24"
|
||||
class="shrink-0 border-r border-default bg-default"
|
||||
>
|
||||
<template #header>
|
||||
<TenantDropdown class="w-full"/>
|
||||
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UDashboardSidebar id="sidebar">
|
||||
|
||||
<MainNav/>
|
||||
|
||||
<div class="flex-1"/>
|
||||
|
||||
<template #footer>
|
||||
<template #default="{ collapsed }">
|
||||
<MainNav :collapsed="collapsed" />
|
||||
</template>
|
||||
|
||||
<template #footer="{ collapsed }">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<UColorModeToggle :class="[collapsed ? 'mx-auto' : 'ml-3']"/>
|
||||
<UDashboardSidebarCollapse v-if="collapsed" class="mx-auto" />
|
||||
</div>
|
||||
|
||||
<UColorModeToggle class="ml-3"/>
|
||||
<LabelPrinterButton class="w-full"/>
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
@@ -272,10 +288,10 @@ onMounted(() => {
|
||||
:icon="item.icon"
|
||||
@click="item.click ? item.click() : null"
|
||||
>
|
||||
{{ item.label }}
|
||||
<span v-if="!collapsed">{{ item.label }}</span>
|
||||
|
||||
<template #trailing>
|
||||
<UBadge v-if="item.badge" color="primary" variant="solid" size="xs">
|
||||
<UBadge v-if="!collapsed && item.badge" color="primary" variant="solid" size="xs">
|
||||
{{ item.badge }}
|
||||
</UBadge>
|
||||
</template>
|
||||
@@ -287,19 +303,18 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
</UDashboardSidebar>
|
||||
</UDashboardPanel>
|
||||
|
||||
<UDashboardPage>
|
||||
<UDashboardPanel grow>
|
||||
|
||||
<div class="flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
<slot/>
|
||||
</UDashboardPanel>
|
||||
</UDashboardPage>
|
||||
|
||||
</div>
|
||||
</UDashboardGroup>
|
||||
|
||||
<HelpSlideover/>
|
||||
|
||||
<Calculator v-if="calculatorStore.isOpen"/>
|
||||
|
||||
</UDashboardLayout>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
||||
@@ -7,7 +7,7 @@ export default defineNuxtConfig({
|
||||
}
|
||||
},
|
||||
|
||||
modules: ['@vite-pwa/nuxt','@pinia/nuxt', '@nuxt/ui', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', '@nuxtjs/leaflet', '@vueuse/nuxt'],
|
||||
modules: ['@vite-pwa/nuxt','@pinia/nuxt', '@nuxt/ui-pro', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', '@nuxtjs/leaflet', '@vueuse/nuxt'],
|
||||
|
||||
ssr: false,
|
||||
|
||||
@@ -15,14 +15,12 @@ export default defineNuxtConfig({
|
||||
dirs: ['stores']
|
||||
},
|
||||
|
||||
extends: [
|
||||
'@nuxt/ui-pro'
|
||||
],
|
||||
|
||||
components: [{
|
||||
path: '~/components'
|
||||
}],
|
||||
|
||||
css: ['~/assets/css/main.css'],
|
||||
|
||||
build: {
|
||||
transpile: ['@vuepic/vue-datepicker','@tiptap/vue-3','@tiptap/extension-code-block-lowlight',
|
||||
'lowlight',]
|
||||
@@ -74,10 +72,6 @@ export default defineNuxtConfig({
|
||||
},
|
||||
},
|
||||
|
||||
ui: {
|
||||
icons: ['heroicons', 'mdi', 'simple-icons']
|
||||
},
|
||||
|
||||
colorMode: {
|
||||
preference: 'system'
|
||||
},
|
||||
|
||||
3506
frontend/package-lock.json
generated
3506
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,8 @@
|
||||
"@fullcalendar/vue3": "^6.1.10",
|
||||
"@iconify/json": "^2.2.171",
|
||||
"@mmote/niimbluelib": "^0.0.1-alpha.29",
|
||||
"@nuxt/ui-pro": "^1.6.0",
|
||||
"@nuxt/ui": "^3.3.7",
|
||||
"@nuxt/ui-pro": "^3.3.7",
|
||||
"@nuxtjs/fontaine": "^0.4.1",
|
||||
"@nuxtjs/google-fonts": "^3.1.0",
|
||||
"@nuxtjs/strapi": "^1.9.3",
|
||||
|
||||
@@ -253,7 +253,7 @@ onMounted(loadData)
|
||||
</template>
|
||||
|
||||
<UTable
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
:rows="periods"
|
||||
:loading="loading"
|
||||
:empty-state="{ icon: 'i-heroicons-calculator', label: 'Keine Daten für die USt-Auswertung vorhanden' }"
|
||||
|
||||
@@ -195,7 +195,7 @@ setupPage()
|
||||
</UDashboardToolbar>
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => router.push(`/accounts/show/${i.id}`)"
|
||||
|
||||
@@ -138,7 +138,7 @@ const saldo = computed(() => {
|
||||
<UTable
|
||||
v-if="statementallocations"
|
||||
:rows="renderedAllocations"
|
||||
:columns="[{key:'amount', label:'Betrag'},{key:'date', label:'Datum'},{key:'partner', label:'Partner'},{key:'description', label:'Beschreibung'}]"
|
||||
:columns="normalizeTableColumns([{key:'amount', label:'Betrag'},{key:'date', label:'Datum'},{key:'partner', label:'Partner'},{key:'description', label:'Beschreibung'}])"
|
||||
@select="(i) => selectAllocation(i)"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Buchungen anzuzeigen' }"
|
||||
>
|
||||
|
||||
@@ -601,7 +601,7 @@ onMounted(() => {
|
||||
</div>
|
||||
<PageLeaveGuard :when="isSyncing"/>
|
||||
|
||||
<UModal v-model="suggestionsModalOpen" :ui="{ width: 'sm:max-w-6xl' }">
|
||||
<UModal v-model:open="suggestionsModalOpen" :ui="{ width: 'sm:max-w-6xl' }">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
|
||||
@@ -2453,7 +2453,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
icon="i-heroicons-magnifying-glass"
|
||||
@click="showProductSelectionModal = true"
|
||||
/>
|
||||
<UModal v-model="showProductSelectionModal">
|
||||
<UModal v-model:open="showProductSelectionModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
Artikel Auswählen
|
||||
@@ -2472,11 +2472,11 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
</InputGroup>
|
||||
<UTable
|
||||
:rows="selectedProductcategorie !== 'not set' ? products.filter(i => i.productcategories.includes(selectedProductcategorie)) : products.filter(i => i.productcategories.length === 0)"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{key: 'name',label:'Name'},
|
||||
{key: 'manufacturer',label:'Hersteller'},
|
||||
{key: 'articleNumber',label:'Artikelnummer'},
|
||||
]"
|
||||
])"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Artikel anzuzeigen' }"
|
||||
@select=" (i) => {
|
||||
row.product = i.id
|
||||
@@ -2525,7 +2525,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
icon="i-heroicons-magnifying-glass"
|
||||
@click="showServiceSelectionModal = true"
|
||||
/>
|
||||
<UModal v-model="showServiceSelectionModal">
|
||||
<UModal v-model:open="showServiceSelectionModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
Leistung Auswählen
|
||||
@@ -2544,11 +2544,11 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
</InputGroup>
|
||||
<UTable
|
||||
:rows="selectedServicecategorie !== 'not set' ? services.filter(i => i.servicecategories.includes(selectedServicecategorie)) : services.filter(i => i.servicecategories.length === 0)"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{key: 'name',label:'Name'},
|
||||
{key: 'serviceNumber',label:'Leistungsnummer'},
|
||||
{key: 'sellingPrice',label:'Verkaufspreis'},
|
||||
]"
|
||||
])"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Leistungen anzuzeigen' }"
|
||||
@select=" (i) => {
|
||||
row.service = i.id
|
||||
@@ -2670,7 +2670,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
v-if="row.agriculture"
|
||||
@click="row.showEditDiesel = true"
|
||||
/>
|
||||
<UModal v-model="row.showEdit">
|
||||
<UModal v-model:open="row.showEdit">
|
||||
<UCard>
|
||||
<!-- <template #header>
|
||||
Zeile bearbeiten
|
||||
@@ -2842,7 +2842,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
||||
|
||||
|
||||
</UModal>
|
||||
<UModal v-model="row.showEditDiesel">
|
||||
<UModal v-model:open="row.showEditDiesel">
|
||||
<UCard>
|
||||
<template #header>
|
||||
Dieselverbrauch bearbeiten
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<template #item="{item}">
|
||||
<div style="height: 80vh; overflow-y: scroll">
|
||||
<UTable
|
||||
:columns="getColumnsForTab(item.key)"
|
||||
:columns="normalizeTableColumns(getColumnsForTab(item.key))"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Belege anzuzeigen' }"
|
||||
:rows="getRowsForTab(item.key)"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(row) => router.push(`/createDocument/edit/${row.id}`)"
|
||||
@@ -139,7 +139,7 @@
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<UModal v-model="showExecutionModal" :ui="{ width: 'sm:max-w-4xl' }">
|
||||
<UModal v-model:open="showExecutionModal" :ui="{ width: 'sm:max-w-4xl' }">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -205,7 +205,7 @@
|
||||
<UTable
|
||||
v-model="selectedExecutionRows"
|
||||
:rows="filteredExecutionList"
|
||||
:columns="executionColumns"
|
||||
:columns="normalizeTableColumns(executionColumns)"
|
||||
:ui="{ th: { base: 'whitespace-nowrap' } }"
|
||||
>
|
||||
<template #partner-data="{row}">
|
||||
@@ -247,7 +247,7 @@
|
||||
</UCard>
|
||||
</UModal>
|
||||
|
||||
<USlideover v-model="showExecutionsSlideover" :ui="{ width: 'w-screen max-w-md' }">
|
||||
<USlideover v-model:open="showExecutionsSlideover" :ui="{ width: 'w-screen max-w-md' }">
|
||||
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -111,14 +111,14 @@ const createExport = async () => {
|
||||
|
||||
<UTable
|
||||
:rows="filteredExports"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{ key: 'created_at', label: 'Erstellt am' },
|
||||
{ key: 'start_date', label: 'Start' },
|
||||
{ key: 'end_date', label: 'Ende' },
|
||||
{ key: 'valid_until', label: 'Gültig bis' },
|
||||
{ key: 'type', label: 'Typ' },
|
||||
{ key: 'download', label: 'Download' },
|
||||
]"
|
||||
])"
|
||||
>
|
||||
<template #created_at-data="{row}">
|
||||
{{dayjs(row.created_at).format("DD.MM.YYYY HH:mm")}}
|
||||
@@ -137,7 +137,7 @@ const createExport = async () => {
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<UModal v-model="showCreateExportModal">
|
||||
<UModal v-model:open="showCreateExportModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
Export erstellen
|
||||
|
||||
@@ -453,7 +453,7 @@ const syncdokubox = async () => {
|
||||
</div>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<UModal v-model="createFolderModalOpen">
|
||||
<UModal v-model:open="createFolderModalOpen">
|
||||
<UCard>
|
||||
<template #header><h3 class="font-bold">Ordner erstellen</h3></template>
|
||||
|
||||
@@ -494,7 +494,7 @@ const syncdokubox = async () => {
|
||||
</UCard>
|
||||
</UModal>
|
||||
|
||||
<UModal v-model="renameModalOpen">
|
||||
<UModal v-model:open="renameModalOpen">
|
||||
<UCard>
|
||||
<template #header><h3 class="font-bold">Umbenennen</h3></template>
|
||||
<UFormGroup label="Neuer Name">
|
||||
|
||||
@@ -251,7 +251,7 @@ const selectIncomingInvoice = (invoice) => {
|
||||
sort-mode="manual"
|
||||
@update:sort="setupPage"
|
||||
:rows="filteredRows.filter(i => item.label === 'Gebucht' ? i.state === 'Gebucht' : i.state !== 'Gebucht' )"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => selectIncomingInvoice(i) "
|
||||
|
||||
@@ -382,8 +382,7 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UDashboardPanelContent>
|
||||
<div v-if="visibleWidgets.length > 0" ref="gridElement" class="grid-stack dashboard-grid">
|
||||
<div v-if="visibleWidgets.length > 0" ref="gridElement" class="grid-stack dashboard-grid overflow-y-auto">
|
||||
<div
|
||||
v-for="widget in visibleWidgets"
|
||||
:key="widget.id"
|
||||
@@ -396,9 +395,9 @@ onBeforeUnmount(() => {
|
||||
:gs-min-w="widget.minW"
|
||||
:gs-min-h="widget.minH"
|
||||
>
|
||||
<div class="grid-stack-item-content dashboard-grid-item">
|
||||
<div class="dashboard-widget-card border border-gray-200 dark:border-gray-800">
|
||||
<div class="dashboard-widget-header border-b border-gray-200 dark:border-gray-800">
|
||||
<div class="grid-stack-item-content dashboard-grid-item">
|
||||
<div class="dashboard-widget-card border border-gray-200 dark:border-gray-800">
|
||||
<div class="dashboard-widget-header border-b border-gray-200 dark:border-gray-800">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div :class="['dashboard-widget-drag-handle font-semibold', isEditMode ? 'cursor-move' : 'cursor-default']">
|
||||
@@ -449,57 +448,58 @@ onBeforeUnmount(() => {
|
||||
Karte hinzufügen
|
||||
</UButton>
|
||||
</div>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<UModal v-model="manageCardsOpen">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="font-semibold">Dashboard-Karten</h2>
|
||||
<p class="text-sm">
|
||||
Karten ein- oder ausblenden und bei Bedarf auf das Standardlayout zurücksetzen.
|
||||
</p>
|
||||
<UModal v-model:open="manageCardsOpen">
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="font-semibold">Dashboard-Karten</h2>
|
||||
<p class="text-sm">
|
||||
Karten ein- oder ausblenden und bei Bedarf auf das Standardlayout zurücksetzen.
|
||||
</p>
|
||||
</div>
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-arrow-path" @click="resetDashboard">
|
||||
Zurücksetzen
|
||||
</UButton>
|
||||
</div>
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-arrow-path" @click="resetDashboard">
|
||||
Zurücksetzen
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
v-for="definition in DASHBOARD_WIDGETS"
|
||||
:key="definition.id"
|
||||
class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 dark:border-gray-800 px-4 py-3"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium">{{ definition.title }}</p>
|
||||
<p class="text-sm">{{ definition.description }}</p>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
v-for="definition in DASHBOARD_WIDGETS"
|
||||
:key="definition.id"
|
||||
class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 dark:border-gray-800 px-4 py-3"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium">{{ definition.title }}</p>
|
||||
<p class="text-sm">{{ definition.description }}</p>
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
v-if="getWidgetLayout(definition.id)?.visible"
|
||||
color="gray"
|
||||
variant="soft"
|
||||
icon="i-heroicons-minus"
|
||||
:disabled="visibleWidgets.length <= 1"
|
||||
@click="removeWidget(definition.id)"
|
||||
>
|
||||
Entfernen
|
||||
</UButton>
|
||||
<UButton
|
||||
v-else
|
||||
color="primary"
|
||||
variant="soft"
|
||||
icon="i-heroicons-plus"
|
||||
@click="addWidget(definition.id)"
|
||||
>
|
||||
Hinzufügen
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
v-if="getWidgetLayout(definition.id)?.visible"
|
||||
color="gray"
|
||||
variant="soft"
|
||||
icon="i-heroicons-minus"
|
||||
:disabled="visibleWidgets.length <= 1"
|
||||
@click="removeWidget(definition.id)"
|
||||
>
|
||||
Entfernen
|
||||
</UButton>
|
||||
<UButton
|
||||
v-else
|
||||
color="primary"
|
||||
variant="soft"
|
||||
icon="i-heroicons-plus"
|
||||
@click="addWidget(definition.id)"
|
||||
>
|
||||
Hinzufügen
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</UCard>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -453,7 +453,7 @@ onMounted(() => {
|
||||
/>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<UModal v-model="isAbsenceModalOpen">
|
||||
<UModal v-model:open="isAbsenceModalOpen">
|
||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -247,7 +247,7 @@ const addPhase = () => {
|
||||
{{ button.label }}
|
||||
</UButton>
|
||||
|
||||
<UModal v-model="openQuickActionModal">
|
||||
<UModal v-model:open="openQuickActionModal">
|
||||
<UCard>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||
|
||||
@@ -88,7 +88,7 @@ const filteredRows = computed(() => {
|
||||
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@select="(i) => router.push(`/projecttypes/show/${i.id}`) "
|
||||
|
||||
@@ -105,12 +105,14 @@ const userTableColumns = [
|
||||
{ key: "tenant_count", label: "Tenants" },
|
||||
{ key: "is_admin", label: "Admin" },
|
||||
]
|
||||
const normalizedUserTableColumns = normalizeTableColumns(userTableColumns)
|
||||
|
||||
const tenantTableColumns = [
|
||||
{ key: "name", label: "Tenant" },
|
||||
{ key: "short", label: "Kürzel" },
|
||||
{ key: "user_count", label: "Benutzer" },
|
||||
]
|
||||
const normalizedTenantTableColumns = normalizeTableColumns(tenantTableColumns)
|
||||
|
||||
const userTableRows = computed(() =>
|
||||
sortedUsers.value.map((user) => ({
|
||||
@@ -491,7 +493,7 @@ onMounted(async () => {
|
||||
<UTable
|
||||
v-if="!loading"
|
||||
:rows="userTableRows"
|
||||
:columns="userTableColumns"
|
||||
:columns="normalizedUserTableColumns"
|
||||
@select="selectUser"
|
||||
/>
|
||||
|
||||
@@ -673,7 +675,7 @@ onMounted(async () => {
|
||||
<UTable
|
||||
v-if="!loading"
|
||||
:rows="tenantTableRows"
|
||||
:columns="tenantTableColumns"
|
||||
:columns="normalizedTenantTableColumns"
|
||||
@select="selectTenant"
|
||||
/>
|
||||
|
||||
@@ -740,7 +742,7 @@ onMounted(async () => {
|
||||
</UTabs>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<UModal v-model="createUserModalOpen">
|
||||
<UModal v-model:open="createUserModalOpen">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="text-lg font-semibold">Benutzer anlegen</div>
|
||||
@@ -797,7 +799,7 @@ onMounted(async () => {
|
||||
</UCard>
|
||||
</UModal>
|
||||
|
||||
<UModal v-model="createTenantModalOpen">
|
||||
<UModal v-model:open="createTenantModalOpen">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="text-lg font-semibold">Tenant anlegen</div>
|
||||
|
||||
@@ -151,7 +151,7 @@ setupPage()
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UModal v-model="showReqData">
|
||||
<UModal v-model:open="showReqData">
|
||||
<UCard>
|
||||
<template #header>
|
||||
Verfügbare Bankkonten
|
||||
@@ -181,7 +181,7 @@ setupPage()
|
||||
|
||||
<UTable
|
||||
:rows="bankaccounts"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{
|
||||
key: 'expired',
|
||||
label: 'Aktiv'
|
||||
@@ -198,7 +198,7 @@ setupPage()
|
||||
key: 'balance',
|
||||
label: 'Saldo'
|
||||
},
|
||||
]"
|
||||
])"
|
||||
>
|
||||
<template #expired-data="{row}">
|
||||
<span v-if="row.expired" class="text-rose-600">Ausgelaufen</span>
|
||||
|
||||
@@ -34,10 +34,11 @@ const columns = computed(() => templateColumns.filter((column) => selectedColumn
|
||||
<UModal
|
||||
v-model="showEmailAddressModal"
|
||||
>
|
||||
<UCard>
|
||||
<template #header>
|
||||
E-Mail Adresse
|
||||
</template>
|
||||
<template #content>
|
||||
<UCard>
|
||||
<template #header>
|
||||
E-Mail Adresse
|
||||
</template>
|
||||
<!-- <UFormGroup
|
||||
label="E-Mail Adresse:"
|
||||
>
|
||||
@@ -57,14 +58,15 @@ const columns = computed(() => templateColumns.filter((column) => selectedColumn
|
||||
v-model="createEMailType"
|
||||
/>
|
||||
</UFormGroup>-->
|
||||
<template #footer>
|
||||
<UButton
|
||||
@click="createAccount"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
<template #footer>
|
||||
<UButton
|
||||
@click="createAccount"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
</UModal>
|
||||
<UDashboardNavbar title="E-Mail Konten">
|
||||
@@ -81,7 +83,7 @@ const columns = computed(() => templateColumns.filter((column) => selectedColumn
|
||||
</UDashboardNavbar>
|
||||
<UTable
|
||||
:rows="items"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
@select="(i) => navigateTo(`/settings/emailaccounts/edit/${i.id}`)"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
|
||||
@@ -164,13 +164,13 @@ const getDocLabel = (type) => {
|
||||
:loading="loading"
|
||||
v-model:expand="expand"
|
||||
:empty-state="{ icon: 'i-heroicons-document-text', label: 'Keine Textvorlagen gefunden' }"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{ key: 'name', label: 'Bezeichnung' },
|
||||
{ key: 'documentType', label: 'Verwendung' },
|
||||
{ key: 'pos', label: 'Position' },
|
||||
{ key: 'default', label: 'Standard' },
|
||||
{ key: 'actions', label: '' }
|
||||
]"
|
||||
])"
|
||||
>
|
||||
<template #name-data="{ row }">
|
||||
<span class="font-medium text-gray-900 dark:text-white">{{ row.name }}</span>
|
||||
@@ -236,7 +236,7 @@ const getDocLabel = (type) => {
|
||||
</UTable>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<UModal v-model="editTemplateModalOpen" :ui="{ width: 'sm:max-w-4xl' }">
|
||||
<UModal v-model:open="editTemplateModalOpen" :ui="{ width: 'sm:max-w-4xl' }">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</UDashboardNavbar>
|
||||
<UTable
|
||||
:rows="items"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
@select="(i) => navigateTo(`/staff/profiles/${i.id}`)"
|
||||
>
|
||||
|
||||
|
||||
@@ -296,13 +296,13 @@ await setupPage()
|
||||
v-if="workingTimeInfo"
|
||||
:rows="workingTimeInfo.spans"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Anwesenheiten' }"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{ key: 'status', label: 'Status' },
|
||||
{ key: 'startedAt', label: 'Start' },
|
||||
{ key: 'endedAt', label: 'Ende' },
|
||||
{ key: 'duration', label: 'Dauer' },
|
||||
{ key: 'type', label: 'Typ' }
|
||||
]"
|
||||
])"
|
||||
@select="(row) => router.push(`/workingtimes/edit/${row.sourceEventIds[0]}`)"
|
||||
>
|
||||
<template #status-data="{row}">
|
||||
|
||||
@@ -228,7 +228,7 @@ onMounted(async () => {
|
||||
<UCard v-if="view === 'list'" :ui="{ body: { padding: 'p-0 sm:p-0' } }">
|
||||
<UTable
|
||||
:rows="entries"
|
||||
:columns="[
|
||||
:columns="normalizeTableColumns([
|
||||
{ key: 'actions', label: 'Aktionen', class: 'w-32' },
|
||||
{ key: 'state', label: 'Status' },
|
||||
{ key: 'started_at', label: 'Start' },
|
||||
@@ -236,7 +236,7 @@ onMounted(async () => {
|
||||
{ key: 'duration_minutes', label: 'Dauer' },
|
||||
{ key: 'type', label: 'Typ' },
|
||||
{ key: 'description', label: 'Beschreibung' },
|
||||
]"
|
||||
])"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Zeiten anzuzeigen' }"
|
||||
>
|
||||
<template #state-data="{ row }">
|
||||
@@ -444,7 +444,7 @@ onMounted(async () => {
|
||||
:default-user-id="selectedUser"
|
||||
/>
|
||||
|
||||
<UModal v-model="showRejectModal">
|
||||
<UModal v-model:open="showRejectModal">
|
||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -388,7 +388,7 @@ const handleFilterChange = async (action,column) => {
|
||||
@update:sort="setupPage"
|
||||
v-if="dataType && columns && items.length > 0 && !loading"
|
||||
:rows="items"
|
||||
:columns="columns"
|
||||
:columns="normalizeTableColumns(columns)"
|
||||
class="w-full"
|
||||
style="height: 85dvh"
|
||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||
@@ -428,7 +428,7 @@ const handleFilterChange = async (action,column) => {
|
||||
<template #empty>
|
||||
Keine Einträge in der Spalte {{column.label}}
|
||||
</template>
|
||||
<template #default="{open}">
|
||||
<template #default="slotProps">
|
||||
<UButton
|
||||
:disabled="!columnsToFilter[column.key]?.length > 0"
|
||||
:variant="columnsToFilter[column.key]?.length !== itemsMeta.distinctValues?.[column.key]?.length ? 'outline' : 'solid'"
|
||||
@@ -436,7 +436,7 @@ const handleFilterChange = async (action,column) => {
|
||||
>
|
||||
<span class="truncate">{{ column.label }}</span>
|
||||
|
||||
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="[open && 'transform rotate-90']" />
|
||||
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="[slotProps?.open && 'transform rotate-90']" />
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ const addMessage = async () => {
|
||||
>
|
||||
+ Eintrag
|
||||
</UButton>
|
||||
<UModal v-model="showAddEntryModal">
|
||||
<UModal v-model:open="showAddEntryModal">
|
||||
<UCard>
|
||||
<template #header>
|
||||
Eintrag hinzufügen
|
||||
|
||||
@@ -75,7 +75,7 @@ const filteredRows = computed(() => {
|
||||
:rows="filteredRows"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine Tickets anzuzeigen` }"
|
||||
@select="(i) => router.push(`/support/${i.id}`)"
|
||||
:columns="[{key:'created_at',label:'Datum'}, ...profileStore.currentTenant === 5 ? [{key:'tenant',label:'Tenant'}] : [],{key:'status',label:'Status'},{key:'title',label:'Titel'},{key:'created_by',label:'Ersteller'},{key:'ticketmessages',label:'Nachrichten'}]"
|
||||
:columns="normalizeTableColumns([{key:'created_at',label:'Datum'}, ...profileStore.currentTenant === 5 ? [{key:'tenant',label:'Tenant'}] : [],{key:'status',label:'Status'},{key:'title',label:'Titel'},{key:'created_by',label:'Ersteller'},{key:'ticketmessages',label:'Nachrichten'}])"
|
||||
>
|
||||
<template #tenant-data="{ row }">
|
||||
{{row.tenant.name}}
|
||||
|
||||
@@ -88,6 +88,7 @@ const listColumns = [
|
||||
{ key: "customer", label: "Kunde" },
|
||||
{ key: "plant", label: "Objekt" }
|
||||
]
|
||||
const normalizedListColumns = normalizeTableColumns(listColumns)
|
||||
|
||||
function getEmptyTask() {
|
||||
return {
|
||||
@@ -458,7 +459,7 @@ onMounted(async () => {
|
||||
<UTable
|
||||
v-else-if="filteredTasks.length"
|
||||
:rows="filteredTasks"
|
||||
:columns="listColumns"
|
||||
:columns="normalizedListColumns"
|
||||
@select="(task) => openTaskViaRoute(task)"
|
||||
>
|
||||
<template #actions-data="{ row }">
|
||||
@@ -497,7 +498,7 @@ onMounted(async () => {
|
||||
/>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<UModal v-model="isModalOpen" :prevent-close="saving || deleting">
|
||||
<UModal v-model:open="isModalOpen" :prevent-close="saving || deleting">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<UModal v-model="isModalOpen">
|
||||
<UModal v-model:open="isModalOpen">
|
||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user