ffa8e0d480
Two surfaces had invisible 50-row caps that this commit removes.
Exercise history popup (clock button in WorkoutForm):
- /api/exercises/[id] now accepts ?offset=N&limit=N (default 25,
max 100) and returns { exercise, history, hasMore }. Pagination
uses take: limit + 1 to detect hasMore without a second COUNT
round-trip.
- Query rewritten to use Prisma's setLogs.some filter — single SQL
that hits the (userId, deletedAt, date) composite index, instead
of fetching all set logs then grouping in JS.
- ExerciseHistoryPopup now uses an IntersectionObserver on a
sentinel div. When sentinel scrolls into view (root: the popup
itself, not the viewport), fetches next page and appends. Status
row at the bottom shows a spinner while loading and "End of
history" when done.
- Container max height bumped from h-64 -> h-80 for a bit more
breathing room on first render.
Workout history page (/main/workouts):
- Page still server-renders the first 50 workouts (instant paint
+ correct date filter forwarding). Now uses take: PAGE_SIZE + 1
to detect hasMore.
- New WorkoutsList client component takes initial workouts +
hasMore + filter values as props. IntersectionObserver on a
sentinel below the cards auto-fetches the next page from
/api/workouts?offset=N&limit=50&q=...&dateFrom=...&dateTo=...
when scrolled to. Filters round-trip through URL params, so a
filter change re-renders the page from scratch with a fresh
first page.
- "End of history · N workouts" line shown once everything is
loaded.
Tests:
- tests/routes-exercise-history.test.ts: 6 new tests covering
auth, cross-user 404, first-page hasMore=true, second-page
hasMore=false + no overlap, set-log filter scoped to the
queried exerciseId, soft-deleted workouts excluded.
- All 87 tests pass.
No schema changes, no migration. /data untouched.
43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
|
|
|
/**
|
|
* v1.0.0:6 — paginate history, no more 50-row caps.
|
|
*
|
|
* Two surfaces had hard 50-row caps that were invisible to the user:
|
|
*
|
|
* - The clock-button "Exercise History" popup in the workout
|
|
* logging form: only ever showed the most recent 50 workouts
|
|
* containing that exercise. No way to see further back.
|
|
*
|
|
* - The /main/workouts page: only ever rendered the most recent
|
|
* 50 workouts. The only way to reach older ones was the date
|
|
* filter, but you had to know the date.
|
|
*
|
|
* Both now use server-side pagination + client-side infinite scroll
|
|
* via IntersectionObserver. The first page renders identically to
|
|
* before (instant paint, server-rendered for /main/workouts; first
|
|
* 25 fetched on popup open). Subsequent pages auto-load when the
|
|
* sentinel element scrolls into view. "End of history · N workouts"
|
|
* shown once everything is loaded.
|
|
*
|
|
* Server queries use the `take: limit + 1` trick to detect hasMore
|
|
* without a second COUNT() round-trip. The exercise-history query
|
|
* was also rewritten to use Prisma's `setLogs.some` filter
|
|
* (single SQL, hits the (userId, deletedAt, date) composite index)
|
|
* instead of fetching all set logs and grouping in JS.
|
|
*
|
|
* No schema changes, no migration, no data movement. /data is
|
|
* untouched.
|
|
*/
|
|
export const v_1_0_0_6 = VersionInfo.of({
|
|
version: '1.0.0:6',
|
|
releaseNotes: {
|
|
en_US:
|
|
'Paginate workout history. The clock-button "Exercise History" popup in the workout logger now scrolls infinitely to load older workouts. The /main/workouts page now does the same — scroll to the bottom and the next page auto-loads. No more invisible 50-row cap. No data migration; existing /data untouched.',
|
|
},
|
|
migrations: {
|
|
up: async () => {},
|
|
down: IMPOSSIBLE,
|
|
},
|
|
})
|