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()
|
||||
//console.log(user.value)
|
||||
const router = useRouter()
|
||||
@@ -12,11 +14,6 @@ const tenants = (await supabase.from("tenants").select()).data
|
||||
|
||||
const dataStore = useDataStore()
|
||||
|
||||
|
||||
const userProfile = (user.value ? dataStore.getProfileById(user.value.id) : {})
|
||||
//console.log(userProfile)
|
||||
|
||||
|
||||
const isLight = computed({
|
||||
get () {
|
||||
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()
|
||||
|
||||
watch(viewport.breakpoint, (newBreakpoint, oldBreakpoint) => {
|
||||
@@ -56,7 +79,7 @@ const navLinks = [
|
||||
icon: "i-heroicons-truck"
|
||||
},
|
||||
{
|
||||
label: "Kontakte",
|
||||
label: "Ansprechpartner",
|
||||
to: "/contacts",
|
||||
icon: "i-heroicons-user-group"
|
||||
},
|
||||
@@ -131,9 +154,14 @@ const navLinks = [
|
||||
children: [
|
||||
{
|
||||
label: "Zeiterfassung",
|
||||
to: "/timetracking",
|
||||
to: "/employees/timetracking",
|
||||
icon: "i-heroicons-clock"
|
||||
},
|
||||
{
|
||||
label: "Abwesenheiten",
|
||||
to: "/employees/absenceRequests",
|
||||
icon: "i-heroicons-document-text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -141,15 +169,15 @@ const navLinks = [
|
||||
icon: 'i-heroicons-home',
|
||||
children: [
|
||||
{
|
||||
label: "Artikel",
|
||||
to: "/products",
|
||||
icon: "i-heroicons-puzzle-piece"
|
||||
},
|
||||
{
|
||||
label: "Inventar",
|
||||
label: "Steuerung",
|
||||
to: "/inventory",
|
||||
icon: "i-heroicons-square-3-stack-3d"
|
||||
},
|
||||
{
|
||||
label: "Artikelstamm",
|
||||
to: "/products",
|
||||
icon: "i-heroicons-puzzle-piece"
|
||||
},
|
||||
{
|
||||
label: "Lagerplätze",
|
||||
to: "/inventory/spaces",
|
||||
@@ -166,37 +194,7 @@ const navLinks = [
|
||||
|
||||
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'
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
const items = [
|
||||
[{
|
||||
label: user.value ? user.value.email : "",
|
||||
@@ -250,7 +251,7 @@ const items = [
|
||||
label: 'Status',
|
||||
icon: 'i-heroicons-signal'
|
||||
}],*/ [{
|
||||
label: 'Sign out',
|
||||
label: 'Ausloggen',
|
||||
icon: 'i-heroicons-arrow-left-on-rectangle',
|
||||
click: async () => {
|
||||
await supabase.auth.signOut()
|
||||
@@ -279,8 +280,12 @@ const items = [
|
||||
<UNavigationTree :links="navLinks"/>
|
||||
</template>
|
||||
|
||||
<template #right v-if="user">
|
||||
<ClientOnly>
|
||||
<template #right>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <ClientOnly>
|
||||
<UButton
|
||||
:icon="!isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
||||
color="gray"
|
||||
@@ -292,11 +297,128 @@ const items = [
|
||||
<template #fallback>
|
||||
<div class="w-8 h-8" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
||||
</ClientOnly>-->
|
||||
<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
|
||||
: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 }">
|
||||
@@ -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" />
|
||||
</template>
|
||||
</UDropdown>
|
||||
</UDropdown>-->
|
||||
</template>
|
||||
</UHeader>
|
||||
<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
|
||||
}
|
||||
})
|
||||
const dataStore = useDataStore()
|
||||
const user = useSupabaseUser()
|
||||
const supabase = useSupabaseClient()
|
||||
const toast = useToast()
|
||||
const {type, elementId} = props
|
||||
const showAddHistoryItemModal = ref(false)
|
||||
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(() => {
|
||||
|
||||
let items = []
|
||||
|
||||
if(type === "customer") {
|
||||
items = getHistoryItemsByCustomer(elementId)
|
||||
items = dataStore.getHistoryItemsByCustomer(elementId)
|
||||
} else if(type === "vendor") {
|
||||
items = dataStore.getHistoryItemsByVendor(elementId)
|
||||
}
|
||||
|
||||
return items.reverse()
|
||||
@@ -68,7 +54,7 @@ const addHistoryItem = async () => {
|
||||
} else {
|
||||
toast.add({title: "Eintrag erfolgreich erstellt"})
|
||||
showAddHistoryItemModal.value = false
|
||||
await fetchHistoryItems()
|
||||
await dataStore.fetchHistoryItems()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -130,11 +116,11 @@ const renderText = (text) => {
|
||||
:src="colorMode.value === 'light' ? '/spaces_hell.svg' : '/spaces.svg' "
|
||||
/>
|
||||
<UAvatar
|
||||
:alt="profiles.find(profile => profile.id === item.user).fullName"
|
||||
:alt="dataStore.profiles.find(profile => profile.id === item.user).fullName"
|
||||
v-else
|
||||
/>
|
||||
<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>
|
||||
<span v-html="renderText(item.text)"/><br>
|
||||
<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/vue3": "^6.1.10",
|
||||
"@nuxt/content": "^2.9.0",
|
||||
"@nuxt/ui-pro": "^0.5.0",
|
||||
"@nuxt/ui-pro": "^0.6.1",
|
||||
"@nuxtjs/fontaine": "^0.4.1",
|
||||
"@nuxtjs/google-fonts": "^3.1.0",
|
||||
"@nuxtjs/strapi": "^1.9.3",
|
||||
@@ -38,6 +38,7 @@
|
||||
"@zip.js/zip.js": "^2.7.32",
|
||||
"axios": "^1.6.2",
|
||||
"buffer": "^6.0.3",
|
||||
"client-oauth2": "^4.3.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"jsprintmanager": "^6.0.3",
|
||||
"nuxt-editorjs": "^1.0.4",
|
||||
|
||||
@@ -70,17 +70,17 @@ const cancelEditorCreate = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateCustomer = async () => {
|
||||
const updateItem = async () => {
|
||||
const {error} = await supabase
|
||||
.from("contacts")
|
||||
.update(itemInfo.value)
|
||||
.eq('id',itemInfo.value.id)
|
||||
console.log(error)
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
|
||||
if(error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
router.push(`/contacts/show/${currentContact.id}`)
|
||||
toast.add({title: "Kontakt erfolgreich gespeichert"})
|
||||
dataStore.fetchContacts()
|
||||
}
|
||||
@@ -261,7 +261,7 @@ setupPage()
|
||||
<template #footer>
|
||||
<UButton
|
||||
v-if="mode == 'edit'"
|
||||
@click="updateCustomer"
|
||||
@click="updateItem"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
|
||||
@@ -71,17 +71,16 @@ const cancelEditorCreate = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateCustomer = async () => {
|
||||
const updateItem = async () => {
|
||||
const {error} = await supabase
|
||||
.from("contracts")
|
||||
.update(itemInfo.value)
|
||||
.eq('id',itemInfo.value.id)
|
||||
console.log(error)
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
if(error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
router.push(`/contracts/show/${currentContract.id}`)
|
||||
toast.add({title: "Vertrag erfolgreich gespeichert"})
|
||||
dataStore.fetchContracts()
|
||||
}
|
||||
@@ -192,7 +191,7 @@ setupPage()
|
||||
<template #footer>
|
||||
<UButton
|
||||
v-if="mode == 'edit'"
|
||||
@click="updateCustomer"
|
||||
@click="updateItem"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
|
||||
@@ -81,14 +81,12 @@ const updateCustomer = async () => {
|
||||
.from("customers")
|
||||
.update(customerInfo.value)
|
||||
.eq('id',customerInfo.value.id)
|
||||
console.log(error)
|
||||
mode.value = "show"
|
||||
customerInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
infoData: {}
|
||||
|
||||
if(error) {
|
||||
console.log(error)
|
||||
}
|
||||
toast.add({title: "Kunde erfolgreich gespeichert"})
|
||||
router.push(`/customers/show/${currentCustomer.id}`)
|
||||
dataStore.fetchCustomers()
|
||||
}
|
||||
|
||||
@@ -121,7 +119,14 @@ setupPage()
|
||||
/>
|
||||
|
||||
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
|
||||
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"
|
||||
})
|
||||
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const supabase = useSupabaseClient()
|
||||
|
||||
const {spaces, movements, products} = storeToRefs(useDataStore())
|
||||
|
||||
|
||||
const searchinput = ref("")
|
||||
const mode = ref("")
|
||||
|
||||
const router = useRouter()
|
||||
const mode = ref("incoming")
|
||||
const toast = useToast()
|
||||
const showReader = ref(false)
|
||||
|
||||
const inventoryChangeData = ref({
|
||||
productId: "",
|
||||
spaceId: "",
|
||||
quantity: 0
|
||||
quantity: 1
|
||||
})
|
||||
|
||||
const createMovement = async () => {
|
||||
if(mode.value === 'incoming'){
|
||||
//await create('movements', inventoryChangeData.value)
|
||||
|
||||
const {data,error} = await supabase
|
||||
.from("movements")
|
||||
.insert([inventoryChangeData.value])
|
||||
.select()
|
||||
if(mode.value === '' || !checkSpaceId(inventoryChangeData.value.spaceId) || !checkArticle(inventoryChangeData.value.productId)){
|
||||
|
||||
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'){
|
||||
inventoryChangeData.value.quantity *= -1
|
||||
//await create('movements', inventoryChangeData.value)
|
||||
} else if (mode.value === 'outgoing'){
|
||||
inventoryChangeData.value.quantity *= -1
|
||||
|
||||
const {data,error} = await supabase
|
||||
.from("movements")
|
||||
.insert([inventoryChangeData.value])
|
||||
.select()
|
||||
const {error} = await supabase
|
||||
.from("movements")
|
||||
.insert([inventoryChangeData.value])
|
||||
.select()
|
||||
if(error) console.log(error)
|
||||
} else if (mode.value === 'change'){}
|
||||
|
||||
console.log(error)
|
||||
} else if (mode.value === 'change'){}
|
||||
|
||||
inventoryChangeData.value = {
|
||||
productId: 0,
|
||||
spaceId: 0,
|
||||
quantity: 0
|
||||
inventoryChangeData.value = {
|
||||
productId: "",
|
||||
spaceId: "",
|
||||
quantity: 1
|
||||
}
|
||||
|
||||
|
||||
dataStore.fetchMovements()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
defineShortcuts({
|
||||
meta_enter: {
|
||||
usingInput: true,
|
||||
handler: () => {
|
||||
createMovement()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
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) {
|
||||
return spaces.value.filter(space => Number(space.spaceNumber) === Number(spaceId)).length > 0;
|
||||
return dataStore.spaces.filter(space => space.id === spaceId).length > 0;
|
||||
}
|
||||
|
||||
function changeFocusToSpaceId() {
|
||||
@@ -73,42 +84,66 @@ function changeFocusToQuantity() {
|
||||
<div id="main">
|
||||
|
||||
<div class="my-3">
|
||||
<UButton @click="mode = 'incoming'" class="ml-3" >Wareneingang</UButton>
|
||||
<UButton @click="mode = 'outgoing'" class="ml-1">Warenausgang</UButton>
|
||||
<UButton @click="mode = 'change'" class="ml-1" disabled>Umlagern</UButton>
|
||||
<UButton
|
||||
@click="mode = 'incoming'"
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<UFormGroup
|
||||
label="Artikel:"
|
||||
class="mt-3 w-80"
|
||||
>
|
||||
<UInput
|
||||
<USelectMenu
|
||||
:options="dataStore.products"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
variant="outline"
|
||||
searchable
|
||||
:search-attributes="['name','ean', 'barcode']"
|
||||
:color="checkArticle(inventoryChangeData.productId) ? 'primary' : 'rose'"
|
||||
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
|
||||
label="Lagerplatz:"
|
||||
class="mt-3 w-80"
|
||||
><!--.map(space => {return {id: space.id, name: space.spaceNumber}}-->
|
||||
<!-- <USelectMenu
|
||||
:options="spaces"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="dataStore.spaces"
|
||||
searchable
|
||||
option-attribute="spaceNumber"
|
||||
option-attribute="description"
|
||||
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
|
||||
v-model="inventoryChangeData.spaceId"
|
||||
v-on:select="changeFocusToQuantity"
|
||||
value-attribute="id"
|
||||
/>-->
|
||||
<UInput
|
||||
v-model="inventoryChangeData.spaceId"
|
||||
:color="checkSpaceId(inventoryChangeData.spaceId) ? 'primary' : 'rose'"
|
||||
id="spaceIdInput"
|
||||
v-on:keyup.enter="changeFocusToQuantity"
|
||||
/>
|
||||
>
|
||||
<template #label>
|
||||
{{dataStore.spaces.find(space => space.id === inventoryChangeData.spaceId) ? dataStore.spaces.find(space => space.id === inventoryChangeData.spaceId).description : "Kein Lagerplatz ausgewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
|
||||
@@ -5,7 +5,7 @@ definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
//
|
||||
const dataStore = useDataStore()
|
||||
const supabase = useSupabaseClient()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -13,9 +13,6 @@ const toast = useToast()
|
||||
const id = ref(route.params.id ? route.params.id : null )
|
||||
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)
|
||||
@@ -26,7 +23,7 @@ const mode = ref(route.params.mode || "show")
|
||||
const itemInfo = ref({
|
||||
spaceNumber: ""
|
||||
})
|
||||
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz"]
|
||||
const spaceTypes = ["Regalplatz", "Kiste", "Palettenplatz", "Sonstiges"]
|
||||
const spaceProducts = ref([])
|
||||
const spaceMovements = ref([])
|
||||
|
||||
@@ -34,12 +31,12 @@ const spaceMovements = ref([])
|
||||
const setupPage = async () => {
|
||||
console.log("Called Setup")
|
||||
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 = []
|
||||
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])
|
||||
mode.value = "show"
|
||||
toast.add({title: "Lagerplatz erfolgreich erstellt"})
|
||||
await fetchSpaces()
|
||||
await dataStore.fetchSpaces()
|
||||
router.push(`/inventory/spaces/show/${data[0].id}`)
|
||||
|
||||
|
||||
@@ -93,7 +90,7 @@ const updateItem = async () => {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {}
|
||||
toast.add({title: "Lagerplatz erfolgreich gespeichert"})
|
||||
fetchSpaces()
|
||||
dataStore.fetchSpaces()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +108,7 @@ const printSpaceLabel = async () => {
|
||||
|
||||
|
||||
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)
|
||||
.catch(console.log)
|
||||
}
|
||||
@@ -149,7 +146,7 @@ setupPage()
|
||||
<tr v-for="product in spaceProducts">
|
||||
<td>{{product.name}}</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>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -72,12 +72,13 @@ const updateItem = async () => {
|
||||
.from("jobs")
|
||||
.update(itemInfo.value)
|
||||
.eq('id',itemInfo.value.id)
|
||||
console.log(error)
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
title: ""
|
||||
|
||||
if(error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
|
||||
router.push(`/jobs/show/${currentItem.id}`)
|
||||
toast.add({title: "Job erfolgreich gespeichert"})
|
||||
dataStore.fetchJobs()
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
:columns="columns"
|
||||
@select="selectJob"
|
||||
: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>
|
||||
</template>
|
||||
|
||||
@@ -4,6 +4,7 @@ import resourceTimelinePlugin from '@fullcalendar/resource-timeline'
|
||||
import deLocale from '@fullcalendar/core/locales/de'
|
||||
import interactionPlugin from '@fullcalendar/interaction'
|
||||
import listPlugin from '@fullcalendar/list';
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
const viewport = useViewport()
|
||||
@@ -19,6 +20,7 @@ const eventTypes = dataStore.getEventTypes
|
||||
const events = dataStore.getEvents
|
||||
|
||||
const openNewEventModal = ref(false)
|
||||
const showEventModal = ref(false)
|
||||
const newEventData = ref({
|
||||
resourceId: "",
|
||||
resourceType: "",
|
||||
@@ -27,6 +29,9 @@ const newEventData = ref({
|
||||
start: "",
|
||||
end: null
|
||||
})
|
||||
|
||||
const selectedEvent = ref({})
|
||||
|
||||
const createEvent = async () => {
|
||||
const {data,error} = await supabase
|
||||
.from("events")
|
||||
@@ -76,6 +81,9 @@ const calendarOptionsTimeline = reactive({
|
||||
openNewEventModal.value = true
|
||||
},
|
||||
eventClick: function (info){
|
||||
selectedEvent.value = info.event
|
||||
showEventModal.value = true
|
||||
|
||||
console.log(info)
|
||||
},
|
||||
resourceGroupField: "type",
|
||||
@@ -153,7 +161,28 @@ const calendarOptionsTimeline = reactive({
|
||||
|
||||
</UCard>
|
||||
</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')">
|
||||
<FullCalendar
|
||||
:options="calendarOptionsList"
|
||||
|
||||
@@ -107,10 +107,12 @@ setupPage()
|
||||
{{tag}}
|
||||
</UBadge>
|
||||
|
||||
|
||||
|
||||
<UDivider
|
||||
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 : ""}}
|
||||
|
||||
<DevOnly>
|
||||
@@ -193,6 +195,26 @@ setupPage()
|
||||
v-model="itemInfo.ean"
|
||||
/>
|
||||
</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>
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
@select="selectItem"
|
||||
: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}">
|
||||
<UBadge
|
||||
v-if="row.tags.length > 0"
|
||||
@@ -48,24 +54,33 @@ const router = useRouter()
|
||||
|
||||
const itemColumns = [
|
||||
{
|
||||
key: "id",
|
||||
label: "Id"
|
||||
key: "stock",
|
||||
label: "Bestand"
|
||||
},
|
||||
{
|
||||
key: "name",
|
||||
label: "Name"
|
||||
label: "Name",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "manufacturer",
|
||||
label: "Hersteller",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "unit",
|
||||
label: "Einheit"
|
||||
label: "Einheit",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "purchasePrice",
|
||||
label: "Einkaufspreis",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "tags",
|
||||
label: "Tags"
|
||||
label: "Tags",
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -8,10 +8,16 @@ import * as dayjs from 'dayjs'
|
||||
const supabase = useSupabaseClient()
|
||||
const user = useSupabaseUser()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const dataStore = useDataStore()
|
||||
|
||||
|
||||
const {getProjectById, getFormSubmitsWithLabelProp, getTimesByProjectId, getDocumentTags, getDocumentsByProjectId, fetchDocuments} = useDataStore()
|
||||
const {forms, formSubmits, times, profiles,documents} = storeToRefs(useDataStore())
|
||||
|
||||
|
||||
|
||||
|
||||
fetchDocuments()
|
||||
const currentProject = getProjectById(Number(route.params.id))
|
||||
|
||||
@@ -74,6 +80,9 @@ const tabItems = [
|
||||
{
|
||||
key: "phases",
|
||||
label: "Phasen"
|
||||
},{
|
||||
key: "tasks",
|
||||
label: "Aufgaben"
|
||||
},{
|
||||
key: "forms",
|
||||
label: "Formulare"
|
||||
@@ -235,6 +244,17 @@ const phaseInfo = ref({
|
||||
</a>
|
||||
</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 v-else-if="item.key === 'forms'" class="space-y-3">
|
||||
<UButton
|
||||
|
||||
@@ -31,6 +31,9 @@ const resources = {
|
||||
vendors: {
|
||||
label: "Lieferanten"
|
||||
},
|
||||
products: {
|
||||
label: "Artikel"
|
||||
},
|
||||
spaces: {
|
||||
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"
|
||||
value-attribute="id"
|
||||
option-attribute="fullName"
|
||||
v-model="createTaskData.users"
|
||||
multiple
|
||||
v-model="createTaskData.user"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
@@ -87,6 +86,19 @@
|
||||
v-model="taskData.categorie"
|
||||
/>
|
||||
</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
|
||||
label="Benutzer ändern:"
|
||||
@@ -134,6 +146,9 @@
|
||||
<template #user-data="{row}">
|
||||
{{dataStore.profiles.find(profile => profile.id === row.user ) ? dataStore.profiles.find(profile => profile.id === row.user ).fullName : row.user}}
|
||||
</template>
|
||||
<template #project-data="{row}">
|
||||
{{dataStore.projects.find(item => item.id === row.project) ? dataStore.projects.find(item => item.id === row.project).name : "" }}
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
|
||||
@@ -179,6 +194,11 @@ const taskColumns = [
|
||||
key:"categorie",
|
||||
label: "Kategorie:",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key:"project",
|
||||
label: "Projekt:",
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ const cancelEditorCreate = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateCustomer = async () => {
|
||||
const updateItem = async () => {
|
||||
const {error} = await supabase
|
||||
.from("vehicles")
|
||||
.update(itemInfo.value)
|
||||
@@ -80,13 +80,8 @@ const updateCustomer = async () => {
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
licensePlate: "",
|
||||
type: ""
|
||||
}
|
||||
|
||||
router.push(`/vehicles/show/${currentItem.id}`)
|
||||
toast.add({title: "Fahrzeug erfolgreich gespeichert"})
|
||||
dataStore.fetchVehicles()
|
||||
}
|
||||
@@ -187,7 +182,7 @@ setupPage()
|
||||
<template #footer>
|
||||
<UButton
|
||||
v-if="mode == 'edit'"
|
||||
@click="updateCustomer"
|
||||
@click="updateItem"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
|
||||
@@ -69,6 +69,97 @@
|
||||
/>
|
||||
</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>
|
||||
<UInput
|
||||
type="number"
|
||||
@@ -79,7 +170,7 @@
|
||||
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||
</template>
|
||||
</UInput>
|
||||
</UFormGroup>
|
||||
</UFormGroup>-->
|
||||
|
||||
<InputGroup class="mt-3">
|
||||
<UButton
|
||||
@@ -117,6 +208,48 @@ let currentDocument = ref(null)
|
||||
//Working
|
||||
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
|
||||
const setupPage = async () => {
|
||||
@@ -139,9 +272,24 @@ const itemInfo = ref({
|
||||
dueDate: null,
|
||||
paymentType: "",
|
||||
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 {error} = await supabase
|
||||
.from("vendorInvoices")
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
{{row.dueDate ? dayjs(row.dueDate).format("DD.MM.YY") : ''}}
|
||||
</template>
|
||||
<template #amount-data="{row}">
|
||||
{{row.amount ? row.amount.toFixed(2) + ' €' : ''}}
|
||||
{{getRowAmount(row) === 0 ? '' : `${getRowAmount(row)} €`}}
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
@@ -101,6 +101,17 @@ const selectItem = (item) => {
|
||||
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('')
|
||||
|
||||
|
||||
96
spaces/pages/vendors/[mode]/[[id]].vue
vendored
96
spaces/pages/vendors/[mode]/[[id]].vue
vendored
@@ -1,4 +1,6 @@
|
||||
<script setup>
|
||||
import HistoryDisplay from "~/components/HistoryDisplay.vue";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
@@ -9,6 +11,7 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const id = ref(route.params.id ? route.params.id : null )
|
||||
const numberRange = useNumberRange("vendors")
|
||||
|
||||
let currentItem = null
|
||||
|
||||
@@ -16,7 +19,9 @@ let currentItem = null
|
||||
|
||||
//Working
|
||||
const mode = ref(route.params.mode || "show")
|
||||
const itemInfo = ref({})
|
||||
const itemInfo = ref({
|
||||
infoData: {}
|
||||
})
|
||||
|
||||
//Functions
|
||||
const setupPage = () => {
|
||||
@@ -31,6 +36,9 @@ const setupPage = () => {
|
||||
}
|
||||
|
||||
const createItem = async () => {
|
||||
|
||||
if(!itemInfo.value.vendorNumber) itemInfo.value.vendorNumber = await numberRange.useNextNumber()
|
||||
|
||||
const {data,error} = await supabase
|
||||
.from("vendors")
|
||||
.insert([itemInfo.value])
|
||||
@@ -66,9 +74,8 @@ const updateItem = async () => {
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {}
|
||||
toast.add({title: "Lieferant erfolgreich gespeichert"})
|
||||
router.push(`/vendors/show/${currentItem.id}`)
|
||||
dataStore.fetchVendors()
|
||||
}
|
||||
|
||||
@@ -87,9 +94,29 @@ setupPage()
|
||||
{{currentItem.name}}
|
||||
</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>
|
||||
<UButton
|
||||
@@ -110,8 +137,8 @@ setupPage()
|
||||
|
||||
|
||||
</UCard>
|
||||
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
||||
<template #header>
|
||||
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
|
||||
<template #header v-if="mode === 'edit'">
|
||||
{{itemInfo.name}}
|
||||
</template>
|
||||
|
||||
@@ -127,6 +154,57 @@ setupPage()
|
||||
>
|
||||
<UInput
|
||||
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>
|
||||
|
||||
@@ -154,6 +232,12 @@ setupPage()
|
||||
</template>
|
||||
|
||||
</UCard>
|
||||
|
||||
<HistoryDisplay
|
||||
type="vendor"
|
||||
v-if="currentItem"
|
||||
:element-id="currentItem.id"
|
||||
/>
|
||||
</div>
|
||||
</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 {createClient} from '@supabase/supabase-js'
|
||||
import * as dayJs from "dayjs"
|
||||
|
||||
//const supabase = createClient('https://uwppvcxflrcsibuzsbil.supabase.co','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDA5MzgxOTQsImV4cCI6MjAxNjUxNDE5NH0.CkxYSQH0uLfwx9GVUlO6AYMU2FMLAxGMrwEKvyPv7Oo')
|
||||
|
||||
@@ -47,6 +46,10 @@ export const useDataStore = defineStore('data', () => {
|
||||
const bankStatements = ref([])
|
||||
const historyItems = ref([])
|
||||
const numberRanges = ref([])
|
||||
const notifications = ref([])
|
||||
const absenceRequests = ref([])
|
||||
const accounts = ref([])
|
||||
const taxTypes = ref([])
|
||||
|
||||
async function fetchData () {
|
||||
fetchDocuments()
|
||||
@@ -74,6 +77,10 @@ export const useDataStore = defineStore('data', () => {
|
||||
await fetchBankStatements()
|
||||
await fetchHistoryItems()
|
||||
await fetchNumberRanges()
|
||||
await fetchNotifications()
|
||||
await fetchAbsenceRequests()
|
||||
await fetchAccounts()
|
||||
await fetchTaxTypes()
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
@@ -104,6 +111,10 @@ export const useDataStore = defineStore('data', () => {
|
||||
bankStatements.value= []
|
||||
historyItems.value = []
|
||||
numberRanges.value = []
|
||||
notifications.value = []
|
||||
absenceRequests.value = []
|
||||
accounts.value = []
|
||||
taxTypes.value = []
|
||||
}
|
||||
|
||||
async function fetchOwnTenant () {
|
||||
@@ -117,7 +128,7 @@ export const useDataStore = defineStore('data', () => {
|
||||
bankAccounts.value = (await supabase.from("bankAccounts").select()).data
|
||||
}
|
||||
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 () {
|
||||
events.value = (await supabase.from("events").select()).data
|
||||
@@ -176,6 +187,18 @@ export const useDataStore = defineStore('data', () => {
|
||||
async function fetchNumberRanges () {
|
||||
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 () {
|
||||
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)
|
||||
})
|
||||
|
||||
const getContactsByVendorId = computed(() => (vendorId:string) => {
|
||||
return contacts.value.filter(item => item.vendor === vendorId)
|
||||
})
|
||||
|
||||
const getDocumentsByProjectId = computed(() => (projectId:string) => {
|
||||
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)
|
||||
})
|
||||
|
||||
const getTasksByProjectId = computed(() => (projectId:string) => {
|
||||
return tasks.value.filter(item => item.project === projectId)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const getHistoryItemsByCustomer = computed(() => (customerId:string) => {
|
||||
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(() => {
|
||||
return ownTenant.value.calendarConfig.eventTypes
|
||||
})
|
||||
@@ -279,6 +332,16 @@ export const useDataStore = defineStore('data', () => {
|
||||
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)
|
||||
})
|
||||
|
||||
const getVehiclesById = computed(() => (itemId:string) => {
|
||||
const getVehicleById = computed(() => (itemId:string) => {
|
||||
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)
|
||||
})
|
||||
|
||||
const getAbsenceRequestById = computed(() => (itemId:string) => {
|
||||
return absenceRequests.value.find(item => item.id === itemId)
|
||||
})
|
||||
|
||||
const getProfileById = computed(() => (itemId:string) => {
|
||||
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) => {
|
||||
let project = projects.value.find(project => project.id === itemId)
|
||||
|
||||
@@ -367,6 +438,10 @@ export const useDataStore = defineStore('data', () => {
|
||||
bankStatements,
|
||||
historyItems,
|
||||
numberRanges,
|
||||
notifications,
|
||||
absenceRequests,
|
||||
accounts,
|
||||
taxTypes,
|
||||
//Functions
|
||||
fetchData,
|
||||
clearStore,
|
||||
@@ -393,15 +468,22 @@ export const useDataStore = defineStore('data', () => {
|
||||
fetchVendors,
|
||||
fetchVendorInvoices,
|
||||
fetchNumberRanges,
|
||||
fetchNotifications,
|
||||
fetchDocuments,
|
||||
fetchAbsenceRequests,
|
||||
addHistoryItem,
|
||||
//Getters
|
||||
getOpenTasksCount,
|
||||
getMovementsBySpace,
|
||||
getContactsByCustomerId,
|
||||
getContactsByVendorId,
|
||||
getDocumentsByProjectId,
|
||||
getTimesByProjectId,
|
||||
getTasksByProjectId,
|
||||
getMovementsBySpaceId,
|
||||
getStockByProductId,
|
||||
getHistoryItemsByCustomer,
|
||||
getHistoryItemsByVendor,
|
||||
getEventTypes,
|
||||
getTimeTypes,
|
||||
getDocumentTags,
|
||||
@@ -412,13 +494,15 @@ export const useDataStore = defineStore('data', () => {
|
||||
getVendorInvoiceById,
|
||||
getContractById,
|
||||
getContactById,
|
||||
getVehiclesById,
|
||||
getVehicleById,
|
||||
getDocumentById,
|
||||
getSpaceById,
|
||||
getCustomerById,
|
||||
getJobById,
|
||||
getAbsenceRequestById,
|
||||
getProjectById,
|
||||
getProfileById
|
||||
getProfileById,
|
||||
getAccountById,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user