// SOLARA SDR — operator console shell.
// Pulls all data from real /api/* endpoints. Polls /api/status every 8s for
// live KPI updates without blowing up the connection count.
function useApi(url, initial = null, deps = []) {
const [data, setData] = useState(initial);
const [error, setError] = useState(null);
const reload = useCallback(() => {
fetch(url)
.then((r) => (r.ok ? r.json() : Promise.reject(new Error("HTTP " + r.status))))
.then(setData)
.catch(setError);
}, [url]);
useEffect(() => { reload(); }, deps); // eslint-disable-line
return { data, error, reload };
}
window.useCallback = React.useCallback;
function App() {
const [view, setView] = useState("overview");
const [threadId, setThreadId] = useState(null);
const [focusedCamp, setFocusedCamp] = useState(null);
const [cmdkOpen, setCmdkOpen] = useState(false);
const [railOpen, setRailOpen] = useState(false);
const [asideOpen, setAsideOpen] = useState(false);
const [now, setNow] = useState(new Date());
// Live data sources.
const [status, setStatus] = useState(null);
const [threads, setThreads] = useState([]);
const [funnel, setFunnel] = useState([]);
const [sparkline, setSparkline] = useState({ hours: 12, buckets: [] });
const [escalationsQueue, setEscalationsQueue] = useState([]);
const refreshAll = useCallback(() => {
fetch("/api/status").then((r) => r.json()).then(setStatus);
fetch("/api/threads?limit=60").then((r) => r.json()).then((d) => setThreads(d.threads || []));
fetch("/api/funnel").then((r) => r.json()).then((d) => setFunnel(d.rows || []));
fetch("/api/sparkline/sends?hours=12").then((r) => r.json()).then(setSparkline);
fetch("/api/escalations/queue").then((r) => r.json()).then((d) => setEscalationsQueue(d.escalations || []));
}, []);
// Initial load + poll.
useEffect(() => {
refreshAll();
const t = setInterval(refreshAll, 8000);
return () => clearInterval(t);
}, [refreshAll]);
// Cmd+K + Escape.
useEffect(() => {
const onKey = (e) => {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
e.preventDefault();
setCmdkOpen((o) => !o);
}
if (e.key === "Escape") {
setCmdkOpen(false);
setRailOpen(false);
setAsideOpen(false);
}
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
// Body class lock when any drawer is open (mobile).
useEffect(() => {
document.body.classList.toggle("drawer-open", railOpen || asideOpen);
}, [railOpen, asideOpen]);
// Clock for the footer.
useEffect(() => {
const t = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(t);
}, []);
const nav = (v, id) => {
setView(v);
if (v === "inbox" && id) setThreadId(id);
if (v === "campaigns" && id) setFocusedCamp(id);
setRailOpen(false);
};
// Loading shell — show as soon as status is in.
if (!status) {
return (
);
}
// Compose the data prop each view sees (single source of truth for child).
const data = {
...status,
threads,
funnel,
sparkline,
escalations_queue: escalationsQueue,
engine_status: "scaffold", // Day-2 sender lights this up to "operational"
};
const inflightCount =
(status.totals?.leads_inflight || 0);
const timeStr = now.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
});
return (
{/* ============ HEADER ============ */}
{/* Drawer backdrop on mobile */}
{(railOpen || asideOpen) && (
{
setRailOpen(false);
setAsideOpen(false);
}}
/>
)}
{/* ============ RAIL ============ */}
{/* ============ MAIN ============ */}
{view === "overview" && (
setAsideOpen(true)}
/>
)}
{view === "inbox" && (
)}
{view === "pipeline" && }
{view === "campaigns" && (
)}
{/* ============ ASIDE ============ */}
{/* ============ FOOTER ============ */}
{/* ============ COMMAND PALETTE ============ */}
{cmdkOpen && (
setCmdkOpen(false)}>
e.stopPropagation()}>
esc
Navigate
{[
["overview", "home", "Go to Overview", "G O"],
["inbox", "inbox", "Open Inbox", "G I"],
["pipeline", "pipeline", "View Pipeline", "G P"],
["campaigns", "campaign", "Manage Campaigns", "G C"],
].map(([v, ic, label, kb]) => (
{
nav(v);
setCmdkOpen(false);
}}
>
{label}
{kb}
))}
Campaigns
{status.campaigns.map((c) => (
{
nav("campaigns", c.id);
setCmdkOpen(false);
}}
>
{c.display_name}
{c.persona_name}
))}
{threads.length > 0 && (
<>
Recent threads
{threads.slice(0, 5).map((t) => (
{
nav("inbox", t.id);
setCmdkOpen(false);
}}
>
{t.lead.name}
{t.lead.company || t.lead.email}
))}
>
)}
)}
);
}
ReactDOM.createRoot(document.getElementById("root")).render(
);