diff --git a/src/components/contacts/contacts-link-tab.tsx b/src/components/contacts/contacts-link-tab.tsx index 9de612c..5c06687 100644 --- a/src/components/contacts/contacts-link-tab.tsx +++ b/src/components/contacts/contacts-link-tab.tsx @@ -172,32 +172,47 @@ function ContactPickerDialog({ }) { const [q, setQ] = useState(""); const [rows, setRows] = useState([]); + const [loading, setLoading] = useState(false); useEffect(() => { if (!open) return; setQ(""); - supabase - .from("contacts") - .select("id, name, company, contact_type, email") - .is("archived_at", null) - .order("name") - .limit(200) - .then(({ data }) => setRows(data ?? [])); }, [open]); - const filtered = useMemo(() => { - const term = q.trim().toLowerCase(); - return rows - .filter((r) => !excludeIds.has(r.id)) - .filter((r) => { - if (!term) return true; - return ( - r.name.toLowerCase().includes(term) || - (r.company ?? "").toLowerCase().includes(term) || - (r.email ?? "").toLowerCase().includes(term) + useEffect(() => { + if (!open) return; + let cancelled = false; + const term = q.trim(); + setLoading(true); + const handle = setTimeout(async () => { + let query = supabase + .from("contacts") + .select("id, name, company, contact_type, email") + .is("archived_at", null) + .order("name") + .limit(50); + if (term) { + const esc = term.replace(/[%,]/g, " "); + query = query.or( + `name.ilike.%${esc}%,company.ilike.%${esc}%,email.ilike.%${esc}%`, ); - }); - }, [rows, q, excludeIds]); + } + const { data } = await query; + if (!cancelled) { + setRows(data ?? []); + setLoading(false); + } + }, 200); + return () => { + cancelled = true; + clearTimeout(handle); + }; + }, [open, q]); + + const filtered = useMemo( + () => rows.filter((r) => !excludeIds.has(r.id)), + [rows, excludeIds], + ); return (