diff --git a/README.md b/README.md index abd3807..1e90a13 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,137 @@ -# Event Crew +# EventCrew -Full-stack event crew operations management platform. +Multi-tenant SaaS platform for **event and festival operations**: planning, people, accreditation, artist advancing, volunteer shifts, briefings, and show-day tooling (Mission Control). The backend is a **JSON-only** Laravel API; all user interfaces are **Vue 3** single-page apps. -## Tech Stack +--- + +## What EventCrew covers + +- **Organisations & events** — Multi-tenant data with organisation-scoped access; events move through a defined lifecycle (draft → published → registration → buildup → show day → teardown → closed). +- **Festival structure** — Sections, time slots, shifts, assignments, claiming/approval flows for volunteers and crew. +- **People & crowds** — Crowd types (crew, volunteers, artists, guests, press, partners, suppliers), persons, lists, and (planned) rich accreditation (items, zones, hand-out). +- **Artists & advancing** — Booking status, stages/timetable concepts, advance sections and token-based portal access for external artists. +- **Communication** — Briefings, campaigns, and operational messaging (see architecture doc for target modules). +- **Portal (external users)** — One portal app, two modes: **login** (Sanctum) for long-term users such as volunteers, and **token** access for per-event links (e.g. artists, suppliers). + +Implementation is phased; the authoritative feature and schema list lives in the architecture and design references below. + +--- + +## Applications + +| App | Path | Port | Role | +|-----|------|------|------| +| **Admin** | `apps/admin/` | 5173 | Platform **super admin**: organisations, billing-style flags, global settings. Full Vuexy shell. | +| **Organizer** | `apps/app/` | 5174 | Main product for **org and event staff**: events, sections, shifts, people, artists, accreditation, briefings, reports, etc. | +| **Portal** | `apps/portal/` | 5175 | **External** users: stripped layout; login- or token-based access. | + +All apps talk to the API over **CORS** with **Laravel Sanctum** tokens. + +--- + +## Tech stack | Layer | Technology | |-------|------------| -| Backend | Laravel 12 + PHP 8.3 + Sanctum | -| Database | MySQL 8.0 | -| Frontend | Vue 3 + TypeScript + Vuexy | -| Local Dev | Native PHP/Node + Docker | +| API | PHP 8.2+, Laravel 12, Sanctum, Spatie Permission (and Activity Log / Media Library where used) | +| Data | MySQL 8, Redis (cache/queues) | +| Frontends | Vue 3, TypeScript, Vite, Vuexy + Vuetify, Pinia, TanStack Query, VeeValidate + Zod | +| Local services | Docker Compose (MySQL, Redis, Mailpit) | -## Quick Start +**Rule of thumb:** business tables use **ULID** primary keys; event-related data is scoped by **organisation** (global scopes + policies), not ad hoc `where` clauses in controllers. -```bash -# 1. Start Docker services -make services +--- -# 2. Open in Cursor and start building! -``` - -See [docs/SETUP.md](docs/SETUP.md) for detailed instructions. - -## Project Structure +## Project structure ``` event-crew/ -├── api/ # Laravel 12 API +├── api/ # Laravel 12 REST API (JSON only) ├── apps/ -│ ├── admin/ # Admin Dashboard -│ ├── band/ # Band Member Portal -│ └── customers/ # Customer Portal -├── docker/ # Docker configs -├── docs/ # Documentation -├── resources/ # Vuexy template source (reference) -├── .cursor/rules # Cursor AI instructions -└── Makefile # Development commands +│ ├── admin/ # Super Admin SPA +│ ├── app/ # Organizer SPA (primary UI) +│ └── portal/ # External portal SPA +├── docker/ # Docker / Compose assets +├── docs/ # SETUP, API notes, schema notes +├── resources/ +│ ├── design/ # Product source of truth (design docs, see table below) +│ └── vuexy-admin-*/ # Vuexy template reference (bundled kit) +├── .cursor/ # ARCHITECTURE.md, instructions.md, rules for AI/helpers +└── Makefile # Dev commands ``` -## Frontend Apps (Vuexy v10.11.1) +Vuexy **`@core/`** and **`@layouts/`** in each app should stay untouched; customize via app config, navigation, and app-level components. -All frontend apps are built with **Vue 3 + TypeScript** using the [Vuexy Admin Template](https://pixinvent.com/vuexy-vuejs-admin-dashboard-template/). +--- -| App | Template | Description | -|-----|----------|-------------| -| **Admin** | TypeScript Full Version | Complete admin dashboard with all Vuexy features | -| **Band Portal** | TypeScript Starter Kit | Lightweight portal for band members | -| **Customer Portal** | TypeScript Starter Kit | Lightweight portal for customers | - -**Template source:** `resources/vuexy-admin-v10.11.1/vue-version/typescript-version/` - -> **Note:** The `@core/` and `@layouts/` folders should not be modified directly. Customize through `themeConfig.ts` and override styles in `assets/styles/`. - -## URLs - -| App | Development | Production | -|-----|-------------|------------| -| API | http://localhost:8000/api/v1 | https://api.eventcrew.nl | -| Admin | http://localhost:5173 | https://admin.eventcrew.nl | -| Band Portal | http://localhost:5174 | https://band.eventcrew.nl | -| Customer Portal | http://localhost:5175 | https://customers.eventcrew.nl | - -## Development Commands +## Quick start ```bash -make services # Start MySQL, Redis, Mailpit -make services-stop # Stop Docker services -make api # Start Laravel API -make admin # Start Admin SPA -make band # Start Band Portal -make customers # Start Customer Portal +# 1. Infrastructure +make services + +# 2. API env, dependencies, database (see docs/SETUP.md) +cd api && cp .env.example .env && composer install && php artisan key:generate && php artisan migrate + +# 3. Run API + the SPAs you need (separate terminals) +make api +make admin # optional +make app # optional +make portal # optional ``` + +Detailed setup: **[docs/SETUP.md](docs/SETUP.md)**. + +--- + +## Development URLs + +| Service | Development | Env / notes | +|---------|-------------|-------------| +| API | http://localhost:8000/api/v1 | Base path `/api/v1` | +| Admin | http://localhost:5173 | `FRONTEND_ADMIN_URL` in Laravel CORS | +| Organizer | http://localhost:5174 | `FRONTEND_APP_URL` | +| Portal | http://localhost:5175 | `FRONTEND_PORTAL_URL` | +| Mailpit | http://localhost:8025 | Local mail capture | + +Production hostnames are placeholders until you deploy; configure `config/cors.php` via `.env`. + +--- + +## Makefile commands + +```bash +make services # MySQL, Redis, Mailpit +make services-stop +make api # Laravel on :8000 +make admin # Admin on :5173 +make app # Organizer on :5174 +make portal # Portal on :5175 +make migrate +make fresh # migrate:fresh --seed +make db-shell +``` + +--- + +## Documentation + +| Resource | Contents | +|----------|----------| +| [resources/design/](resources/design/) | **Canonical product specs** — place design documents here (often Word). Referenced by `.cursor` as source of truth for features and data model. Expected names include `EventCrew_Design_Document_v1.3.docx`, `EventCrew_Dev_Guide_v1.0.docx`, `EventCrew_Start_Guide_v1.0.docx` (adjust versions if you update files). | +| [.cursor/ARCHITECTURE.md](.cursor/ARCHITECTURE.md) | System diagram, apps, multi-tenancy, roles, event lifecycle, API route map, core schema overview (summarises `resources/design` when present) | +| [.cursor/instructions.md](.cursor/instructions.md) | Quick reference, phased roadmap, module build order | +| [.cursor/rules/](.cursor/rules/) | Workspace, Laravel, Vue, testing conventions | +| [docs/SETUP.md](docs/SETUP.md) | Environment and local setup | +| [docs/API.md](docs/API.md) | API notes (if maintained) | +| [docs/SCHEMA.md](docs/SCHEMA.md) | Schema notes (if maintained) | + +--- + +## Testing + +```bash +cd api && php artisan test +``` + +Feature tests should cover happy paths plus **401** (unauthenticated), **403** (wrong organisation), and **422** (validation) where applicable. diff --git a/apps/admin/auto-imports.d.ts b/apps/admin/auto-imports.d.ts index 20d68a7..200bbb9 100644 --- a/apps/admin/auto-imports.d.ts +++ b/apps/admin/auto-imports.d.ts @@ -55,6 +55,7 @@ declare global { const getActivePinia: typeof import('pinia')['getActivePinia'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] + const getUserAbilityRules: typeof import('./src/utils/auth-ability')['getUserAbilityRules'] const h: typeof import('vue')['h'] const hexToRgb: typeof import('./src/@core/utils/colorConverter')['hexToRgb'] const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] @@ -200,6 +201,7 @@ declare global { const useCssVar: typeof import('@vueuse/core')['useCssVar'] const useCssVars: typeof import('vue')['useCssVars'] const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] + const useCurrentOrganisationId: typeof import('./src/composables/useOrganisationContext')['useCurrentOrganisationId'] const useCustomFetch: typeof import('./src/composables/useFetch')['useCustomFetch'] const useCycleList: typeof import('@vueuse/core')['useCycleList'] const useDark: typeof import('@vueuse/core')['useDark'] @@ -423,6 +425,7 @@ declare module 'vue' { readonly getActivePinia: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getUserAbilityRules: UnwrapRef readonly h: UnwrapRef readonly hexToRgb: UnwrapRef readonly ignorableWatch: UnwrapRef @@ -529,7 +532,6 @@ declare module 'vue' { readonly useAbs: UnwrapRef readonly useActiveElement: UnwrapRef readonly useAnimate: UnwrapRef - readonly useApi: UnwrapRef readonly useArrayDifference: UnwrapRef readonly useArrayEvery: UnwrapRef readonly useArrayFilter: UnwrapRef @@ -566,6 +568,7 @@ declare module 'vue' { readonly useCssVar: UnwrapRef readonly useCssVars: UnwrapRef readonly useCurrentElement: UnwrapRef + readonly useCurrentOrganisationId: UnwrapRef readonly useCycleList: UnwrapRef readonly useDark: UnwrapRef readonly useDateFormat: UnwrapRef diff --git a/apps/app/auto-imports.d.ts b/apps/app/auto-imports.d.ts index 772a4c8..29d148d 100644 --- a/apps/app/auto-imports.d.ts +++ b/apps/app/auto-imports.d.ts @@ -200,6 +200,7 @@ declare global { const useCssVar: typeof import('@vueuse/core')['useCssVar'] const useCssVars: typeof import('vue')['useCssVars'] const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] + const useCurrentOrganisationId: typeof import('./src/composables/useOrganisationContext')['useCurrentOrganisationId'] const useCustomFetch: typeof import('./src/composables/useFetch')['useCustomFetch'] const useCycleList: typeof import('@vueuse/core')['useCycleList'] const useDark: typeof import('@vueuse/core')['useDark'] @@ -376,7 +377,6 @@ import { UnwrapRef } from 'vue' declare module 'vue' { interface GlobalComponents {} interface ComponentCustomProperties { - readonly $api: UnwrapRef readonly COOKIE_MAX_AGE_1_YEAR: UnwrapRef readonly EffectScope: UnwrapRef readonly acceptHMRUpdate: UnwrapRef @@ -528,7 +528,6 @@ declare module 'vue' { readonly useAbs: UnwrapRef readonly useActiveElement: UnwrapRef readonly useAnimate: UnwrapRef - readonly useApi: UnwrapRef readonly useArrayDifference: UnwrapRef readonly useArrayEvery: UnwrapRef readonly useArrayFilter: UnwrapRef @@ -565,6 +564,7 @@ declare module 'vue' { readonly useCssVar: UnwrapRef readonly useCssVars: UnwrapRef readonly useCurrentElement: UnwrapRef + readonly useCurrentOrganisationId: UnwrapRef readonly useCycleList: UnwrapRef readonly useDark: UnwrapRef readonly useDateFormat: UnwrapRef diff --git a/apps/app/typed-router.d.ts b/apps/app/typed-router.d.ts index 1d909c4..de1c253 100644 --- a/apps/app/typed-router.d.ts +++ b/apps/app/typed-router.d.ts @@ -23,6 +23,5 @@ declare module 'vue-router/auto-routes' { 'events': RouteRecordInfo<'events', '/events', Record, Record>, 'events-id': RouteRecordInfo<'events-id', '/events/:id', { id: ParamValue }, { id: ParamValue }>, 'login': RouteRecordInfo<'login', '/login', Record, Record>, - 'second-page': RouteRecordInfo<'second-page', '/second-page', Record, Record>, } }