Zwischenstand
This commit is contained in:
@@ -47,14 +47,16 @@ useSeoMeta({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="safearea">
|
<UApp>
|
||||||
<NuxtLayout>
|
<div class="safearea">
|
||||||
<NuxtPage/>
|
<NuxtLayout>
|
||||||
</NuxtLayout>
|
<NuxtPage/>
|
||||||
<UNotifications/>
|
</NuxtLayout>
|
||||||
<USlideovers />
|
<UNotifications/>
|
||||||
<UModals/>
|
<USlideovers />
|
||||||
</div>
|
<UModals/>
|
||||||
|
</div>
|
||||||
|
</UApp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -136,4 +138,4 @@ useSeoMeta({
|
|||||||
.scroll {
|
.scroll {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -39,33 +39,35 @@ const emitConfirm = () => {
|
|||||||
Archivieren
|
Archivieren
|
||||||
</UButton>
|
</UButton>
|
||||||
<UModal v-model:open="showModal">
|
<UModal v-model:open="showModal">
|
||||||
<UCard>
|
<template #content>
|
||||||
<template #header>
|
<UCard>
|
||||||
<span class="text-md font-bold">Archivieren bestätigen</span>
|
<template #header>
|
||||||
</template>
|
<span class="text-md font-bold">Archivieren bestätigen</span>
|
||||||
Möchten Sie diese/-s/-n {{dataType.labelSingle}} wirklich archivieren?
|
</template>
|
||||||
|
Möchten Sie diese/-s/-n {{dataType.labelSingle}} wirklich archivieren?
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<UButtonGroup>
|
<UButtonGroup>
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@click="showModal = false"
|
@click="showModal = false"
|
||||||
>
|
>
|
||||||
Abbrechen
|
Abbrechen
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
@click="emitConfirm"
|
@click="emitConfirm"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
color="rose"
|
color="error"
|
||||||
>
|
>
|
||||||
Archivieren
|
Archivieren
|
||||||
</UButton>
|
</UButton>
|
||||||
</UButtonGroup>
|
</UButtonGroup>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UCard>
|
</UCard>
|
||||||
|
</template>
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const assignByIban = async () => {
|
|||||||
|
|
||||||
const match = accounts.value.find((a) => normalizeIban(a.iban) === search)
|
const match = accounts.value.find((a) => normalizeIban(a.iban) === search)
|
||||||
if (!match) {
|
if (!match) {
|
||||||
toast.add({ title: "Kein Bankkonto mit dieser IBAN gefunden.", color: "rose" })
|
toast.add({ title: "Kein Bankkonto mit dieser IBAN gefunden.", color: "error" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ const removeAssigned = (id) => {
|
|||||||
|
|
||||||
const createAndAssign = async () => {
|
const createAndAssign = async () => {
|
||||||
if (!createPayload.value.iban || !createPayload.value.bic || !createPayload.value.bankName) {
|
if (!createPayload.value.iban || !createPayload.value.bic || !createPayload.value.bankName) {
|
||||||
toast.add({ title: "IBAN, BIC und Bankinstitut sind Pflichtfelder.", color: "rose" })
|
toast.add({ title: "IBAN, BIC und Bankinstitut sind Pflichtfelder.", color: "error" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,42 +141,44 @@ loadAccounts()
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UModal v-model:open="showCreate">
|
<UModal v-model:open="showCreate">
|
||||||
<UCard>
|
<template #content>
|
||||||
<template #header>Neue Bankverbindung erstellen</template>
|
<UCard>
|
||||||
<div class="space-y-3">
|
<template #header>Neue Bankverbindung erstellen</template>
|
||||||
<UFormGroup label="IBAN">
|
<div class="space-y-3">
|
||||||
<InputGroup>
|
<UFormField label="IBAN">
|
||||||
<UInput
|
<InputGroup>
|
||||||
v-model="createPayload.iban"
|
<UInput
|
||||||
@blur="resolveCreatePayloadFromIban"
|
v-model="createPayload.iban"
|
||||||
@keydown.enter.prevent="resolveCreatePayloadFromIban"
|
@blur="resolveCreatePayloadFromIban"
|
||||||
/>
|
@keydown.enter.prevent="resolveCreatePayloadFromIban"
|
||||||
<UButton
|
/>
|
||||||
color="gray"
|
<UButton
|
||||||
variant="outline"
|
color="gray"
|
||||||
:loading="resolvingIban"
|
variant="outline"
|
||||||
@click="resolveCreatePayloadFromIban"
|
:loading="resolvingIban"
|
||||||
>
|
@click="resolveCreatePayloadFromIban"
|
||||||
Ermitteln
|
>
|
||||||
</UButton>
|
Ermitteln
|
||||||
</InputGroup>
|
</UButton>
|
||||||
</UFormGroup>
|
</InputGroup>
|
||||||
<UFormGroup label="BIC">
|
</UFormField>
|
||||||
<UInput v-model="createPayload.bic" />
|
<UFormField label="BIC">
|
||||||
</UFormGroup>
|
<UInput v-model="createPayload.bic" />
|
||||||
<UFormGroup label="Bankinstitut">
|
</UFormField>
|
||||||
<UInput v-model="createPayload.bankName" />
|
<UFormField label="Bankinstitut">
|
||||||
</UFormGroup>
|
<UInput v-model="createPayload.bankName" />
|
||||||
<UFormGroup label="Beschreibung (optional)">
|
</UFormField>
|
||||||
<UInput v-model="createPayload.description" />
|
<UFormField label="Beschreibung (optional)">
|
||||||
</UFormGroup>
|
<UInput v-model="createPayload.description" />
|
||||||
</div>
|
</UFormField>
|
||||||
<template #footer>
|
|
||||||
<div class="flex justify-end gap-2">
|
|
||||||
<UButton color="gray" variant="outline" @click="showCreate = false">Abbrechen</UButton>
|
|
||||||
<UButton @click="createAndAssign">Erstellen und zuweisen</UButton>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<template #footer>
|
||||||
</UCard>
|
<div class="flex justify-end gap-2">
|
||||||
|
<UButton color="gray" variant="outline" @click="showCreate = false">Abbrechen</UButton>
|
||||||
|
<UButton @click="createAndAssign">Erstellen und zuweisen</UButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</template>
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const emitConfirm = () => {
|
|||||||
<UButton
|
<UButton
|
||||||
@click="emitConfirm"
|
@click="emitConfirm"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
color="rose"
|
color="error"
|
||||||
>
|
>
|
||||||
Archivieren
|
Archivieren
|
||||||
</UButton>
|
</UButton>
|
||||||
|
|||||||
@@ -51,8 +51,8 @@
|
|||||||
<div class="grid grid-cols-4 gap-2">
|
<div class="grid grid-cols-4 gap-2">
|
||||||
<UTooltip text="Brutto (+19%)"><UButton color="green" variant="soft" block size="xs" @click="applyTax(19)">+19%</UButton></UTooltip>
|
<UTooltip text="Brutto (+19%)"><UButton color="green" variant="soft" block size="xs" @click="applyTax(19)">+19%</UButton></UTooltip>
|
||||||
<UTooltip text="Brutto (+7%)"><UButton color="green" variant="soft" block size="xs" @click="applyTax(7)">+7%</UButton></UTooltip>
|
<UTooltip text="Brutto (+7%)"><UButton color="green" variant="soft" block size="xs" @click="applyTax(7)">+7%</UButton></UTooltip>
|
||||||
<UTooltip text="Netto (-19%)"><UButton color="rose" variant="soft" block size="xs" @click="removeTax(19)">-19%</UButton></UTooltip>
|
<UTooltip text="Netto (-19%)"><UButton color="error" variant="soft" block size="xs" @click="removeTax(19)">-19%</UButton></UTooltip>
|
||||||
<UTooltip text="Netto (-7%)"><UButton color="rose" variant="soft" block size="xs" @click="removeTax(7)">-7%</UButton></UTooltip>
|
<UTooltip text="Netto (-7%)"><UButton color="error" variant="soft" block size="xs" @click="removeTax(7)">-7%</UButton></UTooltip>
|
||||||
|
|
||||||
<UTooltip text="Löschen"><UButton color="gray" variant="ghost" block @click="clear">C</UButton></UTooltip>
|
<UTooltip text="Löschen"><UButton color="gray" variant="ghost" block @click="clear">C</UButton></UTooltip>
|
||||||
<UTooltip text="Speicher +"><UButton color="gray" variant="ghost" block @click="addToSum">M+</UButton></UTooltip>
|
<UTooltip text="Speicher +"><UButton color="gray" variant="ghost" block @click="addToSum">M+</UButton></UTooltip>
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ const moveFile = async () => {
|
|||||||
<div class="w-2/3 p-5" v-if="!false">
|
<div class="w-2/3 p-5" v-if="!false">
|
||||||
<UButtonGroup>
|
<UButtonGroup>
|
||||||
<ArchiveButton
|
<ArchiveButton
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
type="files"
|
type="files"
|
||||||
@confirmed="archiveDocument"
|
@confirmed="archiveDocument"
|
||||||
@@ -203,7 +203,7 @@ const moveFile = async () => {
|
|||||||
</UButton>
|
</UButton>
|
||||||
</UButtonGroup>
|
</UButtonGroup>
|
||||||
|
|
||||||
<UDivider>Zuweisungen</UDivider>
|
<USeparator label="Zuweisungen"/>
|
||||||
<table class="w-full">
|
<table class="w-full">
|
||||||
<tr v-if="props.documentData.project">
|
<tr v-if="props.documentData.project">
|
||||||
<td>Projekt</td>
|
<td>Projekt</td>
|
||||||
@@ -279,7 +279,7 @@ const moveFile = async () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<UDivider class="my-3">Datei zuweisen</UDivider>
|
<USeparator class="my-3" label="Datei zuweisen"/>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Resource auswählen"
|
label="Resource auswählen"
|
||||||
@@ -308,7 +308,7 @@ const moveFile = async () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<UDivider class="my-5">Datei verschieben</UDivider>
|
<USeparator class="my-5" label="Datei verschieben"/>
|
||||||
|
|
||||||
<InputGroup class="w-full">
|
<InputGroup class="w-full">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
@@ -325,7 +325,7 @@ const moveFile = async () => {
|
|||||||
>Verschieben</UButton>
|
>Verschieben</UButton>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
<UDivider class="my-5">Dateityp</UDivider>
|
<USeparator class="my-5" label="Dateityp"/>
|
||||||
|
|
||||||
<InputGroup class="w-full">
|
<InputGroup class="w-full">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
@@ -337,7 +337,7 @@ const moveFile = async () => {
|
|||||||
@change="updateDocument"
|
@change="updateDocument"
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<UDivider class="my-5">Dokumentenbox</UDivider>
|
<USeparator class="my-5" label="Dokumentenbox" />
|
||||||
|
|
||||||
<InputGroup class="w-full">
|
<InputGroup class="w-full">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
|
|||||||
@@ -211,6 +211,22 @@ const contentChanged = (content, datapoint) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSelectItems = (datapoint) => {
|
||||||
|
return datapoint.selectManualOptions || loadedOptions.value[datapoint.selectDataType] || []
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSelectValueKey = (datapoint) => {
|
||||||
|
return datapoint.selectValueAttribute || 'id'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSelectLabelKey = (datapoint) => {
|
||||||
|
return datapoint.selectOptionAttribute || 'label'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSelectSearchInput = (datapoint) => {
|
||||||
|
return datapoint.selectSearchAttributes ? { placeholder: 'Suche...' } : false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const createItem = async () => {
|
const createItem = async () => {
|
||||||
let ret = null
|
let ret = null
|
||||||
@@ -264,7 +280,7 @@ const updateItem = async () => {
|
|||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<ArchiveButton
|
<ArchiveButton
|
||||||
color="rose"
|
color="error"
|
||||||
v-if="platform !== 'mobile'"
|
v-if="platform !== 'mobile'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:type="type"
|
:type="type"
|
||||||
@@ -336,12 +352,12 @@ const updateItem = async () => {
|
|||||||
v-for="(columnName,index) in dataType.inputColumns"
|
v-for="(columnName,index) in dataType.inputColumns"
|
||||||
:class="platform === 'mobile' ? ['w-full'] : [`w-1/${dataType.inputColumns.length}`, ... index < dataType.inputColumns.length -1 ? ['mr-5'] : []]"
|
:class="platform === 'mobile' ? ['w-full'] : [`w-1/${dataType.inputColumns.length}`, ... index < dataType.inputColumns.length -1 ? ['mr-5'] : []]"
|
||||||
>
|
>
|
||||||
<UDivider>{{ columnName }}</UDivider>
|
<USeparator :label="columnName"/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="datapoint in dataType.templateColumns.filter(i => i.inputType && i.inputColumn === columnName)"
|
v-for="datapoint in dataType.templateColumns.filter(i => i.inputType && i.inputColumn === columnName)"
|
||||||
>
|
>
|
||||||
<UFormGroup
|
<UFormField
|
||||||
v-if="(datapoint.showFunction ? datapoint.showFunction(item) : true)"
|
v-if="(datapoint.showFunction ? datapoint.showFunction(item) : true)"
|
||||||
:label="datapoint.label"
|
:label="datapoint.label"
|
||||||
>
|
>
|
||||||
@@ -354,7 +370,7 @@ const updateItem = async () => {
|
|||||||
</template>
|
</template>
|
||||||
<InputGroup class="w-full" v-if="datapoint.key.includes('.')">
|
<InputGroup class="w-full" v-if="datapoint.key.includes('.')">
|
||||||
<UInput
|
<UInput
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-if="['text','number'].includes(datapoint.inputType)"
|
v-if="['text','number'].includes(datapoint.inputType)"
|
||||||
@@ -367,25 +383,25 @@ const updateItem = async () => {
|
|||||||
<span class="text-gray-500 dark:text-gray-400 text-xs">{{ datapoint.inputTrailing }}</span>
|
<span class="text-gray-500 dark:text-gray-400 text-xs">{{ datapoint.inputTrailing }}</span>
|
||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
<UToggle
|
<USwitch
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'primary'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'primary'"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'bool'"
|
v-else-if="datapoint.inputType === 'bool'"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'select'"
|
v-else-if="datapoint.inputType === 'select'"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
:option-attribute="datapoint.selectOptionAttribute"
|
:items="getSelectItems(datapoint)"
|
||||||
:value-attribute="datapoint.selectValueAttribute || 'id'"
|
:label-key="getSelectLabelKey(datapoint)"
|
||||||
:options="datapoint.selectManualOptions || loadedOptions[datapoint.selectDataType]"
|
:value-key="getSelectValueKey(datapoint)"
|
||||||
:searchable="datapoint.selectSearchAttributes"
|
:search-input="getSelectSearchInput(datapoint)"
|
||||||
:search-attributes="datapoint.selectSearchAttributes"
|
:filter-fields="datapoint.selectSearchAttributes"
|
||||||
:multiple="datapoint.selectMultiple"
|
:multiple="datapoint.selectMultiple"
|
||||||
>
|
>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
@@ -393,7 +409,7 @@ const updateItem = async () => {
|
|||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UTextarea
|
<UTextarea
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'textarea'"
|
v-else-if="datapoint.inputType === 'textarea'"
|
||||||
@@ -401,9 +417,9 @@ const updateItem = async () => {
|
|||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
rows="4"
|
rows="4"
|
||||||
/>
|
/>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'date'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'date'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -411,17 +427,17 @@ const updateItem = async () => {
|
|||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" @close="close"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'datetime'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'datetime'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -429,10 +445,10 @@ const updateItem = async () => {
|
|||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" @close="close"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
mode="datetime"
|
mode="datetime"
|
||||||
/>
|
/>
|
||||||
@@ -460,7 +476,7 @@ const updateItem = async () => {
|
|||||||
<InputGroup class="w-full" v-else>
|
<InputGroup class="w-full" v-else>
|
||||||
<UInput
|
<UInput
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-if="['text','number'].includes(datapoint.inputType)"
|
v-if="['text','number'].includes(datapoint.inputType)"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
@@ -472,34 +488,33 @@ const updateItem = async () => {
|
|||||||
{{ datapoint.inputTrailing }}
|
{{ datapoint.inputTrailing }}
|
||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
<UToggle
|
<USwitch
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'primary'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'primary'"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'bool'"
|
v-else-if="datapoint.inputType === 'bool'"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'select'"
|
v-else-if="datapoint.inputType === 'select'"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
:option-attribute="datapoint.selectOptionAttribute"
|
:items="getSelectItems(datapoint)"
|
||||||
:value-attribute="datapoint.selectValueAttribute || 'id'"
|
:label-key="getSelectLabelKey(datapoint)"
|
||||||
:options="datapoint.selectManualOptions || loadedOptions[datapoint.selectDataType]"
|
:value-key="getSelectValueKey(datapoint)"
|
||||||
:searchable="datapoint.selectSearchAttributes"
|
:search-input="getSelectSearchInput(datapoint)"
|
||||||
:search-attributes="datapoint.selectSearchAttributes"
|
:filter-fields="datapoint.selectSearchAttributes"
|
||||||
:multiple="datapoint.selectMultiple"
|
:multiple="datapoint.selectMultiple"
|
||||||
searchable-placeholder="Suche..."
|
|
||||||
>
|
>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
Keine Optionen verfügbar
|
Keine Optionen verfügbar
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UTextarea
|
<UTextarea
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'textarea'"
|
v-else-if="datapoint.inputType === 'textarea'"
|
||||||
@@ -507,37 +522,36 @@ const updateItem = async () => {
|
|||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
rows="4"
|
rows="4"
|
||||||
/>
|
/>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'date'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'date'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key]" @close="close"
|
v-model="item[datapoint.key]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'datetime'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'datetime'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
@close="close"
|
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
mode="datetime"
|
mode="datetime"
|
||||||
/>
|
/>
|
||||||
@@ -572,11 +586,11 @@ const updateItem = async () => {
|
|||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UFormGroup
|
<UFormField
|
||||||
v-for="datapoint in dataType.templateColumns.filter(i => i.inputType && !i.inputColumn)"
|
v-for="datapoint in dataType.templateColumns.filter(i => i.inputType && !i.inputColumn)"
|
||||||
:label="datapoint.label"
|
:label="datapoint.label"
|
||||||
>
|
>
|
||||||
@@ -589,7 +603,7 @@ const updateItem = async () => {
|
|||||||
</template>
|
</template>
|
||||||
<InputGroup class="w-full" v-if="datapoint.key.includes('.')">
|
<InputGroup class="w-full" v-if="datapoint.key.includes('.')">
|
||||||
<UInput
|
<UInput
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-if="['text','number'].includes(datapoint.inputType)"
|
v-if="['text','number'].includes(datapoint.inputType)"
|
||||||
@@ -602,25 +616,25 @@ const updateItem = async () => {
|
|||||||
<span class="text-gray-500 dark:text-gray-400 text-xs">{{ datapoint.inputTrailing }}</span>
|
<span class="text-gray-500 dark:text-gray-400 text-xs">{{ datapoint.inputTrailing }}</span>
|
||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
<UToggle
|
<USwitch
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'primary'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'primary'"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'bool'"
|
v-else-if="datapoint.inputType === 'bool'"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'select'"
|
v-else-if="datapoint.inputType === 'select'"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
:option-attribute="datapoint.selectOptionAttribute"
|
:items="getSelectItems(datapoint)"
|
||||||
:value-attribute="datapoint.selectValueAttribute || 'id'"
|
:label-key="getSelectLabelKey(datapoint)"
|
||||||
:options="datapoint.selectManualOptions || loadedOptions[datapoint.selectDataType]"
|
:value-key="getSelectValueKey(datapoint)"
|
||||||
:searchable="datapoint.selectSearchAttributes"
|
:search-input="getSelectSearchInput(datapoint)"
|
||||||
:search-attributes="datapoint.selectSearchAttributes"
|
:filter-fields="datapoint.selectSearchAttributes"
|
||||||
:multiple="datapoint.selectMultiple"
|
:multiple="datapoint.selectMultiple"
|
||||||
>
|
>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
@@ -628,7 +642,7 @@ const updateItem = async () => {
|
|||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UTextarea
|
<UTextarea
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'textarea'"
|
v-else-if="datapoint.inputType === 'textarea'"
|
||||||
@@ -636,9 +650,9 @@ const updateItem = async () => {
|
|||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
rows="4"
|
rows="4"
|
||||||
/>
|
/>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'date'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'date'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -646,17 +660,17 @@ const updateItem = async () => {
|
|||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" @close="close"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'datetime'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'datetime'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
:label="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] ? dayjs(item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -664,10 +678,10 @@ const updateItem = async () => {
|
|||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" @close="close"
|
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
mode="datetime"
|
mode="datetime"
|
||||||
/>
|
/>
|
||||||
@@ -695,7 +709,7 @@ const updateItem = async () => {
|
|||||||
<InputGroup class="w-full" v-else>
|
<InputGroup class="w-full" v-else>
|
||||||
<UInput
|
<UInput
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-if="['text','number'].includes(datapoint.inputType)"
|
v-if="['text','number'].includes(datapoint.inputType)"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
@@ -707,34 +721,33 @@ const updateItem = async () => {
|
|||||||
{{ datapoint.inputTrailing }}
|
{{ datapoint.inputTrailing }}
|
||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
<UToggle
|
<USwitch
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'primary'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'primary'"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'bool'"
|
v-else-if="datapoint.inputType === 'bool'"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'select'"
|
v-else-if="datapoint.inputType === 'select'"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
:option-attribute="datapoint.selectOptionAttribute"
|
:items="getSelectItems(datapoint)"
|
||||||
:value-attribute="datapoint.selectValueAttribute || 'id'"
|
:label-key="getSelectLabelKey(datapoint)"
|
||||||
:options="datapoint.selectManualOptions || loadedOptions[datapoint.selectDataType]"
|
:value-key="getSelectValueKey(datapoint)"
|
||||||
:searchable="datapoint.selectSearchAttributes"
|
:search-input="getSelectSearchInput(datapoint)"
|
||||||
:search-attributes="datapoint.selectSearchAttributes"
|
:filter-fields="datapoint.selectSearchAttributes"
|
||||||
:multiple="datapoint.selectMultiple"
|
:multiple="datapoint.selectMultiple"
|
||||||
searchable-placeholder="Suche..."
|
|
||||||
>
|
>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
Keine Optionen verfügbar
|
Keine Optionen verfügbar
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UTextarea
|
<UTextarea
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-else-if="datapoint.inputType === 'textarea'"
|
v-else-if="datapoint.inputType === 'textarea'"
|
||||||
@@ -742,37 +755,36 @@ const updateItem = async () => {
|
|||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
rows="4"
|
rows="4"
|
||||||
/>
|
/>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'date'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'date'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YYYY') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key]" @close="close"
|
v-model="item[datapoint.key]"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }" v-else-if="datapoint.inputType === 'datetime'">
|
<UPopover :content="{ side: 'bottom', align: 'start' }" v-else-if="datapoint.inputType === 'datetime'">
|
||||||
<UButton
|
<UButton
|
||||||
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'rose') : 'white'"
|
:color="datapoint.required ? (item[datapoint.key] ? 'primary' : 'error') : 'white'"
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
:label="item[datapoint.key] ? dayjs(item[datapoint.key]).format('DD.MM.YY HH:mm') : 'Datum auswählen'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker
|
<LazyDatePicker
|
||||||
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
@change="datapoint.inputChangeFunction ? datapoint.inputChangeFunction(item,loadedOptions) : null"
|
||||||
v-model="item[datapoint.key]"
|
v-model="item[datapoint.key]"
|
||||||
@close="close"
|
|
||||||
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
|
||||||
mode="datetime"
|
mode="datetime"
|
||||||
/>
|
/>
|
||||||
@@ -807,7 +819,7 @@ const updateItem = async () => {
|
|||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
</UForm>
|
</UForm>
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -110,12 +110,6 @@ const filteredRows = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
|
||||||
:label="`+ ${dataType.labelSingle}`"
|
|
||||||
variant="outline"
|
|
||||||
v-if="platform === 'mobile'"
|
|
||||||
@click="router.push(`/standardEntity/${type}/create`)"
|
|
||||||
/>
|
|
||||||
<UDashboardNavbar :title="dataType.label" :badge="filteredRows.length">
|
<UDashboardNavbar :title="dataType.label" :badge="filteredRows.length">
|
||||||
<template #toggle>
|
<template #toggle>
|
||||||
<div v-if="platform === 'mobile'"></div>
|
<div v-if="platform === 'mobile'"></div>
|
||||||
@@ -138,7 +132,7 @@ const filteredRows = computed(() => {
|
|||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="clearSearchString()"
|
@click="clearSearchString()"
|
||||||
v-if="searchString.length > 0"
|
v-if="searchString.length > 0"
|
||||||
/>
|
/>
|
||||||
@@ -169,7 +163,7 @@ const filteredRows = computed(() => {
|
|||||||
:ui-menu="{ width: 'min-w-max' }"
|
:ui-menu="{ width: 'min-w-max' }"
|
||||||
@change="tempStore.modifyColumns(type,selectedColumns)"
|
@change="tempStore.modifyColumns(type,selectedColumns)"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template>
|
||||||
Spalten
|
Spalten
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ const selectItem = (item) => {
|
|||||||
</template>
|
</template>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<UTable
|
<UTable
|
||||||
:rows="props.item.createddocuments.filter(i => !i.archived)"
|
:data="props.item.createddocuments.filter(i => !i.archived)"
|
||||||
:columns="normalizeTableColumns(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' }"
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const renderDatapointValue = (datapoint) => {
|
|||||||
</template>
|
</template>
|
||||||
<UAlert
|
<UAlert
|
||||||
v-if="props.item.archived"
|
v-if="props.item.archived"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:title="`${dataType.labelSingle} archiviert`"
|
:title="`${dataType.labelSingle} archiviert`"
|
||||||
icon="i-heroicons-archive-box"
|
icon="i-heroicons-archive-box"
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const renderedAllocations = computed(() => {
|
|||||||
<UCard class="mt-5">
|
<UCard class="mt-5">
|
||||||
<UTable
|
<UTable
|
||||||
v-if="props.item.statementallocations"
|
v-if="props.item.statementallocations"
|
||||||
:rows="renderedAllocations"
|
:data="renderedAllocations"
|
||||||
:columns="normalizeTableColumns([{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' }"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const columns = [
|
|||||||
<UTable
|
<UTable
|
||||||
class="mt-3"
|
class="mt-3"
|
||||||
:columns="normalizeTableColumns(columns)"
|
:columns="normalizeTableColumns(columns)"
|
||||||
:rows="props.item.times"
|
:data="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' }"
|
||||||
>
|
>
|
||||||
<template #state-data="{row}">
|
<template #state-data="{row}">
|
||||||
|
|||||||
@@ -58,77 +58,101 @@
|
|||||||
const dataType = dataStore.dataTypes[props.type]
|
const dataType = dataStore.dataTypes[props.type]
|
||||||
|
|
||||||
const selectedItem = ref(0)
|
const selectedItem = ref(0)
|
||||||
const sort = ref({
|
const sorting = ref([{
|
||||||
column: dataType.sortColumn || "date",
|
id: dataType.sortColumn || "date",
|
||||||
direction: 'desc'
|
desc: true
|
||||||
})
|
}])
|
||||||
const normalizedColumns = computed(() => normalizeTableColumns(props.columns))
|
const normalizedColumns = computed(() => normalizeTableColumns(props.columns))
|
||||||
|
const truncateValue = (value, maxLength) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '\u00A0'
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringValue = String(value)
|
||||||
|
if (!maxLength || stringValue.length <= maxLength) {
|
||||||
|
return stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${stringValue.substring(0, maxLength)}...`
|
||||||
|
}
|
||||||
|
const handleSortChange = (value) => {
|
||||||
|
const nextSort = Array.isArray(value) ? value[0] : undefined
|
||||||
|
|
||||||
|
if (!nextSort?.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('sort', {
|
||||||
|
sort_column: nextSort.id,
|
||||||
|
sort_direction: nextSort.desc ? 'desc' : 'asc'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleSelect = (row) => {
|
||||||
|
router.push(getShowRoute(props.type, row.original.id))
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UTable
|
<UTable
|
||||||
:loading="props.loading"
|
:loading="props.loading"
|
||||||
:loading-state="{ icon: 'i-heroicons-arrow-path-20-solid', label: 'Loading...' }"
|
|
||||||
sort-mode="manual"
|
sort-mode="manual"
|
||||||
v-model:sort="sort"
|
v-model:sorting="sorting"
|
||||||
@update:sort="emit('sort',{sort_column: sort.column, sort_direction: sort.direction})"
|
@update:sorting="handleSortChange"
|
||||||
v-if="dataType && columns"
|
v-if="dataType && columns"
|
||||||
:rows="props.rows"
|
:data="props.rows"
|
||||||
:columns="normalizedColumns"
|
: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))"
|
:on-select="handleSelect"
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine ${dataType.label} anzuzeigen` }"
|
:empty="`Keine ${dataType.label} anzuzeigen`"
|
||||||
>
|
>
|
||||||
<!-- <template
|
<template #name-cell="{ row }">
|
||||||
v-for="column in dataType.templateColumns.filter(i => !i.disabledInTable)"
|
|
||||||
v-slot:[`${column.key}-header`]="{row}">
|
|
||||||
<span class="text-nowrap">{{column.label}}</span>
|
|
||||||
</template>-->
|
|
||||||
<template #name-data="{row}">
|
|
||||||
<span
|
<span
|
||||||
v-if="row.id === props.rows[selectedItem].id"
|
v-if="row.original.id === props.rows[selectedItem]?.id"
|
||||||
class="text-primary-500 font-bold">
|
class="block truncate text-primary-500 font-bold"
|
||||||
<UTooltip
|
>
|
||||||
:text="row.name"
|
<UTooltip :text="row.original.name">
|
||||||
>
|
<span class="block truncate">
|
||||||
{{dataType.templateColumns.find(i => i.key === "name").maxLength ? (row.name.length > dataType.templateColumns.find(i => i.key === "name").maxLength ? `${row.name.substring(0,dataType.templateColumns.find(i => i.key === "name").maxLength)}...` : row.name ) : row.name}}
|
{{ truncateValue(row.original.name, dataType.templateColumns.find(i => i.key === "name")?.maxLength) }}
|
||||||
|
</span>
|
||||||
</UTooltip> </span>
|
</UTooltip> </span>
|
||||||
<span v-else>
|
<span v-else class="block truncate">
|
||||||
<UTooltip
|
<UTooltip :text="row.original.name">
|
||||||
:text="row.name"
|
<span class="block truncate">
|
||||||
>
|
{{ truncateValue(row.original.name, dataType.templateColumns.find(i => i.key === "name")?.maxLength) }}
|
||||||
{{dataType.templateColumns.find(i => i.key === "name").maxLength ? (row.name.length > dataType.templateColumns.find(i => i.key === "name").maxLength ? `${row.name.substring(0,dataType.templateColumns.find(i => i.key === "name").maxLength)}...` : row.name ) : row.name}}
|
</span>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #fullName-data="{row}">
|
<template #fullName-cell="{ row }">
|
||||||
<span
|
<span
|
||||||
v-if="row.id === props.rows[selectedItem].id"
|
v-if="row.original.id === props.rows[selectedItem]?.id"
|
||||||
class="text-primary-500 font-bold">{{row.fullName}}
|
class="block truncate text-primary-500 font-bold">{{ row.original.fullName }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else class="block truncate">
|
||||||
{{row.fullName}}
|
{{ row.original.fullName }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #licensePlate-data="{row}">
|
<template #licensePlate-cell="{ row }">
|
||||||
<span
|
<span
|
||||||
v-if="row.id === props.rows[selectedItem].id"
|
v-if="row.original.id === props.rows[selectedItem]?.id"
|
||||||
class="text-primary-500 font-bold">{{row.licensePlate}}
|
class="block truncate text-primary-500 font-bold">{{ row.original.licensePlate }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else class="block truncate">
|
||||||
{{row.licensePlate}}
|
{{ row.original.licensePlate }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
|
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
|
||||||
v-slot:[`${column.key}-data`]="{row}">
|
v-slot:[`${column.key}-cell`]="{ row }">
|
||||||
<component v-if="column.component" :is="column.component" :row="row"></component>
|
<component v-if="column.component" :is="column.component" :row="row.original"></component>
|
||||||
<span v-else-if="row[column.key]">
|
<span v-else-if="row.original[column.key]" class="block truncate">
|
||||||
<UTooltip :text="row[column.key]">
|
<UTooltip :text="String(row.original[column.key])">
|
||||||
{{row[column.key] ? `${column.maxLength ? (row[column.key].length > column.maxLength ? `${row[column.key].substring(0,column.maxLength)}...` : row[column.key]) : row[column.key]} ${column.unit ? column.unit : ''}`: ''}}
|
<span class="block truncate">
|
||||||
</UTooltip>
|
{{ `${truncateValue(row.original[column.key], column.maxLength)}${column.unit ? ` ${column.unit}` : ''}` }}
|
||||||
|
</span>
|
||||||
|
</UTooltip>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
<!-- <UTable
|
<!-- <UTable
|
||||||
v-if="dataType && columns"
|
v-if="dataType && columns"
|
||||||
:rows="props.rows"
|
:data="props.rows"
|
||||||
:columns="props.columns"
|
:columns="props.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' }"
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ const addContactRequest = async () => {
|
|||||||
toast.add({title: "Anfrage erfolgreich erstellt"})
|
toast.add({title: "Anfrage erfolgreich erstellt"})
|
||||||
resetContactRequest()
|
resetContactRequest()
|
||||||
} else {
|
} else {
|
||||||
toast.add({title: "Anfrage konnte nicht erstellt werden",color:"rose"})
|
toast.add({title: "Anfrage konnte nicht erstellt werden",color:"error"})
|
||||||
}
|
}
|
||||||
loadingContactRequest.value = false
|
loadingContactRequest.value = false
|
||||||
}
|
}
|
||||||
@@ -302,7 +302,7 @@ watch(isHelpSlideoverOpen, async (isOpen) => {
|
|||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
type="reset"
|
type="reset"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
:disabled="!contactRequestData.title && !contactRequestData.message"
|
:disabled="!contactRequestData.title && !contactRequestData.message"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const renderText = (text) => {
|
|||||||
+ Eintrag
|
+ Eintrag
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
<UDivider class="my-3"/>
|
<USeparator class="my-3"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ITEM LIST -->
|
<!-- ITEM LIST -->
|
||||||
@@ -138,7 +138,7 @@ const renderText = (text) => {
|
|||||||
v-if="items.length > 0"
|
v-if="items.length > 0"
|
||||||
v-for="(item,index) in items.slice().reverse()"
|
v-for="(item,index) in items.slice().reverse()"
|
||||||
>
|
>
|
||||||
<UDivider
|
<USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
v-if="index !== 0"
|
v-if="index !== 0"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ defineShortcuts({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UDivider />
|
<USeparator />
|
||||||
</div>
|
</div>
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
</template>
|
</template>
|
||||||
@@ -34,7 +34,7 @@ defineProps({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UDivider class="my-5" />
|
<USeparator class="my-5" />
|
||||||
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<p class="text-lg">
|
<p class="text-lg">
|
||||||
@@ -42,7 +42,7 @@ defineProps({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UDivider class="my-5" />
|
<USeparator class="my-5" />
|
||||||
|
|
||||||
<form @submit.prevent>
|
<form @submit.prevent>
|
||||||
<UTextarea color="gray" required size="xl" :rows="5" :placeholder="`Reply to ${mail.from.name}`">
|
<UTextarea color="gray" required size="xl" :rows="5" :placeholder="`Reply to ${mail.from.name}`">
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
|||||||
emit('saved')
|
emit('saved')
|
||||||
isOpen.value = false
|
isOpen.value = false
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.add({ title: 'Fehler', description: error.message, color: 'red' })
|
toast.add({ title: 'Fehler', description: error.message, color: 'error' })
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -125,56 +125,62 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UModal v-model:open="isOpen">
|
<UModal v-model:open="isOpen">
|
||||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
<template #content>
|
||||||
<template #header>
|
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||||
<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">
|
||||||
{{ entry ? 'Eintrag bearbeiten' : 'Neue Zeit erfassen' }}
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
</h3>
|
{{ entry ? 'Eintrag bearbeiten' : 'Neue Zeit erfassen' }}
|
||||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
|
</h3>
|
||||||
</div>
|
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
|
||||||
</template>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||||
|
<UFormField label="Typ" name="type">
|
||||||
|
<USelectMenu
|
||||||
|
v-model="state.type"
|
||||||
|
:items="types"
|
||||||
|
value-key="value"
|
||||||
|
label-key="label"
|
||||||
|
/>
|
||||||
|
</UFormField>
|
||||||
|
|
||||||
<UFormGroup label="Typ" name="type">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<USelectMenu v-model="state.type" :options="types" value-attribute="value" option-attribute="label" />
|
<UFormField label="Start Datum" name="start_date">
|
||||||
</UFormGroup>
|
<div class="flex items-center gap-2">
|
||||||
|
<UInput type="date" v-model="state.start_date" class="flex-1" />
|
||||||
|
<UButton color="gray" variant="soft" label="Heute" @click="setDateFieldToToday('start_date')" />
|
||||||
|
</div>
|
||||||
|
</UFormField>
|
||||||
|
<UFormField label="Start Zeit" name="start_time">
|
||||||
|
<UInput type="time" v-model="state.start_time" />
|
||||||
|
</UFormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<UFormGroup label="Start Datum" name="start_date">
|
<UFormField label="Ende Datum" name="end_date">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<UInput type="date" v-model="state.start_date" class="flex-1" />
|
<UInput type="date" v-model="state.end_date" class="flex-1" />
|
||||||
<UButton color="gray" variant="soft" label="Heute" @click="setDateFieldToToday('start_date')" />
|
<UButton color="gray" variant="soft" label="Heute" @click="setDateFieldToToday('end_date')" />
|
||||||
</div>
|
</div>
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
<UFormGroup label="Start Zeit" name="start_time">
|
<UFormField label="Ende Zeit" name="end_time">
|
||||||
<UInput type="time" v-model="state.start_time" />
|
<UInput type="time" v-model="state.end_time" />
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="text-xs text-gray-500 -mt-2">Leer lassen, wenn die Zeit noch läuft.</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<UFormField label="Beschreibung / Notiz" name="description">
|
||||||
<UFormGroup label="Ende Datum" name="end_date">
|
<UTextarea v-model="state.description" placeholder="Was wurde gemacht?" />
|
||||||
<div class="flex items-center gap-2">
|
</UFormField>
|
||||||
<UInput type="date" v-model="state.end_date" class="flex-1" />
|
|
||||||
<UButton color="gray" variant="soft" label="Heute" @click="setDateFieldToToday('end_date')" />
|
|
||||||
</div>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup label="Ende Zeit" name="end_time">
|
|
||||||
<UInput type="time" v-model="state.end_time" />
|
|
||||||
</UFormGroup>
|
|
||||||
</div>
|
|
||||||
<p class="text-xs text-gray-500 -mt-2">Leer lassen, wenn die Zeit noch läuft.</p>
|
|
||||||
|
|
||||||
<UFormGroup label="Beschreibung / Notiz" name="description">
|
<div class="flex justify-end gap-2 pt-4">
|
||||||
<UTextarea v-model="state.description" placeholder="Was wurde gemacht?" />
|
<UButton label="Abbrechen" color="gray" variant="ghost" @click="isOpen = false" />
|
||||||
</UFormGroup>
|
<UButton type="submit" label="Speichern" color="primary" :loading="loading" />
|
||||||
|
</div>
|
||||||
<div class="flex justify-end gap-2 pt-4">
|
</UForm>
|
||||||
<UButton label="Abbrechen" color="gray" variant="ghost" @click="isOpen = false" />
|
</UCard>
|
||||||
<UButton type="submit" label="Speichern" color="primary" :loading="loading" />
|
</template>
|
||||||
</div>
|
|
||||||
</UForm>
|
|
||||||
</UCard>
|
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<slot name="right"/>
|
<slot name="right"/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<UDivider class="my-3"/>
|
<USeparator class="my-3"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
27
frontend/components/UDashboardPanelContent.vue
Normal file
27
frontend/components/UDashboardPanelContent.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<script setup>
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
as: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: 'div'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const attrs = useAttrs()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="props.as"
|
||||||
|
v-bind="attrs"
|
||||||
|
:class="[
|
||||||
|
'min-h-0 flex-1 overflow-y-auto px-4 py-4 sm:px-6 sm:py-5',
|
||||||
|
attrs.class
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
@@ -243,26 +243,26 @@ loadData()
|
|||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="granularity"
|
v-model="granularity"
|
||||||
:options="granularityOptions"
|
:items="granularityOptions"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="w-28"
|
class="w-28"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedYear"
|
v-model="selectedYear"
|
||||||
:options="yearOptions"
|
:items="yearOptions"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="w-24"
|
class="w-24"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-if="granularity === 'month'"
|
v-if="granularity === 'month'"
|
||||||
v-model="selectedMonth"
|
v-model="selectedMonth"
|
||||||
:options="monthOptions"
|
:items="monthOptions"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="w-36"
|
class="w-36"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -288,26 +288,26 @@ loadData()
|
|||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="granularity"
|
v-model="granularity"
|
||||||
:options="granularityOptions"
|
:items="granularityOptions"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="w-28"
|
class="w-28"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedYear"
|
v-model="selectedYear"
|
||||||
:options="yearOptions"
|
:items="yearOptions"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="w-24"
|
class="w-24"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-if="granularity === 'month'"
|
v-if="granularity === 'month'"
|
||||||
v-model="selectedMonth"
|
v-model="selectedMonth"
|
||||||
:options="monthOptions"
|
:items="monthOptions"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="w-36"
|
class="w-36"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ setupPage()
|
|||||||
<template>
|
<template>
|
||||||
<UTable
|
<UTable
|
||||||
v-if="openTasks.length > 0"
|
v-if="openTasks.length > 0"
|
||||||
:rows="openTasks"
|
:data="openTasks"
|
||||||
:columns="normalizeTableColumns([{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}`)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const startTime = async () => {
|
|||||||
await setupPage()
|
await setupPage()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
toast.add({title: "Fehler beim starten der Projektzeit",color:"rose"})
|
toast.add({title: "Fehler beim starten der Projektzeit",color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ const stopStartedTime = async () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
let errorId = await useError().logError(`${JSON.stringify(error)}`)
|
let errorId = await useError().logError(`${JSON.stringify(error)}`)
|
||||||
toast.add({title: errorId ? `Fehler beim stoppen der Projektzeit (Fehler ID: ${errorId})` : `Fehler beim stoppen der Projektzeit`,color:"rose"})
|
toast.add({title: errorId ? `Fehler beim stoppen der Projektzeit (Fehler ID: ${errorId})` : `Fehler beim stoppen der Projektzeit`,color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const startTime = async () => {
|
|||||||
await setupPage()
|
await setupPage()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
toast.add({title: "Fehler beim starten der Zeit",color:"rose"})
|
toast.add({title: "Fehler beim starten der Zeit",color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ const stopStartedTime = async () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
let errorId = await useError().logError(`${JSON.stringify(error)}`)
|
let errorId = await useError().logError(`${JSON.stringify(error)}`)
|
||||||
toast.add({title: errorId ? `Fehler beim stoppen der Anwesenheit (Fehler ID: ${errorId})` : `Fehler beim stoppen der Anwesenheit`,color:"rose"})
|
toast.add({title: errorId ? `Fehler beim stoppen der Anwesenheit (Fehler ID: ${errorId})` : `Fehler beim stoppen der Anwesenheit`,color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ const props = defineProps({
|
|||||||
const products = ref([])
|
const products = ref([])
|
||||||
const units = ref([])
|
const units = ref([])
|
||||||
|
|
||||||
|
const productSearchInput = {
|
||||||
|
placeholder: 'Artikel suchen...'
|
||||||
|
}
|
||||||
|
|
||||||
const setup = async () => {
|
const setup = async () => {
|
||||||
products.value = await useEntities("products").select()
|
products.value = await useEntities("products").select()
|
||||||
units.value = await useEntities("units").selectSpecial()
|
units.value = await useEntities("units").selectSpecial()
|
||||||
@@ -80,16 +84,16 @@ const setRowData = (row) => {
|
|||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
searchable
|
:items="products"
|
||||||
:search-attributes="['name']"
|
label-key="name"
|
||||||
:options="products"
|
value-key="id"
|
||||||
value-attribute="id"
|
:search-input="productSearchInput"
|
||||||
option-attribute="name"
|
:filter-fields="['name']"
|
||||||
v-model="product.product"
|
v-model="product.product"
|
||||||
:color="product.product ? 'primary' : 'rose'"
|
:color="product.product ? 'primary' : 'error'"
|
||||||
@change="setRowData(product)"
|
@change="setRowData(product)"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #default>
|
||||||
{{products.find(i => i.id === product.product) ? products.find(i => i.id === product.product).name : 'Kein Artikel ausgewählt'}}
|
{{products.find(i => i.id === product.product) ? products.find(i => i.id === product.product).name : 'Kein Artikel ausgewählt'}}
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
@@ -104,9 +108,9 @@ const setRowData = (row) => {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:options="units"
|
:items="units"
|
||||||
value-attribute="id"
|
label-key="name"
|
||||||
option-attribute="name"
|
value-key="id"
|
||||||
v-model="product.unit"
|
v-model="product.unit"
|
||||||
></USelectMenu>
|
></USelectMenu>
|
||||||
</td>
|
</td>
|
||||||
@@ -123,7 +127,7 @@ const setRowData = (row) => {
|
|||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
@click="removeProductFromMaterialComposition(product.id)"
|
@click="removeProductFromMaterialComposition(product.id)"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -135,4 +139,4 @@ const setRowData = (row) => {
|
|||||||
td {
|
td {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ const props = defineProps({
|
|||||||
const hourrates = ref([])
|
const hourrates = ref([])
|
||||||
const units = ref([])
|
const units = ref([])
|
||||||
|
|
||||||
|
const hourrateSearchInput = {
|
||||||
|
placeholder: 'Stundensatz suchen...'
|
||||||
|
}
|
||||||
|
|
||||||
const setup = async () => {
|
const setup = async () => {
|
||||||
hourrates.value = await useEntities("hourrates").select()
|
hourrates.value = await useEntities("hourrates").select()
|
||||||
units.value = await useEntities("units").selectSpecial()
|
units.value = await useEntities("units").selectSpecial()
|
||||||
@@ -82,14 +86,14 @@ const setRowData = (row) => {
|
|||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
searchable
|
:items="hourrates"
|
||||||
:search-attributes="['name']"
|
label-key="name"
|
||||||
:options="hourrates"
|
value-key="id"
|
||||||
value-attribute="id"
|
:search-input="hourrateSearchInput"
|
||||||
option-attribute="name"
|
:filter-fields="['name']"
|
||||||
v-model="row.hourrate"
|
v-model="row.hourrate"
|
||||||
:color="row.hourrate ? 'primary' : 'rose'"
|
:color="row.hourrate ? 'primary' : 'error'"
|
||||||
@change="setRowData(row)"
|
@change="setRowData(row)"
|
||||||
>
|
>
|
||||||
<!-- <template #label>
|
<!-- <template #label>
|
||||||
{{products.find(i => i.id === product.product) ? products.find(i => i.id === product.product).name : 'Kein Artikel ausgewählt'}}
|
{{products.find(i => i.id === product.product) ? products.find(i => i.id === product.product).name : 'Kein Artikel ausgewählt'}}
|
||||||
@@ -106,10 +110,10 @@ const setRowData = (row) => {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:options="units"
|
:items="units"
|
||||||
disabled
|
disabled
|
||||||
value-attribute="id"
|
label-key="name"
|
||||||
option-attribute="name"
|
value-key="id"
|
||||||
v-model="row.unit"
|
v-model="row.unit"
|
||||||
></USelectMenu>
|
></USelectMenu>
|
||||||
</td>
|
</td>
|
||||||
@@ -134,7 +138,7 @@ const setRowData = (row) => {
|
|||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
@click="removeRowFromPersonalComposition(row.id)"
|
@click="removeRowFromPersonalComposition(row.id)"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -146,4 +150,4 @@ const setRowData = (row) => {
|
|||||||
td {
|
td {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
35
frontend/composables/useModal.ts
Normal file
35
frontend/composables/useModal.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
type ModalComponent = any
|
||||||
|
type ModalProps = Record<string, any> | undefined
|
||||||
|
|
||||||
|
const modalStack = useState<any[]>('__fed_modal_stack__', () => [])
|
||||||
|
|
||||||
|
export const useModal = () => {
|
||||||
|
const overlay = useOverlay()
|
||||||
|
|
||||||
|
const open = (component: ModalComponent, props?: ModalProps) => {
|
||||||
|
const instance = overlay.create(component, { props, destroyOnClose: true })
|
||||||
|
modalStack.value.push(instance)
|
||||||
|
|
||||||
|
const result = instance.open(props)
|
||||||
|
result.finally(() => {
|
||||||
|
modalStack.value = modalStack.value.filter((entry) => entry.id !== instance.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = (value?: any) => {
|
||||||
|
const current = modalStack.value[modalStack.value.length - 1]
|
||||||
|
|
||||||
|
if (!current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
current.close(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
open,
|
||||||
|
close
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -248,7 +248,7 @@ onMounted(() => {
|
|||||||
<TenantDropdown class="min-w-0 w-full max-w-sm" />
|
<TenantDropdown class="min-w-0 w-full max-w-sm" />
|
||||||
</div>-->
|
</div>-->
|
||||||
|
|
||||||
<UDashboardGroup class="flex min-h-0 flex-1 flex-col overflow-hidden">
|
<UDashboardGroup class="flex min-h-0 flex-1 overflow-hidden">
|
||||||
|
|
||||||
|
|
||||||
<UDashboardSidebar
|
<UDashboardSidebar
|
||||||
@@ -298,7 +298,7 @@ onMounted(() => {
|
|||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UDivider class="sticky bottom-0 w-full"/>
|
<USeparator class="sticky bottom-0 w-full"/>
|
||||||
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;" class="w-full"/>
|
<UserDropdown style="margin-bottom: env(safe-area-inset-bottom, 10px) !important;" class="w-full"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -345,7 +345,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="auth.logout()"
|
@click="auth.logout()"
|
||||||
>Abmelden
|
>Abmelden
|
||||||
</UButton>
|
</UButton>
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ onMounted(loadData)
|
|||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
:columns="normalizeTableColumns(columns)"
|
:columns="normalizeTableColumns(columns)"
|
||||||
:rows="periods"
|
:data="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' }"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ setupPage()
|
|||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="clearSearchString()"
|
@click="clearSearchString()"
|
||||||
v-if="searchString.length > 0"
|
v-if="searchString.length > 0"
|
||||||
/>
|
/>
|
||||||
@@ -194,7 +194,7 @@ setupPage()
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
<UTable
|
<UTable
|
||||||
:rows="filteredRows"
|
:data="filteredRows"
|
||||||
:columns="normalizeTableColumns(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' }"
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ const saldo = computed(() => {
|
|||||||
<UCard class="mt-5" v-if="item.label === 'Buchungen'">
|
<UCard class="mt-5" v-if="item.label === 'Buchungen'">
|
||||||
<UTable
|
<UTable
|
||||||
v-if="statementallocations"
|
v-if="statementallocations"
|
||||||
:rows="renderedAllocations"
|
:data="renderedAllocations"
|
||||||
:columns="normalizeTableColumns([{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' }"
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ onMounted(() => {
|
|||||||
placeholder="Konten"
|
placeholder="Konten"
|
||||||
class="w-48"
|
class="w-48"
|
||||||
/>
|
/>
|
||||||
<UDivider orientation="vertical" class="h-6"/>
|
<USeparator orientation="vertical" class="h-6"/>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedPeriod"
|
v-model="selectedPeriod"
|
||||||
@@ -633,7 +633,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-2 flex flex-wrap gap-1">
|
<div class="mt-2 flex flex-wrap gap-1">
|
||||||
<UBadge v-if="entry.suggestions?.topDocument" size="xs" color="emerald" variant="subtle">Rechnung</UBadge>
|
<UBadge v-if="entry.suggestions?.topDocument" size="xs" color="emerald" variant="subtle">Rechnung</UBadge>
|
||||||
<UBadge v-if="entry.suggestions?.topIncomingInvoice" size="xs" color="rose" variant="subtle">Eingangsbeleg</UBadge>
|
<UBadge v-if="entry.suggestions?.topIncomingInvoice" size="xs" color="error" variant="subtle">Eingangsbeleg</UBadge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -690,7 +690,7 @@ onMounted(() => {
|
|||||||
</UBadge>
|
</UBadge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UButton size="sm" color="rose" @click="handleAssignIncomingInvoice(selectedSuggestionRow.row, selectedSuggestionRow.suggestions.topIncomingInvoice, $event)">
|
<UButton size="sm" color="error" @click="handleAssignIncomingInvoice(selectedSuggestionRow.row, selectedSuggestionRow.suggestions.topIncomingInvoice, $event)">
|
||||||
Zuweisen
|
Zuweisen
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton size="sm" color="gray" variant="ghost" @click="dismissSuggestion(selectedSuggestionRow.row, selectedSuggestionRow.suggestions.topIncomingInvoice.suggestionKey, $event)">
|
<UButton size="sm" color="gray" variant="ghost" @click="dismissSuggestion(selectedSuggestionRow.row, selectedSuggestionRow.suggestions.topIncomingInvoice.suggestionKey, $event)">
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ setup()
|
|||||||
<UBadge v-else color="amber" variant="subtle">Offen</UBadge>
|
<UBadge v-else color="amber" variant="subtle">Offen</UBadge>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<ArchiveButton color="rose" variant="outline" type="bankstatements" @confirmed="archiveStatement"/>
|
<ArchiveButton color="error" variant="outline" type="bankstatements" @confirmed="archiveStatement"/>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ setup()
|
|||||||
<div class="font-mono text-sm font-semibold">{{ displayCurrency(item.amount) }}</div>
|
<div class="font-mono text-sm font-semibold">{{ displayCurrency(item.amount) }}</div>
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-trash"
|
icon="i-heroicons-trash"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="xs"
|
size="xs"
|
||||||
class="opacity-0 group-hover:opacity-100 transition-opacity"
|
class="opacity-0 group-hover:opacity-100 transition-opacity"
|
||||||
@@ -747,7 +747,7 @@ setup()
|
|||||||
</div>
|
</div>
|
||||||
<UButton
|
<UButton
|
||||||
size="xs"
|
size="xs"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="saveAllocation({incominginvoice: invoice.id, bankstatement: itemInfo.id, amount: Number(Math.abs(getInvoiceSum(invoice,true)) > Math.abs(manualAllocationSum) ? manualAllocationSum : getInvoiceSum(invoice,true)), description: allocationDescription || 'Automatischer Vorschlag'})"
|
@click="saveAllocation({incominginvoice: invoice.id, bankstatement: itemInfo.id, amount: Number(Math.abs(getInvoiceSum(invoice,true)) > Math.abs(manualAllocationSum) ? manualAllocationSum : getInvoiceSum(invoice,true)), description: allocationDescription || 'Automatischer Vorschlag'})"
|
||||||
>
|
>
|
||||||
Beleg zuweisen
|
Beleg zuweisen
|
||||||
@@ -867,7 +867,7 @@ setup()
|
|||||||
v-if="!itemInfo.statementallocations.find(i => i.incominginvoice === item.id)"
|
v-if="!itemInfo.statementallocations.find(i => i.incominginvoice === item.id)"
|
||||||
icon="i-heroicons-check"
|
icon="i-heroicons-check"
|
||||||
size="sm"
|
size="sm"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
@click="saveAllocation({incominginvoice: item.id, bankstatement: itemInfo.id, amount: Number(Math.abs(getInvoiceSum(item,true)) > Math.abs(manualAllocationSum) ? manualAllocationSum : getInvoiceSum(item,true)), description: allocationDescription})"
|
@click="saveAllocation({incominginvoice: item.id, bankstatement: itemInfo.id, amount: Number(Math.abs(getInvoiceSum(item,true)) > Math.abs(manualAllocationSum) ? manualAllocationSum : getInvoiceSum(item,true)), description: allocationDescription})"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1609,7 +1609,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<ArchiveButton
|
<ArchiveButton
|
||||||
color="rose"
|
color="error"
|
||||||
type="createddocuments"
|
type="createddocuments"
|
||||||
v-if="itemInfo.state === 'Entwurf' || itemInfo.type === 'serialInvoices'"
|
v-if="itemInfo.state === 'Entwurf' || itemInfo.type === 'serialInvoices'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -1646,7 +1646,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
<UAlert
|
<UAlert
|
||||||
class="my-5"
|
class="my-5"
|
||||||
title="Vorhandene Probleme und Informationen:"
|
title="Vorhandene Probleme und Informationen:"
|
||||||
:color="findDocumentErrors.filter(i => i.type === 'breaking').length > 0 ? 'rose' : 'white'"
|
:color="findDocumentErrors.filter(i => i.type === 'breaking').length > 0 ? 'error' : 'white'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
v-if="findDocumentErrors.length > 0"
|
v-if="findDocumentErrors.length > 0"
|
||||||
>
|
>
|
||||||
@@ -1761,7 +1761,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
searchable-placeholder="Suche..."
|
searchable-placeholder="Suche..."
|
||||||
:search-attributes="['name']"
|
:search-attributes="['name']"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:color="itemInfo.letterhead ? 'primary' : 'rose'"
|
:color="itemInfo.letterhead ? 'primary' : 'error'"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
{{ itemInfo.letterhead ? letterheads.find(i => i.id === itemInfo.letterhead).name : "Kein Briefpapier gewählt" }}
|
{{ itemInfo.letterhead ? letterheads.find(i => i.id === itemInfo.letterhead).name : "Kein Briefpapier gewählt" }}
|
||||||
@@ -1788,7 +1788,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
{{ option.name }}{{ option.nameAddition }}
|
{{ option.name }}{{ option.nameAddition }}
|
||||||
</template>
|
</template>
|
||||||
<UButton
|
<UButton
|
||||||
:color="itemInfo.customer ? 'primary' : 'rose'"
|
:color="itemInfo.customer ? 'primary' : 'error'"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
>
|
>
|
||||||
@@ -1902,7 +1902,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
>Kontakt</UButton>-->
|
>Kontakt</UButton>-->
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
v-if="itemInfo.contact"
|
v-if="itemInfo.contact"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
@click="itemInfo.contact = null"
|
@click="itemInfo.contact = null"
|
||||||
@@ -1923,7 +1923,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
<UInput
|
<UInput
|
||||||
v-model="itemInfo.address.street"
|
v-model="itemInfo.address.street"
|
||||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.street : 'Straße + Hausnummer'"
|
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.street : 'Straße + Hausnummer'"
|
||||||
:color="itemInfo.address.street ? 'primary' : 'rose'"
|
:color="itemInfo.address.street ? 'primary' : 'error'"
|
||||||
/>
|
/>
|
||||||
<UInput
|
<UInput
|
||||||
v-model="itemInfo.address.special"
|
v-model="itemInfo.address.special"
|
||||||
@@ -1940,13 +1940,13 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
@input="sanitizeAddressZipInput"
|
@input="sanitizeAddressZipInput"
|
||||||
@change="checkAddressZip"
|
@change="checkAddressZip"
|
||||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.zip : 'PLZ'"
|
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.zip : 'PLZ'"
|
||||||
:color="itemInfo.address.zip ? 'primary' : 'rose'"
|
:color="itemInfo.address.zip ? 'primary' : 'error'"
|
||||||
/>
|
/>
|
||||||
<UInput
|
<UInput
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
v-model="itemInfo.address.city"
|
v-model="itemInfo.address.city"
|
||||||
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.city : 'Ort'"
|
:placeholder="itemInfo.customer ? customers.find(i => i.id === itemInfo.customer).infoData.city : 'Ort'"
|
||||||
:color="itemInfo.address.city ? 'primary' : 'rose'"
|
:color="itemInfo.address.city ? 'primary' : 'error'"
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
@@ -2132,7 +2132,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
v-if="itemInfo.plant"
|
v-if="itemInfo.plant"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
@click="itemInfo.plant = null"
|
@click="itemInfo.plant = null"
|
||||||
@@ -2171,7 +2171,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
v-if="itemInfo.project"
|
v-if="itemInfo.project"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
@click="itemInfo.project = null"
|
@click="itemInfo.project = null"
|
||||||
@@ -2210,7 +2210,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
v-if="itemInfo.contract"
|
v-if="itemInfo.contract"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
@click="itemInfo.contract = null"
|
@click="itemInfo.contract = null"
|
||||||
@@ -2229,9 +2229,9 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
<div v-if="itemInfo.type === 'serialInvoices'" class="mb-5">
|
<div v-if="itemInfo.type === 'serialInvoices'" class="mb-5">
|
||||||
<UDivider class="mt-5 mb-3">
|
<USeparator class="mt-5 mb-3">
|
||||||
Einstellungen für die Serienrechnung
|
Einstellungen für die Serienrechnung
|
||||||
</UDivider>
|
</USeparator>
|
||||||
|
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row">
|
||||||
<div class="w-1/3">
|
<div class="w-1/3">
|
||||||
@@ -2301,7 +2301,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UDivider
|
<USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -2317,7 +2317,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
<UInput v-model="itemInfo.description"/>
|
<UInput v-model="itemInfo.description"/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UDivider
|
<USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -2350,7 +2350,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
<UDivider
|
<USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -2389,7 +2389,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
v-if="row.mode === 'pagebreak'"
|
v-if="row.mode === 'pagebreak'"
|
||||||
colspan="7"
|
colspan="7"
|
||||||
>
|
>
|
||||||
<UDivider/>
|
<USeparator/>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
v-if="row.mode === 'text'"
|
v-if="row.mode === 'text'"
|
||||||
@@ -2429,7 +2429,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||||
class="w-60"
|
class="w-60"
|
||||||
:options="products"
|
:options="products"
|
||||||
:color="row.product ? 'primary' : 'rose'"
|
:color="row.product ? 'primary' : 'error'"
|
||||||
option-attribute="name"
|
option-attribute="name"
|
||||||
value-attribute="id"
|
value-attribute="id"
|
||||||
searchable
|
searchable
|
||||||
@@ -2501,7 +2501,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||||
class="w-60"
|
class="w-60"
|
||||||
:options="services"
|
:options="services"
|
||||||
:color="row.service ? 'primary' : 'rose'"
|
:color="row.service ? 'primary' : 'error'"
|
||||||
option-attribute="name"
|
option-attribute="name"
|
||||||
value-attribute="id"
|
value-attribute="id"
|
||||||
searchable
|
searchable
|
||||||
@@ -2942,7 +2942,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
<UButton
|
<UButton
|
||||||
:disabled="itemInfo.type === 'cancellationInvoices'"
|
:disabled="itemInfo.type === 'cancellationInvoices'"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="rose"
|
color="error"
|
||||||
icon="i-heroicons-x-mark-16-solid"
|
icon="i-heroicons-x-mark-16-solid"
|
||||||
@click="removePosition(row.id)"
|
@click="removePosition(row.id)"
|
||||||
/>
|
/>
|
||||||
@@ -2954,7 +2954,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
<UAlert
|
<UAlert
|
||||||
v-else
|
v-else
|
||||||
title="Keine Positionen hinzugefügt"
|
title="Keine Positionen hinzugefügt"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-light-bulb"
|
icon="i-heroicons-light-bulb"
|
||||||
></UAlert>
|
></UAlert>
|
||||||
@@ -3004,12 +3004,12 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</UButton>
|
</UButton>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
<!-- <UDivider
|
<!-- <USeparator
|
||||||
class="mt-5 mb-3"
|
class="mt-5 mb-3"
|
||||||
v-if="openAdvanceInvoices.length > 0 || itemInfo.usedAdvanceInvoices.length > 0"
|
v-if="openAdvanceInvoices.length > 0 || itemInfo.usedAdvanceInvoices.length > 0"
|
||||||
>
|
>
|
||||||
Noch nicht abgerechnete Abschlagsrechnungen
|
Noch nicht abgerechnete Abschlagsrechnungen
|
||||||
</UDivider>
|
</USeparator>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="advanceInvoice in openAdvanceInvoices"
|
v-for="advanceInvoice in openAdvanceInvoices"
|
||||||
@@ -3029,7 +3029,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
<UButton
|
<UButton
|
||||||
@click="itemInfo.usedAdvanceInvoices = itemInfo.usedAdvanceInvoices.filter(i => i !== advanceInvoice.id)"
|
@click="itemInfo.usedAdvanceInvoices = itemInfo.usedAdvanceInvoices.filter(i => i !== advanceInvoice.id)"
|
||||||
:disabled="!itemInfo.usedAdvanceInvoices.includes(advanceInvoice.id)"
|
:disabled="!itemInfo.usedAdvanceInvoices.includes(advanceInvoice.id)"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
X
|
X
|
||||||
@@ -3037,7 +3037,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>-->
|
</div>-->
|
||||||
|
|
||||||
<UDivider class="my-3" v-if="Object.keys(documentTotal.titleSums).length > 0">Überschriften</UDivider>
|
<USeparator class="my-3" v-if="Object.keys(documentTotal.titleSums).length > 0" label="Überschriften"/>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr v-for="sumKey in Object.keys(documentTotal.titleSums) ">
|
<tr v-for="sumKey in Object.keys(documentTotal.titleSums) ">
|
||||||
@@ -3046,7 +3046,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<UDivider class="my-3" v-if="itemInfo.rows.length > 0">Auswertung & Gesamt</UDivider>
|
<USeparator class="my-3" v-if="itemInfo.rows.length > 0" label="Auswertung & Gesamt"/>
|
||||||
|
|
||||||
|
|
||||||
<div class="w-full flex justify-between" v-if="itemInfo.type !== 'deliveryNotes'">
|
<div class="w-full flex justify-between" v-if="itemInfo.type !== 'deliveryNotes'">
|
||||||
@@ -3112,9 +3112,9 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <UDivider
|
<!-- <USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
>Auswertung</UDivider>
|
>Auswertung</USeparator>
|
||||||
|
|
||||||
<div class="w-full flex justify-end">
|
<div class="w-full flex justify-end">
|
||||||
<table class="w-1/3">
|
<table class="w-1/3">
|
||||||
@@ -3151,7 +3151,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<UDivider
|
<USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</UInput>
|
</UInput>
|
||||||
<UButton
|
<UButton
|
||||||
v-if="searchString.length > 0"
|
v-if="searchString.length > 0"
|
||||||
color="rose"
|
color="error"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@click="clearSearchString()"
|
@click="clearSearchString()"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<UTable
|
<UTable
|
||||||
:columns="normalizeTableColumns(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)"
|
:data="getRowsForTab(item.key)"
|
||||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@select="selectItem"
|
@select="selectItem"
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
:rows="filteredRows"
|
:data="filteredRows"
|
||||||
:columns="normalizeTableColumns(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' }"
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UDivider label="Vorlagen auswählen" />
|
<USeparator label="Vorlagen auswählen" />
|
||||||
|
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
||||||
<div class="flex flex-col sm:flex-row gap-2 w-full sm:w-auto">
|
<div class="flex flex-col sm:flex-row gap-2 w-full sm:w-auto">
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
<div class="max-h-96 overflow-y-auto border border-gray-200 dark:border-gray-800 rounded-md">
|
<div class="max-h-96 overflow-y-auto border border-gray-200 dark:border-gray-800 rounded-md">
|
||||||
<UTable
|
<UTable
|
||||||
v-model="selectedExecutionRows"
|
v-model="selectedExecutionRows"
|
||||||
:rows="filteredExecutionList"
|
:data="filteredExecutionList"
|
||||||
:columns="normalizeTableColumns(executionColumns)"
|
:columns="normalizeTableColumns(executionColumns)"
|
||||||
:ui="{ th: { base: 'whitespace-nowrap' } }"
|
:ui="{ th: { base: 'whitespace-nowrap' } }"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const openBankstatements = () => {
|
|||||||
<UButton
|
<UButton
|
||||||
@click="router.push(`/createDocument/edit/?createddocument=${itemInfo.id}&loadMode=storno`)"
|
@click="router.push(`/createDocument/edit/?createddocument=${itemInfo.id}&loadMode=storno`)"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
:disabled="links.find(i => i.type === 'cancellationInvoices')"
|
:disabled="links.find(i => i.type === 'cancellationInvoices')"
|
||||||
>
|
>
|
||||||
Stornieren
|
Stornieren
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ const sendEmail = async () => {
|
|||||||
|
|
||||||
|
|
||||||
if(!res.success) {
|
if(!res.success) {
|
||||||
toast.add({title: "Fehler beim Absenden der E-Mail", color: "rose"})
|
toast.add({title: "Fehler beim Absenden der E-Mail", color: "error"})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
navigateTo("/")
|
navigateTo("/")
|
||||||
@@ -220,7 +220,7 @@ const sendEmail = async () => {
|
|||||||
v-model="emailData.account"
|
v-model="emailData.account"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
<UDivider class="my-3"/>
|
<USeparator class="my-3"/>
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Empfänger"
|
label="Empfänger"
|
||||||
>
|
>
|
||||||
@@ -255,7 +255,7 @@ const sendEmail = async () => {
|
|||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
</div>
|
</div>
|
||||||
<UDivider class="my-3"/>
|
<USeparator class="my-3"/>
|
||||||
<div id="parentAttachments" class="flex flex-col justify-center mt-3">
|
<div id="parentAttachments" class="flex flex-col justify-center mt-3">
|
||||||
<span class="font-medium mb-2 text-xl">Anhänge</span>
|
<span class="font-medium mb-2 text-xl">Anhänge</span>
|
||||||
<!-- <UIcon
|
<!-- <UIcon
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const createExport = async () => {
|
|||||||
:loading="true"
|
:loading="true"
|
||||||
v-model="selected"
|
v-model="selected"
|
||||||
:loading-state="{ icon: 'i-heroicons-arrow-path-20-solid', label: 'Loading...' }"
|
:loading-state="{ icon: 'i-heroicons-arrow-path-20-solid', label: 'Loading...' }"
|
||||||
:rows="createddocuments" />
|
:data="createddocuments" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const createExport = async () => {
|
|||||||
if(res.success) {
|
if(res.success) {
|
||||||
toast.add({title: "Export wird erstellt. Sie erhalten eine Benachrichtigung sobald es soweit ist."})
|
toast.add({title: "Export wird erstellt. Sie erhalten eine Benachrichtigung sobald es soweit ist."})
|
||||||
} else {
|
} else {
|
||||||
toast.add({title: "Es gab einen Fehler beim erstellen", color: "rose"})
|
toast.add({title: "Es gab einen Fehler beim erstellen", color: "error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ const createExport = async () => {
|
|||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
:rows="filteredExports"
|
:data="filteredExports"
|
||||||
:columns="normalizeTableColumns([
|
:columns="normalizeTableColumns([
|
||||||
{ key: 'created_at', label: 'Erstellt am' },
|
{ key: 'created_at', label: 'Erstellt am' },
|
||||||
{ key: 'start_date', label: 'Start' },
|
{ key: 'start_date', label: 'Start' },
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ const updateIncomingInvoice = async (setBooked = false) => {
|
|||||||
toast.add({
|
toast.add({
|
||||||
title: "Buchen nicht möglich",
|
title: "Buchen nicht möglich",
|
||||||
description: "Bitte beheben Sie zuerst die rot markierten Pflichtfehler.",
|
description: "Bitte beheben Sie zuerst die rot markierten Pflichtfehler.",
|
||||||
color: "rose"
|
color: "error"
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
<template #right>
|
<template #right>
|
||||||
<ArchiveButton
|
<ArchiveButton
|
||||||
v-if="mode !== 'show'"
|
v-if="mode !== 'show'"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
type="incominginvoices"
|
type="incominginvoices"
|
||||||
@confirmed="useEntities('incominginvoices').archive(route.params.id)"
|
@confirmed="useEntities('incominginvoices').archive(route.params.id)"
|
||||||
@@ -286,7 +286,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
<UAlert
|
<UAlert
|
||||||
v-if="findIncomingInvoiceErrors.length > 0"
|
v-if="findIncomingInvoiceErrors.length > 0"
|
||||||
title="Prüfung erforderlich"
|
title="Prüfung erforderlich"
|
||||||
:color="hasBlockingIncomingInvoiceErrors ? 'rose' : 'orange'"
|
:color="hasBlockingIncomingInvoiceErrors ? 'error' : 'orange'"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
icon="i-heroicons-exclamation-triangle"
|
icon="i-heroicons-exclamation-triangle"
|
||||||
>
|
>
|
||||||
@@ -323,7 +323,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h3 class="font-semibold">Stammdaten</h3>
|
<h3 class="font-semibold">Stammdaten</h3>
|
||||||
<div class="flex bg-gray-100 dark:bg-gray-800 p-1 rounded-lg">
|
<div class="flex bg-gray-100 dark:bg-gray-800 p-1 rounded-lg">
|
||||||
<UButton size="xs" :variant="itemInfo.expense ? 'solid' : 'ghost'" color="rose" @click="itemInfo.expense = true" :disabled="mode === 'show'">Ausgabe</UButton>
|
<UButton size="xs" :variant="itemInfo.expense ? 'solid' : 'ghost'" color="error" @click="itemInfo.expense = true" :disabled="mode === 'show'">Ausgabe</UButton>
|
||||||
<UButton size="xs" :variant="!itemInfo.expense ? 'solid' : 'ghost'" color="emerald" @click="itemInfo.expense = false" :disabled="mode === 'show'">Einnahme</UButton>
|
<UButton size="xs" :variant="!itemInfo.expense ? 'solid' : 'ghost'" color="emerald" @click="itemInfo.expense = false" :disabled="mode === 'show'">Einnahme</UButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -354,7 +354,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
v-if="mode !== 'show'"
|
v-if="mode !== 'show'"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
:disabled="!itemInfo.vendor"
|
:disabled="!itemInfo.vendor"
|
||||||
@click="itemInfo.vendor = null"
|
@click="itemInfo.vendor = null"
|
||||||
/>
|
/>
|
||||||
@@ -419,7 +419,7 @@ const hasBlockingIncomingInvoiceErrors = computed(() => blockingIncomingInvoiceE
|
|||||||
<UButton
|
<UButton
|
||||||
v-if="itemInfo.accounts.length > 1 && mode !== 'show'"
|
v-if="itemInfo.accounts.length > 1 && mode !== 'show'"
|
||||||
icon="i-heroicons-trash"
|
icon="i-heroicons-trash"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="xs"
|
size="xs"
|
||||||
class="absolute top-2 right-2"
|
class="absolute top-2 right-2"
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ const selectIncomingInvoice = (invoice) => {
|
|||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="clearSearchString()"
|
@click="clearSearchString()"
|
||||||
v-if="searchString.length > 0"
|
v-if="searchString.length > 0"
|
||||||
/>
|
/>
|
||||||
@@ -250,7 +250,7 @@ const selectIncomingInvoice = (invoice) => {
|
|||||||
v-model:sort="sort"
|
v-model:sort="sort"
|
||||||
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' )"
|
:data="filteredRows.filter(i => item.label === 'Gebucht' ? i.state === 'Gebucht' : i.state !== 'Gebucht' )"
|
||||||
:columns="normalizeTableColumns(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' }"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const doLogin = async (data:any) => {
|
|||||||
await router.push("/")
|
await router.push("/")
|
||||||
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast.add({title:"Zugangsdaten falsch. Bitte überprüfen Sie Ihre Eingaben",color:"rose"})
|
toast.add({title:"Zugangsdaten falsch. Bitte überprüfen Sie Ihre Eingaben",color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ onMounted(() => {
|
|||||||
Urlaub
|
Urlaub
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton
|
<UButton
|
||||||
color="rose"
|
color="error"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
icon="i-heroicons-heart"
|
icon="i-heroicons-heart"
|
||||||
@click="openAbsenceModal('sick')"
|
@click="openAbsenceModal('sick')"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const doChange = async (data:any) => {
|
|||||||
await auth.logout()
|
await auth.logout()
|
||||||
return navigateTo("/login")
|
return navigateTo("/login")
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast.add({title:"Es gab ein Problem beim ändern",color:"rose"})
|
toast.add({title:"Es gab ein Problem beim ändern",color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const doReset = async (data:any) => {
|
|||||||
toast.add({title:"Zurücksetzen erfolgreich"})
|
toast.add({title:"Zurücksetzen erfolgreich"})
|
||||||
return navigateTo("/login")
|
return navigateTo("/login")
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast.add({title:"Problem beim zurücksetzen",color:"rose"})
|
toast.add({title:"Problem beim zurücksetzen",color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ const addPhase = () => {
|
|||||||
|
|
||||||
<UForm v-else-if="mode === 'edit' || mode === 'create'">
|
<UForm v-else-if="mode === 'edit' || mode === 'create'">
|
||||||
<UAlert
|
<UAlert
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="mb-5"
|
class="mb-5"
|
||||||
v-if="mode === 'edit'"
|
v-if="mode === 'edit'"
|
||||||
@@ -167,9 +167,9 @@ const addPhase = () => {
|
|||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UDivider class="mt-5">
|
<USeparator class="mt-5">
|
||||||
Initiale Phasen
|
Initiale Phasen
|
||||||
</UDivider>
|
</USeparator>
|
||||||
<UButton
|
<UButton
|
||||||
class="mt-3"
|
class="mt-3"
|
||||||
@click="addPhase"
|
@click="addPhase"
|
||||||
@@ -284,7 +284,7 @@ const addPhase = () => {
|
|||||||
<UButton
|
<UButton
|
||||||
class="my-2 ml-2"
|
class="my-2 ml-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="itemInfo.initialPhases = itemInfo.initialPhases.filter(i => i !== phase)"
|
@click="itemInfo.initialPhases = itemInfo.initialPhases.filter(i => i !== phase)"
|
||||||
>X</UButton>
|
>X</UButton>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const filteredRows = computed(() => {
|
|||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
:rows="filteredRows"
|
:data="filteredRows"
|
||||||
:columns="normalizeTableColumns(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' }"
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ onMounted(async () => {
|
|||||||
<div class="admin-scroll">
|
<div class="admin-scroll">
|
||||||
<UTable
|
<UTable
|
||||||
v-if="!loading"
|
v-if="!loading"
|
||||||
:rows="userTableRows"
|
:data="userTableRows"
|
||||||
:columns="normalizedUserTableColumns"
|
:columns="normalizedUserTableColumns"
|
||||||
@select="selectUser"
|
@select="selectUser"
|
||||||
/>
|
/>
|
||||||
@@ -565,7 +565,7 @@ onMounted(async () => {
|
|||||||
</UForm>
|
</UForm>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<UDivider label="Rollen pro Tenant" class="mb-4" />
|
<USeparator label="Rollen pro Tenant" class="mb-4" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="userForm.tenant_ids.length"
|
v-if="userForm.tenant_ids.length"
|
||||||
@@ -627,7 +627,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<UDivider label="Profile im System" class="mb-4" />
|
<USeparator label="Profile im System" class="mb-4" />
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<UBadge
|
<UBadge
|
||||||
v-for="profile in userForm.profiles"
|
v-for="profile in userForm.profiles"
|
||||||
@@ -674,7 +674,7 @@ onMounted(async () => {
|
|||||||
<div class="admin-scroll">
|
<div class="admin-scroll">
|
||||||
<UTable
|
<UTable
|
||||||
v-if="!loading"
|
v-if="!loading"
|
||||||
:rows="tenantTableRows"
|
:data="tenantTableRows"
|
||||||
:columns="normalizedTenantTableColumns"
|
:columns="normalizedTenantTableColumns"
|
||||||
@select="selectTenant"
|
@select="selectTenant"
|
||||||
/>
|
/>
|
||||||
@@ -711,7 +711,7 @@ onMounted(async () => {
|
|||||||
</UForm>
|
</UForm>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<UDivider label="Zugeordnete Benutzer" class="mb-4" />
|
<USeparator label="Zugeordnete Benutzer" class="mb-4" />
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<UBadge
|
<UBadge
|
||||||
v-for="user in getUsersForTenant(tenantForm.id)"
|
v-for="user in getUsersForTenant(tenantForm.id)"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const addAccount = async (account) => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
toast.add({title: "Es gab einen Fehler beim Hinzufügen des Accounts", color:"rose"})
|
toast.add({title: "Es gab einen Fehler beim Hinzufügen des Accounts", color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ const updateAccount = async (account) => {
|
|||||||
setupPage()
|
setupPage()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
toast.add({title: "Es gab einen Fehler beim Aktualisieren des Accounts", color:"rose"})
|
toast.add({title: "Es gab einen Fehler beim Aktualisieren des Accounts", color:"error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ setupPage()
|
|||||||
v-else-if="showAlert && !bankData.id"
|
v-else-if="showAlert && !bankData.id"
|
||||||
title="Bank nicht gefunden"
|
title="Bank nicht gefunden"
|
||||||
icon="i-heroicons-x-circle"
|
icon="i-heroicons-x-circle"
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="mt-3"
|
class="mt-3"
|
||||||
/>
|
/>
|
||||||
@@ -180,7 +180,7 @@ setupPage()
|
|||||||
</UModal>
|
</UModal>
|
||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
:rows="bankaccounts"
|
:data="bankaccounts"
|
||||||
:columns="normalizeTableColumns([
|
:columns="normalizeTableColumns([
|
||||||
{
|
{
|
||||||
key: 'expired',
|
key: 'expired',
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const saveAccount = async () => {
|
|||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UDivider> IMAP </UDivider>
|
<USeparator label="IMAP"/>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="IMAP Host"
|
label="IMAP Host"
|
||||||
@@ -102,7 +102,7 @@ const saveAccount = async () => {
|
|||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UDivider> SMTP </UDivider>
|
<USeparator label="SMTP"/>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="SMTP Host"
|
label="SMTP Host"
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ const columns = computed(() => templateColumns.filter((column) => selectedColumn
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
<UTable
|
<UTable
|
||||||
:rows="items"
|
:data="items"
|
||||||
:columns="normalizeTableColumns(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}`)"
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const isLight = computed({
|
|||||||
<UCard class="mt-5">
|
<UCard class="mt-5">
|
||||||
<div v-if="item.label === 'Profil'">
|
<div v-if="item.label === 'Profil'">
|
||||||
|
|
||||||
<UDivider
|
<USeparator
|
||||||
class="my-3"
|
class="my-3"
|
||||||
label="Profil"
|
label="Profil"
|
||||||
/>
|
/>
|
||||||
@@ -58,12 +58,12 @@ const isLight = computed({
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="item.label === 'Projekte'">
|
<div v-else-if="item.label === 'Projekte'">
|
||||||
<UDivider
|
<USeparator
|
||||||
label="Phasenvorlagen"
|
label="Phasenvorlagen"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="item.label === 'Dokumente'">
|
<div v-else-if="item.label === 'Dokumente'">
|
||||||
<UDivider
|
<USeparator
|
||||||
label="Tags"
|
label="Tags"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ const updateNumberRanges = async (range) => {
|
|||||||
<UDashboardToolbar>
|
<UDashboardToolbar>
|
||||||
<UAlert
|
<UAlert
|
||||||
title="Änderungen an diesen Werten betreffen nur neu Erstellte Einträge."
|
title="Änderungen an diesen Werten betreffen nur neu Erstellte Einträge."
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-exclamation-triangle"
|
icon="i-heroicons-exclamation-triangle"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ setupPage()
|
|||||||
<UAlert
|
<UAlert
|
||||||
title="Funktionen ausblenden"
|
title="Funktionen ausblenden"
|
||||||
description="Nur Funktionen mit gesetztem Haken sind im Unternehmen verfügbar. Diese Einstellungen gelten für alle Mitarbeiter und sind unabhängig von Berechtigungen."
|
description="Nur Funktionen mit gesetztem Haken sind im Unternehmen verfügbar. Diese Einstellungen gelten für alle Mitarbeiter und sind unabhängig von Berechtigungen."
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="mb-5"
|
class="mb-5"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const refreshData = async () => {
|
|||||||
// select() filtert bereits archivierte Einträge, wenn dataType.isArchivable true ist
|
// select() filtert bereits archivierte Einträge, wenn dataType.isArchivable true ist
|
||||||
texttemplates.value = await select()
|
texttemplates.value = await select()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({ title: 'Fehler beim Laden', description: e.message, color: 'rose' })
|
toast.add({ title: 'Fehler beim Laden', description: e.message, color: 'error' })
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ const handleCreate = async () => {
|
|||||||
editTemplateModalOpen.value = false
|
editTemplateModalOpen.value = false
|
||||||
await refreshData()
|
await refreshData()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({ title: 'Fehler', description: 'Konnte nicht erstellt werden.', color: 'rose' })
|
toast.add({ title: 'Fehler', description: 'Konnte nicht erstellt werden.', color: 'error' })
|
||||||
} finally {
|
} finally {
|
||||||
isSaving.value = false
|
isSaving.value = false
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ const handleUpdate = async () => {
|
|||||||
editTemplateModalOpen.value = false
|
editTemplateModalOpen.value = false
|
||||||
await refreshData()
|
await refreshData()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({title: 'Fehler', description: 'Konnte nicht gespeichert werden.', color: 'rose'})
|
toast.add({title: 'Fehler', description: 'Konnte nicht gespeichert werden.', color: 'error'})
|
||||||
} finally {
|
} finally {
|
||||||
isSaving.value = false
|
isSaving.value = false
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ const handleArchive = async (row) => {
|
|||||||
|
|
||||||
await refreshData()
|
await refreshData()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({title: 'Fehler', description: 'Konnte nicht archiviert werden.', color: 'rose'})
|
toast.add({title: 'Fehler', description: 'Konnte nicht archiviert werden.', color: 'error'})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ const getDocLabel = (type) => {
|
|||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
class="mt-3"
|
class="mt-3"
|
||||||
:rows="texttemplates"
|
:data="texttemplates"
|
||||||
: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' }"
|
||||||
@@ -212,7 +212,7 @@ const getDocLabel = (type) => {
|
|||||||
|
|
||||||
<div class="flex justify-end gap-3">
|
<div class="flex justify-end gap-3">
|
||||||
<ButtonWithConfirm
|
<ButtonWithConfirm
|
||||||
color="rose"
|
color="error"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
icon="i-heroicons-archive-box"
|
icon="i-heroicons-archive-box"
|
||||||
@confirmed="handleArchive(row)"
|
@confirmed="handleArchive(row)"
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ onMounted(fetchProfile)
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UDivider label="Persönliche Daten" />
|
<USeparator label="Persönliche Daten" />
|
||||||
|
|
||||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
<UFormGroup label="Vorname">
|
<UFormGroup label="Vorname">
|
||||||
@@ -211,7 +211,7 @@ onMounted(fetchProfile)
|
|||||||
</UForm>
|
</UForm>
|
||||||
</UCard>
|
</UCard>
|
||||||
<UCard v-if="!pending && profile" class="mt-3">
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
<UDivider label="Vertragsinformationen" />
|
<USeparator label="Vertragsinformationen" />
|
||||||
|
|
||||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
<UFormGroup label="Vertragsart">
|
<UFormGroup label="Vertragsart">
|
||||||
@@ -256,7 +256,7 @@ onMounted(fetchProfile)
|
|||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard v-if="!pending && profile" class="mt-3">
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
<UDivider label="Adresse & Standort" />
|
<USeparator label="Adresse & Standort" />
|
||||||
|
|
||||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
<UFormGroup label="Straße und Hausnummer">
|
<UFormGroup label="Straße und Hausnummer">
|
||||||
@@ -285,7 +285,7 @@ onMounted(fetchProfile)
|
|||||||
|
|
||||||
|
|
||||||
<UCard v-if="!pending && profile" class="mt-3">
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
<UDivider label="Wöchentliche Arbeitsstunden" />
|
<USeparator label="Wöchentliche Arbeitsstunden" />
|
||||||
|
|
||||||
<div class="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div class="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
<div
|
<div
|
||||||
@@ -313,7 +313,7 @@ onMounted(fetchProfile)
|
|||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
<UCard v-if="!pending && profile" class="mt-3">
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
<UDivider label="Sonstiges" />
|
<USeparator label="Sonstiges" />
|
||||||
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
<UFormGroup label="Kleidergröße (Oberteil)">
|
<UFormGroup label="Kleidergröße (Oberteil)">
|
||||||
<UInput v-model="profile.clothing_size_top" />
|
<UInput v-model="profile.clothing_size_top" />
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
<UTable
|
<UTable
|
||||||
:rows="items"
|
:data="items"
|
||||||
:columns="normalizeTableColumns(columns)"
|
:columns="normalizeTableColumns(columns)"
|
||||||
@select="(i) => navigateTo(`/staff/profiles/${i.id}`)"
|
@select="(i) => navigateTo(`/staff/profiles/${i.id}`)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -29,7 +29,16 @@ const platformIsNative = ref(false)
|
|||||||
const selectedPresetRange = ref("Dieser Monat bis heute")
|
const selectedPresetRange = ref("Dieser Monat bis heute")
|
||||||
const selectedStartDay = ref("")
|
const selectedStartDay = ref("")
|
||||||
const selectedEndDay = ref("")
|
const selectedEndDay = ref("")
|
||||||
const openTab = ref(0)
|
const openTab = ref("0")
|
||||||
|
const presetRangeItems = [
|
||||||
|
'Dieser Monat bis heute',
|
||||||
|
'Diese Woche',
|
||||||
|
'Dieser Monat',
|
||||||
|
'Dieses Jahr',
|
||||||
|
'Letzte Woche',
|
||||||
|
'Letzter Monat',
|
||||||
|
'Letztes Jahr'
|
||||||
|
]
|
||||||
|
|
||||||
const showDocument = ref(false)
|
const showDocument = ref(false)
|
||||||
const uri = ref("")
|
const uri = ref("")
|
||||||
@@ -131,7 +140,7 @@ async function loadWorkingTimeInfo() {
|
|||||||
|
|
||||||
workingTimeInfo.value = data;
|
workingTimeInfo.value = data;
|
||||||
|
|
||||||
openTab.value = 0
|
openTab.value = "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 📄 PDF generieren
|
// 📄 PDF generieren
|
||||||
@@ -172,12 +181,12 @@ async function saveFile() {
|
|||||||
toast.add({title:"Auswertung erfolgreich gespeichert"})
|
toast.add({title:"Auswertung erfolgreich gespeichert"})
|
||||||
fileSaved.value = true
|
fileSaved.value = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.add({title:"Fehler beim Speichern der Auswertung", color: "rose"})
|
toast.add({title:"Fehler beim Speichern der Auswertung", color: "error"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTabChange(index: number) {
|
async function onTabChange(index: string | number) {
|
||||||
if (index === 1) await generateDocument()
|
if (String(index) === "1") await generateDocument()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialisierung
|
// Initialisierung
|
||||||
@@ -204,52 +213,44 @@ await setupPage()
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<UDashboardToolbar>
|
<UDashboardToolbar class="py-3">
|
||||||
<template #left>
|
<template #left>
|
||||||
<UFormGroup label="Zeitraum:">
|
<UFormField label="Zeitraum:">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:options="[
|
:items="presetRangeItems"
|
||||||
'Dieser Monat bis heute',
|
|
||||||
'Diese Woche',
|
|
||||||
'Dieser Monat',
|
|
||||||
'Dieses Jahr',
|
|
||||||
'Letzte Woche',
|
|
||||||
'Letzter Monat',
|
|
||||||
'Letztes Jahr'
|
|
||||||
]"
|
|
||||||
v-model="selectedPresetRange"
|
v-model="selectedPresetRange"
|
||||||
@change="changeRange"
|
@change="changeRange"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
|
|
||||||
<UFormGroup label="Start:">
|
<UFormField label="Start:">
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
<UPopover :content="{ side: 'bottom', align: 'start' }">
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="selectedStartDay ? $dayjs(selectedStartDay).format('DD.MM.YYYY') : 'Datum wählen'"
|
:label="selectedStartDay ? $dayjs(selectedStartDay).format('DD.MM.YYYY') : 'Datum wählen'"
|
||||||
/>
|
/>
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker v-model="selectedStartDay" @close="loadWorkingTimeInfo" />
|
<LazyDatePicker v-model="selectedStartDay" @close="loadWorkingTimeInfo" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
|
|
||||||
<UFormGroup label="Ende:">
|
<UFormField label="Ende:">
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
<UPopover :content="{ side: 'bottom', align: 'start' }">
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
icon="i-heroicons-calendar-days-20-solid"
|
||||||
:label="selectedEndDay ? $dayjs(selectedEndDay).format('DD.MM.YYYY') : 'Datum wählen'"
|
:label="selectedEndDay ? $dayjs(selectedEndDay).format('DD.MM.YYYY') : 'Datum wählen'"
|
||||||
/>
|
/>
|
||||||
<template #panel="{ close }">
|
<template #content>
|
||||||
<LazyDatePicker v-model="selectedEndDay" @close="loadWorkingTimeInfo" />
|
<LazyDatePicker v-model="selectedEndDay" @close="loadWorkingTimeInfo" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
</UFormGroup>
|
</UFormField>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<UTooltip
|
<UTooltip
|
||||||
:text="fileSaved ? 'Bericht bereits gespeichert' : 'Bericht speichern'"
|
:text="fileSaved ? 'Bericht bereits gespeichert' : 'Bericht speichern'"
|
||||||
v-if="openTab === 1 && uri"
|
v-if="openTab === '1' && uri"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-mdi-content-save"
|
icon="i-mdi-content-save"
|
||||||
@@ -265,9 +266,9 @@ await setupPage()
|
|||||||
<UTabs
|
<UTabs
|
||||||
:items="[{ label: 'Information' }, { label: 'Bericht' }]"
|
:items="[{ label: 'Information' }, { label: 'Bericht' }]"
|
||||||
v-model="openTab"
|
v-model="openTab"
|
||||||
@change="onTabChange"
|
@update:model-value="onTabChange"
|
||||||
>
|
>
|
||||||
<template #item="{ item }">
|
<template #content="{ item }">
|
||||||
<div v-if="item.label === 'Information'">
|
<div v-if="item.label === 'Information'">
|
||||||
|
|
||||||
<UCard v-if="workingTimeInfo && workingTimeInfo.summary" class="my-5">
|
<UCard v-if="workingTimeInfo && workingTimeInfo.summary" class="my-5">
|
||||||
@@ -294,8 +295,8 @@ await setupPage()
|
|||||||
<UDashboardPanel>
|
<UDashboardPanel>
|
||||||
<UTable
|
<UTable
|
||||||
v-if="workingTimeInfo"
|
v-if="workingTimeInfo"
|
||||||
:rows="workingTimeInfo.spans"
|
:data="workingTimeInfo.spans"
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Anwesenheiten' }"
|
:empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Anwesenheiten' }"
|
||||||
:columns="normalizeTableColumns([
|
:columns="normalizeTableColumns([
|
||||||
{ key: 'status', label: 'Status' },
|
{ key: 'status', label: 'Status' },
|
||||||
{ key: 'startedAt', label: 'Start' },
|
{ key: 'startedAt', label: 'Start' },
|
||||||
@@ -303,30 +304,30 @@ await setupPage()
|
|||||||
{ 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]}`)"
|
:on-select="(row) => router.push(`/workingtimes/edit/${row.original.sourceEventIds[0]}`)"
|
||||||
>
|
>
|
||||||
<template #status-data="{row}">
|
<template #status-cell="{ row }">
|
||||||
<span v-if="row.status === 'approved'" class="text-primary-500">Genehmigt</span>
|
<span v-if="row.original.status === 'approved'" class="text-primary-500">Genehmigt</span>
|
||||||
<span v-else-if="row.status === 'submitted'" class="text-cyan-500">Eingereicht</span>
|
<span v-else-if="row.original.status === 'submitted'" class="text-cyan-500">Eingereicht</span>
|
||||||
<span v-else-if="row.status === 'factual'" class="text-gray-500">Faktisch</span>
|
<span v-else-if="row.original.status === 'factual'" class="text-gray-500">Faktisch</span>
|
||||||
<span v-else-if="row.status === 'draft'" class="text-red-500">Entwurf</span>
|
<span v-else-if="row.original.status === 'draft'" class="text-error-500">Entwurf</span>
|
||||||
<span v-else>{{ row.status }}</span>
|
<span v-else>{{ row.original.status }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #startedAt-data="{ row }">
|
<template #startedAt-cell="{ row }">
|
||||||
{{ $dayjs(row.startedAt).format('HH:mm DD.MM.YY') }} Uhr
|
{{ $dayjs(row.original.startedAt).format('HH:mm DD.MM.YY') }} Uhr
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #endedAt-data="{ row }">
|
<template #endedAt-cell="{ row }">
|
||||||
{{ $dayjs(row.endedAt).format('HH:mm DD.MM.YY') }} Uhr
|
{{ $dayjs(row.original.endedAt).format('HH:mm DD.MM.YY') }} Uhr
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #duration-data="{ row }">
|
<template #duration-cell="{ row }">
|
||||||
{{ formatSpanDuration(row.startedAt, row.endedAt) }}
|
{{ formatSpanDuration(row.original.startedAt, row.original.endedAt) }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #type-data="{ row }">
|
<template #type-cell="{ row }">
|
||||||
{{ row.type.charAt(0).toUpperCase() + row.type.slice(1).replace('_', ' ') }}
|
{{ row.original.type.charAt(0).toUpperCase() + row.original.type.slice(1).replace('_', ' ') }}
|
||||||
</template>
|
</template>
|
||||||
</UTable>
|
</UTable>
|
||||||
</UDashboardPanel>
|
</UDashboardPanel>
|
||||||
@@ -360,15 +361,7 @@ await setupPage()
|
|||||||
<div class="p-4 space-y-4 border-b bg-white dark:bg-gray-900">
|
<div class="p-4 space-y-4 border-b bg-white dark:bg-gray-900">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedPresetRange"
|
v-model="selectedPresetRange"
|
||||||
:options="[
|
:items="presetRangeItems"
|
||||||
'Dieser Monat bis heute',
|
|
||||||
'Diese Woche',
|
|
||||||
'Dieser Monat',
|
|
||||||
'Dieses Jahr',
|
|
||||||
'Letzte Woche',
|
|
||||||
'Letzter Monat',
|
|
||||||
'Letztes Jahr'
|
|
||||||
]"
|
|
||||||
@change="changeRange"
|
@change="changeRange"
|
||||||
placeholder="Zeitraum wählen"
|
placeholder="Zeitraum wählen"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@@ -377,13 +370,13 @@ await setupPage()
|
|||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-gray-500 mb-1">Start</p>
|
<p class="text-xs text-gray-500 mb-1">Start</p>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
<UPopover :content="{ side: 'bottom', align: 'start' }">
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-calendar"
|
icon="i-heroicons-calendar"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:label="$dayjs(selectedStartDay).format('DD.MM.YYYY')"
|
:label="$dayjs(selectedStartDay).format('DD.MM.YYYY')"
|
||||||
/>
|
/>
|
||||||
<template #panel>
|
<template #content>
|
||||||
<LazyDatePicker v-model="selectedStartDay" @close="loadWorkingTimeInfo" />
|
<LazyDatePicker v-model="selectedStartDay" @close="loadWorkingTimeInfo" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
@@ -391,13 +384,13 @@ await setupPage()
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs text-gray-500 mb-1">Ende</p>
|
<p class="text-xs text-gray-500 mb-1">Ende</p>
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
<UPopover :content="{ side: 'bottom', align: 'start' }">
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-calendar"
|
icon="i-heroicons-calendar"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:label="$dayjs(selectedEndDay).format('DD.MM.YYYY')"
|
:label="$dayjs(selectedEndDay).format('DD.MM.YYYY')"
|
||||||
/>
|
/>
|
||||||
<template #panel>
|
<template #content>
|
||||||
<LazyDatePicker v-model="selectedEndDay" @close="loadWorkingTimeInfo" />
|
<LazyDatePicker v-model="selectedEndDay" @close="loadWorkingTimeInfo" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
@@ -408,12 +401,11 @@ await setupPage()
|
|||||||
<UTabs
|
<UTabs
|
||||||
:items="[{ label: 'Information' }, { label: 'Bericht' }]"
|
:items="[{ label: 'Information' }, { label: 'Bericht' }]"
|
||||||
v-model="openTab"
|
v-model="openTab"
|
||||||
@change="onTabChange"
|
@update:model-value="onTabChange"
|
||||||
class="mt-3 mx-3"
|
class="mt-3 mx-3"
|
||||||
>
|
>
|
||||||
<template #item="{ item }">
|
<template #content="{ item }">
|
||||||
|
<div v-if="item.label === 'Information'" class="space-y-4">
|
||||||
<div v-if="item.label === 'Information'" class="space-y-4">
|
|
||||||
|
|
||||||
<UCard v-if="workingTimeInfo && workingTimeInfo.summary" class="mt-3">
|
<UCard v-if="workingTimeInfo && workingTimeInfo.summary" class="mt-3">
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -459,7 +451,7 @@ await setupPage()
|
|||||||
|
|
||||||
<div v-else-if="item.label === 'Bericht'">
|
<div v-else-if="item.label === 'Bericht'">
|
||||||
<UButton
|
<UButton
|
||||||
v-if="uri && !fileSaved"
|
v-if="openTab === '1' && uri && !fileSaved"
|
||||||
icon="i-mdi-content-save"
|
icon="i-mdi-content-save"
|
||||||
color="primary"
|
color="primary"
|
||||||
class="w-full mb-3"
|
class="w-full mb-3"
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ const rejectReason = ref("")
|
|||||||
const users = ref([])
|
const users = ref([])
|
||||||
const selectedUser = ref(auth.user.id)
|
const selectedUser = ref(auth.user.id)
|
||||||
const canViewAll = computed(() => auth.permissions.includes('staff.time.read_all'))
|
const canViewAll = computed(() => auth.permissions.includes('staff.time.read_all'))
|
||||||
|
const userItems = computed(() => users.value.map(u => ({
|
||||||
|
label: u.full_name || u.email,
|
||||||
|
value: u.user_id
|
||||||
|
})))
|
||||||
|
|
||||||
// DATA
|
// DATA
|
||||||
const entries = ref([])
|
const entries = ref([])
|
||||||
@@ -61,7 +65,7 @@ const typeLabel = {
|
|||||||
const typeColor = {
|
const typeColor = {
|
||||||
work: "gray",
|
work: "gray",
|
||||||
vacation: "yellow",
|
vacation: "yellow",
|
||||||
sick: "rose",
|
sick: "error",
|
||||||
holiday: "blue",
|
holiday: "blue",
|
||||||
other: "gray"
|
other: "gray"
|
||||||
}
|
}
|
||||||
@@ -130,7 +134,7 @@ async function confirmReject() {
|
|||||||
showRejectModal.value = false
|
showRejectModal.value = false
|
||||||
await load()
|
await load()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({ title: 'Fehler beim Ablehnen', description: e.message, color: 'red' })
|
toast.add({ title: 'Fehler beim Ablehnen', description: e.message, color: 'error' })
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
entryToReject.value = null
|
entryToReject.value = null
|
||||||
@@ -167,10 +171,10 @@ onMounted(async () => {
|
|||||||
<div v-if="canViewAll" class="flex items-center gap-2">
|
<div v-if="canViewAll" class="flex items-center gap-2">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedUser"
|
v-model="selectedUser"
|
||||||
:options="users.map(u => ({ label: u.full_name || u.email, value: u.user_id }))"
|
:items="userItems"
|
||||||
placeholder="Benutzer auswählen"
|
placeholder="Benutzer auswählen"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="label"
|
label-key="label"
|
||||||
class="min-w-[220px]"
|
class="min-w-[220px]"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
/>
|
/>
|
||||||
@@ -211,11 +215,11 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="isViewingSelf">
|
<template v-if="isViewingSelf">
|
||||||
<UButton v-if="active" color="red" icon="i-heroicons-stop" :loading="loading" label="Stoppen" @click="handleStop" />
|
<UButton v-if="active" color="error" icon="i-heroicons-stop" :loading="loading" label="Stoppen" @click="handleStop" />
|
||||||
<UButton v-else color="green" icon="i-heroicons-play" :loading="loading" label="Starten" @click="handleStart" />
|
<UButton v-else color="green" icon="i-heroicons-play" :loading="loading" label="Starten" @click="handleStart" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="active && canViewAll">
|
<template v-else-if="active && canViewAll">
|
||||||
<UButton color="red" variant="soft" icon="i-heroicons-stop" :loading="loading" label="Mitarbeiter stoppen" @click="handleStop" />
|
<UButton color="error" variant="soft" icon="i-heroicons-stop" :loading="loading" label="Mitarbeiter stoppen" @click="handleStop" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<UButton color="gray" variant="solid" icon="i-heroicons-plus" label="Erfassen" @click="() => { entryToEdit = null; showEditModal = true }" />
|
<UButton color="gray" variant="solid" icon="i-heroicons-plus" label="Erfassen" @click="() => { entryToEdit = null; showEditModal = true }" />
|
||||||
@@ -227,7 +231,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"
|
:data="entries"
|
||||||
:columns="normalizeTableColumns([
|
:columns="normalizeTableColumns([
|
||||||
{ key: 'actions', label: 'Aktionen', class: 'w-32' },
|
{ key: 'actions', label: 'Aktionen', class: 'w-32' },
|
||||||
{ key: 'state', label: 'Status' },
|
{ key: 'state', label: 'Status' },
|
||||||
@@ -237,49 +241,49 @@ onMounted(async () => {
|
|||||||
{ 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="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Zeiten anzuzeigen' }"
|
||||||
>
|
>
|
||||||
<template #state-data="{ row }">
|
<template #state-cell="{ row }">
|
||||||
<UBadge v-if="row.state === 'approved'" color="green" variant="subtle">Genehmigt</UBadge>
|
<UBadge v-if="row.original.state === 'approved'" color="green" variant="subtle">Genehmigt</UBadge>
|
||||||
<UBadge v-else-if="row.state === 'submitted'" color="cyan" variant="subtle">Eingereicht</UBadge>
|
<UBadge v-else-if="row.original.state === 'submitted'" color="cyan" variant="subtle">Eingereicht</UBadge>
|
||||||
<UBadge v-else-if="row.state === 'rejected'" color="red" variant="subtle">Abgelehnt</UBadge>
|
<UBadge v-else-if="row.original.state === 'rejected'" color="error" variant="subtle">Abgelehnt</UBadge>
|
||||||
<UBadge v-else color="gray" variant="subtle">Entwurf</UBadge>
|
<UBadge v-else color="gray" variant="subtle">Entwurf</UBadge>
|
||||||
</template>
|
</template>
|
||||||
<template #type-data="{ row }">
|
<template #type-cell="{ row }">
|
||||||
<UBadge :color="typeColor[row.type] || 'gray'" variant="soft">{{ typeLabel[row.type] || row.type }}</UBadge>
|
<UBadge :color="typeColor[row.original.type] || 'gray'" variant="soft">{{ typeLabel[row.original.type] || row.original.type }}</UBadge>
|
||||||
</template>
|
</template>
|
||||||
<template #started_at-data="{ row }">
|
<template #started_at-cell="{ row }">
|
||||||
<span v-if="['vacation','sick'].includes(row.type)">{{ useNuxtApp().$dayjs(row.started_at).format("DD.MM.YY") }}</span>
|
<span v-if="['vacation','sick'].includes(row.original.type)">{{ useNuxtApp().$dayjs(row.original.started_at).format("DD.MM.YY") }}</span>
|
||||||
<span v-else>{{ useNuxtApp().$dayjs(row.started_at).format("DD.MM.YY HH:mm") }}</span>
|
<span v-else>{{ useNuxtApp().$dayjs(row.original.started_at).format("DD.MM.YY HH:mm") }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #stopped_at-data="{ row }">
|
<template #stopped_at-cell="{ row }">
|
||||||
<span v-if="!row.stopped_at" class="text-primary-500 font-medium animate-pulse">läuft...</span>
|
<span v-if="!row.original.stopped_at" class="text-primary-500 font-medium animate-pulse">läuft...</span>
|
||||||
<span v-else-if="['vacation','sick'].includes(row.type)">{{ useNuxtApp().$dayjs(row.stopped_at).format("DD.MM.YY") }}</span>
|
<span v-else-if="['vacation','sick'].includes(row.original.type)">{{ useNuxtApp().$dayjs(row.original.stopped_at).format("DD.MM.YY") }}</span>
|
||||||
<span v-else>{{ useNuxtApp().$dayjs(row.stopped_at).format("DD.MM.YY HH:mm") }}</span>
|
<span v-else>{{ useNuxtApp().$dayjs(row.original.stopped_at).format("DD.MM.YY HH:mm") }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #duration_minutes-data="{ row }">
|
<template #duration_minutes-cell="{ row }">
|
||||||
{{ row.duration_minutes ? useFormatDuration(row.duration_minutes) : "-" }}
|
{{ row.original.duration_minutes ? useFormatDuration(row.original.duration_minutes) : "-" }}
|
||||||
</template>
|
</template>
|
||||||
<template #actions-data="{ row }">
|
<template #actions-cell="{ row }">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<UTooltip text="Einreichen" v-if="(row.state === 'draft' || row.state === 'factual') && row.stopped_at">
|
<UTooltip text="Einreichen" v-if="(row.original.state === 'draft' || row.original.state === 'factual') && row.original.stopped_at">
|
||||||
<UButton size="xs" color="cyan" variant="ghost" icon="i-heroicons-paper-airplane" @click="handleSubmit(row)" :loading="loading" />
|
<UButton size="xs" color="cyan" variant="ghost" icon="i-heroicons-paper-airplane" @click="handleSubmit(row.original)" :loading="loading" />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
<UTooltip text="Genehmigen" v-if="row.state === 'submitted' && canViewAll">
|
<UTooltip text="Genehmigen" v-if="row.original.state === 'submitted' && canViewAll">
|
||||||
<UButton size="xs" color="green" variant="ghost" icon="i-heroicons-check" @click="handleApprove(row)" :loading="loading" />
|
<UButton size="xs" color="green" variant="ghost" icon="i-heroicons-check" @click="handleApprove(row.original)" :loading="loading" />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
<UTooltip text="Ablehnen" v-if="(row.state === 'submitted' || row.state === 'approved') && canViewAll">
|
<UTooltip text="Ablehnen" v-if="(row.original.state === 'submitted' || row.original.state === 'approved') && canViewAll">
|
||||||
<UButton size="xs" color="red" variant="ghost" icon="i-heroicons-x-mark" @click="openRejectModal(row)" :loading="loading" />
|
<UButton size="xs" color="error" variant="ghost" icon="i-heroicons-x-mark" @click="openRejectModal(row.original)" :loading="loading" />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
<UTooltip text="Bearbeiten" v-if="['draft', 'factual', 'submitted'].includes(row.state)">
|
<UTooltip text="Bearbeiten" v-if="['draft', 'factual', 'submitted'].includes(row.original.state)">
|
||||||
<UButton size="xs" color="gray" variant="ghost" icon="i-heroicons-pencil-square" @click="handleEdit(row)" />
|
<UButton size="xs" color="gray" variant="ghost" icon="i-heroicons-pencil-square" @click="handleEdit(row.original)" />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #description-data="{ row }">
|
<template #description-cell="{ row }">
|
||||||
<span v-if="row.type === 'vacation'">{{row.vacation_reason}}</span>
|
<span v-if="row.original.type === 'vacation'">{{ row.original.vacation_reason }}</span>
|
||||||
<span v-else-if="row.type === 'sick'">{{row.sick_reason}}</span>
|
<span v-else-if="row.original.type === 'sick'">{{ row.original.sick_reason }}</span>
|
||||||
<span v-else>{{row.description}}</span>
|
<span v-else>{{ row.original.description }}</span>
|
||||||
</template>
|
</template>
|
||||||
</UTable>
|
</UTable>
|
||||||
</UCard>
|
</UCard>
|
||||||
@@ -334,7 +338,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<UBadge v-if="entry.state === 'approved'" color="green" size="xs" variant="solid">Genehmigt</UBadge>
|
<UBadge v-if="entry.state === 'approved'" color="green" size="xs" variant="solid">Genehmigt</UBadge>
|
||||||
<UBadge v-else-if="entry.state === 'submitted'" color="cyan" size="xs" variant="solid">Eingereicht</UBadge>
|
<UBadge v-else-if="entry.state === 'submitted'" color="cyan" size="xs" variant="solid">Eingereicht</UBadge>
|
||||||
<UBadge v-else-if="entry.state === 'rejected'" color="red" size="xs" variant="solid">Abgelehnt</UBadge>
|
<UBadge v-else-if="entry.state === 'rejected'" color="error" size="xs" variant="solid">Abgelehnt</UBadge>
|
||||||
<UBadge v-else color="gray" size="xs" variant="subtle">Entwurf</UBadge>
|
<UBadge v-else color="gray" size="xs" variant="subtle">Entwurf</UBadge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -363,7 +367,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
v-if="(entry.state === 'submitted' || entry.state === 'approved') && canViewAll"
|
v-if="(entry.state === 'submitted' || entry.state === 'approved') && canViewAll"
|
||||||
size="xs" color="red" variant="ghost" icon="i-heroicons-x-mark" label="Ablehnen"
|
size="xs" color="error" variant="ghost" icon="i-heroicons-x-mark" label="Ablehnen"
|
||||||
@click="openRejectModal(entry)" :loading="loading"
|
@click="openRejectModal(entry)" :loading="loading"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -403,7 +407,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<template v-if="isViewingSelf">
|
<template v-if="isViewingSelf">
|
||||||
<UButton v-if="active" color="red" icon="i-heroicons-stop" :loading="loading" @click="handleStop" />
|
<UButton v-if="active" color="error" icon="i-heroicons-stop" :loading="loading" @click="handleStop" />
|
||||||
<UButton v-else color="green" icon="i-heroicons-play" :loading="loading" @click="handleStart" />
|
<UButton v-else color="green" icon="i-heroicons-play" :loading="loading" @click="handleStart" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -422,7 +426,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
<UBadge v-if="row.state === 'approved'" color="green">Genehmigt</UBadge>
|
<UBadge v-if="row.state === 'approved'" color="green">Genehmigt</UBadge>
|
||||||
<UBadge v-else-if="row.state === 'submitted'" color="cyan">Eingereicht</UBadge>
|
<UBadge v-else-if="row.state === 'submitted'" color="cyan">Eingereicht</UBadge>
|
||||||
<UBadge v-else-if="row.state === 'rejected'" color="red">Abgelehnt</UBadge>
|
<UBadge v-else-if="row.state === 'rejected'" color="error">Abgelehnt</UBadge>
|
||||||
<UBadge v-else color="gray">Entwurf</UBadge>
|
<UBadge v-else color="gray">Entwurf</UBadge>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-gray-500 mt-1">Start: {{ useNuxtApp().$dayjs(row.started_at).format('DD.MM.YY HH:mm') }}</p>
|
<p class="text-sm text-gray-500 mt-1">Start: {{ useNuxtApp().$dayjs(row.started_at).format('DD.MM.YY HH:mm') }}</p>
|
||||||
@@ -445,29 +449,31 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<UModal v-model:open="showRejectModal">
|
<UModal v-model:open="showRejectModal">
|
||||||
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
<template #content>
|
||||||
<template #header>
|
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||||
<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">
|
||||||
Zeiteintrag ablehnen
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
</h3>
|
Zeiteintrag ablehnen
|
||||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="showRejectModal = false" />
|
</h3>
|
||||||
|
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="showRejectModal = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
Der Eintrag wird als "Rejected" markiert und nicht mehr zur Arbeitszeit gezählt.
|
||||||
|
</p>
|
||||||
|
<UFormField label="Grund (optional)" name="reason">
|
||||||
|
<UTextarea v-model="rejectReason" placeholder="Falsche Buchung, Doppelt, etc." autofocus />
|
||||||
|
</UFormField>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<template #footer>
|
||||||
<div class="space-y-4">
|
<div class="flex justify-end gap-2">
|
||||||
<p class="text-sm text-gray-500">
|
<UButton color="gray" variant="soft" @click="showRejectModal = false">Abbrechen</UButton>
|
||||||
Der Eintrag wird als "Rejected" markiert und nicht mehr zur Arbeitszeit gezählt.
|
<UButton color="error" :loading="loading" @click="confirmReject">Bestätigen</UButton>
|
||||||
</p>
|
</div>
|
||||||
<UFormGroup label="Grund (optional)" name="reason">
|
</template>
|
||||||
<UTextarea v-model="rejectReason" placeholder="Falsche Buchung, Doppelt, etc." autofocus />
|
</UCard>
|
||||||
</UFormGroup>
|
</template>
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="flex justify-end gap-2">
|
|
||||||
<UButton color="gray" variant="soft" @click="showRejectModal = false">Abbrechen</UButton>
|
|
||||||
<UButton color="red" :loading="loading" @click="confirmReject">Bestätigen</UButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UCard>
|
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -73,6 +73,24 @@ const sort = ref({
|
|||||||
column: dataType.sortColumn || "created_at",
|
column: dataType.sortColumn || "created_at",
|
||||||
direction: 'desc'
|
direction: 'desc'
|
||||||
})
|
})
|
||||||
|
const sorting = computed({
|
||||||
|
get: () => [{
|
||||||
|
id: sort.value.column,
|
||||||
|
desc: sort.value.direction === 'desc'
|
||||||
|
}],
|
||||||
|
set: (value) => {
|
||||||
|
const nextSort = Array.isArray(value) ? value[0] : undefined
|
||||||
|
if (!nextSort?.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.value = {
|
||||||
|
column: nextSort.id,
|
||||||
|
direction: nextSort.desc ? 'desc' : 'asc'
|
||||||
|
}
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const columnsToFilter = ref({})
|
const columnsToFilter = ref({})
|
||||||
|
|
||||||
@@ -282,6 +300,19 @@ const handleFilterChange = async (action,column) => {
|
|||||||
setupPage()
|
setupPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const truncateValue = (value, maxLength) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '\u00A0'
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringValue = String(value)
|
||||||
|
if (!maxLength || stringValue.length <= maxLength) {
|
||||||
|
return stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${stringValue.substring(0, maxLength)}...`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -313,7 +344,7 @@ const handleFilterChange = async (action,column) => {
|
|||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="clearSearchString()"
|
@click="clearSearchString()"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@@ -336,10 +367,10 @@ const handleFilterChange = async (action,column) => {
|
|||||||
<template #left>
|
<template #left>
|
||||||
<UTooltip :text="`${dataType.label} pro Seite`">
|
<UTooltip :text="`${dataType.label} pro Seite`">
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:options="[{value:10},{value:15, disabled: itemsMeta.total < 15},{value:25, disabled: itemsMeta.total < 25},{value:50, disabled: itemsMeta.total < 50},{value:100, disabled: itemsMeta.total < 100},{value:250, disabled: itemsMeta.total < 250}]"
|
:items="[{value:10},{value:15, disabled: itemsMeta.total < 15},{value:25, disabled: itemsMeta.total < 25},{value:50, disabled: itemsMeta.total < 50},{value:100, disabled: itemsMeta.total < 100},{value:250, disabled: itemsMeta.total < 250}]"
|
||||||
v-model="pageLimit"
|
v-model="pageLimit"
|
||||||
value-attribute="value"
|
value-key="value"
|
||||||
option-attribute="value"
|
label-key="value"
|
||||||
@change="setupPage"
|
@change="setupPage"
|
||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
@@ -363,15 +394,15 @@ const handleFilterChange = async (action,column) => {
|
|||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selectedColumns"
|
v-model="selectedColumns"
|
||||||
icon="i-heroicons-adjustments-horizontal-solid"
|
icon="i-heroicons-adjustments-horizontal-solid"
|
||||||
:options="dataType.templateColumns.filter(i => !i.disabledInTable)"
|
:items="dataType.templateColumns.filter(i => !i.disabledInTable)"
|
||||||
multiple
|
multiple
|
||||||
class="hidden lg:block"
|
class="hidden lg:block"
|
||||||
by="key"
|
by="key"
|
||||||
:color="selectedColumns.length !== dataType.templateColumns.filter(i => !i.disabledInTable).length ? 'primary' : 'white'"
|
:color="selectedColumns.length !== dataType.templateColumns.filter(i => !i.disabledInTable).length ? 'primary' : 'white'"
|
||||||
:ui-menu="{ width: 'min-w-max' }"
|
:content="{ width: 'min-w-max' }"
|
||||||
@change="tempStore.modifyColumns(type,selectedColumns)"
|
@change="tempStore.modifyColumns(type,selectedColumns)"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #default>
|
||||||
Spalten
|
Spalten
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
@@ -382,18 +413,15 @@ const handleFilterChange = async (action,column) => {
|
|||||||
<div v-if="!platformIsNative">
|
<div v-if="!platformIsNative">
|
||||||
<UTable
|
<UTable
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:loading-state="{ icon: 'i-heroicons-arrow-path-20-solid', label: 'Loading...' }"
|
v-model:sorting="sorting"
|
||||||
sort-mode="manual"
|
|
||||||
v-model:sort="sort"
|
|
||||||
@update:sort="setupPage"
|
|
||||||
v-if="dataType && columns && items.length > 0 && !loading"
|
v-if="dataType && columns && items.length > 0 && !loading"
|
||||||
:rows="items"
|
:data="items"
|
||||||
:columns="normalizeTableColumns(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' }"
|
||||||
@select="(i) => router.push(`/standardEntity/${type}/show/${i.id}`) "
|
:on-select="(row) => router.push(`/standardEntity/${type}/show/${row.original.id}`)"
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine ${dataType.label} anzuzeigen` }"
|
:empty="`Keine ${dataType.label} anzuzeigen`"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="column in dataType.templateColumns.filter(i => !i.disabledInTable)"
|
v-for="column in dataType.templateColumns.filter(i => !i.disabledInTable)"
|
||||||
@@ -414,15 +442,14 @@ const handleFilterChange = async (action,column) => {
|
|||||||
:text="!columnsToFilter[column.key]?.length > 0 ? `Keine Einträge für ${column.label} verfügbar` : `${column.label} Spalte nach Einträgen filtern`"
|
:text="!columnsToFilter[column.key]?.length > 0 ? `Keine Einträge für ${column.label} verfügbar` : `${column.label} Spalte nach Einträgen filtern`"
|
||||||
>
|
>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
:options="itemsMeta?.distinctValues?.[column.key]"
|
:items="(itemsMeta?.distinctValues?.[column.key] || []).map(value => ({ label: value, value }))"
|
||||||
v-model="columnsToFilter[column.key]"
|
v-model="columnsToFilter[column.key]"
|
||||||
multiple
|
multiple
|
||||||
@change="handleFilterChange('change', column.key)"
|
@change="handleFilterChange('change', column.key)"
|
||||||
searchable
|
:search-input="{ placeholder: 'Suche...' }"
|
||||||
searchable-placeholder="Suche..."
|
value-key="value"
|
||||||
:search-attributes="[column.key]"
|
label-key="label"
|
||||||
:ui-menu="{ width: 'min-w-max' }"
|
:content="{ width: 'min-w-max' }"
|
||||||
clear-search-on-close
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
@@ -458,7 +485,7 @@ const handleFilterChange = async (action,column) => {
|
|||||||
<UButton
|
<UButton
|
||||||
@click="handleFilterChange('reset',column.key)"
|
@click="handleFilterChange('reset',column.key)"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="rose"
|
color="error"
|
||||||
>
|
>
|
||||||
X
|
X
|
||||||
</UButton>
|
</UButton>
|
||||||
@@ -468,48 +495,54 @@ const handleFilterChange = async (action,column) => {
|
|||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<template #name-data="{row}">
|
<template #name-cell="{row}">
|
||||||
<span
|
<span
|
||||||
v-if="row.id === items[selectedItem].id"
|
v-if="row.original.id === items[selectedItem]?.id"
|
||||||
class="text-primary-500 font-bold">
|
class="block truncate text-primary-500 font-bold">
|
||||||
<UTooltip
|
<UTooltip
|
||||||
:text="row.name"
|
:text="row.original.name"
|
||||||
>
|
>
|
||||||
{{dataType.templateColumns.find(i => i.key === "name").maxLength ? (row.name.length > dataType.templateColumns.find(i => i.key === "name").maxLength ? `${row.name.substring(0,dataType.templateColumns.find(i => i.key === "name").maxLength)}...` : row.name ) : row.name}}
|
<span class="block truncate">
|
||||||
|
{{ truncateValue(row.original.name, dataType.templateColumns.find(i => i.key === "name")?.maxLength) }}
|
||||||
|
</span>
|
||||||
</UTooltip> </span>
|
</UTooltip> </span>
|
||||||
<span v-else>
|
<span v-else class="block truncate">
|
||||||
<UTooltip
|
<UTooltip
|
||||||
:text="row.name"
|
:text="row.original.name"
|
||||||
>
|
>
|
||||||
{{dataType.templateColumns.find(i => i.key === "name").maxLength ? (row.name.length > dataType.templateColumns.find(i => i.key === "name").maxLength ? `${row.name.substring(0,dataType.templateColumns.find(i => i.key === "name").maxLength)}...` : row.name ) : row.name}}
|
<span class="block truncate">
|
||||||
|
{{ truncateValue(row.original.name, dataType.templateColumns.find(i => i.key === "name")?.maxLength) }}
|
||||||
|
</span>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #fullName-data="{row}">
|
<template #fullName-cell="{row}">
|
||||||
<span
|
<span
|
||||||
v-if="row.id === items[selectedItem].id"
|
v-if="row.original.id === items[selectedItem]?.id"
|
||||||
class="text-primary-500 font-bold">{{row.fullName}}
|
class="block truncate text-primary-500 font-bold">{{ row.original.fullName }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else class="block truncate">
|
||||||
{{row.fullName}}
|
{{ row.original.fullName }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #licensePlate-data="{row}">
|
<template #licensePlate-cell="{row}">
|
||||||
<span
|
<span
|
||||||
v-if="row.id === items[selectedItem].id"
|
v-if="row.original.id === items[selectedItem]?.id"
|
||||||
class="text-primary-500 font-bold">{{row.licensePlate}}
|
class="block truncate text-primary-500 font-bold">{{ row.original.licensePlate }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else class="block truncate">
|
||||||
{{row.licensePlate}}
|
{{ row.original.licensePlate }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
|
v-for="column in dataType.templateColumns.filter(i => i.key !== 'name' && i.key !== 'fullName' && i.key !== 'licensePlate' && !i.disabledInTable)"
|
||||||
v-slot:[`${column.key}-data`]="{row}">
|
v-slot:[`${column.key}-cell`]="{row}">
|
||||||
<component v-if="column.component" :is="column.component" :row="row"></component>
|
<component v-if="column.component" :is="column.component" :row="row.original"></component>
|
||||||
<span v-else-if="row[column.key]">
|
<span v-else-if="row.original[column.key]" class="block truncate">
|
||||||
<UTooltip :text="row[column.key]">
|
<UTooltip :text="String(row.original[column.key])">
|
||||||
{{row[column.key] ? `${column.maxLength ? (row[column.key].length > column.maxLength ? `${row[column.key].substring(0,column.maxLength)}...` : row[column.key]) : row[column.key]} ${column.unit ? column.unit : ''}`: ''}}
|
<span class="block truncate">
|
||||||
|
{{ `${truncateValue(row.original[column.key], column.maxLength)}${column.unit ? ` ${column.unit}` : ''}` }}
|
||||||
|
</span>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -550,7 +583,7 @@ const handleFilterChange = async (action,column) => {
|
|||||||
v-if="searchString.length > 0"
|
v-if="searchString.length > 0"
|
||||||
icon="i-heroicons-x-mark"
|
icon="i-heroicons-x-mark"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="rose"
|
color="error"
|
||||||
@click="clearSearchString()"
|
@click="clearSearchString()"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
@@ -687,7 +720,7 @@ const handleFilterChange = async (action,column) => {
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
color="rose"
|
color="error"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
@click="resetMobileFilters"
|
@click="resetMobileFilters"
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const filteredRows = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
</UDashboardToolbar>
|
</UDashboardToolbar>
|
||||||
<UTable
|
<UTable
|
||||||
:rows="filteredRows"
|
:data="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="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'}])"
|
: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'}])"
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
<UTable
|
<UTable
|
||||||
v-else-if="filteredTasks.length"
|
v-else-if="filteredTasks.length"
|
||||||
:rows="filteredTasks"
|
:data="filteredTasks"
|
||||||
:columns="normalizedListColumns"
|
:columns="normalizedListColumns"
|
||||||
@select="(task) => openTaskViaRoute(task)"
|
@select="(task) => openTaskViaRoute(task)"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user