From 51c40c61d9d81f7da5aacb4b3cebe7f67385fa4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 9 Aug 2026 04:08:57 +0200 Subject: [PATCH] fix(dashboard): show a read failure on the Vorsorge tile, not an empty state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tile already read the query's error flag but never branched on it, so a failed reminder read fell through to "no upcoming checkup reminders" — a missed preventive-care nudge hiding behind an honest-looking empty state. Add a QueryErrorRow with retry before the empty check. Test watched fail first against the fall-through. --- ...rsorge-dashboard-card-error-state.test.tsx | 57 +++++++++++++++++++ .../vorsorge-dashboard-card.tsx | 7 ++- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/components/measurement-reminders/__tests__/vorsorge-dashboard-card-error-state.test.tsx diff --git a/src/components/measurement-reminders/__tests__/vorsorge-dashboard-card-error-state.test.tsx b/src/components/measurement-reminders/__tests__/vorsorge-dashboard-card-error-state.test.tsx new file mode 100644 index 000000000..1caf6002e --- /dev/null +++ b/src/components/measurement-reminders/__tests__/vorsorge-dashboard-card-error-state.test.tsx @@ -0,0 +1,57 @@ +/** + * A reminder-read failure on the dashboard Vorsorge tile must not read as "no + * upcoming checkup reminders" — a missed preventive-care nudge is the harm + * behind that fall-through. `useMeasurementReminders` is forced into its error + * state; the tile has to render a `query-error-row` with retry and never the + * empty copy. + * + * SSR-static render; the query-backed hooks are mocked so the error branch is + * synchronous. + */ +import { describe, expect, it, vi } from "vitest"; +import { renderToStaticMarkup } from "react-dom/server"; + +vi.mock("@/hooks/use-measurement-reminders", () => ({ + useMeasurementReminders: () => ({ + data: undefined, + isError: true, + refetch: vi.fn(), + }), + useMeasurementReminderMutations: () => ({ + satisfy: { mutate: vi.fn(), isPending: false }, + }), +})); + +vi.mock("@/hooks/use-encounters", () => ({ + useEncounters: () => ({ data: { upcoming: [] } }), +})); + +vi.mock("@/hooks/use-record-capabilities", () => ({ + useRecordCapabilities: () => ({ canManage: true, canAdd: true }), +})); + +vi.mock("next/navigation", () => ({ + useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), + usePathname: () => "/", + useSearchParams: () => new URLSearchParams(), +})); + +import { I18nProvider } from "@/lib/i18n/context"; +import { VorsorgeDashboardCard } from "../vorsorge-dashboard-card"; + +function render() { + return renderToStaticMarkup( + + + , + ); +} + +describe(" — a read failure is honest, not empty", () => { + it("renders the query-error row with retry and never the empty copy", () => { + const html = render(); + expect(html).toContain('data-slot="query-error-row"'); + expect(html).toContain('data-slot="query-error-row-retry"'); + expect(html).not.toContain("No upcoming checkup reminders"); + }); +}); diff --git a/src/components/measurement-reminders/vorsorge-dashboard-card.tsx b/src/components/measurement-reminders/vorsorge-dashboard-card.tsx index 5f65c474e..848e5e996 100644 --- a/src/components/measurement-reminders/vorsorge-dashboard-card.tsx +++ b/src/components/measurement-reminders/vorsorge-dashboard-card.tsx @@ -32,6 +32,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { ListRow } from "@/components/ui/list-row"; +import { QueryErrorRow } from "@/components/ui/query-error-row"; import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; import { Skeleton } from "@/components/ui/skeleton"; import { MeasurementForm } from "@/components/measurements/measurement-form"; @@ -78,7 +79,7 @@ export function VorsorgeDashboardCard() { // the dashboard summary offering what the dedicated page withholds was the // tell that this row had never been asked. const { canManage } = useRecordCapabilities(); - const { data: reminders, isError } = useMeasurementReminders(); + const { data: reminders, isError, refetch } = useMeasurementReminders(); // A query's `isLoading` is not a hydration-safe branch: TanStack reports the // optimistic mount fetch on the client's very first render and cannot on the @@ -153,6 +154,10 @@ export function VorsorgeDashboardCard() { ))} + ) : isError ? ( + // A read failure must not read as "no upcoming reminders" — a missed + // checkup is the harm behind that fall-through (§6). + refetch()} /> ) : upcoming.length === 0 ? (