Add confirm dialog to reminder button.
Add a show function in alertStore so we can use alerts in a defined way everywhere. #12 #33
This commit is contained in:
@@ -24,13 +24,14 @@ import { Input } from '@/components/ui/input';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'
|
||||
import { StatusBadge, statusBadgeLabels, statusBadgeTextColor, StatusBadgeVariants } from '@/components/ui/status-badge'
|
||||
import LineItemTable from '@/components/documents/LineItemTable.vue'
|
||||
import { Eye, FileText, CircleEllipsis, Trash, Trash2, BookUser, User, CodeXml, CalendarIcon, MessageCircleQuestion, X, CircleX, Logs, ListCheck, ClipboardCheck, ClipboardList } from "lucide-vue-next"
|
||||
import { Eye, FileText, CircleEllipsis, Trash, Trash2, BookUser, User, CodeXml, CalendarIcon, MessageCircleQuestion, X, CircleX, Logs, ListCheck, ClipboardCheck, ClipboardList, Loader, Loader2 } from "lucide-vue-next"
|
||||
import { alertStore } from "@/stores/alertStore"
|
||||
import { Calendar } from "@/components/ui/calendar"
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||
import { exportPdf, exportXml } from "@/routes/invoice"
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from '@/components/ui/sheet'
|
||||
import { GrowingTextarea } from '../ui/growing-textarea'
|
||||
import { toast } from "vue-sonner"
|
||||
|
||||
const props = defineProps<{
|
||||
invoiceData: Invoice | null,
|
||||
@@ -47,6 +48,7 @@ const isLoading = ref(false);
|
||||
const importContact = ref(newContact() as Contact)
|
||||
const importCustomer = ref(newCustomer() as Customer)
|
||||
const alert = alertStore()
|
||||
const reminderLoading = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
const response = await axios.get('/api/paymentterms')
|
||||
@@ -95,6 +97,7 @@ watch(invoice,
|
||||
postalCode: invoice.value.customer?.billingAddress?.postalCode || "",
|
||||
countryCode: invoice.value.customer?.billingAddress?.countryCode || "",
|
||||
},
|
||||
contactSalutation: invoice.value.customer?.contacts && invoice.value.customer.contacts.length > 0 ? invoice.value.customer.contacts[0].salutation : "",
|
||||
contactFirstName: invoice.value.customer?.contacts && invoice.value.customer.contacts.length > 0 ? invoice.value.customer.contacts[0].firstName : "",
|
||||
contactLastName: invoice.value.customer?.contacts && invoice.value.customer.contacts.length > 0 ? invoice.value.customer.contacts[0].lastName : "",
|
||||
paymentTerms: invoice.value.customer?.paymentTerms || paymentTermsData.value.length > 0 ? paymentTermsData.value[2] : null,
|
||||
@@ -190,24 +193,23 @@ const saveChanges = () => {
|
||||
|
||||
const cancelChanges = (event: Event | null) => {
|
||||
if (isDirty.value) {
|
||||
alert.title = "Wirklich schließen?"
|
||||
alert.message = "Es gibt ungespeicherte Änderungen, die dann verloren gehen."
|
||||
alert.cancelText = "Abbrechen"
|
||||
alert.onCancel = () => {
|
||||
console.log('cancel')
|
||||
event?.preventDefault()
|
||||
event.returnValue = true
|
||||
alert.open = false
|
||||
}
|
||||
alert.actionText = "Schließen"
|
||||
// alert.actionVariant = "destructive"
|
||||
alert.onAction = () => {
|
||||
console.log('action')
|
||||
alert.show(
|
||||
"Wirklich schließen?",
|
||||
"Es gibt ungespeicherte Änderungen, die dann verloren gehen.",
|
||||
{
|
||||
actionText: "Änderungen verwerfen",
|
||||
actionVariant: "destructive",
|
||||
onAction: () => {
|
||||
emit('cancel')
|
||||
isOpen.value = false
|
||||
alert.open = false
|
||||
},
|
||||
onCancel: () => {
|
||||
if(!event) return
|
||||
event.preventDefault()
|
||||
event.returnValue = true
|
||||
}
|
||||
alert.open = true
|
||||
}
|
||||
)
|
||||
} else {
|
||||
emit('cancel')
|
||||
isOpen.value = false
|
||||
@@ -230,19 +232,46 @@ const downloadXml = function () {
|
||||
}
|
||||
|
||||
const deleteInvoice = function () {
|
||||
let confirm = window.confirm('Möchtest Du diese Rechnung wirklich löschen?')
|
||||
if (!confirm) return
|
||||
alert.show(
|
||||
"Möchtest Du diese Rechnung wirklich löschen?",
|
||||
(invoice.value?.paymentStatus == "draft") ? null : "Nach GoBD musst Du alle Belege und Daten in unveränderter Form aufbewahren",
|
||||
{
|
||||
actionText: "Löschen",
|
||||
actionVariant: "destructive",
|
||||
onAction: () => {
|
||||
emit('delete', invoice.value?.id)
|
||||
isOpen.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const remind = function () {
|
||||
// await axios call
|
||||
// make button spin
|
||||
// success -> set new status and save
|
||||
// error -> toast
|
||||
if (!invoice.value) return;
|
||||
window?.open('/api/invoices/' + invoice.value.id + '/remind', '_blank')?.focus();
|
||||
|
||||
alert.show(
|
||||
"Zahlungserinnerung senden?",
|
||||
"E-mail an " + invoice.value.customer?.contacts[0].email,
|
||||
{
|
||||
actionText: "Senden",
|
||||
onAction: async () => {
|
||||
// make button spin and disable button
|
||||
reminderLoading.value = true
|
||||
|
||||
// await axios call
|
||||
await axios.get('/api/invoices/' + invoice.value.id + '/remind')
|
||||
.then(function (response) {
|
||||
toast.success("Zahlungserinnerung gesendet", { description: "daniel@vollstock.de" })
|
||||
})
|
||||
.catch(function (error) {
|
||||
toast.error(error.title, { description: error.message })
|
||||
})
|
||||
.finally(() => {
|
||||
reminderLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const updateTotalAmount = () => {
|
||||
@@ -364,7 +393,7 @@ const updateTotalAmount = () => {
|
||||
|
||||
<div id="document">
|
||||
<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">
|
||||
class="sticky top-0 py-4 bg-white dark:bg-neutral-800 z-1 flex items-end gap-12">
|
||||
<div class="grow">
|
||||
|
||||
<h1 class="text-xl text-primary-foreground font-bold" v-if="invoice.id > 0">
|
||||
@@ -483,7 +512,12 @@ const updateTotalAmount = () => {
|
||||
</Select>
|
||||
|
||||
<Button v-if="['due', 'reminded'].includes(invoice.paymentStatus)"
|
||||
:size="'sm'" :variant="'destructive'" @click="remind">Mahnen</Button>
|
||||
:size="'sm'" :variant="'destructive'" @click="remind"
|
||||
:disabled="reminderLoading">
|
||||
<Loader2 class="h-4 w-4 transition-[width] ease-in-out animate-spin"
|
||||
:class="{ 'w-0!': !reminderLoading }" />
|
||||
Mahnen
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
|
||||
@@ -70,13 +70,11 @@ onMounted(() => {
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{{ alert.title }}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{{ alert.message }}</AlertDialogDescription>
|
||||
<AlertDialogDescription v-if="alert.message">{{ alert.message }}</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<Button v-if="alert.onCancel" @click="alert.onCancel">{{ alert.cancelText }}</Button>
|
||||
<Button :variant="alert.actionVariant" v-if="alert.onAction" @click="alert.onAction">{{
|
||||
alert.actionText
|
||||
}}</Button>
|
||||
<Button @click="alert.cancel">{{ alert.cancelText }}</Button>
|
||||
<Button :variant="alert.actionVariant" @click="alert.action">{{ alert.actionText }}</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import { options } from '@coders-tm/vue-number-format';
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export interface AlertOptions {
|
||||
cancelText?: string;
|
||||
onCancel?: () => void;
|
||||
actionText?: string;
|
||||
actionVariant?: "action" | "destructive";
|
||||
onAction?: () => void;
|
||||
}
|
||||
|
||||
export const alertStore = defineStore('alert', {
|
||||
state: () => {
|
||||
return {
|
||||
open: false,
|
||||
title: "",
|
||||
message: "",
|
||||
message: "" as string | null,
|
||||
cancelText: "Abbrechen",
|
||||
onCancel: () => { },
|
||||
actionText: "Ok",
|
||||
@@ -13,5 +22,25 @@ export const alertStore = defineStore('alert', {
|
||||
onAction: () => { }
|
||||
}
|
||||
},
|
||||
actions: {}
|
||||
|
||||
actions: {
|
||||
show(title: string, message: string | null, options?: AlertOptions) {
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.cancelText = options?.cancelText ? options.cancelText : "Abbrechen"
|
||||
this.onCancel = options?.onCancel ? options.onCancel : () => { }
|
||||
this.actionText = options?.actionText ? options.actionText : "Ok"
|
||||
this.actionVariant = options?.actionVariant ? options.actionVariant : "action"
|
||||
this.onAction = options?.onAction ? options.onAction : () => { }
|
||||
this.open = true;
|
||||
},
|
||||
cancel() {
|
||||
this.onCancel();
|
||||
this.open = false;
|
||||
},
|
||||
action() {
|
||||
this.onAction();
|
||||
this.open = false;
|
||||
}
|
||||
}
|
||||
})
|
||||
+2
-2
@@ -28,8 +28,8 @@
|
||||
|
||||
Route::get('/invoices/{id}/remind', function ($id) {
|
||||
$invoice = InvoiceController::single($id);
|
||||
// Mail::to('daniel@vollstock.de')->send(new Reminder($invoice));
|
||||
return new Reminder($invoice);
|
||||
Mail::to('daniel@vollstock.de')->send(new Reminder($invoice));
|
||||
// return new Reminder($invoice);
|
||||
});
|
||||
|
||||
Route::get('/offers/{id}/confirm', function ($id) {
|
||||
|
||||
Reference in New Issue
Block a user