From 1f3fd28159a469a41aab1ce5bd1992eb19b99d0e 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 02:23:12 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- .../contacts/contact-form-dialog.tsx | 200 +++++++++++++++ src/components/contacts/contacts-link-tab.tsx | 240 ++++++++++++++++++ src/routeTree.gen.ts | 51 ++++ src/routes/contacts.$contactId.tsx | 188 ++++++++++++++ src/routes/contacts.index.tsx | 201 +++++++++++++++ 5 files changed, 880 insertions(+) create mode 100644 src/components/contacts/contact-form-dialog.tsx create mode 100644 src/components/contacts/contacts-link-tab.tsx create mode 100644 src/routes/contacts.$contactId.tsx create mode 100644 src/routes/contacts.index.tsx diff --git a/src/components/contacts/contact-form-dialog.tsx b/src/components/contacts/contact-form-dialog.tsx new file mode 100644 index 0000000..3eed4be --- /dev/null +++ b/src/components/contacts/contact-form-dialog.tsx @@ -0,0 +1,200 @@ +import { useEffect, useState } from "react"; +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { supabase } from "@/integrations/supabase/client"; +import { useAuth } from "@/lib/auth"; +import { toast } from "sonner"; +import { Loader2 } from "lucide-react"; + +export const CONTACT_TYPES = [ + { value: "opposing_counsel", label: "Opposing counsel" }, + { value: "co_counsel", label: "Co-counsel" }, + { value: "expert", label: "Expert" }, + { value: "witness", label: "Witness" }, + { value: "vendor", label: "Vendor" }, + { value: "court", label: "Court / clerk" }, + { value: "process_server", label: "Process server" }, + { value: "client_contact", label: "Client contact" }, + { value: "other", label: "Other" }, +] as const; + +export interface ContactRecord { + id?: string; + name: string; + company?: string | null; + title?: string | null; + email?: string | null; + phone?: string | null; + address_line1?: string | null; + address_line2?: string | null; + city?: string | null; + state?: string | null; + postal_code?: string | null; + notes?: string | null; + contact_type?: string | null; +} + +export function ContactFormDialog({ + open, + onOpenChange, + contact, + onSaved, +}: { + open: boolean; + onOpenChange: (v: boolean) => void; + contact?: ContactRecord | null; + onSaved?: (saved: { id: string; name: string }) => void; +}) { + const { user } = useAuth(); + const [saving, setSaving] = useState(false); + const [form, setForm] = useState({ + name: "", + contact_type: "other", + }); + + useEffect(() => { + if (open) { + setForm( + contact + ? { ...contact } + : { name: "", contact_type: "other" }, + ); + } + }, [open, contact]); + + const set = (k: K, v: ContactRecord[K]) => + setForm((f) => ({ ...f, [k]: v })); + + const onSave = async () => { + if (!form.name.trim()) { + toast.error("Name is required"); + return; + } + setSaving(true); + const payload = { + name: form.name.trim(), + company: form.company || null, + title: form.title || null, + email: form.email || null, + phone: form.phone || null, + address_line1: form.address_line1 || null, + address_line2: form.address_line2 || null, + city: form.city || null, + state: form.state || null, + postal_code: form.postal_code || null, + notes: form.notes || null, + contact_type: form.contact_type || "other", + }; + let saved: { id: string; name: string } | null = null; + if (contact?.id) { + const { data, error } = await supabase + .from("contacts") + .update(payload) + .eq("id", contact.id) + .select("id, name") + .single(); + if (error) { + toast.error(error.message); + setSaving(false); + return; + } + saved = data; + } else { + const { data, error } = await supabase + .from("contacts") + .insert({ ...payload, created_by: user?.id }) + .select("id, name") + .single(); + if (error) { + toast.error(error.message); + setSaving(false); + return; + } + saved = data; + } + setSaving(false); + toast.success(contact?.id ? "Contact updated" : "Contact created"); + onOpenChange(false); + if (saved) onSaved?.(saved); + }; + + return ( + + + + {contact?.id ? "Edit contact" : "New contact"} + + +
+
+ + set("name", e.target.value)} /> +
+
+ + +
+
+ + set("title", e.target.value)} /> +
+
+ + set("company", e.target.value)} /> +
+
+ + set("email", e.target.value)} /> +
+
+ + set("phone", e.target.value)} /> +
+
+ + set("address_line1", e.target.value)} /> +
+
+ + set("address_line2", e.target.value)} /> +
+
+ + set("city", e.target.value)} /> +
+
+ + set("state", e.target.value)} /> +
+
+ + set("postal_code", e.target.value)} /> +
+
+ +