/* Eno.Health Console — primary views: Overview, Documents, Runs, Upload */ const E = window.ENO; /* ======================================================================== */ /* OVERVIEW */ /* ======================================================================== */ function OverviewView({ cell, onOpenDoc, goto, onSelectCell, onOpenDevice }) { const k = E.kpis(cell.id); const tp = useMemo(() => E.series(3, 28, 60, 14), []); const stageCounts = useMemo(() => { const map = { quarantine: 9, scanning: 14, promotion: 7, redaction: 23, ingest: 41 }; return map; }, []); const services = E.SERVICES[cell.id]; const attention = E.DOCUMENTS.filter((d) => d.statusKey === "failed" || d.stuck) .sort((a, b) => a.createdMin - b.createdMin); const pipeline = [ { key: "upload", label: "Upload", count: 1842, tone: "neutral", icon: "upload" }, { key: "quarantine", label: "Quarantine", count: stageCounts.quarantine, tone: "warn", icon: "lock" }, { key: "scan", label: "Malware scan", count: stageCounts.scanning, tone: "info", icon: "scan" }, { key: "promote", label: "Validate · MIME / size", count: stageCounts.promotion, tone: "info", icon: "database" }, { key: "redact", label: "PII scrub", count: stageCounts.redaction, tone: "purple", icon: "shield" }, { key: "ingest", label: "Handoff", count: stageCounts.ingest, tone: "ok", icon: "runs" }, { key: "extract", label: "Extraction", future: true, tone: "neutral", icon: "database" }, ]; const [flow, setFlow] = useState("documents"); const activeFlow = flow === "signals" ? E.SIGNAL_STAGES : pipeline; const flowMeta = flow === "signals" ? { title: "Signal pre-processing pipeline", sub: "Decode → unit normalization → range / plausibility checks → de-identification → time-series store. Continuous telemetry from wearables & smart devices.", foot: "Readings · per min · last 28 min" } : { title: "Pre-processing pipeline", sub: "Sanity & validation, malware scan, MIME / size gating, and PII scrubbing — before structured extraction. Later stages plug in here as they ship.", foot: "Throughput · docs/min · last 28 min" }; return h("div", { className: "view" }, h(PageHead, { title: "Pipeline overview", sub: `Document ingestion across cell ${cell.id} · ${cell.name}`, right: h(Pill, { tone: cell.status === "healthy" ? "ok" : cell.status === "degraded" ? "warn" : "bad" }, cell.status === "healthy" ? "All systems operational" : cell.status === "degraded" ? "Degraded" : "Cell offline"), }), /* KPI row */ h("div", { className: "stat-grid" }, h(Card, { pad: false }, h(Stat, { label: "Ingested today", value: E.fmt ? "" : fmtNum(k.ingestedToday), spark: E.series(1, 16, 40, 10), tone: "ok", icon: "runs" })), h(Card, { pad: false }, h(Stat, { label: "In-flight now", value: fmtNum(k.inFlight), spark: E.series(5, 16, 30, 8), tone: "info", icon: "clock" })), h(Card, { pad: false }, h(Stat, { label: "Failed · 24h", value: fmtNum(k.failed24h), tone: "bad", delta: "needs triage", icon: "alert" })), h(Card, { pad: false }, h(Stat, { label: "Dead-letters", value: fmtNum(k.deadLetters), tone: k.deadLetters ? "bad" : "muted", delta: k.deadLetters ? "in outbox" : "clear", icon: "webhooks" })), h(Card, { pad: false }, h(Stat, { label: "End-to-end p95", value: k.p95, unit: "s", tone: "ok", icon: "activity" })), h(Card, { pad: false }, h(Stat, { label: "Redaction success", value: k.redactionRate, unit: "%", tone: "ok", icon: "shield" })), ), /* Pipeline flow */ h(Card, { title: flowMeta.title, subtitle: flowMeta.sub, actions: h("div", { className: "flow-toggle" }, h("button", { className: "flow-btn" + (flow === "documents" ? " on" : ""), onClick: () => setFlow("documents") }, "Documents"), h("button", { className: "flow-btn" + (flow === "signals" ? " on" : ""), onClick: () => setFlow("signals") }, "Signals")) }, h("div", { className: "pipeflow" }, activeFlow.map((p, i) => h(React.Fragment, { key: p.key }, h("div", { className: "pipenode" + (p.future ? " future" : "") }, h("div", { className: `pipeicon tone-${p.tone}` }, h(Icon, { name: p.icon, size: 18 })), p.future ? h("div", { className: "pipesoon" }, "soon") : h("div", { className: "pipecount" }, fmtNum(p.count)), h("div", { className: "pipelabel" }, p.label)), i < activeFlow.length - 1 && h("div", { className: "pipearrow" + (activeFlow[i + 1].future ? " future" : "") }, h(Icon, { name: "arrowRight", size: 16 }))))), h("div", { className: "pipefoot" }, h(MiniBars, { data: tp, tone: "accent", height: 40 }), h("span", { className: "pipefoot-label" }, flowMeta.foot))), /* Cell network map */ h(Card, { title: "Cell network", subtitle: "Regional cells and live status across jurisdictions", actions: h("div", { className: "cm-legend" }, h("span", { className: "cm-leg" }, h("span", { className: "svc-dot svc-up" }), "Healthy"), h("span", { className: "cm-leg" }, h("span", { className: "svc-dot svc-stale" }), "Degraded"), h("span", { className: "cm-leg" }, h("span", { className: "svc-dot svc-down" }), "Down")) }, h("div", { className: "cm-split" }, h(CellMap, { active: cell.id, onSelect: onSelectCell }), h("div", { className: "cm-aside" }, E.CELLS.map((c) => { const svcs = E.SERVICES[c.id]; const up = svcs.filter((s) => s.up).length; const sd = c.status === "healthy" ? "up" : c.status === "degraded" ? "stale" : "down"; const tone = c.status === "healthy" ? "ok" : c.status === "degraded" ? "warn" : "bad"; const label = c.status === "healthy" ? "Operational" : c.status === "degraded" ? "Degraded" : "Offline"; return h("button", { key: c.id, className: "cm-row" + (c.id === cell.id ? " on" : ""), onClick: () => onSelectCell(c.id) }, h("span", { className: "cm-flag" }, c.flag), h("div", { className: "cm-row-main" }, h("div", { className: "cm-row-id" }, h(Mono, null, c.id), c.id === cell.id && h("span", { className: "cm-active-tag" }, "viewing")), h("div", { className: "dim small" }, c.name, " · ", c.region, " · ", c.jurisdiction.toUpperCase())), h("span", { className: "cm-row-svc dim" }, up, "/", svcs.length), h(Pill, { tone, soft: true }, label)); })))), h(FleetHealth, { onOpenDevice, goto }), h("div", { className: "split-2" }, /* Cell health */ h(Card, { title: "Cell health", subtitle: `${services.filter((s) => s.up).length}/${services.length} services reporting`, actions: h(Button, { variant: "ghost", size: "sm", icon: "refresh" }, "Refresh") }, h("div", { className: "svc-list" }, services.map((s) => h("div", { className: "svc-row", key: s.name }, h("span", { className: `svc-dot svc-${s.status}` }), h("span", { className: "svc-name mono" }, s.name), h("span", { className: "svc-meta" }, s.up ? `${s.p95}ms p95` : s.status === "stale" ? "stale scrape" : "no scrape"), h("span", { className: "svc-meta dim" }, s.up ? `${s.freshness}s` : "—"), h(Pill, { tone: s.status === "up" ? "ok" : s.status === "stale" ? "warn" : "bad", soft: true }, s.status === "up" ? "up" : s.status))))), /* Needs attention */ h(Card, { title: "Needs attention", subtitle: "Failed or stuck documents in this cell", actions: h(Button, { variant: "ghost", size: "sm", onClick: () => goto("documents") }, "All documents") }, attention.length ? h("div", { className: "attn-list" }, attention.map((d) => h("button", { className: "attn-row", key: d.id, onClick: () => onOpenDoc(d) }, h("div", { className: `attn-icon tone-${d.status.tone}` }, h(Icon, { name: d.stuck ? "clock" : "alert", size: 15 })), h("div", { className: "attn-main" }, h("div", { className: "attn-file" }, d.file), h("div", { className: "attn-sub" }, E.tenantName(d.tenant), " · ", d.failReason ? d.failReason.slice(0, 46) + "…" : "awaiting redaction worker")), h(StatusPill, { status: d.status }), h("span", { className: "attn-age" }, fmtAgo(d.createdAt))))) : h(Empty, { icon: "check", title: "No failures", sub: "Everything is flowing cleanly." })))); } /* ======================================================================== */ /* DOCUMENTS */ /* ======================================================================== */ const STATUS_FILTERS = [ { value: "all", label: "All statuses" }, { value: "scanning", label: "Scanning" }, { value: "redacting", label: "Redacting" }, { value: "redaction_pending", label: "Redaction queued" }, { value: "validated", label: "Validated" }, { value: "redacted", label: "Redacted" }, { value: "ingested", label: "Ingested" }, { value: "failed", label: "Failed" }, { value: "expired", label: "Expired" }, ]; function DocumentsView({ cell, onOpenDoc }) { const [status, setStatus] = useState("all"); const [tenant, setTenant] = useState("all"); const [q, setQ] = useState(""); const rows = E.DOCUMENTS.filter((d) => { if (status !== "all" && d.statusKey !== status) return false; if (tenant !== "all" && d.tenant !== tenant) return false; if (q && !(d.id + d.file + d.patient + d.correlationId).toLowerCase().includes(q.toLowerCase())) return false; return true; }); return h("div", { className: "view" }, h(PageHead, { title: "Documents", sub: "Search, inspect, and debug any document in the pipeline" }), 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 document ID, file, patient, correlation…", value: q, onChange: (e) => setQ(e.target.value) })), h(Select, { value: status, onChange: setStatus, options: STATUS_FILTERS }), h(Select, { value: tenant, onChange: setTenant, options: [{ value: "all", label: "All tenants" }, ...E.TENANTS.map((t) => ({ value: t.id, label: t.name }))] }), h("span", { className: "toolbar-count" }, rows.length, " of ", E.DOCUMENTS.length)), h("div", { className: "table-wrap" }, h("table", { className: "table" }, h("thead", null, h("tr", null, ["Document", "Tenant", "Status", "Lifecycle", "Cell", "Size", "Age", ""].map((c, i) => h("th", { key: i, className: i >= 5 && i <= 6 ? "ta-r" : "" }, c)))), h("tbody", null, rows.map((d) => h(DocRow, { 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 filters." })))) ) ) ) ); } function DocRow({ d, onClick }) { const reached = E.stageIndex(d.stage); const failed = !!d.failedAt; 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" }, E.tenantName(d.tenant))), h("td", null, h(StatusPill, { status: d.status })), h("td", null, h(LifecycleBar, { reached, failed, total: E.STAGES_LEN })), h("td", null, h(Mono, null, d.tenant && E.tenantCell(d.tenant))), h("td", { className: "ta-r mono" }, fmtBytes(d.size)), h("td", { className: "ta-r dim" }, fmtAgo(d.createdAt)), h("td", null, h(Icon, { name: "chevronR", size: 15, className: "row-arrow" }))); } function LifecycleBar({ reached, failed, total }) { const pct = Math.round(((reached + 1) / total) * 100); return h("div", { className: "lifebar", title: `${reached + 1}/${total} stages` }, h("div", { className: "lifebar-track" }, h("div", { className: "lifebar-fill", style: { width: pct + "%", background: failed ? "var(--bad)" : pct === 100 ? "var(--ok)" : "var(--info)" } })), h("span", { className: "lifebar-pct" }, failed ? "halted" : pct + "%")); } /* ---- Document drill-down drawer ----------------------------------------- */ function DocumentDrawer({ doc, onClose }) { const [tab, setTab] = useState("timeline"); const toast = useToast(); const [holds, setHolds] = useState({}); useEffect(() => { setTab("timeline"); }, [doc && doc.id]); if (!doc) return null; const legalHold = holds[doc.id] != null ? holds[doc.id] : doc.retention.legalHold; const act = (msg) => toast(msg); const footer = h("div", { className: "drawer-actions" }, h(Button, { variant: "ghost", size: "sm", icon: "copy", onClick: () => { navigator.clipboard?.writeText(doc.id); act("Document ID copied"); } }, "Copy ID"), doc.outbox.some((e) => e.status === "dead_letter") && h(Button, { variant: "default", size: "sm", icon: "refresh", onClick: () => act("Replay queued for dead-letter event") }, "Replay outbox"), h(Button, { variant: legalHold ? "danger-ghost" : "ghost", size: "sm", icon: "lock", onClick: () => { setHolds((x) => ({ ...x, [doc.id]: !legalHold })); act(legalHold ? "Legal hold released" : "Legal hold placed"); } }, legalHold ? "Release hold" : "Place legal hold"), h(Button, { variant: "danger", size: "sm", icon: "trash", onClick: () => act("Erasure request recorded (GDPR Art. 17)") }, "Request erasure")); const tabs = [ { key: "timeline", label: "Lifecycle" }, { key: "audit", label: "Audit", count: doc.timeline.length }, { key: "redaction", label: "Redaction", count: doc.redaction ? doc.redaction.spans : null }, { key: "outbox", label: "Outbox / Kafka", count: doc.outbox.length }, { key: "meta", label: "Metadata" }, ]; return h(Drawer, { open: true, onClose, width: 760, footer, title: doc.file, sub: h("div", { className: "drawer-subline" }, h(Mono, { copy: true }, doc.id), h(StatusPill, { status: doc.status }), legalHold && h(Pill, { tone: "warn" }, "Legal hold")), }, h(Tabs, { tabs, active: tab, onChange: setTab }), h("div", { className: "drawer-tabbody" }, tab === "timeline" && h(TimelineTab, { doc }), tab === "audit" && h(AuditTab, { doc }), tab === "redaction" && h(RedactionTab, { doc }), tab === "outbox" && h(OutboxTab, { doc, act }), tab === "meta" && h(MetaTab, { doc }))); } function TimelineTab({ doc }) { return h("div", { className: "timeline" }, doc.timeline.map((t, i) => h("div", { className: "tl-row", key: i }, h("div", { className: `tl-marker tl-${t.state}` }, h(Icon, { name: t.state === "failed" ? "x" : t.state === "active" ? "dot" : "check", size: 13 })), h("div", { className: "tl-main" }, h("div", { className: "tl-label" }, t.label, h("span", { className: "tl-service mono" }, t.service)), h("div", { className: "tl-time" }, fmtTime(t.at), " · ", fmtAgo(t.at)), t.note && h("div", { className: "tl-note" }, h(Icon, { name: "alert", size: 13 }), t.note)), i < doc.timeline.length - 1 && h("div", { className: "tl-line" }))), doc.stuck && h("div", { className: "tl-hint" }, h(Icon, { name: "clock", size: 14 }), "Document has been awaiting the redaction worker longer than expected — check ", h(Mono, null, "redaction-worker"), " health.")); } function AuditTab({ doc }) { const head = ["Event", "Stage", "Producer", "Status", "Occurred"]; const rows = doc.timeline.map((t, i) => h("tr", { key: i }, h("td", null, h(Mono, null, "document." + t.key)), h("td", null, t.label), h("td", null, h(Mono, { className: "dim" }, t.service)), h("td", null, h(Pill, { tone: t.state === "failed" ? "bad" : "ok", soft: true }, t.state === "failed" ? "error" : "ok")), h("td", { className: "dim" }, fmtTime(t.at)))); return h("div", null, h("div", { className: "audit-banner" }, h(Icon, { name: "shield", size: 14 }), "Append-only · immutable · 10-year retention (MDR + GDPR Art. 30)"), h("div", { className: "table-wrap flush" }, h("table", { className: "table table-tight" }, h("thead", null, h("tr", null, head.map((c) => h("th", { key: c }, c)))), h("tbody", null, rows)))); } function RedactionTab({ doc }) { const r = doc.redaction; if (!r) return h(Empty, { icon: "shield", title: "No redaction job", sub: "This document has not reached the PHI/PII gate yet." }); return h("div", { className: "redaction" }, h("div", { className: "rdx-head" }, h("div", null, h("div", { className: "rdx-job mono" }, r.jobId), h("div", { className: "rdx-policy" }, "Policy ", h(Mono, null, r.policyVersion), " · consumer ", h(Mono, null, r.consumer), " · strategy ", h(Mono, null, r.strategy))), h(Pill, { tone: r.status === "completed" ? "ok" : r.status === "failed" ? "bad" : "purple" }, r.status)), h("div", { className: "engine-grid" }, r.requiredEngines.map((e) => { const ok = r.completedEngines.includes(e); const failed = r.failedEngines.includes(e); return h("div", { className: "engine", key: e }, h("span", { className: `svc-dot svc-${failed ? "down" : ok ? "up" : "stale"}` }), h("span", { className: "engine-name mono" }, e), h("span", { className: "engine-state" }, failed ? "failed" : ok ? "passed" : "pending")); })), r.status === "failed" ? h("div", { className: "rdx-fail" }, h(Icon, { name: "alert", size: 15 }), "Fail-closed: ", doc.failReason) : h("div", null, h("div", { className: "rdx-spans" }, h("strong", null, r.spans), " PHI/PII spans detected and ", r.strategy, "d"), h("div", { className: "entity-grid" }, Object.entries(r.entityCounts).filter(([, v]) => v > 0).map(([k, v]) => h("div", { className: "entity", key: k }, h("span", { className: "entity-type mono" }, k), h("span", { className: "entity-count" }, v)))))); } function OutboxTab({ doc, act }) { const head = ["Event type", "Topic / target", "Status", "Attempts", "At", ""]; const rows = doc.outbox.map((e, i) => h("tr", { key: i }, h("td", null, h(Mono, null, e.type)), h("td", null, h(Mono, { className: "dim" }, e.topic)), h("td", null, h(Pill, { tone: e.status === "published" ? "ok" : e.status === "dead_letter" ? "bad" : "warn", soft: true }, e.status.replace("_", "-"))), h("td", { className: "ta-c mono" }, e.attempts), h("td", { className: "dim" }, fmtTime(e.at)), h("td", null, (e.status === "dead_letter" || e.status === "retrying") && h(Button, { variant: "ghost", size: "sm", onClick: () => act("Replay requested for " + e.type) }, "Replay")))); return h("div", null, h("div", { className: "table-wrap flush" }, h("table", { className: "table table-tight" }, h("thead", null, h("tr", null, head.map((c) => h("th", { key: c }, c)))), h("tbody", null, rows))), h("div", { className: "sql-hint" }, h("span", null, "Document-scoped triage SQL"), h(Mono, { copy: true, className: "sql" }, `SELECT * FROM outbox_events WHERE payload_document_id = '${doc.id}' ORDER BY created_at;`))); } function MetaTab({ doc }) { const rows = [ ["Document ID", doc.id, true], ["Correlation ID", doc.correlationId, true], ["Tenant", E.tenantName(doc.tenant) + " (" + doc.tenant + ")"], ["Patient", doc.patient + " (anonymized)"], ["Initiated by", doc.user], ["Cell", E.tenantCell(doc.tenant)], ["Declared type", doc.mime], ["Size", fmtBytes(doc.size)], ["SHA-256", doc.sha256 + "…", true], ["Object state", doc.objectState], ["Retention policy", doc.retention.policy], ["Retention basis", doc.retention.basis], ["Legal hold", doc.retention.legalHold ? "Yes" : "No"], ["Created", fmtTime(doc.createdAt) + " · " + fmtDate(doc.createdAt)], ]; return h("div", { className: "meta-grid" }, rows.map(([k, v, mono], i) => h("div", { className: "meta-row", key: i }, h("span", { className: "meta-k" }, k), h("span", { className: "meta-v" }, mono ? h(Mono, { copy: true }, v) : v)))); } /* ======================================================================== */ /* INGESTION RUNS (live / SSE simulation) */ /* ======================================================================== */ function RunsView({ cell, onOpenDoc }) { const [feed, setFeed] = useState([]); const [paused, setPaused] = useState(false); const liveRun = E.RUNS.find((r) => r.status === "live"); useEffect(() => { if (paused) return; const samples = [ ["doc_8f3ac21b", "ingest.start.v1", "ok"], ["doc_5c0d77e9", "document.redacting", "purple"], ["doc_1a99be40", "document.status.v1", "info"], ["doc_e7710b54", "document.redacting", "purple"], ["doc_3d2e6a55", "document.redacted", "ok"], ["doc_4f8a1c63", "redact.document.v1 retry", "warn"], ["doc_b401ff7c", "scan_clean", "info"], ["doc_9c34d771", "document.validated", "info"], ]; const id = setInterval(() => { const s = samples[Math.floor(Math.random() * samples.length)]; setFeed((f) => [{ id: Math.random().toString(36).slice(2), doc: s[0], ev: s[1], tone: s[2], at: new Date() }, ...f].slice(0, 14)); }, 1400); return () => clearInterval(id); }, [paused]); return h("div", { className: "view" }, h(PageHead, { title: "Ingestion runs", sub: "Batch and streaming ingestion runs with live event streams" }), h("div", { className: "split-runs" }, h("div", { className: "runs-col" }, E.RUNS.map((r) => h(RunCard, { key: r.id, run: r, onOpenDoc }))), h(Card, { className: "live-card", title: "Live event stream", subtitle: liveRun ? `SSE · /v1/ingestion-runs/${liveRun.id}/events` : "no live run", actions: h(Button, { variant: "ghost", size: "sm", icon: paused ? "play" : "pause", onClick: () => setPaused((p) => !p) }, paused ? "Resume" : "Pause") }, h("div", { className: "sse-feed" }, feed.length === 0 && h("div", { className: "sse-wait" }, h(Icon, { name: "spinner", size: 16, className: "spin" }), "Waiting for events…"), feed.map((f) => h("div", { className: "sse-row", key: f.id }, h("span", { className: "sse-time mono" }, fmtTime(f.at)), h("span", { className: `sse-tone tone-${f.tone}` }), h(Mono, { className: "sse-ev" }, f.ev), h(Mono, { className: "sse-doc dim" }, f.doc))))))); } function RunCard({ run, onOpenDoc }) { const [open, setOpen] = useState(run.status === "live"); const a = run.agg; const docs = E.DOCUMENTS.filter((d) => d.run === run.id); return h(Card, { pad: false, className: "run-card" }, h("button", { className: "run-head", onClick: () => setOpen((o) => !o) }, h(Icon, { name: open ? "chevron" : "chevronR", size: 15, className: "run-chev" }), h("div", { className: "run-id" }, h("span", { className: "run-label" }, run.label), h(Mono, { className: "dim" }, run.id, " · ", E.tenantName(run.tenant))), h("div", { className: "run-stats" }, h("span", { className: "run-stat" }, h("strong", null, a.done), " done"), a.inFlight > 0 && h("span", { className: "run-stat info" }, h("strong", null, a.inFlight), " in-flight"), a.failed > 0 && h("span", { className: "run-stat bad" }, h("strong", null, a.failed), " failed")), run.status === "live" ? h(Pill, { tone: "info" }, "live") : h(Pill, { tone: "ok", soft: true }, "completed")), h("div", { className: "run-progress" }, h(Bar, { height: 6, segments: [ { value: a.done, tone: "ok", label: "done" }, { value: a.inFlight, tone: "info", label: "in-flight" }, { value: a.failed, tone: "bad", label: "failed" }, ] })), open && h("div", { className: "run-docs" }, docs.map((d) => h("button", { className: "run-doc", key: d.id, onClick: () => onOpenDoc(d) }, h("span", { className: "run-doc-file" }, d.file), h(StatusPill, { status: d.status }), h("span", { className: "run-doc-age dim" }, fmtAgo(d.createdAt)))), !docs.length && h("div", { className: "run-empty dim" }, "No documents recorded for this run in mock data."))); } /* ======================================================================== */ /* UPLOAD */ /* ======================================================================== */ function UploadView({ cell }) { const toast = useToast(); const [tenant, setTenant] = useState(E.TENANTS[0].id); const users = E.USERS.filter((u) => u.tenant === tenant); const [user, setUser] = useState(users[0] ? users[0].id : ""); const [patient, setPatient] = useState(""); const [file, setFile] = useState(null); const [drag, setDrag] = useState(false); const [jobs, setJobs] = useState([]); const fileRef = useRef(); useEffect(() => { const u = E.USERS.filter((x) => x.tenant === tenant); setUser(u[0] ? u[0].id : ""); }, [tenant]); const pick = (f) => { if (!f) return; setFile({ name: f.name, size: f.size || Math.round(200000 + Math.random() * 4000000), type: f.type || "application/pdf" }); }; const STEPS = ["Initiate", "Presigned PUT", "Quarantine", "Scan", "Promote", "Redact", "Ingest"]; const submit = () => { if (!file) { toast("Choose a file first", "bad"); return; } const jid = "doc_" + Math.random().toString(16).slice(2, 10); const job = { id: jid, file: file.name, tenant, user, patient: patient || "pt_" + Math.random().toString(16).slice(2, 6), step: 0, done: false, failed: false }; setJobs((j) => [job, ...j]); toast("Upload initiated · " + jid); let step = 0; const adv = () => { step++; setJobs((js) => js.map((x) => x.id === jid ? { ...x, step, done: step >= STEPS.length } : x)); if (step < STEPS.length) setTimeout(adv, 750 + Math.random() * 700); else toast("Document ingested · " + jid, "ok"); }; setTimeout(adv, 700); setFile(null); setPatient(""); }; return h("div", { className: "view" }, h(PageHead, { title: "Upload documents", sub: "Initiate an ingestion on behalf of a tenant user — the same path the public API drives" }), h("div", { className: "split-upload" }, h(Card, { title: "New upload" }, h("div", { className: "upload-form" }, h(Field, { label: "Tenant" }, h(Select, { value: tenant, onChange: setTenant, options: E.TENANTS.map((t) => ({ value: t.id, label: t.name })) })), h(Field, { label: "On behalf of user" }, h(Select, { value: user, onChange: setUser, options: users.map((u) => ({ value: u.id, label: u.name + " · " + u.role })) })), h(Field, { label: "Patient reference", hint: "Pseudonymous — never a real identifier" }, h("input", { className: "input", placeholder: "pt_… (blank to auto-generate)", value: patient, onChange: (e) => setPatient(e.target.value) })), h("div", { className: "dropzone" + (drag ? " drag" : "") + (file ? " filled" : ""), onDragOver: (e) => { e.preventDefault(); setDrag(true); }, onDragLeave: () => setDrag(false), onDrop: (e) => { e.preventDefault(); setDrag(false); pick(e.dataTransfer.files[0]); }, onClick: () => fileRef.current.click(), }, h("input", { ref: fileRef, type: "file", hidden: true, onChange: (e) => pick(e.target.files[0]) }), file ? h("div", { className: "dz-file" }, h(Icon, { name: "documents", size: 20 }), h("div", null, h("div", { className: "dz-name" }, file.name), h("div", { className: "dz-size dim" }, fmtBytes(file.size), " · ", file.type))) : h("div", { className: "dz-empty" }, h(Icon, { name: "upload", size: 22 }), h("div", null, h("strong", null, "Drop a file"), " or click to browse"), h("div", { className: "dim" }, "PDF, PNG, JPEG, HEIC, TIFF, TXT, JSON, DOCX"))), h(Button, { variant: "primary", icon: "upload", onClick: submit }, "Initiate ingestion"), h("div", { className: "upload-note" }, h(Icon, { name: "shield", size: 13 }), "Bytes upload directly to the cell-local quarantine bucket via presigned URL — never proxied through the API tier.")), ), h(Card, { title: "Recent uploads", subtitle: "Live progress through the DIP-1 pipeline" }, jobs.length === 0 ? h(Empty, { icon: "upload", title: "No uploads yet", sub: "Submitted uploads will stream their progress here." }) : h("div", { className: "job-list" }, jobs.map((j) => h(UploadJob, { key: j.id, job: j, steps: STEPS })))))); } function UploadJob({ job, steps }) { return h("div", { className: "job" }, h("div", { className: "job-head" }, h("span", { className: "job-file" }, job.file), h(Mono, { className: "dim" }, job.id), job.done ? h(Pill, { tone: "ok" }, "ingested") : h(Pill, { tone: "info" }, steps[Math.min(job.step, steps.length - 1)])), h("div", { className: "job-steps" }, steps.map((s, i) => h("div", { className: "job-step" + (i < job.step ? " done" : i === job.step ? " active" : ""), key: s }, h("span", { className: "job-dot" }, i < job.step ? h(Icon, { name: "check", size: 11 }) : i === job.step ? h(Icon, { name: "spinner", size: 11, className: "spin" }) : null), h("span", { className: "job-step-label" }, s))))); } /* ---- Geographic cell map ------------------------------------------------- */ function CellMap({ active, onSelect }) { const box = { lonMin: -12, lonMax: 62, latMin: 18, latMax: 58 }; const proj = (c) => ({ x: ((c.lon - box.lonMin) / (box.lonMax - box.lonMin)) * 100, y: ((box.latMax - c.lat) / (box.latMax - box.latMin)) * 100, }); const dir = { "eu-be": "sw", "eu-nl": "ne", "eu-de": "ne", "ae-du": "nw" }; return h("div", { className: "cellmap" }, h("div", { className: "cm-grid" }), h("span", { className: "cm-region", style: { left: "21%", top: "5%" } }, "EU West"), h("span", { className: "cm-region", style: { left: "40%", top: "4%" } }, "EU Central"), h("span", { className: "cm-region", style: { left: "80%", top: "92%" } }, "Middle East"), E.CELLS.map((c) => { const p = proj(c); const st = c.status === "healthy" ? "up" : c.status === "degraded" ? "stale" : "down"; return h("button", { key: c.id, className: "cm-marker cm-" + st + (c.id === active ? " on" : ""), style: { left: p.x + "%", top: p.y + "%" }, onClick: () => onSelect(c.id), title: c.name, }, h("span", { className: "cm-pulse" }), h("span", { className: "cm-pin" }), h("span", { className: "cm-label cm-" + dir[c.id] }, c.flag, " ", h(Mono, null, c.id))); })); } /* ---- Device fleet health (overview widget) ------------------------------ */ function FleetHealth({ onOpenDevice, goto }) { const k = E.streamKpis(); const byStatus = {}; E.DEVICES.forEach((d) => (byStatus[d.status] = (byStatus[d.status] || 0) + 1)); const segs = [ { key: "connected", tone: "ok", label: "Connected" }, { key: "syncing", tone: "info", label: "Syncing" }, { key: "stale", tone: "warn", label: "Stale" }, { key: "token_expired", tone: "warn", label: "Token expired" }, { key: "disconnected", tone: "bad", label: "Disconnected" }, ].map((s) => ({ ...s, value: byStatus[s.key] || 0 })).filter((s) => s.value > 0); const attention = E.DEVICES.filter((d) => ["stale", "token_expired", "disconnected"].includes(d.status)); return h(Card, { title: "Device fleet health", subtitle: `${k.activeDevices} of ${k.totalDevices} devices streaming`, actions: h(Button, { variant: "ghost", size: "sm", icon: "external", onClick: () => goto("streams") }, "Streams") }, h("div", { className: "fleet-split" }, h("div", { className: "fleet-left" }, h("div", { className: "fleet-readings" }, h("div", null, h("div", { className: "fleet-big" }, fmtNum(k.readingsPerMin)), h("div", { className: "dim small" }, "readings / min")), h(Sparkline, { data: E.series(4, 18, 30, 9), tone: "info", height: 42 })), h("div", { className: "fleet-dist" }, h(Bar, { height: 9, segments: segs }), h("div", { className: "fleet-legend" }, segs.map((s) => h("span", { className: "fleet-leg", key: s.key }, h("span", { className: "fleet-dot", style: { background: `var(--${s.tone})` } }), s.label, h("strong", null, s.value)))))), h("div", { className: "fleet-attn" }, h("div", { className: "fleet-attn-head" }, "Needs attention", h("span", { className: "fleet-attn-count" }, attention.length)), attention.length ? h("div", { className: "fleet-attn-list" }, attention.map((d) => { const st = E.DEVICE_STATUS[d.status]; return h("button", { className: "fleet-row", key: d.id, onClick: () => onOpenDevice(d) }, h("span", { className: `svc-dot svc-${st.dot}` }), h("div", { className: "fleet-row-main" }, h("div", { className: "fleet-row-model" }, d.model), h("div", { className: "dim small" }, E.sourceName(d.source), " · ", E.tenantName(d.tenant))), h(Pill, { tone: st.tone, soft: true }, st.label), h("span", { className: "dim small fleet-row-age" }, fmtAgo(d.lastSync))); })) : h(Empty, { icon: "check", title: "All devices healthy", sub: "Every connected device is streaming cleanly." })))); } /* ---- shared page header -------------------------------------------------- */ function PageHead({ title, sub, right }) { return h("div", { className: "pagehead" }, h("div", null, h("h1", { className: "page-title" }, title), sub && h("p", { className: "page-sub" }, sub)), right && h("div", { className: "pagehead-right" }, right)); } Object.assign(window, { OverviewView, DocumentsView, RunsView, UploadView, PageHead, DocumentDrawer });