Skip to content

2026-07-17 — XP-143 keyset pagination for settlement lists

  • packages/service/src/finance/list-student-settlements.ts + list-tutor-settlements.ts — forward-only keyset: where composes the optional status eq with the seek tuple (created_at, id) DESC via RQBv2 RAW on the aliased root table, fetch limit+1 to detect more, response { items, next } (was a bare array). next set only when a full page returned.
  • packages/contract/src/service/finance/list-student-settlements.schema.ts + tutor twin — input +limit (default 20, max 100) +next cursor; response shape updated.
  • apps/web/src/lib/api-client.ts + routes/admin/finance/settlements.tsx + settlements-tutor.tsxSettlementsPagination (Awal reset / Berikutnya next, no Back), cursor state, data.items, reset-on-filter effect.
  • packages/service/src/finance/list-pagination.integration.test.ts — +2 cases (student, tutor); apps/web/e2e/settlements-pagination-visual.spec.ts (new 3-viewport visual e2e).
  • Mechanical mirror of the shipped XP-134 keyset pattern (list-refundable-settlements / list-refunds → itself mirrors users.list). No new pagination shape invented; per-endpoint file duplication is the endorsed house style, not a smell.
  • Selection stays per-page (forward-only + reset-to-first, no Back); bulk mark-paid invalidates the all query key so it covers every cursor.
  • The reset-on-filter useEffect needs biome-ignore useExhaustiveDependenciesstatusFilter is a trigger-only dep the body doesn’t read; this rule is NOT downgraded to warn in biome.json, so without the ignore bun run lint hard-fails (2 errors). Matches sidebar.tsx / review-apply-dialog.tsx convention.
  • Full bun run typecheck halts on a pre-existing apps/docs TS18003 “no inputs” quirk, unrelated to this diff (confirmed via git stash on clean tree).

None. No canonical doc (plans/ARCHITECTURE.md, plans/scope/*.md, .claude/rules/*.md) changed.