#15
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 17s
Build and Push Docker Images / build-frontend (push) Successful in 1m9s

This commit is contained in:
2026-01-30 16:49:40 +01:00
parent e496a62b36
commit 71d249d8bf
2 changed files with 73 additions and 31 deletions

View File

@@ -151,6 +151,8 @@ const fileNames = computed(() => {
:disabled="uploadInProgress || selectedFiles.length === 0" :disabled="uploadInProgress || selectedFiles.length === 0"
>Hochladen</UButton> >Hochladen</UButton>
</template> </template>
{{props.fileData}}
</UCard> </UCard>
</div> </div>
</UModal> </UModal>

View File

@@ -28,7 +28,9 @@ const displayModes = [
] ]
const createFolderModalOpen = ref(false) const createFolderModalOpen = ref(false)
const createFolderData = ref({name: '', standardFiletype: null, standardFiletypeIsOptional: true}) const initialCreateFolderData = { name: '', standardFiletype: null, standardFiletypeIsOptional: true }
const createFolderData = ref({ ...initialCreateFolderData })
const renameModalOpen = ref(false) const renameModalOpen = ref(false)
const renameData = ref({id: null, name: '', type: ''}) const renameData = ref({id: null, name: '', type: ''})
@@ -77,16 +79,14 @@ let dragCounter = 0
const handleGlobalDragEnter = (e) => { const handleGlobalDragEnter = (e) => {
dragCounter++ dragCounter++
// 1. Abbrechen, wenn wir gerade intern Ordner/Dateien verschieben
if (draggedItem.value) return if (draggedItem.value) return
// 2. Prüfen ob Files von außen kommen
if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes('Files')) { if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes('Files')) {
// Modal öffnen, falls noch nicht offen (optionaler Check, useModal handhabt das meist selbst)
// Wir übergeben den aktuellen Ordner
modal.open(DocumentUploadModal, { modal.open(DocumentUploadModal, {
fileData: {folder: currentFolder.value?.id, typeEnabled: true}, fileData: {
folder: currentFolder.value?.id,
type: currentFolder?.value?.standardFiletype,
typeEnabled: currentFolder?.value?.standardFiletype ? currentFolder?.value?.standardFiletypeIsOptional : true
},
onUploadFinished: () => { onUploadFinished: () => {
setupPage() setupPage()
dragCounter = 0 dragCounter = 0
@@ -101,7 +101,6 @@ const handleGlobalDragLeave = (e) => {
const handleGlobalDrop = (e) => { const handleGlobalDrop = (e) => {
dragCounter = 0 dragCounter = 0
// Verhindert, dass der Browser die Datei öffnet, falls man neben das Modal droppt
e.preventDefault() e.preventDefault()
} }
@@ -111,8 +110,6 @@ const handleGlobalDragOver = (e) => {
onMounted(() => { onMounted(() => {
setupPage() setupPage()
// Globale Listener registrieren
window.addEventListener('dragenter', handleGlobalDragEnter) window.addEventListener('dragenter', handleGlobalDragEnter)
window.addEventListener('dragleave', handleGlobalDragLeave) window.addEventListener('dragleave', handleGlobalDragLeave)
window.addEventListener('dragover', handleGlobalDragOver) window.addEventListener('dragover', handleGlobalDragOver)
@@ -120,7 +117,6 @@ onMounted(() => {
}) })
onUnmounted(() => { onUnmounted(() => {
// Aufräumen
window.removeEventListener('dragenter', handleGlobalDragEnter) window.removeEventListener('dragenter', handleGlobalDragEnter)
window.removeEventListener('dragleave', handleGlobalDragLeave) window.removeEventListener('dragleave', handleGlobalDragLeave)
window.removeEventListener('dragover', handleGlobalDragOver) window.removeEventListener('dragover', handleGlobalDragOver)
@@ -146,7 +142,6 @@ const handleDragStart = (entry) => {
} }
const handleDrop = async (targetFolderId) => { const handleDrop = async (targetFolderId) => {
// targetFolderId kann null sein (Root)
if (!draggedItem.value) return if (!draggedItem.value) return
if (draggedItem.value.id === targetFolderId) return if (draggedItem.value.id === targetFolderId) return
@@ -189,13 +184,11 @@ const breadcrumbLinks = computed(() => {
// --- Data Mapping --- // --- Data Mapping ---
const renderedFileList = computed(() => { const renderedFileList = computed(() => {
// 1. Aktuelle Ordner filtern
const folderList = folders.value const folderList = folders.value
.filter(i => currentFolder.value ? i.parent === currentFolder.value.id : !i.parent) .filter(i => currentFolder.value ? i.parent === currentFolder.value.id : !i.parent)
.map(i => ({...i, label: i.name, type: "folder"})) .map(i => ({...i, label: i.name, type: "folder"}))
.sort((a, b) => a.label.localeCompare(b.label)) .sort((a, b) => a.label.localeCompare(b.label))
// 2. Aktuelle Dateien filtern
const fileList = documents.value const fileList = documents.value
.filter(i => currentFolder.value ? i.folder === currentFolder.value.id : !i.folder) .filter(i => currentFolder.value ? i.folder === currentFolder.value.id : !i.folder)
.map(i => ({...i, label: i.path.split("/").pop(), type: "file"})) .map(i => ({...i, label: i.path.split("/").pop(), type: "file"}))
@@ -206,7 +199,6 @@ const renderedFileList = computed(() => {
combined = useSearch(debouncedSearch.value, combined) combined = useSearch(debouncedSearch.value, combined)
} }
// 3. "Nach oben" (..) einfügen, wenn wir nicht im Root sind und nicht suchen
if (currentFolder.value && !debouncedSearch.value) { if (currentFolder.value && !debouncedSearch.value) {
combined.unshift({ combined.unshift({
id: 'go-up', id: 'go-up',
@@ -215,18 +207,46 @@ const renderedFileList = computed(() => {
parentId: currentFolder.value.parent || null parentId: currentFolder.value.parent || null
}) })
} }
return combined return combined
}) })
// --- Logic: Mandatory File Types ---
// Prüft, ob der aktuelle Ordner strikte Vorgaben macht
const isParentTypeMandatory = computed(() => {
return currentFolder.value
&& currentFolder.value.standardFiletype
&& !currentFolder.value.standardFiletypeIsOptional
})
// Überwacht das Öffnen des "Ordner erstellen" Modals, um Vorgaben zu setzen
watch(createFolderModalOpen, (isOpen) => {
if (isOpen) {
if (isParentTypeMandatory.value) {
// Wenn Elternordner strikt ist: Werte übernehmen und erzwingen
createFolderData.value = {
name: '',
standardFiletype: currentFolder.value.standardFiletype,
standardFiletypeIsOptional: false // Muss auch false sein, damit Kette weitergeht
}
} else {
// Reset auf Standardwerte
createFolderData.value = { ...initialCreateFolderData }
}
}
})
// --- Actions --- // --- Actions ---
const createFolder = async () => { const createFolder = async () => {
await useEntities("folders").create({ await useEntities("folders").create({
parent: currentFolder.value?.id, parent: currentFolder.value?.id,
name: createFolderData.value.name name: createFolderData.value.name,
standardFiletype: createFolderData.value.standardFiletype,
standardFiletypeIsOptional: createFolderData.value.standardFiletypeIsOptional
}) })
createFolderModalOpen.value = false createFolderModalOpen.value = false
createFolderData.value = {name: ''} // Reset passiert beim nächsten Öffnen durch den Watcher, aber sicherheitshalber hier clean
createFolderData.value = { ...initialCreateFolderData }
setupPage() setupPage()
} }
@@ -288,15 +308,12 @@ const syncdokubox = async () => {
isSyncing.value = true isSyncing.value = true
try { try {
await $api('/api/functions/services/syncdokubox', {method: 'POST'}) await $api('/api/functions/services/syncdokubox', {method: 'POST'})
toast.add({ toast.add({
title: 'Erfolg', title: 'Erfolg',
description: 'Dokubox wurde synchronisiert.', description: 'Dokubox wurde synchronisiert.',
icon: 'i-heroicons-check-circle', icon: 'i-heroicons-check-circle',
color: 'green' color: 'green'
}) })
// Liste neu laden
await setupPage() await setupPage()
} catch (error) { } catch (error) {
console.error(error) console.error(error)
@@ -310,7 +327,6 @@ const syncdokubox = async () => {
isSyncing.value = false isSyncing.value = false
} }
} }
</script> </script>
<template> <template>
@@ -325,8 +341,7 @@ const syncdokubox = async () => {
@click="syncdokubox" @click="syncdokubox"
class="mr-2" class="mr-2"
/> />
<UInput id="searchinput" v-model="searchString" icon="i-heroicons-magnifying-glass" placeholder="Suche..." <UInput id="searchinput" v-model="searchString" icon="i-heroicons-magnifying-glass" placeholder="Suche..." class="w-64"/>
class="w-64"/>
</template> </template>
</UDashboardNavbar> </UDashboardNavbar>
@@ -335,8 +350,7 @@ const syncdokubox = async () => {
<UBreadcrumb :links="breadcrumbLinks"/> <UBreadcrumb :links="breadcrumbLinks"/>
</template> </template>
<template #right> <template #right>
<USelectMenu v-model="displayMode" :options="displayModes" value-attribute="key" class="w-32" <USelectMenu v-model="displayMode" :options="displayModes" value-attribute="key" class="w-32" :ui-menu="{ zIndex: 'z-50' }">
:ui-menu="{ zIndex: 'z-50' }">
<template #label> <template #label>
<UIcon :name="displayModes.find(i => i.key === displayMode).icon" class="w-4 h-4"/> <UIcon :name="displayModes.find(i => i.key === displayMode).icon" class="w-4 h-4"/>
<span>{{ displayModes.find(i => i.key === displayMode).label }}</span> <span>{{ displayModes.find(i => i.key === displayMode).label }}</span>
@@ -344,7 +358,7 @@ const syncdokubox = async () => {
</USelectMenu> </USelectMenu>
<UButtonGroup size="sm"> <UButtonGroup size="sm">
<UButton icon="i-heroicons-document-plus" color="primary" <UButton icon="i-heroicons-document-plus" color="primary"
@click="modal.open(DocumentUploadModal, { fileData: { folder: currentFolder?.id }, onUploadFinished: setupPage })"> @click="modal.open(DocumentUploadModal, { fileData: { folder: currentFolder?.id,type: currentFolder?.standardFiletype, typeEnabled: currentFolder?.standardFiletype ? currentFolder?.standardFiletypeIsOptional : true }, onUploadFinished: setupPage })">
Datei Datei
</UButton> </UButton>
<UButton icon="i-heroicons-folder-plus" color="white" @click="createFolderModalOpen = true">Ordner</UButton> <UButton icon="i-heroicons-folder-plus" color="white" @click="createFolderModalOpen = true">Ordner</UButton>
@@ -431,9 +445,35 @@ const syncdokubox = async () => {
<UModal v-model="createFolderModalOpen"> <UModal v-model="createFolderModalOpen">
<UCard> <UCard>
<template #header><h3 class="font-bold">Ordner erstellen</h3></template> <template #header><h3 class="font-bold">Ordner erstellen</h3></template>
<UFormGroup label="Name" required>
<UInput v-model="createFolderData.name" autofocus @keyup.enter="createFolder"/> <div class="space-y-4">
</UFormGroup> <UFormGroup label="Name" required>
<UInput v-model="createFolderData.name" autofocus @keyup.enter="createFolder"/>
</UFormGroup>
<UFormGroup
label="Standard Dateityp (Tag)"
:help="isParentTypeMandatory ? 'Vom übergeordneten Ordner vorgegeben' : ''"
>
<USelectMenu
v-model="createFolderData.standardFiletype"
:options="filetags"
value-attribute="id"
option-attribute="name"
placeholder="Kein Standardtyp"
searchable
clear-search-on-close
:disabled="isParentTypeMandatory"
/>
</UFormGroup>
<UCheckbox
v-model="createFolderData.standardFiletypeIsOptional"
label="Dateityp ist optional"
:disabled="isParentTypeMandatory"
/>
</div>
<template #footer> <template #footer>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<UButton color="gray" @click="createFolderModalOpen = false">Abbrechen</UButton> <UButton color="gray" @click="createFolderModalOpen = false">Abbrechen</UButton>