From 63b5fd24dc6edea9170dde4ed454aa6b5846c26c 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 18:42:44 +0000 Subject: [PATCH 1/4] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/invoices.index.tsx b/src/routes/invoices.index.tsx index a7b030c..90012e5 100644 --- a/src/routes/invoices.index.tsx +++ b/src/routes/invoices.index.tsx @@ -10,7 +10,7 @@ import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { supabase } from "@/integrations/supabase/client"; -import { Receipt, Search, FilePlus, Clock, DollarSign, CheckCircle2, Undo2 } from "lucide-react"; +import { Receipt, Search, FilePlus, Clock, DollarSign, CheckCircle2, Undo2, Trash2, Send, Ban } from "lucide-react"; import { formatCurrency, formatDate, statusBadgeClass } from "@/lib/format"; import { ensureMarkedInvoicedPlaceholder } from "@/lib/invoice-generation"; import { useAuth } from "@/lib/auth"; From 08809bc25e1454a5929a5212beab4e0716291580 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 18:42:52 +0000 Subject: [PATCH 2/4] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/invoices.index.tsx b/src/routes/invoices.index.tsx index 90012e5..3dff250 100644 --- a/src/routes/invoices.index.tsx +++ b/src/routes/invoices.index.tsx @@ -41,6 +41,7 @@ function InvoicesIndex() { const [expFilter, setExpFilter] = useState("unbilled"); const [selectedTime, setSelectedTime] = useState>(new Set()); const [selectedExp, setSelectedExp] = useState>(new Set()); + const [selectedInvoices, setSelectedInvoices] = useState>(new Set()); const [working, setWorking] = useState(false); // Map of placeholder invoice id -> true (placeholder = MARKED-INVOICED-* or IMPORT-PREBILLED-*) From ff394f6f570e2b8221ba344fb73bf544eece074a 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 18:43:04 +0000 Subject: [PATCH 3/4] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.index.tsx | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/routes/invoices.index.tsx b/src/routes/invoices.index.tsx index 3dff250..a9ff543 100644 --- a/src/routes/invoices.index.tsx +++ b/src/routes/invoices.index.tsx @@ -206,6 +206,47 @@ function InvoicesIndex() { } }; + const refreshInvoices = async () => { + const { data } = await supabase + .from("invoices") + .select("*, client:clients(id, name), case:cases(id, case_number, title)") + .order("created_at", { ascending: false }); + setInvoices(data ?? []); + setPlaceholderIds(new Set( + (data ?? []) + .filter((i: any) => + typeof i.invoice_number === "string" && + (i.invoice_number.startsWith("MARKED-INVOICED-") || i.invoice_number.startsWith("IMPORT-PREBILLED-")) + ) + .map((i: any) => i.id), + )); + }; + + const bulkInvoiceAction = async (action: "delete" | "sent" | "void") => { + if (selectedInvoices.size === 0) { toast.error("Select at least one invoice"); return; } + const ids = Array.from(selectedInvoices); + const verb = action === "delete" ? "delete" : `mark as ${action}`; + if (!confirm(`${verb.charAt(0).toUpperCase() + verb.slice(1)} ${ids.length} invoice(s)?`)) return; + setWorking(true); + try { + if (action === "delete") { + const { error } = await supabase.from("invoices").delete().in("id", ids); + if (error) throw error; + toast.success(`Deleted ${ids.length} invoice(s)`); + } else { + const { error } = await supabase.from("invoices").update({ status: action }).in("id", ids); + if (error) throw error; + toast.success(`Marked ${ids.length} invoice(s) as ${action}`); + } + setSelectedInvoices(new Set()); + await refreshInvoices(); + } catch (err: any) { + toast.error(err?.message ?? "Bulk action failed"); + } finally { + setWorking(false); + } + }; + return ( Date: Sun, 19 Apr 2026 18:43:25 +0000 Subject: [PATCH 4/4] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/invoices.index.tsx | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/routes/invoices.index.tsx b/src/routes/invoices.index.tsx index a9ff543..37503ea 100644 --- a/src/routes/invoices.index.tsx +++ b/src/routes/invoices.index.tsx @@ -299,9 +299,32 @@ function InvoicesIndex() { + {selectedInvoices.size > 0 && ( +
+ {selectedInvoices.size} selected +
+ + + + +
+
+ )} + @@ -312,9 +335,9 @@ function InvoicesIndex() { - {loading && } + {loading && } {!loading && filtered.length === 0 && ( - @@ -323,6 +346,12 @@ function InvoicesIndex() { const balance = Number(i.total) - Number(i.amount_paid); return ( +
+ 0 && selectedInvoices.size === filtered.length} + onCheckedChange={(c) => setSelectedInvoices(c ? new Set(filtered.map((i) => i.id)) : new Set())} + /> + Invoice # Client Issued
Loading…
Loading…
+
No invoices yet. Click New invoice to generate one.
e.stopPropagation()}> + setSelectedInvoices((s) => toggleSel(s, i.id))} + /> + {i.invoice_number}