Some progress on the customer editor #6
This commit is contained in:
@@ -0,0 +1,120 @@
|
|||||||
|
<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 { Button } from '@/components/ui/button'
|
||||||
|
import { Customer } from '@/types'
|
||||||
|
import { Check, Ellipsis, Trash } from "lucide-vue-next"
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: boolean
|
||||||
|
customerData: Customer | null,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'update:open', 'save', 'cancel', 'delete'])
|
||||||
|
|
||||||
|
const customer = ref<Customer | null>(props.customerData)
|
||||||
|
const isDirty = ref(false);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
const isOpen = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value) => {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// watch for new external invoice data
|
||||||
|
watch(() => props.customerData as Customer,
|
||||||
|
(newValue, oldValue) => {
|
||||||
|
if (newValue == oldValue) return
|
||||||
|
customer.value = newValue
|
||||||
|
|
||||||
|
// Set initial state for a newly opened document
|
||||||
|
isDirty.value = false
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
|
console.log("customerData", "Dirty: " + isDirty.value, "loading: " + isLoading.value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(customer, (newValue) => {
|
||||||
|
console.log(newValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
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 class="sr-only">Kunde</DialogTitle>
|
||||||
|
|
||||||
|
<div v-if="customer && customer.id > 0" class="hidden md:flex mr-4 gap-2">
|
||||||
|
<Button :size="'sm'" variant="action" @click="" class="hidden" :class="{ flex: isDirty }">
|
||||||
|
<Check :strokeWidth="1.5" class="text-current" />
|
||||||
|
<span>Speichern</span>
|
||||||
|
</Button>
|
||||||
|
<Button :size="'sm'" variant="destructive" @click="">
|
||||||
|
<Trash :strokeWidth="1.5" class="text-current" />
|
||||||
|
<span>Löschen</span>
|
||||||
|
</Button>
|
||||||
|
<Button :size="'sm'" variant="ghost" @click="">
|
||||||
|
<Ellipsis class="text-muted-foreground" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="overflow-y-auto px-6">
|
||||||
|
<div id="document-header"
|
||||||
|
class="block sticky top-0 py-4 bg-slate-100 bg-white dark:bg-neutral-800 z-1 flex items-end gap-12">
|
||||||
|
<div class="grow">
|
||||||
|
<DialogTitle class="text-xl text-primary-foreground font-bold">Edit profile</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Make changes to your profile here. Click save when you're done.
|
||||||
|
</DialogDescription>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter></DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import { ref, onMounted, computed, useTemplateRef, watch } from 'vue'
|
||||||
import AppLayout from '@/layouts/AppLayout.vue'
|
import AppLayout from '@/layouts/AppLayout.vue'
|
||||||
import { customers } from '@/routes'
|
import { customers } from '@/routes'
|
||||||
import { Address, type BreadcrumbItem } from '@/types'
|
import { Address, type BreadcrumbItem } from '@/types'
|
||||||
import { Head } from '@inertiajs/vue3'
|
import { Head } from '@inertiajs/vue3'
|
||||||
import { ref, onMounted, computed, useTemplateRef } from 'vue'
|
|
||||||
import api from '@/axios'
|
import api from '@/axios'
|
||||||
import { Customer, Contact } from '@/types'
|
import { Customer, Contact } from '@/types'
|
||||||
import { randomInt, bgColorForString } from '@/lib/utils'
|
import { randomInt, bgColorForString } from '@/lib/utils'
|
||||||
@@ -16,7 +16,7 @@ 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'
|
import CustomerDialog from '@/components/CustomerDialog.vue'
|
||||||
|
|
||||||
const breadcrumbs: BreadcrumbItem[] = [
|
const breadcrumbs: BreadcrumbItem[] = [
|
||||||
{
|
{
|
||||||
@@ -40,10 +40,13 @@ onMounted(async () => {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// TODO: toast, depends on #33
|
// TODO: toast, depends on #33
|
||||||
console.log(error);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(activeCustomer, () => {
|
||||||
|
console.log(activeCustomer.value?.companyName)
|
||||||
|
})
|
||||||
|
|
||||||
const fuse = computed(() => {
|
const fuse = computed(() => {
|
||||||
return new Fuse(customersData.value, {
|
return new Fuse(customersData.value, {
|
||||||
keys: ['companyName', 'firstName', 'lastName', 'email', 'phone'],
|
keys: ['companyName', 'firstName', 'lastName', 'email', 'phone'],
|
||||||
@@ -78,7 +81,6 @@ const showDetail = (customer: Customer) => {
|
|||||||
// make a deep copy, so the changes in the dialog won’t affect the data until saved
|
// make a deep copy, so the changes in the dialog won’t affect the data until saved
|
||||||
activeCustomer.value = JSON.parse(JSON.stringify(customer))
|
activeCustomer.value = JSON.parse(JSON.stringify(customer))
|
||||||
detailDialogOpen.value = true
|
detailDialogOpen.value = true
|
||||||
console.log(activeCustomer.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -104,7 +106,7 @@ const showDetail = (customer: Customer) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button :size="'sm'" :variant="'action'" @click="newCustomer">
|
<Button :size="'sm'" :variant="'action'" @click="">
|
||||||
<Plus /> Neu
|
<Plus /> Neu
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -137,7 +139,7 @@ const showDetail = (customer: Customer) => {
|
|||||||
<Tooltip v-for="contact in customer.contacts">
|
<Tooltip v-for="contact in customer.contacts">
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<Avatar class="-mr-2 border-2 size-14">
|
<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))">
|
||||||
{{ getInitials(contact.firstName + ' ' + contact.lastName) }}
|
{{ getInitials(contact.firstName + ' ' + contact.lastName) }}
|
||||||
@@ -156,9 +158,27 @@ const showDetail = (customer: Customer) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Invoice detail dialog -->
|
<!-- Invoice detail dialog -->
|
||||||
<EditCustomer :model-value="activeCustomer" :is-open="detailDialogOpen" @save="" @delete="" />
|
<CustomerDialog :customerData="activeCustomer" v-model="detailDialogOpen" @save="" @delete="" />
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Remove close X */
|
||||||
|
[data-slot=dialog-content] button.ring-offset-background {
|
||||||
|
/* display: none; */
|
||||||
|
border-radius: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
color: var(--color-destructive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backdrop */
|
||||||
|
[data-slot=dialog-overlay] {
|
||||||
|
backdrop-filter: blur(var(--blur-sm));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user