Files
crewli/dev-docs/MASTER_PROMPT_CURSOR.md
bert.hausmans 945e22f322 docs: remove admin SPA references and update production URLs
The admin SPA (apps/admin/) has been retired. Its functionality now
lives in apps/app/ under /platform/* routes for super_admin users.
Updated all documentation to reflect: 2 SPAs instead of 3, removed
FRONTEND_ADMIN_URL/port 5173 references, changed production URL from
app.crewli.app to crewli.app. Retired admin-specific security audit
findings (A13-2, A13-4, A13-5, A13-7) and APPS-01 backlog item.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:21:44 +02:00

177 lines
7.4 KiB
Markdown

# Crewli — Cursor Master Prompt
# Plak dit BOVEN elke task. Vervang [TASK] onderaan.
## MANDATORY PREAMBLE
Read `@CLAUDE.md` and `@dev-docs/SCHEMA.md` before starting. Use `@workspace`
for full codebase context. These documents are your source of truth.
If modifying existing code, read the current implementation first. Understand
the patterns already in use before writing anything new.
## PROJECT CONTEXT
Crewli — multi-tenant SaaS for event/festival management.
- **Frontend:** Vue 3 + TypeScript + Vuexy 9.5 (Vuetify 3.10) + Pinia +
TanStack Vue Query + VeeValidate + Zod
- **Two SPAs:**
- `apps/app/` — Organizer + Platform Admin main app (port 5174)
- `apps/portal/` — Dual-mode portal (port 5175)
- **API base:** `VITE_API_URL` from `.env.local` (default `http://localhost:8000`)
- **Axios instance:** `src/lib/axios.ts` — this is the ONLY axios instance.
Never create another. Never import axios directly in components.
- **Backend Enums:** PHP Enums in `api/app/Enums/` define all valid status/type
values. TypeScript equivalents must mirror these exactly in `src/types/`.
### Frontend file structure (per app)
```
src/
├── lib/axios.ts # Singleton axios instance (DO NOT DUPLICATE)
├── types/ # TypeScript interfaces per module
│ └── [module].ts
├── composables/api/ # TanStack Query composables per module
│ └── use[Module].ts
├── stores/ # Pinia stores (cross-component state only)
│ └── use[Module]Store.ts
├── pages/ # Page components
│ └── [module]/
│ ├── index.vue # List view
│ └── [id].vue # Detail view (or side panel)
└── router/ # Vue Router config
```
---
## ZERO-COMPROMISE RULES — VIOLATIONS ARE BLOCKERS
### TypeScript & typing
1. **NO `any` TYPES. EVER.** Every variable, prop, emit, return type, ref,
reactive, computed, and API response is fully typed. If you don't know the
type, check the backend API Resource in `api/app/Http/Resources/` — that
defines the contract. Create matching interfaces in `src/types/[module].ts`.
2. **TYPES FIRST.** Before writing any composable or component for a new
module, create the TypeScript interfaces in `src/types/[module].ts`. These
mirror the backend API Resource shape. Include:
- Entity interface (e.g., `Shift`, `Person`, `Event`)
- Create/Update DTOs (matching backend Form Request fields)
- Enum-like union types or const objects matching backend PHP Enums
- Paginated response wrapper if applicable
3. **ENUMS AS CONST OBJECTS.** Mirror backend PHP Enums as TypeScript const
objects with `as const`, not as loose string unions scattered across files:
```typescript
// src/types/shift.ts
export const ShiftAssignmentStatus = {
PENDING_APPROVAL: 'pending_approval',
APPROVED: 'approved',
REJECTED: 'rejected',
CANCELLED: 'cancelled',
COMPLETED: 'completed',
} as const
export type ShiftAssignmentStatus = typeof ShiftAssignmentStatus[keyof typeof ShiftAssignmentStatus]
```
### Architecture & patterns
4. **NO DIRECT AXIOS IN COMPONENTS.** All API calls go through composables in
`composables/api/use[Module].ts` using TanStack Query (`useQuery` /
`useMutation`). Components never import axios or `src/lib/axios.ts`.
The composable is the only layer that knows about HTTP.
5. **NO OPTIONS API.** Always `<script setup lang="ts">`. Props via
`defineProps<{...}>()`, emits via `defineEmits<{...}>()`, expose via
`defineExpose()`. No `export default { ... }`.
6. **NO PROP DRILLING.** If state needs to cross more than one component
boundary, use a Pinia store (`src/stores/use[Module]Store.ts`). Access
reactive state via `storeToRefs()`. Mutations only through store actions.
7. **DELETE OVER ADAPT.** If you find duplicate composables, overlapping
stores, or conflicting patterns: delete the worse version. Do not build
alongside existing code that does the same thing. One implementation per
concern.
8. **CONSISTENCY OVER CLEVERNESS.** Look at how existing modules handle the
same pattern (table views, form dialogs, detail panels, error handling).
Use the exact same approach. Never introduce a "better" alternative without
refactoring ALL existing modules to match.
9. **SINGLE SOURCE OF TRUTH.** Don't duplicate:
- Validation logic → Zod schema mirrors backend Form Request. One schema
per form, referenced by VeeValidate.
- Enum values → const objects in `src/types/`, matching backend Enums.
Never use raw string literals like `'approved'` in templates or logic.
- API URLs → constructed in composable from module prefix, never
hardcoded in components.
### UI & UX
10. **NO CUSTOM CSS WHERE VUETIFY HAS A SOLUTION.** Before writing any CSS:
check if a Vuetify utility class, component prop, or slot achieves the
result. Custom CSS is a last resort, must be `<style scoped>`, and must
have a comment explaining why Vuetify couldn't handle it.
11. **EVERY PAGE HAS THREE STATES:**
- **Loading:** Vuetify skeleton loader or progress indicator
- **Error:** User-facing message with retry action (`v-alert` with retry
button). Show what went wrong in user terms, not technical jargon.
- **Empty:** Helpful message explaining why the list is empty and what
action to take (not a blank white screen).
Never show only the happy path.
12. **MOBILE-FIRST.** Every component must be usable at 375px width. Use
Vuetify's responsive props (`cols`, `sm`, `md`, `lg` on `v-col`). Never
use fixed pixel widths for layout. Tables on mobile → card views or
horizontal scroll with visual indicator.
13. **FORMS USE VEEVALIDATE + ZOD.** Define the Zod schema matching the
backend Form Request rules. No inline validation logic in templates. Use
`useForm()` and `useField()` from VeeValidate with the Zod resolver.
### Code quality
14. **NO TODO / FIXME / HACK.** Complete the implementation or report the
blocker. No stubs, no "implement later", no placeholder components.
15. **NO ORPHANED IMPORTS OR UNUSED VARIABLES.** Run `npx tsc --noEmit`
after every change. Zero errors, zero warnings. Dead code is deleted
immediately, not commented out.
16. **NO GENERIC NAMES.** Component and composable names must be specific:
- ❌ `DataTable.vue`, `useApi.ts`, `helpers.ts`, `utils.ts`
- ✅ `ShiftAssignmentTable.vue`, `useShifts.ts`,
`formatShiftTimeRange.ts`
### Routing & auth
17. **ROUTER GUARDS.** Protected routes check auth state via navigation
guard. Portal routes validate token. No unguarded routes to
authenticated content. Redirect to login on 401.
### Process
18. **COMPONENT CREATION ORDER.** For every new page/feature:
1. TypeScript types in `src/types/[module].ts`
2. API composable in `src/composables/api/use[Module].ts`
3. Pinia store in `src/stores/use[Module]Store.ts` (if cross-component)
4. Vue page component in `src/pages/[module]/`
5. Router entry in `src/router/`
19. **VERIFY BEFORE DONE:**
- `npx tsc --noEmit` — zero errors
- No `any` types anywhere (search: `grep -rn ": any\|as any" src/`)
- No TODO/FIXME/HACK
- All three states (loading, error, empty) implemented
- Mobile responsive at 375px
- Consistent with existing module patterns
---
## TASK
[INSERT SPECIFIC TASK HERE]