import { createFileRoute, Link } from "@tanstack/react-router"; import { useEffect, useMemo, useState } from "react"; import { ProtectedLayout } from "@/components/protected-layout"; import { PageContainer, PageHeader } from "@/components/app-shell"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { supabase } from "@/integrations/supabase/client"; import { Receipt, Search } from "lucide-react"; import { formatCurrency, formatDate, statusBadgeClass } from "@/lib/format"; export const Route = createFileRoute("/invoices/")({ component: () => ( ), }); function InvoicesIndex() { const [invoices, setInvoices] = useState([]); const [loading, setLoading] = useState(true); const [q, setQ] = useState(""); const [status, setStatus] = useState("all"); useEffect(() => { (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 ?? []); setLoading(false); })(); }, []); const filtered = useMemo(() => { return invoices.filter((i) => { if (status !== "all" && i.status !== status) return false; if (!q) return true; const s = q.toLowerCase(); return ( i.invoice_number?.toLowerCase().includes(s) || i.client?.name?.toLowerCase().includes(s) || i.case?.case_number?.toLowerCase().includes(s) ); }); }, [invoices, q, status]); const totals = useMemo(() => { const outstanding = filtered.reduce((s, i) => s + (Number(i.total) - Number(i.amount_paid)), 0); const paid = filtered.reduce((s, i) => s + Number(i.amount_paid), 0); return { outstanding, paid, count: filtered.length }; }, [filtered]); return (
setQ(e.target.value)} />
{loading && } {!loading && filtered.length === 0 && ( )} {filtered.map((i) => { const balance = Number(i.total) - Number(i.amount_paid); return ( ); })}
Invoice # Client Issued Due Status Total Balance
Loading…
No invoices yet. Generate one from a Client or Case page.
{i.invoice_number} {i.client ? ( {i.client.name} ) : "—"} {formatDate(i.issue_date)} {formatDate(i.due_date)} {i.status} {formatCurrency(i.total)} {formatCurrency(balance)}
); } function Stat({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); }