// ================= App shell =================
const { useState: useStateS, useEffect: useEffectS, useRef: useRefS, useMemo: useMemoS, useCallback: useCallbackS } = React;
// Sidebar
const Sidebar = ({ view, setView, alertsCount, onLogout }) => {
const me = window.ME || { name: "—", email: "—", picture: null };
const initials = (me.name || me.email || "??").split(/[\s@]+/).filter(Boolean).slice(0, 2).map(s => s[0] || "").join("").toUpperCase() || "??";
const roleLabel = window.ME_ADMIN ? "Admin" : (window.ME_BRANDS ? `${(window.ME_BRANDS || []).length} brands` : "viewer");
const items = [
{ id: "home", label: "Overview", icon: "home" },
{ id: "brands", label: "Brands", icon: "brand", count: window.BRANDS.length },
{ id: "pacing", label: "Budget pacing", icon: "pacing" },
{ id: "nonmedia", label: "Non-media", icon: "nonmedia", count: window.NON_MEDIA.length },
{ id: "changes", label: "Changes", icon: "clock" },
{ id: "alerts", label: "Alerts", icon: "alert", count: alertsCount },
];
const settings = [
{ id: "settings_meta", label: "Meta settings", icon: "settings" },
{ id: "settings_google", label: "Google settings", icon: "settings" },
{ id: "settings_dv360", label: "DV360 settings", icon: "settings" },
];
return (
);
};
// Topbar
const Topbar = ({ crumbs, theme, setTheme, setAlertsOpen, alertsCount, onRefresh, lastSynced, role, setRole }) => {
return (
Synced {lastSynced}
{["AM", "TL", "Admin"].map(r => (
))}
);
};
// Command palette
const CommandPalette = ({ open, onClose, onNav }) => {
const [q, setQ] = useStateS("");
const [idx, setIdx] = useStateS(0);
const inputRef = useRefS();
useEffectS(() => { if (open) { setQ(""); setIdx(0); setTimeout(() => inputRef.current?.focus(), 10); } }, [open]);
const results = useMemoS(() => {
const ql = q.toLowerCase().trim();
const out = [];
// actions
const acts = [
{ kind: "nav", label: "Go to Overview", to: { name: "home" } },
{ kind: "nav", label: "Go to Brands", to: { name: "brands" } },
{ kind: "nav", label: "Go to Budget pacing", to: { name: "pacing" } },
{ kind: "nav", label: "Go to Non-media", to: { name: "nonmedia" } },
{ kind: "nav", label: "Go to Changes", to: { name: "changes" } },
{ kind: "nav", label: "Open Alerts drawer", action: "alerts" },
];
for (const a of acts) if (!ql || a.label.toLowerCase().includes(ql)) out.push(a);
for (const b of window.BRANDS) {
if (!ql || `${b.code} ${b.name}`.toLowerCase().includes(ql)) {
out.push({ kind: "brand", label: b.name, trail: b.code, to: { name: "brand", brand: b.code } });
}
}
for (const a of window.ALL_ACCOUNTS) {
if (ql && `${a.id} ${a.name}`.toLowerCase().includes(ql)) {
out.push({ kind: "account", label: a.name, trail: a.id, to: { name: "account", brand: a.brand_code, account: a.id } });
}
}
for (const c of window.ALL_CAMPAIGNS) {
if (ql && c.name.toLowerCase().includes(ql)) {
out.push({ kind: "campaign", label: c.name, trail: c.platform.toUpperCase(), to: { name: "campaign", brand: c.brand_code, account: c.account_id, campaign: c.id } });
}
}
return out.slice(0, 40);
}, [q]);
useEffectS(() => {
if (!open) return;
const onKey = (e) => {
if (e.key === "Escape") onClose();
else if (e.key === "ArrowDown") { e.preventDefault(); setIdx(i => Math.min(results.length - 1, i + 1)); }
else if (e.key === "ArrowUp") { e.preventDefault(); setIdx(i => Math.max(0, i - 1)); }
else if (e.key === "Enter") { const r = results[idx]; if (r) pickResult(r); }
};
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [open, results, idx]);
const pickResult = (r) => {
if (r.action === "alerts") window.__openAlerts?.();
else if (r.to) onNav(r.to);
onClose();
};
if (!open) return null;
return (
e.stopPropagation()}>
{ setQ(e.target.value); setIdx(0); }}/>
{results.length === 0 &&
No matches
}
{results.map((r, i) => (
setIdx(i)}
onClick={() => pickResult(r)}>
{r.kind}
{r.label}
{r.trail && {r.trail}}
{idx === i && ↵}
))}
↑↓ navigate
↵ open
esc close
{results.length} results
);
};
// Alerts drawer
const AlertsDrawer = ({ open, onClose, onNav }) => {
if (!open) return null;
const groups = { danger: [], warn: [], info: [] };
for (const a of window.ALERTS) (groups[a.severity] || groups.info).push(a);
return (
<>
Alerts
{window.ALERTS.length} active
{groups.danger.length} critical
{groups.warn.length} warn
{groups.info.length} info
{window.ALERTS.map(a => (
{ onNav({ name: "campaign", brand: a.brand_code, account: null, campaign: a.campaign_id }); onClose(); }}>
{a.title}
{window.fmt.mins(a.detected_at)}
{a.brand_name} ·
{a.account_name}
{a.campaign_name}
{a.kind}
Jump to entity →
))}
>
);
};
// Tweaks panel
const TweaksPanel = ({ enabled, tweaks, setTweaks }) => {
if (!enabled) return null;
const accents = [
{ id: "teal", color: "#0ea5a0" },
{ id: "violet", color: "#8b5cf6" },
{ id: "amber", color: "#f59e0b" },
{ id: "pink", color: "#ec4899" },
{ id: "green", color: "#22c55e" },
];
const set = (k, v) => setTweaks(t => ({ ...t, [k]: v }));
return (
Tweaks
Design knobs
Density
{["compact", "cozy", "comfortable"].map(d => (
))}
Accent
{accents.map(a => (
set("accent", a.id)}/>
))}
Table style
{[["plain", "Plain"], ["striped", "Striped"], ["lined", "Lined"]].map(([id, lbl]) => (
))}
);
};
Object.assign(window, { Sidebar, Topbar, CommandPalette, AlertsDrawer, TweaksPanel });