Added Phases to Projects

This commit is contained in:
2024-02-15 22:49:09 +01:00
parent 2fc45b3ea0
commit 1a0b7288df
37 changed files with 2342 additions and 1483 deletions

View File

@@ -12,4 +12,4 @@ ENV NUXT_PORT=3000
EXPOSE 3000
ENTRYPOINT ["node", ".output/server/index.mjs"]
ENTRYPOINT ["node", ".output/server/index.js"]

View File

@@ -12,7 +12,6 @@ const viewport = useViewport()
console.log('Breakpoint updated:', oldBreakpoint, '->', newBreakpoint)
})*/
dataStore.initializeData()
useHead({
meta: [
@@ -52,10 +51,7 @@ useSeoMeta({
width: auto;
}
#contentContainer {
width: 95vw;
height: 85vh;
}
.documentList {
display: flex;
@@ -103,4 +99,18 @@ useSeoMeta({
a:hover {
color: #69c350
}
.table > div {
width: 78vw;
height: 90vh;
overflow: scroll !important;
-ms-overflow-style: none; /*!* IE and Edge *!*/
scrollbar-width: none; /*!* Firefox *!*/
}
.table > div::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -99,7 +99,16 @@ const archiveDocument = () => {
:data="documentData.url"
class="previewEmbed"
type="application/pdf"
v-if="!documentData.tags.includes('Bild')"
/>
<img
v-else
alt=""
:src="documentData.url"
/>
<UButton
@click="openDocument"
class="mt-3"
@@ -141,6 +150,13 @@ const archiveDocument = () => {
class="h-full w-full"
:data="documentData.url"
type="application/pdf"
v-if="!documentData.tags.includes('Bild')"
/>
<img
class="h-full w-full"
:src="documentData.url"
alt=""
v-else
/>
</UContainer>
@@ -159,7 +175,11 @@ const archiveDocument = () => {
Eingangsrechnung erstellen
</UButton>
</UButtonGroup>
<br>
<a
:href="documentData.url"
target="_blank"
>In neuen Tab anzeigen</a>
<UFormGroup
label="Tags ändern:"

View File

@@ -32,7 +32,7 @@ const uploadFiles = async () => {
let fileData = fileUploadFormData.value
fileData[type] = elementId
await dataStore.uploadFiles(fileData, document.getElementById("fileUploadInput").files)
await dataStore.uploadFiles(fileData, document.getElementById("fileUploadInput").files,true)
uploadModalOpen.value = false;
uploadInProgress.value = false;
@@ -40,7 +40,7 @@ const uploadFiles = async () => {
</script>
<template>
<UModal
<USlideover
v-model="uploadModalOpen"
>
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
@@ -66,6 +66,7 @@ const uploadFiles = async () => {
<UInput
type="file"
id="fileUploadInput"
multiple
/>
</UFormGroup>
<UFormGroup
@@ -78,7 +79,12 @@ const uploadFiles = async () => {
searchable-placeholder="Suchen..."
:options="tags"
v-model="fileUploadFormData.tags"
/>
>
<template #label>
<span v-if="fileUploadFormData.tags.length > 0">{{fileUploadFormData.tags.join(", ")}}</span>
<span v-else>Keine Tags ausgewählt</span>
</template>
</USelectMenu>
</UFormGroup>
<template #footer>
@@ -89,10 +95,11 @@ const uploadFiles = async () => {
</template>
</UCard>
</UModal>
</USlideover>
<UButton
@click="openModal"
icon="i-heroicons-arrow-up-tray"
>
Hochladen
</UButton>

View File

@@ -10,6 +10,7 @@ const props = defineProps({
type: String
}
})
const { metaSymbol } = useShortcuts()
const dataStore = useDataStore()
const user = useSupabaseUser()
const supabase = useSupabaseClient()
@@ -34,6 +35,14 @@ const historyItems = computed(() => {
items = dataStore.historyItems.filter(i => i.incomingInvoice === elementId)
} else if(type === "document") {
items = dataStore.historyItems.filter(i => i.document === elementId)
} else if(type === "contact") {
items = dataStore.historyItems.filter(i => i.contact === elementId)
} else if(type === "contract") {
items = dataStore.historyItems.filter(i => i.contract === elementId)
} else if(type === "inventoryitem") {
items = dataStore.historyItems.filter(i => i.inventoryitem === elementId)
} else if(type === "product") {
items = dataStore.historyItems.filter(i => i.product === elementId)
}
return items
@@ -59,6 +68,14 @@ const addHistoryItem = async () => {
addHistoryItemData.value.incomingInvoice = elementId
} else if(type === "document") {
addHistoryItemData.value.document = elementId
} else if(type === "contact") {
addHistoryItemData.value.contact = elementId
} else if(type === "contract") {
addHistoryItemData.value.contract = elementId
} else if(type === "inventoryitem") {
addHistoryItemData.value.inventoryitem = elementId
} else if(type === "product") {
addHistoryItemData.value.product = elementId
}
@@ -102,56 +119,55 @@ const renderText = (text) => {
>
<UTextarea
v-model="addHistoryItemData.text"
@keyup.meta.enter="addHistoryItem"
/>
<!-- <template #help>
<UKbd>{{metaSymbol}}</UKbd> <UKbd>Enter</UKbd> Speichern
</template>-->
</UFormGroup>
<template #footer>
<UButton @click="addHistoryItem">Hinzufügen</UButton>
<UButton @click="addHistoryItem">Speichern</UButton>
</template>
</UCard>
</UModal>
<UCard class="mt-5">
<template #header>
<InputGroup>
<UButton
@click="showAddHistoryItemModal = true"
>
+ Eintrag
</UButton>
</InputGroup>
</template>
<div
v-if="historyItems.length > 0"
v-for="(item,index) in historyItems.slice().reverse()
"
<Toolbar>
<UButton
@click="showAddHistoryItemModal = true"
>
<UDivider
class="my-3"
v-if="index !== 0"
+ Eintrag
</UButton>
</Toolbar>
<div
v-if="historyItems.length > 0"
v-for="(item,index) in historyItems.slice().reverse()"
>
<UDivider
class="my-3"
v-if="index !== 0"
/>
<div class="flex items-center gap-3">
<UAvatar
v-if="!item.user"
:src="colorMode.value === 'light' ? '/spaces_hell.svg' : '/spaces.svg' "
/>
<div class="flex items-center gap-3">
<UAvatar
v-if="!item.user"
:src="colorMode.value === 'light' ? '/spaces_hell.svg' : '/spaces.svg' "
/>
<UAvatar
:alt="dataStore.profiles.find(profile => profile.id === item.user).fullName"
v-else
/>
<div>
<h3 v-if="item.user">{{dataStore.getProfileById(item.user) ? dataStore.getProfileById(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>
</div>
<UAvatar
:alt="dataStore.profiles.find(profile => profile.id === item.user).fullName"
v-else
/>
<div>
<h3 v-if="item.user">{{dataStore.getProfileById(item.user) ? dataStore.getProfileById(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>
</div>
</div>
</UCard>
</div>
</template>
<style scoped>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
</script>
<template>
<InputGroup>
<slot/>
</InputGroup>
<UDivider class="my-3"/>
</template>
<style scoped>
</style>

View File

@@ -6,13 +6,15 @@ const colorMode = useColorMode()
const userProfile = dataStore.getOwnProfile
const supabase = useSupabaseClient()
const router = useRouter()
const route = useRoute()
dataStore.initializeData((await supabase.auth.getUser()).data.user.id)
const isLight = computed({
get () {
get() {
return colorMode.value !== 'dark'
},
set () {
set() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
@@ -52,12 +54,12 @@ const navLinks = [
label: "Belege",
to: "/receipts",
icon: "i-heroicons-document-text"
},/*
},
{
label: "Bank",
to: "/banking",
icon: "i-heroicons-currency-euro"
},*/
},
]
},
{
@@ -105,6 +107,11 @@ const navLinks = [
to: "/documents",
icon: "i-heroicons-document"
},
{
label: "E-Mail",
to: "/email",
icon: "i-heroicons-envelope"
},
]
},/*
{
@@ -122,7 +129,7 @@ const navLinks = [
icon: "i-heroicons-clock"
},
{
label: "Stechuhr",
label: "Anwesenheiten",
to: "/workingtimes",
icon: "i-heroicons-clock"
},
@@ -156,6 +163,10 @@ const navLinks = [
label: "Fahrzeuge",
to: "/vehicles",
icon: "i-heroicons-truck"
}, {
label: "Inventar",
to: "/inventoryitems",
icon: "i-heroicons-puzzle-piece"
},
]
},
@@ -173,6 +184,11 @@ const userMenuItems = ref([
icon: "i-heroicons-cog-8-tooth",
to: "/settings/numberRanges"
},
{
label: "Einstellungen",
icon: "i-heroicons-cog-8-tooth",
to: "/settings"
},
{
label: 'Benutzer',
icon: 'i-heroicons-user-group',
@@ -180,127 +196,255 @@ const userMenuItems = ref([
}
])
const links = [[{
label: 'Profil',
avatar: {
alt: userProfile ? userProfile.fullName : "XY"
}
},{
label: "Dashboard",
to: "/",
icon: "i-heroicons-home"
}], [{
label: "Aufgaben",
to: "/tasks",
icon: "i-heroicons-rectangle-stack"
},
{
label: "Plantafel",
to: "/calendar/timeline",
icon: "i-heroicons-calendar-days"
},
{
label: "Kalender",
to: "/calendar/grid",
icon: "i-heroicons-calendar-days"
},
{
label: "Dokumente",
to: "/documents",
icon: "i-heroicons-document"
},
{
label: "E-Mail",
to: "/email",
icon: "i-heroicons-envelope"
}],[{
label: "Kunden",
to: "/customers",
icon: "i-heroicons-user-group"
},
{
label: "Lieferanten",
to: "/vendors",
icon: "i-heroicons-truck"
},
{
label: "Ansprechpartner",
to: "/contacts",
icon: "i-heroicons-user-group"
}], [{
label: "Belege",
to: "/receipts",
icon: "i-heroicons-document-text"
},
{
label: "Bank",
to: "/banking",
icon: "i-heroicons-currency-euro"
}], [{
label: "Projekte",
to: "/projects",
icon: "i-heroicons-clipboard-document-check"
},
{
label: "Verträge",
to: "/contracts",
icon: "i-heroicons-clipboard-document"
},
{
label: "Objekte",
to: "/plants",
icon: "i-heroicons-clipboard-document"
}],[{
label: "Zeiterfassung",
to: "/employees/timetracking",
icon: "i-heroicons-clock"
},
{
label: "Anwesenheiten",
to: "/workingtimes",
icon: "i-heroicons-clock"
},
{
label: "Abwesenheiten",
to: "/absenceRequests",
icon: "i-heroicons-document-text"
}],[{
label: "Steuerung",
to: "/inventory",
icon: "i-heroicons-square-3-stack-3d"
},
{
label: "Artikelstamm",
to: "/products",
icon: "i-heroicons-puzzle-piece"
},
{
label: "Lagerplätze",
to: "/spaces",
icon: "i-heroicons-square-3-stack-3d"
},
{
label: "Fahrzeuge",
to: "/vehicles",
icon: "i-heroicons-truck"
}, {
label: "Inventar",
to: "/inventoryitems",
icon: "i-heroicons-puzzle-piece"
},]
]
</script>
<template>
<div v-if="dataStore.loaded">
<UHeader :links="navLinks" :to="null">
<template #logo>
<div id="logo">
<img
:src="!isLight ? '/spaces.svg' : '/spaces_hell.svg'"
alt="Logo"
/>
</div>
<div v-if="dataStore.loaded" class="flex justify-center flex-row">
<!-- <UHeader :links="navLinks" :to="null">
<template #logo>
<div id="logo">
<img
:src="!isLight ? '/spaces.svg' : '/spaces_hell.svg'"
alt="Logo"
/>
</div>
</template>
</template>
<template #panel>
<UNavigationTree :links="navLinks"/>
</template>
<template #panel>
<UNavigationTree :links="navLinks"/>
</template>
<template #right>
<template #right>
<GlobalSearch/>
<GlobalSearch/>
<UButton
@click="showUserMenu = true"
variant="ghost"
color="gray"
>
<UAvatar
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
icon="i-heroicons-user-20-solid"
:chip-color="dataStore.notifications.filter(item => !item.read).length > 0 ? 'primary' : null"
<UButton
@click="showUserMenu = true"
variant="ghost"
color="gray"
>
<UAvatar
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
icon="i-heroicons-user-20-solid"
:chip-color="dataStore.notifications.filter(item => !item.read).length > 0 ? 'primary' : null"
/>
</UButton>
/>
</UButton>
<USlideover
v-model="showUserMenu"
>
<UCard
class="h-full"
>
<USlideover
v-model="showUserMenu"
>
<UCard
class="h-full"
>
<UDivider
class="my-3"
label="Tenant"
/>
<div v-if="dataStore.getOwnProfile.tenants.length > 1">
<UDivider
class="my-3"
label="Tenant"
/>
<USelectMenu
:options="dataStore.getOwnProfile.tenants"
option-attribute="name"
value-attribute="id"
v-model="dataStore.currentTenant"
@change="dataStore.changeTenant()"
/>
<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()
}"
/>-->
<USelectMenu
:options="dataStore.getOwnProfile.tenants"
option-attribute="name"
value-attribute="id"
v-model="dataStore.currentTenant"
@change="dataStore.changeTenant()"
/>
</div>
<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"
<UDivider
class="my-3"
label="Menü"
/>
<UButton
color="rose"
variant="outline"
@click="async () => {
showUserMenu = false
await supabase.auth.signOut()
await dataStore.clearStore()
await router.push('/login')
}"
>
Ausloggen
</UButton>
</InputGroup>
<UVerticalNavigation
:links="userMenuItems"
/>
</template>
&lt;!&ndash; <UDivider
class="my-3"
label="Benachrichtigungen"
/>
</UCard>
</USlideover>
<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()
}"
/>&ndash;&gt;
<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>
</template>
</UHeader>
<UDivider />-->
<div class="ml-2 mt-3" id="menuLeft">
<UVerticalNavigation
:links="links"
>
<template #avatar="{link}">
<UAvatar
v-if="link.avatar"
v-bind="link.avatar"
/>
</template>
</UVerticalNavigation>
</div>
</template>
</UHeader>
<UDivider />
<div class="m-3" id="contentContainer">
<slot/>
<slot id="content"/>
</div>
</div>
<div
@@ -317,5 +461,28 @@ const userMenuItems = ref([
</template>
<style scoped>
#menuLeft {
height: 95vh;
width: 20vw;
overflow-y: scroll;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
#menuLeft::-webkit-scrollbar {
display: none;
}
#contentContainer {
width: 77vw;
height: 95vh;
}
/* -ms-overflow-style: none; !* IE and Edge *!
scrollbar-width: none; !* Firefox *!
}
#contentContainer::-webkit-scrollbar {
display: none;
}*/
</style>

View File

@@ -43,7 +43,7 @@
"@vicons/ionicons5": "^0.12.0",
"@vuepic/vue-datepicker": "^7.4.0",
"@zip.js/zip.js": "^2.7.32",
"axios": "^1.6.2",
"axios": "^1.6.7",
"base64-arraybuffer": "^1.0.2",
"buffer": "^6.0.3",
"client-oauth2": "^4.3.3",

View File

@@ -120,14 +120,6 @@ setupPage()
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
<!-- TODO: Kunde archivieren -->
</template>

View File

@@ -88,6 +88,7 @@ const calendarOptionsGrid = reactive({
nowIndicator: true,
height: "80vh",
selectable: true,
weekNumbers: true,
select: function(info) {
console.log(info)
/*newEventData.value.resourceId = info.resource.id

View File

@@ -1,4 +1,6 @@
<script setup>
import HistoryDisplay from "~/components/HistoryDisplay.vue";
definePageMeta({
middleware: "auth"
})
@@ -12,7 +14,7 @@ const id = ref(route.params.id ? route.params.id : null )
//Store
const dataStore = useDataStore()
let currentContact = null
let currentItem = null
@@ -25,10 +27,10 @@ const itemInfo = ref({
//Functions
const setupPage = () => {
if(mode.value === "show" || mode.value === "edit"){
currentContact = dataStore.getContactById(Number(useRoute().params.id))
currentItem = dataStore.getContactById(Number(useRoute().params.id))
}
if(mode.value === "edit") itemInfo.value = currentContact
if(mode.value === "edit") itemInfo.value = currentItem
if(mode.value === "create") {
let query = route.query
@@ -41,213 +43,199 @@ const setupPage = () => {
}
const editCustomer = async () => {
router.push(`/contacts/edit/${currentContact.id}`)
router.push(`/contacts/edit/${currentItem.id}`)
setupPage()
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
name: "",
if(currentItem.value) {
router.push(`/contacts/show/${currentItem.value.id}`)
} else {
router.push(`/contacts`)
}
}
setupPage()
</script>
<template>
<div>
<UCard v-if="currentContact && mode == 'show'" >
<template #header>
<UBadge
v-if="currentContact.active"
>
Kontakt aktiv
</UBadge>
<UBadge
v-else
color="red"
>
Kontakt inaktiv
</UBadge>
{{currentContact.fullName}}
</template>
<h1
class="mb-3 font-bold text-2xl truncate"
v-if="currentItem"
>Ansprechpartner: {{currentItem.fullName}}</h1>
<UTabs
:items="[{label: 'Informationen'}, {label: 'Logbuch'}]"
v-if="currentItem && mode == 'show'"
>
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
</Toolbar>
<InputGroup class="mb-3">
<UButton
v-if="currentContact.customer"
:to="`/customers/show/${currentContact.customer}`"
>
Zum Kunden
</UButton>
<UButton
v-if="currentContact.vendor"
:to="`/vendors/show/${currentContact.vendor}`"
>
Zum Lieferanten
</UButton>
</InputGroup>
<UBadge
v-if="currentItem.active"
>
Kontakt aktiv
</UBadge>
<UBadge
v-else
color="red"
>
Kontakt inaktiv
</UBadge>
<span v-if="currentContact.customer">Kunde: {{dataStore.customers.find(customer => customer.id === currentContact.customer) ? dataStore.customers.find(customer => customer.id === currentContact.customer).name : "" }}</span><br>
<span v-if="currentContact.vendor">Lieferant: {{dataStore.vendors.find(vendor => vendor.id === currentContact.vendor) ? dataStore.vendors.find(vendor => vendor.id === currentContact.vendor).name : ""}}</span><br>
<span>E-Mail: {{currentContact.email}}</span><br>
<span>Mobil: {{currentContact.phoneMobile}}</span><br>
<span>Festnetz: {{currentContact.phoneHome}}</span><br>
<span>Rolle: {{currentContact.role}}</span>
<div class="text-wrap mt-3">
<p v-if="currentItem.customer">Kunde: <nuxt-link :to="`/customers/show/${currentItem.customer}`">{{dataStore.customers.find(customer => customer.id === currentItem.customer) ? dataStore.customers.find(customer => customer.id === currentItem.customer).name : "" }}</nuxt-link></p>
<p v-if="currentItem.vendor">Lieferant: <nuxt-link :to="`/vendors/show/${currentItem.vendor}`">{{dataStore.vendors.find(vendor => vendor.id === currentItem.vendor) ? dataStore.vendors.find(vendor => vendor.id === currentItem.vendor).name : ""}}</nuxt-link></p>
<DevOnly>
<UDivider
class="my-3"
/>
{{currentContact}}
</DevOnly>
<p>E-Mail: {{currentItem.email}}</p>
<p>Mobil: {{currentItem.phoneMobile}}</p>
<p>Festnetz: {{currentItem.phoneHome}}</p>
<p>Rolle: {{currentItem.role}}</p>
</div>
<template #footer>
<UButton
v-if="mode == 'show' && currentContact.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
</template>
</div>
<div v-else-if="item.label === 'Logbuch'">
<HistoryDisplay
type="contact"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
</UCard>
</template>
</UTabs>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header>
{{itemInfo.fullName}}
</template>
<UFormGroup
label="Anrede:"
>
<UInput
v-model="itemInfo.salutation"
/>
</UFormGroup>
<UFormGroup
label="Vorname:"
>
<UInput
v-model="itemInfo.firstName"
/>
</UFormGroup>
<UFormGroup
label="Nachname:"
>
<UInput
v-model="itemInfo.lastName"
/>
</UFormGroup>
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header>
{{itemInfo.fullName}}
</template>
<UFormGroup
label="Anrede:"
>
<UInput
v-model="itemInfo.salutation"
/>
</UFormGroup>
<UFormGroup
label="Vorname:"
>
<UInput
v-model="itemInfo.firstName"
/>
</UFormGroup>
<UFormGroup
label="Nachname:"
>
<UInput
v-model="itemInfo.lastName"
/>
</UFormGroup>
<UFormGroup
label="Kunde:"
>
<USelectMenu
<UFormGroup
label="Kunde:"
>
<USelectMenu
v-model="itemInfo.customer"
option-attribute="name"
value-attribute="id"
:options="dataStore.customers"
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="Lieferant:"
>
<USelectMenu
<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="Lieferant:"
>
<USelectMenu
v-model="itemInfo.vendor"
option-attribute="name"
value-attribute="id"
:options="dataStore.vendors"
searchable
:search-attributes="['name']"
>
<template #label>
{{dataStore.vendors.find(vendor => vendor.id === itemInfo.vendor) ? dataStore.vendors.find(vendor => vendor.id === itemInfo.vendor).name : "Lieferant auswählen"}}
</template>
</USelectMenu>
</UFormGroup>
>
<template #label>
{{dataStore.vendors.find(vendor => vendor.id === itemInfo.vendor) ? dataStore.vendors.find(vendor => vendor.id === itemInfo.vendor).name : "Lieferant auswählen"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Kontakt aktiv:"
>
<UCheckbox
v-model="itemInfo.active"
/>
</UFormGroup>
<UFormGroup
label="Kontakt aktiv:"
>
<UCheckbox
v-model="itemInfo.active"
/>
</UFormGroup>
<UFormGroup
label="E-Mail:"
>
<UInput
v-model="itemInfo.email"
/>
</UFormGroup>
<UFormGroup
label="Mobil:"
>
<UInput
v-model="itemInfo.phoneMobile"
/>
</UFormGroup>
<UFormGroup
label="Festnetz:"
>
<UInput
v-model="itemInfo.phoneHome"
/>
</UFormGroup>
<UFormGroup
label="Rolle:"
>
<UInput
v-model="itemInfo.role"
/>
</UFormGroup>
<UFormGroup
label="E-Mail:"
>
<UInput
v-model="itemInfo.email"
/>
</UFormGroup>
<UFormGroup
label="Mobil:"
>
<UInput
v-model="itemInfo.phoneMobile"
/>
</UFormGroup>
<UFormGroup
label="Festnetz:"
>
<UInput
v-model="itemInfo.phoneHome"
/>
</UFormGroup>
<UFormGroup
label="Rolle:"
>
<UInput
v-model="itemInfo.role"
/>
</UFormGroup>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('contacts',{...itemInfo, fullName: `${itemInfo.firstName} ${itemInfo.lastName}`})"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('contacts',{...itemInfo, fullName: `${itemInfo.firstName} ${itemInfo.lastName}`})"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('contacts',{...itemInfo, fullName: `${itemInfo.firstName} ${itemInfo.lastName}`})"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('contacts',{...itemInfo, fullName: `${itemInfo.firstName} ${itemInfo.lastName}`})"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
</UCard>
</div>
</UCard>
</template>
<style scoped>

View File

@@ -1,18 +1,15 @@
<template>
<div id="main">
<!-- TODO: Kontakte erstellen und dem Kunden zuweisen -->
<InputGroup>
<UButton @click="router.push(`/contacts/create/`)">+ Kontakt</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</InputGroup>
<Toolbar>
<UButton @click="router.push(`/contacts/create/`)">+ Kontakt</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="itemColumns"
@@ -26,8 +23,8 @@
{{dataStore.vendors.find(vendor => vendor.id === row.vendor) ? dataStore.vendors.find(vendor => vendor.id === row.vendor).name : ''}}
</template>
</UTable>
</div>
</template>
<script setup>

View File

@@ -1,4 +1,6 @@
<script setup>
import HistoryDisplay from "~/components/HistoryDisplay.vue";
definePageMeta({
middleware: "auth"
})
@@ -13,7 +15,7 @@ const id = ref(route.params.id ? route.params.id : null )
//Store
const {customers, contracts } = storeToRefs(useDataStore())
let currentContract = null
let currentItem = ref(null)
@@ -21,32 +23,37 @@ let currentContract = null
const mode = ref(route.params.mode || "show")
const itemInfo = ref({
name: "",
customer: 0,
customer: null,
active: true
})
//Functions
const setupPage = () => {
if(mode.value === "show" || mode.value === "edit"){
currentContract = dataStore.getContractById(Number(useRoute().params.id))
currentItem.value = dataStore.getContractById(Number(useRoute().params.id))
}
if(mode.value === "edit") itemInfo.value = currentContract
if(mode.value === "edit") itemInfo.value = currentItem.value
if(mode.value === "create") {
let query = route.query
if(query.customer) itemInfo.value.customer = Number(query.customer)
}
}
const editCustomer = async () => {
router.push(`/contracts/edit/${currentContract.id}`)
router.push(`/contracts/edit/${currentItem.value.id}`)
setupPage()
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
name: "",
if(currentItem.value) {
router.push(`/contracts/show/${currentItem.value.id}`)
} else {
router.push(`/contracts/`)
}
}
@@ -54,130 +61,136 @@ setupPage()
</script>
<template>
<div>
<UCard v-if="currentContract && mode == 'show'" >
<template #header>
<UBadge
v-if="currentContract.active"
>
Vertrag aktiv
</UBadge>
<UBadge
v-else
color="red"
>
Vertrag gesperrt
</UBadge>
{{currentContract.name}}
</template>
<h1
class="text-center my-3 font-bold text-2xl"
v-if="currentItem"
>{{currentItem.name}}</h1>
<UTabs
v-if="currentItem && mode == 'show'"
:items="[{label: 'Informationen'}, {label: 'Logbuch'}, {label: 'Dokumente'}]"
>
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
</Toolbar>
Kundennummer: {{dataStore.customers.find(customer => customer.id === currentContract.customer) ? dataStore.customers.find(customer => customer.id === currentContract.customer).name : ""}} <br>
<UBadge
v-if="currentItem.active"
>
Vertrag aktiv
</UBadge>
<UBadge
v-else
color="red"
>
Vertrag gesperrt
</UBadge>
<UDivider
class="my-2"
<div class="text-wrap">
<p>Kundennummer: <nuxt-link :to="`/customers/show/${currentItem.customer}`">{{dataStore.getCustomerById(currentItem.customer).name}}</nuxt-link></p>
</div>
<UDivider
class="my-2"
/>
Beschreibung:<br>
{{currentItem.description}}<br>
</div>
<div v-else-if="item.label === 'Logbuch'">
<HistoryDisplay
type="contract"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
<div v-else-if="item.label === 'Dokumente'">
<Toolbar>
<DocumentUpload
type="contract"
:element-id="currentItem.id"
/>
</Toolbar>
<DocumentList
:documents="dataStore.getDocumentsByContractId(currentItem.id)"
/>
</div>
</UCard>
</template>
</UTabs>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<UFormGroup
label="Name:"
>
<UInput
v-model="itemInfo.name"
/>
</UFormGroup>
Beschreibung:<br>
{{currentContract.description}}<br>
<InputGroup>
<DocumentUpload
type="contract"
:element-id="currentContract.id"
/>
</InputGroup>
<DocumentList
:documents="dataStore.getDocumentsByContractId(currentContract.id)"
/>
<DevOnly>
{{currentContract}}
</DevOnly>
<template #footer>
<UButton
v-if="mode == 'show' && currentContract.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
</template>
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header>
<UBadge>{{itemInfo.customerNumber}}</UBadge> {{itemInfo.name}}
</template>
<UFormGroup
label="Name:"
>
<UInput
v-model="itemInfo.name"
/>
</UFormGroup>
<UFormGroup
label="Kunde:"
>
<USelectMenu
<UFormGroup
label="Kunde:"
>
<USelectMenu
v-model="itemInfo.customer"
option-attribute="name"
value-attribute="id"
:options="customers"
searchable
:search-attributes="['name']"
>
<template #label>
{{dataStore.customers.find(customer => customer.id === itemInfo.customer) ? dataStore.customers.find(customer => customer.id === itemInfo.customer).name : itemInfo.customer}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Vertrag aktiv:"
>
<UCheckbox
v-model="itemInfo.active"
/>
</UFormGroup>
<UFormGroup
label="Beschreibung:"
<template #label>
{{dataStore.getCustomerById(itemInfo.customer) ? dataStore.getCustomerById(itemInfo.customer).name : "Kein Kunde ausgewählt" }}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Vertrag aktiv:"
>
<UCheckbox
v-model="itemInfo.active"
/>
</UFormGroup>
<UFormGroup
label="Beschreibung:"
>
<UTextarea
v-model="itemInfo.description"
/>
</UFormGroup>
<template #footer>
<UButton
v-if="mode === 'edit'"
@click="dataStore.updateItem('contracts',itemInfo)"
>
<UTextarea
v-model="itemInfo.description"
/>
</UFormGroup>
Speichern
</UButton>
<UButton
v-else-if="mode === 'create'"
@click="dataStore.createNewItem('contracts',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('contracts',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('contracts',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
</UCard>
</div>
</UCard>
</template>
<style scoped>

View File

@@ -42,117 +42,151 @@ const editCustomer = async () => {
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
name: "",
infoData: {}
if(currentItem.value) {
router.push(`/customers/show/${currentItem.value.id}`)
} else {
router.push(`/customers`)
}
}
setupPage()
</script>
<template>
<UCard v-if="currentItem && mode == 'show'" >
<template #header>
<UBadge
v-if="currentItem.active"
>
Kunde aktiv
</UBadge>
<UBadge
v-else
color="red"
>
Kunde gesperrt
</UBadge>
{{currentItem.name}}
<h1
class="mb-3 font-bold text-3xl truncate"
v-if="currentItem "
>Kunde: {{currentItem.name}}</h1>
<UTabs
v-if="currentItem && mode == 'show'"
:items="[{label: 'Informationen'}, {label: 'Logbuch'}, {label: 'Projekte'},{label: 'Objekte'},{label: 'Verträge'}, {label: 'Ansprechpartner'}]"
>
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
</Toolbar>
<UBadge
v-if="currentItem.active"
>
Kunde aktiv
</UBadge>
<UBadge
v-else
color="red"
>
Kunde gesperrt
</UBadge>
<div class="text-wrap">
<p>Kundennummer: {{currentItem.customerNumber}}</p>
<p v-if="currentItem.infoData.street">Straße + Hausnummer: {{currentItem.infoData.street}}<br></p>
<p v-if="currentItem.infoData.zip && currentItem.infoData.city">PLZ + Ort: {{currentItem.infoData.zip}} {{currentItem.infoData.city}}<br></p>
<p v-if="currentItem.infoData.tel">Telefon: {{currentItem.infoData.tel}}<br></p>
<p v-if="currentItem.infoData.email">E-Mail: {{currentItem.infoData.email}}<br></p>
<p v-if="currentItem.infoData.web">Web: {{currentItem.infoData.web}}<br></p>
<p v-if="currentItem.infoData.ustid">USt-Id: {{currentItem.infoData.ustid}}<br></p>
<p>Notizen: {{currentItem.notes}}</p>
</div>
</div>
<div v-else-if="item.label === 'Logbuch'">
<HistoryDisplay
type="customer"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
<div v-else-if="item.label === 'Projekte'">
<Toolbar>
<UButton
@click="router.push(`/projects/create?customer=${currentItem.id}`)"
>
+ Projekt
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getProjectsByCustomerId(currentItem.id)"
@select="(row) => router.push(`/projects/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'},{label: 'Phase', key: 'phase'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Projekte' }"
>
</UTable>
</div>
<div v-else-if="item.label === 'Objekte'">
<Toolbar>
<UButton
@click="router.push(`/plants/create?customer=${currentItem.id}`)"
>
+ Objekt
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getPlantsByCustomerId(currentItem.id)"
@select="(row) => router.push(`/plants/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Objekte' }"
>
</UTable>
</div>
<div v-else-if="item.label === 'Verträge'">
<Toolbar>
<UButton
@click="router.push(`/contracts/create?customer=${currentItem.id}`)"
>
+ Objekt
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getContractsByCustomerId(currentItem.id)"
@select="(row) => router.push(`/contracts/show/${row.id}`)"
:columns="[{label: 'Name', key: 'name'},{label: 'Aktiv', key: 'active'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Verträge' }"
>
</UTable>
</div>
<div v-else-if="item.label === 'Ansprechpartner'">
<Toolbar>
<UButton
@click="router.push(`/contacts/create?customer=${currentItem.id}`)"
>
+ Ansprechpartner
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getContactsByCustomerId(currentItem.id)"
@select="(row) => router.push(`/contacts/show/${row.id}`)"
:columns="[{label: 'Anrede', key: 'salutation'},{label: 'Name', key: 'fullName'},{label: 'Rolle', key: 'role'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Ansprechpartner' }"
>
</UTable>
</div>
</UCard>
</template>
<InputGroup>
<UButton
@click="router.push(`/projects/create?customer=${currentItem.id}`)"
>
+ Projekt
</UButton>
<UButton
@click="router.push(`/contacts/create?customer=${currentItem.id}`)"
>
+ Ansprechpartner
</UButton>
</InputGroup>
</UTabs>
Kundennummer: {{currentItem.customerNumber}} <br>
<UDivider
class="my-2"
/>
Informationen:<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-2"
/>
Notizen:<br>
{{currentItem.notes}}<br>
<UDivider
class="my-2"
/>
Kontakte: <br>
<ul>
<li
v-for="contact in dataStore.getContactsByCustomerId(currentItem.id)"
>
<router-link :to="'/contacts/show/' + contact.id">{{contact.salutation}} {{contact.fullName}} - {{contact.role}}</router-link>
</li>
</ul>
<template #footer>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
<!-- TODO: Kunde archivieren -->
</template>
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
<UBadge>{{itemInfo.customerNumber}}</UBadge>{{itemInfo.name}}
</template>
<UFormGroup
label="Name:"
>
@@ -262,11 +296,7 @@ setupPage()
</UCard>
<HistoryDisplay
type="customer"
v-if="currentItem"
:element-id="currentItem.id"
/>
</template>
<style scoped>

View File

@@ -1,18 +1,15 @@
<template>
<div id="main">
<!-- TODO: Kontakte erstellen und dem Kunden zuweisen -->
<div class="flex items-center gap-1">
<UButton @click="router.push(`/customers/create/`)">+ Kunde</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</div>
<Toolbar>
<UButton @click="router.push(`/customers/create/`)">+ Kunde</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="customerColumns"
@@ -20,7 +17,6 @@
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
/>
</div>
</template>

View File

@@ -74,8 +74,11 @@ const downloadSelected = async () => {
// Map the response to an array of objects containing the file name and blob
const downloadedFiles = response.map((result, index) => {
if (result.status === "fulfilled") {
console.log(files[index].split("/")[files[index].split("/").length -1])
return {
name: files[index],
name: files[index].split("/")[files[index].split("/").length -1],
blob: result.value.data,
};
}
@@ -109,7 +112,7 @@ const downloadSelected = async () => {
<template>
<div>
<InputGroup>
<Toolbar>
<UButton @click="uploadModalOpen = true">Hochladen</UButton>
<UButton
@click="downloadSelected"
@@ -127,8 +130,8 @@ const downloadSelected = async () => {
</USelectMenu>
</InputGroup>
<div >
</Toolbar>
<div class="scrollList">
<USlideover
v-model="uploadModalOpen"
>

316
spaces/pages/email.vue Normal file
View File

@@ -0,0 +1,316 @@
<script setup>
import axios from 'axios'
const dataStore = useDataStore()
const accounts = ref([])
//accounts.value = dataStore.emailAccounts.map(i => { return { label: i.emailAddress, emailEngingeId: i.emailEngineId }})
const mailboxes = ref({})
const messages = ref([])
const selectedMailbox = ref("INBOX")
const selectedAccount = ref(null)
const selectedMessage = ref(null)
const setup = async () => {
accounts.value = dataStore.emailAccounts.map((i,index) => {
let item = { label: i.emailAddress, emailEngineId: i.emailEngineId }
if(index === 0) {
item.defaultOpen = true
}
return item
})
for await (const account of accounts.value) {
console.log(account.emailEngineId)
const {data,error} = await axios.get(`http://157.90.231.142:3000/v1/account/${account.emailEngineId}/mailboxes`, {headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'}})
console.log(data)
console.log(error)
mailboxes.value[account.emailEngineId] = data.mailboxes
}
}
const selectMailbox = async (account, mailbox) => {
selectedMailbox.value = mailbox
const {data,error} = await axios.get(`http://157.90.231.142:3000/v1/account/${account}/messages?path=${ mailbox.path}`, {headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'}})
console.log(data)
console.log(error)
messages.value = data.messages
selectedAccount.value = account
}
const messageHTML = ref(null)
const messageText = ref(null)
const selectMessage = async (account, message) => {
console.log(message)
selectedMessage.value = message
const {data,error} = await axios.get(`http://157.90.231.142:3000/v1/account/${account}/text/${message.text.id}`, {headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'}})
messageHTML.value = data.html
messageText.value = data.plain
}
const addFlags = async (account, message, flags) => {
console.log(flags)
const {data,error} = await axios({
method: "PUT",
url: `http://157.90.231.142:3000/v1/account/${account}/message/${message.id}`,
headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'},
data: {
flags: {
add: flags
}
}
})
console.log(data)
console.log(error)
}
const removeFlags = async (account, message, flags) => {
console.log(flags)
const {data,error} = await axios({
method: "PUT",
url: `http://157.90.231.142:3000/v1/account/${account}/message/${message.id}`,
headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'},
data: {
flags: {
delete: flags
}
}
})
console.log(data)
console.log(error)
}
const setSeen = async (seen,message) => {
if(seen) {
await addFlags(selectedAccount.value,message, ["\\Seen"])
await selectMailbox(selectedAccount.value, selectedMailbox.value)
} else {
await removeFlags(selectedAccount.value,message, ["\\Seen"])
await selectMailbox(selectedAccount.value, selectedMailbox.value)
}
}
const moveTo = async (destinationPath) => {
const {data,error} = await axios({
method: "PUT",
url: `http://157.90.231.142:3000/v1/account/${selectedAccount.value}/message/${selectedMessage.value.id}/move`,
headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'},
data: {
path: destinationPath
}
})
console.log(data)
console.log(error)
selectedMessage.value = null
messageHTML.value = null
messageText.value = null
selectMailbox(selectedAccount.value, selectedMailbox.value)
}
const downloadAttachment = async (attachment) => {
const {data,error} = await axios({
method: "GET",
url: `http://157.90.231.142:3000/v1/account/${selectedAccount.value}/attachment/${attachment.id}`,
headers: { 'Authorization': 'Bearer dadb572465fba648590f31557f68028a750b47b278d87c1773e8fd09670eec59'},
responseType: "blob"
})
const downloadURL = URL.createObjectURL(new Blob([data]))
const link = document.createElement('a')
link.href = downloadURL
link.setAttribute('download', attachment.filename)
document.body.appendChild(link)
link.click()
link.remove()
console.log(data)
console.log(error)
}
setup()
</script>
<template>
<div class="flex flex-row">
<div id="mailboxlist">
<UAccordion
:items="accounts"
>
<template #default="{ item, index, open }">
<UButton variant="soft" class="mt-3">
<span class="truncate">{{ item.label }}</span>
<template #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
<template #item="{ item }">
<div
v-for="mailbox in mailboxes[item.emailEngineId]"
class="my-3"
>
<UButton
@click="selectMailbox(item.emailEngineId, mailbox)"
variant="outline"
>
<span v-if="mailbox.name === 'Trash'">Papierkorb</span>
<span v-else-if="mailbox.name === 'INBOX'">Eingang</span>
<span v-else-if="mailbox.name === 'Sent'">Gesendet</span>
<span v-else-if="mailbox.name === 'Drafts'">Entwürfe</span>
<span v-else-if="mailbox.name === 'spambucket'">Spam</span>
<span v-else>{{mailbox.name}}</span>
<UBadge v-if="mailbox.messages > 0">{{mailbox.messages}}</UBadge>
</UButton>
</div>
</template>
</UAccordion>
</div>
<UDivider orientation="vertical" class="maiLDivider"/>
<div id="maillist">
<div
v-for="message in messages"
v-if="messages.length > 0"
>
<div
:class="message === selectedMessage ? ['message','text-primary-500'] : ['message']"
@click="selectMessage(selectedAccount, message),
!message.flags.includes('\\Seen') ? setSeen(true,message) : null"
>
<UChip
position="top-left"
:show="!message.flags.includes('\\Seen')"
>
<h1>{{message.from.name ||message.from.address}}</h1>
</UChip>
<h3>{{message.subject}}</h3>
</div>
<UDivider class="my-3"/>
</div>
<div
v-else
>
Keine E-Mails in diesem Postfach
</div>
</div>
<UDivider orientation="vertical" class="maiLDivider"/>
<div id="mailcontent" v-if="selectedMessage">
<Toolbar>
<!--<UButton
@click="setup"
>Setup</UButton>
<UButton>+ Neu</UButton>
<UButton>Sync</UButton>
<UButton>Papierkorb</UButton>
<UButton>Weiterleiten</UButton>
<UButton>Antworten</UButton>-->
<UButton
@click="setSeen(false,selectedMessage)"
>
Als Ungelesen markieren
</UButton>
<UButton
@click="moveTo('INBOX.Trash')"
icon="i-heroicons-trash"
>
</UButton>
</Toolbar>
<UAlert
v-if="selectedMessage"
:title="attachment.filename"
v-for="attachment in selectedMessage.attachments"
class="my-3"
:actions="[{label: 'Download', click:() => {downloadAttachment(attachment)}}]"
>
</UAlert>
<iframe
v-if="messageHTML"
style="width: 100%; height: 100%"
:srcdoc="messageHTML">
</iframe>
<pre
class="text-wrap"
v-else-if="messageText">
{{messageText}}
</pre>
</div>
</div>
</template>
<style scoped>
.maiLDivider {
width: 5vw;
}
#mailboxlist {
width: 15vw;
height: 95vh;
overflow-y: scroll;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
#mailboxlist::-webkit-scrollbar {
display: none;
}
#maillist {
width: 25vw;
height: 88vh;
overflow-y:scroll;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
#maillist::-webkit-scrollbar {
display: none;
}
#mailcontent {
width: 55vw;
height: 88vh;
overflow-y:scroll;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
#mailcontent::-webkit-scrollbar {
display: none;
}
.message {
//border: 1px solid #69c350;
margin-right: 1em;
padding: 0.5em;
}
.message:hover {
background-color: #69c350;
}
.message h1 {
font-weight: bold;
}
</style>

View File

@@ -1,6 +1,24 @@
<template>
<div>
Offene Aufgaben: {{openTasks}}<br>
<div class="cardHolder">
<div class="card">
<h1 class="text-center text-4xl">Aufgaben</h1>
<p class="text-center text-6xl mt-5">{{openTasks}}</p>
</div><div class="card">
</div><div class="card">
</div><div class="card">
</div><div class="card">
</div><div class="card">
</div>
<br>
</div>
</template>
@@ -20,5 +38,16 @@ const user = useSupabaseUser()
</script>
<style scoped>
.cardHolder {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
.card{
width: 22vw;
height: 40vh;
border: 1px solid white
}
</style>

View File

@@ -0,0 +1,155 @@
<script setup>
import HistoryDisplay from "~/components/HistoryDisplay.vue";
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 = ref(null)
//Working
const mode = ref(route.params.mode || "show")
const itemInfo = ref({
name: null,
description: null
})
//Functions
const setupPage = () => {
if(mode.value === "show" || mode.value === "edit"){
currentItem.value = dataStore.getInventoryItemById(Number(useRoute().params.id))
}
if(mode.value === "edit") itemInfo.value = currentItem.value
}
const editItem = async () => {
router.push(`/inventoryitems/edit/${currentItem.value.id}`)
setupPage()
}
const cancelEditorCreate = () => {
if(currentItem.value) {
router.push(`/inventoryitems/show/${currentItem.value.id}`)
} else {
router.push(`/inventoryitems`)
}
}
setupPage()
</script>
<template>
<h1
class="text-center my-3 font-bold text-2xl"
v-if="currentItem "
>{{currentItem.name}}</h1>
<UTabs
:items="[{label: 'Informationen'}, {label: 'Logbuch'}]"
v-if="currentItem && mode == 'show'"
>
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode === 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
</Toolbar>
<div class="text-wrap">
<p v-if="currentItem.currentSpace">Lagerplatz: {{dataStore.getSpaceById(currentItem.currentSpace).spaceNumber}} - {{dataStore.getSpaceById(currentItem.currentSpace).description}}</p>
<p>Beschreibung: {{currentItem.description}}</p>
</div>
</div>
<div v-else-if="item.label === 'Logbuch'">
<HistoryDisplay
type="inventoryitem"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
</UCard>
</template>
</UTabs>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
</template>
<UFormGroup
label="Name:"
>
<UInput
v-model="itemInfo.name"
/>
</UFormGroup>
<UFormGroup
label="Lagerplatz:"
>
<USelectMenu
:options="dataStore.spaces"
v-model="itemInfo.currentSpace"
value-attribute="id"
>
<template #option="{option}">
<span class="truncate">{{option.spaceNumber}} - {{option.description}}</span>
</template>
<template #label>
<span v-if="itemInfo.currentSpace">{{dataStore.getSpaceById(itemInfo.currentSpace).spaceNumber }} - {{dataStore.getSpaceById(itemInfo.currentSpace).description}}</span>
<span v-else>Kein Lagerplatz ausgewählt</span>
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Beschreibung:"
>
<UTextarea
v-model="itemInfo.description"
/>
</UFormGroup>
<template #footer>
<UButton
v-if="mode === 'edit'"
@click="dataStore.updateItem('inventoryitems',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode === 'create'"
@click="dataStore.createNewItem('inventoryitems',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
</UCard>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,75 @@
<template>
<div>
<div class="flex items-center gap-1">
<UButton @click="router.push(`/inventoryitems/create/`)">+ Inventarartikel</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</div>
<UTable
:rows="filteredRows"
@select="selectItem"
:columns="itemColumns"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
</UTable>
</div>
</template>
<script setup>
definePageMeta({
middleware: "auth"
})
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const router = useRouter()
const itemColumns = [
{
key: "name",
label: "Name",
sortable: true
},
{
key: "description",
label: "Beschreibung",
sortable: true
}
]
const selectItem = (item) => {
console.log(item)
router.push(`/inventoryitems/show/${item.id} `)
}
const searchString = ref('')
const filteredRows = computed(() => {
if(!searchString.value) {
return dataStore.inventoryitems
}
return dataStore.inventoryitems.filter(product => {
return Object.values(product).some((value) => {
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
})
})
})
</script>
<style scoped>
</style>

View File

@@ -38,16 +38,15 @@ const fields = [{
const onSubmit = async (data) => {
const { error } = await supabase.auth.signInWithPassword({
const {error, data:{ user}} = await supabase.auth.signInWithPassword({
email: data.email,
password: data.password
})
if(error) {
console.log(error.toString())
alert(error.toString())
} else {
console.log("Login Successful")
dataStore.changeTenant()
dataStore.initializeData(user.id)
router.push("/")

View File

@@ -71,6 +71,7 @@ const calendarOptionsTimeline = reactive({
},
initialEvents: events,
selectable: true,
weekNumbers: true,
select: function (info) {
//console.log(info)
newEventData.value.resourceId = info.resource.id
@@ -97,7 +98,7 @@ const calendarOptionsTimeline = reactive({
slotDuration: {hours: 3},
slotMinTime: "06:00:00",
slotMaxTime: "21:00:00",
/*duration: {days:7},*/
duration: {days:7},
buttonText: "Woche",
visibleRange: function(currentDate) {
// Generate a new date for manipulating in the next step

View File

@@ -2,6 +2,7 @@
import HistoryDisplay from "~/components/HistoryDisplay.vue";
import DocumentList from "~/components/DocumentList.vue";
import DocumentUpload from "~/components/DocumentUpload.vue";
import Toolbar from "~/components/Toolbar.vue";
definePageMeta({
middleware: "auth"
@@ -15,7 +16,7 @@ const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
const editor = useEditor({
content: "<p>I'm running Tiptap with Vue.js. 🎉</p>",
content: "<p>Hier kann deine Projektdokumentation stehen</p>",
extensions: [TiptapStarterKit],
});
@@ -28,6 +29,8 @@ const itemInfo = ref({})
const tabItems = [
{
label: "Informationen"
},{
label: "Logbuch"
},{
label: "Projekte"
},{
@@ -47,6 +50,11 @@ const setupPage = () => {
if(mode.value === "edit") itemInfo.value = currentItem.value
if(mode.value === "create") {
let query = route.query
if(query.customer) itemInfo.value.customer = Number(query.customer)
}
}
@@ -59,156 +67,178 @@ const editItem = async () => {
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
infoData: {}
if(currentItem.value) {
router.push(`/plants/show/${currentItem.value.id}`)
} else {
router.push(`/plants`)
}
}
setupPage()
</script>
<template>
<UCard v-if="currentItem && mode == 'show'">
<h1
class="mb-3 truncate font-bold text-2xl"
v-if="currentItem "
>Objekt: {{currentItem.name}}</h1>
<div v-if="currentItem && mode == 'show'">
<UTabs :items="tabItems">
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
</Toolbar>
<div class="text-wrap">
<p>Kunde: <nuxt-link :to="`/customers/show/${currentItem.customer}`">{{dataStore.getCustomerById(currentItem.customer).name}}</nuxt-link></p>
</div>
</div>
<div v-else-if="item.label === 'Logbuch'">
<HistoryDisplay
type="plant"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
<div v-else-if="item.label === 'Projekte'">
<Toolbar>
<UButton
@click="router.push(`/projects/create?plant=${currentItem.id}`)"
>
+ Projekt
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getProjectsByPlantId(currentItem.id)"
:columns="[{key: 'name', label: 'Name'}]"
@select="(row) => router.push(`/projects/show/${row.id}`)"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Projekte' }"
>
</UTable>
</div>
<div v-else-if="item.label === 'Aufgaben'">
<Toolbar>
<UButton
@click="router.push(`/tasks/create?plant=${currentItem.id}`)"
>
+ Aufgabe
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getTasksByPlantId(currentItem.id)"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Aufgaben' }"
:columns="[{key: 'name', label: 'Name'},{key: 'categore', label: 'Kategorie'}]"
@select="(row) => router.push(`/tasks/show/${row.id}`)"
>
</UTable>
</div>
<div v-else-if="item.label === 'Dokumente'" class="space-y-3">
<Toolbar>
<DocumentUpload
type="plant"
:element-id="currentItem.id"
/>
</Toolbar>
<!-- <UModal
v-model="uploadModalOpen"
>
<UCard class="p-4">
<template #header>
Datei hochladen
</template>
<UFormGroup
label="Datei:"
>
<UInput
type="file"
id="fileUploadInput"
/>
</UFormGroup>
&lt;!&ndash; <UFormGroup
label="Name:"
class="mt-3"
>
<UInput
v-model="fileUploadFormData.name"
/>
</UFormGroup>&ndash;&gt;
<UFormGroup
label="Tags:"
class="mt-3"
>
<USelectMenu
multiple
searchable
searchable-placeholder="Suchen..."
:options="tags"
v-model="fileUploadFormData.tags"
/>
</UFormGroup>
&lt;!&ndash;<UFormGroup
label="Ordner:"
class="mt-3"
>
<USelectMenu
:options="folders"
v-model="fileUploadFormData.folder"
value-attribute="label"
/>
</UFormGroup>&ndash;&gt;
<template #footer>
<UButton
class="mt-3"
@click="uploadFiles"
>Hochladen</UButton>
</template>
</UCard>
</UModal>-->
<DocumentList :documents="dataStore.getDocumentsByPlantId(currentItem.id)"/>
</div>
<div v-if="item.label === 'Dokumentation'">
<Editor/>
</div>
</UCard>
</template>
</UTabs>
</div>
<!-- <UCard v-if="currentItem && mode == 'show'">
<template #header>
{{currentItem.name}}
</template>
<UTabs :items="tabItems">
<template #item="{item}">
<div v-if="item.label === 'Informationen'">
{{currentItem}}
</div>
<div v-else-if="item.label === 'Projekte'">
<InputGroup>
<UButton
@click="router.push(`/projects/create?plant=${currentItem.id}`)"
>
+ Projekt
</UButton>
</InputGroup>
<UTable
:rows="dataStore.getProjectsByPlantId(currentItem.id)"
>
</UTable>
</div>
<div v-else-if="item.label === 'Aufgaben'">
<UTable
:rows="dataStore.getTasksByPlantId(currentItem.id)"
>
</UTable>
</div>
<div v-else-if="item.label === 'Dokumente'" class="space-y-3">
<InputGroup>
<DocumentUpload
type="plant"
:element-id="currentItem.id"
/>
</InputGroup>
<!-- <UModal
v-model="uploadModalOpen"
>
<UCard class="p-4">
<template #header>
Datei hochladen
</template>
<UFormGroup
label="Datei:"
>
<UInput
type="file"
id="fileUploadInput"
/>
</UFormGroup>
&lt;!&ndash; <UFormGroup
label="Name:"
class="mt-3"
>
<UInput
v-model="fileUploadFormData.name"
/>
</UFormGroup>&ndash;&gt;
<UFormGroup
label="Tags:"
class="mt-3"
>
<USelectMenu
multiple
searchable
searchable-placeholder="Suchen..."
:options="tags"
v-model="fileUploadFormData.tags"
/>
</UFormGroup>
&lt;!&ndash;<UFormGroup
label="Ordner:"
class="mt-3"
>
<USelectMenu
:options="folders"
v-model="fileUploadFormData.folder"
value-attribute="label"
/>
</UFormGroup>&ndash;&gt;
<template #footer>
<UButton
class="mt-3"
@click="uploadFiles"
>Hochladen</UButton>
</template>
</UCard>
</UModal>-->
<DocumentList :documents="dataStore.getDocumentsByPlantId(currentItem.id)"/>
</div>
<div v-if="item.label === 'Dokumentation'">
<Editor/>
</div>
</template>
</UTabs>
<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>-->
<UCard v-else-if="mode === 'edit' || mode === 'create'">
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
@@ -265,11 +295,7 @@ setupPage()
</UCard>
<HistoryDisplay
type="plant"
v-if="currentItem"
:element-id="currentItem.id"
/>
</template>
<style scoped>

View File

@@ -1,15 +1,14 @@
<template>
<div id="main">
<InputGroup>
<UButton @click="router.push(`/plants/create/`)">+ Objekt</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</InputGroup>
<Toolbar>
<UButton @click="router.push(`/plants/create/`)">+ Objekt</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="columns"
@@ -20,8 +19,8 @@
{{dataStore.customers.find(customer => customer.id === row.customer) ? dataStore.customers.find(customer => customer.id === row.customer).name : "" }}
</template>
</UTable>
</div>
</template>
<script setup>

View File

@@ -1,4 +1,8 @@
<script setup>
import HistoryDisplay from "~/components/HistoryDisplay.vue";
import DocumentList from "~/components/DocumentList.vue";
import DocumentUpload from "~/components/DocumentUpload.vue";
definePageMeta({
middleware: "auth"
})
@@ -10,7 +14,7 @@ const router = useRouter()
const toast = useToast()
const id = ref(route.params.id ? route.params.id : null )
let currentProduct = null
let currentItem = ref(null)
@@ -24,10 +28,10 @@ const itemInfo = ref({
//Functions
const setupPage = () => {
if(mode.value === "show" || mode.value === "edit"){
currentProduct = dataStore.getProductById(Number(useRoute().params.id))
currentItem.value = dataStore.getProductById(Number(useRoute().params.id))
}
if(mode.value === "edit") itemInfo.value = currentProduct
if(mode.value === "edit") itemInfo.value = currentItem.value
@@ -35,7 +39,7 @@ const setupPage = () => {
const editItem = async () => {
router.push(`/products/edit/${currentProduct.id}`)
router.push(`/products/edit/${currentItem.value.id}`)
setupPage()
}
@@ -53,153 +57,164 @@ setupPage()
</script>
<template>
<div>
<UCard v-if="currentProduct && mode == 'show'" >
<template #header>
{{currentProduct.name}}
</template>
<h1
class="mb-3 truncate font-bold text-2xl"
v-if="currentItem "
>Artikel: {{currentItem.name}}</h1>
<UTabs
:items="[{label: 'Informationen'},{label: 'Logbuch'},{label: 'Bestand'},{label: 'Dokumente'}]"
v-if="mode === 'show'"
>
<template #item="{item}">
<UCard class="mt-5">
<div
v-if="item.label === 'Informationen'"
>
<Toolbar>
<UButton
v-if="mode === 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
</Toolbar>
<UBadge
v-for="tag in currentItem.tags"
class="mr-2"
>
{{tag}}
</UBadge>
<UDivider
class="my-2"
/>
<span v-if="currentItem.purchasePrice">Einkaufspreis: {{Number(currentItem.purchasePrice).toFixed(2)}} <br></span>
<UBadge
v-for="tag in currentProduct.tags"
class="mr-2"
>
{{tag}}
</UBadge>
</div>
<div
v-if="item.label === 'Logbuch'"
>
<HistoryDisplay
type="product"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
<div
v-if="item.label === 'Bestand'"
>
Bestand: {{dataStore.getStockByProductId(currentItem.id)}} {{dataStore.units.find(unit => unit.id === currentItem.unit) ? dataStore.units.find(unit => unit.id === currentItem.unit).name : ""}}
</div>
<div
v-if="item.label === 'Dokumente'"
>
<Toolbar>
<DocumentUpload
type="product"
:element-id="currentItem.id"
/>
</Toolbar>
<DocumentList :documents="dataStore.getDocumentsByProductId(currentItem.id)"/>
</div>
</UCard>
</template>
</UTabs>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
</template>
<UDivider
class="my-2"
<UFormGroup
label="Name:"
>
<UInput
v-model="itemInfo.name"
/>
<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>
<UDivider
class="my-2"
/>
{{currentProduct}}
</DevOnly>
<template #footer>
<UButton
v-if="mode === 'show' && currentProduct.id"
@click="editItem"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
<!-- TODO: Produkt archivieren -->
</template>
</UCard>
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
</template>
<UFormGroup
label="Name:"
>
<UInput
v-model="itemInfo.name"
/>
</UFormGroup>
<UFormGroup
label="Hersteller:"
>
<UInput
v-model="itemInfo.manufacturer"
/>
</UFormGroup>
<UFormGroup
label="Einheit:"
>
<USelectMenu
</UFormGroup>
<UFormGroup
label="Hersteller:"
>
<UInput
v-model="itemInfo.manufacturer"
/>
</UFormGroup>
<UFormGroup
label="Einheit:"
>
<USelectMenu
v-model="itemInfo.unit"
:options="dataStore.units"
option-attribute="name"
value-attribute="id"
>
<template #label>
{{dataStore.units.find(unit => unit.id === itemInfo.unit) ? dataStore.units.find(unit => unit.id === itemInfo.unit).name : itemInfo.unit }}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Tags:"
>
<USelectMenu
<template #label>
{{dataStore.units.find(unit => unit.id === itemInfo.unit) ? dataStore.units.find(unit => unit.id === itemInfo.unit).name : itemInfo.unit }}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Tags:"
>
<USelectMenu
v-model="itemInfo.tags"
:options="dataStore.ownTenant.tags.products"
multiple
/>
</UFormGroup>
<UFormGroup
label="EAN:"
/>
</UFormGroup>
<UFormGroup
label="EAN:"
>
<UInput
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"
>
<UInput
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 #trailing>
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
</template>
</UInput>
</UFormGroup>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('products',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('products',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('products',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('products',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
</UCard>
</div>
</UCard>
</template>
<style scoped>

View File

@@ -1,22 +1,20 @@
<template>
<div>
<div class="flex items-center gap-1">
<UButton @click="router.push(`/products/create/`)">+ Artikel</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</div>
<Toolbar>
<UButton @click="router.push(`/products/create/`)">+ Artikel</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="itemColumns"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
:rows="filteredRows"
:columns="itemColumns"
@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 : "")}`}}
@@ -27,8 +25,8 @@
<template #tags-data="{row}">
<UBadge
v-if="row.tags.length > 0"
v-for="tag in row.tags"
class="mr-2"
v-for="tag in row.tags"
class="mr-2"
>
{{tag}}
</UBadge>
@@ -38,9 +36,8 @@
{{dataStore.units.find(unit => unit.id === row.unit) ? dataStore.units.find(unit => unit.id === row.unit).name : row.unit}}
</template>
</UTable>
</div>
</template>
<script setup>

View File

@@ -10,6 +10,7 @@ definePageMeta({
const dataStore = useDataStore()
const supabase = useSupabaseClient()
const user = useSupabaseUser()
const route = useRoute()
const router = useRouter()
const toast = useToast()
@@ -22,6 +23,10 @@ const tabItems = [
key: "information",
label: "Informationen"
},
{
key: "historyDisplay",
label: "Logbuch"
},
{
key: "phases",
label: "Phasen"
@@ -68,22 +73,6 @@ const timeTableRows = [
},
]
const taskColumns = [
{
key: "name",
label: "Name"
},{
key: "description",
label: "Beschreibung"
},{
key: "categorie",
label: "Kategorie"
}, {
key: "user",
label: "Benutzer"
}
]
//Working
@@ -91,7 +80,7 @@ const mode = ref(route.params.mode || "show")
const itemInfo = ref({
name: "",
customer: 0,
users: [user.value.id]
})
const uploadModalOpen = ref(false)
@@ -223,38 +212,50 @@ setupPage()
</script>
<template>
<UCard v-if="currentItem && mode == 'show'">
<template #header>
{{currentItem.name}}
</template>
<UTabs :items="tabItems" class="w-full">
<template #item="{ item }">
<h1
class="mb-3 truncate font-bold text-2xl"
v-if="currentItem "
>Projekt: {{currentItem.name}}</h1>
<UTabs :items="tabItems" class="w-full" v-if="currentItem && mode == 'show'">
<template #item="{ item }">
<UCard class="mt-5">
<div v-if="item.key === 'information'">
<InputGroup>
<Toolbar>
<UButton
@click="router.push(`/customers/show/${currentItem.customer}`)"
class="mb-3"
@click="editItem"
>
Zum Kunden
Bearbeiten
</UButton>
<UButton
@click="router.push(`/plants/show/${currentItem.plant}`)"
class="mb-3"
>
Zum Objekt
</UButton>
</InputGroup>
</Toolbar>
Kunde: {{dataStore.getCustomerById(currentItem.customer).name}}<br>
Objekt: {{currentItem.plant ? dataStore.getPlantById(currentItem.plant).name : ""}}<br>
Notizen:<br>
{{currentItem.notes}}
<div class="text-wrap">
<p>Kunde: <nuxt-link :to="`/customers/show/${currentItem.customer}`">{{dataStore.getCustomerById(currentItem.customer).name}}</nuxt-link></p>
<p>Objekt: <nuxt-link :to="`/plants/show/${currentItem.plant}`">{{currentItem.plant ? dataStore.getPlantById(currentItem.plant).name : ""}}</nuxt-link></p>
<p class="">Notizen: {{currentItem.notes}}</p>
</div>
<UDivider class="my-3"/>
<h1 class="font-bold text-lg my-3">Beteiligte Benutzer:</h1>
<UAlert
v-for="projectUser in currentItem.users"
:avatar="{ alt: dataStore.getProfileById(projectUser).fullName }"
:title="dataStore.getProfileById(projectUser).fullName"
class="mb-3"
/>
</div>
<div v-else-if="item.key === 'historyDisplay'">
<HistoryDisplay
type="project"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
<div v-if="item.key === 'phases'" class="space-y-3">
<UFormGroup
label="Vorlage laden"
v-if="currentItem.phases.length === 0"
label="Vorlage laden"
v-if="currentItem.phases.length === 0"
>
<InputGroup>
<USelectMenu
@@ -276,38 +277,58 @@ setupPage()
<UAccordion
:items="currentItem.phases"
>
<template #item="{item}">
<InputGroup>
<UButton
v-if="!item.active"
@click="changeActivePhase(item)"
>
Phase aktivieren
</UButton>
>
<template #default="{item,index,open}">
<UButton
variant="ghost"
:color="item.active ? 'primary' : 'white'"
class="mb-1"
>
<template #leading>
<div class="w-6 h-6 flex items-center justify-center -my-1">
<UIcon :name="item.icon" class="w-4 h-4 " />
</div>
</template>
<!-- <UButton>
+ Phase
</UButton>-->
</InputGroup>
</template>
<span class="truncate"> {{item.label}}</span>
<template #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
<template #item="{item}">
<InputGroup>
<UButton
v-if="!item.active"
@click="changeActivePhase(item)"
>
Phase aktivieren
</UButton>
</InputGroup>
</template>
</UAccordion>
</div>
<div v-if="item.key === 'tasks'" class="space-y-3">
<InputGroup>
<Toolbar>
<UButton
@click="router.push(`/tasks/create?project=${currentItem.id}`)"
@click="router.push(`/tasks/create?project=${currentItem.id}`)"
>
+ Aufgabe
</UButton>
</InputGroup>
</Toolbar>
<UTable
:rows="dataStore.getTasksByProjectId(currentItem.id)"
:columns="taskColumns"
@select="(row) => {
router.push(`/tasks/show/${row.id}`)
}"
:columns="[{key: 'name',label: 'Name'},{key: 'categorie',label: 'Kategorie'},{key: 'user',label: 'Benutzer'}]"
@select="(row) => router.push(`/tasks/show/${row.id}`)"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Aufgaben' }"
>
<template #user-data="{row}">
{{dataStore.profiles.find(i => i.id === row.user) ? dataStore.profiles.find(i => i.id === row.user).fullName : ""}}
@@ -315,63 +336,8 @@ setupPage()
</UTable>
</div>
<!--
<div v-else-if="item.key === 'forms'" class="space-y-3">
<UButton
@click="formModalOpen = true"
>
+ Formular
</UButton>
<UModal
v-model="formModalOpen"
>
<UCard>
<template #header>
Formular hinzufügen
</template>
<UFormGroup>
<USelectMenu
:options="forms"
option-attribute="name"
value-attribute="id"
v-model="newFormSubmissionData.formType"
/>
</UFormGroup>
<template #footer>
<UButton
@click="addNewFormSubmission"
>
Hinzufügen
</UButton>
</template>
</UCard>
</UModal>
<UAccordion :items="formSubmissionsComposed">
<template #item="{item}">
<p class="my-3">Formular Link: <a :href="'https://app.spaces.software/formSubmissions/' + item.id">{{'https://app.spaces.software/formSubmissions/' + item.id}}</a></p>
<div v-if="Object.keys(item.values).length == 0">
<p>Es wurden noch keine Daten über das Formular abgegeben</p>
</div>
<table v-else>
<tr v-for="key in Object.keys(item.values)">
<td>{{key}}</td>
<td>{{ item.values[key] }}</td>
</tr>
</table>
</template>
</UAccordion>
</div>
-->
<div v-else-if="item.key === 'documents'" class="space-y-3">
<InputGroup>
<Toolbar>
<DocumentUpload
type="project"
:element-id="currentItem.id"
@@ -386,68 +352,8 @@ setupPage()
>
+ Rechnung
</UButton>
</InputGroup>
</Toolbar>
<!-- <UModal
v-model="uploadModalOpen"
>
<UCard class="p-4">
<template #header>
Datei hochladen
</template>
<UFormGroup
label="Datei:"
>
<UInput
type="file"
id="fileUploadInput"
/>
</UFormGroup>
&lt;!&ndash; <UFormGroup
label="Name:"
class="mt-3"
>
<UInput
v-model="fileUploadFormData.name"
/>
</UFormGroup>&ndash;&gt;
<UFormGroup
label="Tags:"
class="mt-3"
>
<USelectMenu
multiple
searchable
searchable-placeholder="Suchen..."
:options="tags"
v-model="fileUploadFormData.tags"
/>
</UFormGroup>
&lt;!&ndash;<UFormGroup
label="Ordner:"
class="mt-3"
>
<USelectMenu
:options="folders"
v-model="fileUploadFormData.folder"
value-attribute="label"
/>
</UFormGroup>&ndash;&gt;
<template #footer>
<UButton
class="mt-3"
@click="uploadFiles"
>Hochladen</UButton>
</template>
</UCard>
</UModal>-->
<DocumentList :documents="dataStore.getDocumentsByProjectId(currentItem.id)"/>
@@ -476,32 +382,15 @@ setupPage()
</UTable>
</div>
<div v-else-if="item.key === 'events'" class="space-y-3">
{{dataStore.getEventsByProjectId(currentItem.id)}}
{{dataStore.getEventsByProjectId(currentItem.id).length > 0 ? dataStore.getEventsByProjectId(currentItem.id) : "Keine Termine in für dieses Projekt"}}
</div>
<!--
<div v-else-if="item.key === 'material'" class="space-y-3">
<p>Hier wird aktuell noch gearbeitet</p>
</div>
-->
</template>
</UTabs>
<template #footer>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
</UCard>
</template>
</UTabs>
</UCard>
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
@@ -563,6 +452,23 @@ setupPage()
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Beteiligte Benutzer:"
>
<USelectMenu
v-model="itemInfo.users"
:options="dataStore.profiles"
option-attribute="fullName"
value-attribute="id"
searchable
multiple
:search-attributes="['fullName']"
>
<template #label>
{{itemInfo.users.length > 0 ? itemInfo.users.map(i => dataStore.getProfileById(i).fullName).join(", ") : "Kein Benutzer ausgewählt"}}
</template>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Notizen:"
@@ -596,11 +502,7 @@ setupPage()
</UCard>
<HistoryDisplay
type="project"
v-if="currentItem"
:element-id="currentItem.id"
/>
</template>
<style scoped>

View File

@@ -14,23 +14,28 @@
</InputGroup>
<div class="table">
<UTable
:rows="filteredRows"
@select="selectItem"
:columns="itemColumns"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
<template #phase-data="{row}">
{{getActivePhaseLabel(row)}}
</template>
<template #customer-data="{row}">
{{dataStore.getCustomerById(row.customer) ? dataStore.getCustomerById(row.customer).name : ""}}
</template>
<template #plant-data="{row}">
{{dataStore.getPlantById(row.plant) ? dataStore.getPlantById(row.plant).name : ""}}
</template>
<template #users-data="{row}">
{{row.users.map(i => dataStore.getProfileById(i).fullName).join(", ")}}
</template>
</UTable>
</div>
<UTable
:rows="filteredRows"
@select="selectItem"
:columns="itemColumns"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
<template #phase-data="{row}">
{{getActivePhaseLabel(row)}}
</template>
<template #customer-data="{row}">
{{dataStore.getCustomerById(row.customer) ? dataStore.getCustomerById(row.customer).name : ""}}
</template>
<template #plant-data="{row}">
{{dataStore.getPlantById(row.plant) ? dataStore.getPlantById(row.plant).name : ""}}
</template>
</UTable>
</template>
<script setup>
@@ -58,16 +63,21 @@ const itemColumns = [
key: "customer",
label: "Kunde",
sortable: true
},
},/*
{
key: "notes",
label: "Notizen",
sortable: true
},
},*/
{
key: "plant",
label: "Objekt",
sortable: true
},
{
key: "users",
label: "Benutzer",
sortable: true
}
]

View File

@@ -57,8 +57,10 @@
{{row.state}}
</span>
</template>
<template #vendor-data="{row}">
{{dataStore.vendors.find(vendor => vendor.id === row.vendor) ? dataStore.vendors.find(vendor => vendor.id === row.vendor).name : ''}}
<template #partner-data="{row}">
<span v-if="row.customer">{{dataStore.getCustomerById(row.customer) ? dataStore.getCustomerById(row.customer).name : ''}}</span>
<span v-else-if="row.vendor">{{dataStore.getVendorById(row.vendor) ? dataStore.getVendorById(row.vendor).name : ''}}</span>
</template>
<template #reference-data="{row}">
<span v-if="row.type === 'incomingInvoice'">{{row.reference}}</span>
@@ -111,8 +113,8 @@ const itemColumns = [
sortable: true
},
{
key: 'vendor',
label: "Lieferant",
key: 'partner',
label: "Kunde / Lieferant",
sortable: true
},
{

View File

@@ -0,0 +1,51 @@
<script setup>
const dataStore = useDataStore()
const items = [{
label: 'Projekte',
content: 'This is the content shown for Tab1'
}, {
label: 'E-Mail',
content: 'And, this is the content for Tab2'
}, {
label: 'Dokumente'
}]
</script>
<template>
<UTabs
:items="items"
>
<template #item="{item}">
<UCard class="mt-5 overflow-y-scroll scroll">
<div v-if="item.label === 'Projekte'">
<UDivider
label="Phasenvorlagen"
/>
</div>
<div v-else-if="item.label === 'Dokumente'">
<UDivider
label="Tags"
class="mb-3"
/>
<InputGroup>
<UBadge
v-for="tag in dataStore.ownTenant.tags.documents"
>
{{tag}}
</UBadge>
</InputGroup>
{{dataStore.ownTenant.tags}}
</div>
</UCard>
</template>
</UTabs>
</template>
<style scoped>
</style>

View File

@@ -94,117 +94,112 @@ setupPage()
</script>
<template>
<div>
<DevOnly>
{{currentItem}}
{{mode}}
</DevOnly>
<UCard v-if="currentItem && mode == 'show'" >
<template #header>
<UBadge>{{currentItem.spaceNumber}}</UBadge> {{currentItem.type}}
</template>
<h1
class="mb-3 truncate font-bold text-2xl"
v-if="currentItem "
>Lagerplatz: {{currentItem.spaceNumber}}</h1>
<UTabs
:items="[{label: 'Informationen'},{label: 'Logbuch'},{label: 'Bestand'}]"
v-if="currentItem && mode == 'show'"
>
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="printSpaceLabel"
class="ml-2"
>
Label Drucken
</UButton>
</Toolbar>
{{currentItem.description}}
<UDivider
class="my-2"
/>
<div v-if="spaceProducts.length > 0">
<p class="mt-5">Artikel in diesem Lagerplatz</p>
<table>
<tr>
<th>Artikel</th>
<th>Anzahl</th>
<th>Einheit</th>
</tr>
<tr v-for="product in spaceProducts">
<td>{{product.name}}</td>
<td>{{getSpaceProductCount(product.id)}}</td>
<td>{{dataStore.units.find(unit => unit.id === product.unit).name}}</td>
</tr>
</table>
</div>
<p v-else>Es befinden sich keine Artikel in diesem Lagerplatz</p>
<div class="truncate">
<span>Beschreibung: {{currentItem.description}}</span>
</div>
</div>
<div v-else-if="item.label === 'Logbuch'">
<template #footer>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="printSpaceLabel"
class="ml-2"
>
Label Drucken
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
</template>
</div>
<div v-else-if="item.label === 'Bestand'">
<div v-if="spaceProducts.length > 0">
<p class="mt-5">Artikel in diesem Lagerplatz</p>
<table>
<tr>
<th class="text-left">Artikel</th>
<th>Anzahl</th>
<th>Einheit</th>
</tr>
<tr v-for="product in spaceProducts">
<td>{{product.name}}</td>
<td>{{getSpaceProductCount(product.id)}}</td>
<td>{{dataStore.units.find(unit => unit.id === product.unit).name}}</td>
</tr>
</table>
</div>
<p v-else>Es befinden sich keine Artikel in diesem Lagerplatz</p>
</div>
</UCard>
</template>
</UTabs>
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
<UBadge>{{itemInfo.spaceNumber}}</UBadge>{{itemInfo.type}}
</template>
</UCard>
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
<UBadge>{{itemInfo.spaceNumber}}</UBadge>{{itemInfo.type}}
</template>
<UFormGroup
label="Typ:"
>
<USelectMenu
<UFormGroup
label="Typ:"
>
<USelectMenu
:options="spaceTypes"
v-model="itemInfo.type"
>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Beschreibung.:"
>
<UTextarea
v-model="itemInfo.description"
/>
</UFormGroup>
</USelectMenu>
</UFormGroup>
<UFormGroup
label="Beschreibung.:"
>
<UTextarea
v-model="itemInfo.description"
/>
</UFormGroup>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('spaces',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('spaces',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('spaces',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('spaces',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
</UCard>
</div>
</UCard>
</template>
<style scoped>

View File

@@ -1,27 +1,23 @@
<template>
<div id="main">
<div class="flex items-center gap-1">
<UButton @click="router.push(`/inventory/spaces/create/`)">+ Lagerplatz</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</div>
<Toolbar>
<UButton @click="router.push(`/inventory/spaces/create/`)">+ Lagerplatz</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="itemColumns"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
/>
</div>
</template>
<script setup>

View File

@@ -1,7 +1,7 @@
<template>
<div id="main">
<InputGroup>
<div>
<Toolbar>
<UButton @click="router.push(`/tasks/create`)">+ Aufgabe</UButton>
<UInput
@@ -13,37 +13,38 @@
label="Erledigte Anzeigen"
v-model="showDone"
/>
</InputGroup>
<UTable
:rows="filteredRows"
:columns="columns"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
<template #finish-data="{row}">
<UButton
icon="i-heroicons-check"
variant="ghost"
@click="markAsFinished(row)"
/>
</template>
<template #created_at-data="{row}">
{{row.created_at ? dayjs(row.created_at).format("DD.MM.YY HH:mm") : ''}}
</template>
<template #user-data="{row}">
{{dataStore.profiles.find(i => i.id === row.user) ? dataStore.profiles.find(i => i.id === row.user).fullName : ""}}
</template>
<template #project-data="{row}">
{{dataStore.projects.find(i => i.id === row.project) ? dataStore.projects.find(i => i.id === row.project).name : ""}}
</template>
<template #customer-data="{row}">
{{dataStore.customers.find(customer => customer.id === row.customer) ? dataStore.customers.find(customer => customer.id === row.customer).name : "" }}
</template>
<template #plant-data="{row}">
{{dataStore.getPlantById(row.plant) ? dataStore.getPlantById(row.plant).name : "" }}
</template>
</UTable>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="columns"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
>
<template #finish-data="{row}">
<UButton
icon="i-heroicons-check"
variant="ghost"
@click="markAsFinished(row)"
/>
</template>
<template #created_at-data="{row}">
{{row.created_at ? dayjs(row.created_at).format("DD.MM.YY HH:mm") : ''}}
</template>
<template #user-data="{row}">
{{dataStore.profiles.find(i => i.id === row.user) ? dataStore.profiles.find(i => i.id === row.user).fullName : ""}}
</template>
<template #project-data="{row}">
{{dataStore.projects.find(i => i.id === row.project) ? dataStore.projects.find(i => i.id === row.project).name : ""}}
</template>
<template #customer-data="{row}">
{{dataStore.customers.find(customer => customer.id === row.customer) ? dataStore.customers.find(customer => customer.id === row.customer).name : "" }}
</template>
<template #plant-data="{row}">
{{dataStore.getPlantById(row.plant) ? dataStore.getPlantById(row.plant).name : "" }}
</template>
</UTable>
</div>
</div>
</template>

View File

@@ -19,10 +19,12 @@ let currentItem = ref(null)
//Working
const mode = ref(route.params.mode || "show")
const itemInfo = ref({
id: null,
name: "",
licensePlate: "",
type: "",
driver: null
driver: null,
active: true
})
const tabItems = [{
@@ -76,13 +78,12 @@ const editCustomer = async () => {
}
const cancelEditorCreate = () => {
mode.value = "show"
itemInfo.value = {
id: 0,
name: "",
licensePlate: "",
type: ""
if(currentItem.value) {
router.push(`/vehicles/show/${currentItem.value.id}`)
} else {
router.push(`/vehicles`)
}
}
const updateItem = async () => {
@@ -135,6 +136,13 @@ setupPage()
<UTabs :items="tabItems">
<template #item="{item}">
<div v-if="item.label === 'Informationen'">
<InputGroup class="mb-3">
<UButton
@click="editCustomer"
>
Bearbeiten
</UButton>
</InputGroup>
Typ: {{currentItem.type}} <br>
Fahrer: {{dataStore.profiles.find(profile => profile.id === currentItem.driver) ? dataStore.profiles.find(profile => profile.id === currentItem.driver).fullName : 'Kein Fahrer gewählt'}} <br>
</div>
@@ -175,22 +183,7 @@ setupPage()
<template #footer>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editCustomer"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
<!-- TODO: Fahrzeug archivieren -->
</template>
@@ -229,7 +222,7 @@ setupPage()
>
<USelectMenu
v-model="itemInfo.driver"
:options="dataStore.profiles"
:options="[{id: null, fullName: 'Kein Fahrer'},...dataStore.profiles]"
option-attribute="fullName"
value-attribute="id"

View File

@@ -33,177 +33,178 @@ const setupPage = () => {
}
const editItem = async () => {
router.push(`/vendors/edit/${currentItem.id}`)
router.push(`/vendors/edit/${currentItem.value.id}`)
setupPage()
}
const cancelEditorCreate = () => {
router.push(`/vendors/`)
if(currentItem.value) {
router.push(`/vendors/show/${currentItem.value.id}`)
} else {
router.push(`/vendors`)
}
}
setupPage()
</script>
<template>
<div>
<UCard v-if="currentItem && mode == 'show'" >
<template #header>
{{currentItem.name}}
</template>
<h1
class=" mb-3 font-bold text-2xl truncate"
v-if="currentItem "
>Lieferant: {{currentItem.name}}</h1>
<UTabs
:items="[{label: 'Informationen'},{label: 'Logbuch'},{label: 'Ansprechpartner'}]"
v-if="currentItem && mode == 'show'"
>
<template #item="{item}">
<UCard class="mt-5">
<div v-if="item.label === 'Informationen'">
<Toolbar>
<UButton
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
</Toolbar>
<InputGroup>
<UButton
@click="router.push(`/contacts/create?vendor=${currentItem.id}`)"
>
+ Ansprechpartner
</UButton>
</InputGroup>
<div v-if="currentItem.infoData" class="text-wrap">
<p v-if="currentItem.infoData.street">Straße + Hausnummer: {{currentItem.infoData.street}}</p>
<p v-if="currentItem.infoData.zip && currentItem.infoData.city">PLZ + Ort: {{currentItem.infoData.zip}} {{currentItem.infoData.city}}</p>
<p v-if="currentItem.infoData.tel">Telefon: {{currentItem.infoData.tel}}</p>
<p v-if="currentItem.infoData.email">E-Mail: {{currentItem.infoData.email}}</p>
<p v-if="currentItem.infoData.web">Web: {{currentItem.infoData.web}}</p>
<p v-if="currentItem.infoData.ustid">USt-Id: {{currentItem.infoData.ustid}}</p>
</div>
</div>
<div v-else-if="item.label === 'Logbuch'">
<HistoryDisplay
type="vendor"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
<div v-else-if="item.label === 'Ansprechpartner'">
<Toolbar>
<UButton
@click="router.push(`/contacts/create?vendor=${currentItem.id}`)"
>
+ Ansprechpartner
</UButton>
</Toolbar>
<UTable
:rows="dataStore.getContactsByVendorId(currentItem.id)"
@select="(row) => router.push(`/contacts/show/${row.id}`)"
:columns="[{label: 'Anrede', key: 'salutation'},{label: 'Name', key: 'fullName'},{label: 'Rolle', key: 'role'}]"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine zugehörigen Ansprechpartner' }"
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
v-if="mode == 'show' && currentItem.id"
@click="editItem"
>
Bearbeiten
</UButton>
<UButton
color="red"
class="ml-2"
disabled
>
Archivieren
</UButton>
</template>
</UTable>
</div>
</UCard>
</template>
</UTabs>
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
</template>
<UFormGroup
label="Name:"
>
<UInput
v-model="itemInfo.name"
/>
</UFormGroup>
<UFormGroup
label="Lieferantennr.:"
>
<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>
</UCard>
<UCard v-else-if="mode === 'edit' || mode === 'create'" >
<template #header v-if="mode === 'edit'">
{{itemInfo.name}}
</template>
<UFormGroup
label="Name:"
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('vendors',itemInfo)"
>
<UInput
v-model="itemInfo.name"
/>
</UFormGroup>
<UFormGroup
label="Lieferantennr.:"
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('vendors',itemInfo)"
>
<UInput
v-model="itemInfo.vendorNumber"
placeholder="Leer lassen für automatisch generierte Nummer"
/>
</UFormGroup>
<UFormGroup
label="Straße + Hausnummer"
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
<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>
Abbrechen
</UButton>
</template>
<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>
</UCard>
<template #footer>
<UButton
v-if="mode == 'edit'"
@click="dataStore.updateItem('vendors',itemInfo)"
>
Speichern
</UButton>
<UButton
v-else-if="mode == 'create'"
@click="dataStore.createNewItem('vendors',itemInfo)"
>
Erstellen
</UButton>
<UButton
@click="cancelEditorCreate"
color="red"
class="ml-2"
>
Abbrechen
</UButton>
</template>
</UCard>
<HistoryDisplay
type="vendor"
v-if="currentItem"
:element-id="currentItem.id"
/>
</div>
</template>
<style scoped>

View File

@@ -1,26 +1,23 @@
<template>
<div id="main">
<!-- TODO: Kontakte erstellen und dem Lieferanten zuweisen -->
<div class="flex items-center gap-1">
<UButton @click="router.push(`/vendors/create/`)">+ Lieferant</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</div>
<Toolbar>
<UButton @click="router.push(`/vendors/create/`)">+ Lieferant</UButton>
<UInput
v-model="searchString"
placeholder="Suche..."
/>
</Toolbar>
<div class="table">
<UTable
:rows="filteredRows"
:columns="itemColumns"
@select="selectItem"
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
/>
</div>
</template>
<script setup>

View File

@@ -87,6 +87,11 @@ export const useDataStore = defineStore('data', () => {
incomingInvoices: {
label: "Eingangsrechnungen",
labelSingle: "Eingangsrechnung"
},
inventoryitems: {
label: "Inventarartikel",
labelSingle: "Inventarartikel",
redirect: true
}
}
@@ -151,12 +156,13 @@ export const useDataStore = defineStore('data', () => {
const accounts = ref([])
const taxTypes = ref([])
const plants = ref([])
const inventoryItems = ref([])
const inventoryitems = ref([])
const chats = ref([])
const messages = ref([])
const createddocuments = ref([])
const workingtimes = ref([])
const phasesTemplates = ref([])
const emailAccounts = ref([])
const rights = ref({
@@ -217,13 +223,10 @@ export const useDataStore = defineStore('data', () => {
}
])
async function initializeData () {
//console.log(user.value.id)
let profile = (await supabase.from("profiles").select().eq("id",user.value.id).single()).data
async function initializeData (userId) {
let profile = (await supabase.from("profiles").select().eq("id",userId).single()).data
//console.log(profile)
currentTenant.value = profile.tenant//profiles.value.find(i => i.id === user.value.id).tenants[0].id
currentTenant.value = profile.tenant
await fetchData()
@@ -273,6 +276,7 @@ export const useDataStore = defineStore('data', () => {
await fetchCreatedDocuments()
await fetchWorkingTimes()
await fetchPhasesTemplates()
await fetchEmailAccounts()
loaded.value = true
}
@@ -280,7 +284,7 @@ export const useDataStore = defineStore('data', () => {
console.log("Clear")
loaded.value = false
ownTenant.value = {}
profiles.value= []
profiles.value = []
events.value= []
customers.value= []
tasks.value= []
@@ -307,12 +311,13 @@ export const useDataStore = defineStore('data', () => {
accounts.value = []
taxTypes.value = []
plants.value = []
inventoryItems.value = []
inventoryitems.value = []
chats.value = []
messages.value = []
createddocuments.value = []
workingtimes.value = []
phasesTemplates.value = []
emailAccounts.value = []
}
function hasRight (right) {
@@ -338,13 +343,13 @@ export const useDataStore = defineStore('data', () => {
(payload) => {
//console.log(payload)
if(payload.eventType === 'INSERT') {
/*if(payload.eventType === 'INSERT') {
const c = payload.table + '.value.push(' + JSON.stringify(payload.new) + ')'
eval(c)
} else if(payload.eventType === 'UPDATE'){
const c = payload.table + '.value[' + payload.table + '.value.findIndex(i => i.id === ' + JSON.stringify(payload.old.id) + ')] = ' + JSON.stringify(payload.new)
eval(c)
}
}*/
}
)
.subscribe()
@@ -371,7 +376,7 @@ export const useDataStore = defineStore('data', () => {
if(supabaseError) {
console.log(supabaseError)
} else if (supabaseData) {
//await eval( dataType + '.value.push(' + JSON.stringify(...supabaseData) + ')')
await eval( dataType + '.value.push(' + JSON.stringify(...supabaseData) + ')')
toast.add({title: `${dataTypes[dataType].labelSingle} hinzugefügt`})
if(dataTypes[dataType].redirect) await router.push(`/${dataType}/show/${supabaseData[0].id}`)
return supabaseData
@@ -396,7 +401,7 @@ export const useDataStore = defineStore('data', () => {
}
const uploadFiles = async (formData, files, upsert) => {
console.log(files)
let documentsToInsert = []
const uploadSingleFile = async (file) => {
@@ -436,11 +441,13 @@ export const useDataStore = defineStore('data', () => {
} else if( files.length > 1) {
for(let i = 0; i < files.length; i++){
uploadSingleFile(files[i])
await uploadSingleFile(files[i])
}
}
console.log(documentsToInsert)
const {data, error} = await supabase
.from("documents")
.insert(documentsToInsert)
@@ -464,7 +471,7 @@ export const useDataStore = defineStore('data', () => {
}
async function fetchProfiles () {
profiles.value = (await supabase.from("profiles").select('* , tenants (id, name)')).data
profiles.value = (await supabase.from("profiles").select('* , tenants (id, name)')/*.eq("tenant", currentTenant.value)*/.order("fullName")).data
}
async function fetchBankAccounts () {
bankAccounts.value = (await supabase.from("bankaccounts").select().eq('tenant', currentTenant.value)).data
@@ -543,7 +550,7 @@ export const useDataStore = defineStore('data', () => {
plants.value = (await supabase.from("plants").select().eq('tenant', currentTenant.value)).data
}
async function fetchInventoryItems () {
inventoryItems.value = (await supabase.from("inventoryitems").select().eq('tenant', currentTenant.value)).data
inventoryitems.value = (await supabase.from("inventoryitems").select().eq('tenant', currentTenant.value)).data
}
async function fetchChats() {
chats.value = (await supabase.from("chats").select()).data
@@ -563,25 +570,35 @@ export const useDataStore = defineStore('data', () => {
phasesTemplates.value = (await supabase.from("phasesTemplates").select().eq('tenant', currentTenant.value).order('created_at', {ascending:true})).data
}
async function fetchEmailAccounts() {
emailAccounts.value = (await supabase.from("emailAccounts").select().eq('tenant', currentTenant.value)).data
}
async function fetchDocuments () {
let tempDocuments = (await supabase.from("documents").select().eq('tenant', currentTenant.value)).data
let paths = []
tempDocuments.forEach(doc => {
paths.push(doc.path)
})
if(tempDocuments.length > 0){
let paths = []
tempDocuments.forEach(doc => {
paths.push(doc.path)
})
const {data,error} = await supabase.storage.from('files').createSignedUrls(paths,3600)
const {data,error} = await supabase.storage.from('files').createSignedUrls(paths,3600)
tempDocuments = tempDocuments.map((doc,index) => {
tempDocuments = tempDocuments.map((doc,index) => {
return {
...doc,
url: data[index].signedUrl
}
})
documents.value = tempDocuments
} else {
documents.value = []
}
return {
...doc,
url: data[index].signedUrl
}
})
documents.value = tempDocuments
}
async function addHistoryItem(text, user, elementId, resourceType) {
@@ -615,7 +632,8 @@ export const useDataStore = defineStore('data', () => {
})
const getOwnProfile = computed(() => {
return profiles.value.find(profile => profile.id === user.value.id)
return profiles.value.find(i => i.id === user.value.id)
})
const getMovementsBySpace = computed(() => (spaceId) => {
@@ -626,6 +644,18 @@ export const useDataStore = defineStore('data', () => {
return contacts.value.filter(item => item.customer === customerId)
})
const getProjectsByCustomerId = computed(() => (customerId) => {
return projects.value.filter(item => item.customer === customerId)
})
const getPlantsByCustomerId = computed(() => (customerId) => {
return plants.value.filter(item => item.customer === customerId)
})
const getContractsByCustomerId = computed(() => (customerId) => {
return contracts.value.filter(item => item.customer === customerId)
})
const getContactsByVendorId = computed(() => (vendorId) => {
return contacts.value.filter(item => item.vendor === vendorId)
})
@@ -646,6 +676,10 @@ export const useDataStore = defineStore('data', () => {
return documents.value.filter(item => item.vehicle === itemId && !item.tags.includes("Archiviert"))
})
const getDocumentsByProductId = computed(() => (itemId) => {
return documents.value.filter(item => item.product === itemId && !item.tags.includes("Archiviert"))
})
const getEventsByProjectId = computed(() => (projectId) => {
return events.value.filter(item => item.project === projectId)
})
@@ -722,7 +756,7 @@ export const useDataStore = defineStore('data', () => {
id: `F-${vehicle.id}`
}
}),
...inventoryItems.value.filter(i=> i.usePlanning).map(item => {
...inventoryitems.value.filter(i=> i.usePlanning).map(item => {
return {
type: 'Inventar',
title: item.name,
@@ -903,6 +937,10 @@ export const useDataStore = defineStore('data', () => {
return createddocuments.value.find(item => item.id === documentId)
})
const getInventoryItemById = computed(() => (itemId) => {
return inventoryitems.value.find(item => item.id === itemId)
})
const getProjectById = computed(() => (itemId) => {
if(projects.value.find(i => i.id === itemId)) {
let project = projects.value.find(project => project.id === itemId)
@@ -961,13 +999,15 @@ export const useDataStore = defineStore('data', () => {
accounts,
taxTypes,
plants,
inventoryItems,
inventoryitems,
chats,
messages,
createddocuments,
workingtimes,
phasesTemplates,
emailAccounts,
documentTypesForCreation,
//Functions
createNewItem,
updateItem,
@@ -1008,11 +1048,15 @@ export const useDataStore = defineStore('data', () => {
getOwnProfile,
getMovementsBySpace,
getContactsByCustomerId,
getProjectsByCustomerId,
getPlantsByCustomerId,
getContractsByCustomerId,
getContactsByVendorId,
getDocumentsByProjectId,
getDocumentsByPlantId,
getDocumentsByContractId,
getDocumentsByVehicleId,
getDocumentsByProductId,
getEventsByProjectId,
getTimesByProjectId,
getTasksByProjectId,
@@ -1045,7 +1089,8 @@ export const useDataStore = defineStore('data', () => {
getProfileById,
getAccountById,
getPlantById,
getCreatedDocumentById
getCreatedDocumentById,
getInventoryItemById,
}