Added Vehicles
Added Bankimport, BankAccounts, BankStatements Some Visual Changes Added Contacts Changes in VendorInvoices Added layouts with default an one for Login PAge Added Input Group Component
This commit is contained in:
@@ -10,7 +10,7 @@ const colorMode = useColorMode()
|
||||
const supabase = useSupabaseClient()
|
||||
const tenants = (await supabase.from("tenants").select()).data
|
||||
const {loaded, profiles} = storeToRefs(useDataStore())
|
||||
const {fetchData, getProfileById} = useDataStore()
|
||||
const {fetchData, getProfileById, clearStore} = useDataStore()
|
||||
const userProfile = (user.value ? getProfileById(user.value.id) : {})
|
||||
//console.log(userProfile)
|
||||
|
||||
@@ -43,6 +43,11 @@ const navLinks = [
|
||||
to: "/vendors",
|
||||
icon: "i-heroicons-truck"
|
||||
},
|
||||
{
|
||||
label: "Kontakte",
|
||||
to: "/contacts",
|
||||
icon: "i-heroicons-user-group"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -54,6 +59,11 @@ const navLinks = [
|
||||
to: "/vendorinvoices",
|
||||
icon: "i-heroicons-user-group"
|
||||
},
|
||||
{
|
||||
label: "Bank",
|
||||
to: "/banking",
|
||||
icon: "i-heroicons-currency-euro"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -128,6 +138,11 @@ const navLinks = [
|
||||
to: "/inventory/spaces",
|
||||
icon: "i-heroicons-square-3-stack-3d"
|
||||
},
|
||||
{
|
||||
label: "Fahrzeuge",
|
||||
to: "/vehicles",
|
||||
icon: "i-heroicons-truck"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
@@ -201,8 +216,9 @@ const items = [
|
||||
icon: 'i-heroicons-cog-8-tooth',
|
||||
to: "/settings/externalDevices"
|
||||
},{
|
||||
label: 'Settings',
|
||||
icon: 'i-heroicons-cog-8-tooth'
|
||||
label: 'Benutzer',
|
||||
icon: 'i-heroicons-user-group',
|
||||
to: "/settings/users"
|
||||
}], /*[{
|
||||
label: 'Documentation',
|
||||
icon: 'i-heroicons-book-open'
|
||||
@@ -215,9 +231,11 @@ const items = [
|
||||
}],*/ [{
|
||||
label: 'Sign out',
|
||||
icon: 'i-heroicons-arrow-left-on-rectangle',
|
||||
click: () => {
|
||||
supabase.auth.signOut()
|
||||
router.push("/login")
|
||||
click: async () => {
|
||||
await supabase.auth.signOut()
|
||||
await clearStore()
|
||||
await router.push("/login")
|
||||
|
||||
}
|
||||
}]
|
||||
]
|
||||
@@ -251,6 +269,7 @@ const items = [
|
||||
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
||||
<UAvatar
|
||||
:alt="userProfile ? userProfile.firstName + ' ' + userProfile.lastName : '' "
|
||||
icon="i-heroicons-photo"
|
||||
/>
|
||||
|
||||
<template #account="{ item }">
|
||||
@@ -274,14 +293,11 @@ const items = [
|
||||
</UHeader>
|
||||
<UDivider />
|
||||
<div class="m-3" id="contentContainer">
|
||||
<NuxtPage
|
||||
v-if="loaded"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<UProgress animation="carousel" />
|
||||
</div>
|
||||
<NuxtLayout>
|
||||
<NuxtPage/>
|
||||
</NuxtLayout>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- <UFooter>
|
||||
<template #left>
|
||||
|
||||
@@ -16,7 +16,7 @@ const props = defineProps({
|
||||
|
||||
const {document, openShowModal:openShowModalProp } = props;
|
||||
const {fetchDocuments, getDocumentTags, fetchVendorInvoices} = useDataStore()
|
||||
const {projects, customers} = storeToRefs(useDataStore())
|
||||
const {projects, customers, vendorInvoices} = storeToRefs(useDataStore())
|
||||
const tags = getDocumentTags
|
||||
const openShowModal = ref(false)
|
||||
|
||||
@@ -103,15 +103,35 @@ const updateDocument = async () => {
|
||||
}
|
||||
|
||||
const createVendorInvoice = async () => {
|
||||
const {data,error} = await supabase
|
||||
const {data:vendorInvoiceData,error:vendorInvoiceError} = await supabase
|
||||
.from("vendorInvoices")
|
||||
.insert([{
|
||||
document: document.id,
|
||||
}])
|
||||
.select()
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else if(data) {
|
||||
if(vendorInvoiceError) {
|
||||
console.log(vendorInvoiceError)
|
||||
} else if(vendorInvoiceData) {
|
||||
|
||||
const {data:documentData,error:documentError} = await supabase
|
||||
.from("documents")
|
||||
.update({
|
||||
vendorInvoice: vendorInvoiceData[0].id
|
||||
})
|
||||
.eq('id',document.id)
|
||||
.select()
|
||||
|
||||
if(documentError) {
|
||||
console.log(documentError)
|
||||
} else {
|
||||
toast.add({title: "Dokument aktualisiert"})
|
||||
fetchDocuments()
|
||||
openShowModal.value = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fetchVendorInvoices()
|
||||
await router.push("/vendorinvoices")
|
||||
}
|
||||
@@ -135,6 +155,10 @@ const createVendorInvoice = async () => {
|
||||
v-model="document.selected"
|
||||
class="ml-2"
|
||||
/>
|
||||
<br>
|
||||
<UBadge
|
||||
v-if="document.vendorInvoice"
|
||||
>{{vendorInvoices.find(item => item.id === document.vendorInvoice) ? vendorInvoices.find(item => item.id === document.vendorInvoice).reference : ''}}</UBadge>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -226,7 +250,7 @@ const createVendorInvoice = async () => {
|
||||
.documentListItem {
|
||||
display:block;
|
||||
width: 15vw;
|
||||
height: 30vh;
|
||||
height: 33vh;
|
||||
padding:1em;
|
||||
margin: 0.7em;
|
||||
border: 1px solid lightgrey;
|
||||
|
||||
20
spaces/components/InputGroup.vue
Normal file
20
spaces/components/InputGroup.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
gap: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
|
||||
const {gap} = props
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="`flex items-center gap-${gap}`">
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
21
spaces/layouts/default.vue
Normal file
21
spaces/layouts/default.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
const {loaded, profiles} = storeToRefs(useDataStore())
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
|
||||
<slot v-if="loaded"/>
|
||||
|
||||
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<UProgress animation="carousel" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
11
spaces/layouts/notLoggedIn.vue
Normal file
11
spaces/layouts/notLoggedIn.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,18 +0,0 @@
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
const supabase = useSupabaseClient()
|
||||
console.log(route.params.account)
|
||||
|
||||
const accounts = (await supabase.from("bankAccounts").select()).data
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,103 +1,88 @@
|
||||
<script setup>
|
||||
import Papa from 'papaparse'
|
||||
|
||||
const supabase = useSupabaseClient()
|
||||
|
||||
const accounts = (supabase.from("bankAccounts").select()).data
|
||||
|
||||
let items = ref([])
|
||||
async function readSingleFile(evt) {
|
||||
var f = document.getElementById('fileInput').files[0];
|
||||
if (f) {
|
||||
let results = []
|
||||
let text = await f.text()
|
||||
results = Papa.parse(text).data
|
||||
console.log(results[0])
|
||||
results = results.slice(1)
|
||||
results = results.map(temp => {
|
||||
return {
|
||||
iban: temp[1],
|
||||
bank: temp[3],
|
||||
date: temp[4],
|
||||
partnerName: temp[6],
|
||||
partnerIban: temp[7],
|
||||
type: temp[9],
|
||||
text: temp[10],
|
||||
value: temp[11],
|
||||
currency: temp[12]
|
||||
}
|
||||
})
|
||||
|
||||
console.log(results)
|
||||
items.value = results
|
||||
|
||||
parseStatements()
|
||||
|
||||
} else {
|
||||
alert("Failed to load file");
|
||||
}
|
||||
}
|
||||
|
||||
const newStatements = ref([])
|
||||
|
||||
const parseStatements = async () => {
|
||||
items.value.forEach(item => {
|
||||
if(item.date) {
|
||||
let returnObj = {
|
||||
date: item.date,
|
||||
partnerIban: item.partnerIban,
|
||||
partnerName: item.partnerName,
|
||||
text: item.text,
|
||||
value: Number(item.value.replace(",",".")),
|
||||
|
||||
}
|
||||
|
||||
newStatements.value.push(returnObj)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const {data,error} = await supabase
|
||||
.from("bankStatements")
|
||||
.insert(newStatements.value)
|
||||
.select()
|
||||
|
||||
console.log(data)
|
||||
console.log(error)
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<UInput
|
||||
type="file"
|
||||
id="fileInput"
|
||||
/>
|
||||
<UButton
|
||||
@click="readSingleFile"
|
||||
>Test</UButton>
|
||||
<div id="main">
|
||||
<!-- TODO: Kontakte erstellen und dem Kunden zuweisen -->
|
||||
|
||||
<table>
|
||||
<tr
|
||||
v-for="temp in items"
|
||||
<!--<InputGroup>
|
||||
<UButton @click="router.push(`/banking/accounts/create/`)">+ Kontakt</UButton>
|
||||
|
||||
<UInput
|
||||
v-model="searchString"
|
||||
placeholder="Suche..."
|
||||
/>
|
||||
</InputGroup>-->
|
||||
|
||||
|
||||
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="itemColumns"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
>
|
||||
<td>{{temp.date}}</td>
|
||||
<td>{{temp.partnerName}}</td>
|
||||
<td>{{temp.text}}</td>
|
||||
<td>{{temp.value}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</UTable>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const {bankAccounts} = storeToRefs(useDataStore())
|
||||
const mode = ref("show")
|
||||
|
||||
const itemColumns = [
|
||||
|
||||
{
|
||||
key: "name",
|
||||
label: "Name",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "iban",
|
||||
label: "IBAN",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "bankId",
|
||||
label: "Bank",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "ownerName",
|
||||
label: "Besitzer",
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const selectItem = (item) => {
|
||||
router.push(`/banking/statements/${item.id} `)
|
||||
}
|
||||
|
||||
|
||||
const searchString = ref('')
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
bankAccounts.value = bankAccounts.value.filter(account => account.used)
|
||||
|
||||
if(!searchString.value) {
|
||||
return bankAccounts.value
|
||||
}
|
||||
|
||||
return bankAccounts.value.filter(item => {
|
||||
return Object.values(item).some((value) => {
|
||||
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
142
spaces/pages/banking/statements/[[accountId]].vue
Normal file
142
spaces/pages/banking/statements/[[accountId]].vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script setup>
|
||||
import * as dayjs from "dayjs";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const supabase = useSupabaseClient()
|
||||
|
||||
const {bankStatements,bankAccounts} = storeToRefs(useDataStore())
|
||||
|
||||
const searchString = ref("")
|
||||
const showAssigned = ref(false)
|
||||
const selectedAccount = ref(0)
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
let statements = bankStatements.value
|
||||
|
||||
if(!showAssigned.value) {
|
||||
statements = statements.filter(statement => !(statement.customerInvoice || statement.vendorInvoice))
|
||||
}
|
||||
|
||||
if(selectedAccount.value !== 0) {
|
||||
statements = statements.filter(statement => statement.account === selectedAccount.value)
|
||||
}
|
||||
|
||||
if(searchString.value.length > 0) {
|
||||
return statements.value.filter(item => {
|
||||
return Object.values(item).some((value) => {
|
||||
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||
})
|
||||
})
|
||||
} else {
|
||||
return statements
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
const showStatementSlideover = ref(false)
|
||||
const selectedStatement = ref({})
|
||||
|
||||
const selectStatement = (statement) => {
|
||||
selectedStatement.value = statement
|
||||
showStatementSlideover.value = true
|
||||
}
|
||||
|
||||
const statementColumns = [
|
||||
{
|
||||
key:"amount",
|
||||
label: "Betrag"
|
||||
},
|
||||
{
|
||||
key:"date",
|
||||
label: "Datum",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "credName",
|
||||
label: "Empfänger"
|
||||
},
|
||||
{
|
||||
key: "debName",
|
||||
label: "Sender"
|
||||
},
|
||||
{
|
||||
key: "text",
|
||||
label: "Verwendungszweck"
|
||||
},
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USlideover
|
||||
v-model="showStatementSlideover"
|
||||
>
|
||||
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
|
||||
|
||||
|
||||
|
||||
<DevOnly>
|
||||
{{selectedStatement}}
|
||||
</DevOnly>
|
||||
</UCard>
|
||||
|
||||
</USlideover>
|
||||
|
||||
<InputGroup gap="2">
|
||||
<UInput
|
||||
v-model="searchString"
|
||||
placeholder="Suche..."
|
||||
/>
|
||||
<USelectMenu
|
||||
:options="bankAccounts.filter(account => account.used)"
|
||||
v-model="selectedAccount"
|
||||
option-attribute="iban"
|
||||
value-attribute="id"
|
||||
>
|
||||
<template #label>
|
||||
{{bankAccounts.find(account => account.id === selectedAccount) ? bankAccounts.find(account => account.id === selectedAccount).iban : "Kontoauswählen"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
<UCheckbox
|
||||
v-model="showAssigned"
|
||||
label="Zugeordnete Anzeigen"
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
<UTable
|
||||
:rows="bankStatements"
|
||||
:columns="statementColumns"
|
||||
@select="selectStatement"
|
||||
>
|
||||
<template #amount-data="{row}">
|
||||
<span
|
||||
v-if="row.amount >= 0"
|
||||
class="text-primary-500"
|
||||
>
|
||||
{{row.amount.toFixed(2) + " €"}}
|
||||
</span>
|
||||
<span
|
||||
v-if="row.amount < 0"
|
||||
class="text-rose-600"
|
||||
>
|
||||
{{row.amount.toFixed(2) + " €"}}
|
||||
</span>
|
||||
</template>
|
||||
<template #date-data="{row}">
|
||||
{{dayjs(row.date).format("DD.MM.YY")}}
|
||||
</template>
|
||||
</UTable>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
291
spaces/pages/contacts/[mode]/[[id]].vue
Normal file
291
spaces/pages/contacts/[mode]/[[id]].vue
Normal file
@@ -0,0 +1,291 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
//
|
||||
const supabase = useSupabaseClient()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const id = ref(route.params.id ? route.params.id : null )
|
||||
|
||||
//Store
|
||||
const {customers, vendors, contacts } = storeToRefs(useDataStore())
|
||||
const {fetchContacts, getContactById} = useDataStore()
|
||||
|
||||
let currentContact = null
|
||||
|
||||
|
||||
|
||||
//Working
|
||||
const mode = ref(route.params.mode || "show")
|
||||
const itemInfo = ref({
|
||||
active: true
|
||||
})
|
||||
|
||||
//Functions
|
||||
const setupPage = () => {
|
||||
if(mode.value === "show" || mode.value === "edit"){
|
||||
currentContact = getContactById(Number(useRoute().params.id))
|
||||
}
|
||||
|
||||
if(mode.value === "edit") itemInfo.value = currentContact
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const createItem = async () => {
|
||||
let fullName = itemInfo.value.firstName + ' ' + itemInfo.value.lastName
|
||||
|
||||
|
||||
const {data,error} = await supabase
|
||||
.from("contacts")
|
||||
.insert([{...itemInfo.value, fullName}])
|
||||
.select()
|
||||
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
}
|
||||
toast.add({title: "Kontakt erfolgreich erstellt"})
|
||||
await fetchContacts()
|
||||
router.push(`/contacts/show/${data[0].id}`)
|
||||
setupPage()
|
||||
}
|
||||
}
|
||||
|
||||
const editCustomer = async () => {
|
||||
router.push(`/contacts/edit/${currentContact.id}`)
|
||||
setupPage()
|
||||
}
|
||||
|
||||
const cancelEditorCreate = () => {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
}
|
||||
}
|
||||
|
||||
const updateCustomer = async () => {
|
||||
const {error} = await supabase
|
||||
.from("contacts")
|
||||
.update(itemInfo.value)
|
||||
.eq('id',itemInfo.value.id)
|
||||
console.log(error)
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
}
|
||||
toast.add({title: "Kontakt erfolgreich gespeichert"})
|
||||
fetchContacts()
|
||||
}
|
||||
|
||||
|
||||
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
<span v-if="currentContact.customer">Kunde: {{customers.find(customer => customer.id === currentContact.customer) ? customers.find(customer => customer.id === currentContact.customer).name : "" }}</span><br>
|
||||
<span v-if="currentContact.vendor">Lieferant: {{vendors.find(vendor => vendor.id === currentContact.vendor) ? 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>
|
||||
|
||||
|
||||
|
||||
<DevOnly>
|
||||
<UDivider
|
||||
class="my-3"
|
||||
/>
|
||||
{{currentContact}}
|
||||
</DevOnly>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<UButton
|
||||
v-if="mode == 'show' && currentContact.id"
|
||||
@click="editCustomer"
|
||||
>
|
||||
Bearbeiten
|
||||
</UButton>
|
||||
<UButton
|
||||
color="red"
|
||||
class="ml-2"
|
||||
disabled
|
||||
>
|
||||
Archivieren
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
</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
|
||||
v-model="itemInfo.customer"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
:options="customers"
|
||||
searchable
|
||||
:search-attributes="['name']"
|
||||
>
|
||||
<template #label>
|
||||
{{customers.find(customer => customer.id === itemInfo.customer) ? 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="vendors"
|
||||
searchable
|
||||
:search-attributes="['name']"
|
||||
>
|
||||
<template #label>
|
||||
{{vendors.find(vendor => vendor.id === itemInfo.vendor) ? 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="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="updateCustomer"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
<UButton
|
||||
v-else-if="mode == 'create'"
|
||||
@click="createItem"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="cancelEditorCreate"
|
||||
color="red"
|
||||
class="ml-2"
|
||||
>
|
||||
Abbrechen
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
90
spaces/pages/contacts/index.vue
Normal file
90
spaces/pages/contacts/index.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="itemColumns"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
>
|
||||
<template #customer-data="{row}">
|
||||
{{customers.find(customer => customer.id === row.customer) ? customers.find(customer => customer.id === row.customer).name : ''}}
|
||||
</template>
|
||||
<template #vendor-data="{row}">
|
||||
{{vendors.find(vendor => vendor.id === row.vendor) ? vendors.find(vendor => vendor.id === row.vendor).name : ''}}
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const {contacts,customers,vendors} = storeToRefs(useDataStore())
|
||||
const mode = ref("show")
|
||||
|
||||
const itemColumns = [
|
||||
|
||||
{
|
||||
key: "fullName",
|
||||
label: "Name",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "customer",
|
||||
label: "Kunde",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "vendor",
|
||||
label: "Lieferant",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "role",
|
||||
label: "Rolle",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const selectItem = (item) => {
|
||||
router.push(`/contacts/show/${item.id} `)
|
||||
}
|
||||
|
||||
|
||||
const searchString = ref('')
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
if(!searchString.value) {
|
||||
return contacts.value
|
||||
}
|
||||
|
||||
return contacts.value.filter(item => {
|
||||
return Object.values(item).some((value) => {
|
||||
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -119,7 +119,9 @@ setupPage()
|
||||
Beschreibung:<br>
|
||||
{{currentContract.description}}<br>
|
||||
|
||||
|
||||
<DevOnly>
|
||||
{{currentContract}}
|
||||
</DevOnly>
|
||||
|
||||
|
||||
<template #footer>
|
||||
|
||||
@@ -11,8 +11,8 @@ const toast = useToast()
|
||||
const id = ref(route.params.id ? route.params.id : null )
|
||||
|
||||
//Store
|
||||
const {customers } = storeToRefs(useDataStore())
|
||||
const {fetchCustomers, getCustomerById} = useDataStore()
|
||||
const {customers, contacts } = storeToRefs(useDataStore())
|
||||
const {fetchCustomers, getCustomerById, getContactsByCustomerId} = useDataStore()
|
||||
|
||||
let currentCustomer = null
|
||||
|
||||
@@ -130,6 +130,24 @@ setupPage()
|
||||
Notizen:<br>
|
||||
{{currentCustomer.notes}}<br>
|
||||
|
||||
<UDivider
|
||||
class="my-2"
|
||||
/>
|
||||
|
||||
Kontakte: <br>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Anrede</th>
|
||||
<th>Name</th>
|
||||
<th>Rolle</th>
|
||||
</tr>
|
||||
<tr v-for="contact in getContactsByCustomerId(currentCustomer.id)">
|
||||
<td>{{contact.salutation}}</td>
|
||||
<td>{{contact.fullName}}</td>
|
||||
<td>{{contact.role}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Kontakte:<br>
|
||||
<!– <ul>
|
||||
<li v-for="contact in currentCustomer.contacts.data">{{contact.lastName}}, {{contact.firstName}}</li>
|
||||
@@ -279,5 +297,7 @@ setupPage()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
td {
|
||||
padding: 0.2em;
|
||||
}
|
||||
</style>
|
||||
@@ -174,7 +174,11 @@ setupPage()
|
||||
value-attribute="id"
|
||||
searchable
|
||||
:search-attributes="['name']"
|
||||
/>
|
||||
>
|
||||
<template #label>
|
||||
{{customers.find(customer => customer.id === itemInfo.customer) ? customers.find(customer => customer.id === itemInfo.customer).name : "Kunde auswählen"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
|
||||
16
spaces/pages/settings/users.vue
Normal file
16
spaces/pages/settings/users.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
const {profiles} = storeToRefs(useDataStore())
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
{{profiles}}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -12,7 +12,7 @@ const supabase = useSupabaseClient()
|
||||
const user = useSupabaseUser()
|
||||
const toast = useToast()
|
||||
|
||||
const {times, projects, profiles} = storeToRefs(useDataStore())
|
||||
const {times, projects, profiles, jobs} = storeToRefs(useDataStore())
|
||||
const {fetchTimes, getTimeTypes} = useDataStore()
|
||||
const timeTypes = getTimeTypes
|
||||
const timeInfo = ref({
|
||||
@@ -71,6 +71,10 @@ const columns = [
|
||||
key: "projectId",
|
||||
label: "Projekt"
|
||||
},
|
||||
{
|
||||
key: "job",
|
||||
label: "Job"
|
||||
},
|
||||
{
|
||||
key: "notes",
|
||||
label: "Notizen"
|
||||
@@ -218,6 +222,21 @@ const selectStartedTime = () => {
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Job:"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="jobs"
|
||||
option-attribute="title"
|
||||
value-attribute="id"
|
||||
v-model="runningTimeInfo.job"
|
||||
>
|
||||
<template #label>
|
||||
{{ jobs.find(job => job.id === runningTimeInfo.job) ? jobs.find(job => job.id === runningTimeInfo.job).title : "Job auswählen" }}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup
|
||||
label="Kategorie:"
|
||||
>
|
||||
@@ -307,6 +326,20 @@ const selectStartedTime = () => {
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Job:"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="jobs"
|
||||
option-attribute="title"
|
||||
value-attribute="id"
|
||||
v-model="createTimeInfo.job"
|
||||
>
|
||||
<template #label>
|
||||
{{ jobs.find(job => job.id === runningTimeInfo.job) ? jobs.find(job => job.id === runningTimeInfo.job).title : "Job auswählen" }}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Typ:"
|
||||
>
|
||||
@@ -361,6 +394,9 @@ const selectStartedTime = () => {
|
||||
<template #projectId-data="{row}">
|
||||
{{projects.find(project => project.id === row.projectId) ? projects.find(project => project.id === row.projectId).name : ""}}
|
||||
</template>
|
||||
<template #job-data="{row}">
|
||||
{{jobs.find(job => job.id === row.job) ? jobs.find(job => job.id === row.job).title : ""}}
|
||||
</template>
|
||||
</UTable>
|
||||
</template>
|
||||
|
||||
|
||||
223
spaces/pages/vehicles/[mode]/[[id]].vue
Normal file
223
spaces/pages/vehicles/[mode]/[[id]].vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
//
|
||||
const supabase = useSupabaseClient()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const id = ref(route.params.id ? route.params.id : null )
|
||||
|
||||
//Store
|
||||
const {vehicles, profiles } = storeToRefs(useDataStore())
|
||||
const {fetchVehicles, getVehicleById} = useDataStore()
|
||||
|
||||
let currentItem = null
|
||||
|
||||
|
||||
|
||||
//Working
|
||||
const mode = ref(route.params.mode || "show")
|
||||
const itemInfo = ref({
|
||||
id: 0,
|
||||
name: "",
|
||||
licensePlate: "",
|
||||
type: "",
|
||||
driver: ""
|
||||
})
|
||||
|
||||
//Functions
|
||||
const setupPage = () => {
|
||||
if(mode.value === "show" || mode.value === "edit"){
|
||||
currentItem = getVehicleById(Number(useRoute().params.id))
|
||||
}
|
||||
|
||||
if(mode.value === "edit") itemInfo.value = currentItem
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const createItem = async () => {
|
||||
const {data,error} = await supabase
|
||||
.from("vehicles")
|
||||
.insert([itemInfo.value])
|
||||
.select()
|
||||
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: ""
|
||||
}
|
||||
toast.add({title: "Fahrzeug erfolgreich erstellt"})
|
||||
await fetchVehicles()
|
||||
router.push(`/vehicles/show/${data[0].id}`)
|
||||
setupPage()
|
||||
}
|
||||
}
|
||||
|
||||
const editCustomer = async () => {
|
||||
router.push(`/vehicles/edit/${currentItem.id}`)
|
||||
setupPage()
|
||||
}
|
||||
|
||||
const cancelEditorCreate = () => {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
licensePlate: "",
|
||||
type: ""
|
||||
}
|
||||
}
|
||||
|
||||
const updateCustomer = async () => {
|
||||
const {error} = await supabase
|
||||
.from("vehicles")
|
||||
.update(itemInfo.value)
|
||||
.eq('id',itemInfo.value.id)
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
mode.value = "show"
|
||||
itemInfo.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
licensePlate: "",
|
||||
type: ""
|
||||
}
|
||||
toast.add({title: "Fahrzeug erfolgreich gespeichert"})
|
||||
fetchVehicles()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
setupPage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<UCard v-if="currentItem && mode == 'show'" >
|
||||
<template #header>
|
||||
<UBadge
|
||||
v-if="currentItem.active"
|
||||
>
|
||||
Fahrzeug aktiv
|
||||
</UBadge>
|
||||
<UBadge
|
||||
v-else
|
||||
color="red"
|
||||
>
|
||||
Fahrzeug gesperrt
|
||||
</UBadge>
|
||||
{{currentItem.licensePlate}}
|
||||
</template>
|
||||
|
||||
Typ: {{currentItem.type}} <br>
|
||||
Fahrer: {{profiles.find(profile => profile.id === currentItem.driver) ? profiles.find(profile => profile.id === currentItem.driver).fullName : 'Kein Fahrer gewählt'}} <br>
|
||||
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
</UCard>
|
||||
<UCard v-else-if="mode == 'edit' || mode == 'create'" >
|
||||
<template #header>
|
||||
{{itemInfo.licensePlate}}
|
||||
</template>
|
||||
|
||||
<UFormGroup
|
||||
label="Kennzeichen:"
|
||||
>
|
||||
<UInput
|
||||
v-model="itemInfo.licensePlate"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
|
||||
<UFormGroup
|
||||
label="Fahrzeug aktiv:"
|
||||
>
|
||||
<UCheckbox
|
||||
v-model="itemInfo.active"
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Typ:"
|
||||
>
|
||||
<UTextarea
|
||||
v-model="itemInfo.type"
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Fahrer:"
|
||||
>
|
||||
<USelectMenu
|
||||
v-model="itemInfo.driver"
|
||||
:options="profiles"
|
||||
option-attribute="fullName"
|
||||
value-attribute="id"
|
||||
|
||||
>
|
||||
<template #label>
|
||||
{{profiles.find(profile => profile.id === itemInfo.driver) ? profiles.find(profile => profile.id === itemInfo.driver).fullName : 'Kein Fahrer ausgewählt'}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<UButton
|
||||
v-if="mode == 'edit'"
|
||||
@click="updateCustomer"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
<UButton
|
||||
v-else-if="mode == 'create'"
|
||||
@click="createItem"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="cancelEditorCreate"
|
||||
color="red"
|
||||
class="ml-2"
|
||||
>
|
||||
Abbrechen
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
</UCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
74
spaces/pages/vehicles/index.vue
Normal file
74
spaces/pages/vehicles/index.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div id="main">
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<UButton @click="router.push(`/vehicles/create/`)">+ Fahrzeug</UButton>
|
||||
|
||||
<UInput
|
||||
v-model="searchString"
|
||||
placeholder="Suche..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<UTable
|
||||
:rows="filteredRows"
|
||||
:columns="itemColumns"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
>
|
||||
|
||||
</UTable>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const {vehicles } = storeToRefs(useDataStore())
|
||||
const mode = ref("show")
|
||||
|
||||
const itemColumns = [
|
||||
{
|
||||
key: 'licensePlate',
|
||||
label: "Kennzeichen:",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "type",
|
||||
label: "Typ:",
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const selectItem = (item) => {
|
||||
router.push(`/vehicles/show/${item.id} `)
|
||||
}
|
||||
|
||||
|
||||
const searchString = ref('')
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
if(!searchString.value) {
|
||||
return vehicles.value
|
||||
}
|
||||
|
||||
return vehicles.value.filter(item => {
|
||||
return Object.values(item).some((value) => {
|
||||
return String(value).toLowerCase().includes(searchString.value.toLowerCase())
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -9,146 +9,166 @@
|
||||
<div
|
||||
class="previewDoc"
|
||||
>
|
||||
<!-- <embed
|
||||
:src="fileurl + '#toolbar=0&navpanes=0&scrollbar=0&statusbar=0&messages=0&scrollbar=0'"
|
||||
width="40vw"
|
||||
height="50vh"
|
||||
>-->
|
||||
<embed
|
||||
v-if="currentDocument"
|
||||
:src="currentDocument.url + '#toolbar=0&navpanes=0&scrollbar=0&statusbar=0&messages=0&scrollbar=0'"
|
||||
|
||||
>
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="inputData"
|
||||
>
|
||||
<UFormGroup label="Lieferant:" required>
|
||||
<USelectMenu
|
||||
v-model="invoice.vendor"
|
||||
v-model="itemInfo.vendor"
|
||||
:options="vendors"
|
||||
option-attribute="name"
|
||||
value-attribute="id"
|
||||
searchable
|
||||
:search-attributes="['name','vendorNumber']"
|
||||
>
|
||||
|
||||
<template #label>
|
||||
{{vendors.find(vendor => vendor.id === itemInfo.vendor) ? vendors.find(vendor => vendor.id === itemInfo.vendor).name : 'Lieferant auswählen'}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Rechnungsreferenz:" required>
|
||||
<UFormGroup
|
||||
class="mt-3"
|
||||
label="Rechnungsreferenz:"
|
||||
required
|
||||
>
|
||||
<UInput
|
||||
v-model="invoice.reference"
|
||||
v-model="itemInfo.reference"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Rechnungsdatum:" required>
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="labelDate" />
|
||||
<InputGroup class="mt-3" gap="2">
|
||||
<UFormGroup label="Rechnungsdatum:" required>
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.date ? dayjs(itemInfo.date).format('DD.MM.YYYY') : 'Datum auswählen'" />
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="invoice.date" @close="close" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="itemInfo.date" @close="close" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Fälligkeitsdatum:" required>
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="itemInfo.dueDate ? dayjs(itemInfo.dueDate).format('DD.MM.YYYY') : 'Datum auswählen'" />
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="itemInfo.dueDate" @close="close" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
</InputGroup>
|
||||
|
||||
<UFormGroup label="Fälligkeitsdatum:" required>
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="labelDueDate" />
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="invoice.dueDate" @close="close" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Beschreibung:" required>
|
||||
<UTextarea
|
||||
v-model="invoice.description"
|
||||
v-model="itemInfo.description"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
<!-- <UButton @click="vendorInvoiceData.lineItems.push({})">+ Reihe</UButton>-->
|
||||
<UFormGroup label="Betrag:" required>
|
||||
<UInput
|
||||
type="number"
|
||||
step="0.01"
|
||||
v-model="itemInfo.amount"
|
||||
>
|
||||
<template #trailing>
|
||||
<span class="text-gray-500 dark:text-gray-400 text-xs">EUR</span>
|
||||
</template>
|
||||
</UInput>
|
||||
</UFormGroup>
|
||||
|
||||
<InputGroup class="mt-3">
|
||||
<UButton
|
||||
@click="updateItem"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
</InputGroup>
|
||||
|
||||
<DevOnly>
|
||||
{{itemInfo}}<br>
|
||||
{{currentVendorInvoice}}<br>
|
||||
{{currentDocument}}
|
||||
</DevOnly>
|
||||
|
||||
|
||||
<!-- <div v-for="lineItem in vendorInvoiceData.lineItems" class="lineItemRow">
|
||||
<UFormGroup label="Text:" required>
|
||||
<UInput v-model="lineItem.text"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Produkt:" required>
|
||||
<UInput v-model="lineItem.productId"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Projekt:" required>
|
||||
<UInput v-model="lineItem.projectId"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Anzahl:" required>
|
||||
<UInput v-model="lineItem.quantity"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Einheit:" required>
|
||||
<UInput v-model="lineItem.unit"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Einzelpreis:" required>
|
||||
<UInput v-model="lineItem.unitPriceNet"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="USt:" required>
|
||||
<UInput v-model="lineItem.vat"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Rabatt:" required>
|
||||
<UInput v-model="lineItem.discount"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Buchungskonto:" required>
|
||||
<UInput v-model="lineItem.skrAccountId"/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Positionspreis:" required>
|
||||
<UInput disabled/>
|
||||
</UFormGroup>
|
||||
</div>-->
|
||||
{{vendorInvoiceData}}<br>
|
||||
{{currentVendorInvoice}}<br>
|
||||
{{getDocumentById(78)}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import InputGroup from "~/components/InputGroup.vue";
|
||||
import * as dayjs from "dayjs";
|
||||
|
||||
const supabase = useSupabaseClient()
|
||||
const route = useRoute()
|
||||
const toast = useToast()
|
||||
|
||||
const {vendors} = storeToRefs(useDataStore())
|
||||
const {getVendorInvoiceById, getDocumentById} = useDataStore()
|
||||
const {getVendorInvoiceById, getDocumentById, fetchVendorInvoices} = useDataStore()
|
||||
|
||||
let currentVendorInvoice = null
|
||||
let currentDocument = ref(null)
|
||||
//Working
|
||||
const mode = ref(route.params.mode || "show")
|
||||
|
||||
|
||||
const invoice = ref({
|
||||
vendor: 0,
|
||||
reference: "",
|
||||
date: new Date(),
|
||||
dueDate: new Date(),
|
||||
paymentType: "",
|
||||
description: "",
|
||||
//Functions
|
||||
const setupPage = async () => {
|
||||
if(mode.value === "show" || mode.value === "edit"){
|
||||
currentVendorInvoice = await getVendorInvoiceById(Number(useRoute().params.id))
|
||||
currentDocument.value = await getDocumentById(currentVendorInvoice.document)
|
||||
}
|
||||
|
||||
if(mode.value === "edit") itemInfo.value = currentVendorInvoice
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const itemInfo = ref({
|
||||
vendor: 0,
|
||||
reference: "",
|
||||
date: null,
|
||||
dueDate: null,
|
||||
paymentType: "",
|
||||
description: "",
|
||||
state: "Entwurf"
|
||||
})
|
||||
|
||||
const labelDate = computed(() => invoice.value.date.toLocaleDateString('de-de', { weekday: 'long', year: 'numeric', month: 'short', day: 'numeric' }))
|
||||
const labelDueDate = computed(() => invoice.value.dueDate.toLocaleDateString('de-de', { weekday: 'long', year: 'numeric', month: 'short', day: 'numeric' }))
|
||||
|
||||
|
||||
|
||||
const currentVendorInvoice = ref(getVendorInvoiceById(Number(route.params.id)))
|
||||
console.log(currentVendorInvoice)
|
||||
const currentDocument = getDocumentById(Number(currentVendorInvoice.document))
|
||||
console.log(currentDocument)
|
||||
//console.log(document)
|
||||
|
||||
//let fileurl = (await supabase.storage.from('documents').createSignedUrl(document.path,60*60)).data.signedUrl
|
||||
|
||||
|
||||
let vendorInvoiceData = ref({
|
||||
reference: "",
|
||||
date: "",
|
||||
vendorId: 0,
|
||||
lineItems: []
|
||||
})
|
||||
|
||||
|
||||
|
||||
const updateItem = async () => {
|
||||
const {error} = await supabase
|
||||
.from("vendorInvoices")
|
||||
.update(itemInfo.value)
|
||||
.eq('id',itemInfo.value.id)
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
mode.value = "show"
|
||||
/*itemInfo.value = {
|
||||
id: 0,
|
||||
}*/
|
||||
toast.add({title: "Eingangsrechnung erfolgreich gespeichert"})
|
||||
fetchVendorInvoices()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
setupPage()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -158,7 +178,7 @@ let vendorInvoiceData = ref({
|
||||
}
|
||||
|
||||
.previewDoc {
|
||||
width: 50vw;
|
||||
min-width: 50vw;
|
||||
min-height: 80vh;
|
||||
}
|
||||
|
||||
@@ -168,7 +188,7 @@ let vendorInvoiceData = ref({
|
||||
}
|
||||
|
||||
.inputData {
|
||||
|
||||
max-width: 40vw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,34 @@
|
||||
:columns="itemColumns"
|
||||
@select="selectItem"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
/>
|
||||
>
|
||||
<template #state-data="{row}">
|
||||
<span
|
||||
v-if="row.state === 'Entwurf'"
|
||||
class="text-cyan-500"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>
|
||||
<span
|
||||
v-if="row.state === 'Bezahlt'"
|
||||
class="text-primary-500"
|
||||
>
|
||||
{{row.state}}
|
||||
</span>
|
||||
</template>
|
||||
<template #vendor-data="{row}">
|
||||
{{vendors.find(vendor => vendor.id === row.vendor) ? vendors.find(vendor => vendor.id === row.vendor).name : ''}}
|
||||
</template>
|
||||
<template #date-data="{row}">
|
||||
{{row.date ? dayjs(row.date).format("DD.MM.YY") : ''}}
|
||||
</template>
|
||||
<template #dueDate-data="{row}">
|
||||
{{row.dueDate ? dayjs(row.dueDate).format("DD.MM.YY") : ''}}
|
||||
</template>
|
||||
<template #amount-data="{row}">
|
||||
{{row.amount ? row.amount.toFixed(2) + ' €' : ''}}
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +52,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as dayjs from "dayjs";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
@@ -32,15 +60,10 @@ definePageMeta({
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const {vendorInvoices } = storeToRefs(useDataStore())
|
||||
const {vendorInvoices, vendors} = storeToRefs(useDataStore())
|
||||
const mode = ref("show")
|
||||
|
||||
const itemColumns = [
|
||||
{
|
||||
key: 'id',
|
||||
label: "Id",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
label: "Status.",
|
||||
@@ -48,7 +71,7 @@ const itemColumns = [
|
||||
},
|
||||
{
|
||||
key: 'vendor',
|
||||
label: "Lieferantennr.",
|
||||
label: "Lieferant",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
@@ -61,12 +84,22 @@ const itemColumns = [
|
||||
label: "Datum",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "dueDate",
|
||||
label: "Fällig:",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "amount",
|
||||
label: "Betrag",
|
||||
sortable: true
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
const selectItem = (item) => {
|
||||
console.log(item)
|
||||
router.push(`/vendorinvoices/show/${item.id} `)
|
||||
router.push(`/vendorinvoices/edit/${item.id} `)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,10 +38,13 @@ export const useDataStore = defineStore('data', {
|
||||
contacts: [] as any[],
|
||||
vehicles: [] as any[],
|
||||
vendors: [] as any[],
|
||||
vendorInvoices: [] as any[]
|
||||
vendorInvoices: [] as any[],
|
||||
bankAccounts: [] as any[],
|
||||
bankStatements: [] as any[]
|
||||
}),
|
||||
actions: {
|
||||
async fetchData() {
|
||||
this.fetchDocuments()
|
||||
await this.fetchOwnTenant()
|
||||
await this.fetchProfiles()
|
||||
await this.fetchEvents()
|
||||
@@ -54,6 +57,7 @@ export const useDataStore = defineStore('data', {
|
||||
|
||||
await this.fetchCustomers()
|
||||
await this.fetchContracts()
|
||||
await this.fetchContacts()
|
||||
await this.fetchForms()
|
||||
await this.fetchFormSubmits()
|
||||
await this.fetchProducts()
|
||||
@@ -64,7 +68,36 @@ export const useDataStore = defineStore('data', {
|
||||
await this.fetchVehicles()
|
||||
await this.fetchVendors()
|
||||
await this.fetchVendorInvoices()
|
||||
await this.fetchBankAccounts()
|
||||
await this.fetchBankStatements()
|
||||
|
||||
|
||||
},
|
||||
async clearStore() {
|
||||
console.log("Clear")
|
||||
this.loaded = false
|
||||
this.ownTenant = {}
|
||||
this.profiles= []
|
||||
this.events= []
|
||||
this.customers= []
|
||||
this.tasks= []
|
||||
this.projects= []
|
||||
this.documents= []
|
||||
this.spaces= []
|
||||
this.units= []
|
||||
this.times= []
|
||||
this.products= []
|
||||
this.movements= []
|
||||
this.forms= []
|
||||
this.contracts= []
|
||||
this.jobs= []
|
||||
this.formSubmits= []
|
||||
this.contacts= []
|
||||
this.vehicles= []
|
||||
this.vendors= []
|
||||
this.vendorInvoices= []
|
||||
this.bankAccounts= []
|
||||
this.bankStatements= []
|
||||
},
|
||||
async fetchOwnTenant() {
|
||||
//TODO: Tenant ID Dynamisch machen
|
||||
@@ -75,6 +108,14 @@ export const useDataStore = defineStore('data', {
|
||||
// @ts-ignore
|
||||
this.profiles = (await supabase.from("profiles").select()).data
|
||||
},
|
||||
async fetchBankAccounts() {
|
||||
// @ts-ignore
|
||||
this.bankAccounts = (await supabase.from("bankAccounts").select()).data
|
||||
},
|
||||
async fetchBankStatements() {
|
||||
// @ts-ignore
|
||||
this.bankStatements = (await supabase.from("bankStatements").select()).data
|
||||
},
|
||||
async fetchEvents() {
|
||||
// @ts-ignore
|
||||
this.events = (await supabase.from("events").select()).data
|
||||
@@ -83,6 +124,10 @@ export const useDataStore = defineStore('data', {
|
||||
// @ts-ignore
|
||||
this.contracts = (await supabase.from("contracts").select()).data
|
||||
},
|
||||
async fetchContacts() {
|
||||
// @ts-ignore
|
||||
this.contacts = (await supabase.from("contacts").select()).data
|
||||
},
|
||||
async fetchCustomers() {
|
||||
// @ts-ignore
|
||||
this.customers = (await supabase.from("customers").select().order('customerNumber', {ascending: true})).data
|
||||
@@ -167,6 +212,8 @@ export const useDataStore = defineStore('data', {
|
||||
getVendorById: (state) => (itemId:number) => state.vendors.find(item => item.id === itemId),
|
||||
getVendorInvoiceById: (state) => (itemId:number) => state.vendorInvoices.find(item => item.id === itemId),
|
||||
getContractById: (state) => (itemId:number) => state.contracts.find(item => item.id === itemId),
|
||||
getContactById: (state) => (itemId:number) => state.contacts.find(item => item.id === itemId),
|
||||
getVehicleById: (state) => (itemId:number) => state.vehicles.find(item => item.id === itemId),
|
||||
getDocumentById: (state) => (itemId:number) => state.documents.find(item => item.id === itemId),
|
||||
getSpaceById: (state) => (itemId:number) => state.spaces.find(item => item.id === itemId),
|
||||
getProjectById: (state) => (projectId:number) => {
|
||||
@@ -184,6 +231,7 @@ export const useDataStore = defineStore('data', {
|
||||
},
|
||||
getCustomerById: (state) => (customerId:number) => state.customers.find(customer => customer.id === customerId),
|
||||
getJobById: (state) => (jobId:number) => state.jobs.find(job => job.id === jobId),
|
||||
getContactsByCustomerId: (state) => (customerId) => state.contacts.filter(contact => contact.customer === customerId),
|
||||
getTimesByProjectId: (state) => (projectId:number) => {
|
||||
let times = state.times.filter(time => time.projectId === projectId)
|
||||
console.log(times.length)
|
||||
|
||||
Reference in New Issue
Block a user