RFC-TIMETABLE v0.2 Session 4 — Frontend Timetable + Test Coverage Closure #18
18
CLAUDE.md
18
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<Record<string, string>>` 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
|
||||
`<sha>` (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`
|
||||
|
||||
@@ -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",
|
||||
|
||||
30
apps/app/pnpm-lock.yaml
generated
30
apps/app/pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
@@ -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'`, `<Field>`, `<Form>`,
|
||||
`<ErrorMessage>`, `defineRule(`, `useForm(<args>)`) 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.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user