Merge branch 'dev' into beta

This commit is contained in:
2025-04-12 12:31:53 +02:00
11 changed files with 175 additions and 194 deletions

View File

@@ -42,7 +42,7 @@ const showFile = (file) => {
<iframe
:src="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
class="previewEmbed"
v-if="documentData.path.includes('pdf')"
v-if="documentData.path.toLowerCase().includes('pdf')"
loading="lazy"
/>
<img

View File

@@ -24,6 +24,7 @@ const emit = defineEmits(["updateNeeded"])
const folders = ref([])
const filetypes = ref([])
const documentboxes = ref([])
const setup = async () => {
const {data} = await supabase.from("folders").select().eq("tenant",useProfileStore().currentTenant)
@@ -55,6 +56,7 @@ const setup = async () => {
})
filetypes.value = await useSupabaseSelect("filetags")
documentboxes.value = await useSupabaseSelect("documentboxes")
}
setup()
@@ -180,7 +182,7 @@ const moveFile = async () => {
<template>
<UModal fullscreen >
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }" class="h-full">
<template #header>
<div class="flex flex-row justify-between">
<div class="flex items-center gap-2">
@@ -200,7 +202,7 @@ const moveFile = async () => {
class="bigPreview"
:data="`${props.documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
type="application/pdf"
v-if="props.documentData.path.includes('pdf')"
v-if="props.documentData.path.toLowerCase().includes('pdf')"
/>
<img
@@ -371,6 +373,18 @@ const moveFile = async () => {
@change="updateDocument"
/>
</InputGroup>
<UDivider class="my-5">Dokumentenbox</UDivider>
<InputGroup class="w-full">
<USelectMenu
class="flex-auto"
v-model="props.documentData.documentbox"
value-attribute="id"
option-attribute="key"
:options="documentboxes"
@change="updateDocument"
/>
</InputGroup>
</div>
</div>

View File

@@ -1,5 +1,7 @@
<script setup >
import DocumentUploadModal from "~/components/DocumentUploadModal.vue";
const props = defineProps({
type: {
type: String
@@ -13,103 +15,20 @@ const {type, elementId} = props
const emit = defineEmits(["uploadFinished"])
const dataStore = useDataStore()
const profileStore = useProfileStore()
const uploadModalOpen = ref(false)
const uploadInProgress = ref(false)
const fileUploadFormData = ref({
project: null,
tenant: profileStore.currentTenant
})
const availableTags = ref([])
const selectedTags = ref([])
const setup = async () => {
availableTags.value = await useSupabaseSelect("filetags")
}
setup()
const modal = useModal()
const openModal = () => {
uploadModalOpen.value = true
}
let fileProps = {folder: null, type: null, typeEnabled: true}
const uploadFiles = async () => {
uploadInProgress.value = true;
fileProps[props.type] = props.elementId
let fileData = fileUploadFormData.value
fileData[type] = elementId
console.log(fileProps)
await useFiles().uploadFiles(fileData, document.getElementById("fileUploadInput").files,selectedTags.value,true)
uploadModalOpen.value = false;
uploadInProgress.value = false;
emit("uploadFinished")
modal.open(DocumentUploadModal,{fileData: fileProps, onUploadFinished: () => emit("uploadFinished")})
}
</script>
<template>
<USlideover
v-model="uploadModalOpen"
>
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }" class="h-full">
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Datei hochladen
</h3>
<UButton
color="gray"
variant="ghost"
icon="i-heroicons-x-mark-20-solid"
class="-my-1"
@click="uploadModalOpen = false"
:disabled="uploadInProgress"
/>
</div>
</template>
<UFormGroup
label="Datei:"
>
<UInput
type="file"
id="fileUploadInput"
multiple
accept="image/jpeg, image/png, image/gif, application/pdf"
/>
</UFormGroup>
<UFormGroup
label="Tags:"
class="mt-3"
>
<USelectMenu
multiple
option-attribute="name"
value-attribute="id"
searchable
searchable-placeholder="Suchen..."
:options="availableTags"
v-model="selectedTags"
>
<template #label>
<span v-if="selectedTags.length > 0">{{selectedTags.map(i => availableTags.find(x => x.id === i).name).join(", ")}}</span>
<span v-else>Keine Tags ausgewählt</span>
</template>
</USelectMenu>
</UFormGroup>
<template #footer>
<UButton
@click="uploadFiles"
:loading="uploadInProgress"
>Hochladen</UButton>
</template>
</UCard>
</USlideover>
<UButton
@click="openModal"
icon="i-heroicons-arrow-up-tray"

View File

@@ -38,12 +38,13 @@ defineShortcuts({
const emit = defineEmits(["updateNeeded"])
const router = useRouter()
const route = useRoute()
const dataStore = useDataStore()
const modal = useModal()
const dataType = dataStore.dataTypes[type]
const openTab = ref(0)
const openTab = ref(route.query.tabIndex || 0)
@@ -88,6 +89,10 @@ const getAvailableQueryStringData = (keys) => {
}
const onTabChange = (index) => {
router.push(`${router.currentRoute.value.path}?tabIndex=${index}`)
}
</script>
<template>
@@ -188,6 +193,7 @@ const getAvailableQueryStringData = (keys) => {
v-if="props.item.id && platform !== 'mobile'"
class="p-5"
v-model="openTab"
@change="onTabChange"
>
<template #item="{item:tab}">
<div v-if="tab.label === 'Informationen'" class="flex flex-row">

View File

@@ -46,7 +46,7 @@ setup()
</template>
<Toolbar>
<DocumentUpload
:type="props.type.substring(0,props.type.length-1)"
:type="props.topLevelType.substring(0,props.topLevelType.length-1)"
:element-id="props.item.id"
@uploadFinished="emit('updateNeeded')"
/>

View File

@@ -27,8 +27,9 @@ const dataType = dataStore.dataTypes[props.topLevelType]
<template>
<UCard class="mt-5 scroll" :style="props.platform !== 'mobile' ? 'height: 80vh' : ''">
<HistoryDisplay
:type="props.topLevelType.substring(0,props.topLevelType.length-1)"
v-if="props.item.id"
:type="dataType.historyItemHolder"
v-if="props
.item.id"
:element-id="props.item.id"
render-headline
/>

View File

@@ -54,10 +54,26 @@ const links = computed(() => {
to: "/standardEntity/events",
icon: "i-heroicons-calendar-days"
}] : [],
/*{
label: "Dateien",
to: "/files",
icon: "i-heroicons-document"
},*/
]
},
{
label: "Dokumente",
icon: "i-heroicons-rectangle-stack",
defaultOpen: false,
children: [
{
label: "Dateien",
to: "/files",
icon: "i-heroicons-document"
},{
label: "Boxen",
to: "/standardEntity/documentboxes",
icon: "i-heroicons-archive-box"
},
]
},

View File

@@ -246,6 +246,18 @@ export const useRole = () => {
label: "Buchungskonten erstellen",
parent: "ownaccounts"
},
documentboxes: {
label: "Dokuemntenboxen",
showToAllUsers: false
},
"documentboxes-viewAll": {
label: "Alle Dokuemntenboxen einsehen",
parent: "documentboxesx"
},
"documentboxes-create": {
label: "Dokuemntenboxen erstellen",
parent: "documentboxes"
},
"inventory": {
label: "Lager",
},

View File

@@ -1,6 +1,7 @@
<script setup>
import dayjs from "dayjs";
import {useSupabaseSelect} from "~/composables/useSupabase.js";
const supabase = useSupabaseClient()
const route = useRoute()
@@ -8,21 +9,57 @@ const router = useRouter()
const profileStore = useProfileStore()
const itemInfo = ref(null)
const statementallocations = ref(null)
const statementallocations = ref([])
const incominginvoices = ref([])
const setup = async () => {
itemInfo.value = (await supabase.from("accounts").select("*").eq("id",route.params.id).single()).data
statementallocations.value = (await supabase.from("statementallocations").select("*, bs_id(*)").eq("account", route.params.id).eq("tenant",profileStore.currentTenant).order("created_at",{ascending: true})).data
incominginvoices.value = (await useSupabaseSelect("incominginvoices", "*, vendor(*)")).filter(i => i.accounts.find(x => x.account == route.params.id))
}
setup()
const selectAllocation = (allocation) => {
if(allocation.bs_id) {
if(allocation.type === "statementallocation") {
router.push(`/banking/statements/edit/${allocation.bs_id.id}`)
} else if(allocation.type === "incominginvoice") {
router.push(`/incominginvoices/show/${allocation.incominginvoiceid}`)
}
}
const renderedAllocations = computed(() => {
let tempstatementallocations = statementallocations.value.map(i => {
return {
...i,
type: "statementallocation",
date: i.bs_id.date,
partner: i.bs_id ? (i.bs_id.debName ? i.bs_id.debName : (i.bs_id.credName ? i.bs_id.credName : '')) : ''
}
})
let incominginvoicesallocations = []
incominginvoices.value.forEach(i => {
incominginvoicesallocations.push(...i.accounts.filter(x => x.account == route.params.id).map(x => {
return {
...x,
incominginvoiceid: i.id,
type: "incominginvoice",
amount: x.amountGross ? x.amountGross : x.amountNet,
date: i.date,
partner: i.vendor.name,
description: i.description,
color: i.expense ? "red" : "green"
}
}))
})
return [...tempstatementallocations, ... incominginvoicesallocations]
})
</script>
@@ -76,21 +113,18 @@ const selectAllocation = (allocation) => {
<UCard class="mt-5" v-if="item.label === 'Buchungen'">
<UTable
v-if="statementallocations"
:rows="statementallocations"
:rows="renderedAllocations"
:columns="[{key:'amount', label:'Betrag'},{key:'date', label:'Datum'},{key:'partner', label:'Partner'},{key:'description', label:'Beschreibung'}]"
@select="(i) => selectAllocation(i)"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Buchungen anzuzeigen' }"
>
<template #amount-data="{row}">
<span class="text-right text-rose-600" v-if="row.amount < 0">{{useCurrency(row.amount)}}</span>
<span class="text-right text-primary-500" v-else-if="row.amount > 0">{{useCurrency(row.amount)}}</span>
<span class="text-right text-rose-600" v-if="row.amount < 0 || row.color === 'red'">{{useCurrency(row.amount)}}</span>
<span class="text-right text-primary-500" v-else-if="row.amount > 0 || row.color === 'green'">{{useCurrency(row.amount)}}</span>
<span v-else>{{useCurrency(row.amount)}}</span>
</template>
<template #date-data="{row}">
{{row.bs_id ? dayjs(row.bs_id.date).format('DD.MM.YYYY') : ''}}
</template>
<template #partner-data="{row}">
{{row.bs_id ? (row.bs_id.debName ? row.bs_id.debName : (row.bs_id.credName ? row.bs_id.credName : '')) : ''}}
{{row.date ? dayjs(row.date).format('DD.MM.YYYY') : ''}}
</template>
<template #description-data="{row}">
{{row.description ? row.description : ''}}

View File

@@ -56,7 +56,6 @@ const profileStore = useProfileStore()
const supabase = useSupabaseClient()
const router = useRouter()
const route = useRoute()
const slideover = useSlideover()
const modal = useModal()
dataStore.fetchDocuments()
@@ -73,8 +72,6 @@ const fileUploadFormData = ref({
const files = useFiles()
let tags = dataStore.getDocumentTags
const displayMode = ref("list")
const displayModes = ref([{label: 'Liste',key:'list', icon: 'i-heroicons-list-bullet'},{label: 'Kacheln',key:'rectangles', icon: 'i-heroicons-squares-2x2'}])
@@ -107,8 +104,9 @@ const setupPage = async () => {
const dropZone = document.getElementById("drop_zone")
dropZone.ondragover = function (event) {
console.log(event)
isDragTarget.value = true
modal.open(DocumentUploadModal,{fileData: {folder: currentFolder.value.id, type: currentFolder.value.standardFiletype, typeEnabled: currentFolder.value.standardFiletypeIsOptional}, onUploadFinished: () => {
setupPage()
}})
event.preventDefault()
}
@@ -120,9 +118,6 @@ const setupPage = async () => {
console.log("files dropped")
event.preventDefault()
await uploadFiles(event.dataTransfer.files)
isDragTarget.value = false
setupPage()
}
@@ -230,23 +225,6 @@ const createFolder = async () => {
}
const uploadFiles = async (files) => {
uploadInProgress.value = true;
if(files) {
//await dataStore.uploadFiles({tags: ["Ablage"],tenant: profileStore.currentTenant,folder: currentFolder.value.id}, files, true)
await dataStore.uploadFiles({tags: ["Ablage"],tenant: profileStore.currentTenant}, files, true)
} else {
await dataStore.uploadFiles(fileUploadFormData.value, document.getElementById("fileUploadInput").files, true)
}
uploadModalOpen.value = false;
uploadInProgress.value = false;
}
const downloadSelected = async () => {
const bucket = "filesdev";
@@ -352,9 +330,7 @@ const selectAll = () => {
<UDashboardNavbar
title="Dateien"
>
</UDashboardNavbar>
></UDashboardNavbar>
<UDashboardToolbar>
<template #left>
<UBreadcrumb
@@ -375,7 +351,7 @@ const selectAll = () => {
</USelectMenu>
<UButton @click="modal.open(DocumentUploadModal,{fileData: {folder: currentFolder.id, type: currentFolder.standardFiletype, typeEnabled: currentFolder.standardFiletypeIsOptional}})">+ Datei</UButton>
<UButton @click="modal.open(DocumentUploadModal,{fileData: {folder: currentFolder.id, type: currentFolder.standardFiletype, typeEnabled: currentFolder.standardFiletypeIsOptional}, onUploadFinished: () => {setupPage()}})">+ Datei</UButton>
<UButton
@click="createFolderModalOpen = true"
variant="outline"
@@ -419,7 +395,7 @@ const selectAll = () => {
</UDashboardToolbar>
<div id="drop_zone" class="h-full scrollList" >
<div v-if="loaded">
<UDashboardPanelContent v-if="!isDragTarget" >
<UDashboardPanelContent>
<div v-if="displayMode === 'list'">
<table class="w-full">
<thead>
@@ -498,70 +474,10 @@ const selectAll = () => {
/>
</div>
</UDashboardPanelContent>
<UCard
class=" m-5"
v-else>
<template #header>
<p class="mx-auto">Dateien zum hochladen hierher ziehen</p>
</template>
</UCard>
</div>
<UProgress animation="carousel" v-else class="w-5/6 mx-auto mt-5"/>
</div>
<USlideover
v-model="uploadModalOpen"
>
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
Datei Hochladen
</template>
<div class="h-full">
<UFormGroup
label="Datei:"
>
<UInput
type="file"
id="fileUploadInput"
multiple
/>
</UFormGroup>
<UFormGroup
label="Tags:"
class="mt-3"
>
<USelectMenu
multiple
searchable
searchable-placeholder="Suchen..."
option-attribute="name"
value-attribute="id"
:options="filetags"
v-model="fileUploadFormData.tags"
/>
</UFormGroup>
</div>
<template #footer>
<UButton
v-if="!uploadInProgress"
class="mt-3"
@click="uploadFiles"
>Hochladen</UButton>
<UProgress
v-else
animation="carousel"
/>
</template>
</UCard>
</USlideover>
</template>
<style scoped>

View File

@@ -1528,6 +1528,69 @@ export const useDataStore = defineStore('data', () => {
}
]
},
documentboxes: {
isArchivable: true,
label: "Dokumentenboxen",
labelSingle: "Dokumentenbox",
isStandardEntity: true,
supabaseSelectWithInformation: "*, space(*), files(*)",
redirect: true,
numberRangeHolder: "key",
historyItemHolder: "documentbox",
inputColumns: [
"Allgemeines",
],
filters:[{
name: "Archivierte ausblenden",
default: true,
"filterFunction": function (row) {
if(!row.archived) {
return true
} else {
return false
}
}
}],
templateColumns: [
{
key: "space",
label: "Aktueller Lagerplatz",
inputType: "select",
selectDataType: "spaces",
selectOptionAttribute: "name",
selectSearchAttributes: ['name'],
inputColumn: "Allgemeines",
component: space
},
{
key: "key",
label: "Nummer",
inputType: "text",
inputIsNumberRange: true,
inputColumn: "Allgemeines",
title: true,
required: true,
},
{
key: "profiles",
label: "Berechtigte Benutzer",
inputType: "select",
selectDataType: "profiles",
selectOptionAttribute: "fullName",
selectSearchAttributes: ['fullName'],
selectMultiple: true,
component: profiles
},
],
showTabs: [
{
label: 'Informationen',
}, {
label: 'Dateien',
}
]
},
services: {
isArchivable: true,
label: "Leistungen",