fix(measurements): tighten W7c grouped-view pagination and timezone comment

Self-review polish on top of the v1.4.37 W7c step-consolidation
landing:

- The collapsed daily view returns up to 5000 synthesised rows in
  one shot, so the legacy 25-row pagination would have re-fired the
  same scan with offset=0 returning the same slice. Disable
  pagination on the day-grouped path; per-sample lists keep their
  PAGE_SIZE=25 behaviour.
- Bump the per-page row scan from 1250 to the server max of 5000 so
  even chatty pre-drain Apple-Watch accounts (~200 chunks/day on
  busy days) get a useful number of daily rows on the first paint.
  Post-drain the scan is trivially short (each day reduces to one
  stats: row).
- Reset offset=0 explicitly on the grouped path so a sort-direction
  flip never paginates into an empty server slice.
- Clarify the day-boundary comment in the dayKey drill-down branch
  — the ±12h around canonical noon shift works for every IANA
  offset, including sub-half-hour zones (Asia/Kathmandu +5:45,
  Pacific/Chatham +12:45).
This commit is contained in:
Marc-André Bombeck committed 2026-05-17 14:38:44 +02:00
1 parent 1a61d25bdf
commit c7480e90e3
2 files changed
+27 -6

No files matched your search

+5 -1
View File
@@ -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);
@@ -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) {