// SOLARA SDR — view components. // Adapted from the Stitch handoff. All data flows through props from real // /api/* endpoints; no mock state. The visual structure mirrors the design // pixel-for-pixel (see static/styles.css). const fmt = { num: (n) => (n == null ? "—" : Number(n).toLocaleString()), pct: (n, d = 1) => (n == null ? "—" : `${Number(n).toFixed(d)}%`), // epoch seconds → relative ("12s ago", "3m ago", "Mar 14") rel: (ts) => { if (!ts) return "—"; const now = Math.floor(Date.now() / 1000); const d = now - Number(ts); if (d < 0) return "now"; if (d < 60) return `${d}s ago`; if (d < 3600) return `${Math.floor(d / 60)}m ago`; if (d < 86400) return `${Math.floor(d / 3600)}h ago`; if (d < 86400 * 7) return `${Math.floor(d / 86400)}d ago`; const dt = new Date(ts * 1000); return dt.toLocaleDateString("en-US", { month: "short", day: "numeric" }); }, shortName: (n) => { if (!n) return "??"; const parts = String(n).split(/[\s,@.]+/).filter(Boolean); return parts.slice(0, 2).map((s) => s[0].toUpperCase()).join("") || "??"; }, }; // ================================================================= // OVERVIEW // ================================================================= function OverviewView({ data, onNav, onOpenAside }) { const t = data.totals || {}; const sparkData = data.sparkline?.buckets || []; return ( <>

Pipeline overview

{data.campaigns.filter((c) => c.enabled).length} campaigns enabled. {" "} {fmt.num(t.leads_inflight)} leads in flight · agent engine{" "} {data.engine_status || "scaffold"}.
{/* KPI row */}
Leads all-time
{fmt.num(t.leads_total)}
{fmt.num(t.suppressions || 0)} suppressed
Sent · 12h live
{fmt.num(sparkData.reduce((a, b) => a + b, 0))}
{fmt.num(t.msgs_sent || 0)} all-time outbound
{sparkData.length > 0 && (
)}
Replies {fmt.pct(t.reply_rate || 0)} reply rate
{fmt.num(t.msgs_received || 0)}
{fmt.num(t.leads_replied || 0)} leads replied
Open escalations needs review
{data.open_escalations}
{data.open_escalations > 0 ? "Awaiting human" : "All clear"}
{/* Funnel */}
Pipeline · all time
{fmt.num(t.leads_total)} leads {fmt.num(t.leads_converted || 0)} won
{data.funnel && data.funnel.length > 0 ? ( ) : (
Funnel data unavailable.
)}
{/* Agent / engine status */}
Agent status {data.engine_status === "operational" ? "OPERATING" : "SCAFFOLD"}
Mailboxes
{data.campaigns.length} configured · 0 polling
● IMAP listener pending (Day 2)
LLM engine
not yet wired
scheduler + sender Day 2
Database
SQLite · {fmt.num(t.leads_total)} leads
● healthy
Suppressions
{fmt.num(t.suppressions || 0)} addresses
bounce-webhook → suppression list active
{/* Top conversations */}
Top conversations
{data.threads && data.threads.length > 0 ? (
{data.threads.slice(0, 6).map((th) => { const c = data.campaigns.find((c) => c.id === th.campaign_id) || {}; return ( onNav("inbox", th.id)}> ); })}
Lead Campaign Subject Status When
{th.lead.name}
{th.lead.company || th.lead.email}
{c.display_name || th.campaign_id} {th.subject} {fmt.rel(th.ts)}
) : ( )}
{/* Campaign quick cards */}
Campaigns
{data.campaigns.slice(0, 6).map((c) => { const pc = data.per_campaign?.[c.id] || {}; const total = pc.leads_total || 0; const active = pc.leads_inflight || 0; const pct = total > 0 ? active / total : 0; const replyRate = pc.leads_total > 0 ? ((pc.leads_replied || 0) / pc.leads_total) * 100 : 0; return (
onNav("campaigns", c.id)} style={{ padding: "10px 12px", border: "1px solid var(--border-soft)", borderRadius: 10, cursor: "pointer", transition: "all 150ms var(--ease)", }} >
{c.display_name} · {c.persona_name}
{c.enabled ? null : ( paused )}
{fmt.num(active)} / {fmt.num(total)} active {fmt.pct(replyRate)} reply
); })}
{/* Escalation queue */}
Escalation queue · awaiting human {data.escalations_queue?.length || 0} open
{data.escalations_queue && data.escalations_queue.length > 0 ? (
{data.escalations_queue.map((e) => { const c = data.campaigns.find((c) => c.id === e.campaign_id) || {}; return ( ); })}
Lead Reason Campaign Age Actions
{e.lead}
{e.company || ""}
{e.reason} {c.display_name || e.campaign_id} {fmt.rel(e.created_at)}
) : ( )}
); } const kpiLabel = { fontSize: 11, textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600, marginBottom: 6, }; const kpiData = { fontFamily: "var(--font-mono)", fontSize: 12 }; const kpiSub = { fontFamily: "var(--font-mono)", fontSize: 10.5, color: "var(--fg-muted)", marginTop: 2, }; // ================================================================= // INBOX // ================================================================= function InboxView({ data, threadId, setThreadId, onOpenRail }) { const [search, setSearch] = useState(""); const [filter, setFilter] = useState("all"); // all | unread const [pane, setPane] = useState("list"); // mobile single-pane router const [thread, setThread] = useState(null); const [loading, setLoading] = useState(false); const threads = useMemo(() => { let list = data.threads || []; if (filter === "unread") list = list.filter((t) => t.unread); if (search) { const s = search.toLowerCase(); list = list.filter((t) => ((t.lead.name || "") + (t.lead.company || "") + (t.subject || "") + (t.snippet || "") ) .toLowerCase() .includes(s) ); } return list; }, [data.threads, filter, search]); // Auto-select first thread once data is available. useEffect(() => { if (!threadId && threads.length > 0) { setThreadId(threads[0].id); } }, [threadId, threads, setThreadId]); // Load thread detail. useEffect(() => { if (!threadId) { setThread(null); return; } const leadId = threadId.replace(/^l/, ""); setLoading(true); fetch(`/api/threads/${leadId}`) .then((r) => (r.ok ? r.json() : null)) .then((data) => { setThread(data); setLoading(false); }) .catch(() => setLoading(false)); }, [threadId]); const t = thread?.lead; const c = data.campaigns.find((cc) => cc.id === t?.campaign_id) || {}; return ( <>

Inbox

{threads.length} conversation{threads.length === 1 ? "" : "s"} ·{" "} {data.open_escalations} need attention
{/* Thread list */}
setSearch(e.target.value)} />
{threads.length === 0 ? ( ) : ( threads.map((th) => { const tc = data.campaigns.find((cc) => cc.id === th.campaign_id) || {}; return (
{ setThreadId(th.id); setPane("view"); }} >
{th.lead.name} {fmt.rel(th.ts)}
{th.subject}
{th.snippet || ""}
{tc.display_name || th.campaign_id}
); }) )}
{/* Thread view */}
{thread && t ? ( <>
{fmt.shortName(t.name)}

{t.name}

{t.title && {t.title}} {t.title && t.company && ( · )} {t.company && ( {t.company} )} {t.email && ( <> · {t.email} )} {t.linkedin && ( <> · linkedin )}
{thread.messages && thread.messages.length > 0 ? ( thread.messages.map((m) => (
{m.direction === "outbound" ? c.persona_name : t.name} {m.direction === "outbound" ? m.from_addr : m.from_addr}
{fmt.rel(m.sent_at || m.received_at || m.created_at)}
{m.body_text || "(no body)"}
)) ) : ( )}
AI draft
pending Day-2 LLM engine
Drafts will appear here once the LLM engine ships. The persona ({c.persona_name}) and ICP brief from{" "} {c.display_name} are already wired in.
) : loading ? (
Loading thread…
) : ( )}
); } // ================================================================= // PIPELINE // ================================================================= function PipelineView({ data, onNav }) { const [pipeline, setPipeline] = useState(null); const [campFilter, setCampFilter] = useState("all"); useEffect(() => { const url = campFilter === "all" ? "/api/pipeline" : `/api/pipeline?campaign_id=${encodeURIComponent(campFilter)}`; fetch(url) .then((r) => r.json()) .then(setPipeline); }, [campFilter]); if (!pipeline) { return (
Loading pipeline…
); } const total = Object.values(pipeline.by_stage).reduce( (a, arr) => a + arr.length, 0 ); return ( <>

Pipeline

{total} lead{total === 1 ? "" : "s"} in motion · stage updates from agent activity
{data.campaigns.map((c) => ( ))}
{pipeline.stages.map((stage) => { const items = pipeline.by_stage[stage.id] || []; return (
{stage.name}
{items.length}
{items.length === 0 ? (
Empty
) : ( items.map((l) => { const c = data.campaigns.find((c) => c.id === l.campaign_id) || {}; return (
onNav("inbox", l.id)} >
{l.name}
{l.title || ""} {l.title && l.company ? " · " : ""} {l.company || ""}
{c.display_name || l.campaign_id} {l.email}
); }) )}
); })}
); } // ================================================================= // CAMPAIGNS // ================================================================= function CampaignsView({ data, onUpload }) { return ( <>

Campaigns

{data.campaigns.filter((c) => c.enabled).length} enabled · drop a CSV onto any card to add leads
{data.campaigns.map((c) => { const pc = data.per_campaign?.[c.id] || {}; const total = pc.leads_total || 0; const inflight = pc.leads_inflight || 0; const replied = pc.leads_replied || 0; const converted = pc.leads_converted || 0; const replyRate = total > 0 ? (replied / total) * 100 : 0; const pct = total > 0 ? inflight / total : 0; return ( ); })}
); } function CampaignCard({ campaign: c, total, inflight, replied, converted, replyRate, pct, onUpload, }) { const [drag, setDrag] = useState(false); const [result, setResult] = useState(null); // {kind, msg} const inputRef = useRef(null); const upload = (file) => { if (!file) return; setResult({ kind: "work", msg: `Uploading ${file.name}…` }); const fd = new FormData(); fd.append("file", file); fetch(`/api/campaigns/${encodeURIComponent(c.id)}/leads/csv`, { method: "POST", body: fd, }) .then((r) => { if (!r.ok) throw new Error("HTTP " + r.status); return r.json(); }) .then((d) => { setResult({ kind: "ok", msg: `Imported ${d.inserted} · ${d.skipped} skipped`, }); if (onUpload) setTimeout(onUpload, 600); }) .catch((e) => setResult({ kind: "err", msg: "Failed: " + e.message })); }; return (
{ e.preventDefault(); setDrag(true); }} onDragOver={(e) => { e.preventDefault(); setDrag(true); }} onDragLeave={() => setDrag(false)} onDrop={(e) => { e.preventDefault(); setDrag(false); const f = e.dataTransfer?.files?.[0]; if (f) upload(f); }} >

{c.display_name}

{c.persona_name} · {c.sender_email}
{c.enabled ? "Enabled" : "Paused"}
{c.vertical || ""}
{fmt.num(total)}
Leads
{fmt.num(inflight)}
Active
{fmt.pct(replyRate)}
Reply
{fmt.num(converted)}
Won
{Math.round(pct * 100)}% active {fmt.num(total - inflight)} remaining
inputRef.current?.click()} style={{ marginTop: 14, padding: 10, border: `1.5px dashed ${ drag ? "var(--primary)" : "var(--border)" }`, borderRadius: 8, textAlign: "center", background: drag ? "hsl(var(--hsl-primary) / 0.06)" : "var(--bg)", color: drag ? "var(--primary)" : "var(--fg-muted)", cursor: "pointer", fontSize: 12, transition: "all 120ms var(--ease)", }} > { const f = e.target.files?.[0]; if (f) upload(f); e.target.value = ""; }} /> Drop CSV or click · needs an{" "} email column
{result && (
{result.msg}
)}
); } // ================================================================= // EMPTY STATE // ================================================================= function EmptyState({ icon, title, text }) { return (
{title}
{text}
); } Object.assign(window, { OverviewView, InboxView, PipelineView, CampaignsView, EmptyState, fmt, });