From 82f943c3082051b934fbcedfba19abca224c0aac Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:03:38 +0000 Subject: [PATCH 1/5] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/integrations/supabase/types.ts | 57 +++++++++++++++++++ ...5_883e6f41-1dfc-4553-96a3-ea4ed89432db.sql | 20 +++++++ 2 files changed, 77 insertions(+) create mode 100644 supabase/migrations/20260417000335_883e6f41-1dfc-4553-96a3-ea4ed89432db.sql diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 50a58eb..b4820bd 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -17,49 +17,106 @@ export type Database = { cases: { Row: { assigned_attorney_id: string | null + case_caption: string | null case_number: string + case_summary: string | null + claim_amount: number | null client_id: string closed_at: string | null + court: string | null + court_case_number: string | null created_at: string created_by: string | null default_hourly_rate: number | null description: string | null + filing_date: string | null id: string + judge: string | null + jurisdiction: string | null + key_dates: Json + litigation_stage: string | null + next_hearing_date: string | null + next_hearing_notes: string | null opened_at: string + opposing_counsel: string | null + opposing_counsel_email: string | null + opposing_counsel_firm: string | null + opposing_counsel_phone: string | null + opposing_party: string | null practice_area: string | null + settlement_amount: number | null status: Database["public"]["Enums"]["case_status"] + statute_of_limitations: string | null title: string updated_at: string } Insert: { assigned_attorney_id?: string | null + case_caption?: string | null case_number: string + case_summary?: string | null + claim_amount?: number | null client_id: string closed_at?: string | null + court?: string | null + court_case_number?: string | null created_at?: string created_by?: string | null default_hourly_rate?: number | null description?: string | null + filing_date?: string | null id?: string + judge?: string | null + jurisdiction?: string | null + key_dates?: Json + litigation_stage?: string | null + next_hearing_date?: string | null + next_hearing_notes?: string | null opened_at?: string + opposing_counsel?: string | null + opposing_counsel_email?: string | null + opposing_counsel_firm?: string | null + opposing_counsel_phone?: string | null + opposing_party?: string | null practice_area?: string | null + settlement_amount?: number | null status?: Database["public"]["Enums"]["case_status"] + statute_of_limitations?: string | null title: string updated_at?: string } Update: { assigned_attorney_id?: string | null + case_caption?: string | null case_number?: string + case_summary?: string | null + claim_amount?: number | null client_id?: string closed_at?: string | null + court?: string | null + court_case_number?: string | null created_at?: string created_by?: string | null default_hourly_rate?: number | null description?: string | null + filing_date?: string | null id?: string + judge?: string | null + jurisdiction?: string | null + key_dates?: Json + litigation_stage?: string | null + next_hearing_date?: string | null + next_hearing_notes?: string | null opened_at?: string + opposing_counsel?: string | null + opposing_counsel_email?: string | null + opposing_counsel_firm?: string | null + opposing_counsel_phone?: string | null + opposing_party?: string | null practice_area?: string | null + settlement_amount?: number | null status?: Database["public"]["Enums"]["case_status"] + statute_of_limitations?: string | null title?: string updated_at?: string } diff --git a/supabase/migrations/20260417000335_883e6f41-1dfc-4553-96a3-ea4ed89432db.sql b/supabase/migrations/20260417000335_883e6f41-1dfc-4553-96a3-ea4ed89432db.sql new file mode 100644 index 0000000..74152cd --- /dev/null +++ b/supabase/migrations/20260417000335_883e6f41-1dfc-4553-96a3-ea4ed89432db.sql @@ -0,0 +1,20 @@ +ALTER TABLE public.cases + ADD COLUMN IF NOT EXISTS case_summary text, + ADD COLUMN IF NOT EXISTS opposing_party text, + ADD COLUMN IF NOT EXISTS opposing_counsel text, + ADD COLUMN IF NOT EXISTS opposing_counsel_firm text, + ADD COLUMN IF NOT EXISTS opposing_counsel_email text, + ADD COLUMN IF NOT EXISTS opposing_counsel_phone text, + ADD COLUMN IF NOT EXISTS court text, + ADD COLUMN IF NOT EXISTS jurisdiction text, + ADD COLUMN IF NOT EXISTS judge text, + ADD COLUMN IF NOT EXISTS court_case_number text, + ADD COLUMN IF NOT EXISTS case_caption text, + ADD COLUMN IF NOT EXISTS filing_date date, + ADD COLUMN IF NOT EXISTS next_hearing_date date, + ADD COLUMN IF NOT EXISTS next_hearing_notes text, + ADD COLUMN IF NOT EXISTS statute_of_limitations date, + ADD COLUMN IF NOT EXISTS claim_amount numeric, + ADD COLUMN IF NOT EXISTS settlement_amount numeric, + ADD COLUMN IF NOT EXISTS litigation_stage text, + ADD COLUMN IF NOT EXISTS key_dates jsonb NOT NULL DEFAULT '[]'::jsonb; \ No newline at end of file From ece6b645afe99164e58ed463ddb8f634e5e857a2 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:04:14 +0000 Subject: [PATCH 2/5] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/components/cases/litigation-tab.tsx | 260 ++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 src/components/cases/litigation-tab.tsx diff --git a/src/components/cases/litigation-tab.tsx b/src/components/cases/litigation-tab.tsx new file mode 100644 index 0000000..977fd79 --- /dev/null +++ b/src/components/cases/litigation-tab.tsx @@ -0,0 +1,260 @@ +import { useState } from "react"; +import { supabase } from "@/integrations/supabase/client"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Separator } from "@/components/ui/separator"; +import { Pencil, Save, X, Calendar, Gavel, Scale, FileSearch, Users, AlertCircle } from "lucide-react"; +import { toast } from "sonner"; +import { formatCurrency, formatDate } from "@/lib/format"; + +interface Props { + caseRecord: any; + canManage: boolean; + onSaved: () => void; +} + +const FIELDS: Array<{ key: string; label: string; type?: string; full?: boolean }> = [ + { key: "case_caption", label: "Case caption", full: true }, + { key: "court_case_number", label: "Court case #" }, + { key: "litigation_stage", label: "Litigation stage" }, + { key: "court", label: "Court" }, + { key: "jurisdiction", label: "Jurisdiction" }, + { key: "judge", label: "Judge" }, + { key: "filing_date", label: "Filing date", type: "date" }, + { key: "next_hearing_date", label: "Next hearing", type: "date" }, + { key: "statute_of_limitations", label: "Statute of limitations", type: "date" }, + { key: "claim_amount", label: "Claim amount", type: "number" }, + { key: "settlement_amount", label: "Settlement amount", type: "number" }, +]; + +const OPPOSING: Array<{ key: string; label: string }> = [ + { key: "opposing_party", label: "Opposing party" }, + { key: "opposing_counsel", label: "Opposing counsel" }, + { key: "opposing_counsel_firm", label: "Counsel firm" }, + { key: "opposing_counsel_email", label: "Counsel email" }, + { key: "opposing_counsel_phone", label: "Counsel phone" }, +]; + +export function CaseLitigationTab({ caseRecord, canManage, onSaved }: Props) { + const [editing, setEditing] = useState(false); + const [saving, setSaving] = useState(false); + const [form, setForm] = useState(() => ({ ...caseRecord })); + const [keyDates, setKeyDates] = useState>( + Array.isArray(caseRecord.key_dates) ? caseRecord.key_dates : [], + ); + + const startEdit = () => { + setForm({ ...caseRecord }); + setKeyDates(Array.isArray(caseRecord.key_dates) ? caseRecord.key_dates : []); + setEditing(true); + }; + + const cancel = () => { + setEditing(false); + }; + + const save = async () => { + setSaving(true); + const payload: any = { key_dates: keyDates.filter((k) => k.date || k.label) }; + [...FIELDS, ...OPPOSING, { key: "case_summary", label: "", type: "text" }, { key: "next_hearing_notes", label: "", type: "text" }].forEach((f) => { + const v = form[f.key]; + if (f.type === "number") payload[f.key] = v === "" || v == null ? null : Number(v); + else payload[f.key] = v === "" ? null : v ?? null; + }); + const { error } = await supabase.from("cases").update(payload).eq("id", caseRecord.id); + setSaving(false); + if (error) { + toast.error("Could not save", { description: error.message }); + return; + } + toast.success("Litigation details updated"); + setEditing(false); + onSaved(); + }; + + const renderValue = (key: string, type?: string) => { + const v = caseRecord[key]; + if (v == null || v === "") return —; + if (type === "date") return formatDate(v); + if (type === "number") return formatCurrency(Number(v)); + return String(v); + }; + + const updateKeyDate = (i: number, patch: Partial<{ date: string; label: string }>) => { + setKeyDates((prev) => prev.map((k, idx) => (idx === i ? { ...k, ...patch } : k))); + }; + + return ( +
+
+
+

Litigation overview

+

Court details, opposing parties, and key dates.

+
+ {canManage && !editing && ( + + )} + {editing && ( +
+ + +
+ )} +
+ + + +
} title="Case summary"> + {editing ? ( +