/* Eno.Health Console — Documents: Live pipeline board + enhanced history */ const P = window.ENO; /* ======================================================================== */ /* LIVE PIPELINE (client-side simulation of the processing pipeline) */ /* ======================================================================== */ const SAMPLE_FILES = ["cbc-panel.pdf", "lipid-profile.pdf", "metabolic.pdf", "hba1c.pdf", "thyroid.pdf", "liver-fn.pdf", "blood-gas.pdf", "coag-pt-inr.pdf", "wgs-report.pdf", "gene-panel.json", "ecg-strip.png", "intake.pdf"]; let LIVE_SEQ = 1; function makeLiveDoc(bornAt) { const lab = P.LABS[Math.floor(Math.random() * P.LABS.length)]; const t = P.TENANTS[Math.floor(Math.random() * P.TENANTS.length)]; const panelKeys = (lab.panels && lab.panels.length) ? lab.panels : P.TEST_PANELS.map((p) => p.key); const panel = panelKeys[Math.floor(Math.random() * panelKeys.length)]; const willFail = lab.status === "unsupported" ? Math.random() < 0.16 : Math.random() < 0.035; let failAt = null; if (willFail) failAt = [1, 3, 3, 9][Math.floor(Math.random() * 4)]; const willReview = lab.status !== "supported" && Math.random() < 0.45; return { uid: "L" + (LIVE_SEQ++), id: "doc_lv" + Math.random().toString(36).slice(2, 8), file: SAMPLE_FILES[Math.floor(Math.random() * SAMPLE_FILES.length)], tenant: t.id, lab: lab.id, labStatus: lab.status, panel, stage: 0, enteredAt: bornAt, bornAt, state: "active", justMoved: true, failAt, willReview, review: false }; } function seedLive() { const now = Date.now(); const arr = []; for (let i = 0; i < 22; i++) { const d = makeLiveDoc(now - (3000 + Math.random() * 40000)); d.stage = Math.floor(Math.random() * P.PIPE_LEN); d.enteredAt = now - Math.random() * 5000; if (d.failAt != null && d.stage >= d.failAt) d.failAt = null; if (d.stage >= 4 && d.willReview) d.review = true; arr.push(d); } return arr; } function LivePipelineView({ onOpenDoc }) { const docsRef = useRef(seedLive()); const [docs, setDocs] = useState(docsRef.current); const [log, setLog] = useState([]); const [paused, setPaused] = useState(false); const [sel, setSel] = useState(null); const stats = useRef({ published: [], failures: [], latencies: [] }); useEffect(() => { if (paused) return; const iv = setInterval(() => { const now = Date.now(); const events = []; let next = docsRef.current.map((d) => ({ ...d, justMoved: false })); next.forEach((d) => { if (d.state !== "active") return; if (Math.random() < 0.5) { const from = d.stage; d.stage = Math.min(P.PIPE_LEN - 1, d.stage + 1); d.justMoved = true; d.enteredAt = now; if (d.failAt != null && d.stage === d.failAt) { d.state = "failed"; d.failedAt = now; stats.current.failures.push(now); events.unshift({ k: "fail", id: d.id, file: d.file, stage: d.stage, at: now }); } else { if (d.stage >= 4 && d.willReview) d.review = true; events.unshift({ k: "move", id: d.id, file: d.file, from, stage: d.stage, at: now, review: d.review }); } } }); next.forEach((d) => { if (d.state === "active" && d.stage === P.PIPE_LEN - 1 && now - d.enteredAt > 1400) { d.state = "done"; d.doneAt = now; stats.current.published.push(now); stats.current.latencies.push(now - d.bornAt); events.unshift({ k: "done", id: d.id, file: d.file, stage: d.stage, at: now }); } }); next = next.filter((d) => { if (d.state === "done") return now - d.doneAt < 450; if (d.state === "failed") return now - d.failedAt < 2200; return true; }); const active = next.filter((d) => d.state === "active").length; if (active < 28) { const n = 1 + (Math.random() < 0.5 ? 1 : 0); for (let i = 0; i < n; i++) next.push(makeLiveDoc(now)); } docsRef.current = next; setDocs(next); if (events.length) setLog((l) => [...events, ...l].slice(0, 9)); }, 1150); return () => clearInterval(iv); }, [paused]); const now = Date.now(); stats.current.published = stats.current.published.filter((t) => now - t < 60000); stats.current.failures = stats.current.failures.filter((t) => now - t < 60000); const active = docs.filter((d) => d.state === "active"); const perStage = P.PIPELINE.map((_, i) => docs.filter((d) => d.stage === i && d.state === "active").length); const deepest = perStage.reduce((m, c, i) => (c > m.c ? { c, i } : m), { c: 0, i: 0 }); const lat = stats.current.latencies.slice(-12); const avgLat = lat.length ? (lat.reduce((a, b) => a + b, 0) / lat.length / 1000) : 0; const reviewable = docs.filter((d) => d.stage >= 4 && d.state === "active"); const reviewPct = reviewable.length ? Math.round(100 * reviewable.filter((d) => d.review).length / reviewable.length) : 0; return h("div", { className: "view" }, h("div", { className: "ref-banner" }, h(Icon, { name: "activity", size: 16, className: "dim" }), h("div", null, "Realtime view of documents flowing through the processing pipeline. ", "Cards advance ", h("strong", null, "Ingest → Publish"), " as each stage completes; unsupported-lab documents fail or route to review more often. Click any card to inspect it.")), h(LiveMetrics, { throughput: stats.current.published.length, inflight: active.length, avgLat, reviewPct, failures: stats.current.failures.length, deepest, paused, onToggle: () => setPaused((p) => !p) }), h(LiveRows, { docs, onSelect: setSel }), h(LiveDocDrawer, { doc: sel, onClose: () => setSel(null) })); } function LiveMetrics({ throughput, inflight, avgLat, reviewPct, failures, deepest, paused, onToggle }) { const tiles = [ { label: "Throughput", value: throughput, unit: "/min", icon: "activity", tone: "ok" }, { label: "In-flight", value: inflight, icon: "runs" }, { label: "Avg latency", value: avgLat ? avgLat.toFixed(1) : "—", unit: avgLat ? "s" : "", icon: "clock" }, { label: "Manual review", value: reviewPct, unit: "%", icon: "alert", tone: reviewPct > 25 ? "warn" : undefined }, { label: "Failures", value: failures, unit: "/min", icon: "x", tone: failures ? "bad" : "ok" }, ]; return h("div", { className: "pipe-metrics-row" }, h("div", { className: "pipe-metrics" }, tiles.map((t) => h("div", { className: "pmetric", key: t.label }, h("div", { className: "pmetric-top" }, h("span", { className: "pmetric-label" }, t.label), h(Icon, { name: t.icon, size: 14, className: "dim" })), h("div", { className: "pmetric-val " + (t.tone ? "tone-ink-" + t.tone : "") }, t.value, t.unit && h("span", { className: "pmetric-unit" }, t.unit))))), h("div", { className: "pipe-live-ctl" }, h("span", { className: "live-dot" + (paused ? " paused" : "") }), h("span", { className: "live-word" }, paused ? "Paused" : "Live"), h(Button, { variant: "ghost", size: "sm", icon: paused ? "play" : "pause", onClick: onToggle }, paused ? "Resume" : "Pause"))); } function PipelineDiagram({ perStage, docs }) { const max = Math.max(1, ...perStage); const failPer = P.PIPELINE.map((_, i) => docs.filter((d) => d.stage === i && d.state === "failed").length); return h(Card, { pad: false, className: "pipe-diagram-card" }, h("div", { className: "pipe-diagram" }, P.PIPELINE.map((s, i) => h(React.Fragment, { key: s.key }, h("div", { className: "pnode" + (perStage[i] ? " pnode-on" : "") + (failPer[i] ? " pnode-fail" : "") }, h("div", { className: "pnode-count" }, perStage[i]), h("div", { className: "pnode-bar" }, h("span", { style: { height: Math.round((perStage[i] / max) * 100) + "%" } })), h("div", { className: "pnode-label" }, s.short), failPer[i] ? h("div", { className: "pnode-failbadge", title: failPer[i] + " failing" }, failPer[i]) : null), i < P.PIPELINE.length - 1 ? h("div", { className: "pnode-arrow" }, h(Icon, { name: "arrowRight", size: 14 })) : null)))); } const CONF_DOT = (status) => status === "supported" ? "ok" : status === "partial" ? "warn" : "bad"; function LiveRows({ docs, onSelect }) { const rows = docs.filter((d) => d.state !== "done"); // ready docs leave the live view return h(Card, { pad: false, className: "liverows-card", title: "Live documents", subtitle: "In processing now · completed documents leave the view" }, h("div", { className: "liverows" }, rows.length === 0 && h("div", { className: "sse-wait" }, h(Icon, { name: "spinner", size: 16, className: "spin" }), "Waiting for documents…"), rows.map((d) => h(LiveRow, { key: d.uid, d, onSelect })))); } function LiveRow({ d, onSelect }) { const pct = Math.round(((d.stage + 1) / P.PIPE_LEN) * 100); const stage = P.PIPELINE[d.stage]; return h("button", { className: "lrow" + (d.justMoved ? " lrow-moved" : "") + (d.state === "failed" ? " lrow-failed" : "") + (d.review ? " lrow-review" : ""), onClick: () => onSelect(d) }, h("div", { className: "lrow-id" }, h("span", { className: "lrow-file" }, d.file), h("span", { className: "lrow-lab" }, h("span", { className: "svc-dot svc-" + (CONF_DOT(d.labStatus) === "ok" ? "up" : CONF_DOT(d.labStatus) === "warn" ? "stale" : "down") }), P.labName(d.lab), " · ", P.tenantName(d.tenant))), h("div", { className: "lrow-prog" }, h("div", { className: "lrow-segs" }, P.PIPELINE.map((s, i) => h("span", { key: i, title: s.label, className: "lseg " + (i < d.stage ? "lseg-done" : i === d.stage ? (d.state === "failed" ? "lseg-fail" : "lseg-active") : "lseg-pending") }))), h("div", { className: "lrow-stagelbl" }, d.state === "failed" ? "Failed at " + stage.short : stage.label)), h("div", { className: "lrow-right" }, d.state === "failed" ? h(Pill, { tone: "bad" }, "failed") : d.review ? h(Pill, { tone: "warn", soft: true }, "review") : h("span", { className: "lrow-pct mono" }, pct, "%"), h(Icon, { name: "chevronR", size: 14, className: "row-arrow" }))); } function TransitionLog({ log }) { return h(Card, { pad: false, className: "translog-card", title: "Live transitions", subtitle: "Most recent stage changes" }, h("div", { className: "translog" }, log.length === 0 && h("div", { className: "sse-wait" }, h(Icon, { name: "spinner", size: 15, className: "spin" }), "Waiting for transitions…"), log.map((e) => h("div", { className: "trow trow-" + e.k, key: e.id + e.at }, h("span", { className: "trow-icon" }, h(Icon, { name: e.k === "fail" ? "x" : e.k === "done" ? "check" : "arrowRight", size: 13 })), h(Mono, { className: "trow-file" }, e.file), h("span", { className: "trow-stage" }, e.k === "done" ? "published" : e.k === "fail" ? "failed at " + P.PIPELINE[e.stage].short : P.PIPELINE[e.stage].short), e.review && e.k === "move" ? h("span", { className: "kcard-tag warn" }, "review") : null, h("span", { className: "trow-time mono dim" }, "just now"))))); } function LiveDocDrawer({ doc, onClose }) { if (!doc) return h(Drawer, { open: false, onClose }); const lab = P.LABS.find((l) => l.id === doc.lab) || {}; const elapsed = ((Date.now() - doc.bornAt) / 1000).toFixed(1); const reachedFail = doc.state === "failed" ? doc.stage : -1; return h(Drawer, { open: true, onClose, width: 560, title: doc.file, sub: h(React.Fragment, null, h(Mono, { copy: true }, doc.id), doc.state === "failed" ? h(Pill, { tone: "bad" }, "failed") : doc.state === "done" ? h(Pill, { tone: "ok" }, "published") : h(Pill, { tone: "info" }, "in-flight")) }, h("div", { className: "refbox" }, h("div", { className: "ref-banner" }, h(Icon, { name: "clock", size: 15, className: "dim" }), h("div", null, "In-flight document — the full immutable audit trail becomes available in ", h("strong", null, "History"), " once it reaches Publish.")), h("div", { className: "meta-grid" }, h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Tenant"), h("span", { className: "meta-v" }, P.tenantName(doc.tenant))), h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Lab / author"), h("span", { className: "meta-v" }, lab.name + " ", h("span", { className: "tag tag-" + (CONF_DOT(doc.labStatus) === "ok" ? "" : "accent") }, doc.labStatus))), h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Test type"), h("span", { className: "meta-v" }, doc.panel ? P.panelName(doc.panel) : "—")), h("div", { className: "meta-row" }, h("span", { className: "meta-k" }, "Elapsed"), h("span", { className: "meta-v mono" }, elapsed + "s"))), h("div", { className: "refsec" }, h("p", { className: "refsec-title" }, "Pipeline progress"), h("div", { className: "vstage" }, P.PIPELINE.map((s, i) => { const st = reachedFail === i ? "failed" : i < doc.stage ? "done" : i === doc.stage ? (doc.state === "done" ? "done" : "active") : "pending"; return h("div", { className: "vstage-row", key: s.key }, h("span", { className: "vstage-mark vstage-" + st }, h(Icon, { name: st === "failed" ? "x" : st === "done" ? "check" : st === "active" ? "dot" : "dot", size: 12 })), h("span", { className: "vstage-label" }, s.label), h("span", { className: "vstage-svc mono dim" }, s.service), i < P.PIPELINE.length - 1 ? h("span", { className: "vstage-line" }) : null); }))))); } /* ======================================================================== */ /* HISTORY (enhanced query over ingested documents) */ /* ======================================================================== */ const DATE_RANGES = [ { value: "all", label: "Any time", ms: Infinity }, { value: "1h", label: "Last hour", ms: 3600e3 }, { value: "24h", label: "Last 24 hours", ms: 24 * 3600e3 }, { value: "7d", label: "Last 7 days", ms: 7 * 24 * 3600e3 }, { value: "30d", label: "Last 30 days", ms: 30 * 24 * 3600e3 }, ]; function DocumentHistory({ onOpenDoc }) { const [q, setQ] = useState(""); const [status, setStatus] = useState("all"); const [tenant, setTenant] = useState("all"); const [lab, setLab] = useState("all"); const [type, setType] = useState("all"); const [bm, setBm] = useState("all"); const [stage, setStage] = useState("all"); const [conf, setConf] = useState("all"); const [range, setRange] = useState("all"); const statusOpts = [{ value: "all", label: "All statuses" }, ...Object.keys(P.STATUS).map((k) => ({ value: k, label: P.STATUS[k].label }))]; const typeOpts = [{ value: "all", label: "All types" }, ...P.TEST_PANELS.map((p) => ({ value: "p:" + p.key, label: "Panel · " + p.name })), ...P.DOC_TYPES.filter((t) => t.key !== "lab").map((t) => ({ value: "t:" + t.key, label: t.label }))]; const stageOpts = [{ value: "all", label: "Any stage reached" }, ...P.PIPELINE.map((s, i) => ({ value: String(i), label: (i + 1) + ". " + s.label })), { value: "halted", label: "⚠ Halted / failed only" }]; const confOpts = [{ value: "all", label: "Any confidence" }, { value: "review", label: "Requires review (<85%)" }, { value: "high", label: "High (≥95%)" }, { value: "mid", label: "Medium (85–95%)" }, { value: "na", label: "No score (failed)" }]; const rangeMs = (DATE_RANGES.find((r) => r.value === range) || {}).ms || Infinity; const rows = P.DOCUMENTS.filter((d) => { if (status !== "all" && d.statusKey !== status) return false; if (tenant !== "all" && d.tenant !== tenant) return false; if (lab !== "all" && d.lab !== lab) return false; if (type !== "all") { if (type.startsWith("p:") && d.panel !== type.slice(2)) return false; if (type.startsWith("t:") && d.docType !== type.slice(2)) return false; } if (bm !== "all" && !(d.biomarkers || []).includes(bm)) return false; if (stage !== "all") { if (stage === "halted") { if (!d.pipeHalted) return false; } else if (d.pipeReached !== Number(stage)) return false; } if (conf !== "all") { const c = d.extractionConfidence; if (conf === "review" && !d.requiresReview) return false; if (conf === "na" && c != null) return false; if (conf === "high" && !(c != null && c >= 95)) return false; if (conf === "mid" && !(c != null && c >= 85 && c < 95)) return false; } if (rangeMs !== Infinity && (P.NOW - d.createdAt.getTime()) > rangeMs) return false; if (q && !(d.id + d.file + d.patient + d.correlationId + P.labName(d.lab)).toLowerCase().includes(q.toLowerCase())) return false; return true; }); const reset = () => { setStatus("all"); setTenant("all"); setLab("all"); setType("all"); setBm("all"); setStage("all"); setConf("all"); setRange("all"); setQ(""); }; const active = [status, tenant, lab, type, bm, stage, conf, range].filter((v) => v !== "all").length + (q ? 1 : 0); const head = ["Document", "Tenant", "Lab / author", "Type", "Status", "Stage reached", "Confidence", "Age"]; return h("div", { className: "view" }, h(Card, { pad: false }, h("div", { className: "toolbar" }, h("div", { className: "search" }, h(Icon, { name: "search", size: 16 }), h("input", { className: "search-input", placeholder: "Search ID, file, patient, correlation, lab…", value: q, onChange: (e) => setQ(e.target.value) })), h("span", { className: "toolbar-count" }, rows.length, " of ", P.DOCUMENTS.length), active > 0 && h(Button, { variant: "ghost", size: "sm", icon: "x", onClick: reset }, "Clear " + active)), h("div", { className: "filterbar" }, h(Select, { value: status, onChange: setStatus, options: statusOpts }), h(Select, { value: tenant, onChange: setTenant, options: [{ value: "all", label: "All tenants" }, ...P.TENANTS.map((t) => ({ value: t.id, label: t.name }))] }), h(Select, { value: lab, onChange: setLab, options: [{ value: "all", label: "All labs" }, ...P.LABS.map((l) => ({ value: l.id, label: l.name }))] }), h(Select, { value: type, onChange: setType, options: typeOpts }), h(Select, { value: bm, onChange: setBm, options: [{ value: "all", label: "Any biomarker" }, ...P.BIOMARKERS.map((b) => ({ value: b.id, label: b.name }))] }), h(Select, { value: stage, onChange: setStage, options: stageOpts }), h(Select, { value: conf, onChange: setConf, options: confOpts }), h(Select, { value: range, onChange: setRange, options: DATE_RANGES.map((r) => ({ value: r.value, label: r.label })) })), h("div", { className: "table-wrap" }, h("table", { className: "table" }, h("thead", null, h("tr", null, head.map((c, i) => h("th", { key: i, className: i === 7 ? "ta-r" : "" }, c)))), h("tbody", null, rows.map((d) => h(HistRow, { key: d.id, d, onClick: () => onOpenDoc(d) })), !rows.length && h("tr", null, h("td", { colSpan: 8 }, h(Empty, { title: "No documents match", sub: "Try clearing some filters." })))))))); } function HistRow({ d, onClick }) { const c = d.extractionConfidence; const confTone = c == null ? "neutral" : c >= 95 ? "ok" : c >= 85 ? "info" : "warn"; return h("tr", { className: "row-click", onClick }, h("td", null, h("div", { className: "cell-doc" }, h("span", { className: "cell-file" }, d.file), h(Mono, { className: "cell-id" }, d.id))), h("td", null, h("span", { className: "cell-tenant" }, P.tenantName(d.tenant))), h("td", null, P.labName(d.lab)), h("td", null, d.panel ? h("span", { className: "tag tag-accent" }, P.panelName(d.panel)) : h("span", { className: "tag" }, (P.DOC_TYPES.find((t) => t.key === d.docType) || {}).label || d.docType)), h("td", null, h(StatusPill, { status: d.status })), h("td", null, h(StageReached, { d })), h("td", null, c == null ? h("span", { className: "dim" }, "—") : h("span", { className: "conf-cell-sm" }, h(Pill, { tone: confTone, soft: true }, c.toFixed(1) + "%"), d.requiresReview ? h("span", { className: "kcard-tag warn" }, "review") : null)), h("td", { className: "ta-r dim" }, fmtAgo(d.createdAt))); } function StageReached({ d }) { const pct = Math.round(((d.pipeReached + 1) / P.PIPE_LEN) * 100); const tone = d.pipeHalted ? "bad" : pct === 100 ? "ok" : "info"; return h("div", { className: "stagereached" }, h("div", { className: "sr-bar" }, h("span", { style: { width: pct + "%", background: "var(--" + tone + ")" } })), h("span", { className: "sr-label" + (d.pipeHalted ? " bad-ink" : "") }, d.pipeHalted ? "halted · " : "", P.PIPELINE[d.pipeReached].short)); } Object.assign(window, { LivePipelineView, DocumentHistory });