diff --git a/src/routes/cases.index.tsx b/src/routes/cases.index.tsx index 8d93c6b..70d7093 100644 --- a/src/routes/cases.index.tsx +++ b/src/routes/cases.index.tsx @@ -13,8 +13,9 @@ import { formatDate, statusBadgeClass } from "@/lib/format"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { setArchived } from "@/lib/archive"; import { NewCaseDialog } from "@/components/cases/new-case-dialog"; +import { formatDistanceToNow } from "date-fns"; -type SortKey = "title" | "client" | "status" | "attorney" | "opened_at"; +type SortKey = "title" | "client" | "status" | "attorney" | "opened_at" | "last_activity"; type SortDir = "asc" | "desc"; export const Route = createFileRoute("/cases/")({ @@ -32,15 +33,58 @@ function CasesList() { const [statusFilter, setStatusFilter] = useState("all"); const [view, setView] = useState<"active" | "archived">("active"); const [newOpen, setNewOpen] = useState(false); - const [sortKey, setSortKey] = useState("title"); - const [sortDir, setSortDir] = useState("asc"); + const [sortKey, setSortKey] = useState("last_activity"); + const [sortDir, setSortDir] = useState("desc"); const load = async () => { - const { data } = await supabase + const { data: caseRows } = await supabase .from("cases") .select("*, client:clients(name), assignee:profiles!cases_assigned_attorney_id_fkey(full_name, email)") .order("updated_at", { ascending: false }); - setCases(data ?? []); + + const baseRows = caseRows ?? []; + + // Aggregate latest activity from related child tables. + const [ + tasksRes, timeRes, expensesRes, docsRes, callRes, + commentsRes, invoicesRes, eventsRes, + ] = 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), + supabase.from("expenses").select("case_id, updated_at").limit(20000), + supabase.from("documents").select("case_id, created_at").limit(20000), + supabase.from("call_logs").select("case_id, updated_at").limit(20000), + 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), + ]); + + const latest: Record = {}; + const ingest = (rows: any[] | null | undefined, field: "updated_at" | "created_at") => { + for (const r of rows ?? []) { + const cid = r.case_id as string | null; + const ts = r[field] as string | null; + if (!cid || !ts) continue; + if (!latest[cid] || ts > latest[cid]) latest[cid] = ts; + } + }; + ingest(tasksRes.data, "updated_at"); + ingest(timeRes.data, "updated_at"); + ingest(expensesRes.data, "updated_at"); + ingest(docsRes.data, "created_at"); + ingest(callRes.data, "updated_at"); + ingest(commentsRes.data, "created_at"); + ingest(invoicesRes.data, "updated_at"); + ingest(eventsRes.data, "updated_at"); + + // 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 }; + }); + + setCases(enriched); }; useEffect(() => { @@ -64,6 +108,7 @@ function CasesList() { case "status": return (c.status ?? "").toLowerCase(); case "attorney": return (c.assignee?.full_name || c.assignee?.email || "").toLowerCase(); case "opened_at": return c.opened_at ?? ""; + case "last_activity": return c.last_activity ?? ""; } }; const arr = [...filtered]; @@ -82,7 +127,7 @@ function CasesList() { setSortDir(sortDir === "asc" ? "desc" : "asc"); } else { setSortKey(key); - setSortDir(key === "opened_at" ? "desc" : "asc"); + setSortDir(key === "opened_at" || key === "last_activity" ? "desc" : "asc"); } }; @@ -158,12 +203,13 @@ function CasesList() { + {sorted.length === 0 && ( - + {view === "archived" ? "No archived cases." : "No cases."} )} @@ -190,6 +236,9 @@ function CasesList() { {c.assignee?.full_name || c.assignee?.email || "—"} {formatDate(c.opened_at)} + + {c.last_activity ? `${formatDistanceToNow(new Date(c.last_activity))} ago` : "—"} + e.stopPropagation()}>