diff --git a/src/app/api/measurements/route.ts b/src/app/api/measurements/route.ts index 4f37f2310..c4acc89c7 100644 --- a/src/app/api/measurements/route.ts +++ b/src/app/api/measurements/route.ts @@ -87,7 +87,11 @@ export const GET = apiHandler(async (request: NextRequest) => { // Resolve `[dayStart, dayEnd)` for the requested calendar day in // the user's local zone. We piggy-back on `canonicalDailyTimestamp` // (which returns the local-noon instant) and shift ±12 h to land - // on the day boundary — works for whole-hour and half-hour zones. + // on the day boundary. Works for every IANA offset — including + // sub-half-hour zones like Asia/Kathmandu (UTC+5:45) and + // Pacific/Chatham (UTC+12:45) — because the noon instant already + // carries the zone's offset and the ±12 h shift is independent + // of it. const noonLocal = canonicalDailyTimestamp(dayKey, tz); const dayStart = new Date(noonLocal.getTime() - 12 * 60 * 60 * 1000); const dayEnd = new Date(noonLocal.getTime() + 12 * 60 * 60 * 1000); diff --git a/src/components/measurements/measurement-list.tsx b/src/components/measurements/measurement-list.tsx index a46764990..4946b37b2 100644 --- a/src/components/measurements/measurement-list.tsx +++ b/src/components/measurements/measurement-list.tsx @@ -234,10 +234,18 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) { if (isCumulativeFilter) { params.set("groupBy", "day"); // The collapsed mode synthesises one row per day from the - // window of raw samples — bump the per-page row scan so a - // wide history can render a full page of daily rows without - // missing back-fill from a chatty Apple-Watch day. - params.set("limit", String(Math.max(PAGE_SIZE * 50, 1000))); + // window of raw samples. Use the server-side ceiling (5000) + // so a chatty pre-drain Apple-Watch account (≈200 per-sample + // chunks/day on the busiest days) still fills several screens + // of daily rows before the nightly drain reduces each day to + // a single `stats:` row. Post-drain the scan is trivially + // short — every day reduces to 1 row. + params.set("limit", "5000"); + // The synthesised rows live entirely in the collapse branch + // — pagination/offset don't apply on the server side. Reset + // the offset so a sort-direction flip doesn't paginate into + // an empty slice. + params.set("offset", "0"); } const res = await fetch(`/api/measurements?${params}`); if (!res.ok) throw new Error("Failed to fetch"); @@ -321,7 +329,16 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) { }, }); - const totalPages = data ? Math.ceil(data.meta.total / PAGE_SIZE) : 0; + // v1.4.37 W7c — the collapsed daily view paints up to 5000 + // synthesised rows in one shot (one row per day for the + // configured window). Pagination would force a second server + // round-trip that re-fires the same scan, so the day-grouped + // path collapses to a single page. Per-sample lists continue to + // paginate at PAGE_SIZE = 25. + const totalPages = + data && !isCumulativeFilter + ? Math.ceil(data.meta.total / PAGE_SIZE) + : 0; function startEdit(measurement: Measurement) { if (onEdit) {