From 6d76acc0bc6dec4085c91e4d6719bdeb7e7ad3a9 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 31 Aug 2025 18:28:59 +0200 Subject: [PATCH 001/103] New Backend changes --- components/EntityEdit.vue | 13 +- components/EntityList.vue | 22 +- components/EntityShow.vue | 4 +- components/EntityShowSubHistoryDisplay.vue | 5 +- components/HistoryDisplay.vue | 67 +- components/MainNav.vue | 121 +- components/ProfileDropdown.vue | 29 - components/TenantDropdown.vue | 27 + components/UserDropdown.vue | 22 +- composables/useEntities.ts | 122 ++ composables/usePermission.ts | 9 + composables/useUsers.ts | 28 + layouts/default.vue | 231 +++- middleware/auth.global.ts | 17 + middleware/auth.ts | 8 - middleware/redirectToMobileIndex.ts | 9 - nuxt.config.ts | 3 +- pages/index.vue | 22 +- pages/login.vue | 136 +-- pages/password-change.vue | 62 + pages/password-reset.vue | 55 + .../standardEntity/[type]/[[mode]]/[[id]].vue | 14 +- plugins/api.ts | 24 + plugins/auth-init.client.ts | 4 + stores/auth.ts | 84 ++ stores/data.js | 1054 +---------------- 26 files changed, 813 insertions(+), 1379 deletions(-) delete mode 100644 components/ProfileDropdown.vue create mode 100644 components/TenantDropdown.vue create mode 100644 composables/useEntities.ts create mode 100644 composables/usePermission.ts create mode 100644 composables/useUsers.ts create mode 100644 middleware/auth.global.ts delete mode 100644 middleware/auth.ts delete mode 100644 middleware/redirectToMobileIndex.ts create mode 100644 pages/password-change.vue create mode 100644 pages/password-reset.vue create mode 100644 plugins/api.ts create mode 100644 plugins/auth-init.client.ts create mode 100644 stores/auth.ts diff --git a/components/EntityEdit.vue b/components/EntityEdit.vue index 3367d57..c356355 100644 --- a/components/EntityEdit.vue +++ b/components/EntityEdit.vue @@ -48,17 +48,15 @@ defineShortcuts({ }, }) - +const supabase = useSupabaseClient() const router = useRouter() const route = useRoute() const dataStore = useDataStore() -const profileStore = useProfileStore() -const supabase = useSupabaseClient() const modal = useModal() + const dataType = dataStore.dataTypes[type] const openTab = ref(0) - const item = ref(JSON.parse(props.item)) console.log(item.value) @@ -156,7 +154,7 @@ const loadOptions = async () => { } else if(option.option === "units") { loadedOptions.value[option.option] = (await supabase.from("units").select()).data } else { - loadedOptions.value[option.option] = (await useSupabaseSelect(option.option)) + loadedOptions.value[option.option] = (await useEntities(option.option).select()) if(dataType.templateColumns.find(x => x.key === option.key).selectDataTypeFilter){ loadedOptions.value[option.option] = loadedOptions.value[option.option].filter(i => dataType.templateColumns.find(x => x.key === option.key).selectDataTypeFilter(i, item)) @@ -210,7 +208,7 @@ const createItem = async () => { if(props.inModal) { ret = await dataStore.createNewItem(type,item.value,true) } else { - ret = dataStore.createNewItem(type,item.value) + ret = await useEntities(type).create(item.value)//dataStore.createNewItem(type,item.value) } emit('returnData', ret) @@ -222,7 +220,8 @@ const updateItem = async () => { if(props.inModal) { ret = await dataStore.updateItem(type,item.value, oldItem.value,true) } else { - ret = await dataStore.updateItem(type,item.value, oldItem.value) + console.log(item.value) + ret = await useEntities(type).update(item.value.id, item.value)//await dataStore.updateItem(type,item.value, oldItem.value) } emit('returnData', ret) diff --git a/components/EntityList.vue b/components/EntityList.vue index a76b031..3505423 100644 --- a/components/EntityList.vue +++ b/components/EntityList.vue @@ -4,6 +4,9 @@ import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue"; import EntityTable from "~/components/EntityTable.vue"; import EntityListMobile from "~/components/EntityListMobile.vue"; +const { has } = usePermission() + + const props = defineProps({ type: { required: true, @@ -83,23 +86,6 @@ const filteredRows = computed(() => { }) } - if(!useRole().generalAvailableRights.value[type].showToAllUsers) { - if(useRole().checkRight(`${type}-viewAll`)){ - console.log("Right to Show All") - } else if(useRole().checkRight(type)){ - console.log("Only Righty to show Own") - console.log(tempItems) - tempItems = tempItems.filter(item => item.profiles.includes(profileStore.activeProfile.id)) - } else { - console.log("No Right to Show") - tempItems = [] - } - } - - - - - return useSearch(searchString.value, tempItems) }) @@ -139,7 +125,7 @@ const filteredRows = computed(() => { /> + {{dataType.labelSingle}} diff --git a/components/EntityShow.vue b/components/EntityShow.vue index 6f5291f..dcdc540 100644 --- a/components/EntityShow.vue +++ b/components/EntityShow.vue @@ -217,7 +217,7 @@ const onTabChange = (index) => { /> - { :top-level-type="type" v-else :platform="platform" - /> + />--> diff --git a/components/EntityShowSubHistoryDisplay.vue b/components/EntityShowSubHistoryDisplay.vue index 5f026c4..5b5ec89 100644 --- a/components/EntityShowSubHistoryDisplay.vue +++ b/components/EntityShowSubHistoryDisplay.vue @@ -27,9 +27,8 @@ const dataType = dataStore.dataTypes[props.topLevelType] \ No newline at end of file diff --git a/components/ProfileDropdown.vue b/components/ProfileDropdown.vue deleted file mode 100644 index a5c7281..0000000 --- a/components/ProfileDropdown.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - \ No newline at end of file diff --git a/components/TenantDropdown.vue b/components/TenantDropdown.vue new file mode 100644 index 0000000..039860a --- /dev/null +++ b/components/TenantDropdown.vue @@ -0,0 +1,27 @@ + + + \ No newline at end of file diff --git a/components/UserDropdown.vue b/components/UserDropdown.vue index b0b57f0..7d15569 100644 --- a/components/UserDropdown.vue +++ b/components/UserDropdown.vue @@ -1,4 +1,5 @@ \ No newline at end of file diff --git a/middleware/auth.global.ts b/middleware/auth.global.ts new file mode 100644 index 0000000..8d0d033 --- /dev/null +++ b/middleware/auth.global.ts @@ -0,0 +1,17 @@ +export default defineNuxtRouteMiddleware(async (to, from) => { + const auth = useAuthStore() + + + + // Wenn nicht eingeloggt → auf /login (außer er will schon dahin) + if (!auth.user && !["/login", "/password-reset"].includes(to.path)) { + return navigateTo("/login") + } + + // Wenn eingeloggt → von /login auf /dashboard umleiten + if (auth.user && !auth.user?.must_change_password && to.path === "/login") { + return navigateTo("/") + } else if(auth.user && auth.user.must_change_password && to.path !== "/password-change") { + return navigateTo("/password-change") + } +}) \ No newline at end of file diff --git a/middleware/auth.ts b/middleware/auth.ts deleted file mode 100644 index 0bdf3b3..0000000 --- a/middleware/auth.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default defineNuxtRouteMiddleware((to, _from) => { - const user = useSupabaseUser() - const router = useRouter() - if (!user.value) { - //useCookie('redirect', { path: '/' }).value = to.fullPath - return router.push("/login") - } -}) \ No newline at end of file diff --git a/middleware/redirectToMobileIndex.ts b/middleware/redirectToMobileIndex.ts deleted file mode 100644 index 6c4b111..0000000 --- a/middleware/redirectToMobileIndex.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default defineNuxtRouteMiddleware(async (to, _from) => { - const router = useRouter() - - console.log(await useCapacitor().getIsPhone()) - - if(await useCapacitor().getIsPhone()) { - return router.push('/mobile') - } -}) \ No newline at end of file diff --git a/nuxt.config.ts b/nuxt.config.ts index 9f690eb..873a7cf 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -34,7 +34,8 @@ export default defineNuxtConfig({ supabase: { key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo", - url: "https://uwppvcxflrcsibuzsbil.supabase.co" + url: "https://uwppvcxflrcsibuzsbil.supabase.co", + redirect:false }, vite: { diff --git a/pages/index.vue b/pages/index.vue index d46258a..8447b85 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/pages/password-change.vue b/pages/password-change.vue new file mode 100644 index 0000000..527a64f --- /dev/null +++ b/pages/password-change.vue @@ -0,0 +1,62 @@ + + + \ No newline at end of file diff --git a/pages/password-reset.vue b/pages/password-reset.vue new file mode 100644 index 0000000..e0abd34 --- /dev/null +++ b/pages/password-reset.vue @@ -0,0 +1,55 @@ + + + \ No newline at end of file diff --git a/pages/standardEntity/[type]/[[mode]]/[[id]].vue b/pages/standardEntity/[type]/[[mode]]/[[id]].vue index 34673e3..81f7ba4 100644 --- a/pages/standardEntity/[type]/[[mode]]/[[id]].vue +++ b/pages/standardEntity/[type]/[[mode]]/[[id]].vue @@ -3,12 +3,12 @@ import {setPageLayout} from "#app"; import {useCapacitor} from "~/composables/useCapacitor.js"; definePageMeta({ - middleware: "auth", layout: "default", }) const route = useRoute() const dataStore = useDataStore() -const supabase = useSupabaseClient() +const api = useNuxtApp().$api + const type = route.params.type const platform = await useCapacitor().getIsPhone() ? "mobile" : "default" @@ -32,12 +32,14 @@ const setupPage = async () => { if(mode.value === "show") { //Load Data for Show - item.value = await useSupabaseSelectSingle(type, route.params.id, dataType.supabaseSelectWithInformation || "*") + //item.value = await useSupabaseSelectSingle(type, route.params.id, dataType.supabaseSelectWithInformation || "*") + item.value = await useEntities(type).selectSingle(route.params.id,"*",true) } else if(mode.value === "edit") { //Load Data for Edit - const data = JSON.stringify((await supabase.from(type).select().eq("id", route.params.id).single()).data) + //const data = JSON.stringify((await supabase.from(type).select().eq("id", route.params.id).single()).data) //await useSupabaseSelectSingle(type, route.params.id) - item.value = data + item.value = JSON.stringify(await useEntities(type).selectSingle(route.params.id)) + //item.value = data } else if(mode.value === "create") { //Load Data for Create @@ -46,7 +48,7 @@ const setupPage = async () => { console.log(item.value) } else if(mode.value === "list") { //Load Data for List - items.value = await useSupabaseSelect(type, dataType.supabaseSelectWithInformation || "*", dataType.supabaseSortColumn,dataType.supabaseSortAscending || false, true) + items.value = await useEntities(type).select() } loaded.value = true diff --git a/plugins/api.ts b/plugins/api.ts new file mode 100644 index 0000000..b7d1b30 --- /dev/null +++ b/plugins/api.ts @@ -0,0 +1,24 @@ +export default defineNuxtPlugin(() => { + const api = $fetch.create({ + baseURL: "http://localhost:3100", + credentials: "include", + onRequest({ options }) { + // Token aus Cookie holen + let token = useCookie("token").value + + // Falls im Request explizit ein anderer JWT übergeben wird → diesen verwenden + if (options.context && (options.context as any).jwt) { + token = (options.context as any).jwt + } + + if (token) { + options.headers = { + ...options.headers, + Authorization: `Bearer ${token}`, + } + } + } + }) + + return { provide: { api } } +}) \ No newline at end of file diff --git a/plugins/auth-init.client.ts b/plugins/auth-init.client.ts new file mode 100644 index 0000000..68affde --- /dev/null +++ b/plugins/auth-init.client.ts @@ -0,0 +1,4 @@ +export default defineNuxtPlugin(async () => { + const auth = useAuthStore() + await auth.init() +}) \ No newline at end of file diff --git a/stores/auth.ts b/stores/auth.ts new file mode 100644 index 0000000..30d935c --- /dev/null +++ b/stores/auth.ts @@ -0,0 +1,84 @@ +import { defineStore } from "pinia" + +export const useAuthStore = defineStore("auth", { + state: () => ({ + user: null as null | { user_id: string; email: string; tenant_id?: string; role?: string }, + profile: null as null | any, + tenants: [] as { tenant_id: string; role: string; tenants: { id: string; name: string } }[], + permissions: [] as string[], + activeTenant: null as any, + loading: true as boolean, + }), + + actions: { + async init(token) { + await this.fetchMe(token) + }, + + async login(email: string, password: string) { + const { token } = await useNuxtApp().$api("/auth/login", { + method: "POST", + body: { email, password } + }) + useCookie("token").value = token // persistieren + await this.fetchMe(token) + }, + + async logout() { + try { + await useNuxtApp().$api("/auth/logout", { method: "POST" }) + } catch (e) { + console.error("Logout fehlgeschlagen:", e) + } + this.user = null + this.permissions = [] + this.profile = null + this.activeTenant = null + this.tenants = [] + useCookie("token").value = null + navigateTo("/login") + }, + + async fetchMe(jwt= null) { + try { + const me = await useNuxtApp().$api("/api/me", { + headers: { Authorization: `Bearer ${jwt}`, + context: { + jwt + }} + }) + console.log(me) + this.user = me.user + this.permissions = me.permissions + this.tenants = me.tenants + this.profile = me.profile + + if(me.activeTenant) { + this.activeTenant = me.activeTenant + this.loading = false + + } else { + + } + + + } catch (err: any) { + if (err?.response?.status === 401) this.logout() + } + }, + + async switchTenant(tenant_id: string) { + this.loading = true + const { token } = await useNuxtApp().$api("/api/tenant/switch", { + method: "POST", + body: { tenant_id } + }) + useCookie("token").value = token + await this.init(token) + }, + + hasPermission(key: string) { + return this.permissions.includes(key) + } + } +}) \ No newline at end of file diff --git a/stores/data.js b/stores/data.js index 3eed2dd..7c569be 100644 --- a/stores/data.js +++ b/stores/data.js @@ -429,17 +429,17 @@ export const useDataStore = defineStore('data', () => { inputType: "textarea", inputColumn: "Allgemeines" }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles, + selectMultiple: true,/!* + component: profiles,*!/ inputColumn: "Allgemeines" - }, + },*/ ], showTabs: [{label: 'Informationen'},{label: 'Ansprechpartner'},{label: 'Dateien'},{label: 'Ausgangsbelege'},{label: 'Projekte'},{label: 'Objekte'},{label: 'Termine'},{label: 'Verträge'}] }, @@ -1080,16 +1080,17 @@ export const useDataStore = defineStore('data', () => { label: "Notizen", inputType: "textarea" }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - },], + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/], showTabs: [ { key: "information", @@ -1198,16 +1199,17 @@ export const useDataStore = defineStore('data', () => { unit: "kW", inputType: "number" }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ ], showTabs: [ { @@ -1344,16 +1346,17 @@ export const useDataStore = defineStore('data', () => { label: "Notizen", inputType: "textarea" }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ ], showTabs: [ { @@ -1624,16 +1627,17 @@ export const useDataStore = defineStore('data', () => { inputType: "text", inputColumn: "Anschaffung" }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ { key: "currentValue", label: "Aktueller Wert", @@ -1698,16 +1702,17 @@ export const useDataStore = defineStore('data', () => { selectMultiple: true, required: true, component: inventoryitemsWithLoad - },{ + },/*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ ], showTabs: [ @@ -1759,16 +1764,17 @@ export const useDataStore = defineStore('data', () => { title: true, required: true, }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ ], showTabs: [ @@ -2340,16 +2346,17 @@ export const useDataStore = defineStore('data', () => { selectOptionAttribute: "name", selectSearchAttributes: ['name'], }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ ], showTabs: [{label: 'Informationen'},{label: 'Auswertung Kostenstelle'}] }, @@ -2390,16 +2397,17 @@ export const useDataStore = defineStore('data', () => { label: "Beschreibung", inputType: "textarea" }, - { + /*{ key: "profiles", label: "Berechtigte Benutzer", inputType: "select", selectDataType: "profiles", selectOptionAttribute: "fullName", selectSearchAttributes: ['fullName'], - selectMultiple: true, - component: profiles - }, + selectMultiple: true,/!* + component: profiles,*!/ + inputColumn: "Allgemeines" + },*/ ], showTabs: [{label: 'Informationen'},{label: 'Buchungen'}] }, @@ -2438,455 +2446,6 @@ export const useDataStore = defineStore('data', () => { }) - const events = ref([]) - const customers = ref([]) - const tasks = ref([]) - const projects = ref([]) - const documents = ref([]) - const spaces = ref([]) - const units = ref([]) - const times = ref([]) - const products = ref([]) - const movements = ref([]) - const forms = ref([]) - const contracts = ref([]) - const formSubmits = ref([]) - const contacts = ref([]) - const vendors = ref([]) - const incominginvoices = ref([]) - const bankAccounts = ref([]) - const bankstatements = ref([]) - const bankrequisitions = ref([]) - const historyItems = ref([]) - const notifications = ref([]) - const accounts = ref([]) - const taxTypes = ref([]) - const plants = ref([]) - const chats = ref([]) - const messages = ref([]) - const createddocuments = ref([]) - const workingtimes = ref([]) - const emailAccounts = ref([]) - const texttemplates =ref([]) - const resources =ref([]) - - async function fetchData () { - await fetchDocuments() - await fetchEvents() - await fetchTasks() - await fetchProjects() - await fetchTimes() - await fetchCustomers() - await fetchContracts() - await fetchContacts() - await fetchForms() - await fetchFormSubmits() - await fetchProducts() - await fetchUnits() - await fetchMovements() - await fetchSpaces() - await fetchVendors() - await fetchIncomingInvoices() - await fetchBankAccounts() - await fetchBankStatements() - await fetchBankRequisitions() - await fetchHistoryItems() - await fetchNotifications() - await fetchAccounts() - await fetchTaxTypes() - await fetchPlants() - await fetchChats() - await fetchMessages() - await fetchCreatedDocuments() - await fetchWorkingTimes() - await fetchEmailAccounts() - await fetchTextTemplates() - await fetchResources() - } - - function clearStore () { - events.value= [] - customers.value= [] - tasks.value= [] - projects.value= [] - documents.value= [] - spaces.value= [] - units.value= [] - times.value= [] - products.value= [] - movements.value= [] - forms.value= [] - contracts.value= [] - formSubmits.value= [] - contacts.value= [] - vendors.value= [] - incominginvoices.value= [] - bankAccounts.value= [] - bankstatements.value= [] - bankrequisitions.value= [] - historyItems.value = [] - notifications.value = [] - accounts.value = [] - taxTypes.value = [] - plants.value = [] - chats.value = [] - messages.value = [] - createddocuments.value = [] - workingtimes.value = [] - emailAccounts.value = [] - texttemplates.value = [] - resources.value = [] - } - - var deepDiffMapper = function () { - return { - VALUE_CREATED: 'created', - VALUE_UPDATED: 'updated', - VALUE_DELETED: 'deleted', - VALUE_UNCHANGED: 'unchanged', - map: function(obj1, obj2, k = null) { - if (this.isFunction(obj1) || this.isFunction(obj2)) { - throw 'Invalid argument. Function given, object expected.'; - } - if (this.isValue(obj1) || this.isValue(obj2)) { - return { - type: this.compareValues(obj1, obj2), - data: {o: obj1, n: obj2, k: k}//obj1 === undefined ? obj2 : obj1 - }; - } - - var diff = {}; - for (var key in obj1) { - if (this.isFunction(obj1[key])) { - continue; - } - - var value2 = undefined; - if (obj2[key] !== undefined) { - value2 = obj2[key]; - } - - diff[key] = this.map(obj1[key], value2,key); - } - for (var key in obj2) { - if (this.isFunction(obj2[key]) || diff[key] !== undefined) { - continue; - } - - diff[key] = this.map(undefined, obj2[key],key); - } - - return diff; - - }, - compareValues: function (value1, value2) { - if (value1 === value2) { - return this.VALUE_UNCHANGED; - } - if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) { - return this.VALUE_UNCHANGED; - } - if (value1 === undefined) { - return this.VALUE_CREATED; - } - if (value2 === undefined) { - return this.VALUE_DELETED; - } - return this.VALUE_UPDATED; - }, - isFunction: function (x) { - return Object.prototype.toString.call(x) === '[object Function]'; - }, - isArray: function (x) { - return Object.prototype.toString.call(x) === '[object Array]'; - }, - isDate: function (x) { - return Object.prototype.toString.call(x) === '[object Date]'; - }, - isObject: function (x) { - return Object.prototype.toString.call(x) === '[object Object]'; - }, - isValue: function (x) { - return !this.isObject(x) && !this.isArray(x); - } - } - }(); - - const generateHistoryItems = async (dataType,newData, oldData=null) => { - //console.log(oldData) - //console.log(newData) - - if(dataTypes[dataType].historyItemHolder){ - let itemsToCreate = [] - - const checkPropState = (key,propContent) => { - //console.log(propContent) - if(propContent.type && propContent.data){ - if(propContent.type === "updated" ||propContent.type === "created"){ - createHistoryItem(key,propContent) - } - } else { - for (let prop in propContent) { - checkPropState(prop,propContent[prop]) - } - } - } - - const createHistoryItem = (key,prop) => { - - //console.log("OLD: " + prop.data.o) - //console.log("NEW: " + prop.data.n) - - let name = "" || key - let oldVal = prop.data.o || "-" - let newVal = prop.data.n || "-" - - /*console.log(key) - console.log(oldVal) - console.log(newVal)*/ - - if(key === "project") { - name = "Projekt" - oldVal = oldVal !== "-" ? projects.value.find(i => i.id === prop.data.o).name : "-" - newVal = projects.value.find(i => i.id === prop.data.n).name - } else if (key === "title") { - name = "Titel" - } else if(key === "type") { - name = "Typ" - } else if(key === "notes") { - name = "Notizen" - } else if(key === "link") { - name = "Link" - } else if(key === "start") { - name = "Start" - oldVal = dayjs(prop.data.o).format("DD.MM.YYYY HH:mm") - newVal = dayjs(prop.data.n).format("DD.MM.YYYY HH:mm") - } else if(key === "end") { - name = "Ende" - oldVal = dayjs(prop.data.o).format("DD.MM.YYYY HH:mm") - newVal = dayjs(prop.data.n).format("DD.MM.YYYY HH:mm") - } else if(key === "birthday") { - name = "Geburtstag" - oldVal = dayjs(prop.data.o).format("DD.MM.YYY") - newVal = dayjs(prop.data.n).format("DD.MM.YYY") - } else if(key === "resources") { - name = "Resourcen" - oldVal = prop.data.o.map(i => i.title).join(", ") - newVal = prop.data.n.map(i => i.title).join(", ") - } else if(key === "customerNumber") { - name = "Kundennummer" - } else if(key === "active") { - name = "Aktiv" - if(oldVal === true){ - oldVal = "Aktiv" - newVal = "Gesperrt" - } else if(oldVal === "-") { - oldVal = "Gesperrt" - newVal = "Aktiv" - } - } else if(key === "isCompany") { - name = "Firmenkunde" - if(oldVal === true){ - oldVal = "Firma" - newVal = "Privatkunde" - } else if(oldVal === "-") { - oldVal = "Privatkunde" - newVal = "Firma" - } - } else if(key === "special") { - name = "Adresszusatz" - } else if(key === "street") { - name = "Straße & Hausnummer" - } else if(key === "city") { - name = "Ort" - } else if(key === "zip") { - name = "Postleitzahl" - } else if(key === "country") { - name = "Land" - } else if(key === "web") { - name = "Webseite" - } else if(key === "email") { - name = "E-Mail" - } else if(key === "tel") { - name = "Telefon" - } else if(key === "ustid") { - name = "USt-ID" - } else if(key === "role") { - name = "Rolle" - } else if(key === "phoneHome") { - name = "Festnetz" - } else if(key === "phoneMobile") { - name = "Mobiltelefon" - } else if(key === "salutation") { - name = "Anrede" - } else if(key === "firstName") { - name = "Vorname" - } else if(key === "lastName") { - name = "Nachname" - } else if(key === "name") { - name = "Name" - } else if(key === "nameAddition") { - name = "Name Zusatz" - } else if(key === "approved") { - name = "Genehmigt" - } else if(key === "manufacturer") { - name = "Hersteller" - } else if(key === "purchasePrice") { - name = "Kaufpreis" - } else if(key === "purchaseDate") { - name = "Kaufdatum" - } else if(key === "serialNumber") { - name = "Seriennummer" - } else if(key === "usePlanning") { - name = "In Plantafel verwenden" - } else if(key === "currentSpace") { - name = "In Plantafel verwenden" - } else if(key === "customer") { - name = "Kunde" - if(prop.data.o) oldVal = customers.value.find(i => i.id === prop.data.o).name - if(prop.data.n) newVal = customers.value.find(i => i.id === prop.data.n).name - } else if(key === "vendor") { - name = "Lieferant" - if(prop.data.o) oldVal = vendors.value.find(i => i.id === prop.data.o).name - if(prop.data.n) newVal = vendors.value.find(i => i.id === prop.data.n).name - - } else if(key === "description") { - name = "Beschreibung" - } else if(key === "categorie") { - name = "Kategorie" - } else if(key === "profile") { - name = "Mitarbeiter" - if(prop.data.o) oldVal = profileStore.profiles.find(i => i.id === prop.data.o).fullName - if(prop.data.n) newVal = profileStore.profiles.find(i => i.id === prop.data.n).fullName - } else if(key === "plant") { - name = "Objekt" - if(prop.data.o) oldVal = plants.value.find(i => i.id === prop.data.o).name - if(prop.data.n) newVal = plants.value.find(i => i.id === prop.data.n).name - } else if(key === "annualPaidLeaveDays") { - name = "Urlaubstage" - } else if(key === "employeeNumber") { - name = "Mitarbeiternummer" - } else if(key === "weeklyWorkingDays") { - name = "Wöchentliche Arbeitstage" - } else if(key === "weeklyWorkingHours") { - name = "Wöchentliche Arbeitszeit" - } else if(key === "customerRef") { - name = "Referenz des Kunden" - } else if(key === "licensePlate") { - name = "Kennzeichen" - }else if(key === "tankSize") { - name = "Tankvolumen" - }else if(key === "towingCapacity") { - name = "Anhängelast" - }else if(key === "color") { - name = "Farbe" - }else if(key === "customPaymentDays") { - name = "Zahlungsziel in Tagen" - }else if(key === "customSurchargePercentage") { - name = "Individueller Aufschlag" - }else if(key === "powerInKW") { - name = "Leistung" - } else if(key === "driver") { - name = "Fahrer" - if(prop.data.o) oldVal = profileStore.profiles.find(i => i.id === prop.data.o).fullName - if(prop.data.n) newVal = profileStore.profiles.find(i => i.id === prop.data.n).fullName - } else if(key === "projecttype") { - name = "Projekttyp" - - //TODO: Resolving for Projecttypes - //if(prop.data.o) oldVal = profiles.value.find(i => i.id === prop.data.o).fullName - //if(prop.data.n) newVal = profiles.value.find(i => i.id === prop.data.n).fullName - } else if(key === "fixed") { - name = "Festgeschrieben" - if(newVal === true){ - oldVal = "Nein" - newVal = "Ja" - } else if(newVal === false) { - oldVal = "Ja" - newVal = "Nein" - } - } else if(key === "archived") { - name = "Archiviert" - if(newVal === true){ - oldVal = "Nein" - newVal = "Ja" - } else if(newVal === false) { - oldVal = "Ja" - newVal = "Nein" - } - } - - - - let text = "" - if(prop.type === "updated" && newVal !== "-" && oldVal !== "-") { - text = `Geändert: ${name} von "${oldVal}" zu "${newVal}"` - } else if(prop.type === "updated" && newVal !== "-" && oldVal === "-") { - text = `Hinzugefügt: ${name} "${newVal}"` - } else if(prop.type === "created") { - text = `Hinzugefügt: ${name} "${newVal}"` - } else if(prop.type === "updated" && newVal === "-" && oldVal !== "-") { - text = `Entfernt: ${name} "${oldVal}"` - } - - let historyItem = { - text: text, - createdBy: profileStore.activeProfile.id, - oldVal: prop.data.o, - newVal: prop.data.n, - tenant: profileStore.currentTenant - } - - historyItem[dataTypes[dataType].historyItemHolder] = newData.id - - - const checkIfNaN = (x) => { - return typeof x === "number" && x !== x; - } - //console.log(key) - //console.log(checkIfNaN(key)) - - if(key !== "fullName" && key !== "phases") { - //console.log(historyItem) - itemsToCreate.push(historyItem) - } - - } - - if(oldData) { - let result = deepDiffMapper.map(oldData,newData) - - //console.log(result) - - for (let prop in result) { - //console.log(prop) - checkPropState(prop,result[prop]) - - } - - } else { - let historyItem = { - text: `${dataTypes[dataType].labelSingle} erstellt`, - createdBy: profileStore.activeProfile.id, - tenant: profileStore.currentTenant - } - - historyItem[dataTypes[dataType].historyItemHolder] = newData.id - - //console.log(historyItem) - itemsToCreate.push(historyItem) - } - - const {data,error} = await supabase.from("historyitems").insert(itemsToCreate) - - if(error) { - console.log(error) - } else { - fetchHistoryItems() - } - } - } - async function createNewItem (dataType,data,noRedirect=false){ if(typeof(data) === 'object') { data = {...data, tenant: profileStore.currentTenant} @@ -3018,540 +2577,15 @@ export const useDataStore = defineStore('data', () => { } } - const uploadFiles = async (formData, files, upsert) => { - const uploadSingleFile = async (file) => { - 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) { - 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(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) { - 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 { - 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]) - } - - } - } - - async function fetchBankAccounts () { - bankAccounts.value = (await supabase.from("bankaccounts").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchBankStatements () { - bankstatements.value = (await supabase.from("bankstatements").select().eq('tenant', profileStore.currentTenant).order("date", {ascending:false})).data - } - async function fetchBankRequisitions () { - bankrequisitions.value = (await supabase.from("bankrequisitions").select().eq('status', "LN")).data - } - async function fetchEvents () { - events.value = (await supabase.from("events").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchContracts () { - contracts.value = (await supabase.from("contracts").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchContacts () { - contacts.value = (await supabase.from("contacts").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchCustomers () { - customers.value = (await supabase.from("customers").select().eq('tenant', profileStore.currentTenant).order("customerNumber", {ascending:true})).data - } - async function fetchTasks () { - tasks.value = (await supabase.from("tasks").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchForms () { - forms.value = (await supabase.from("forms").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchFormSubmits () { - formSubmits.value = (await supabase.from("formSubmits").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchProducts () { - products.value = (await supabase.from("products").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchUnits () { - units.value = (await supabase.from("units").select()).data - } - async function fetchProjects () { - projects.value = (await supabase.from("projects").select().eq("tenant",profileStore.currentTenant)).data - } - async function fetchSpaces () { - spaces.value = (await supabase.from("spaces").select().eq('tenant', profileStore.currentTenant).order("spaceNumber", {ascending:true})).data - } - async function fetchMovements () { - movements.value = (await supabase.from("movements").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchTimes () { - times.value = (await supabase.from("times").select().eq('tenant', profileStore.currentTenant).order("startDate", {ascending:false})).data - } - async function fetchHistoryItems () { - - historyItems.value = (await supabase.from("historyitems").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchVendors () { - vendors.value = (await supabase.from("vendors").select().eq('tenant', profileStore.currentTenant).order("vendorNumber", {ascending:true})).data - } - async function fetchIncomingInvoices () { - incominginvoices.value = (await supabase.from("incominginvoices").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchNotifications () { - notifications.value = (await supabase.from("notifications").select().eq('tenant', profileStore.currentTenant).order("created_at", {ascending: false})).data - } - async function fetchAccounts () { - accounts.value = (await supabase.from("accounts").select()).data - } - async function fetchTaxTypes () { - taxTypes.value = (await supabase.from("taxtypes").select()).data - } - async function fetchPlants () { - plants.value = (await supabase.from("plants").select().eq('tenant', profileStore.currentTenant)).data - } - async function fetchChats() { - chats.value = (await supabase.from("chats").select()).data - } - async function fetchMessages() { - messages.value = (await supabase.from("messages").select().eq('tenant', profileStore.currentTenant).order('created_at', {ascending:true})).data - } - async function fetchCreatedDocuments() { - createddocuments.value = (await supabase.from("createddocuments").select().eq('tenant', profileStore.currentTenant).order('created_at', {ascending:true})).data - } - - async function fetchWorkingTimes() { - workingtimes.value = (await supabase.from("workingtimes").select().eq('tenant', profileStore.currentTenant).order('created_at', {ascending:true})).data - } - - async function fetchEmailAccounts() { - emailAccounts.value = (await supabase.from("emailAccounts").select().eq('tenant', profileStore.currentTenant)).data - } - - async function fetchTextTemplates() { - texttemplates.value = (await supabase.from("texttemplates").select().eq('tenant', profileStore.currentTenant)).data - } - - async function fetchResources() { - resources.value = (await supabase.from("resources").select().eq('tenant', profileStore.currentTenant)).data - } - - async function fetchDocuments () { - let tempDocuments = (await supabase.from("documents").select().eq('tenant', profileStore.currentTenant)).data - - if(tempDocuments.length > 0){ - let paths = [] - tempDocuments.forEach(doc => { - paths.push(doc.path) - }) - - const {data,error} = await supabase.storage.from('files').createSignedUrls(paths,3600) - - tempDocuments = tempDocuments.map((doc,index) => { - - return { - ...doc, - url: data[index].signedUrl - } - }) - - documents.value = tempDocuments - } else { - documents.value = [] - } - - - } - - async function addHistoryItem(text, user, elementId, resourceType) { - let data = { - user: user, - text: text - } - - if(resourceType === "customers") { - data.customer = elementId - } - - const {data:insertData,error:insertError} = await supabase - .from("historyItems") - .insert([addHistoryItemData.value]) - .select() - - if(insertError) { - console.log(insertError) - } else { - toast.add({title: "Eintrag erfolgreich erstellt"}) - await fetchHistoryItems() - } - - } - - //Getters - - const getDocumentsByProfileId = computed(() => (profileId) => { - return documents.value.filter(item => item.profile === profileId && !item.tags.includes("Archiviert")) - }) - - const getMessagesByChatId = computed(() => (chatId) => { - return messages.value.filter(i => i.destination === chatId) - }) - - const getTextTemplatesByDocumentType = computed(() => (documentType) => { - //console.log(documentType) - - let type = "" - if(documentType === "serialInvoices") { - type = "invoices" - } else { - type = documentType - } - - //console.log(type) - - return texttemplates.value.filter(i => i.documentType === type) - }) - - const getStartedWorkingTimes = computed(() => () => { - return workingtimes.value.filter(i => !i.endDate) - }) - - const getStockByProductId = computed(() => (productId) => { - let productMovements = movements.value.filter(movement => movement.productId === productId && movement.projectId === null) - - let count = 0 - - productMovements.forEach(movement => { - count += movement.quantity - }) - - return count - }) - - const getEventTypes = computed(() => { - return profileStore.ownTenant.calendarConfig.eventTypes - }) - - const getTimeTypes = computed(() => { - return profileStore.ownTenant.timeConfig.timeTypes - }) - - const getDocumentTags = computed(() => { - return profileStore.ownTenant.tags.documents - }) - - const getResources = computed(() => { - return [ - ...profiles.value.filter(i => i.tenant === profileStore.currentTenant).map(profile => { - return { - type: 'Mitarbeiter', - title: profile.fullName, - id: profile.id - } - }), - /*...vehicles.value.map(vehicle => { - return { - type: 'Fahrzeug', - title: vehicle.licensePlate, - id: `F-${vehicle.id}` - } - }),*/ - ...inventoryitems.value.filter(i=> i.usePlanning).map(item => { - return { - type: 'Inventar', - title: item.name, - id: `I-${item.id}` - } - }) - ] - }) - - const getEvents = computed(() => { - return [ - ...events.value.map(event => { - let eventColor = profileStore.ownTenant.calendarConfig.eventTypes.find(type => type.label === event.type).color - - let title = "" - if(event.title) { - title = event.title - } else if(event.project) { - projects.value.find(i => i.id === event.project) ? projects.value.find(i => i.id === event.project).name : "" - } - - return { - ...event, - title: title, - borderColor: eventColor, - textColor: eventColor, - backgroundColor: "black" - } - }), - ...absencerequests.value.map(absence => { - return { - id: absence.id, - resourceId: absence.user, - resourceType: "person", - title: `Abw.: ${absence.reason}`, - start: dayjs(absence.start).toDate(), - end: dayjs(absence.end).add(1,'day').toDate(), - allDay: true, - } - }) - ] - }) - - const getEventsByResource = computed(() => { - let tempEvents = [] - events.value.forEach(event => { - console.log(event) - event.resources.forEach(resource => { - console.log(resource) - let eventColor = profileStore.ownTenant.calendarConfig.eventTypes.find(type => type.label === event.type).color - - let title = "" - if(event.title) { - title = event.title - } /*else if(event.project) { - projects.value.find(i => i.id === event.project) ? projects.value.find(i => i.id === event.project).name : "" - }*/ - - - - tempEvents.push({ - ...event, - resourceId: /*resource.type !== 'Mitarbeiter' ? `${resource.type[0]}-${resource.id}`:*/ resource.id, - resourceType: "Mitarbeiter", - title: title, - borderColor: eventColor, - textColor: eventColor, - backgroundColor: "black" - }) - - - }) - }) - - - - return [ - ...tempEvents, - /*...events.value.map(event => { - - - let eventColor = profileStore.ownTenant.calendarConfig.eventTypes.find(type => type.label === event.type).color - - return { - ...event, - title: !event.title ? projects.value.find(i => i.id === event.project).name : event.title, - borderColor: eventColor, - textColor: eventColor, - backgroundColor: "black" - } - }),*/ - ...absencerequests.value.map(absence => { - return { - id: absence.id, - resourceId: absence.user, - resourceType: "person", - title: `Abw.: ${absence.reason}`, - start: dayjs(absence.start).toDate(), - end: dayjs(absence.end).add(1,'day').toDate(), - allDay: true - } - }) - ] - }) - - const getCostCentresComposed = computed(() => { - return [ - /*...vehicles.value.map(vehicle => { - return { - label: "Fahrzeug - " + vehicle.licensePlate, - id: vehicle.id - } - }),*/ - ...projects.value.map(project => { - return { - label: "Projekt - " + project.name, - id: project.id - } - }) - ] - }) - - const getServiceById = computed(() => (itemId) => { - return services.value.find(item => item.id === itemId) - }) - - const getVendorById = computed(() => (itemId) => { - return vendors.value.find(item => item.id === itemId) - }) - - const getIncomingInvoiceById = computed(() => (itemId) => { - return incominginvoices.value.find(item => item.id === itemId) - }) - - const getContactById = computed(() => (itemId) => { - return contacts.value.find(item => item.id === itemId) - }) - - const getDocumentById = computed(() => (itemId) => { - return documents.value.find(item => item.id === itemId) - }) - - const getCustomerById = computed(() => (itemId) => { - return customers.value.find(item => item.id === itemId) - }) - - const getAccountById = computed(() => (accountId) => { - return accounts.value.find(item => item.id === accountId) - }) - - const getCreatedDocumentById = computed(() => (documentId) => { - return createddocuments.value.find(item => item.id === documentId) - }) - - const getBankAccountById = computed(() => (itemId) => { - return bankAccounts.value.find(item => item.id === itemId) - }) - - const getWorkingTimeById = computed(() => (itemId) => { - return workingtimes.value.find(item => item.id === itemId) - }) - - const getProjectById = computed(() => (itemId) => { - if(projects.value.find(i => i.id === itemId)) { - let project = projects.value.find(project => project.id === itemId) - - /*let projectHours = 0 - - let projectTimes = times.value.filter(time => time.projectId === itemId) - projectTimes.forEach(time => projectHours += time.duration) - - project.projectHours = projectHours*/ - - return project - } else { - return null - } - - - - }) return { - uploadFiles, - generateHistoryItems, dataTypes, - - //Data - customers, - tasks, - projects, - documents, - spaces, - units, - times, - products, - movements, - forms, - contracts, - formSubmits, - contacts, - vendors, - incominginvoices, - bankAccounts, - bankstatements, - bankrequisitions, - historyItems, - notifications, - accounts, - taxTypes, - plants, - chats, - messages, - createddocuments, - workingtimes, - emailAccounts, - texttemplates, documentTypesForCreation, - - //Functions createNewItem, updateItem, - fetchData, - clearStore, - fetchTimes, - fetchIncomingInvoices, - fetchDocuments, - fetchWorkingTimes, - //Getters - getDocumentsByProfileId, - getMessagesByChatId, - getTextTemplatesByDocumentType, - getStartedWorkingTimes, - getStockByProductId, - getEventTypes, - getTimeTypes, - getDocumentTags, - getResources, - getEvents, - getEventsByResource, - getCostCentresComposed, - getServiceById, - getVendorById, - getIncomingInvoiceById, - getContactById, - getDocumentById, - getCustomerById, - getProjectById, - getAccountById, - getCreatedDocumentById, - getBankAccountById, - getWorkingTimeById, } }) \ No newline at end of file From 27af6a09539e0496eda7cc38d5cfb5e362da8433 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 2 Sep 2025 18:47:12 +0200 Subject: [PATCH 002/103] New Backend changes --- components/DocumentDisplay.vue | 8 +- components/DocumentDisplayModal.vue | 58 ++--- components/DocumentUploadModal.vue | 2 +- components/EntityList.vue | 10 + components/EntityShow.vue | 28 +-- components/EntityShowSub.vue | 3 +- components/EntityShowSubCreatedDocuments.vue | 4 +- components/EntityShowSubFiles.vue | 4 +- components/EntityTable.vue | 22 +- components/MainNav.vue | 67 +----- components/costcentreDisplay.vue | 2 +- components/displayBankaccounts.vue | 4 +- components/displayIncomeAndExpenditure.vue | 13 +- components/displayOpenBalances.vue | 3 +- components/displayOpenTasks.vue | 6 +- components/displayProjectsInPhases.vue | 2 +- composables/useEntities.ts | 6 +- composables/useFiles.js | 211 ------------------ composables/useFiles.ts | 142 ++++++++++++ pages/accounts/index.vue | 4 - pages/banking/dep/[[accountId]].vue | 4 +- pages/banking/index.vue | 4 - pages/banking/statements/[mode]/[[id]].vue | 4 +- pages/calendar/[mode].vue | 19 +- pages/chats/index.vue | 4 +- pages/chats/show/[id].vue | 4 +- pages/contacts/[mode]/[[id]].vue | 4 +- pages/contacts/index.vue | 4 +- pages/createDocument/edit/[[id]].vue | 83 ++----- pages/createDocument/index.vue | 7 +- pages/createDocument/serialInvoice.vue | 3 +- pages/createDocument/show/[id].vue | 4 +- pages/files/index.vue | 98 ++------ pages/incomingInvoices/create.vue | 4 +- pages/incomingInvoices/edit/[id].vue | 4 +- pages/incomingInvoices/index.vue | 31 ++- pages/incomingInvoices/show/[id].vue | 4 +- pages/index.vue | 14 +- pages/inventory/index.vue | 4 +- pages/inventory/stocks.vue | 4 +- pages/projecttypes/[mode]/[[id]].vue | 4 +- pages/projecttypes/index.vue | 4 +- pages/roles/[mode]/[[id]].vue | 4 +- pages/roles/index.vue | 4 +- pages/settings/index.vue | 4 +- pages/settings/labels/index.vue | 4 +- pages/settings/numberRanges.vue | 4 +- .../standardEntity/[type]/[[mode]]/[[id]].vue | 18 +- pages/test.vue | 15 ++ pages/times/index.vue | 4 +- pages/trackingTrips/[mode]/[[id]].client.vue | 4 +- pages/trackingTrips/index.vue | 4 +- pages/workingtimes/index.vue | 4 +- stores/data.js | 186 ++++++++++----- 54 files changed, 485 insertions(+), 684 deletions(-) delete mode 100644 composables/useFiles.js create mode 100644 composables/useFiles.ts create mode 100644 pages/test.vue diff --git a/components/DocumentDisplay.vue b/components/DocumentDisplay.vue index 7e4715b..0c695a9 100644 --- a/components/DocumentDisplay.vue +++ b/components/DocumentDisplay.vue @@ -66,18 +66,18 @@ const showFile = (file) => { From 9f59b943362d3d0bbb2a4fa8e828dfef130ff09f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Sep 2025 19:10:08 +0200 Subject: [PATCH 009/103] Removed Unused Files --- pages/chat.vue | 108 ------- pages/communication/historyItems/index.vue | 84 ----- pages/contacts/[mode]/[[id]].vue | 338 --------------------- pages/contacts/index.vue | 24 -- 4 files changed, 554 deletions(-) delete mode 100644 pages/chat.vue delete mode 100644 pages/communication/historyItems/index.vue delete mode 100644 pages/contacts/[mode]/[[id]].vue delete mode 100644 pages/contacts/index.vue diff --git a/pages/chat.vue b/pages/chat.vue deleted file mode 100644 index 421b901..0000000 --- a/pages/chat.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/communication/historyItems/index.vue b/pages/communication/historyItems/index.vue deleted file mode 100644 index 8c81f86..0000000 --- a/pages/communication/historyItems/index.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/contacts/[mode]/[[id]].vue b/pages/contacts/[mode]/[[id]].vue deleted file mode 100644 index 53a41a6..0000000 --- a/pages/contacts/[mode]/[[id]].vue +++ /dev/null @@ -1,338 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/contacts/index.vue b/pages/contacts/index.vue deleted file mode 100644 index f66a74a..0000000 --- a/pages/contacts/index.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - - - \ No newline at end of file From 34c5764472c6a6483ecb43da719989a0f7ea59a5 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Sep 2025 19:20:50 +0200 Subject: [PATCH 010/103] Added select-special Fixed Banking Page --- composables/useEntities.ts | 20 ++++++++++++++++++- pages/banking/statements/[mode]/[[id]].vue | 23 +++++++++++++--------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/composables/useEntities.ts b/composables/useEntities.ts index 281a0f1..9aa2c21 100644 --- a/composables/useEntities.ts +++ b/composables/useEntities.ts @@ -36,6 +36,24 @@ export const useEntities = ( return data } + const selectSpecial = async ( + select: string = "*", + sortColumn: string | null = null, + ascending: boolean = false, + ) => { + + const res = await useNuxtApp().$api(`/api/resource-special/${relation}`, { + method: "GET", + params: { + select, + sort: sortColumn || undefined, + asc: ascending + } + }) + + return res || [] + } + const selectSingle = async ( idToEq: string | number, select: string = "*", @@ -113,7 +131,7 @@ export const useEntities = ( return res } - return {select, create, update, archive, selectSingle} + return {select, create, update, archive, selectSingle, selectSpecial} } diff --git a/pages/banking/statements/[mode]/[[id]].vue b/pages/banking/statements/[mode]/[[id]].vue index 00b206e..71a1d2d 100644 --- a/pages/banking/statements/[mode]/[[id]].vue +++ b/pages/banking/statements/[mode]/[[id]].vue @@ -36,6 +36,7 @@ const ownaccounts = ref([]) const loading = ref(true) const setup = async () => { + loading.value = true if(route.params.id) { itemInfo.value = await useEntities("bankstatements").selectSingle(route.params.id,"*, statementallocations(*, cd_id(*), ii_id(*))", undefined, undefined, true) } @@ -48,9 +49,9 @@ const setup = async () => { const incominginvoices = (await useEntities("incominginvoices").select("*, statementallocations(*), vendor(id,name)")).filter(i => i.state === "Gebucht") accounts.value = (await useEntities("accounts").selectSpecial("*","number",true)) - ownaccounts.value = (await useEntities("ownaccounts").select()).data - customers.value = (await useEntities("customers").select()).data - vendors.value = (await useEntities("vendors").select()).data + ownaccounts.value = (await useEntities("ownaccounts").select()) + customers.value = (await useEntities("customers").select()) + vendors.value = (await useEntities("vendors").select()) openDocuments.value = documents.filter(i => i.statementallocations.reduce((n,{amount}) => n + amount, 0).toFixed(2) !== useSum().getCreatedDocumentSum(i,createddocuments.value).toFixed(2)) openDocuments.value = openDocuments.value.map(i => { @@ -149,12 +150,14 @@ const saveAllocation = async (allocation) => { //TODO: BACKEND CHANGE SAVE/REMOVE console.log(allocation) - const {data,error} = await supabase.from("statementallocations").insert({ - ...allocation, - tenant: profileStore.currentTenant - }).select() + const res = await useNuxtApp().$api("/api/banking/statements",{ + method: "POST", + body: { + data: allocation + } + }) - if(data) { + if(res) { await setup() accountToSave.value = null vendorAccountToSave.value = null @@ -168,7 +171,9 @@ const saveAllocation = async (allocation) => { } const removeAllocation = async (allocationId) => { - const {data,error} = await supabase.from("statementallocations").delete().eq("id",allocationId) + const res = await useNuxtApp().$api(`/api/banking/statements/${allocationId}`,{ + method: "DELETE" + }) await setup() } From 949b09449050394a6b3112c94f2cf217a18e838d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Sep 2025 19:26:46 +0200 Subject: [PATCH 011/103] Fixed auth, NAV, projecttypes,numberranges,tenant,textemplates --- components/MainNav.vue | 9 +- pages/projecttypes/[mode]/[[id]].vue | 20 ++--- pages/projecttypes/index.vue | 17 ++-- pages/settings/numberRanges.vue | 39 ++++---- pages/settings/tenant.vue | 25 +++--- pages/settings/texttemplates.vue | 128 ++++++++++++++------------- stores/auth.ts | 16 +++- 7 files changed, 129 insertions(+), 125 deletions(-) diff --git a/components/MainNav.vue b/components/MainNav.vue index ba43fca..4d16dd6 100644 --- a/components/MainNav.vue +++ b/components/MainNav.vue @@ -289,19 +289,14 @@ const links = computed(() => { icon: "i-heroicons-cog-8-tooth", children: [ { - label: "Abrechnung", - to: "https://billing.stripe.com/p/login/cN29Eb32Vdx0gOk288", - icon: "i-heroicons-document-currency-euro", - target: "_blank" - },{ label: "Nummernkreise", to: "/settings/numberRanges", icon: "i-heroicons-clipboard-document-list" - },{ + },/*{ label: "Rollen", to: "/roles", icon: "i-heroicons-key" - },{ + },*/{ label: "E-Mail Konten", to: "/settings/emailAccounts", icon: "i-heroicons-envelope", diff --git a/pages/projecttypes/[mode]/[[id]].vue b/pages/projecttypes/[mode]/[[id]].vue index 4e8c338..49304b5 100644 --- a/pages/projecttypes/[mode]/[[id]].vue +++ b/pages/projecttypes/[mode]/[[id]].vue @@ -21,8 +21,6 @@ defineShortcuts({ }) const openTab = ref(0) -const dataStore = useDataStore() -const supabase = useSupabaseClient() const route = useRoute() const router = useRouter() const toast = useToast() @@ -53,9 +51,9 @@ const setKeys = () => { const setupPage = async() => { if(mode.value === "show" ){ - itemInfo.value = await useSupabaseSelectSingle("projecttypes",route.params.id,"*") + itemInfo.value = await useEntities("projecttypes").selectSingle(route.params.id,"*") } else if (mode.value === "edit") { - itemInfo.value = await useSupabaseSelectSingle("projecttypes",route.params.id,"*") + itemInfo.value = await useEntities("projecttypes").selectSingle(route.params.id,"*") } if(mode.value === "create") { @@ -74,7 +72,7 @@ setupPage() const addPhase = () => { itemInfo.value.initialPhases.push({label: '', icon: ''}), - setKeys + setKeys } @@ -99,13 +97,13 @@ const addPhase = () => {