feat(tickets): migrate new-ticket form to FormGuard, enforce Description (PMS-518) #352

Merged
nrupard merged 2 commits from feat/PMS-518-migrate-ticket-time-forms into main 2026-06-25 20:15:37 +02:00
Owner

What

First per-page increment of the PMS-515 form-validation migration (PMS-518). Migrates the new-ticket form (TicketNewPage) onto the shared FormGuard (PMS-517) and fixes the first silent gap.

PMS-518 is a multi-PR effort (~30 form surfaces). This PR is the reference adoption; log-time and the rest follow as separate increments. The issue stays open.

Changes

  • handle_submit -> FormGuard. Each required field goes through guard.field(id, value, label, rules) (returns the inline message, records first-invalid); guard.blocked() focuses the first invalid field and bails before the POST. Replaces the bespoke PMS-514 filter(|_| !title_empty) trick.
  • Silent gap #1 fixed: Description is now enforced. It carried a required asterisk but was never validated and had no error slot. Now it has a description_error signal, an inline error slot, Rule::Required (blur), and a guard check (submit). The enforce direction was confirmed with the product owner. The server still accepts an empty body, so this is a purely client-side rule; the trimmed (now non-empty) Description is sent.
  • Title gains rules: vec![Rule::Required] for on-blur parity.
  • Company has no inline slot, so its failure stays in the form-level banner and blocks via guard.note_invalid(None).
  • dev-docs/form-validation-audit.md: corrects the decomposition to the real issue ids and adds a per-page migration-progress checklist (new-ticket done).

Behaviour

  • Submit with empty Title + Description + no Company -> all three errors show at once (Title + Description inline, Company in the banner), first invalid field focused, no POST.
  • Each field's message clears as it is corrected (blur + live), and guard.field clears a stale message on a passing resubmit.
  • A complete form submits and navigates as before.

Verification

just check parity via the rust-builder image: clippy --all-targets -- -D warnings, fmt --check, cargo check --target wasm32-unknown-unknown - all green. (Logic lives in the already-unit-tested validation / form_guard modules; this is the wiring.)

## What First per-page increment of the PMS-515 form-validation migration (PMS-518). Migrates the **new-ticket form** (`TicketNewPage`) onto the shared `FormGuard` (PMS-517) and fixes the first silent gap. > PMS-518 is a multi-PR effort (~30 form surfaces). This PR is the reference adoption; log-time and the rest follow as separate increments. The issue stays open. ## Changes - **`handle_submit` -> `FormGuard`.** Each required field goes through `guard.field(id, value, label, rules)` (returns the inline message, records first-invalid); `guard.blocked()` focuses the first invalid field and bails before the POST. Replaces the bespoke PMS-514 `filter(|_| !title_empty)` trick. - **Silent gap #1 fixed: Description is now enforced.** It carried a required asterisk but was never validated and had no error slot. Now it has a `description_error` signal, an inline error slot, `Rule::Required` (blur), and a guard check (submit). The enforce direction was confirmed with the product owner. The server still accepts an empty body, so this is a purely client-side rule; the trimmed (now non-empty) Description is sent. - **Title** gains `rules: vec![Rule::Required]` for on-blur parity. - **Company** has no inline slot, so its failure stays in the form-level banner and blocks via `guard.note_invalid(None)`. - **`dev-docs/form-validation-audit.md`:** corrects the decomposition to the real issue ids and adds a per-page migration-progress checklist (new-ticket done). ## Behaviour - Submit with empty Title + Description + no Company -> all three errors show at once (Title + Description inline, Company in the banner), first invalid field focused, no POST. - Each field's message clears as it is corrected (blur + live), and `guard.field` clears a stale message on a passing resubmit. - A complete form submits and navigates as before. ## Verification `just check` parity via the rust-builder image: `clippy --all-targets -- -D warnings`, `fmt --check`, `cargo check --target wasm32-unknown-unknown` - all green. (Logic lives in the already-unit-tested `validation` / `form_guard` modules; this is the wiring.)
feat(tickets): migrate new-ticket form to FormGuard, enforce Description (PMS-518)
Some checks failed
Check / fmt + clippy + tests (pull_request) Has been cancelled
9eaf450d31
First per-page increment of the PMS-515 migration. Moves TicketNewPage::handle_submit onto the shared FormGuard (PMS-517): each required field is validated through guard.field(...) which returns the field's inline message and records the first invalid, then guard.blocked() focuses the first invalid field and bails before the POST. Replaces the bespoke PMS-514 `filter(|_| !title_empty)` trick.

Fixes silent gap #1: the Description field carried a required asterisk but was never validated and had no error slot. It is now genuinely required (product-owner confirmed the enforce direction) - it gets a description_error signal, an inline error slot, a Rule::Required for blur validation, and is checked by the guard on submit. The server still accepts an empty body, so this is a purely client-side rule. The trimmed Description is sent (it is now always non-empty).

Title also gains rules: vec![Rule::Required] for on-blur parity. The CompanyPicker has no inline error slot, so its failure stays in the form-level banner and blocks via guard.note_invalid(None).

Updates dev-docs/form-validation-audit.md: corrects the decomposition to the real issue ids (PMS-516/517/518) and adds a per-page migration-progress checklist (new-ticket done; log-time and the remaining ~28 surfaces are follow-up increments).

just check parity (rust-builder image): clippy --all-targets -D warnings, fmt --check, cargo check --target wasm32-unknown-unknown all green.

#PMS-518
feat(time): migrate log-time form to FormGuard, enforce Description (PMS-518)
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 10m59s
Create release / Create release from merged PR (pull_request) Has been skipped
815c4f8c96
Second silent-gap form, same branch/PR as the new-ticket migration. Moves TimeEntryNewPage's onsubmit handler onto the shared FormGuard: Work Item, Work Type, and Description are validated through guard.field(...) into their own inline error slots (reported all at once, first invalid focused), and the free-text Hours field keeps its custom H:MM/decimal parse but reports through the guard via note_invalid so it joins the same up-front pass. The cross-field work-item resolution errors and the per-day-cap check have no single field, so they stay on the form-level banner.

Fixes silent gap #2: the Description carried a required asterisk but was never validated and had no error slot; it is now genuinely required (product-owner confirmed enforce), with an inline slot, a Rule::Required for blur, and a guard check on submit. The trimmed Description is sent as notes (now always non-empty). Previously a missing field showed only the first error ("Please pick a work type."); now every missing required field surfaces together.

Adds per-field inline error signals (work_item_error / work_type_error / hours_error / description_error) and wires each field's error slot + clear-on-change. Updates the migration-progress checklist in dev-docs/form-validation-audit.md (log-time done).

just check parity (rust-builder image): clippy --all-targets -D warnings, fmt --check, cargo check --target wasm32-unknown-unknown all green.

#PMS-518
nrupard scheduled this pull request to auto merge when all checks succeed 2026-06-25 20:07:25 +02:00
nrupard deleted branch feat/PMS-518-migrate-ticket-time-forms 2026-06-25 20:15:37 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!352
No description provided.