import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useReminderClearance } from '../../../components/Chat/useReminderClearance'; import type { Encounter, Patient } from 'fhir/r4'; import { useLocation, useNavigate } from 'react-router-dom'; import { readOpenVisit } from './openVisitState'; import { useAuthServiceContext } from '../../../services/AuthService'; import { usePatientServiceContext } from '../../../services/PatientService'; import { useEncounterServiceContext } from '../../../services/EncounterService'; import { formatFhirHumanName } from '../../../utils/fhirHumanName'; import { roleTier, type ClinicalTier } from '../../../utils/roleTier'; import { holdsClinicianSeat } from '../../../utils/clinicianEnabled'; import { CLINICIAN_SEAT_GRANTED_BY } from '../../../utils/recordEditability'; import { getPractitionerRolePrimaryCoding } from '../../../utils/staffRoleCatalog'; import { useClinicianDay } from './useClinicianDay'; import { useOpenEncounterActions } from './useOpenEncounterActions'; import { EncounterQueue, supportModeLabel } from './EncounterQueue'; import { QueueCountCards } from './QueueCountCards'; import { OverviewEmptyState } from './OverviewEmptyState'; import { EncounterFocusPanel } from './EncounterFocusPanel'; import { SupportFocusPanel } from './SupportFocusPanel'; import { RoomingDayPanel } from './RoomingDayPanel'; import { EncounterWorkspace } from './EncounterWorkspace'; import { WaitingOnYouCard } from './WaitingOnYouCard'; import { OwedWorkPanel } from './OwedWorkPanel'; import { OpenVisitsPanel } from './OpenVisitsPanel'; import { useOpenVisits } from './useOpenVisits'; import { useOwedWork } from './useOwedWork'; import { useUnfinishedNoteNag } from './useUnfinishedNoteNag'; import type { OwedWorkRow } from './owedWork'; import { resultToReviewPath } from './owedWork'; import { usePatientNamesByIds } from '../../../hooks/useAppointmentPatientNames'; import { useContainerWidth } from '../../../hooks/useContainerWidth'; import { nextCandidateKey, selectionAfterQueueChange } from './nextCandidate'; import { usePatientTaskTallies } from '../../../hooks/usePatientTaskTallies'; import { useAppointmentPatientNames } from '../../../hooks/useAppointmentPatientNames'; import { useActiveEncounterContext } from './ActiveEncounterContext'; import { deriveStatus, type QueueItem } from './encounterLifecycle'; import { usePracticeRoomingRead } from './usePracticeRooming'; import { useFhirLocations } from '../../../hooks/useFhirLocations'; import { onlyVirtual } from '../../../utils/roomLocation'; // Explain the missing mode without directing the clinician to purchase a seat. The mode is named only // once the practice's rooming is known (undefined while its rooms are still being read). export const ClinicalModeNotice = ({ seated, rooming: roomsInUse }: { seated: boolean; rooming: boolean | undefined }) => { if (seated) return null; return (
{roomsInUse !== undefined && (

I am: {supportModeLabel(roomsInUse)}

)}

Seeing your own patients needs a clinician seat. {CLINICIAN_SEAT_GRANTED_BY}

); }; /** * - what the focus panel is entitled to SAY when no patient is selected. * * THE DEFECT THIS CLOSES WAS A FALSE CLINICAL STATEMENT. The branch asked only `queue.length === 0`, * which is equally true of a day that has not arrived yet - so the panel announced *"No visits on your * schedule today."* while the queue beside it still said *"Loading your day..."*, and four seconds later * showed a 1:00 PM patient. The all-clear was not merely early, it was WRONG, and it is the first thing * a clinician reads. A clinician who believes it and walks away has been told something false. * * THREE STATES, because two of them are indistinguishable if you let them be: not measured yet - * measured and empty - measured and populated. The old code had two, so "we have not looked" and "there * is nothing" shared a rendering. Same family as an absent count rendering as a zero. * * ON THE CARD'S "DO NOT KEY IT OFF THE QUEUE'S LOADING FLAG": the panel has no fetch of its own - its * emptiness IS the queue's emptiness, and its sentence is a claim ABOUT the queue. So the honest fix is * not a second flag but a rule that the claim cannot be made from an unmeasured set. Sharing a flag is * what the card warns against; sharing a MEASUREMENT is what makes the two agree by construction. */ export const focusPanelPlaceholder = (state: { loading: boolean; queueCount: number; /** Rooming reads the practice's day, so its empty state is not about the user's own schedule. */ practice?: boolean; }): string | undefined => { // `loading` is the day's first read only (useClinicianDay): until then the count measures nothing. // A later re-read keeps the last answered count on screen rather than flashing the panel away. if (state.loading) return undefined; if (state.practice) return state.queueCount === 0 ? 'No visits at the practice today.' : 'Select a patient from the queue.'; return state.queueCount === 0 ? 'No visits on your schedule today.' : 'Select a patient from your queue.'; }; /** * The panel's own empty state, as a component. * * IT IS A COMPONENT BECAUSE MUTATION SAID SO. With the rule alone extracted, a mutant that rendered the * box anyway with `placeholder ?? 'No visits on your schedule today.'` survived every test - the exact * careless "fix" that would reintroduce the defect, passing because the rule was proven and the WIRING was * not. It is presentational and takes no context, so it can be rendered directly and the withholding is a * fact a test can see. */ export const FocusPanelPlaceholder: React.FC<{ loading: boolean; queueCount: number; practice?: boolean }> = ({ loading, queueCount, practice, }) => { const placeholder = focusPanelPlaceholder({ loading, queueCount, practice }); /* NOT an empty card: a bordered box saying nothing still reads as "here is your day, and it is empty". The absence of a measurement has to look like absence. */ if (!placeholder) return null; return (
{placeholder}
); }; // The identity card shows the date only: every practitioner deck identity card draws "Monday, August 24" with // no time. The tick is not a leftover of the dropped time: it keeps the // open-visits day boundary (useOpenVisits) and the owed drafts' closing days (nowMs) current while the day // stays open, so a page left open past midnight still sorts yesterday's visits as earlier. const useClock = (): Date => { const [now, setNow] = useState(() => new Date()); useEffect(() => { const t = window.setInterval(() => setNow(new Date()), 30_000); return () => window.clearInterval(t); }, []); return now; }; const ClinicianOverviewPage: React.FC = () => { const { currentUser, practitionerRoles, selectedPractitionerRole } = useAuthServiceContext(); const navigate = useNavigate(); const location = useLocation(); const { fetchPatient, setEncounterState, setPatientState } = usePatientServiceContext(); const { fetchEncounter } = useEncounterServiceContext(); // Tier defaults from the active role's primary coding, but is a view-only toggle the user can flip - // it grants no permissions, it only changes which queue buckets/actions/focus panel render. // WITHOUT A PAID SEAT THERE IS NO SEEING-PATIENTS SIDE, so there is no tier to choose either. // // Spencer, 2026-09-03: *"users without a clinician seat can room patients but don't have access to the // 'seeing patients' section and functionality."* The org's per-role grant opens this workspace for a // medical assistant deliberately and costs the practice nothing; the seat is what decides what is // behind the door. // // MEASURED ON THE DEPLOYED BUILD 2026-09-04, which is why this is a card rather than a tidy-up: an // unseated user landed here with "Seeing patients" SELECTED - the tier defaults from the primary role // coding, and a medical assistant whose primary role is administrative resolves to 'provider'. Nothing // behind it was reachable, because their token carries no clinical write and the request-time guard // refuses one anyway - so what they met was a mode that does nothing, in their first minute. const seated = useMemo(() => holdsClinicianSeat(practitionerRoles), [practitionerRoles]); const rooming = usePracticeRoomingRead(); // A failed read stops waiting: the card names the rooming default the screens already use, and says so. const roomingAnswer = rooming.failed ? true : rooming.answer; const roomsInUse = roomingAnswer ?? true; const defaultTier = useMemo( () => (seated ? roleTier(getPractitionerRolePrimaryCoding(selectedPractitionerRole)) : 'support'), [seated, selectedPractitionerRole], ); const [tier, setTier] = useState(defaultTier); useEffect(() => setTier(defaultTier), [defaultTier]); // reset to the role's default when the active role changes const { queue, loading, error, errorIsRetryable, refresh, noteLocalEncounter } = useClinicianDay(tier === 'support' ? 'practice' : 'own'); // + Spencer's 2026-08-15 ruling - every task the patient still owes, and how many // of them were due by THIS visit. It is what makes a row actionable without opening it: the // practitioner learns before the patient arrives that things are outstanding, and at check-in the desk // can decide whether they can be finished in the waiting room or the visit should be rebooked. // Read HERE rather than inside the queue: fetching in that component would give every surface that // mounts a queue a new provider-tree dependency ( R5's coupling defect, one screen over - and // the first version of this hit exactly that, breaking a test that mounts the queue bare). const taskTallySubjects = useMemo( () => queue.map((item) => ({ key: item.key, patientId: item.patientId, appointment: item.appointment })), [queue], ); const taskCountByRowKey = usePatientTaskTallies(taskTallySubjects); // - the queue row printed `actor.display` ALONE, so a correctly-formed appointment carrying // none read "Unknown patient" beside a detail pane that named them, on screen together. // ONE resolution for the whole queue, fed every appointment it holds: the hook dedups by id, so a // patient on two rows costs one read. const { locationsList } = useFhirLocations(); const virtualLocationId = onlyVirtual(locationsList)[0]?.id; const queuePatientNames = useAppointmentPatientNames( useMemo( () => queue .map((item) => item.appointment) .filter((appointment): appointment is NonNullable => Boolean(appointment)), [queue], ), ); // sub-task 1: both of these now live ABOVE the router (App.tsx), so the note draft and the // active encounter survive navigating away from /overview. This page consumes them; it no longer owns them. // - what is waiting on THIS clinician, independent of today's queue. Read at the page // for the same reason the task tallies are: a component that fetches gives every surface mounting it a // new provider-tree dependency. // Only a practitioner has ordered anything, and the results route refuses anybody else by naming // the refusal rather than answering an empty list - so asking as a non-practitioner would be a 403 // on every poll of a landing screen. Saying what the caller is lets the hook answer the honest // empty list, which is a different fact from a failed read. const owedWork = useOwedWork(Boolean(selectedPractitionerRole)); // - which owed row has BORROWED the patient panel. Not a route: the queue keeps // its selection the whole time, so closing (or clicking any patient) returns exactly where you were. const [owedPanel, setOwedPanel] = useState(); // A waiting note carries a patient id and nothing else, so the panel resolves names the same way every // other surface does. Never a bare id in front of a clinician. const owedDraftPatientIds = useMemo( () => owedWork.drafts.map((draft) => draft.patientId), [owedWork.drafts], ); const owedPatientNames = usePatientNamesByIds(owedDraftPatientIds); const { active: openEncounter, setActive, clearActive, } = useActiveEncounterContext(); const now = useClock(); /** * - VISITS THIS CLINICIAN NEVER CLOSED, which the day cannot show and nothing else asks for. * * The day is built from TODAY's appointments and then asks for encounters per appointment, so a visit * that outlived its day is invisible to every one of its four cells. Measured 2026-09-14: six open on * one patient, the oldest since August 2, under a board reading "IN PROGRESS 0". The read is its own, * deliberately - it is not urgent, and a slow or refused answer must never hold up the day. */ const openVisits = useOpenVisits(now); // stagnant-draft nagger is mounted further down, once `openDraftEncounter` exists - it is on // the clinician's HOME rather than inside the encounter workspace on purpose: the drafts that most need // chasing belong to encounters nobody has reopened, so a nag that only appeared once you were already // looking at the encounter would never fire for them. const [selectedKey, setSelectedKey] = useState(); const [patient, setPatient] = useState(); const patientToken = useRef(0); // PURE VIEW STATE - whether the queue is railed to 64px to give the visit room. Spencer, 2026-08-05: // *"we don't want simply expanding the queue to end the encounter OR put it on leave, that's silly. // Expanding it is just to actually see the queue, not an action to do anything."* // It must NEVER touch an Encounter's status and must never unmount the workspace. Visibility used to be // inferred from `openEncounter`, which is what made a layout toggle double as "leave the visit". const [queueCollapsed, setQueueCollapsed] = useState(false); /** * - the CONTAINER, never the viewport. We render inside the practice's own page, * so the window can report 1440 while this column has 900. * `undefined` is UNMEASURED and reads as "not narrow": jsdom has no ResizeObserver and neither does * an old browser, and railing a surface for everyone we could not ask is the wrong failure direction. * 768 is the design's PORTRAIT frame. Its stated reason no longer holds once the queue narrows to * 296 (see index.css) - the frame is built as drawn and the arithmetic is on the card. */ /* * THE REF IS ON THE CONTAINER ELEMENT ITSELF, not on the columns inside it. Both * thresholds - this one and the `@container` bands in index.css - have to read the SAME box or they * land on two different numbers, and the box that means anything is the FRAME: the padded child is * 48px narrower, which is exactly the discrepancy that made the widest layout unreachable. * Moving this ref back down to `.ak-clinday__columns` restores that split silently - nothing * errors, the rail simply engages 48px early again. */ const frameRef = useRef(null); const containerWidth = useContainerWidth(frameRef); // The unfinished-note reminder sits over the bottom of the screen; the page ends below it so its last row can be reached. const reminderRoomRef = useRef(null); const reminderRoom = useReminderClearance(reminderRoomRef, frameRef, { endsPage: true }); const narrowContainer = containerWidth !== undefined && containerWidth <= 768; // Two independent reasons, and they must stay independent. A visit needs room; a portrait tablet has // none to begin with. Collapsing them into one flag is how "expand the queue" would start meaning // something different depending on why it was railed. // The overview is the day as count cards (Spencer, 2026-08-26), for rooming-only staff too, as the // deck draws them; the expanded queue stays beside an open visit. // The SELECTION rule reads the day as cards exactly as before; only the drawing below changes. const dayAsCards = (seated || tier === 'support') && !openEncounter; // A finished visit selected with no visit open rails the day (deck "Finished"; Codex P06 ruling item 2). // View only, and reset with the selection, so expanding it once does not stick to the next patient. const [finishedQueueExpanded, setFinishedQueueExpanded] = useState(false); useEffect(() => setFinishedQueueExpanded(false), [selectedKey]); const finishedSelected = queue.find((q) => q.key === selectedKey)?.status === 'finished'; const finishedRail = finishedSelected && !openEncounter && !finishedQueueExpanded; const countCards = dayAsCards && !finishedRail; const queueRailed = !countCards && (narrowContainer || (Boolean(openEncounter) && queueCollapsed) || finishedRail); /** * At portrait width the queue opens OVER the panel rather than beside it - there is no beside. * * It is a separate state from `queueCollapsed`, which is the with-a-visit-open one. Reusing that * would leave the queue permanently expanded into a column that cannot hold it. */ const [dayOverlayOpen, setDayOverlayOpen] = useState(false); // A width change must not strand the overlay open on a desktop, where the queue is a column again. useEffect(() => { if (!narrowContainer) setDayOverlayOpen(false); }, [narrowContainer]); // Show the workspace while the SELECTED queue item is the active encounter. Not `openEncounter` alone: // with the queue now visible beside an open visit, the practitioner must still be able to click another // patient and see their focus panel (the only place "Start encounter" lives). Rendering purely on // `openEncounter` would leave the queue visible but inert. // Previewing another patient does NOT end or park the active visit - the slot keeps holding it, and // only actually ENTERING the other patient parks it. Unmounting the workspace is safe now because // sub-task 1 moved the draft store and the active slot above the router, so nothing is lost. // Rooming never opens the seeing-patients workspace; without a seat there is no other mode. const showWorkspace = tier !== 'support' && Boolean(openEncounter) && selectedKey === openEncounter?.item.key; // (B47) - an action that WROTE an encounter tells the board what it wrote, then refreshes. // The refresh alone was never enough: it re-runs a search, and HealthLake is ~30s stale after a write, // so the board faithfully redrew the pre-write world and `Start encounter` looked like it did nothing. // See useClinicianDay's overlay for why this is confirmed state rather than an optimistic guess. const handleChanged = useCallback((writtenEncounter?: Encounter) => { if (writtenEncounter) noteLocalEncounter(writtenEncounter); refresh(); // AND THE OPEN-VISITS LIST, which is a second reader of the same fact. Finishing one of those visits // is exactly the write that should take it off this list, and without this the row it was opened // from would still be sitting there afterwards - a closed visit advertised as open. openVisits.refresh(); }, [noteLocalEncounter, refresh, openVisits]); // Auto-select the first item (prefer in-progress) once the queue loads / when selection is stale. const selected = useMemo(() => queue.find((q) => q.key === selectedKey), [queue, selectedKey]); useEffect(() => { // The rule - including why an ACTIVE encounter is never stale selection - lives in // `selectionAfterQueueChange`, where it is unit-tested. See its comment before changing this. const outcome = selectionAfterQueueChange(queue, selectedKey, openEncounter?.item.key); // With count cards nobody is chosen for the clinician: the panel invites the next encounter // instead, and a selection that left the day is dropped rather than replaced. if (outcome.kind === 'clear' || (dayAsCards && outcome.kind === 'select' && selectedKey !== undefined)) { setSelectedKey(undefined); } else if (outcome.kind === 'select' && !dayAsCards) setSelectedKey(outcome.key); }, [queue, selectedKey, openEncounter, dayAsCards]); // Whose chart is on screen. Normally the selected queue row - but an ACTIVE encounter that is not a queue // row at all is a real state ( nag opens a days-old visit that is not on today's board), and // reading only from `selected` left that case with no patient, so the workspace showed "Patient" and the // note hook had nothing to link to. const patientIdInView = selected?.patientId ?? (openEncounter && selectedKey === openEncounter.item.key ? openEncounter.item.patientId : undefined); // Resolve the patient object for the focus panel / workspace (age/sex + snapshot). useEffect(() => { if (!patientIdInView) { setPatient(undefined); return; } if (patient?.id === patientIdInView) return; // already the right one - e.g. the draft opener fetched it const token = ++patientToken.current; fetchPatient(patientIdInView) .then((p) => { if (token === patientToken.current) setPatient(p); }) .catch(() => { if (token === patientToken.current) setPatient(undefined); }); }, [patientIdInView, patient?.id, fetchPatient]); // The patient read last is kept until the next one settles, so for that while it is the previous row's. A panel is // handed only the patient its selected row names: never A's chart, age or vitals capture under B's heading. const admittedPatient = selected && patient?.id === selected.patientId ? patient : undefined; // Enter the in-place workspace (called by EncounterFocusPanel's actions instead of navigating). const handleEnter = useCallback((encounter: Encounter, item: QueueItem) => { setSelectedKey(item.key); //: entering an encounter PARKS whichever one was active as `onleave` - there is only ever one // active encounter. Fire-and-forget: the park must never gate opening the next patient's chart. // THIS is the only place a park belongs: a switch between patients is the transition that actually // happened. Merely looking at the queue is not, which is why onExpand does not go through here. void setActive({ item, encounter }); setQueueCollapsed(true); // entering a visit rails the queue to give the workspace room (view only) setEncounterState(encounter); // so ConditionModal + the note hook auto-link to this encounter // Only trust `patient` if it actually matches this item; otherwise undefined (safe-fail, never a wrong patient). setPatientState(patient?.id === item.patientId ? patient : undefined); }, [setEncounterState, setPatientState, patient, setActive]); /** * - open the encounter a stagnant draft is waiting on. * * The draft is usually NOT on today's queue. That is the whole reason the nagger exists: the drafts * that need chasing belong to encounters nobody has reopened, often days old and `finished`. So this * cannot simply select a queue row - it resolves the encounter and the patient and enters it directly. * * It goes through `handleEnter`, which means entering a waiting note **parks whichever visit was * active** as `onleave`. That is deliberate: there is exactly one active encounter, and landing in * another patient's note IS a switch. Inventing a non-parking path here would create a second way to be * "active" - the ambiguity `ActiveEncounterContext` was built to remove. */ // Takes the two fields it actually reads rather than a whole ScribeDraftSummary: the unfinished-note // reminder needs this same door and has no scribe draft to hand it. Widening the parameter is honest - // nothing in here ever looked at anything else. const openDraftEncounter = useCallback(async (draft: { encounterId: string; patientId: string }) => { // If today's board already holds it, enter the queue's own object rather than a synthesised twin - // same encounter, but the row stays selected and the queue keeps its own status/room/appointment. const queued = queue.find((q) => q.encounter?.id === draft.encounterId); if (queued?.encounter) { handleEnter(queued.encounter, queued); return; } try { const encounter = await fetchEncounter(draft.encounterId); // The patient is resolved but NOT awaited into the entry decision: a name is presentation, and // failing to load one must not stop a clinician reaching a note that is about to expire. const record = await fetchPatient(draft.patientId).catch(() => undefined); // Setting the page's patient BEFORE entering lets the existing "keep patientState pointed at the // OPEN encounter's patient as it resolves" effect do its job - `handleEnter` reads a stale closure // and will null it out first, which that effect then corrects on the next render. setPatient(record); handleEnter(encounter, { key: `scribe-draft-${draft.encounterId}`, patientId: draft.patientId, patientName: record ? formatFhirHumanName(record.name) : undefined, encounter, status: deriveStatus(undefined, encounter), atISO: encounter.period?.start, }); } catch (err) { // Never fail silently here. A dead click is the exact defect this card exists to remove, and a // deleted or unreachable encounter must not look identical to a button that was never wired. console.error('Failed to open the encounter for a waiting draft', err); window.alert('Could not open that visit. The note is still waiting โ€” please try again.'); } }, [queue, handleEnter, fetchEncounter, fetchPatient]); /** * A PAST VISIT, ASKED FOR FROM THE CHART (View encounter): open it through the same door as a waiting * draft, once, then clear the ask so a re-render or a back-navigation does not open it again. */ const askedVisit = useRef(undefined); useEffect(() => { const asked = readOpenVisit(location.state); if (!asked || askedVisit.current === asked.encounterId) return; askedVisit.current = asked.encounterId; void openDraftEncounter(asked); navigate(location.pathname, { replace: true, state: null }); }, [location.state, location.pathname, openDraftEncounter, navigate]); /* * The reminder that gets louder the longer an unfinished note sits. * * Here rather than in the encounter workspace for the reason stated above `openDraftEncounter`: the * notes that most need chasing belong to encounters nobody has reopened, so a reminder that only * appeared once you were already looking at one would never fire for them. * * SUPPRESSED WHILE A VISIT IS OPEN, which is `openEncounter` here. That outranks every step of the * ladder including the un-dismissable one - a nag a practitioner cannot get past mid-consultation * stops being a reminder and becomes an outage. */ useUnfinishedNoteNag( Boolean(openEncounter), useCallback( (encounterId: string, patientId: string) => { void openDraftEncounter({ encounterId, patientId }); }, [openDraftEncounter], ), ); /** * - THE STAGNANT-DRAFT TOAST IS GONE, and its own words are why. * * It existed because a waiting note is *"usually NOT on today's queue"* - i.e. because there was no * list to put one in. **A toast is what you build when the surface is missing.** "Waiting on you" * is that surface, it names the same drafts with the same deadline, and it does not interrupt anyone * to do it. Keeping both would be two things nagging about one note, which is how people learn that * neither means anything in particular. * * Retired only AFTER the card that replaces it existed - the design's own build order, and the * reason it is last: deleting the nag first would have left the drafts with no surface at all. */ // clearActive, NOT setActive(undefined). Both callers (Finish, Set-on-leave) have ALREADY written this // encounter's status, but the active slot still holds the pre-write copy saying `in-progress` - so a // parking clear would read that stale status and PUT `onleave` over a just-FINISHED visit, reopening a // closed clinical record. Terminal transitions clear; only an un-written exit parks. // // - IT NO LONGER REFRESHES, and that is deliberate rather than an omission. Every caller is // `useOpenEncounterActions`, which calls `onChanged(written)` immediately before this and therefore has // already refreshed. Refreshing again would bump the board's tick counter twice in one turn and run the // whole day-fetch a second time for nothing. const exitToSwitchboard = useCallback((closedKey: string) => { clearActive(); setEncounterState(undefined); setPatientState(undefined); setSelectedKey(nextCandidateKey(queue, closedKey)); // preview, not enter }, [queue, setEncounterState, setPatientState, clearActive]); /** * - the workspace's own writes go through the SAME reporting rule as the focus panel's. * * These three used to live inline here and called a bare `refresh()`, so **finishing a visit from * inside the documentation surface wrote `finished` server-side and the board never learned it** - the * surface closed, the patient stayed under IN PROGRESS, and the clinician had been told it was done. * The hook carries the full reasoning; the short version is that a refresh reads an eventually * consistent search and `onChanged` reads what the API returned. */ const openActions = useOpenEncounterActions({ onChanged: handleChanged, onExit: exitToSwitchboard }); // Keep patientState pointed at the OPEN encounter's patient as it resolves - never a stale/wrong one. useEffect(() => { if (openEncounter && patient?.id === openEncounter.item.patientId) setPatientState(patient); }, [openEncounter, patient, setPatientState]); if (!currentUser?.fhir?.id) return null; // reload-loop safety const providerName = currentUser.fhir.resourceType === 'Practitioner' ? formatFhirHumanName(currentUser.fhir.name) : 'Clinician'; /* - THE CONTAINER AND THE FRAME ARE TWO ELEMENTS, and that separation is the whole fix. `container-type: inline-size` measures the CONTENT box, so while the container also carried `p-4 md:p-6` the query saw 1180 - 48 = 1132 and `@container (min-width: 1180px)` could never fire at ANY window size - the 358px queue was unreachable code, measured at a 2200px viewport rather than argued. The same 48px shifted the other two bands early too, so a tablet with 780-816px of frame got the portrait rail where the design says it holds two columns. NOT fixed by adding 48 to the breakpoints: that hard-codes today's padding into the rules and breaks silently the day someone changes `p-6`, with no error. `max-w` STAYS on the container rather than moving to the child (the card offered a bare wrapper): with no padding and no border, this element's content box IS the frame width, so the query reads the exact number the design was drawn against instead of the whole page width. A bare wrapper picks the same band at every width today, but re-creates this bug in mirror image the day the cap drops below 1180. */ return (
{/* The deck's identity card: who, the working mode, the date - one row that wraps on a phone. */}
{/* ๐Ÿท๏ธ CARD 644 โ€” an `

`, NOT an `

`: the WordPress theme already titles the host page, and every sibling staff screen titles itself at h2. */}

{providerName}

{/* Tier toggle โ€” view-only, grants nothing. HIDDEN WITHOUT A SEAT, NOT DISABLED: with one mode available the toggle is not a choice, so the card says who they are instead. */} {seated && (
{/* Unnamed while rooming is being read, so the label never flips under the reader. */}
)} {now.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' })}
{rooming.failed && (
We couldn't read your practice's rooms, so this page assumes you room patients.
)} {error && (
{error} {/* * Card 242 (B46) โ€” Retry is offered only when retrying could actually work. The design pass * found this affordance sitting on a 403 explicit deny, where every press fails identically. * The message still renders either way: a refusal that shows nothing is a false assertion * that there is no data โ‡’ reference-authz-denial-never-renders-as-empty-data. */} {errorIsRetryable && ( )}
)} {/* Card 114 โ€” visits this clinician has been asked to join. Renders nothing when there is nothing to answer, so it costs the majority who are never invited to anything. It sits ABOVE the day queue deliberately: an unanswered invitation is time-sensitive, and a cross-org invitee will never see the visit anywhere else until they accept. */} {/* ๐Ÿšจ Card 334 slice 5 โ€” the widths moved to CONTAINER queries (`.ak-clinday` in index.css). They were `md:` VIEWPORT breakpoints, which answer a question nobody asked: we render inside the practice's own page, so the window can say 1440 while this column has 900. Column width still follows the QUEUE VIEW state, not whether a visit is open. */}
{/* โ›” The overlay class goes on the COLUMN, not on the queue: the queue must not learn that it is sometimes floating. It renders the same way in both places, and only its box moves. */}
{/* ๐Ÿ”‘ Card 334 slice 2 โ€” owed work sits ABOVE the day, at the queue's width. The overview only ever knew about TODAY, so an unfinished note from a previous day had no surface anywhere in the product; the stagnant-draft toast exists only because there was no list to put it in. โ›” It leaves with the column when a visit opens (design ยง4.4), exactly as the messages card below already does โ€” during a visit this whole column is a 64px rail. โ›” And it is NOT passed the tier: a nurse practitioner in "Rooming patients" must not have their own unfinished note hidden by a view setting. */} {!openEncounter && !finishedRail && ( setOwedPanel((current) => (current === id ? undefined : id))} /* ยง6 - at portrait width the full card costs three patients' worth of the visible day, so it becomes one line. It does not disappear: this is the ONLY place the card changes shape, and hiding owed work to make room for the day inverts what the card is for. */ compact={queueRailed} /> )} {loading && queue.length === 0 ?

Loading your dayโ€ฆ

: countCards ? { setOwedPanel(undefined); setSelectedKey(i.key); }} taskCountByRowKey={taskCountByRowKey} patientNames={queuePatientNames} virtualLocationId={virtualLocationId} currentKey={tier === 'support' ? 'to-room' : undefined} rooming={roomsInUse} /> : { setOwedPanel(undefined); setDayOverlayOpen(false); setSelectedKey(i.key); }} collapsed={queueRailed && !dayOverlayOpen} rooming={roomsInUse} /* VIEW ONLY. Expanding the queue used to clear the open encounter - so a layout toggle silently doubled as "leave the visit", and (in a later attempt) as "park the visit", writing two Encounter versions per glance. It now changes nothing but the layout: the visit stays open and active, and the workspace stays mounted beside the queue. At portrait width there is no column to expand INTO, so it opens over the top instead - the design's DAY. */ onExpand={() => (finishedRail && !narrowContainer ? setFinishedQueueExpanded(true) : narrowContainer ? setDayOverlayOpen(true) : setQueueCollapsed(false))} onCollapse={ dayOverlayOpen ? () => setDayOverlayOpen(false) : openEncounter && !queueCollapsed && !narrowContainer ? () => setQueueCollapsed(true) : undefined } tier={tier} taskCountByRowKey={taskCountByRowKey} patientNames={queuePatientNames} virtualLocationId={virtualLocationId} />} {/* OUTSIDE THE DAY, directly under its counts. A visit six weeks old is not part of "who is here now", but it is the one thing on this screen nobody can otherwise act on. It draws nothing at all when there is nothing open. */} {!openEncounter && !finishedRail && tier !== 'support' && ( )} {/* ๐Ÿ—‘๏ธ CARD 334 SLICE 6 โ€” the messages card that used to sit here is GONE. It was a THIRD door to the same threads, and it sat BELOW the day, where a long queue buried the very thing it was meant to surface. What is owed now reads above the day in "Waiting on you"; everything else is Messages, which is in the nav. โ›” And "unread" goes with it as a signal โ€” read and unanswered is the state that needs chasing. */}
{/* ๐Ÿšจ Card 334 ยง4.3 โ€” an owed row BORROWS this panel. It is checked before the workspace so a panel opened from the card cannot be silently replaced, and the queue's selection is untouched underneath: `returnToName` is the chart that comes straight back. โ›” The card is not rendered while a visit is open (ยง4.4), so this state is unreachable from inside one โ€” but the ordering makes that a fact about the layout rather than a coincidence of which condition happened to be checked first. */} {owedPanel ? setOwedPanel(undefined)} onOpenDraft={(draft) => { setOwedPanel(undefined); void openDraftEncounter(draft); }} /* TO THE CHART, not to a one-click acknowledgement. Marking a result reviewed is a clinical decision and the control for it lives beside the values, in the patient's own Tests & Imaging area - acknowledging from a summary list would be signing for something you have not read. */ onOpenResult={(result) => { setOwedPanel(undefined); // The exact order travels with the link, so the result opens where it is read and reviewed. const path = resultToReviewPath(result); if (path) navigate(path); }} onSendReply={owedWork.sendReply} onDismiss={owedWork.dismissThread} /> : showWorkspace && openEncounter ? : selected ? (tier === 'support' ? : ) /* - this branch used to ask only `queue.length === 0`, which is equally true of a queue that has not ARRIVED. So the panel announced "No visits on your schedule today." beside a queue still saying "Loading your day...", and four seconds later showed a 1:00 PM patient. The all-clear was not early, it was wrong - and it is the first thing a clinician reads. The component withholds until the day has actually been read. */ : countCards && tier === 'support' ? { setOwedPanel(undefined); setSelectedKey(i.key); }} onChanged={handleChanged} onEnter={handleEnter} rooming={roomsInUse} /> : countCards ? : }
); }; export default ClinicianOverviewPage;