← all branches

fix/rpu-12-digit

needs attentionviewing older commit
586531a · fullPR #246reviewed 2026-07-05 20:51 UTC0H · 5M · 10L · 4I
The branch
Purpose
Fix broken UX when creating a CFE contract with an incomplete RPU — 11-digit RPU was shown as 'Disponible' and triggered a server-side ZodError with blank toast + frozen submit.
Goal
Ensure only valid 12-digit RPUs reach the contract create endpoint; surface real server error messages to users; fix submit lock freeze on error path.
Sub-goals
  • SG-1: Gate lookup and create form on isValidRpu (exactly 12 digits)
  • SG-2: Extract real error messages from all ts-rest/JSend response shapes
  • SG-3: Release submit lock on non-201 response (fix frozen 'Creando...')
  • SG-4: Send stripped rpuDigits (not raw rpu) to API
  • SG-5: Add i18n strings rpuInvalid + createFailed
  • SG-6: Cover all changes with unit + jsdom component tests
The changes (whole branch)
What
New _lib/rpu-errors.ts with isValidRpu + extractServerErrorMessage. AddContractDrawer: gate on isValidRpu (was >= 11). InlineContractForm: RPU guard, submit lock fix, better error messages, send rpuDigits. Three new test files.
Why
11-digit RPU slipped through client validation, causing opaque server rejection with blank toast and frozen UI. Server-side validation was already strict (z.string().length(12)) but the client gate was off-by-one.
Areas
apps/platform/src/app/[locale]/(dashboard)/bills/_lib+1340apps/platform/src/app/[locale]/(dashboard)/bills/_components+2757apps/platform/src/messages+62
Blast
8 files, +415/-9 lines. Frontend only — bills route. No domain/API/DB changes.
hotfix frontend-only no-api-changes
CI checks· No CI data availableCodeRabbit· No .coderabbit.yamlcorrectness· 2 medium findingssecurity· 1 low findingconventions· 4 low findingstests· 3 medium findingsimprovement· 2 low findings

Findings · 9

correctness2

medium

13-digit RPU causes permanent loading state

apps/platform/src/app/[locale]/(dashboard)/bills/_components/AddContractDrawer.tsx:176

digits.length < 12 guard skips 'too-short' for 13+ digits, but isValidRpu also rejects them — UI freezes in loading indefinitely. Fix: `if (!isValidRpu(digits) && digits.length > 0)` → too-short.

medium

data.error no longer extracted — regression in error messages

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/rpu-errors.ts:37

JSend fail shape uses data.error (tag), not data.message. Old code read data.error; new extractor probes only data.message → falls back to generic t('createFailed') for all existing fail responses.

security1

low

bodyErrors ZodError message surfaced verbatim to user

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/rpu-errors.ts:44

Benign for RPU field; generic extractor could leak regex patterns from stricter schemas if reused on other endpoints.

conventions2

low

rpu-errors.ts name undersells validation content

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/rpu-errors.ts:1

Contains isValidRpu (validation) + extractServerErrorMessage (error). rpu-utils.ts would better describe both exports.

low

JSDoc block explains WHAT, not WHY

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/rpu-errors.ts:1

8-line header narrates self-evident content. One sentence for the non-obvious WHY (avoid opaque ZodError) is enough.

tests3

medium

setTimeout(r, 400) debounce wait is timing-dependent

apps/platform/src/app/[locale]/(dashboard)/bills/_components/__tests__/AddContractDrawer.rpuGate.test.tsx:81

Fragile under CI process starvation. Use vi.useFakeTimers() + vi.advanceTimersByTime(400) for deterministic control.

medium

Missing regression test: data.error-only JSend fail should return undefined

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/__tests__/rpu-errors.test.ts:60

Current test has both data.error + data.message. A case with only data.error asserts the exact regression this PR fixed.

medium

document.querySelector('form') breaks with multiple forms in jsdom

apps/platform/src/app/[locale]/(dashboard)/bills/_components/__tests__/InlineContractForm.rpuError.test.tsx:127

Picks first form element — fragile if mocks render additional forms. Prefer aria-role or data-testid.

improvement1

low

Hardcoded Spanish strings in CFE job-failure toast bypass i18n

apps/platform/src/app/[locale]/(dashboard)/bills/_components/InlineContractForm.tsx:279

Pre-existing: 'RPU creado' and the warning copy should use t(...) keys per ui-patterns.md.

History · 6 commits

  1. 427115eneeds attentionincremental0H · 1M · 3L2026-07-05 21:43
  2. c6ce7d8needs attentionincremental0H · 3M · 6L2026-07-05 21:35
  3. d8cbd91needs attentionincremental0H · 0M · 3L2026-07-05 21:25
  4. 20aa256safeincremental0H · 0M · 2L2026-07-05 21:14
  5. 77a5d70needs attentionincremental0H · 2M · 9L2026-07-05 21:09
  6. 586531aneeds attentionfull0H · 5M · 10L2026-07-05 20:51current