akefin-design-system/ui_kits/web/LedgerScreen.jsx
2026-05-23 11:59:45 +09:00

86 lines
3.3 KiB
JavaScript

// LedgerScreen — per-entity account tree with balances
function TreeNode({ node, depth, expanded, onToggle }) {
const indent = depth * 18;
const isBranch = node.kind === "branch";
return (
<div className={`tree-node ${isBranch ? "branch" : "leaf"}`}
onClick={() => isBranch && onToggle(node.path)}>
<span style={{ width: indent }}></span>
<span className="tree-chevron">
{isBranch ? <Icon name={expanded ? "chevDown" : "chevRight"} size={12} color="var(--fg-muted)"/> : <span className="dot"></span>}
</span>
<span className="tree-path">
{node.path.split(":").slice(-1)[0]}
</span>
<span className="tree-full">{node.path}</span>
<span className="tree-amount"><Amount value={node.balance} ccy={node.ccy} /></span>
</div>
);
}
function LedgerScreen({ entity }) {
const data = window.AKEFIN_DATA.accountsByEntity;
const [expanded, setExpanded] = React.useState(new Set(["Personal:Assets", "Personal:Expenses", "9TFox:Assets", "9TFox:Income"]));
const toggle = (path) => setExpanded(prev => {
const next = new Set(prev);
next.has(path) ? next.delete(path) : next.add(path);
return next;
});
const entities = entity === "all" ? ["personal", "tfox", "finacode"] : [entity];
return (
<div className="ledger-screen">
<div className="screen-head">
<div>
<h1 className="screen-title">Ledger</h1>
<div className="screen-sub">Read-only · synced from <code>~/akefin/ledger/</code> · git rev <code>e4a82c1</code></div>
</div>
<div className="screen-actions">
<Btn><Icon name="refresh" size={13} /> PULL</Btn>
<Btn variant="primary"><Icon name="import" size={13} /> EXPORT</Btn>
</div>
</div>
{entities.map(e => {
const map = window.AKEFIN_DATA.entities.find(x => x.id === e);
const accounts = data[e] || [];
return (
<div key={e} className="ledger-entity">
<div className="ledger-entity-head">
<span className="stripe" style={{ background: map.color }}></span>
<span className="ledger-entity-name">{map.label}</span>
<span className="ledger-entity-meta">{accounts.length} accounts · {e}-2026-03.ldgr</span>
</div>
<div className="tree">
<div className="tree-head">
<span></span>
<span></span>
<span>ACCOUNT</span>
<span>FULL PATH</span>
<span style={{textAlign:"right"}}>BALANCE</span>
</div>
{accounts.map(node => {
if (node.kind === "leaf") {
const parent = node.path.split(":").slice(0, -1).join(":");
if (parent && parent.includes(":") && !expanded.has(parent.split(":").slice(0,2).join(":"))) {
return null;
}
}
const depth = node.path.split(":").length - 1;
return (
<TreeNode key={node.path} node={node} depth={depth}
expanded={expanded.has(node.path)}
onToggle={toggle} />
);
})}
</div>
</div>
);
})}
</div>
);
}
Object.assign(window, { LedgerScreen });