/* global React */ const { useState, useEffect, useRef, useMemo, useCallback } = React; /* ============================================================ ICON — Lucide via inline SVG paths ============================================================ */ const ICONS = { // Nav dashboard: 'M3 3h7v9H3zM14 3h7v5h-7zM14 12h7v9h-7zM3 16h7v5H3z', inbox: 'M22 12h-6l-2 3h-4l-2-3H2 M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z', pipeline: 'M3 6h18 M7 12h10 M11 18h6', contacts: 'M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M8.5 7a4 4 0 1 1-8 0 4 4 0 0 1 8 0 M22 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75', accounts: 'M3 21h18 M5 21V7l8-4v18 M19 21V11l-6-4', calendar: 'M8 2v4 M16 2v4 M3 10h18 M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z', funnels: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z', campaigns: 'M3 11l18-8-8 18-2-8-8-2z', documents: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8', contracts: 'M9 12h6 M9 16h6 M9 8h2 M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6', payments: 'M2 7h20 M2 12h20 M2 17h20 M5 7l-2 5 2 5 M19 7l2 5-2 5', settings: 'M12 1v6m0 6v6m11-9h-6m-6 0H1m17.5-6.5L15 7m-6 6l-3.5 3.5M19.5 19.5L15 15m-6-6L5.5 5.5 M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10z', // UI search: 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z M21 21l-4.35-4.35', bell: 'M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9 M10.3 21a1.94 1.94 0 0 0 3.4 0', plus: 'M12 5v14 M5 12h14', chevDown: 'M6 9l6 6 6-6', chevRight: 'M9 18l6-6-6-6', chevLeft: 'M15 18l-6-6 6-6', chevUp: 'M18 15l-6-6-6 6', x: 'M18 6L6 18 M6 6l12 12', filter: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z', more: 'M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z M19 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z M5 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z', arrowRight: 'M5 12h14 M12 5l7 7-7 7', arrowUp: 'M12 19V5 M5 12l7-7 7 7', arrowDown: 'M12 5v14 M19 12l-7 7-7-7', external: 'M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6 M15 3h6v6 M10 14L21 3', copy: 'M9 9h13v13H9z M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1', check: 'M20 6L9 17l-5-5', link: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71', edit: 'M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7 M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z', trash: 'M3 6h18 M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2', send: 'M22 2L11 13 M22 2l-7 20-4-9-9-4 20-7z', paperclip: 'M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48', emoji: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M8 14s1.5 2 4 2 4-2 4-2 M9 9h.01 M15 9h.01', phone: 'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z', mail: 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z M22 6l-10 7L2 6', building: 'M3 21h18 M5 21V7l5-4v18 M19 21V11l-9-7 M9 9v.01 M9 12v.01 M9 15v.01 M9 18v.01', user: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z', users: 'M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M8.5 7a4 4 0 1 1-8 0 4 4 0 0 1 8 0 M22 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75', clock: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M12 6v6l4 2', tag: 'M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z M7 7h.01', zap: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z', play: 'M5 3l14 9-14 9V3z', pause: 'M6 4h4v16H6z M14 4h4v16h-4z', drag: 'M9 6h.01 M9 12h.01 M9 18h.01 M15 6h.01 M15 12h.01 M15 18h.01', layers: 'M12 2l10 5-10 5L2 7l10-5z M2 17l10 5 10-5 M2 12l10 5 10-5', layout: 'M3 3h18v18H3z M3 9h18 M9 21V9', globe: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M2 12h20 M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z', upload: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M17 8l-5-5-5 5 M12 3v12', download: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M7 10l5 5 5-5 M12 15V3', refresh: 'M23 4v6h-6 M1 20v-6h6 M3.51 9a9 9 0 0 1 14.85-3.36L23 10 M1 14l4.64 4.36A9 9 0 0 0 20.49 15', eye: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z', card: 'M2 5h20v14H2z M2 10h20', shield: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z', pencil: 'M12 20h9 M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z', fileText: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8', presentation: 'M2 3h20 M2 3v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V3 M8 21l4-4 4 4 M12 17v4', linkedinIcon: 'M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z M2 9h4v12H2z M4 6a2 2 0 1 0 0-4 2 2 0 0 0 0 4z', whatsappIcon: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z', instagramIcon: 'M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z M17.5 6.5h.01', emailIcon: 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z M22 6l-10 7L2 6', facebookIcon: 'M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z', grid: 'M3 3h7v7H3z M14 3h7v7h-7z M14 14h7v7h-7z M3 14h7v7H3z', list: 'M8 6h13 M8 12h13 M8 18h13 M3 6h.01 M3 12h.01 M3 18h.01', trending: 'M23 6l-9.5 9.5-5-5L1 18 M17 6h6v6', flame: 'M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z', globe2: 'M2 12h20 M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z M12 2a10 10 0 0 1 0 20 10 10 0 0 1 0-20z', spark: 'M12 3v18 M3 12h18 M5.6 5.6l12.8 12.8 M18.4 5.6L5.6 18.4', branch: 'M6 3v12 M6 15a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6z M18 9a9 9 0 0 1-9 9', dollar: 'M12 1v22 M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6', }; function Icon({ name, size = 16, stroke = 2, className = '', style = {} }) { const path = ICONS[name]; if (!path) return null; const paths = path.split(' M').map((p, i) => i === 0 ? p : 'M' + p); return ( ); } /* ============================================================ STAGE CHIP ============================================================ */ function StageChip({ stage, pipeline = 'outbound', size = 'sm' }) { const stages = pipeline === 'outbound' ? window.STAGES_OUTBOUND : window.STAGES_INBOUND; const s = stages.find(x => x.id === stage) || { label: stage, color: '#6b7280' }; return ( {s.label} ); } /* ============================================================ SOURCE CHIP ============================================================ */ function SourceChip({ source }) { const s = window.SOURCES[source] || { label: source, icon: 'tag', color: '#6b7280' }; return ( {s.label} ); } /* ============================================================ TEMPERATURE DOT ============================================================ */ function TemperatureDot({ temp, label = false }) { const t = window.TEMPS[temp] || window.TEMPS.cold; return ( {label && {t.label}} ); } /* ============================================================ CHANNEL BADGE / ICON ============================================================ */ function ChannelIcon({ kind, size = 12 }) { const map = { linkedin: 'linkedinIcon', whatsapp: 'whatsappIcon', instagram: 'instagramIcon', email: 'emailIcon', }; const colors = { linkedin: '#3b82f6', whatsapp: '#22c55e', instagram: '#a855f7', email: '#a3a3a3', }; return ; } function ChannelBadge({ kind, lastContact }) { return ( {lastContact} ); } /* ============================================================ AVATAR ============================================================ */ function Avatar({ name, initials, color = '#3b82f6', size = 'md' }) { const inits = initials || (name ? name.split(' ').map(s => s[0]).slice(0,2).join('') : '?'); return (
{inits}
); } /* ============================================================ KPI CARD ============================================================ */ function KpiCard({ label, value, unit, delta, deltaDirection, sub }) { return (
{label}
{value} {unit && {unit}}
{(delta || sub) && (
{delta && ( {deltaDirection === 'down' ? '−' : '+'}{delta} )} {sub && {sub}}
)}
); } /* ============================================================ EMPTY STATE ============================================================ */ function EmptyState({ icon = 'inbox', title, body, ctaLabel, onCta }) { return (
{title}
{body &&
{body}
} {ctaLabel && }
); } /* ============================================================ CHANNEL DOT (small, no label) ============================================================ */ function ChannelDot({ kind }) { const colors = { linkedin: '#3b82f6', whatsapp: '#22c55e', instagram: '#a855f7', email: '#a3a3a3', }; return ( ); } /* ============================================================ STATUS PILL (deal/contract/payment status) ============================================================ */ function StatusPill({ status }) { const map = { 'draft': { label: 'Draft', dot: '#6b7280' }, 'sent': { label: 'Sent', dot: '#3b82f6' }, 'viewed': { label: 'Viewed', dot: '#a855f7' }, 'accepted': { label: 'Accepted', dot: '#22c55e' }, 'signed': { label: 'Signed', dot: '#22c55e' }, 'paid': { label: 'Paid', dot: '#22c55e' }, 'partial': { label: 'Partial', dot: '#f97316' }, 'live': { label: 'Live', dot: '#22c55e' }, 'paused': { label: 'Paused', dot: '#eab308' }, 'active': { label: 'Active', dot: '#22c55e' }, 'error': { label: 'Error', dot: '#ef4444' }, 'stopped': { label: 'Stopped', dot: '#ef4444' }, 'none': { label: 'No contract', dot: '#646464' }, }; const s = map[status] || { label: status, dot: '#6b7280' }; return ( {s.label} ); } /* ============================================================ AED MONEY FORMATTER ============================================================ */ function fmtAED(n) { if (n == null) return '—'; return n.toLocaleString('en-AE'); } /* ============================================================ EXPORT ============================================================ */ Object.assign(window, { Icon, StageChip, SourceChip, TemperatureDot, ChannelIcon, ChannelBadge, Avatar, KpiCard, EmptyState, ChannelDot, StatusPill, fmtAED, });