import { useState } from "react"; import { Link } from "react-router-dom"; import { AlertCircle, ChevronDown, ShieldCheck, TriangleAlert } from "lucide-react"; import { Alert, AlertDescription } from "../../../components/ui/alert.tsx"; import { Button } from "../../../components/ui/button.tsx"; import { useStaffPermissionServiceContext } from "../../../services"; import { useCapabilityServiceContext } from "../../../services/CapabilityService.tsx"; import { useAdmission } from "../../../hooks/useAdmission.ts"; import { useQualityControl, type ControlRun, type OverdueControl, type UncheckedControl } from "../../../hooks/useQualityControl.ts"; import { ControlRunDialog } from "./ControlRunDialog.tsx"; import { capitalized, dayAndTime } from "./benchTime.ts"; import "./QualityControlStrip.css"; // Shown where the work is. A settled practice is told so, because silence here would read as "controls // are fine" to somebody about to release a result; and nothing is dismissed, a failure is only answered. type Pending = | { kind: "run"; tests: { orderableId: string; test?: string }[] } | { kind: "correction"; runId: string; test?: string; lot?: string; ranAt?: string }; interface TestNeedingAttention { key: string; test?: string; failures: ControlRun[]; overdue?: OverdueControl; /** Run here with no catalogue test to match a control to, so its controls cannot be current. */ unchecked?: boolean; } /** One row per test, so a test that failed and is also owed a run today is said once. */ const byTest = (failures: ControlRun[], overdue: OverdueControl[], unchecked: UncheckedControl[]): TestNeedingAttention[] => { const tests = new Map(); const entry = (key: string, test?: string) => { const found = tests.get(key) ?? { key, test, failures: [] }; tests.set(key, found); return found; }; for (const run of failures) entry(run.orderableId ?? run.test ?? run.runId ?? "", run.test).failures.push(run); for (const row of overdue) entry(row.orderableId ?? row.test ?? "", row.test).overdue = row; for (const row of unchecked) entry(`unchecked:${row.test ?? ""}`, row.test).unchecked = true; return [...tests.values()]; }; const whenLast = (row: OverdueControl) => { if (!row.lastRunAt) return "Never run"; const when = new Date(row.lastRunAt).toLocaleDateString("en-US"); if (row.lastOutcome === "fail") return `Last failed ${when}`; if (row.lastOutcome === "pass") return `Last passed ${when}`; return `Last run ${when}, outcome not recorded`; }; /** Today's newest run of the test, from the server's list of today's runs (newest first). */ const latestToday = (row: OverdueControl, today: ControlRun[]) => row.orderableId ? today.find((run) => run.orderableId === row.orderableId) : undefined; // A test stays owed while today's latest run is not a pass, so a run today is never "none". const owedLine = (row: OverdueControl, today: ControlRun[]) => { if (!row.orderableId) return "A passing control is still needed for today's work."; const latest = latestToday(row, today); if (!latest) return "No control recorded today"; if (latest.outcome === "fail" && latest.correctiveAction) { return "Today's latest control failed and its corrective action is recorded. A passing control is still needed for today's work."; } if (latest.outcome === "fail") return "Today's latest control failed. A passing control is still needed for today's work."; if (!latest.outcome) return "Today's latest control has no recorded outcome. A passing control is still needed for today's work."; return "A passing control is still needed for today's work."; }; const failureLine = (run: ControlRun) => { const when = dayAndTime(run.ranAt); return ["Control failed", run.lot ? `Lot ${run.lot}` : undefined, when ? capitalized(when) : "Run not dated"] .filter(Boolean) .join(" · "); }; export const QualityControlStrip = () => { const { quality, loading, error, reload, logRun, recordCorrectiveAction } = useQualityControl(); // Both writes are gated at diagnostics manage; a viewer sees the warning without the acts. const permissions = useStaffPermissionServiceContext(); const mayWork = permissions.canManage("diagnostics"); // The server's own answer for each write can still say no; an unanswered capability read narrows nothing. const { mayCall } = useCapabilityServiceContext(); const mayLogRun = mayWork && mayCall("POST", "/diagnostic-quality-control") !== false; const mayCorrect = mayWork && mayCall("POST", "/diagnostic-quality-control/*/correction") !== false; // An open run or correction follows its own write's answer, up to the moment it is sent. const admitRun = useAdmission(mayLogRun); const admitCorrection = useAdmission(mayCorrect); // The catalogue is practice configuration, so only those who may change it are sent there. const mayConfigure = permissions.canManage("organization"); const [pending, setPending] = useState(); // The load says nothing about the controls either way, so it must not read as settled. if (loading) return null; if (error) { return ( {error} ); } const tests = byTest(quality.openFailures, quality.overdue, quality.unchecked); // The one loose run action covers every test owed a control; its dialog names the test chosen. const runnable = tests.flatMap((entry) => { const orderableId = entry.overdue?.orderableId ?? entry.failures[0]?.orderableId; return orderableId ? [{ orderableId, test: entry.test }] : []; }); return (
{tests.length === 0 ? (

) : (
{tests.map((entry) => (
{entry.test ?? "Test not recorded"} {entry.failures.map((run) => {failureLine(run)})} {entry.overdue ? {owedLine(entry.overdue, quality.today)} : null} {entry.unchecked ? ( Not in the test catalogue, so no control can be matched to it ) : null} {entry.overdue && entry.failures.length === 0 && !latestToday(entry.overdue, quality.today) ? ( {whenLast(entry.overdue)} ) : null}
{mayCorrect && entry.failures.map((run) => ( ))}
))} {mayLogRun && runnable.length ? ( ) : null} {tests.some((entry) => entry.unchecked) ? (

{"Controls are matched to bench work through the practice's test catalogue, so a test missing from it has no control to show. "} {mayConfigure ? Add it in Lab and imaging setup : "A practice administrator can add it in Lab and imaging setup."}

) : null}

Review the control record. This warning does not automatically block result release.

)} setPending(undefined)} logRun={(run) => logRun(run, admitRun)} recordCorrectiveAction={(runId, action) => recordCorrectiveAction(runId, action, admitCorrection)} withdrawn={pending ? !(pending.kind === "run" ? mayLogRun : mayCorrect) : false} />
); }; export type { Pending };