From e7a6ed5d53778757ecdada111bc8217c9e57277a Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 01:43:35 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/clients.$clientId.tsx | 22 +++++++++++- src/routes/contacts.$contactId.tsx | 34 +++++++++++++++---- src/routes/contacts.index.tsx | 54 +++++++++++++++++++++++------- 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/src/routes/clients.$clientId.tsx b/src/routes/clients.$clientId.tsx index 8e6a592..5a5b3d6 100644 --- a/src/routes/clients.$clientId.tsx +++ b/src/routes/clients.$clientId.tsx @@ -9,13 +9,14 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { supabase } from "@/integrations/supabase/client"; import { ClientFormDialog } from "@/components/clients/client-form-dialog"; import { useAuth } from "@/lib/auth"; -import { ArrowLeft, Edit, Plus, Building2, User, MapPin, Mail, Phone, Users, Activity, Briefcase, FileDown, Contact, Receipt } from "lucide-react"; +import { ArrowLeft, Edit, Plus, Building2, User, MapPin, Mail, Phone, Users, Activity, Briefcase, FileDown, Contact, Receipt, Archive, ArchiveRestore } from "lucide-react"; import { ContactsLinkTab } from "@/components/contacts/contacts-link-tab"; import { formatCurrency, formatDate, formatDateTime, statusBadgeClass } from "@/lib/format"; import { toast } from "sonner"; import { downloadStatusReport } from "@/lib/status-pdf"; import { GenerateInvoiceDialog } from "@/components/invoices/generate-invoice-dialog"; import { TrustAccountPanel } from "@/components/trust/trust-account-panel"; +import { setArchived } from "@/lib/archive"; export const Route = createFileRoute("/clients/$clientId")({ component: () => ( @@ -123,11 +124,30 @@ function ClientDetail() { description={client.management_company || client.client_type.toUpperCase()} actions={ <> + {client.archived_at && ( + + Archived + + )} {canEdit && ( setEditOpen(true)}> Edit )} + {canEdit && ( + { + if (await setArchived("clients", client.id, !client.archived_at)) load(); + }} + > + {client.archived_at ? ( + <> Restore> + ) : ( + <> Archive> + )} + + )} setInvoiceOpen(true)}> Generate invoice diff --git a/src/routes/contacts.$contactId.tsx b/src/routes/contacts.$contactId.tsx index ec01cdb..0aa683f 100644 --- a/src/routes/contacts.$contactId.tsx +++ b/src/routes/contacts.$contactId.tsx @@ -8,8 +8,9 @@ import { Badge } from "@/components/ui/badge"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/lib/auth"; import { toast } from "sonner"; -import { ArrowLeft, Edit, Trash2, Mail, Phone, MapPin, Building2, Briefcase, Users } from "lucide-react"; +import { ArrowLeft, Edit, Trash2, Mail, Phone, MapPin, Building2, Briefcase, Users, Archive, ArchiveRestore } from "lucide-react"; import { ContactFormDialog, CONTACT_TYPES } from "@/components/contacts/contact-form-dialog"; +import { setArchived } from "@/lib/archive"; export const Route = createFileRoute("/contacts/$contactId")({ component: () => ( @@ -87,12 +88,31 @@ function ContactDetail() { title={contact.name} description={[contact.title, contact.company].filter(Boolean).join(" · ") || typeLabel} actions={ - canEdit ? ( - <> - setEditOpen(true)}> Edit - Delete - > - ) : null + <> + {contact.archived_at && ( + + Archived + + )} + {canEdit && ( + <> + setEditOpen(true)}> Edit + { + if (await setArchived("contacts", contact.id, !contact.archived_at)) load(); + }} + > + {contact.archived_at ? ( + <> Restore> + ) : ( + <> Archive> + )} + + Delete + > + )} + > } /> diff --git a/src/routes/contacts.index.tsx b/src/routes/contacts.index.tsx index d23950c..4d342b0 100644 --- a/src/routes/contacts.index.tsx +++ b/src/routes/contacts.index.tsx @@ -6,11 +6,13 @@ import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; 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 { useAuth } from "@/lib/auth"; import { toast } from "sonner"; -import { Plus, Search, Mail, Phone, Building2, ChevronRight, Edit, Trash2, Briefcase, Users } from "lucide-react"; +import { Plus, Search, Mail, Phone, Building2, ChevronRight, Edit, Trash2, Briefcase, Users, Archive, ArchiveRestore } from "lucide-react"; import { ContactFormDialog, CONTACT_TYPES, type ContactRecord } from "@/components/contacts/contact-form-dialog"; +import { setArchived } from "@/lib/archive"; export const Route = createFileRoute("/contacts/")({ component: () => ( @@ -25,6 +27,7 @@ interface ContactRow extends Required>, ContactRecor created_by: string | null; case_links: number; client_links: number; + archived_at: string | null; } function ContactsIndex() { @@ -33,6 +36,7 @@ function ContactsIndex() { const [loading, setLoading] = useState(true); const [q, setQ] = useState(""); const [typeFilter, setTypeFilter] = useState("all"); + const [view, setView] = useState<"active" | "archived">("active"); const [editing, setEditing] = useState(null); const [open, setOpen] = useState(false); @@ -65,6 +69,7 @@ function ContactsIndex() { const filtered = useMemo(() => { const term = q.trim().toLowerCase(); return rows.filter((r) => { + if (view === "archived" ? !r.archived_at : !!r.archived_at) return false; if (typeFilter !== "all" && r.contact_type !== typeFilter) return false; if (!term) return true; return ( @@ -74,7 +79,10 @@ function ContactsIndex() { (r.phone ?? "").toLowerCase().includes(term) ); }); - }, [rows, q, typeFilter]); + }, [rows, q, typeFilter, view]); + + const archivedCount = rows.filter((r) => r.archived_at).length; + const activeCount = rows.length - archivedCount; const remove = async (id: string, name: string) => { if (!confirm(`Delete contact "${name}"? This will also remove all case and client links.`)) return; @@ -87,6 +95,10 @@ function ContactsIndex() { setRows((r) => r.filter((x) => x.id !== id)); }; + const onArchive = async (id: string, archived: boolean) => { + if (await setArchived("contacts", id, archived)) load(); + }; + const startNew = () => { setEditing(null); setOpen(true); @@ -110,6 +122,12 @@ function ContactsIndex() { + setView(v as "active" | "archived")}> + + Active ({activeCount}) + Archived ({archivedCount}) + + Loading… ) : filtered.length === 0 ? ( - {rows.length === 0 ? "No contacts yet." : "No contacts match your filters."} + {rows.length === 0 ? "No contacts yet." : view === "archived" ? "No archived contacts." : "No contacts match your filters."} ) : ( @@ -171,16 +189,26 @@ function ContactsIndex() { - {canEdit && ( - - startEdit(c)}> - - - remove(c.id, c.name)}> - - - - )} + + onArchive(c.id, !c.archived_at)} + > + {c.archived_at ? : } + + {canEdit && ( + <> + startEdit(c)}> + + + remove(c.id, c.name)}> + + + > + )} + );
- {rows.length === 0 ? "No contacts yet." : "No contacts match your filters."} + {rows.length === 0 ? "No contacts yet." : view === "archived" ? "No archived contacts." : "No contacts match your filters."}