diff --git a/src/routes/cases.index.tsx b/src/routes/cases.index.tsx index 42b5629..c58e82a 100644 --- a/src/routes/cases.index.tsx +++ b/src/routes/cases.index.tsx @@ -6,10 +6,12 @@ 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 } from "lucide-react"; +import { Plus, Search, Archive, ArchiveRestore } from "lucide-react"; import { formatDate, statusBadgeClass } from "@/lib/format"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { setArchived } from "@/lib/archive"; export const Route = createFileRoute("/cases/")({ component: () => ( @@ -23,22 +25,34 @@ function CasesList() { const [cases, setCases] = useState([]); const [q, setQ] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); + const [view, setView] = useState<"active" | "archived">("active"); + + 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(() => { - (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 ?? []); - })(); + load(); }, []); - const filtered = cases.filter((c) => { + 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 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(); + }; return ( @@ -53,6 +67,12 @@ function CasesList() { />
+ setView(v as "active" | "archived")}> + + Active ({activeCount}) + Archived ({archivedCount}) + +
setQ(e.target.value)} placeholder="Search cases…" className="pl-9" /> @@ -81,11 +101,14 @@ function CasesList() { Status Attorney Opened + {filtered.length === 0 && ( - No cases. + + {view === "archived" ? "No archived cases." : "No cases."} + )} {filtered.map((c) => ( @@ -101,6 +124,16 @@ function CasesList() { {c.assignee?.full_name || c.assignee?.email || "—"} {formatDate(c.opened_at)} + + + ))} diff --git a/src/routes/clients.index.tsx b/src/routes/clients.index.tsx index 6a697d7..a851a78 100644 --- a/src/routes/clients.index.tsx +++ b/src/routes/clients.index.tsx @@ -6,10 +6,12 @@ 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 { ClientFormDialog } from "@/components/clients/client-form-dialog"; -import { Plus, Search, Building2, User, Briefcase as Building } from "lucide-react"; +import { Plus, Search, Building2, User, Archive, ArchiveRestore } from "lucide-react"; import { formatDate } from "@/lib/format"; +import { setArchived } from "@/lib/archive"; export const Route = createFileRoute("/clients/")({ component: () => ( @@ -23,6 +25,7 @@ function ClientsList() { const [clients, setClients] = useState([]); const [q, setQ] = useState(""); const [open, setOpen] = useState(false); + const [view, setView] = useState<"active" | "archived">("active"); const load = async () => { const { data } = await supabase @@ -36,7 +39,10 @@ function ClientsList() { load(); }, []); - const filtered = clients.filter((c) => + const visible = clients.filter((c) => + view === "archived" ? c.archived_at : !c.archived_at, + ); + const filtered = visible.filter((c) => [c.name, c.management_company, c.primary_contact_name, c.primary_contact_email] .filter(Boolean) .join(" ") @@ -44,9 +50,16 @@ function ClientsList() { .includes(q.toLowerCase()), ); + const archivedCount = clients.filter((c) => c.archived_at).length; + const activeCount = clients.length - archivedCount; + const typeIcon = (t: string) => t === "hoa" || t === "condo" ? Building2 : User; + const onArchive = async (id: string, archived: boolean) => { + if (await setArchived("clients", id, archived)) load(); + }; + return ( -
- - setQ(e.target.value)} - placeholder="Search by name, contact, manager…" - className="pl-9" - /> +
+ setView(v as "active" | "archived")}> + + Active ({activeCount}) + Archived ({archivedCount}) + + +
+ + setQ(e.target.value)} + placeholder="Search by name, contact, manager…" + className="pl-9" + /> +
@@ -80,13 +101,18 @@ function ClientsList() { Contact Units Created + {filtered.length === 0 && ( - - {clients.length === 0 ? "No clients yet. Add your first one." : "No matches."} + + {visible.length === 0 + ? view === "archived" + ? "No archived clients." + : "No clients yet. Add your first one." + : "No matches."} )} @@ -118,6 +144,20 @@ function ClientsList() { {c.num_units ?? "—"} {formatDate(c.created_at)} + + + ); })}