Skip to content

fix(forms): validate only rendered fields so create forms can submit#25

Merged
simonorzel26 merged 1 commit into
mainfrom
fix/crud-form-silent-validation
Jul 20, 2026
Merged

fix(forms): validate only rendered fields so create forms can submit#25
simonorzel26 merged 1 commit into
mainfrom
fix/crud-form-silent-validation

Conversation

@simonorzel26

Copy link
Copy Markdown
Contributor

Problem

User report (2026-07-17, asset step of the journey): filling out Asset hinzufügen and clicking Speichern does nothing. Reproduced in Firefox and Edge — browser-independent.

Root cause: SchemaForm passes the full insert schema to zodResolver while CrudPage omits fields like companyId from rendering. companyId is .notNull() with no default on every tenant-scoped table, so validation fails on every submit — on a field that has no rendered error slot. handleSubmit has no invalid-handler, so nothing visible happens. This silently broke create on all 13 CrudPage forms (assets, suppliers, risks, incidents, patches, …). Edit worked, because defaultValues carry the existing row's companyId.

Fix

  • Resolver validates schema.omit(omitted keys) — exactly the fields the form renders. Mask is filtered against the shape (Zod v4 .omit() throws on unknown keys). Server keeps binding companyId from session, unchanged.
  • Asset date columns (lastPatchDate, lastVulnScanDate, lastBackupTestDate, endOfLife) tightened to z.iso.date().nullable(): forms now render native date inputs, empty dates are null (not "", which Postgres rejects as ''::date), and free-text dates like 17.07.2026 get a visible client-side error instead of a server 500.
  • Asset mutations surface tRPC errors as sonner toasts instead of failing silently.
  • Added missing accessManagement / privilegedAccountCount labels + descriptions (CIR 2024/2690 points 11.2/11.3) in all 10 asset locale files.

Verification

Scripted the exact reported payload through the fixed pipeline:

  • resolver-derived schema: payload passes; companyId no longer validated client-side
  • lastPatchDate/endOfLife introspect as date → native date pickers, optional
  • ISO date passes, 17.07.2026 rejected with a field-level error
  • empty required name errors on the rendered field only
  • typecheck: error set identical to main (the activatedAt errors on main are a local cross-worktree artifact, see CI)

🤖 Generated with Claude Code

SchemaForm passed the full insert schema to zodResolver while omitting
fields like companyId from rendering. companyId is required by every
tenant-scoped insert schema and bound server-side, so submit failed
validation on a field with no rendered error slot: clicking save did
nothing, silently, on every CrudPage create form (assets, suppliers,
risks, incidents, ...). Reported by a user on the asset step.

- resolver now validates schema minus the omitted keys (mask filtered
  against the shape; Zod v4 omit throws on unknown keys)
- asset date columns tightened to z.iso.date().nullable() so forms
  render native date inputs and Postgres never sees ''::date or free-
  text dates; empty date inputs and defaults are null, not ""
- asset mutations surface server errors as toasts instead of nothing
- add missing accessManagement / privilegedAccountCount labels and
  descriptions in all 10 asset locale files
@simonorzel26
simonorzel26 merged commit ba69426 into main Jul 20, 2026
2 checks passed
@simonorzel26
simonorzel26 deleted the fix/crud-form-silent-validation branch July 20, 2026 11:37
simonorzel26 added a commit that referenced this pull request Jul 20, 2026
…mps, global mutation error toasts (#26)

Follow-up sweep after #25 for the same bug classes across all forms:

- isoDateColumns(table) helper derives z.iso.date() overrides from drizzle
  table metadata and is spread into every insert schema whose table has pg
  date columns (18 schemas). Forms render date pickers, Postgres never
  receives '' or free-text dates, and notNull date columns (exercise/
  audit scheduledDate, managementReview reviewDate) become properly
  required. nullish (not nullable) preserves drizzle-zod's absent-key
  semantics — this also fixes a #25 regression where asset.create 400d
  server-side because the omitted lastVulnScanDate/lastBackupTestDate
  keys were absent from the payload.
- z.date() timestamp fields (incident discoveredAt/occurredAt etc.) get
  real Date objects from the date input instead of strings that could
  never validate; introspection marks them via FieldMeta.jsDate.
- companyCertificationCreateSchema date regexes replaced with z.iso.date()
  so those fields render date pickers too.
- Global MutationCache onError toasts any tRPC mutation failure that has
  no local handler; the AssetsPage-local handler from #25 is superseded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant