Download app/frontend/src/components/Board.tsx from ml-intern-explorers/primarydraft: direct link, hf CLI and curl.
- Browser
- Download file 9.92 kB
-
https://huggingface.co/spaces/ml-intern-explorers/primarydraft/resolve/main/app/frontend/src/components/Board.tsx
- Command line
-
hf download hf://spaces/ml-intern-explorers/primarydraft/app/frontend/src/components/Board.tsx
-
curl -L -o Board.tsx https://huggingface.co/spaces/ml-intern-explorers/primarydraft/resolve/main/app/frontend/src/components/Board.tsx
9.92 kB
| 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<RuleResult[] | null>(null); | |
| const [error, setError] = useState<string | null>(null); | |
| const [working, setWorking] = useState<string | null>(null); | |
| const [showPasses, setShowPasses] = useState(false); | |
| const [summary, setSummary] = useState<RunSummary>(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 ( | |
| <> | |
| <div className="page-head"> | |
| <h1 className="page">Consistency check</h1> | |
| <p className="lede"> | |
| {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. | |
| </p> | |
| </div> | |
| {error && <div style={{ marginBottom: 14 }}><ErrorNote message={error} onRetry={check} /></div>} | |
| <div className="stats" style={{ marginBottom: 16 }}> | |
| <div className="stat"> | |
| <div className={`stat-v ${summary.severity.blocker ? "stop" : "ok"}`}> | |
| {summary.severity.blocker ?? 0} | |
| </div> | |
| <div className="stat-l">blockers</div> | |
| </div> | |
| <div className="stat"> | |
| <div className={`stat-v ${summary.severity.high ? "warn" : ""}`}> | |
| {summary.severity.high ?? 0} | |
| </div> | |
| <div className="stat-l">high</div> | |
| </div> | |
| <div className="stat"> | |
| <div className="stat-v">{summary.severity.medium ?? 0}</div> | |
| <div className="stat-l">medium</div> | |
| </div> | |
| <div className="stat"> | |
| <div className="stat-v ok">{summary.rules_passed}</div> | |
| <div className="stat-l">checks passed</div> | |
| </div> | |
| <div className="stat"> | |
| <div className="stat-v">{summary.rules_na}</div> | |
| <div className="stat-l">not applicable</div> | |
| </div> | |
| <div className="stat"> | |
| <div className="stat-v">{done.length}</div> | |
| <div className="stat-l">resolved by you</div> | |
| </div> | |
| </div> | |
| {!results ? ( | |
| <div className="panel"><div className="panel-b"><TableSkeleton rows={6} /></div></div> | |
| ) : ( | |
| <> | |
| {open.length === 0 ? ( | |
| <div className="panel" style={{ marginBottom: 14 }}> | |
| <Empty icon={<CheckCircle size={28} weight="fill" />} 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. | |
| </Empty> | |
| </div> | |
| ) : ( | |
| <section style={{ marginBottom: 16 }}> | |
| {open.map((f) => ( | |
| <FindingCard | |
| key={`${f.rule_id}-${f.title}`} | |
| f={f} | |
| working={working} | |
| onResolve={resolve} | |
| /> | |
| ))} | |
| </section> | |
| )} | |
| {done.length > 0 && ( | |
| <section style={{ marginBottom: 16 }}> | |
| <div className="panel-h" style={{ border: 0, padding: "4px 0 8px" }}> | |
| <h2>Resolved</h2> | |
| <span className="count">{done.length}</span> | |
| </div> | |
| {done.map((f) => ( | |
| <div className="finding" key={`${f.rule_id}-${f.title}-done`}> | |
| <div className="finding-h"> | |
| <span className="sev sev-resolved">resolved</span> | |
| <div> | |
| <h4>{f.title}</h4> | |
| <p> | |
| {f.resolved_by} 路 {f.resolved_at?.replace("T", " ").replace("+00:00", " UTC")} | |
| </p> | |
| </div> | |
| <span className="finding-rule">{f.rule_id}</span> | |
| </div> | |
| </div> | |
| ))} | |
| </section> | |
| )} | |
| <section className="panel"> | |
| <div className="panel-h"> | |
| <ShieldCheck size={16} /> | |
| <h2>All checks</h2> | |
| <span className="count"> | |
| {summary.rules_passed} passed 路 {summary.rules_failed} failed 路 {summary.rules_na} n/a | |
| </span> | |
| <button | |
| className="btn btn-ghost btn-sm" | |
| style={{ marginLeft: "auto" }} | |
| onClick={() => setShowPasses((v) => !v)} | |
| > | |
| {showPasses ? "Hide detail" : "Show detail"} | |
| </button> | |
| </div> | |
| {showPasses && ( | |
| <div style={{ maxHeight: "42vh", overflowY: "auto" }}> | |
| {results.map((r) => ( | |
| <div className="rule-row" key={r.rule_id}> | |
| <span className="rule-id">{r.rule_id}</span> | |
| <span className={`mark mark-${r.status === "not_applicable" ? "na" : r.status}`}> | |
| {r.status === "not_applicable" ? "n/a" : r.status} | |
| </span> | |
| <span className="rule-detail"> | |
| {r.detail} | |
| {r.icdr_ref && ( | |
| <span className="f-id" style={{ display: "block", marginTop: 2 }}> | |
| {r.icdr_ref} | |
| </span> | |
| )} | |
| </span> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </section> | |
| </> | |
| )} | |
| <div style={{ marginTop: 16 }}> | |
| <button className="btn btn-primary" onClick={onNext} disabled={!results}> | |
| Continue to handoff <ArrowRight size={15} /> | |
| </button> | |
| </div> | |
| </> | |
| ); | |
| } | |
| function FindingCard({ | |
| f, working, onResolve, | |
| }: { f: Finding; working: string | null; onResolve: (f: Finding, id: string) => void }) { | |
| return ( | |
| <div className="finding"> | |
| <div className="finding-h"> | |
| <span className={`sev sev-${f.severity}`}>{f.severity}</span> | |
| <div> | |
| <h4>{f.title}</h4> | |
| <p>{f.explanation}</p> | |
| </div> | |
| <span className="finding-rule">{f.rule_id}</span> | |
| </div> | |
| <div className="finding-b"> | |
| {f.delta && <div className="delta">difference: {f.delta}</div>} | |
| {f.evidence.length > 0 && ( | |
| <div className="ev-grid"> | |
| {f.evidence.map((e, i) => ( | |
| <div className="ev" key={i}> | |
| <div> | |
| <div className="ev-l">{e.label}</div> | |
| {e.locator && <div className="ev-loc">{e.locator}</div>} | |
| {e.raw_line && <div className="ev-raw">{e.raw_line}</div>} | |
| </div> | |
| <div className="ev-v">{e.display}</div> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {f.icdr_ref && ( | |
| <div style={{ display: "flex", gap: 6, alignItems: "center", marginBottom: 10 }}> | |
| <Gavel size={13} style={{ color: "var(--text-3)" }} /> | |
| <span className="f-id">{f.icdr_ref}</span> | |
| </div> | |
| )} | |
| {f.resolutions.length > 0 ? ( | |
| <div className="res-list"> | |
| {f.resolutions.map((r) => ( | |
| <button | |
| key={r.id} | |
| className={`res ${r.recommended ? "res-rec" : ""}`} | |
| onClick={() => onResolve(f, r.id)} | |
| disabled={working !== null} | |
| > | |
| <div className="res-l"> | |
| {working === `${f.rule_id}:${r.id}` && <Spinner />} {r.label} | |
| {r.recommended && ( | |
| <span className="tag tag-ok" style={{ marginLeft: 6 }}>recommended</span> | |
| )} | |
| </div> | |
| <div className="res-d">{r.description}</div> | |
| {r.justification && ( | |
| <div className="res-d" style={{ fontStyle: "italic", marginTop: 3 }}> | |
| {r.justification} | |
| </div> | |
| )} | |
| </button> | |
| ))} | |
| </div> | |
| ) : ( | |
| <Note tone="quiet"> | |
| This finding has no automatic remedy. It must be reconciled against the audited | |
| statements by the merchant banker. | |
| </Note> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } | |