From 76862e7a64a08a5c682d3f806b0c0e255b75c6c9 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 20:08:42 +0000 Subject: [PATCH 1/3] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.$invoiceId.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/routes/invoices.$invoiceId.tsx b/src/routes/invoices.$invoiceId.tsx index 535eaae..d71eded 100644 --- a/src/routes/invoices.$invoiceId.tsx +++ b/src/routes/invoices.$invoiceId.tsx @@ -130,6 +130,19 @@ function InvoiceDetail() { const isDraft = invoice.status === "draft"; const balance = Number(invoice.total) - Number(invoice.amount_paid); + // Billable fees vs billable expenses subtotals (non-billable items have amount 0 and are excluded). + const { feesSubtotal, expensesSubtotal } = useMemo(() => { + let fees = 0; + let exp = 0; + for (const it of items) { + const amt = Number(it.amount) || 0; + if (amt === 0) continue; + if (it.kind === "expense") exp += amt; + else fees += amt; + } + return { feesSubtotal: fees, expensesSubtotal: exp }; + }, [items]); + const setStatus = async (status: string) => { if (status === "void") { if (!confirm("Void this invoice? Totals will be zeroed out and time/expenses will be unbilled.")) return; From b3c09ae9d03022c111899da4fa28244eeef14348 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 20:08:54 +0000 Subject: [PATCH 2/3] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.$invoiceId.tsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/routes/invoices.$invoiceId.tsx b/src/routes/invoices.$invoiceId.tsx index d71eded..a74b8e1 100644 --- a/src/routes/invoices.$invoiceId.tsx +++ b/src/routes/invoices.$invoiceId.tsx @@ -114,6 +114,19 @@ function InvoiceDetail() { return Array.from(map.values()); }, [items]); + // Billable fees vs billable expenses subtotals (non-billable items have amount 0 and are excluded). + const { feesSubtotal, expensesSubtotal } = useMemo(() => { + let fees = 0; + let exp = 0; + for (const it of items) { + const amt = Number(it.amount) || 0; + if (amt === 0) continue; + if (it.kind === "expense") exp += amt; + else fees += amt; + } + return { feesSubtotal: fees, expensesSubtotal: exp }; + }, [items]); + if (loading) return

Loading…

; if (!invoice) { return ( @@ -130,19 +143,6 @@ function InvoiceDetail() { const isDraft = invoice.status === "draft"; const balance = Number(invoice.total) - Number(invoice.amount_paid); - // Billable fees vs billable expenses subtotals (non-billable items have amount 0 and are excluded). - const { feesSubtotal, expensesSubtotal } = useMemo(() => { - let fees = 0; - let exp = 0; - for (const it of items) { - const amt = Number(it.amount) || 0; - if (amt === 0) continue; - if (it.kind === "expense") exp += amt; - else fees += amt; - } - return { feesSubtotal: fees, expensesSubtotal: exp }; - }, [items]); - const setStatus = async (status: string) => { if (status === "void") { if (!confirm("Void this invoice? Totals will be zeroed out and time/expenses will be unbilled.")) return; From 89ec7d99e74561e1f215b8022baeb64b50a9bce4 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 20:09:04 +0000 Subject: [PATCH 3/3] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.$invoiceId.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/invoices.$invoiceId.tsx b/src/routes/invoices.$invoiceId.tsx index a74b8e1..2048dcc 100644 --- a/src/routes/invoices.$invoiceId.tsx +++ b/src/routes/invoices.$invoiceId.tsx @@ -510,6 +510,8 @@ function InvoiceDetail() {
+ +
Tax