diff --git a/src/components/cases/collections-tab.tsx b/src/components/cases/collections-tab.tsx index 1e7904d..d6d0e57 100644 --- a/src/components/cases/collections-tab.tsx +++ b/src/components/cases/collections-tab.tsx @@ -77,6 +77,7 @@ import { import { SortableLedgerRow } from "./ledger-row"; import { ApplyCollectionWorkflowDialog } from "@/components/collections/apply-collection-workflow-dialog"; import { MergeCollectionsDialog } from "./merge-collections-dialog"; +import { StatusFlagBadges } from "@/components/collections/status-flag-badges"; const TXN_TYPES = [ { value: "assessment", label: "Assessment" }, @@ -303,7 +304,12 @@ export function CaseCollectionsTab({ onClick={() => setActiveId(c.id)} > - {c.homeowner?.last_name}, {c.homeowner?.first_name} + + + {c.homeowner?.last_name}, {c.homeowner?.first_name} + + + {c.homeowner?.unit_number || "—"} diff --git a/src/components/collections/status-flag-badges.tsx b/src/components/collections/status-flag-badges.tsx new file mode 100644 index 0000000..423a516 --- /dev/null +++ b/src/components/collections/status-flag-badges.tsx @@ -0,0 +1,40 @@ +import { Badge } from "@/components/ui/badge"; + +/** + * Compact BK / PP indicator badges used next to case/collection names. + * - BK = Bankruptcy + * - PP = Payment Plan + */ +export function StatusFlagBadges({ + bk, + pp, + className, +}: { + bk?: boolean | null; + pp?: boolean | null; + className?: string; +}) { + if (!bk && !pp) return null; + return ( + + {bk && ( + + BK + + )} + {pp && ( + + PP + + )} + + ); +} diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 4947872..d22a0e1 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -880,9 +880,11 @@ export type Database = { current_stage: string | null homeowner_id: string | null id: string + in_bankruptcy: boolean interest_rate_override: number | null name: string | null notes: string | null + on_payment_plan: boolean opened_at: string status: string updated_at: string @@ -895,9 +897,11 @@ export type Database = { current_stage?: string | null homeowner_id?: string | null id?: string + in_bankruptcy?: boolean interest_rate_override?: number | null name?: string | null notes?: string | null + on_payment_plan?: boolean opened_at?: string status?: string updated_at?: string @@ -910,9 +914,11 @@ export type Database = { current_stage?: string | null homeowner_id?: string | null id?: string + in_bankruptcy?: boolean interest_rate_override?: number | null name?: string | null notes?: string | null + on_payment_plan?: boolean opened_at?: string status?: string updated_at?: string diff --git a/src/routes/cases.index.tsx b/src/routes/cases.index.tsx index 4eb500c..31740a6 100644 --- a/src/routes/cases.index.tsx +++ b/src/routes/cases.index.tsx @@ -16,6 +16,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { setArchived } from "@/lib/archive"; import { NewCaseDialog } from "@/components/cases/new-case-dialog"; import { BulkConvertToCollectionsDialog } from "@/components/cases/bulk-convert-to-collections-dialog"; +import { StatusFlagBadges } from "@/components/collections/status-flag-badges"; import { formatDistanceToNow } from "date-fns"; import { toast } from "sonner"; @@ -61,7 +62,7 @@ function CasesList() { // Aggregate latest activity from related child tables. const [ tasksRes, timeRes, expensesRes, docsRes, callRes, - commentsRes, invoicesRes, eventsRes, + commentsRes, invoicesRes, eventsRes, collectionsRes, ] = await Promise.all([ supabase.from("tasks").select("case_id, updated_at").not("case_id", "is", null).limit(20000), supabase.from("time_entries").select("case_id, updated_at").limit(20000), @@ -71,6 +72,7 @@ function CasesList() { supabase.from("comments").select("case_id, created_at").not("case_id", "is", null).limit(20000), supabase.from("invoices").select("case_id, updated_at").not("case_id", "is", null).limit(20000), supabase.from("events").select("case_id, updated_at").not("case_id", "is", null).limit(20000), + supabase.from("collections").select("case_id, in_bankruptcy, on_payment_plan").limit(20000), ]); const latest: Record = {}; @@ -91,11 +93,23 @@ function CasesList() { ingest(invoicesRes.data, "updated_at"); ingest(eventsRes.data, "updated_at"); + // Roll up BK / PP flags: case has the flag if ANY of its collections do. + const flagMap: Record = {}; + for (const r of collectionsRes.data ?? []) { + const cid = (r as any).case_id as string | null; + if (!cid) continue; + const cur = flagMap[cid] ?? { bk: false, pp: false }; + if ((r as any).in_bankruptcy) cur.bk = true; + if ((r as any).on_payment_plan) cur.pp = true; + flagMap[cid] = cur; + } + // Fallback to the case's own updated_at if no child activity found. const enriched = baseRows.map((c: any) => { const childTs = latest[c.id]; const lastActivity = childTs && (!c.updated_at || childTs > c.updated_at) ? childTs : c.updated_at; - return { ...c, last_activity: lastActivity }; + const flags = flagMap[c.id]; + return { ...c, last_activity: lastActivity, in_bankruptcy: !!flags?.bk, on_payment_plan: !!flags?.pp }; }); setCases(enriched); @@ -457,14 +471,17 @@ function CasesList() { /> - e.stopPropagation()} - > - {c.title} - + + e.stopPropagation()} + > + {c.title} + + +
{c.case_number}
{c.client?.name ?? "—"} diff --git a/src/routes/collections.$collectionId.tsx b/src/routes/collections.$collectionId.tsx index a8665e3..7e62441 100644 --- a/src/routes/collections.$collectionId.tsx +++ b/src/routes/collections.$collectionId.tsx @@ -67,6 +67,7 @@ import { SearchableSelect } from "@/components/ui/searchable-select"; import { toast } from "sonner"; import { spawnNextCollectionWorkflowTask } from "@/lib/workflow-chain"; import { ApplyCollectionWorkflowDialog } from "@/components/collections/apply-collection-workflow-dialog"; +import { StatusFlagBadges } from "@/components/collections/status-flag-badges"; export const Route = createFileRoute("/collections/$collectionId")({ component: CollectionDetailRoute, @@ -356,8 +357,49 @@ function CollectionDetailRoute() { Closed{collection.closed_at ? ` · ${formatDate(collection.closed_at)}` : ""} )} +
+ +