From a1b56f0b75ada7c2a8e83c6cc04a3436a2189f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 20 Oct 2026 10:10:36 +0200 Subject: [PATCH] fix(visits): keep the since-last-visit card on a failed read A failed read replaced the card and its title with a bare row, so the section changed height and the reader lost what had failed. The card now keeps its header and shows the error inside. The visit card's header moves onto TileHeader instead of its own icon and title row. --- src/components/day/since-last-visit.tsx | 30 +++++++++++++---- .../__tests__/procedures-section.test.tsx | 2 +- .../__tests__/visits-section.test.tsx | 5 ++- src/components/encounters/visit-card.tsx | 32 +++++++++---------- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/components/day/since-last-visit.tsx b/src/components/day/since-last-visit.tsx index 7793771ba..6a0aff03f 100644 --- a/src/components/day/since-last-visit.tsx +++ b/src/components/day/since-last-visit.tsx @@ -194,14 +194,31 @@ export function SinceLastVisit({ }) : []; + const lastLabel = fmt.dateShortSmartCalendar(lastDay); + + // A failed read keeps the card and its title, so the page does not jump + // and the reader still sees what could not be loaded. if (window.isError && rows.length === 0) { return ( - void window.refetch()} - /> + + + + + + void window.refetch()} + /> + + ); } // Nothing to show is nothing drawn: still loading, or a quiet window. The @@ -209,7 +226,6 @@ export function SinceLastVisit({ if (rows.length === 0) return null; const visible = showAll ? rows : rows.slice(0, VISIBLE_ROWS); - const lastLabel = fmt.dateShortSmartCalendar(lastDay); const nextDay = dateKeyOfInstant(next.occurredAt, timeZone); return ( diff --git a/src/components/encounters/__tests__/procedures-section.test.tsx b/src/components/encounters/__tests__/procedures-section.test.tsx index 515c593cb..7d6233b39 100644 --- a/src/components/encounters/__tests__/procedures-section.test.tsx +++ b/src/components/encounters/__tests__/procedures-section.test.tsx @@ -164,7 +164,7 @@ describe("", () => { bodySites: [], total: 1, }); - expect(html).toMatch(/font-medium">Meniscus repair]*>Meniscus repair since the last visit", () => { expect(html).toContain('data-slot="since-last-visit-error"'); expect(html).toContain('role="alert"'); expect(html).not.toContain('data-slot="since-last-visit"'); - expect(upcomingCards(html)).toBe(upcomingCards(baseline)); + // The failed window keeps its own card and title, so the page does not + // jump; the visit cards beside it stay as they were. + expect(upcomingCards(html)).toBe(upcomingCards(baseline) + 1); + expect(html).toContain("Since the last visit on"); }); // Visits need the profile section, the window reads measurements, and a diff --git a/src/components/encounters/visit-card.tsx b/src/components/encounters/visit-card.tsx index 2377256e9..b8b662be6 100644 --- a/src/components/encounters/visit-card.tsx +++ b/src/components/encounters/visit-card.tsx @@ -28,6 +28,7 @@ import { encounterKindText, encounterStatusText, } from "./encounter-labels"; +import { TileHeader } from "@/components/insights/tile-header"; /** The two statuses worth calling out on a row; the rest the section implies. */ function isNoteworthy(status: EncounterStatus): boolean { @@ -97,23 +98,20 @@ export function VisitCard({ > -
- -
-

- {heading} -

-

- {format.dateTime(encounter.occurredAt)} -

-
- - {encounterKindText(t, encounter.kind as EncounterKind)} - -
+ + {encounterKindText(t, encounter.kind as EncounterKind)} + + } + /> +

+ {format.dateTime(encounter.occurredAt)} +

{reasonLine ? (

{reasonLine}