109 lines
4.5 KiB
JavaScript
109 lines
4.5 KiB
JavaScript
// Chrome — top header strip + left navigation rail
|
|
|
|
function EntityScopeSwitcher({ value, onChange }) {
|
|
const entities = window.AKEFIN_DATA.entities;
|
|
const [open, setOpen] = React.useState(false);
|
|
const current = entities.find(e => e.id === value);
|
|
return (
|
|
<div className={`scope-switcher ${open ? "open" : ""}`}>
|
|
<button className="scope-trigger" onClick={() => setOpen(!open)}>
|
|
<span className="scope-stripe" style={{ background: current.color || "#1A1814" }}></span>
|
|
<span className="scope-label">SCOPE</span>
|
|
<span className="scope-name">{current.label}</span>
|
|
<Icon name="chevDown" size={14} />
|
|
</button>
|
|
{open && (
|
|
<div className="scope-menu">
|
|
{entities.map(e => (
|
|
<button key={e.id} className={`scope-item ${e.id === value ? "active" : ""}`}
|
|
onClick={() => { onChange(e.id); setOpen(false); }}>
|
|
<span className="scope-stripe" style={{ background: e.color || "#B5AE9F" }}></span>
|
|
<span>{e.label}</span>
|
|
{e.id === value && <Icon name="check" size={12} />}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Header({ entity, onEntityChange, query, setQuery, statusLine, onOpenPalette, onImport }) {
|
|
return (
|
|
<header className="ak-header">
|
|
<div className="ak-logo">
|
|
<svg viewBox="0 0 48 48" width="22" height="22" aria-hidden="true">
|
|
<g fill="currentColor">
|
|
<rect x="6" y="14" width="6" height="28"/>
|
|
<rect x="12" y="8" width="6" height="6"/>
|
|
<rect x="18" y="8" width="6" height="6"/>
|
|
<rect x="24" y="14" width="6" height="28"/>
|
|
<rect x="12" y="26" width="12" height="6"/>
|
|
</g>
|
|
<rect x="36" y="36" width="6" height="6" fill="#2F7D55"/>
|
|
</svg>
|
|
<span className="ak-wordmark">akefin</span>
|
|
</div>
|
|
<EntityScopeSwitcher value={entity} onChange={onEntityChange} />
|
|
<button className="ak-search ak-search-btn" onClick={onOpenPalette}>
|
|
<Icon name="search" size={14} color="var(--fg-muted)" />
|
|
<span className="ak-search-placeholder">Search · run command · jump to anything…</span>
|
|
<span className="ak-search-kbd">⌘K</span>
|
|
</button>
|
|
<div className="ak-header-actions">
|
|
<button className="ak-header-btn" onClick={onImport}>
|
|
<Icon name="import" size={13} />
|
|
<span>IMPORT CSV</span>
|
|
</button>
|
|
</div>
|
|
<div className="ak-status">
|
|
<span className="dot" style={{ background: "var(--conf-rules)" }}></span>
|
|
<span className="ak-status-text">{statusLine}</span>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
function Sidebar({ screen, setScreen, counts }) {
|
|
const items = [
|
|
{ id: "dashboard", label: "Overview", icon: "spark", count: null },
|
|
{ id: "review", label: "Review queue", icon: "activity", count: counts.review },
|
|
{ id: "rules", label: "Rules", icon: "rule", count: counts.rules },
|
|
{ id: "ledger", label: "Ledger", icon: "ledger", count: null },
|
|
{ id: "import", label: "Import", icon: "import", count: counts.import },
|
|
];
|
|
return (
|
|
<nav className="ak-sidebar">
|
|
<div className="ak-sb-section">PIPELINE</div>
|
|
{items.map(it => (
|
|
<button key={it.id}
|
|
className={`ak-sb-item ${screen === it.id ? "active" : ""}`}
|
|
onClick={() => setScreen(it.id)}>
|
|
<Icon name={it.icon} size={16} />
|
|
<span className="ak-sb-label">{it.label}</span>
|
|
{it.count !== null && <span className="ak-sb-count">{it.count}</span>}
|
|
</button>
|
|
))}
|
|
<div className="ak-sb-section" style={{ marginTop: 16 }}>SETTINGS</div>
|
|
<button className="ak-sb-item"><Icon name="card" size={16} /><span className="ak-sb-label">Accounts</span></button>
|
|
<button className="ak-sb-item"><Icon name="clock" size={16} /><span className="ak-sb-label">History</span></button>
|
|
<div style={{ flex: 1 }}></div>
|
|
<div className="ak-sb-foot">
|
|
<div className="ak-sb-foot-row">
|
|
<span className="lbl">Ledger</span>
|
|
<span className="val mono">9tfox-2026-03.ldgr</span>
|
|
</div>
|
|
<div className="ak-sb-foot-row">
|
|
<span className="lbl">Branch</span>
|
|
<span className="val mono">main · clean</span>
|
|
</div>
|
|
<div className="ak-sb-foot-row">
|
|
<span className="lbl">Last push</span>
|
|
<span className="val mono">4m ago</span>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
Object.assign(window, { Header, Sidebar });
|