/* global React, Icon, Avatar, StageChip, SourceChip, TemperatureDot, ChannelBadge, ChannelIcon, fmtAED */ const { useState, useEffect, useMemo } = React; function _initials(name) { if (!name) return '?'; const parts = String(name).replace(/[._@-]/g, ' ').split(/\s+/).filter(Boolean); if (!parts.length) return name.slice(0, 2).toUpperCase(); if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase(); return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); } function _color(s) { const palette = ['#22c55e', '#3b82f6', '#a855f7', '#f97316', '#eab308', '#ec4899', '#06b6d4', '#84cc16']; let h = 0; for (let i = 0; i < (s || '').length; i++) h = ((h << 5) - h) + s.charCodeAt(i); return palette[Math.abs(h) % palette.length]; } function _relTime(iso) { if (!iso) return '—'; const d = new Date(iso); const diff = (Date.now() - d.getTime()) / 1000; if (diff < 60) return 'now'; if (diff < 3600) return `${Math.round(diff / 60)}m`; if (diff < 86400) return `${Math.round(diff / 3600)}h`; if (diff < 86400 * 7) return `${Math.round(diff / 86400)}d`; return d.toLocaleDateString(); } /* ============================================================ CONTACTS LIST (data table) ============================================================ */ function Contacts({ goto }) { const [search, setSearch] = useState(''); const [contacts, setContacts] = useState(null); const [error, setError] = useState(null); useEffect(() => { fetch('/api/contacts?limit=500', { credentials: 'include' }) .then((r) => r.ok ? r.json() : Promise.reject(r.statusText)) .then((d) => setContacts(d.items || [])) .catch((e) => setError(String(e))); }, []); const filtered = useMemo(() => { if (!contacts) return []; const q = search.toLowerCase(); if (!q) return contacts; return contacts.filter((c) => (c.name || '').toLowerCase().includes(q) || (c.company || '').toLowerCase().includes(q) || (c.email || '').toLowerCase().includes(q) || (c.linkedin_url || '').toLowerCase().includes(q) ); }, [contacts, search]); const total = contacts ? contacts.length : 0; return (
| Name | Company | Title | Source | Temp | Stage | Created | Channels |
|---|---|---|---|---|---|---|---|
|
|
{c.company || '—'} | {c.title || '—'} | {c.source ? |
{c.temperature ? |
{c.pipeline_stage || 'New'} | {_relTime(c.created_at)} |
{channelKinds.length === 0 && —}
{channelKinds.map((k) =>
|