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

89 lines
4.1 KiB
JavaScript

// ImportScreen — recent import runs and status
function ImportBar({ run }) {
const total = run.rows;
const pct = (n) => (n / total) * 100;
return (
<div className="import-bar">
<div style={{ width: `${pct(run.auto)}%`, background: "var(--conf-rules)" }} title={`auto ${run.auto}`}></div>
<div style={{ width: `${pct(run.high)}%`, background: "var(--conf-high)" }} title={`high ${run.high}`}></div>
<div style={{ width: `${pct(run.review)}%`, background: "var(--conf-mid)" }} title={`review ${run.review}`}></div>
<div style={{ width: `${pct(run.failed)}%`, background: "var(--conf-low)" }} title={`failed ${run.failed}`}></div>
</div>
);
}
function ImportScreen({ entity, onOpenImport }) {
const runs = window.AKEFIN_DATA.importRuns.filter(r => entity === "all" || r.entity === entity);
const total = runs.reduce((acc, r) => ({
rows: acc.rows + r.rows,
auto: acc.auto + r.auto,
high: acc.high + r.high,
review: acc.review + r.review,
failed: acc.failed + r.failed,
}), { rows: 0, auto: 0, high: 0, review: 0, failed: 0 });
return (
<div className="import-screen">
<div className="screen-head">
<div>
<h1 className="screen-title">Import status</h1>
<div className="screen-sub">{runs.length} recent runs · {total.rows} rows · {total.failed} failed</div>
</div>
<div className="screen-actions">
<Btn><Icon name="refresh" size={13} /> POLL TOSS</Btn>
<Btn variant="primary" onClick={onOpenImport}><Icon name="import" size={13} /> IMPORT FILE</Btn>
</div>
</div>
<div className="import-stats">
<div className="stat"><span className="stat-num">{total.rows}</span><span className="stat-lbl">TOTAL ROWS</span></div>
<div className="stat"><span className="stat-num" style={{ color: "var(--conf-rules)" }}>{total.auto}</span><span className="stat-lbl">AUTO · TIER 1</span></div>
<div className="stat"><span className="stat-num" style={{ color: "var(--conf-high)" }}>{total.high}</span><span className="stat-lbl">HIGH · TIER 2</span></div>
<div className="stat"><span className="stat-num" style={{ color: "var(--conf-mid)" }}>{total.review}</span><span className="stat-lbl">REVIEW · TIER 3</span></div>
<div className="stat"><span className="stat-num" style={{ color: "var(--conf-low)" }}>{total.failed}</span><span className="stat-lbl">FAILED</span></div>
</div>
<TermFrame title="AKEFIN — IMPORT PIPELINE" status="ok">
<span className="m">$</span> akefin import --watch{"\n"}
<span className="m">polling 4 sources · last poll 4 min ago · next in 11 min</span>{"\n"}
<span className="m">tier 1 rules:</span> 47 active <span className="m">·</span> tier 2 llm: gpt-4o-mini <span className="m">·</span> tier 3 agent: claude-haiku
</TermFrame>
<div className="import-list">
<div className="import-list-head">
<span>RUN AT</span>
<span>SOURCE</span>
<span>ENTITY</span>
<span style={{textAlign:"right"}}>ROWS</span>
<span>BREAKDOWN</span>
<span></span>
</div>
{runs.map(r => (
<div key={r.id} className="import-row">
<span className="mono">{r.at}</span>
<span>{r.source}</span>
<span><EntityBadge entity={r.entity} /></span>
<span className="mono right">{r.rows}</span>
<span className="import-bar-cell">
<ImportBar run={r} />
<span className="import-counts">
<span style={{color:"var(--conf-rules)"}}>{r.auto}</span>
<span> · </span>
<span style={{color:"var(--conf-high)"}}>{r.high}</span>
<span> · </span>
<span style={{color:"var(--conf-mid)"}}>{r.review}</span>
<span> · </span>
<span style={{color:"var(--conf-low)"}}>{r.failed}</span>
</span>
</span>
<span><Btn bracket>OPEN</Btn></span>
</div>
))}
</div>
</div>
);
}
Object.assign(window, { ImportScreen });