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 { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { supabase } from "@/integrations/supabase/client"; import { Plus, Search, Archive, ArchiveRestore, ArrowUp, ArrowDown, ArrowUpDown } from "lucide-react"; 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"; type SortKey = "title" | "client" | "status" | "attorney" | "opened_at"; type SortDir = "asc" | "desc"; export const Route = createFileRoute("/cases/")({ component: () => ( ), }); function CasesList() { const [cases, setCases] = useState([]); const [q, setQ] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const [view, setView] = useState<"active" | "archived">("active"); const [newOpen, setNewOpen] = useState(false); const [sortKey, setSortKey] = useState("opened_at"); const [sortDir, setSortDir] = useState("desc"); const load = async () => { const { data } = 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 ?? []); }; useEffect(() => { load(); }, []); const visible = cases.filter((c) => view === "archived" ? c.archived_at : !c.archived_at, ); const filtered = visible.filter((c) => { const okStatus = statusFilter === "all" || c.status === statusFilter; const okQ = [c.title, c.case_number, c.client?.name].filter(Boolean).join(" ").toLowerCase().includes(q.toLowerCase()); return okStatus && okQ; }); const sorted = useMemo(() => { const getVal = (c: any): string => { switch (sortKey) { case "title": return (c.title ?? "").toLowerCase(); case "client": return (c.client?.name ?? "").toLowerCase(); case "status": return (c.status ?? "").toLowerCase(); case "attorney": return (c.assignee?.full_name || c.assignee?.email || "").toLowerCase(); case "opened_at": return c.opened_at ?? ""; } }; const arr = [...filtered]; arr.sort((a, b) => { const av = getVal(a); const bv = getVal(b); if (av < bv) return sortDir === "asc" ? -1 : 1; if (av > bv) return sortDir === "asc" ? 1 : -1; return 0; }); return arr; }, [filtered, sortKey, sortDir]); const toggleSort = (key: SortKey) => { if (sortKey === key) { setSortDir(sortDir === "asc" ? "desc" : "asc"); } else { setSortKey(key); setSortDir(key === "opened_at" ? "desc" : "asc"); } }; const archivedCount = cases.filter((c) => c.archived_at).length; const activeCount = cases.length - archivedCount; const onArchive = async (id: string, archived: boolean) => { if (await setArchived("cases", id, archived)) load(); }; const SortHeader = ({ label, k, align = "left" }: { label: string; k: SortKey; align?: "left" | "right" }) => { const active = sortKey === k; const Icon = !active ? ArrowUpDown : sortDir === "asc" ? ArrowUp : ArrowDown; return ( ); }; return ( setNewOpen(true)}> New case } />
setView(v as "active" | "archived")}> Active ({activeCount}) Archived ({archivedCount})
setQ(e.target.value)} placeholder="Search cases…" className="pl-9" />
{sorted.length === 0 && ( )} {sorted.map((c) => ( ))}
{view === "archived" ? "No archived cases." : "No cases."}
{c.title}
{c.case_number}
{c.client?.name ?? "—"} {c.status.replace("_", " ")} {c.assignee?.full_name || c.assignee?.email || "—"} {formatDate(c.opened_at)}
); }