Many Changes

This commit is contained in:
2025-01-05 18:23:44 +01:00
parent 1c6c6e4a33
commit efbb97967a
16 changed files with 970 additions and 441 deletions

View File

@@ -50,6 +50,8 @@ useSeoMeta({
</NuxtLayout> </NuxtLayout>
<UNotifications/> <UNotifications/>
<USlideovers />
<UModals />
<VitePwaManifest/> <VitePwaManifest/>

View File

@@ -57,8 +57,8 @@ const updateDocument = async () => {
} }
const archiveDocument = () => { const archiveDocument = () => {
documentData.tags.push("Archiviert") //documentData.tags.push("Archiviert")
updateDocument() //updateDocument()
} }
const resourceOptions = ref([ const resourceOptions = ref([
@@ -106,7 +106,7 @@ const updateDocumentAssignment = async () => {
<iframe <iframe
:src="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`" :src="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
class="previewEmbed" class="previewEmbed"
v-if="!documentData.tags.includes('Bild')" v-if="!documentData.filetags.includes('Bild')"
loading="lazy" loading="lazy"
/> />
<img <img
@@ -121,9 +121,9 @@ const updateDocumentAssignment = async () => {
<InputGroup class="mt-3 flex-wrap"> <InputGroup class="mt-3 flex-wrap">
<UBadge <UBadge
v-for="tag in documentData.tags" v-for="tag in documentData.filetags"
><span class="text-nowrap">{{ tag }}</span></UBadge> ><span class="text-nowrap">{{ tag.name }}</span></UBadge>
</InputGroup> </InputGroup>
@@ -157,9 +157,9 @@ const updateDocumentAssignment = async () => {
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<UBadge <UBadge
v-for="tag in documentData.tags" v-for="tag in documentData.filetags"
> >
{{tag}} {{tag.name}}
</UBadge> </UBadge>
</div> </div>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="openShowModal = false" /> <UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="openShowModal = false" />
@@ -172,7 +172,7 @@ const updateDocumentAssignment = async () => {
class="bigPreview" class="bigPreview"
:data="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`" :data="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
type="application/pdf" type="application/pdf"
v-if="!documentData.tags.includes('Bild')" v-if="!documentData.filetags.includes('Bild')"
/> />
<img <img
@@ -186,11 +186,11 @@ const updateDocumentAssignment = async () => {
<template #footer> <template #footer>
<UButtonGroup> <UButtonGroup>
<UButton <!-- <UButton
@click="archiveDocument" @click="archiveDocument"
> >
Archivieren Archivieren
</UButton> </UButton>-->
<UButton <UButton
:to="documentData.url" :to="documentData.url"
variant="outline" variant="outline"
@@ -206,12 +206,12 @@ const updateDocumentAssignment = async () => {
> >
<USelectMenu <USelectMenu
:options="tags" :options="tags"
v-model="documentData.tags" v-model="documentData.filetags"
@change="updateDocument" @change="updateDocument"
multiple multiple
> >
<template #label> <template #label>
{{documentData.tags.length}} ausgewählt {{documentData.filetags.length}} ausgewählt
</template> </template>
</USelectMenu> </USelectMenu>
</UFormGroup> </UFormGroup>

View File

@@ -0,0 +1,196 @@
<script setup>
const toast = useToast()
const supabase = useSupabaseClient()
const dataStore = useDataStore()
const profileStore = useProfileStore()
const router = useRouter()
const modal = useModal()
const props = defineProps({
documentData: {
type: Object,
required: true
},
openShowModal: {
type: Boolean,
required: false,
},
returnEmit: {
type: Boolean
}
})
const emits = defineEmits("close")
const showSlideover = ref(props.openShowModal)
//Functions
const openDocument = async () => {
//selectedDocument.value = doc
openShowModal.value = true
console.log("open")
}
const updateDocument = async () => {
console.log("Update")
const {url, ...objData} = props.documentData
delete objData.url
delete objData.filetags
const {data,error} = await supabase
.from("files")
.update(objData)
.eq('id',objData.id)
.select()
if(error) {
console.log(error)
} else {
toast.add({title: "Datei aktualisiert"})
//openShowModal.value = false
}
}
const archiveDocument = () => {
//documentData.tags.push("Archiviert")
//updateDocument()
}
const resourceOptions = ref([
{label: 'Projekt', value: 'project', optionAttr: "name"},
{label: 'Kunde', value: 'customer', optionAttr: "name"},
{label: 'Lieferant', value: 'vendor', optionAttr: "name"},
{label: 'Fahrzeug', value: 'vehicle', optionAttr: "licensePlate"},
{label: 'Objekt', value: 'plant', optionAttr: "name"},
{label: 'Vertrag', value: 'contract', optionAttr: "name"},
{label: 'Produkt', value: 'product', optionAttr: "name"}
])
const resourceToAssign = ref("project")
const itemOptions = ref([])
const idToAssign = ref(null)
const getItemsBySelectedResource = () => {
if(resourceToAssign.value === "project") {
itemOptions.value = dataStore.projects
} else if(resourceToAssign.value === "customer") {
itemOptions.value = dataStore.customers
} else if(resourceToAssign.value === "vendor") {
itemOptions.value = dataStore.vendors
} else if(resourceToAssign.value === "vehicle") {
itemOptions.value = dataStore.vehicles
} else if(resourceToAssign.value === "product") {
itemOptions.value = dataStore.products
} else if(resourceToAssign.value === "plant") {
itemOptions.value = dataStore.plants
} else if(resourceToAssign.value === "contract") {
itemOptions.value = dataStore.contracts
} else {
itemOptions.value = []
}
}
getItemsBySelectedResource()
const updateDocumentAssignment = async () => {
props.documentData[resourceToAssign.value] = idToAssign.value
await updateDocument()
}
</script>
<template>
<UModal >
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<div class="flex flex-row justify-between">
<div class="flex items-center gap-2">
<UBadge
v-for="tag in props.documentData.filetags"
>
{{tag.name}}
</UBadge>
</div>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="modal.close()" />
</div>
</template>
<div class="flex flex-row">
<div class="w-1/2">
<object
class="bigPreview"
:data="`${props.documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
type="application/pdf"
v-if="!props.documentData.filetags.includes('Bild')"
/>
<img
class=" w-full"
:src="props.documentData.url"
alt=""
v-else
/>
</div>
<div class="w-1/2 p-5">
<UButtonGroup>
<!-- <UButton
@click="archiveDocument"
>
Archivieren
</UButton>-->
<UButton
:to="props.documentData.url"
variant="outline"
icon="i-heroicons-arrow-top-right-on-square"
target="_blank"
>
Öffnen
</UButton>
</UButtonGroup>
<p>Dokument zuweisen:</p>
<UFormGroup
label="Resource auswählen"
>
<USelectMenu
:options="resourceOptions"
v-model="resourceToAssign"
value-attribute="value"
option-attribute="label"
@change="getItemsBySelectedResource"
>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Eintrag auswählen:"
>
</UFormGroup>
<USelectMenu
:options="itemOptions"
v-model="idToAssign"
:option-attribute="resourceOptions.find(i => i.value === resourceToAssign)? resourceOptions.find(i => i.value === resourceToAssign).optionAttr : 'name'"
value-attribute="id"
@change="updateDocumentAssignment"
></USelectMenu>
</div>
</div>
</UCard>
</UModal>
</template>
<style scoped>
.bigPreview {
width: 100%;
aspect-ratio: 1/ 1.414;
}
</style>

View File

@@ -145,6 +145,19 @@ const contentChanged = (content, datapoint) => {
>{{item.id ? `${dataType.labelSingle} bearbeiten` : `${dataType.labelSingle} erstellen` }}</h1> >{{item.id ? `${dataType.labelSingle} bearbeiten` : `${dataType.labelSingle} erstellen` }}</h1>
</template> </template>
<template #right> <template #right>
<ButtonWithConfirm
color="rose"
variant="outline"
@confirmed="dataStore.updateItem(type,{...item,archived: true}, oldItem)"
>
<template #button>
Archivieren
</template>
<template #header>
<span class="text-md text-black font-bold">Archivieren bestätigen</span>
</template>
Möchten Sie das {{dataType.labelSingle}} {{item[dataType.templateColumns.find(i => i.title).key]}} wirklich archivieren?
</ButtonWithConfirm>
<UButton <UButton
v-if="item.id" v-if="item.id"
@click="dataStore.updateItem(type,item, oldItem)" @click="dataStore.updateItem(type,item, oldItem)"
@@ -172,8 +185,8 @@ const contentChanged = (content, datapoint) => {
> >
<div class="flex flex-row"> <div class="flex flex-row">
<div <div
v-for="columnName in dataType.inputColumns" v-for="(columnName,index) in dataType.inputColumns"
class="w-1/2 mr-5" :class="['w-1/2', ... index < dataType.inputColumns.length -1 ? ['mr-5'] : []]"
> >
<UDivider>{{columnName}}</UDivider> <UDivider>{{columnName}}</UDivider>
@@ -192,8 +205,9 @@ const contentChanged = (content, datapoint) => {
:item="item" :item="item"
/> />
</template> </template>
<div v-if="datapoint.key.includes('.')"> <InputGroup class="w-full" v-if="datapoint.key.includes('.')">
<UInput <UInput
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)"
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
@@ -212,6 +226,7 @@ const contentChanged = (content, datapoint) => {
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false" :disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
/> />
<USelectMenu <USelectMenu
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]]"
@@ -228,6 +243,7 @@ const contentChanged = (content, datapoint) => {
</template> </template>
</USelectMenu> </USelectMenu>
<UTextarea <UTextarea
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'"
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
@@ -275,9 +291,18 @@ const contentChanged = (content, datapoint) => {
@updateContent="(i) => contentChanged(i,datapoint)" @updateContent="(i) => contentChanged(i,datapoint)"
:preloadedContent="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]].html" :preloadedContent="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]].html"
/> />
</div>
<div v-else> <UButton
v-if="['text','number','select','date','datetime','textarea'].includes(datapoint.inputType)"
@click="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] = null"
variant="outline"
color="white"
icon="i-heroicons-x-mark"
/>
</InputGroup>
<InputGroup class="w-full" v-else>
<UInput <UInput
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)"
v-model="item[datapoint.key]" v-model="item[datapoint.key]"
@@ -296,6 +321,7 @@ const contentChanged = (content, datapoint) => {
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false" :disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
/> />
<USelectMenu <USelectMenu
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]"
@@ -313,6 +339,7 @@ const contentChanged = (content, datapoint) => {
</template> </template>
</USelectMenu> </USelectMenu>
<UTextarea <UTextarea
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'"
v-model="item[datapoint.key]" v-model="item[datapoint.key]"
@@ -358,7 +385,15 @@ const contentChanged = (content, datapoint) => {
@updateContent="(i) => contentChanged(i,datapoint)" @updateContent="(i) => contentChanged(i,datapoint)"
:preloadedContent="item[datapoint.key].html" :preloadedContent="item[datapoint.key].html"
/> />
</div>
<UButton
v-if="['text','number','select','date','datetime','textarea'].includes(datapoint.inputType)"
@click="item[datapoint.key] = null"
variant="outline"
color="white"
icon="i-heroicons-x-mark"
/>
</InputGroup>
<div <div
v-if="profileStore.ownTenant.ownFields" v-if="profileStore.ownTenant.ownFields"
> >
@@ -387,7 +422,7 @@ const contentChanged = (content, datapoint) => {
</div> </div>
</div> </div>
<UFormGroup <UFormGroup
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)"
:label="datapoint.label" :label="datapoint.label"
> >
<template #help> <template #help>
@@ -397,8 +432,9 @@ const contentChanged = (content, datapoint) => {
:item="item" :item="item"
/> />
</template> </template>
<div v-if="datapoint.key.includes('.')"> <InputGroup class="w-full" v-if="datapoint.key.includes('.')">
<UInput <UInput
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)"
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
@@ -417,6 +453,7 @@ const contentChanged = (content, datapoint) => {
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false" :disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
/> />
<USelectMenu <USelectMenu
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]]"
@@ -433,6 +470,7 @@ const contentChanged = (content, datapoint) => {
</template> </template>
</USelectMenu> </USelectMenu>
<UTextarea <UTextarea
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'"
v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]" v-model="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]]"
@@ -480,9 +518,18 @@ const contentChanged = (content, datapoint) => {
@updateContent="(i) => contentChanged(i,datapoint)" @updateContent="(i) => contentChanged(i,datapoint)"
:preloadedContent="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]].html" :preloadedContent="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]].html"
/> />
</div>
<div v-else> <UButton
v-if="['text','number','select','date','datetime','textarea'].includes(datapoint.inputType)"
@click="item[datapoint.key.split('.')[0]][datapoint.key.split('.')[1]] = null"
variant="outline"
color="white"
icon="i-heroicons-x-mark"
/>
</InputGroup>
<InputGroup class="w-full" v-else>
<UInput <UInput
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)"
v-model="item[datapoint.key]" v-model="item[datapoint.key]"
@@ -501,6 +548,7 @@ const contentChanged = (content, datapoint) => {
:disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false" :disabled="datapoint.disabledFunction ? datapoint.disabledFunction(item) : false"
/> />
<USelectMenu <USelectMenu
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]"
@@ -518,6 +566,7 @@ const contentChanged = (content, datapoint) => {
</template> </template>
</USelectMenu> </USelectMenu>
<UTextarea <UTextarea
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'"
v-model="item[datapoint.key]" v-model="item[datapoint.key]"
@@ -563,7 +612,15 @@ const contentChanged = (content, datapoint) => {
@updateContent="(i) => contentChanged(i,datapoint)" @updateContent="(i) => contentChanged(i,datapoint)"
:preloadedContent="item[datapoint.key].html" :preloadedContent="item[datapoint.key].html"
/> />
</div>
<UButton
v-if="['text','number','select','date','datetime','textarea'].includes(datapoint.inputType)"
@click="item[datapoint.key] = null"
variant="outline"
color="white"
icon="i-heroicons-x-mark"
/>
</InputGroup>
<div <div
v-if="profileStore.ownTenant.ownFields" v-if="profileStore.ownTenant.ownFields"
> >

View File

@@ -1,6 +1,5 @@
<script setup> <script setup>
import dayjs from "dayjs"; import dayjs from "dayjs";
import {useSupabaseSelectSomeDocuments} from "~/composables/useSupabase.js";
const props = defineProps({ const props = defineProps({
type: { type: {
@@ -36,14 +35,15 @@ const router = useRouter()
const dataStore = useDataStore() const dataStore = useDataStore()
const profileStore = useProfileStore() const profileStore = useProfileStore()
const supabase = useSupabaseClient() const supabase = useSupabaseClient()
const files = useFiles()
const dataType = dataStore.dataTypes[type] const dataType = dataStore.dataTypes[type]
const documents = ref([]) const availableFiles = ref([])
const setup = async () => { const setup = async () => {
if(props.item.documents) { if(props.item.files) {
documents.value = await useSupabaseSelectSomeDocuments(props.item.documents.map(i => i.id)) || [] availableFiles.value = await files.selectSomeDocuments(props.item.files.map(i => i.id)) || []
} }
} }
@@ -154,19 +154,6 @@ const getAvailableQueryStringData = () => {
>{{item ? `${dataType.labelSingle}${props.item[dataType.templateColumns.find(i => i.title).key] ? ': ' + props.item[dataType.templateColumns.find(i => i.title).key] : ''}`: '' }}</h1> >{{item ? `${dataType.labelSingle}${props.item[dataType.templateColumns.find(i => i.title).key] ? ': ' + props.item[dataType.templateColumns.find(i => i.title).key] : ''}`: '' }}</h1>
</template> </template>
<template #right> <template #right>
<ButtonWithConfirm
color="rose"
variant="outline"
@confirmed="dataStore.updateItem(type,{...item, archived: true})"
>
<template #button>
Archivieren
</template>
<template #header>
<span class="text-md text-black font-bold">Archivieren bestätigen</span>
</template>
Möchten Sie das Fahrzeug {{item.name}} wirklich archivieren?
</ButtonWithConfirm>
<UButton <UButton
@click="router.push(`/standardEntity/${type}/edit/${item.id}`)" @click="router.push(`/standardEntity/${type}/edit/${item.id}`)"
> >
@@ -174,25 +161,22 @@ const getAvailableQueryStringData = () => {
</UButton> </UButton>
</template> </template>
</UDashboardNavbar> </UDashboardNavbar>
<UDashboardPanelContent>
</UDashboardPanelContent>
<UTabs <UTabs
:items="dataType.showTabs" :items="dataType.showTabs"
v-if="props.item.id" v-if="props.item.id"
class="p-5" class="p-5"
v-model="openTab" v-model="openTab"
> >
<template #item="{item}"> <template #item="{item:tab}">
<div class="scroll"> <div class="scroll">
<div v-if="item.label === 'Informationen'" class="flex flex-row mt-5"> <div v-if="tab.label === 'Informationen'" class="flex flex-row mt-5">
<UCard class="w-1/2 mr-5"> <UCard class="w-1/2 mr-5">
<UAlert <UAlert
v-if="item.archived" v-if="item.archived"
color="rose" color="rose"
variant="outline" variant="outline"
:title="`${dataType.labelSingle} archiviert`" :title="`${dataType.labelSingle} archiviert`"
icon="i-heroicons-light-bulb" icon="i-heroicons-archive-box"
class="mb-5" class="mb-5"
/> />
<div class="text-wrap"> <div class="text-wrap">
@@ -225,7 +209,7 @@ const getAvailableQueryStringData = () => {
/> />
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Dokumente'"> <div v-else-if="tab.label === 'Dateien'">
<UCard class="mt-5"> <UCard class="mt-5">
<Toolbar> <Toolbar>
<DocumentUpload <DocumentUpload
@@ -235,11 +219,17 @@ const getAvailableQueryStringData = () => {
</Toolbar> </Toolbar>
<DocumentList <DocumentList
:documents="documents" :documents="availableFiles"
v-if="availableFiles.length > 0"
/>
<UAlert
v-else
icon="i-heroicons-x-mark"
title="Keine Dateien verfügbar"
/> />
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Projekte'"> <div v-else-if="tab.label === 'Projekte'">
<UCard class="mt-5"> <UCard class="mt-5">
<Toolbar> <Toolbar>
<UButton <UButton
@@ -262,7 +252,7 @@ const getAvailableQueryStringData = () => {
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Objekte'"> <div v-else-if="tab.label === 'Objekte'">
<UCard class="mt-5"> <UCard class="mt-5">
<Toolbar> <Toolbar>
<UButton <UButton
@@ -288,7 +278,7 @@ const getAvailableQueryStringData = () => {
</UTable> </UTable>
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Aufgaben'"> <div v-else-if="tab.label === 'Aufgaben'">
<UCard class="mt-5"> <UCard class="mt-5">
<Toolbar> <Toolbar>
<UButton <UButton
@@ -305,7 +295,7 @@ const getAvailableQueryStringData = () => {
/> />
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Verträge'"> <div v-else-if="tab.label === 'Verträge'">
<UCard class="mt-5"> <UCard class="mt-5">
<Toolbar> <Toolbar>
<UButton <UButton
@@ -323,7 +313,7 @@ const getAvailableQueryStringData = () => {
></UTable> ></UTable>
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Überprüfungen'"> <div v-else-if="tab.label === 'Überprüfungen'">
<UCard class="mt-5"> <UCard class="mt-5">
<UTable <UTable
:rows="props.item.checks" :rows="props.item.checks"
@@ -341,7 +331,7 @@ const getAvailableQueryStringData = () => {
</UTable> </UTable>
</UCard> </UCard>
</div> </div>
<div v-else-if="item.label === 'Phasen'"> <div v-else-if="tab.label === 'Phasen'">
<UCard class="mt-5"> <UCard class="mt-5">
<UAccordion <UAccordion
:items="renderedPhases" :items="renderedPhases"
@@ -404,7 +394,8 @@ const getAvailableQueryStringData = () => {
</template> </template>
</UAccordion> </UAccordion>
</UCard> </UCard>
</div><div v-else-if="item.label === 'Ausgangsbelege'"> </div>
<div v-else-if="tab.label === 'Ausgangsbelege'">
<UCard class="mt-5"> <UCard class="mt-5">
<Toolbar> <Toolbar>

View File

@@ -2,11 +2,9 @@
import dayjs from "dayjs" import dayjs from "dayjs"
const props = defineProps({ const props = defineProps({
type: { type: {
required: true,
type: String type: String
}, },
elementId: { elementId: {
required: true,
type: String type: String
}, },
renderHeadline: { renderHeadline: {
@@ -26,43 +24,12 @@ const items = ref([])
const setup = async () => { const setup = async () => {
if(type && elementId){
items.value = (await supabase.from("historyitems").select().eq(type,elementId).order("created_at",{ascending: true})).data || [] items.value = (await supabase.from("historyitems").select().eq(type,elementId).order("created_at",{ascending: true})).data || []
} else {
items.value = (await supabase.from("historyitems").select().order("created_at",{ascending: true})).data || []
/*if(type === "customer") { }
items.value = (await supabase.from("historyitems").select().eq("customer",elementId)).data || []
} else if(type === "vendor") {
items.value = (await supabase.from("historyitems").select().eq("vendor",elementId)).data || []
} else if(type === "project") {
items.value = (await supabase.from("historyitems").select().eq("project",elementId)).data || []
} else if(type === "plant") {
items.value = (await supabase.from("historyitems").select().eq("plant",elementId)).data || []
} else if(type === "incomingInvoice") {
items.value = (await supabase.from("historyitems").select().eq("incomingInvoice",elementId)).data || []
} else if(type === "document") {
items.value = (await supabase.from("historyitems").select().eq("document",elementId)).data || []
} else if(type === "contact") {
items.value = (await supabase.from("historyitems").select().eq("contact",elementId)).data || []
} else if(type === "contract") {
items.value = (await supabase.from("historyitems").select().eq("contract",elementId)).data || []
} else if(type === "inventoryitem") {
items.value = (await supabase.from("historyitems").select().eq("inventoryitem",elementId)).data || []
} else if(type === "product") {
items.value = (await supabase.from("historyitems").select().eq("product",elementId)).data || []
} else if(type === "profile") {
items.value = (await supabase.from("historyitems").select().eq("profile",elementId)).data || []
} else if(type === "absencerequest") {
items.value = (await supabase.from("historyitems").select().eq("absenceRequest",elementId)).data || []
} else if(type === "event") {
items.value = (await supabase.from("historyitems").select().eq("event",elementId)).data || []
} else if(type === "task") {
items.value = (await supabase.from("historyitems").select().eq("task",elementId)).data || []
} else if(type === "vehicle") {
items.value = (await supabase.from("historyitems").select().eq("vehicle",elementId)).data || []
} else if(type === "space") {
items.value = (await supabase.from("historyitems").select().eq("space",elementId)).data || []
} else if(type === "trackingtrip") {
items.value = (await supabase.from("historyitems").select().eq("trackingtrip",elementId)).data || []
}*/
} }
setup() setup()
@@ -162,7 +129,7 @@ const renderText = (text) => {
</UCard> </UCard>
</UModal> </UModal>
<Toolbar <Toolbar
v-if="!renderHeadline" v-if="!renderHeadline && elementId && type"
> >
<UButton <UButton
@click="showAddHistoryItemModal = true" @click="showAddHistoryItemModal = true"
@@ -170,7 +137,7 @@ const renderText = (text) => {
+ Eintrag + Eintrag
</UButton> </UButton>
</Toolbar> </Toolbar>
<div v-else> <div v-else-if="renderHeadline && elementId && type">
<div :class="`flex justify-between`"> <div :class="`flex justify-between`">
<p class=""><span class="text-xl">Logbuch</span> <UBadge variant="outline">{{items.length}}</UBadge></p> <p class=""><span class="text-xl">Logbuch</span> <UBadge variant="outline">{{items.length}}</UBadge></p>
<UButton <UButton

View File

@@ -18,6 +18,11 @@ const links = computed(() => {
label: "Dashboard", label: "Dashboard",
to: "/", to: "/",
icon: "i-heroicons-home" icon: "i-heroicons-home"
}, {
id: 'historyitems',
label: "Logbuch",
to: "/historyitems",
icon: "i-heroicons-book-open"
}, },
{ {
label: "Organisation", label: "Organisation",
@@ -46,7 +51,7 @@ const links = computed(() => {
}] : [], }] : [],
{ {
label: "Dateien", label: "Dateien",
to: "/documents", to: "/files",
icon: "i-heroicons-document" icon: "i-heroicons-document"
}, },
] ]
@@ -113,6 +118,11 @@ const links = computed(() => {
to: "/standardEntity/absencerequests", to: "/standardEntity/absencerequests",
icon: "i-heroicons-document-text" icon: "i-heroicons-document-text"
}] : [], }] : [],
{
label: "Fahrten",
to: "/trackingTrips",
icon: "i-heroicons-map"
},
] ]
}, },
... profileStore.ownTenant.features.accounting ? [{ ... profileStore.ownTenant.features.accounting ? [{
@@ -156,30 +166,9 @@ const links = computed(() => {
}, },
... role.checkRight("spaces") ? [{ ... role.checkRight("spaces") ? [{
label: "Lagerplätze", label: "Lagerplätze",
to: "/spaces", to: "/standardEntity/spaces",
icon: "i-heroicons-square-3-stack-3d" icon: "i-heroicons-square-3-stack-3d"
}] : [], }] : [],
... role.checkRight("inventoryitems") ? [{
label: "Inventar",
to: "/standardEntity/inventoryitems",
icon: "i-heroicons-puzzle-piece"
}] : [],
]
},] : [],
... role.checkRight("vehicles") ? [{
label: "Fuhrpark",
defaultOpen: false,
icon: "i-heroicons-truck",
children: [
... role.checkRight("vehicles") ? [{
label: "Fahrzeuge",
to: "/standardEntity/vehicles",
icon: "i-heroicons-truck"
}] : [],{
label: "Fahrten",
to: "/trackingTrips",
icon: "i-heroicons-map"
},
] ]
},] : [], },] : [],
{ {
@@ -200,11 +189,26 @@ const links = computed(() => {
... role.checkRight("services") ? [{ ... role.checkRight("services") ? [{
label: "Leistungen", label: "Leistungen",
to: "/standardEntity/services", to: "/standardEntity/services",
icon: "i-heroicons-puzzle-piece" icon: "i-heroicons-wrench-screwdriver"
}] : [], }] : [],
... role.checkRight("servicecategories") ? [{ ... role.checkRight("servicecategories") ? [{
label: "Leistungskategorien", label: "Leistungskategorien",
to: "/standardEntity/servicecategories", to: "/standardEntity/servicecategories",
icon: "i-heroicons-wrench-screwdriver"
}] : [],
{
label: "Mitarbeiter",
to: "/profiles",
icon: "i-heroicons-user-group"
},
... role.checkRight("vehicles") ? [{
label: "Fahrzeuge",
to: "/standardEntity/vehicles",
icon: "i-heroicons-truck"
}] : [],
... role.checkRight("inventoryitems") ? [{
label: "Inventar",
to: "/standardEntity/inventoryitems",
icon: "i-heroicons-puzzle-piece" icon: "i-heroicons-puzzle-piece"
}] : [], }] : [],
] ]
@@ -244,10 +248,6 @@ const links = computed(() => {
label: "Nummernkreise", label: "Nummernkreise",
to: "/settings/numberRanges", to: "/settings/numberRanges",
icon: "i-heroicons-clipboard-document-list" icon: "i-heroicons-clipboard-document-list"
},{
label: "Mitarbeiter",
to: "/profiles",
icon: "i-heroicons-clipboard-document-list"
},{ },{
label: "Rollen", label: "Rollen",
to: "/roles", to: "/roles",
@@ -276,63 +276,15 @@ const links = computed(() => {
label: "Projekttypen", label: "Projekttypen",
to: "/projecttypes", to: "/projecttypes",
icon: "i-heroicons-clipboard-document-list" icon: "i-heroicons-clipboard-document-list"
}/*,{
label: "Integrationen",
to: "/settings/integrations",
icon: "i-heroicons-clipboard-document-list"
}*/,{
label: "Labels",
to: "/settings/labels/",
icon: "i-heroicons-bars-3-solid"
} }
] ]
} }
] ]
}) })
/*const setOpen = (index,open) => {
console.log(!open)
}*/
</script> </script>
<template> <template>
<!--
<UAccordion :items="links" :ui="{ wrapper: 'flex flex-col w-full' }">
<template #default="{ item, index, open }">
<UButton color="gray" variant="ghost" class="border-b border-gray-200 dark:border-gray-700" :ui="{ rounded: 'rounded-none', padding: { sm: 'p-3' } }">
<template #leading>
&lt;!&ndash; <div class="w-6 h-6 rounded-full bg-primary-500 dark:bg-primary-400 flex items-center justify-center -my-1">
<UIcon :name="item.icon" class="w-4 h-4 text-white dark:text-gray-900" />
</div>&ndash;&gt;
<UIcon :name="item.icon" class="w-4 h-4 dark:text-white text-gray-900" />
</template>
<span class="truncate">{{ item.label }}</span>
<template #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
<template #item="{item}">
<div v-if="item.children" class="flex flex-col">
<UButton
v-for="child in item.children"
variant="ghost"
color="gray"
>
{{child.label}}
</UButton>
</div>
</template>
</UAccordion>
-->
<div <div
v-for="item in links" v-for="item in links"
> >

210
composables/useFiles.js Normal file
View File

@@ -0,0 +1,210 @@
import index from "v-calendar";
export const useFiles = () => {
const supabase = useSupabaseClient()
const toast = useToast()
let bucket = "filesdev"
const profileStore = useProfileStore()
const uploadFiles = async (formData, files,tags, upsert) => {
const uploadSingleFile = async (file) => {
//Create File Entry to Get ID for Folder
const {data:createdFileData,error:createdFileError} = await supabase
.from("files")
.insert({
tenant: profileStore.currentTenant,
})
.select()
.single()
if(createdFileError){
console.log(createdFileError)
toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else if(createdFileData) {
//Upload File to ID Folder
const {data:uploadData, error: uploadError} = await supabase
.storage
.from(bucket)
.upload(`${profileStore.currentTenant}/filesbyid/${createdFileData.id}/${file.name}`, file, {upsert: upsert})
if(uploadError) {
console.log(uploadError)
console.log(uploadError.statusCode)
if(uploadError.statusCode === '400') {
console.log("is 400")
toast.add({title: "Hochladen fehlgeschlagen", description: "Die Datei enthält ungültige Zeichen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else if(uploadError.statusCode === '409') {
console.log("is 409")
toast.add({title: "Hochladen fehlgeschlagen", description: "Es existiert bereits eine Datei mit diesem Namen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else {
toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
}
} else if(uploadData) {
//Update File with Corresponding Path
const {data:updateFileData, error:updateFileError} = await supabase
.from("files")
.update({
...formData,
path: uploadData.path,
})
.eq("id", createdFileData.id)
if(updateFileError) {
console.log(updateFileError)
toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else {
const {data:tagData, error:tagError} = await supabase
.from("filetagmembers")
.insert(tags.map(tag => {
return {
file_id: createdFileData.id,
tag_id: tag
}
}))
toast.add({title: "Hochladen erfolgreich"})
}
}
}
}
if(files.length === 1) {
await uploadSingleFile(files[0])
} else if( files.length > 1) {
for(let i = 0; i < files.length; i++){
await uploadSingleFile(files[i])
}
}
}
const selectDocuments = async (sortColumn = null, folder = null) => {
let data = null
if(sortColumn !== null ) {
data = (await supabase
.from("files")
.select('*, filetags(*)')
.eq("tenant", profileStore.currentTenant)
.not("path","is",null)
.order(sortColumn, {ascending: true})).data
} else {
data = (await supabase
.from("files")
.select('*, filetags(*)')
.eq("tenant", profileStore.currentTenant)
.not("path","is",null)).data
}
if(data.length > 0){
let paths = []
data.forEach(doc => {
paths.push(doc.path)
})
const {data: supabaseData,error} = await supabase.storage.from(bucket).createSignedUrls(paths,3600)
data = data.map((doc,index) => {
return {
...doc,
url: supabaseData[index].signedUrl
}
})
}
//console.log(data)
return data
}
const selectSomeDocuments = async (documentIds, sortColumn = null, folder = null) => {
let data = null
if(sortColumn !== null ) {
data = (await supabase
.from("files")
.select('*, filetags(*)')
.in("id",documentIds)
.eq("tenant", profileStore.currentTenant)
.not("path","is",null)
.order(sortColumn, {ascending: true})).data
} else {
data = (await supabase
.from("files")
.select('*, filetags(*)')
.in("id",documentIds)
.not("path","is",null)
.eq("tenant", profileStore.currentTenant)).data
}
if(data.length > 0){
let paths = []
data.forEach(doc => {
paths.push(doc.path)
})
const {data: supabaseData,error} = await supabase.storage.from(bucket).createSignedUrls(paths,3600)
data = data.map((doc,index) => {
return {
...doc,
url: supabaseData[index].signedUrl
}
})
}
//console.log(data)
return data
}
const selectDocument = async (id) => {
const {data,error} = await supabase
.from("files")
.select('*, filetags(*)')
.eq("id",id)
.single()
const {data: supabaseData,error:supabaseError} = await supabase.storage.from(bucket).createSignedUrl(data.path,3600)
return {
...data,
url: supabaseData.signedUrl
}
/*
if(data.length > 0){
let paths = []
data.forEach(doc => {
paths.push(doc.path)
})
const {data: supabaseData,error} = await supabase.storage.from(bucket).createSignedUrls(paths,3600)
data = data.map((doc,index) => {
return {
...doc,
url: supabaseData[index].signedUrl
}
})
}
//console.log(data)
return data[0]*/
}
return {uploadFiles, selectDocuments, selectSomeDocuments, selectDocument}
}

View File

@@ -22,97 +22,6 @@ export const useSupabaseSelect = async (relation,select = '*', sortColumn = null
return data return data
} }
export const useSupabaseSelectDocuments = async (select = '*', sortColumn = null, folderPath = "_") => {
const supabase = useSupabaseClient()
const profileStore = useProfileStore()
let data = null
if(sortColumn !== null ) {
data = (await supabase
.from("documents")
.select(select)
.eq("tenant", profileStore.currentTenant)
.order(sortColumn, {ascending: true})).data
} else {
data = (await supabase
.from("documents")
.select(select)
.eq("tenant", profileStore.currentTenant)).data
}
if(data.length > 0){
let paths = []
data.forEach(doc => {
paths.push(doc.path)
})
const {data: supabaseData,error} = await supabase.storage.from('files').createSignedUrls(paths,3600)
data = data.map((doc,index) => {
return {
...doc,
url: supabaseData[index].signedUrl
}
})
}
//console.log(data)
return data
}
export const useSupabaseSelectSomeDocuments = async (documentIds, select = '*', sortColumn = null, folderPath = "_") => {
const supabase = useSupabaseClient()
const profileStore = useProfileStore()
let data = null
if(sortColumn !== null ) {
data = (await supabase
.from("documents")
.select(select)
.in("id",documentIds)
.eq("tenant", profileStore.currentTenant)
.order(sortColumn, {ascending: true})).data
} else {
data = (await supabase
.from("documents")
.select(select)
.in("id",documentIds)
.eq("tenant", profileStore.currentTenant)).data
}
if(data.length > 0){
let paths = []
data.forEach(doc => {
paths.push(doc.path)
})
const {data: supabaseData,error} = await supabase.storage.from('files').createSignedUrls(paths,3600)
data = data.map((doc,index) => {
return {
...doc,
url: supabaseData[index].signedUrl
}
})
}
//console.log(data)
return data
}
export const useSupabaseSelectSingle = async (relation,idToEq,select = '*' ) => { export const useSupabaseSelectSingle = async (relation,idToEq,select = '*' ) => {
const supabase = useSupabaseClient() const supabase = useSupabaseClient()
const profileStore = useProfileStore() const profileStore = useProfileStore()

View File

@@ -125,18 +125,8 @@ const footerLinks = [/*{
<UDashboardSearchButton label="Suche..."/> <UDashboardSearchButton label="Suche..."/>
</template> </template>
<!-- <UDashboardSidebarLinks :links="links">
</UDashboardSidebarLinks>-->
<MainNav/> <MainNav/>
<!-- <UAccordion
:items="links"
>
</UAccordion>-->
<div class="flex-1" /> <div class="flex-1" />
<UDashboardSidebarLinks :links="footerLinks" /> <UDashboardSidebarLinks :links="footerLinks" />

View File

@@ -457,7 +457,7 @@ const renderCurrency = (value, currency = "€") => {
const documentTotal = computed(() => { const documentTotal = computed(() => {
let totalNet = 0 let totalNet = 0
let total19 = 0 let total19 = 0
let totalGross = 0 let total7 = 0
itemInfo.value.rows.forEach(row => { itemInfo.value.rows.forEach(row => {
if(!['pagebreak','title','text'].includes(row.mode)){ if(!['pagebreak','title','text'].includes(row.mode)){
@@ -466,11 +466,13 @@ const documentTotal = computed(() => {
if(row.taxPercent === 19) { if(row.taxPercent === 19) {
total19 = total19 + Number(rowPrice * 0.19) total19 = total19 + Number(rowPrice * 0.19)
} else if(row.taxPercent === 7) {
total7 = total7 + Number(rowPrice * 0.07)
} }
} }
}) })
totalGross = totalNet + total19 let totalGross = Number(totalNet.toFixed(2)) + Number(total19.toFixed(2)) + Number(total7.toFixed(2))
let totalGrossAlreadyPaid = 0 let totalGrossAlreadyPaid = 0
@@ -488,19 +490,14 @@ const documentTotal = computed(() => {
let sumToPay = totalGross - totalGrossAlreadyPaid let sumToPay = totalGross - totalGrossAlreadyPaid
return { return {
totalNet: totalNet, totalNet: totalNet,
total19: total19, total19: total19,
total7: total7,
totalGross: totalGross, totalGross: totalGross,
totalGrossAlreadyPaid: totalGrossAlreadyPaid, totalGrossAlreadyPaid: totalGrossAlreadyPaid,
totalSumToPay: sumToPay totalSumToPay: sumToPay
} }
}) })
const processDieselPosition = () => { const processDieselPosition = () => {
@@ -831,14 +828,13 @@ const closeDocument = async () => {
await generateDocument() await generateDocument()
let fileData = { let fileData = {}
tags: [],
project: null
}
fileData.project = itemInfo.value.project fileData.project = itemInfo.value.project
fileData.createdDocument = itemInfo.value.id fileData.createddocument = itemInfo.value.id
fileData.tags.push(dataStore.documentTypesForCreation[itemInfo.value.type].labelSingle) fileData.folder = (await supabase.from("folders").select("id").eq("tenant", profileStore.currentTenant).eq("function", itemInfo.value.type).eq("year",dayjs().format("YYYY")).single()).data.id
let tag = (await supabase.from("filetags").select("id").eq("tenant", profileStore.currentTenant).eq("createddocumenttype", itemInfo.value.type).single()).data
function dataURLtoFile(dataurl, filename) { function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","), var arr = dataurl.split(","),
@@ -854,7 +850,7 @@ const closeDocument = async () => {
let file = dataURLtoFile(uri.value, `${itemInfo.value.documentNumber}.pdf`) let file = dataURLtoFile(uri.value, `${itemInfo.value.documentNumber}.pdf`)
await dataStore.uploadFiles(fileData, [file], true) await dataStore.uploadFiles(fileData, [file],[tag.id], true)
await router.push(`/createDocument/show/${itemInfo.value.id}`) await router.push(`/createDocument/show/${itemInfo.value.id}`)
@@ -894,13 +890,28 @@ const setRowData = (row) => {
<template> <template>
<UDashboardNavbar> <UDashboardNavbar>
<template #right> <template #right>
<!-- <ButtonWithConfirm
v-if="itemInfo.state === 'Entwurf'"
color="rose"
variant="outline"
@confirmed="dataStore.updateItem('createddocuments',{id:itemInfo.id,archived: true}),
router.push('/createDocument')"
>
<template #button>
Archivieren
</template>
<template #header>
<span class="text-md text-black font-bold">Archivieren bestätigen</span>
</template>
Möchten Sie diesen Ausgangsbeleg wirklich archivieren?
</ButtonWithConfirm>-->
<UButton <UButton
icon="i-mdi-content-save" icon="i-mdi-content-save"
@click="saveDocument('Entwurf',true)" @click="saveDocument('Entwurf',true)"
v-if="itemInfo.type !== 'serialInvoices' " v-if="itemInfo.type !== 'serialInvoices' "
:disabled="!itemInfo.customer" :disabled="!itemInfo.customer"
> >
Entwurf Entwurf speichern
</UButton> </UButton>
<UButton <UButton
@click="closeDocument" @click="closeDocument"
@@ -952,7 +963,7 @@ const setRowData = (row) => {
v-model="itemInfo.type" v-model="itemInfo.type"
value-attribute="type" value-attribute="type"
option-attribute="label" option-attribute="label"
@input="setDocumentTypeConfig" @change="setDocumentTypeConfig"
class="flex-auto" class="flex-auto"
> >
<template #label> <template #label>

View File

@@ -21,9 +21,16 @@ const linkedDocument =ref({})
const currentTenant = ref({}) const currentTenant = ref({})
const setupPage = async () => { const setupPage = async () => {
if(route.params) { if(route.params) {
if(route.params.id) itemInfo.value = await useSupabaseSelectSingle("createddocuments",route.params.id,"*") if(route.params.id) itemInfo.value = await useSupabaseSelectSingle("createddocuments",route.params.id,"*, files(*)")
const {data,error} = await supabase.from("documents").select("id").eq("createdDocument", route.params.id).order("id",{ascending:true})
linkedDocument.value = data[data.length -1] console.log(itemInfo.value)
linkedDocument.value = await useFiles().selectDocument(itemInfo.value.files[0].id)
//const {data,error} = await supabase.from("files").select("id").eq("createddocument", route.params.id).order("id",{ascending:true})
//linkedDocument.value = data[data.length -1]
} }
@@ -72,11 +79,19 @@ const openEmail = () => {
> >
E-Mail E-Mail
</UButton> </UButton>
<UButton
v-if="itemInfo.project"
@click="router.push(`/standardEntity/projects/show/${itemInfo.project}`)"
icon="i-heroicons-arrow-right-end-on-rectangle"
variant="outline"
>
Projekt
</UButton>
</template> </template>
</UDashboardToolbar> </UDashboardToolbar>
<UDashboardPanelContent> <UDashboardPanelContent>
<object <object
:data="dataStore.documents.find(i => i.id === linkedDocument.id) ? dataStore.documents.find(i => i.id === linkedDocument.id).url : ''" :data="linkedDocument.url"
class="h-full" class="h-full"
/> />
</UDashboardPanelContent> </UDashboardPanelContent>

View File

@@ -2,22 +2,66 @@
import {BlobReader, BlobWriter, ZipWriter} from "@zip.js/zip.js"; import {BlobReader, BlobWriter, ZipWriter} from "@zip.js/zip.js";
import {useSupabaseSelectDocuments, useSupabaseSelectSingle} from "~/composables/useSupabase.js"; import {useSupabaseSelectSingle} from "~/composables/useSupabase.js";
import DocumentDisplayModal from "~/components/DocumentDisplayModal.vue";
import dayjs from "dayjs";
definePageMeta({ definePageMeta({
middleware: "auth" middleware: "auth"
}) })
defineShortcuts({
/*'/': () => {
//console.log(searchinput)
//searchinput.value.focus()
document.getElementById("searchinput").focus()
},*/
'+': () => {
//Hochladen
uploadModalOpen.value = true
},
'Enter': {
usingInput: true,
handler: () => {
let entry = renderedFileList.value[selectedFileIndex.value]
if(entry.type === "file") {
showFile(entry.id)
console.log(entry)
} else {
changeFolder(currentFolders.value.find(i => i.id === entry.id))
}
}
},
'arrowdown': () => {
if(selectedFileIndex.value < renderedFileList.value.length - 1) {
selectedFileIndex.value += 1
} else {
selectedFileIndex.value = 0
}
},
'arrowup': () => {
if(selectedFileIndex.value === 0) {
selectedFileIndex.value = renderedFileList.value.length - 1
} else {
selectedFileIndex.value -= 1
}
}
})
const dataStore = useDataStore() const dataStore = useDataStore()
const profileStore = useProfileStore() const profileStore = useProfileStore()
const supabase = useSupabaseClient() const supabase = useSupabaseClient()
const user = useSupabaseUser()
const toast = useToast()
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const slideover = useSlideover()
const modal = useModal()
dataStore.fetchDocuments() dataStore.fetchDocuments()
const uploadModalOpen = ref(false) const uploadModalOpen = ref(false)
const createFolderModalOpen = ref(false)
const uploadInProgress = ref(false) const uploadInProgress = ref(false)
const fileUploadFormData = ref({ const fileUploadFormData = ref({
tags: ["Eingang"], tags: ["Eingang"],
@@ -26,11 +70,18 @@ const fileUploadFormData = ref({
folder: null folder: null
}) })
const files = useFiles()
let tags = dataStore.getDocumentTags 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'}])
const documents = ref([]) const documents = ref([])
const folders = ref([]) const folders = ref([])
const filetags = ref([])
const currentFolder = ref(null) const currentFolder = ref(null)
@@ -39,11 +90,12 @@ const isDragTarget = ref(false)
const setupPage = async () => { const setupPage = async () => {
folders.value = await useSupabaseSelect("folders") folders.value = await useSupabaseSelect("folders")
documents.value = await useSupabaseSelectDocuments("*",null) documents.value = await files.selectDocuments()
filetags.value = await useSupabaseSelect("filetags")
if(route.query) { if(route.query) {
if(route.query.folder) { if(route.query.folder) {
console.log(route.query.folder)
currentFolder.value = await useSupabaseSelectSingle("folders", route.query.folder) currentFolder.value = await useSupabaseSelectSingle("folders", route.query.folder)
} }
} }
@@ -135,10 +187,6 @@ const breadcrumbLinks = computed(() => {
} }
}) })
const filteredDocuments = computed(() => { const filteredDocuments = computed(() => {
return documents.value.filter(i => currentFolder.value ? i.folder === currentFolder.value.id : !i.folder) return documents.value.filter(i => currentFolder.value ? i.folder === currentFolder.value.id : !i.folder)
@@ -151,20 +199,38 @@ const changeFolder = async (newFolder) => {
if(newFolder) { if(newFolder) {
fileUploadFormData.value.folder = newFolder.id fileUploadFormData.value.folder = newFolder.id
await router.push(`/documents?folder=${newFolder.id}`) await router.push(`/files?folder=${newFolder.id}`)
} else { } else {
fileUploadFormData.value.folder = null fileUploadFormData.value.folder = null
await router.push(`/documents`) await router.push(`/files`)
} }
setupPage() setupPage()
} }
const createFolderData = ref({})
const createFolder = async () => {
const {data,error} = await supabase
.from("folders")
.insert({
tenant: profileStore.currentTenant,
parent: currentFolder.value ? currentFolder.value.id : undefined,
name: createFolderData.value.name,
})
createFolderModalOpen.value = false
setupPage()
}
const uploadFiles = async (files) => { const uploadFiles = async (files) => {
uploadInProgress.value = true; uploadInProgress.value = true;
if(files) { if(files) {
await dataStore.uploadFiles({tags: ["Ablage"],tenant: profileStore.currentTenant,folder: currentFolder.value.id}, files, true) //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 { } else {
await dataStore.uploadFiles(fileUploadFormData.value, document.getElementById("fileUploadInput").files, true) await dataStore.uploadFiles(fileUploadFormData.value, document.getElementById("fileUploadInput").files, true)
@@ -237,37 +303,46 @@ const downloadSelected = async () => {
link.click(); link.click();
} }
const renderedFileList = computed(() => {
let files = filteredDocuments.value.map(i => {
return {
label: i.path.split("/")[i.path.split("/").length -1],
id: i.id,
type: "file"
}
})
let folders = currentFolders.value.map(i => {
return {
label: i.name,
id: i.id,
type: "folder"
}
})
return [...folders,...files]
})
const selectedFileIndex = ref(0)
const showFile = (fileId) => {
console.log(fileId)
modal.open(DocumentDisplayModal,{
documentData: documents.value.find(i => i.id === fileId),
})
}
</script> </script>
<template> <template>
<UDashboardNavbar <UDashboardNavbar
title="Dateien" title="Dateien"
> >
</UDashboardNavbar> </UDashboardNavbar>
<!-- <UDashboardToolbar>
<template #right>
<UButton @click="uploadModalOpen = true">Hochladen</UButton>
&lt;!&ndash; <UButton
@click="downloadSelected"
:disabled="dataStore.documents.filter(doc => doc.selected).length === 0"
>Herunterladen</UButton>&ndash;&gt;
</template>
&lt;!&ndash; <template #right>
<USelectMenu
:options="tags"
v-model="selectedTags"
class="w-40"
>
<template #label>
{{selectedTags}}
</template>
</USelectMenu>
</template>&ndash;&gt;
</UDashboardToolbar>-->
<UDashboardToolbar> <UDashboardToolbar>
<template #left> <template #left>
<UBreadcrumb <UBreadcrumb
@@ -275,11 +350,79 @@ const downloadSelected = async () => {
/> />
</template> </template>
<template #right> <template #right>
<UButton @click="uploadModalOpen = true">Hochladen</UButton> <USelectMenu
:options="displayModes"
value-attribute="key"
option-attribute="label"
v-model="displayMode"
:ui-menu="{ width: 'min-w-max'}"
>
<template #label>
<UIcon class="w-5 h-5" :name="displayModes.find(i => i.key === displayMode).icon"/>
</template>
</USelectMenu>
<UButton @click="uploadModalOpen = true">+ Hochladen</UButton>
<UButton
@click="createFolderModalOpen = true"
variant="outline"
>+ Ordner</UButton>
<UModal
v-model="createFolderModalOpen"
>
<UCard>
<template #header>
Ordner Erstellen
</template>
<UFormGroup
label="Ordner erstellen"
>
<UInput
v-model="createFolderData.name"
/>
</UFormGroup>
<template #footer>
<UButton
@click="createFolder"
>
Erstellen
</UButton>
</template>
</UCard>
</UModal>
</template> </template>
</UDashboardToolbar> </UDashboardToolbar>
<div id="drop_zone" class="h-full scrollList"> <div id="drop_zone" class="h-full scrollList">
<UDashboardPanelContent v-if="!isDragTarget" > <UDashboardPanelContent v-if="!isDragTarget" >
<div v-if="displayMode === 'list'">
<table class="w-full">
<thead>
<td class="font-bold">Name</td>
<td class="font-bold">Erstellt am</td>
</thead>
<tr v-for="(entry,index) in renderedFileList">
<td>
<UIcon :name="entry.type === 'folder' ? 'i-heroicons-folder' : 'i-heroicons-document'"/>
<a
:class="[...index === selectedFileIndex ? ['text-primary'] : ['dark:text-white','text-black']]"
@click="entry.type === 'folder' ? changeFolder(currentFolders.find(i => i.id === entry.id)) : showFile(entry.id)"
>{{entry.label}}</a>
</td>
<td>
<span v-if="entry.type === 'file'">{{dayjs(documents.find(i => i.id === entry.id).created_at).format("DD.MM.YY HH:mm")}}</span>
<span v-if="entry.type === 'folder'">{{dayjs(currentFolders.find(i => i.id === entry.id).created_at).format("DD.MM.YY HH:mm")}}</span>
</td>
</tr>
</table>
</div>
<div v-else-if="displayMode === 'rectangles'">
<div class="flex flex-row w-full flex-wrap" v-if="currentFolders.length > 0"> <div class="flex flex-row w-full flex-wrap" v-if="currentFolders.length > 0">
<a <a
class="w-1/6 folderIcon flex flex-col p-5 m-2" class="w-1/6 folderIcon flex flex-col p-5 m-2"
@@ -318,6 +461,14 @@ const downloadSelected = async () => {
v-else v-else
class="w-2/3 my-5 mx-auto" class="w-2/3 my-5 mx-auto"
/> />
</div>
</UDashboardPanelContent> </UDashboardPanelContent>
<UCard <UCard
@@ -361,7 +512,9 @@ const downloadSelected = async () => {
multiple multiple
searchable searchable
searchable-placeholder="Suchen..." searchable-placeholder="Suchen..."
:options="tags" option-attribute="name"
value-attribute="id"
:options="filetags"
v-model="fileUploadFormData.tags" v-model="fileUploadFormData.tags"
/> />
</UFormGroup> </UFormGroup>
@@ -382,8 +535,6 @@ const downloadSelected = async () => {
</USlideover> </USlideover>
</template> </template>
<style scoped> <style scoped>
.folderIcon { .folderIcon {
border: 1px solid lightgrey; border: 1px solid lightgrey;
@@ -396,4 +547,8 @@ const downloadSelected = async () => {
color: #69c350; color: #69c350;
} }
tr:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.2);
}
</style> </style>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
</script>
<template>
<UDashboardNavbar>
<template #center>
<h1
:class="['text-xl','font-medium']"
>Zentrales Logbuch</h1>
</template>
</UDashboardNavbar>
<UDashboardPanelContent>
<HistoryDisplay/>
</UDashboardPanelContent>
</template>
<style scoped>
</style>

View File

@@ -96,11 +96,15 @@ const columns = computed(() => templateColumns.filter((column) => selectedColumn
</UModal> </UModal>
<UDashboardNavbar title="E-Mail Konten"> <UDashboardNavbar title="E-Mail Konten">
<template #right> <template #right>
<UTooltip title="In der Beta nicht verfügbar">
<UButton <UButton
:disabled="true"
@click="showEmailAddressModal = true" @click="showEmailAddressModal = true"
> >
+ E-Mail Konto + E-Mail Konto
</UButton> </UButton>
</UTooltip>
</template> </template>
</UDashboardNavbar> </UDashboardNavbar>
<UTable <UTable

View File

@@ -122,7 +122,7 @@ export const useDataStore = defineStore('data', () => {
numberRangeHolder: "customerNumber", numberRangeHolder: "customerNumber",
historyItemHolder: "customer", historyItemHolder: "customer",
supabaseSortColumn: "customerNumber", supabaseSortColumn: "customerNumber",
supabaseSelectWithInformation: "*, projects(*), plants(*), contracts(*), contacts(*), createddocuments(*), documents(*)", supabaseSelectWithInformation: "*, projects(*), plants(*), contracts(*), contacts(*), createddocuments(*), files(*)",
filters: [], filters: [],
templateColumns: [ templateColumns: [
{ {
@@ -228,7 +228,7 @@ export const useDataStore = defineStore('data', () => {
component: profiles component: profiles
}, },
], ],
showTabs: [{label: 'Informationen'},{label: 'Dokumente'},{label: 'Ausgangsbelege'},{label: 'Projekte'},{label: 'Objekte'},{label: 'Verträge'}] showTabs: [{label: 'Informationen'},{label: 'Dateien'},{label: 'Ausgangsbelege'},{label: 'Projekte'},{label: 'Objekte'},{label: 'Verträge'}]
}, },
contacts: { contacts: {
label: "Kontakte", label: "Kontakte",
@@ -329,23 +329,30 @@ export const useDataStore = defineStore('data', () => {
isStandardEntity: true, isStandardEntity: true,
redirect:true, redirect:true,
filters:[], filters:[],
supabaseSelectWithInformation: "*, customer(*), documents(*)", inputColumns: [
"Allgemeines",
"Abrechnung"
],
supabaseSelectWithInformation: "*, customer(*), files(*)",
templateColumns: [ templateColumns: [
{ {
key: "name", key: "name",
label: "Name", label: "Name",
title: true, title: true,
inputType: "text" inputType: "text",
inputColumn: "Allgemeines"
},{ },{
key: "active", key: "active",
label: "Aktiv", label: "Aktiv",
component: active, component: active,
inputType: "bool" inputType: "bool",
inputColumn: "Allgemeines"
},{ },{
key: "recurring", key: "recurring",
label: "Wiederkehrend", label: "Wiederkehrend",
component: recurring, component: recurring,
inputType: "bool" inputType: "bool",
inputColumn: "Allgemeines"
},{ },{
key: 'customer', key: 'customer',
label: "Kunde", label: "Kunde",
@@ -354,6 +361,7 @@ export const useDataStore = defineStore('data', () => {
selectDataType: "customers", selectDataType: "customers",
selectOptionAttribute: "name", selectOptionAttribute: "name",
selectSearchAttributes: ['name'], selectSearchAttributes: ['name'],
inputColumn: "Allgemeines"
},{ },{
key: 'contact', key: 'contact',
label: "Ansprechpartner", label: "Ansprechpartner",
@@ -365,64 +373,91 @@ export const useDataStore = defineStore('data', () => {
}, },
selectOptionAttribute: "fullName", selectOptionAttribute: "fullName",
selectSearchAttributes: ['fullName'], selectSearchAttributes: ['fullName'],
inputColumn: "Allgemeines"
},{ },{
key: 'duration', key: 'duration',
label: "mindest Vertragslaufzeit", label: "mindest Vertragslaufzeit",
inputType: "select", inputType: "select",
selectManualOptions: ['12 Monate','24 Monate','36 Monate','48 Monate'] selectValueAttribute:"label",
selectManualOptions: [
{label:'12 Monate'},
{label:'24 Monate'},
{label:'36 Monate'},
{label:'48 Monate'},
],
inputColumn: "Allgemeines"
},{ },{
key: 'invoiceDispatch', key: 'invoiceDispatch',
label: "Rechnungsversand", label: "Rechnungsversand",
inputType: "select", inputType: "select",
selectManualOptions: ['E-Mail','Post'] selectValueAttribute: "label",
selectManualOptions: [
{label:'E-Mail'},
{label:'Post'}
],
inputColumn: "Abrechnung"
},{ },{
key: 'paymentType', key: 'paymentType',
label: "Zahlungsart", label: "Zahlungsart",
inputType: "select", inputType: "select",
selectManualOptions: ['Einzug','Überweisung'] selectValueAttribute: "label",
selectManualOptions: [
{label:'Einzug'},
{label:'Überweisung'}
],
inputColumn: "Abrechnung"
},{ },{
key: 'startDate', key: 'startDate',
label: "Vertragsstart", label: "Vertragsstart",
inputType: "date", inputType: "date",
inputColumn: "Allgemeines"
},{ },{
key: 'endDate', key: 'endDate',
label: "Vertragsende", label: "Vertragsende",
inputType: "date", inputType: "date",
inputColumn: "Allgemeines"
},{ },{
key: 'signDate', key: 'signDate',
label: "Unterschrieben am", label: "Unterschrieben am",
inputType: "date", inputType: "date",
inputColumn: "Allgemeines"
},{ },{
key: 'sepaDate', key: 'sepaDate',
label: "SEPA Datum", label: "SEPA Datum",
inputType: "date", inputType: "date",
inputColumn: "Abrechnung"
},{ },{
key: 'sepaRef', key: 'sepaRef',
label: "Mandatsreferenz", label: "Mandatsreferenz",
inputType: "text", inputType: "text",
inputColumn: "Abrechnung"
},{ },{
key: 'bankingIban', key: 'bankingIban',
label: "IBAN", label: "IBAN",
inputType: "text", inputType: "text",
inputColumn: "Abrechnung"
},{ },{
key: 'bankingOwner', key: 'bankingOwner',
label: "Inhaber", label: "Inhaber",
inputType: "text", inputType: "text",
inputColumn: "Abrechnung"
},{ },{
key: 'bankingName', key: 'bankingName',
label: "Bank", label: "Bank",
inputType: "text", inputType: "text",
inputColumn: "Abrechnung"
},{ },{
key: 'bankinBIC', key: 'bankinBIC',
label: "BIC", label: "BIC",
inputType: "text", inputType: "text",
inputColumn: "Abrechnung"
},{ },{
key: "notes", key: "notes",
label: "Notizen", label: "Notizen",
inputType: "textarea" inputType: "textarea",
} }
], ],
showTabs: [{label: 'Informationen'},{label: 'Dokumente'}] showTabs: [{label: 'Informationen'},{label: 'Dateien'}]
}, },
absencerequests: { absencerequests: {
label: "Abwesenheitsanträge", label: "Abwesenheitsanträge",
@@ -527,7 +562,7 @@ export const useDataStore = defineStore('data', () => {
},{ },{
label: "Aufgaben" label: "Aufgaben"
},{ },{
label: "Dokumente" label: "Dateien"
}] }]
}, },
products: { products: {
@@ -536,6 +571,7 @@ export const useDataStore = defineStore('data', () => {
isStandardEntity: true, isStandardEntity: true,
redirect:true, redirect:true,
supabaseSelectWithInformation: "*, unit(name)", supabaseSelectWithInformation: "*, unit(name)",
historyItemHolder: "product",
filters: [], filters: [],
templateColumns: [ templateColumns: [
{ {
@@ -568,6 +604,16 @@ export const useDataStore = defineStore('data', () => {
label: "Verkaufpreispreis", label: "Verkaufpreispreis",
component: sellingPrice, component: sellingPrice,
inputType: "number" inputType: "number"
},{
key: "taxPercentage",
label: "Umsatzsteuer",
inputType: "select",
selectOptionAttribute: "label",
selectValueAttribute: 'key',
selectManualOptions: [
{label: "19 %", key: 19},
{label: "7 %", key: 7},
{label: "0 %", key: 0}]
}, },
/*{ /*{
key: "tags", key: "tags",
@@ -601,7 +647,7 @@ export const useDataStore = defineStore('data', () => {
redirect:true, redirect:true,
historyItemHolder: "project", historyItemHolder: "project",
numberRangeHolder: "projectNumber", numberRangeHolder: "projectNumber",
supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*), documents(*), createddocuments(*)", supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*), files(*), createddocuments(*)",
supabaseSortColumn: "projectNumber", supabaseSortColumn: "projectNumber",
filters: [ filters: [
{ {
@@ -685,8 +731,8 @@ export const useDataStore = defineStore('data', () => {
key: "tasks", key: "tasks",
label: "Aufgaben" label: "Aufgaben"
},{ },{
key: "documents", key: "files",
label: "Dokumente" label: "Dateien"
},{ },{
label: "Ausgangsbelege" label: "Ausgangsbelege"
}/*,{ }/*,{
@@ -706,7 +752,7 @@ export const useDataStore = defineStore('data', () => {
isStandardEntity: true, isStandardEntity: true,
redirect:true, redirect:true,
historyItemHolder: "vehicle", historyItemHolder: "vehicle",
supabaseSelectWithInformation: "*, checks(*), documents(*)", supabaseSelectWithInformation: "*, checks(*), files(*)",
filters:[], filters:[],
templateColumns:[ templateColumns:[
{ {
@@ -779,7 +825,7 @@ export const useDataStore = defineStore('data', () => {
{ {
label: 'Informationen', label: 'Informationen',
}, { }, {
label: 'Dokumente', label: 'Dateien',
}, { }, {
label: 'Überprüfungen', label: 'Überprüfungen',
} }
@@ -902,7 +948,7 @@ export const useDataStore = defineStore('data', () => {
{ {
label: 'Informationen', label: 'Informationen',
}, { }, {
label: 'Dokumente', label: 'Dateien',
} }
] ]
}, },
@@ -914,7 +960,7 @@ export const useDataStore = defineStore('data', () => {
label: "Lagerplätze", label: "Lagerplätze",
labelSingle: "Lagerplatz", labelSingle: "Lagerplatz",
isStandardEntity: true, isStandardEntity: true,
supabaseSelectWithInformation: "*, documents(*)", supabaseSelectWithInformation: "*, files(*)",
supabaseSortColumn: "spaceNumber", supabaseSortColumn: "spaceNumber",
redirect: true, redirect: true,
numberRangeHolder: "spaceNumber", numberRangeHolder: "spaceNumber",
@@ -943,7 +989,14 @@ export const useDataStore = defineStore('data', () => {
key: "type", key: "type",
label: "Typ", label: "Typ",
inputType: "select", inputType: "select",
selectManualOptions: ["Standort","Regalplatz", "Kiste", "Palettenplatz", "Sonstiges"], selectValueAttribute: "label",
selectManualOptions: [
{label:"Standort"},
{label:"Regalplatz"},
{label:"Kiste"},
{label:"Palettenplatz"},
{label:"Sonstiges"}
],
inputColumn: "Allgemeines" inputColumn: "Allgemeines"
}, },
{ {
@@ -1013,7 +1066,7 @@ export const useDataStore = defineStore('data', () => {
{ {
label: 'Informationen', label: 'Informationen',
}, { }, {
label: 'Dokumente', label: 'Dateien',
},{label: 'Inventarartikel'} },{label: 'Inventarartikel'}
] ]
}, },
@@ -1034,7 +1087,7 @@ export const useDataStore = defineStore('data', () => {
label: "Inventarartikel", label: "Inventarartikel",
labelSingle: "Inventarartikel", labelSingle: "Inventarartikel",
isStandardEntity: true, isStandardEntity: true,
supabaseSelectWithInformation: "*, documents(*), vendor(id,name), currentSpace(id,name)", supabaseSelectWithInformation: "*, files(*), vendor(id,name), currentSpace(id,name)",
redirect: true, redirect: true,
numberRangeHolder: "articleNumber", numberRangeHolder: "articleNumber",
inputColumns: [ inputColumns: [
@@ -1151,7 +1204,7 @@ export const useDataStore = defineStore('data', () => {
{ {
label: 'Informationen', label: 'Informationen',
}, { }, {
label: 'Dokumente', label: 'Dateien',
} }
] ]
}, },
@@ -1405,7 +1458,7 @@ export const useDataStore = defineStore('data', () => {
label: "Überprüfungen", label: "Überprüfungen",
labelSingle: "Überprüfung", labelSingle: "Überprüfung",
isStandardEntity: true, isStandardEntity: true,
supabaseSelectWithInformation: "*, vehicle(id,licensePlate), profile(id, fullName), inventoryitem(name), documents(*)", supabaseSelectWithInformation: "*, vehicle(id,licensePlate), profile(id, fullName), inventoryitem(name), files(*)",
redirect: true, redirect: true,
historyItemHolder: "check", historyItemHolder: "check",
filters: [], filters: [],
@@ -1451,7 +1504,7 @@ export const useDataStore = defineStore('data', () => {
showTabs: [ showTabs: [
{ {
label: 'Informationen', label: 'Informationen',
}, {label: 'Dokumente'}, {label: 'Ausführungen'}] }, {label: 'Dateien'}, {label: 'Ausführungen'}]
}, },
roles: { roles: {
label: "Rollen", label: "Rollen",
@@ -2060,43 +2113,59 @@ export const useDataStore = defineStore('data', () => {
} }
const uploadFiles = async (formData, files, upsert) => { const uploadFiles = async (formData, files, upsert) => {
//console.log(files)
//console.log(formData)
let documentsToInsert = []
const uploadSingleFile = async (file) => { const uploadSingleFile = async (file) => {
const {data, error} = await supabase const {data:createdFileData,error:createdFileError} = await supabase
.storage
.from("files") .from("files")
.upload(`${profileStore.currentTenant}/${file.name}`, file, {upsert: upsert}) .insert({
tenant: profileStore.currentTenant,
})
.select()
.single()
if (error) { if(createdFileError){
console.log(error) console.log(createdFileError)
console.log(error.statusCode) toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else if(createdFileData) {
console.log(createdFileData)
const {data:uploadData, error: uploadError} = await supabase
.storage
.from("filesdev")
.upload(`${profileStore.currentTenant}/filesbyid/${createdFileData.id}/${file.name}`, file, {upsert: upsert})
if(error.statusCode === '400') { if(uploadError) {
console.log(uploadError)
console.log(uploadError.statusCode)
if(uploadError.statusCode === '400') {
console.log("is 400") console.log("is 400")
toast.add({title: "Hochladen fehlgeschlagen", description: "Die Datei enthält ungültige Zeichen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000}) toast.add({title: "Hochladen fehlgeschlagen", description: "Die Datei enthält ungültige Zeichen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else if(error.statusCode === '409') { } else if(uploadError.statusCode === '409') {
console.log("is 409") console.log("is 409")
toast.add({title: "Hochladen fehlgeschlagen", description: "Es existiert bereits eine Datei mit diesem Namen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000}) toast.add({title: "Hochladen fehlgeschlagen", description: "Es existiert bereits eine Datei mit diesem Namen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else { } else {
toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000}) toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} }
} else if(uploadData) {
const {data:updateFileData, error:updateFileError} = await supabase
.from("files")
.update({
...formData,
path: uploadData.path,
})
.eq("id", createdFileData.id)
} else if (data) { if(updateFileError) {
const returnPath = data.path console.log(updateFileError)
toast.add({title: "Hochladen fehlgeschlagen", icon: "i-heroicons-x-circle", color: "rose", timeout: 10000})
} else {
toast.add({title: "Hochladen erfolgreich"})
documentsToInsert.push({...formData, path: returnPath, tenant: profileStore.currentTenant})
} }
//console.log(data)
} }
}
//uploadInProgress.value = true }
if(files.length === 1) { if(files.length === 1) {
await uploadSingleFile(files[0]) await uploadSingleFile(files[0])
@@ -2107,25 +2176,6 @@ export const useDataStore = defineStore('data', () => {
} }
} }
//console.log(documentsToInsert)
const {data, error} = await supabase
.from("documents")
.insert(documentsToInsert)
.select()
if(error) console.log(error)
else {
//console.log(data)
await fetchDocuments()
//documents.value.push(...data)
}
//uploadModalOpen.value = false;
//uploadInProgress.value = false;
} }
async function fetchBankAccounts () { async function fetchBankAccounts () {
@@ -2177,7 +2227,7 @@ export const useDataStore = defineStore('data', () => {
vehicles.value = (await supabase.from("vehicles").select().eq('tenant', profileStore.currentTenant)).data vehicles.value = (await supabase.from("vehicles").select().eq('tenant', profileStore.currentTenant)).data
} }
async function fetchTimes () { async function fetchTimes () {
times.value = (await supabase.from("times").select().eq('tenant', profileStore.currentTenant).order("start", {ascending:false})).data times.value = (await supabase.from("times").select().eq('tenant', profileStore.currentTenant).order("startDate", {ascending:false})).data
} }
async function fetchHistoryItems () { async function fetchHistoryItems () {