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