mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
fix(dashboard): show a read failure on the Vorsorge tile, not an empty state
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.
This commit is contained in:
1 parent
48e4882400
commit
51c40c61d9
2 files changed
+63
-1
No files matched your search
+57
@@ -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(
|
||||
<I18nProvider initialLocale="en">
|
||||
<VorsorgeDashboardCard />
|
||||
</I18nProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("<VorsorgeDashboardCard> — 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");
|
||||
});
|
||||
});
|
||||
@@ -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() {
|
||||
<Skeleton key={i} className="h-12 w-full" />
|
||||
))}
|
||||
</div>
|
||||
) : isError ? (
|
||||
// A read failure must not read as "no upcoming reminders" — a missed
|
||||
// checkup is the harm behind that fall-through (§6).
|
||||
<QueryErrorRow onRetry={() => refetch()} />
|
||||
) : upcoming.length === 0 ? (
|
||||
<div className="space-y-2">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
|
||||
Reference in new issue
Block a user