From dfc8e59407e29b3c25d82fd8e6fbecba8cbee231 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 17:15:53 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/components/clients/client-form-dialog.tsx | 23 +++- .../clients/contact-picker-popover.tsx | 111 ++++++++++++++++++ src/components/contacts/contacts-link-tab.tsx | 5 +- 3 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/components/clients/contact-picker-popover.tsx diff --git a/src/components/clients/client-form-dialog.tsx b/src/components/clients/client-form-dialog.tsx index ae78d7c..78282fa 100644 --- a/src/components/clients/client-form-dialog.tsx +++ b/src/components/clients/client-form-dialog.tsx @@ -12,6 +12,7 @@ import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/lib/auth"; import { Loader2, Plus, Trash2 } from "lucide-react"; import { toast } from "sonner"; +import { ContactPickerPopover } from "./contact-picker-popover"; const boardMember = z.object({ name: z.string().trim().min(1, "Required").max(120), @@ -299,9 +300,25 @@ export function ClientFormDialog({
- +
+ + form.setValue("board_members", [ + ...board, + { + name: c.name, + role: c.title ?? "", + email: c.email ?? "", + phone: c.phone ?? "", + }, + ]) + } + /> + +
{board.length === 0 && ( diff --git a/src/components/clients/contact-picker-popover.tsx b/src/components/clients/contact-picker-popover.tsx new file mode 100644 index 0000000..9717edc --- /dev/null +++ b/src/components/clients/contact-picker-popover.tsx @@ -0,0 +1,111 @@ +import { useEffect, useMemo, useState } from "react"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { supabase } from "@/integrations/supabase/client"; +import { Search, UserSearch } from "lucide-react"; + +interface PickResult { + id: string; + name: string; + email: string | null; + phone: string | null; + title: string | null; + company: string | null; + contact_type: string; +} + +/** + * Compact "pick from contacts" popover used inside list-style editors + * (e.g. board members rows in the client form). + */ +export function ContactPickerPopover({ + onPick, + label = "From contacts", +}: { + onPick: (c: PickResult) => void; + label?: string; +}) { + const [open, setOpen] = useState(false); + const [q, setQ] = useState(""); + const [rows, setRows] = useState([]); + + useEffect(() => { + if (!open) return; + setQ(""); + supabase + .from("contacts") + .select("id, name, email, phone, title, company, contact_type") + .is("archived_at", null) + .order("name") + .limit(200) + .then(({ data }) => setRows((data ?? []) as PickResult[])); + }, [open]); + + const filtered = useMemo(() => { + const term = q.trim().toLowerCase(); + if (!term) return rows; + return rows.filter( + (r) => + r.name.toLowerCase().includes(term) || + (r.company ?? "").toLowerCase().includes(term) || + (r.email ?? "").toLowerCase().includes(term), + ); + }, [rows, q]); + + return ( + + + + + +
+
+ + setQ(e.target.value)} + placeholder="Search contacts…" + className="h-8 pl-8 text-sm" + /> +
+
+
+ {filtered.length === 0 ? ( +

No matching contacts.

+ ) : ( +
    + {filtered.map((c) => ( +
  • + +
  • + ))} +
+ )} +
+
+
+ ); +} diff --git a/src/components/contacts/contacts-link-tab.tsx b/src/components/contacts/contacts-link-tab.tsx index 7e9b34b..9de612c 100644 --- a/src/components/contacts/contacts-link-tab.tsx +++ b/src/components/contacts/contacts-link-tab.tsx @@ -89,9 +89,12 @@ export function ContactsLinkTab({
-
Contacts
+
+ {parentTable === "client_contacts" ? "Additional contacts" : "Contacts"} +
{linked.length} linked contact{linked.length === 1 ? "" : "s"} + {parentTable === "client_contacts" && " — accountants, vendors, board liaisons, etc."}