mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
fix(insights): recoverable read failures on the ECG detail and list page
The ECG detail rendered a read failure as a muted, non-recoverable line; the routed list page had no error branch at all, leaving the shell heading over an empty body that read the same as loading. The detail now paints a QueryErrorCard with retry (an alert is content, not muted meta), and the page shows the same instead of an empty shell. The overview teaser still unmounts silently, which is right for the overview. Tests watched each fail first.
This commit is contained in:
1 parent
22d5c01a68
commit
1229d54d30
4 files changed
+72
-12
No files matched your search
@@ -78,18 +78,21 @@ describe("<EcgDetail> — non-diagnostic framing", () => {
|
||||
expect(html).toContain("as reported by the recording device");
|
||||
});
|
||||
|
||||
it("says a failed read failed instead of painting an empty recording", () => {
|
||||
it("shows a recoverable read failure instead of painting an empty recording", () => {
|
||||
useQueryMock.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
isError: true,
|
||||
refetch: vi.fn(),
|
||||
});
|
||||
const html = renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="en">
|
||||
<EcgDetail recordingId="ecg_1" />
|
||||
</I18nProvider>,
|
||||
);
|
||||
expect(html).toContain('data-slot="ecg-detail-error"');
|
||||
// The failure is a recoverable card, not a muted "no data" line.
|
||||
expect(html).toContain('data-slot="query-error-card"');
|
||||
expect(html).toContain('data-slot="query-error-retry"');
|
||||
expect(html).not.toContain('data-slot="ecg-trace"');
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { apiGet } from "@/lib/api/api-fetch";
|
||||
import { useTranslations, useFormatters } from "@/lib/i18n/context";
|
||||
import { EcgWaveform } from "@/components/insights/ecg-waveform";
|
||||
import { InsightSectionCard } from "@/components/insights/insight-section-card";
|
||||
import { QueryErrorCard } from "@/components/ui/query-error-card";
|
||||
import {
|
||||
ecgResultLabel,
|
||||
isNonNormalEcg,
|
||||
@@ -41,7 +42,7 @@ export function EcgDetail({ recordingId }: { recordingId: string }) {
|
||||
const { t } = useTranslations();
|
||||
const fmt = useFormatters();
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
const { data, isLoading, isError, refetch } = useQuery({
|
||||
queryKey: queryKeys.insightsEcgDetail(recordingId, false),
|
||||
queryFn: async () => {
|
||||
try {
|
||||
@@ -55,15 +56,13 @@ export function EcgDetail({ recordingId }: { recordingId: string }) {
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
// A failed read must never read as "no data" (UI-STANDARDS §6).
|
||||
// A failed read must never read as "no data" (UI-STANDARDS §6), and the
|
||||
// alert is content, not muted meta — it also has to be recoverable.
|
||||
return (
|
||||
<p
|
||||
data-slot="ecg-detail-error"
|
||||
role="alert"
|
||||
className="text-muted-foreground text-sm"
|
||||
>
|
||||
{t("insights.ecg.loadError")}
|
||||
</p>
|
||||
<QueryErrorCard
|
||||
title={t("insights.ecg.loadError")}
|
||||
onRetry={() => refetch()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user