"use client"; import { CircleCheck, Download, Pencil, Split, Trash2, } from "lucide-react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { AiBadge } from "@/components/ai-badge"; import { SubmitClaimButton } from "@/components/integrations/integration-actions"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Sheet, SheetFooter, SheetHeader, SheetPanel, SheetPopup, SheetTitle, } from "@/components/ui/sheet"; import { downloadInvoicePdf } from "@/lib/invoice-pdf"; import { deleteInvoice, formatInvoiceDate, formatMoney, type Invoice, type InvoiceStatus, invoiceTotal, isInstallmentOverdue, markInvoicePaid, payInstallment, splitInvoice, } from "@/lib/invoices"; import { notify } from "@/lib/toast"; const statusVariant: Record< InvoiceStatus, "default" | "secondary" | "destructive" | "outline" > = { draft: "secondary", sent: "default", paid: "outline", void: "destructive", }; export function InvoiceDetailSheet({ invoice, open, onOpenChange, onChanged, onDeleted, onEdit, }: { invoice: Invoice | null; open: boolean; onOpenChange: (open: boolean) => void; onChanged: (invoice: Invoice) => void; onDeleted: (id: string) => void; onEdit: (invoice: Invoice) => void; }) { const { t } = useTranslation(); const [count, setCount] = useState(3); const [busy, setBusy] = useState(false); useEffect(() => { setCount(3); }, [invoice?.id]); if (!invoice) { return ( {t("invoices.sheet.fallbackTitle")} ); } const total = invoiceTotal(invoice); const isPaid = invoice.status === "paid"; const isSettled = isPaid || invoice.status === "void"; // The schedule window: the latest installment due date, for the timeframe hint. const lastDue = invoice.installments.reduce( (latest, it) => it.dueAt && (!latest || it.dueAt > latest) ? it.dueAt : latest, null, ); const split = async () => { setBusy(true); try { const updated = await splitInvoice(invoice.id, count); onChanged(updated); notify.success( t("invoices.sheet.splitTitle"), `${invoice.number} · ${count}`, ); } catch { notify.error( t("invoices.sheet.splitFailedTitle"), t("invoices.sheet.splitFailedBody"), ); } finally { setBusy(false); } }; const payAll = async () => { setBusy(true); try { const updated = await markInvoicePaid(invoice); onChanged(updated); notify.success(t("invoices.sheet.paidTitle"), invoice.number); } catch { notify.error( t("invoices.sheet.payFailedTitle"), t("invoices.sheet.payFailedBody"), ); } finally { setBusy(false); } }; const payOne = async (index: number) => { setBusy(true); try { const updated = await payInstallment(invoice, index); onChanged(updated); notify.success( t("invoices.sheet.installmentPaidTitle"), invoice.installments[index]?.label ?? invoice.number, ); } catch { notify.error( t("invoices.sheet.payFailedTitle"), t("invoices.sheet.payFailedBody"), ); } finally { setBusy(false); } }; const remove = async () => { setBusy(true); try { await deleteInvoice(invoice.id); onDeleted(invoice.id); notify.success(t("invoices.sheet.deletedTitle"), invoice.number); onOpenChange(false); } catch { notify.error( t("invoices.sheet.deleteFailedTitle"), t("invoices.sheet.deleteFailedBody"), ); } finally { setBusy(false); } }; return ( {invoice.number} {t(`invoices.status.${invoice.status}`)}

{invoice.name}

{t("invoices.dialog.fileNumber", { number: invoice.fileNumber || "—", })}

{t("invoices.sheet.issued")}
{formatInvoiceDate(invoice.issuedAt)}
{t("invoices.sheet.due")}
{invoice.dueAt ? formatInvoiceDate(invoice.dueAt) : "—"}
{t("invoices.sheet.lineItems")}
{invoice.lineItems.map((li, i) => (
{li.description} {li.quantity} × {formatMoney(li.unitPrice)} {formatMoney(li.quantity * li.unitPrice)}
))}
{t("invoices.sheet.total")} {formatMoney(total)}
{/* Installments are a payment plan for an open invoice — once it's settled there's nothing left to schedule, so hide them. */} {isPaid ? null : (
{t("invoices.sheet.installments")} {invoice.installments.length > 0 ? (
{invoice.installments.map((it, i) => { const overdue = isInstallmentOverdue(it); return (
{it.label} {overdue ? ( {t("invoices.sheet.overdue")} ) : null} {it.dueAt ? formatInvoiceDate(it.dueAt) : "—"} {formatMoney(it.amount)} {it.paid ? ( {t("invoices.sheet.installmentPaid")} ) : ( )}
); })}
) : (

{t("invoices.sheet.noInstallments")}

)} {invoice.installments.length > 0 && lastDue ? (

{t("invoices.sheet.timeframe", { count: invoice.installments.length, date: formatInvoiceDate(lastDue), })}

) : null}
)} {invoice.notes ? (

{invoice.notes}

) : null}
{isSettled ? null : ( )}
); }