From 05550e119154eca16699d4b759942dc4029c47dd 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:40:44 +0000 Subject: [PATCH 1/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/integrations/supabase/types.ts | 12 ++++++++++++ ...18014040_ad9b0c93-2adf-460b-9868-857319a7a19d.sql | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 supabase/migrations/20260418014040_ad9b0c93-2adf-460b-9868-857319a7a19d.sql diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index a14d4eb..83e55c5 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -185,6 +185,7 @@ export type Database = { } cases: { Row: { + archived_at: string | null assigned_attorney_id: string | null case_caption: string | null case_number: string @@ -221,6 +222,7 @@ export type Database = { updated_at: string } Insert: { + archived_at?: string | null assigned_attorney_id?: string | null case_caption?: string | null case_number: string @@ -257,6 +259,7 @@ export type Database = { updated_at?: string } Update: { + archived_at?: string | null assigned_attorney_id?: string | null case_caption?: string | null case_number?: string @@ -356,6 +359,7 @@ export type Database = { address_line1: string | null address_line2: string | null annual_interest_rate: number | null + archived_at: string | null board_members: Json city: string | null client_type: Database["public"]["Enums"]["client_type"] @@ -378,6 +382,7 @@ export type Database = { address_line1?: string | null address_line2?: string | null annual_interest_rate?: number | null + archived_at?: string | null board_members?: Json city?: string | null client_type?: Database["public"]["Enums"]["client_type"] @@ -400,6 +405,7 @@ export type Database = { address_line1?: string | null address_line2?: string | null annual_interest_rate?: number | null + archived_at?: string | null board_members?: Json city?: string | null client_type?: Database["public"]["Enums"]["client_type"] @@ -786,6 +792,7 @@ export type Database = { Row: { address_line1: string | null address_line2: string | null + archived_at: string | null city: string | null company: string | null contact_type: string @@ -805,6 +812,7 @@ export type Database = { Insert: { address_line1?: string | null address_line2?: string | null + archived_at?: string | null city?: string | null company?: string | null contact_type?: string @@ -824,6 +832,7 @@ export type Database = { Update: { address_line1?: string | null address_line2?: string | null + archived_at?: string | null city?: string | null company?: string | null contact_type?: string @@ -1447,6 +1456,7 @@ export type Database = { homeowners: { Row: { address: string | null + archived_at: string | null client_id: string created_at: string created_by: string | null @@ -1463,6 +1473,7 @@ export type Database = { } Insert: { address?: string | null + archived_at?: string | null client_id: string created_at?: string created_by?: string | null @@ -1479,6 +1490,7 @@ export type Database = { } Update: { address?: string | null + archived_at?: string | null client_id?: string created_at?: string created_by?: string | null diff --git a/supabase/migrations/20260418014040_ad9b0c93-2adf-460b-9868-857319a7a19d.sql b/supabase/migrations/20260418014040_ad9b0c93-2adf-460b-9868-857319a7a19d.sql new file mode 100644 index 0000000..8cefb10 --- /dev/null +++ b/supabase/migrations/20260418014040_ad9b0c93-2adf-460b-9868-857319a7a19d.sql @@ -0,0 +1,10 @@ +-- Add archived_at to the four entity tables +ALTER TABLE public.clients ADD COLUMN archived_at timestamptz; +ALTER TABLE public.cases ADD COLUMN archived_at timestamptz; +ALTER TABLE public.contacts ADD COLUMN archived_at timestamptz; +ALTER TABLE public.homeowners ADD COLUMN archived_at timestamptz; + +CREATE INDEX idx_clients_archived_at ON public.clients (archived_at); +CREATE INDEX idx_cases_archived_at ON public.cases (archived_at); +CREATE INDEX idx_contacts_archived_at ON public.contacts (archived_at); +CREATE INDEX idx_homeowners_archived_at ON public.homeowners (archived_at); \ No newline at end of file From 4ab142e60e42cc309a4a286f43ed39b009bd99bd 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:41:55 +0000 Subject: [PATCH 2/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/components/contacts/contacts-link-tab.tsx | 1 + .../invoices/generate-invoice-dialog.tsx | 2 +- src/components/quick-add/quick-add.tsx | 3 +- src/lib/archive.ts | 39 +++++++++++++++++++ src/lib/forms-shared.ts | 3 ++ src/routes/cases.new.tsx | 2 +- src/routes/status.index.tsx | 1 + src/routes/tasks.index.tsx | 2 +- 8 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 src/lib/archive.ts diff --git a/src/components/contacts/contacts-link-tab.tsx b/src/components/contacts/contacts-link-tab.tsx index 4f59c46..7e9b34b 100644 --- a/src/components/contacts/contacts-link-tab.tsx +++ b/src/components/contacts/contacts-link-tab.tsx @@ -176,6 +176,7 @@ function ContactPickerDialog({ supabase .from("contacts") .select("id, name, company, contact_type, email") + .is("archived_at", null) .order("name") .limit(200) .then(({ data }) => setRows(data ?? [])); diff --git a/src/components/invoices/generate-invoice-dialog.tsx b/src/components/invoices/generate-invoice-dialog.tsx index e4ad656..17144b7 100644 --- a/src/components/invoices/generate-invoice-dialog.tsx +++ b/src/components/invoices/generate-invoice-dialog.tsx @@ -50,7 +50,7 @@ export function GenerateInvoiceDialog({ open, onOpenChange, clientId, clientName (async () => { setLoading(true); const [{ data: cs }, { data: firm }] = await Promise.all([ - supabase.from("cases").select("id, case_number, title").eq("client_id", clientId), + supabase.from("cases").select("id, case_number, title").eq("client_id", clientId).is("archived_at", null), supabase.from("firm_settings").select("default_tax_rate").maybeSingle(), ]); const caseIds = (cs ?? []).map((c) => c.id); diff --git a/src/components/quick-add/quick-add.tsx b/src/components/quick-add/quick-add.tsx index 1995942..a486a1e 100644 --- a/src/components/quick-add/quick-add.tsx +++ b/src/components/quick-add/quick-add.tsx @@ -53,10 +53,11 @@ function useClientsAndCases(open: boolean) { if (!open) return; (async () => { const [{ data: cs }, { data: ks }] = await Promise.all([ - supabase.from("clients").select("id, name").order("name"), + supabase.from("clients").select("id, name").is("archived_at", null).order("name"), supabase .from("cases") .select("id, case_number, title, client_id, default_hourly_rate") + .is("archived_at", null) .order("case_number", { ascending: false }), ]); setClients(cs ?? []); diff --git a/src/lib/archive.ts b/src/lib/archive.ts new file mode 100644 index 0000000..0962033 --- /dev/null +++ b/src/lib/archive.ts @@ -0,0 +1,39 @@ +import { supabase } from "@/integrations/supabase/client"; +import { toast } from "sonner"; + +export type ArchivableTable = "clients" | "cases" | "contacts" | "homeowners"; + +const LABELS: Record = { + clients: "Client", + cases: "Case", + contacts: "Contact", + homeowners: "Homeowner", +}; + +/** + * Archive (soft) or restore a row by setting/clearing `archived_at`. + * Returns true on success. + */ +export async function setArchived( + table: ArchivableTable, + id: string, + archived: boolean, +): Promise { + const patch = { archived_at: archived ? new Date().toISOString() : null }; + const { error } = await (supabase.from(table) as any).update(patch).eq("id", id); + if (error) { + toast.error(`Could not ${archived ? "archive" : "restore"} ${LABELS[table].toLowerCase()}`, { + description: error.message, + }); + return false; + } + toast.success(`${LABELS[table]} ${archived ? "archived" : "restored"}`); + return true; +} + +/** Coerce common CSV truthy/falsy values into a boolean. */ +export function parseArchivedFlag(v: unknown): boolean { + if (typeof v === "boolean") return v; + const s = String(v ?? "").trim().toLowerCase(); + return s === "true" || s === "yes" || s === "1" || s === "y" || s === "archived"; +} diff --git a/src/lib/forms-shared.ts b/src/lib/forms-shared.ts index d31c96f..714e795 100644 --- a/src/lib/forms-shared.ts +++ b/src/lib/forms-shared.ts @@ -32,6 +32,7 @@ export async function fetchClients(): Promise { .select( "id,name,address_line1,address_line2,city,state,postal_code,primary_contact_email,primary_contact_phone,annual_interest_rate", ) + .is("archived_at", null) .order("name"); return (data ?? []) as ClientLite[]; } @@ -41,6 +42,7 @@ export async function fetchHomeowners(clientId: string): Promise { const { data } = await supabase .from("cases") .select("id,case_number,title") + .is("archived_at", null) .order("opened_at", { ascending: false }) .limit(500); return (data ?? []) as CaseLite[]; diff --git a/src/routes/cases.new.tsx b/src/routes/cases.new.tsx index 98b61da..4e7c735 100644 --- a/src/routes/cases.new.tsx +++ b/src/routes/cases.new.tsx @@ -47,7 +47,7 @@ function NewCase() { useEffect(() => { (async () => { const [{ data: cs }, { data: us }] = await Promise.all([ - supabase.from("clients").select("id, name").order("name"), + supabase.from("clients").select("id, name").is("archived_at", null).order("name"), supabase.from("profiles").select("id, full_name, email").order("full_name"), ]); setClients(cs ?? []); diff --git a/src/routes/status.index.tsx b/src/routes/status.index.tsx index bae6de9..1b40a95 100644 --- a/src/routes/status.index.tsx +++ b/src/routes/status.index.tsx @@ -48,6 +48,7 @@ function StatusUpdatesPage() { const { data, error } = await supabase .from("cases") .select("id, case_number, title, client:clients(name)") + .is("archived_at", null) .order("opened_at", { ascending: false }) .limit(500); if (error) toast.error("Could not load cases", { description: error.message }); diff --git a/src/routes/tasks.index.tsx b/src/routes/tasks.index.tsx index e729a45..ba5b12a 100644 --- a/src/routes/tasks.index.tsx +++ b/src/routes/tasks.index.tsx @@ -93,7 +93,7 @@ function TasksPage() { .order("due_date", { ascending: true, nullsFirst: false }), supabase.from("task_assignees").select("task_id, user_id"), supabase.from("profiles").select("id, full_name, email"), - supabase.from("cases").select("id, case_number, title"), + supabase.from("cases").select("id, case_number, title").is("archived_at", null), ]); setTasks((t.data ?? []) as TaskRow[]); setAssignees((a.data ?? []) as AssigneeRow[]); From 40f57cc2b73620625f022fbefdcf99ef1c4055ab 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:42:39 +0000 Subject: [PATCH 3/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/cases.index.tsx | 53 +++++++++++++++++++++++------ src/routes/clients.index.tsx | 64 +++++++++++++++++++++++++++++------- 2 files changed, 95 insertions(+), 22 deletions(-) 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)} + + + ); })} 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 4/9] 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 && ( )} + {canEdit && ( + + )} 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 ? ( - <> - - - - ) : null + <> + {contact.archived_at && ( + + Archived + + )} + {canEdit && ( + <> + + + + + )} + } /> 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 && ( -
- - -
- )} +
+ + {canEdit && ( + <> + + + + )} +
); From 894f9fd1593cb18a68dd7c6fa6ad98361cc57a4b 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:44:00 +0000 Subject: [PATCH 5/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/cases.$caseId.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/cases.$caseId.tsx b/src/routes/cases.$caseId.tsx index 4fc116d..5e3d3a0 100644 --- a/src/routes/cases.$caseId.tsx +++ b/src/routes/cases.$caseId.tsx @@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { supabase } from "@/integrations/supabase/client"; -import { ArrowLeft, FileText, Clock, DollarSign, Activity, Receipt, Scale, Users, Contact, Phone, Tag } from "lucide-react"; +import { ArrowLeft, FileText, Clock, DollarSign, Activity, Receipt, Scale, Users, Contact, Phone, Tag, Archive, ArchiveRestore } from "lucide-react"; import { ContactsLinkTab } from "@/components/contacts/contacts-link-tab"; import { CaseCallLogsTab } from "@/components/cases/call-logs-tab"; import { formatCurrency, formatDate, statusBadgeClass } from "@/lib/format"; @@ -20,6 +20,7 @@ import { CaseInvoicesTab } from "@/components/cases/invoices-tab"; import { CaseLitigationTab } from "@/components/cases/litigation-tab"; import { CaseCollectionsTab } from "@/components/cases/collections-tab"; import { CaseCustomFieldsTab } from "@/components/cases/custom-fields-tab"; +import { setArchived } from "@/lib/archive"; import { toast } from "sonner"; import { useAuth } from "@/lib/auth"; From 626593d68c1618bdd2f3edd4709fb5103ef2738c 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:44:18 +0000 Subject: [PATCH 6/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/cases.$caseId.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/routes/cases.$caseId.tsx b/src/routes/cases.$caseId.tsx index 5e3d3a0..ab9c829 100644 --- a/src/routes/cases.$caseId.tsx +++ b/src/routes/cases.$caseId.tsx @@ -101,6 +101,11 @@ function CaseDetail() {
{data.case_number} {data.status.replace("_", " ")} + {data.archived_at && ( + + Archived + + )}

{data.title}

@@ -129,6 +134,18 @@ function CaseDetail() { Closed +

)}
From 15861f06908871292cee7ad2dc6921ce42dd8b87 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:44:33 +0000 Subject: [PATCH 7/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 666a49b..30c6798 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -111,6 +111,7 @@ const IMPORTERS: ImporterConfig[] = [ management: "management_company", managementcompany: "management_company", units: "num_units", numunits: "num_units", notes: "notes", + archived: "_archived", isarchived: "_archived", status: "_archived", }, numeric: ["num_units", "annual_interest_rate"], transform: (r) => { From f64d5ac0c2a17479ecfa403fb35783ca140bdade 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:45:02 +0000 Subject: [PATCH 8/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 30c6798..451ce41 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -146,6 +146,7 @@ const IMPORTERS: ImporterConfig[] = [ zip: "postal_code", zipcode: "postal_code", postalcode: "postal_code", type: "contact_type", contacttype: "contact_type", notes: "notes", + archived: "_archived", isarchived: "_archived", }, transform: (r) => { if (!r.name && (r._first || r._last)) { @@ -198,6 +199,7 @@ const IMPORTERS: ImporterConfig[] = [ firstname: "first_name", lastname: "last_name", email: "email", phone: "phone", notes: "notes", + archived: "_archived", isarchived: "_archived", }, transform: (r, ctx) => { const cid = r._clientext ? ctx.clientByExt.get(String(r._clientext)) : null; @@ -244,6 +246,7 @@ const IMPORTERS: ImporterConfig[] = [ opposingcounselphone: "opposing_counsel_phone", claimamount: "claim_amount", settlementamount: "settlement_amount", hourlyrate: "default_hourly_rate", rate: "default_hourly_rate", + archived: "_archived", isarchived: "_archived", }, numeric: ["claim_amount", "settlement_amount", "default_hourly_rate"], dateCols: ["opened_at", "closed_at", "filing_date", "next_hearing_date", "statute_of_limitations"], From 7d946da7d08fd6284df5a3f5b9e09c7b8e8264a6 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:45:16 +0000 Subject: [PATCH 9/9] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 451ce41..535d3d1 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -578,6 +578,15 @@ function ImportPage() { const transformed = cfg.transform ? cfg.transform(row, ctx) : row; if (!transformed) { skipped++; continue; } + // archived → archived_at (only on tables that support it) + if ("_archived" in transformed) { + const flag = transformed._archived; + delete transformed._archived; + if (["clients", "cases", "contacts", "homeowners"].includes(cfg.table)) { + transformed.archived_at = toBool(flag) ? new Date().toISOString() : null; + } + } + // required check const missing = cfg.required.find((k) => !transformed[k] && transformed[k] !== 0); if (missing) { skipped++; continue; }