diff --git a/Dockerfile b/Dockerfile index 21c098e..322cca4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine +FROM node:20-alpine RUN mkdir -p /usr/src/nuxt-app WORKDIR /usr/src/nuxt-app diff --git a/components/EntityShowSub.vue b/components/EntityShowSub.vue index 174fbe8..e788fee 100644 --- a/components/EntityShowSub.vue +++ b/components/EntityShowSub.vue @@ -110,12 +110,15 @@ setup() - +
+ +
+ diff --git a/components/MainNav.vue b/components/MainNav.vue index 4639d9e..85b6680 100644 --- a/components/MainNav.vue +++ b/components/MainNav.vue @@ -95,6 +95,11 @@ const links = computed(() => { icon: "i-heroicons-megaphone", defaultOpen: false, children: [ + { + label: "Helpdesk", + to: "/helpdesk", + icon: "i-heroicons-chat-bubble-left-right" + }, { label: "E-Mail", to: "/email/new", @@ -137,21 +142,16 @@ const links = computed(() => { defaultOpen:false, icon: "i-heroicons-user-group", children: [ - ... true ? [{ - label: "Projektzeiten", - to: "/times", - icon: "i-heroicons-clock" - }] : [], ... true ? [{ label: "Anwesenheiten", - to: "/workingtimes", + to: "/staff/time", icon: "i-heroicons-clock" }] : [], - ... has("absencerequests") ? [{ + /*... has("absencerequests") ? [{ label: "Abwesenheiten", to: "/standardEntity/absencerequests", icon: "i-heroicons-document-text" - }] : [], + }] : [],*/ /*{ label: "Fahrten", to: "/trackingTrips", @@ -244,7 +244,7 @@ const links = computed(() => { }] : [], { label: "Mitarbeiter", - to: "/profiles", + to: "/staff/profiles", icon: "i-heroicons-user-group" }, { @@ -310,8 +310,7 @@ const links = computed(() => { },{ label: "Bankkonten", to: "/settings/banking", - icon: "i-heroicons-currency-euro", - disabled: true + icon: "i-heroicons-currency-euro" },{ label: "Textvorlagen", to: "/settings/texttemplates", diff --git a/components/PDFViewer.client.vue b/components/PDFViewer.client.vue index 8d8e414..2dffbf3 100644 --- a/components/PDFViewer.client.vue +++ b/components/PDFViewer.client.vue @@ -14,11 +14,18 @@ const props = defineProps({ type: Number, default: 1.2, }, + location: { + type: String, + + } }) + const config = useRuntimeConfig() useLicense(config.public.pdfLicense) +const tempStore = useTempStore() + const pdfSrc = ref(null) // ObjectURL fürs Viewer const { $api } = useNuxtApp() @@ -59,19 +66,26 @@ const currentScale = computed(() => { return zoomControl.value?.scale }) -const handleZoomTool = (type) => { +const handleZoomTool = (type, rawScale) => { console.log(type) const zoomCtrl = unref(zoomControl) if (!zoomCtrl) return const scale = unref(currentScale) - if (type === "in") { + if(!type ){ + zoomCtrl.zoom(rawScale) + } else if (type === "in") { scale && zoomCtrl.zoom(scale + 0.25) } else if (type === "out") { scale && zoomCtrl.zoom(scale - 0.25) } else { zoomCtrl.zoom(type) } + + if(["in","out"].includes(type)){ + tempStore.modifySettings(`pdfviewer-scale-${props.location}`,scale) + } + } //Page Control const pageControl = computed(() => vpvRef.value?.pageControl) @@ -196,6 +210,7 @@ watch(downloadControl, (downloadCtrl) => { style="height: 78vh; width: 100%;" :toolbar-options="false" ref="vpvRef" + @loaded="handleZoomTool(null,tempStore.settings[`pdfviewer-scale-${props.location}`] || 1)" />
+import dayjs from "dayjs"; + +const props = defineProps<{ + modelValue: boolean; + entry?: null; +}>(); + +const emit = defineEmits(["update:modelValue", "saved"]); + +const { create, update } = useStaffTime(); + +// v-model für das Modal +const show = computed({ + get: () => props.modelValue, + set: (v: boolean) => emit("update:modelValue", v), +}); + +// 🧱 Lokale reactive Kopie, die beim Öffnen aus props.entry befüllt wird +const local = reactive<{ + id?: string; + description: string; + started_at: string; + stopped_at: string | null; + type: string; +}>({ + id: "", + description: "", + started_at: dayjs().startOf("hour").format("YYYY-MM-DDTHH:mm"), + stopped_at: dayjs().add(1, "hour").format("YYYY-MM-DDTHH:mm"), + type: "work", +}); + +// 📡 Wenn das Modal geöffnet wird, Entry-Daten übernehmen +watch( + () => props.entry, + (val) => { + if (val) { + Object.assign(local, { + id: val.id, + description: val.description || "", + started_at: dayjs(val.started_at).format("YYYY-MM-DDTHH:mm"), + stopped_at: val.stopped_at + ? dayjs(val.stopped_at).format("YYYY-MM-DDTHH:mm") + : dayjs(val.started_at).add(1, "hour").format("YYYY-MM-DDTHH:mm"), + type: val.type || "work", + }); + } else { + Object.assign(local, { + id: "", + description: "", + started_at: dayjs().startOf("hour").format("YYYY-MM-DDTHH:mm"), + stopped_at: dayjs().add(1, "hour").format("YYYY-MM-DDTHH:mm"), + type: "work", + }); + } + }, + { immediate: true } +); + +const loading = ref(false); + +async function handleSubmit() { + loading.value = true; + try { + const payload = { + description: local.description, + started_at: dayjs(local.started_at).toISOString(), + stopped_at: local.stopped_at ? dayjs(local.stopped_at).toISOString() : null, + type: local.type, + }; + + if (local.id) { + await update(local.id, payload); + } else { + await create(payload); + } + + emit("saved"); + show.value = false; + } finally { + loading.value = false; + } +} + + + diff --git a/composables/useCapacitor.js b/composables/useCapacitor.js index 64ec2be..1e4c198 100644 --- a/composables/useCapacitor.js +++ b/composables/useCapacitor.js @@ -2,6 +2,8 @@ import {Capacitor} from "@capacitor/core"; import {Device} from "@capacitor/device"; import {Network} from "@capacitor/network"; +const override = false + export const useCapacitor = () => { const getPlatform = () => { return Capacitor.getPlatform() @@ -14,11 +16,11 @@ export const useCapacitor = () => { const getIsPhone = async () => { let deviceInfo = await useCapacitor().getDeviceInfo() - return deviceInfo.model.toLowerCase().includes('iphone') + return override || deviceInfo.model.toLowerCase().includes('iphone') } const getIsNative = () => { - return Capacitor.isNativePlatform() + return override || Capacitor.isNativePlatform() } const getNetworkStatus = async () => { diff --git a/composables/useEntities.ts b/composables/useEntities.ts index 9975b02..1a92bb4 100644 --- a/composables/useEntities.ts +++ b/composables/useEntities.ts @@ -36,6 +36,83 @@ export const useEntities = ( return data } + + const selectPaginated = async (options: { + select?: string + filters?: Record + sort?: { field: string; direction?: 'asc' | 'desc' }[] + page?: number + limit?: number + includeArchived?: boolean + noPagination?: boolean, + search?: string, + searchColumns?: string[], + distinctColumns?: string[], + }): Promise<{ data: any[]; meta: any }> => { + const { + select = '*', + filters = {}, + sort = [], + page = 1, + limit = 25, + includeArchived = false, + noPagination = false, + search, + searchColumns = [], + distinctColumns = [], + } = options + + const queryParams: Record = { + select, + page, + limit, + noPagination: noPagination ? 'true' : undefined + } + + // --- 🔍 Search-Parameter (optional) --- + if (search && search.trim().length > 0) { + queryParams.search = search.trim() + } + if (searchColumns.length > 0) queryParams.searchColumns = searchColumns.join(',') + if (distinctColumns.length > 0) queryParams.distinctColumns = distinctColumns.join(',') + + + // --- Sortierung --- + if (sort.length > 0) { + queryParams.sort = sort + .map(s => `${s.field}:${s.direction || 'asc'}`) + .join(',') + } + + // --- Filter --- + for (const [key, value] of Object.entries(filters)) { + if (Array.isArray(value)) { + queryParams[`filter[${key}]`] = value.join(',') + } else { + queryParams[`filter[${key}]`] = value + } + } + + const response = await useNuxtApp().$api(`/api/resource/${relation}/paginated`, { + method: 'GET', + params: queryParams + }) + + if (!response) { + return { data: [], meta: null } + } + + let data = response.data || [] + const meta = response.queryConfig || {} + + // --- Optional: Archivierte ausblenden --- + if (!includeArchived) { + data = data.filter((i: any) => !i.archived) + } + + return { data, meta } + } + const selectSpecial = async ( select: string = "*", sortColumn: string | null = null, @@ -134,7 +211,7 @@ export const useEntities = ( } - return {select, create, update, archive, selectSingle, selectSpecial} + return {select, create, update, archive, selectSingle, selectSpecial, selectPaginated} } diff --git a/composables/useFormat.ts b/composables/useFormat.ts new file mode 100644 index 0000000..c908968 --- /dev/null +++ b/composables/useFormat.ts @@ -0,0 +1,8 @@ +export const useFormatDuration = (durationInMinutes:number,) => { + if (!durationInMinutes || durationInMinutes <= 0) return "00:00" + + const hrs = Math.floor(durationInMinutes / 60) + const mins = Math.floor(durationInMinutes % 60) + + return `${String(hrs).padStart(2, "0")}:${String(mins).padStart(2, "0")}` +} \ No newline at end of file diff --git a/composables/useFunctions.js b/composables/useFunctions.js index 7d8d729..a56ac3c 100644 --- a/composables/useFunctions.js +++ b/composables/useFunctions.js @@ -6,25 +6,11 @@ const baseURL = /*"http://192.168.1.129:3333"*/ /*"http://localhost:3333"*/ "htt export const useFunctions = () => { const supabase = useSupabaseClient() - const getWorkingTimesEvaluationData = async (profileId, startDate, endDate) => { - - return (await useNuxtApp().$api(`/api/functions/workingtimeevaluation/${profileId}?start_date=${startDate}&end_date=${endDate}`)) - + const getWorkingTimesEvaluationData = async (user_id, startDate, endDate) => { + return (await useNuxtApp().$api(`/api/functions/timeevaluation/${user_id}?start_date=${startDate}&end_date=${endDate}`)) } const useNextNumber = async (numberRange) => { - /*const {data:{session:{access_token}}} = await supabase.auth.getSession() - - return (await axios({ - method: "POST", - url: `${baseURL}/functions/usenextnumber`, - data: { - numberRange: numberRange, - }, - headers: { - Authorization: `Bearer ${access_token}` - } - })).data.usedNumber*/ return (await useNuxtApp().$api(`/api/functions/usenextnumber/${numberRange}`,)).usedNumber @@ -53,55 +39,35 @@ export const useFunctions = () => { } const useBankingGenerateLink = async (institutionId) => { - const {data:{session:{access_token}}} = await supabase.auth.getSession() - - const {data} = await axios({ - method: "POST", - url: `${baseURL}/functions/bankstatements/generatelink`, - data: { - institutionId - }, - headers: { - Authorization: `Bearer ${access_token}` - } - }) - - console.log(data) - - return data.link - + return (await useNuxtApp().$api(`/api/banking/link/${institutionId}`)).link } - const useCreatePDF = async (invoiceData,path) => { - //const {data:{session:{access_token}}} = await supabase.auth.getSession() - - const data = await useNuxtApp().$api(`/api/functions/createinvoicepdf`, { + const useCreatePDF = async (data,path,type) => { + const returnData = await useNuxtApp().$api(`/api/functions/pdf/${type}`, { method: "POST", body: { - invoiceData: invoiceData, + data: data, backgroundPath: path, } }) - /*const {data} = await axios({ - method: "POST", - url: `${baseURL}/functions/createpdf`, - data: { - invoiceData: invoiceData, - backgroundPath: path, - returnMode: "base64" - }, - headers: { - Authorization: `Bearer ${access_token}` - } - })*/ + console.log(returnData) - console.log(data) - - return `data:${data.mimeType};base64,${data.base64}` + return `data:${returnData.mimeType};base64,${returnData.base64}` } + const useZipCheck = async (zip) => { + const returnData = await useNuxtApp().$api(`/api/functions/check-zip/${zip}`, { + method: "GET", + }) + + return returnData + + } + + + const useGetInvoiceData = async (file) => { const {data:{session:{access_token}}} = await supabase.auth.getSession() @@ -144,34 +110,16 @@ export const useFunctions = () => { } const useBankingCheckInstitutions = async (bic) => { - const {data:{session:{access_token}}} = await supabase.auth.getSession() - const {data} = await axios({ - method: "GET", - url: `${baseURL}/functions/bankstatements/checkinstitutions/${bic}`, - headers: { - Authorization: `Bearer ${access_token}` - } - }) - - return data + return await useNuxtApp().$api(`/api/banking/institutions/${bic}`) } const useBankingListRequisitions = async (reqId) => { - const {data:{session:{access_token}}} = await supabase.auth.getSession() - const {data} = await axios({ - method: "GET", - url: `${baseURL}/functions/bankstatements/listrequisitions/${reqId}`, - headers: { - Authorization: `Bearer ${access_token}` - } - }) - - return data + return await useNuxtApp().$api(`/api/banking/requisitions/${reqId}`) } - return {getWorkingTimesEvaluationData, useNextNumber, useCreateTicket, useBankingGenerateLink, useBankingCheckInstitutions, useBankingListRequisitions, useCreatePDF,useGetInvoiceData, useSendTelegramNotification} + return {getWorkingTimesEvaluationData, useNextNumber, useCreateTicket, useBankingGenerateLink, useZipCheck, useBankingCheckInstitutions, useBankingListRequisitions, useCreatePDF,useGetInvoiceData, useSendTelegramNotification} } \ No newline at end of file diff --git a/composables/useHelpdesk.ts b/composables/useHelpdesk.ts new file mode 100644 index 0000000..d91191c --- /dev/null +++ b/composables/useHelpdesk.ts @@ -0,0 +1,110 @@ +// composables/useHelpdeskApi.ts +import { ref } from 'vue' +import { useNuxtApp } from '#app' + +export function useHelpdeskApi() { + const { $api } = useNuxtApp() + const loading = ref(false) + const error = ref(null) + + const base = '/api/helpdesk' + + // 🔹 Konversationen abrufen + async function getConversations(status?: string) { + loading.value = true + error.value = null + try { + const query = status ? `?status=${status}` : '' + const data = await $api(`${base}/conversations${query}`) + return data + } catch (err: any) { + error.value = err.message || 'Fehler beim Laden der Konversationen' + return [] + } finally { + loading.value = false + } + } + + // 🔹 Einzelne Konversation + async function getConversation(id: string) { + try { + return await $api(`${base}/conversations/${id}`) + } catch (err: any) { + error.value = err.message + return null + } + } + + // 🔹 Nachrichten einer Konversation + async function getMessages(conversationId: string) { + try { + return await $api(`${base}/conversations/${conversationId}/messages`) + } catch (err: any) { + error.value = err.message + return [] + } + } + + // 🔹 Neue Nachricht senden + async function sendMessage(conversationId: string, text: string) { + try { + return await $api(`${base}/conversations/${conversationId}/messages`, { + method: 'POST', + body: { text }, + }) + } catch (err: any) { + error.value = err.message + throw err + } + } + + async function replyMessage(conversationId: string, text: string) { + try { + return await $api(`${base}/conversations/${conversationId}/reply`, { + method: 'POST', + body: { text }, + }) + } catch (err: any) { + error.value = err.message + throw err + } + } + + // 🔹 Neuen Kontakt (manuell) anlegen + async function createContact(payload: { email?: string; phone?: string; display_name?: string }) { + try { + return await $api(`${base}/contacts`, { + method: 'POST', + body: payload, + }) + } catch (err: any) { + error.value = err.message + throw err + } + } + + // 🔹 Konversation-Status ändern + async function updateConversationStatus(conversationId: string, status: string) { + try { + return await $api(`${base}/conversations/${conversationId}/status`, { + method: 'PATCH', + body: { status }, + }) + } catch (err: any) { + error.value = err.message + throw err + } + } + + return { + loading, + error, + getConversations, + getConversation, + getMessages, + sendMessage, + createContact, + updateConversationStatus, + replyMessage, + } +} diff --git a/composables/usePDFGenerator.js b/composables/usePDFGenerator.js deleted file mode 100644 index 046f66e..0000000 --- a/composables/usePDFGenerator.js +++ /dev/null @@ -1,1046 +0,0 @@ -import {PDFDocument, StandardFonts, rgb} from "pdf-lib" - - - -const getCoordinatesForPDFLib = (x ,y, page) => { - /* - * @param x the wanted X Parameter in Millimeters from Top Left - * @param y the wanted Y Parameter in Millimeters from Top Left - * @param page the page Object - * - * @returns x,y object - * */ - - - let retX = x * 2.83 - let retY = page.getHeight()-(y*2.83) - - return { - x: retX, - y: retY - } - -} - - - - -export const useCreatePdf = async (invoiceData,backgroundSourceBuffer) => { - - const uri = ref("test") - const genPDF = async () => { - const pdfDoc = await PDFDocument.create() - - const font = await pdfDoc.embedFont(StandardFonts.Helvetica) - const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold) - - let pages = [] - let pageCounter = 1 - - - - //const backgroundPdfSourceBuffer = await fetch("/Briefpapier.pdf").then((res) => res.arrayBuffer()) - const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer) - - const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0]) - const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0]) - - //console.log("TEST") - const page1 = pdfDoc.addPage() - - //console.log(page1.getSize().width/2.83) - - page1.drawPage(firstPageBackground, { - x: 0, - y: 0, - }) - //console.log(page1.getSize()) - pages.push(page1) - //console.log(pages) - - - //Falzmarke 1 - pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,105,page1), - end: getCoordinatesForPDFLib(7,105,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - }) - - //Lochmarke - pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,148.5,page1), - end: getCoordinatesForPDFLib(7,148.5,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - }) - - //Falzmarke 2 - pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,210,page1), - end: getCoordinatesForPDFLib(7,210,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - }) - - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(20,45,page1), - end: getCoordinatesForPDFLib(105,45,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - pages[pageCounter - 1].drawText(invoiceData.adressLine, { - ...getCoordinatesForPDFLib(21,48, page1), - size:6, - color:rgb(0,0,0), - lineHeight:6, - opacity: 1, - maxWidth: 240 - }) - - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(20,50,page1), - end: getCoordinatesForPDFLib(105,50,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - - - - pages[pageCounter - 1].drawText(invoiceData.recipient.name, { - ...getCoordinatesForPDFLib(21,55, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - if(invoiceData.recipient.contact && !invoiceData.recipient.special) { - pages[pageCounter - 1].drawText(invoiceData.recipient.contact, { - ...getCoordinatesForPDFLib(21,60, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(invoiceData.recipient.street, { - ...getCoordinatesForPDFLib(21,65, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(`${invoiceData.recipient.zip} ${invoiceData.recipient.city}`, { - ...getCoordinatesForPDFLib(21,70, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } else if(invoiceData.recipient.contact && invoiceData.recipient.special) { - pages[pageCounter - 1].drawText(invoiceData.recipient.contact, { - ...getCoordinatesForPDFLib(21,60, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(invoiceData.recipient.special, { - ...getCoordinatesForPDFLib(21,65, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - - pages[pageCounter - 1].drawText(invoiceData.recipient.street, { - ...getCoordinatesForPDFLib(21,70, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(`${invoiceData.recipient.zip} ${invoiceData.recipient.city}`, { - ...getCoordinatesForPDFLib(21,75, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } else if(!invoiceData.recipient.contact && !invoiceData.recipient.special) { - - pages[pageCounter - 1].drawText(invoiceData.recipient.street, { - ...getCoordinatesForPDFLib(21,60, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(`${invoiceData.recipient.zip} ${invoiceData.recipient.city}`, { - ...getCoordinatesForPDFLib(21,65, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } else { - pages[pageCounter - 1].drawText(invoiceData.recipient.contact, { - ...getCoordinatesForPDFLib(21,60, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(invoiceData.recipient.special, { - ...getCoordinatesForPDFLib(21,65, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - - pages[pageCounter - 1].drawText(invoiceData.recipient.street, { - ...getCoordinatesForPDFLib(21,70, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText(`${invoiceData.recipient.zip} ${invoiceData.recipient.city}`, { - ...getCoordinatesForPDFLib(21,75, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } - - - - - - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(20,90,page1), - end: getCoordinatesForPDFLib(105,90,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - - //Rechts - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(125,50,page1), - end: getCoordinatesForPDFLib(200,50,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - pages[pageCounter - 1].drawText(invoiceData.info.documentNumberTitle, { - ...getCoordinatesForPDFLib(126,55, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.documentNumber ? invoiceData.info.documentNumber : "XXXX", { - y: getCoordinatesForPDFLib(126,55, page1).y, - x: getCoordinatesForPDFLib(126,55,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.documentNumber ? invoiceData.info.documentNumber : "XXXX",10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - pages[pageCounter - 1].drawText("Kundennummer", { - ...getCoordinatesForPDFLib(126,60, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.customerNumber, { - y: getCoordinatesForPDFLib(126,60, page1).y, - x: getCoordinatesForPDFLib(126,60,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.customerNumber,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText("Belegdatum", { - ...getCoordinatesForPDFLib(126,65, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.documentDate, { - y: getCoordinatesForPDFLib(126,65, page1).y, - x: getCoordinatesForPDFLib(126,65,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.documentDate,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - if(invoiceData.info.deliveryDateType !== "Kein Lieferdatum anzeigen") { - pages[pageCounter - 1].drawText(invoiceData.info.deliveryDateType, { - ...getCoordinatesForPDFLib(126,70, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.deliveryDate, { - y: getCoordinatesForPDFLib(126,70, page1).y, - x: getCoordinatesForPDFLib(126,70,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.deliveryDate,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } - - - - pages[pageCounter - 1].drawText("Ansprechpartner", { - ...getCoordinatesForPDFLib(126,75, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.contactPerson, { - y: getCoordinatesForPDFLib(126,75, page1).y, - x: getCoordinatesForPDFLib(126,75,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.contactPerson,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - if(invoiceData.info.contactTel && invoiceData.info.contactEMail) { - pages[pageCounter - 1].drawText("Telefon", { - ...getCoordinatesForPDFLib(126,80, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.contactTel, { - y: getCoordinatesForPDFLib(126,80, page1).y, - x: getCoordinatesForPDFLib(126,80,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.contactTel,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText("E-Mail", { - ...getCoordinatesForPDFLib(126,85, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.contactEMail, { - y: getCoordinatesForPDFLib(126,85, page1).y, - x: getCoordinatesForPDFLib(126,85,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.contactEMail,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } else if(invoiceData.info.contactTel && !invoiceData.info.contactEMail) { - pages[pageCounter - 1].drawText("Telefon", { - ...getCoordinatesForPDFLib(126,80, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.contactTel, { - y: getCoordinatesForPDFLib(126,80, page1).y, - x: getCoordinatesForPDFLib(126,80,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.contactTel,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } else if(!invoiceData.info.contactTel && invoiceData.info.contactEMail) { - pages[pageCounter - 1].drawText("E-Mail", { - ...getCoordinatesForPDFLib(126,80, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.contactEMail, { - y: getCoordinatesForPDFLib(126,80, page1).y, - x: getCoordinatesForPDFLib(126,80,page1).x + 210 - font.widthOfTextAtSize(invoiceData.info.contactEMail,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - } - - - - /*pages[pageCounter - 1].drawText("Projekt:", { - ...getCoordinatesForPDFLib(126,90, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(invoiceData.info.project, { - y: getCoordinatesForPDFLib(126,90, page1).y, - x: getCoordinatesForPDFLib(126,90,page1).x + 50 /!*+ 210 - font.widthOfTextAtSize(invoiceData.info.project,10)*!/, - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 150 - })*/ - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(125,90,page1), - end: getCoordinatesForPDFLib(200,90,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - //Title - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(20,95,page1), - end: getCoordinatesForPDFLib(200,95,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - pages[pageCounter - 1].drawText(invoiceData.title, { - ...getCoordinatesForPDFLib(20,100, page1), - size:13, - color:rgb(0,0,0), - lineHeight:15, - opacity: 1, - maxWidth: 500 - }) - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(20,105,page1), - end: getCoordinatesForPDFLib(200,105,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - if(invoiceData.description) { - pages[pageCounter - 1].drawText(invoiceData.description, { - ...getCoordinatesForPDFLib(20,112, page1), - size:13, - color:rgb(0,0,0), - lineHeight:15, - opacity: 1, - maxWidth: 500 - }) - } - - - pages[pageCounter - 1].drawText(invoiceData.startText,{ - ...getCoordinatesForPDFLib(20,119, page1), - size: 10, - color: rgb(0,0,0), - lineHeight: 10, - opacity: 1, - maxWidth: 500 - }) - - /*page1.drawLine({ - start: getCoordinatesForPDFLib(20,115,page1), - end: getCoordinatesForPDFLib(200,115,page1), - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1 - })*/ - - pages[pageCounter - 1].drawRectangle({ - ...getCoordinatesForPDFLib(20,140, page1), - width: 180 * 2.83, - height: 8 * 2.83, - color: rgb(0,0,0), - opacity: 0.25 - }) - - //Header - - pages[pageCounter - 1].drawText("Pos", { - ...getCoordinatesForPDFLib(21,137, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - pages[pageCounter - 1].drawText("Menge", { - ...getCoordinatesForPDFLib(35,137, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - pages[pageCounter - 1].drawText("Bezeichnung", { - ...getCoordinatesForPDFLib(52,137, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - if( invoiceData.type !== "deliveryNotes") { - pages[pageCounter - 1].drawText("Einheitspreis", { - ...getCoordinatesForPDFLib(135,137, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - pages[pageCounter - 1].drawText("Gesamt", { - y: getCoordinatesForPDFLib(25,137, page1).y, - x: getCoordinatesForPDFLib(25,137,page1).x + 490 - fontBold.widthOfTextAtSize("Gesamt",12), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - } - - - - - - let rowHeight = 145.5 - - let pageIndex = 0 - - - invoiceData.rows.forEach((row,index) => { - - - - if(!["pagebreak","title","text"].includes(row.mode)){ - - console.log(row) - - pages[pageCounter - 1].drawText(String(row.pos), { - ...getCoordinatesForPDFLib(21,rowHeight, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(`${row.quantity} ${row.unit}`, { - ...getCoordinatesForPDFLib(35,rowHeight, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - console.log(row.text.match(/.{1,35}/g)) - - if(invoiceData.type !== "deliveryNotes") { - pages[pageCounter - 1].drawText(row.text.match(/.{1,35}/g).join("\n"), { - ...getCoordinatesForPDFLib(52,rowHeight, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - font: fontBold - }) - } else { - pages[pageCounter - 1].drawText(row.text.match(/.{1,80}/g).join("\n"), { - ...getCoordinatesForPDFLib(52,rowHeight, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - font: fontBold - }) - } - - - - //let textLineBreaks = (row.text.match(/\n/g) || []).length - let textAutoLines = (invoiceData.type !== "deliveryNotes" ? row.text.match(/.{1,35}/g) : row.text.match(/.{1,80}/g) || []).length - console.log(textAutoLines) - - console.log(row) - - - if(row.descriptionText) { - if(invoiceData.type !== "deliveryNotes") { - pages[pageCounter - 1].drawText(row.descriptionText.match(/.{1,70}/g).join("\n"), { - ...getCoordinatesForPDFLib(52,rowHeight + ( textAutoLines * 4), page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - }) - } else { - pages[pageCounter - 1].drawText(row.descriptionText.match(/.{1,80}/g).join("\n"), { - ...getCoordinatesForPDFLib(52,rowHeight + ( textAutoLines * 4), page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - }) - } - - - } - - - if(invoiceData.type !== "deliveryNotes") { - pages[pageCounter - 1].drawText(row.price, { - ...getCoordinatesForPDFLib(135,rowHeight, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240 - }) - - pages[pageCounter - 1].drawText(row.rowAmount, { - y: getCoordinatesForPDFLib(25,rowHeight, page1).y, - x: getCoordinatesForPDFLib(25,rowHeight,page1).x + 490 - font.widthOfTextAtSize(row.rowAmount,10), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240, - }) - - if(row.discountPercent > 0) { - pages[pageCounter - 1].drawText(row.discountText, { - y: getCoordinatesForPDFLib(25,rowHeight + 5, page1).y, - x: getCoordinatesForPDFLib(25,rowHeight + 5,page1).x + 490 - font.widthOfTextAtSize(row.discountText,8), - size:8, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240, - }) - } - } - - - - - - if(row.descriptionText) { - let lineBreaks = (row.descriptionText.match(/\n/g) || []).length - let autoLines = (row.descriptionText.match(/.{1,70}/g) || []).length - rowHeight += 12 + (lineBreaks + autoLines + textAutoLines) * 2.5 - } else { - rowHeight += 12 + textAutoLines * 2.5 - } - - - pageIndex += 1 - - - } else if(row.mode === 'pagebreak') { - - const page = pdfDoc.addPage() - - page.drawPage(secondPageBackground, { - x: 0, - y: 0, - }) - - //Falzmarke 1 - page.drawLine({ - start: getCoordinatesForPDFLib(0,105,page1), - end: getCoordinatesForPDFLib(7,105,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - }) - - //Lochmarke - page.drawLine({ - start: getCoordinatesForPDFLib(0,148.5,page1), - end: getCoordinatesForPDFLib(7,148.5,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - }) - - //Falzmarke 2 - page.drawLine({ - start: getCoordinatesForPDFLib(0,210,page1), - end: getCoordinatesForPDFLib(7,210,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - }) - - page.drawRectangle({ - ...getCoordinatesForPDFLib(20,25, page1), - width: 180 * 2.83, - height: 8 * 2.83, - color: rgb(0,0,0), - opacity: 0.25 - }) - - //Header - - page.drawText("Pos", { - ...getCoordinatesForPDFLib(21,22, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - page.drawText("Menge", { - ...getCoordinatesForPDFLib(35,22, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - page.drawText("Bezeichnung", { - ...getCoordinatesForPDFLib(52,22, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - if(invoiceData.type !== "deliveryNotes"){ - page.drawText("Einheitspreis", { - ...getCoordinatesForPDFLib(135,22, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - page.drawText("Gesamt", { - y: getCoordinatesForPDFLib(25,22, page1).y, - x: getCoordinatesForPDFLib(25,22,page1).x + 490 - fontBold.widthOfTextAtSize("Gesamt",12), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - } - - - - pageCounter += 1; - pageIndex = 0; - rowHeight = 30; - - pages.push(page) - //console.log(pages) - - - - - } else if (row.mode === 'title') { - if(index === 0 ||pageIndex === 0) { - rowHeight += 3 - } - - pages[pageCounter - 1].drawText(String(row.pos), { - ...getCoordinatesForPDFLib(21,rowHeight -3, page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - - pages[pageCounter - 1].drawRectangle({ - ...getCoordinatesForPDFLib(20,rowHeight, page1), - width: 180 * 2.83, - height: 8 * 2.83, - color: rgb(0,0,0), - opacity: 0.25 - }) - - pages[pageCounter - 1].drawText(row.text, { - ...getCoordinatesForPDFLib(35,rowHeight -3, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - maxWidth: 500, - font: fontBold - }) - - rowHeight += 8 - } else if (row.mode === 'text') { - if(index === 0 ||pageIndex === 0) { - rowHeight += 3 - } - - let textAutoLines = (row.text.match(/.{1,60}/g) || []).length - - pages[pageCounter - 1].drawRectangle({ - ...getCoordinatesForPDFLib(20,rowHeight, page1), - width: 180 * 2.83, - height: 8 * 2.83, - color: rgb(0,0,0), - opacity: 0.25 - }) - - pages[pageCounter - 1].drawText(row.text.match(/.{1,60}/g).join("\n"), { - ...getCoordinatesForPDFLib(21,rowHeight - 2.6, page1), - size:12, - color:rgb(0,0,0), - lineHeight:12, - opacity: 1, - font: fontBold - }) - - //let textLineBreaks = (row.text.match(/\n/g) || []).length - //console.log(textAutoLines) - - - if(row.descriptionText) { - pages[pageCounter - 1].drawText(row.descriptionText.match(/.{1,100}/g).join("\n"), { - ...getCoordinatesForPDFLib(21,rowHeight + ( textAutoLines * 4), page1), - size:10, - color:rgb(0,0,0), - lineHeight:10, - opacity: 1, - }) - - let addHeight = (row.descriptionText.match(/.{1,80}/g) || []).length * 4 + 10 - console.log(addHeight) - rowHeight += addHeight - } else { - rowHeight += 6 - } - - - - } - - - }) - - - //Pos 1 - - - - - - //Footer - //console.log(rowHeight) - //rowHeight += 25 - - if(invoiceData.type !== "deliveryNotes"){ - pages[pageCounter - 1].drawRectangle({ - ...getCoordinatesForPDFLib(20,rowHeight, page1), - width: 180 * 2.83, - height: 8 * 2.83, - color: rgb(0,0,0), - opacity: 0.25 - }) - - pages[pageCounter - 1].drawRectangle({ - ...getCoordinatesForPDFLib(20,rowHeight +16, page1), - width: 180 * 2.83, - height: 8 * 2.83, - color: rgb(0,0,0), - opacity: 0.25 - }) - - pages[pageCounter - 1].drawText("Nettobetrag", { - ...getCoordinatesForPDFLib(21,rowHeight-3, page1), - size:11, - color:rgb(0,0,0), - lineHeight:11, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - pages[pageCounter - 1].drawText(invoiceData.total.totalNet, { - y: getCoordinatesForPDFLib(21,rowHeight-3, page1).y, - x: getCoordinatesForPDFLib(21,rowHeight-3,page1).x + 500 - fontBold.widthOfTextAtSize(invoiceData.total.totalNet,11), - size:11, - color:rgb(0,0,0), - lineHeight:11, - opacity: 1, - maxWidth: 240, - font:fontBold - }) - - pages[pageCounter - 1].drawText("zzgl. 19% MwSt", { - ...getCoordinatesForPDFLib(21,rowHeight+5, page1), - size:11, - color:rgb(0,0,0), - lineHeight:11, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - pages[pageCounter - 1].drawText(invoiceData.total.total19, { - y: getCoordinatesForPDFLib(21,rowHeight+5, page1).y, - x: getCoordinatesForPDFLib(21,rowHeight+5,page1).x + 500 - fontBold.widthOfTextAtSize(invoiceData.total.total19,11), - size:11, - color:rgb(0,0,0), - lineHeight:11, - opacity: 1, - maxWidth: 240, - font:fontBold - }) - - pages[pageCounter - 1].drawText("Gesamtsumme", { - ...getCoordinatesForPDFLib(21,rowHeight+13, page1), - size:11, - color:rgb(0,0,0), - lineHeight:11, - opacity: 1, - maxWidth: 240, - font: fontBold - }) - - pages[pageCounter - 1].drawText(invoiceData.total.totalGross, { - y: getCoordinatesForPDFLib(21,rowHeight+13, page1).y, - x: getCoordinatesForPDFLib(21,rowHeight+13,page1).x + 500 - fontBold.widthOfTextAtSize(invoiceData.total.totalGross,11), - size:11, - color:rgb(0,0,0), - lineHeight:11, - opacity: 1, - maxWidth: 240, - font:fontBold - }) - } - - - pages[pageCounter - 1].drawText(invoiceData.endText,{ - ...getCoordinatesForPDFLib(20,rowHeight+22, page1), - size: 10, - color: rgb(0,0,0), - lineHeight: 10, - opacity: 1, - maxWidth: 500 - }) - - //console.log(await pdfDoc.saveAsBase64({dataUri: true})) - - uri.value = await pdfDoc.saveAsBase64({dataUri: true}) - //console.log(uri.value) - - } - - await genPDF() - - //const pdfBytes = await pdfDoc.save() - - //const pdfDataUri = await pdfDoc.saveAsBase64({dataUri: true}) - return uri.value - //let blob = new Blob(pdfBytes, {type: "application/pdf"}) - /*let link = document.createElement('a') - link.href = pdfDataUri//window.URL.createObjectURL(blob) - link.download = "test.pdf" - link.click()*/ - -} \ No newline at end of file diff --git a/composables/useStaffTime.ts b/composables/useStaffTime.ts new file mode 100644 index 0000000..749166a --- /dev/null +++ b/composables/useStaffTime.ts @@ -0,0 +1,54 @@ +interface StaffTimeEntry { + id: string + started_at: string + stopped_at?: string | null + duration_minutes?: number | null + type: string + description?: string | null + created_at?: string +} + +export function useStaffTime() { + const { $api } = useNuxtApp() + + + + async function list(params?: { user_id?: string }) { + const query = new URLSearchParams() + if (params?.user_id) query.append("user_id", params.user_id) + + return await $api(`/api/staff/time${query.toString() ? `?${query}` : ''}`, { method: 'GET' }) + } + + async function start(description?: string) { + return await $api('/api/staff/time', { + method: 'POST', + body: { + started_at: new Date().toISOString(), + type: 'work', + description, + }, + }) + } + + async function stop(id: string) { + return await $api(`/api/staff/time/${id}/stop`, { + method: 'PUT', + body: { stopped_at: new Date().toISOString() }, + }) + } + + async function get(id: string) { + return await $api(`/api/staff/time/${id}`, { method: 'GET' }) + } + + async function create(data: Record) { + return await $api('/api/staff/time', { method: 'POST', body: data }) + } + + async function update(id: string, data: Record) { + return await $api(`/api/staff/time/${id}`, { method: 'PUT', body: data }) + } + + return { list, start, stop, get, create, update } +} diff --git a/composables/useWorkingTimePDFGenerator.js b/composables/useWorkingTimePDFGenerator.js deleted file mode 100644 index a3d2404..0000000 --- a/composables/useWorkingTimePDFGenerator.js +++ /dev/null @@ -1,232 +0,0 @@ -import {PDFDocument, StandardFonts, rgb} from "pdf-lib" -import dayjs from "dayjs" - - -const getCoordinatesForPDFLib = (x ,y, page) => { - /* - * @param x the wanted X Parameter in Millimeters from Top Left - * @param y the wanted Y Parameter in Millimeters from Top Left - * @param page the page Object - * - * @returns x,y object - * */ - - - let retX = x * 2.83 - let retY = page.getHeight()-(y*2.83) - - return { - x: retX, - y: retY - } - -} - -const getDuration = (time) => { - const minutes = Math.floor(dayjs(time.endDate).diff(dayjs(time.startDate),'minutes',true)) - const hours = Math.floor(minutes/60) - return { - //dezimal: dez, - hours: hours, - minutes: minutes, - composed: `${hours}:${String(minutes % 60).padStart(2,"0")} Std` - } -} - - - - -export const useCreateWorkingTimesPdf = async (input,backgroundSourceBuffer) => { - - const uri = ref("test") - const genPDF = async () => { - const pdfDoc = await PDFDocument.create() - - const font = await pdfDoc.embedFont(StandardFonts.Helvetica) - const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold) - - let pages = [] - let pageCounter = 1 - - - - const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer) - - const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0]) - const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0]) - - const page1 = pdfDoc.addPage() - - page1.drawPage(firstPageBackground, { - x: 0, - y: 0, - }) - - pages.push(page1) - - - //Falzmarke 1 - /*pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,105,page1), - end: getCoordinatesForPDFLib(7,105,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - })*/ - - //Lochmarke - /*pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,148.5,page1), - end: getCoordinatesForPDFLib(7,148.5,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - })*/ - - //Falzmarke 2 - /*pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,210,page1), - end: getCoordinatesForPDFLib(7,210,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - })*/ - console.log(input) - pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.profile}`,{ - x: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Eingereicht: ${Math.floor(input.sumWorkingMinutesEingereicht/60)}:${String(input.sumWorkingMinutesEingereicht % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Genehmigt: ${Math.floor(input.sumWorkingMinutesApproved/60)}:${String(input.sumWorkingMinutesApproved % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`Feiertagsausgleich: ${Math.floor(input.sumWorkingMinutesRecreationDays/60)}:${String(input.sumWorkingMinutesRecreationDays % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Urlaubsausgleich: ${Math.floor(input.sumWorkingMinutesVacationDays/60)}:${String(input.sumWorkingMinutesVacationDays % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Krankheitsausgleich: ${Math.floor(input.sumWorkingMinutesSickDays/60)}:${String(input.sumWorkingMinutesSickDays % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Soll Stunden: ${Math.floor(input.timeSpanWorkingMinutes/60)}:${String(input.timeSpanWorkingMinutes % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Inoffizielles Saldo: ${Math.sign(input.saldoInOfficial) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldoInOfficial/60))}:${String(Math.abs(input.saldoInOfficial) % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Saldo: ${Math.sign(input.saldo) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldo/60))}:${String(Math.abs(input.saldo) % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,105,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,105,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`Start:`,{ - x: getCoordinatesForPDFLib(20,110,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,110,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`Ende:`,{ - x: getCoordinatesForPDFLib(60,110,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(60,110,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`Dauer:`,{ - x: getCoordinatesForPDFLib(100,110,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(100,110,pages[pageCounter -1]).y, - size: 10, - }) - - - let rowHeight = 115 - - - let splitted = [] - - let reversedInput = input.times.slice().reverse() - - const splittedLength = Math.floor((reversedInput.length - 25) / 40) - - splitted.push(reversedInput.slice(0,25)) - - let lastIndex = 25 - for (let i = 0; i < splittedLength; ++i ) { - splitted.push(reversedInput.slice(lastIndex, lastIndex + (i + 1) * 40)) - lastIndex = lastIndex + (i + 1) * 40 + 1 - } - - splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) - - - splitted.forEach((chunk,index) => { - if(index > 0) { - const page = pdfDoc.addPage() - - page.drawPage(secondPageBackground, { - x: 0, - y: 0, - }) - - pages.push(page) - pageCounter++ - rowHeight = 20 - - } - - chunk.forEach(time => { - pages[pageCounter - 1].drawText(`${dayjs(time.startDate).format("HH:mm DD.MM.YY")}`,{ - x: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`${dayjs(time.endDate).format("HH:mm DD.MM.YY")}`,{ - x: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`${getDuration(time).composed}`,{ - x: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).y, - size: 10, - }) - - rowHeight += 6 - - }) - }) - - - - - uri.value = await pdfDoc.saveAsBase64({dataUri: true}) - - } - - await genPDF() - - return uri.value - - -} \ No newline at end of file diff --git a/composables/useZipCheck.js b/composables/useZipCheck.js deleted file mode 100644 index 7251659..0000000 --- a/composables/useZipCheck.js +++ /dev/null @@ -1,8 +0,0 @@ - -export const useZipCheck = async (zip) => { - const supabase = useSupabaseClient() - - const result = (await supabase.from("citys").select().eq("zip",Number(zip)).maybeSingle()).data - - return result ? result.short : null -} \ No newline at end of file diff --git a/nuxt.config.ts b/nuxt.config.ts index 0b7f3a5..ad3b05c 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,69 +1,70 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ - devtools: { - enabled: false, - timeline: { - enabled: true - } - }, + devtools: { + enabled: false, + timeline: { + enabled: true + } + }, - ssr:false, + modules: ['@pinia/nuxt', '@nuxt/ui', '@nuxtjs/supabase', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', 'nuxt-tiptap-editor', '@nuxtjs/leaflet'], - imports: { - dirs: ['stores'] - }, + ssr: false, - extends: [ - '@nuxt/ui-pro' - ], + imports: { + dirs: ['stores'] + }, - components: [{ - path: '~/components' - }], + extends: [ + '@nuxt/ui-pro' + ], - build: { - transpile: ['@vuepic/vue-datepicker'] - }, + components: [{ + path: '~/components' + }], - modules: ['@pinia/nuxt', '@nuxt/ui', '@nuxtjs/supabase', "nuxt-editorjs", '@nuxtjs/fontaine', 'nuxt-viewport', 'nuxt-tiptap-editor', '@nuxtjs/leaflet'], - - routeRules: { - '/printing': {ssr: false} - - }, - - supabase: { - key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo", - url: "https://uwppvcxflrcsibuzsbil.supabase.co", - redirect:false - }, - - vite: { - optimizeDeps: { - include: ["@editorjs/editorjs", "dayjs"], - }, - }, - - ui: { - icons: ['heroicons','mdi','simple-icons'] - }, - - colorMode: { - preference: 'system' - }, + build: { + transpile: ['@vuepic/vue-datepicker'] + }, - tiptap: { - prefix: "Tiptap" - }, + routeRules: { + '/printing': {ssr: false} + + }, + + supabase: { + key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo", + url: "https://uwppvcxflrcsibuzsbil.supabase.co", + redirect: false + }, + + vite: { + optimizeDeps: { + include: ["@editorjs/editorjs", "dayjs"], + }, + }, + + ui: { + icons: ['heroicons', 'mdi', 'simple-icons'] + }, + + colorMode: { + preference: 'system' + }, + + + tiptap: { + prefix: "Tiptap" + }, runtimeConfig: { - public: { - apiBase: '', - pdfLicense: '' - } + public: { + apiBase: '', + pdfLicense: '' + } }, - compatibilityDate: '2024-12-18' + compatibilityDate: '2024-12-18' }) \ No newline at end of file diff --git a/pages/banking/statements/[mode]/[[id]].vue b/pages/banking/statements/[mode]/[[id]].vue index b23655f..37f5db9 100644 --- a/pages/banking/statements/[mode]/[[id]].vue +++ b/pages/banking/statements/[mode]/[[id]].vue @@ -69,7 +69,7 @@ const setup = async () => { allocatedIncomingInvoices.value = incominginvoices.filter(i => i.statementallocations.find(x => x.bs_id === itemInfo.value.id)) console.log(allocatedDocuments.value) console.log(allocatedIncomingInvoices.value) - openIncomingInvoices.value = (await useEntities("incominginvoices").select("*, statementallocations(*), vendor(*)")).filter(i => i.statementallocations.reduce((n,{amount}) => n + amount, 0).toFixed(2) !== getInvoiceSum(i,false)) + openIncomingInvoices.value = (await useEntities("incominginvoices").select("*, statementallocations(*), vendor(*)")).filter(i => !i.archived && i.statementallocations.reduce((n,{amount}) => n + amount, 0).toFixed(2) !== getInvoiceSum(i,false)) //console.log(openIncomingInvoices.value) // return incominginvoices.value.filter(i => bankstatements.value.filter(x => x.assignments.find(y => y.type === 'incomingInvoice' && y.id === i.id)).length === 0) @@ -181,14 +181,14 @@ const searchString = ref("") const filteredDocuments = computed(() => { - return useSearch(searchString.value, openDocuments.value) + return useSearch(searchString.value, openDocuments.value.filter(i => i.state === "Gebucht")) }) const filteredIncomingInvoices = computed(() => { - return useSearch(searchString.value, openIncomingInvoices.value) + return useSearch(searchString.value, openIncomingInvoices.value.filter(i => i.state === "Gebucht")) }) diff --git a/pages/createDocument/edit/[[id]].vue b/pages/createDocument/edit/[[id]].vue index f4d86af..82c3659 100644 --- a/pages/createDocument/edit/[[id]].vue +++ b/pages/createDocument/edit/[[id]].vue @@ -1150,9 +1150,9 @@ const getDocumentData = async () => { label: "Ansprechpartner", content: contactPerson.full_name, }, - ...contactPerson.fixedTel || contactPerson.mobileTel ? [{ + ...itemInfo.value.contactTel || contactPerson.fixed_tel || contactPerson.mobile_tel ? [{ label: "Telefon", - content: contactPerson.fixedTel || contactPerson.mobileTel, + content: itemInfo.value.contactTel || contactPerson.fixed_tel || contactPerson.mobile_tel, }] : [], ...contactPerson.email ? [{ label: "E-Mail", @@ -1225,7 +1225,7 @@ const generateDocument = async () => { showDocument.value = false const path = letterheads.value.find(i => i.id === itemInfo.value.letterhead).path - uri.value = await useFunctions().useCreatePDF(await getDocumentData(), path) + uri.value = await useFunctions().useCreatePDF(await getDocumentData(), path, "createdDocument") /*uri.value = await useNuxtApp().$api("/api/functions/createinvoicepdf",{ method: "POST", body: { @@ -3108,24 +3108,11 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
- - - - -
diff --git a/pages/createDocument/show/[id].vue b/pages/createDocument/show/[id].vue index 199f8ff..81ae4dc 100644 --- a/pages/createDocument/show/[id].vue +++ b/pages/createDocument/show/[id].vue @@ -128,7 +128,7 @@ const openBankstatements = () => { :data="linkedDocument.url" class="w-full previewDocumentMobile" />--> - + diff --git a/pages/export.vue b/pages/export/index.vue similarity index 96% rename from pages/export.vue rename to pages/export/index.vue index cb1e725..b1c99e8 100644 --- a/pages/export.vue +++ b/pages/export/index.vue @@ -4,6 +4,7 @@ const exports = ref([]) const auth = useAuthStore() const toast = useToast() +const router = useRouter() const setupPage = async () => { exports.value = await useNuxtApp().$api("/api/exports",{ @@ -56,7 +57,10 @@ const createExport = async () => { +import {ref, onMounted, watch} from 'vue' +import {format, isToday, formatDistanceToNow} from 'date-fns' +import {de as deLocale} from 'date-fns/locale' + +const {getConversations, getMessages, sendMessage, replyMessage, updateConversationStatus} = useHelpdeskApi() + +const conversations = ref([]) +const selectedConversation = ref(null) +const messages = ref([]) +const messageText = ref('') +const filterStatus = ref('') +const loading = ref(false) + +const route = useRoute() + +// Referenzen für Scroll + Shortcuts +const convRefs = ref([]) + +async function loadConversations() { + loading.value = true + conversations.value = await getConversations(filterStatus.value) + + if(route.params.id){ + await selectConversation(conversations.value.find(i => i.id === route.params.id)) + } + + loading.value = false +} + +async function selectConversation(conv: any) { + selectedConversation.value = conv + messages.value = await getMessages(conv.id) +} + +async function send() { + if (!messageText.value || !selectedConversation.value) return + await replyMessage(selectedConversation.value.id, messageText.value) + messageText.value = '' + messages.value = await getMessages(selectedConversation.value.id) +} + +// Keyboard navigation +defineShortcuts({ + arrowdown: () => { + const index = conversations.value.findIndex(c => c.id === selectedConversation.value?.id) + if (index === -1) selectedConversation.value = conversations.value[0] + else if (index < conversations.value.length - 1) + selectedConversation.value = conversations.value[index + 1] + }, + arrowup: () => { + const index = conversations.value.findIndex(c => c.id === selectedConversation.value?.id) + if (index === -1) selectedConversation.value = conversations.value.at(-1) + else if (index > 0) + selectedConversation.value = conversations.value[index - 1] + } +}) + +watch(selectedConversation, () => { + if (!selectedConversation.value) return + const ref = convRefs.value[selectedConversation.value.id] + if (ref) ref.scrollIntoView({block: 'nearest'}) +}) + +onMounted(loadConversations) +watch(filterStatus, loadConversations) + +// Gruppierung nach aufeinanderfolgenden gleichen Autoren +const groupedMessages = computed(() => { + if (!messages.value.length) return [] + + const groups: any[] = [] + let current: any = null + + for (const msg of messages.value) { + const authorKey = `${msg.direction}-${msg.author_user_id || msg.author_name || 'anon'}` + if (!current || current.key !== authorKey) { + current = { + key: authorKey, + direction: msg.direction, + author_name: msg.direction === 'outgoing' ? 'Du' : msg.author_name || 'Kunde', + author_avatar: msg.author_avatar || null, + messages: [msg], + latest_created_at: msg.created_at, + } + groups.push(current) + } else { + current.messages.push(msg) + current.latest_created_at = msg.created_at + } + } + return groups +}) + + + diff --git a/pages/index.vue b/pages/index.vue index e231764..03bb084 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -68,11 +68,6 @@ definePageMeta({ const { isNotificationsSlideoverOpen } = useDashboard() -const setup = async () => { - -} - -setup() \ No newline at end of file diff --git a/pages/profiles/show/[id].vue b/pages/profiles/show/[id].vue deleted file mode 100644 index 364d0a1..0000000 --- a/pages/profiles/show/[id].vue +++ /dev/null @@ -1,637 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/settings/banking/index.vue b/pages/settings/banking/index.vue index 1d18001..d550833 100644 --- a/pages/settings/banking/index.vue +++ b/pages/settings/banking/index.vue @@ -15,12 +15,17 @@ const reqData = ref({}) const bankaccounts = ref([]) +const showReqData = ref(false) + const setupPage = async () => { if(route.query.ref) { reqData.value = await useFunctions().useBankingListRequisitions(route.query.ref) + if(reqData.value.accounts.length > 0){ + showReqData.value = true + } } - bankaccounts.value = await useSupabaseSelect("bankaccounts") + bankaccounts.value = await useEntities("bankaccounts").select() } const checkBIC = async () => { @@ -61,12 +66,17 @@ const addAccount = async (account) => { } const updateAccount = async (account) => { - const {data,error} = await supabase.from("bankaccounts").update({accountId: account.id, expired: false}).eq("iban",account.iban).select() - if(error) { + + let bankaccountId = bankaccounts.value.find(i => i.iban === account.iban).id + + const res = await useEntities("bankaccounts").update(bankaccountId, {accountId: account.id, expired: false}) + + if(!res) { console.log(error) toast.add({title: "Es gab einen Fehler bei aktualisieren des Accounts", color:"rose"}) - } else if(data) { + } else { toast.add({title: "Account erfolgreich aktualisiert"}) + reqData.value = null setupPage() } } @@ -133,25 +143,34 @@ setupPage() -
- {{account.iban}} - {{account.owner_name}} + + + +
+ {{account.iban}} - {{account.owner_name}} + + + Hinzufügen + + + Aktualisieren + +
+
+
+ - - + Konto - - - Aktualisieren - -
+ + + + + + + + + + + + + +
+ +
+

{{ profile.full_name }}

+

{{ profile.employee_number || '–' }}

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ {{ day.label }} +
+ + Std +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/profiles/index.vue b/pages/staff/profiles/index.vue similarity index 92% rename from pages/profiles/index.vue rename to pages/staff/profiles/index.vue index 6513a1a..51a2690 100644 --- a/pages/profiles/index.vue +++ b/pages/staff/profiles/index.vue @@ -1,6 +1,4 @@ + + diff --git a/pages/staff/time/index.vue b/pages/staff/time/index.vue new file mode 100644 index 0000000..2ac58f1 --- /dev/null +++ b/pages/staff/time/index.vue @@ -0,0 +1,173 @@ + + + diff --git a/pages/standardEntity/[type]/[[mode]]/[[id]].vue b/pages/standardEntity/[type]/[mode]/[[id]].vue similarity index 87% rename from pages/standardEntity/[type]/[[mode]]/[[id]].vue rename to pages/standardEntity/[type]/[mode]/[[id]].vue index 4e9a95e..456a378 100644 --- a/pages/standardEntity/[type]/[[mode]]/[[id]].vue +++ b/pages/standardEntity/[type]/[mode]/[[id]].vue @@ -43,7 +43,12 @@ const setupPage = async (sort_column = null, sort_direction = null) => { console.log(item.value) } else if (mode.value === "list") { //Load Data for List - items.value = await useEntities(type).select(dataType.supabaseSelectWithInformation, sort_column || dataType.supabaseSortColumn, sort_direction === "asc", true) + //items.value = await useEntities(type).select(dataType.supabaseSelectWithInformation, sort_column || dataType.supabaseSortColumn, sort_direction === "asc", true) + items.value = await useEntities(type).select({ + filters: {}, + sort: [], + page:1 + }) } loaded.value = true diff --git a/pages/standardEntity/[type]/index.vue b/pages/standardEntity/[type]/index.vue new file mode 100644 index 0000000..bc0f445 --- /dev/null +++ b/pages/standardEntity/[type]/index.vue @@ -0,0 +1,449 @@ + + + + + \ No newline at end of file diff --git a/pages/workingtimes/[mode]/[[id]].vue b/pages/workingtimes/[mode]/[[id]].vue deleted file mode 100644 index 6e4946e..0000000 --- a/pages/workingtimes/[mode]/[[id]].vue +++ /dev/null @@ -1,150 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/workingtimes/evaluate/[id].vue b/pages/workingtimes/evaluate/[id].vue deleted file mode 100644 index d246c1d..0000000 --- a/pages/workingtimes/evaluate/[id].vue +++ /dev/null @@ -1,291 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/workingtimes/index.vue b/pages/workingtimes/index.vue deleted file mode 100644 index a7b64b3..0000000 --- a/pages/workingtimes/index.vue +++ /dev/null @@ -1,327 +0,0 @@ - - - - - \ No newline at end of file diff --git a/plugins/dayjs.ts b/plugins/dayjs.ts new file mode 100644 index 0000000..723c115 --- /dev/null +++ b/plugins/dayjs.ts @@ -0,0 +1,14 @@ +import dayjs from 'dayjs' +import duration from 'dayjs/plugin/duration' +import relativeTime from 'dayjs/plugin/relativeTime' +import localizedFormat from 'dayjs/plugin/localizedFormat' +import 'dayjs/locale/de' + +dayjs.extend(duration) +dayjs.extend(relativeTime) +dayjs.extend(localizedFormat) +dayjs.locale('de') + +export default defineNuxtPlugin(() => { + return { provide: { dayjs } } +}) \ No newline at end of file diff --git a/stores/auth.ts b/stores/auth.ts index d3fd5a6..7f6a38a 100644 --- a/stores/auth.ts +++ b/stores/auth.ts @@ -17,6 +17,10 @@ export const useAuthStore = defineStore("auth", { async init(token) { await this.fetchMe(token) + const tempStore = useTempStore() + + if(this.profile.temp_config) tempStore.setStoredTempConfig(this.profile.temp_config) + if(useCapacitor().getIsNative()) { navigateTo("/mobile") } else { diff --git a/stores/data.js b/stores/data.js index 264537a..5133bd1 100644 --- a/stores/data.js +++ b/stores/data.js @@ -40,7 +40,6 @@ import space from "~/components/columnRenderings/space.vue" import driver from "~/components/columnRenderings/driver.vue" import quantity from "~/components/helpRenderings/quantity.vue" -import {useZipCheck} from "~/composables/useZipCheck.js"; import {useFunctions} from "~/composables/useFunctions.js"; import signDate from "~/components/columnRenderings/signDate.vue"; import sepaDate from "~/components/columnRenderings/sepaDate.vue"; @@ -214,7 +213,8 @@ export const useDataStore = defineStore('data', () => { }, inputColumn: "Allgemeines", sortable: true, - maxLength: 20 + maxLength: 20, + distinct: true, }, { key: "nameAddition", label: "Firmenname Zusatz", @@ -226,7 +226,8 @@ export const useDataStore = defineStore('data', () => { return item.isCompany }, inputColumn: "Allgemeines", - maxLength: 20 + maxLength: 20, + distinct: true, },{ key: "salutation", label: "Anrede", @@ -253,7 +254,8 @@ export const useDataStore = defineStore('data', () => { return !item.isCompany }, inputColumn: "Allgemeines", - sortable: true + sortable: true, + distinct: true },{ key: "title", label: "Titel", @@ -340,7 +342,8 @@ export const useDataStore = defineStore('data', () => { component: active, inputType: "bool", inputColumn: "Allgemeines", - sortable: true + sortable: true, + distinct: true }, { key: "customPaymentDays", label: "Zahlungsziel in Tagen", @@ -385,7 +388,7 @@ export const useDataStore = defineStore('data', () => { inputType: "number", inputChangeFunction: async function (row) { if(row.infoData.zip) { - row.infoData.city = (await useZipCheck(row.infoData.zip)) + row.infoData.city = (await useFunctions().useZipCheck(row.infoData.zip)).short } }, disabledInTable: true, @@ -1331,7 +1334,7 @@ export const useDataStore = defineStore('data', () => { disabledInTable: true, inputChangeFunction: async function (row) { if(row.infoData.zip) { - row.infoData.city = (await useZipCheck(row.infoData.zip)) + row.infoData.city = (await useFunctions().useZipCheck(row.infoData.zip)).short } }, }, @@ -1513,7 +1516,7 @@ export const useDataStore = defineStore('data', () => { inputColumn: "Ort", inputChangeFunction: async function (row) { if(row.infoData.zip) { - row.infoData.city = (await useZipCheck(row.infoData.zip)) + row.infoData.city = (await useFunctions().useZipCheck(row.infoData.zip)).short } }, }, @@ -2580,6 +2583,10 @@ export const useDataStore = defineStore('data', () => { ], showTabs: [{label: 'Informationen'},{label: 'Buchungen'}] }, + bankaccounts: { + label: "Bankkonten", + labelSingle: "Bankkonto", + } } const documentTypesForCreation = ref({ diff --git a/stores/temp.js b/stores/temp.js index 19be026..25c58f5 100644 --- a/stores/temp.js +++ b/stores/temp.js @@ -3,28 +3,68 @@ import {defineStore} from 'pinia' // @ts-ignore export const useTempStore = defineStore('temp', () => { + const auth = useAuthStore() + const searchStrings = ref({}) const filters = ref({}) const columns = ref({}) + const pages = ref({}) + const settings = ref({}) + + const storeTempConfig = async () => { + const config = { + searchStrings: searchStrings.value, + columns: columns.value, + pages: pages.value, + settings: settings.value, + } + + await useNuxtApp().$api(`/api/profiles/${auth.profile.id}`,{ + method: 'PUT', + body: {temp_config: config} + }) + } + + function setStoredTempConfig (config) { + searchStrings.value = config.searchStrings + columns.value = config.columns + pages.value = config.pages + settings.value = config.settings + } function modifySearchString(type,input) { searchStrings.value[type] = input + storeTempConfig() } function clearSearchString(type) { searchStrings.value[type] = "" + storeTempConfig() } function modifyFilter(type,input) { filters.value[type] = input + storeTempConfig() } function modifyColumns(type,input) { columns.value[type] = input + storeTempConfig() + } + + function modifyPages(type,input) { + pages.value[type] = input + storeTempConfig() + } + + function modifySettings(type,input) { + settings.value[type] = input + storeTempConfig() } return { + setStoredTempConfig, searchStrings, modifySearchString, clearSearchString, @@ -32,6 +72,10 @@ export const useTempStore = defineStore('temp', () => { modifyFilter, columns, modifyColumns, + modifyPages, + pages, + modifySettings, + settings }