import { useEffect, useRef, useState } from "react"; import { AlertCircle, FlaskConical, Loader2 } from "lucide-react"; import { Alert, AlertDescription } from "../../../components/ui/alert.tsx"; import { Button } from "../../../components/ui/button.tsx"; import { EmptyState } from "../../../components/ui/empty-state.tsx"; import { useStaffPermissionServiceContext } from "../../../services"; import { newOperationId, offers, useHeldLabResults, type HeldLabResult } from "../../../hooks/useHeldLabResults.ts"; import type { ListOwner } from "../../../hooks/useListOwner.ts"; import { relayServerFailure } from "../../../utils/fetchFailureMessage"; import { formatDateTime } from "../../../utils/dateUtils"; import { heldReasonWords, labStatusWords, orderReadWords } from "./heldLabResultWords.ts"; import MatchHeldResultDialog from "./MatchHeldResultDialog.tsx"; import SetAsideHeldResultDialog from "./SetAsideHeldResultDialog.tsx"; import "./HeldLabResultsPage.css"; // Lab results that came back and could not be matched to an order on their own, each shown as the laboratory sent it: // what test, its own status and time, who it is about, and why it waits. Never its values, which belong to the order // it is matched to and reach a patient only when released. Seeing the list needs `view`; matching or setting aside // needs `manage`, the split the API makes. const HeldLabResultsPage = () => { const { canManage } = useStaffPermissionServiceContext(); const mayAct = canManage("diagnostics"); const { items, loading, error, more, reload, loadMore, candidates, resolve, dismiss, readAgain, owner, authority, opening, live } = useHeldLabResults(); const [matching, setMatching] = useState(); const [settingAside, setSettingAside] = useState(); // Read again, per item and per opening: one operation kept across a retry of the same ask, what is in flight, and // what was answered. A request the server names as unconfirmed is retried under its own operation after a reload. const operations = useRef(new WeakMap>()); const operationsOf = (of: ListOwner) => { let held = operations.current.get(of); if (!held) operations.current.set(of, (held = new Map())); return held; }; const [asking, setAsking] = useState>(new Set()); const [answers, setAnswers] = useState>({}); // Another viewer, practice or authority: nothing asked or answered under the last one is shown. useEffect(() => { setAsking(new Set()); setAnswers({}); }, [owner, authority]); const askAgain = async (item: HeldLabResult) => { if (asking.has(item.id)) return; // Owned by this setup, viewer and authority: answered, failed or finished only while all three still stand. const of = opening(); const here = () => live(of); const held = operationsOf(of.owner); const operationId = item.pendingRead?.operationId ?? held.get(item.id) ?? newOperationId(); held.set(item.id, operationId); setAsking((now) => new Set(now).add(item.id)); try { const answer = await readAgain(item.id, operationId, of); // Only an unconfirmed ask keeps its operation, so its retry is recognised, not repeated. if (answer?.queued !== "unknown") held.delete(item.id); if (!here()) return; setAnswers((now) => ({ ...now, [item.id]: { failed: answer?.queued === "unknown", words: answer?.message ?? "Asked to be read again." } })); } catch (failure) { if (!here()) return; setAnswers((now) => ({ ...now, [item.id]: { failed: true, words: relayServerFailure(failure, "We could not confirm the request was sent. Try again.") } })); } finally { if (here()) setAsking((now) => { const next = new Set(now); next.delete(item.id); return next; }); } }; // An open match or set-aside closes as soon as `manage` is taken away; the hook refuses at send as well. useEffect(() => { if (mayAct) return; setMatching(undefined); setSettingAside(undefined); }, [mayAct]); return (

Laboratory results to match

These came back from a laboratory this practice orders from electronically, and could not be matched to an order on their own. Match each to the order it answers, or set it aside.

{error ? ( {error} ) : null} {loading && items.length === 0 ? (

Loading…

) : !error && items.length === 0 ? ( ) : (
    {items.map((item) => (
  • {item.kind === "order-read" ? (

    Results for an order could not be searched for

    {orderReadWords(item.reason)}

    Since {formatDateTime(item.receivedAt)}

    ) : (

    {item.report?.code ?? (item.unreadable ? "Could not be read back as received" : "Not named")}

    {item.unreadable ? (

    What the laboratory sent is kept but could not be read back as received, so it is not shown.

    ) : (

    {[labStatusWords(item.report?.status), item.report?.issued ? `issued ${formatDateTime(item.report.issued)}` : undefined, item.report?.subject?.display ? `at the laboratory: ${item.report.subject.display}` : undefined] .filter(Boolean).join(" · ")}

    )}

    {heldReasonWords(item.heldReason)}

    Received {formatDateTime(item.receivedAt)}

    {item.readAgain === "queued" ?

    Asked to be read again. It stays here until the laboratory answers.

    : null}
    )} {answers[item.id] ? (

    {answers[item.id].words}

    ) : null} {mayAct ? (
    {offers(item, "resolve") ? : null} {offers(item, "read-again") ? ( ) : null} {offers(item, "dismiss") ? : null}
    ) : null}
  • ))}
)} {more ? ( ) : null} setMatching(undefined)} onDone={() => setMatching(undefined)} candidates={candidates} resolve={resolve} /> setSettingAside(undefined)} onDone={() => setSettingAside(undefined)} dismiss={dismiss} />
); }; export default HeldLabResultsPage;