Startet work on customer editor #6
This commit is contained in:
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, watch, onMounted, onUpdated, useTemplateRef } from "vue"
|
||||||
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog';
|
||||||
|
import { Customer } from '@/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: Customer | null,
|
||||||
|
isOpen: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isOpen = computed({
|
||||||
|
get: () => props.isOpen,
|
||||||
|
set: (value) => {
|
||||||
|
emit('update:open', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'update:open', 'save', 'delete'])
|
||||||
|
|
||||||
|
const saveChanges = () => {
|
||||||
|
// if (invoice.value) {
|
||||||
|
// emit('save', invoice.value)
|
||||||
|
// isOpen.value = false
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelChanges = (event: Event | null) => {
|
||||||
|
// if (isDirty.value) {
|
||||||
|
// alert.value.title = "Wirklich schließen?"
|
||||||
|
// alert.value.message = "Es gibt ungespeicherte Änderungen, die dann verloren gehen."
|
||||||
|
// alert.value.cancelText = "Abbrechen"
|
||||||
|
// alert.value.onCancel = () => {
|
||||||
|
// event?.preventDefault()
|
||||||
|
// event.returnValue = true
|
||||||
|
// alert.value.open = false
|
||||||
|
// }
|
||||||
|
// alert.value.confirmText = "Schließen"
|
||||||
|
// alert.value.onConfirm = () => {
|
||||||
|
// emit('cancel')
|
||||||
|
// isOpen.value = false
|
||||||
|
// alert.value.open = false
|
||||||
|
// }
|
||||||
|
// alert.value.open = true
|
||||||
|
// } else {
|
||||||
|
// emit('cancel')
|
||||||
|
// isOpen.value = false
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog id="customer-dialog" v-model:open="isOpen">
|
||||||
|
<DialogContent
|
||||||
|
class="sm:max-w-[min((100%-2rem),1152px)] grid-rows-[auto_minmax(0,1fr)_auto] p-0 h-[calc(100dvh-2rem)]"
|
||||||
|
@escapeKeyDown="cancelChanges" @interactOutside="cancelChanges">
|
||||||
|
<DialogHeader class="px-3 pt-3 flex flex-row justify-end">
|
||||||
|
<DialogTitle>Edit profile</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Make changes to your profile here. Click save when you're done.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
Save changes
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
@@ -11,11 +11,12 @@ import { randomInt, bgColorForString } from '@/lib/utils'
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Copy, Delete, Phone, Plus, Search } from "lucide-vue-next"
|
import { Copy, Delete, Edit, Phone, Plus, Search } from "lucide-vue-next"
|
||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
import { getInitials } from '@/composables/useInitials';
|
import { getInitials } from '@/composables/useInitials';
|
||||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card'
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||||
|
import EditCustomer from '@/components/EditCustomer.vue'
|
||||||
|
|
||||||
const breadcrumbs: BreadcrumbItem[] = [
|
const breadcrumbs: BreadcrumbItem[] = [
|
||||||
{
|
{
|
||||||
@@ -27,6 +28,8 @@ const breadcrumbs: BreadcrumbItem[] = [
|
|||||||
const customersData = ref([] as Customer[])
|
const customersData = ref([] as Customer[])
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
const searchField = ref()
|
const searchField = ref()
|
||||||
|
const activeCustomer = ref<Customer | null>(null)
|
||||||
|
const detailDialogOpen = ref(false)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -36,7 +39,7 @@ onMounted(async () => {
|
|||||||
searchField.value.focus()
|
searchField.value.focus()
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// TODO: toast
|
// TODO: toast, depends on #33
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -62,13 +65,22 @@ const addressToClipbard = async function (address: Address) {
|
|||||||
address.lineOne + '\n' +
|
address.lineOne + '\n' +
|
||||||
(address.lineTwo ? address.lineTwo + '\n' : '') +
|
(address.lineTwo ? address.lineTwo + '\n' : '') +
|
||||||
address.postalCode + ' ' + address.city
|
address.postalCode + ' ' + address.city
|
||||||
|
// TODO: toast, depends on #33
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error)
|
if (error instanceof Error)
|
||||||
|
// TODO: toast, depends on #33
|
||||||
console.error(error.message)
|
console.error(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showDetail = (customer: Customer) => {
|
||||||
|
// make a deep copy, so the changes in the dialog won’t affect the data until saved
|
||||||
|
activeCustomer.value = JSON.parse(JSON.stringify(customer))
|
||||||
|
detailDialogOpen.value = true
|
||||||
|
console.log(activeCustomer.value)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -101,7 +113,7 @@ const addressToClipbard = async function (address: Address) {
|
|||||||
<div class="columns-xs gap-4">
|
<div class="columns-xs gap-4">
|
||||||
|
|
||||||
<Card v-for="customer in filteredCustomers" :key="customer.id"
|
<Card v-for="customer in filteredCustomers" :key="customer.id"
|
||||||
class="mb-4 shadow-xl break-inside-avoid hover:bg-accent">
|
class="mb-4 shadow-xl break-inside-avoid hover:bg-accent" @click="showDetail(customer)">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{{ customer.companyName }}</CardTitle>
|
<CardTitle>{{ customer.companyName }}</CardTitle>
|
||||||
<CardDescription>Kategorie / Umsatz</CardDescription>
|
<CardDescription>Kategorie / Umsatz</CardDescription>
|
||||||
@@ -124,7 +136,7 @@ const addressToClipbard = async function (address: Address) {
|
|||||||
|
|
||||||
<Tooltip v-for="contact in customer.contacts">
|
<Tooltip v-for="contact in customer.contacts">
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<Avatar class="mr-2 md:-mr-2 border-2 size-10 md:size-12">
|
<Avatar class="-mr-2 border-2 size-14">
|
||||||
<AvatarImage :src="contact.avatar" />
|
<AvatarImage :src="contact.avatar" />
|
||||||
<AvatarFallback
|
<AvatarFallback
|
||||||
:class="bgColorForString(getInitials(contact.firstName + ' ' + contact.lastName))">
|
:class="bgColorForString(getInitials(contact.firstName + ' ' + contact.lastName))">
|
||||||
@@ -142,6 +154,11 @@ const addressToClipbard = async function (address: Address) {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Invoice detail dialog -->
|
||||||
|
<EditCustomer :model-value="activeCustomer" :is-open="detailDialogOpen" @save="" @delete="" />
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
</template>
|
</template>
|
||||||
Reference in New Issue
Block a user