import { createFileRoute, Link } from "@tanstack/react-router"; import { useEffect, useState } from "react"; import { ProtectedLayout } from "@/components/protected-layout"; import { PageContainer, PageHeader } from "@/components/app-shell"; import { supabase } from "@/integrations/supabase/client"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { formatCurrency, formatDate } from "@/lib/format"; import { ChevronRight, Wallet, Search } from "lucide-react"; export const Route = createFileRoute("/collections/")({ component: CollectionsIndexPage, }); interface Stage { key: string; label: string; sort_order: number; } function CollectionsIndexPage() { const [rows, setRows] = useState([]); const [balances, setBalances] = useState>({}); const [openTaskCounts, setOpenTaskCounts] = useState>({}); const [stages, setStages] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); const [stageFilter, setStageFilter] = useState("all"); const load = async () => { setLoading(true); const [{ data: cols }, { data: stgs }] = await Promise.all([ supabase .from("collections") .select( "id, status, current_stage, opened_at, homeowner:homeowners(id, first_name, last_name, unit_number, opening_balance), case:cases(id, case_number, title, client:clients(id, name))", ) .order("created_at", { ascending: false }), supabase .from("collection_workflow_stages") .select("*") .order("sort_order"), ]); const list = cols ?? []; setRows(list); setStages((stgs ?? []) as Stage[]); if (list.length) { const ids = list.map((c: any) => c.id); const [{ data: ents }, { data: tasks }] = await Promise.all([ supabase .from("collection_ledger_entries") .select("collection_id, debit, credit") .in("collection_id", ids), supabase .from("collection_tasks") .select("collection_id") .in("collection_id", ids) .eq("done", false), ]); const balMap: Record = {}; list.forEach((c: any) => { balMap[c.id] = Number(c.homeowner?.opening_balance ?? 0); }); (ents ?? []).forEach((e: any) => { balMap[e.collection_id] = (balMap[e.collection_id] ?? 0) + Number(e.debit) - Number(e.credit); }); setBalances(balMap); const taskMap: Record = {}; (tasks ?? []).forEach((t: any) => { taskMap[t.collection_id] = (taskMap[t.collection_id] ?? 0) + 1; }); setOpenTaskCounts(taskMap); } setLoading(false); }; useEffect(() => { load(); }, []); const stageLabel = (key: string | null) => stages.find((s) => s.key === key)?.label ?? key ?? "—"; const filtered = rows.filter((r) => { if (stageFilter !== "all" && r.current_stage !== stageFilter) return false; if (!search.trim()) return true; const q = search.toLowerCase(); return ( `${r.homeowner?.first_name ?? ""} ${r.homeowner?.last_name ?? ""}` .toLowerCase() .includes(q) || (r.case?.case_number ?? "").toLowerCase().includes(q) || (r.case?.client?.name ?? "").toLowerCase().includes(q) || (r.homeowner?.unit_number ?? "").toLowerCase().includes(q) ); }); return (
setSearch(e.target.value)} className="pl-9" />
{loading ? (
Loading…
) : filtered.length === 0 ? (
No collections found.
) : ( Homeowner HOA / Case Stage Open tasks Opened Balance {filtered.map((r) => { const bal = balances[r.id] ?? 0; const tcount = openTaskCounts[r.id] ?? 0; return ( {}} > {r.homeowner?.last_name}, {r.homeowner?.first_name} {r.homeowner?.unit_number && ( · Unit {r.homeowner.unit_number} )}
{r.case?.client?.name}
{r.case?.case_number} · {r.case?.title}
{stageLabel(r.current_stage)} {tcount > 0 ? ( {tcount} ) : ( — )} {formatDate(r.opened_at)} 0 ? "text-destructive font-semibold" : bal < 0 ? "text-emerald-600" : "" }`} > {formatCurrency(Math.abs(bal))} {bal < 0 ? " CR" : ""}
); })}
)}
); }