perf(charts): wire aggregate=daily for windows beyond a week

The chart fetcher always pulled raw rows from `/api/measurements` —
up to ~5 000 pulse samples per range-tab change on a continuous-
monitoring account. The server's `aggregate=daily` SQL path lands
in v1.4.29 C2; this commit teaches `<HealthChart>` to ask for it
on every window over 7 days. Short windows (7-day overview) keep
raw fetching so hour-by-hour detail stays visible.

Effect on a 30-day pulse view: payload drops from up to ~5 000
rows to one row per day. The client-side `dailyAggregates` Map
becomes a no-op on already-bucketed rows.

Range test gains two assertions: the default 30-day window asks
for `aggregate=daily`; the `windowOverride="last7days"` mini-chart
omits the parameter.
This commit is contained in:
Marc-André Bombeck committed 2026-05-16 10:38:27 +02:00
1 parent 642e78a6f7
commit cdc9c5f27f
2 files changed
+39

No files matched your search

@@ -84,6 +84,36 @@ describe("<HealthChart> — 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(
<I18nProvider initialLocale="en">
<HealthChart
types={["PULSE"]}
title="Pulse"
unit="bpm"
windowOverride="last7days"
/>
</I18nProvider>,
);
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 () => {
+9
View File
@@ -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;