docs(claude): point UI-framework conventions to PRIMEVUE_COMPONENTS.md; document migration-phase guidance

CLAUDE.md updated for the Vuetify→PrimeVue migration phase per
RFC-WS-FRONTEND-PRIMEVUE F2:

- Stack line: notes PrimeVue + Tailwind v4 as target, Vuetify still
  present on un-migrated surfaces
- Replaced "Vuexy reference source" + "Vuexy-first strategy" sections
  with a single "UI framework strategy (migration-aware)" section that
  splits guidance into migrated / un-migrated / new surfaces and
  forwards to PRIMEVUE_COMPONENTS.md
- Forms section now documents both target (@primevue/forms + Zod
  resolver via FormField) and legacy (ref + VForm + :rules) patterns,
  with the surface-level-consistency rule
- UI section reframed: PrimeVue + Tailwind on migrated surfaces,
  Vuetify utilities on legacy surfaces, three-state pattern preserved
  on both
- Order of work: framework note added for new pages during F4

Framework-agnostic sections (database, multi-tenancy, ULID,
controllers, models, security, testing) untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 22:50:10 +02:00
parent 4f07a673a1
commit b5765221bb

113
CLAUDE.md
View File

@@ -11,7 +11,7 @@ Design document: `/dev-docs/design-document.md`
## Tech stack ## Tech stack
- Backend: PHP 8.2+, Laravel 12, Sanctum, Spatie Permission, MySQL 8, Redis - Backend: PHP 8.2+, Laravel 12, Sanctum, Spatie Permission, MySQL 8, Redis
- Frontend: TypeScript, Vue 3 (Composition API), Vuexy/Vuetify, Pinia, TanStack Query - Frontend: TypeScript, Vue 3 (Composition API), PrimeVue + Tailwind v4 (target state, migration in progress per [RFC-WS-FRONTEND-PRIMEVUE](./dev-docs/RFC-WS-FRONTEND-PRIMEVUE.md)) — Vuetify/Vuexy still present on un-migrated surfaces during F4; see [`PRIMEVUE_COMPONENTS.md`](./dev-docs/PRIMEVUE_COMPONENTS.md). Pinia, TanStack Query.
- Testing: PHPUnit (backend), Vitest (frontend) - Testing: PHPUnit (backend), Vitest (frontend)
## Quality gates ## Quality gates
@@ -166,54 +166,47 @@ right tier per the decision tree there before adding new tests.
## Frontend rules (strict) ## Frontend rules (strict)
### Vuexy reference source (mandatory) ### UI framework strategy (migration-aware)
When referencing Vuexy demo pages, components, or patterns, ALWAYS use the TypeScript Vue version at: The SPA is migrating Vuetify/Vuexy → PrimeVue + Tailwind v4 per
[RFC-WS-FRONTEND-PRIMEVUE](./dev-docs/RFC-WS-FRONTEND-PRIMEVUE.md).
During F4 (sub-packages F4aF4d), both frameworks ship in the same build
on different surfaces. The component-selection rules depend on which side
of the migration the surface is on.
``` **Always read [`PRIMEVUE_COMPONENTS.md`](./dev-docs/PRIMEVUE_COMPONENTS.md)
resources/vuexy-admin-v10.11.1/vue-version/typescript-version/full-version/ before any frontend task** — it is the authoritative reference for
``` component selection, theming, forms, and DataTable conventions across
both phases.
This is the **ONLY** valid reference path. Never use: #### On migrated surfaces (target state)
- `javascript-version/` — wrong language
- `starter-kit/` — incomplete, missing components
- Any other variant or version
Before implementing any Vuexy-based page or component, read the reference implementation from this path first: PrimeVue is the framework. Follow [`PRIMEVUE_COMPONENTS.md`](./dev-docs/PRIMEVUE_COMPONENTS.md):
```bash
# Example: find auth page references
find resources/vuexy-admin-v10.11.1/vue-version/typescript-version/full-version/src/pages -name "*.vue" | grep -i "login\|auth"
```
### Vuexy-first strategy 1. **Can a Tailwind utility do this?** (layout, spacing, typography) → use it.
2. **Does PrimeVue provide a component?** → use it (see §3 component mapping).
3. **Forms**`@primevue/forms` + Zod resolver via `<FormField>` (§5; full API in [RFC Appendix A](./dev-docs/RFC-WS-FRONTEND-PRIMEVUE.md#appendix-a--formfield-api-specification)).
4. **DataTables**`<DataTable>` with `:lazy="true"` for server-side (§6).
5. **None of the above?** → cross-reference https://primevue.org/ for the closest match. Add a note in `PRIMEVUE_COMPONENTS.md` §3 if it's a recurring need.
Before writing ANY frontend component, consult `/dev-docs/VUEXY_COMPONENTS.md` and follow this decision tree: Customization order: Tailwind utilities (layout) → `pt` API (component-internal) → Aura preset extension (brand-wide) → `<style scoped>` (last resort, with comment).
1. **Can a standard Vuetify component do this?** → Use it with default props. #### On un-migrated surfaces (legacy, transient)
Do not wrap it in a custom component.
2. **Does Vuexy provide an @core component for this?** → Use it. Check
`/dev-docs/VUEXY_COMPONENTS.md` section 1 for the full registry.
3. **Does an existing Crewli page already solve a similar UI pattern?**
Copy that pattern exactly. Check `/dev-docs/VUEXY_COMPONENTS.md` section 3
for established patterns and their reference implementations.
4. **None of the above?** → Only then write custom code. Add `<style scoped>`
with a comment explaining why Vuexy/Vuetify couldn't handle it.
Concrete component rules: Vuetify + Vuexy `@core/` components remain in use until the surface's F4
- Tables: `v-data-table-server` with server-side pagination — never client-side for API data sub-package lands. When extending these surfaces during the transition:
- Cards: `v-card` directly, or `AppCardActions` when collapse/refresh/remove is needed
- Forms in dialogs: `v-dialog` + `v-card` + `v-form` — follow the established dialog pattern
- Detail panels: `v-navigation-drawer` with `temporary` and `location="end"` — follow ShiftDetailPanel pattern
- Date/time pickers: `AppDateTimePicker` from @core — never raw input[type=date]
- Status indicators: `v-chip` with color prop — never custom styled spans
- Loading states: `v-skeleton-loader` — never custom spinners
- Error states: `v-alert` with retry button — never custom error divs
- Empty states: `v-card` with icon + message + action button
- Notifications: `v-snackbar` — never custom toast components
- Page layout: `v-row` + `v-col` with Vuetify breakpoint props — never CSS grid or custom flexbox
**Before ANY frontend task:** read `/dev-docs/VUEXY_COMPONENTS.md` to verify - Match the surrounding code (`<VBtn>`, `<VTextField>`, `<v-data-table-server>`, etc.)
you are using available components rather than building custom ones. - Reference the pre-F2 Vuexy registry via git: `git show 1c449ff6204cae6371da08c34ea8934d6b2ffcb8:dev-docs/VUEXY_COMPONENTS.md`
- Vuexy template reference (when needed): `resources/vuexy-admin-v10.11.1/vue-version/typescript-version/full-version/` — TypeScript Vue version is the only valid path
Do **not** introduce PrimeVue components inside an un-migrated surface
("no back-porting" — see `PRIMEVUE_COMPONENTS.md` §9).
#### On new surfaces (created during or after F4)
Start in PrimeVue. The migration phase is not a license to add new
Vuetify code.
### Vue components ### Vue components
@@ -240,7 +233,16 @@ you are using available components rather than building custom ones.
### Forms ### Forms
Canonical form pattern (used everywhere in the SPA): The canonical form pattern depends on the migration phase of the surface:
**Target state (migrated surfaces, new surfaces):** `@primevue/forms` +
Zod resolver via the `<FormField>` wrapper. Full API specification in
[RFC-WS-FRONTEND-PRIMEVUE Appendix A](./dev-docs/RFC-WS-FRONTEND-PRIMEVUE.md#appendix-a--formfield-api-specification);
Crewli conventions in [`PRIMEVUE_COMPONENTS.md` §5](./dev-docs/PRIMEVUE_COMPONENTS.md).
One Zod schema per form, field names mirror backend Form Request keys
(snake_case), 422 errors merge via `useFormError(formRef)`.
**Legacy state (un-migrated surfaces, transient until each F4 sub-package):**
- `ref({ field: ... })` for form state - `ref({ field: ... })` for form state
- `VForm` ref + per-field rules drawn from `@core/utils/validators` - `VForm` ref + per-field rules drawn from `@core/utils/validators`
@@ -248,13 +250,16 @@ Canonical form pattern (used everywhere in the SPA):
- A separate `errors: Ref<Record<string, string>>` for server-validation - A separate `errors: Ref<Record<string, string>>` for server-validation
feedback (mapped from 422 responses) feedback (mapped from 422 responses)
- **Zod** for runtime validation of API payloads/responses (in - **Zod** for runtime validation of API payloads/responses (in
`apps/app/src/schemas/*.ts`) — Zod schemas mirror backend Form Requests `apps/app/src/schemas/*.ts`) — schemas already mirror backend Form
(field names, required/optional, types) and are the canonical contract Requests and carry forward unchanged into the target state
- No inline validation logic in components - No inline validation logic in components
VeeValidate is **NOT** the form library here. It was previously listed A single form is either fully Zod-resolver-validated (target) or fully
but never actually adopted in any page; it was removed in commit `:rules`-validated (legacy) — never a hybrid. VeeValidate is **NOT** in
`<sha>` (Session 4 follow-up). Reference forms: `apps/app/src/components/sections/CreateShiftDialog.vue`, the stack on either side of the migration.
Reference forms (legacy pattern, will migrate during F4):
`apps/app/src/components/sections/CreateShiftDialog.vue`,
`apps/app/src/components/timetable/AddPerformanceDialog.vue`, `apps/app/src/components/timetable/AddPerformanceDialog.vue`,
`apps/app/src/pages/register/[public_token].vue`. `apps/app/src/pages/register/[public_token].vue`.
@@ -268,12 +273,12 @@ but never actually adopted in any page; it was removed in commit
### UI ### UI
- Always use Vuexy/Vuetify for layout, forms, tables, dialogs - Component framework selection: see "UI framework strategy" above and [`PRIMEVUE_COMPONENTS.md`](./dev-docs/PRIMEVUE_COMPONENTS.md). PrimeVue + Tailwind v4 on migrated/new surfaces; Vuetify on un-migrated surfaces during F4
- Do not write custom CSS when a Vuetify utility class exists - Do not write custom CSS when a framework utility (Tailwind on migrated surfaces, Vuetify utilities on legacy surfaces) exists
- Responsive: mobile-first, usable from 375px width - Responsive: mobile-first, usable from 375px width
- **Three states per page:** every data-driven view must handle loading (skeleton/spinner), error (`v-alert` with retry button), and empty (helpful message with action button) - **Three states per page:** every data-driven view must handle loading (skeleton), error (`Message` / `v-alert` with retry button), and empty (helpful message with action button) — both frameworks support this pattern
- Use Vuetify responsive props (`cols`, `sm`, `md`, `lg`) — no fixed pixel widths - Responsive layout: Tailwind grid (`grid grid-cols-12 gap-4` + `col-span-N md:col-span-M`) on migrated surfaces; Vuetify `v-row` + `v-col` with breakpoint props on legacy surfaces — no fixed pixel widths
- Custom CSS via `<style scoped>` only as last resort when no Vuetify utility exists - Custom CSS via `<style scoped>` only as last resort when no framework utility / `pt` API / Aura token can do the job
## Forbidden patterns ## Forbidden patterns
@@ -321,6 +326,12 @@ allowed only with a `TODO TECH-*` reference to a backlog item.
13. Vue page component in `src/pages/[module]/` 13. Vue page component in `src/pages/[module]/`
14. Add route in Vue Router 14. Add route in Vue Router
> **Framework note for steps 1314 during F4 migration:** new pages
> follow the PrimeVue + Tailwind conventions in [`PRIMEVUE_COMPONENTS.md`](./dev-docs/PRIMEVUE_COMPONENTS.md).
> If the new module is grafted onto a not-yet-migrated surface (rare),
> match the surrounding Vuetify style and let the surface's F4
> sub-package migrate it later.
## Diagnostic discipline: audit before assume ## Diagnostic discipline: audit before assume
When debugging or fixing any bug, the first action is to verify the When debugging or fixing any bug, the first action is to verify the