Many Changes
This commit is contained in:
222
spaces/app.vue
222
spaces/app.vue
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import GlobalSearch from "~/components/GlobalSearch.vue";
|
||||||
|
|
||||||
const user = useSupabaseUser()
|
const user = useSupabaseUser()
|
||||||
//console.log(user.value)
|
//console.log(user.value)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -12,11 +14,6 @@ const tenants = (await supabase.from("tenants").select()).data
|
|||||||
|
|
||||||
const dataStore = useDataStore()
|
const dataStore = useDataStore()
|
||||||
|
|
||||||
|
|
||||||
const userProfile = (user.value ? dataStore.getProfileById(user.value.id) : {})
|
|
||||||
//console.log(userProfile)
|
|
||||||
|
|
||||||
|
|
||||||
const isLight = computed({
|
const isLight = computed({
|
||||||
get () {
|
get () {
|
||||||
return colorMode.value !== 'dark'
|
return colorMode.value !== 'dark'
|
||||||
@@ -26,6 +23,32 @@ const isLight = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const userProfile = (user.value ? dataStore.getProfileById(user.value.id) : {})
|
||||||
|
//console.log(userProfile)
|
||||||
|
|
||||||
|
const showUserMenu = ref(false)
|
||||||
|
const userMenuItems = ref([
|
||||||
|
{
|
||||||
|
label: "Profil",
|
||||||
|
badge: dataStore.notifications.filter(item => item.read).length,
|
||||||
|
icon: "i-heroicons-user-20-solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Nummernkreise",
|
||||||
|
icon: "i-heroicons-cog-8-tooth",
|
||||||
|
to: "/settings/numberRanges"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Benutzer',
|
||||||
|
icon: 'i-heroicons-user-group',
|
||||||
|
to: "/settings/users"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const viewport = useViewport()
|
const viewport = useViewport()
|
||||||
|
|
||||||
watch(viewport.breakpoint, (newBreakpoint, oldBreakpoint) => {
|
watch(viewport.breakpoint, (newBreakpoint, oldBreakpoint) => {
|
||||||
@@ -56,7 +79,7 @@ const navLinks = [
|
|||||||
icon: "i-heroicons-truck"
|
icon: "i-heroicons-truck"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Kontakte",
|
label: "Ansprechpartner",
|
||||||
to: "/contacts",
|
to: "/contacts",
|
||||||
icon: "i-heroicons-user-group"
|
icon: "i-heroicons-user-group"
|
||||||
},
|
},
|
||||||
@@ -131,9 +154,14 @@ const navLinks = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: "Zeiterfassung",
|
label: "Zeiterfassung",
|
||||||
to: "/timetracking",
|
to: "/employees/timetracking",
|
||||||
icon: "i-heroicons-clock"
|
icon: "i-heroicons-clock"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Abwesenheiten",
|
||||||
|
to: "/employees/absenceRequests",
|
||||||
|
icon: "i-heroicons-document-text"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -141,15 +169,15 @@ const navLinks = [
|
|||||||
icon: 'i-heroicons-home',
|
icon: 'i-heroicons-home',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: "Artikel",
|
label: "Steuerung",
|
||||||
to: "/products",
|
|
||||||
icon: "i-heroicons-puzzle-piece"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Inventar",
|
|
||||||
to: "/inventory",
|
to: "/inventory",
|
||||||
icon: "i-heroicons-square-3-stack-3d"
|
icon: "i-heroicons-square-3-stack-3d"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Artikelstamm",
|
||||||
|
to: "/products",
|
||||||
|
icon: "i-heroicons-puzzle-piece"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Lagerplätze",
|
label: "Lagerplätze",
|
||||||
to: "/inventory/spaces",
|
to: "/inventory/spaces",
|
||||||
@@ -166,37 +194,7 @@ const navLinks = [
|
|||||||
|
|
||||||
const linksForBreadcrumbs = ref([])
|
const linksForBreadcrumbs = ref([])
|
||||||
|
|
||||||
const generateLinks = () => {
|
|
||||||
let pathSteps = route.fullPath.split("/")
|
|
||||||
let returnArr = []
|
|
||||||
|
|
||||||
pathSteps.forEach((step,index) => {
|
|
||||||
|
|
||||||
let stepLink = navLinks.find(link => link.link == step)
|
|
||||||
|
|
||||||
if(stepLink) {
|
|
||||||
returnArr[index] = {
|
|
||||||
label: stepLink.label,
|
|
||||||
icon: stepLink.icon,
|
|
||||||
to: '/' + stepLink.link
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
linksForBreadcrumbs.value = returnArr
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route.path,
|
|
||||||
() => {
|
|
||||||
generateLinks()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
generateLinks()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -223,6 +221,9 @@ useSeoMeta({
|
|||||||
twitterCard: 'summary_large_image'
|
twitterCard: 'summary_large_image'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
[{
|
[{
|
||||||
label: user.value ? user.value.email : "",
|
label: user.value ? user.value.email : "",
|
||||||
@@ -250,7 +251,7 @@ const items = [
|
|||||||
label: 'Status',
|
label: 'Status',
|
||||||
icon: 'i-heroicons-signal'
|
icon: 'i-heroicons-signal'
|
||||||
}],*/ [{
|
}],*/ [{
|
||||||
label: 'Sign out',
|
label: 'Ausloggen',
|
||||||
icon: 'i-heroicons-arrow-left-on-rectangle',
|
icon: 'i-heroicons-arrow-left-on-rectangle',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
await supabase.auth.signOut()
|
await supabase.auth.signOut()
|
||||||
@@ -279,8 +280,12 @@ const items = [
|
|||||||
<UNavigationTree :links="navLinks"/>
|
<UNavigationTree :links="navLinks"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #right v-if="user">
|
<template #right>
|
||||||
<ClientOnly>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <ClientOnly>
|
||||||
<UButton
|
<UButton
|
||||||
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
||||||
color="gray"
|
color="gray"
|
||||||
@@ -292,11 +297,128 @@ const items = [
|
|||||||
<template #fallback>
|
<template #fallback>
|
||||||
<div class="w-8 h-8" />
|
<div class="w-8 h-8" />
|
||||||
</template>
|
</template>
|
||||||
</ClientOnly>
|
</ClientOnly>-->
|
||||||
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
<GlobalSearch/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <UPopover :popper="{placement: 'bottom-start'}">
|
||||||
|
<ClientOnly>
|
||||||
|
<UChip
|
||||||
|
:show="dataStore.notifications.filter(notification => !notification.read).length > 0"
|
||||||
|
inset
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-envelope-20-solid"
|
||||||
|
variant="ghost"
|
||||||
|
color="gray"
|
||||||
|
class="mx-2"
|
||||||
|
/>
|
||||||
|
</UChip>
|
||||||
|
|
||||||
|
<template #fallback>
|
||||||
|
<div class="w-8 h-8" />
|
||||||
|
</template>
|
||||||
|
</ClientOnly>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #panel>
|
||||||
|
<div class="w-60 p-3">
|
||||||
|
|
||||||
|
<UAlert
|
||||||
|
class="mb-3"
|
||||||
|
v-for="(notification,index) in dataStore.notifications"
|
||||||
|
:color="!notification.read ? 'primary' : 'grey'"
|
||||||
|
variant="outline"
|
||||||
|
:description="notification.text"
|
||||||
|
:title="notification.title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UPopover>-->
|
||||||
|
<UButton
|
||||||
|
@click="showUserMenu = true"
|
||||||
|
variant="ghost"
|
||||||
|
color="gray"
|
||||||
|
>
|
||||||
<UAvatar
|
<UAvatar
|
||||||
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
||||||
icon="i-heroicons-photo"
|
icon="i-heroicons-user-20-solid"
|
||||||
|
:chip-color="dataStore.notifications.filter(item => !item.read).length > 0 ? 'primary' : null"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</UButton>
|
||||||
|
|
||||||
|
<USlideover
|
||||||
|
v-model="showUserMenu"
|
||||||
|
>
|
||||||
|
<UCard
|
||||||
|
class="h-full"
|
||||||
|
>
|
||||||
|
|
||||||
|
<UDivider
|
||||||
|
class="my-3"
|
||||||
|
label="Menü"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UVerticalNavigation
|
||||||
|
:links="userMenuItems"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UDivider
|
||||||
|
class="my-3"
|
||||||
|
label="Benachrichtigungen"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UAlert
|
||||||
|
class="mb-3"
|
||||||
|
v-for="(notification) in dataStore.notifications"
|
||||||
|
:color="!notification.read ? 'primary' : 'white'"
|
||||||
|
:variant="!notification.read ? 'outline' : 'soft'"
|
||||||
|
:description="notification.text"
|
||||||
|
:title="notification.title"
|
||||||
|
:close-button="!notification.read ? { icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'link', padded: false } : null"
|
||||||
|
@close="async () => {
|
||||||
|
const {error} = await supabase.from('notifications').update({read:true}).eq('id', notification.id)
|
||||||
|
if(error) console.log(error)
|
||||||
|
dataStore.fetchNotifications()
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<InputGroup>
|
||||||
|
<UButton
|
||||||
|
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
||||||
|
color="white"
|
||||||
|
variant="outline"
|
||||||
|
aria-label="Theme"
|
||||||
|
@click="isLight = !isLight"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
color="rose"
|
||||||
|
variant="outline"
|
||||||
|
@click="async () => {
|
||||||
|
showUserMenu = false
|
||||||
|
await supabase.auth.signOut()
|
||||||
|
await dataStore.clearStore()
|
||||||
|
await router.push('/login')
|
||||||
|
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
Ausloggen
|
||||||
|
</UButton>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</USlideover>
|
||||||
|
<!-- <UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
||||||
|
<UAvatar
|
||||||
|
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
||||||
|
icon="i-heroicons-user-20-solid"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #account="{ item }">
|
<template #account="{ item }">
|
||||||
@@ -315,7 +437,7 @@ const items = [
|
|||||||
|
|
||||||
<UIcon :name="item.icon" class="flex-shrink-0 h-4 w-4 text-gray-400 dark:text-gray-500 ms-auto" />
|
<UIcon :name="item.icon" class="flex-shrink-0 h-4 w-4 text-gray-400 dark:text-gray-500 ms-auto" />
|
||||||
</template>
|
</template>
|
||||||
</UDropdown>
|
</UDropdown>-->
|
||||||
</template>
|
</template>
|
||||||
</UHeader>
|
</UHeader>
|
||||||
<UDivider />
|
<UDivider />
|
||||||
|
|||||||
110
spaces/components/GlobalSearch.vue
Normal file
110
spaces/components/GlobalSearch.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
const showCommandPalette = ref(false)
|
||||||
|
const selectedCommand = ref("")
|
||||||
|
const commandPaletteRef = ref()
|
||||||
|
const router = useRouter()
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const openCommandPalette = () => {
|
||||||
|
showCommandPalette.value = true
|
||||||
|
console.log("Open Command Palette")
|
||||||
|
}
|
||||||
|
|
||||||
|
defineShortcuts({
|
||||||
|
meta_k: {
|
||||||
|
usingInput: true,
|
||||||
|
handler: () => {
|
||||||
|
openCommandPalette()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
id: 'new-customer',
|
||||||
|
label: 'Kunde hinzufügen',
|
||||||
|
icon: 'i-heroicons-document-plus',
|
||||||
|
to: "/customers/create" ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'new-vendor',
|
||||||
|
label: 'Lieferant hinzufügen',
|
||||||
|
icon: 'i-heroicons-truck',
|
||||||
|
to: "/vendors/create" ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'new-contact',
|
||||||
|
label: 'Ansprechpartner hinzufügen',
|
||||||
|
icon: 'i-heroicons-user',
|
||||||
|
to: "/contacts/create" ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'new-product',
|
||||||
|
label: 'Artikel hinzufügen',
|
||||||
|
icon: 'i-heroicons-puzzle-piece',
|
||||||
|
to: "/products/create" ,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const groups = computed(() =>
|
||||||
|
[{
|
||||||
|
key: 'actions',
|
||||||
|
commands: actions
|
||||||
|
},{
|
||||||
|
key: "customers",
|
||||||
|
label: "Kunden",
|
||||||
|
commands: dataStore.customers.map(item => { return {id: item.id, label: item.name, to: `/customers/show/${item.id}`}})
|
||||||
|
},{
|
||||||
|
key: "vendors",
|
||||||
|
label: "Lieferanten",
|
||||||
|
commands: dataStore.vendors.map(item => { return {id: item.id, label: item.name, to: `/vendors/show/${item.id}`}})
|
||||||
|
},{
|
||||||
|
key: "contacts",
|
||||||
|
label: "Ansprechpartner",
|
||||||
|
commands: dataStore.contacts.map(item => { return {id: item.id, label: item.fullName, to: `/contacts/show/${item.id}`}})
|
||||||
|
},{
|
||||||
|
key: "products",
|
||||||
|
label: "Artikel",
|
||||||
|
commands: dataStore.products.map(item => { return {id: item.id, label: item.name, to: `/products/show/${item.id}`}})
|
||||||
|
}
|
||||||
|
].filter(Boolean))
|
||||||
|
|
||||||
|
function onSelect (option) {
|
||||||
|
if (option.click) {
|
||||||
|
option.click()
|
||||||
|
} else if (option.to) {
|
||||||
|
router.push(option.to)
|
||||||
|
} else if (option.href) {
|
||||||
|
window.open(option.href, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
showCommandPalette.value = false
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UButton
|
||||||
|
icon="i-heroicons-magnifying-glass"
|
||||||
|
variant="ghost"
|
||||||
|
@click="openCommandPalette"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UModal
|
||||||
|
v-model="showCommandPalette"
|
||||||
|
>
|
||||||
|
<UCommandPalette
|
||||||
|
v-model="selectedCommand"
|
||||||
|
:groups="groups"
|
||||||
|
:autoselect="false"
|
||||||
|
@update:model-value="onSelect"
|
||||||
|
ref="commandPaletteRef"
|
||||||
|
/>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -10,36 +10,22 @@ const props = defineProps({
|
|||||||
type: String
|
type: String
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const dataStore = useDataStore()
|
||||||
const user = useSupabaseUser()
|
const user = useSupabaseUser()
|
||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const {type, elementId} = props
|
const {type, elementId} = props
|
||||||
const showAddHistoryItemModal = ref(false)
|
const showAddHistoryItemModal = ref(false)
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
/*const historyItems = ref([
|
|
||||||
{
|
|
||||||
user: "86e67794-0ea8-41b0-985a-1072e84f56e9",
|
|
||||||
text: "<a class='text-primary-500'>@marielesindern</a> magst du die einmal anschauen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: "3b795486-6b71-4ed8-a1f6-dbc52360d826",
|
|
||||||
text: "<a class='text-primary-500'>@florianfederspiel</a> Jo alles bestens",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: "Spaces Bot",
|
|
||||||
text: "Erstellt",
|
|
||||||
}
|
|
||||||
])*/
|
|
||||||
|
|
||||||
const {profiles} = storeToRefs(useDataStore())
|
|
||||||
const {getHistoryItemsByCustomer, fetchHistoryItems} = useDataStore()
|
|
||||||
|
|
||||||
const historyItems = computed(() => {
|
const historyItems = computed(() => {
|
||||||
|
|
||||||
let items = []
|
let items = []
|
||||||
|
|
||||||
if(type === "customer") {
|
if(type === "customer") {
|
||||||
items = getHistoryItemsByCustomer(elementId)
|
items = dataStore.getHistoryItemsByCustomer(elementId)
|
||||||
|
} else if(type === "vendor") {
|
||||||
|
items = dataStore.getHistoryItemsByVendor(elementId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return items.reverse()
|
return items.reverse()
|
||||||
@@ -68,7 +54,7 @@ const addHistoryItem = async () => {
|
|||||||
} else {
|
} else {
|
||||||
toast.add({title: "Eintrag erfolgreich erstellt"})
|
toast.add({title: "Eintrag erfolgreich erstellt"})
|
||||||
showAddHistoryItemModal.value = false
|
showAddHistoryItemModal.value = false
|
||||||
await fetchHistoryItems()
|
await dataStore.fetchHistoryItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -130,11 +116,11 @@ const renderText = (text) => {
|
|||||||
:src="colorMode.value === 'light' ? '/spaces_hell.svg' : '/spaces.svg' "
|
:src="colorMode.value === 'light' ? '/spaces_hell.svg' : '/spaces.svg' "
|
||||||
/>
|
/>
|
||||||
<UAvatar
|
<UAvatar
|
||||||
:alt="profiles.find(profile => profile.id === item.user).fullName"
|
:alt="dataStore.profiles.find(profile => profile.id === item.user).fullName"
|
||||||
v-else
|
v-else
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h3 v-if="item.user">{{profiles.find(profile => profile.id === item.user) ? profiles.find(profile => profile.id === item.user).fullName : ""}}</h3>
|
<h3 v-if="item.user">{{dataStore.profiles.find(profile => profile.id === item.user) ? dataStore.profiles.find(profile => profile.id === item.user).fullName : ""}}</h3>
|
||||||
<h3 v-else>Spaces Bot</h3>
|
<h3 v-else>Spaces Bot</h3>
|
||||||
<span v-html="renderText(item.text)"/><br>
|
<span v-html="renderText(item.text)"/><br>
|
||||||
<span class="text-gray-500">{{dayjs(item.created_at).format("DD:MM:YY HH:mm")}}</span>
|
<span class="text-gray-500">{{dayjs(item.created_at).format("DD:MM:YY HH:mm")}}</span>
|
||||||
|
|||||||
21
spaces/components/Placeholder.vue
Normal file
21
spaces/components/Placeholder.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative overflow-hidden rounded border border-dashed border-gray-400 dark:border-gray-500 opacity-75 px-4 flex items-center justify-center">
|
||||||
|
<svg class="absolute inset-0 h-full w-full stroke-gray-900/10 dark:stroke-white/10" fill="none">
|
||||||
|
<defs>
|
||||||
|
<pattern
|
||||||
|
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="10"
|
||||||
|
height="10"
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<path d="M-3 13 15-5M-5 5l18-18M-1 21 17 3" />
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect stroke="none" fill="url(#pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e)" width="100%" height="100%" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"@fullcalendar/resource-timeline": "^6.1.10",
|
"@fullcalendar/resource-timeline": "^6.1.10",
|
||||||
"@fullcalendar/vue3": "^6.1.10",
|
"@fullcalendar/vue3": "^6.1.10",
|
||||||
"@nuxt/content": "^2.9.0",
|
"@nuxt/content": "^2.9.0",
|
||||||
"@nuxt/ui-pro": "^0.5.0",
|
"@nuxt/ui-pro": "^0.6.1",
|
||||||
"@nuxtjs/fontaine": "^0.4.1",
|
"@nuxtjs/fontaine": "^0.4.1",
|
||||||
"@nuxtjs/google-fonts": "^3.1.0",
|
"@nuxtjs/google-fonts": "^3.1.0",
|
||||||
"@nuxtjs/strapi": "^1.9.3",
|
"@nuxtjs/strapi": "^1.9.3",
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
"@zip.js/zip.js": "^2.7.32",
|
"@zip.js/zip.js": "^2.7.32",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
|
"client-oauth2": "^4.3.3",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"jsprintmanager": "^6.0.3",
|
"jsprintmanager": "^6.0.3",
|
||||||
"nuxt-editorjs": "^1.0.4",
|
"nuxt-editorjs": "^1.0.4",
|
||||||
|
|||||||
@@ -70,17 +70,17 @@ const cancelEditorCreate = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCustomer = async () => {
|
const updateItem = async () => {
|
||||||
const {error} = await supabase
|
const {error} = await supabase
|
||||||
.from("contacts")
|
.from("contacts")
|
||||||
.update(itemInfo.value)
|
.update(itemInfo.value)
|
||||||
.eq('id',itemInfo.value.id)
|
.eq('id',itemInfo.value.id)
|
||||||
console.log(error)
|
|
||||||
mode.value = "show"
|
if(error) {
|
||||||
itemInfo.value = {
|
console.log(error)
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.push(`/contacts/show/${currentContact.id}`)
|
||||||
toast.add({title: "Kontakt erfolgreich gespeichert"})
|
toast.add({title: "Kontakt erfolgreich gespeichert"})
|
||||||
dataStore.fetchContacts()
|
dataStore.fetchContacts()
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,7 @@ setupPage()
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<UButton
|
<UButton
|
||||||
v-if="mode == 'edit'"
|
v-if="mode == 'edit'"
|
||||||
@click="updateCustomer"
|
@click="updateItem"
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</UButton>
|
</UButton>
|
||||||
|
|||||||
@@ -71,17 +71,16 @@ const cancelEditorCreate = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCustomer = async () => {
|
const updateItem = async () => {
|
||||||
const {error} = await supabase
|
const {error} = await supabase
|
||||||
.from("contracts")
|
.from("contracts")
|
||||||
.update(itemInfo.value)
|
.update(itemInfo.value)
|
||||||
.eq('id',itemInfo.value.id)
|
.eq('id',itemInfo.value.id)
|
||||||
console.log(error)
|
if(error) {
|
||||||
mode.value = "show"
|
console.log(error)
|
||||||
itemInfo.value = {
|
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.push(`/contracts/show/${currentContract.id}`)
|
||||||
toast.add({title: "Vertrag erfolgreich gespeichert"})
|
toast.add({title: "Vertrag erfolgreich gespeichert"})
|
||||||
dataStore.fetchContracts()
|
dataStore.fetchContracts()
|
||||||
}
|
}
|
||||||
@@ -192,7 +191,7 @@ setupPage()
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<UButton
|
<UButton
|
||||||
v-if="mode == 'edit'"
|
v-if="mode == 'edit'"
|
||||||
@click="updateCustomer"
|
@click="updateItem"
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</UButton>
|
</UButton>
|
||||||
|
|||||||
@@ -81,14 +81,12 @@ const updateCustomer = async () => {
|
|||||||
.from("customers")
|
.from("customers")
|
||||||
.update(customerInfo.value)
|
.update(customerInfo.value)
|
||||||
.eq('id',customerInfo.value.id)
|
.eq('id',customerInfo.value.id)
|
||||||
console.log(error)
|
|
||||||
mode.value = "show"
|
if(error) {
|
||||||
customerInfo.value = {
|
console.log(error)
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
infoData: {}
|
|
||||||
}
|
}
|
||||||
toast.add({title: "Kunde erfolgreich gespeichert"})
|
toast.add({title: "Kunde erfolgreich gespeichert"})
|
||||||
|
router.push(`/customers/show/${currentCustomer.id}`)
|
||||||
dataStore.fetchCustomers()
|
dataStore.fetchCustomers()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +119,14 @@ setupPage()
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
Informationen:<br>
|
Informationen:<br>
|
||||||
{{currentCustomer.infoData}}<br>
|
<div v-if="currentCustomer.infoData">
|
||||||
|
<span v-if="currentCustomer.infoData.street">Straße + Hausnummer: {{currentCustomer.infoData.street}}<br></span>
|
||||||
|
<span v-if="currentCustomer.infoData.zip && currentCustomer.infoData.city">PLZ + Ort: {{currentCustomer.infoData.zip}} {{currentCustomer.infoData.city}}<br></span>
|
||||||
|
<span v-if="currentCustomer.infoData.tel">Telefon: {{currentCustomer.infoData.tel}}<br></span>
|
||||||
|
<span v-if="currentCustomer.infoData.email">E-Mail: {{currentCustomer.infoData.email}}<br></span>
|
||||||
|
<span v-if="currentCustomer.infoData.web">Web: {{currentCustomer.infoData.web}}<br></span>
|
||||||
|
<span v-if="currentCustomer.infoData.ustid">USt-Id: {{currentCustomer.infoData.ustid}}<br></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<UDivider
|
<UDivider
|
||||||
class="my-2"
|
class="my-2"
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { BlobReader, BlobWriter, ZipWriter } from "@zip.js/zip.js";
|
|
||||||
|
|
||||||
const downloadFolder = async (folder) => {
|
|
||||||
const supabaseClient = useSupabaseClient();
|
|
||||||
|
|
||||||
const bucket = "documents";
|
|
||||||
|
|
||||||
// Get a list of all the files in the path /my-bucket/images
|
|
||||||
/*const { data: files, error } = await supabaseClient.storage
|
|
||||||
.from(bucket)
|
|
||||||
.list(folder);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
let files = [
|
|
||||||
"1/Eingang/Rechnung_VRB170A0249604_2023-12-06.pdf"
|
|
||||||
]
|
|
||||||
|
|
||||||
console.log(files)
|
|
||||||
|
|
||||||
// If there are no files in the folder, throw an error
|
|
||||||
if (!files || !files.length) {
|
|
||||||
throw new Error("No files to download");
|
|
||||||
}
|
|
||||||
|
|
||||||
const promises = [];
|
|
||||||
|
|
||||||
// Download each file in the folder
|
|
||||||
files.forEach((file) => {
|
|
||||||
promises.push(
|
|
||||||
supabaseClient.storage.from(bucket).download(`${file}`)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for all the files to download
|
|
||||||
const response = await Promise.allSettled(promises);
|
|
||||||
|
|
||||||
// Map the response to an array of objects containing the file name and blob
|
|
||||||
const downloadedFiles = response.map((result, index) => {
|
|
||||||
if (result.status === "fulfilled") {
|
|
||||||
return {
|
|
||||||
name: files[index],
|
|
||||||
blob: result.value.data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new zip file
|
|
||||||
const zipFileWriter = new BlobWriter("application/zip");
|
|
||||||
const zipWriter = new ZipWriter(zipFileWriter, { bufferedWrite: true });
|
|
||||||
|
|
||||||
// Add each file to the zip file
|
|
||||||
downloadedFiles.forEach((downloadedFile) => {
|
|
||||||
if (downloadedFile) {
|
|
||||||
zipWriter.add(downloadedFile.name, new BlobReader(downloadedFile.blob));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Download the zip file
|
|
||||||
const url = URL.createObjectURL(await zipWriter.close());
|
|
||||||
const link = document.createElement("a");
|
|
||||||
|
|
||||||
link.href = url;
|
|
||||||
link.setAttribute("download", "documents.zip");
|
|
||||||
|
|
||||||
document.body.appendChild(link);
|
|
||||||
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<UButton
|
|
||||||
@click="downloadFolder('1/Eingang')"
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</UButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
256
spaces/pages/employees/absenceRequests/[mode]/[[id]].vue
Normal file
256
spaces/pages/employees/absenceRequests/[mode]/[[id]].vue
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
<script setup>
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
|
|
||||||
|
let currentItem = null
|
||||||
|
|
||||||
|
//Working
|
||||||
|
const mode = ref(route.params.mode || "show")
|
||||||
|
const itemInfo = ref({
|
||||||
|
approved: null
|
||||||
|
})
|
||||||
|
const states = ["Offen","Genehmigt", "Abgelehnt"]
|
||||||
|
|
||||||
|
const absenceReasons = [
|
||||||
|
"Elternzeit",
|
||||||
|
"Kind krank - Kinderbetreuung",
|
||||||
|
"Krankheit",
|
||||||
|
"Krankheit 1 Tag (mit Attest)",
|
||||||
|
"Krankheit ab 2. Tag (mit Attest)",
|
||||||
|
"Mutterschutz",
|
||||||
|
"Sonderurlaub (bezahlt)",
|
||||||
|
"Überstundenausgleich",
|
||||||
|
"Unbezahlter Urlaub",
|
||||||
|
"Urlaub"
|
||||||
|
]
|
||||||
|
|
||||||
|
//Functions
|
||||||
|
const setupPage = () => {
|
||||||
|
if(mode.value === "show" || mode.value === "edit"){
|
||||||
|
currentItem = dataStore.getAbsenceRequestById(Number(useRoute().params.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode.value === "edit") itemInfo.value = currentItem
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const createItem = async () => {
|
||||||
|
const {data,error} = await supabase
|
||||||
|
.from("absenceRequests")
|
||||||
|
.insert([itemInfo.value])
|
||||||
|
.select()
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
} else {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
}
|
||||||
|
toast.add({title: "Abwesenheit erfolgreich erstellt"})
|
||||||
|
await dataStore.fetchAbsenceRequests()
|
||||||
|
router.push(`/employees/absenceRequests/show/${data[0].id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editItem = async () => {
|
||||||
|
router.push(`/employees/absenceRequests/edit/${currentItem.id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelEditorCreate = () => {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
infoData: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateItem = async () => {
|
||||||
|
const {error} = await supabase
|
||||||
|
.from("absenceRequests")
|
||||||
|
.update(itemInfo.value)
|
||||||
|
.eq('id',itemInfo.value.id)
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
router.push(`/employees/absenceRequests/show/${currentItem.id}`)
|
||||||
|
toast.add({title: "Abwesenheit erfolgreich gespeichert"})
|
||||||
|
dataStore.fetchAbsenceRequests()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupPage()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<UCard v-if="currentItem && mode == 'show'" >
|
||||||
|
<template #header>
|
||||||
|
<UBadge
|
||||||
|
v-if="currentItem.approved === 'Offen'"
|
||||||
|
color="blue"
|
||||||
|
>{{currentItem.approved}}</UBadge>
|
||||||
|
<UBadge
|
||||||
|
v-else-if="currentItem.approved === 'Genehmigt'"
|
||||||
|
color="primary"
|
||||||
|
>{{currentItem.approved}}</UBadge>
|
||||||
|
<UBadge
|
||||||
|
v-else-if="currentItem.approved === 'Abgelehnt'"
|
||||||
|
color="rose"
|
||||||
|
>{{currentItem.approved}}</UBadge>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{currentItem.reason}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<span>Mitarbeiter: {{dataStore.profiles.find(item => item.id === currentItem.user) ? dataStore.profiles.find(item => item.id === currentItem.user).fullName : ""}}<br></span>
|
||||||
|
<span>Start: {{dayjs(currentItem.start).format("DD.MM.YYYY")}}<br></span>
|
||||||
|
<span>Ende: {{dayjs(currentItem.end).format("DD.MM.YYYY")}}<br></span>
|
||||||
|
|
||||||
|
Notizen:<br>
|
||||||
|
{{currentItem.note}}<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'show' && currentItem.id"
|
||||||
|
@click="editItem"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Archivieren
|
||||||
|
</UButton>
|
||||||
|
<!-- TODO: Kunde archivieren -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
||||||
|
<template #header v-if="mode === 'edit'">
|
||||||
|
{{itemInfo.reason}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Status:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.approved"
|
||||||
|
:options="states"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="value"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Grund:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.reason"
|
||||||
|
:options="absenceReasons"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Mitarbeiter:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.user"
|
||||||
|
:options="dataStore.profiles"
|
||||||
|
option-attribute="fullName"
|
||||||
|
value-attribute="id"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['fullName']"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{dataStore.profiles.find(item => item.id === itemInfo.user) ? dataStore.profiles.find(item => item.id === itemInfo.user).fullName : "Mitarbeiter auswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Start:">
|
||||||
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||||
|
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.start ? dayjs(itemInfo.start).format('DD.MM.YYYY') : 'Datum auswählen'" />
|
||||||
|
|
||||||
|
<template #panel="{ close }">
|
||||||
|
<LazyDatePicker v-model="itemInfo.start" @close="close" />
|
||||||
|
</template>
|
||||||
|
</UPopover>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Ende:">
|
||||||
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||||
|
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.end ? dayjs(itemInfo.end).format('DD.MM.YYYY') : 'Datum auswählen'" />
|
||||||
|
|
||||||
|
<template #panel="{ close }">
|
||||||
|
<LazyDatePicker v-model="itemInfo.end" @close="close" />
|
||||||
|
</template>
|
||||||
|
</UPopover>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Notizen:"
|
||||||
|
>
|
||||||
|
<UTextarea
|
||||||
|
v-model="itemInfo.note"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'edit'"
|
||||||
|
@click="updateItem"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-else-if="mode == 'create'"
|
||||||
|
@click="createItem"
|
||||||
|
>
|
||||||
|
Erstellen
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="cancelEditorCreate"
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
84
spaces/pages/employees/absenceRequests/index.vue
Normal file
84
spaces/pages/employees/absenceRequests/index.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div id="main">
|
||||||
|
|
||||||
|
<UButton @click="router.push(`/employees/absenceRequests/create/`)">+ Abwesenheit</UButton>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
:rows="dataStore.absenceRequests"
|
||||||
|
:columns="columns"
|
||||||
|
@select="selectItem"
|
||||||
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
|
>
|
||||||
|
<template #approved-data="{row}">
|
||||||
|
<span v-if="!row.approved">
|
||||||
|
Genehmigung offen
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else-if="row.approved"
|
||||||
|
class="text-primary"
|
||||||
|
>
|
||||||
|
Genemigt
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-rose"
|
||||||
|
>
|
||||||
|
Abgelehnt
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #user-data="{row}">
|
||||||
|
{{dataStore.profiles.find(profile => profile.id === row.user) ? dataStore.profiles.find(profile => profile.id === row.user).fullName : ""}}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const router = useRouter()
|
||||||
|
const mode = ref("show")
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: "approved",
|
||||||
|
label: "Genehmigt",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "user",
|
||||||
|
label: "Mitarbeiter",
|
||||||
|
sortable: true
|
||||||
|
},{
|
||||||
|
key: "reason",
|
||||||
|
label: "Grund",
|
||||||
|
sortable: true
|
||||||
|
},{
|
||||||
|
key: "start",
|
||||||
|
label: "Start",
|
||||||
|
sortable: true
|
||||||
|
},{
|
||||||
|
key: "end",
|
||||||
|
label: "Ende",
|
||||||
|
sortable: true
|
||||||
|
},{
|
||||||
|
key: "note",
|
||||||
|
label: "Notizen",
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const selectItem = (item) => {
|
||||||
|
router.push(`/employees/absenceRequests/show/${item.id} `)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -3,60 +3,71 @@ definePageMeta({
|
|||||||
middleware: "auth"
|
middleware: "auth"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const dataStore = useDataStore()
|
||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
|
const router = useRouter()
|
||||||
const {spaces, movements, products} = storeToRefs(useDataStore())
|
const mode = ref("incoming")
|
||||||
|
|
||||||
|
|
||||||
const searchinput = ref("")
|
|
||||||
const mode = ref("")
|
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const showReader = ref(false)
|
|
||||||
|
|
||||||
const inventoryChangeData = ref({
|
const inventoryChangeData = ref({
|
||||||
productId: "",
|
productId: "",
|
||||||
spaceId: "",
|
spaceId: "",
|
||||||
quantity: 0
|
quantity: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
const createMovement = async () => {
|
const createMovement = async () => {
|
||||||
if(mode.value === 'incoming'){
|
|
||||||
//await create('movements', inventoryChangeData.value)
|
|
||||||
|
|
||||||
const {data,error} = await supabase
|
if(mode.value === '' || !checkSpaceId(inventoryChangeData.value.spaceId) || !checkArticle(inventoryChangeData.value.productId)){
|
||||||
.from("movements")
|
|
||||||
.insert([inventoryChangeData.value])
|
|
||||||
.select()
|
|
||||||
|
|
||||||
console.log(error)
|
} else {
|
||||||
|
if(mode.value === 'incoming'){
|
||||||
|
|
||||||
|
const {error} = await supabase
|
||||||
|
.from("movements")
|
||||||
|
.insert([inventoryChangeData.value])
|
||||||
|
.select()
|
||||||
|
if(error) console.log(error)
|
||||||
|
|
||||||
|
|
||||||
} else if (mode.value === 'outgoing'){
|
} else if (mode.value === 'outgoing'){
|
||||||
inventoryChangeData.value.quantity *= -1
|
inventoryChangeData.value.quantity *= -1
|
||||||
//await create('movements', inventoryChangeData.value)
|
|
||||||
|
|
||||||
const {data,error} = await supabase
|
const {error} = await supabase
|
||||||
.from("movements")
|
.from("movements")
|
||||||
.insert([inventoryChangeData.value])
|
.insert([inventoryChangeData.value])
|
||||||
.select()
|
.select()
|
||||||
|
if(error) console.log(error)
|
||||||
|
} else if (mode.value === 'change'){}
|
||||||
|
|
||||||
console.log(error)
|
|
||||||
} else if (mode.value === 'change'){}
|
|
||||||
|
|
||||||
inventoryChangeData.value = {
|
inventoryChangeData.value = {
|
||||||
productId: 0,
|
productId: "",
|
||||||
spaceId: 0,
|
spaceId: "",
|
||||||
quantity: 0
|
quantity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dataStore.fetchMovements()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineShortcuts({
|
||||||
|
meta_enter: {
|
||||||
|
usingInput: true,
|
||||||
|
handler: () => {
|
||||||
|
createMovement()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
function checkArticle(productId) {
|
function checkArticle(productId) {
|
||||||
return products.value.filter(product =>product.ean === productId).length > 0;
|
return dataStore.products.filter(product =>product.id === productId).length > 0;
|
||||||
}
|
}
|
||||||
function checkSpaceId(spaceId) {
|
function checkSpaceId(spaceId) {
|
||||||
return spaces.value.filter(space => Number(space.spaceNumber) === Number(spaceId)).length > 0;
|
return dataStore.spaces.filter(space => space.id === spaceId).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeFocusToSpaceId() {
|
function changeFocusToSpaceId() {
|
||||||
@@ -73,42 +84,66 @@ function changeFocusToQuantity() {
|
|||||||
<div id="main">
|
<div id="main">
|
||||||
|
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<UButton @click="mode = 'incoming'" class="ml-3" >Wareneingang</UButton>
|
<UButton
|
||||||
<UButton @click="mode = 'outgoing'" class="ml-1">Warenausgang</UButton>
|
@click="mode = 'incoming'"
|
||||||
<UButton @click="mode = 'change'" class="ml-1" disabled>Umlagern</UButton>
|
class="ml-3"
|
||||||
|
:variant="mode === 'incoming' ? 'solid' : 'outline'"
|
||||||
|
>Wareneingang</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="mode = 'outgoing'"
|
||||||
|
class="ml-1"
|
||||||
|
:variant="mode === 'outgoing' ? 'solid' : 'outline'"
|
||||||
|
>Warenausgang</UButton>
|
||||||
|
<!-- <UButton
|
||||||
|
@click="mode = 'change'"
|
||||||
|
class="ml-1"
|
||||||
|
disabled
|
||||||
|
:variant="mode === 'change' ? 'solid' : 'outline'"
|
||||||
|
>Umlagern</UButton>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Artikel:"
|
label="Artikel:"
|
||||||
class="mt-3 w-80"
|
class="mt-3 w-80"
|
||||||
>
|
>
|
||||||
<UInput
|
<USelectMenu
|
||||||
|
:options="dataStore.products"
|
||||||
|
option-attribute="name"
|
||||||
|
value-attribute="id"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['name','ean', 'barcode']"
|
||||||
:color="checkArticle(inventoryChangeData.productId) ? 'primary' : 'rose'"
|
:color="checkArticle(inventoryChangeData.productId) ? 'primary' : 'rose'"
|
||||||
v-model="inventoryChangeData.productId"
|
v-model="inventoryChangeData.productId"
|
||||||
v-on:keyup.enter="changeFocusToSpaceId"
|
v-on:select="changeFocusToSpaceId"
|
||||||
/>
|
>
|
||||||
|
<template #label>
|
||||||
|
{{dataStore.products.find(product => product.id === inventoryChangeData.productId) ? dataStore.products.find(product => product.id === inventoryChangeData.productId).name : "Bitte Artikel auswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Lagerplatz:"
|
label="Lagerplatz:"
|
||||||
class="mt-3 w-80"
|
class="mt-3 w-80"
|
||||||
><!--.map(space => {return {id: space.id, name: space.spaceNumber}}-->
|
>
|
||||||
<!-- <USelectMenu
|
<USelectMenu
|
||||||
:options="spaces"
|
:options="dataStore.spaces"
|
||||||
searchable
|
searchable
|
||||||
option-attribute="spaceNumber"
|
option-attribute="description"
|
||||||
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
|
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
|
||||||
v-model="inventoryChangeData.spaceId"
|
v-model="inventoryChangeData.spaceId"
|
||||||
|
v-on:select="changeFocusToQuantity"
|
||||||
value-attribute="id"
|
value-attribute="id"
|
||||||
/>-->
|
>
|
||||||
<UInput
|
<template #label>
|
||||||
v-model="inventoryChangeData.spaceId"
|
{{dataStore.spaces.find(space => space.id === inventoryChangeData.spaceId) ? dataStore.spaces.find(space => space.id === inventoryChangeData.spaceId).description : "Kein Lagerplatz ausgewählt"}}
|
||||||
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
|
</template>
|
||||||
id="spaceIdInput"
|
</USelectMenu>
|
||||||
v-on:keyup.enter="changeFocusToQuantity"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ definePageMeta({
|
|||||||
middleware: "auth"
|
middleware: "auth"
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
const dataStore = useDataStore()
|
||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -13,9 +13,6 @@ const toast = useToast()
|
|||||||
const id = ref(route.params.id ? route.params.id : null )
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
const numberRange = useNumberRange("spaces")
|
const numberRange = useNumberRange("spaces")
|
||||||
|
|
||||||
//Store
|
|
||||||
const {spaces,movements,products,units,ownTenant, numberRanges} = storeToRefs(useDataStore())
|
|
||||||
const {fetchSpaces, getSpaceById, movementsBySpace, getProductById, fetchNumberRanges} = useDataStore()
|
|
||||||
|
|
||||||
|
|
||||||
let currentItem = ref(null)
|
let currentItem = ref(null)
|
||||||
@@ -26,7 +23,7 @@ const mode = ref(route.params.mode || "show")
|
|||||||
const itemInfo = ref({
|
const itemInfo = ref({
|
||||||
spaceNumber: ""
|
spaceNumber: ""
|
||||||
})
|
})
|
||||||
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz"]
|
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz", "Sonstiges"]
|
||||||
const spaceProducts = ref([])
|
const spaceProducts = ref([])
|
||||||
const spaceMovements = ref([])
|
const spaceMovements = ref([])
|
||||||
|
|
||||||
@@ -34,12 +31,12 @@ const spaceMovements = ref([])
|
|||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
console.log("Called Setup")
|
console.log("Called Setup")
|
||||||
if(mode.value === "show" || mode.value === "edit"){
|
if(mode.value === "show" || mode.value === "edit"){
|
||||||
currentItem.value = await getSpaceById(Number(useRoute().params.id))
|
currentItem.value = await dataStore.getSpaceById(Number(useRoute().params.id))
|
||||||
|
|
||||||
spaceMovements.value = await movementsBySpace(currentItem.value.id)
|
spaceMovements.value = await dataStore.getMovementsBySpace(currentItem.value.id)
|
||||||
spaceProducts.value = []
|
spaceProducts.value = []
|
||||||
spaceMovements.value.forEach(movement => {
|
spaceMovements.value.forEach(movement => {
|
||||||
if(spaceProducts.value.filter(product => product.id === movement.productId).length === 0) spaceProducts.value.push(getProductById(movement.productId))
|
if(spaceProducts.value.filter(product => product.id === movement.productId).length === 0) spaceProducts.value.push(dataStore.getProductById(movement.productId))
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -63,7 +60,7 @@ const createItem = async () => {
|
|||||||
console.log(data[0])
|
console.log(data[0])
|
||||||
mode.value = "show"
|
mode.value = "show"
|
||||||
toast.add({title: "Lagerplatz erfolgreich erstellt"})
|
toast.add({title: "Lagerplatz erfolgreich erstellt"})
|
||||||
await fetchSpaces()
|
await dataStore.fetchSpaces()
|
||||||
router.push(`/inventory/spaces/show/${data[0].id}`)
|
router.push(`/inventory/spaces/show/${data[0].id}`)
|
||||||
|
|
||||||
|
|
||||||
@@ -93,7 +90,7 @@ const updateItem = async () => {
|
|||||||
mode.value = "show"
|
mode.value = "show"
|
||||||
itemInfo.value = {}
|
itemInfo.value = {}
|
||||||
toast.add({title: "Lagerplatz erfolgreich gespeichert"})
|
toast.add({title: "Lagerplatz erfolgreich gespeichert"})
|
||||||
fetchSpaces()
|
dataStore.fetchSpaces()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +108,7 @@ const printSpaceLabel = async () => {
|
|||||||
|
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post(`http://${ownTenant.value.labelPrinterIp}/pstprnt`, `^XA^FO10,20^BCN,100^FD${currentItem.value.spaceNumber}^XZ` )
|
.post(`http://${dataStore.ownTenant.value.labelPrinterIp}/pstprnt`, `^XA^FO10,20^BCN,100^FD${currentItem.value.spaceNumber}^XZ` )
|
||||||
.then(console.log)
|
.then(console.log)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
}
|
}
|
||||||
@@ -149,7 +146,7 @@ setupPage()
|
|||||||
<tr v-for="product in spaceProducts">
|
<tr v-for="product in spaceProducts">
|
||||||
<td>{{product.name}}</td>
|
<td>{{product.name}}</td>
|
||||||
<td>{{getSpaceProductCount(product.id)}}</td>
|
<td>{{getSpaceProductCount(product.id)}}</td>
|
||||||
<td>{{units.find(unit => unit.id === product.unit).name}}</td>
|
<td>{{dataStore.units.find(unit => unit.id === product.unit).name}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -72,12 +72,13 @@ const updateItem = async () => {
|
|||||||
.from("jobs")
|
.from("jobs")
|
||||||
.update(itemInfo.value)
|
.update(itemInfo.value)
|
||||||
.eq('id',itemInfo.value.id)
|
.eq('id',itemInfo.value.id)
|
||||||
console.log(error)
|
|
||||||
mode.value = "show"
|
if(error) {
|
||||||
itemInfo.value = {
|
console.log(error)
|
||||||
id: 0,
|
|
||||||
title: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
router.push(`/jobs/show/${currentItem.id}`)
|
||||||
toast.add({title: "Job erfolgreich gespeichert"})
|
toast.add({title: "Job erfolgreich gespeichert"})
|
||||||
dataStore.fetchJobs()
|
dataStore.fetchJobs()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
:columns="columns"
|
:columns="columns"
|
||||||
@select="selectJob"
|
@select="selectJob"
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
/>
|
>
|
||||||
|
<template #customer-data="{row}">
|
||||||
|
{{dataStore.customers.find(customer => customer.id === row.customer) ? dataStore.customers.find(customer => customer.id === row.customer).name : "" }}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import resourceTimelinePlugin from '@fullcalendar/resource-timeline'
|
|||||||
import deLocale from '@fullcalendar/core/locales/de'
|
import deLocale from '@fullcalendar/core/locales/de'
|
||||||
import interactionPlugin from '@fullcalendar/interaction'
|
import interactionPlugin from '@fullcalendar/interaction'
|
||||||
import listPlugin from '@fullcalendar/list';
|
import listPlugin from '@fullcalendar/list';
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
|
||||||
const viewport = useViewport()
|
const viewport = useViewport()
|
||||||
@@ -19,6 +20,7 @@ const eventTypes = dataStore.getEventTypes
|
|||||||
const events = dataStore.getEvents
|
const events = dataStore.getEvents
|
||||||
|
|
||||||
const openNewEventModal = ref(false)
|
const openNewEventModal = ref(false)
|
||||||
|
const showEventModal = ref(false)
|
||||||
const newEventData = ref({
|
const newEventData = ref({
|
||||||
resourceId: "",
|
resourceId: "",
|
||||||
resourceType: "",
|
resourceType: "",
|
||||||
@@ -27,6 +29,9 @@ const newEventData = ref({
|
|||||||
start: "",
|
start: "",
|
||||||
end: null
|
end: null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const selectedEvent = ref({})
|
||||||
|
|
||||||
const createEvent = async () => {
|
const createEvent = async () => {
|
||||||
const {data,error} = await supabase
|
const {data,error} = await supabase
|
||||||
.from("events")
|
.from("events")
|
||||||
@@ -76,6 +81,9 @@ const calendarOptionsTimeline = reactive({
|
|||||||
openNewEventModal.value = true
|
openNewEventModal.value = true
|
||||||
},
|
},
|
||||||
eventClick: function (info){
|
eventClick: function (info){
|
||||||
|
selectedEvent.value = info.event
|
||||||
|
showEventModal.value = true
|
||||||
|
|
||||||
console.log(info)
|
console.log(info)
|
||||||
},
|
},
|
||||||
resourceGroupField: "type",
|
resourceGroupField: "type",
|
||||||
@@ -153,7 +161,28 @@ const calendarOptionsTimeline = reactive({
|
|||||||
|
|
||||||
</UCard>
|
</UCard>
|
||||||
</UModal>
|
</UModal>
|
||||||
|
<UModal
|
||||||
|
v-model="showEventModal"
|
||||||
|
>
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
{{selectedEvent.title}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
Start: {{dayjs(selectedEvent.startStr).format("DD.MM.YYYY HH:mm")}}<br>
|
||||||
|
Ende: {{dayjs(selectedEvent.endStr).format("DD.MM.YYYY HH:mm")}}
|
||||||
|
|
||||||
|
<DevOnly>
|
||||||
|
<UDivider class="my-3"/>
|
||||||
|
{{selectedEvent}}
|
||||||
|
</DevOnly>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
|
||||||
|
|
||||||
|
</UModal>
|
||||||
<div v-if="viewport.isLessThan('tablet')">
|
<div v-if="viewport.isLessThan('tablet')">
|
||||||
<FullCalendar
|
<FullCalendar
|
||||||
:options="calendarOptionsList"
|
:options="calendarOptionsList"
|
||||||
|
|||||||
@@ -107,10 +107,12 @@ setupPage()
|
|||||||
{{tag}}
|
{{tag}}
|
||||||
</UBadge>
|
</UBadge>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<UDivider
|
<UDivider
|
||||||
class="my-2"
|
class="my-2"
|
||||||
/>
|
/>
|
||||||
|
<span v-if="currentProduct.purchasePrice">Einkaufspreis: {{Number(currentProduct.purchasePrice).toFixed(2)}} €<br></span>
|
||||||
Bestand: {{dataStore.getStockByProductId(currentProduct.id)}} {{dataStore.units.find(unit => unit.id === currentProduct.unit) ? dataStore.units.find(unit => unit.id === currentProduct.unit).name : ""}}
|
Bestand: {{dataStore.getStockByProductId(currentProduct.id)}} {{dataStore.units.find(unit => unit.id === currentProduct.unit) ? dataStore.units.find(unit => unit.id === currentProduct.unit).name : ""}}
|
||||||
|
|
||||||
<DevOnly>
|
<DevOnly>
|
||||||
@@ -193,6 +195,26 @@ setupPage()
|
|||||||
v-model="itemInfo.ean"
|
v-model="itemInfo.ean"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Barcode:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.barcode"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Einkaufspreis:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.purchasePrice"
|
||||||
|
type="number"
|
||||||
|
steps="0.01"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -18,6 +18,12 @@
|
|||||||
@select="selectItem"
|
@select="selectItem"
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||||
>
|
>
|
||||||
|
<template #stock-data="{row}">
|
||||||
|
{{`${dataStore.getStockByProductId(row.id)} ${(dataStore.units.find(unit => unit.id === row.unit) ? dataStore.units.find(unit => unit.id === row.unit).name : "")}`}}
|
||||||
|
</template>
|
||||||
|
<template #purchasePrice-data="{row}">
|
||||||
|
{{row.purchasePrice ? Number(row.purchasePrice).toFixed(2) + " €" : ""}}
|
||||||
|
</template>
|
||||||
<template #tags-data="{row}">
|
<template #tags-data="{row}">
|
||||||
<UBadge
|
<UBadge
|
||||||
v-if="row.tags.length > 0"
|
v-if="row.tags.length > 0"
|
||||||
@@ -48,24 +54,33 @@ const router = useRouter()
|
|||||||
|
|
||||||
const itemColumns = [
|
const itemColumns = [
|
||||||
{
|
{
|
||||||
key: "id",
|
key: "stock",
|
||||||
label: "Id"
|
label: "Bestand"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
label: "Name"
|
label: "Name",
|
||||||
|
sortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "manufacturer",
|
key: "manufacturer",
|
||||||
label: "Hersteller",
|
label: "Hersteller",
|
||||||
|
sortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "unit",
|
key: "unit",
|
||||||
label: "Einheit"
|
label: "Einheit",
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "purchasePrice",
|
||||||
|
label: "Einkaufspreis",
|
||||||
|
sortable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "tags",
|
key: "tags",
|
||||||
label: "Tags"
|
label: "Tags",
|
||||||
|
sortable: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,16 @@ import * as dayjs from 'dayjs'
|
|||||||
const supabase = useSupabaseClient()
|
const supabase = useSupabaseClient()
|
||||||
const user = useSupabaseUser()
|
const user = useSupabaseUser()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
|
||||||
|
|
||||||
const {getProjectById, getFormSubmitsWithLabelProp, getTimesByProjectId, getDocumentTags, getDocumentsByProjectId, fetchDocuments} = useDataStore()
|
const {getProjectById, getFormSubmitsWithLabelProp, getTimesByProjectId, getDocumentTags, getDocumentsByProjectId, fetchDocuments} = useDataStore()
|
||||||
const {forms, formSubmits, times, profiles,documents} = storeToRefs(useDataStore())
|
const {forms, formSubmits, times, profiles,documents} = storeToRefs(useDataStore())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fetchDocuments()
|
fetchDocuments()
|
||||||
const currentProject = getProjectById(Number(route.params.id))
|
const currentProject = getProjectById(Number(route.params.id))
|
||||||
|
|
||||||
@@ -74,6 +80,9 @@ const tabItems = [
|
|||||||
{
|
{
|
||||||
key: "phases",
|
key: "phases",
|
||||||
label: "Phasen"
|
label: "Phasen"
|
||||||
|
},{
|
||||||
|
key: "tasks",
|
||||||
|
label: "Aufgaben"
|
||||||
},{
|
},{
|
||||||
key: "forms",
|
key: "forms",
|
||||||
label: "Formulare"
|
label: "Formulare"
|
||||||
@@ -235,6 +244,17 @@ const phaseInfo = ref({
|
|||||||
</a>
|
</a>
|
||||||
</div>-->
|
</div>-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div v-if="item.key === 'tasks'" class="space-y-3">
|
||||||
|
<UTable
|
||||||
|
:rows="dataStore.getTasksByProjectId(currentProject.id)"
|
||||||
|
@select="(row) => {
|
||||||
|
router.push(`/tasks/show/${row.id}`)
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
|
||||||
|
</UTable>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="item.key === 'forms'" class="space-y-3">
|
<div v-else-if="item.key === 'forms'" class="space-y-3">
|
||||||
<UButton
|
<UButton
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ const resources = {
|
|||||||
vendors: {
|
vendors: {
|
||||||
label: "Lieferanten"
|
label: "Lieferanten"
|
||||||
},
|
},
|
||||||
|
products: {
|
||||||
|
label: "Artikel"
|
||||||
|
},
|
||||||
spaces: {
|
spaces: {
|
||||||
label: "Lagerplätze"
|
label: "Lagerplätze"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
definePageMeta({
|
|
||||||
middleware: "auth"
|
|
||||||
})
|
|
||||||
const dataStore = useDataStore()
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
{{dataStore.profiles}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
206
spaces/pages/settings/users/[mode]/[[id]].vue
Normal file
206
spaces/pages/settings/users/[mode]/[[id]].vue
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
<script setup>
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: Build User Page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const supabase = useSupabaseClient()
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
|
|
||||||
|
let currentItem = null
|
||||||
|
|
||||||
|
//Working
|
||||||
|
const mode = ref(route.params.mode || "show")
|
||||||
|
const itemInfo = ref({})
|
||||||
|
|
||||||
|
//Functions
|
||||||
|
const setupPage = () => {
|
||||||
|
if(mode.value === "show" || mode.value === "edit"){
|
||||||
|
currentItem = dataStore.getProfileById(useRoute().params.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode.value === "edit") itemInfo.value = currentItem
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*const createItem = async () => {
|
||||||
|
const {data,error} = await supabase
|
||||||
|
.from("profiles")
|
||||||
|
.insert([itemInfo.value])
|
||||||
|
.select()
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
} else {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
title: "",
|
||||||
|
}
|
||||||
|
toast.add({title: "Job erfolgreich erstellt"})
|
||||||
|
await dataStore.fetchJobs()
|
||||||
|
router.push(`/jobs/show/${data[0].id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const editItem = async () => {
|
||||||
|
router.push(`/users/edit/${currentItem.id}`)
|
||||||
|
setupPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelEditorCreate = () => {
|
||||||
|
mode.value = "show"
|
||||||
|
itemInfo.value = {
|
||||||
|
id: 0,
|
||||||
|
infoData: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateItem = async () => {
|
||||||
|
const {error} = await supabase
|
||||||
|
.from("jobs")
|
||||||
|
.update(itemInfo.value)
|
||||||
|
.eq('id',itemInfo.value.id)
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
router.push(`/jobs/show/${currentItem.id}`)
|
||||||
|
toast.add({title: "Job erfolgreich gespeichert"})
|
||||||
|
dataStore.fetchJobs()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupPage()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<UCard v-if="currentItem && mode == 'show'" >
|
||||||
|
<template #header>
|
||||||
|
<UBadge>
|
||||||
|
{{currentItem.state}}
|
||||||
|
</UBadge>
|
||||||
|
|
||||||
|
{{currentItem.title}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
Beschreibung:<br>
|
||||||
|
{{currentItem.description}}<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'show' && currentItem.id"
|
||||||
|
@click="editItem"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Archivieren
|
||||||
|
</UButton>
|
||||||
|
<!-- TODO: Kunde archivieren -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
||||||
|
<template #header>
|
||||||
|
{{itemInfo.title}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Titel:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.title"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Status:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.state"
|
||||||
|
:options="states"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Kundennummer:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
v-model="itemInfo.customer"
|
||||||
|
:options="dataStore.customers"
|
||||||
|
option-attribute="name"
|
||||||
|
value-attribute="id"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['name']"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{dataStore.customers.find(customer => customer.id === itemInfo.customer) ? dataStore.customers.find(customer => customer.id === itemInfo.customer).name : "Kunde auswählen"}}
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Beschreibung:"
|
||||||
|
>
|
||||||
|
<UTextarea
|
||||||
|
v-model="itemInfo.description"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<UButton
|
||||||
|
v-if="mode == 'edit'"
|
||||||
|
@click="updateItem"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-else-if="mode == 'create'"
|
||||||
|
@click="createItem"
|
||||||
|
>
|
||||||
|
Erstellen
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
@click="cancelEditorCreate"
|
||||||
|
color="red"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
51
spaces/pages/settings/users/index.vue
Normal file
51
spaces/pages/settings/users/index.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key:"id",
|
||||||
|
label: "Id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key:"firstName",
|
||||||
|
label: "Vorname:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "lastName",
|
||||||
|
label: "Nachname:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "username",
|
||||||
|
label: "Benutzername"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "role",
|
||||||
|
label: "Rolle"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
:rows="dataStore.profiles"
|
||||||
|
:columns="columns"
|
||||||
|
@select="(item) => router.push(`/settings/users/show/${item.id}`)"
|
||||||
|
>
|
||||||
|
|
||||||
|
</UTable>
|
||||||
|
|
||||||
|
<DevOnly>
|
||||||
|
{{dataStore.profiles}}
|
||||||
|
</DevOnly>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -45,8 +45,7 @@
|
|||||||
:options="dataStore.profiles"
|
:options="dataStore.profiles"
|
||||||
value-attribute="id"
|
value-attribute="id"
|
||||||
option-attribute="fullName"
|
option-attribute="fullName"
|
||||||
v-model="createTaskData.users"
|
v-model="createTaskData.user"
|
||||||
multiple
|
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
@@ -87,6 +86,19 @@
|
|||||||
v-model="taskData.categorie"
|
v-model="taskData.categorie"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Projekt:"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
:options="dataStore.projects"
|
||||||
|
@change="updateTask"
|
||||||
|
v-model="taskData.project"
|
||||||
|
option-attribute="name"
|
||||||
|
value-attribute="id"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['name']"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup
|
<UFormGroup
|
||||||
label="Benutzer ändern:"
|
label="Benutzer ändern:"
|
||||||
@@ -134,6 +146,9 @@
|
|||||||
<template #user-data="{row}">
|
<template #user-data="{row}">
|
||||||
{{dataStore.profiles.find(profile => profile.id === row.user ) ? dataStore.profiles.find(profile => profile.id === row.user ).fullName : row.user}}
|
{{dataStore.profiles.find(profile => profile.id === row.user ) ? dataStore.profiles.find(profile => profile.id === row.user ).fullName : row.user}}
|
||||||
</template>
|
</template>
|
||||||
|
<template #project-data="{row}">
|
||||||
|
{{dataStore.projects.find(item => item.id === row.project) ? dataStore.projects.find(item => item.id === row.project).name : "" }}
|
||||||
|
</template>
|
||||||
</UTable>
|
</UTable>
|
||||||
|
|
||||||
|
|
||||||
@@ -179,6 +194,11 @@ const taskColumns = [
|
|||||||
key:"categorie",
|
key:"categorie",
|
||||||
label: "Kategorie:",
|
label: "Kategorie:",
|
||||||
sortable: true
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key:"project",
|
||||||
|
label: "Projekt:",
|
||||||
|
sortable: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const cancelEditorCreate = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCustomer = async () => {
|
const updateItem = async () => {
|
||||||
const {error} = await supabase
|
const {error} = await supabase
|
||||||
.from("vehicles")
|
.from("vehicles")
|
||||||
.update(itemInfo.value)
|
.update(itemInfo.value)
|
||||||
@@ -80,13 +80,8 @@ const updateCustomer = async () => {
|
|||||||
if(error) {
|
if(error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
} else {
|
} else {
|
||||||
mode.value = "show"
|
|
||||||
itemInfo.value = {
|
router.push(`/vehicles/show/${currentItem.id}`)
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
licensePlate: "",
|
|
||||||
type: ""
|
|
||||||
}
|
|
||||||
toast.add({title: "Fahrzeug erfolgreich gespeichert"})
|
toast.add({title: "Fahrzeug erfolgreich gespeichert"})
|
||||||
dataStore.fetchVehicles()
|
dataStore.fetchVehicles()
|
||||||
}
|
}
|
||||||
@@ -187,7 +182,7 @@ setupPage()
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<UButton
|
<UButton
|
||||||
v-if="mode == 'edit'"
|
v-if="mode == 'edit'"
|
||||||
@click="updateCustomer"
|
@click="updateItem"
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</UButton>
|
</UButton>
|
||||||
|
|||||||
@@ -69,6 +69,97 @@
|
|||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="my-3"
|
||||||
|
v-for="(item,index) in itemInfo.accounts"
|
||||||
|
>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Kategorie"
|
||||||
|
class=" mb-3"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
:options="dataStore.accounts"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="id"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['label']"
|
||||||
|
searchable-placeholder="Suche..."
|
||||||
|
v-model="item.account"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{dataStore.accounts.find(account => account.id === item.account) ? dataStore.accounts.find(account => account.id === item.account).label : "Keine Kategorie ausgewählt" }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<InputGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Steuer"
|
||||||
|
class="w-32"
|
||||||
|
:help="`Betrag: ${String(item.amountTax).replace('.',',')} €`"
|
||||||
|
>
|
||||||
|
<USelectMenu
|
||||||
|
:options="[19,7,0]"
|
||||||
|
v-model="item.taxType"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
{{item.taxType}} %
|
||||||
|
</template>
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Gesamtbetrag exkl. Steuer in EUR"
|
||||||
|
class="flex-auto"
|
||||||
|
:help="`Betrag inkl. Steuern: ${String(Number(calculateWithTax(item)) + Number(item.amountNet)).replace('.',',')} €` "
|
||||||
|
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
v-model="item.amountNet"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
class="mt-3"
|
||||||
|
@click="itemInfo.accounts = [...itemInfo.accounts.slice(0,index+1),{account:null, amountNet: null, amountTax:null, taxType: null} , ...itemInfo.accounts.slice(index+1)]"
|
||||||
|
>
|
||||||
|
Position hinzufügen
|
||||||
|
</UButton>
|
||||||
|
<UButton
|
||||||
|
v-if="index !== 0"
|
||||||
|
class="mt-3"
|
||||||
|
variant="ghost"
|
||||||
|
color="rose"
|
||||||
|
@click="itemInfo.accounts = itemInfo.accounts.filter((account,itemIndex) => itemIndex !== index)"
|
||||||
|
>
|
||||||
|
Position entfernen
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<UFormGroup label="Kategorie:" required>
|
||||||
|
<USelectMenu
|
||||||
|
:options="dataStore.accounts"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="number"
|
||||||
|
searchable
|
||||||
|
:search-attributes="['label']"
|
||||||
|
v-model="itemInfo.account"
|
||||||
|
>
|
||||||
|
|
||||||
|
</USelectMenu>
|
||||||
|
</UFormGroup>
|
||||||
<UFormGroup label="Betrag:" required>
|
<UFormGroup label="Betrag:" required>
|
||||||
<UInput
|
<UInput
|
||||||
type="number"
|
type="number"
|
||||||
@@ -79,7 +170,7 @@
|
|||||||
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||||
</template>
|
</template>
|
||||||
</UInput>
|
</UInput>
|
||||||
</UFormGroup>
|
</UFormGroup>-->
|
||||||
|
|
||||||
<InputGroup class="mt-3">
|
<InputGroup class="mt-3">
|
||||||
<UButton
|
<UButton
|
||||||
@@ -117,6 +208,48 @@ let currentDocument = ref(null)
|
|||||||
//Working
|
//Working
|
||||||
const mode = ref(route.params.mode || "show")
|
const mode = ref(route.params.mode || "show")
|
||||||
|
|
||||||
|
const types = [
|
||||||
|
{
|
||||||
|
number: "3200",
|
||||||
|
label: "Wareneinkauf",
|
||||||
|
description: "Der Einkauf von Produkten umfasst auch die Kosten für die Bearbeitung/Verarbeitung und den Handel"
|
||||||
|
},{
|
||||||
|
number: "3000",
|
||||||
|
label: "Materialeinkauf",
|
||||||
|
description: "Hierzu gehören sämtliche Kosten, die im Rahmen der Produktion für Roh, Hilfs- und Betriebsstoffe anfallen."
|
||||||
|
},{
|
||||||
|
number: "3800",
|
||||||
|
label: "Bezugsnebenkosten",
|
||||||
|
description: "Dazu zählen alle Beschaffungskosten für Material und Waren bzw. Produkte"
|
||||||
|
},{
|
||||||
|
number: "4930",
|
||||||
|
label: "Bürobedarf",
|
||||||
|
description: ""
|
||||||
|
},{
|
||||||
|
number: "4964",
|
||||||
|
label: "Lizenzen und Konzessionen",
|
||||||
|
description: ""
|
||||||
|
},{
|
||||||
|
number: "4925",
|
||||||
|
label: "Internet",
|
||||||
|
description: ""
|
||||||
|
},{
|
||||||
|
number: "4920",
|
||||||
|
label: "Telekommunikation",
|
||||||
|
description: ""
|
||||||
|
},{
|
||||||
|
number: "4530",
|
||||||
|
label: "Kraftstoff/Ladestrom",
|
||||||
|
description: ""
|
||||||
|
},{
|
||||||
|
number: "4969",
|
||||||
|
label: "Müllgebühren",
|
||||||
|
description: ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Functions
|
//Functions
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
@@ -139,9 +272,24 @@ const itemInfo = ref({
|
|||||||
dueDate: null,
|
dueDate: null,
|
||||||
paymentType: "",
|
paymentType: "",
|
||||||
description: "",
|
description: "",
|
||||||
state: "Entwurf"
|
state: "Entwurf",
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
account: null,
|
||||||
|
amountNet: null,
|
||||||
|
amountTax: null,
|
||||||
|
taxType: null
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const calculateWithTax = (item) => {
|
||||||
|
item.amountTax = Number((item.amountNet / 100 * Number(item.taxType)).toFixed(2))
|
||||||
|
|
||||||
|
return item.amountTax
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const updateItem = async () => {
|
const updateItem = async () => {
|
||||||
const {error} = await supabase
|
const {error} = await supabase
|
||||||
.from("vendorInvoices")
|
.from("vendorInvoices")
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
{{row.dueDate ? dayjs(row.dueDate).format("DD.MM.YY") : ''}}
|
{{row.dueDate ? dayjs(row.dueDate).format("DD.MM.YY") : ''}}
|
||||||
</template>
|
</template>
|
||||||
<template #amount-data="{row}">
|
<template #amount-data="{row}">
|
||||||
{{row.amount ? row.amount.toFixed(2) + ' €' : ''}}
|
{{getRowAmount(row) === 0 ? '' : `${getRowAmount(row)} €`}}
|
||||||
</template>
|
</template>
|
||||||
</UTable>
|
</UTable>
|
||||||
|
|
||||||
@@ -101,6 +101,17 @@ const selectItem = (item) => {
|
|||||||
router.push(`/vendorinvoices/edit/${item.id} `)
|
router.push(`/vendorinvoices/edit/${item.id} `)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getRowAmount = (row) => {
|
||||||
|
let amount = 0
|
||||||
|
|
||||||
|
row.accounts.forEach(account => {
|
||||||
|
amount += account.amountNet
|
||||||
|
amount += account.amountTax
|
||||||
|
})
|
||||||
|
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const searchString = ref('')
|
const searchString = ref('')
|
||||||
|
|
||||||
|
|||||||
96
spaces/pages/vendors/[mode]/[[id]].vue
vendored
96
spaces/pages/vendors/[mode]/[[id]].vue
vendored
@@ -1,4 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import HistoryDisplay from "~/components/HistoryDisplay.vue";
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: "auth"
|
middleware: "auth"
|
||||||
})
|
})
|
||||||
@@ -9,6 +11,7 @@ const route = useRoute()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const id = ref(route.params.id ? route.params.id : null )
|
const id = ref(route.params.id ? route.params.id : null )
|
||||||
|
const numberRange = useNumberRange("vendors")
|
||||||
|
|
||||||
let currentItem = null
|
let currentItem = null
|
||||||
|
|
||||||
@@ -16,7 +19,9 @@ let currentItem = null
|
|||||||
|
|
||||||
//Working
|
//Working
|
||||||
const mode = ref(route.params.mode || "show")
|
const mode = ref(route.params.mode || "show")
|
||||||
const itemInfo = ref({})
|
const itemInfo = ref({
|
||||||
|
infoData: {}
|
||||||
|
})
|
||||||
|
|
||||||
//Functions
|
//Functions
|
||||||
const setupPage = () => {
|
const setupPage = () => {
|
||||||
@@ -31,6 +36,9 @@ const setupPage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createItem = async () => {
|
const createItem = async () => {
|
||||||
|
|
||||||
|
if(!itemInfo.value.vendorNumber) itemInfo.value.vendorNumber = await numberRange.useNextNumber()
|
||||||
|
|
||||||
const {data,error} = await supabase
|
const {data,error} = await supabase
|
||||||
.from("vendors")
|
.from("vendors")
|
||||||
.insert([itemInfo.value])
|
.insert([itemInfo.value])
|
||||||
@@ -66,9 +74,8 @@ const updateItem = async () => {
|
|||||||
if(error) {
|
if(error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
} else {
|
} else {
|
||||||
mode.value = "show"
|
|
||||||
itemInfo.value = {}
|
|
||||||
toast.add({title: "Lieferant erfolgreich gespeichert"})
|
toast.add({title: "Lieferant erfolgreich gespeichert"})
|
||||||
|
router.push(`/vendors/show/${currentItem.id}`)
|
||||||
dataStore.fetchVendors()
|
dataStore.fetchVendors()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +94,29 @@ setupPage()
|
|||||||
{{currentItem.name}}
|
{{currentItem.name}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
{{currentItem}}
|
Information: <br>
|
||||||
|
<div v-if="currentItem.infoData">
|
||||||
|
<span v-if="currentItem.infoData.street">Straße + Hausnummer: {{currentItem.infoData.street}}<br></span>
|
||||||
|
<span v-if="currentItem.infoData.zip && currentItem.infoData.city">PLZ + Ort: {{currentItem.infoData.zip}} {{currentItem.infoData.city}}<br></span>
|
||||||
|
<span v-if="currentItem.infoData.tel">Telefon: {{currentItem.infoData.tel}}<br></span>
|
||||||
|
<span v-if="currentItem.infoData.email">E-Mail: {{currentItem.infoData.email}}<br></span>
|
||||||
|
<span v-if="currentItem.infoData.web">Web: {{currentItem.infoData.web}}<br></span>
|
||||||
|
<span v-if="currentItem.infoData.ustid">USt-Id: {{currentItem.infoData.ustid}}<br></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UDivider class="my-3"/>
|
||||||
|
|
||||||
|
<div v-if="dataStore.getContactsByVendorId(currentItem.id).length > 0">
|
||||||
|
Kontakte: <br>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="contact in dataStore.getContactsByVendorId(currentItem.id)"
|
||||||
|
>
|
||||||
|
<router-link :to="'/contacts/show/' + contact.id">{{contact.salutation}} {{contact.fullName}} - {{contact.role}}</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<UButton
|
<UButton
|
||||||
@@ -110,8 +137,8 @@ setupPage()
|
|||||||
|
|
||||||
|
|
||||||
</UCard>
|
</UCard>
|
||||||
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
|
||||||
<template #header>
|
<template #header v-if="mode === 'edit'">
|
||||||
{{itemInfo.name}}
|
{{itemInfo.name}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -127,6 +154,57 @@ setupPage()
|
|||||||
>
|
>
|
||||||
<UInput
|
<UInput
|
||||||
v-model="itemInfo.vendorNumber"
|
v-model="itemInfo.vendorNumber"
|
||||||
|
placeholder="Leer lassen für automatisch generierte Nummer"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Straße + Hausnummer"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.street"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Postleitzahl"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.zip"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Ort"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.city"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup
|
||||||
|
label="Telefon:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.tel"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="E-Mail:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.email"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="Webseite:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.web"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup
|
||||||
|
label="USt-Id:"
|
||||||
|
>
|
||||||
|
<UInput
|
||||||
|
v-model="itemInfo.infoData.ustid"
|
||||||
/>
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
@@ -154,6 +232,12 @@ setupPage()
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
</UCard>
|
</UCard>
|
||||||
|
|
||||||
|
<HistoryDisplay
|
||||||
|
type="vendor"
|
||||||
|
v-if="currentItem"
|
||||||
|
:element-id="currentItem.id"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
3
spaces/server/middleware/auth.ts
Normal file
3
spaces/server/middleware/auth.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default defineEventHandler((event) => {
|
||||||
|
event.context.auth = { user: 123 }
|
||||||
|
})
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import {defineStore} from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
|
import * as dayJs from "dayjs"
|
||||||
import {createClient} from '@supabase/supabase-js'
|
|
||||||
|
|
||||||
//const supabase = createClient('https://uwppvcxflrcsibuzsbil.supabase.co','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo')
|
//const supabase = createClient('https://uwppvcxflrcsibuzsbil.supabase.co','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo')
|
||||||
|
|
||||||
@@ -47,6 +46,10 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
const bankStatements = ref([])
|
const bankStatements = ref([])
|
||||||
const historyItems = ref([])
|
const historyItems = ref([])
|
||||||
const numberRanges = ref([])
|
const numberRanges = ref([])
|
||||||
|
const notifications = ref([])
|
||||||
|
const absenceRequests = ref([])
|
||||||
|
const accounts = ref([])
|
||||||
|
const taxTypes = ref([])
|
||||||
|
|
||||||
async function fetchData () {
|
async function fetchData () {
|
||||||
fetchDocuments()
|
fetchDocuments()
|
||||||
@@ -74,6 +77,10 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
await fetchBankStatements()
|
await fetchBankStatements()
|
||||||
await fetchHistoryItems()
|
await fetchHistoryItems()
|
||||||
await fetchNumberRanges()
|
await fetchNumberRanges()
|
||||||
|
await fetchNotifications()
|
||||||
|
await fetchAbsenceRequests()
|
||||||
|
await fetchAccounts()
|
||||||
|
await fetchTaxTypes()
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +111,10 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
bankStatements.value= []
|
bankStatements.value= []
|
||||||
historyItems.value = []
|
historyItems.value = []
|
||||||
numberRanges.value = []
|
numberRanges.value = []
|
||||||
|
notifications.value = []
|
||||||
|
absenceRequests.value = []
|
||||||
|
accounts.value = []
|
||||||
|
taxTypes.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchOwnTenant () {
|
async function fetchOwnTenant () {
|
||||||
@@ -117,7 +128,7 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
bankAccounts.value = (await supabase.from("bankAccounts").select()).data
|
bankAccounts.value = (await supabase.from("bankAccounts").select()).data
|
||||||
}
|
}
|
||||||
async function fetchBankStatements () {
|
async function fetchBankStatements () {
|
||||||
bankStatements.value = (await supabase.from("bankStatements").select()).data
|
bankStatements.value = (await supabase.from("bankStatements").select().order("date", {ascending:false})).data
|
||||||
}
|
}
|
||||||
async function fetchEvents () {
|
async function fetchEvents () {
|
||||||
events.value = (await supabase.from("events").select()).data
|
events.value = (await supabase.from("events").select()).data
|
||||||
@@ -176,6 +187,18 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
async function fetchNumberRanges () {
|
async function fetchNumberRanges () {
|
||||||
numberRanges.value = (await supabase.from("numberRanges").select()).data
|
numberRanges.value = (await supabase.from("numberRanges").select()).data
|
||||||
}
|
}
|
||||||
|
async function fetchNotifications () {
|
||||||
|
notifications.value = (await supabase.from("notifications").select().order("created_at", {ascending: false})).data
|
||||||
|
}
|
||||||
|
async function fetchAbsenceRequests () {
|
||||||
|
absenceRequests.value = (await supabase.from("absenceRequests").select()).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 fetchDocuments () {
|
async function fetchDocuments () {
|
||||||
documents.value = (await supabase.from("documents").select()).data
|
documents.value = (await supabase.from("documents").select()).data
|
||||||
@@ -224,6 +247,10 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
return contacts.value.filter(item => item.customer === customerId)
|
return contacts.value.filter(item => item.customer === customerId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getContactsByVendorId = computed(() => (vendorId:string) => {
|
||||||
|
return contacts.value.filter(item => item.vendor === vendorId)
|
||||||
|
})
|
||||||
|
|
||||||
const getDocumentsByProjectId = computed(() => (projectId:string) => {
|
const getDocumentsByProjectId = computed(() => (projectId:string) => {
|
||||||
return documents.value.filter(item => item.project === projectId)
|
return documents.value.filter(item => item.project === projectId)
|
||||||
})
|
})
|
||||||
@@ -232,10 +259,36 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
return times.value.filter(time => time.projectId === projectId)
|
return times.value.filter(time => time.projectId === projectId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getTasksByProjectId = computed(() => (projectId:string) => {
|
||||||
|
return tasks.value.filter(item => item.project === projectId)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getHistoryItemsByCustomer = computed(() => (customerId:string) => {
|
const getHistoryItemsByCustomer = computed(() => (customerId:string) => {
|
||||||
return historyItems.value.filter(item => item.customer === customerId)
|
return historyItems.value.filter(item => item.customer === customerId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getHistoryItemsByVendor = computed(() => (vendorId:string) => {
|
||||||
|
return historyItems.value.filter(item => item.vendor === vendorId)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getMovementsBySpaceId = computed(() => (spaceId:string) => {
|
||||||
|
return movements.value.filter(movement => movement.spaceId === spaceId)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getStockByProductId = computed(() => (productId:string) => {
|
||||||
|
let productMovements = movements.value.filter(movement => movement.productId === productId)
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
productMovements.forEach(movement => {
|
||||||
|
count += movement.quantity
|
||||||
|
})
|
||||||
|
|
||||||
|
return count
|
||||||
|
})
|
||||||
|
|
||||||
const getEventTypes = computed(() => {
|
const getEventTypes = computed(() => {
|
||||||
return ownTenant.value.calendarConfig.eventTypes
|
return ownTenant.value.calendarConfig.eventTypes
|
||||||
})
|
})
|
||||||
@@ -279,6 +332,16 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
backgroundColor: "black"
|
backgroundColor: "black"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
...absenceRequests.value.map(absence => {
|
||||||
|
return {
|
||||||
|
resourceId: absence.user,
|
||||||
|
resourceType: "person",
|
||||||
|
title: absence.reason,
|
||||||
|
start: dayJs(absence.start).toDate(),
|
||||||
|
end: dayJs(absence.end).add(1,'day').toDate(),
|
||||||
|
allDay: true
|
||||||
|
}
|
||||||
|
})
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -304,7 +367,7 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
return contacts.value.find(item => item.id === itemId)
|
return contacts.value.find(item => item.id === itemId)
|
||||||
})
|
})
|
||||||
|
|
||||||
const getVehiclesById = computed(() => (itemId:string) => {
|
const getVehicleById = computed(() => (itemId:string) => {
|
||||||
return vehicles.value.find(item => item.id === itemId)
|
return vehicles.value.find(item => item.id === itemId)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -324,10 +387,18 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
return jobs.value.find(item => item.id === itemId)
|
return jobs.value.find(item => item.id === itemId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getAbsenceRequestById = computed(() => (itemId:string) => {
|
||||||
|
return absenceRequests.value.find(item => item.id === itemId)
|
||||||
|
})
|
||||||
|
|
||||||
const getProfileById = computed(() => (itemId:string) => {
|
const getProfileById = computed(() => (itemId:string) => {
|
||||||
return profiles.value.find(item => item.id === itemId)
|
return profiles.value.find(item => item.id === itemId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getAccountById = computed(() => (accountId:string) => {
|
||||||
|
return accounts.value.find(item => item.id === accountId)
|
||||||
|
})
|
||||||
|
|
||||||
const getProjectById = computed(() => (itemId:string) => {
|
const getProjectById = computed(() => (itemId:string) => {
|
||||||
let project = projects.value.find(project => project.id === itemId)
|
let project = projects.value.find(project => project.id === itemId)
|
||||||
|
|
||||||
@@ -367,6 +438,10 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
bankStatements,
|
bankStatements,
|
||||||
historyItems,
|
historyItems,
|
||||||
numberRanges,
|
numberRanges,
|
||||||
|
notifications,
|
||||||
|
absenceRequests,
|
||||||
|
accounts,
|
||||||
|
taxTypes,
|
||||||
//Functions
|
//Functions
|
||||||
fetchData,
|
fetchData,
|
||||||
clearStore,
|
clearStore,
|
||||||
@@ -393,15 +468,22 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
fetchVendors,
|
fetchVendors,
|
||||||
fetchVendorInvoices,
|
fetchVendorInvoices,
|
||||||
fetchNumberRanges,
|
fetchNumberRanges,
|
||||||
|
fetchNotifications,
|
||||||
fetchDocuments,
|
fetchDocuments,
|
||||||
|
fetchAbsenceRequests,
|
||||||
addHistoryItem,
|
addHistoryItem,
|
||||||
//Getters
|
//Getters
|
||||||
getOpenTasksCount,
|
getOpenTasksCount,
|
||||||
getMovementsBySpace,
|
getMovementsBySpace,
|
||||||
getContactsByCustomerId,
|
getContactsByCustomerId,
|
||||||
|
getContactsByVendorId,
|
||||||
getDocumentsByProjectId,
|
getDocumentsByProjectId,
|
||||||
getTimesByProjectId,
|
getTimesByProjectId,
|
||||||
|
getTasksByProjectId,
|
||||||
|
getMovementsBySpaceId,
|
||||||
|
getStockByProductId,
|
||||||
getHistoryItemsByCustomer,
|
getHistoryItemsByCustomer,
|
||||||
|
getHistoryItemsByVendor,
|
||||||
getEventTypes,
|
getEventTypes,
|
||||||
getTimeTypes,
|
getTimeTypes,
|
||||||
getDocumentTags,
|
getDocumentTags,
|
||||||
@@ -412,13 +494,15 @@ export const useDataStore = defineStore('data', () => {
|
|||||||
getVendorInvoiceById,
|
getVendorInvoiceById,
|
||||||
getContractById,
|
getContractById,
|
||||||
getContactById,
|
getContactById,
|
||||||
getVehiclesById,
|
getVehicleById,
|
||||||
getDocumentById,
|
getDocumentById,
|
||||||
getSpaceById,
|
getSpaceById,
|
||||||
getCustomerById,
|
getCustomerById,
|
||||||
getJobById,
|
getJobById,
|
||||||
|
getAbsenceRequestById,
|
||||||
getProjectById,
|
getProjectById,
|
||||||
getProfileById
|
getProfileById,
|
||||||
|
getAccountById,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user