import { useEffect, useState } from "react"; import { ShieldCheck, ArrowRight, CheckCircle, Gavel } from "@phosphor-icons/react"; import { api, ApiError, type Finding, type RuleResult, type RunSummary } from "../lib/api"; import { Empty, ErrorNote, Note, Spinner, TableSkeleton } from "./common"; const ORDER = { blocker: 0, high: 1, medium: 2, info: 3 } as const; const ACTOR = "Merchant banker"; export function Board({ run, onNext, onRunUpdate, }: { run: RunSummary; onNext: () => void; onRunUpdate: (r: RunSummary) => void }) { const [results, setResults] = useState(null); const [error, setError] = useState(null); const [working, setWorking] = useState(null); const [showPasses, setShowPasses] = useState(false); const [summary, setSummary] = useState(run); const check = async () => { setError(null); setResults(null); try { const r = await api.check(run.id); setResults(r.rule_results); setSummary(r); onRunUpdate(r); } catch (e) { setError(e instanceof ApiError ? e.message : String(e)); } }; useEffect(() => { check(); }, [run.id]); const resolve = async (f: Finding, resolutionId: string) => { setWorking(`${f.rule_id}:${resolutionId}`); setError(null); try { const r = await api.resolve(run.id, f.rule_id, resolutionId, ACTOR); setResults(r.rule_results); setSummary(r); onRunUpdate(r); } catch (e) { setError(e instanceof ApiError ? e.message : String(e)); } finally { setWorking(null); } }; const findings = (results ?? []).flatMap((r) => r.findings); const open = findings.filter((f) => !f.resolved).sort((a, b) => ORDER[a.severity] - ORDER[b.severity]); const done = findings.filter((f) => f.resolved); const passes = (results ?? []).filter((r) => r.status === "pass"); const nas = (results ?? []).filter((r) => r.status === "not_applicable"); return ( <>

Consistency check

{summary.rule_count} deterministic checks over the issuer's real figures and your proposed terms. This is plain Python: no language model takes part in any verdict here, and the same result appears whether or not the narrative layer is enabled.

{error &&
}
{summary.severity.blocker ?? 0}
blockers
{summary.severity.high ?? 0}
high
{summary.severity.medium ?? 0}
medium
{summary.rules_passed}
checks passed
{summary.rules_na}
not applicable
{done.length}
resolved by you
{!results ? (
) : ( <> {open.length === 0 ? (
} title="No open findings"> Every deterministic check that applied to this filing passed. {nas.length} checks did not apply because the underlying figures were not machine-readable, and those are listed as gaps rather than passes.
) : (
{open.map((f) => ( ))}
)} {done.length > 0 && (

Resolved

{done.length}
{done.map((f) => (
resolved

{f.title}

{f.resolved_by} · {f.resolved_at?.replace("T", " ").replace("+00:00", " UTC")}

{f.rule_id}
))}
)}

All checks

{summary.rules_passed} passed · {summary.rules_failed} failed · {summary.rules_na} n/a
{showPasses && (
{results.map((r) => (
{r.rule_id} {r.status === "not_applicable" ? "n/a" : r.status} {r.detail} {r.icdr_ref && ( {r.icdr_ref} )}
))}
)}
)}
); } function FindingCard({ f, working, onResolve, }: { f: Finding; working: string | null; onResolve: (f: Finding, id: string) => void }) { return (
{f.severity}

{f.title}

{f.explanation}

{f.rule_id}
{f.delta &&
difference: {f.delta}
} {f.evidence.length > 0 && (
{f.evidence.map((e, i) => (
{e.label}
{e.locator &&
{e.locator}
} {e.raw_line &&
{e.raw_line}
}
{e.display}
))}
)} {f.icdr_ref && (
{f.icdr_ref}
)} {f.resolutions.length > 0 ? (
{f.resolutions.map((r) => ( ))}
) : ( This finding has no automatic remedy. It must be reconciled against the audited statements by the merchant banker. )}
); }