import React from 'react'; import { FlaskConical, Pill, TriangleAlert, Stethoscope, Activity } from 'lucide-react'; import './EncounterChartPanel.css'; import type { SinceLastVisitResult } from './sinceLastVisit'; import { selectCounts, type Countable } from '../../../utils/counting/countingSelector'; import type { SnapshotItem } from '../../../hooks/usePatientSnapshot'; // - the FIRST adoption of the shared counting selector, and this surface is why the card exists. // Before this, ONE set was counted FOUR separate times in adjacent lines: // - the card header `totalCount` - accumulated in a parallel `let` inside sinceLastVisit.ts // - each group's header `group.items.length` // - each group's visible list `group.items.slice(0, 3)` // - each group's overflow `group.items.length - 3` // They agreed, which is exactly why this was invisible: four expressions kept in sync by hand agree right // up until one of them grows a filter. That is B16's shape - a row that meant two things - and B28's. // // THE FIX IS STRUCTURAL, NOT ARITHMETIC. `visible` and `more` now come off ONE slice, so // `visible.length + more === total` holds BY CONSTRUCTION rather than by two expressions agreeing. // Do not reintroduce a second derivation "just for the header" - that is the defect, and it will look // correct on the day it is written. // // The card total is derived from THE GROUPS THIS CARD RENDERS, not from `result.totalCount`. A header // must count the list beneath it; taking the number from a parallel accumulation is how a strip and its // own list come to disagree. `result.totalCount` is left alone - other callers may read it. // ONE literal for how many are previewed. Before the selector this number appeared TWICE - once in // `slice(0, 3)` and once in `length - 3` - and a mutation restoring that pair is *arithmetically // identical* while both say 3, so no fixture can tell them apart. The drift only appears the day someone // changes one of them. The defence is that there is now only one to change. const PREVIEW_LIMIT = 3; const toCountable = (item: SnapshotItem): Countable => ({ id: item.id, label: item.label, // The age that matters is how long it has been waiting, which for a snapshot item is its last update. since: item.lastUpdated, }); const ICON: Record = { Observation: , MedicationRequest: , MedicationStatement: , AllergyIntolerance: , Condition: , }; const fmtDate = (iso?: string): string => iso ? new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) : ''; interface Props { result: SinceLastVisitResult; running?: boolean; } export const SinceLastVisit: React.FC = ({ result, running }) => { const { lastVisitISO, groups } = result; /** * NOTHING IN THIS CARD MAY ASSERT A FACT UNTIL THE SNAPSHOT HAS COME BACK. * * Measured on the clinician overview, 2026-08-22, sampling every 400ms through a real load: * 5.2s "- 0 updates - **First visit on record.** - No changes since the last visit." * 6.4s "First visit on record. - Checking what's changed..." * 9.2s "Last seen **Aug 7, 2026** - anchored care view. - Checking what's changed..." * 10.0s "- 0 updates - Last seen Aug 7, 2026 - anchored care view. - No changes since the last visit." * * For ~4 seconds a clinician about to open an encounter was told this was the patient's **first * visit**, and at 5.2s it carried a settled "0 updates / no changes" badge, so it did not even read as * still loading. It looked finished and it was wrong. * * `running` ALONE IS NOT ENOUGH, which is the part worth keeping: the first paint had * `running === false` because the query had not started yet. **Not-started and finished-and-empty are * the same shape**, so the card needs `loaded` as well the same class as summary band * reporting "0 invoices unpaid" before admitting there were two. */ const settled = result.loaded && !running; // One derivation per level, all from the same items - see the note above `toCountable`. const cardView = selectCounts(groups.flatMap((group) => group.items.map(toCountable))); const groupViews = groups.map((group) => ({ group, view: selectCounts(group.items.map(toCountable), { visibleLimit: PREVIEW_LIMIT }), })); return (

Since your last visit {settled && ( {' '}· {cardView.total} update{cardView.total === 1 ? '' : 's'} )}

{/* ⛔ Three states, not two. The middle one is the whole point — see `settled` above. */} {!settled ? Looking up this patient's last visit… : lastVisitISO ? <>Last seen {fmtDate(lastVisitISO)} — anchored care view. : <>First visit on record.}

{!settled && groups.length === 0 && (

Checking what's changed…

)} {settled && groups.length === 0 && (

No changes since the last visit.

)}
    {groupViews.map(({ group, view }) => (
  • {ICON[group.resourceType] ?? }
    {group.label} · {view.total} new/updated
    {view.visible.map((i) => i.label).join(' · ')} {view.more > 0 && ` +${view.more} more`}
  • ))}
); };