import { useState, type ReactNode } from 'react'; import type { AdverseEvent, AllergyIntolerance, CarePlan, CareTeam, ClinicalImpression, Condition, DetectedIssue, Device, DeviceRequest, DeviceUseStatement, DocumentReference, Encounter, EpisodeOfCare, FamilyMemberHistory, Goal, Immunization, ImmunizationEvaluation, ImmunizationRecommendation, MedicationAdministration, MedicationDispense, MedicationRequest, MedicationStatement, NutritionOrder, Observation, Procedure, QuestionnaireResponse, Resource, RiskAssessment, Task, VisionPrescription, } from 'fhir/r4'; import { cn } from '../../lib/utils'; import type { SnapshotItem, SnapshotTypeResult } from '../../hooks/usePatientSnapshot'; import { SNAPSHOT_SECTION_ORDER, SNAPSHOT_TYPES } from './snapshotRegistry'; import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; import { resourceLoadFailureMessage } from '../ui/resource-list-status'; import type { VisitNoteSource } from '../FhirEncounterDisplay/useEncounterVisitNote'; import { Sheet, SheetContent } from '../ui/sheet'; import { SHEET_BODY_TYPES } from '../../utils/sheetBodyDisplays'; import { FhirAdverseEventDisplay, FhirAllergyIntoleranceDisplay, FhirCarePlanDisplay, FhirCareTeamDisplay, FhirClinicalImpressionDisplay, FhirConditionDisplay, FhirDetectedIssueDisplay, FhirDeviceDisplay, FhirDeviceRequestDisplay, FhirDeviceUseStatementDisplay, FhirDocumentReferenceDisplay, FhirEncounterDisplay, FhirEpisodeOfCareDisplay, FhirFamilyMemberHistoryDisplay, FhirGoalDisplay, FhirImmunizationDisplay, FhirImmunizationEvaluationDisplay, FhirImmunizationRecommendationDisplay, FhirMedicationAdministrationDisplay, FhirMedicationDispenseDisplay, FhirMedicationRequestDisplay, FhirMedicationStatementDisplay, FhirNutritionOrderDisplay, FhirObservationDisplay, FhirProcedureDisplay, FhirQuestionnaireResponseDisplay, FhirRiskAssessmentDisplay, FhirTaskDisplay, FhirVisionPrescriptionDisplay, } from '../index.ts'; import { ArchivedBadge } from '../ArchivedNotice/ArchivedNotice'; /** Resource types that own a `FhirDisplay` modal (everything except Coverage + Flag). */ const DISPLAYABLE = new Set( SNAPSHOT_TYPES.filter((t) => t.hasDisplay).map((t) => t.resourceType), ); /** Small, dependency-free spinner (Tailwind only - no @wordpress/components in the chrome). */ export function Spinner({ className }: { className?: string }) { return ( ); } interface SnapshotChartProps { /** The reconstructed / resolved per-type results to render as sections + rows. */ results: SnapshotTypeResult[]; /** A staff reader: a reading's detail shows what the patient said about where it came from. */ staffReader?: boolean; /** Optional banner rendered above the sections (e.g. an integrity or historical notice). */ banner?: ReactNode; /** Trailing text for the per-section empty-state, e.g. `as of 7/16/2026` or `in this anchor`. */ emptyLabel?: string; /** * Card 263 E7 โ€” render the empty sections as ONE line naming them, instead of one card each. * * On the encounter's reference column this said *"Nothing recorded currently."* **six times** - * ~350px of a ~640px column spent saying nothing, on the screen a clinician reads before speaking to * a patient. E7's rule for that column is **reference, never actions**, and six restatements of * absence are not reference. * * DEFAULT OFF, and that is not timidity. This chart also renders the **legal record print view** * and the point-in-time care-view anchor, where *"this section was empty at this date"* is an * evidentiary claim about a disclosed record. Collapsing six such claims into one line changes what * a legal export says. Opted into by the LIVE encounter surfaces only - the three that pass * `emptyLabel="currently"`. */ collapseEmptySections?: boolean; /** * Historical reconstruction lists types whose `_history` page could not reach a version * at T. Print already carries this sentence from `truncatedTypes`; the live chart does * not unless this is set. */ discloseTruncatedTypes?: boolean; /** * ๐Ÿท๏ธ CARD 644 โ€” WHAT HEADING LEVEL THE SECTION TITLES RENDER AT. Defaults to `3`. * * THE DEFECT THIS EXISTS FOR, watched in a browser 2026-08-23 on the clinician overview: * *"Chart snapshot"* is an `

` and every section title beneath it was **also** an `

`, so its * children were its siblings. Page-wide there were **zero headings deeper than h3** - a screen-reader * user navigating by heading got a flat list instead of an outline. * * WHY A PROP AND NOT JUST `h4`. This chart has **six** call sites at **three** different depths, * and a hardcoded level is right for some and wrong for the rest: * * EncounterWorkspace - EncounterFocusPanel - SupportFocusPanel under an h3 -> needs 4 * LegalRecordPrintView (ร—2) under an h2 -> 3 is correct * PatientSnapshot - AnchorView no local heading -> 3 * * **The default stays 3 because that is already right for half of them**, and only the three * clinician-overview surfaces opt in. Changing the default would silently break the legal print * view's outline, which is the one place the document structure is part of the evidence. * * **This fixes OUR tree only.** The rendered page also carries the WordPress theme's own `

` * alongside ours, so a genuinely correct page outline is a wider question than this prop * is the same "we are a mount in someone else's * document" shape. */ headingLevel?: 2 | 3 | 4 | 5 | 6; /** Print what a clinical note says under its row; a printed page cannot open the row to read it. */ showWrittenText?: boolean; } const writtenTextOf = (resource: Resource): string => { if (resource.resourceType !== 'ClinicalImpression') return ''; const note = resource as ClinicalImpression; return [note.summary, ...(note.note ?? []).map((n) => n.text)] .filter((text): text is string => Boolean(text?.trim())) .join('\n\n'); }; /** * Presentational, READ-ONLY chart: renders a set of `SnapshotTypeResult`s as sectioned rows, * each row opening its `FhirDisplay` in a read-only modal on click. Owns only local modal * state - it never fetches, reconstructs, or writes. Reused by `PatientSnapshot` (Cap A * "View as of...") and the care-view `AnchorView`. */ export function SnapshotChart({ results, banner, emptyLabel, collapseEmptySections = false, headingLevel = 3, discloseTruncatedTypes = false, showWrittenText = false, staffReader = false, }: SnapshotChartProps) { // A tag name from a number, so the level is data rather than six copies of the same JSX. React // treats a lowercase string as an intrinsic element, which is exactly what `h3`/`h4` are. const SectionHeading = `h${headingLevel}` as 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; // Read-only modal state: which reconstructed resource is open in its FhirDisplay. const [selected, setSelected] = useState<{ resourceType: string; resource: Resource } | null>( null, ); const [showModal, setShowModal] = useState(false); const openItem = (resourceType: string, resource: Resource) => { setSelected({ resourceType, resource }); setShowModal(true); }; const closeModal = () => { setShowModal(false); setSelected(null); }; const renderRow = (result: SnapshotTypeResult, item: SnapshotItem) => { const clickable = DISPLAYABLE.has(item.resourceType); const updated = item.lastUpdated ? new Date(item.lastUpdated).toLocaleString() : ''; const writtenText = showWrittenText ? writtenTextOf(item.resource) : ''; if (writtenText) { return (
{renderRowLine(result, item, clickable, updated)}

{writtenText}

); } return renderRowLine(result, item, clickable, updated, item.id); }; const renderRowLine = ( result: SnapshotTypeResult, item: SnapshotItem, clickable: boolean, updated: string, key?: string, ) => { return (
openItem(item.resourceType, item.resource) : undefined} onKeyDown={ clickable ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openItem(item.resourceType, item.resource); } } : undefined } className={cn( 'flex items-center justify-between gap-3 rounded-md border px-3 py-2 text-sm', clickable ? 'cursor-pointer hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring' : 'cursor-default', )} > {item.label || result.label} {/* An archived record keeps its recorded status in its label; this says it is no longer current. */} } /> {updated && ( {updated} )}
); }; const renderTypeResult = (result: SnapshotTypeResult) => { // A fully-resolved, empty type is skipped entirely (no heading, no line). if (result.status === 'ready' && result.items.length === 0 && result.truncatedCount === 0 && !result.omitsDeleted) { return null; } const loading = result.status === 'loading' || result.status === 'idle'; return (
{/* ๐Ÿท๏ธ Card 644 โ€” the LEVEL varies by host, the LOOK does not: the classes are unchanged, so this is a document-structure change with no visual one. */} {result.label} {loading && }
{/* * Card 179 โ€” this said only "Couldn't load conditions.", which cannot be told apart * from an outage, and sits beside sections that say "Nothing recorded". A clinician * must be able to distinguish "you may not be allowed to see this" from "there is * none". The words come from `ui/resource-list-status` so the two surfaces cannot * drift; only the LAYOUT differs (a compact line here, a banner on the list pages, * because this section can list ~30 types at once). */} {result.status === 'error' && (

{resourceLoadFailureMessage(result.label.toLowerCase())}

)} {result.status === 'ready' && result.items.map((item) => renderRow(result, item))} {result.truncatedCount > 0 && (

{result.truncatedCount} item(s) couldn't be reconstructed at this date.

)} {result.status === 'ready' && result.omitsDeleted && (

{result.label} deleted from the record since this date are not shown.

)}
); }; /** * Whether a section has anything to show. * * `loading`, `idle` and `error` ALL count as content. An unresolved section is not an empty one, * and an errored section least of all - collapsing *"we could not load your allergies"* into a list * of things you do not have would be the same class of defect card 179 fixed in this very component */ const sectionHasContent = (sectionResults: SnapshotTypeResult[]): boolean => sectionResults.some( (r) => r.status === 'loading' || r.status === 'idle' || r.status === 'error' || (r.status === 'ready' && (r.items.length > 0 || r.truncatedCount > 0 || Boolean(r.omitsDeleted))), ); /** * The sections that have resolved to genuinely empty - named, in one line, for E7. * * A section with NO results yet is not listed. "Not initialised" and "resolved to nothing" are * different facts, and the first is what the global spinner covers. */ const emptySectionNames = SNAPSHOT_SECTION_ORDER.filter((section) => { const sectionResults = results.filter((r) => r.section === section); return sectionResults.length > 0 && !sectionHasContent(sectionResults); }); const renderSection = (section: string) => { const sectionResults = results.filter((r) => r.section === section); // Results not yet initialized for this section (first paint) - skip; the global // spinner covers the initial load. if (sectionResults.length === 0) return null; const hasContent = sectionHasContent(sectionResults); // Card 263 E7 โ€” the empty ones are gathered into a single line below instead of a card each. if (!hasContent && collapseEmptySections) return null; return ( {section} {hasContent ? ( sectionResults.map(renderTypeResult) ) : (

Nothing recorded {emptyLabel}.

)}
); }; const truncatedTypeNames = results .filter((r) => r.truncatedCount > 0) .map((r) => r.resourceType); // The encounter dialog shows the notes this chart resolved, never a fresh read of today's note. const resolvedNotes = results.find((r) => r.resourceType === 'ClinicalImpression'); const visitNote: VisitNoteSource | undefined = resolvedNotes && { kind: 'resolved', status: resolvedNotes.status, resources: resolvedNotes.items.map((item) => item.resource), incomplete: resolvedNotes.truncatedCount > 0 || Boolean(resolvedNotes.omitsDeleted), }; return ( <> {banner} {discloseTruncatedTypes && truncatedTypeNames.length > 0 && (

Some resource types could not be fully reconstructed at this point in time and were truncated:{' '} {truncatedTypeNames.join(', ')}.

)} {SNAPSHOT_SECTION_ORDER.map(renderSection)} {/* ๐Ÿšจ CARD 263 E7 โ€” ONE LINE, NAMING THEM, instead of one card each saying the same sentence. โญ It is strictly MORE informative than what it replaces: six cards each said "Nothing recorded currently" without saying nothing-of-WHAT, so a reader had to look at six headings to learn the same thing this line states outright. */} {collapseEmptySections && emptySectionNames.length > 0 && (

Nothing recorded {emptyLabel} in: {emptySectionNames.join(' ยท ')}.

)} {/* Read-only detail modals: only the selected record's own display is drawn, so a closing or other-type selection never hands a display a record it is not about. */} {selected?.resourceType === 'Condition' && ( )} {selected?.resourceType === 'AllergyIntolerance' && ( )} {selected?.resourceType === 'Procedure' && ( )} {selected?.resourceType === 'AdverseEvent' && ( )} {selected?.resourceType === 'ClinicalImpression' && ( )} {selected?.resourceType === 'DetectedIssue' && ( )} {selected?.resourceType === 'FamilyMemberHistory' && ( )} {/* These displays draw only a sheet's contents, so the sheet is ours to open. */} { if (!open) closeModal(); }} > {selected?.resourceType === 'Observation' && ( )} {selected?.resourceType === 'MedicationStatement' && ( )} {selected?.resourceType === 'MedicationRequest' && ( )} {selected?.resourceType === 'Device' && ( )} {selected?.resourceType === 'DeviceRequest' && ( )} {selected?.resourceType === 'DeviceUseStatement' && ( )} {selected?.resourceType === 'CarePlan' && ( )} {selected?.resourceType === 'CareTeam' && ( )} {selected?.resourceType === 'Goal' && ( )} {selected?.resourceType === 'NutritionOrder' && ( )} {selected?.resourceType === 'RiskAssessment' && ( )} {selected?.resourceType === 'VisionPrescription' && ( )} {selected?.resourceType === 'Task' && ( )} {selected?.resourceType === 'MedicationAdministration' && ( )} {selected?.resourceType === 'MedicationDispense' && ( )} {selected?.resourceType === 'Immunization' && ( )} {selected?.resourceType === 'ImmunizationEvaluation' && ( )} {selected?.resourceType === 'ImmunizationRecommendation' && ( )} {selected?.resourceType === 'Encounter' && ( )} {selected?.resourceType === 'EpisodeOfCare' && ( )} {selected?.resourceType === 'DocumentReference' && ( )} {selected?.resourceType === 'QuestionnaireResponse' && ( )} ); } export default SnapshotChart;