inspiren-sem-tool/resources/js/pages/client-invoices/edit.tsx
brian-inspiren 221d3f8173
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
feat: sem codebase
2026-05-21 11:28:03 +08:00

117 lines
4.6 KiB
TypeScript

import React from "react";
import AppLayout from "@/layouts/app-layout";
import { router, useForm } from "@inertiajs/react";
import { Badge, Button, Card, Container, Group, Title } from "@mantine/core";
import { IconArrowLeft } from "@tabler/icons-react";
import { Link } from "@inertiajs/react";
import InvoiceForm, { InvoiceFormValues } from "@/forms/account/InvoiceForm";
import { ClientInvoice } from "@/types";
type InvoiceOptionSource = Pick<ClientInvoice, "id" | "invoice_no"> & {
linked_invoice_id?: number | null;
};
interface Props {
invoice: ClientInvoice & { client?: { customer_id: string } };
availableInvoices: InvoiceOptionSource[];
}
export default function Page({ invoice, availableInvoices }: Props) {
const form = useForm<InvoiceFormValues>({
invoice_no: invoice.invoice_no ?? "",
linked_invoice_id: invoice.linked_invoice_id ? String(invoice.linked_invoice_id) : "",
is_credit_card: !!invoice.is_credit_card,
is_paid: !!invoice.is_paid,
payment_no: invoice.payment_no ?? "",
start_date: invoice.start_date ?? "",
end_date: invoice.end_date ?? "",
amount: invoice.amount !== null && invoice.amount !== undefined ? String(invoice.amount) : "",
management_fee:
invoice.management_fee !== null && invoice.management_fee !== undefined
? String(invoice.management_fee)
: "",
media_fee:
invoice.media_fee !== null && invoice.media_fee !== undefined
? String(invoice.media_fee)
: "",
tax_percent:
invoice.tax_percent !== null && invoice.tax_percent !== undefined
? String(invoice.tax_percent)
: "",
total_spending:
invoice.total_spending !== null && invoice.total_spending !== undefined
? String(invoice.total_spending)
: "",
client_id: invoice.client_id ? String(invoice.client_id) : undefined,
customer_id: invoice.client?.customer_id ?? "",
});
const isApproved = !!invoice.approved_at;
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const totalSpending = form.data.total_spending
? parseFloat(form.data.total_spending)
: null;
form.transform((data) => ({
...data,
linked_invoice_id: data.linked_invoice_id || null,
is_credit_card: !!data.is_credit_card,
is_paid: !!data.is_paid,
payment_no: data.payment_no || null,
start_date: data.start_date || null,
end_date: data.end_date || null,
amount: parseFloat(data.amount) || 0,
management_fee: parseFloat(data.management_fee) || 0,
media_fee: parseFloat(data.media_fee) || 0,
tax_percent: parseFloat(data.tax_percent) || 0,
total_spending: totalSpending,
}));
form.put(route("client-invoices.update", { invoice: invoice.id }));
};
const handleApprove = () => {
router.patch(route("client-invoices.approve", { invoice: invoice.id }));
};
const invoiceOptions = availableInvoices.map((item) => ({
value: String(item.id),
label: item.invoice_no,
}));
return (
<AppLayout>
<Container size="lg" px="xs">
<Group position="apart" mb="md">
<Group gap="sm">
<Title order={2}>Edit invoice</Title>
<Badge color={isApproved ? "green" : "yellow"} variant="light">
{isApproved ? "Approved" : "Pending approval"}
</Badge>
</Group>
<Group gap="sm">
{!isApproved && (
<Button color="green" onClick={handleApprove}>
Approve invoice
</Button>
)}
<Button
component={Link}
href={route("google-ads.accounts.show", { id: invoice.client?.customer_id ?? "" })}
leftIcon={<IconArrowLeft size={16} />}
variant="outline"
>
Back to account
</Button>
</Group>
</Group>
<Card withBorder>
<InvoiceForm form={form} onSubmit={handleSubmit} invoiceOptions={invoiceOptions} />
</Card>
</Container>
</AppLayout>
);
}