diff --git a/CLAUDE.md b/CLAUDE.md index 06cdde68..83e75554 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -236,10 +236,24 @@ you are using available components rather than building custom ones. ### Forms -- VeeValidate for form state + Zod for schema validation — always together -- Zod schemas must mirror the backend Form Request rules (field names, required/optional, types) +Canonical form pattern (used everywhere in the SPA): + +- `ref({ field: ... })` for form state +- `VForm` ref + per-field rules drawn from `@core/utils/validators` + (`requiredValidator`, `emailValidator`, etc.) +- A separate `errors: Ref>` for server-validation + feedback (mapped from 422 responses) +- **Zod** for runtime validation of API payloads/responses (in + `apps/app/src/schemas/*.ts`) — Zod schemas mirror backend Form Requests + (field names, required/optional, types) and are the canonical contract - No inline validation logic in components +VeeValidate is **NOT** the form library here. It was previously listed +but never actually adopted in any page; it was removed in commit +`` (Session 4 follow-up). Reference forms: `apps/app/src/components/sections/CreateShiftDialog.vue`, +`apps/app/src/components/timetable/AddPerformanceDialog.vue`, +`apps/app/src/pages/register/[public_token].vue`. + ### Naming - DB columns: `snake_case` diff --git a/apps/app/package.json b/apps/app/package.json index 2786095c..7ad3e112 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -30,7 +30,6 @@ "@tiptap/pm": "^2.27.1", "@tiptap/starter-kit": "^2.27.1", "@tiptap/vue-3": "^2.27.1", - "@vee-validate/zod": "^4.15.1", "@vueuse/core": "10.11.1", "@vueuse/math": "10.11.1", "apexcharts": "3.54.1", @@ -50,7 +49,6 @@ "shepherd.js": "13.0.3", "ufo": "1.6.1", "unplugin-vue-define-options": "1.5.5", - "vee-validate": "^4.15.1", "vue": "3.5.22", "vue-chartjs": "5.3.2", "vue-flatpickr-component": "11.0.5", diff --git a/apps/app/pnpm-lock.yaml b/apps/app/pnpm-lock.yaml index 89377565..60001748 100644 --- a/apps/app/pnpm-lock.yaml +++ b/apps/app/pnpm-lock.yaml @@ -51,9 +51,6 @@ importers: '@tiptap/vue-3': specifier: ^2.27.1 version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)(vue@3.5.22(typescript@5.9.3)) - '@vee-validate/zod': - specifier: ^4.15.1 - version: 4.15.1(vue@3.5.22(typescript@5.9.3))(zod@3.25.76) '@vueuse/core': specifier: 10.11.1 version: 10.11.1(vue@3.5.22(typescript@5.9.3)) @@ -111,9 +108,6 @@ importers: unplugin-vue-define-options: specifier: 1.5.5 version: 1.5.5(vue@3.5.22(typescript@5.9.3)) - vee-validate: - specifier: ^4.15.1 - version: 4.15.1(vue@3.5.22(typescript@5.9.3)) vue: specifier: 3.5.22 version: 3.5.22(typescript@5.9.3) @@ -1931,11 +1925,6 @@ packages: cpu: [x64] os: [win32] - '@vee-validate/zod@4.15.1': - resolution: {integrity: sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==} - peerDependencies: - zod: ^3.24.0 - '@vitejs/plugin-vue-jsx@5.1.1': resolution: {integrity: sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5001,11 +4990,6 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vee-validate@4.15.1: - resolution: {integrity: sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==} - peerDependencies: - vue: ^3.4.26 - vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -7013,14 +6997,6 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vee-validate/zod@4.15.1(vue@3.5.22(typescript@5.9.3))(zod@3.25.76)': - dependencies: - type-fest: 4.41.0 - vee-validate: 4.15.1(vue@3.5.22(typescript@5.9.3)) - zod: 3.25.76 - transitivePeerDependencies: - - vue - '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@babel/core': 7.28.5 @@ -10722,12 +10698,6 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vee-validate@4.15.1(vue@3.5.22(typescript@5.9.3)): - dependencies: - '@vue/devtools-api': 7.7.7 - type-fest: 4.41.0 - vue: 3.5.22(typescript@5.9.3) - vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 diff --git a/dev-docs/BACKLOG.md b/dev-docs/BACKLOG.md index f7a8a528..793d9e69 100644 --- a/dev-docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -2016,8 +2016,9 @@ RFC-FORM-BUILDER-UI implementatie begint. Argumenten: hoge-prioriteit endpoints (`/auth/me`, form-builder list-endpoints, identity-match endpoints). Verdere composable-uitrol gebeurt organisch als features worden geraakt of toegevoegd. -**Out of scope:** form-input validatie (al via VeeValidate + Zod), WebSocket-validatie -(separaat, COMM-01), publieke API-contracten voor third parties (separaat, DIFF-03). +**Out of scope:** form-input validatie (via `@core/utils/validators` + Zod +payload schemas — see CLAUDE.md "Forms"), WebSocket-validatie (separaat, +COMM-01), publieke API-contracten voor third parties (separaat, DIFF-03). **Open beslissingen:** codegen toolchain (Scramble-pipeline vs hand-rolled), validation failure-mode (hard fail vs soft fail per env), per-route opt-out, boundary placement @@ -2284,3 +2285,21 @@ opgezet. **Refs:** `apps/app/index.html`, `deploy/nginx/csp-spa.conf`, `tests/Feature/Security/CspConnectsToObservabilityTest.php`, RFC-WS-7-OBSERVABILITY.md §3.3, ARCH-OBSERVABILITY.md §7 + §10.4. + +### VEE-001 — VeeValidate removed from stack ✅ Resolved + +**Status:** Closed in `feat/timetable-session-4` follow-up. + +`vee-validate` and `@vee-validate/zod` shipped in `apps/app/package.json` +since Vuexy onboarding but were never imported anywhere in the SPA. A +strict regex sweep (`from 'vee-validate'`, ``, `
`, +``, `defineRule(`, `useForm()`) returned **zero +hits** across `apps/app/src/`. Earlier fuzzy matches were false +positives from `useForm` colliding with Crewli's own `useFormDraft` / +`useFormSteps` / `useFormSchemas` / `useFormFailures` composables. + +Removed both packages from `apps/app/package.json`, regenerated +`pnpm-lock.yaml`. Canonical form pattern formalized in `CLAUDE.md` +"Forms" + `dev-docs/VUEXY_COMPONENTS.md` "Form validation" row. + +**Refs:** Session 4 follow-up Step 1; `apps/app/src/components/timetable/AddPerformanceDialog.vue` and `apps/app/src/components/sections/CreateShiftDialog.vue` as canonical references. diff --git a/dev-docs/VUEXY_COMPONENTS.md b/dev-docs/VUEXY_COMPONENTS.md index f84477e5..960e12b6 100644 --- a/dev-docs/VUEXY_COMPONENTS.md +++ b/dev-docs/VUEXY_COMPONENTS.md @@ -430,9 +430,12 @@ Two approaches from Vuexy: - StatusCard shows different UI per approval status (pending/approved/rejected) - Conditional tab visibility based on approval status -#### Registration (Multi-step Form with VeeValidate + Zod) +#### Registration (Multi-step Public Form) **Reference:** `apps/app/src/pages/register/[public_token].vue` -- VForm with VeeValidate field binding + Zod schemas from `@/schemas/` +- VForm + `useFormDraft` composable for state, autosave, idempotency-key drafts +- Per-field validators from `@core/utils/validators` (`emailValidator`, `requiredValidator`) +- Zod schemas in `apps/app/src/schemas/registrationSchema.ts` validate the + outgoing payload at submit time - Conditional form fields based on event configuration - Real-time email duplicate checking - Password creation for new users @@ -480,7 +483,7 @@ Preferred Vuetify components for common needs. Use these, not custom solutions. | Menu / dropdown actions | VMenu + VList + VListItem | Custom popover | | Drag-and-drop lists | `vuedraggable` (external library) | Custom drag logic | | Rich text editing | TiptapEditor (@core) | Custom editor | -| Form validation | VForm ref + `@core/utils/validators` + API error mapping | (VeeValidate + Zod only in portal registration) | +| Form validation | VForm ref + `@core/utils/validators` + Zod schema for payload + API 422 error map | VeeValidate (removed; was never actually adopted) | | Multi-step wizard | AppStepper (@core) | Custom step logic | | Drawer header | AppDrawerHeaderSection (@core) | Custom header | | Code display | AppCardCode (@core) | Custom code block |