This commit is contained in:
2025-12-27 12:56:54 +01:00
parent 7d4adbb3e4
commit d5999bfb20
9 changed files with 249 additions and 135 deletions

View File

@@ -1,4 +1,6 @@
<script setup > <script setup>
// Falls useDropZone nicht auto-importiert wird:
// import { useDropZone } from '@vueuse/core'
const props = defineProps({ const props = defineProps({
fileData: { fileData: {
@@ -12,11 +14,35 @@ const props = defineProps({
const emit = defineEmits(["uploadFinished"]) const emit = defineEmits(["uploadFinished"])
const modal = useModal() const modal = useModal()
const profileStore = useProfileStore() // const profileStore = useProfileStore() // Wird im Snippet nicht genutzt, aber ich lasse es drin
const uploadInProgress = ref(false) const uploadInProgress = ref(false)
const availableFiletypes = ref([]) const availableFiletypes = ref([])
// 1. State für die Dateien und die Dropzone Referenz
const selectedFiles = ref([])
const dropZoneRef = ref(null)
// 2. Setup der Dropzone
const onDrop = (files) => {
// Wenn Dateien gedroppt werden, speichern wir sie
// files ist hier meist ein Array, wir stellen sicher, dass es passt
selectedFiles.value = files || []
}
const { isOverDropZone } = useDropZone(dropZoneRef, {
onDrop,
// Verhindert, dass der Browser das Bild einfach öffnet
preventDefaultForDrop: true,
})
// 3. Handler für den klassischen Datei-Input Klick
const onFileInputChange = (e) => {
if (e.target.files) {
selectedFiles.value = Array.from(e.target.files)
}
}
const setup = async () => { const setup = async () => {
availableFiletypes.value = await useEntities("filetags").select() availableFiletypes.value = await useEntities("filetags").select()
} }
@@ -24,81 +50,112 @@ const setup = async () => {
setup() setup()
const uploadFiles = async () => { const uploadFiles = async () => {
// Validierung: Keine Dateien ausgewählt
if (!selectedFiles.value || selectedFiles.value.length === 0) {
alert("Bitte wählen Sie zuerst Dateien aus.") // Oder eine schönere Toast Notification
return
}
uploadInProgress.value = true; uploadInProgress.value = true;
let fileData = props.fileData let fileData = props.fileData
delete fileData.typeEnabled delete fileData.typeEnabled
// 4. Hier nutzen wir nun selectedFiles.value statt document.getElementById
await useFiles().uploadFiles(fileData, selectedFiles.value, [], true)
await useFiles().uploadFiles(fileData, document.getElementById("fileUploadInput").files,[],true)
uploadInProgress.value = false; uploadInProgress.value = false;
emit("uploadFinished") emit("uploadFinished")
modal.close() modal.close()
} }
// Helper Funktion um Dateinamen anzuzeigen (da das Input Feld leer bleibt beim Droppen)
const fileNames = computed(() => {
if (!selectedFiles.value.length) return ''
return selectedFiles.value.map(f => f.name).join(', ')
})
</script> </script>
<template> <template>
<UModal> <UModal>
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }"> <div ref="dropZoneRef" class="relative h-full flex flex-col">
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Datei hochladen
</h3>
<UButton
color="gray"
variant="ghost"
icon="i-heroicons-x-mark-20-solid"
class="-my-1"
@click="modal.close()"
:disabled="uploadInProgress"
/>
</div>
</template>
<UFormGroup <div
label="Datei:" v-if="isOverDropZone"
class="absolute inset-0 z-50 flex items-center justify-center bg-primary-500/10 border-2 border-primary-500 border-dashed rounded-lg backdrop-blur-sm transition-all"
> >
<UInput <span class="text-xl font-bold text-primary-600 bg-white/80 px-4 py-2 rounded shadow-sm">
type="file" Dateien hier ablegen
id="fileUploadInput" </span>
multiple </div>
accept="image/jpeg, image/png, image/gif, application/pdf"
/> <UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
</UFormGroup> <template #header>
<UFormGroup <div class="flex items-center justify-between">
label="Typ:" <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
class="mt-3" Datei hochladen
> </h3>
<USelectMenu <UButton
option-attribute="name" color="gray"
value-attribute="id" variant="ghost"
searchable icon="i-heroicons-x-mark-20-solid"
searchable-placeholder="Suchen..." class="-my-1"
:options="availableFiletypes" @click="modal.close()"
v-model="props.fileData.type" :disabled="uploadInProgress"
:disabled="!props.fileData.typeEnabled" />
</div>
</template>
<UFormGroup
label="Datei:"
:help="selectedFiles.length > 0 ? `${selectedFiles.length} Datei(en) ausgewählt` : 'Ziehen Sie Dateien hierher oder klicken Sie'"
> >
<template #label> <UInput
<span v-if="availableFiletypes.find(x => x.id === props.fileData.type)">{{availableFiletypes.find(x => x.id === props.fileData.type).name}}</span> v-if="selectedFiles.length === 0"
<span v-else>Keine Typ ausgewählt</span> type="file"
</template> id="fileUploadInput"
</USelectMenu> multiple
</UFormGroup> accept="image/jpeg, image/png, image/gif, application/pdf"
@change="onFileInputChange"
/>
<template #footer> <div v-if="selectedFiles.length > 0" class="mt-2 text-sm text-gray-500">
<UButton Ausgewählt: <span class="font-medium text-gray-700 dark:text-gray-300">{{ fileNames }}</span>
@click="uploadFiles" </div>
:loading="uploadInProgress" </UFormGroup>
:disabled="uploadInProgress"
>Hochladen</UButton> <UFormGroup
</template> label="Typ:"
</UCard> class="mt-3"
>
<USelectMenu
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Suchen..."
:options="availableFiletypes"
v-model="props.fileData.type"
:disabled="!props.fileData.typeEnabled"
>
<template #label>
<span v-if="availableFiletypes.find(x => x.id === props.fileData.type)">{{availableFiletypes.find(x => x.id === props.fileData.type).name}}</span>
<span v-else>Kein Typ ausgewählt</span>
</template>
</USelectMenu>
</UFormGroup>
<template #footer>
<UButton
@click="uploadFiles"
:loading="uploadInProgress"
:disabled="uploadInProgress || selectedFiles.length === 0"
>Hochladen</UButton>
</template>
</UCard>
</div>
</UModal> </UModal>
</template> </template>
<style scoped> <style scoped>
/* Optional: Animationen für das Overlay */
</style> </style>

View File

@@ -9,6 +9,7 @@ const props = defineProps({
</script> </script>
<template> <template>
<span v-if="props.row.infoData.streetNumber">{{props.row.infoData.streetNumber}},</span>
<span v-if="props.row.infoData.street">{{props.row.infoData.street}},</span> <span v-if="props.row.infoData.street">{{props.row.infoData.street}},</span>
<span v-if="props.row.infoData.special">{{props.row.infoData.special}},</span> <span v-if="props.row.infoData.special">{{props.row.infoData.special}},</span>
<span v-if="props.row.infoData.zip">{{props.row.infoData.zip}},</span> <span v-if="props.row.infoData.zip">{{props.row.infoData.zip}},</span>

View File

@@ -7,7 +7,7 @@ export default defineNuxtConfig({
} }
}, },
modules: ['@pinia/nuxt', '@nuxt/ui', '@nuxtjs/supabase', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', 'nuxt-tiptap-editor', '@nuxtjs/leaflet'], modules: ['@pinia/nuxt', '@nuxt/ui', '@nuxtjs/supabase', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', 'nuxt-tiptap-editor', '@nuxtjs/leaflet', '@vueuse/nuxt'],
ssr: false, ssr: false,

View File

@@ -13,6 +13,8 @@
"@capacitor/cli": "^7.0.0", "@capacitor/cli": "^7.0.0",
"@nuxtjs/leaflet": "^1.2.3", "@nuxtjs/leaflet": "^1.2.3",
"@nuxtjs/supabase": "^1.1.4", "@nuxtjs/supabase": "^1.1.4",
"@vueuse/core": "^14.1.0",
"@vueuse/nuxt": "^14.1.0",
"nuxt": "^3.14.1592", "nuxt": "^3.14.1592",
"nuxt-tiptap-editor": "^1.2.0", "nuxt-tiptap-editor": "^1.2.0",
"vue": "^3.5.13", "vue": "^3.5.13",

View File

@@ -58,7 +58,7 @@ const clearSearchString = () => {
searchString.value = '' searchString.value = ''
} }
const filterAccount = ref(bankaccounts || []) const filterAccount = ref(bankaccounts.value || [])
const displayCurrency = (value, currency = "€") => { const displayCurrency = (value, currency = "€") => {
return `${Number(value).toFixed(2).replace(".",",")} ${currency}` return `${Number(value).toFixed(2).replace(".",",")} ${currency}`
@@ -150,7 +150,7 @@ setupPage()
</USelectMenu> </USelectMenu>
</template> </template>
<template #right> <template #right>
<USelectMenu <!-- <USelectMenu
v-model="selectedColumns" v-model="selectedColumns"
icon="i-heroicons-adjustments-horizontal-solid" icon="i-heroicons-adjustments-horizontal-solid"
:options="templateColumns" :options="templateColumns"
@@ -162,7 +162,7 @@ setupPage()
<template #label> <template #label>
Spalten Spalten
</template> </template>
</USelectMenu> </USelectMenu>-->
<USelectMenu <USelectMenu
icon="i-heroicons-adjustments-horizontal-solid" icon="i-heroicons-adjustments-horizontal-solid"
multiple multiple

View File

@@ -12,7 +12,7 @@ defineShortcuts({
}) })
const dataStore = useDataStore() const dataStore = useDataStore()
const profileStore = useProfileStore() const tempStore = useTempStore()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const mode = ref(route.params.mode || "show") const mode = ref(route.params.mode || "show")
@@ -177,7 +177,14 @@ const removeAllocation = async (allocationId) => {
await setup() await setup()
} }
const searchString = ref("") const searchString = ref(tempStore.searchStrings["bankstatementsedit"] ||'')
const clearSearchString = () => {
searchString.value = ''
tempStore.clearSearchString("bankstatementsedit")
}
const filteredDocuments = computed(() => { const filteredDocuments = computed(() => {
@@ -546,7 +553,15 @@ const archiveStatement = async () => {
class="mr-3 mt-3" class="mr-3 mt-3"
@click="removeAllocation(item.id)" @click="removeAllocation(item.id)"
/> />
<UButton
icon="i-heroicons-eye"
variant="outline"
color="primary"
class="mr-3 mt-3"
@click="navigateTo(`/createDocument/show/${item.createddocument}`)"
/>
</UCard> </UCard>
</div> </div>
</div> </div>
@@ -780,6 +795,7 @@ const archiveStatement = async () => {
placeholder="Suche..." placeholder="Suche..."
class="hidden lg:block w-full mr-1" class="hidden lg:block w-full mr-1"
@keydown.esc="$event.target.blur()" @keydown.esc="$event.target.blur()"
@change="tempStore.modifySearchString('bankstatementsedit',searchString)"
> >
<template #trailing> <template #trailing>
<UKbd value="/" /> <UKbd value="/" />
@@ -789,7 +805,7 @@ const archiveStatement = async () => {
variant="outline" variant="outline"
icon="i-heroicons-x-mark" icon="i-heroicons-x-mark"
color="rose" color="rose"
@click="searchString = ''" @click="clearSearchString"
/> />
</InputGroup> </InputGroup>
</div> </div>

View File

@@ -1,26 +1,26 @@
<template> <template>
<UDashboardNavbar title="Ausgangsbelege" :badge="filteredRows.length"> <UDashboardNavbar :badge="filteredRows.length" title="Ausgangsbelege">
<template #right> <template #right>
<UInput <UInput
id="searchinput" id="searchinput"
v-model="searchString" v-model="searchString"
icon="i-heroicons-funnel"
autocomplete="off" autocomplete="off"
placeholder="Suche..."
class="hidden lg:block" class="hidden lg:block"
@keydown.esc="$event.target.blur()" icon="i-heroicons-funnel"
placeholder="Suche..."
@change="tempStore.modifySearchString('createddocuments',searchString)" @change="tempStore.modifySearchString('createddocuments',searchString)"
@keydown.esc="$event.target.blur()"
> >
<template #trailing> <template #trailing>
<UKbd value="/" /> <UKbd value="/"/>
</template> </template>
</UInput> </UInput>
<UButton <UButton
v-if="searchString.length > 0"
color="rose"
icon="i-heroicons-x-mark" icon="i-heroicons-x-mark"
variant="outline" variant="outline"
color="rose"
@click="clearSearchString()" @click="clearSearchString()"
v-if="searchString.length > 0"
/> />
<UButton <UButton
@click="router.push(`/createDocument/edit`)" @click="router.push(`/createDocument/edit`)"
@@ -33,27 +33,27 @@
<template #right> <template #right>
<USelectMenu <!-- <USelectMenu
v-model="selectedColumns" v-model="selectedColumns"
icon="i-heroicons-adjustments-horizontal-solid"
:options="templateColumns" :options="templateColumns"
multiple
class="hidden lg:block"
by="key" by="key"
class="hidden lg:block"
icon="i-heroicons-adjustments-horizontal-solid"
multiple
@change="tempStore.modifyColumns('createddocuments',selectedColumns)" @change="tempStore.modifyColumns('createddocuments',selectedColumns)"
> >
<template #label> <template #label>
Spalten Spalten
</template> </template>
</USelectMenu> </USelectMenu>-->
<USelectMenu <USelectMenu
v-if="selectableFilters.length > 0" v-if="selectableFilters.length > 0"
v-model="selectedFilters"
:color="selectedFilters.length > 0 ? 'primary' : 'white'"
:options="selectableFilters"
:ui-menu="{ width: 'min-w-max' }"
icon="i-heroicons-adjustments-horizontal-solid" icon="i-heroicons-adjustments-horizontal-solid"
multiple multiple
v-model="selectedFilters"
:options="selectableFilters"
:color="selectedFilters.length > 0 ? 'primary' : 'white'"
:ui-menu="{ width: 'min-w-max' }"
> >
<template #label> <template #label>
Filter Filter
@@ -63,36 +63,36 @@
</UDashboardToolbar> </UDashboardToolbar>
<UTabs :items="selectedTypes" class="m-3"> <UTabs :items="selectedTypes" class="m-3">
<template #default="{item}"> <template #default="{item}">
{{item.label}} {{ item.label }}
<UBadge <UBadge
variant="outline"
class="ml-2" class="ml-2"
variant="outline"
> >
{{filteredRows.filter(i => item.key === 'invoices' ? ['invoices','advanceInvoices','cancellationInvoices'].includes(i.type) : item.key === i.type).length}} {{ filteredRows.filter(i => item.key === 'invoices' ? ['invoices', 'advanceInvoices', 'cancellationInvoices'].includes(i.type) : item.key === i.type).length }}
</UBadge> </UBadge>
</template> </template>
<template #item="{item}"> <template #item="{item}">
<div style="height: 80vh; overflow-y: scroll"> <div style="height: 80vh; overflow-y: scroll">
<UTable <UTable
:rows="filteredRows.filter(i => item.key === 'invoices' ? ['invoices','advanceInvoices','cancellationInvoices'].includes(i.type) : item.key === i.type)"
:columns="columns" :columns="columns"
class="w-full"
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
@select="selectItem"
: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="filteredRows.filter(i => item.key === 'invoices' ? ['invoices','advanceInvoices','cancellationInvoices'].includes(i.type) : item.key === i.type)"
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
class="w-full"
@select="selectItem"
> >
<template #type-data="{row}"> <template #type-data="{row}">
{{dataStore.documentTypesForCreation[row.type].labelSingle}} {{ dataStore.documentTypesForCreation[row.type].labelSingle }}
<!-- <!--
<span v-if="row.type === 'cancellationInvoices'"> zu {{row.linkedDocument.documentNumber}}</span> <span v-if="row.type === 'cancellationInvoices'"> zu {{row.linkedDocument.documentNumber}}</span>
--> -->
</template> </template>
<template #state-data="{row}"> <template #state-data="{row}">
<span <span
v-if="row.state === 'Entwurf'" v-if="row.state === 'Entwurf'"
class="text-rose-500" class="text-rose-500"
> >
{{row.state}} {{ row.state }}
</span> </span>
<!-- <span <!-- <span
v-if="row.state === 'Gebucht'" v-if="row.state === 'Gebucht'"
@@ -104,49 +104,56 @@
v-if="row.state === 'Gebucht' && !items.find(i => i.createddocument && i.createddocument.id === row.id)" v-if="row.state === 'Gebucht' && !items.find(i => i.createddocument && i.createddocument.id === row.id)"
class="text-primary-500" class="text-primary-500"
> >
{{row.state}} {{ row.state }}
</span> </span>
<span <span
v-else-if="row.state === 'Gebucht' && items.find(i => i.createddocument && i.createddocument.id === row.id && i.type === 'cancellationInvoices') && ['invoices','advanceInvoices'].includes(row.type)" v-else-if="row.state === 'Gebucht' && items.find(i => i.createddocument && i.createddocument.id === row.id && i.type === 'cancellationInvoices') && ['invoices','advanceInvoices'].includes(row.type)"
class="text-cyan-500" class="text-cyan-500"
> >
Storniert mit {{items.find(i => i.createddocument && i.createddocument.id === row.id).documentNumber}} Storniert mit {{ items.find(i => i.createddocument && i.createddocument.id === row.id).documentNumber }}
</span> </span>
<span <span
v-else-if="row.state === 'Gebucht'" v-else-if="row.state === 'Gebucht'"
class="text-primary-500" class="text-primary-500"
> >
{{row.state}} {{ row.state }}
</span> </span>
</template> </template>
<template #partner-data="{row}"> <template #partner-data="{row}">
<span v-if="row.customer && row.customer.name.length <21">{{row.customer ? row.customer.name : ""}}</span> <span v-if="row.customer && row.customer.name.length <21">{{ row.customer ? row.customer.name : "" }}</span>
<UTooltip v-else-if="row.customer && row.customer.name.length > 20" :text="row.customer.name"> <UTooltip v-else-if="row.customer && row.customer.name.length > 20" :text="row.customer.name">
{{row.customer.name.substring(0,20)}}... {{ row.customer.name.substring(0, 20) }}...
</UTooltip> </UTooltip>
</template> </template>
<template #reference-data="{row}"> <template #reference-data="{row}">
<span v-if="row === filteredRows[selectedItem]" class="text-primary-500 font-bold">{{row.documentNumber}}</span> <span v-if="row === filteredRows[selectedItem]"
<span v-else>{{row.documentNumber}}</span> class="text-primary-500 font-bold">{{ row.documentNumber }}</span>
<span v-else>{{ row.documentNumber }}</span>
</template> </template>
<template #date-data="{row}"> <template #date-data="{row}">
<span v-if="row.date">{{row.date ? dayjs(row.date).format("DD.MM.YY") : ''}}</span> <span v-if="row.date">{{ row.date ? dayjs(row.date).format("DD.MM.YY") : '' }}</span>
<span v-if="row.documentDate">{{row.documentDate ? dayjs(row.documentDate).format("DD.MM.YY") : ''}}</span> <span
v-if="row.documentDate">{{ row.documentDate ? dayjs(row.documentDate).format("DD.MM.YY") : '' }}</span>
</template> </template>
<template #dueDate-data="{row}"> <template #dueDate-data="{row}">
<span v-if="row.state === 'Gebucht' && row.paymentDays && ['invoices','advanceInvoices'].includes(row.type) && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)" :class="dayjs(row.documentDate).add(row.paymentDays,'day').diff(dayjs()) <= 0 && !isPaid(row) ? ['text-rose-500'] : '' ">{{row.documentDate ? dayjs(row.documentDate).add(row.paymentDays,'day').format("DD.MM.YY") : ''}}</span> <span
v-if="row.state === 'Gebucht' && row.paymentDays && ['invoices','advanceInvoices'].includes(row.type) && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)"
:class="dayjs(row.documentDate).add(row.paymentDays,'day').diff(dayjs()) <= 0 && !isPaid(row) ? ['text-rose-500'] : '' ">{{ row.documentDate ? dayjs(row.documentDate).add(row.paymentDays, 'day').format("DD.MM.YY") : '' }}</span>
</template> </template>
<template #paid-data="{row}"> <template #paid-data="{row}">
<div v-if="(row.type === 'invoices' ||row.type === 'advanceInvoices') && row.state === 'Gebucht' && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)"> <div
v-if="(row.type === 'invoices' ||row.type === 'advanceInvoices') && row.state === 'Gebucht' && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id)">
<span v-if="useSum().getIsPaid(row,items)" class="text-primary-500">Bezahlt</span> <span v-if="useSum().getIsPaid(row,items)" class="text-primary-500">Bezahlt</span>
<span v-else class="text-rose-600">Offen</span> <span v-else class="text-rose-600">Offen</span>
</div> </div>
</template> </template>
<template #amount-data="{row}"> <template #amount-data="{row}">
<span v-if="row.type !== 'deliveryNotes'">{{displayCurrency(useSum().getCreatedDocumentSum(row,items))}}</span> <span
v-if="row.type !== 'deliveryNotes'">{{ displayCurrency(useSum().getCreatedDocumentSum(row, items)) }}</span>
</template> </template>
<template #amountOpen-data="{row}"> <template #amountOpen-data="{row}">
<span v-if="!['deliveryNotes','cancellationInvoices','quotes','confirmationOrders'].includes(row.type) && row.state !== 'Entwurf' && !useSum().getIsPaid(row,items) && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id) ">{{displayCurrency(useSum().getCreatedDocumentSum(row, items) - row.statementallocations.reduce((n,{amount}) => n + amount, 0))}}</span> <span
v-if="!['deliveryNotes','cancellationInvoices','quotes','confirmationOrders'].includes(row.type) && row.state !== 'Entwurf' && !useSum().getIsPaid(row,items) && !items.find(i => i.linkedDocument && i.linkedDocument.id === row.id) ">{{ displayCurrency(useSum().getCreatedDocumentSum(row, items) - row.statementallocations.reduce((n, {amount}) => n + amount, 0)) }}</span>
</template> </template>
</UTable> </UTable>
</div> </div>
@@ -175,14 +182,14 @@ defineShortcuts({
} }
}, },
'arrowdown': () => { 'arrowdown': () => {
if(selectedItem.value < filteredRows.value.length - 1) { if (selectedItem.value < filteredRows.value.length - 1) {
selectedItem.value += 1 selectedItem.value += 1
} else { } else {
selectedItem.value = 0 selectedItem.value = 0
} }
}, },
'arrowup': () => { 'arrowup': () => {
if(selectedItem.value === 0) { if (selectedItem.value === 0) {
selectedItem.value = filteredRows.value.length - 1 selectedItem.value = filteredRows.value.length - 1
} else { } else {
selectedItem.value -= 1 selectedItem.value -= 1
@@ -202,7 +209,7 @@ const selectedItem = ref(0)
const setupPage = async () => { const setupPage = async () => {
items.value = (await useEntities("createddocuments").select("*, customer(id,name), statementallocations(id,amount),linkedDocument(*)","documentNumber",true, true)) items.value = (await useEntities("createddocuments").select("*, customer(id,name), statementallocations(id,amount),linkedDocument(*)", "documentNumber", true, true))
} }
setupPage() setupPage()
@@ -309,7 +316,7 @@ const filteredRows = computed(() => {
} }
}) })
if(selectedFilters.value.length > 0) { if (selectedFilters.value.length > 0) {
selectedFilters.value.forEach(filterName => { selectedFilters.value.forEach(filterName => {
let filter = dataType.filters.find(i => i.name === filterName) let filter = dataType.filters.find(i => i.name === filterName)
tempItems = tempItems.filter(filter.filterFunction) tempItems = tempItems.filter(filter.filterFunction)
@@ -317,7 +324,6 @@ const filteredRows = computed(() => {
} }
tempItems = useSearch(searchString.value, tempItems) tempItems = useSearch(searchString.value, tempItems)

View File

@@ -1,5 +1,3 @@
<template> <template>
<UDashboardNavbar title="Serienrechnungen" :badge="filteredRows.length"> <UDashboardNavbar title="Serienrechnungen" :badge="filteredRows.length">
<template #right> <template #right>
@@ -25,7 +23,7 @@
</UDashboardNavbar> </UDashboardNavbar>
<UDashboardToolbar> <UDashboardToolbar>
<template #right> <template #right>
<USelectMenu <!-- <USelectMenu
v-model="selectedColumns" v-model="selectedColumns"
icon="i-heroicons-adjustments-horizontal-solid" icon="i-heroicons-adjustments-horizontal-solid"
:options="templateColumns" :options="templateColumns"
@@ -36,7 +34,7 @@
<template #label> <template #label>
Spalten Spalten
</template> </template>
</USelectMenu> </USelectMenu>-->
<USelectMenu <USelectMenu
v-model="selectedFilters" v-model="selectedFilters"
icon="i-heroicons-adjustments-horizontal-solid" icon="i-heroicons-adjustments-horizontal-solid"

View File

@@ -364,33 +364,67 @@ const clearSearchString = () => {
variant="outline" variant="outline"
v-if="Object.keys(selectedFiles).find(i => selectedFiles[i] === true)" v-if="Object.keys(selectedFiles).find(i => selectedFiles[i] === true)"
>Herunterladen</UButton> >Herunterladen</UButton>
<UModal <UModal v-model="createFolderModalOpen">
v-model="createFolderModalOpen" <UCard :ui="{ body: { base: 'space-y-4' } }">
>
<UCard>
<template #header> <template #header>
Ordner Erstellen <div class="flex items-center justify-between">
</template> <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Ordner Erstellen
</h3>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="createFolderModalOpen = false" />
</div>
</template>
<UFormGroup label="Name des Ordners" required>
<UInput
v-model="createFolderData.name"
placeholder="z.B. Rechnungen 2024"
autofocus
/>
</UFormGroup>
<UFormGroup
<UFormGroup label="Standard Dateityp"
label="Ordner erstellen" >
<USelectMenu
v-model="createFolderData.standardFiletype"
:options="filetags"
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Typ suchen..."
placeholder="Kein Standard-Typ"
clear-search-on-close
> >
<UInput <template #label>
v-model="createFolderData.name" <span v-if="createFolderData.standardFiletype">
{{ filetags.find(t => t.id === createFolderData.standardFiletype)?.name }}
</span>
<span v-else class="text-gray-400">Kein Typ ausgewählt</span>
</template>
</USelectMenu>
</UFormGroup>
<div v-if="createFolderData.standardFiletype">
<UCheckbox
v-model="createFolderData.standardFiletypeIsOptional"
name="isOptional"
label="Dateityp ist optional"
help="Wenn deaktiviert, MUSS der Nutzer beim Upload diesen Typ verwenden."
/> />
</UFormGroup> </div>
<template #footer> <template #footer>
<UButton <div class="flex justify-end gap-2">
@click="createFolder" <UButton color="gray" variant="ghost" @click="createFolderModalOpen = false">
> Abbrechen
Erstellen </UButton>
</UButton> <UButton @click="createFolder" :disabled="!createFolderData.name">
Erstellen
</UButton>
</div>
</template> </template>
</UCard> </UCard>
</UModal> </UModal>
</template> </template>
@@ -429,8 +463,8 @@ const clearSearchString = () => {
</td> </td>
<td> <td>
<span v-if="entry.type === 'file'" class="text-xl">{{dayjs(documents.find(i => i.id === entry.id).created_at).format("DD.MM.YY HH:mm")}}</span> <span v-if="entry.type === 'file'" class="text-xl">{{dayjs(documents.find(i => i.id === entry.id).createdAt).format("DD.MM.YY HH:mm")}}</span>
<span v-if="entry.type === 'folder'" class="text-xl">{{dayjs(currentFolders.find(i => i.id === entry.id).created_at).format("DD.MM.YY HH:mm")}}</span> <span v-if="entry.type === 'folder'" class="text-xl">{{dayjs(currentFolders.find(i => i.id === entry.id).createdAt).format("DD.MM.YY HH:mm")}}</span>
</td> </td>
</tr> </tr>
</table> </table>