fix/rpu-12-digit
needs attentionviewing older commit77a5d70 · incrementalPR #246reviewed 2026-07-05 21:09 UTC0H · 2M · 9L · 12I- Purpose
- Fix blank toast and frozen submit button when creating a contract with a short RPU — the drawer was marking an 11-digit RPU as 'Disponible' and the form had no useful error surfacing on the server's 400 ZodError rejection.
- Goal
- Add explicit RPU validation at both entry points so incomplete RPUs never reach the server, and surface meaningful errors when they do.
- Sub-goals
- SG-1: RPU_LENGTH constant as single source of truth
- SG-2: normalizeRpuInput — strip non-digits, cap at 12 — applied at all write paths
- SG-3: AddContractDrawer: lookup gate at exactly 12 digits
- SG-4: InlineContractForm: client-side guard + descriptive toast via extractServerErrorMessage
- SG-5: Test coverage for pure rule logic, drawer gate, and form error paths
- What
- Added RPU_LENGTH + normalizeRpuInput to rpu-errors.ts. AddContractDrawer now normalizes on onChange and onExtracted, maxLength changed 14→12. InlineContractForm uses RPU_LENGTH direct length check. Tests expanded: normalizeRpuInput suite, pluralization, empty-string, ALREADY_EXISTS fallback, container-scoped form query.
- Why
- Prior loop review (586531a5) flagged 13-digit permanent-loading, flaky setTimeout, and missing test cases. This commit addresses all of them.
- Areas
- apps/platform/src/app/[locale]/(dashboard)/bills/_lib/rpu-errors.ts+23−0apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx+14−6apps/platform/src/app/[locale]/(dashboard)/bills/_components/InlineContractForm.tsx+25−4apps/platform/src/app/[locale]/(dashboard)/bills/_lib/__tests__/rpu-errors.test.ts+108−0apps/platform/src/app/[locale]/(dashboard)/bills/_components/__tests__/AddContractDrawer.rpuGate.test.tsx+110−0apps/platform/src/app/[locale]/(dashboard)/bills/_components/__tests__/InlineContractForm.rpuError.test.tsx+143−0
- Blast
- 8 files, +472/-12. Confined to /bills route-scope. No backend, API schema, or migration changes.
Findings · 11
correctness2
maxLength=12 truncates pasted formatted RPUs before normalizeRpuInput runs
apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:415
Browser enforces maxLength before onChange fires. Pasting '1234 5678 9012' (14 chars) truncates to '1234 5678 90' — normalizeRpuInput yields only 10 digits. Remove maxLength or raise to ≥16.
Telemetry rpu_length tracks raw PDF string length, not digit count
apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:366
security1
Server schema missing .regex(/^\d{12}$/) on contractNumber
packages/api/src/schemas/utility-contract.schemas.ts:353
conventions3
Literal 12 used in AddContractDrawer instead of RPU_LENGTH
apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:176
Lines 176 and 389 hardcode 12. RPU_LENGTH was introduced for single-source-of-truth but AddContractDrawer doesn't import it.
Same 12-digit predicate expressed two ways across call sites
apps/platform/src/app/[locale]/(dashboard)/bills/_components/InlineContractForm.tsx:150
onChange comment's first clause describes what, not just why
apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:418
tests2
normalizeRpuInput('') not tested
apps/platform/src/app/[locale]/(dashboard)/bills/_lib/__tests__/rpu-errors.test.ts:39
ALREADY_EXISTS → createFailed fallback not integration-tested in InlineContractForm
apps/platform/src/app/[locale]/(dashboard)/bills/_components/__tests__/InlineContractForm.rpuError.test.tsx:85
improvement3
isValidRpu encodes literal 12 in regex instead of delegating to RPU_LENGTH
apps/platform/src/app/[locale]/(dashboard)/bills/_lib/rpu-errors.ts:24
digits useMemo strips non-digits from already-digit-only value (no-op)
apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:147
isValidRpu(debounced) enabled flag is always equivalent to !!debounced
apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:166
History · 6 commits
- 427115eneeds attentionincremental0H · 1M · 3L2026-07-05 21:43
- c6ce7d8needs attentionincremental0H · 3M · 6L2026-07-05 21:35
- d8cbd91needs attentionincremental0H · 0M · 3L2026-07-05 21:25
- 20aa256safeincremental0H · 0M · 2L2026-07-05 21:14
- 77a5d70needs attentionincremental0H · 2M · 9L2026-07-05 21:09current
- 586531aneeds attentionfull0H · 5M · 10L2026-07-05 20:51