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
This commit is contained in:
@@ -1,223 +1,168 @@
|
||||
---
|
||||
description: Core workspace rules for Laravel + Vue/TypeScript full-stack application
|
||||
description: Core workspace rules for EventCrew multi-tenant SaaS platform
|
||||
globs: ["**/*"]
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Workspace Rules
|
||||
|
||||
You are an expert full-stack developer working on a Laravel API backend with a Vue 3/TypeScript frontend using Vuexy admin template. This is an API-first architecture where the backend and frontend are completely separated.
|
||||
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.3+
|
||||
- Laravel 12+
|
||||
- Laravel Sanctum for SPA authentication (token-based)
|
||||
- MySQL 8.0 database
|
||||
- Redis for cache and queues
|
||||
- Pest for testing
|
||||
- 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)
|
||||
- Vue 3 with TypeScript (strict mode)
|
||||
- Vite as build tool
|
||||
- Vuexy Admin Template
|
||||
- TanStack Query (Vue Query) for server state
|
||||
- Pinia for client state
|
||||
- Vue Router for routing
|
||||
- Axios for HTTP client
|
||||
- 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
|
||||
|
||||
```
|
||||
band-management/
|
||||
├── api/ # Laravel 12 API
|
||||
event-crew/
|
||||
├── api/ # Laravel 12 REST API (JSON only)
|
||||
│ ├── app/
|
||||
│ │ ├── Actions/ # Single-purpose business logic
|
||||
│ │ ├── Enums/ # PHP enums
|
||||
│ │ ├── Http/
|
||||
│ │ │ ├── Controllers/Api/V1/
|
||||
│ │ │ ├── Middleware/
|
||||
│ │ │ ├── Requests/ # Form Request validation
|
||||
│ │ │ └── Resources/ # API Resources
|
||||
│ │ ├── Models/ # Eloquent models
|
||||
│ │ ├── Policies/ # Authorization
|
||||
│ │ ├── Services/ # Complex business logic
|
||||
│ │ └── Traits/ # Shared traits
|
||||
│ │ │ ├── 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/
|
||||
│ │ ├── factories/
|
||||
│ │ ├── migrations/
|
||||
│ │ ├── factories/
|
||||
│ │ └── seeders/
|
||||
│ ├── routes/
|
||||
│ │ └── api.php # API routes
|
||||
│ └── tests/
|
||||
│ ├── Feature/Api/
|
||||
│ └── Unit/
|
||||
│ └── tests/Feature/Api/V1/
|
||||
│
|
||||
├── apps/
|
||||
│ ├── admin/ # Admin Dashboard (Vuexy full)
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── @core/ # Vuexy core (don't modify)
|
||||
│ │ │ ├── @layouts/ # Vuexy layouts (don't modify)
|
||||
│ │ │ ├── components/ # Custom components
|
||||
│ │ │ ├── composables/ # Vue composables
|
||||
│ │ │ ├── layouts/ # App layouts
|
||||
│ │ │ ├── lib/ # Utilities (api-client, etc.)
|
||||
│ │ │ ├── navigation/ # Menu configuration
|
||||
│ │ │ ├── pages/ # Page components
|
||||
│ │ │ ├── plugins/ # Vue plugins
|
||||
│ │ │ ├── router/ # Vue Router
|
||||
│ │ │ ├── stores/ # Pinia stores
|
||||
│ │ │ └── types/ # TypeScript types
|
||||
│ │ └── ...
|
||||
│ ├── 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
|
||||
│ │
|
||||
│ ├── band/ # Band Portal (Vuexy starter)
|
||||
│ └── customers/ # Customer Portal (Vuexy starter)
|
||||
│ ├── 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
|
||||
│
|
||||
├── docker/ # Docker configurations
|
||||
├── docs/ # Documentation
|
||||
└── .cursor/ # Cursor AI configuration
|
||||
├── 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`, `MusicNumber` |
|
||||
| Controllers | PascalCase + Controller | `EventController` |
|
||||
| Form Requests | Action + Resource + Request | `StoreEventRequest` |
|
||||
| Resources | Resource + Resource | `EventResource` |
|
||||
| Actions | Verb + Resource + Action | `CreateEventAction` |
|
||||
| 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`, `music_numbers` |
|
||||
| Columns | snake_case | `event_date`, `created_at` |
|
||||
| Enums | Singular PascalCase | `EventStatus` |
|
||||
| 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 | `EventCard.vue` |
|
||||
| Pages | PascalCase + Page | `EventsPage.vue` |
|
||||
| Composables | camelCase with "use" | `useEvents.ts` |
|
||||
| Stores | camelCase | `authStore.ts` |
|
||||
| Types/Interfaces | PascalCase | `Event`, `ApiResponse` |
|
||||
| Files | kebab-case or camelCase | `api-client.ts` |
|
||||
| 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` |
|
||||
|
||||
## Code Style
|
||||
|
||||
### General Principles
|
||||
|
||||
1. **Explicit over implicit** - Be clear about types, returns, and intentions
|
||||
2. **Small, focused units** - Each file/function does one thing well
|
||||
3. **Consistent formatting** - Use automated formatters
|
||||
4. **Descriptive names** - Names should explain purpose
|
||||
5. **No magic** - Avoid hidden behavior
|
||||
|
||||
### PHP
|
||||
|
||||
- Use `declare(strict_types=1);` in all files
|
||||
- Use `final` for classes that shouldn't be extended
|
||||
- Use readonly properties where applicable
|
||||
- Prefer named arguments for clarity
|
||||
- Use enums instead of string constants
|
||||
|
||||
### TypeScript
|
||||
|
||||
- Enable strict mode in tsconfig
|
||||
- No `any` types - use `unknown` if truly unknown
|
||||
- Use interface for objects, type for unions/primitives
|
||||
- Prefer `const` over `let`
|
||||
- Use optional chaining and nullish coalescing
|
||||
### 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 |
|
||||
|---------|-----|
|
||||
| API | http://localhost:8000/api/v1 |
|
||||
| Admin SPA | http://localhost:5173 |
|
||||
| Band SPA | http://localhost:5174 |
|
||||
| Customer SPA | http://localhost:5175 |
|
||||
| MySQL | localhost:3306 |
|
||||
| Redis | localhost:6379 |
|
||||
| Mailpit | http://localhost:8025 |
|
||||
| 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` | - |
|
||||
|
||||
### Database Credentials (Development)
|
||||
|
||||
```
|
||||
Host: 127.0.0.1
|
||||
Port: 3306
|
||||
Database: band_management
|
||||
Username: band_management
|
||||
Password: secret
|
||||
```
|
||||
|
||||
### Production URLs
|
||||
|
||||
| Service | URL |
|
||||
|---------|-----|
|
||||
| API | https://api.bandmanagement.nl |
|
||||
| Admin | https://admin.bandmanagement.nl |
|
||||
| Band | https://band.bandmanagement.nl |
|
||||
| Customers | https://customers.bandmanagement.nl |
|
||||
### 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/event-management`
|
||||
- `fix/rsvp-validation`
|
||||
- `refactor/auth-system`
|
||||
- `feature/shift-planning`
|
||||
- `fix/organisation-scoping`
|
||||
- `refactor/accreditation-engine`
|
||||
|
||||
### Commit Messages
|
||||
```
|
||||
feat: add event RSVP functionality
|
||||
fix: correct date validation in events
|
||||
refactor: extract event creation to action class
|
||||
docs: update API documentation
|
||||
test: add event controller tests
|
||||
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
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
## Forbidden Patterns
|
||||
|
||||
### PHP (api/composer.json)
|
||||
- PHP 8.3+
|
||||
- Laravel 12
|
||||
- Laravel Sanctum
|
||||
- Laravel Pint (formatting)
|
||||
- Pest PHP (testing)
|
||||
|
||||
### Node (apps/*/package.json)
|
||||
- Vue 3.4+
|
||||
- TypeScript 5.3+
|
||||
- Vite 5+
|
||||
- Pinia
|
||||
- @tanstack/vue-query
|
||||
- axios
|
||||
- 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. **Readability over cleverness** - Write code that is easy to understand
|
||||
1. **Explicit over implicit** - Be clear about types, returns, and intentions
|
||||
2. **Single Responsibility** - Each class/function does one thing well
|
||||
3. **Type Safety** - Leverage TypeScript and PHP type hints everywhere
|
||||
4. **Testability** - Write code that is easy to test
|
||||
5. **API Consistency** - Follow RESTful conventions
|
||||
|
||||
## Response Format
|
||||
|
||||
When generating code:
|
||||
1. Always include proper type hints/annotations
|
||||
2. Add brief comments for complex logic only
|
||||
3. Follow the established patterns in the codebase
|
||||
4. Consider error handling and edge cases
|
||||
5. Suggest tests for new functionality
|
||||
|
||||
## Communication Style
|
||||
|
||||
- Be concise and direct
|
||||
- Provide working code examples
|
||||
- Explain architectural decisions briefly
|
||||
- Ask clarifying questions only when truly ambiguous
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user