// Layout primitives — sidebar, topbar, card, badges.
const { useState: uS, useEffect: uE, useMemo: uM, useRef: uR, useCallback: uC } = React;
// =================== Sidebar ===================
function Sidebar({ collapsed, onToggle, route, setRoute, plan = "Free", activeExchange = "binance", mode = "testnet" }) {
const items = [
{ id: "dash", label: "Dashboard", Icon: IconHome },
{ id: "signals", label: "Signals", Icon: IconSignal, badge: 3 },
{ id: "trades", label: "Trade History", Icon: IconList },
{ id: "risk", label: "Risk & Guards", Icon: IconShield },
{ id: "back", label: "Backtester", Icon: IconClock, soon: true },
{ id: "alerts", label: "Alerts", Icon: IconBell },
{ id: "logs", label: "Logs", Icon: IconTerm },
{ id: "build", label: "Build & Roadmap",Icon: IconRocket },
{ id: "docs", label: "Documentation", Icon: IconLayers },
{ id: "set", label: "Settings", Icon: IconGear },
];
const ex = EXCHANGES[activeExchange] || EXCHANGES.binance;
const ExIcon = ex.Icon;
const modeColor = mode === "live" ? "var(--loss)" : mode === "paper" ? "var(--info)" : "var(--warn)";
const w = collapsed ? 72 : 240;
return (
);
}
// =================== Top bar ===================
function TopBar({ route, onNew, activeExchange = "binance" }) {
const ex = EXCHANGES[activeExchange] || EXCHANGES.binance;
const titles = {
dash: { eyebrow: "Overview", title: "Dashboard" },
signals: { eyebrow: "Webhooks", title: "TradingView signals" },
trades: { eyebrow: "Execution", title: "Trade history" },
risk: { eyebrow: "Safety", title: "Risk & circuit breaker" },
back: { eyebrow: "Research", title: "Backtester" },
alerts: { eyebrow: "Notifications", title: "Telegram alerts" },
logs: { eyebrow: "Diagnostics", title: "System logs" },
build: { eyebrow: "Project", title: "Build & roadmap" },
docs: { eyebrow: "Reference", title: "Documentation" },
set: { eyebrow: "Configuration", title: "Settings" },
}[route] || { eyebrow: "Overview", title: "Dashboard" };
return (
{titles.eyebrow}
{titles.title}
⌘K
);
}
function StatusPill({ label, status, Icon }) {
const colors = { ok: "var(--profit)", warn: "var(--warn)", err: "var(--loss)" };
const c = colors[status] || colors.ok;
return (
{Icon ? : }
{label}
{Icon && }
);
}
// =================== Card primitive ===================
function Card({ title, subtitle, right, children, padded = true, style }) {
return (
{(title || right) && (
{title &&
{title}
}
{subtitle &&
{subtitle}
}
{right}
)}
{children}
);
}
function Badge({ children, variant = "default" }) {
const styles = {
default: { bg: "rgba(255,255,255,0.06)", fg: "var(--text-2)" },
accent: { bg: "var(--accent-soft)", fg: "var(--accent)" },
profit: { bg: "rgba(25,229,166,0.14)", fg: "var(--profit)" },
loss: { bg: "rgba(242,63,92,0.14)", fg: "#FCA5A5" },
warn: { bg: "rgba(245,166,35,0.14)", fg: "#FBBF24" },
info: { bg: "rgba(96,165,250,0.14)", fg: "#93C5FD" },
}[variant] || { bg: "rgba(255,255,255,0.06)", fg: "var(--text-2)" };
return (
{children}
);
}
function StatusDot({ status }) {
const map = {
Running: { c: "var(--profit)", pulse: true },
Paused: { c: "var(--warn)", pulse: false },
Error: { c: "var(--loss)", pulse: false },
Idle: { c: "var(--text-3)", pulse: false },
};
const s = map[status] || map.Idle;
return ;
}
// Section header inside a page (for non-card sections)
function PageSection({ title, subtitle, right, children }) {
return (
{title}
{subtitle &&
{subtitle}
}
{right}
{children}
);
}
// Reusable input row
function FieldRow({ label, hint, children, danger }) {
return (
);
}
// Compact input
function TextInput({ value, onChange, placeholder, mono, type = "text", suffix, style }) {
return (
onChange && onChange(e.target.value)}
placeholder={placeholder} type={type} className="focus-ring"
style={{
width: "100%", padding: suffix ? "9px 70px 9px 12px" : "9px 12px", borderRadius: 8,
border: "1px solid var(--line-strong)", background: "rgba(255,255,255,0.03)",
color: "var(--text)", fontSize: 13, fontFamily: mono ? "JetBrains Mono" : "inherit",
outline: "none", ...style,
}} />
{suffix && (
{suffix}
)}
);
}
function Toggle({ value, onChange, label }) {
return (
);
}
function PrimaryBtn({ children, onClick, icon, danger, ghost, style }) {
const bg = danger ? "var(--loss)" : ghost ? "rgba(255,255,255,0.04)" : "var(--accent)";
const fg = danger ? "#fff" : ghost ? "var(--text)" : "#06150F";
const bd = ghost ? "1px solid var(--line-strong)" : "0";
return (
);
}
function IconBtn({ children, title, onClick, danger }) {
return (
);
}
const th = (align) => ({
padding: "10px 12px", textAlign: align || "left",
fontWeight: 500, fontSize: 11,
});
const td = (align) => ({
padding: "14px 12px", textAlign: align || "left", verticalAlign: "middle",
});
Object.assign(window, {
Sidebar, TopBar, StatusPill, Card, Badge, StatusDot,
PageSection, FieldRow, TextInput, Toggle, PrimaryBtn, IconBtn, th, td,
});