// AccountsScreen — connected data sources + chart of accounts (function () { const fmt = (n, ccy) => { if (n == null || isNaN(n)) return "—"; const abs = Math.abs(n); const isInt = ccy === "KRW" || ccy === "JPY" || Math.abs(abs - Math.round(abs)) < 0.005; return (n < 0 ? "− " : "") + abs.toLocaleString(undefined, { minimumFractionDigits: isInt ? 0 : 2, maximumFractionDigits: isInt ? 0 : 2, }); }; const entityShort = { personal: "P/", tfox: "9T/", finacode: "FC/" }; const entityClass = { personal: "personal", tfox: "tfox", finacode: "finacode" }; // ----- Status pill -------------------------------------------------- function StatusPill({ status }) { const map = { ok: { lbl: "OK", cls: "ok" }, warn: { lbl: "WARN", cls: "warn" }, fail: { lbl: "FAILED", cls: "fail" }, idle: { lbl: "IDLE", cls: "idle" }, }; const m = map[status] || map.idle; return {m.lbl}; } function MethodTag({ method }) { const map = { "api": "API", "csv-poll": "CSV · POLL", "csv-drop": "CSV · DROP", "manual": "MANUAL", }; return {map[method] || method.toUpperCase()}; } // ----- Connected source card --------------------------------------- function SourceCard({ src, selected, onClick }) { return ( ); } // ----- Chart of accounts tree -------------------------------------- // accountsByEntity rows have full path; we render them grouped per entity, indented by depth. function ChartOfAccounts({ entity }) { const data = window.AKEFIN_DATA.accountsByEntity; const sets = entity === "all" ? [["Personal", data.personal, "personal"], ["9TFox", data.tfox, "tfox"], ["Finacode", data.finacode, "finacode"]] : [[entity === "tfox" ? "9TFox" : entity[0].toUpperCase() + entity.slice(1), data[entity], entity]]; return (
{sets.map(([title, rows, cls]) => (
{entityShort[cls]} {title} {rows.length} accounts
{rows.map((r) => { const depth = (r.path.match(/:/g) || []).length; const leaf = r.path.split(":").pop(); return (
{r.kind === "branch" ? "▸" : "·"} {leaf} {r.path} {r.balance >= 0 ? "+ " : "− "}{fmt(Math.abs(r.balance), r.ccy)} {r.ccy}
); })}
))}
); } // ----- Detail panel for a selected source -------------------------- function SourceDetail({ src }) { if (!src) { return (
SELECT A SOURCE
Click a connected account to inspect sync settings, recent imports, and rotation.
); } return (
{src.kind.toUpperCase()} · {src.country}

{src.name}

{src.maskedNo}
BALANCE
{fmt(src.balance, src.ccy)} {src.ccy}
TX · 30D
{src.txCount30d}
LAST SYNC
{src.lastSync.split(" ")[1]}
{src.lastSync.split(" ")[0]}
NEXT
{src.nextRun}
SYNC
METHOD
ENTITY{entityShort[src.entity]} {src.entity === "tfox" ? "9TFox" : src.entity[0].toUpperCase() + src.entity.slice(1)}
SCHEDULE{src.method === "api" ? "every 4 hours" : src.method === "csv-poll" ? "every 12 hours" : "on demand"}
CCY{src.ccy}
RECENT IMPORTS
{src.lastSync.split(" ")[1]} Last successful sync · {src.txCount30d} rows in 30d
{src.status === "fail" && (
{src.lastSync.split(" ")[1]} CSV parse failed · encoding mismatch
)} {src.status === "warn" && (
{src.lastSync.split(" ")[1]} 11 rows queued for review · 3 failed
)}
); } // ----- Top-level screen -------------------------------------------- function AccountsScreen({ entity }) { const [tab, setTab] = React.useState("sources"); // sources | coa const sources = window.AKEFIN_DATA.connectedAccounts; const visible = sources.filter(s => entity === "all" || s.entity === entity); const [selectedId, setSelectedId] = React.useState(visible[0]?.id); React.useEffect(() => { if (visible.length && !visible.find(s => s.id === selectedId)) { setSelectedId(visible[0].id); } }, [entity]); const selected = visible.find(s => s.id === selectedId); // Totals (in KRW equivalent) const totalKrw = visible.reduce((a, s) => a + window.AKEFIN_DATA.convert(s.balance, s.ccy, "KRW"), 0); const okCount = visible.filter(s => s.status === "ok").length; const warnCount = visible.filter(s => s.status === "warn" || s.status === "fail").length; return (
ACCOUNTS · {entity === "all" ? "ALL ENTITIES" : entity.toUpperCase()}

Connected sources

{visible.length} sources · {okCount} healthy · {warnCount} need attention · combined ≈ {fmt(totalKrw, "KRW")} KRW
{tab === "sources" && (
{visible.map(s => ( setSelectedId(s.id)} /> ))}
)} {tab === "coa" && }
); } Object.assign(window, { AccountsScreen }); })();