Files
band-management/.cursor/rules/001_workspace.mdc
bert.hausmans 1cb7674d52 refactor: align codebase with EventCrew domain and trim legacy band stack
- Update API: events, users, policies, routes, resources, migrations
- Remove deprecated models/resources (customers, setlists, invitations, etc.)
- Refresh admin app and docs; remove apps/band

Made-with: Cursor
2026-03-29 23:19:06 +02:00

169 lines
6.7 KiB
Plaintext

---
description: Core workspace rules for EventCrew multi-tenant SaaS platform
globs: ["**/*"]
alwaysApply: true
---
# Workspace Rules
You are an expert full-stack developer working on EventCrew, a multi-tenant SaaS platform for event and festival management. The backend is a Laravel 12 REST API (JSON only, no Blade), and three Vue 3 SPA frontends communicate via CORS + Sanctum tokens.
## Tech Stack
### Backend (Laravel)
- PHP 8.2+
- Laravel 12
- Laravel Sanctum (SPA token auth)
- Spatie laravel-permission (three-level roles)
- Spatie laravel-activitylog (audit log)
- Spatie laravel-medialibrary (file management)
- MySQL 8 (primary), Redis (cache, queues, sessions)
- Laravel Horizon (queue monitoring)
- PHPUnit for testing
### Frontend (Vue)
- TypeScript 5.9+
- Vue 3.5+ (Composition API, `<script setup>` only)
- Vite 7+
- Vuexy 9.5 + Vuetify 3.10
- Pinia 3 (client state)
- TanStack Query / Vue Query (server state)
- Axios (HTTP client)
- VeeValidate + Zod (form validation)
- VueDraggable (drag-and-drop)
- Vue I18n (internationalization)
## Project Structure
```
event-crew/
├── api/ # Laravel 12 REST API (JSON only)
│ ├── app/
│ │ ├── Http/
│ │ │ ├── Controllers/Api/V1/
│ │ │ ├── Middleware/ # OrganisationRoleMiddleware, EventRoleMiddleware, PortalTokenMiddleware
│ │ │ ├── Requests/Api/V1/ # Form Request validation
│ │ │ └── Resources/Api/V1/ # API Resources
│ │ ├── Models/ # Eloquent models with HasUlids
│ │ ├── Policies/ # Authorization (never hardcode roles)
│ │ ├── Services/ # Complex business logic
│ │ ├── Events/ + Listeners/
│ │ └── Jobs/ # Queue jobs (briefings, PDF, notifications)
│ ├── database/
│ │ ├── migrations/
│ │ ├── factories/
│ │ └── seeders/
│ └── tests/Feature/Api/V1/
├── apps/
│ ├── admin/ # Super Admin SPA (Vuexy full)
│ │ └── src/
│ │ ├── @core/ # Vuexy core (NEVER modify)
│ │ ├── @layouts/ # Vuexy layouts (NEVER modify)
│ │ ├── components/
│ │ ├── composables/ # useModule.ts composables
│ │ ├── lib/ # axios.ts (single instance)
│ │ ├── pages/
│ │ ├── plugins/ # vue-query, casl, vuetify
│ │ ├── stores/ # Pinia stores
│ │ └── types/ # TypeScript interfaces
│ │
│ ├── app/ # Organizer SPA (Vuexy full) - MAIN APP
│ │ └── src/ # Same structure as admin/
│ │
│ └── portal/ # External Portal SPA (Vuexy stripped)
│ └── src/ # No sidebar, no customizer, top-bar only
├── resources/design/ # Design documents (source of truth)
└── .cursor/ # Cursor AI configuration
```
## Multi-Tenancy Rules (CRITICAL)
1. **EVERY query on event-data MUST scope on `organisation_id`** via `OrganisationScope` Eloquent Global Scope.
2. **Never use direct id-checks in controllers** - always use Policies.
3. **Never use `Model::all()` without a where-clause** - always scope.
4. **Never hardcode role strings** like `$user->role === 'admin'` - use `$user->hasRole()` and Policies.
## Naming Conventions
### PHP (Laravel)
| Type | Convention | Example |
|------|------------|---------|
| Models | Singular PascalCase | `Event`, `FestivalSection`, `ShiftAssignment` |
| Controllers | PascalCase + Controller | `EventController`, `ShiftController` |
| Form Requests | Action + Resource + Request | `StoreEventRequest`, `UpdateShiftRequest` |
| Resources | Resource + Resource | `EventResource`, `PersonResource` |
| Services | PascalCase + Service | `ZenderService`, `BriefingService` |
| Migrations | snake_case with timestamp | `create_events_table` |
| Tables | Plural snake_case | `events`, `festival_sections`, `shift_assignments` |
| Columns | snake_case | `organisation_id`, `slots_total`, `created_at` |
| Enums | Singular PascalCase | `EventStatus`, `BookingStatus` |
### TypeScript (Vue)
| Type | Convention | Example |
|------|------------|---------|
| Components | PascalCase | `ShiftAssignPanel.vue`, `PersonCard.vue` |
| Composables | use-prefix camelCase | `useShifts.ts`, `usePersons.ts` |
| Pinia Stores | use-prefix + Store suffix | `useEventStore.ts`, `useAuthStore.ts` |
| Types/Interfaces | PascalCase | `Event`, `Person`, `ShiftAssignment` |
| Variables | camelCase | `slotsFilled`, `fillRate` |
### Database
- Primary keys: ULID via `HasUlids` trait (NOT UUID v4, NOT auto-increment for business tables)
- Pure pivot tables: auto-increment integer PK for join performance
- DB columns: `snake_case`
## Environment Configuration
### Development URLs
| Service | URL | Env Variable |
|---------|-----|--------------|
| API | `http://localhost:8000/api/v1` | - |
| Admin SPA | `http://localhost:5173` | `FRONTEND_ADMIN_URL` |
| Organizer SPA | `http://localhost:5174` | `FRONTEND_APP_URL` |
| Portal SPA | `http://localhost:5175` | `FRONTEND_PORTAL_URL` |
| MySQL | `localhost:3306` | - |
| Redis | `localhost:6379` | - |
| Mailpit | `http://localhost:8025` | - |
### CORS
Three frontend origins configured in `config/cors.php` via env variables. Each Vite dev server gets its own port for CORS isolation.
## Git Conventions
### Branch Names
- `feature/shift-planning`
- `fix/organisation-scoping`
- `refactor/accreditation-engine`
### Commit Messages
```
feat: add shift claiming with approval flow
fix: enforce organisation scope on persons query
refactor: extract briefing logic to BriefingService
test: add accreditation assignment tests
```
## Forbidden Patterns
- NEVER: `$user->role === 'admin'` (use Policies + Spatie roles)
- NEVER: `Model::all()` without where-clause (always scope)
- NEVER: `dd()` or `var_dump()` left in code
- NEVER: Hardcode `.env` values in code
- NEVER: JSON columns for queryable/filterable data
- NEVER: UUID v4 as primary key (use HasUlids for ULID)
- NEVER: Blade views or Inertia (API-only backend)
- NEVER: Business logic in controllers without Policy authorization
## Code Style Principles
1. **Explicit over implicit** - Be clear about types, returns, and intentions
2. **Single Responsibility** - Each class/function does one thing well
3. **Type Safety** - PHP type hints and TypeScript strict mode everywhere
4. **Multi-tenant first** - Every feature must respect organisation boundaries
5. **Mobile-first** - Responsive design, minimum 375px width