From da2b9ee9e7e1619f0f943e42acacedace96bf554 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Tue, 14 Apr 2026 01:07:23 +0200 Subject: [PATCH] docs: migrate frontend conventions from .cursorrules to CLAUDE.md Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9c03418a..837d01e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -89,9 +89,26 @@ Configure three frontend origins in both Laravel (`config/cors.php` via env) and ### API calls - Use TanStack Query (`useQuery` / `useMutation`) for **all** API calls -- Never call axios directly from a component — always via a composable under `composables/` +- Never call axios directly from a component — always via a composable under `composables/api/use[Module].ts` +- `src/lib/axios.ts` is the canonical axios instance — the only place axios is imported directly - Use Pinia stores for cross-component state — no prop drilling +### TypeScript + +- No `any` types — ever. Every variable, prop, emit, return type, ref, computed must be fully typed +- Types first: create `src/types/[module].ts` before composables or components +- Mirror backend PHP Enums as `as const` objects in `src/types/`: + ```typescript + export const ShiftStatus = { PENDING: 'pending', APPROVED: 'approved' } as const + export type ShiftStatus = typeof ShiftStatus[keyof typeof ShiftStatus] + ``` + +### 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) +- No inline validation logic in components + ### Naming - DB columns: `snake_case` @@ -105,6 +122,9 @@ Configure three frontend origins in both Laravel (`config/cors.php` via env) and - Always use Vuexy/Vuetify for layout, forms, tables, dialogs - Do not write custom CSS when a Vuetify utility class exists - 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) +- Use Vuetify responsive props (`cols`, `sm`, `md`, `lg`) — no fixed pixel widths +- Custom CSS via `