New Backend changes

This commit is contained in:
2025-08-31 18:28:59 +02:00
parent b0497142ed
commit 6d76acc0bc
26 changed files with 813 additions and 1379 deletions

View File

@@ -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)

View File

@@ -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)
})
</script>
@@ -139,7 +125,7 @@ const filteredRows = computed(() => {
/>
<UButton
v-if="platform !== 'mobile' && useRole().checkRight(`${type}-create`)"
v-if="platform !== 'mobile' && has(`${type}-create`)/*&& useRole().checkRight(`${type}-create`)*/"
@click="router.push(`/standardEntity/${type}/create`)"
class="ml-3"
>+ {{dataType.labelSingle}}</UButton>

View File

@@ -217,7 +217,7 @@ const onTabChange = (index) => {
/>
</div>
<EntityShowSubFiles
<!--<EntityShowSubFiles
:item="props.item"
:query-string-data="getAvailableQueryStringData()"
v-else-if="tab.label === 'Dateien'"
@@ -268,7 +268,7 @@ const onTabChange = (index) => {
:top-level-type="type"
v-else
:platform="platform"
/>
/>-->
</template>
</UTabs>
<UDashboardPanelContent v-else style="overflow-x: hidden;">

View File

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

View File

@@ -14,8 +14,9 @@ const props = defineProps({
default: false
}
})
const profileStore = useProfileStore()
const supabase = useSupabaseClient()
const auth = useAuthStore()
const toast = useToast()
const showAddHistoryItemModal = ref(false)
const colorMode = useColorMode()
@@ -28,11 +29,12 @@ const setup = async () => {
if(await useCapacitor().getIsPhone()) platform.value = "mobile"
if(props.type && props.elementId){
items.value = (await supabase.from("historyitems").select().eq(props.type,props.elementId).order("created_at",{ascending: true})).data || []
} else {
//items.value = (await supabase.from("historyitems").select().eq(props.type,props.elementId).order("created_at",{ascending: true})).data || []
items.value = await useNuxtApp().$api(`/api/resource/${props.type}/${props.elementId}/history`)
} /*else {
items.value = (await supabase.from("historyitems").select().order("created_at",{ascending: true})).data || []
}
}*/
}
setup()
@@ -40,55 +42,24 @@ setup()
const addHistoryItemData = ref({
text: "",
config: {
type: props.type,
id: props.elementId
}
text: ""
})
const addHistoryItem = async () => {
console.log(addHistoryItemData.value)
addHistoryItemData.value.createdBy = profileStore.activeProfile.id
addHistoryItemData.value[props.type] = props.elementId
const res = await useNuxtApp().$api(`/api/resource/${props.type}/${props.elementId}/history`, {
method: "POST",
body: addHistoryItemData.value
})
const {data,error} = await supabase
.from("historyitems")
.insert([{...addHistoryItemData.value, tenant: profileStore.currentTenant}])
.select()
if(error) {
console.log(error)
} else {
if(addHistoryItemData.value.text.includes("@")){
let usernames = [...addHistoryItemData.value.text.matchAll(/@(\S*)/gm)]
const {data:profiles} = await supabase.from("profiles").select("id,username")
let notifications = usernames.map(i => {
let rawUsername = i[1]
addHistoryItemData.value = {}
toast.add({title: "Eintrag erfolgreich erstellt"})
showAddHistoryItemModal.value = false
await setup()
return {
tenant: profileStore.currentTenant,
profile: profiles.find(x => x.username === rawUsername).id,
initiatingProfile: profileStore.activeProfile.id,
title: "Sie wurden im Logbuch erwähnt",
link: `/${props.type}s/show/${props.elementId}`,
message: addHistoryItemData.value.text
}
})
console.log(notifications)
const {error} = await supabase.from("notifications").insert(notifications)
}
addHistoryItemData.value = {}
toast.add({title: "Eintrag erfolgreich erstellt"})
showAddHistoryItemModal.value = false
await setup()
}
}
@@ -170,15 +141,15 @@ const renderText = (text) => {
/>
<div class="flex items-center gap-3">
<UAvatar
v-if="!item.createdBy"
v-if="!item.created_by"
:src="colorMode.value === 'light' ? '/Logo.png' : '/Logo_Dark.png' "
/>
<UAvatar
:alt="profileStore.getProfileById(item.createdBy).fullName"
:alt="item.created_by_profile?.full_name"
v-else
/>
<div>
<h3 v-if="item.createdBy">{{profileStore.getProfileById(item.createdBy) ? profileStore.getProfileById(item.createdBy).fullName : ""}}</h3>
<h3 v-if="item.created_by">{{item.created_by_profile?.full_name}}</h3>
<h3 v-else>FEDEO Bot</h3>
<span v-html="renderText(item.text)"/><br>
<span class="text-gray-500">{{dayjs(item.created_at).format("DD.MM.YY HH:mm")}}</span>

View File

@@ -1,19 +1,25 @@
<script setup>
import {useRole} from "~/composables/useRole.js";
const profileStore = useProfileStore()
const route = useRoute()
const auth = useAuthStore()
const {has} = usePermission()
const role = useRole()
const links = computed(() => {
return [
... profileStore.currentTenant === 21 ? [{
label: "Lieferschein Formular",
to: "https://loar.ma-agrarservice.de/erfassung",
icon: "i-heroicons-rectangle-stack",
target: "_blank",
}] : [],
... profileStore.currentTenant === 5 ? [{
...auth.profile.pinned_on_navigation.map(pin => {
if(pin.type === "external") {
return {
label: pin.label,
to: pin.link,
icon: pin.icon,
target: "_blank",
pinned: true
}
}
}),
... false ? [{
label: "Support Tickets",
to: "/support",
icon: "i-heroicons-rectangle-stack",
@@ -34,22 +40,22 @@ const links = computed(() => {
icon: "i-heroicons-rectangle-stack",
defaultOpen: false,
children: [
... role.checkRight("tasks") ? [{
... has("tasks") ? [{
label: "Aufgaben",
to: "/standardEntity/tasks",
icon: "i-heroicons-rectangle-stack"
}] : [],
... profileStore.ownTenant.features.planningBoard ? [{
... true ? [{
label: "Plantafel",
to: "/calendar/timeline",
icon: "i-heroicons-calendar-days"
}] : [],
... profileStore.ownTenant.features.calendar ? [{
... true ? [{
label: "Kalender",
to: "/calendar/grid",
icon: "i-heroicons-calendar-days"
}] : [],
... profileStore.ownTenant.features.calendar ? [{
... true ? [{
label: "Termine",
to: "/standardEntity/events",
icon: "i-heroicons-calendar-days"
@@ -97,22 +103,22 @@ const links = computed(() => {
}
]
},
... (role.checkRight("customers") || role.checkRight("vendors") || role.checkRight("contacts")) ? [{
... (has("customers") || has("vendors") || has("contacts")) ? [{
label: "Kontakte",
defaultOpen: false,
icon: "i-heroicons-user-group",
children: [
... role.checkRight("customers") ? [{
... has("customers") ? [{
label: "Kunden",
to: "/standardEntity/customers",
icon: "i-heroicons-user-group"
}] : [],
... role.checkRight("vendors") ? [{
... has("vendors") ? [{
label: "Lieferanten",
to: "/standardEntity/vendors",
icon: "i-heroicons-truck"
}] : [],
... role.checkRight("contacts") ? [{
... has("contacts") ? [{
label: "Ansprechpartner",
to: "/standardEntity/contacts",
icon: "i-heroicons-user-group"
@@ -124,17 +130,17 @@ const links = computed(() => {
defaultOpen:false,
icon: "i-heroicons-user-group",
children: [
... profileStore.ownTenant.features.timeTracking ? [{
... true ? [{
label: "Projektzeiten",
to: "/times",
icon: "i-heroicons-clock"
}] : [],
... profileStore.ownTenant.features.workingTimeTracking ? [{
... true ? [{
label: "Anwesenheiten",
to: "/workingtimes",
icon: "i-heroicons-clock"
}] : [],
... role.checkRight("absencerequests") ? [{
... has("absencerequests") ? [{
label: "Abwesenheiten",
to: "/standardEntity/absencerequests",
icon: "i-heroicons-document-text"
@@ -146,7 +152,7 @@ const links = computed(() => {
},
]
},
... profileStore.ownTenant.features.accounting ? [{
... true ? [{
label: "Buchhaltung",
defaultOpen: false,
icon: "i-heroicons-chart-bar-square",
@@ -183,7 +189,7 @@ const links = computed(() => {
},
]
},] : [],
... role.checkRight("inventory") ? [{
... has("inventory") ? [{
label: "Lager",
icon: "i-heroicons-puzzle-piece",
defaultOpen: false,
@@ -197,7 +203,7 @@ const links = computed(() => {
to: "/inventory/stocks",
icon: "i-heroicons-square-3-stack-3d"
},*/
... role.checkRight("spaces") ? [{
... has("spaces") ? [{
label: "Lagerplätze",
to: "/standardEntity/spaces",
icon: "i-heroicons-square-3-stack-3d"
@@ -209,22 +215,22 @@ const links = computed(() => {
defaultOpen: false,
icon: "i-heroicons-clipboard-document",
children: [
... role.checkRight("products") ? [{
... has("products") ? [{
label: "Artikel",
to: "/standardEntity/products",
icon: "i-heroicons-puzzle-piece"
}] : [],
... role.checkRight("productcategories") ? [{
... has("productcategories") ? [{
label: "Artikelkategorien",
to: "/standardEntity/productcategories",
icon: "i-heroicons-puzzle-piece"
}] : [],
... role.checkRight("services") ? [{
... has("services") ? [{
label: "Leistungen",
to: "/standardEntity/services",
icon: "i-heroicons-wrench-screwdriver"
}] : [],
... role.checkRight("servicecategories") ? [{
... has("servicecategories") ? [{
label: "Leistungskategorien",
to: "/standardEntity/servicecategories",
icon: "i-heroicons-wrench-screwdriver"
@@ -239,39 +245,39 @@ const links = computed(() => {
to: "/standardEntity/hourrates",
icon: "i-heroicons-user-group"
},
... role.checkRight("vehicles") ? [{
... has("vehicles") ? [{
label: "Fahrzeuge",
to: "/standardEntity/vehicles",
icon: "i-heroicons-truck"
}] : [],
... role.checkRight("inventoryitems") ? [{
... has("inventoryitems") ? [{
label: "Inventar",
to: "/standardEntity/inventoryitems",
icon: "i-heroicons-puzzle-piece"
}] : [],
... role.checkRight("inventoryitems") ? [{
... has("inventoryitems") ? [{
label: "Inventargruppen",
to: "/standardEntity/inventoryitemgroups",
icon: "i-heroicons-puzzle-piece"
}] : [],
]
},
... role.checkRight("checks") ? [{
... has("checks") ? [{
label: "Überprüfungen",
to: "/standardEntity/checks",
icon: "i-heroicons-magnifying-glass"
},] : [],
... role.checkRight("projects") ? [{
... has("projects") ? [{
label: "Projekte",
to: "/standardEntity/projects",
icon: "i-heroicons-clipboard-document-check"
},] : [],
... role.checkRight("contracts") ? [{
... has("contracts") ? [{
label: "Verträge",
to: "/standardEntity/contracts",
icon: "i-heroicons-clipboard-document"
}] : [],
... role.checkRight("plants") ? [{
... has("plants") ? [{
label: "Objekte",
to: "/standardEntity/plants",
icon: "i-heroicons-clipboard-document"
@@ -331,8 +337,49 @@ const links = computed(() => {
</script>
<template>
<UAccordion
:items="links"
:multiple="false"
>
<template #default="{ item, index, open }">
<UButton
:variant="item.pinned ? 'ghost' : 'ghost'"
:color="(item.to && route.path === item.to) || (item.children?.some(c => route.path.includes(c.to))) ? 'primary' : (item.pinned ? 'amber' : 'gray')"
:icon="item.icon"
class="w-full"
:to="item.to"
:target="item.target"
>
{{ item.label }}
<div
<template v-if="item.children" #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
<template #item="{ item }">
<div class="flex flex-col">
<UButton
v-for="child in item.children"
:key="child.label"
variant="ghost"
:color="child.to === route.path ? 'primary' : 'gray'"
:icon="child.icon"
class="ml-4"
:to="child.to"
:target="child.target"
>
{{ child.label }}
</UButton>
</div>
</template>
</UAccordion>
<!-- <div
v-for="item in links"
>
<UAccordion
@@ -382,5 +429,5 @@ const links = computed(() => {
>
{{item.label}}
</UButton>
</div>
</div>-->
</template>

View File

@@ -1,29 +0,0 @@
<script setup>
const dataStore = useDataStore()
const profileStore = useProfileStore()
const supabase = useSupabaseClient()
const selectedProfile = ref(profileStore.activeProfile.id)
</script>
<template>
<USelectMenu
:options="profileStore.ownProfiles"
value-attribute="id"
class="w-40"
@change="profileStore.changeProfile(selectedProfile)"
v-model="selectedProfile"
>
<UButton color="gray" variant="ghost" :class="[open && 'bg-gray-50 dark:bg-gray-800']" class="w-full">
<UAvatar :alt="profileStore.tenants.find(i => profileStore.profiles.find(i => i.id === selectedProfile).tenant === i.id).name" size="md" />
<span class="truncate text-gray-900 dark:text-white font-semibold">{{profileStore.tenants.find(i => profileStore.profiles.find(i => i.id === selectedProfile).tenant === i.id).name}}</span>
</UButton>
<template #option="{option}">
{{profileStore.tenants.find(i => i.id === option.tenant).name}}
</template>
</USelectMenu>
</template>

View File

@@ -0,0 +1,27 @@
<script setup>
const auth = useAuthStore()
const selectedTenant = ref(auth.user.tenant_id)
</script>
<template>
<USelectMenu
:options="auth.tenants"
value-attribute="id"
class="w-40"
@change="auth.switchTenant(selectedTenant)"
v-model="selectedTenant"
>
<UButton color="gray" variant="ghost" :class="[open && 'bg-gray-50 dark:bg-gray-800']" class="w-full">
<UAvatar :alt="auth.tenants.find(i => auth.activeTenant === i.id).name" size="md" />
<span class="truncate text-gray-900 dark:text-white font-semibold">{{auth.tenants.find(i => auth.activeTenant === i.id).name}}</span>
</UButton>
<template #option="{option}">
{{option.name}}
</template>
</USelectMenu>
</template>

View File

@@ -1,4 +1,5 @@
<script setup>
const { isHelpSlideoverOpen } = useDashboard()
const { isDashboardSearchModalOpen } = useUIState()
const { metaSymbol } = useShortcuts()
@@ -7,23 +8,26 @@ const dataStore = useDataStore()
const profileStore = useProfileStore()
const supabase = useSupabaseClient()
const router = useRouter()
const auth = useAuthStore()
const items = computed(() => [
[{
slot: 'account',
label: '',
disabled: true
}], [{
}], [/*{
label: 'Mein Profil',
icon: 'i-heroicons-user',
to: `/profiles/show/${profileStore.activeProfile.id}`
},*/{
label: 'Passwort ändern',
icon: 'i-heroicons-shield-check',
to: `/password-change`
},{
label: 'Abmelden',
icon: 'i-heroicons-arrow-left-on-rectangle',
click: async () => {
await supabase.auth.signOut()
//await dataStore.clearStore()
await router.push('/login')
await auth.logout()
}
}]
@@ -33,10 +37,10 @@ const items = computed(() => [
<template>
<UDropdown mode="hover" :items="items" :ui="{ width: 'w-full', item: { disabled: 'cursor-text select-text' } }" :popper="{ strategy: 'absolute', placement: 'top' }" class="w-full">
<template #default="{ open }">
<UButton color="gray" variant="ghost" class="w-full" :label="profileStore.activeProfile.fullName" :class="[open && 'bg-gray-50 dark:bg-gray-800']">
<template #leading>
<UAvatar :alt="profileStore.activeProfile ? profileStore.activeProfile.fullName : ''" size="xs" />
</template>
<UButton color="gray" variant="ghost" class="w-full" :label="auth.user.email" :class="[open && 'bg-gray-50 dark:bg-gray-800']">
<!-- <template #leading>
<UAvatar :alt="auth.user.email" size="xs" />
</template>-->
<template #trailing>
<UIcon name="i-heroicons-ellipsis-vertical" class="w-5 h-5 ml-auto" />
@@ -50,7 +54,7 @@ const items = computed(() => [
Angemeldet als
</p>
<p class="truncate font-medium text-gray-900 dark:text-white">
{{profileStore.activeProfile.email}}
{{auth.user.email}}
</p>
</div>
</template>