Skip to content

2026-07-17 — XP-134 Refund/settlement pagination + error-body sanitize

  • Keyset (forward-only, cursor+limit) pagination on listRefundableSettlements and listRefunds — previously findMany over the whole table with nested relations, a Worker CPU/memory blow-up as settlements accrue. Contract input gains { limit (coerce, 1–100, default 20), next: uuid? }; response goes bare-array → { items, next? }, mirroring the users.list keyset shape. UI (/admin/finance/refunds) gets a per-panel forward-only pager (“Awal”/“Berikutnya”), same as the admin-users pager.
  • Global non-JSON error-body sanitize: extracted parseApiErrorBody(status, rawBody) in api-client.ts. A JSON oRPC object error still passes message+data.code through; anything else (HTML CF error page, plain text, bare scalar, empty {}) collapses to a concise Indonesian sentence — never the raw body. Fixes the incident where a 5xx HTML page rendered as a wall of raw HTML in the error display.
  • Kept the per-file convention (two client fns, two schemas, two service files repeat the keyset shape) matching the repo’s users.list lazy-load isolation, rather than extracting a shared helper — advisory-only per standards review.
  • Non-JSON messages surface clean; JSON errors keep the dev API <method> <url> → debug prefix.
  • Drizzle RQBv2 (1.0.0-rc.3) where is an object DSL, not raw SQL. A raw SQL seek predicate emits WHERE "refunds"."requested_at" < … while the query aliases the root table as d0 → Postgres 42P01 invalid reference to FROM-clause entry. Fix: the v2 RAW operator’s callback form { RAW: (t) => or(lt(t.col,…), and(eq(t.col,…), lt(t.id,…))) }t is the aliased root, so the comparison binds to d0. (Covered by .claude/rules/drizzle-relational-raw-alias.md.)
  • A static screenshot cannot distinguish overflow-x-auto scroll from silent overflow, nor measure a button’s touch-target hit-area — added that nuance to frontend-visual-test.md.
  • .claude/rules/frontend-visual-test.md — added the “PNG can’t prove scroll vs overflow, nor tap-target size; flag for manual scroll/tap check” note to the review checklist.