import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { AlertCircle, Loader2, TriangleAlert } from "lucide-react"; import { Alert, AlertDescription } from "../../../components/ui/alert.tsx"; import { RowPriority, RowStatus } from "../../../components/ui/row-meta.tsx"; import { Button } from "../../../components/ui/button.tsx"; import { Input } from "../../../components/ui/input.tsx"; import { Label } from "../../../components/ui/label.tsx"; import { useDiagnosticOrderQueue, type DiagnosticOrderQueueRow } from "../../../hooks/useDiagnosticOrderQueue.ts"; import SendOutDialog from "./SendOutDialog.tsx"; import { useSendOutDiagnosticOrder } from "../../../hooks/useSendOutDiagnosticOrder.ts"; import ReleaseResultDialog from "./ReleaseResultDialog.tsx"; import AddAddendumDialog from "./AddAddendumDialog.tsx"; import FaxResultDialog from "./FaxResultDialog.tsx"; import ReturnReportDialog from "./ReturnReportDialog.tsx"; import ScheduleStudyPage from "./ScheduleStudyPage.tsx"; import ArchiveConfirmModal from "../../../components/ArchiveConfirmModal/ArchiveConfirmModal"; import type { ArchiveReason } from "../../../utils/archivedResource"; import { describeDrill, diagnosticDrillFrom, matchesDrill } from "./diagnosticDrill"; import { relayServerFailure, ownFailureSentence } from "../../../utils/fetchFailureMessage"; import { useImagingUploadService } from "../../../services/ImagingUploadService.tsx"; import { useOptionalAuthServiceContext } from "../../../services/AuthService.tsx"; import { authSessionId } from "../../../services/authStorage"; import { SaveRetiredError, isSaveRetired, isSaveTimedOut, outcomeUnknown, useSaveScope } from "../../../utils/saveOperation"; import { useRowOwnedSaves, type RowSave } from "../../../utils/rowOwnedSave"; import EnterTestResultDialog from "./EnterTestResultDialog.tsx"; import UnreviewedResults from "./UnreviewedResults.tsx"; import RecordRefusalDialog from "./RecordRefusalDialog.tsx"; import WithdrawQueueOrderDialog, { UnconfirmedWithdrawals } from "./WithdrawQueueOrderDialog.tsx"; import RecollectionNeededDialog from "./RecollectionNeededDialog.tsx"; import { QueueOrderDialog, QueueResultDialog } from "./QueueOrderDialogs.tsx"; import { ImagingGroup, ImagingRow } from "./ImagingWorklist.tsx"; import UploadOrderImagesDialog from "./UploadOrderImagesDialog.tsx"; import SendForReadingDialog from "./SendForReadingDialog.tsx"; import OutsideReadArrivalDialog from "./OutsideReadArrivalDialog.tsx"; import { useImagingReadHandoff, type ImagingReader } from "../../../hooks/useImagingReadHandoff.ts"; import { useRecordOrderRefusal } from "../../../hooks/useRecordOrderRefusal.ts"; import { useStaffPermissionServiceContext } from "../../../services"; import { isDiagnosticOrderState, diagnosticsQueueLabel, nextStatesFor, lensFor, type DiagnosticOrderState, } from "../../../utils/diagnosticOrderQueue.ts"; import { deliveryLine, panelStanding, returnLine, sortQueue, stateLabel } from "./queueLines.ts"; import "../BenchPage/BenchPage.css"; // THE LAB QUEUE -- every order this practice is working, across patients. // // The practitioner places the order; staff carry it out. This screen is the EXECUTION half, and // nothing on it places an order. Each row carries its one next step; everything else that can be // done to an order opens with the order, so a row is never a strip of buttons. const joined = (...parts: (string | undefined)[]) => parts.filter(Boolean).join(" · "); const orderedOn = (iso?: string) => { if (!iso) return undefined; const at = new Date(iso); return Number.isNaN(at.getTime()) ? undefined : `Ordered ${at.toLocaleDateString("en-US", { month: "short", day: "numeric" })}`; }; const timeOf = (iso?: string) => { if (!iso) return undefined; const at = new Date(iso); if (Number.isNaN(at.getTime())) return undefined; const time = at.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }); return `${at.toLocaleDateString("en-US", { month: "short", day: "numeric" })}, ${time}`; }; /** Where a study stands in the department: booked where and when, who performed it, what images are filed. */ const imagingLines = (row: DiagnosticOrderQueueRow, importing: boolean): string[] => { if (row.kind !== "imaging") return []; const lines = [joined(row.modality, row.room, timeOf(row.scheduledFor))]; if (row.performed) { lines.push(joined(`Performed ${timeOf(row.performed.at) ?? ""}`.trim(), row.performed.by ? `by ${row.performed.by}` : undefined)); } // An uncounted study says nothing; only a count of zero is "no images". if (row.images) { const { studies, instances } = row.images; lines.push(instances ? `${instances} ${instances === 1 ? "image" : "images"} in ${studies} ${studies === 1 ? "study" : "studies"}` : importing ? "Images uploaded, still being filed" : "No images yet"); } return lines.filter(Boolean); }; /** Received here and not sent anywhere: the specimen is on this laboratory's bench. */ const onTheBench = (row: DiagnosticOrderQueueRow) => row.kind === "laboratory" && Boolean(row.accession) && !row.sentOut && !row.akashicRoute && !row.awaitingRelease; // Refused at receipt and waiting for the replacement tube: the order is still collected, the tube is not. const awaitingRedraw = (row: DiagnosticOrderQueueRow) => row.kind === "laboratory" && row.state === "collected" && !row.accession && row.rejection?.disposition === "redraw"; const isUrgent = (row: DiagnosticOrderQueueRow) => row.priority === "stat" || row.priority === "urgent"; const matches = (row: DiagnosticOrderQueueRow, query: string) => [row.patientDisplay, row.test, row.accession, row.theirOrderNumber, row.fromAkashic?.theirOrderId] .some((value) => value?.toLowerCase().includes(query)); const panelLine = (of: number) => `Ordered with ${of - 1} other ${of === 2 ? "test" : "tests"} — one draw`; const QueueRow = ({ row, panelOf, settled, importing, action }: { row: DiagnosticOrderQueueRow; panelOf: number; settled: boolean; importing: boolean; action: React.ReactNode; }) => (
{row.accession ? {row.accession} : null}
{row.test ?? "Test not named"}
{/* The patient, because whoever draws a specimen has to label the tube. Not a route into the record. One draw serves the whole panel; the tests stay separate rows because they are worked separately. */} {joined(row.patientDisplay ?? "Patient not named", orderedOn(row.authoredOn), panelOf > 1 ? panelLine(panelOf) : undefined)} {/* State is a word, never a pill; released is never implied to be reviewed. */} {awaitingRedraw(row) && !settled ? ( Recollection needed ) : row.refusal && !settled ? ( ) : {settled ? (row.state === "reviewed" ? "Reviewed" : "Not yet reviewed") : row.awaitingRelease ? "Awaiting release" : onTheBench(row) ? "On the bench" : stateLabel(row.state)}} {imagingLines(row, importing).map((line) => {line})} {row.fromAkashic ? ( {joined(`From ${row.fromOutside ?? "the sending practice"}`, row.fromAkashic.orderedBy, `Sender’s order ${row.fromAkashic.theirOrderId}`)} ) : row.fromOutside ? ( {joined(`From ${row.fromOutside}`, row.theirOrderNumber ? `Sender’s order ${row.theirOrderNumber}` : undefined)} ) : null} {deliveryLine(row) ? {deliveryLine(row)} : null} {returnLine(row) ? {returnLine(row)} : null} {/* Work stopped dead, and the state cannot say so: the order is still sent because it was. */} {awaitingRedraw(row) ? ( {"Last specimen rejected: " + (row.rejection?.reason ?? "no reason recorded")} ) : null} {row.refusal ? {row.refusal.reason ?? "The laboratory refused this order"} : null}
{action}
); /** An imaging department works a study in the order it happens, so its open work is grouped by stage. */ const IMAGING_STAGES: { state: DiagnosticOrderState; title: string; empty: string }[] = [ { state: "ordered", title: "To schedule", empty: "No study is waiting to be scheduled." }, { state: "scheduled", title: "Scheduled", empty: "No study is scheduled." }, { state: "performed", title: "Performed", empty: "No performed study is waiting on its result." }, ]; /** The stage a row moves on to from the list itself, and the words for that move. */ const DIRECT_MOVE: Partial> = { ordered: { to: "scheduled", label: "Schedule" }, scheduled: { to: "performed", label: "Mark performed" }, }; /** A section always states its count, so a zero reads as settled rather than as a failed load. */ const Group = ({ title, rows, empty, searching, children }: { title: string; rows: DiagnosticOrderQueueRow[]; empty: string; searching: boolean; children: (row: DiagnosticOrderQueueRow) => React.ReactNode; }) => (

{title}

{`· ${rows.length}`}
{rows.length ?
{rows.map(children)}
: searching ? null :

{empty}

}
); /** A performed, imaged, unreported study no reader has: it may go out, to a reader on Akashic or not. */ const sendsToBeRead = (row?: DiagnosticOrderQueueRow): boolean => { if (!row?.orderId || row.kind !== "imaging" || row.state !== "performed") return false; if ((row.images?.instances ?? 0) === 0 || row.resultOnFile || row.readFor) return false; if (row.outsideRead && row.outsideRead.confirmation?.outcome !== "not-received") return false; return !row.readElsewhere || Boolean(row.readElsewhere.declinedAt && !row.readElsewhere.returnedAt); }; /** The one upload a discard is about (order, upload, job), only while its malware check refused it and nothing filed. */ // The upload a check is for: its order, upload and job (none yet when the start's answer was lost). const checkKey = (row: DiagnosticOrderQueueRow): string | undefined => { const upload = row.imageImport; if (row.state !== "performed" || !row.orderId || upload?.state !== "pending") return undefined; return [row.orderId, upload.uploadId, upload.jobId ?? ""].join("|"); }; const discardKey = (row: DiagnosticOrderQueueRow): string | undefined => { const upload = row.imageImport; const refused = upload?.state === "pending" && (upload.filingHeld === "blocked" || upload.filingHeld === "unscannable"); if (row.state !== "performed" || !row.orderId || row.images?.instances !== 0 || !refused || !upload?.jobId) return undefined; return [row.orderId, upload.uploadId, upload.jobId].join("|"); }; const DiagnosticOrdersPage = () => { const navigate = useNavigate(); const { rows, loading, error, advance, cancelStudy, reload, recountImages, markReleased, markReturned, markSentOut } = useDiagnosticOrderQueue(); const { activeWorkspace, canManage, permissionsSettled } = useStaffPermissionServiceContext(); const mayEnterLab = permissionsSettled && canManage("diagnostics"); const [busyOrderId, setBusyOrderId] = useState(); const [advanceError, setAdvanceError] = useState(); const [query, setQuery] = useState(""); const [viewing, setViewing] = useState(); const [viewingResult, setViewingResult] = useState(); const [viewingRejection, setViewingRejection] = useState(); const [sendingOut, setSendingOut] = useState(); const [releasing, setReleasing] = useState(); const [adding, setAdding] = useState(); const [faxing, setFaxing] = useState(); const [returning, setReturning] = useState(); const [entering, setEntering] = useState(); const [refusing, setRefusing] = useState(); const [withdrawing, setWithdrawing] = useState(); const [scheduling, setScheduling] = useState(); const [callingOff, setCallingOff] = useState(); const [callingOffBusy, setCallingOffBusy] = useState(false); const [callOffError, setCallOffError] = useState(); const [uploading, setUploading] = useState(); // Orders whose upload finished here but whose images are not counted yet; they are not offered again. const [importing, setImporting] = useState>(new Set()); const [checkingImport, setCheckingImport] = useState(); const [importCheck, setImportCheck] = useState<{ text: string; bad: boolean }>(); const { reconcileImport, resumeImport, discardImport } = useImagingUploadService(); // A discard is asked of one upload: its order, upload and job, in this practice and sign-in. A replacement upload, // another practice or another person retires the question and any discard in flight, so neither answers it. const wpOrganization = useOptionalAuthServiceContext()?.wpOrganization; const practiceId = wpOrganization?.current?.id; const [confirmingDiscard, setConfirmingDiscard] = useState(); const [discarding, setDiscarding] = useState(); const letGo = (key?: string) => { setConfirmingDiscard((held) => (key === undefined || held === key ? undefined : held)); setDiscarding((held) => (key === undefined || held === key ? undefined : held)); }; const discardScope = useSaveScope((practiceId ?? "") + "|" + (authSessionId() ?? ""), () => letGo()); const discardableKeys = useMemo( () => new Set(rows.map(discardKey).filter((key): key is string => Boolean(key))), [rows], ); const discardSaves = useRowOwnedSaves(discardScope, discardableKeys, !loading, letGo); // The question ends when its upload leaves the list, before anything is pressed: shown again, it is asked afresh. useLayoutEffect(() => { setConfirmingDiscard((held) => (held && !discardableKeys.has(held) ? undefined : held)); }, [discardableKeys]); // Retires an upload its malware check refused, so the study can be uploaded again; the row re-reads either way. const discardUpload = async (row: DiagnosticOrderQueueRow) => { const key = discardKey(row); const orderId = row.orderId; const jobId = row.imageImport?.jobId; if (!key || !orderId || !jobId || confirmingDiscard !== key) return; const save = discardSaves.start(key); if (!save) return; const pinned = practiceId; const owned = { admit: () => { save.transport.admit(); if (wpOrganization?.current?.id !== pinned) throw new SaveRetiredError(); }, signal: save.transport.signal, organizationId: pinned, }; setDiscarding(key); setImportCheck(undefined); try { const text = await save.step(() => discardImport(jobId, { orderId }, owned), true); setImportCheck({ text, bad: false }); await save.step(() => recountImages(orderId, owned)); } catch (failure) { if (isSaveRetired(failure) || !save.belongs()) return; console.error("Error discarding an image upload:", failure); setImportCheck({ text: isSaveTimedOut(failure) ? ownFailureSentence(failure) : relayServerFailure(failure, "The upload could not be discarded. Refresh the worklist to see where it stands."), bad: true, }); } finally { if (save.belongs()) letGo(key); } }; // A check is asked in this practice and sign-in; a change retires it, so its answer and notice never land elsewhere. const checkScope = useSaveScope((practiceId ?? "") + "|" + (authSessionId() ?? ""), () => { setCheckingImport(undefined); setImportCheck(undefined); }); // A check belongs to its exact upload and job; one whose row shows another upload is retired for good. const checkableKeys = useMemo( () => new Set(rows.map(checkKey).filter((key): key is string => Boolean(key))), [rows], ); const checkSaves = useRowOwnedSaves(checkScope, checkableKeys, !loading, (key) => { setCheckingImport((held) => (held !== undefined && key.startsWith(held + "|") ? undefined : held)); }); // The page has one notice and one busy mark: only the newest check publishes them. const latestCheck = useRef(); // Files the study if its completion event was lost, or records the failure; either way the row re-reads. const checkImport = async (row: DiagnosticOrderQueueRow) => { const orderId = row.orderId; const upload = row.imageImport; const key = checkKey(row); if (!orderId || !upload || !key) return; const check = checkSaves.start(key); if (!check) return; latestCheck.current = check; const latest = () => check.belongs() && latestCheck.current === check; const pinned = practiceId; const owned = { admit: () => { check.transport.admit(); if (wpOrganization?.current?.id !== pinned) throw new SaveRetiredError(); }, signal: check.transport.signal, organizationId: pinned, }; setCheckingImport(orderId); setImportCheck(undefined); try { // Without a job id the start answer was lost; resuming on the same upload finds or starts that job. const jobId = upload.jobId ?? await check.step(() => resumeImport(upload.uploadId, { orderId }, owned), true); if (!jobId) throw new Error("Could not find the job for this upload. Try again."); const outcome = await check.step(() => reconcileImport(jobId, { orderId }, owned), true); if (!latest()) return; // A study not provably this patient's: the upload stays as it is, and so does its Check again. if (typeof outcome === "object" && "kind" in outcome) { setImportCheck({ text: outcome.message, bad: true }); return; } if (outcome === "processing") setImportCheck({ text: (row.test ?? "The study") + " is still being processed. Check again later.", bad: false }); if (typeof outcome === "object") setImportCheck({ text: outcome.alreadyFiled, bad: true }); // The worklist read is the list's own, under its own owner; this check only admits around it. await check.step(() => recountImages(orderId)); } catch (failure) { if (isSaveRetired(failure) || !latest()) return; console.error("Error checking an image upload:", failure); setImportCheck({ text: isSaveTimedOut(failure) ? ownFailureSentence(failure) : relayServerFailure(failure, "The upload could not be checked. Refresh the worklist to see where it stands."), bad: true, }); } finally { if (latest()) setCheckingImport(undefined); } }; const { markDealtWith } = useRecordOrderRefusal(); const { resumeTransmission } = useSendOutDiagnosticOrder(); // Each opening of the order dialog is its own owner for a retry pressed in it, as are the practice and the sign-in. const [viewingOpening, setViewingOpening] = useState(0); useLayoutEffect(() => setViewingOpening((opening) => opening + 1), [viewing]); const [retrying, setRetrying] = useState(); const retryScope = useSaveScope(`${practiceId ?? ""}|${authSessionId() ?? ""}|${viewingOpening}`, () => setRetrying(undefined)); const lens = lensFor(activeWorkspace); // The practices this one may send a study to for reading; with none, no study offers it. const { listReaders } = useImagingReadHandoff(); const [readers, setReaders] = useState([]); const [sendingToRead, setSendingToRead] = useState(); const [recordingArrival, setRecordingArrival] = useState(); useEffect(() => { if (lens !== "imaging") return; let live = true; listReaders() .then((found) => { if (live) setReaders(found); }) .catch((failure) => console.error("Error loading imaging readers:", failure)); return () => { live = false; }; }, [lens, listReaders]); // The heading comes from the same place the rail reads, so the two can never name different queues. const queueLabel = diagnosticsQueueLabel(activeWorkspace); // An order of no known kind stays visible under both lenses; neither can claim it. const [searchParams, setSearchParams] = useSearchParams(); const drill = useMemo(() => diagnosticDrillFrom(searchParams), [searchParams]); const inLens = useMemo( () => (lens ? rows.filter((row) => !row.kind || row.kind === lens) : rows) .filter((row) => !drill || matchesDrill(row, drill)), [rows, lens, drill], ); const queue = useMemo(() => sortQueue(inLens), [inLens]); // Counted across the whole queue: a panel whose tests are at different stages sits in both groups. const panelSizes = useMemo(() => { const sizes = new Map(); for (const row of queue) { if (row.panelId) sizes.set(row.panelId, (sizes.get(row.panelId) ?? 0) + 1); } return sizes; }, [queue]); const panelOf = (row?: DiagnosticOrderQueueRow) => (row?.panelId ? panelSizes.get(row.panelId) ?? 1 : 1); const siblingsOf = (row?: DiagnosticOrderQueueRow) => (row?.panelId ? queue.filter((other) => other.panelId === row.panelId && other.orderId !== row.orderId).map(panelStanding) : []); const searching = query.trim().toLowerCase(); const shown = searching ? queue.filter((row) => matches(row, searching)) : queue; // Resulted and reviewed are the shared tail of both machines, so "is this still work" is asked by name. const SETTLED: readonly string[] = ["resulted", "reviewed"]; const outstanding = shown.filter((row) => !row.state || !SETTLED.includes(row.state)); const settled = shown.filter((row) => !outstanding.includes(row)); // Opening a follow-on dialog closes the order it came from, so one dialog is on screen at a time. const from = (open: (row: DiagnosticOrderQueueRow) => void) => (row: DiagnosticOrderQueueRow) => { setViewing(undefined); setViewingResult(undefined); open(row); }; const onEnter = (row: DiagnosticOrderQueueRow) => { if (row.kind === "laboratory") { if (!permissionsSettled || !canManage("diagnostics")) return; setEntering(row); } else navigate(`/dashboard/diagnostic-orders/${row.orderId}/result`); }; /** A refusal dealt with re-reads the queue: the answer to "is this still stopped" genuinely moved. */ const onRefusalDealtWith = async (row: DiagnosticOrderQueueRow) => { if (!row.orderId) return; try { await markDealtWith(row.orderId); setViewing(undefined); await reload(); } catch (failure) { console.error("Error clearing the laboratory refusal:", failure); } }; /** * The same electronic send handed on again; the server says when it has started or can no longer go. It belongs to * the order dialog it was pressed in, this practice and this sign-in: closing or reopening the dialog, or either * changing, retires it, so nothing late reaches a later dialog and a retry not yet sent never goes. */ const onRetryTransmission = async (row: DiagnosticOrderQueueRow) => { const orderId = row.orderId; if (!orderId || retrying) return; const operation = retryScope.start(); setRetrying(orderId); setAdvanceError(undefined); try { const answer = await operation.step(() => resumeTransmission(orderId, operation.transport), true); // An Akashic delivery is done once the laboratory has it; an electronic send once it is queued. const stillOwed = row.deliveryOwed ? !answer?.akashic?.deliveredAt : answer?.transmission !== "queued"; setViewing(undefined); setAdvanceError(!stillOwed ? undefined : row.deliveryOwed ? "The laboratory has still not received this order. Try again shortly." : "The electronic send is still not confirmed. Try again shortly, or send the requisition with the specimen."); void reload(); } catch (err) { if (isSaveRetired(err) || !operation.belongs()) return; console.error("Error retrying an electronic send:", err); setAdvanceError(outcomeUnknown(err) ? `Whether the ${row.deliveryOwed ? "delivery" : "electronic send"} went through could not be confirmed. Reload to check before trying again.` : isSaveTimedOut(err) ? ownFailureSentence(err) : relayServerFailure(err, `Could not retry the ${row.deliveryOwed ? "delivery" : "electronic send"}. Reload and check the order.`)); } finally { if (operation.belongs()) setRetrying(undefined); } }; const onAdvance = async (row: DiagnosticOrderQueueRow, state: DiagnosticOrderState) => { if (!row.orderId) return; setBusyOrderId(row.orderId); setAdvanceError(undefined); try { // Collection moves the order only; the accession is minted when the laboratory receives the tube. await advance(row.orderId, state); setViewing(undefined); } catch (err) { console.error("Error advancing a lab order:", err); setAdvanceError("Could not update that order. It may have moved on already — reload and check."); } finally { setBusyOrderId(undefined); } }; /** * One next step per row; everything else is inside the order. A collected specimen is sent out * from here and resulted in-house at the bench; under imaging a study moves on to its next stage. */ const rowAction = (row: DiagnosticOrderQueueRow, isSettled: boolean) => { if (isSettled) { return ; } // An entered result is this test's next step, whatever its siblings are doing. if (row.awaitingRelease && row.orderId && (row.kind !== "laboratory" || mayEnterLab)) { return ; } if (onTheBench(row) && row.orderId && mayEnterLab) { return ; } if (awaitingRedraw(row)) { return ; } if (row.refusal) { return ; } const moves = row.orderId ? nextStatesFor(row.kind, row.state) : []; if (row.state === "collected" && !row.sentOut && !row.akashicRoute && moves.includes("sent") && (row.kind !== "laboratory" || mayEnterLab)) { return ; } const move = lens === "imaging" && row.state ? DIRECT_MOVE[row.state] : undefined; if (move && moves.includes(move.to)) { return ( ); } return ; }; // A booked study never returns to the schedule page, so its row calls it off through the same confirmation. const callOff = async (reason: ArchiveReason, note?: string) => { if (!callingOff?.orderId) return; setCallingOffBusy(true); setCallOffError(undefined); try { await cancelStudy(callingOff.orderId, reason, note); setCallingOff(undefined); } catch (refusal) { setCallOffError(relayServerFailure(refusal, "Could not cancel this study. It may have moved on already.")); } finally { setCallingOffBusy(false); } }; /** The worklist move: booking opens over the worklist, the machine step is one press, and upload waits on a count. */ const imagingAction = (row: DiagnosticOrderQueueRow) => { const moves = row.orderId ? nextStatesFor(row.kind, row.state) : []; if (row.state === "ordered" && moves.includes("scheduled")) { return ; } if (row.state === "scheduled" && moves.includes("performed")) { return ( <> ); } // A finished import its malware check refused may be discarded, once asked twice; still checking never is. if (discardKey(row)) { const key = discardKey(row); const busy = Boolean(key) && (discarding === key || checkingImport === row.orderId); return key && confirmingDiscard === key && discardableKeys.has(key) ? ( <> ) : ( ); } // The order remembers an upload across a reload: a pending one is checked, never uploaded twice. if (row.state === "performed" && row.orderId && row.images?.instances === 0 && row.imageImport?.state === "pending") { return ( ); } // A failure the server confirmed outranks this page's own memory of having just uploaded. const failed = row.imageImport?.state === "failed"; if (row.state === "performed" && row.orderId && row.images?.instances === 0 && row.imageImport?.state !== "pending" && (failed || !importing.has(row.orderId))) { return ( ); } // Going to an outside reader: nothing more to send until somebody records what they said. if (row.state === "performed" && row.orderId && row.outsideRead && !row.outsideRead.confirmation && !row.resultOnFile) { return ; } if (sendsToBeRead(row) && (readers.length || row.outsideRead)) { return ( ); } return ; }; const imagingRow = (row: DiagnosticOrderQueueRow) => ( ); const queueRow = (settledRow: boolean) => (row: DiagnosticOrderQueueRow) => ( ); const staged = lens === "imaging"; const unstaged = staged ? outstanding.filter((row) => !IMAGING_STAGES.some((stage) => stage.state === row.state)) : outstanding; // Scheduling is its own page, as imaging.html draws it; the worklist returns once it is saved or left. if (scheduling?.orderId) { return ( setScheduling(undefined)} onSchedule={(orderId, schedule) => advance(orderId, "scheduled", schedule)} onCancel={cancelStudy} /> ); } return (

{queueLabel.heading}

{lens === "laboratory" ? ( ) : null}

{queueLabel.subtitle}

{/* Above the queue, because results nobody has acknowledged are what should be dealt with first. */} {drill ? (

{describeDrill(drill)}.{" "}

) : null} {error && ( {error} )} {importCheck ? ( importCheck.bad ? ( {importCheck.text} ) :

{importCheck.text}

) : null} {advanceError && ( {advanceError} )} {loading ? (

Loading orders…

) : ( <>
{staged ? IMAGING_STAGES.map((stage) => ( row.state === stage.state)} empty={stage.empty} searching={Boolean(searching)} > {imagingRow} )) : null} {/* Under imaging only what no stage holds is left here, and an empty remainder is not drawn. */} {staged && unstaged.length ? ( {imagingRow} ) : null} {!staged ? ( {queueRow(false)} ) : null} {/* A technologist's worklist ends where their work does; results are read elsewhere. */} {!staged ? ( {queueRow(true)} ) : null} {searching && outstanding.length === 0 && settled.length === 0 ? (

No order matches your search. Clear it to see the whole queue.

) : null} setViewing(undefined)} onAdvance={(row, state) => void onAdvance(row, state)} onSendOut={from(setSendingOut)} onSendToRead={sendsToBeRead(viewing) ? from(setSendingToRead) : undefined} onRefused={from(setRefusing)} onWithdraw={from(setWithdrawing)} onRefusalDealtWith={(row) => void onRefusalDealtWith(row)} onRelease={from(setReleasing)} onEnter={from(onEnter)} onRetryTransmission={(row) => void onRetryTransmission(row)} /> setViewingRejection(undefined)} /> setViewingResult(undefined)} onAddendum={from(setAdding)} onReturn={from(setReturning)} onFaxBack={from(setFaxing)} /> setUploading(undefined)} onImported={(row) => { if (row.orderId) setImporting((prev) => new Set(prev).add(row.orderId as string)); }} recount={recountImages} /> setWithdrawing(undefined)} onSettled={(orderId) => reload(orderId)} /> setRefusing(undefined)} onRecorded={() => void reload()} /> setSendingOut(undefined)} onSent={(sent) => { if (sendingOut?.orderId) markSentOut(sendingOut.orderId, sent); }} /> 1} siblings={siblingsOf(releasing)} onClose={() => setReleasing(undefined)} onReleased={(state, returned) => { if (releasing?.orderId) { markReleased(releasing.orderId, isDiagnosticOrderState(state) ? state : undefined, returned); } }} onOpenControls={() => navigate("/dashboard/bench")} /> setAdding(undefined)} onAdded={(version, returned) => { if (adding?.orderId && version) markReturned(adding.orderId, returned ?? { version, outcome: "none" }); }} /> 1, } : undefined} onClose={() => setEntering(undefined)} onSaved={() => void reload()} /> setSendingToRead(undefined)} onSent={() => void reload()} onSentOutside={() => void reload()} /> setRecordingArrival(undefined)} onRecorded={() => void reload()} /> setReturning(undefined)} onRecorded={(returned) => { if (returning?.orderId) markReturned(returning.orderId, returned); }} /> setFaxing(undefined)} /> { if (!open && !callingOffBusy) setCallingOff(undefined); }} resourceLabel={callingOff?.test ?? "this study"} title={`Cancel ${callingOff?.test ?? "this study"}?`} description="It stays on the patient's record marked Withdrawn and comes off the imaging worklist." confirmLabel="Cancel the study" submittingLabel="Cancelling..." cancelLabel="Keep the study" submitting={callingOffBusy} error={callOffError} onConfirm={(reason, note) => void callOff(reason, note)} /> )}
); }; export default DiagnosticOrdersPage;