perf(insights): mount the medication efficacy summary when near

Insights > Medications sent two requests per medication on entry: the
compliance calendar inside each card and the efficacy read behind the
summary card, which sits below every card and shows one line per
medication. The summary now mounts through `DeferUntilNear`, like the
overview's sections, so the efficacy reads go out only when the reader
scrolls toward it. The cards and what the summary shows are unchanged.

The comment that called the summary "never a second computation" goes:
the efficacy route builds the view on every request.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 22:05:48 +01:00
1 parent d310058bc5
commit 6a1c91d5d4
2 files changed
+88 -6

No files matched your search

@@ -0,0 +1,81 @@
import { describe, expect, it, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import type { ReactNode } from "react";
/**
* Insights > Medications: the efficacy summary sits below every medication
* card and asks one efficacy read per medication. On entry it holds its slot
* with a sentinel and mounts only when scrolled near, so opening the page
* sends the cards' reads and not the summary's.
*/
vi.mock("@tanstack/react-query", () => ({
useQuery: ({ queryKey }: { queryKey: readonly unknown[] }) =>
queryKey[1] === "comprehensive"
? {
data: {
medications: [
{
id: "m1",
name: "Ramipril",
dose: "5 mg",
category: "BLOOD_PRESSURE",
compliance7: 100,
compliance30: 95,
streak: 7,
taken7: 7,
skipped7: 0,
missed7: 0,
},
],
},
isLoading: false,
isError: false,
}
: { data: undefined, isLoading: false, isError: false },
}));
vi.mock("@/hooks/use-auth", () => ({
useAuth: () => ({ isAuthenticated: true }),
}));
vi.mock("@/hooks/use-ai-capability", () => ({
useAiCapability: () => ({ available: false }),
useAiProviderState: () => ({ responseTimeoutMs: null }),
}));
vi.mock("@/components/insights/medication-efficacy-summary", () => ({
MedicationEfficacySummary: () => <div data-slot="efficacy-summary-mounted" />,
}));
vi.mock("@/components/insights/sub-page-shell", () => ({
SubPageShell: ({ children }: { children: ReactNode }) => <>{children}</>,
}));
vi.mock("@/components/insights/glp1-substance-curve-summary", () => ({
Glp1SubstanceCurveSummary: () => null,
}));
vi.mock("@/components/insights/metric-target-summary", () => ({
MetricTargetSummary: () => null,
}));
vi.mock("@/components/insights/insight-status-card", () => ({
InsightStatusCard: () => null,
}));
vi.mock("@/components/insights/coach-launch-button", () => ({
CoachLaunchButton: () => null,
}));
vi.mock("@/components/charts/compliance-heatmap", () => ({
ComplianceHeatmap: () => null,
}));
import { I18nProvider } from "@/lib/i18n/context";
import InsightsMedikamentePage from "../page";
describe("/insights/medications on entry", () => {
it("renders the cards and holds the efficacy summary until scrolled near", () => {
const html = renderToStaticMarkup(
<I18nProvider initialLocale="en">
<InsightsMedikamentePage />
</I18nProvider>,
);
expect(html).toContain("Ramipril");
expect(html).toContain('data-deferred-section="medication-efficacy"');
expect(html).not.toContain("efficacy-summary-mounted");
});
});
+7 -6
View File
@@ -24,6 +24,7 @@ import { CoachLaunchButton } from "@/components/insights/coach-launch-button";
import { InsightStatusCard } from "@/components/insights/insight-status-card";
import { MetricTargetSummary } from "@/components/insights/metric-target-summary";
import { MedicationEfficacySummary } from "@/components/insights/medication-efficacy-summary";
import { DeferUntilNear } from "@/components/insights/defer-until-near";
import { Glp1SubstanceCurveSummary } from "@/components/insights/glp1-substance-curve-summary";
import { SubPageShell } from "@/components/insights/sub-page-shell";
import { TileHeader } from "@/components/insights/tile-header";
@@ -321,12 +322,12 @@ export default function InsightsMedikamentePage() {
})}
</div>
{/* v1.28 — compact efficacy summary: reuses the same server-computed
efficacy DTO the detail-page "Wirkung" tab reads (never a second
computation), association-only, linking through to the full view. */}
<MedicationEfficacySummary
medications={medications.map((m) => ({ id: m.id, name: m.name }))}
/>
{/* v1.28 — one efficacy read per medication, below every card: mounted when near. */}
<DeferUntilNear id="medication-efficacy">
<MedicationEfficacySummary
medications={medications.map((m) => ({ id: m.id, name: m.name }))}
/>
</DeferUntilNear>
{/* v1.28.20 — estimated active-substance curve for GLP-1 medications
(additive; renders nothing when the user has no GLP-1 medication). */}