diff --git a/src/components/charts/__tests__/health-chart-range.test.tsx b/src/components/charts/__tests__/health-chart-range.test.tsx index bf01f2394..0610579e1 100644 --- a/src/components/charts/__tests__/health-chart-range.test.tsx +++ b/src/components/charts/__tests__/health-chart-range.test.tsx @@ -84,6 +84,36 @@ describe(" — bounded range fetch", () => { expect(measurementsUrl!).toMatch(/type=PULSE/); // Guard against the legacy unbounded paginator returning. expect(measurementsUrl!).not.toMatch(/offset=/); + // v1.4.29 C3 — the default 30-day window must request server- + // side daily aggregation so the client doesn't pay for ~5000 + // raw pulse rows on every range-tab change. + expect(measurementsUrl!).toMatch(/aggregate=daily/); + }); + + it("omits aggregate=daily on windows of 7 days or fewer", async () => { + // v1.4.29 C3 — short windows keep raw fetching so the user can + // see hour-by-hour detail on the 7-day view. + const { I18nProvider } = await import("@/lib/i18n/context"); + const { HealthChart } = await import("../health-chart"); + + renderToStaticMarkup( + + + , + ); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + const measurementsUrl = fetchCalls.find((u) => + u.includes("/api/measurements"), + ); + expect(measurementsUrl).toBeDefined(); + expect(measurementsUrl!).not.toMatch(/aggregate=daily/); }); it("re-keys the query when the range window changes", async () => { diff --git a/src/components/charts/health-chart.tsx b/src/components/charts/health-chart.tsx index 0dcfbc535..fd33024b5 100644 --- a/src/components/charts/health-chart.tsx +++ b/src/components/charts/health-chart.tsx @@ -577,6 +577,15 @@ export function HealthChart({ typeParams.set("from", fetchWindow.from); typeParams.set("to", fetchWindow.to); typeParams.set("limit", "5000"); + // v1.4.29 C3 — windows over 7 days ask the server to bucket + // daily. Caps the chart's per-type payload at ~365 rows + // instead of ~5 000 for high-density types (pulse), and drops + // Recharts paint cost ~50× on continuous-monitoring accounts. + // Short windows keep raw fetching so hour-by-hour detail + // stays visible. + if (fetchWindow.windowDays > 7) { + typeParams.set("aggregate", "daily"); + } const res = await fetch(`/api/measurements?${typeParams}`); if (!res.ok) return;