diff --git a/.cursor/ARCHITECTURE.md b/.cursor/ARCHITECTURE.md
index 4dc52be..3def2bb 100644
--- a/.cursor/ARCHITECTURE.md
+++ b/.cursor/ARCHITECTURE.md
@@ -1,97 +1,152 @@
-# Band Management - Architecture
+# EventCrew - Architecture
-> This document describes the system architecture, design decisions, and patterns used in this application.
+> Multi-tenant SaaS platform for event- and festival management.
+> Source of truth: `/resources/design/EventCrew_Design_Document_v1.3.docx`
## System Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
-│ INTERNET │
+│ INTERNET │
└─────────────────────────────────────────────────────────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
-│ Admin SPA │ │ Band SPA │ │ Customer SPA │
-│ (Vuexy Full) │ │ (Vuexy Lite) │ │ (Vuexy Lite) │
+│ Admin SPA │ │ Organizer │ │ Portal SPA │
+│ (Super Admin)│ │ SPA (Main) │ │ (External) │
│ :5173 │ │ :5174 │ │ :5175 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└───────────────────────────┼───────────────────────────┘
- │
+ │ CORS + Sanctum tokens
▼
┌───────────────────────┐
- │ Laravel API │
- │ (Sanctum) │
- │ :8000 │
+ │ Laravel 12 REST API │
+ │ (JSON only, no │
+ │ Blade views) │
+ │ :8000 │
└───────────┬───────────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
- │ MySQL │ │ Redis │ │ Mailpit │
+ │ MySQL 8 │ │ Redis │ │ Mailpit │
│ :3306 │ │ :6379 │ │ :8025 │
└───────────┘ └───────────┘ └───────────┘
```
+**Golden Rule:** Laravel is exclusively a JSON REST API. No Blade views, no Mix, no Inertia. Every response is `application/json`. Vue handles ALL UI via three SPAs.
+
---
## Applications
### Admin Dashboard (`apps/admin/`)
-**Purpose**: Full management interface for band administrators.
+**Purpose**: Super Admin platform management.
-**Users**: Band leaders, managers, booking agents
+**Users**: Platform owner only (super_admin role).
**Features**:
-- Member management (CRUD, roles, invitations)
-- Event/gig management (calendar, list, RSVP tracking)
-- Music catalog (songs, attachments, metadata)
-- Setlist builder (drag-drop, templates)
-- Location management (venues, contacts)
-- Customer CRM (companies, individuals, history)
-- Booking request management
-- Reports and analytics
+- Organisation management (CRUD, billing status)
+- Platform user management
+- Global settings
**Vuexy Version**: `typescript-version/full-version`
---
-### Band Portal (`apps/band/`)
+### Organizer App (`apps/app/`)
-**Purpose**: Member-facing interface for band members.
+**Purpose**: Main application for event management per organisation.
-**Users**: Musicians, performers, crew
+**Users**: Organisation Admins, Event Managers, Staff Coordinators, Artist Managers, Volunteer Coordinators.
**Features**:
-- Personal dashboard (upcoming gigs)
-- Event calendar with RSVP
-- View setlists and music
-- Download attachments (lyrics, charts)
-- Profile settings
-- Notifications
+- Event lifecycle management (Draft through Closed)
+- Festival Sections, Time Slots, Shift planning
+- Person & Crowd management (Crew, Volunteers, Artists, Guests, Press, Partners, Suppliers)
+- Accreditation engine (categories, items, access zones)
+- Artist booking & advancing
+- Timetable & stage management
+- Briefing builder & communication hub
+- Mission Control (show day operations)
+- Form builder with conditional logic
+- Supplier & production management
+- Reporting & insights
-**Vuexy Version**: `typescript-version/starter-kit`
+**Vuexy Version**: `typescript-version/full-version` (customized navigation)
---
-### Customer Portal (`apps/customers/`)
+### Portal (`apps/portal/`)
-**Purpose**: Client-facing interface for customers.
+**Purpose**: External-facing portal with two access modes.
-**Users**: Event organizers, venue managers, clients
+**Users**: Volunteers, Crew (login-based), Artists, Suppliers, Press (token-based).
-**Features**:
-- View booked events
-- Submit booking requests
-- Track request status
-- View assigned setlists (if permitted)
-- Profile settings
+**Access Modes**:
-**Vuexy Version**: `typescript-version/starter-kit`
+| User | Access Mode | Rationale |
+|------|-------------|-----------|
+| Volunteer / Crew | Login (`auth:sanctum`) | Long-term relationship, festival passport, shift history |
+| Artist / Tour Manager | Token (`portal.token` middleware) | Per-event, advance portal via signed URL |
+| Supplier / Partner | Token (`portal.token` middleware) | Per-event, production request via token |
+| Press / Media | Token (`portal.token` middleware) | Per-event accreditation, no recurring relationship |
+
+**Router guard logic**: If `route.query.token` -> token mode. If `authStore.isAuthenticated` -> login mode. Otherwise -> redirect to `/login`.
+
+**Vuexy Version**: `typescript-version/starter-kit` (stripped: no sidebar, no customizer, no dark mode toggle; uses Vuetify components + Vuexy SCSS)
+
+---
+
+## Multi-Tenant Data Model
+
+Shared database schema with organisation scoping on all tables. No row-level security at DB level; scoping enforced via Laravel Policies and Eloquent Global Scopes.
+
+**Scoping Rule**: EVERY query on event data MUST have an `organisation_id` scope via `OrganisationScope` Global Scope.
+
+**Tenancy Hierarchy**:
+
+```
+Platform (Super Admin)
+ └─ Organisation (client A)
+ └─ Event (event 1)
+ └─ Festival Section (Bar, Hospitality, Technical, ...)
+ ├─ Time Slots (DAY1-EARLY-CREW, DAY1-EARLY-VOLUNTEER, ...)
+ └─ Shifts (Bar x DAY1-EARLY-VOLUNTEER, 5 slots)
+```
+
+---
+
+## Three-Level Role & Permission Model
+
+Managed via Spatie `laravel-permission` with team-based permissions.
+
+| Level | Scope | Roles | Implementation |
+|-------|-------|-------|----------------|
+| App Level | Whole platform | `super_admin`, `support_agent` | Spatie role |
+| Organisation Level | Within one org | `org_admin`, `org_member`, `org_readonly` | Spatie team = organisation |
+| Event Level | Within one event | `event_manager`, `artist_manager`, `staff_coordinator`, `volunteer_coordinator`, `accreditation_officer` | `event_user_roles` pivot table |
+
+**Middleware**: `OrganisationRoleMiddleware` and `EventRoleMiddleware` check per route.
+
+---
+
+## Event Lifecycle
+
+| Phase | Description |
+|-------|-------------|
+| `draft` | Created but not published. Only admin sees it. |
+| `published` | Active in planning. Internal modules available. External portals closed. |
+| `registration_open` | Volunteer registration and artist advance portals open. |
+| `buildup` | Setup days. Crew shifts begin. Accreditation distribution starts. |
+| `showday` | Active event days. Mission Control active. Real-time check-in. |
+| `teardown` | Breakdown days. Inventory return. Shift closure. |
+| `closed` | Event completed. Read-only. Reports available. |
---
@@ -99,410 +154,102 @@
### Base URL
- Development: `http://localhost:8000/api/v1`
-- Production: `https://api.bandmanagement.nl/api/v1`
-### Authentication Endpoints
-```
-POST /auth/register Register new user
-POST /auth/login Login, returns token
-POST /auth/logout Logout, revokes token
-GET /auth/user Get authenticated user
-POST /auth/forgot-password Request password reset
-POST /auth/reset-password Reset password with token
-```
-
-### Resource Endpoints
-```
-# Events
-GET /events List events (paginated, filterable)
-POST /events Create event
-GET /events/{id} Get event details
-PUT /events/{id} Update event
-DELETE /events/{id} Delete event
-POST /events/{id}/invite Invite members to event
-GET /events/{id}/invitations Get event invitations
-POST /events/{id}/rsvp Submit RSVP response
-POST /events/{id}/duplicate Duplicate event
-
-# Members
-GET /members List members
-POST /members Create member
-GET /members/{id} Get member details
-PUT /members/{id} Update member
-DELETE /members/{id} Delete/deactivate member
-POST /members/invite Send invitation email
-
-# Music
-GET /music List music numbers
-POST /music Create music number
-GET /music/{id} Get music number
-PUT /music/{id} Update music number
-DELETE /music/{id} Delete music number
-POST /music/{id}/attachments Upload attachment
-DELETE /music/{id}/attachments/{aid} Delete attachment
-
-# Setlists
-GET /setlists List setlists
-POST /setlists Create setlist
-GET /setlists/{id} Get setlist with items
-PUT /setlists/{id} Update setlist
-DELETE /setlists/{id} Delete setlist
-POST /setlists/{id}/clone Clone setlist
-PUT /setlists/{id}/items Reorder/update items
-
-# Locations
-GET /locations List locations
-POST /locations Create location
-GET /locations/{id} Get location
-PUT /locations/{id} Update location
-DELETE /locations/{id} Delete location
-
-# Customers
-GET /customers List customers
-POST /customers Create customer
-GET /customers/{id} Get customer
-PUT /customers/{id} Update customer
-DELETE /customers/{id} Delete customer
-
-# Booking Requests (Customer Portal)
-GET /booking-requests List user's requests
-POST /booking-requests Submit booking request
-GET /booking-requests/{id} Get request details
-
-# Notifications
-GET /notifications List notifications
-PUT /notifications/{id}/read Mark as read
-POST /notifications/read-all Mark all as read
-```
-
----
-
-## Database Schema
-
-### Entity Relationship Diagram
+### Route Groups
```
-┌──────────────┐ ┌──────────────┐ ┌──────────────┐
-│ users │ │ customers │ │ locations │
-├──────────────┤ ├──────────────┤ ├──────────────┤
-│ id (ULID) │──┐ │ id (ULID) │ │ id (ULID) │
-│ name │ │ │ user_id (FK) │───────│ name │
-│ email │ │ │ name │ │ address │
-│ type │ │ │ company_name │ │ city │
-│ role │ │ │ type │ │ capacity │
-│ status │ │ │ email │ │ contact_* │
-└──────────────┘ │ └──────────────┘ └──────────────┘
- │ │ │ │
- │ │ │ │
- ▼ │ ▼ ▼
-┌──────────────┐ │ ┌──────────────┐ ┌──────────────┐
-│event_invites │ │ │ events │───────│ setlists │
-├──────────────┤ │ ├──────────────┤ ├──────────────┤
-│ id (ULID) │ │ │ id (ULID) │ │ id (ULID) │
-│ event_id(FK) │──┼───▶│ title │ │ name │
-│ user_id (FK) │──┘ │ location_id │ │ description │
-│ rsvp_status │ │ customer_id │ │ is_template │
-│ rsvp_note │ │ setlist_id │◀──────│ is_archived │
-└──────────────┘ │ event_date │ └──────────────┘
- │ status │ │
- │ created_by │ │
- └──────────────┘ ▼
- ┌──────────────┐
-┌──────────────┐ ┌──────────────┐ │setlist_items │
-│music_numbers │───────│music_attach │ ├──────────────┤
-├──────────────┤ ├──────────────┤ │ id (ULID) │
-│ id (ULID) │ │ id (ULID) │ │ setlist_id │
-│ title │ │ music_num_id │ │ music_num_id │
-│ artist │ │ file_name │ │ position │
-│ duration │ │ file_type │ │ set_number │
-│ key, tempo │ │ file_path │ │ is_break │
-│ tags (JSON) │ └──────────────┘ └──────────────┘
-└──────────────┘
+# Public (no auth)
+POST /auth/login
+POST /portal/token-auth Token-based portal access
+POST /portal/form-submit Public form submission
+
+# Protected (auth:sanctum)
+POST /auth/logout
+GET /auth/me Returns user + organisations + event roles
+
+# Organisations
+GET/POST /organisations
+GET/PUT /organisations/{id}
+POST /organisations/{id}/invite
+GET /organisations/{id}/members
+
+# Events (nested under organisations)
+GET/POST /organisations/{org}/events
+GET/PUT/DELETE /events/{id}
+PUT /events/{id}/status
+
+# Festival Sections
+GET/POST /events/{event}/sections
+GET/PUT/DELETE /sections/{id}
+GET /sections/{id}/dashboard
+
+# Time Slots
+GET/POST /events/{event}/time-slots
+PUT/DELETE /time-slots/{id}
+
+# Shifts
+GET/POST /sections/{section}/shifts
+PUT/DELETE /shifts/{id}
+POST /shifts/{id}/assign
+POST /shifts/{id}/claim Volunteer self-service
+
+# Persons
+GET/POST /events/{event}/persons
+GET/PUT /persons/{id}
+POST /persons/{id}/approve
+POST /persons/{id}/checkin
+
+# Crowd Types & Lists
+GET/POST /organisations/{org}/crowd-types
+GET/POST /events/{event}/crowd-lists
+
+# Artists & Advancing
+GET/POST /events/{event}/artists
+GET/PUT /artists/{id}
+GET/POST /artists/{id}/sections Advance sections
+POST /sections/{id}/submit Advance submission
+
+# Accreditation
+GET/POST /events/{event}/accreditation-items
+POST /persons/{id}/accreditations
+GET/POST /events/{event}/access-zones
+
+# Briefings & Communication
+GET/POST /events/{event}/briefings
+POST /briefings/{id}/send
+GET/POST /events/{event}/campaigns
+POST /campaigns/{id}/send
+
+# Mission Control
+GET /events/{event}/mission-control
+POST /persons/{id}/checkin-item
+
+# Scanners & Inventory
+GET/POST /events/{event}/scanners
+POST /scan
+GET/POST /events/{event}/inventory
+
+# Reports
+GET /events/{event}/reports/{type}
+
+# Portal (token-based, portal.token middleware)
+GET /portal/artist
+POST /portal/advancing
+GET /portal/supplier
+POST /portal/production-request
```
-### Table Definitions
+### API Response Format
-#### users
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| name | string | Full name |
-| email | string | Unique email |
-| email_verified_at | timestamp | Email verification |
-| password | string | Hashed password |
-| phone | string? | Phone number |
-| bio | text? | Biography |
-| instruments | json? | Array of instruments |
-| avatar_path | string? | Avatar file path |
-| type | enum | `member`, `customer` |
-| role | enum? | `admin`, `booking_agent`, `music_manager`, `member` |
-| status | enum | `active`, `inactive` |
-| invited_at | timestamp? | When invited |
-| last_login_at | timestamp? | Last login |
-| remember_token | string? | Remember me token |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### customers
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| user_id | ULID? | FK to users (for portal access) |
-| name | string | Contact name |
-| company_name | string? | Company name |
-| type | enum | `individual`, `company` |
-| email | string? | Email |
-| phone | string? | Phone |
-| address | string? | Street address |
-| city | string? | City |
-| postal_code | string? | Postal code |
-| country | string | Country (default: NL) |
-| notes | text? | Internal notes |
-| is_portal_enabled | boolean | Can access portal |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### locations
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| name | string | Venue name |
-| address | string | Street address |
-| city | string | City |
-| postal_code | string? | Postal code |
-| country | string | Country (default: NL) |
-| latitude | decimal? | GPS latitude |
-| longitude | decimal? | GPS longitude |
-| capacity | integer? | Max capacity |
-| contact_name | string? | Contact person |
-| contact_email | string? | Contact email |
-| contact_phone | string? | Contact phone |
-| stage_specs | text? | Stage specifications |
-| technical_notes | text? | Technical requirements |
-| parking_info | text? | Parking information |
-| notes | text? | General notes |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### events
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| title | string | Event title |
-| description | text? | Description |
-| location_id | ULID? | FK to locations |
-| customer_id | ULID? | FK to customers |
-| setlist_id | ULID? | FK to setlists |
-| event_date | date | Date of event |
-| start_time | time | Start time |
-| end_time | time? | End time |
-| load_in_time | time? | Load-in time |
-| soundcheck_time | time? | Soundcheck time |
-| fee | decimal(10,2)? | Payment amount |
-| currency | string | Currency (default: EUR) |
-| status | enum | `draft`, `pending`, `confirmed`, `completed`, `cancelled` |
-| visibility | enum | `private`, `members`, `public` |
-| rsvp_deadline | datetime? | RSVP deadline |
-| notes | text? | Public notes |
-| internal_notes | text? | Admin-only notes |
-| is_public_setlist | boolean | Show setlist to customer |
-| created_by | ULID | FK to users |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### event_invitations
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| event_id | ULID | FK to events |
-| user_id | ULID | FK to users |
-| rsvp_status | enum | `pending`, `available`, `unavailable`, `tentative` |
-| rsvp_note | text? | Response note |
-| rsvp_responded_at | timestamp? | When responded |
-| invited_at | timestamp | When invited |
-| reminder_sent_at | timestamp? | Last reminder |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### music_numbers
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| title | string | Song title |
-| artist | string? | Original artist |
-| genre | string? | Genre/style |
-| duration_seconds | integer? | Duration in seconds |
-| key | string? | Musical key (e.g., "Am", "G") |
-| tempo_bpm | integer? | Tempo in BPM |
-| time_signature | string? | Time signature (e.g., "4/4") |
-| lyrics | text? | Full lyrics |
-| notes | text? | Performance notes |
-| tags | json? | Array of tags |
-| play_count | integer | Times played (default: 0) |
-| last_played_at | timestamp? | Last performed |
-| is_active | boolean | Active in catalog |
-| created_by | ULID? | FK to users |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### music_attachments
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| music_number_id | ULID | FK to music_numbers |
-| file_name | string | Stored filename |
-| original_name | string | Original filename |
-| file_path | string | Storage path |
-| file_type | enum | `lyrics`, `chords`, `sheet_music`, `audio`, `other` |
-| file_size | integer | Size in bytes |
-| mime_type | string | MIME type |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### setlists
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| name | string | Setlist name |
-| description | text? | Description |
-| total_duration_seconds | integer? | Calculated total |
-| is_template | boolean | Is a template |
-| is_archived | boolean | Archived |
-| created_by | ULID? | FK to users |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### setlist_items
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| setlist_id | ULID | FK to setlists |
-| music_number_id | ULID? | FK to music_numbers |
-| position | integer | Order position |
-| set_number | integer | Set number (1, 2, 3) |
-| is_break | boolean | Is a break |
-| break_duration_seconds | integer? | Break length |
-| notes | string? | Item notes |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### booking_requests
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| customer_id | ULID | FK to customers |
-| event_date | date | Requested date |
-| start_time | time? | Requested start |
-| end_time | time? | Requested end |
-| location_name | string? | Venue name |
-| location_address | string? | Venue address |
-| event_type | string? | Type of event |
-| expected_guests | integer? | Guest count |
-| message | text? | Request message |
-| status | enum | `pending`, `reviewed`, `accepted`, `declined` |
-| admin_notes | text? | Admin notes |
-| event_id | ULID? | FK to created event |
-| reviewed_by | ULID? | FK to users |
-| reviewed_at | timestamp? | When reviewed |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### notifications
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| user_id | ULID | FK to users |
-| type | string | Notification type |
-| title | string | Title |
-| message | text | Message body |
-| data | json? | Additional data |
-| action_url | string? | Link URL |
-| read_at | timestamp? | When read |
-| created_at | timestamp | Created |
-| updated_at | timestamp | Updated |
-
-#### activity_logs
-| Column | Type | Description |
-|--------|------|-------------|
-| id | ULID | Primary key |
-| user_id | ULID? | FK to users |
-| loggable_type | string | Model class |
-| loggable_id | ULID | Model ID |
-| action | string | Action performed |
-| description | text? | Description |
-| changes | json? | Before/after data |
-| ip_address | string? | Client IP |
-| user_agent | string? | Browser info |
-| created_at | timestamp | Created |
-
----
-
-## API Response Format
-
-### Success Response
```json
{
- "success": true,
"data": { ... },
- "message": "Event created successfully",
"meta": {
"pagination": {
"current_page": 1,
"per_page": 15,
"total": 100,
- "last_page": 7,
- "from": 1,
- "to": 15
- }
- }
-}
-```
-
-### Error Response
-```json
-{
- "success": false,
- "message": "Validation failed",
- "errors": {
- "title": ["The title field is required."],
- "event_date": ["The event date must be a future date."]
- }
-}
-```
-
-### Single Resource
-```json
-{
- "success": true,
- "data": {
- "id": "01HQ3K5P7X...",
- "title": "Summer Concert",
- "event_date": "2025-07-15",
- "status": "confirmed",
- "location": {
- "id": "01HQ3K5P7X...",
- "name": "City Park Amphitheater"
- },
- "created_at": "2025-01-15T10:30:00Z",
- "updated_at": "2025-01-15T10:30:00Z"
- }
-}
-```
-
-### Collection (Paginated)
-```json
-{
- "success": true,
- "data": [
- { "id": "...", "title": "Event 1" },
- { "id": "...", "title": "Event 2" }
- ],
- "meta": {
- "pagination": {
- "current_page": 1,
- "per_page": 15,
- "total": 45,
- "last_page": 3
+ "last_page": 7
}
}
}
@@ -510,357 +257,193 @@ POST /notifications/read-all Mark all as read
---
-## User Roles & Permissions
-
-### Roles
-
-| Role | Description |
-|------|-------------|
-| `admin` | Full access to everything |
-| `booking_agent` | Manage events, locations, customers |
-| `music_manager` | Manage music catalog and setlists |
-| `member` | View events, RSVP, view music |
-
-### Permissions Matrix
-
-| Resource | Admin | Booking Agent | Music Manager | Member |
-|----------|-------|---------------|---------------|--------|
-| Members | CRUD | Read | Read | Read |
-| Events | CRUD | CRUD | Read | Read |
-| Locations | CRUD | CRUD | Read | Read |
-| Customers | CRUD | CRUD | Read | - |
-| Music | CRUD | Read | CRUD | Read |
-| Setlists | CRUD | Read | CRUD | Read |
-| Booking Requests | CRUD | CRUD | - | - |
-| RSVP | All | All | Own | Own |
-
----
-
-## File Storage
-
-### Structure
-```
-storage/app/
-├── public/
-│ ├── avatars/ # User avatars
-│ └── music/ # Music attachments
-│ ├── lyrics/
-│ ├── chords/
-│ ├── sheet_music/
-│ └── audio/
-└── private/
- └── exports/ # Generated reports
-```
-
-### File Types Allowed
-
-| Type | Extensions | Max Size |
-|------|------------|----------|
-| Avatar | jpg, png, webp | 2 MB |
-| Lyrics | txt, pdf, docx | 5 MB |
-| Chords | pdf, png, jpg | 10 MB |
-| Sheet Music | pdf, png, jpg | 10 MB |
-| Audio | mp3, wav, m4a | 50 MB |
-
----
-
-## Architectural Decisions
-
-### ADR-001: API-First Architecture
-
-**Status**: Accepted
-**Date**: 2025-01-01
-
-**Context**: We need to build a web application with three separate SPAs (Admin, Band, Customers) that may have mobile clients in the future.
-
-**Decision**: Implement a completely separated frontend and backend communicating via RESTful JSON API.
-
-**Consequences**:
-- ✅ Frontend and backend can be developed/deployed independently
-- ✅ Easy to add mobile or other clients later
-- ✅ Clear API contracts
-- ✅ Better scalability options
-- ⚠️ More complex initial setup
-- ⚠️ Requires CORS configuration
-
----
-
-### ADR-002: Laravel Sanctum for Authentication
-
-**Status**: Accepted
-**Date**: 2025-01-01
-
-**Context**: Need authentication for SPAs that's secure and simple to implement.
-
-**Decision**: Use Laravel Sanctum with token-based authentication for the SPAs.
-
-**Alternatives Considered**:
-- **Passport**: Too complex for our needs (OAuth2 overkill for first-party SPA)
-- **JWT**: Requires token storage in localStorage (XSS vulnerable)
-
-**Consequences**:
-- ✅ Simple token-based auth for multiple SPAs
-- ✅ Built into Laravel, minimal setup
-- ✅ Works well with separate domains
-- ⚠️ Need to handle token storage securely
-
----
-
-### ADR-003: TanStack Query for Server State
-
-**Status**: Accepted
-**Date**: 2025-01-01
-
-**Context**: Need efficient data fetching with caching, background updates, and optimistic updates.
-
-**Decision**: Use TanStack Query (Vue Query) for all server state management.
-
-**Consequences**:
-- ✅ Automatic caching and deduplication
-- ✅ Built-in loading/error states
-- ✅ Background refetching
-- ✅ Optimistic updates for better UX
-- ✅ DevTools for debugging
-- ⚠️ Learning curve for query key management
-
----
-
-### ADR-004: Action Pattern for Business Logic
-
-**Status**: Accepted
-**Date**: 2025-01-01
-
-**Context**: Controllers should be thin, and business logic needs to be reusable and testable.
-
-**Decision**: Use single-responsibility Action classes for all business logic.
-
-**Pattern**:
-```php
-class CreateEventAction
-{
- public function execute(array $data): Event
- {
- // Business logic here
- }
-}
-
-// Usage in controller
-public function store(Request $request, CreateEventAction $action)
-{
- return $action->execute($request->validated());
-}
-```
-
-**Consequences**:
-- ✅ Single Responsibility Principle
-- ✅ Easy to test in isolation
-- ✅ Reusable across controllers, commands, jobs
-- ⚠️ More files to manage
-
----
-
-### ADR-005: Vuexy for All SPAs
-
-**Status**: Accepted
-**Date**: 2025-01-01
-
-**Context**: Need consistent UI across three SPAs with professional admin components.
-
-**Decision**: Use Vuexy Vue template for all SPAs (full version for Admin, starter-kit for Band/Customers).
-
-**Consequences**:
-- ✅ Consistent UI/UX across all portals
-- ✅ Pre-built admin components
-- ✅ Single learning curve for the team
-- ✅ Professional look out of the box
-- ⚠️ License cost
-- ⚠️ Dependency on third-party template
-
----
-
-## Security Architecture
-
-### Defense in Depth
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. Network Layer │
-│ - HTTPS only │
-│ - Rate limiting │
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 2. Application Layer │
-│ - CORS validation │
-│ - Input validation (Form Requests) │
-│ - SQL injection prevention (Eloquent) │
-│ - XSS prevention (Vue escaping) │
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 3. Authentication & Authorization │
-│ - Sanctum token authentication │
-│ - Role-based access control │
-│ - Resource authorization (Policies) │
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 4. Data Layer │
-│ - Encrypted connections (TLS) │
-│ - Sensitive data hashing (bcrypt) │
-│ - Database credentials via environment │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### CORS Configuration
-
-```php
-// config/cors.php
-'allowed_origins' => [
- 'http://localhost:5173', // Admin
- 'http://localhost:5174', // Band
- 'http://localhost:5175', // Customers
-],
-'supports_credentials' => true,
-```
-
----
-
-## Performance Considerations
-
-### Backend Optimizations
-
-1. **Database**
- - Eager loading relationships (`with()`)
- - Database indexes on filtered/sorted columns
- - Query caching for expensive operations
-
-2. **Caching Strategy**
- ```php
- // Cache expensive queries
- Cache::remember('stats:dashboard', 3600, fn() =>
- $this->calculateStats()
- );
- ```
-
-3. **Queue Heavy Operations**
- - Email sending
- - File processing
- - Report generation
-
-### Frontend Optimizations
-
-1. **Code Splitting**
- - Lazy load routes
- - Dynamic imports for heavy components
-
-2. **Query Optimization**
- ```typescript
- // Deduplicate requests
- useQuery({ queryKey: ['events'], staleTime: 5 * 60 * 1000 })
-
- // Prefetch on hover
- queryClient.prefetchQuery({ queryKey: ['event', id] })
- ```
-
-3. **Bundle Size**
- - Tree shaking enabled
- - Dynamic imports for routes
-
----
-
-## Monitoring & Observability
-
-### Logging Strategy
-
-| Level | Use Case | Example |
-|-------|----------|---------|
-| DEBUG | Development only | Query details, variable dumps |
-| INFO | Normal operations | User login, API calls |
-| WARNING | Unexpected but handled | Rate limit approached |
-| ERROR | Errors requiring attention | Failed payment |
-| CRITICAL | System failures | Database down |
-
-### Health Checks
-
-```
-GET /api/v1/health
-{
- "status": "healthy",
- "checks": {
- "database": "ok",
- "redis": "ok",
- "queue": "ok"
- }
-}
-```
+## Core Database Schema
+
+**Primary Keys**: ULID on all business tables via `HasUlids` trait. Pure pivot tables use auto-increment integer PK.
+
+**Soft Deletes ON**: organisations, events, festival_sections, shifts, shift_assignments, persons, artists, companies, production_requests.
+
+**Soft Deletes OFF** (audit records): check_ins, briefing_sends, message_replies, shift_waitlist, volunteer_festival_history.
+
+**JSON Columns**: ONLY for opaque config (blocks, fields, settings, items). NEVER for dates, status values, foreign keys, booleans, or anything filtered/sorted/aggregated.
+
+### 1. Foundation
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `users` | id (ULID), name, email, password, timezone, locale, avatar, deleted_at | Platform-wide, unique per email. |
+| `organisations` | id (ULID), name, slug, billing_status, settings (JSON: display prefs only), deleted_at | hasMany events, crowd_types. |
+| `organisation_user` | id (int AI), user_id, organisation_id, role | Pivot. Integer PK. |
+| `user_invitations` | id (ULID), email, invited_by_user_id, organisation_id, event_id (nullable), role, token (ULID unique), status, expires_at | INDEX: (token), (email, status). |
+| `events` | id (ULID), organisation_id, name, slug, start_date, end_date, timezone, status (enum), deleted_at | INDEX: (organisation_id, status). |
+| `event_user_roles` | id (int AI), user_id, event_id, role | Pivot. Integer PK. |
+
+### 2. Locations
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `locations` | id (ULID), event_id, name, address, lat, lng, description, access_instructions | INDEX: (event_id). |
+
+### 3. Festival Sections, Time Slots & Shifts
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `festival_sections` | id (ULID), event_id, name, sort_order, deleted_at | INDEX: (event_id, sort_order). |
+| `time_slots` | id (ULID), event_id, name, person_type (CREW/VOLUNTEER/PRESS/...), date, start_time, end_time | INDEX: (event_id, person_type, date). |
+| `shifts` | id (ULID), festival_section_id, time_slot_id, location_id, slots_total, slots_open_for_claiming, status, deleted_at | INDEX: (festival_section_id, time_slot_id). |
+| `shift_assignments` | id (ULID), shift_id, person_id, time_slot_id (denormalized), status (pending_approval/approved/rejected/cancelled/completed), auto_approved, deleted_at | UNIQUE(person_id, time_slot_id). |
+| `volunteer_availabilities` | id (ULID), person_id, time_slot_id, submitted_at | UNIQUE(person_id, time_slot_id). |
+| `shift_waitlist` | id (ULID), shift_id, person_id, position, added_at | UNIQUE(shift_id, person_id). |
+| `shift_swap_requests` | id (ULID), from_assignment_id, to_person_id, status, auto_approved | |
+| `shift_absences` | id (ULID), shift_assignment_id, person_id, reason, status | |
+
+### 4. Volunteer Profile & History
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `volunteer_profiles` | id (ULID), user_id (unique), bio, tshirt_size, first_aid, driving_licence, reliability_score (0.00-5.00) | Platform-wide, 1:1 with users. |
+| `volunteer_festival_history` | id (ULID), user_id, event_id, hours_planned, hours_completed, no_show_count, coordinator_rating, would_reinvite | UNIQUE(user_id, event_id). Never visible to volunteer. |
+| `post_festival_evaluations` | id (ULID), event_id, person_id, overall_rating, would_return, feedback_text | |
+| `festival_retrospectives` | id (ULID), event_id (unique), KPI columns, top_feedback (JSON) | |
+
+### 5. Crowd Types, Persons & Crowd Lists
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `crowd_types` | id (ULID), organisation_id, name, system_type (CREW/GUEST/ARTIST/VOLUNTEER/PRESS/PARTNER/SUPPLIER), color, icon | Org-level config. |
+| `persons` | id (ULID), user_id (nullable), event_id, crowd_type_id, company_id (nullable), name, email, phone, status, is_blacklisted, custom_fields (JSON), deleted_at | user_id nullable for externals. UNIQUE(event_id, user_id) WHERE user_id IS NOT NULL. |
+| `companies` | id (ULID), organisation_id, name, type, contact_*, deleted_at | Shared across events within org. |
+| `crowd_lists` | id (ULID), event_id, crowd_type_id, name, type (internal/external), auto_approve, max_persons | |
+| `crowd_list_persons` | id (int AI), crowd_list_id, person_id | Pivot. |
+
+### 6. Accreditation Engine
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `accreditation_categories` | id (ULID), organisation_id, name, sort_order, icon | Org-level. |
+| `accreditation_items` | id (ULID), accreditation_category_id, name, is_date_dependent, barcode_type, cost_price | Org-level items. |
+| `event_accreditation_items` | id (ULID), event_id, accreditation_item_id, max_quantity_per_person, is_active | Activates item per event. UNIQUE(event_id, accreditation_item_id). |
+| `accreditation_assignments` | id (ULID), person_id, accreditation_item_id, event_id, date, quantity, is_handed_out | |
+| `access_zones` | id (ULID), event_id, name, zone_code (unique per event) | |
+| `access_zone_days` | id (int AI), access_zone_id, day_date | UNIQUE(access_zone_id, day_date). |
+| `person_access_zones` | id (int AI), person_id, access_zone_id, valid_from, valid_to | |
+
+### 7. Artists & Advancing
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `artists` | id (ULID), event_id, name, booking_status (concept/requested/option/confirmed/contracted/cancelled), portal_token (ULID unique), deleted_at | |
+| `performances` | id (ULID), artist_id, stage_id, date, start_time, end_time, check_in_status | INDEX: (stage_id, date, start_time). |
+| `stages` | id (ULID), event_id, name, color, capacity | |
+| `stage_days` | id (int AI), stage_id, day_date | UNIQUE(stage_id, day_date). |
+| `advance_sections` | id (ULID), artist_id, name, type, is_open, sort_order | |
+| `advance_submissions` | id (ULID), advance_section_id, data (JSON), status | |
+| `artist_contacts` | id (ULID), artist_id, name, email, role | |
+| `artist_riders` | id (ULID), artist_id, category (technical/hospitality), items (JSON) | |
+
+### 8. Communication & Briefings
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `briefing_templates` | id (ULID), event_id, name, type, blocks (JSON) | |
+| `briefings` | id (ULID), event_id, briefing_template_id, name, target_crowd_types (JSON), status | |
+| `briefing_sends` | id (ULID), briefing_id, person_id, status (queued/sent/opened/downloaded) | NO soft delete. |
+| `communication_campaigns` | id (ULID), event_id, type (email/sms/whatsapp), status | |
+| `messages` | id (ULID), event_id, sender_user_id, recipient_person_id, urgency (normal/urgent/emergency) | |
+| `broadcast_messages` | id (ULID), event_id, sender_user_id, body, urgency | |
+
+### 9. Forms, Check-In & Operational
+
+| Table | Key Columns | Notes |
+|-------|-------------|-------|
+| `public_forms` | id (ULID), event_id, crowd_type_id, fields (JSON), conditional_logic (JSON), iframe_token | |
+| `form_submissions` | id (ULID), public_form_id, person_id, data (JSON) | |
+| `check_ins` | id (ULID), event_id, person_id, scanned_by_user_id, scanned_at | NO soft delete. Immutable audit record. |
+| `scanners` | id (ULID), event_id, name, type, pairing_code | |
+| `inventory_items` | id (ULID), event_id, name, item_code, assigned_to_person_id | |
+| `production_requests` | id (ULID), event_id, company_id, title, status, token (ULID unique) | |
+| `material_requests` | id (ULID), production_request_id, category, name, quantity, status | |
---
## Model Relationships
**User**
-- User has many EventInvitations
-- User has many Notifications
-- User has many ActivityLogs
-- User has many created Events (as creator)
-- User has many created MusicNumbers (as creator)
-- User has many created Setlists (as creator)
+- belongsToMany Organisations (via `organisation_user`)
+- belongsToMany Events (via `event_user_roles`)
+
+**Organisation**
+- hasMany Events
+- hasMany CrowdTypes
+- hasMany AccreditationCategories
+- hasMany Companies
+- belongsToMany Users (via `organisation_user`)
**Event**
-- Event belongs to Location (nullable)
-- Event belongs to Customer (nullable)
-- Event belongs to Setlist (nullable)
-- Event belongs to User (created_by)
-- Event has many EventInvitations
-- Event has many Users through EventInvitations (invited members)
+- belongsTo Organisation
+- hasMany FestivalSections
+- hasMany TimeSlots
+- hasMany Persons
+- hasMany Artists
+- hasMany Briefings
+- hasMany Locations
+- hasMany AccessZones
+- hasMany PublicForms
-**EventInvitation**
-- EventInvitation belongs to Event
-- EventInvitation belongs to User
+**FestivalSection**
+- belongsTo Event
+- hasMany Shifts
-**Location**
-- Location has many Events
+**TimeSlot**
+- belongsTo Event
+- hasMany Shifts
+- hasMany ShiftAssignments (denormalized)
-**Customer**
-- Customer belongs to User (nullable, for portal access)
-- Customer has many Events
-- Customer has many BookingRequests
+**Shift**
+- belongsTo FestivalSection
+- belongsTo TimeSlot
+- belongsTo Location (nullable)
+- hasMany ShiftAssignments
-**MusicNumber**
-- MusicNumber belongs to User (created_by, nullable)
-- MusicNumber has many MusicAttachments
-- MusicNumber has many SetlistItems
-- MusicNumber has many Setlists through SetlistItems
+**Person**
+- belongsTo Event
+- belongsTo CrowdType
+- belongsTo User (nullable)
+- belongsTo Company (nullable)
+- hasMany ShiftAssignments
+- hasMany AccreditationAssignments
+- hasMany CheckIns
-**MusicAttachment**
-- MusicAttachment belongs to MusicNumber
-
-**Setlist**
-- Setlist belongs to User (created_by, nullable)
-- Setlist has many SetlistItems
-- Setlist has many MusicNumbers through SetlistItems
-- Setlist has many Events
-
-**SetlistItem**
-- SetlistItem belongs to Setlist
-- SetlistItem belongs to MusicNumber (nullable, null when is_break = true)
-
-**BookingRequest**
-- BookingRequest belongs to Customer
-- BookingRequest belongs to Event (nullable, when accepted)
-- BookingRequest belongs to User (reviewed_by, nullable)
-
-**Notification**
-- Notification belongs to User
-
-**ActivityLog**
-- ActivityLog belongs to User (nullable)
-- ActivityLog is polymorphic (loggable_type, loggable_id)
+**Artist**
+- belongsTo Event
+- hasMany Performances
+- hasMany AdvanceSections
+- hasMany ArtistContacts
---
-*Last updated: 2025-01-01*
+## Security & CORS
+
+Three frontend origins in `config/cors.php` (via env):
+
+| App | Dev URL | Env Variable |
+|-----|---------|--------------|
+| Admin | `http://localhost:5173` | `FRONTEND_ADMIN_URL` |
+| App | `http://localhost:5174` | `FRONTEND_APP_URL` |
+| Portal | `http://localhost:5175` | `FRONTEND_PORTAL_URL` |
+
+---
+
+## Real-time Events (WebSocket)
+
+Via Laravel Echo + Pusher/Soketi:
+- `PersonCheckedIn`
+- `ShiftFillRateChanged`
+- `ArtistCheckInStatusChanged`
+- `AdvanceSectionSubmitted`
+- `AccreditationItemHandedOut`
+- `BriefingSendQueued`
+
+---
+
+*Source: EventCrew Design Document v1.3, March 2026*
diff --git a/.cursor/instructions.md b/.cursor/instructions.md
index 7e105ac..bb24a67 100644
--- a/.cursor/instructions.md
+++ b/.cursor/instructions.md
@@ -1,28 +1,30 @@
-# Band Management - Cursor AI Instructions
+# EventCrew - Cursor AI Instructions
-> This document provides AI assistants with comprehensive context about the project.
-> Update this file as the project evolves.
+> Multi-tenant SaaS platform for event- and festival management.
+> Design Document: `/resources/design/EventCrew_Design_Document_v1.3.docx`
+> Dev Guide: `/resources/design/EventCrew_Dev_Guide_v1.0.docx`
+> Start Guide: `/resources/design/EventCrew_Start_Guide_v1.0.docx`
## Project Overview
-**Name**: Band Management Platform
-**Type**: Full-stack web application (API-first architecture)
+**Name**: EventCrew
+**Type**: Multi-tenant SaaS platform (API-first architecture)
**Status**: Development
### Description
-Band Management is a full-stack web application designed to streamline band operations by centralizing member coordination, gig management, music cataloging, and setlist planning. The platform serves as the single source of truth for all band-related activities.
+EventCrew is a multi-tenant SaaS platform for professional event and festival management. It supports the full operational cycle: artist booking and advancing, staff planning and volunteer management, accreditation, briefings, and real-time show-day operations (Mission Control). Built for a professional volunteer organisation, with SaaS expansion potential.
## Quick Reference
| Component | Technology | Location | Port |
|-----------|------------|----------|------|
-| API | Laravel 12 + Sanctum | `api/` | 8000 |
-| Admin Dashboard | Vue 3 + Vuexy (full) | `apps/admin/` | 5173 |
-| Band Portal | Vue 3 + Vuexy (starter) | `apps/band/` | 5174 |
-| Customer Portal | Vue 3 + Vuexy (starter) | `apps/customers/` | 5175 |
-| Database | MySQL 8.0 | Docker | 3306 |
-| Cache | Redis | Docker | 6379 |
+| API | Laravel 12 + Sanctum + Spatie Permission | `api/` | 8000 |
+| Admin (Super Admin) | Vue 3 + Vuexy (full) | `apps/admin/` | 5173 |
+| Organizer App (Main) | Vue 3 + Vuexy (full) | `apps/app/` | 5174 |
+| Portal (External) | Vue 3 + Vuexy (stripped) | `apps/portal/` | 5175 |
+| Database | MySQL 8 | Docker | 3306 |
+| Cache / Queues | Redis | Docker | 6379 |
| Mail | Mailpit | Docker | 8025 |
## Documentation Structure
@@ -30,220 +32,118 @@ Band Management is a full-stack web application designed to streamline band oper
```
.cursor/
├── instructions.md # This file - overview and quick start
-├── ARCHITECTURE.md # System architecture and data models
+├── ARCHITECTURE.md # System architecture, schema, API routes
└── rules/
- ├── 001_workspace.mdc # Project structure and conventions
- ├── 100_laravel.mdc # Laravel API patterns
- ├── 101_vue.mdc # Vue + Vuexy patterns
- └── 200_testing.mdc # Testing strategies
+ ├── 001_workspace.mdc # Project structure, conventions, multi-tenancy
+ ├── 100_laravel.mdc # Laravel API patterns and templates
+ ├── 101_vue.mdc # Vue + Vuexy patterns and templates
+ └── 200_testing.mdc # Testing strategies and templates
```
---
-## Core Features
+## Core Modules
-### Authentication & Authorization
+### Phase 1 - Foundation
+- [ ] Multi-tenant architecture + Auth (Sanctum + Spatie)
+- [ ] Users, Roles & Permissions (three-level model)
+- [ ] Organisations CRUD + User Invitations
+- [ ] Events CRUD with lifecycle status
+- [ ] Crowd Types (org-level configuration)
+- [ ] Festival Sections + Time Slots + Shifts
+- [ ] Persons & Crowd Lists
+- [ ] Accreditation Engine (categories, items, access zones)
-- [ ] User registration with email verification
-- [ ] User login/logout
-- [ ] Password reset functionality
-- [ ] Role-based access control (Admin, Booking Agent, Music Manager, Member)
-- [ ] Permission middleware for route protection
-- [ ] Session management
+### Phase 2 - Core Operations
+- [ ] Briefings & Communication (template builder, queue-based sending)
+- [ ] Staff & Crew Management (crowd pool, accreditation matrix)
+- [ ] Volunteer Management + Portal (registration, shift claiming, approval flow)
+- [ ] Form Builder (drag-drop, conditional logic, iframe embed)
+- [ ] Artist Advancing + Portal (token-based access)
+- [ ] Timetable & Stage management
+- [ ] Show Day Mode
+- [ ] Shift Swap & Waitlist
+- [ ] Volunteer Profile + Festival Passport
+- [ ] Communication Hub (email/SMS/WhatsApp via Zender, urgency levels)
-### Member Management
+### Phase 3 - Advancing & Show Day
+- [ ] Guests & Hospitality
+- [ ] Suppliers & Production (production requests, supplier portal)
+- [ ] Mission Control (real-time check-in, artist handling, scanner management)
+- [ ] Communication Campaigns (email + SMS batch)
+- [ ] Allocation Sheet PDF (Browsershot)
+- [ ] Scan infrastructure (hardware pairing)
+- [ ] Reporting & Insights
+- [ ] No-show automation
+- [ ] Post-festival evaluation + retrospective
-- [ ] List all members with search and filter
-- [ ] Create new member with role assignment
-- [ ] Edit member profile and roles
-- [ ] Deactivate/reactivate members
-- [ ] Member profile page (instruments, bio, contact info)
-- [ ] Avatar upload
-- [ ] Member invitation via email
-- [ ] Activity log per member
+### Phase 4 - Differentiators
+- [ ] Real-time WebSocket notifications (Echo + Pusher/Soketi)
+- [ ] Cross-event crew pool with reliability score
+- [ ] Global search (cmd+K)
+- [ ] Crew PWA
+- [ ] Public REST API + webhook system
+- [ ] CO2/sustainability reporting
-### Events/Gigs Management
+---
-- [ ] List events with calendar and list view
-- [ ] Create event with details (title, date, time, fee, notes)
-- [ ] Edit/delete events
-- [ ] Link event to location (from Location Manager)
-- [ ] Link event to customer (from Customer Manager)
-- [ ] Event status workflow (Draft → Pending → Confirmed → Completed → Cancelled)
-- [ ] Invite members to event
-- [ ] View RSVP responses per event
-- [ ] Attach setlist to event
-- [ ] Event detail page with all related info
-- [ ] Duplicate event functionality
+## Module Development Order (per module)
-### RSVP System
+Always follow this sequence:
-- [ ] Member receives event invitation notification
-- [ ] RSVP response options (Available, Unavailable, Tentative)
-- [ ] Add note/reason with RSVP
-- [ ] Change RSVP before deadline
-- [ ] RSVP deadline per event
-- [ ] Overview of member availability per event
-- [ ] Automatic reminders for pending RSVPs
-
-### Music Management
-
-- [ ] List all music numbers with search and filter
-- [ ] Add music number with metadata (title, artist, genre, duration)
-- [ ] Edit/delete music numbers
-- [ ] Additional fields: key, tempo (BPM), time signature
-- [ ] File attachments (lyrics, chord sheets, audio files)
-- [ ] Categorization with tags/genres
-- [ ] Notes field for arrangements/cues
-
-### Setlist Manager
-
-- [ ] List all setlists
-- [ ] Create setlist with name and description
-- [ ] Add music numbers to setlist from catalog
-- [ ] Drag-and-drop reordering of songs
-- [ ] Add set breaks/intermissions
-- [ ] Auto-calculate total duration
-- [ ] Clone existing setlist
-- [ ] Link setlist to event(s)
-- [ ] Delete/archive setlists
-
-### Location Manager
-
-- [ ] List all locations with search
-- [ ] Add location with details (name, address, capacity)
-- [ ] Edit/delete locations
-- [ ] Contact information (phone, email, contact person)
-- [ ] Technical specifications (stage size, PA, backline, parking)
-- [ ] Notes and special requirements
-
-### Customer Manager
-
-- [ ] List all customers with search
-- [ ] Add customer (company or individual)
-- [ ] Edit/delete customers
-- [ ] Contact details (name, email, phone, address)
-- [ ] Customer type classification
-- [ ] Notes and preferences
-- [ ] View booking history per customer
-
-### Customer Portal
-
-- [ ] Customer dashboard with booked events
-- [ ] Submit booking requests
-- [ ] Track request status
-- [ ] View assigned setlists (if permitted)
-- [ ] Profile settings
-
-### Band Member Portal
-
-- [ ] Member dashboard with upcoming events
-- [ ] Personal event calendar
-- [ ] RSVP management interface
-- [ ] View event details (location, time, setlist)
-- [ ] Browse music catalog (view-only)
-- [ ] View setlists assigned to events
-- [ ] Profile settings
-- [ ] Notification preferences
-
-### Admin Dashboard
-
-- [ ] Dashboard with statistics/overview
-- [ ] Quick actions panel
-- [ ] Recent activity feed
-- [ ] Upcoming events widget
-- [ ] Pending RSVPs overview
-- [ ] Booking requests management
-
-### Notifications
-
-- [ ] Email notifications for event invitations
-- [ ] RSVP reminder notifications
-- [ ] Event update notifications
-- [ ] In-app notification center
-- [ ] Notification preferences per user
+1. Migration(s) - ULID PKs, composite indexes, constrained FKs
+2. Eloquent Model - HasUlids, relations, scopes, OrganisationScope
+3. Factory - realistic Dutch test data
+4. Policy - authorization via Spatie roles
+5. Form Request(s) - Store + Update validation
+6. API Resource - computed fields, `whenLoaded()`, permission-dependent fields
+7. Resource Controller - index/show/store/update/destroy
+8. Routes in `api.php`
+9. PHPUnit Feature Test - happy path (200/201) + unauthenticated (401) + wrong organisation (403) + validation (422)
+10. Vue Composable (`useModuleName.ts`) - TanStack Query
+11. Pinia Store (if cross-component state needed)
+12. Vue Page Component
+13. Vue Router entry
---
## Getting Started Prompts
-### 1. Create Laravel API
+### 1. Phase 1 Foundation (Backend)
```
-Create a Laravel 12 project in api/ with:
-- Sanctum for API authentication
-- MySQL configuration (host: 127.0.0.1, db: band_management, user: band_management, pass: secret)
-- CORS configured for localhost:5173, localhost:5174, localhost:5175
-- API response trait for consistent JSON responses
-- Base controller with response helpers
+Read CLAUDE.md. Then generate Phase 1 Foundation:
-Follow the patterns in .cursor/rules/100_laravel.mdc
+1. Migrations: Update users (add timezone, locale, deleted_at). Create organisations (ULID, name, slug, billing_status, settings JSON, deleted_at), organisation_user pivot, user_invitations, events (ULID, organisation_id, name, slug, start_date, end_date, timezone, status enum, deleted_at), event_user_roles pivot.
+2. Models: User (update), Organisation, UserInvitation, Event. All with HasUlids, SoftDeletes where applicable, OrganisationScope on Event.
+3. Spatie Permission: RoleSeeder with roles: super_admin, org_admin, org_member, event_manager, staff_coordinator, volunteer_coordinator.
+4. Auth: LoginController, LogoutController, MeController (returns user + organisations + active event roles).
+5. Organisations: Controller, Policy, Request, Resource.
+6. Events: Controller nested under organisations, Policy, Request, Resource.
+7. Feature tests per step. Run php artisan test after each step.
```
-### 2. Create Database Migrations
+### 2. Phase 1 Foundation (Frontend)
```
-Create all migrations based on the schema in .cursor/ARCHITECTURE.md:
-- Users, Customers, Locations
-- Events, EventInvitations
-- MusicNumbers, MusicAttachments
-- Setlists, SetlistItems
-- BookingRequests, Notifications, ActivityLogs
-
-Use ULIDs for primary keys. Follow Laravel conventions.
+Build auth flow in apps/app/:
+1. stores/useAuthStore.ts - token storage, isAuthenticated, me() loading
+2. pages/login.vue - use Vuexy login layout
+3. Router guard - redirect to login if not authenticated
+4. Replace Vuexy demo navigation with EventCrew structure
+5. CASL permissions: connect to Spatie roles from auth/me response
```
-### 3. Create Models with Relationships
+### 3. Module Generation (example: Shifts)
```
-Create Eloquent models for all tables with:
-- HasUlids trait for ULID primary keys
-- Proper relationships (belongsTo, hasMany, etc.)
-- Fillable arrays
-- Casts for enums, dates, and JSON fields
-- Scopes for common queries
-
-Follow patterns in .cursor/rules/100_laravel.mdc
-```
-
-### 4. Create Authentication System
-
-```
-Create auth system with:
-- AuthController (login, logout, register, user, forgot-password, reset-password)
-- Form requests for validation
-- API resources for responses
-- Sanctum token generation
-
-Follow patterns in .cursor/rules/100_laravel.mdc
-```
-
-### 5. Integrate Vuexy with API
-
-```
-I've copied Vuexy Vue (typescript-version/full-version) to apps/admin/.
-
-Update it to:
-1. Create src/lib/api-client.ts for API calls with auth token handling
-2. Install and configure @tanstack/vue-query
-3. Replace Vuexy's fake auth with our Laravel API
-4. Update navigation menu for our modules
-
-Follow patterns in .cursor/rules/101_vue.mdc
-```
-
-### 6. Create Feature Modules
-
-```
-Create the Events module with:
-- EventController with CRUD + invite/RSVP endpoints
-- StoreEventRequest, UpdateEventRequest for validation
-- EventResource, EventCollection for responses
-- CreateEventAction, UpdateEventAction for business logic
-- EventPolicy for authorization
-- Feature tests
-
-Follow patterns in .cursor/rules/100_laravel.mdc and .cursor/rules/200_testing.mdc
+Build the Shifts module following CLAUDE.md module order:
+- Migration with ULID PK, festival_section_id, time_slot_id, location_id, slots_total, slots_open_for_claiming, status. Composite indexes.
+- Model with HasUlids, SoftDeletes, relations, computed accessors (slots_filled, fill_rate).
+- shift_assignments with denormalized time_slot_id, status machine (pending_approval > approved/rejected/cancelled/completed), UNIQUE(person_id, time_slot_id).
+- Configurable auto-approve per shift.
+- Queued notification jobs using ZenderService for WhatsApp.
+- Feature tests covering 200/401/403/422.
```
---
@@ -256,25 +156,25 @@ Follow patterns in .cursor/rules/100_laravel.mdc and .cursor/rules/200_testing.m
2. Create Form Request in `app/Http/Requests/Api/V1/`
3. Create/update API Resource in `app/Http/Resources/Api/V1/`
4. Add route in `routes/api.php`
-5. Create Action class if complex logic needed
-6. Write feature test
+5. Create Service class if complex business logic needed
+6. Write PHPUnit Feature Test (200/401/403/422)
### Add a New Vue Page
1. Create page component in `src/pages/`
-2. Add route in `src/router/index.ts`
+2. Route added automatically by file-based routing (or add to router)
3. Add navigation item in `src/navigation/`
4. Create composable for API calls in `src/composables/`
-5. Use Vuexy components for UI
+5. Use Vuexy/Vuetify components for UI
### Add a New Database Table
-1. Create migration: `php artisan make:migration create_tablename_table`
-2. Create model with relationships
-3. Create factory and seeder
-4. Create controller, requests, resources
-5. Add API routes
-6. Write tests
+1. Create migration with ULID PK, composite indexes
+2. Create model with HasUlids, relations, OrganisationScope (if applicable)
+3. Create factory with realistic Dutch test data
+4. Create Policy, Form Request, Resource, Controller
+5. Register routes in `api.php`
+6. Write PHPUnit Feature Test
---
@@ -282,15 +182,15 @@ Follow patterns in .cursor/rules/100_laravel.mdc and .cursor/rules/200_testing.m
When generating code, always:
-- Use PHP 8.3 features (typed properties, enums, match, readonly)
-- Use strict types: `declare(strict_types=1);`
-- Use `final` classes for Actions, Form Requests, Resources
-- Use ULIDs for all primary keys
-- Follow PSR-12 coding standards
-- Use TypeScript strict mode in Vue
-- Use Vue 3 Composition API with `
@@ -559,7 +454,7 @@ function handleDelete() {
Events
- Manage your gigs and performances
+ Manage events for your organisation
- Loading events...
+
+
+ {{ error?.message ?? 'Failed to load events' }}
+
+
-
-
-
- Title
- Date
- Location
- Status
- Actions
-
-
-
-
-
-
- {{ event.title }}
-
-
- {{ event.event_date }}
- {{ event.location?.name ?? '-' }}
-
-
- {{ event.status_label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {{ item.name }}
+
+
-
-
-
- Delete Event?
-
- This action cannot be undone.
-
-
-
- Cancel
+
+ {{ item.start_date }} - {{ item.end_date }}
+
+
+
+
+ {{ item.status_label }}
+
+
+
+
- Delete
+
-
-
-
+
+
+
-
-
```
-### Router Configuration
-
-```typescript
-// src/router/index.ts
-import { createRouter, createWebHistory } from 'vue-router'
-import { useAuthStore } from '@/stores/auth'
-
-const router = createRouter({
- history: createWebHistory(import.meta.env.BASE_URL),
- routes: [
- {
- path: '/',
- component: () => import('@/layouts/default.vue'),
- children: [
- {
- path: '',
- name: 'dashboard',
- component: () => import('@/pages/dashboard.vue'),
- meta: { requiresAuth: true },
- },
- {
- path: 'events',
- name: 'events',
- component: () => import('@/pages/events/index.vue'),
- meta: { requiresAuth: true },
- },
- {
- path: 'events/create',
- name: 'events-create',
- component: () => import('@/pages/events/create.vue'),
- meta: { requiresAuth: true },
- },
- {
- path: 'events/:id',
- name: 'events-show',
- component: () => import('@/pages/events/[id].vue'),
- meta: { requiresAuth: true },
- },
- {
- path: 'events/:id/edit',
- name: 'events-edit',
- component: () => import('@/pages/events/[id]/edit.vue'),
- meta: { requiresAuth: true },
- },
- // Add more routes...
- ],
- },
- {
- path: '/login',
- name: 'login',
- component: () => import('@/pages/login.vue'),
- meta: { layout: 'blank', requiresAuth: false },
- },
- {
- path: '/:pathMatch(.*)*',
- name: 'not-found',
- component: () => import('@/pages/[...error].vue'),
- },
- ],
-})
-
-// Navigation guard
-router.beforeEach(async (to, from, next) => {
- const authStore = useAuthStore()
-
- // Check if route requires auth
- if (to.meta.requiresAuth && !authStore.isAuthenticated) {
- // Try to fetch user if we have a token
- if (authStore.token) {
- const success = await authStore.fetchUser()
- if (success) {
- return next()
- }
- }
- return next({ name: 'login', query: { redirect: to.fullPath } })
- }
-
- // Redirect to dashboard if logged in and going to login
- if (to.name === 'login' && authStore.isAuthenticated) {
- return next({ name: 'dashboard' })
- }
-
- next()
-})
-
-export default router
-```
-
-### Navigation Menu
+### Navigation Menu (Organizer App)
```typescript
// src/navigation/vertical/index.ts
@@ -766,291 +533,153 @@ export default [
to: { name: 'dashboard' },
icon: { icon: 'tabler-smart-home' },
},
- {
- heading: 'Management',
- },
+ { heading: 'Event Management' },
{
title: 'Events',
to: { name: 'events' },
icon: { icon: 'tabler-calendar-event' },
},
{
- title: 'Members',
- to: { name: 'members' },
+ title: 'Festival Sections',
+ to: { name: 'festival-sections' },
+ icon: { icon: 'tabler-layout-grid' },
+ },
+ {
+ title: 'Time Slots & Shifts',
+ to: { name: 'shifts' },
+ icon: { icon: 'tabler-clock' },
+ },
+ { heading: 'People' },
+ {
+ title: 'Persons',
+ to: { name: 'persons' },
icon: { icon: 'tabler-users' },
},
{
- title: 'Music',
- to: { name: 'music' },
+ title: 'Artists',
+ to: { name: 'artists' },
icon: { icon: 'tabler-music' },
},
{
- title: 'Setlists',
- to: { name: 'setlists' },
- icon: { icon: 'tabler-playlist' },
+ title: 'Volunteers',
+ to: { name: 'volunteers' },
+ icon: { icon: 'tabler-heart-handshake' },
+ },
+ { heading: 'Operations' },
+ {
+ title: 'Accreditation',
+ to: { name: 'accreditation' },
+ icon: { icon: 'tabler-id-badge-2' },
},
{
- heading: 'CRM',
+ title: 'Briefings',
+ to: { name: 'briefings' },
+ icon: { icon: 'tabler-mail' },
},
{
- title: 'Locations',
- to: { name: 'locations' },
- icon: { icon: 'tabler-map-pin' },
+ title: 'Mission Control',
+ to: { name: 'mission-control' },
+ icon: { icon: 'tabler-broadcast' },
},
+ { heading: 'Insights' },
{
- title: 'Customers',
- to: { name: 'customers' },
- icon: { icon: 'tabler-building' },
+ title: 'Reports',
+ to: { name: 'reports' },
+ icon: { icon: 'tabler-chart-bar' },
},
] as VerticalNavItems
```
-## Best Practices
-
-### Always Use
-
-- `
```
-## Event Handlers
+## Best Practices
-Name handlers with `handle` prefix:
+### Always Use
+- `
-```
-
-## File Organization
-
-```
-src/
-├── @core/ # Vuexy core (DON'T MODIFY)
-├── @layouts/ # Vuexy layouts (DON'T MODIFY)
-├── assets/ # Static assets
-│ ├── images/
-│ └── styles/
-├── components/ # Shared components
-│ ├── ui/ # Base UI components
-│ └── features/ # Feature-specific components
-├── composables/ # Custom composables
-│ ├── useEvents.ts
-│ ├── useMembers.ts
-│ └── useAuth.ts
-├── layouts/ # App layouts
-├── lib/ # Utilities
-│ ├── api-client.ts
-│ ├── utils.ts
-│ └── query-client.ts
-├── navigation/ # Menu configuration
-├── pages/ # Route pages
-│ ├── dashboard.vue
-│ ├── events/
-│ │ ├── index.vue
-│ │ ├── create.vue
-│ │ └── [id]/
-│ │ ├── index.vue
-│ │ └── edit.vue
-│ └── login.vue
-├── plugins/ # Vue plugins
-├── router/ # Vue Router
-├── stores/ # Pinia stores
-├── types/ # TypeScript types
-│ └── index.ts
-└── App.vue
-```
-
-## Performance
-
-### Lazy Load Routes
+## Portal Router Guards
```typescript
-// ✅ Good - lazy load all route components
-const routes = [
- {
- path: '/events',
- component: () => import('@/pages/events/index.vue'),
- },
-]
-```
-
-### Use Computed for Derived State
-
-```typescript
-// ✅ Good - computed is cached
-const upcomingEvents = computed(() =>
- events.value.filter(e => new Date(e.event_date) > new Date())
-)
-
-// ❌ Avoid - recalculates on every render
-const upcomingEvents = events.value.filter(e => new Date(e.event_date) > new Date())
-```
-
-### Prefetch on Hover
-
-```typescript
-// Prefetch event details when user hovers
-function handleEventHover(id: string) {
- queryClient.prefetchQuery({
- queryKey: ['events', id],
- queryFn: () => fetchEvent(id),
- })
-}
-```
-
-### Use v-once for Static Content
-
-```vue
-
-
-
Welcome to Band Management
-
Manage your band operations efficiently.
-
-```
-
-## Error Handling
-
-```vue
-
-
-
-
-
-
-
-
- {{ error?.message ?? 'Something went wrong' }}
- Retry
-
-
-
-
-
-
-
-```
-
-## API Integration Pattern
-
-Create typed API functions:
-
-```typescript
-// lib/api/events.ts
-import { apiClient } from '@/lib/api-client'
-import type { Event, CreateEventData, ApiResponse } from '@/types'
-
-export const eventsApi = {
- list: async (params?: { page?: number; status?: string }) => {
- const { data } = await apiClient.get>('/events', { params })
- return data
- },
-
- get: async (id: string) => {
- const { data } = await apiClient.get>(`/events/${id}`)
- return data.data
- },
-
- create: async (eventData: CreateEventData) => {
- const { data } = await apiClient.post>('/events', eventData)
- return data.data
- },
-
- update: async (id: string, eventData: Partial) => {
- const { data } = await apiClient.put>(`/events/${id}`, eventData)
- return data.data
- },
-
- delete: async (id: string) => {
- await apiClient.delete(`/events/${id}`)
- },
+// apps/portal/src/router/guards.ts
+export function determineAccessMode(route: RouteLocationNormalized): 'token' | 'login' | 'unauthenticated' {
+ if (route.query.token) return 'token'
+ if (authStore.isAuthenticated) return 'login'
+ return 'unauthenticated'
}
+
+// Token-based: POST /api/v1/portal/token-auth { token: '...' } -> returns person context
+// Login-based: Same /api/v1/auth/login as app/
```
diff --git a/.cursor/rules/102_multi_tenancy.mdc b/.cursor/rules/102_multi_tenancy.mdc
new file mode 100644
index 0000000..2cc4f49
--- /dev/null
+++ b/.cursor/rules/102_multi_tenancy.mdc
@@ -0,0 +1,221 @@
+---
+description: Multi-tenancy and portal architecture rules for EventCrew
+globs: ["api/**/*.php", "apps/portal/**/*.{vue,ts}"]
+alwaysApply: true
+---
+
+# Multi-Tenancy & Portal Rules
+
+## Organisation Scoping (CRITICAL)
+
+Every query on event-related data MUST be scoped to the current organisation. This is enforced via Eloquent Global Scopes, NOT manual where-clauses in controllers.
+
+### OrganisationScope Implementation
+
+```php
+// Applied in model's booted() method
+protected static function booted(): void
+{
+ static::addGlobalScope('organisation', function (Builder $builder) {
+ if ($organisationId = auth()->user()?->current_organisation_id) {
+ $builder->where('organisation_id', $organisationId);
+ }
+ });
+}
+```
+
+### Models That Need OrganisationScope
+- Event (direct `organisation_id`)
+- CrowdType (direct `organisation_id`)
+- AccreditationCategory (direct `organisation_id`)
+- Company (direct `organisation_id`)
+
+### Models Scoped via Parent
+These don't have `organisation_id` directly but inherit scope through their parent:
+- FestivalSection → via Event
+- TimeSlot → via Event
+- Shift → via FestivalSection → Event
+- Person → via Event
+- Artist → via Event
+- All other event-child models
+
+### Rules
+1. NEVER use `Model::all()` without a scope
+2. NEVER pass organisation_id in URL params for filtering — always derive from authenticated user
+3. ALWAYS use Policies to verify the user belongs to the organisation
+4. ALWAYS test that users from Organisation A cannot see Organisation B's data
+
+## Three-Level Authorization
+
+### Level 1: App Level (Spatie Roles)
+```php
+// super_admin, support_agent
+$user->hasRole('super_admin');
+```
+
+### Level 2: Organisation Level (Spatie Team Permissions)
+```php
+// org_admin, org_member, org_readonly
+// Organisation acts as Spatie "team"
+$user->hasRole('org_admin'); // within current organisation context
+```
+
+### Level 3: Event Level (Custom Pivot)
+```php
+// event_manager, artist_manager, staff_coordinator, volunteer_coordinator, accreditation_officer
+// Stored in event_user_roles pivot table
+$user->eventRoles()->where('event_id', $event->id)->pluck('role');
+```
+
+### Middleware Stack
+```php
+// routes/api.php
+Route::middleware(['auth:sanctum', 'organisation.role:org_admin,org_member'])->group(...);
+Route::middleware(['auth:sanctum', 'event.role:event_manager'])->group(...);
+```
+
+## User Invitation Flow
+
+### Internal Staff (login-based)
+1. Organiser enters email + selects role
+2. System checks if account exists
+3. If no: invitation email with activation link (24h valid), account created on activation
+4. If yes: invitation email, existing user linked to new org/event role on acceptance
+5. User can switch between organisations via org-switcher
+
+### Volunteer Registration
+1. Volunteer fills public registration form (multi-step)
+2. Person record created with `status = 'pending'`
+3. Organiser approves/rejects → `status = 'approved'`
+4. On approval: check if email has platform account → link or create
+5. Volunteer logs in to portal
+
+## Portal Architecture
+
+### Two Access Modes in One App (`apps/portal/`)
+
+| Mode | Middleware | Users | Token Source |
+|------|-----------|-------|-------------|
+| Login | `auth:sanctum` | Volunteers, Crew | Bearer token from login |
+| Token | `portal.token` | Artists, Suppliers, Press | URL token param: `?token=ULID` |
+
+### Token-Based Authentication Flow
+```
+1. Artist/supplier receives email with link: https://portal.eventcrew.app/advance?token=01HQ3K...
+2. Portal detects token in URL query parameter
+3. POST /api/v1/portal/token-auth { token: '01HQ3K...' }
+4. Backend validates token against artists.portal_token or production_requests.token
+5. Returns person context (name, event, crowd_type, permissions)
+6. Portal stores context in Pinia, shows relevant portal view
+```
+
+### Login-Based Authentication Flow
+```
+1. Volunteer navigates to https://portal.eventcrew.app/login
+2. Enters email + password
+3. POST /api/v1/auth/login (same endpoint as apps/app/)
+4. Returns user + organisations + event roles
+5. Portal shows volunteer-specific views (My Shifts, Claim Shifts, Messages, Profile)
+```
+
+### Backend Route Structure
+```php
+// Public
+Route::post('auth/login', ...);
+Route::post('portal/token-auth', ...);
+Route::post('portal/form-submit', ...); // Public form submission
+
+// Login-based portal (auth:sanctum)
+Route::middleware('auth:sanctum')->prefix('portal')->group(function () {
+ Route::get('my-shifts', ...);
+ Route::post('shifts/{shift}/claim', ...);
+ Route::get('messages', ...);
+ Route::get('profile', ...);
+});
+
+// Token-based portal (portal.token)
+Route::middleware('portal.token')->prefix('portal')->group(function () {
+ Route::get('artist', ...);
+ Route::post('advancing', ...);
+ Route::get('supplier', ...);
+ Route::post('production-request', ...);
+});
+```
+
+### PortalTokenMiddleware
+```php
+bearerToken() ?? $request->query('token');
+
+ if (!$token) {
+ return response()->json(['message' => 'Token required'], 401);
+ }
+
+ // Try artist token
+ $artist = Artist::where('portal_token', $token)->first();
+ if ($artist) {
+ $request->merge(['portal_context' => 'artist', 'portal_entity' => $artist]);
+ return $next($request);
+ }
+
+ // Try production request token
+ $productionRequest = ProductionRequest::where('token', $token)->first();
+ if ($productionRequest) {
+ $request->merge(['portal_context' => 'supplier', 'portal_entity' => $productionRequest]);
+ return $next($request);
+ }
+
+ return response()->json(['message' => 'Invalid token'], 401);
+ }
+}
+```
+
+## CORS Configuration
+
+```php
+// config/cors.php
+'allowed_origins' => [
+ env('FRONTEND_ADMIN_URL', 'http://localhost:5173'),
+ env('FRONTEND_APP_URL', 'http://localhost:5174'),
+ env('FRONTEND_PORTAL_URL', 'http://localhost:5175'),
+],
+'supports_credentials' => true,
+```
+
+## Shift Claiming & Approval Flow
+
+### Three Assignment Strategies per Shift
+1. **Fully controlled**: `slots_open_for_claiming = 0`. Organiser assigns manually.
+2. **Fully self-service**: `slots_open_for_claiming = slots_total`. Volunteers fill all spots.
+3. **Hybrid**: `slots_open_for_claiming < slots_total`. Some reserved for manual.
+
+### Claim Flow
+```
+1. Volunteer claims shift → POST /shifts/{id}/claim
+2. Backend checks: slot availability, time_slot conflict (UNIQUE person_id + time_slot_id)
+3. Creates ShiftAssignment with status = 'pending_approval' (or 'approved' if auto_approve)
+4. Dispatches NotifyCoordinatorOfClaimJob (queued, WhatsApp via Zender)
+5. Coordinator approves/rejects via Organizer app
+6. Volunteer receives confirmation email
+```
+
+### Status Machine
+```
+pending_approval → approved → completed
+ → rejected (final)
+ → cancelled (by volunteer or organiser)
+```
diff --git a/.cursor/rules/200_testing.mdc b/.cursor/rules/200_testing.mdc
index 8c9234c..14cad25 100644
--- a/.cursor/rules/200_testing.mdc
+++ b/.cursor/rules/200_testing.mdc
@@ -1,5 +1,5 @@
---
-description: Testing standards for Laravel API and Vue frontend
+description: Testing standards for EventCrew multi-tenant platform
globs: ["**/tests/**", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/Test.php"]
alwaysApply: true
---
@@ -10,9 +10,9 @@ alwaysApply: true
1. **Test behavior, not implementation** - Focus on what, not how
2. **Feature tests for APIs** - Test full request/response cycles
-3. **Unit tests for logic** - Test Actions and complex functions
-4. **Integration tests for Vue** - Test component interactions
-5. **Fast and isolated** - Each test should be independent
+3. **Multi-tenant isolation** - Every test must verify organisation scoping
+4. **Minimum per endpoint**: happy path (200/201) + unauthenticated (401) + wrong organisation (403) + validation (422)
+5. **PHPUnit** for backend, **Vitest** for frontend
## Laravel Testing
@@ -22,326 +22,270 @@ alwaysApply: true
api/tests/
├── Feature/
│ └── Api/
-│ ├── AuthTest.php
-│ ├── EventTest.php
-│ ├── MemberTest.php
-│ ├── MusicTest.php
-│ ├── SetlistTest.php
-│ ├── LocationTest.php
-│ └── CustomerTest.php
+│ └── V1/
+│ ├── AuthTest.php
+│ ├── OrganisationTest.php
+│ ├── EventTest.php
+│ ├── FestivalSectionTest.php
+│ ├── TimeSlotTest.php
+│ ├── ShiftTest.php
+│ ├── PersonTest.php
+│ ├── ArtistTest.php
+│ ├── AccreditationTest.php
+│ └── BriefingTest.php
├── Unit/
-│ ├── Actions/
-│ │ ├── CreateEventActionTest.php
-│ │ └── ...
+│ ├── Services/
│ └── Models/
-│ ├── EventTest.php
-│ └── ...
└── TestCase.php
```
-### Feature Test Template
+### Feature Test Template (Multi-Tenant)
```php
admin = User::factory()->admin()->create();
- $this->member = User::factory()->member()->create();
+ // Seed Spatie roles
+ Role::create(['name' => 'org_admin']);
+ Role::create(['name' => 'org_member']);
+
+ // Create organisations
+ $this->organisation = Organisation::factory()->create();
+ $this->otherOrganisation = Organisation::factory()->create();
+
+ // Create users with Spatie roles
+ $this->orgAdmin = User::factory()->create();
+ $this->orgAdmin->assignRole('org_admin');
+ $this->organisation->users()->attach($this->orgAdmin);
+
+ $this->orgMember = User::factory()->create();
+ $this->orgMember->assignRole('org_member');
+ $this->organisation->users()->attach($this->orgMember);
+
+ $this->otherOrgUser = User::factory()->create();
+ $this->otherOrgUser->assignRole('org_admin');
+ $this->otherOrganisation->users()->attach($this->otherOrgUser);
}
// ==========================================
- // INDEX
+ // AUTHENTICATION (401)
// ==========================================
- public function test_guests_cannot_list_events(): void
+ public function test_unauthenticated_user_cannot_list_events(): void
{
- $response = $this->getJson('/api/v1/events');
-
- $response->assertStatus(401);
+ $this->getJson("/api/v1/organisations/{$this->organisation->id}/events")
+ ->assertStatus(401);
}
- public function test_authenticated_users_can_list_events(): void
+ public function test_unauthenticated_user_cannot_create_event(): void
{
- Event::factory()->count(3)->create();
+ $this->postJson("/api/v1/organisations/{$this->organisation->id}/events", [])
+ ->assertStatus(401);
+ }
- $response = $this->actingAs($this->member)
- ->getJson('/api/v1/events');
+ // ==========================================
+ // ORGANISATION ISOLATION (403)
+ // ==========================================
+
+ public function test_user_cannot_list_events_from_other_organisation(): void
+ {
+ Event::factory()->for($this->otherOrganisation)->count(3)->create();
+
+ $this->actingAs($this->orgAdmin)
+ ->getJson("/api/v1/organisations/{$this->otherOrganisation->id}/events")
+ ->assertStatus(403);
+ }
+
+ public function test_user_cannot_view_event_from_other_organisation(): void
+ {
+ $event = Event::factory()->for($this->otherOrganisation)->create();
+
+ $this->actingAs($this->orgAdmin)
+ ->getJson("/api/v1/events/{$event->id}")
+ ->assertStatus(403);
+ }
+
+ public function test_user_cannot_update_event_from_other_organisation(): void
+ {
+ $event = Event::factory()->for($this->otherOrganisation)->create();
+
+ $this->actingAs($this->orgAdmin)
+ ->putJson("/api/v1/events/{$event->id}", ['name' => 'Hacked'])
+ ->assertStatus(403);
+
+ $this->assertDatabaseMissing('events', ['name' => 'Hacked']);
+ }
+
+ // ==========================================
+ // INDEX (200)
+ // ==========================================
+
+ public function test_org_admin_can_list_events(): void
+ {
+ Event::factory()->for($this->organisation)->count(3)->create();
+ Event::factory()->for($this->otherOrganisation)->count(2)->create(); // should not appear
+
+ $response = $this->actingAs($this->orgAdmin)
+ ->getJson("/api/v1/organisations/{$this->organisation->id}/events");
$response->assertStatus(200)
+ ->assertJsonCount(3, 'data')
->assertJsonStructure([
- 'success',
- 'data' => [
- '*' => ['id', 'title', 'event_date', 'status'],
- ],
- 'meta' => ['pagination'],
- ])
- ->assertJsonCount(3, 'data');
- }
-
- public function test_events_are_paginated(): void
- {
- Event::factory()->count(20)->create();
-
- $response = $this->actingAs($this->member)
- ->getJson('/api/v1/events?per_page=10');
-
- $response->assertStatus(200)
- ->assertJsonCount(10, 'data')
- ->assertJsonPath('meta.pagination.total', 20)
- ->assertJsonPath('meta.pagination.per_page', 10);
+ 'data' => ['*' => ['id', 'name', 'slug', 'start_date', 'end_date', 'status']],
+ 'meta',
+ ]);
}
// ==========================================
- // SHOW
+ // SHOW (200)
// ==========================================
- public function test_can_view_single_event(): void
+ public function test_can_view_own_organisation_event(): void
{
- $event = Event::factory()->create(['title' => 'Summer Concert']);
+ $event = Event::factory()->for($this->organisation)->create([
+ 'name' => 'Festivalpark 2026',
+ ]);
- $response = $this->actingAs($this->member)
+ $response = $this->actingAs($this->orgMember)
->getJson("/api/v1/events/{$event->id}");
$response->assertStatus(200)
- ->assertJsonPath('success', true)
- ->assertJsonPath('data.title', 'Summer Concert');
- }
-
- public function test_returns_404_for_nonexistent_event(): void
- {
- $response = $this->actingAs($this->member)
- ->getJson('/api/v1/events/nonexistent-id');
-
- $response->assertStatus(404);
+ ->assertJsonPath('data.name', 'Festivalpark 2026')
+ ->assertJsonPath('data.organisation_id', $this->organisation->id);
}
// ==========================================
- // STORE
+ // STORE (201)
// ==========================================
- public function test_admin_can_create_event(): void
+ public function test_org_admin_can_create_event(): void
{
- $location = Location::factory()->create();
-
$eventData = [
- 'title' => 'New Year Concert',
- 'event_date' => now()->addMonth()->toDateString(),
- 'start_time' => '20:00',
- 'location_id' => $location->id,
- 'status' => 'draft',
+ 'name' => 'Zomerfestival 2026',
+ 'slug' => 'zomerfestival-2026',
+ 'start_date' => '2026-07-15',
+ 'end_date' => '2026-07-17',
+ 'timezone' => 'Europe/Amsterdam',
];
- $response = $this->actingAs($this->admin)
- ->postJson('/api/v1/events', $eventData);
+ $response = $this->actingAs($this->orgAdmin)
+ ->postJson("/api/v1/organisations/{$this->organisation->id}/events", $eventData);
$response->assertStatus(201)
- ->assertJsonPath('success', true)
- ->assertJsonPath('data.title', 'New Year Concert');
+ ->assertJsonPath('data.name', 'Zomerfestival 2026');
$this->assertDatabaseHas('events', [
- 'title' => 'New Year Concert',
- 'location_id' => $location->id,
+ 'name' => 'Zomerfestival 2026',
+ 'organisation_id' => $this->organisation->id,
]);
}
- public function test_member_cannot_create_event(): void
+ public function test_org_member_cannot_create_event(): void
{
- $eventData = [
- 'title' => 'Unauthorized Event',
- 'event_date' => now()->addMonth()->toDateString(),
- 'start_time' => '20:00',
- ];
-
- $response = $this->actingAs($this->member)
- ->postJson('/api/v1/events', $eventData);
-
- $response->assertStatus(403);
- }
-
- public function test_validation_errors_are_returned(): void
- {
- $response = $this->actingAs($this->admin)
- ->postJson('/api/v1/events', []);
-
- $response->assertStatus(422)
- ->assertJsonPath('success', false)
- ->assertJsonValidationErrors(['title', 'event_date', 'start_time']);
- }
-
- public function test_event_date_must_be_future(): void
- {
- $response = $this->actingAs($this->admin)
- ->postJson('/api/v1/events', [
- 'title' => 'Past Event',
- 'event_date' => now()->subDay()->toDateString(),
- 'start_time' => '20:00',
- ]);
-
- $response->assertStatus(422)
- ->assertJsonValidationErrors(['event_date']);
+ $this->actingAs($this->orgMember)
+ ->postJson("/api/v1/organisations/{$this->organisation->id}/events", [
+ 'name' => 'Unauthorized Event',
+ 'slug' => 'unauthorized',
+ 'start_date' => '2026-07-15',
+ 'end_date' => '2026-07-17',
+ ])
+ ->assertStatus(403);
}
// ==========================================
- // UPDATE
+ // VALIDATION (422)
// ==========================================
- public function test_admin_can_update_event(): void
+ public function test_validation_errors_for_missing_required_fields(): void
{
- $event = Event::factory()->create(['title' => 'Old Title']);
+ $this->actingAs($this->orgAdmin)
+ ->postJson("/api/v1/organisations/{$this->organisation->id}/events", [])
+ ->assertStatus(422)
+ ->assertJsonValidationErrors(['name', 'start_date', 'end_date']);
+ }
- $response = $this->actingAs($this->admin)
- ->putJson("/api/v1/events/{$event->id}", [
- 'title' => 'Updated Title',
- ]);
+ public function test_end_date_must_be_after_start_date(): void
+ {
+ $this->actingAs($this->orgAdmin)
+ ->postJson("/api/v1/organisations/{$this->organisation->id}/events", [
+ 'name' => 'Invalid Event',
+ 'slug' => 'invalid-event',
+ 'start_date' => '2026-07-20',
+ 'end_date' => '2026-07-15', // before start
+ ])
+ ->assertStatus(422)
+ ->assertJsonValidationErrors(['end_date']);
+ }
- $response->assertStatus(200)
- ->assertJsonPath('data.title', 'Updated Title');
+ // ==========================================
+ // UPDATE (200)
+ // ==========================================
+
+ public function test_org_admin_can_update_event(): void
+ {
+ $event = Event::factory()->for($this->organisation)->create();
+
+ $this->actingAs($this->orgAdmin)
+ ->putJson("/api/v1/events/{$event->id}", ['name' => 'Updated Name'])
+ ->assertStatus(200)
+ ->assertJsonPath('data.name', 'Updated Name');
$this->assertDatabaseHas('events', [
'id' => $event->id,
- 'title' => 'Updated Title',
+ 'name' => 'Updated Name',
]);
}
- public function test_can_update_event_status(): void
- {
- $event = Event::factory()->draft()->create();
-
- $response = $this->actingAs($this->admin)
- ->putJson("/api/v1/events/{$event->id}", [
- 'status' => 'confirmed',
- ]);
-
- $response->assertStatus(200)
- ->assertJsonPath('data.status', 'confirmed');
- }
-
// ==========================================
- // DELETE
+ // DELETE (204)
// ==========================================
- public function test_admin_can_delete_event(): void
+ public function test_org_admin_can_delete_event(): void
{
- $event = Event::factory()->create();
+ $event = Event::factory()->for($this->organisation)->create();
- $response = $this->actingAs($this->admin)
- ->deleteJson("/api/v1/events/{$event->id}");
+ $this->actingAs($this->orgAdmin)
+ ->deleteJson("/api/v1/events/{$event->id}")
+ ->assertStatus(204);
- $response->assertStatus(200)
- ->assertJsonPath('success', true);
-
- $this->assertDatabaseMissing('events', ['id' => $event->id]);
+ $this->assertSoftDeleted('events', ['id' => $event->id]);
}
- public function test_member_cannot_delete_event(): void
+ public function test_org_member_cannot_delete_event(): void
{
- $event = Event::factory()->create();
+ $event = Event::factory()->for($this->organisation)->create();
- $response = $this->actingAs($this->member)
- ->deleteJson("/api/v1/events/{$event->id}");
+ $this->actingAs($this->orgMember)
+ ->deleteJson("/api/v1/events/{$event->id}")
+ ->assertStatus(403);
- $response->assertStatus(403);
$this->assertDatabaseHas('events', ['id' => $event->id]);
}
-
- // ==========================================
- // RELATIONSHIPS
- // ==========================================
-
- public function test_event_includes_location_when_loaded(): void
- {
- $location = Location::factory()->create(['name' => 'City Hall']);
- $event = Event::factory()->create(['location_id' => $location->id]);
-
- $response = $this->actingAs($this->member)
- ->getJson("/api/v1/events/{$event->id}");
-
- $response->assertStatus(200)
- ->assertJsonPath('data.location.name', 'City Hall');
- }
-}
-```
-
-### Unit Test Template (Action)
-
-```php
-action = new CreateEventAction();
- }
-
- public function test_creates_event_with_valid_data(): void
- {
- $user = User::factory()->create();
- $this->actingAs($user);
-
- $data = [
- 'title' => 'Test Event',
- 'event_date' => now()->addMonth()->toDateString(),
- 'start_time' => '20:00',
- ];
-
- $event = $this->action->execute($data);
-
- $this->assertInstanceOf(Event::class, $event);
- $this->assertEquals('Test Event', $event->title);
- $this->assertEquals($user->id, $event->created_by);
- }
-
- public function test_sets_default_values(): void
- {
- $user = User::factory()->create();
- $this->actingAs($user);
-
- $event = $this->action->execute([
- 'title' => 'Test',
- 'event_date' => now()->addMonth()->toDateString(),
- 'start_time' => '20:00',
- ]);
-
- $this->assertEquals('EUR', $event->currency);
- $this->assertEquals('draft', $event->status->value);
- }
}
```
@@ -355,32 +299,30 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Enums\EventStatus;
-use App\Enums\EventVisibility;
use App\Models\Event;
-use App\Models\Location;
-use App\Models\User;
+use App\Models\Organisation;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory
*/
-final class EventFactory extends Factory
+class EventFactory extends Factory
{
protected $model = Event::class;
public function definition(): array
{
+ $startDate = fake('nl_NL')->dateTimeBetween('+1 week', '+6 months');
+ $endDate = (clone $startDate)->modify('+' . fake()->numberBetween(1, 5) . ' days');
+
return [
- 'title' => fake()->sentence(3),
- 'description' => fake()->optional()->paragraph(),
- 'event_date' => fake()->dateTimeBetween('+1 week', '+6 months'),
- 'start_time' => fake()->time('H:i'),
- 'end_time' => fake()->optional()->time('H:i'),
- 'fee' => fake()->optional()->randomFloat(2, 100, 5000),
- 'currency' => 'EUR',
+ 'organisation_id' => Organisation::factory(),
+ 'name' => fake('nl_NL')->words(3, true) . ' Festival',
+ 'slug' => fake()->slug(),
+ 'start_date' => $startDate,
+ 'end_date' => $endDate,
+ 'timezone' => 'Europe/Amsterdam',
'status' => fake()->randomElement(EventStatus::cases()),
- 'visibility' => EventVisibility::Members,
- 'created_by' => User::factory(),
];
}
@@ -389,79 +331,54 @@ final class EventFactory extends Factory
return $this->state(fn () => ['status' => EventStatus::Draft]);
}
- public function confirmed(): static
+ public function published(): static
{
- return $this->state(fn () => ['status' => EventStatus::Confirmed]);
+ return $this->state(fn () => ['status' => EventStatus::Published]);
}
- public function withLocation(): static
- {
- return $this->state(fn () => ['location_id' => Location::factory()]);
- }
-
- public function upcoming(): static
+ public function showday(): static
{
return $this->state(fn () => [
- 'event_date' => fake()->dateTimeBetween('+1 day', '+1 month'),
- 'status' => EventStatus::Confirmed,
- ]);
- }
-
- public function past(): static
- {
- return $this->state(fn () => [
- 'event_date' => fake()->dateTimeBetween('-6 months', '-1 day'),
- 'status' => EventStatus::Completed,
+ 'status' => EventStatus::ShowDay,
+ 'start_date' => now(),
+ 'end_date' => now()->addDays(2),
]);
}
}
```
-### User Factory States
+### Organisation Factory
```php
state(fn () => [
- 'type' => 'member',
- 'role' => 'admin',
- 'status' => 'active',
- ]);
-}
+namespace Database\Factories;
-public function member(): static
-{
- return $this->state(fn () => [
- 'type' => 'member',
- 'role' => 'member',
- 'status' => 'active',
- ]);
-}
+use App\Models\Organisation;
+use Illuminate\Database\Eloquent\Factories\Factory;
-public function bookingAgent(): static
+/**
+ * @extends Factory
+ */
+class OrganisationFactory extends Factory
{
- return $this->state(fn () => [
- 'type' => 'member',
- 'role' => 'booking_agent',
- 'status' => 'active',
- ]);
-}
+ protected $model = Organisation::class;
-public function customer(): static
-{
- return $this->state(fn () => [
- 'type' => 'customer',
- 'role' => null,
- 'status' => 'active',
- ]);
+ public function definition(): array
+ {
+ return [
+ 'name' => fake('nl_NL')->company(),
+ 'slug' => fake()->unique()->slug(),
+ 'billing_status' => 'active',
+ 'settings' => [],
+ ];
+ }
}
```
-### Test Helpers (TestCase.php)
+### TestCase Base Class
```php
admin()->create();
- $this->actingAs($admin);
- return $admin;
+ Role::firstOrCreate(['name' => 'org_admin']);
+
+ $organisation ??= Organisation::factory()->create();
+ $user = User::factory()->create();
+ $user->assignRole('org_admin');
+ $organisation->users()->attach($user);
+
+ return $user;
}
/**
- * Create and authenticate as regular member.
+ * Create user with org_member role attached to an organisation.
*/
- protected function actingAsMember(): User
+ protected function createOrgMember(?Organisation $organisation = null): User
{
- $member = User::factory()->member()->create();
- $this->actingAs($member);
- return $member;
- }
+ Role::firstOrCreate(['name' => 'org_member']);
- /**
- * Assert API success response structure.
- */
- protected function assertApiSuccess($response, int $status = 200): void
- {
- $response->assertStatus($status)
- ->assertJsonStructure(['success', 'data'])
- ->assertJsonPath('success', true);
- }
+ $organisation ??= Organisation::factory()->create();
+ $user = User::factory()->create();
+ $user->assignRole('org_member');
+ $organisation->users()->attach($user);
- /**
- * Assert API error response structure.
- */
- protected function assertApiError($response, int $status = 400): void
- {
- $response->assertStatus($status)
- ->assertJsonPath('success', false)
- ->assertJsonStructure(['success', 'message']);
+ return $user;
}
}
```
## Running Tests
-### Laravel
-
```bash
-# Run all tests
+# All tests
cd api && php artisan test
-# Run with coverage
+# Specific test class
+php artisan test --filter=EventTest
+
+# Specific test method
+php artisan test --filter=test_org_admin_can_create_event
+
+# With coverage
php artisan test --coverage
-# Run specific test file
-php artisan test tests/Feature/Api/EventTest.php
-
-# Run specific test method
-php artisan test --filter test_admin_can_create_event
-
-# Run in parallel
-php artisan test --parallel
+# After each module
+php artisan test --filter=ModuleName
```
-### Pest PHP Syntax
+## Vue Testing (Vitest)
-```php
-admin = User::factory()->admin()->create();
-});
-
-it('allows admin to create events', function () {
- $response = $this->actingAs($this->admin)
- ->postJson('/api/v1/events', [
- 'title' => 'Concert',
- 'event_date' => now()->addMonth()->toDateString(),
- 'start_time' => '20:00',
- ]);
-
- $response->assertStatus(201);
- expect(Event::count())->toBe(1);
-});
-
-it('validates required fields', function () {
- $response = $this->actingAs($this->admin)
- ->postJson('/api/v1/events', []);
-
- $response->assertStatus(422)
- ->assertJsonValidationErrors(['title', 'event_date']);
-});
-
-it('returns paginated results', function () {
- Event::factory()->count(25)->create();
-
- $response = $this->actingAs($this->admin)
- ->getJson('/api/v1/events?per_page=10');
-
- expect($response->json('data'))->toHaveCount(10);
- expect($response->json('meta.pagination.total'))->toBe(25);
-});
-```
-
-## Vue Testing (Vitest + Vue Test Utils)
-
-### File Organization
-
-```
-src/
-├── components/
-│ └── EventCard/
-│ ├── EventCard.vue
-│ └── EventCard.test.ts
-├── composables/
-│ └── useEvents.test.ts
-└── test/
- ├── setup.ts
- ├── mocks/
- │ ├── handlers.ts # MSW handlers
- │ └── server.ts
- └── utils.ts # Custom render with providers
-```
-
-### Vitest Configuration
+### Configuration
```typescript
// vitest.config.ts
@@ -624,247 +470,80 @@ export default defineConfig({
})
```
-### Test Setup
-
-```typescript
-// src/test/setup.ts
-import { vi, beforeAll, afterAll, afterEach } from 'vitest'
-import { config } from '@vue/test-utils'
-import { server } from './mocks/server'
-
-// Start MSW server
-beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
-afterEach(() => server.resetHandlers())
-afterAll(() => server.close())
-
-// Global stubs for Vuexy components
-config.global.stubs = {
- VBtn: true,
- VCard: true,
- VCardText: true,
- VCardTitle: true,
- VTable: true,
- VDialog: true,
- VProgressCircular: true,
- RouterLink: true,
-}
-```
-
-### MSW Setup for API Mocking
-
-```typescript
-// src/test/mocks/handlers.ts
-import { http, HttpResponse } from 'msw'
-
-export const handlers = [
- http.get('/api/v1/events', () => {
- return HttpResponse.json({
- success: true,
- data: [
- { id: '1', title: 'Event 1', status: 'confirmed' },
- { id: '2', title: 'Event 2', status: 'draft' },
- { id: '3', title: 'Event 3', status: 'pending' },
- ],
- meta: { pagination: { current_page: 1, total: 3, per_page: 15 } }
- })
- }),
-
- http.post('/api/v1/events', async ({ request }) => {
- const body = await request.json()
- return HttpResponse.json({
- success: true,
- data: { id: '4', ...body },
- message: 'Event created successfully'
- }, { status: 201 })
- }),
-
- http.get('/api/v1/auth/user', () => {
- return HttpResponse.json({
- success: true,
- data: { id: '1', name: 'Test User', email: 'test@example.com', role: 'admin' }
- })
- }),
-]
-
-// src/test/mocks/server.ts
-import { setupServer } from 'msw/node'
-import { handlers } from './handlers'
-
-export const server = setupServer(...handlers)
-```
-
-### Test Utilities
-
-```typescript
-// src/test/utils.ts
-import { mount, VueWrapper } from '@vue/test-utils'
-import { createPinia, setActivePinia } from 'pinia'
-import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'
-import { createRouter, createWebHistory } from 'vue-router'
-import type { Component } from 'vue'
-
-export function createTestingPinia() {
- const pinia = createPinia()
- setActivePinia(pinia)
- return pinia
-}
-
-export function createTestQueryClient() {
- return new QueryClient({
- defaultOptions: {
- queries: { retry: false, gcTime: 0 },
- mutations: { retry: false },
- },
- })
-}
-
-export function mountWithProviders(component: Component, options = {}) {
- const pinia = createTestingPinia()
- const queryClient = createTestQueryClient()
- const router = createRouter({
- history: createWebHistory(),
- routes: [{ path: '/', component: { template: '
' } }],
- })
-
- return mount(component, {
- global: {
- plugins: [pinia, router, [VueQueryPlugin, { queryClient }]],
- stubs: {
- VBtn: true,
- VCard: true,
- VTable: true,
- },
- },
- ...options,
- })
-}
-```
-
-### Component Test Pattern
-
-```typescript
-// src/components/EventCard.test.ts
-import { describe, it, expect, vi } from 'vitest'
-import { mount } from '@vue/test-utils'
-import EventCard from './EventCard.vue'
-
-describe('EventCard', () => {
- const mockEvent = {
- id: '1',
- title: 'Summer Concert',
- event_date: '2025-07-15',
- status: 'confirmed',
- status_label: 'Confirmed',
- }
-
- it('displays event title', () => {
- const wrapper = mount(EventCard, {
- props: { event: mockEvent },
- })
-
- expect(wrapper.text()).toContain('Summer Concert')
- })
-
- it('emits edit event when edit button clicked', async () => {
- const wrapper = mount(EventCard, {
- props: { event: mockEvent },
- })
-
- await wrapper.find('[data-test="edit-btn"]').trigger('click')
-
- expect(wrapper.emitted('edit')).toBeTruthy()
- expect(wrapper.emitted('edit')?.[0]).toEqual([mockEvent])
- })
-
- it('shows correct status color', () => {
- const wrapper = mount(EventCard, {
- props: { event: mockEvent },
- })
-
- const chip = wrapper.find('[data-test="status-chip"]')
- expect(chip.classes()).toContain('bg-success')
- })
-})
-```
-
### Composable Test Pattern
```typescript
// src/composables/__tests__/useEvents.test.ts
-import { describe, it, expect, vi, beforeEach } from 'vitest'
+import { describe, it, expect, beforeEach } from 'vitest'
import { flushPromises } from '@vue/test-utils'
-import { useEvents } from '../useEvents'
-import { createTestQueryClient, createTestingPinia } from '@/test/utils'
-import { VueQueryPlugin } from '@tanstack/vue-query'
+import { createPinia, setActivePinia } from 'pinia'
+import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'
import { createApp } from 'vue'
describe('useEvents', () => {
beforeEach(() => {
- createTestingPinia()
+ setActivePinia(createPinia())
})
- it('fetches events successfully', async () => {
+ it('fetches events for organisation', async () => {
const app = createApp({ template: '
' })
- const queryClient = createTestQueryClient()
+ const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false } },
+ })
app.use(VueQueryPlugin, { queryClient })
-
- // Mount and test...
+
await flushPromises()
-
- // Assertions
+ // Assert API was called with correct organisation_id
})
})
```
-### Pest Expectations (Laravel)
-
-```php
-// Use fluent expectations
-expect($user)->toBeInstanceOf(User::class);
-expect($collection)->toHaveCount(5);
-expect($response->json('data'))->toMatchArray([...]);
-
-// Chain expectations
-expect($user)
- ->name->toBe('John')
- ->email->toContain('@')
- ->created_at->toBeInstanceOf(Carbon::class);
-```
-
## Test Coverage Goals
| Area | Target | Priority |
|------|--------|----------|
| API Endpoints | 90%+ | High |
-| Actions | 100% | High |
+| Organisation Isolation | 100% | Critical |
+| Services | 100% | High |
| Models | 80%+ | Medium |
| Vue Composables | 80%+ | Medium |
| Vue Components | 60%+ | Low |
-**Minimum Coverage**: 80% line coverage
+## Testing Checklist (per module)
-## Coverage Requirements
+### Backend
+- [ ] Index: returns only own organisation's data (200)
+- [ ] Show: can view own org resource (200)
+- [ ] Show: cannot view other org resource (403)
+- [ ] Store: org_admin can create (201)
+- [ ] Store: org_member cannot create (403)
+- [ ] Store: validation errors returned (422)
+- [ ] Store: unauthenticated rejected (401)
+- [ ] Update: org_admin can update (200)
+- [ ] Update: cannot update other org resource (403)
+- [ ] Delete: org_admin can delete (204)
+- [ ] Delete: org_member cannot delete (403)
+- [ ] Soft delete: record still exists with deleted_at
-- **Feature tests**: All API endpoints must have tests
-- **Unit tests**: All Actions and Services with business logic
-- **Component tests**: All interactive components
-- **Composable tests**: All custom composables that fetch data
+### Frontend
+- [ ] Loading state shown during fetch
+- [ ] Error state with retry button on failure
+- [ ] Empty state when no data
+- [ ] Mobile responsive (375px)
## Best Practices
### Do
-
-- Test happy path AND edge cases
-- Use descriptive test names
+- Test organisation isolation for every endpoint
+- Use Spatie roles in test setup (assignRole, hasRole)
+- Use factories with realistic Dutch test data (`fake('nl_NL')`)
+- Test soft deletes with `assertSoftDeleted`
- One assertion focus per test
-- Use factories for test data
-- Clean up after tests (RefreshDatabase)
-- Test authorization separately
+- Use RefreshDatabase trait
### Don't
-
-- Test framework code (Laravel, Vue)
-- Test private methods directly
+- Skip organisation isolation tests
- Share state between tests
-- Use real external APIs
-- Over-mock (test real behavior)
+- Use real external APIs (Zender, SendGrid)
+- Test Laravel/Vue framework internals
+- Over-mock (test real behavior where possible)
diff --git a/.cursorrules b/.cursorrules
new file mode 100644
index 0000000..49121b6
--- /dev/null
+++ b/.cursorrules
@@ -0,0 +1,21 @@
+# EventCrew Cursor Rules
+
+## Stack
+PHP 8.2 + Laravel 12 | TypeScript + Vue 3 + Vuexy/Vuetify | Pinia + TanStack Query
+
+## Laravel
+- Resource Controllers, Form Requests, API Resources — altijd
+- HasUlids op business modellen, HasFactory, SoftDeletes waar gedocumenteerd
+- Global Scope OrganisationScope op event-gerelateerde modellen
+- Policies voor autorisatie, nooit inline role checks
+
+## Vue 3
+-
-
-
-
-
- Invite Members to Event
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ user.name }}
-
-
- {{ user.email }}
-
-
-
-
-
-
- No members found
-
-
-
-
-
-
-
-
-
- Cancel
-
-
- Invite {{ selectedUserIds.length }} Member(s)
-
-
-
-
-
-
diff --git a/apps/admin/src/composables/useApi.ts b/apps/admin/src/composables/useApi.ts
deleted file mode 100644
index e4b7811..0000000
--- a/apps/admin/src/composables/useApi.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { createFetch } from '@vueuse/core'
-import { destr } from 'destr'
-
-export const useApi = createFetch({
- baseUrl: import.meta.env.VITE_API_BASE_URL || '/api',
- fetchOptions: {
- headers: {
- Accept: 'application/json',
- },
- },
- options: {
- refetch: true,
- async beforeFetch({ options }) {
- const accessToken = useCookie('accessToken').value
-
- if (accessToken) {
- options.headers = {
- ...options.headers,
- Authorization: `Bearer ${accessToken}`,
- }
- }
-
- return { options }
- },
- afterFetch(ctx) {
- const { data, response } = ctx
-
- // Parse data if it's JSON
-
- let parsedData = null
- try {
- parsedData = destr(data)
- }
- catch (error) {
- console.error(error)
- }
-
- return { data: parsedData, response }
- },
- },
-})
diff --git a/apps/admin/src/composables/useEvents.ts b/apps/admin/src/composables/useEvents.ts
index 7f67c95..71f34a0 100644
--- a/apps/admin/src/composables/useEvents.ts
+++ b/apps/admin/src/composables/useEvents.ts
@@ -1,177 +1,137 @@
-import { ref, computed } from "vue";
-import { $api } from "@/utils/api";
-import type {
- Event,
- CreateEventData,
- UpdateEventData,
- InviteToEventData,
- ApiResponse,
- Pagination,
-} from "@/types/events";
+import { computed, ref } from 'vue'
+import { apiClient } from '@/lib/api-client'
+import { useCurrentOrganisationId } from '@/composables/useOrganisationContext'
+import type { ApiResponse, CreateEventData, Event, Pagination, UpdateEventData } from '@/types/events'
+
+/** Laravel paginated JSON resource response (no `success` wrapper). */
+interface LaravelPaginatedEventsBody {
+ data: Event[]
+ meta: {
+ current_page: number
+ per_page: number
+ total: number
+ last_page: number
+ from: number | null
+ to: number | null
+ }
+}
+
+function requireOrganisationId(organisationId: string | null): string {
+ if (!organisationId) {
+ throw new Error('No organisation in session. Log in again or select an organisation.')
+ }
+ return organisationId
+}
export function useEvents() {
- const events = ref([]);
- const currentEvent = ref(null);
- const pagination = ref(null);
- const isLoading = ref(false);
- const error = ref(null);
+ const { organisationId } = useCurrentOrganisationId()
+ const events = ref([])
+ const currentEvent = ref(null)
+ const pagination = ref(null)
+ const isLoading = ref(false)
+ const error = ref(null)
+
+ function eventsPath(): string {
+ const id = requireOrganisationId(organisationId.value)
+ return `/organisations/${id}/events`
+ }
- // Fetch all events
async function fetchEvents(params?: {
- page?: number;
- per_page?: number;
- status?: string;
+ page?: number
+ per_page?: number
}) {
- isLoading.value = true;
- error.value = null;
+ isLoading.value = true
+ error.value = null
try {
- const response = await $api>("/events", {
- method: "GET",
- query: params,
- });
- events.value = response.data;
- pagination.value = response.meta?.pagination || null;
- } catch (err) {
- error.value =
- err instanceof Error ? err : new Error("Failed to fetch events");
- throw error.value;
- } finally {
- isLoading.value = false;
+ const { data } = await apiClient.get(eventsPath(), { params })
+ events.value = data.data
+ pagination.value = {
+ current_page: data.meta.current_page,
+ per_page: data.meta.per_page,
+ total: data.meta.total,
+ last_page: data.meta.last_page,
+ from: data.meta.from,
+ to: data.meta.to,
+ }
+ }
+ catch (err) {
+ error.value = err instanceof Error ? err : new Error('Failed to fetch events')
+ throw error.value
+ }
+ finally {
+ isLoading.value = false
}
}
- // Fetch single event
async function fetchEvent(id: string) {
- isLoading.value = true;
- error.value = null;
+ isLoading.value = true
+ error.value = null
try {
- const response = await $api>(`/events/${id}`, {
- method: "GET",
- });
- currentEvent.value = response.data;
- return response.data;
- } catch (err) {
- error.value =
- err instanceof Error ? err : new Error("Failed to fetch event");
- throw error.value;
- } finally {
- isLoading.value = false;
+ const { data } = await apiClient.get>(`${eventsPath()}/${id}`)
+ currentEvent.value = data.data
+ return data.data
+ }
+ catch (err) {
+ error.value = err instanceof Error ? err : new Error('Failed to fetch event')
+ throw error.value
+ }
+ finally {
+ isLoading.value = false
}
}
- // Create event
async function createEvent(eventData: CreateEventData) {
- isLoading.value = true;
- error.value = null;
+ isLoading.value = true
+ error.value = null
try {
- const response = await $api>("/events", {
- method: "POST",
- body: eventData,
- });
- events.value.unshift(response.data);
- return response.data;
- } catch (err) {
- error.value =
- err instanceof Error ? err : new Error("Failed to create event");
- throw error.value;
- } finally {
- isLoading.value = false;
+ const { data } = await apiClient.post>(eventsPath(), eventData)
+ events.value.unshift(data.data)
+ return data.data
+ }
+ catch (err) {
+ error.value = err instanceof Error ? err : new Error('Failed to create event')
+ throw error.value
+ }
+ finally {
+ isLoading.value = false
}
}
- // Update event
async function updateEvent(id: string, eventData: UpdateEventData) {
- isLoading.value = true;
- error.value = null;
+ isLoading.value = true
+ error.value = null
try {
- const response = await $api>(`/events/${id}`, {
- method: "PUT",
- body: eventData,
- });
- const index = events.value.findIndex((e) => e.id === id);
- if (index !== -1) {
- events.value[index] = response.data;
- }
- if (currentEvent.value?.id === id) {
- currentEvent.value = response.data;
- }
- return response.data;
- } catch (err) {
- error.value =
- err instanceof Error ? err : new Error("Failed to update event");
- throw error.value;
- } finally {
- isLoading.value = false;
+ const { data } = await apiClient.put>(`${eventsPath()}/${id}`, eventData)
+ const index = events.value.findIndex(e => e.id === id)
+ if (index !== -1)
+ events.value[index] = data.data
+ if (currentEvent.value?.id === id)
+ currentEvent.value = data.data
+ return data.data
}
- }
-
- // Delete event
- async function deleteEvent(id: string) {
- isLoading.value = true;
- error.value = null;
-
- try {
- await $api(`/events/${id}`, {
- method: "DELETE",
- });
- events.value = events.value.filter((e) => e.id !== id);
- if (currentEvent.value?.id === id) {
- currentEvent.value = null;
- }
- } catch (err) {
- error.value =
- err instanceof Error ? err : new Error("Failed to delete event");
- throw error.value;
- } finally {
- isLoading.value = false;
+ catch (err) {
+ error.value = err instanceof Error ? err : new Error('Failed to update event')
+ throw error.value
}
- }
-
- // Invite members to event
- async function inviteToEvent(eventId: string, inviteData: InviteToEventData) {
- isLoading.value = true;
- error.value = null;
-
- try {
- const response = await $api>(
- `/events/${eventId}/invite`,
- {
- method: "POST",
- body: inviteData,
- }
- );
- // Refresh event to get updated invitations
- if (currentEvent.value?.id === eventId) {
- await fetchEvent(eventId);
- }
- return response.data;
- } catch (err) {
- error.value =
- err instanceof Error ? err : new Error("Failed to invite members");
- throw error.value;
- } finally {
- isLoading.value = false;
+ finally {
+ isLoading.value = false
}
}
return {
- // State
+ organisationId: computed(() => organisationId.value),
events: computed(() => events.value),
currentEvent: computed(() => currentEvent.value),
pagination: computed(() => pagination.value),
isLoading: computed(() => isLoading.value),
error: computed(() => error.value),
-
- // Actions
fetchEvents,
fetchEvent,
createEvent,
updateEvent,
- deleteEvent,
- inviteToEvent,
- };
+ }
}
diff --git a/apps/admin/src/composables/useOrganisationContext.ts b/apps/admin/src/composables/useOrganisationContext.ts
new file mode 100644
index 0000000..bfe5db6
--- /dev/null
+++ b/apps/admin/src/composables/useOrganisationContext.ts
@@ -0,0 +1,28 @@
+import { useCookie } from '@core/composable/useCookie'
+import { computed } from 'vue'
+
+export interface AuthOrganisationSummary {
+ id: string
+ name: string
+ slug: string
+ role: string
+}
+
+export interface AuthUserCookie {
+ id: string
+ name: string
+ email: string
+ roles?: string[]
+ organisations?: AuthOrganisationSummary[]
+}
+
+/**
+ * First organisation from the session cookie (set at login). Super-admins still need an organisation context for nested event routes.
+ */
+export function useCurrentOrganisationId() {
+ const userData = useCookie('userData')
+
+ const organisationId = computed(() => userData.value?.organisations?.[0]?.id ?? null)
+
+ return { organisationId }
+}
diff --git a/apps/admin/src/lib/api-client.ts b/apps/admin/src/lib/api-client.ts
index 6714c8a..90ab992 100644
--- a/apps/admin/src/lib/api-client.ts
+++ b/apps/admin/src/lib/api-client.ts
@@ -1,24 +1,36 @@
import axios from 'axios'
+import { parse } from 'cookie-es'
import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
+/**
+ * Single axios instance for the real Laravel API (VITE_API_URL).
+ * Auth: Bearer token from cookie 'accessToken' (set by login).
+ * Use this for all event-crew API calls; useApi (composables/useApi) stays for Vuexy demo/mock endpoints.
+ */
const apiClient: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: {
'Content-Type': 'application/json',
- 'Accept': 'application/json',
+ Accept: 'application/json',
},
timeout: 30000,
})
-// Request interceptor - add auth token
+function getAccessToken(): string | null {
+ if (typeof document === 'undefined') return null
+ const cookies = parse(document.cookie)
+ const token = cookies.accessToken
+
+ return token ?? null
+}
+
apiClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
- const token = localStorage.getItem('auth_token')
+ const token = getAccessToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
- // Log in development
if (import.meta.env.DEV) {
console.log(`🚀 ${config.method?.toUpperCase()} ${config.url}`, config.data)
}
@@ -28,7 +40,6 @@ apiClient.interceptors.request.use(
error => Promise.reject(error),
)
-// Response interceptor - handle errors
apiClient.interceptors.response.use(
response => {
if (import.meta.env.DEV) {
@@ -39,13 +50,20 @@ apiClient.interceptors.response.use(
},
error => {
if (import.meta.env.DEV) {
- console.error(`❌ ${error.response?.status} ${error.config?.url}`, error.response?.data)
+ console.error(
+ `❌ ${error.response?.status} ${error.config?.url}`,
+ error.response?.data,
+ )
}
- // Handle 401 - redirect to login
if (error.response?.status === 401) {
- localStorage.removeItem('auth_token')
- window.location.href = '/login'
+ // Clear auth cookies (align with utils/api.ts / login flow)
+ document.cookie = 'accessToken=; path=/; max-age=0'
+ document.cookie = 'userData=; path=/; max-age=0'
+ document.cookie = 'userAbilityRules=; path=/; max-age=0'
+ if (window.location.pathname !== '/login') {
+ window.location.href = '/login'
+ }
}
return Promise.reject(error)
@@ -53,4 +71,3 @@ apiClient.interceptors.response.use(
)
export { apiClient }
-
diff --git a/apps/admin/src/main.ts b/apps/admin/src/main.ts
index ba6c598..159c494 100644
--- a/apps/admin/src/main.ts
+++ b/apps/admin/src/main.ts
@@ -1,4 +1,5 @@
import { createApp } from 'vue'
+import { VueQueryPlugin } from '@tanstack/vue-query'
import App from '@/App.vue'
import { registerPlugins } from '@core/utils/plugins'
@@ -17,6 +18,14 @@ app.config.errorHandler = (err, instance, info) => {
}
// Register plugins
+app.use(VueQueryPlugin, {
+ queryClientConfig: {
+ defaultOptions: {
+ queries: { staleTime: 1000 * 60 * 5, retry: 1 },
+ },
+ },
+})
+
try {
registerPlugins(app)
} catch (error) {
diff --git a/apps/admin/src/pages/events/[id]/edit.vue b/apps/admin/src/pages/events/[id]/edit.vue
index 9c8da20..a18bf41 100644
--- a/apps/admin/src/pages/events/[id]/edit.vue
+++ b/apps/admin/src/pages/events/[id]/edit.vue
@@ -1,7 +1,7 @@
-
+
- {{ currentEvent.title }}
+ {{ currentEvent.name }}
- {{ currentEvent.status_label }}
+ {{ formatStatusLabel(currentEvent.status) }}
-
-
- Edit
-
-
- Invite Members
-
-
+
+ Edit
+
@@ -93,70 +77,37 @@ function getRsvpColor(status: string): string {
>
- Event Date
+ Slug
- {{ new Date(currentEvent.event_date).toLocaleDateString() }}
+ {{ currentEvent.slug }}
- Start Time
+ Start date
- {{ currentEvent.start_time }}
+ {{ new Date(currentEvent.start_date).toLocaleDateString() }}
-
+
- End Time
+ End date
- {{ currentEvent.end_time }}
+ {{ new Date(currentEvent.end_date).toLocaleDateString() }}
-
+
- Location
+ Timezone
- {{ currentEvent.location.name }}
-
- {{ currentEvent.location.address }}, {{ currentEvent.location.city }}
-
-
-
-
-
-
- Customer
-
-
- {{ currentEvent.customer.name }}
-
-
-
-
-
- Fee
-
-
- {{ currentEvent.currency }} {{ currentEvent.fee }}
+ {{ currentEvent.timezone }}
@@ -166,94 +117,19 @@ function getRsvpColor(status: string): string {
md="6"
>
- Description
+ Organisation
- {{ currentEvent.description }}
-
-
-
-
-
- Notes
-
-
- {{ currentEvent.notes }}
-
-
-
-
-
- Internal Notes
-
-
- {{ currentEvent.internal_notes }}
+ {{ currentEvent.organisation.name }}
-
-
-
-
-
-
-
- Invitations ({{ currentEvent.invitations?.length || 0 }})
-
-
-
-
-
-
- Member
- Email
- RSVP Status
- Response Date
- Note
-
-
-
-
- {{ invitation.user?.name || '-' }}
- {{ invitation.user?.email || '-' }}
-
-
- {{ invitation.rsvp_status }}
-
-
-
- {{ invitation.rsvp_responded_at ? new Date(invitation.rsvp_responded_at).toLocaleString() : '-' }}
-
- {{ invitation.rsvp_note || '-' }}
-
-
-
-
-
- No members invited yet. Click "Invite Members" to send invitations.
-
-
@@ -264,13 +140,6 @@ function getRsvpColor(status: string): string {
/>
-
-
-
diff --git a/apps/admin/src/pages/events/create.vue b/apps/admin/src/pages/events/create.vue
index f1df63e..11455d5 100644
--- a/apps/admin/src/pages/events/create.vue
+++ b/apps/admin/src/pages/events/create.vue
@@ -1,7 +1,7 @@
+
+ You need an organisation to manage events. Create an organisation in the API or attach your user to one, then log in again.
+
+
@@ -104,38 +92,26 @@ function getStatusColor(status: string): string {
color="primary"
prepend-icon="tabler-plus"
to="/events/create"
+ :disabled="!organisationId"
>
Create Event
-
-
-
+
-
-
-
+
- {{ item.title }}
+ {{ item.name }}
-
- {{ new Date(item.event_date).toLocaleDateString() }}
+
+ {{ new Date(item.start_date).toLocaleDateString() }}
-
- {{ item.start_time }}
-
-
-
- {{ item.location?.name || '-' }}
+
+ {{ new Date(item.end_date).toLocaleDateString() }}
@@ -181,28 +151,17 @@ function getStatusColor(status: string): string {
:color="getStatusColor(item.status)"
size="small"
>
- {{ item.status_label }}
+ {{ formatStatusLabel(item.status) }}
-
+
-
-
+
-
-
-
-
@@ -216,34 +175,5 @@ function getStatusColor(status: string): string {
-
-
-
-
- Delete Event?
-
- Are you sure you want to delete "{{ eventToDelete?.title }}"? This action cannot be undone.
-
-
-
-
- Cancel
-
-
- Delete
-
-
-
-
-
diff --git a/apps/admin/src/pages/login.vue b/apps/admin/src/pages/login.vue
index c2fc921..895043e 100644
--- a/apps/admin/src/pages/login.vue
+++ b/apps/admin/src/pages/login.vue
@@ -11,6 +11,18 @@ import authV2MaskDark from '@images/pages/misc-mask-dark.png'
import authV2MaskLight from '@images/pages/misc-mask-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
+import { getUserAbilityRules } from '@/utils/auth-ability'
+import type { Rule } from '@/plugins/casl/ability'
+import type { AuthUserCookie } from '@/composables/useOrganisationContext'
+
+interface LoginApiPayload {
+ success: boolean
+ data: {
+ user: AuthUserCookie & Record
+ token: string
+ }
+ message?: string
+}
const authThemeImg = useGenerateImageVariant(authV2LoginIllustrationLight, authV2LoginIllustrationDark, authV2LoginIllustrationBorderedLight, authV2LoginIllustrationBorderedDark, true)
@@ -46,7 +58,7 @@ const rememberMe = ref(false)
const login = async () => {
try {
- const res = await $api('/auth/login', {
+ const res = await $api('/auth/login', {
method: 'POST',
body: {
email: credentials.value.email,
@@ -71,14 +83,14 @@ const login = async () => {
const userData = data.user
const accessToken = data.token
- // Set ability rules based on user role
- const userAbilityRules = getUserAbilityRules(userData.role)
+ const roles = Array.isArray(userData.roles) ? userData.roles : []
+ const userAbilityRules = getUserAbilityRules(roles)
- useCookie('userAbilityRules').value = userAbilityRules
+ useCookie('userAbilityRules').value = userAbilityRules
ability.update(userAbilityRules)
- useCookie('userData').value = userData
- useCookie('accessToken').value = accessToken
+ useCookie('userData').value = userData
+ useCookie('accessToken').value = accessToken
// Redirect to `to` query if exist or redirect to index route
await nextTick()
@@ -89,42 +101,6 @@ const login = async () => {
}
}
-// Generate ability rules based on user role
-function getUserAbilityRules(role: string | null) {
- // Admin can do everything
- if (role === 'admin') {
- return [{ action: 'manage', subject: 'all' }]
- }
-
- // Booking agent can manage events and customers
- if (role === 'booking_agent') {
- return [
- { action: 'read', subject: 'all' },
- { action: 'manage', subject: 'Event' },
- { action: 'manage', subject: 'Customer' },
- { action: 'manage', subject: 'Location' },
- { action: 'manage', subject: 'BookingRequest' },
- ]
- }
-
- // Music manager can manage music and setlists
- if (role === 'music_manager') {
- return [
- { action: 'read', subject: 'all' },
- { action: 'manage', subject: 'MusicNumber' },
- { action: 'manage', subject: 'Setlist' },
- ]
- }
-
- // Default member permissions
- return [
- { action: 'read', subject: 'Event' },
- { action: 'read', subject: 'MusicNumber' },
- { action: 'read', subject: 'Setlist' },
- { action: 'manage', subject: 'User', conditions: { id: '{{ user.id }}' } },
- ]
-}
-
const onSubmit = () => {
refVForm.value?.validate()
.then(({ valid: isValid }) => {
diff --git a/apps/admin/src/pages/register.vue b/apps/admin/src/pages/register.vue
index d0e0a1a..fc6a22f 100644
--- a/apps/admin/src/pages/register.vue
+++ b/apps/admin/src/pages/register.vue
@@ -4,6 +4,18 @@ import { VForm } from 'vuetify/components/VForm'
import AuthProvider from '@/views/pages/authentication/AuthProvider.vue'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
+import { getUserAbilityRules } from '@/utils/auth-ability'
+import type { Rule } from '@/plugins/casl/ability'
+import type { AuthUserCookie } from '@/composables/useOrganisationContext'
+
+interface RegisterApiPayload {
+ success: boolean
+ data: {
+ user: AuthUserCookie & Record
+ token: string
+ }
+ message?: string
+}
import authV2RegisterIllustrationBorderedDark from '@images/pages/auth-v2-register-illustration-bordered-dark.png'
import authV2RegisterIllustrationBorderedLight from '@images/pages/auth-v2-register-illustration-bordered-light.png'
@@ -50,7 +62,7 @@ const refVForm = ref()
const register = async () => {
isLoading.value = true
try {
- const res = await $api('/auth/register', {
+ const res = await $api('/auth/register', {
method: 'POST',
body: {
name: form.value.name,
@@ -77,18 +89,13 @@ const register = async () => {
const userData = data.user
const accessToken = data.token
- // Set ability rules based on user role
- const userAbilityRules = [
- { action: 'read', subject: 'Event' },
- { action: 'read', subject: 'MusicNumber' },
- { action: 'read', subject: 'Setlist' },
- { action: 'manage', subject: 'User', conditions: { id: userData.id } },
- ]
+ const roles = Array.isArray(userData.roles) ? userData.roles : []
+ const userAbilityRules = getUserAbilityRules(roles)
- useCookie('userAbilityRules').value = userAbilityRules
+ useCookie('userAbilityRules').value = userAbilityRules
ability.update(userAbilityRules)
- useCookie('userData').value = userData
- useCookie('accessToken').value = accessToken
+ useCookie('userData').value = userData
+ useCookie('accessToken').value = accessToken
await nextTick(() => {
router.replace('/')
diff --git a/apps/admin/src/plugins/casl/ability.ts b/apps/admin/src/plugins/casl/ability.ts
index dbc3ded..48511be 100644
--- a/apps/admin/src/plugins/casl/ability.ts
+++ b/apps/admin/src/plugins/casl/ability.ts
@@ -2,8 +2,7 @@ import { createMongoAbility } from '@casl/ability'
export type Actions = 'create' | 'read' | 'update' | 'delete' | 'manage'
-// ex: Post, Comment, User, etc. We haven't used any of these in our demo though.
-export type Subjects = 'Post' | 'Comment' | 'all'
+export type Subjects = 'Post' | 'Comment' | 'User' | 'Event' | 'Organisation' | 'all'
export interface Rule { action: Actions; subject: Subjects }
diff --git a/apps/admin/src/types/events.ts b/apps/admin/src/types/events.ts
index bb59a05..648f364 100644
--- a/apps/admin/src/types/events.ts
+++ b/apps/admin/src/types/events.ts
@@ -1,4 +1,4 @@
-// API Response wrapper
+// API Response wrapper (used by endpoints that use the ApiResponse trait)
export interface ApiResponse {
success: boolean
data: T
@@ -13,145 +13,47 @@ export interface Pagination {
per_page: number
total: number
last_page: number
- from: number
- to: number
+ from: number | null
+ to: number | null
}
-// Event types
-export type EventStatus = 'draft' | 'pending' | 'confirmed' | 'completed' | 'cancelled'
-export type EventVisibility = 'private' | 'members' | 'public'
-export type RsvpStatus = 'pending' | 'available' | 'unavailable' | 'tentative'
+/** EventCrew festival / multi-day event (API resource). */
+export type EventCrewEventStatus =
+ | 'draft'
+ | 'published'
+ | 'registration_open'
+ | 'buildup'
+ | 'showday'
+ | 'teardown'
+ | 'closed'
-export interface Location {
+export interface OrganisationSummary {
id: string
name: string
- address: string
- city: string
- postal_code: string | null
- country: string
- latitude: number | null
- longitude: number | null
- capacity: number | null
- contact_name: string | null
- contact_email: string | null
- contact_phone: string | null
- created_at: string
- updated_at: string
-}
-
-export interface Customer {
- id: string
- name: string
- company_name: string | null
- type: 'individual' | 'company'
- email: string | null
- phone: string | null
- address: string | null
- city: string | null
- postal_code: string | null
- country: string
- is_portal_enabled: boolean
- created_at: string
- updated_at: string
-}
-
-export interface Setlist {
- id: string
- name: string
- description: string | null
- total_duration_seconds: number | null
- is_template: boolean
- is_archived: boolean
- created_at: string
- updated_at: string
-}
-
-export interface User {
- id: string
- name: string
- email: string
- phone: string | null
- bio: string | null
- instruments: string[] | null
- avatar_path: string | null
- type: 'member' | 'customer'
- role: 'admin' | 'booking_agent' | 'music_manager' | 'member' | null
- status: 'active' | 'inactive'
- created_at: string
- updated_at: string
-}
-
-export interface EventInvitation {
- id: string
- event_id: string
- user_id: string
- rsvp_status: RsvpStatus
- rsvp_note: string | null
- rsvp_responded_at: string | null
- invited_at: string
- user?: User
+ slug: string
}
export interface Event {
id: string
- title: string
- description: string | null
- event_date: string
- start_time: string
- end_time: string | null
- load_in_time: string | null
- soundcheck_time: string | null
- fee: number | null
- currency: string
- status: EventStatus
- status_label: string
- status_color: string
- visibility: EventVisibility
- visibility_label: string
- rsvp_deadline: string | null
- notes: string | null
- internal_notes: string | null
- is_public_setlist: boolean
- location: Location | null
- customer: Customer | null
- setlist: Setlist | null
- invitations: EventInvitation[]
- creator: User | null
+ organisation_id: string
+ name: string
+ slug: string
+ start_date: string
+ end_date: string
+ timezone: string
+ status: EventCrewEventStatus
created_at: string
updated_at: string
+ organisation?: OrganisationSummary
}
-// Form types for creating/updating
export interface CreateEventData {
- title: string
- description?: string
- location_id?: string
- customer_id?: string
- setlist_id?: string
- event_date: string
- start_time: string
- end_time?: string
- load_in_time?: string
- soundcheck_time?: string
- fee?: number
- currency?: string
- status?: EventStatus
- visibility?: EventVisibility
- rsvp_deadline?: string
- notes?: string
- internal_notes?: string
- is_public_setlist?: boolean
+ name: string
+ slug: string
+ start_date: string
+ end_date: string
+ timezone?: string
+ status?: EventCrewEventStatus
}
export interface UpdateEventData extends Partial {}
-
-export interface InviteToEventData {
- user_ids: string[]
-}
-
-export interface RsvpEventData {
- status: RsvpStatus
- note?: string
-}
-
-
diff --git a/apps/admin/src/utils/api.ts b/apps/admin/src/utils/api.ts
index 63ffe78..27a755a 100644
--- a/apps/admin/src/utils/api.ts
+++ b/apps/admin/src/utils/api.ts
@@ -1,49 +1,40 @@
-import { ofetch } from 'ofetch'
+import type { AxiosRequestConfig } from 'axios'
+import { apiClient } from '@/lib/api-client'
-export const $api = ofetch.create({
- baseURL: import.meta.env.VITE_API_URL || '/api',
- async onRequest({ options }) {
- options.headers = options.headers || new Headers()
+type ApiOptions = {
+ method?: string
+ body?: unknown
+ query?: Record
+ onResponseError?: (ctx: { response: { status: number; _data?: { errors?: Record; message?: string } } }) => void
+}
- const accessToken = useCookie('accessToken').value
- if (accessToken) {
- if (options.headers instanceof Headers) {
- options.headers.set('Authorization', `Bearer ${accessToken}`)
- }
+/**
+ * Thin ofetch-style wrapper around the single axios client (lib/axios).
+ * Use apiClient from @/lib/axios directly in new code; $api remains for Vuexy template compatibility.
+ */
+export async function $api(url: string, options: ApiOptions = {}): Promise {
+ const { method = 'GET', body, query, onResponseError } = options
+
+ const config: AxiosRequestConfig = {
+ method: method.toLowerCase() as AxiosRequestConfig['method'],
+ url,
+ params: query,
+ data: body,
+ }
+
+ try {
+ const response = await apiClient.request(config)
+ return response.data
+ }
+ catch (error: any) {
+ if (onResponseError && error.response) {
+ onResponseError({
+ response: {
+ status: error.response.status,
+ _data: error.response.data,
+ },
+ })
}
-
- // Set default headers
- if (options.headers instanceof Headers) {
- options.headers.set('Accept', 'application/json')
- options.headers.set('Content-Type', 'application/json')
- }
- },
- async onResponseError({ response }) {
- // Handle 401 by redirecting to login
- if (response.status === 401) {
- if (import.meta.env.DEV) {
- console.error('❌ API 401 Error:', {
- url: response.url,
- pathname: window.location.pathname,
- willRedirect: window.location.pathname !== '/login',
- })
- }
-
- // Clear auth data
- useCookie('accessToken').value = null
- useCookie('userData').value = null
- useCookie('userAbilityRules').value = null
-
- // Only redirect if not already on login page
- // Add a small delay to prevent redirect loops
- if (window.location.pathname !== '/login') {
- // Use setTimeout to break any potential redirect loops
- setTimeout(() => {
- if (window.location.pathname !== '/login') {
- window.location.href = '/login'
- }
- }, 100)
- }
- }
- },
-})
+ throw error
+ }
+}
diff --git a/apps/admin/src/utils/auth-ability.ts b/apps/admin/src/utils/auth-ability.ts
new file mode 100644
index 0000000..cf0d209
--- /dev/null
+++ b/apps/admin/src/utils/auth-ability.ts
@@ -0,0 +1,23 @@
+import type { Rule } from '@/plugins/casl/ability'
+
+/**
+ * CASL rules from Spatie role names returned by the API (`/auth/login`, etc.).
+ */
+export function getUserAbilityRules(roles: string[]): Rule[] {
+ if (roles.includes('super_admin')) {
+ return [{ action: 'manage', subject: 'all' }]
+ }
+
+ if (roles.includes('org_admin')) {
+ return [
+ { action: 'read', subject: 'all' },
+ { action: 'manage', subject: 'Event' },
+ { action: 'manage', subject: 'Organisation' },
+ ]
+ }
+
+ return [
+ { action: 'read', subject: 'Event' },
+ { action: 'read', subject: 'Organisation' },
+ ]
+}
diff --git a/apps/admin/vite.config.ts b/apps/admin/vite.config.ts
index a119ec7..001b81c 100644
--- a/apps/admin/vite.config.ts
+++ b/apps/admin/vite.config.ts
@@ -106,6 +106,15 @@ export default defineConfig({
'@api-utils': fileURLToPath(new URL('./src/plugins/fake-api/utils/', import.meta.url)),
},
},
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8000',
+ changeOrigin: true,
+ },
+ },
+ },
build: {
chunkSizeWarningLimit: 5000,
},
diff --git a/apps/band/README 2.md b/apps/app/README 2.md
similarity index 100%
rename from apps/band/README 2.md
rename to apps/app/README 2.md
diff --git a/apps/band/README.md b/apps/app/README.md
similarity index 100%
rename from apps/band/README.md
rename to apps/app/README.md
diff --git a/apps/band/auto-imports.d.ts b/apps/app/auto-imports.d.ts
similarity index 100%
rename from apps/band/auto-imports.d.ts
rename to apps/app/auto-imports.d.ts
diff --git a/apps/band/components.d.ts b/apps/app/components.d.ts
similarity index 100%
rename from apps/band/components.d.ts
rename to apps/app/components.d.ts
diff --git a/apps/band/dev.Dockerfile b/apps/app/dev.Dockerfile
similarity index 100%
rename from apps/band/dev.Dockerfile
rename to apps/app/dev.Dockerfile
diff --git a/apps/band/docker-compose.dev.yml b/apps/app/docker-compose.dev.yml
similarity index 100%
rename from apps/band/docker-compose.dev.yml
rename to apps/app/docker-compose.dev.yml
diff --git a/apps/band/docker-compose.prod.yml b/apps/app/docker-compose.prod.yml
similarity index 100%
rename from apps/band/docker-compose.prod.yml
rename to apps/app/docker-compose.prod.yml
diff --git a/apps/band/env.d.ts b/apps/app/env.d.ts
similarity index 100%
rename from apps/band/env.d.ts
rename to apps/app/env.d.ts
diff --git a/apps/band/index.html b/apps/app/index.html
similarity index 97%
rename from apps/band/index.html
rename to apps/app/index.html
index 1e67ace..0ab0b07 100644
--- a/apps/band/index.html
+++ b/apps/app/index.html
@@ -6,7 +6,7 @@
- Band Management - Band Portal
+ Event Crew - Band Portal
diff --git a/apps/band/nginx.conf b/apps/app/nginx.conf
similarity index 100%
rename from apps/band/nginx.conf
rename to apps/app/nginx.conf
diff --git a/apps/customers/package.json b/apps/app/package.json
similarity index 95%
rename from apps/customers/package.json
rename to apps/app/package.json
index fef4908..75ffc0e 100644
--- a/apps/customers/package.json
+++ b/apps/app/package.json
@@ -1,5 +1,5 @@
{
- "name": "vuexy-vuejs-admin-template",
+ "name": "eventcrew-app",
"version": "9.5.0",
"private": true,
"type": "module",
@@ -19,6 +19,7 @@
"@floating-ui/dom": "1.6.8",
"@formkit/drag-and-drop": "0.1.6",
"@sindresorhus/is": "7.1.0",
+ "@tanstack/vue-query": "^5.95.2",
"@tiptap/extension-highlight": "^2.27.1",
"@tiptap/extension-image": "^2.27.1",
"@tiptap/extension-link": "^2.27.1",
@@ -26,6 +27,7 @@
"@tiptap/pm": "^2.27.1",
"@tiptap/starter-kit": "^2.27.1",
"@tiptap/vue-3": "^2.27.1",
+ "@vee-validate/zod": "^4.15.1",
"@vueuse/core": "10.11.1",
"@vueuse/math": "10.11.1",
"apexcharts": "3.54.1",
@@ -44,6 +46,7 @@
"swiper": "11.2.10",
"ufo": "1.6.1",
"unplugin-vue-define-options": "1.5.5",
+ "vee-validate": "^4.15.1",
"vue": "3.5.22",
"vue-chartjs": "5.3.2",
"vue-flatpickr-component": "11.0.5",
@@ -53,7 +56,8 @@
"vue3-apexcharts": "1.5.3",
"vue3-perfect-scrollbar": "2.0.0",
"vuetify": "3.10.8",
- "webfontloader": "1.6.28"
+ "webfontloader": "1.6.28",
+ "zod": "3"
},
"devDependencies": {
"@antfu/eslint-config-vue": "0.43.1",
diff --git a/apps/customers/pnpm-lock.yaml b/apps/app/pnpm-lock.yaml
similarity index 99%
rename from apps/customers/pnpm-lock.yaml
rename to apps/app/pnpm-lock.yaml
index c8879c3..2df9ba2 100644
--- a/apps/customers/pnpm-lock.yaml
+++ b/apps/app/pnpm-lock.yaml
@@ -27,6 +27,9 @@ importers:
'@sindresorhus/is':
specifier: 7.1.0
version: 7.1.0
+ '@tanstack/vue-query':
+ specifier: ^5.95.2
+ version: 5.95.2(vue@3.5.22(typescript@5.9.3))
'@tiptap/extension-highlight':
specifier: ^2.27.1
version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))
@@ -48,6 +51,9 @@ importers:
'@tiptap/vue-3':
specifier: ^2.27.1
version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)(vue@3.5.22(typescript@5.9.3))
+ '@vee-validate/zod':
+ specifier: ^4.15.1
+ version: 4.15.1(vue@3.5.22(typescript@5.9.3))(zod@3.25.76)
'@vueuse/core':
specifier: 10.11.1
version: 10.11.1(vue@3.5.22(typescript@5.9.3))
@@ -102,6 +108,9 @@ importers:
unplugin-vue-define-options:
specifier: 1.5.5
version: 1.5.5(vue@3.5.22(typescript@5.9.3))
+ vee-validate:
+ specifier: ^4.15.1
+ version: 4.15.1(vue@3.5.22(typescript@5.9.3))
vue:
specifier: 3.5.22
version: 3.5.22(typescript@5.9.3)
@@ -132,6 +141,9 @@ importers:
webfontloader:
specifier: 1.6.28
version: 1.6.28
+ zod:
+ specifier: '3'
+ version: 3.25.76
devDependencies:
'@antfu/eslint-config-vue':
specifier: 0.43.1
@@ -1025,56 +1037,67 @@ packages:
resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.52.5':
resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
cpu: [arm]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.52.5':
resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.52.5':
resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.52.5':
resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
cpu: [loong64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.52.5':
resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.52.5':
resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.52.5':
resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.52.5':
resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.52.5':
resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.52.5':
resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-openharmony-arm64@4.52.5':
resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
@@ -1160,6 +1183,22 @@ packages:
peerDependencies:
stylelint: ^16.0.2
+ '@tanstack/match-sorter-utils@8.19.4':
+ resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
+ engines: {node: '>=12'}
+
+ '@tanstack/query-core@5.95.2':
+ resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==}
+
+ '@tanstack/vue-query@5.95.2':
+ resolution: {integrity: sha512-GleO0GrUPdvObtff/D3iQ5kUERQM3dM6vT5pWl4zC3ap2JO84x4SQbUa1G7czKx96lETRiHnw7ZuatSRaaZqQQ==}
+ peerDependencies:
+ '@vue/composition-api': ^1.1.2
+ vue: ^2.6.0 || ^3.3.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
'@tiptap/core@2.27.1':
resolution: {integrity: sha512-nkerkl8syHj44ZzAB7oA2GPmmZINKBKCa79FuNvmGJrJ4qyZwlkDzszud23YteFZEytbc87kVd/fP76ROS6sLg==}
peerDependencies:
@@ -1650,41 +1689,49 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -1706,6 +1753,11 @@ packages:
cpu: [x64]
os: [win32]
+ '@vee-validate/zod@4.15.1':
+ resolution: {integrity: sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==}
+ peerDependencies:
+ zod: ^3.24.0
+
'@vitejs/plugin-vue-jsx@5.1.1':
resolution: {integrity: sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -3859,6 +3911,9 @@ packages:
resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==}
hasBin: true
+ remove-accents@0.5.0:
+ resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
+
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -4499,6 +4554,11 @@ packages:
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+ vee-validate@4.15.1:
+ resolution: {integrity: sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==}
+ peerDependencies:
+ vue: ^3.4.26
+
vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
@@ -4801,6 +4861,9 @@ packages:
resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
engines: {node: '>=18'}
+ zod@3.25.76:
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -5690,6 +5753,20 @@ snapshots:
style-search: 0.1.0
stylelint: 16.8.0(typescript@5.9.3)
+ '@tanstack/match-sorter-utils@8.19.4':
+ dependencies:
+ remove-accents: 0.5.0
+
+ '@tanstack/query-core@5.95.2': {}
+
+ '@tanstack/vue-query@5.95.2(vue@3.5.22(typescript@5.9.3))':
+ dependencies:
+ '@tanstack/match-sorter-utils': 8.19.4
+ '@tanstack/query-core': 5.95.2
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.22(typescript@5.9.3)
+ vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3))
+
'@tiptap/core@2.27.1(@tiptap/pm@2.27.1)':
dependencies:
'@tiptap/pm': 2.27.1
@@ -6288,6 +6365,14 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
optional: true
+ '@vee-validate/zod@4.15.1(vue@3.5.22(typescript@5.9.3))(zod@3.25.76)':
+ dependencies:
+ type-fest: 4.41.0
+ vee-validate: 4.15.1(vue@3.5.22(typescript@5.9.3))
+ zod: 3.25.76
+ transitivePeerDependencies:
+ - vue
+
'@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))':
dependencies:
'@babel/core': 7.28.5
@@ -8848,6 +8933,8 @@ snapshots:
dependencies:
jsesc: 0.5.0
+ remove-accents@0.5.0: {}
+
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
@@ -9651,6 +9738,12 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
+ vee-validate@4.15.1(vue@3.5.22(typescript@5.9.3)):
+ dependencies:
+ '@vue/devtools-api': 7.7.7
+ type-fest: 4.41.0
+ vue: 3.5.22(typescript@5.9.3)
+
vfile-message@4.0.3:
dependencies:
'@types/unist': 3.0.3
@@ -9964,4 +10057,6 @@ snapshots:
yoctocolors@2.1.2: {}
+ zod@3.25.76: {}
+
zwitch@2.0.4: {}
diff --git a/apps/band/prod.Dockerfile b/apps/app/prod.Dockerfile
similarity index 100%
rename from apps/band/prod.Dockerfile
rename to apps/app/prod.Dockerfile
diff --git a/apps/band/public/favicon.ico b/apps/app/public/favicon.ico
similarity index 100%
rename from apps/band/public/favicon.ico
rename to apps/app/public/favicon.ico
diff --git a/apps/band/public/images/avatars/avatar-1.png b/apps/app/public/images/avatars/avatar-1.png
similarity index 100%
rename from apps/band/public/images/avatars/avatar-1.png
rename to apps/app/public/images/avatars/avatar-1.png
diff --git a/apps/band/public/images/avatars/avatar-2.png b/apps/app/public/images/avatars/avatar-2.png
similarity index 100%
rename from apps/band/public/images/avatars/avatar-2.png
rename to apps/app/public/images/avatars/avatar-2.png
diff --git a/apps/band/public/images/svg/discord.svg b/apps/app/public/images/svg/discord.svg
similarity index 100%
rename from apps/band/public/images/svg/discord.svg
rename to apps/app/public/images/svg/discord.svg
diff --git a/apps/band/public/images/svg/gift.svg b/apps/app/public/images/svg/gift.svg
similarity index 100%
rename from apps/band/public/images/svg/gift.svg
rename to apps/app/public/images/svg/gift.svg
diff --git a/apps/band/public/images/svg/keyboard.svg b/apps/app/public/images/svg/keyboard.svg
similarity index 100%
rename from apps/band/public/images/svg/keyboard.svg
rename to apps/app/public/images/svg/keyboard.svg
diff --git a/apps/band/public/images/svg/laptop.svg b/apps/app/public/images/svg/laptop.svg
similarity index 100%
rename from apps/band/public/images/svg/laptop.svg
rename to apps/app/public/images/svg/laptop.svg
diff --git a/apps/band/public/images/svg/lightbulb.svg b/apps/app/public/images/svg/lightbulb.svg
similarity index 100%
rename from apps/band/public/images/svg/lightbulb.svg
rename to apps/app/public/images/svg/lightbulb.svg
diff --git a/apps/band/public/images/svg/rocket.svg b/apps/app/public/images/svg/rocket.svg
similarity index 100%
rename from apps/band/public/images/svg/rocket.svg
rename to apps/app/public/images/svg/rocket.svg
diff --git a/apps/band/public/loader.css b/apps/app/public/loader.css
similarity index 100%
rename from apps/band/public/loader.css
rename to apps/app/public/loader.css
diff --git a/apps/band/shims.d.ts b/apps/app/shims.d.ts
similarity index 100%
rename from apps/band/shims.d.ts
rename to apps/app/shims.d.ts
diff --git a/apps/band/src/@core/components/AppBarSearch.vue b/apps/app/src/@core/components/AppBarSearch.vue
similarity index 100%
rename from apps/band/src/@core/components/AppBarSearch.vue
rename to apps/app/src/@core/components/AppBarSearch.vue
diff --git a/apps/band/src/@core/components/AppDrawerHeaderSection.vue b/apps/app/src/@core/components/AppDrawerHeaderSection.vue
similarity index 100%
rename from apps/band/src/@core/components/AppDrawerHeaderSection.vue
rename to apps/app/src/@core/components/AppDrawerHeaderSection.vue
diff --git a/apps/band/src/@core/components/AppStepper.vue b/apps/app/src/@core/components/AppStepper.vue
similarity index 100%
rename from apps/band/src/@core/components/AppStepper.vue
rename to apps/app/src/@core/components/AppStepper.vue
diff --git a/apps/band/src/@core/components/CardStatisticsVerticalSimple.vue b/apps/app/src/@core/components/CardStatisticsVerticalSimple.vue
similarity index 100%
rename from apps/band/src/@core/components/CardStatisticsVerticalSimple.vue
rename to apps/app/src/@core/components/CardStatisticsVerticalSimple.vue
diff --git a/apps/band/src/@core/components/CustomizerSection.vue b/apps/app/src/@core/components/CustomizerSection.vue
similarity index 100%
rename from apps/band/src/@core/components/CustomizerSection.vue
rename to apps/app/src/@core/components/CustomizerSection.vue
diff --git a/apps/band/src/@core/components/DialogCloseBtn.vue b/apps/app/src/@core/components/DialogCloseBtn.vue
similarity index 100%
rename from apps/band/src/@core/components/DialogCloseBtn.vue
rename to apps/app/src/@core/components/DialogCloseBtn.vue
diff --git a/apps/band/src/@core/components/DropZone.vue b/apps/app/src/@core/components/DropZone.vue
similarity index 100%
rename from apps/band/src/@core/components/DropZone.vue
rename to apps/app/src/@core/components/DropZone.vue
diff --git a/apps/band/src/@core/components/I18n.vue b/apps/app/src/@core/components/I18n.vue
similarity index 100%
rename from apps/band/src/@core/components/I18n.vue
rename to apps/app/src/@core/components/I18n.vue
diff --git a/apps/band/src/@core/components/MoreBtn.vue b/apps/app/src/@core/components/MoreBtn.vue
similarity index 100%
rename from apps/band/src/@core/components/MoreBtn.vue
rename to apps/app/src/@core/components/MoreBtn.vue
diff --git a/apps/band/src/@core/components/Notifications.vue b/apps/app/src/@core/components/Notifications.vue
similarity index 100%
rename from apps/band/src/@core/components/Notifications.vue
rename to apps/app/src/@core/components/Notifications.vue
diff --git a/apps/band/src/@core/components/ProductDescriptionEditor.vue b/apps/app/src/@core/components/ProductDescriptionEditor.vue
similarity index 100%
rename from apps/band/src/@core/components/ProductDescriptionEditor.vue
rename to apps/app/src/@core/components/ProductDescriptionEditor.vue
diff --git a/apps/band/src/@core/components/ScrollToTop.vue b/apps/app/src/@core/components/ScrollToTop.vue
similarity index 100%
rename from apps/band/src/@core/components/ScrollToTop.vue
rename to apps/app/src/@core/components/ScrollToTop.vue
diff --git a/apps/band/src/@core/components/Shortcuts.vue b/apps/app/src/@core/components/Shortcuts.vue
similarity index 100%
rename from apps/band/src/@core/components/Shortcuts.vue
rename to apps/app/src/@core/components/Shortcuts.vue
diff --git a/apps/band/src/@core/components/TablePagination.vue b/apps/app/src/@core/components/TablePagination.vue
similarity index 100%
rename from apps/band/src/@core/components/TablePagination.vue
rename to apps/app/src/@core/components/TablePagination.vue
diff --git a/apps/band/src/@core/components/ThemeSwitcher.vue b/apps/app/src/@core/components/ThemeSwitcher.vue
similarity index 100%
rename from apps/band/src/@core/components/ThemeSwitcher.vue
rename to apps/app/src/@core/components/ThemeSwitcher.vue
diff --git a/apps/band/src/@core/components/TiptapEditor.vue b/apps/app/src/@core/components/TiptapEditor.vue
similarity index 100%
rename from apps/band/src/@core/components/TiptapEditor.vue
rename to apps/app/src/@core/components/TiptapEditor.vue
diff --git a/apps/band/src/@core/components/app-form-elements/AppAutocomplete.vue b/apps/app/src/@core/components/app-form-elements/AppAutocomplete.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/AppAutocomplete.vue
rename to apps/app/src/@core/components/app-form-elements/AppAutocomplete.vue
diff --git a/apps/band/src/@core/components/app-form-elements/AppCombobox.vue b/apps/app/src/@core/components/app-form-elements/AppCombobox.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/AppCombobox.vue
rename to apps/app/src/@core/components/app-form-elements/AppCombobox.vue
diff --git a/apps/band/src/@core/components/app-form-elements/AppDateTimePicker.vue b/apps/app/src/@core/components/app-form-elements/AppDateTimePicker.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/AppDateTimePicker.vue
rename to apps/app/src/@core/components/app-form-elements/AppDateTimePicker.vue
diff --git a/apps/band/src/@core/components/app-form-elements/AppSelect.vue b/apps/app/src/@core/components/app-form-elements/AppSelect.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/AppSelect.vue
rename to apps/app/src/@core/components/app-form-elements/AppSelect.vue
diff --git a/apps/band/src/@core/components/app-form-elements/AppTextField.vue b/apps/app/src/@core/components/app-form-elements/AppTextField.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/AppTextField.vue
rename to apps/app/src/@core/components/app-form-elements/AppTextField.vue
diff --git a/apps/band/src/@core/components/app-form-elements/AppTextarea.vue b/apps/app/src/@core/components/app-form-elements/AppTextarea.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/AppTextarea.vue
rename to apps/app/src/@core/components/app-form-elements/AppTextarea.vue
diff --git a/apps/band/src/@core/components/app-form-elements/CustomCheckboxes.vue b/apps/app/src/@core/components/app-form-elements/CustomCheckboxes.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/CustomCheckboxes.vue
rename to apps/app/src/@core/components/app-form-elements/CustomCheckboxes.vue
diff --git a/apps/band/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue b/apps/app/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue
rename to apps/app/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue
diff --git a/apps/band/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue b/apps/app/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue
rename to apps/app/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue
diff --git a/apps/band/src/@core/components/app-form-elements/CustomRadios.vue b/apps/app/src/@core/components/app-form-elements/CustomRadios.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/CustomRadios.vue
rename to apps/app/src/@core/components/app-form-elements/CustomRadios.vue
diff --git a/apps/band/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue b/apps/app/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue
rename to apps/app/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue
diff --git a/apps/band/src/@core/components/app-form-elements/CustomRadiosWithImage.vue b/apps/app/src/@core/components/app-form-elements/CustomRadiosWithImage.vue
similarity index 100%
rename from apps/band/src/@core/components/app-form-elements/CustomRadiosWithImage.vue
rename to apps/app/src/@core/components/app-form-elements/CustomRadiosWithImage.vue
diff --git a/apps/band/src/@core/components/cards/AppCardActions.vue b/apps/app/src/@core/components/cards/AppCardActions.vue
similarity index 100%
rename from apps/band/src/@core/components/cards/AppCardActions.vue
rename to apps/app/src/@core/components/cards/AppCardActions.vue
diff --git a/apps/band/src/@core/components/cards/AppCardCode.vue b/apps/app/src/@core/components/cards/AppCardCode.vue
similarity index 100%
rename from apps/band/src/@core/components/cards/AppCardCode.vue
rename to apps/app/src/@core/components/cards/AppCardCode.vue
diff --git a/apps/band/src/@core/components/cards/CardStatisticsHorizontal.vue b/apps/app/src/@core/components/cards/CardStatisticsHorizontal.vue
similarity index 100%
rename from apps/band/src/@core/components/cards/CardStatisticsHorizontal.vue
rename to apps/app/src/@core/components/cards/CardStatisticsHorizontal.vue
diff --git a/apps/band/src/@core/components/cards/CardStatisticsVertical.vue b/apps/app/src/@core/components/cards/CardStatisticsVertical.vue
similarity index 100%
rename from apps/band/src/@core/components/cards/CardStatisticsVertical.vue
rename to apps/app/src/@core/components/cards/CardStatisticsVertical.vue
diff --git a/apps/band/src/@core/composable/createUrl.ts b/apps/app/src/@core/composable/createUrl.ts
similarity index 100%
rename from apps/band/src/@core/composable/createUrl.ts
rename to apps/app/src/@core/composable/createUrl.ts
diff --git a/apps/band/src/@core/composable/useCookie.ts b/apps/app/src/@core/composable/useCookie.ts
similarity index 100%
rename from apps/band/src/@core/composable/useCookie.ts
rename to apps/app/src/@core/composable/useCookie.ts
diff --git a/apps/band/src/@core/composable/useGenerateImageVariant.ts b/apps/app/src/@core/composable/useGenerateImageVariant.ts
similarity index 100%
rename from apps/band/src/@core/composable/useGenerateImageVariant.ts
rename to apps/app/src/@core/composable/useGenerateImageVariant.ts
diff --git a/apps/band/src/@core/composable/useResponsiveSidebar.ts b/apps/app/src/@core/composable/useResponsiveSidebar.ts
similarity index 100%
rename from apps/band/src/@core/composable/useResponsiveSidebar.ts
rename to apps/app/src/@core/composable/useResponsiveSidebar.ts
diff --git a/apps/band/src/@core/composable/useSkins.ts b/apps/app/src/@core/composable/useSkins.ts
similarity index 100%
rename from apps/band/src/@core/composable/useSkins.ts
rename to apps/app/src/@core/composable/useSkins.ts
diff --git a/apps/band/src/@core/enums.ts b/apps/app/src/@core/enums.ts
similarity index 100%
rename from apps/band/src/@core/enums.ts
rename to apps/app/src/@core/enums.ts
diff --git a/apps/band/src/@core/index.ts b/apps/app/src/@core/index.ts
similarity index 100%
rename from apps/band/src/@core/index.ts
rename to apps/app/src/@core/index.ts
diff --git a/apps/band/src/@core/initCore.ts b/apps/app/src/@core/initCore.ts
similarity index 100%
rename from apps/band/src/@core/initCore.ts
rename to apps/app/src/@core/initCore.ts
diff --git a/apps/band/src/@core/libs/apex-chart/apexCharConfig.ts b/apps/app/src/@core/libs/apex-chart/apexCharConfig.ts
similarity index 100%
rename from apps/band/src/@core/libs/apex-chart/apexCharConfig.ts
rename to apps/app/src/@core/libs/apex-chart/apexCharConfig.ts
diff --git a/apps/band/src/@core/libs/chartjs/chartjsConfig.ts b/apps/app/src/@core/libs/chartjs/chartjsConfig.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/chartjsConfig.ts
rename to apps/app/src/@core/libs/chartjs/chartjsConfig.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/BarChart.ts b/apps/app/src/@core/libs/chartjs/components/BarChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/BarChart.ts
rename to apps/app/src/@core/libs/chartjs/components/BarChart.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/BubbleChart.ts b/apps/app/src/@core/libs/chartjs/components/BubbleChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/BubbleChart.ts
rename to apps/app/src/@core/libs/chartjs/components/BubbleChart.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/DoughnutChart.ts b/apps/app/src/@core/libs/chartjs/components/DoughnutChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/DoughnutChart.ts
rename to apps/app/src/@core/libs/chartjs/components/DoughnutChart.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/LineChart.ts b/apps/app/src/@core/libs/chartjs/components/LineChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/LineChart.ts
rename to apps/app/src/@core/libs/chartjs/components/LineChart.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/PolarAreaChart.ts b/apps/app/src/@core/libs/chartjs/components/PolarAreaChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/PolarAreaChart.ts
rename to apps/app/src/@core/libs/chartjs/components/PolarAreaChart.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/RadarChart.ts b/apps/app/src/@core/libs/chartjs/components/RadarChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/RadarChart.ts
rename to apps/app/src/@core/libs/chartjs/components/RadarChart.ts
diff --git a/apps/band/src/@core/libs/chartjs/components/ScatterChart.ts b/apps/app/src/@core/libs/chartjs/components/ScatterChart.ts
similarity index 100%
rename from apps/band/src/@core/libs/chartjs/components/ScatterChart.ts
rename to apps/app/src/@core/libs/chartjs/components/ScatterChart.ts
diff --git a/apps/band/src/@core/scss/base/_components.scss b/apps/app/src/@core/scss/base/_components.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_components.scss
rename to apps/app/src/@core/scss/base/_components.scss
diff --git a/apps/band/src/@core/scss/base/_dark.scss b/apps/app/src/@core/scss/base/_dark.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_dark.scss
rename to apps/app/src/@core/scss/base/_dark.scss
diff --git a/apps/band/src/@core/scss/base/_default-layout-w-horizontal-nav.scss b/apps/app/src/@core/scss/base/_default-layout-w-horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_default-layout-w-horizontal-nav.scss
rename to apps/app/src/@core/scss/base/_default-layout-w-horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/base/_default-layout-w-vertical-nav.scss b/apps/app/src/@core/scss/base/_default-layout-w-vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_default-layout-w-vertical-nav.scss
rename to apps/app/src/@core/scss/base/_default-layout-w-vertical-nav.scss
diff --git a/apps/band/src/@core/scss/base/_default-layout.scss b/apps/app/src/@core/scss/base/_default-layout.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_default-layout.scss
rename to apps/app/src/@core/scss/base/_default-layout.scss
diff --git a/apps/band/src/@core/scss/base/_horizontal-nav.scss b/apps/app/src/@core/scss/base/_horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_horizontal-nav.scss
rename to apps/app/src/@core/scss/base/_horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/base/_index.scss b/apps/app/src/@core/scss/base/_index.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_index.scss
rename to apps/app/src/@core/scss/base/_index.scss
diff --git a/apps/band/src/@core/scss/base/_layouts.scss b/apps/app/src/@core/scss/base/_layouts.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_layouts.scss
rename to apps/app/src/@core/scss/base/_layouts.scss
diff --git a/apps/band/src/@core/scss/base/_misc.scss b/apps/app/src/@core/scss/base/_misc.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_misc.scss
rename to apps/app/src/@core/scss/base/_misc.scss
diff --git a/apps/band/src/@core/scss/base/_mixins.scss b/apps/app/src/@core/scss/base/_mixins.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_mixins.scss
rename to apps/app/src/@core/scss/base/_mixins.scss
diff --git a/apps/band/src/@core/scss/base/_route-transitions.scss b/apps/app/src/@core/scss/base/_route-transitions.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_route-transitions.scss
rename to apps/app/src/@core/scss/base/_route-transitions.scss
diff --git a/apps/band/src/@core/scss/base/_utilities.scss b/apps/app/src/@core/scss/base/_utilities.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_utilities.scss
rename to apps/app/src/@core/scss/base/_utilities.scss
diff --git a/apps/band/src/@core/scss/base/_utils.scss b/apps/app/src/@core/scss/base/_utils.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_utils.scss
rename to apps/app/src/@core/scss/base/_utils.scss
diff --git a/apps/band/src/@core/scss/base/_variables.scss b/apps/app/src/@core/scss/base/_variables.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_variables.scss
rename to apps/app/src/@core/scss/base/_variables.scss
diff --git a/apps/band/src/@core/scss/base/_vertical-nav.scss b/apps/app/src/@core/scss/base/_vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/_vertical-nav.scss
rename to apps/app/src/@core/scss/base/_vertical-nav.scss
diff --git a/apps/band/src/@core/scss/base/libs/_perfect-scrollbar.scss b/apps/app/src/@core/scss/base/libs/_perfect-scrollbar.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/libs/_perfect-scrollbar.scss
rename to apps/app/src/@core/scss/base/libs/_perfect-scrollbar.scss
diff --git a/apps/band/src/@core/scss/base/libs/vuetify/_index.scss b/apps/app/src/@core/scss/base/libs/vuetify/_index.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/libs/vuetify/_index.scss
rename to apps/app/src/@core/scss/base/libs/vuetify/_index.scss
diff --git a/apps/band/src/@core/scss/base/libs/vuetify/_overrides.scss b/apps/app/src/@core/scss/base/libs/vuetify/_overrides.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/libs/vuetify/_overrides.scss
rename to apps/app/src/@core/scss/base/libs/vuetify/_overrides.scss
diff --git a/apps/band/src/@core/scss/base/libs/vuetify/_variables.scss b/apps/app/src/@core/scss/base/libs/vuetify/_variables.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/libs/vuetify/_variables.scss
rename to apps/app/src/@core/scss/base/libs/vuetify/_variables.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss b/apps/app/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss
rename to apps/app/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss b/apps/app/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss
rename to apps/app/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_default-layout.scss b/apps/app/src/@core/scss/base/placeholders/_default-layout.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_default-layout.scss
rename to apps/app/src/@core/scss/base/placeholders/_default-layout.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_horizontal-nav.scss b/apps/app/src/@core/scss/base/placeholders/_horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_horizontal-nav.scss
rename to apps/app/src/@core/scss/base/placeholders/_horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_index.scss b/apps/app/src/@core/scss/base/placeholders/_index.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_index.scss
rename to apps/app/src/@core/scss/base/placeholders/_index.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_misc.scss b/apps/app/src/@core/scss/base/placeholders/_misc.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_misc.scss
rename to apps/app/src/@core/scss/base/placeholders/_misc.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_nav.scss b/apps/app/src/@core/scss/base/placeholders/_nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_nav.scss
rename to apps/app/src/@core/scss/base/placeholders/_nav.scss
diff --git a/apps/band/src/@core/scss/base/placeholders/_vertical-nav.scss b/apps/app/src/@core/scss/base/placeholders/_vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/placeholders/_vertical-nav.scss
rename to apps/app/src/@core/scss/base/placeholders/_vertical-nav.scss
diff --git a/apps/band/src/@core/scss/base/skins/_bordered.scss b/apps/app/src/@core/scss/base/skins/_bordered.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/skins/_bordered.scss
rename to apps/app/src/@core/scss/base/skins/_bordered.scss
diff --git a/apps/band/src/@core/scss/base/skins/_index.scss b/apps/app/src/@core/scss/base/skins/_index.scss
similarity index 100%
rename from apps/band/src/@core/scss/base/skins/_index.scss
rename to apps/app/src/@core/scss/base/skins/_index.scss
diff --git a/apps/band/src/@core/scss/template/_default-layout-w-horizontal-nav.scss b/apps/app/src/@core/scss/template/_default-layout-w-horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_default-layout-w-horizontal-nav.scss
rename to apps/app/src/@core/scss/template/_default-layout-w-horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/template/_default-layout-w-vertical-nav.scss b/apps/app/src/@core/scss/template/_default-layout-w-vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_default-layout-w-vertical-nav.scss
rename to apps/app/src/@core/scss/template/_default-layout-w-vertical-nav.scss
diff --git a/apps/band/src/@core/scss/template/_horizontal-nav.scss b/apps/app/src/@core/scss/template/_horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_horizontal-nav.scss
rename to apps/app/src/@core/scss/template/_horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/template/_mixins.scss b/apps/app/src/@core/scss/template/_mixins.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_mixins.scss
rename to apps/app/src/@core/scss/template/_mixins.scss
diff --git a/apps/band/src/@core/scss/template/_utilities.scss b/apps/app/src/@core/scss/template/_utilities.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_utilities.scss
rename to apps/app/src/@core/scss/template/_utilities.scss
diff --git a/apps/band/src/@core/scss/template/_variables.scss b/apps/app/src/@core/scss/template/_variables.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_variables.scss
rename to apps/app/src/@core/scss/template/_variables.scss
diff --git a/apps/band/src/@core/scss/template/_vertical-nav.scss b/apps/app/src/@core/scss/template/_vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/_vertical-nav.scss
rename to apps/app/src/@core/scss/template/_vertical-nav.scss
diff --git a/apps/band/src/@core/scss/template/index.scss b/apps/app/src/@core/scss/template/index.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/index.scss
rename to apps/app/src/@core/scss/template/index.scss
diff --git a/apps/band/src/@core/scss/template/libs/apex-chart.scss b/apps/app/src/@core/scss/template/libs/apex-chart.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/apex-chart.scss
rename to apps/app/src/@core/scss/template/libs/apex-chart.scss
diff --git a/apps/band/src/@core/scss/template/libs/full-calendar.scss b/apps/app/src/@core/scss/template/libs/full-calendar.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/full-calendar.scss
rename to apps/app/src/@core/scss/template/libs/full-calendar.scss
diff --git a/apps/band/src/@core/scss/template/libs/shepherd.scss b/apps/app/src/@core/scss/template/libs/shepherd.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/shepherd.scss
rename to apps/app/src/@core/scss/template/libs/shepherd.scss
diff --git a/apps/band/src/@core/scss/template/libs/swiper.scss b/apps/app/src/@core/scss/template/libs/swiper.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/swiper.scss
rename to apps/app/src/@core/scss/template/libs/swiper.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/_variables.scss b/apps/app/src/@core/scss/template/libs/vuetify/_variables.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/_variables.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/_variables.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_alert.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_alert.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_alert.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_alert.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_avatar.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_avatar.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_avatar.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_avatar.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_badge.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_badge.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_badge.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_badge.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_button.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_button.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_button.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_button.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_cards.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_cards.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_cards.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_cards.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_checkbox.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_checkbox.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_checkbox.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_checkbox.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_chip.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_chip.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_chip.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_chip.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_dialog.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_dialog.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_dialog.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_dialog.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_field.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_field.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_field.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_field.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_list.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_list.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_list.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_list.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_menu.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_menu.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_menu.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_menu.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_otp-input.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_otp-input.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_otp-input.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_otp-input.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_pagination.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_pagination.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_pagination.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_pagination.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_progress.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_progress.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_progress.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_progress.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_radio.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_radio.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_radio.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_radio.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_rating.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_rating.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_rating.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_rating.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_slider.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_slider.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_slider.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_slider.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_snackbar.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_snackbar.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_snackbar.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_snackbar.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_switch.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_switch.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_switch.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_switch.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_table.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_table.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_table.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_table.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_tabs.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_tabs.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_tabs.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_tabs.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_textarea.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_textarea.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_textarea.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_textarea.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_timeline.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_timeline.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_timeline.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_timeline.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/_tooltip.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/_tooltip.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/_tooltip.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/_tooltip.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/components/index.scss b/apps/app/src/@core/scss/template/libs/vuetify/components/index.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/components/index.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/components/index.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/index.scss b/apps/app/src/@core/scss/template/libs/vuetify/index.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/index.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/index.scss
diff --git a/apps/band/src/@core/scss/template/libs/vuetify/overrides.scss b/apps/app/src/@core/scss/template/libs/vuetify/overrides.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/libs/vuetify/overrides.scss
rename to apps/app/src/@core/scss/template/libs/vuetify/overrides.scss
diff --git a/apps/band/src/@core/scss/template/pages/misc.scss b/apps/app/src/@core/scss/template/pages/misc.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/pages/misc.scss
rename to apps/app/src/@core/scss/template/pages/misc.scss
diff --git a/apps/band/src/@core/scss/template/pages/page-auth.scss b/apps/app/src/@core/scss/template/pages/page-auth.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/pages/page-auth.scss
rename to apps/app/src/@core/scss/template/pages/page-auth.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss b/apps/app/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss
rename to apps/app/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss b/apps/app/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss
rename to apps/app/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_horizontal-nav.scss b/apps/app/src/@core/scss/template/placeholders/_horizontal-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_horizontal-nav.scss
rename to apps/app/src/@core/scss/template/placeholders/_horizontal-nav.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_index.scss b/apps/app/src/@core/scss/template/placeholders/_index.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_index.scss
rename to apps/app/src/@core/scss/template/placeholders/_index.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_misc.scss b/apps/app/src/@core/scss/template/placeholders/_misc.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_misc.scss
rename to apps/app/src/@core/scss/template/placeholders/_misc.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_nav.scss b/apps/app/src/@core/scss/template/placeholders/_nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_nav.scss
rename to apps/app/src/@core/scss/template/placeholders/_nav.scss
diff --git a/apps/band/src/@core/scss/template/placeholders/_vertical-nav.scss b/apps/app/src/@core/scss/template/placeholders/_vertical-nav.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/placeholders/_vertical-nav.scss
rename to apps/app/src/@core/scss/template/placeholders/_vertical-nav.scss
diff --git a/apps/band/src/@core/scss/template/skins/_bordered.scss b/apps/app/src/@core/scss/template/skins/_bordered.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/skins/_bordered.scss
rename to apps/app/src/@core/scss/template/skins/_bordered.scss
diff --git a/apps/band/src/@core/scss/template/skins/_index.scss b/apps/app/src/@core/scss/template/skins/_index.scss
similarity index 100%
rename from apps/band/src/@core/scss/template/skins/_index.scss
rename to apps/app/src/@core/scss/template/skins/_index.scss
diff --git a/apps/band/src/@core/stores/config.ts b/apps/app/src/@core/stores/config.ts
similarity index 100%
rename from apps/band/src/@core/stores/config.ts
rename to apps/app/src/@core/stores/config.ts
diff --git a/apps/band/src/@core/types.ts b/apps/app/src/@core/types.ts
similarity index 100%
rename from apps/band/src/@core/types.ts
rename to apps/app/src/@core/types.ts
diff --git a/apps/band/src/@core/utils/colorConverter.ts b/apps/app/src/@core/utils/colorConverter.ts
similarity index 100%
rename from apps/band/src/@core/utils/colorConverter.ts
rename to apps/app/src/@core/utils/colorConverter.ts
diff --git a/apps/band/src/@core/utils/formatters.ts b/apps/app/src/@core/utils/formatters.ts
similarity index 100%
rename from apps/band/src/@core/utils/formatters.ts
rename to apps/app/src/@core/utils/formatters.ts
diff --git a/apps/band/src/@core/utils/helpers.ts b/apps/app/src/@core/utils/helpers.ts
similarity index 100%
rename from apps/band/src/@core/utils/helpers.ts
rename to apps/app/src/@core/utils/helpers.ts
diff --git a/apps/band/src/@core/utils/plugins.ts b/apps/app/src/@core/utils/plugins.ts
similarity index 100%
rename from apps/band/src/@core/utils/plugins.ts
rename to apps/app/src/@core/utils/plugins.ts
diff --git a/apps/band/src/@core/utils/validators.ts b/apps/app/src/@core/utils/validators.ts
similarity index 100%
rename from apps/band/src/@core/utils/validators.ts
rename to apps/app/src/@core/utils/validators.ts
diff --git a/apps/band/src/@core/utils/vuetify.ts b/apps/app/src/@core/utils/vuetify.ts
similarity index 100%
rename from apps/band/src/@core/utils/vuetify.ts
rename to apps/app/src/@core/utils/vuetify.ts
diff --git a/apps/band/src/@layouts/components.ts b/apps/app/src/@layouts/components.ts
similarity index 100%
rename from apps/band/src/@layouts/components.ts
rename to apps/app/src/@layouts/components.ts
diff --git a/apps/band/src/@layouts/components/HorizontalNav.vue b/apps/app/src/@layouts/components/HorizontalNav.vue
similarity index 100%
rename from apps/band/src/@layouts/components/HorizontalNav.vue
rename to apps/app/src/@layouts/components/HorizontalNav.vue
diff --git a/apps/band/src/@layouts/components/HorizontalNavGroup.vue b/apps/app/src/@layouts/components/HorizontalNavGroup.vue
similarity index 100%
rename from apps/band/src/@layouts/components/HorizontalNavGroup.vue
rename to apps/app/src/@layouts/components/HorizontalNavGroup.vue
diff --git a/apps/band/src/@layouts/components/HorizontalNavLayout.vue b/apps/app/src/@layouts/components/HorizontalNavLayout.vue
similarity index 100%
rename from apps/band/src/@layouts/components/HorizontalNavLayout.vue
rename to apps/app/src/@layouts/components/HorizontalNavLayout.vue
diff --git a/apps/band/src/@layouts/components/HorizontalNavLink.vue b/apps/app/src/@layouts/components/HorizontalNavLink.vue
similarity index 100%
rename from apps/band/src/@layouts/components/HorizontalNavLink.vue
rename to apps/app/src/@layouts/components/HorizontalNavLink.vue
diff --git a/apps/band/src/@layouts/components/HorizontalNavPopper.vue b/apps/app/src/@layouts/components/HorizontalNavPopper.vue
similarity index 100%
rename from apps/band/src/@layouts/components/HorizontalNavPopper.vue
rename to apps/app/src/@layouts/components/HorizontalNavPopper.vue
diff --git a/apps/band/src/@layouts/components/TransitionExpand.vue b/apps/app/src/@layouts/components/TransitionExpand.vue
similarity index 100%
rename from apps/band/src/@layouts/components/TransitionExpand.vue
rename to apps/app/src/@layouts/components/TransitionExpand.vue
diff --git a/apps/band/src/@layouts/components/VNodeRenderer.tsx b/apps/app/src/@layouts/components/VNodeRenderer.tsx
similarity index 100%
rename from apps/band/src/@layouts/components/VNodeRenderer.tsx
rename to apps/app/src/@layouts/components/VNodeRenderer.tsx
diff --git a/apps/band/src/@layouts/components/VerticalNav.vue b/apps/app/src/@layouts/components/VerticalNav.vue
similarity index 100%
rename from apps/band/src/@layouts/components/VerticalNav.vue
rename to apps/app/src/@layouts/components/VerticalNav.vue
diff --git a/apps/band/src/@layouts/components/VerticalNavGroup.vue b/apps/app/src/@layouts/components/VerticalNavGroup.vue
similarity index 100%
rename from apps/band/src/@layouts/components/VerticalNavGroup.vue
rename to apps/app/src/@layouts/components/VerticalNavGroup.vue
diff --git a/apps/band/src/@layouts/components/VerticalNavLayout.vue b/apps/app/src/@layouts/components/VerticalNavLayout.vue
similarity index 100%
rename from apps/band/src/@layouts/components/VerticalNavLayout.vue
rename to apps/app/src/@layouts/components/VerticalNavLayout.vue
diff --git a/apps/band/src/@layouts/components/VerticalNavLink.vue b/apps/app/src/@layouts/components/VerticalNavLink.vue
similarity index 100%
rename from apps/band/src/@layouts/components/VerticalNavLink.vue
rename to apps/app/src/@layouts/components/VerticalNavLink.vue
diff --git a/apps/band/src/@layouts/components/VerticalNavSectionTitle.vue b/apps/app/src/@layouts/components/VerticalNavSectionTitle.vue
similarity index 100%
rename from apps/band/src/@layouts/components/VerticalNavSectionTitle.vue
rename to apps/app/src/@layouts/components/VerticalNavSectionTitle.vue
diff --git a/apps/band/src/@layouts/config.ts b/apps/app/src/@layouts/config.ts
similarity index 100%
rename from apps/band/src/@layouts/config.ts
rename to apps/app/src/@layouts/config.ts
diff --git a/apps/band/src/@layouts/enums.ts b/apps/app/src/@layouts/enums.ts
similarity index 100%
rename from apps/band/src/@layouts/enums.ts
rename to apps/app/src/@layouts/enums.ts
diff --git a/apps/band/src/@layouts/index.ts b/apps/app/src/@layouts/index.ts
similarity index 100%
rename from apps/band/src/@layouts/index.ts
rename to apps/app/src/@layouts/index.ts
diff --git a/apps/band/src/@layouts/plugins/casl.ts b/apps/app/src/@layouts/plugins/casl.ts
similarity index 100%
rename from apps/band/src/@layouts/plugins/casl.ts
rename to apps/app/src/@layouts/plugins/casl.ts
diff --git a/apps/band/src/@layouts/stores/config.ts b/apps/app/src/@layouts/stores/config.ts
similarity index 100%
rename from apps/band/src/@layouts/stores/config.ts
rename to apps/app/src/@layouts/stores/config.ts
diff --git a/apps/band/src/@layouts/styles/_classes.scss b/apps/app/src/@layouts/styles/_classes.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_classes.scss
rename to apps/app/src/@layouts/styles/_classes.scss
diff --git a/apps/band/src/@layouts/styles/_default-layout.scss b/apps/app/src/@layouts/styles/_default-layout.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_default-layout.scss
rename to apps/app/src/@layouts/styles/_default-layout.scss
diff --git a/apps/band/src/@layouts/styles/_global.scss b/apps/app/src/@layouts/styles/_global.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_global.scss
rename to apps/app/src/@layouts/styles/_global.scss
diff --git a/apps/band/src/@layouts/styles/_mixins.scss b/apps/app/src/@layouts/styles/_mixins.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_mixins.scss
rename to apps/app/src/@layouts/styles/_mixins.scss
diff --git a/apps/band/src/@layouts/styles/_placeholders.scss b/apps/app/src/@layouts/styles/_placeholders.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_placeholders.scss
rename to apps/app/src/@layouts/styles/_placeholders.scss
diff --git a/apps/band/src/@layouts/styles/_rtl.scss b/apps/app/src/@layouts/styles/_rtl.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_rtl.scss
rename to apps/app/src/@layouts/styles/_rtl.scss
diff --git a/apps/band/src/@layouts/styles/_variables.scss b/apps/app/src/@layouts/styles/_variables.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/_variables.scss
rename to apps/app/src/@layouts/styles/_variables.scss
diff --git a/apps/band/src/@layouts/styles/index.scss b/apps/app/src/@layouts/styles/index.scss
similarity index 100%
rename from apps/band/src/@layouts/styles/index.scss
rename to apps/app/src/@layouts/styles/index.scss
diff --git a/apps/band/src/@layouts/symbols.ts b/apps/app/src/@layouts/symbols.ts
similarity index 100%
rename from apps/band/src/@layouts/symbols.ts
rename to apps/app/src/@layouts/symbols.ts
diff --git a/apps/band/src/@layouts/types.ts b/apps/app/src/@layouts/types.ts
similarity index 100%
rename from apps/band/src/@layouts/types.ts
rename to apps/app/src/@layouts/types.ts
diff --git a/apps/band/src/@layouts/utils.ts b/apps/app/src/@layouts/utils.ts
similarity index 100%
rename from apps/band/src/@layouts/utils.ts
rename to apps/app/src/@layouts/utils.ts
diff --git a/apps/band/src/App.vue b/apps/app/src/App.vue
similarity index 100%
rename from apps/band/src/App.vue
rename to apps/app/src/App.vue
diff --git a/apps/band/src/assets/images/avatars/avatar-1.png b/apps/app/src/assets/images/avatars/avatar-1.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-1.png
rename to apps/app/src/assets/images/avatars/avatar-1.png
diff --git a/apps/band/src/assets/images/avatars/avatar-10.png b/apps/app/src/assets/images/avatars/avatar-10.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-10.png
rename to apps/app/src/assets/images/avatars/avatar-10.png
diff --git a/apps/band/src/assets/images/avatars/avatar-11.png b/apps/app/src/assets/images/avatars/avatar-11.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-11.png
rename to apps/app/src/assets/images/avatars/avatar-11.png
diff --git a/apps/band/src/assets/images/avatars/avatar-12.png b/apps/app/src/assets/images/avatars/avatar-12.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-12.png
rename to apps/app/src/assets/images/avatars/avatar-12.png
diff --git a/apps/band/src/assets/images/avatars/avatar-13.png b/apps/app/src/assets/images/avatars/avatar-13.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-13.png
rename to apps/app/src/assets/images/avatars/avatar-13.png
diff --git a/apps/band/src/assets/images/avatars/avatar-14.png b/apps/app/src/assets/images/avatars/avatar-14.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-14.png
rename to apps/app/src/assets/images/avatars/avatar-14.png
diff --git a/apps/band/src/assets/images/avatars/avatar-15.png b/apps/app/src/assets/images/avatars/avatar-15.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-15.png
rename to apps/app/src/assets/images/avatars/avatar-15.png
diff --git a/apps/band/src/assets/images/avatars/avatar-2.png b/apps/app/src/assets/images/avatars/avatar-2.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-2.png
rename to apps/app/src/assets/images/avatars/avatar-2.png
diff --git a/apps/band/src/assets/images/avatars/avatar-3.png b/apps/app/src/assets/images/avatars/avatar-3.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-3.png
rename to apps/app/src/assets/images/avatars/avatar-3.png
diff --git a/apps/band/src/assets/images/avatars/avatar-4.png b/apps/app/src/assets/images/avatars/avatar-4.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-4.png
rename to apps/app/src/assets/images/avatars/avatar-4.png
diff --git a/apps/band/src/assets/images/avatars/avatar-5.png b/apps/app/src/assets/images/avatars/avatar-5.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-5.png
rename to apps/app/src/assets/images/avatars/avatar-5.png
diff --git a/apps/band/src/assets/images/avatars/avatar-6.png b/apps/app/src/assets/images/avatars/avatar-6.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-6.png
rename to apps/app/src/assets/images/avatars/avatar-6.png
diff --git a/apps/band/src/assets/images/avatars/avatar-7.png b/apps/app/src/assets/images/avatars/avatar-7.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-7.png
rename to apps/app/src/assets/images/avatars/avatar-7.png
diff --git a/apps/band/src/assets/images/avatars/avatar-8.png b/apps/app/src/assets/images/avatars/avatar-8.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-8.png
rename to apps/app/src/assets/images/avatars/avatar-8.png
diff --git a/apps/band/src/assets/images/avatars/avatar-9.png b/apps/app/src/assets/images/avatars/avatar-9.png
similarity index 100%
rename from apps/band/src/assets/images/avatars/avatar-9.png
rename to apps/app/src/assets/images/avatars/avatar-9.png
diff --git a/apps/band/src/assets/images/cards/logo-mastercard-small.png b/apps/app/src/assets/images/cards/logo-mastercard-small.png
similarity index 100%
rename from apps/band/src/assets/images/cards/logo-mastercard-small.png
rename to apps/app/src/assets/images/cards/logo-mastercard-small.png
diff --git a/apps/band/src/assets/images/cards/paypal-primary.png b/apps/app/src/assets/images/cards/paypal-primary.png
similarity index 100%
rename from apps/band/src/assets/images/cards/paypal-primary.png
rename to apps/app/src/assets/images/cards/paypal-primary.png
diff --git a/apps/band/src/assets/images/cards/paypal-rounded.png b/apps/app/src/assets/images/cards/paypal-rounded.png
similarity index 100%
rename from apps/band/src/assets/images/cards/paypal-rounded.png
rename to apps/app/src/assets/images/cards/paypal-rounded.png
diff --git a/apps/band/src/assets/images/customizer-icons/border-light.svg b/apps/app/src/assets/images/customizer-icons/border-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/border-light.svg
rename to apps/app/src/assets/images/customizer-icons/border-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/collapsed-light.svg b/apps/app/src/assets/images/customizer-icons/collapsed-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/collapsed-light.svg
rename to apps/app/src/assets/images/customizer-icons/collapsed-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/compact-light.svg b/apps/app/src/assets/images/customizer-icons/compact-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/compact-light.svg
rename to apps/app/src/assets/images/customizer-icons/compact-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/default-light.svg b/apps/app/src/assets/images/customizer-icons/default-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/default-light.svg
rename to apps/app/src/assets/images/customizer-icons/default-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/horizontal-light.svg b/apps/app/src/assets/images/customizer-icons/horizontal-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/horizontal-light.svg
rename to apps/app/src/assets/images/customizer-icons/horizontal-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/ltr-light.svg b/apps/app/src/assets/images/customizer-icons/ltr-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/ltr-light.svg
rename to apps/app/src/assets/images/customizer-icons/ltr-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/rtl-light.svg b/apps/app/src/assets/images/customizer-icons/rtl-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/rtl-light.svg
rename to apps/app/src/assets/images/customizer-icons/rtl-light.svg
diff --git a/apps/band/src/assets/images/customizer-icons/wide-light.svg b/apps/app/src/assets/images/customizer-icons/wide-light.svg
similarity index 100%
rename from apps/band/src/assets/images/customizer-icons/wide-light.svg
rename to apps/app/src/assets/images/customizer-icons/wide-light.svg
diff --git a/apps/band/src/assets/images/icons/payments/ae-icon.png b/apps/app/src/assets/images/icons/payments/ae-icon.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/ae-icon.png
rename to apps/app/src/assets/images/icons/payments/ae-icon.png
diff --git a/apps/band/src/assets/images/icons/payments/american-express.png b/apps/app/src/assets/images/icons/payments/american-express.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/american-express.png
rename to apps/app/src/assets/images/icons/payments/american-express.png
diff --git a/apps/band/src/assets/images/icons/payments/img/ae-dark.png b/apps/app/src/assets/images/icons/payments/img/ae-dark.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/ae-dark.png
rename to apps/app/src/assets/images/icons/payments/img/ae-dark.png
diff --git a/apps/band/src/assets/images/icons/payments/img/american-express.png b/apps/app/src/assets/images/icons/payments/img/american-express.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/american-express.png
rename to apps/app/src/assets/images/icons/payments/img/american-express.png
diff --git a/apps/band/src/assets/images/icons/payments/img/dc-dark.png b/apps/app/src/assets/images/icons/payments/img/dc-dark.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/dc-dark.png
rename to apps/app/src/assets/images/icons/payments/img/dc-dark.png
diff --git a/apps/band/src/assets/images/icons/payments/img/dc-light.png b/apps/app/src/assets/images/icons/payments/img/dc-light.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/dc-light.png
rename to apps/app/src/assets/images/icons/payments/img/dc-light.png
diff --git a/apps/band/src/assets/images/icons/payments/img/jcb-dark.png b/apps/app/src/assets/images/icons/payments/img/jcb-dark.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/jcb-dark.png
rename to apps/app/src/assets/images/icons/payments/img/jcb-dark.png
diff --git a/apps/band/src/assets/images/icons/payments/img/jcb-light.png b/apps/app/src/assets/images/icons/payments/img/jcb-light.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/jcb-light.png
rename to apps/app/src/assets/images/icons/payments/img/jcb-light.png
diff --git a/apps/band/src/assets/images/icons/payments/img/master-dark.png b/apps/app/src/assets/images/icons/payments/img/master-dark.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/master-dark.png
rename to apps/app/src/assets/images/icons/payments/img/master-dark.png
diff --git a/apps/band/src/assets/images/icons/payments/img/mastercard.png b/apps/app/src/assets/images/icons/payments/img/mastercard.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/mastercard.png
rename to apps/app/src/assets/images/icons/payments/img/mastercard.png
diff --git a/apps/band/src/assets/images/icons/payments/img/paypal-dark.png b/apps/app/src/assets/images/icons/payments/img/paypal-dark.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/paypal-dark.png
rename to apps/app/src/assets/images/icons/payments/img/paypal-dark.png
diff --git a/apps/band/src/assets/images/icons/payments/img/paypal-light.png b/apps/app/src/assets/images/icons/payments/img/paypal-light.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/paypal-light.png
rename to apps/app/src/assets/images/icons/payments/img/paypal-light.png
diff --git a/apps/band/src/assets/images/icons/payments/img/visa-dark.png b/apps/app/src/assets/images/icons/payments/img/visa-dark.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/visa-dark.png
rename to apps/app/src/assets/images/icons/payments/img/visa-dark.png
diff --git a/apps/band/src/assets/images/icons/payments/img/visa-light.png b/apps/app/src/assets/images/icons/payments/img/visa-light.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/img/visa-light.png
rename to apps/app/src/assets/images/icons/payments/img/visa-light.png
diff --git a/apps/band/src/assets/images/icons/payments/mastercard-icon.png b/apps/app/src/assets/images/icons/payments/mastercard-icon.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/mastercard-icon.png
rename to apps/app/src/assets/images/icons/payments/mastercard-icon.png
diff --git a/apps/band/src/assets/images/icons/payments/mastercard.png b/apps/app/src/assets/images/icons/payments/mastercard.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/mastercard.png
rename to apps/app/src/assets/images/icons/payments/mastercard.png
diff --git a/apps/band/src/assets/images/icons/payments/visa-icon.png b/apps/app/src/assets/images/icons/payments/visa-icon.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/visa-icon.png
rename to apps/app/src/assets/images/icons/payments/visa-icon.png
diff --git a/apps/band/src/assets/images/icons/payments/visa.png b/apps/app/src/assets/images/icons/payments/visa.png
similarity index 100%
rename from apps/band/src/assets/images/icons/payments/visa.png
rename to apps/app/src/assets/images/icons/payments/visa.png
diff --git a/apps/band/src/assets/images/illustrations/boy-app-academy.png b/apps/app/src/assets/images/illustrations/boy-app-academy.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/boy-app-academy.png
rename to apps/app/src/assets/images/illustrations/boy-app-academy.png
diff --git a/apps/band/src/assets/images/illustrations/congo-illustration.png b/apps/app/src/assets/images/illustrations/congo-illustration.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/congo-illustration.png
rename to apps/app/src/assets/images/illustrations/congo-illustration.png
diff --git a/apps/band/src/assets/images/illustrations/girl-app-academy.png b/apps/app/src/assets/images/illustrations/girl-app-academy.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/girl-app-academy.png
rename to apps/app/src/assets/images/illustrations/girl-app-academy.png
diff --git a/apps/band/src/assets/images/illustrations/laptop-girl.png b/apps/app/src/assets/images/illustrations/laptop-girl.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/laptop-girl.png
rename to apps/app/src/assets/images/illustrations/laptop-girl.png
diff --git a/apps/band/src/assets/images/illustrations/register-multi-step-illustration-dark.png b/apps/app/src/assets/images/illustrations/register-multi-step-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/register-multi-step-illustration-dark.png
rename to apps/app/src/assets/images/illustrations/register-multi-step-illustration-dark.png
diff --git a/apps/band/src/assets/images/illustrations/register-multi-step-illustration-light.png b/apps/app/src/assets/images/illustrations/register-multi-step-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/register-multi-step-illustration-light.png
rename to apps/app/src/assets/images/illustrations/register-multi-step-illustration-light.png
diff --git a/apps/band/src/assets/images/illustrations/sidebar-pic-1.png b/apps/app/src/assets/images/illustrations/sidebar-pic-1.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/sidebar-pic-1.png
rename to apps/app/src/assets/images/illustrations/sidebar-pic-1.png
diff --git a/apps/band/src/assets/images/illustrations/sidebar-pic-2.png b/apps/app/src/assets/images/illustrations/sidebar-pic-2.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/sidebar-pic-2.png
rename to apps/app/src/assets/images/illustrations/sidebar-pic-2.png
diff --git a/apps/band/src/assets/images/illustrations/sidebar-pic-3.png b/apps/app/src/assets/images/illustrations/sidebar-pic-3.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/sidebar-pic-3.png
rename to apps/app/src/assets/images/illustrations/sidebar-pic-3.png
diff --git a/apps/band/src/assets/images/illustrations/sitting-girl-with-laptop.png b/apps/app/src/assets/images/illustrations/sitting-girl-with-laptop.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/sitting-girl-with-laptop.png
rename to apps/app/src/assets/images/illustrations/sitting-girl-with-laptop.png
diff --git a/apps/band/src/assets/images/illustrations/standingGirlImg.png b/apps/app/src/assets/images/illustrations/standingGirlImg.png
similarity index 100%
rename from apps/band/src/assets/images/illustrations/standingGirlImg.png
rename to apps/app/src/assets/images/illustrations/standingGirlImg.png
diff --git a/apps/band/src/assets/images/logo.svg b/apps/app/src/assets/images/logo.svg
similarity index 100%
rename from apps/band/src/assets/images/logo.svg
rename to apps/app/src/assets/images/logo.svg
diff --git a/apps/band/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png b/apps/app/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png
similarity index 100%
rename from apps/band/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png
rename to apps/app/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png
diff --git a/apps/band/src/assets/images/misc/3d-space-rocket-with-smoke.png b/apps/app/src/assets/images/misc/3d-space-rocket-with-smoke.png
similarity index 100%
rename from apps/band/src/assets/images/misc/3d-space-rocket-with-smoke.png
rename to apps/app/src/assets/images/misc/3d-space-rocket-with-smoke.png
diff --git a/apps/band/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png b/apps/app/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png
similarity index 100%
rename from apps/band/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png
rename to apps/app/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png
diff --git a/apps/band/src/assets/images/misc/fleet-car.png b/apps/app/src/assets/images/misc/fleet-car.png
similarity index 100%
rename from apps/band/src/assets/images/misc/fleet-car.png
rename to apps/app/src/assets/images/misc/fleet-car.png
diff --git a/apps/band/src/assets/images/pages/1.png b/apps/app/src/assets/images/pages/1.png
similarity index 100%
rename from apps/band/src/assets/images/pages/1.png
rename to apps/app/src/assets/images/pages/1.png
diff --git a/apps/band/src/assets/images/pages/2.png b/apps/app/src/assets/images/pages/2.png
similarity index 100%
rename from apps/band/src/assets/images/pages/2.png
rename to apps/app/src/assets/images/pages/2.png
diff --git a/apps/band/src/assets/images/pages/3.png b/apps/app/src/assets/images/pages/3.png
similarity index 100%
rename from apps/band/src/assets/images/pages/3.png
rename to apps/app/src/assets/images/pages/3.png
diff --git a/apps/band/src/assets/images/pages/401.png b/apps/app/src/assets/images/pages/401.png
similarity index 100%
rename from apps/band/src/assets/images/pages/401.png
rename to apps/app/src/assets/images/pages/401.png
diff --git a/apps/band/src/assets/images/pages/404.png b/apps/app/src/assets/images/pages/404.png
similarity index 100%
rename from apps/band/src/assets/images/pages/404.png
rename to apps/app/src/assets/images/pages/404.png
diff --git a/apps/band/src/assets/images/pages/5.jpg b/apps/app/src/assets/images/pages/5.jpg
similarity index 100%
rename from apps/band/src/assets/images/pages/5.jpg
rename to apps/app/src/assets/images/pages/5.jpg
diff --git a/apps/band/src/assets/images/pages/6.jpg b/apps/app/src/assets/images/pages/6.jpg
similarity index 100%
rename from apps/band/src/assets/images/pages/6.jpg
rename to apps/app/src/assets/images/pages/6.jpg
diff --git a/apps/band/src/assets/images/pages/DealTypeBackground-dark.png b/apps/app/src/assets/images/pages/DealTypeBackground-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/DealTypeBackground-dark.png
rename to apps/app/src/assets/images/pages/DealTypeBackground-dark.png
diff --git a/apps/band/src/assets/images/pages/DealTypeBackground-light.png b/apps/app/src/assets/images/pages/DealTypeBackground-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/DealTypeBackground-light.png
rename to apps/app/src/assets/images/pages/DealTypeBackground-light.png
diff --git a/apps/band/src/assets/images/pages/TimelineRectangle1.png b/apps/app/src/assets/images/pages/TimelineRectangle1.png
similarity index 100%
rename from apps/band/src/assets/images/pages/TimelineRectangle1.png
rename to apps/app/src/assets/images/pages/TimelineRectangle1.png
diff --git a/apps/band/src/assets/images/pages/TimelineRectangle2.png b/apps/app/src/assets/images/pages/TimelineRectangle2.png
similarity index 100%
rename from apps/band/src/assets/images/pages/TimelineRectangle2.png
rename to apps/app/src/assets/images/pages/TimelineRectangle2.png
diff --git a/apps/band/src/assets/images/pages/TimelineRectangle3.png b/apps/app/src/assets/images/pages/TimelineRectangle3.png
similarity index 100%
rename from apps/band/src/assets/images/pages/TimelineRectangle3.png
rename to apps/app/src/assets/images/pages/TimelineRectangle3.png
diff --git a/apps/band/src/assets/images/pages/TimelineRectangle4.png b/apps/app/src/assets/images/pages/TimelineRectangle4.png
similarity index 100%
rename from apps/band/src/assets/images/pages/TimelineRectangle4.png
rename to apps/app/src/assets/images/pages/TimelineRectangle4.png
diff --git a/apps/band/src/assets/images/pages/academy-course-illustration1.png b/apps/app/src/assets/images/pages/academy-course-illustration1.png
similarity index 100%
rename from apps/band/src/assets/images/pages/academy-course-illustration1.png
rename to apps/app/src/assets/images/pages/academy-course-illustration1.png
diff --git a/apps/band/src/assets/images/pages/academy-course-illustration2-dark.png b/apps/app/src/assets/images/pages/academy-course-illustration2-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/academy-course-illustration2-dark.png
rename to apps/app/src/assets/images/pages/academy-course-illustration2-dark.png
diff --git a/apps/band/src/assets/images/pages/academy-course-illustration2-light.png b/apps/app/src/assets/images/pages/academy-course-illustration2-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/academy-course-illustration2-light.png
rename to apps/app/src/assets/images/pages/academy-course-illustration2-light.png
diff --git a/apps/band/src/assets/images/pages/app-academy-tutor-1.png b/apps/app/src/assets/images/pages/app-academy-tutor-1.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-academy-tutor-1.png
rename to apps/app/src/assets/images/pages/app-academy-tutor-1.png
diff --git a/apps/band/src/assets/images/pages/app-academy-tutor-2.png b/apps/app/src/assets/images/pages/app-academy-tutor-2.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-academy-tutor-2.png
rename to apps/app/src/assets/images/pages/app-academy-tutor-2.png
diff --git a/apps/band/src/assets/images/pages/app-academy-tutor-3.png b/apps/app/src/assets/images/pages/app-academy-tutor-3.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-academy-tutor-3.png
rename to apps/app/src/assets/images/pages/app-academy-tutor-3.png
diff --git a/apps/band/src/assets/images/pages/app-academy-tutor-4.png b/apps/app/src/assets/images/pages/app-academy-tutor-4.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-academy-tutor-4.png
rename to apps/app/src/assets/images/pages/app-academy-tutor-4.png
diff --git a/apps/band/src/assets/images/pages/app-academy-tutor-5.png b/apps/app/src/assets/images/pages/app-academy-tutor-5.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-academy-tutor-5.png
rename to apps/app/src/assets/images/pages/app-academy-tutor-5.png
diff --git a/apps/band/src/assets/images/pages/app-academy-tutor-6.png b/apps/app/src/assets/images/pages/app-academy-tutor-6.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-academy-tutor-6.png
rename to apps/app/src/assets/images/pages/app-academy-tutor-6.png
diff --git a/apps/band/src/assets/images/pages/app-search-header-bg.png b/apps/app/src/assets/images/pages/app-search-header-bg.png
similarity index 100%
rename from apps/band/src/assets/images/pages/app-search-header-bg.png
rename to apps/app/src/assets/images/pages/app-search-header-bg.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png b/apps/app/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png b/apps/app/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png
rename to apps/app/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png b/apps/app/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png b/apps/app/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png
rename to apps/app/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-login-illustration-dark.png b/apps/app/src/assets/images/pages/auth-v2-login-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-login-illustration-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-login-illustration-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-login-illustration-light.png b/apps/app/src/assets/images/pages/auth-v2-login-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-login-illustration-light.png
rename to apps/app/src/assets/images/pages/auth-v2-login-illustration-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png b/apps/app/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png b/apps/app/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png
rename to apps/app/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-register-illustration-dark.png b/apps/app/src/assets/images/pages/auth-v2-register-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-register-illustration-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-register-illustration-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-register-illustration-light.png b/apps/app/src/assets/images/pages/auth-v2-register-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-register-illustration-light.png
rename to apps/app/src/assets/images/pages/auth-v2-register-illustration-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png b/apps/app/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-reset-password-illustration-light.png b/apps/app/src/assets/images/pages/auth-v2-reset-password-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-reset-password-illustration-light.png
rename to apps/app/src/assets/images/pages/auth-v2-reset-password-illustration-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-two-step-illustration-dark.png b/apps/app/src/assets/images/pages/auth-v2-two-step-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-two-step-illustration-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-two-step-illustration-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-two-step-illustration-light.png b/apps/app/src/assets/images/pages/auth-v2-two-step-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-two-step-illustration-light.png
rename to apps/app/src/assets/images/pages/auth-v2-two-step-illustration-light.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png b/apps/app/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png
rename to apps/app/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png
diff --git a/apps/band/src/assets/images/pages/auth-v2-verify-email-illustration-light.png b/apps/app/src/assets/images/pages/auth-v2-verify-email-illustration-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/auth-v2-verify-email-illustration-light.png
rename to apps/app/src/assets/images/pages/auth-v2-verify-email-illustration-light.png
diff --git a/apps/band/src/assets/images/pages/background-1.jpg b/apps/app/src/assets/images/pages/background-1.jpg
similarity index 100%
rename from apps/band/src/assets/images/pages/background-1.jpg
rename to apps/app/src/assets/images/pages/background-1.jpg
diff --git a/apps/band/src/assets/images/pages/background-2.jpg b/apps/app/src/assets/images/pages/background-2.jpg
similarity index 100%
rename from apps/band/src/assets/images/pages/background-2.jpg
rename to apps/app/src/assets/images/pages/background-2.jpg
diff --git a/apps/band/src/assets/images/pages/background-3.jpg b/apps/app/src/assets/images/pages/background-3.jpg
similarity index 100%
rename from apps/band/src/assets/images/pages/background-3.jpg
rename to apps/app/src/assets/images/pages/background-3.jpg
diff --git a/apps/band/src/assets/images/pages/boy-illustration.png b/apps/app/src/assets/images/pages/boy-illustration.png
similarity index 100%
rename from apps/band/src/assets/images/pages/boy-illustration.png
rename to apps/app/src/assets/images/pages/boy-illustration.png
diff --git a/apps/band/src/assets/images/pages/custom-checkbox-img-1.png b/apps/app/src/assets/images/pages/custom-checkbox-img-1.png
similarity index 100%
rename from apps/band/src/assets/images/pages/custom-checkbox-img-1.png
rename to apps/app/src/assets/images/pages/custom-checkbox-img-1.png
diff --git a/apps/band/src/assets/images/pages/custom-checkbox-img-2.png b/apps/app/src/assets/images/pages/custom-checkbox-img-2.png
similarity index 100%
rename from apps/band/src/assets/images/pages/custom-checkbox-img-2.png
rename to apps/app/src/assets/images/pages/custom-checkbox-img-2.png
diff --git a/apps/band/src/assets/images/pages/custom-checkbox-img-3.png b/apps/app/src/assets/images/pages/custom-checkbox-img-3.png
similarity index 100%
rename from apps/band/src/assets/images/pages/custom-checkbox-img-3.png
rename to apps/app/src/assets/images/pages/custom-checkbox-img-3.png
diff --git a/apps/band/src/assets/images/pages/custom-radio-img-1.png b/apps/app/src/assets/images/pages/custom-radio-img-1.png
similarity index 100%
rename from apps/band/src/assets/images/pages/custom-radio-img-1.png
rename to apps/app/src/assets/images/pages/custom-radio-img-1.png
diff --git a/apps/band/src/assets/images/pages/custom-radio-img-2.png b/apps/app/src/assets/images/pages/custom-radio-img-2.png
similarity index 100%
rename from apps/band/src/assets/images/pages/custom-radio-img-2.png
rename to apps/app/src/assets/images/pages/custom-radio-img-2.png
diff --git a/apps/band/src/assets/images/pages/custom-radio-img-3.png b/apps/app/src/assets/images/pages/custom-radio-img-3.png
similarity index 100%
rename from apps/band/src/assets/images/pages/custom-radio-img-3.png
rename to apps/app/src/assets/images/pages/custom-radio-img-3.png
diff --git a/apps/band/src/assets/images/pages/empty-cart.png b/apps/app/src/assets/images/pages/empty-cart.png
similarity index 100%
rename from apps/band/src/assets/images/pages/empty-cart.png
rename to apps/app/src/assets/images/pages/empty-cart.png
diff --git a/apps/band/src/assets/images/pages/forgot-password-illustration.png b/apps/app/src/assets/images/pages/forgot-password-illustration.png
similarity index 100%
rename from apps/band/src/assets/images/pages/forgot-password-illustration.png
rename to apps/app/src/assets/images/pages/forgot-password-illustration.png
diff --git a/apps/band/src/assets/images/pages/girl-using-mobile.png b/apps/app/src/assets/images/pages/girl-using-mobile.png
similarity index 100%
rename from apps/band/src/assets/images/pages/girl-using-mobile.png
rename to apps/app/src/assets/images/pages/girl-using-mobile.png
diff --git a/apps/band/src/assets/images/pages/google-home.png b/apps/app/src/assets/images/pages/google-home.png
similarity index 100%
rename from apps/band/src/assets/images/pages/google-home.png
rename to apps/app/src/assets/images/pages/google-home.png
diff --git a/apps/band/src/assets/images/pages/guitar-course-poster.png b/apps/app/src/assets/images/pages/guitar-course-poster.png
similarity index 100%
rename from apps/band/src/assets/images/pages/guitar-course-poster.png
rename to apps/app/src/assets/images/pages/guitar-course-poster.png
diff --git a/apps/band/src/assets/images/pages/instructor-poster.png b/apps/app/src/assets/images/pages/instructor-poster.png
similarity index 100%
rename from apps/band/src/assets/images/pages/instructor-poster.png
rename to apps/app/src/assets/images/pages/instructor-poster.png
diff --git a/apps/band/src/assets/images/pages/iphone-11.png b/apps/app/src/assets/images/pages/iphone-11.png
similarity index 100%
rename from apps/band/src/assets/images/pages/iphone-11.png
rename to apps/app/src/assets/images/pages/iphone-11.png
diff --git a/apps/band/src/assets/images/pages/misc-coming-soon.png b/apps/app/src/assets/images/pages/misc-coming-soon.png
similarity index 100%
rename from apps/band/src/assets/images/pages/misc-coming-soon.png
rename to apps/app/src/assets/images/pages/misc-coming-soon.png
diff --git a/apps/band/src/assets/images/pages/misc-mask-dark.png b/apps/app/src/assets/images/pages/misc-mask-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/misc-mask-dark.png
rename to apps/app/src/assets/images/pages/misc-mask-dark.png
diff --git a/apps/band/src/assets/images/pages/misc-mask-light.png b/apps/app/src/assets/images/pages/misc-mask-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/misc-mask-light.png
rename to apps/app/src/assets/images/pages/misc-mask-light.png
diff --git a/apps/band/src/assets/images/pages/misc-under-maintenance.png b/apps/app/src/assets/images/pages/misc-under-maintenance.png
similarity index 100%
rename from apps/band/src/assets/images/pages/misc-under-maintenance.png
rename to apps/app/src/assets/images/pages/misc-under-maintenance.png
diff --git a/apps/band/src/assets/images/pages/puma-shoes.jpeg b/apps/app/src/assets/images/pages/puma-shoes.jpeg
similarity index 100%
rename from apps/band/src/assets/images/pages/puma-shoes.jpeg
rename to apps/app/src/assets/images/pages/puma-shoes.jpeg
diff --git a/apps/band/src/assets/images/pages/register-multi-step-bg-dark.png b/apps/app/src/assets/images/pages/register-multi-step-bg-dark.png
similarity index 100%
rename from apps/band/src/assets/images/pages/register-multi-step-bg-dark.png
rename to apps/app/src/assets/images/pages/register-multi-step-bg-dark.png
diff --git a/apps/band/src/assets/images/pages/register-multi-step-bg-light.png b/apps/app/src/assets/images/pages/register-multi-step-bg-light.png
similarity index 100%
rename from apps/band/src/assets/images/pages/register-multi-step-bg-light.png
rename to apps/app/src/assets/images/pages/register-multi-step-bg-light.png
diff --git a/apps/band/src/assets/images/pages/singing-course-poster.png b/apps/app/src/assets/images/pages/singing-course-poster.png
similarity index 100%
rename from apps/band/src/assets/images/pages/singing-course-poster.png
rename to apps/app/src/assets/images/pages/singing-course-poster.png
diff --git a/apps/band/src/assets/images/pages/themeselection-qr.png b/apps/app/src/assets/images/pages/themeselection-qr.png
similarity index 100%
rename from apps/band/src/assets/images/pages/themeselection-qr.png
rename to apps/app/src/assets/images/pages/themeselection-qr.png
diff --git a/apps/band/src/assets/images/pages/tree-pot.png b/apps/app/src/assets/images/pages/tree-pot.png
similarity index 100%
rename from apps/band/src/assets/images/pages/tree-pot.png
rename to apps/app/src/assets/images/pages/tree-pot.png
diff --git a/apps/band/src/assets/images/pages/user-profile-header-bg.png b/apps/app/src/assets/images/pages/user-profile-header-bg.png
similarity index 100%
rename from apps/band/src/assets/images/pages/user-profile-header-bg.png
rename to apps/app/src/assets/images/pages/user-profile-header-bg.png
diff --git a/apps/band/src/assets/images/svg/Card.svg b/apps/app/src/assets/images/svg/Card.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/Card.svg
rename to apps/app/src/assets/images/svg/Card.svg
diff --git a/apps/band/src/assets/images/svg/Check.svg b/apps/app/src/assets/images/svg/Check.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/Check.svg
rename to apps/app/src/assets/images/svg/Check.svg
diff --git a/apps/band/src/assets/images/svg/Diamond.svg b/apps/app/src/assets/images/svg/Diamond.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/Diamond.svg
rename to apps/app/src/assets/images/svg/Diamond.svg
diff --git a/apps/band/src/assets/images/svg/Suitcase.svg b/apps/app/src/assets/images/svg/Suitcase.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/Suitcase.svg
rename to apps/app/src/assets/images/svg/Suitcase.svg
diff --git a/apps/band/src/assets/images/svg/Wallet.svg b/apps/app/src/assets/images/svg/Wallet.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/Wallet.svg
rename to apps/app/src/assets/images/svg/Wallet.svg
diff --git a/apps/band/src/assets/images/svg/address.svg b/apps/app/src/assets/images/svg/address.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/address.svg
rename to apps/app/src/assets/images/svg/address.svg
diff --git a/apps/band/src/assets/images/svg/auth-v1-bottom-shape.svg b/apps/app/src/assets/images/svg/auth-v1-bottom-shape.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/auth-v1-bottom-shape.svg
rename to apps/app/src/assets/images/svg/auth-v1-bottom-shape.svg
diff --git a/apps/band/src/assets/images/svg/auth-v1-top-shape.svg b/apps/app/src/assets/images/svg/auth-v1-top-shape.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/auth-v1-top-shape.svg
rename to apps/app/src/assets/images/svg/auth-v1-top-shape.svg
diff --git a/apps/band/src/assets/images/svg/cart.svg b/apps/app/src/assets/images/svg/cart.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/cart.svg
rename to apps/app/src/assets/images/svg/cart.svg
diff --git a/apps/band/src/assets/images/svg/checkbox-checked.svg b/apps/app/src/assets/images/svg/checkbox-checked.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/checkbox-checked.svg
rename to apps/app/src/assets/images/svg/checkbox-checked.svg
diff --git a/apps/band/src/assets/images/svg/checkbox-indeterminate.svg b/apps/app/src/assets/images/svg/checkbox-indeterminate.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/checkbox-indeterminate.svg
rename to apps/app/src/assets/images/svg/checkbox-indeterminate.svg
diff --git a/apps/band/src/assets/images/svg/checkbox-unchecked.svg b/apps/app/src/assets/images/svg/checkbox-unchecked.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/checkbox-unchecked.svg
rename to apps/app/src/assets/images/svg/checkbox-unchecked.svg
diff --git a/apps/band/src/assets/images/svg/discord.svg b/apps/app/src/assets/images/svg/discord.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/discord.svg
rename to apps/app/src/assets/images/svg/discord.svg
diff --git a/apps/band/src/assets/images/svg/gift.svg b/apps/app/src/assets/images/svg/gift.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/gift.svg
rename to apps/app/src/assets/images/svg/gift.svg
diff --git a/apps/band/src/assets/images/svg/home.svg b/apps/app/src/assets/images/svg/home.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/home.svg
rename to apps/app/src/assets/images/svg/home.svg
diff --git a/apps/band/src/assets/images/svg/keyboard.svg b/apps/app/src/assets/images/svg/keyboard.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/keyboard.svg
rename to apps/app/src/assets/images/svg/keyboard.svg
diff --git a/apps/band/src/assets/images/svg/laptop.svg b/apps/app/src/assets/images/svg/laptop.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/laptop.svg
rename to apps/app/src/assets/images/svg/laptop.svg
diff --git a/apps/band/src/assets/images/svg/lightbulb.svg b/apps/app/src/assets/images/svg/lightbulb.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/lightbulb.svg
rename to apps/app/src/assets/images/svg/lightbulb.svg
diff --git a/apps/band/src/assets/images/svg/office.svg b/apps/app/src/assets/images/svg/office.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/office.svg
rename to apps/app/src/assets/images/svg/office.svg
diff --git a/apps/band/src/assets/images/svg/paper-send.svg b/apps/app/src/assets/images/svg/paper-send.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/paper-send.svg
rename to apps/app/src/assets/images/svg/paper-send.svg
diff --git a/apps/band/src/assets/images/svg/payment.svg b/apps/app/src/assets/images/svg/payment.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/payment.svg
rename to apps/app/src/assets/images/svg/payment.svg
diff --git a/apps/band/src/assets/images/svg/radio-checked.svg b/apps/app/src/assets/images/svg/radio-checked.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/radio-checked.svg
rename to apps/app/src/assets/images/svg/radio-checked.svg
diff --git a/apps/band/src/assets/images/svg/radio-unchecked.svg b/apps/app/src/assets/images/svg/radio-unchecked.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/radio-unchecked.svg
rename to apps/app/src/assets/images/svg/radio-unchecked.svg
diff --git a/apps/band/src/assets/images/svg/rocket.svg b/apps/app/src/assets/images/svg/rocket.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/rocket.svg
rename to apps/app/src/assets/images/svg/rocket.svg
diff --git a/apps/band/src/assets/images/svg/trending.svg b/apps/app/src/assets/images/svg/trending.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/trending.svg
rename to apps/app/src/assets/images/svg/trending.svg
diff --git a/apps/band/src/assets/images/svg/user-info.svg b/apps/app/src/assets/images/svg/user-info.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/user-info.svg
rename to apps/app/src/assets/images/svg/user-info.svg
diff --git a/apps/band/src/assets/images/svg/user.svg b/apps/app/src/assets/images/svg/user.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/user.svg
rename to apps/app/src/assets/images/svg/user.svg
diff --git a/apps/band/src/assets/images/svg/wizard-account.svg b/apps/app/src/assets/images/svg/wizard-account.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/wizard-account.svg
rename to apps/app/src/assets/images/svg/wizard-account.svg
diff --git a/apps/band/src/assets/images/svg/wizard-address.svg b/apps/app/src/assets/images/svg/wizard-address.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/wizard-address.svg
rename to apps/app/src/assets/images/svg/wizard-address.svg
diff --git a/apps/band/src/assets/images/svg/wizard-personal.svg b/apps/app/src/assets/images/svg/wizard-personal.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/wizard-personal.svg
rename to apps/app/src/assets/images/svg/wizard-personal.svg
diff --git a/apps/band/src/assets/images/svg/wizard-social-link.svg b/apps/app/src/assets/images/svg/wizard-social-link.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/wizard-social-link.svg
rename to apps/app/src/assets/images/svg/wizard-social-link.svg
diff --git a/apps/band/src/assets/images/svg/wizard-submit.svg b/apps/app/src/assets/images/svg/wizard-submit.svg
similarity index 100%
rename from apps/band/src/assets/images/svg/wizard-submit.svg
rename to apps/app/src/assets/images/svg/wizard-submit.svg
diff --git a/apps/band/src/assets/styles/styles.scss b/apps/app/src/assets/styles/styles.scss
similarity index 100%
rename from apps/band/src/assets/styles/styles.scss
rename to apps/app/src/assets/styles/styles.scss
diff --git a/apps/band/src/assets/styles/variables/_template.scss b/apps/app/src/assets/styles/variables/_template.scss
similarity index 100%
rename from apps/band/src/assets/styles/variables/_template.scss
rename to apps/app/src/assets/styles/variables/_template.scss
diff --git a/apps/band/src/assets/styles/variables/_vuetify.scss b/apps/app/src/assets/styles/variables/_vuetify.scss
similarity index 100%
rename from apps/band/src/assets/styles/variables/_vuetify.scss
rename to apps/app/src/assets/styles/variables/_vuetify.scss
diff --git a/apps/band/src/components/AppLoadingIndicator.vue b/apps/app/src/components/AppLoadingIndicator.vue
similarity index 100%
rename from apps/band/src/components/AppLoadingIndicator.vue
rename to apps/app/src/components/AppLoadingIndicator.vue
diff --git a/apps/band/src/components/AppPricing.vue b/apps/app/src/components/AppPricing.vue
similarity index 100%
rename from apps/band/src/components/AppPricing.vue
rename to apps/app/src/components/AppPricing.vue
diff --git a/apps/band/src/components/AppSearchHeader.vue b/apps/app/src/components/AppSearchHeader.vue
similarity index 100%
rename from apps/band/src/components/AppSearchHeader.vue
rename to apps/app/src/components/AppSearchHeader.vue
diff --git a/apps/band/src/components/ErrorHeader.vue b/apps/app/src/components/ErrorHeader.vue
similarity index 100%
rename from apps/band/src/components/ErrorHeader.vue
rename to apps/app/src/components/ErrorHeader.vue
diff --git a/apps/band/src/components/dialogs/AddEditAddressDialog.vue b/apps/app/src/components/dialogs/AddEditAddressDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/AddEditAddressDialog.vue
rename to apps/app/src/components/dialogs/AddEditAddressDialog.vue
diff --git a/apps/band/src/components/dialogs/AddEditPermissionDialog.vue b/apps/app/src/components/dialogs/AddEditPermissionDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/AddEditPermissionDialog.vue
rename to apps/app/src/components/dialogs/AddEditPermissionDialog.vue
diff --git a/apps/band/src/components/dialogs/AddEditRoleDialog.vue b/apps/app/src/components/dialogs/AddEditRoleDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/AddEditRoleDialog.vue
rename to apps/app/src/components/dialogs/AddEditRoleDialog.vue
diff --git a/apps/band/src/components/dialogs/ConfirmDialog.vue b/apps/app/src/components/dialogs/ConfirmDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/ConfirmDialog.vue
rename to apps/app/src/components/dialogs/ConfirmDialog.vue
diff --git a/apps/band/src/components/dialogs/CreateAppDialog.vue b/apps/app/src/components/dialogs/CreateAppDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/CreateAppDialog.vue
rename to apps/app/src/components/dialogs/CreateAppDialog.vue
diff --git a/apps/band/src/components/dialogs/EnableOneTimePasswordDialog.vue b/apps/app/src/components/dialogs/EnableOneTimePasswordDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/EnableOneTimePasswordDialog.vue
rename to apps/app/src/components/dialogs/EnableOneTimePasswordDialog.vue
diff --git a/apps/band/src/components/dialogs/PaymentProvidersDialog.vue b/apps/app/src/components/dialogs/PaymentProvidersDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/PaymentProvidersDialog.vue
rename to apps/app/src/components/dialogs/PaymentProvidersDialog.vue
diff --git a/apps/band/src/components/dialogs/ShareProjectDialog.vue b/apps/app/src/components/dialogs/ShareProjectDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/ShareProjectDialog.vue
rename to apps/app/src/components/dialogs/ShareProjectDialog.vue
diff --git a/apps/band/src/components/dialogs/TwoFactorAuthDialog.vue b/apps/app/src/components/dialogs/TwoFactorAuthDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/TwoFactorAuthDialog.vue
rename to apps/app/src/components/dialogs/TwoFactorAuthDialog.vue
diff --git a/apps/band/src/components/dialogs/UserInfoEditDialog.vue b/apps/app/src/components/dialogs/UserInfoEditDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/UserInfoEditDialog.vue
rename to apps/app/src/components/dialogs/UserInfoEditDialog.vue
diff --git a/apps/app/src/composables/useEvents.ts b/apps/app/src/composables/useEvents.ts
new file mode 100644
index 0000000..b060586
--- /dev/null
+++ b/apps/app/src/composables/useEvents.ts
@@ -0,0 +1,91 @@
+import { computed, ref } from 'vue'
+import { apiClient } from '@/lib/api-client'
+import { useCurrentOrganisationId } from '@/composables/useOrganisationContext'
+import type { ApiResponse, Event, Pagination } from '@/types/events'
+
+interface LaravelPaginatedEventsBody {
+ data: Event[]
+ meta: {
+ current_page: number
+ per_page: number
+ total: number
+ last_page: number
+ from: number | null
+ to: number | null
+ }
+}
+
+function requireOrganisationId(organisationId: string | null): string {
+ if (!organisationId) {
+ throw new Error('No organisation in session. Log in again.')
+ }
+ return organisationId
+}
+
+export function useEvents() {
+ const { organisationId } = useCurrentOrganisationId()
+ const events = ref([])
+ const currentEvent = ref(null)
+ const pagination = ref(null)
+ const isLoading = ref(false)
+ const error = ref(null)
+
+ function eventsPath(): string {
+ const id = requireOrganisationId(organisationId.value)
+ return `/organisations/${id}/events`
+ }
+
+ async function fetchEvents(params?: { page?: number; per_page?: number }) {
+ isLoading.value = true
+ error.value = null
+
+ try {
+ const { data } = await apiClient.get(eventsPath(), { params })
+ events.value = data.data
+ pagination.value = {
+ current_page: data.meta.current_page,
+ per_page: data.meta.per_page,
+ total: data.meta.total,
+ last_page: data.meta.last_page,
+ from: data.meta.from,
+ to: data.meta.to,
+ }
+ }
+ catch (err) {
+ error.value = err instanceof Error ? err : new Error('Failed to fetch events')
+ throw error.value
+ }
+ finally {
+ isLoading.value = false
+ }
+ }
+
+ async function fetchEvent(id: string) {
+ isLoading.value = true
+ error.value = null
+
+ try {
+ const { data } = await apiClient.get>(`${eventsPath()}/${id}`)
+ currentEvent.value = data.data
+ return data.data
+ }
+ catch (err) {
+ error.value = err instanceof Error ? err : new Error('Failed to fetch event')
+ throw error.value
+ }
+ finally {
+ isLoading.value = false
+ }
+ }
+
+ return {
+ organisationId: computed(() => organisationId.value),
+ events: computed(() => events.value),
+ currentEvent: computed(() => currentEvent.value),
+ pagination: computed(() => pagination.value),
+ isLoading: computed(() => isLoading.value),
+ error: computed(() => error.value),
+ fetchEvents,
+ fetchEvent,
+ }
+}
diff --git a/apps/app/src/composables/useOrganisationContext.ts b/apps/app/src/composables/useOrganisationContext.ts
new file mode 100644
index 0000000..990c91c
--- /dev/null
+++ b/apps/app/src/composables/useOrganisationContext.ts
@@ -0,0 +1,25 @@
+import { useCookie } from '@core/composable/useCookie'
+import { computed } from 'vue'
+
+export interface AuthOrganisationSummary {
+ id: string
+ name: string
+ slug: string
+ role: string
+}
+
+export interface AuthUserCookie {
+ id: string
+ name: string
+ email: string
+ roles?: string[]
+ organisations?: AuthOrganisationSummary[]
+}
+
+export function useCurrentOrganisationId() {
+ const userData = useCookie('userData')
+
+ const organisationId = computed(() => userData.value?.organisations?.[0]?.id ?? null)
+
+ return { organisationId }
+}
diff --git a/apps/band/src/layouts/blank.vue b/apps/app/src/layouts/blank.vue
similarity index 100%
rename from apps/band/src/layouts/blank.vue
rename to apps/app/src/layouts/blank.vue
diff --git a/apps/band/src/layouts/components/DefaultLayoutWithHorizontalNav.vue b/apps/app/src/layouts/components/DefaultLayoutWithHorizontalNav.vue
similarity index 100%
rename from apps/band/src/layouts/components/DefaultLayoutWithHorizontalNav.vue
rename to apps/app/src/layouts/components/DefaultLayoutWithHorizontalNav.vue
diff --git a/apps/band/src/layouts/components/DefaultLayoutWithVerticalNav.vue b/apps/app/src/layouts/components/DefaultLayoutWithVerticalNav.vue
similarity index 100%
rename from apps/band/src/layouts/components/DefaultLayoutWithVerticalNav.vue
rename to apps/app/src/layouts/components/DefaultLayoutWithVerticalNav.vue
diff --git a/apps/band/src/layouts/components/Footer.vue b/apps/app/src/layouts/components/Footer.vue
similarity index 100%
rename from apps/band/src/layouts/components/Footer.vue
rename to apps/app/src/layouts/components/Footer.vue
diff --git a/apps/band/src/layouts/components/NavBarNotifications.vue b/apps/app/src/layouts/components/NavBarNotifications.vue
similarity index 100%
rename from apps/band/src/layouts/components/NavBarNotifications.vue
rename to apps/app/src/layouts/components/NavBarNotifications.vue
diff --git a/apps/band/src/layouts/components/NavSearchBar.vue b/apps/app/src/layouts/components/NavSearchBar.vue
similarity index 100%
rename from apps/band/src/layouts/components/NavSearchBar.vue
rename to apps/app/src/layouts/components/NavSearchBar.vue
diff --git a/apps/band/src/layouts/components/NavbarShortcuts.vue b/apps/app/src/layouts/components/NavbarShortcuts.vue
similarity index 100%
rename from apps/band/src/layouts/components/NavbarShortcuts.vue
rename to apps/app/src/layouts/components/NavbarShortcuts.vue
diff --git a/apps/band/src/layouts/components/NavbarThemeSwitcher.vue b/apps/app/src/layouts/components/NavbarThemeSwitcher.vue
similarity index 100%
rename from apps/band/src/layouts/components/NavbarThemeSwitcher.vue
rename to apps/app/src/layouts/components/NavbarThemeSwitcher.vue
diff --git a/apps/band/src/layouts/components/UserProfile.vue b/apps/app/src/layouts/components/UserProfile.vue
similarity index 100%
rename from apps/band/src/layouts/components/UserProfile.vue
rename to apps/app/src/layouts/components/UserProfile.vue
diff --git a/apps/band/src/layouts/default.vue b/apps/app/src/layouts/default.vue
similarity index 100%
rename from apps/band/src/layouts/default.vue
rename to apps/app/src/layouts/default.vue
diff --git a/apps/band/src/lib/api-client.ts b/apps/app/src/lib/api-client.ts
similarity index 58%
rename from apps/band/src/lib/api-client.ts
rename to apps/app/src/lib/api-client.ts
index 6714c8a..6b4397c 100644
--- a/apps/band/src/lib/api-client.ts
+++ b/apps/app/src/lib/api-client.ts
@@ -1,24 +1,31 @@
import axios from 'axios'
+import { parse } from 'cookie-es'
import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
const apiClient: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: {
'Content-Type': 'application/json',
- 'Accept': 'application/json',
+ Accept: 'application/json',
},
timeout: 30000,
})
-// Request interceptor - add auth token
+function getAccessToken(): string | null {
+ if (typeof document === 'undefined') return null
+ const cookies = parse(document.cookie)
+ const token = cookies.accessToken
+
+ return token ?? null
+}
+
apiClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
- const token = localStorage.getItem('auth_token')
+ const token = getAccessToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
- // Log in development
if (import.meta.env.DEV) {
console.log(`🚀 ${config.method?.toUpperCase()} ${config.url}`, config.data)
}
@@ -28,7 +35,6 @@ apiClient.interceptors.request.use(
error => Promise.reject(error),
)
-// Response interceptor - handle errors
apiClient.interceptors.response.use(
response => {
if (import.meta.env.DEV) {
@@ -39,13 +45,19 @@ apiClient.interceptors.response.use(
},
error => {
if (import.meta.env.DEV) {
- console.error(`❌ ${error.response?.status} ${error.config?.url}`, error.response?.data)
+ console.error(
+ `❌ ${error.response?.status} ${error.config?.url}`,
+ error.response?.data,
+ )
}
- // Handle 401 - redirect to login
if (error.response?.status === 401) {
- localStorage.removeItem('auth_token')
- window.location.href = '/login'
+ document.cookie = 'accessToken=; path=/; max-age=0'
+ document.cookie = 'userData=; path=/; max-age=0'
+ document.cookie = 'userAbilityRules=; path=/; max-age=0'
+ if (window.location.pathname !== '/login') {
+ window.location.href = '/login'
+ }
}
return Promise.reject(error)
@@ -53,4 +65,3 @@ apiClient.interceptors.response.use(
)
export { apiClient }
-
diff --git a/apps/band/src/main.ts b/apps/app/src/main.ts
similarity index 62%
rename from apps/band/src/main.ts
rename to apps/app/src/main.ts
index 2b4e7b3..dc67339 100644
--- a/apps/band/src/main.ts
+++ b/apps/app/src/main.ts
@@ -1,4 +1,5 @@
import { createApp } from 'vue'
+import { VueQueryPlugin } from '@tanstack/vue-query'
import App from '@/App.vue'
import { registerPlugins } from '@core/utils/plugins'
@@ -13,5 +14,13 @@ const app = createApp(App)
// Register plugins
registerPlugins(app)
+app.use(VueQueryPlugin, {
+ queryClientConfig: {
+ defaultOptions: {
+ queries: { staleTime: 1000 * 60 * 5, retry: 1 },
+ },
+ },
+})
+
// Mount vue app
app.mount('#app')
diff --git a/apps/customers/src/navigation/vertical/index.ts b/apps/app/src/navigation/horizontal/index.ts
similarity index 52%
rename from apps/customers/src/navigation/vertical/index.ts
rename to apps/app/src/navigation/horizontal/index.ts
index f9dbf43..ffa16d8 100644
--- a/apps/customers/src/navigation/vertical/index.ts
+++ b/apps/app/src/navigation/horizontal/index.ts
@@ -4,9 +4,4 @@ export default [
to: { name: 'root' },
icon: { icon: 'tabler-smart-home' },
},
- {
- title: 'Second page',
- to: { name: 'second-page' },
- icon: { icon: 'tabler-file' },
- },
]
diff --git a/apps/band/src/navigation/vertical/index.ts b/apps/app/src/navigation/vertical/index.ts
similarity index 100%
rename from apps/band/src/navigation/vertical/index.ts
rename to apps/app/src/navigation/vertical/index.ts
diff --git a/apps/band/src/pages/[...error].vue b/apps/app/src/pages/[...error].vue
similarity index 100%
rename from apps/band/src/pages/[...error].vue
rename to apps/app/src/pages/[...error].vue
diff --git a/apps/app/src/pages/events/[id].vue b/apps/app/src/pages/events/[id].vue
new file mode 100644
index 0000000..99658b1
--- /dev/null
+++ b/apps/app/src/pages/events/[id].vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+ {{ currentEvent.name }}
+
+ {{ formatStatusLabel(currentEvent.status) }}
+
+
+
+
+
+
+
+
+ Dates
+
+
+ {{ new Date(currentEvent.start_date).toLocaleDateString() }}
+ –
+ {{ new Date(currentEvent.end_date).toLocaleDateString() }}
+
+
+
+
+
+ Timezone
+
+
+ {{ currentEvent.timezone }}
+
+
+
+
+
+ Organisation
+
+
+ {{ currentEvent.organisation.name }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/band/src/pages/events/index.vue b/apps/app/src/pages/events/index.vue
similarity index 53%
rename from apps/band/src/pages/events/index.vue
rename to apps/app/src/pages/events/index.vue
index 7dd81b4..b2584db 100644
--- a/apps/band/src/pages/events/index.vue
+++ b/apps/app/src/pages/events/index.vue
@@ -1,15 +1,16 @@
+
+ You need to belong to an organisation to see events. Ask an administrator to invite you.
+
+
- My Events
+
+ Events
+
- Events you've been invited to
+ Events for your organisation
-
-
-
- {{ event.title }}
+ {{ event.name }}
@@ -99,48 +103,16 @@ function getRsvpColor(status: string): string {
size="small"
class="mr-2"
>
- {{ event.status_label }}
+ {{ formatStatusLabel(event.status) }}
-
- {{ new Date(event.event_date).toLocaleDateString() }}
-
-
-
-
- {{ event.location.name }}, {{ event.location.city }}
+
+ {{ new Date(event.start_date).toLocaleDateString() }}
+ –
+ {{ new Date(event.end_date).toLocaleDateString() }}
-
-
-
- {{ event.start_time }}
- - {{ event.end_time }}
-
-
-
-
- RSVP: {{ event.invitations[0].rsvp_status }}
-
+
+ {{ event.timezone }}
@@ -149,14 +121,13 @@ function getRsvpColor(status: string): string {
:to="{ name: 'events-view-id', params: { id: event.id } }"
variant="text"
>
- View Details
+ View details
-
-
- No events found
-
-
- You haven't been invited to any events yet.
+ No events yet
-
-
diff --git a/apps/band/src/pages/index.vue b/apps/app/src/pages/index.vue
similarity index 100%
rename from apps/band/src/pages/index.vue
rename to apps/app/src/pages/index.vue
diff --git a/apps/band/src/pages/login.vue b/apps/app/src/pages/login.vue
similarity index 96%
rename from apps/band/src/pages/login.vue
rename to apps/app/src/pages/login.vue
index e14e37a..15715c1 100644
--- a/apps/band/src/pages/login.vue
+++ b/apps/app/src/pages/login.vue
@@ -54,12 +54,12 @@ async function handleLogin() {
})
if (data.success && data.data) {
- // Store token in localStorage (api-client expects it there)
- localStorage.setItem('auth_token', data.data.token)
+ // Store token in cookie (api-client reads from accessToken cookie)
+ document.cookie = `accessToken=${data.data.token}; path=/`
- // Store user data if needed
+ // Store user data in cookie if needed
if (data.data.user) {
- localStorage.setItem('user_data', JSON.stringify(data.data.user))
+ document.cookie = `userData=${JSON.stringify(data.data.user)}; path=/`
}
// Redirect to home or the 'to' query parameter
diff --git a/apps/band/src/plugins/1.router/index.ts b/apps/app/src/plugins/1.router/index.ts
similarity index 100%
rename from apps/band/src/plugins/1.router/index.ts
rename to apps/app/src/plugins/1.router/index.ts
diff --git a/apps/band/src/plugins/2.pinia.ts b/apps/app/src/plugins/2.pinia.ts
similarity index 100%
rename from apps/band/src/plugins/2.pinia.ts
rename to apps/app/src/plugins/2.pinia.ts
diff --git a/apps/band/src/plugins/iconify/build-icons.ts b/apps/app/src/plugins/iconify/build-icons.ts
similarity index 100%
rename from apps/band/src/plugins/iconify/build-icons.ts
rename to apps/app/src/plugins/iconify/build-icons.ts
diff --git a/apps/band/src/plugins/iconify/icons.css b/apps/app/src/plugins/iconify/icons.css
similarity index 100%
rename from apps/band/src/plugins/iconify/icons.css
rename to apps/app/src/plugins/iconify/icons.css
diff --git a/apps/band/src/plugins/iconify/index.ts b/apps/app/src/plugins/iconify/index.ts
similarity index 100%
rename from apps/band/src/plugins/iconify/index.ts
rename to apps/app/src/plugins/iconify/index.ts
diff --git a/apps/band/src/plugins/iconify/package.json b/apps/app/src/plugins/iconify/package.json
similarity index 100%
rename from apps/band/src/plugins/iconify/package.json
rename to apps/app/src/plugins/iconify/package.json
diff --git a/apps/band/src/plugins/layouts.ts b/apps/app/src/plugins/layouts.ts
similarity index 100%
rename from apps/band/src/plugins/layouts.ts
rename to apps/app/src/plugins/layouts.ts
diff --git a/apps/band/src/plugins/vuetify/defaults.ts b/apps/app/src/plugins/vuetify/defaults.ts
similarity index 100%
rename from apps/band/src/plugins/vuetify/defaults.ts
rename to apps/app/src/plugins/vuetify/defaults.ts
diff --git a/apps/band/src/plugins/vuetify/icons.ts b/apps/app/src/plugins/vuetify/icons.ts
similarity index 100%
rename from apps/band/src/plugins/vuetify/icons.ts
rename to apps/app/src/plugins/vuetify/icons.ts
diff --git a/apps/band/src/plugins/vuetify/index.ts b/apps/app/src/plugins/vuetify/index.ts
similarity index 100%
rename from apps/band/src/plugins/vuetify/index.ts
rename to apps/app/src/plugins/vuetify/index.ts
diff --git a/apps/band/src/plugins/vuetify/theme.ts b/apps/app/src/plugins/vuetify/theme.ts
similarity index 100%
rename from apps/band/src/plugins/vuetify/theme.ts
rename to apps/app/src/plugins/vuetify/theme.ts
diff --git a/apps/band/src/plugins/webfontloader.ts b/apps/app/src/plugins/webfontloader.ts
similarity index 100%
rename from apps/band/src/plugins/webfontloader.ts
rename to apps/app/src/plugins/webfontloader.ts
diff --git a/apps/app/src/types/events.ts b/apps/app/src/types/events.ts
new file mode 100644
index 0000000..b24334f
--- /dev/null
+++ b/apps/app/src/types/events.ts
@@ -0,0 +1,44 @@
+export interface ApiResponse
{
+ success: boolean
+ data: T
+ message?: string
+}
+
+export interface Pagination {
+ current_page: number
+ per_page: number
+ total: number
+ last_page: number
+ from: number | null
+ to: number | null
+}
+
+/** EventCrew festival / multi-day event (aligned with API `EventResource`). */
+export type EventCrewEventStatus =
+ | 'draft'
+ | 'published'
+ | 'registration_open'
+ | 'buildup'
+ | 'showday'
+ | 'teardown'
+ | 'closed'
+
+export interface OrganisationSummary {
+ id: string
+ name: string
+ slug: string
+}
+
+export interface Event {
+ id: string
+ organisation_id: string
+ name: string
+ slug: string
+ start_date: string
+ end_date: string
+ timezone: string
+ status: EventCrewEventStatus
+ created_at: string
+ updated_at: string
+ organisation?: OrganisationSummary
+}
diff --git a/apps/band/src/utils/constants.ts b/apps/app/src/utils/constants.ts
similarity index 100%
rename from apps/band/src/utils/constants.ts
rename to apps/app/src/utils/constants.ts
diff --git a/apps/band/src/utils/paginationMeta.ts b/apps/app/src/utils/paginationMeta.ts
similarity index 100%
rename from apps/band/src/utils/paginationMeta.ts
rename to apps/app/src/utils/paginationMeta.ts
diff --git a/apps/band/src/views/pages/authentication/AuthProvider.vue b/apps/app/src/views/pages/authentication/AuthProvider.vue
similarity index 100%
rename from apps/band/src/views/pages/authentication/AuthProvider.vue
rename to apps/app/src/views/pages/authentication/AuthProvider.vue
diff --git a/apps/band/themeConfig.ts b/apps/app/themeConfig.ts
similarity index 100%
rename from apps/band/themeConfig.ts
rename to apps/app/themeConfig.ts
diff --git a/apps/band/tsconfig.json b/apps/app/tsconfig.json
similarity index 100%
rename from apps/band/tsconfig.json
rename to apps/app/tsconfig.json
diff --git a/apps/band/typed-router.d.ts b/apps/app/typed-router.d.ts
similarity index 100%
rename from apps/band/typed-router.d.ts
rename to apps/app/typed-router.d.ts
diff --git a/apps/band/vite.config.ts b/apps/app/vite.config.ts
similarity index 96%
rename from apps/band/vite.config.ts
rename to apps/app/vite.config.ts
index 0ac22ad..a6b520f 100644
--- a/apps/band/vite.config.ts
+++ b/apps/app/vite.config.ts
@@ -96,6 +96,12 @@ export default defineConfig({
},
server: {
port: 5174,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8000',
+ changeOrigin: true,
+ },
+ },
},
build: {
chunkSizeWarningLimit: 5000,
diff --git a/apps/band/src/composables/useApi.ts b/apps/band/src/composables/useApi.ts
deleted file mode 100644
index e4b7811..0000000
--- a/apps/band/src/composables/useApi.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { createFetch } from '@vueuse/core'
-import { destr } from 'destr'
-
-export const useApi = createFetch({
- baseUrl: import.meta.env.VITE_API_BASE_URL || '/api',
- fetchOptions: {
- headers: {
- Accept: 'application/json',
- },
- },
- options: {
- refetch: true,
- async beforeFetch({ options }) {
- const accessToken = useCookie('accessToken').value
-
- if (accessToken) {
- options.headers = {
- ...options.headers,
- Authorization: `Bearer ${accessToken}`,
- }
- }
-
- return { options }
- },
- afterFetch(ctx) {
- const { data, response } = ctx
-
- // Parse data if it's JSON
-
- let parsedData = null
- try {
- parsedData = destr(data)
- }
- catch (error) {
- console.error(error)
- }
-
- return { data: parsedData, response }
- },
- },
-})
diff --git a/apps/band/src/composables/useEvents.ts b/apps/band/src/composables/useEvents.ts
deleted file mode 100644
index 1842d85..0000000
--- a/apps/band/src/composables/useEvents.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import { ref, computed } from 'vue'
-import { apiClient } from '@/lib/api-client'
-import type {
- Event,
- RsvpEventData,
- ApiResponse,
- Pagination,
-} from '@/types/events'
-
-export function useEvents() {
- const events = ref([])
- const currentEvent = ref(null)
- const pagination = ref(null)
- const isLoading = ref(false)
- const error = ref(null)
-
- // Fetch events where user is invited
- // Note: The API should filter events by invitations, or we can filter client-side
- async function fetchInvitedEvents(params?: { page?: number; per_page?: number }) {
- isLoading.value = true
- error.value = null
-
- try {
- const { data } = await apiClient.get>('/events', { params })
- // Filter events where user has an invitation
- // This assumes the API returns all events and we filter client-side
- // In production, you might want an API endpoint like /events/invited
- events.value = data.data.filter(event => event.invitations && event.invitations.length > 0)
- pagination.value = data.meta?.pagination || null
- } catch (err) {
- error.value = err instanceof Error ? err : new Error('Failed to fetch events')
- throw error.value
- } finally {
- isLoading.value = false
- }
- }
-
- // Fetch single event (must be invited)
- async function fetchEvent(id: string) {
- isLoading.value = true
- error.value = null
-
- try {
- const { data } = await apiClient.get>(`/events/${id}`)
- // Verify user has invitation
- if (!data.data.invitations || data.data.invitations.length === 0) {
- throw new Error('You are not invited to this event')
- }
- currentEvent.value = data.data
- return data.data
- } catch (err) {
- error.value = err instanceof Error ? err : new Error('Failed to fetch event')
- throw error.value
- } finally {
- isLoading.value = false
- }
- }
-
- // RSVP to event
- async function rsvpToEvent(eventId: string, rsvpData: RsvpEventData) {
- isLoading.value = true
- error.value = null
-
- try {
- const { data } = await apiClient.post>(
- `/events/${eventId}/rsvp`,
- rsvpData
- )
- // Refresh event to get updated invitation status
- if (currentEvent.value?.id === eventId) {
- await fetchEvent(eventId)
- }
- return data.data
- } catch (err) {
- error.value = err instanceof Error ? err : new Error('Failed to update RSVP')
- throw error.value
- } finally {
- isLoading.value = false
- }
- }
-
- // Get current user's invitation for an event
- function getMyInvitation(event: Event) {
- if (!event.invitations || event.invitations.length === 0) {
- return null
- }
- // This assumes the API includes the current user's ID in the invitation
- // You might need to adjust this based on your API response
- return event.invitations[0] // For now, return first invitation
- }
-
- return {
- // State
- events: computed(() => events.value),
- currentEvent: computed(() => currentEvent.value),
- pagination: computed(() => pagination.value),
- isLoading: computed(() => isLoading.value),
- error: computed(() => error.value),
-
- // Actions
- fetchInvitedEvents,
- fetchEvent,
- rsvpToEvent,
- getMyInvitation,
- }
-}
-
-
diff --git a/apps/band/src/pages/events/[id].vue b/apps/band/src/pages/events/[id].vue
deleted file mode 100644
index 2d8a7fb..0000000
--- a/apps/band/src/pages/events/[id].vue
+++ /dev/null
@@ -1,221 +0,0 @@
-
-
-
-
-
-
-
-
-
- {{ currentEvent.title }}
-
- {{ currentEvent.status_label }}
-
-
-
-
-
-
-
-
-
-
- Event Date
-
-
- {{ new Date(currentEvent.event_date).toLocaleDateString() }}
-
-
-
-
-
- Time
-
-
- {{ currentEvent.start_time }}
- - {{ currentEvent.end_time }}
-
-
-
-
-
- Location
-
-
- {{ currentEvent.location.name }}
-
- {{ currentEvent.location.address }}, {{ currentEvent.location.city }}
-
-
-
-
-
-
- Description
-
-
- {{ currentEvent.description }}
-
-
-
-
-
- Notes
-
-
- {{ currentEvent.notes }}
-
-
-
-
-
- Setlist
-
-
- {{ currentEvent.setlist.name }}
-
-
-
-
-
-
- RSVP
-
-
-
-
-
-
-
-
- Update RSVP
-
-
-
-
- RSVP Deadline: {{ new Date(currentEvent.rsvp_deadline).toLocaleString() }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/band/src/types/events.ts b/apps/band/src/types/events.ts
deleted file mode 100644
index cda1046..0000000
--- a/apps/band/src/types/events.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-// API Response wrapper
-export interface ApiResponse {
- success: boolean
- data: T
- message?: string
- meta?: {
- pagination?: Pagination
- }
-}
-
-export interface Pagination {
- current_page: number
- per_page: number
- total: number
- last_page: number
- from: number
- to: number
-}
-
-// Event types
-export type EventStatus = 'draft' | 'pending' | 'confirmed' | 'completed' | 'cancelled'
-export type EventVisibility = 'private' | 'members' | 'public'
-export type RsvpStatus = 'pending' | 'available' | 'unavailable' | 'tentative'
-
-export interface Location {
- id: string
- name: string
- address: string
- city: string
- postal_code: string | null
- country: string
- latitude: number | null
- longitude: number | null
- capacity: number | null
- contact_name: string | null
- contact_email: string | null
- contact_phone: string | null
- created_at: string
- updated_at: string
-}
-
-export interface Customer {
- id: string
- name: string
- company_name: string | null
- type: 'individual' | 'company'
- email: string | null
- phone: string | null
- address: string | null
- city: string | null
- postal_code: string | null
- country: string
- is_portal_enabled: boolean
- created_at: string
- updated_at: string
-}
-
-export interface Setlist {
- id: string
- name: string
- description: string | null
- total_duration_seconds: number | null
- is_template: boolean
- is_archived: boolean
- created_at: string
- updated_at: string
-}
-
-export interface User {
- id: string
- name: string
- email: string
- phone: string | null
- bio: string | null
- instruments: string[] | null
- avatar_path: string | null
- type: 'member' | 'customer'
- role: 'admin' | 'booking_agent' | 'music_manager' | 'member' | null
- status: 'active' | 'inactive'
- created_at: string
- updated_at: string
-}
-
-export interface EventInvitation {
- id: string
- event_id: string
- user_id: string
- rsvp_status: RsvpStatus
- rsvp_note: string | null
- rsvp_responded_at: string | null
- invited_at: string
- user?: User
-}
-
-export interface Event {
- id: string
- title: string
- description: string | null
- event_date: string
- start_time: string
- end_time: string | null
- load_in_time: string | null
- soundcheck_time: string | null
- fee: number | null
- currency: string
- status: EventStatus
- status_label: string
- status_color: string
- visibility: EventVisibility
- visibility_label: string
- rsvp_deadline: string | null
- notes: string | null
- internal_notes: string | null
- is_public_setlist: boolean
- location: Location | null
- customer: Customer | null
- setlist: Setlist | null
- invitations: EventInvitation[]
- creator: User | null
- created_at: string
- updated_at: string
-}
-
-export interface RsvpEventData {
- status: RsvpStatus
- note?: string
-}
-
-
diff --git a/apps/band/src/utils/api.ts b/apps/band/src/utils/api.ts
deleted file mode 100644
index e21402d..0000000
--- a/apps/band/src/utils/api.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { ofetch } from 'ofetch'
-
-export const $api = ofetch.create({
- baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
- async onRequest({ options }) {
- const accessToken = useCookie('accessToken').value
- if (accessToken)
- options.headers.append('Authorization', `Bearer ${accessToken}`)
- },
-})
diff --git a/apps/customers/public/mockServiceWorker.js b/apps/customers/public/mockServiceWorker.js
deleted file mode 100644
index dbc5b1d..0000000
--- a/apps/customers/public/mockServiceWorker.js
+++ /dev/null
@@ -1,307 +0,0 @@
-/* eslint-disable */
-/* tslint:disable */
-
-/**
- * Mock Service Worker.
- * @see https://github.com/mswjs/msw
- * - Please do NOT modify this file.
- * - Please do NOT serve this file on production.
- */
-
-const PACKAGE_VERSION = '2.6.8'
-const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
-const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
-const activeClientIds = new Set()
-
-self.addEventListener('install', function () {
- self.skipWaiting()
-})
-
-self.addEventListener('activate', function (event) {
- event.waitUntil(self.clients.claim())
-})
-
-self.addEventListener('message', async function (event) {
- const clientId = event.source.id
-
- if (!clientId || !self.clients) {
- return
- }
-
- const client = await self.clients.get(clientId)
-
- if (!client) {
- return
- }
-
- const allClients = await self.clients.matchAll({
- type: 'window',
- })
-
- switch (event.data) {
- case 'KEEPALIVE_REQUEST': {
- sendToClient(client, {
- type: 'KEEPALIVE_RESPONSE',
- })
- break
- }
-
- case 'INTEGRITY_CHECK_REQUEST': {
- sendToClient(client, {
- type: 'INTEGRITY_CHECK_RESPONSE',
- payload: {
- packageVersion: PACKAGE_VERSION,
- checksum: INTEGRITY_CHECKSUM,
- },
- })
- break
- }
-
- case 'MOCK_ACTIVATE': {
- activeClientIds.add(clientId)
-
- sendToClient(client, {
- type: 'MOCKING_ENABLED',
- payload: {
- client: {
- id: client.id,
- frameType: client.frameType,
- },
- },
- })
- break
- }
-
- case 'MOCK_DEACTIVATE': {
- activeClientIds.delete(clientId)
- break
- }
-
- case 'CLIENT_CLOSED': {
- activeClientIds.delete(clientId)
-
- const remainingClients = allClients.filter((client) => {
- return client.id !== clientId
- })
-
- // Unregister itself when there are no more clients
- if (remainingClients.length === 0) {
- self.registration.unregister()
- }
-
- break
- }
- }
-})
-
-self.addEventListener('fetch', function (event) {
- const { request } = event
-
- // Bypass navigation requests.
- if (request.mode === 'navigate') {
- return
- }
-
- // Opening the DevTools triggers the "only-if-cached" request
- // that cannot be handled by the worker. Bypass such requests.
- if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
- return
- }
-
- // Bypass all requests when there are no active clients.
- // Prevents the self-unregistered worked from handling requests
- // after it's been deleted (still remains active until the next reload).
- if (activeClientIds.size === 0) {
- return
- }
-
- // Generate unique request ID.
- const requestId = crypto.randomUUID()
- event.respondWith(handleRequest(event, requestId))
-})
-
-async function handleRequest(event, requestId) {
- const client = await resolveMainClient(event)
- const response = await getResponse(event, client, requestId)
-
- // Send back the response clone for the "response:*" life-cycle events.
- // Ensure MSW is active and ready to handle the message, otherwise
- // this message will pend indefinitely.
- if (client && activeClientIds.has(client.id)) {
- ;(async function () {
- const responseClone = response.clone()
-
- sendToClient(
- client,
- {
- type: 'RESPONSE',
- payload: {
- requestId,
- isMockedResponse: IS_MOCKED_RESPONSE in response,
- type: responseClone.type,
- status: responseClone.status,
- statusText: responseClone.statusText,
- body: responseClone.body,
- headers: Object.fromEntries(responseClone.headers.entries()),
- },
- },
- [responseClone.body],
- )
- })()
- }
-
- return response
-}
-
-// Resolve the main client for the given event.
-// Client that issues a request doesn't necessarily equal the client
-// that registered the worker. It's with the latter the worker should
-// communicate with during the response resolving phase.
-async function resolveMainClient(event) {
- const client = await self.clients.get(event.clientId)
-
- if (activeClientIds.has(event.clientId)) {
- return client
- }
-
- if (client?.frameType === 'top-level') {
- return client
- }
-
- const allClients = await self.clients.matchAll({
- type: 'window',
- })
-
- return allClients
- .filter((client) => {
- // Get only those clients that are currently visible.
- return client.visibilityState === 'visible'
- })
- .find((client) => {
- // Find the client ID that's recorded in the
- // set of clients that have registered the worker.
- return activeClientIds.has(client.id)
- })
-}
-
-async function getResponse(event, client, requestId) {
- const { request } = event
-
- // Clone the request because it might've been already used
- // (i.e. its body has been read and sent to the client).
- const requestClone = request.clone()
-
- function passthrough() {
- // Cast the request headers to a new Headers instance
- // so the headers can be manipulated with.
- const headers = new Headers(requestClone.headers)
-
- // Remove the "accept" header value that marked this request as passthrough.
- // This prevents request alteration and also keeps it compliant with the
- // user-defined CORS policies.
- const acceptHeader = headers.get('accept')
- if (acceptHeader) {
- const values = acceptHeader.split(',').map((value) => value.trim())
- const filteredValues = values.filter(
- (value) => value !== 'msw/passthrough',
- )
-
- if (filteredValues.length > 0) {
- headers.set('accept', filteredValues.join(', '))
- } else {
- headers.delete('accept')
- }
- }
-
- return fetch(requestClone, { headers })
- }
-
- // Bypass mocking when the client is not active.
- if (!client) {
- return passthrough()
- }
-
- // Bypass initial page load requests (i.e. static assets).
- // The absence of the immediate/parent client in the map of the active clients
- // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
- // and is not ready to handle requests.
- if (!activeClientIds.has(client.id)) {
- return passthrough()
- }
-
- // Notify the client that a request has been intercepted.
- const requestBuffer = await request.arrayBuffer()
- const clientMessage = await sendToClient(
- client,
- {
- type: 'REQUEST',
- payload: {
- id: requestId,
- url: request.url,
- mode: request.mode,
- method: request.method,
- headers: Object.fromEntries(request.headers.entries()),
- cache: request.cache,
- credentials: request.credentials,
- destination: request.destination,
- integrity: request.integrity,
- redirect: request.redirect,
- referrer: request.referrer,
- referrerPolicy: request.referrerPolicy,
- body: requestBuffer,
- keepalive: request.keepalive,
- },
- },
- [requestBuffer],
- )
-
- switch (clientMessage.type) {
- case 'MOCK_RESPONSE': {
- return respondWithMock(clientMessage.data)
- }
-
- case 'PASSTHROUGH': {
- return passthrough()
- }
- }
-
- return passthrough()
-}
-
-function sendToClient(client, message, transferrables = []) {
- return new Promise((resolve, reject) => {
- const channel = new MessageChannel()
-
- channel.port1.onmessage = (event) => {
- if (event.data && event.data.error) {
- return reject(event.data.error)
- }
-
- resolve(event.data)
- }
-
- client.postMessage(
- message,
- [channel.port2].concat(transferrables.filter(Boolean)),
- )
- })
-}
-
-async function respondWithMock(response) {
- // Setting response status code to 0 is a no-op.
- // However, when responding with a "Response.error()", the produced Response
- // instance will have status code set to 0. Since it's not possible to create
- // a Response instance with status code 0, handle that use-case separately.
- if (response.status === 0) {
- return Response.error()
- }
-
- const mockedResponse = new Response(response.body, response)
-
- Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
- value: true,
- enumerable: true,
- })
-
- return mockedResponse
-}
diff --git a/apps/customers/src/@core/components/BuyNow.vue b/apps/customers/src/@core/components/BuyNow.vue
deleted file mode 100644
index b550454..0000000
--- a/apps/customers/src/@core/components/BuyNow.vue
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
- Buy Now
-
-
-
-
-
diff --git a/apps/customers/src/@core/components/TheCustomizer.vue b/apps/customers/src/@core/components/TheCustomizer.vue
deleted file mode 100644
index 08cf3bf..0000000
--- a/apps/customers/src/@core/components/TheCustomizer.vue
+++ /dev/null
@@ -1,629 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Theme Customizer
-
-
- Customize & Preview in Real Time
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Primary Color
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Theme
-
-
-
-
- {{ item?.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- Skins
-
-
-
-
- {{ item?.label }}
-
-
-
-
-
-
-
- Semi Dark Menu
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Layout
-
-
-
-
- {{ item.label }}
-
-
-
-
-
-
-
- Content
-
-
-
-
- {{ item.label }}
-
-
-
-
-
-
-
- Direction
-
-
-
-
- {{ item?.label }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/components/dialogs/AddAuthenticatorAppDialog.vue b/apps/customers/src/components/dialogs/AddAuthenticatorAppDialog.vue
deleted file mode 100644
index 92dcf60..0000000
--- a/apps/customers/src/components/dialogs/AddAuthenticatorAppDialog.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
- $emit('update:isDialogVisible', val)"
- >
-
-
-
-
-
-
-
- Add Authenticator App
-
-
- Authenticator Apps
-
-
-
- Using an authenticator app like Google Authenticator, Microsoft Authenticator, Authy, or 1Password, scan the QR code. It will generate a 6 digit code for you to enter below.
-
-
-
-
-
-
-
- {}">
-
-
-
-
- Cancel
-
-
-
- Continue
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/components/dialogs/AddPaymentMethodDialog.vue b/apps/customers/src/components/dialogs/AddPaymentMethodDialog.vue
deleted file mode 100644
index 4814d15..0000000
--- a/apps/customers/src/components/dialogs/AddPaymentMethodDialog.vue
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Add payment methods
-
-
- Supported payment methods
-
-
-
-
-
-
-
- {{ item.title }}
-
-
-
- {{ item.type }}
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/components/dialogs/CardAddEditDialog.vue b/apps/customers/src/components/dialogs/CardAddEditDialog.vue
deleted file mode 100644
index b31f38e..0000000
--- a/apps/customers/src/components/dialogs/CardAddEditDialog.vue
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- {{ props.cardDetails.name ? 'Edit Card' : 'Add New Card' }}
-
-
-
- {{ props.cardDetails.name ? 'Edit your saved card details' : 'Add card for future billing' }}
-
-
-
-
- {}">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Submit
-
-
- Cancel
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/components/dialogs/PricingPlanDialog.vue b/apps/customers/src/components/dialogs/PricingPlanDialog.vue
deleted file mode 100644
index 0ce068a..0000000
--- a/apps/customers/src/components/dialogs/PricingPlanDialog.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/components/dialogs/ReferAndEarnDialog.vue b/apps/customers/src/components/dialogs/ReferAndEarnDialog.vue
deleted file mode 100644
index 58cb10d..0000000
--- a/apps/customers/src/components/dialogs/ReferAndEarnDialog.vue
+++ /dev/null
@@ -1,181 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Refer & Earn
-
-
- Invite your friend to {{ themeConfig.app.title }} , if they sign up, you and your friend will get 30 days free trial
-
-
-
-
-
-
-
-
-
- {{ step.title }}
-
- {{ step.subtitle }}
-
-
-
-
-
-
- Invite your friends
-
-
- {}"
- >
-
-
-
- Send
-
-
-
-
- Share the referral link
-
-
- {}"
- >
-
-
-
- Copy link
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/components/dialogs/UserUpgradePlanDialog.vue b/apps/customers/src/components/dialogs/UserUpgradePlanDialog.vue
deleted file mode 100644
index 0bbcb64..0000000
--- a/apps/customers/src/components/dialogs/UserUpgradePlanDialog.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- Upgrade Plan
-
-
- Choose the best plan for user.
-
-
-
-
-
-
-
- User current plan is standard plan
-
-
-
- $
-
- 99
-
-
- / month
-
-
-
- Cancel Subscription
-
-
-
-
-
-
-
-
-
diff --git a/apps/customers/src/composables/useApi.ts b/apps/customers/src/composables/useApi.ts
deleted file mode 100644
index e4b7811..0000000
--- a/apps/customers/src/composables/useApi.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { createFetch } from '@vueuse/core'
-import { destr } from 'destr'
-
-export const useApi = createFetch({
- baseUrl: import.meta.env.VITE_API_BASE_URL || '/api',
- fetchOptions: {
- headers: {
- Accept: 'application/json',
- },
- },
- options: {
- refetch: true,
- async beforeFetch({ options }) {
- const accessToken = useCookie('accessToken').value
-
- if (accessToken) {
- options.headers = {
- ...options.headers,
- Authorization: `Bearer ${accessToken}`,
- }
- }
-
- return { options }
- },
- afterFetch(ctx) {
- const { data, response } = ctx
-
- // Parse data if it's JSON
-
- let parsedData = null
- try {
- parsedData = destr(data)
- }
- catch (error) {
- console.error(error)
- }
-
- return { data: parsedData, response }
- },
- },
-})
diff --git a/apps/customers/src/pages/second-page.vue b/apps/customers/src/pages/second-page.vue
deleted file mode 100644
index 630d8ab..0000000
--- a/apps/customers/src/pages/second-page.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- This is your second page.
-
- Chocolate sesame snaps pie carrot cake pastry pie lollipop muffin.
- Carrot cake dragée chupa chups jujubes. Macaroon liquorice cookie
- wafer tart marzipan bonbon. Gingerbread jelly-o dragée
- chocolate.
-
-
-
-
diff --git a/apps/customers/src/utils/api.ts b/apps/customers/src/utils/api.ts
deleted file mode 100644
index e21402d..0000000
--- a/apps/customers/src/utils/api.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { ofetch } from 'ofetch'
-
-export const $api = ofetch.create({
- baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
- async onRequest({ options }) {
- const accessToken = useCookie('accessToken').value
- if (accessToken)
- options.headers.append('Authorization', `Bearer ${accessToken}`)
- },
-})
diff --git a/apps/customers/README 2.md b/apps/portal/README 2.md
similarity index 100%
rename from apps/customers/README 2.md
rename to apps/portal/README 2.md
diff --git a/apps/customers/README.md b/apps/portal/README.md
similarity index 100%
rename from apps/customers/README.md
rename to apps/portal/README.md
diff --git a/apps/customers/auto-imports.d.ts b/apps/portal/auto-imports.d.ts
similarity index 100%
rename from apps/customers/auto-imports.d.ts
rename to apps/portal/auto-imports.d.ts
diff --git a/apps/customers/components.d.ts b/apps/portal/components.d.ts
similarity index 100%
rename from apps/customers/components.d.ts
rename to apps/portal/components.d.ts
diff --git a/apps/customers/dev.Dockerfile b/apps/portal/dev.Dockerfile
similarity index 100%
rename from apps/customers/dev.Dockerfile
rename to apps/portal/dev.Dockerfile
diff --git a/apps/customers/docker-compose.dev.yml b/apps/portal/docker-compose.dev.yml
similarity index 100%
rename from apps/customers/docker-compose.dev.yml
rename to apps/portal/docker-compose.dev.yml
diff --git a/apps/customers/docker-compose.prod.yml b/apps/portal/docker-compose.prod.yml
similarity index 100%
rename from apps/customers/docker-compose.prod.yml
rename to apps/portal/docker-compose.prod.yml
diff --git a/apps/customers/env.d.ts b/apps/portal/env.d.ts
similarity index 100%
rename from apps/customers/env.d.ts
rename to apps/portal/env.d.ts
diff --git a/apps/customers/index.html b/apps/portal/index.html
similarity index 97%
rename from apps/customers/index.html
rename to apps/portal/index.html
index aaac449..b4c89bb 100644
--- a/apps/customers/index.html
+++ b/apps/portal/index.html
@@ -6,7 +6,7 @@
- Band Management - Customer Portal
+ Event Crew - Customer Portal
diff --git a/apps/customers/nginx.conf b/apps/portal/nginx.conf
similarity index 100%
rename from apps/customers/nginx.conf
rename to apps/portal/nginx.conf
diff --git a/apps/band/package.json b/apps/portal/package.json
similarity index 97%
rename from apps/band/package.json
rename to apps/portal/package.json
index fef4908..ce41f8b 100644
--- a/apps/band/package.json
+++ b/apps/portal/package.json
@@ -1,5 +1,5 @@
{
- "name": "vuexy-vuejs-admin-template",
+ "name": "eventcrew-portal",
"version": "9.5.0",
"private": true,
"type": "module",
@@ -19,6 +19,7 @@
"@floating-ui/dom": "1.6.8",
"@formkit/drag-and-drop": "0.1.6",
"@sindresorhus/is": "7.1.0",
+ "@tanstack/vue-query": "^5.95.2",
"@tiptap/extension-highlight": "^2.27.1",
"@tiptap/extension-image": "^2.27.1",
"@tiptap/extension-link": "^2.27.1",
@@ -53,7 +54,8 @@
"vue3-apexcharts": "1.5.3",
"vue3-perfect-scrollbar": "2.0.0",
"vuetify": "3.10.8",
- "webfontloader": "1.6.28"
+ "webfontloader": "1.6.28",
+ "zod": "^3.25.76"
},
"devDependencies": {
"@antfu/eslint-config-vue": "0.43.1",
diff --git a/apps/band/pnpm-lock.yaml b/apps/portal/pnpm-lock.yaml
similarity index 99%
rename from apps/band/pnpm-lock.yaml
rename to apps/portal/pnpm-lock.yaml
index c8879c3..a285949 100644
--- a/apps/band/pnpm-lock.yaml
+++ b/apps/portal/pnpm-lock.yaml
@@ -27,6 +27,9 @@ importers:
'@sindresorhus/is':
specifier: 7.1.0
version: 7.1.0
+ '@tanstack/vue-query':
+ specifier: ^5.95.2
+ version: 5.95.2(vue@3.5.22(typescript@5.9.3))
'@tiptap/extension-highlight':
specifier: ^2.27.1
version: 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))
@@ -132,6 +135,9 @@ importers:
webfontloader:
specifier: 1.6.28
version: 1.6.28
+ zod:
+ specifier: ^3.25.76
+ version: 3.25.76
devDependencies:
'@antfu/eslint-config-vue':
specifier: 0.43.1
@@ -1025,56 +1031,67 @@ packages:
resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.52.5':
resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
cpu: [arm]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.52.5':
resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.52.5':
resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.52.5':
resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
cpu: [loong64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.52.5':
resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.52.5':
resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.52.5':
resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.52.5':
resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.52.5':
resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.52.5':
resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-openharmony-arm64@4.52.5':
resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
@@ -1160,6 +1177,22 @@ packages:
peerDependencies:
stylelint: ^16.0.2
+ '@tanstack/match-sorter-utils@8.19.4':
+ resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
+ engines: {node: '>=12'}
+
+ '@tanstack/query-core@5.95.2':
+ resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==}
+
+ '@tanstack/vue-query@5.95.2':
+ resolution: {integrity: sha512-GleO0GrUPdvObtff/D3iQ5kUERQM3dM6vT5pWl4zC3ap2JO84x4SQbUa1G7czKx96lETRiHnw7ZuatSRaaZqQQ==}
+ peerDependencies:
+ '@vue/composition-api': ^1.1.2
+ vue: ^2.6.0 || ^3.3.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
'@tiptap/core@2.27.1':
resolution: {integrity: sha512-nkerkl8syHj44ZzAB7oA2GPmmZINKBKCa79FuNvmGJrJ4qyZwlkDzszud23YteFZEytbc87kVd/fP76ROS6sLg==}
peerDependencies:
@@ -1650,41 +1683,49 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -3859,6 +3900,9 @@ packages:
resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==}
hasBin: true
+ remove-accents@0.5.0:
+ resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
+
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -4801,6 +4845,9 @@ packages:
resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
engines: {node: '>=18'}
+ zod@3.25.76:
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -5690,6 +5737,20 @@ snapshots:
style-search: 0.1.0
stylelint: 16.8.0(typescript@5.9.3)
+ '@tanstack/match-sorter-utils@8.19.4':
+ dependencies:
+ remove-accents: 0.5.0
+
+ '@tanstack/query-core@5.95.2': {}
+
+ '@tanstack/vue-query@5.95.2(vue@3.5.22(typescript@5.9.3))':
+ dependencies:
+ '@tanstack/match-sorter-utils': 8.19.4
+ '@tanstack/query-core': 5.95.2
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.22(typescript@5.9.3)
+ vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3))
+
'@tiptap/core@2.27.1(@tiptap/pm@2.27.1)':
dependencies:
'@tiptap/pm': 2.27.1
@@ -8848,6 +8909,8 @@ snapshots:
dependencies:
jsesc: 0.5.0
+ remove-accents@0.5.0: {}
+
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
@@ -9964,4 +10027,6 @@ snapshots:
yoctocolors@2.1.2: {}
+ zod@3.25.76: {}
+
zwitch@2.0.4: {}
diff --git a/apps/customers/prod.Dockerfile b/apps/portal/prod.Dockerfile
similarity index 100%
rename from apps/customers/prod.Dockerfile
rename to apps/portal/prod.Dockerfile
diff --git a/apps/customers/public/favicon.ico b/apps/portal/public/favicon.ico
similarity index 100%
rename from apps/customers/public/favicon.ico
rename to apps/portal/public/favicon.ico
diff --git a/apps/customers/public/images/avatars/avatar-1.png b/apps/portal/public/images/avatars/avatar-1.png
similarity index 100%
rename from apps/customers/public/images/avatars/avatar-1.png
rename to apps/portal/public/images/avatars/avatar-1.png
diff --git a/apps/customers/public/images/avatars/avatar-2.png b/apps/portal/public/images/avatars/avatar-2.png
similarity index 100%
rename from apps/customers/public/images/avatars/avatar-2.png
rename to apps/portal/public/images/avatars/avatar-2.png
diff --git a/apps/customers/public/images/svg/discord.svg b/apps/portal/public/images/svg/discord.svg
similarity index 100%
rename from apps/customers/public/images/svg/discord.svg
rename to apps/portal/public/images/svg/discord.svg
diff --git a/apps/customers/public/images/svg/gift.svg b/apps/portal/public/images/svg/gift.svg
similarity index 100%
rename from apps/customers/public/images/svg/gift.svg
rename to apps/portal/public/images/svg/gift.svg
diff --git a/apps/customers/public/images/svg/keyboard.svg b/apps/portal/public/images/svg/keyboard.svg
similarity index 100%
rename from apps/customers/public/images/svg/keyboard.svg
rename to apps/portal/public/images/svg/keyboard.svg
diff --git a/apps/customers/public/images/svg/laptop.svg b/apps/portal/public/images/svg/laptop.svg
similarity index 100%
rename from apps/customers/public/images/svg/laptop.svg
rename to apps/portal/public/images/svg/laptop.svg
diff --git a/apps/customers/public/images/svg/lightbulb.svg b/apps/portal/public/images/svg/lightbulb.svg
similarity index 100%
rename from apps/customers/public/images/svg/lightbulb.svg
rename to apps/portal/public/images/svg/lightbulb.svg
diff --git a/apps/customers/public/images/svg/rocket.svg b/apps/portal/public/images/svg/rocket.svg
similarity index 100%
rename from apps/customers/public/images/svg/rocket.svg
rename to apps/portal/public/images/svg/rocket.svg
diff --git a/apps/customers/public/loader.css b/apps/portal/public/loader.css
similarity index 100%
rename from apps/customers/public/loader.css
rename to apps/portal/public/loader.css
diff --git a/apps/band/public/mockServiceWorker.js b/apps/portal/public/mockServiceWorker.js
similarity index 100%
rename from apps/band/public/mockServiceWorker.js
rename to apps/portal/public/mockServiceWorker.js
diff --git a/apps/customers/shims.d.ts b/apps/portal/shims.d.ts
similarity index 100%
rename from apps/customers/shims.d.ts
rename to apps/portal/shims.d.ts
diff --git a/apps/customers/src/@core/components/AppBarSearch.vue b/apps/portal/src/@core/components/AppBarSearch.vue
similarity index 100%
rename from apps/customers/src/@core/components/AppBarSearch.vue
rename to apps/portal/src/@core/components/AppBarSearch.vue
diff --git a/apps/customers/src/@core/components/AppDrawerHeaderSection.vue b/apps/portal/src/@core/components/AppDrawerHeaderSection.vue
similarity index 100%
rename from apps/customers/src/@core/components/AppDrawerHeaderSection.vue
rename to apps/portal/src/@core/components/AppDrawerHeaderSection.vue
diff --git a/apps/customers/src/@core/components/AppStepper.vue b/apps/portal/src/@core/components/AppStepper.vue
similarity index 100%
rename from apps/customers/src/@core/components/AppStepper.vue
rename to apps/portal/src/@core/components/AppStepper.vue
diff --git a/apps/band/src/@core/components/BuyNow.vue b/apps/portal/src/@core/components/BuyNow.vue
similarity index 100%
rename from apps/band/src/@core/components/BuyNow.vue
rename to apps/portal/src/@core/components/BuyNow.vue
diff --git a/apps/customers/src/@core/components/CardStatisticsVerticalSimple.vue b/apps/portal/src/@core/components/CardStatisticsVerticalSimple.vue
similarity index 100%
rename from apps/customers/src/@core/components/CardStatisticsVerticalSimple.vue
rename to apps/portal/src/@core/components/CardStatisticsVerticalSimple.vue
diff --git a/apps/customers/src/@core/components/CustomizerSection.vue b/apps/portal/src/@core/components/CustomizerSection.vue
similarity index 100%
rename from apps/customers/src/@core/components/CustomizerSection.vue
rename to apps/portal/src/@core/components/CustomizerSection.vue
diff --git a/apps/customers/src/@core/components/DialogCloseBtn.vue b/apps/portal/src/@core/components/DialogCloseBtn.vue
similarity index 100%
rename from apps/customers/src/@core/components/DialogCloseBtn.vue
rename to apps/portal/src/@core/components/DialogCloseBtn.vue
diff --git a/apps/customers/src/@core/components/DropZone.vue b/apps/portal/src/@core/components/DropZone.vue
similarity index 100%
rename from apps/customers/src/@core/components/DropZone.vue
rename to apps/portal/src/@core/components/DropZone.vue
diff --git a/apps/customers/src/@core/components/I18n.vue b/apps/portal/src/@core/components/I18n.vue
similarity index 100%
rename from apps/customers/src/@core/components/I18n.vue
rename to apps/portal/src/@core/components/I18n.vue
diff --git a/apps/customers/src/@core/components/MoreBtn.vue b/apps/portal/src/@core/components/MoreBtn.vue
similarity index 100%
rename from apps/customers/src/@core/components/MoreBtn.vue
rename to apps/portal/src/@core/components/MoreBtn.vue
diff --git a/apps/customers/src/@core/components/Notifications.vue b/apps/portal/src/@core/components/Notifications.vue
similarity index 100%
rename from apps/customers/src/@core/components/Notifications.vue
rename to apps/portal/src/@core/components/Notifications.vue
diff --git a/apps/customers/src/@core/components/ProductDescriptionEditor.vue b/apps/portal/src/@core/components/ProductDescriptionEditor.vue
similarity index 100%
rename from apps/customers/src/@core/components/ProductDescriptionEditor.vue
rename to apps/portal/src/@core/components/ProductDescriptionEditor.vue
diff --git a/apps/customers/src/@core/components/ScrollToTop.vue b/apps/portal/src/@core/components/ScrollToTop.vue
similarity index 100%
rename from apps/customers/src/@core/components/ScrollToTop.vue
rename to apps/portal/src/@core/components/ScrollToTop.vue
diff --git a/apps/customers/src/@core/components/Shortcuts.vue b/apps/portal/src/@core/components/Shortcuts.vue
similarity index 100%
rename from apps/customers/src/@core/components/Shortcuts.vue
rename to apps/portal/src/@core/components/Shortcuts.vue
diff --git a/apps/customers/src/@core/components/TablePagination.vue b/apps/portal/src/@core/components/TablePagination.vue
similarity index 100%
rename from apps/customers/src/@core/components/TablePagination.vue
rename to apps/portal/src/@core/components/TablePagination.vue
diff --git a/apps/band/src/@core/components/TheCustomizer.vue b/apps/portal/src/@core/components/TheCustomizer.vue
similarity index 100%
rename from apps/band/src/@core/components/TheCustomizer.vue
rename to apps/portal/src/@core/components/TheCustomizer.vue
diff --git a/apps/customers/src/@core/components/ThemeSwitcher.vue b/apps/portal/src/@core/components/ThemeSwitcher.vue
similarity index 100%
rename from apps/customers/src/@core/components/ThemeSwitcher.vue
rename to apps/portal/src/@core/components/ThemeSwitcher.vue
diff --git a/apps/customers/src/@core/components/TiptapEditor.vue b/apps/portal/src/@core/components/TiptapEditor.vue
similarity index 100%
rename from apps/customers/src/@core/components/TiptapEditor.vue
rename to apps/portal/src/@core/components/TiptapEditor.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/AppAutocomplete.vue b/apps/portal/src/@core/components/app-form-elements/AppAutocomplete.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/AppAutocomplete.vue
rename to apps/portal/src/@core/components/app-form-elements/AppAutocomplete.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/AppCombobox.vue b/apps/portal/src/@core/components/app-form-elements/AppCombobox.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/AppCombobox.vue
rename to apps/portal/src/@core/components/app-form-elements/AppCombobox.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/AppDateTimePicker.vue b/apps/portal/src/@core/components/app-form-elements/AppDateTimePicker.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/AppDateTimePicker.vue
rename to apps/portal/src/@core/components/app-form-elements/AppDateTimePicker.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/AppSelect.vue b/apps/portal/src/@core/components/app-form-elements/AppSelect.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/AppSelect.vue
rename to apps/portal/src/@core/components/app-form-elements/AppSelect.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/AppTextField.vue b/apps/portal/src/@core/components/app-form-elements/AppTextField.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/AppTextField.vue
rename to apps/portal/src/@core/components/app-form-elements/AppTextField.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/AppTextarea.vue b/apps/portal/src/@core/components/app-form-elements/AppTextarea.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/AppTextarea.vue
rename to apps/portal/src/@core/components/app-form-elements/AppTextarea.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/CustomCheckboxes.vue b/apps/portal/src/@core/components/app-form-elements/CustomCheckboxes.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/CustomCheckboxes.vue
rename to apps/portal/src/@core/components/app-form-elements/CustomCheckboxes.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue b/apps/portal/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue
rename to apps/portal/src/@core/components/app-form-elements/CustomCheckboxesWithIcon.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue b/apps/portal/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue
rename to apps/portal/src/@core/components/app-form-elements/CustomCheckboxesWithImage.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/CustomRadios.vue b/apps/portal/src/@core/components/app-form-elements/CustomRadios.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/CustomRadios.vue
rename to apps/portal/src/@core/components/app-form-elements/CustomRadios.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue b/apps/portal/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue
rename to apps/portal/src/@core/components/app-form-elements/CustomRadiosWithIcon.vue
diff --git a/apps/customers/src/@core/components/app-form-elements/CustomRadiosWithImage.vue b/apps/portal/src/@core/components/app-form-elements/CustomRadiosWithImage.vue
similarity index 100%
rename from apps/customers/src/@core/components/app-form-elements/CustomRadiosWithImage.vue
rename to apps/portal/src/@core/components/app-form-elements/CustomRadiosWithImage.vue
diff --git a/apps/customers/src/@core/components/cards/AppCardActions.vue b/apps/portal/src/@core/components/cards/AppCardActions.vue
similarity index 100%
rename from apps/customers/src/@core/components/cards/AppCardActions.vue
rename to apps/portal/src/@core/components/cards/AppCardActions.vue
diff --git a/apps/customers/src/@core/components/cards/AppCardCode.vue b/apps/portal/src/@core/components/cards/AppCardCode.vue
similarity index 100%
rename from apps/customers/src/@core/components/cards/AppCardCode.vue
rename to apps/portal/src/@core/components/cards/AppCardCode.vue
diff --git a/apps/customers/src/@core/components/cards/CardStatisticsHorizontal.vue b/apps/portal/src/@core/components/cards/CardStatisticsHorizontal.vue
similarity index 100%
rename from apps/customers/src/@core/components/cards/CardStatisticsHorizontal.vue
rename to apps/portal/src/@core/components/cards/CardStatisticsHorizontal.vue
diff --git a/apps/customers/src/@core/components/cards/CardStatisticsVertical.vue b/apps/portal/src/@core/components/cards/CardStatisticsVertical.vue
similarity index 100%
rename from apps/customers/src/@core/components/cards/CardStatisticsVertical.vue
rename to apps/portal/src/@core/components/cards/CardStatisticsVertical.vue
diff --git a/apps/customers/src/@core/composable/createUrl.ts b/apps/portal/src/@core/composable/createUrl.ts
similarity index 100%
rename from apps/customers/src/@core/composable/createUrl.ts
rename to apps/portal/src/@core/composable/createUrl.ts
diff --git a/apps/customers/src/@core/composable/useCookie.ts b/apps/portal/src/@core/composable/useCookie.ts
similarity index 100%
rename from apps/customers/src/@core/composable/useCookie.ts
rename to apps/portal/src/@core/composable/useCookie.ts
diff --git a/apps/customers/src/@core/composable/useGenerateImageVariant.ts b/apps/portal/src/@core/composable/useGenerateImageVariant.ts
similarity index 100%
rename from apps/customers/src/@core/composable/useGenerateImageVariant.ts
rename to apps/portal/src/@core/composable/useGenerateImageVariant.ts
diff --git a/apps/customers/src/@core/composable/useResponsiveSidebar.ts b/apps/portal/src/@core/composable/useResponsiveSidebar.ts
similarity index 100%
rename from apps/customers/src/@core/composable/useResponsiveSidebar.ts
rename to apps/portal/src/@core/composable/useResponsiveSidebar.ts
diff --git a/apps/customers/src/@core/composable/useSkins.ts b/apps/portal/src/@core/composable/useSkins.ts
similarity index 100%
rename from apps/customers/src/@core/composable/useSkins.ts
rename to apps/portal/src/@core/composable/useSkins.ts
diff --git a/apps/customers/src/@core/enums.ts b/apps/portal/src/@core/enums.ts
similarity index 100%
rename from apps/customers/src/@core/enums.ts
rename to apps/portal/src/@core/enums.ts
diff --git a/apps/customers/src/@core/index.ts b/apps/portal/src/@core/index.ts
similarity index 100%
rename from apps/customers/src/@core/index.ts
rename to apps/portal/src/@core/index.ts
diff --git a/apps/customers/src/@core/initCore.ts b/apps/portal/src/@core/initCore.ts
similarity index 100%
rename from apps/customers/src/@core/initCore.ts
rename to apps/portal/src/@core/initCore.ts
diff --git a/apps/customers/src/@core/libs/apex-chart/apexCharConfig.ts b/apps/portal/src/@core/libs/apex-chart/apexCharConfig.ts
similarity index 100%
rename from apps/customers/src/@core/libs/apex-chart/apexCharConfig.ts
rename to apps/portal/src/@core/libs/apex-chart/apexCharConfig.ts
diff --git a/apps/customers/src/@core/libs/chartjs/chartjsConfig.ts b/apps/portal/src/@core/libs/chartjs/chartjsConfig.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/chartjsConfig.ts
rename to apps/portal/src/@core/libs/chartjs/chartjsConfig.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/BarChart.ts b/apps/portal/src/@core/libs/chartjs/components/BarChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/BarChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/BarChart.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/BubbleChart.ts b/apps/portal/src/@core/libs/chartjs/components/BubbleChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/BubbleChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/BubbleChart.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/DoughnutChart.ts b/apps/portal/src/@core/libs/chartjs/components/DoughnutChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/DoughnutChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/DoughnutChart.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/LineChart.ts b/apps/portal/src/@core/libs/chartjs/components/LineChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/LineChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/LineChart.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/PolarAreaChart.ts b/apps/portal/src/@core/libs/chartjs/components/PolarAreaChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/PolarAreaChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/PolarAreaChart.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/RadarChart.ts b/apps/portal/src/@core/libs/chartjs/components/RadarChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/RadarChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/RadarChart.ts
diff --git a/apps/customers/src/@core/libs/chartjs/components/ScatterChart.ts b/apps/portal/src/@core/libs/chartjs/components/ScatterChart.ts
similarity index 100%
rename from apps/customers/src/@core/libs/chartjs/components/ScatterChart.ts
rename to apps/portal/src/@core/libs/chartjs/components/ScatterChart.ts
diff --git a/apps/customers/src/@core/scss/base/_components.scss b/apps/portal/src/@core/scss/base/_components.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_components.scss
rename to apps/portal/src/@core/scss/base/_components.scss
diff --git a/apps/customers/src/@core/scss/base/_dark.scss b/apps/portal/src/@core/scss/base/_dark.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_dark.scss
rename to apps/portal/src/@core/scss/base/_dark.scss
diff --git a/apps/customers/src/@core/scss/base/_default-layout-w-horizontal-nav.scss b/apps/portal/src/@core/scss/base/_default-layout-w-horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_default-layout-w-horizontal-nav.scss
rename to apps/portal/src/@core/scss/base/_default-layout-w-horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/base/_default-layout-w-vertical-nav.scss b/apps/portal/src/@core/scss/base/_default-layout-w-vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_default-layout-w-vertical-nav.scss
rename to apps/portal/src/@core/scss/base/_default-layout-w-vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/base/_default-layout.scss b/apps/portal/src/@core/scss/base/_default-layout.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_default-layout.scss
rename to apps/portal/src/@core/scss/base/_default-layout.scss
diff --git a/apps/customers/src/@core/scss/base/_horizontal-nav.scss b/apps/portal/src/@core/scss/base/_horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_horizontal-nav.scss
rename to apps/portal/src/@core/scss/base/_horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/base/_index.scss b/apps/portal/src/@core/scss/base/_index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_index.scss
rename to apps/portal/src/@core/scss/base/_index.scss
diff --git a/apps/customers/src/@core/scss/base/_layouts.scss b/apps/portal/src/@core/scss/base/_layouts.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_layouts.scss
rename to apps/portal/src/@core/scss/base/_layouts.scss
diff --git a/apps/customers/src/@core/scss/base/_misc.scss b/apps/portal/src/@core/scss/base/_misc.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_misc.scss
rename to apps/portal/src/@core/scss/base/_misc.scss
diff --git a/apps/customers/src/@core/scss/base/_mixins.scss b/apps/portal/src/@core/scss/base/_mixins.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_mixins.scss
rename to apps/portal/src/@core/scss/base/_mixins.scss
diff --git a/apps/customers/src/@core/scss/base/_route-transitions.scss b/apps/portal/src/@core/scss/base/_route-transitions.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_route-transitions.scss
rename to apps/portal/src/@core/scss/base/_route-transitions.scss
diff --git a/apps/customers/src/@core/scss/base/_utilities.scss b/apps/portal/src/@core/scss/base/_utilities.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_utilities.scss
rename to apps/portal/src/@core/scss/base/_utilities.scss
diff --git a/apps/customers/src/@core/scss/base/_utils.scss b/apps/portal/src/@core/scss/base/_utils.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_utils.scss
rename to apps/portal/src/@core/scss/base/_utils.scss
diff --git a/apps/customers/src/@core/scss/base/_variables.scss b/apps/portal/src/@core/scss/base/_variables.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_variables.scss
rename to apps/portal/src/@core/scss/base/_variables.scss
diff --git a/apps/customers/src/@core/scss/base/_vertical-nav.scss b/apps/portal/src/@core/scss/base/_vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/_vertical-nav.scss
rename to apps/portal/src/@core/scss/base/_vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/base/libs/_perfect-scrollbar.scss b/apps/portal/src/@core/scss/base/libs/_perfect-scrollbar.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/libs/_perfect-scrollbar.scss
rename to apps/portal/src/@core/scss/base/libs/_perfect-scrollbar.scss
diff --git a/apps/customers/src/@core/scss/base/libs/vuetify/_index.scss b/apps/portal/src/@core/scss/base/libs/vuetify/_index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/libs/vuetify/_index.scss
rename to apps/portal/src/@core/scss/base/libs/vuetify/_index.scss
diff --git a/apps/customers/src/@core/scss/base/libs/vuetify/_overrides.scss b/apps/portal/src/@core/scss/base/libs/vuetify/_overrides.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/libs/vuetify/_overrides.scss
rename to apps/portal/src/@core/scss/base/libs/vuetify/_overrides.scss
diff --git a/apps/customers/src/@core/scss/base/libs/vuetify/_variables.scss b/apps/portal/src/@core/scss/base/libs/vuetify/_variables.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/libs/vuetify/_variables.scss
rename to apps/portal/src/@core/scss/base/libs/vuetify/_variables.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss b/apps/portal/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss
rename to apps/portal/src/@core/scss/base/placeholders/_default-layout-horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss b/apps/portal/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss
rename to apps/portal/src/@core/scss/base/placeholders/_default-layout-vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_default-layout.scss b/apps/portal/src/@core/scss/base/placeholders/_default-layout.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_default-layout.scss
rename to apps/portal/src/@core/scss/base/placeholders/_default-layout.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_horizontal-nav.scss b/apps/portal/src/@core/scss/base/placeholders/_horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_horizontal-nav.scss
rename to apps/portal/src/@core/scss/base/placeholders/_horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_index.scss b/apps/portal/src/@core/scss/base/placeholders/_index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_index.scss
rename to apps/portal/src/@core/scss/base/placeholders/_index.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_misc.scss b/apps/portal/src/@core/scss/base/placeholders/_misc.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_misc.scss
rename to apps/portal/src/@core/scss/base/placeholders/_misc.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_nav.scss b/apps/portal/src/@core/scss/base/placeholders/_nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_nav.scss
rename to apps/portal/src/@core/scss/base/placeholders/_nav.scss
diff --git a/apps/customers/src/@core/scss/base/placeholders/_vertical-nav.scss b/apps/portal/src/@core/scss/base/placeholders/_vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/placeholders/_vertical-nav.scss
rename to apps/portal/src/@core/scss/base/placeholders/_vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/base/skins/_bordered.scss b/apps/portal/src/@core/scss/base/skins/_bordered.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/skins/_bordered.scss
rename to apps/portal/src/@core/scss/base/skins/_bordered.scss
diff --git a/apps/customers/src/@core/scss/base/skins/_index.scss b/apps/portal/src/@core/scss/base/skins/_index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/base/skins/_index.scss
rename to apps/portal/src/@core/scss/base/skins/_index.scss
diff --git a/apps/customers/src/@core/scss/template/_default-layout-w-horizontal-nav.scss b/apps/portal/src/@core/scss/template/_default-layout-w-horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_default-layout-w-horizontal-nav.scss
rename to apps/portal/src/@core/scss/template/_default-layout-w-horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/template/_default-layout-w-vertical-nav.scss b/apps/portal/src/@core/scss/template/_default-layout-w-vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_default-layout-w-vertical-nav.scss
rename to apps/portal/src/@core/scss/template/_default-layout-w-vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/template/_horizontal-nav.scss b/apps/portal/src/@core/scss/template/_horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_horizontal-nav.scss
rename to apps/portal/src/@core/scss/template/_horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/template/_mixins.scss b/apps/portal/src/@core/scss/template/_mixins.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_mixins.scss
rename to apps/portal/src/@core/scss/template/_mixins.scss
diff --git a/apps/customers/src/@core/scss/template/_utilities.scss b/apps/portal/src/@core/scss/template/_utilities.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_utilities.scss
rename to apps/portal/src/@core/scss/template/_utilities.scss
diff --git a/apps/customers/src/@core/scss/template/_variables.scss b/apps/portal/src/@core/scss/template/_variables.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_variables.scss
rename to apps/portal/src/@core/scss/template/_variables.scss
diff --git a/apps/customers/src/@core/scss/template/_vertical-nav.scss b/apps/portal/src/@core/scss/template/_vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/_vertical-nav.scss
rename to apps/portal/src/@core/scss/template/_vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/template/index.scss b/apps/portal/src/@core/scss/template/index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/index.scss
rename to apps/portal/src/@core/scss/template/index.scss
diff --git a/apps/customers/src/@core/scss/template/libs/apex-chart.scss b/apps/portal/src/@core/scss/template/libs/apex-chart.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/apex-chart.scss
rename to apps/portal/src/@core/scss/template/libs/apex-chart.scss
diff --git a/apps/customers/src/@core/scss/template/libs/full-calendar.scss b/apps/portal/src/@core/scss/template/libs/full-calendar.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/full-calendar.scss
rename to apps/portal/src/@core/scss/template/libs/full-calendar.scss
diff --git a/apps/customers/src/@core/scss/template/libs/shepherd.scss b/apps/portal/src/@core/scss/template/libs/shepherd.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/shepherd.scss
rename to apps/portal/src/@core/scss/template/libs/shepherd.scss
diff --git a/apps/customers/src/@core/scss/template/libs/swiper.scss b/apps/portal/src/@core/scss/template/libs/swiper.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/swiper.scss
rename to apps/portal/src/@core/scss/template/libs/swiper.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/_variables.scss b/apps/portal/src/@core/scss/template/libs/vuetify/_variables.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/_variables.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/_variables.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_alert.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_alert.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_alert.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_alert.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_avatar.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_avatar.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_avatar.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_avatar.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_badge.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_badge.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_badge.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_badge.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_button.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_button.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_button.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_button.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_cards.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_cards.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_cards.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_cards.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_checkbox.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_checkbox.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_checkbox.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_checkbox.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_chip.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_chip.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_chip.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_chip.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_dialog.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_dialog.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_dialog.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_dialog.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_expansion-panels.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_field.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_field.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_field.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_field.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_list.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_list.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_list.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_list.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_menu.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_menu.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_menu.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_menu.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_otp-input.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_otp-input.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_otp-input.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_otp-input.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_pagination.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_pagination.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_pagination.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_pagination.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_progress.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_progress.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_progress.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_progress.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_radio.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_radio.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_radio.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_radio.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_rating.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_rating.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_rating.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_rating.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_slider.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_slider.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_slider.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_slider.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_snackbar.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_snackbar.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_snackbar.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_snackbar.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_switch.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_switch.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_switch.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_switch.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_table.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_table.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_table.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_table.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_tabs.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_tabs.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_tabs.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_tabs.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_textarea.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_textarea.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_textarea.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_textarea.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_timeline.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_timeline.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_timeline.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_timeline.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/_tooltip.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/_tooltip.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/_tooltip.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/_tooltip.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/components/index.scss b/apps/portal/src/@core/scss/template/libs/vuetify/components/index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/components/index.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/components/index.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/index.scss b/apps/portal/src/@core/scss/template/libs/vuetify/index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/index.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/index.scss
diff --git a/apps/customers/src/@core/scss/template/libs/vuetify/overrides.scss b/apps/portal/src/@core/scss/template/libs/vuetify/overrides.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/libs/vuetify/overrides.scss
rename to apps/portal/src/@core/scss/template/libs/vuetify/overrides.scss
diff --git a/apps/customers/src/@core/scss/template/pages/misc.scss b/apps/portal/src/@core/scss/template/pages/misc.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/pages/misc.scss
rename to apps/portal/src/@core/scss/template/pages/misc.scss
diff --git a/apps/customers/src/@core/scss/template/pages/page-auth.scss b/apps/portal/src/@core/scss/template/pages/page-auth.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/pages/page-auth.scss
rename to apps/portal/src/@core/scss/template/pages/page-auth.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss b/apps/portal/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss
rename to apps/portal/src/@core/scss/template/placeholders/_default-layout-horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss b/apps/portal/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss
rename to apps/portal/src/@core/scss/template/placeholders/_default-layout-vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_horizontal-nav.scss b/apps/portal/src/@core/scss/template/placeholders/_horizontal-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_horizontal-nav.scss
rename to apps/portal/src/@core/scss/template/placeholders/_horizontal-nav.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_index.scss b/apps/portal/src/@core/scss/template/placeholders/_index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_index.scss
rename to apps/portal/src/@core/scss/template/placeholders/_index.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_misc.scss b/apps/portal/src/@core/scss/template/placeholders/_misc.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_misc.scss
rename to apps/portal/src/@core/scss/template/placeholders/_misc.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_nav.scss b/apps/portal/src/@core/scss/template/placeholders/_nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_nav.scss
rename to apps/portal/src/@core/scss/template/placeholders/_nav.scss
diff --git a/apps/customers/src/@core/scss/template/placeholders/_vertical-nav.scss b/apps/portal/src/@core/scss/template/placeholders/_vertical-nav.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/placeholders/_vertical-nav.scss
rename to apps/portal/src/@core/scss/template/placeholders/_vertical-nav.scss
diff --git a/apps/customers/src/@core/scss/template/skins/_bordered.scss b/apps/portal/src/@core/scss/template/skins/_bordered.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/skins/_bordered.scss
rename to apps/portal/src/@core/scss/template/skins/_bordered.scss
diff --git a/apps/customers/src/@core/scss/template/skins/_index.scss b/apps/portal/src/@core/scss/template/skins/_index.scss
similarity index 100%
rename from apps/customers/src/@core/scss/template/skins/_index.scss
rename to apps/portal/src/@core/scss/template/skins/_index.scss
diff --git a/apps/customers/src/@core/stores/config.ts b/apps/portal/src/@core/stores/config.ts
similarity index 100%
rename from apps/customers/src/@core/stores/config.ts
rename to apps/portal/src/@core/stores/config.ts
diff --git a/apps/customers/src/@core/types.ts b/apps/portal/src/@core/types.ts
similarity index 100%
rename from apps/customers/src/@core/types.ts
rename to apps/portal/src/@core/types.ts
diff --git a/apps/customers/src/@core/utils/colorConverter.ts b/apps/portal/src/@core/utils/colorConverter.ts
similarity index 100%
rename from apps/customers/src/@core/utils/colorConverter.ts
rename to apps/portal/src/@core/utils/colorConverter.ts
diff --git a/apps/customers/src/@core/utils/formatters.ts b/apps/portal/src/@core/utils/formatters.ts
similarity index 100%
rename from apps/customers/src/@core/utils/formatters.ts
rename to apps/portal/src/@core/utils/formatters.ts
diff --git a/apps/customers/src/@core/utils/helpers.ts b/apps/portal/src/@core/utils/helpers.ts
similarity index 100%
rename from apps/customers/src/@core/utils/helpers.ts
rename to apps/portal/src/@core/utils/helpers.ts
diff --git a/apps/customers/src/@core/utils/plugins.ts b/apps/portal/src/@core/utils/plugins.ts
similarity index 100%
rename from apps/customers/src/@core/utils/plugins.ts
rename to apps/portal/src/@core/utils/plugins.ts
diff --git a/apps/customers/src/@core/utils/validators.ts b/apps/portal/src/@core/utils/validators.ts
similarity index 100%
rename from apps/customers/src/@core/utils/validators.ts
rename to apps/portal/src/@core/utils/validators.ts
diff --git a/apps/customers/src/@core/utils/vuetify.ts b/apps/portal/src/@core/utils/vuetify.ts
similarity index 100%
rename from apps/customers/src/@core/utils/vuetify.ts
rename to apps/portal/src/@core/utils/vuetify.ts
diff --git a/apps/customers/src/@layouts/components.ts b/apps/portal/src/@layouts/components.ts
similarity index 100%
rename from apps/customers/src/@layouts/components.ts
rename to apps/portal/src/@layouts/components.ts
diff --git a/apps/customers/src/@layouts/components/HorizontalNav.vue b/apps/portal/src/@layouts/components/HorizontalNav.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/HorizontalNav.vue
rename to apps/portal/src/@layouts/components/HorizontalNav.vue
diff --git a/apps/customers/src/@layouts/components/HorizontalNavGroup.vue b/apps/portal/src/@layouts/components/HorizontalNavGroup.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/HorizontalNavGroup.vue
rename to apps/portal/src/@layouts/components/HorizontalNavGroup.vue
diff --git a/apps/customers/src/@layouts/components/HorizontalNavLayout.vue b/apps/portal/src/@layouts/components/HorizontalNavLayout.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/HorizontalNavLayout.vue
rename to apps/portal/src/@layouts/components/HorizontalNavLayout.vue
diff --git a/apps/customers/src/@layouts/components/HorizontalNavLink.vue b/apps/portal/src/@layouts/components/HorizontalNavLink.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/HorizontalNavLink.vue
rename to apps/portal/src/@layouts/components/HorizontalNavLink.vue
diff --git a/apps/customers/src/@layouts/components/HorizontalNavPopper.vue b/apps/portal/src/@layouts/components/HorizontalNavPopper.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/HorizontalNavPopper.vue
rename to apps/portal/src/@layouts/components/HorizontalNavPopper.vue
diff --git a/apps/customers/src/@layouts/components/TransitionExpand.vue b/apps/portal/src/@layouts/components/TransitionExpand.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/TransitionExpand.vue
rename to apps/portal/src/@layouts/components/TransitionExpand.vue
diff --git a/apps/customers/src/@layouts/components/VNodeRenderer.tsx b/apps/portal/src/@layouts/components/VNodeRenderer.tsx
similarity index 100%
rename from apps/customers/src/@layouts/components/VNodeRenderer.tsx
rename to apps/portal/src/@layouts/components/VNodeRenderer.tsx
diff --git a/apps/customers/src/@layouts/components/VerticalNav.vue b/apps/portal/src/@layouts/components/VerticalNav.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/VerticalNav.vue
rename to apps/portal/src/@layouts/components/VerticalNav.vue
diff --git a/apps/customers/src/@layouts/components/VerticalNavGroup.vue b/apps/portal/src/@layouts/components/VerticalNavGroup.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/VerticalNavGroup.vue
rename to apps/portal/src/@layouts/components/VerticalNavGroup.vue
diff --git a/apps/customers/src/@layouts/components/VerticalNavLayout.vue b/apps/portal/src/@layouts/components/VerticalNavLayout.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/VerticalNavLayout.vue
rename to apps/portal/src/@layouts/components/VerticalNavLayout.vue
diff --git a/apps/customers/src/@layouts/components/VerticalNavLink.vue b/apps/portal/src/@layouts/components/VerticalNavLink.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/VerticalNavLink.vue
rename to apps/portal/src/@layouts/components/VerticalNavLink.vue
diff --git a/apps/customers/src/@layouts/components/VerticalNavSectionTitle.vue b/apps/portal/src/@layouts/components/VerticalNavSectionTitle.vue
similarity index 100%
rename from apps/customers/src/@layouts/components/VerticalNavSectionTitle.vue
rename to apps/portal/src/@layouts/components/VerticalNavSectionTitle.vue
diff --git a/apps/customers/src/@layouts/config.ts b/apps/portal/src/@layouts/config.ts
similarity index 100%
rename from apps/customers/src/@layouts/config.ts
rename to apps/portal/src/@layouts/config.ts
diff --git a/apps/customers/src/@layouts/enums.ts b/apps/portal/src/@layouts/enums.ts
similarity index 100%
rename from apps/customers/src/@layouts/enums.ts
rename to apps/portal/src/@layouts/enums.ts
diff --git a/apps/customers/src/@layouts/index.ts b/apps/portal/src/@layouts/index.ts
similarity index 100%
rename from apps/customers/src/@layouts/index.ts
rename to apps/portal/src/@layouts/index.ts
diff --git a/apps/customers/src/@layouts/plugins/casl.ts b/apps/portal/src/@layouts/plugins/casl.ts
similarity index 100%
rename from apps/customers/src/@layouts/plugins/casl.ts
rename to apps/portal/src/@layouts/plugins/casl.ts
diff --git a/apps/customers/src/@layouts/stores/config.ts b/apps/portal/src/@layouts/stores/config.ts
similarity index 100%
rename from apps/customers/src/@layouts/stores/config.ts
rename to apps/portal/src/@layouts/stores/config.ts
diff --git a/apps/customers/src/@layouts/styles/_classes.scss b/apps/portal/src/@layouts/styles/_classes.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_classes.scss
rename to apps/portal/src/@layouts/styles/_classes.scss
diff --git a/apps/customers/src/@layouts/styles/_default-layout.scss b/apps/portal/src/@layouts/styles/_default-layout.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_default-layout.scss
rename to apps/portal/src/@layouts/styles/_default-layout.scss
diff --git a/apps/customers/src/@layouts/styles/_global.scss b/apps/portal/src/@layouts/styles/_global.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_global.scss
rename to apps/portal/src/@layouts/styles/_global.scss
diff --git a/apps/customers/src/@layouts/styles/_mixins.scss b/apps/portal/src/@layouts/styles/_mixins.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_mixins.scss
rename to apps/portal/src/@layouts/styles/_mixins.scss
diff --git a/apps/customers/src/@layouts/styles/_placeholders.scss b/apps/portal/src/@layouts/styles/_placeholders.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_placeholders.scss
rename to apps/portal/src/@layouts/styles/_placeholders.scss
diff --git a/apps/customers/src/@layouts/styles/_rtl.scss b/apps/portal/src/@layouts/styles/_rtl.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_rtl.scss
rename to apps/portal/src/@layouts/styles/_rtl.scss
diff --git a/apps/customers/src/@layouts/styles/_variables.scss b/apps/portal/src/@layouts/styles/_variables.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/_variables.scss
rename to apps/portal/src/@layouts/styles/_variables.scss
diff --git a/apps/customers/src/@layouts/styles/index.scss b/apps/portal/src/@layouts/styles/index.scss
similarity index 100%
rename from apps/customers/src/@layouts/styles/index.scss
rename to apps/portal/src/@layouts/styles/index.scss
diff --git a/apps/customers/src/@layouts/symbols.ts b/apps/portal/src/@layouts/symbols.ts
similarity index 100%
rename from apps/customers/src/@layouts/symbols.ts
rename to apps/portal/src/@layouts/symbols.ts
diff --git a/apps/customers/src/@layouts/types.ts b/apps/portal/src/@layouts/types.ts
similarity index 100%
rename from apps/customers/src/@layouts/types.ts
rename to apps/portal/src/@layouts/types.ts
diff --git a/apps/customers/src/@layouts/utils.ts b/apps/portal/src/@layouts/utils.ts
similarity index 100%
rename from apps/customers/src/@layouts/utils.ts
rename to apps/portal/src/@layouts/utils.ts
diff --git a/apps/customers/src/App.vue b/apps/portal/src/App.vue
similarity index 100%
rename from apps/customers/src/App.vue
rename to apps/portal/src/App.vue
diff --git a/apps/customers/src/assets/images/avatars/avatar-1.png b/apps/portal/src/assets/images/avatars/avatar-1.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-1.png
rename to apps/portal/src/assets/images/avatars/avatar-1.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-10.png b/apps/portal/src/assets/images/avatars/avatar-10.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-10.png
rename to apps/portal/src/assets/images/avatars/avatar-10.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-11.png b/apps/portal/src/assets/images/avatars/avatar-11.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-11.png
rename to apps/portal/src/assets/images/avatars/avatar-11.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-12.png b/apps/portal/src/assets/images/avatars/avatar-12.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-12.png
rename to apps/portal/src/assets/images/avatars/avatar-12.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-13.png b/apps/portal/src/assets/images/avatars/avatar-13.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-13.png
rename to apps/portal/src/assets/images/avatars/avatar-13.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-14.png b/apps/portal/src/assets/images/avatars/avatar-14.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-14.png
rename to apps/portal/src/assets/images/avatars/avatar-14.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-15.png b/apps/portal/src/assets/images/avatars/avatar-15.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-15.png
rename to apps/portal/src/assets/images/avatars/avatar-15.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-2.png b/apps/portal/src/assets/images/avatars/avatar-2.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-2.png
rename to apps/portal/src/assets/images/avatars/avatar-2.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-3.png b/apps/portal/src/assets/images/avatars/avatar-3.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-3.png
rename to apps/portal/src/assets/images/avatars/avatar-3.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-4.png b/apps/portal/src/assets/images/avatars/avatar-4.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-4.png
rename to apps/portal/src/assets/images/avatars/avatar-4.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-5.png b/apps/portal/src/assets/images/avatars/avatar-5.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-5.png
rename to apps/portal/src/assets/images/avatars/avatar-5.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-6.png b/apps/portal/src/assets/images/avatars/avatar-6.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-6.png
rename to apps/portal/src/assets/images/avatars/avatar-6.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-7.png b/apps/portal/src/assets/images/avatars/avatar-7.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-7.png
rename to apps/portal/src/assets/images/avatars/avatar-7.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-8.png b/apps/portal/src/assets/images/avatars/avatar-8.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-8.png
rename to apps/portal/src/assets/images/avatars/avatar-8.png
diff --git a/apps/customers/src/assets/images/avatars/avatar-9.png b/apps/portal/src/assets/images/avatars/avatar-9.png
similarity index 100%
rename from apps/customers/src/assets/images/avatars/avatar-9.png
rename to apps/portal/src/assets/images/avatars/avatar-9.png
diff --git a/apps/customers/src/assets/images/cards/logo-mastercard-small.png b/apps/portal/src/assets/images/cards/logo-mastercard-small.png
similarity index 100%
rename from apps/customers/src/assets/images/cards/logo-mastercard-small.png
rename to apps/portal/src/assets/images/cards/logo-mastercard-small.png
diff --git a/apps/customers/src/assets/images/cards/paypal-primary.png b/apps/portal/src/assets/images/cards/paypal-primary.png
similarity index 100%
rename from apps/customers/src/assets/images/cards/paypal-primary.png
rename to apps/portal/src/assets/images/cards/paypal-primary.png
diff --git a/apps/customers/src/assets/images/cards/paypal-rounded.png b/apps/portal/src/assets/images/cards/paypal-rounded.png
similarity index 100%
rename from apps/customers/src/assets/images/cards/paypal-rounded.png
rename to apps/portal/src/assets/images/cards/paypal-rounded.png
diff --git a/apps/customers/src/assets/images/customizer-icons/border-light.svg b/apps/portal/src/assets/images/customizer-icons/border-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/border-light.svg
rename to apps/portal/src/assets/images/customizer-icons/border-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/collapsed-light.svg b/apps/portal/src/assets/images/customizer-icons/collapsed-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/collapsed-light.svg
rename to apps/portal/src/assets/images/customizer-icons/collapsed-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/compact-light.svg b/apps/portal/src/assets/images/customizer-icons/compact-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/compact-light.svg
rename to apps/portal/src/assets/images/customizer-icons/compact-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/default-light.svg b/apps/portal/src/assets/images/customizer-icons/default-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/default-light.svg
rename to apps/portal/src/assets/images/customizer-icons/default-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/horizontal-light.svg b/apps/portal/src/assets/images/customizer-icons/horizontal-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/horizontal-light.svg
rename to apps/portal/src/assets/images/customizer-icons/horizontal-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/ltr-light.svg b/apps/portal/src/assets/images/customizer-icons/ltr-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/ltr-light.svg
rename to apps/portal/src/assets/images/customizer-icons/ltr-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/rtl-light.svg b/apps/portal/src/assets/images/customizer-icons/rtl-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/rtl-light.svg
rename to apps/portal/src/assets/images/customizer-icons/rtl-light.svg
diff --git a/apps/customers/src/assets/images/customizer-icons/wide-light.svg b/apps/portal/src/assets/images/customizer-icons/wide-light.svg
similarity index 100%
rename from apps/customers/src/assets/images/customizer-icons/wide-light.svg
rename to apps/portal/src/assets/images/customizer-icons/wide-light.svg
diff --git a/apps/customers/src/assets/images/icons/payments/ae-icon.png b/apps/portal/src/assets/images/icons/payments/ae-icon.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/ae-icon.png
rename to apps/portal/src/assets/images/icons/payments/ae-icon.png
diff --git a/apps/customers/src/assets/images/icons/payments/american-express.png b/apps/portal/src/assets/images/icons/payments/american-express.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/american-express.png
rename to apps/portal/src/assets/images/icons/payments/american-express.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/ae-dark.png b/apps/portal/src/assets/images/icons/payments/img/ae-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/ae-dark.png
rename to apps/portal/src/assets/images/icons/payments/img/ae-dark.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/american-express.png b/apps/portal/src/assets/images/icons/payments/img/american-express.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/american-express.png
rename to apps/portal/src/assets/images/icons/payments/img/american-express.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/dc-dark.png b/apps/portal/src/assets/images/icons/payments/img/dc-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/dc-dark.png
rename to apps/portal/src/assets/images/icons/payments/img/dc-dark.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/dc-light.png b/apps/portal/src/assets/images/icons/payments/img/dc-light.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/dc-light.png
rename to apps/portal/src/assets/images/icons/payments/img/dc-light.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/jcb-dark.png b/apps/portal/src/assets/images/icons/payments/img/jcb-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/jcb-dark.png
rename to apps/portal/src/assets/images/icons/payments/img/jcb-dark.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/jcb-light.png b/apps/portal/src/assets/images/icons/payments/img/jcb-light.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/jcb-light.png
rename to apps/portal/src/assets/images/icons/payments/img/jcb-light.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/master-dark.png b/apps/portal/src/assets/images/icons/payments/img/master-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/master-dark.png
rename to apps/portal/src/assets/images/icons/payments/img/master-dark.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/mastercard.png b/apps/portal/src/assets/images/icons/payments/img/mastercard.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/mastercard.png
rename to apps/portal/src/assets/images/icons/payments/img/mastercard.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/paypal-dark.png b/apps/portal/src/assets/images/icons/payments/img/paypal-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/paypal-dark.png
rename to apps/portal/src/assets/images/icons/payments/img/paypal-dark.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/paypal-light.png b/apps/portal/src/assets/images/icons/payments/img/paypal-light.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/paypal-light.png
rename to apps/portal/src/assets/images/icons/payments/img/paypal-light.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/visa-dark.png b/apps/portal/src/assets/images/icons/payments/img/visa-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/visa-dark.png
rename to apps/portal/src/assets/images/icons/payments/img/visa-dark.png
diff --git a/apps/customers/src/assets/images/icons/payments/img/visa-light.png b/apps/portal/src/assets/images/icons/payments/img/visa-light.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/img/visa-light.png
rename to apps/portal/src/assets/images/icons/payments/img/visa-light.png
diff --git a/apps/customers/src/assets/images/icons/payments/mastercard-icon.png b/apps/portal/src/assets/images/icons/payments/mastercard-icon.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/mastercard-icon.png
rename to apps/portal/src/assets/images/icons/payments/mastercard-icon.png
diff --git a/apps/customers/src/assets/images/icons/payments/mastercard.png b/apps/portal/src/assets/images/icons/payments/mastercard.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/mastercard.png
rename to apps/portal/src/assets/images/icons/payments/mastercard.png
diff --git a/apps/customers/src/assets/images/icons/payments/visa-icon.png b/apps/portal/src/assets/images/icons/payments/visa-icon.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/visa-icon.png
rename to apps/portal/src/assets/images/icons/payments/visa-icon.png
diff --git a/apps/customers/src/assets/images/icons/payments/visa.png b/apps/portal/src/assets/images/icons/payments/visa.png
similarity index 100%
rename from apps/customers/src/assets/images/icons/payments/visa.png
rename to apps/portal/src/assets/images/icons/payments/visa.png
diff --git a/apps/customers/src/assets/images/illustrations/boy-app-academy.png b/apps/portal/src/assets/images/illustrations/boy-app-academy.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/boy-app-academy.png
rename to apps/portal/src/assets/images/illustrations/boy-app-academy.png
diff --git a/apps/customers/src/assets/images/illustrations/congo-illustration.png b/apps/portal/src/assets/images/illustrations/congo-illustration.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/congo-illustration.png
rename to apps/portal/src/assets/images/illustrations/congo-illustration.png
diff --git a/apps/customers/src/assets/images/illustrations/girl-app-academy.png b/apps/portal/src/assets/images/illustrations/girl-app-academy.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/girl-app-academy.png
rename to apps/portal/src/assets/images/illustrations/girl-app-academy.png
diff --git a/apps/customers/src/assets/images/illustrations/laptop-girl.png b/apps/portal/src/assets/images/illustrations/laptop-girl.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/laptop-girl.png
rename to apps/portal/src/assets/images/illustrations/laptop-girl.png
diff --git a/apps/customers/src/assets/images/illustrations/register-multi-step-illustration-dark.png b/apps/portal/src/assets/images/illustrations/register-multi-step-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/register-multi-step-illustration-dark.png
rename to apps/portal/src/assets/images/illustrations/register-multi-step-illustration-dark.png
diff --git a/apps/customers/src/assets/images/illustrations/register-multi-step-illustration-light.png b/apps/portal/src/assets/images/illustrations/register-multi-step-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/register-multi-step-illustration-light.png
rename to apps/portal/src/assets/images/illustrations/register-multi-step-illustration-light.png
diff --git a/apps/customers/src/assets/images/illustrations/sidebar-pic-1.png b/apps/portal/src/assets/images/illustrations/sidebar-pic-1.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/sidebar-pic-1.png
rename to apps/portal/src/assets/images/illustrations/sidebar-pic-1.png
diff --git a/apps/customers/src/assets/images/illustrations/sidebar-pic-2.png b/apps/portal/src/assets/images/illustrations/sidebar-pic-2.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/sidebar-pic-2.png
rename to apps/portal/src/assets/images/illustrations/sidebar-pic-2.png
diff --git a/apps/customers/src/assets/images/illustrations/sidebar-pic-3.png b/apps/portal/src/assets/images/illustrations/sidebar-pic-3.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/sidebar-pic-3.png
rename to apps/portal/src/assets/images/illustrations/sidebar-pic-3.png
diff --git a/apps/customers/src/assets/images/illustrations/sitting-girl-with-laptop.png b/apps/portal/src/assets/images/illustrations/sitting-girl-with-laptop.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/sitting-girl-with-laptop.png
rename to apps/portal/src/assets/images/illustrations/sitting-girl-with-laptop.png
diff --git a/apps/customers/src/assets/images/illustrations/standingGirlImg.png b/apps/portal/src/assets/images/illustrations/standingGirlImg.png
similarity index 100%
rename from apps/customers/src/assets/images/illustrations/standingGirlImg.png
rename to apps/portal/src/assets/images/illustrations/standingGirlImg.png
diff --git a/apps/customers/src/assets/images/logo.svg b/apps/portal/src/assets/images/logo.svg
similarity index 100%
rename from apps/customers/src/assets/images/logo.svg
rename to apps/portal/src/assets/images/logo.svg
diff --git a/apps/customers/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png b/apps/portal/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png
similarity index 100%
rename from apps/customers/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png
rename to apps/portal/src/assets/images/misc/3d-safe-box-with-golden-dollar-coins.png
diff --git a/apps/customers/src/assets/images/misc/3d-space-rocket-with-smoke.png b/apps/portal/src/assets/images/misc/3d-space-rocket-with-smoke.png
similarity index 100%
rename from apps/customers/src/assets/images/misc/3d-space-rocket-with-smoke.png
rename to apps/portal/src/assets/images/misc/3d-space-rocket-with-smoke.png
diff --git a/apps/customers/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png b/apps/portal/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png
similarity index 100%
rename from apps/customers/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png
rename to apps/portal/src/assets/images/misc/dollar-coins-flying-pink-piggy-bank.png
diff --git a/apps/customers/src/assets/images/misc/fleet-car.png b/apps/portal/src/assets/images/misc/fleet-car.png
similarity index 100%
rename from apps/customers/src/assets/images/misc/fleet-car.png
rename to apps/portal/src/assets/images/misc/fleet-car.png
diff --git a/apps/customers/src/assets/images/pages/1.png b/apps/portal/src/assets/images/pages/1.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/1.png
rename to apps/portal/src/assets/images/pages/1.png
diff --git a/apps/customers/src/assets/images/pages/2.png b/apps/portal/src/assets/images/pages/2.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/2.png
rename to apps/portal/src/assets/images/pages/2.png
diff --git a/apps/customers/src/assets/images/pages/3.png b/apps/portal/src/assets/images/pages/3.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/3.png
rename to apps/portal/src/assets/images/pages/3.png
diff --git a/apps/customers/src/assets/images/pages/401.png b/apps/portal/src/assets/images/pages/401.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/401.png
rename to apps/portal/src/assets/images/pages/401.png
diff --git a/apps/customers/src/assets/images/pages/404.png b/apps/portal/src/assets/images/pages/404.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/404.png
rename to apps/portal/src/assets/images/pages/404.png
diff --git a/apps/customers/src/assets/images/pages/5.jpg b/apps/portal/src/assets/images/pages/5.jpg
similarity index 100%
rename from apps/customers/src/assets/images/pages/5.jpg
rename to apps/portal/src/assets/images/pages/5.jpg
diff --git a/apps/customers/src/assets/images/pages/6.jpg b/apps/portal/src/assets/images/pages/6.jpg
similarity index 100%
rename from apps/customers/src/assets/images/pages/6.jpg
rename to apps/portal/src/assets/images/pages/6.jpg
diff --git a/apps/customers/src/assets/images/pages/DealTypeBackground-dark.png b/apps/portal/src/assets/images/pages/DealTypeBackground-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/DealTypeBackground-dark.png
rename to apps/portal/src/assets/images/pages/DealTypeBackground-dark.png
diff --git a/apps/customers/src/assets/images/pages/DealTypeBackground-light.png b/apps/portal/src/assets/images/pages/DealTypeBackground-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/DealTypeBackground-light.png
rename to apps/portal/src/assets/images/pages/DealTypeBackground-light.png
diff --git a/apps/customers/src/assets/images/pages/TimelineRectangle1.png b/apps/portal/src/assets/images/pages/TimelineRectangle1.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/TimelineRectangle1.png
rename to apps/portal/src/assets/images/pages/TimelineRectangle1.png
diff --git a/apps/customers/src/assets/images/pages/TimelineRectangle2.png b/apps/portal/src/assets/images/pages/TimelineRectangle2.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/TimelineRectangle2.png
rename to apps/portal/src/assets/images/pages/TimelineRectangle2.png
diff --git a/apps/customers/src/assets/images/pages/TimelineRectangle3.png b/apps/portal/src/assets/images/pages/TimelineRectangle3.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/TimelineRectangle3.png
rename to apps/portal/src/assets/images/pages/TimelineRectangle3.png
diff --git a/apps/customers/src/assets/images/pages/TimelineRectangle4.png b/apps/portal/src/assets/images/pages/TimelineRectangle4.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/TimelineRectangle4.png
rename to apps/portal/src/assets/images/pages/TimelineRectangle4.png
diff --git a/apps/customers/src/assets/images/pages/academy-course-illustration1.png b/apps/portal/src/assets/images/pages/academy-course-illustration1.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/academy-course-illustration1.png
rename to apps/portal/src/assets/images/pages/academy-course-illustration1.png
diff --git a/apps/customers/src/assets/images/pages/academy-course-illustration2-dark.png b/apps/portal/src/assets/images/pages/academy-course-illustration2-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/academy-course-illustration2-dark.png
rename to apps/portal/src/assets/images/pages/academy-course-illustration2-dark.png
diff --git a/apps/customers/src/assets/images/pages/academy-course-illustration2-light.png b/apps/portal/src/assets/images/pages/academy-course-illustration2-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/academy-course-illustration2-light.png
rename to apps/portal/src/assets/images/pages/academy-course-illustration2-light.png
diff --git a/apps/customers/src/assets/images/pages/app-academy-tutor-1.png b/apps/portal/src/assets/images/pages/app-academy-tutor-1.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-academy-tutor-1.png
rename to apps/portal/src/assets/images/pages/app-academy-tutor-1.png
diff --git a/apps/customers/src/assets/images/pages/app-academy-tutor-2.png b/apps/portal/src/assets/images/pages/app-academy-tutor-2.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-academy-tutor-2.png
rename to apps/portal/src/assets/images/pages/app-academy-tutor-2.png
diff --git a/apps/customers/src/assets/images/pages/app-academy-tutor-3.png b/apps/portal/src/assets/images/pages/app-academy-tutor-3.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-academy-tutor-3.png
rename to apps/portal/src/assets/images/pages/app-academy-tutor-3.png
diff --git a/apps/customers/src/assets/images/pages/app-academy-tutor-4.png b/apps/portal/src/assets/images/pages/app-academy-tutor-4.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-academy-tutor-4.png
rename to apps/portal/src/assets/images/pages/app-academy-tutor-4.png
diff --git a/apps/customers/src/assets/images/pages/app-academy-tutor-5.png b/apps/portal/src/assets/images/pages/app-academy-tutor-5.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-academy-tutor-5.png
rename to apps/portal/src/assets/images/pages/app-academy-tutor-5.png
diff --git a/apps/customers/src/assets/images/pages/app-academy-tutor-6.png b/apps/portal/src/assets/images/pages/app-academy-tutor-6.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-academy-tutor-6.png
rename to apps/portal/src/assets/images/pages/app-academy-tutor-6.png
diff --git a/apps/customers/src/assets/images/pages/app-search-header-bg.png b/apps/portal/src/assets/images/pages/app-search-header-bg.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/app-search-header-bg.png
rename to apps/portal/src/assets/images/pages/app-search-header-bg.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png b/apps/portal/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-forgot-password-illustration-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png b/apps/portal/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-forgot-password-illustration-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png b/apps/portal/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-login-illustration-bordered-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png b/apps/portal/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-login-illustration-bordered-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-login-illustration-dark.png b/apps/portal/src/assets/images/pages/auth-v2-login-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-login-illustration-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-login-illustration-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-login-illustration-light.png b/apps/portal/src/assets/images/pages/auth-v2-login-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-login-illustration-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-login-illustration-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png b/apps/portal/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-register-illustration-bordered-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png b/apps/portal/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-register-illustration-bordered-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-register-illustration-dark.png b/apps/portal/src/assets/images/pages/auth-v2-register-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-register-illustration-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-register-illustration-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-register-illustration-light.png b/apps/portal/src/assets/images/pages/auth-v2-register-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-register-illustration-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-register-illustration-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png b/apps/portal/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-reset-password-illustration-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-reset-password-illustration-light.png b/apps/portal/src/assets/images/pages/auth-v2-reset-password-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-reset-password-illustration-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-reset-password-illustration-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-two-step-illustration-dark.png b/apps/portal/src/assets/images/pages/auth-v2-two-step-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-two-step-illustration-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-two-step-illustration-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-two-step-illustration-light.png b/apps/portal/src/assets/images/pages/auth-v2-two-step-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-two-step-illustration-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-two-step-illustration-light.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png b/apps/portal/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png
rename to apps/portal/src/assets/images/pages/auth-v2-verify-email-illustration-dark.png
diff --git a/apps/customers/src/assets/images/pages/auth-v2-verify-email-illustration-light.png b/apps/portal/src/assets/images/pages/auth-v2-verify-email-illustration-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/auth-v2-verify-email-illustration-light.png
rename to apps/portal/src/assets/images/pages/auth-v2-verify-email-illustration-light.png
diff --git a/apps/customers/src/assets/images/pages/background-1.jpg b/apps/portal/src/assets/images/pages/background-1.jpg
similarity index 100%
rename from apps/customers/src/assets/images/pages/background-1.jpg
rename to apps/portal/src/assets/images/pages/background-1.jpg
diff --git a/apps/customers/src/assets/images/pages/background-2.jpg b/apps/portal/src/assets/images/pages/background-2.jpg
similarity index 100%
rename from apps/customers/src/assets/images/pages/background-2.jpg
rename to apps/portal/src/assets/images/pages/background-2.jpg
diff --git a/apps/customers/src/assets/images/pages/background-3.jpg b/apps/portal/src/assets/images/pages/background-3.jpg
similarity index 100%
rename from apps/customers/src/assets/images/pages/background-3.jpg
rename to apps/portal/src/assets/images/pages/background-3.jpg
diff --git a/apps/customers/src/assets/images/pages/boy-illustration.png b/apps/portal/src/assets/images/pages/boy-illustration.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/boy-illustration.png
rename to apps/portal/src/assets/images/pages/boy-illustration.png
diff --git a/apps/customers/src/assets/images/pages/custom-checkbox-img-1.png b/apps/portal/src/assets/images/pages/custom-checkbox-img-1.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/custom-checkbox-img-1.png
rename to apps/portal/src/assets/images/pages/custom-checkbox-img-1.png
diff --git a/apps/customers/src/assets/images/pages/custom-checkbox-img-2.png b/apps/portal/src/assets/images/pages/custom-checkbox-img-2.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/custom-checkbox-img-2.png
rename to apps/portal/src/assets/images/pages/custom-checkbox-img-2.png
diff --git a/apps/customers/src/assets/images/pages/custom-checkbox-img-3.png b/apps/portal/src/assets/images/pages/custom-checkbox-img-3.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/custom-checkbox-img-3.png
rename to apps/portal/src/assets/images/pages/custom-checkbox-img-3.png
diff --git a/apps/customers/src/assets/images/pages/custom-radio-img-1.png b/apps/portal/src/assets/images/pages/custom-radio-img-1.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/custom-radio-img-1.png
rename to apps/portal/src/assets/images/pages/custom-radio-img-1.png
diff --git a/apps/customers/src/assets/images/pages/custom-radio-img-2.png b/apps/portal/src/assets/images/pages/custom-radio-img-2.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/custom-radio-img-2.png
rename to apps/portal/src/assets/images/pages/custom-radio-img-2.png
diff --git a/apps/customers/src/assets/images/pages/custom-radio-img-3.png b/apps/portal/src/assets/images/pages/custom-radio-img-3.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/custom-radio-img-3.png
rename to apps/portal/src/assets/images/pages/custom-radio-img-3.png
diff --git a/apps/customers/src/assets/images/pages/empty-cart.png b/apps/portal/src/assets/images/pages/empty-cart.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/empty-cart.png
rename to apps/portal/src/assets/images/pages/empty-cart.png
diff --git a/apps/customers/src/assets/images/pages/forgot-password-illustration.png b/apps/portal/src/assets/images/pages/forgot-password-illustration.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/forgot-password-illustration.png
rename to apps/portal/src/assets/images/pages/forgot-password-illustration.png
diff --git a/apps/customers/src/assets/images/pages/girl-using-mobile.png b/apps/portal/src/assets/images/pages/girl-using-mobile.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/girl-using-mobile.png
rename to apps/portal/src/assets/images/pages/girl-using-mobile.png
diff --git a/apps/customers/src/assets/images/pages/google-home.png b/apps/portal/src/assets/images/pages/google-home.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/google-home.png
rename to apps/portal/src/assets/images/pages/google-home.png
diff --git a/apps/customers/src/assets/images/pages/guitar-course-poster.png b/apps/portal/src/assets/images/pages/guitar-course-poster.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/guitar-course-poster.png
rename to apps/portal/src/assets/images/pages/guitar-course-poster.png
diff --git a/apps/customers/src/assets/images/pages/instructor-poster.png b/apps/portal/src/assets/images/pages/instructor-poster.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/instructor-poster.png
rename to apps/portal/src/assets/images/pages/instructor-poster.png
diff --git a/apps/customers/src/assets/images/pages/iphone-11.png b/apps/portal/src/assets/images/pages/iphone-11.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/iphone-11.png
rename to apps/portal/src/assets/images/pages/iphone-11.png
diff --git a/apps/customers/src/assets/images/pages/misc-coming-soon.png b/apps/portal/src/assets/images/pages/misc-coming-soon.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/misc-coming-soon.png
rename to apps/portal/src/assets/images/pages/misc-coming-soon.png
diff --git a/apps/customers/src/assets/images/pages/misc-mask-dark.png b/apps/portal/src/assets/images/pages/misc-mask-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/misc-mask-dark.png
rename to apps/portal/src/assets/images/pages/misc-mask-dark.png
diff --git a/apps/customers/src/assets/images/pages/misc-mask-light.png b/apps/portal/src/assets/images/pages/misc-mask-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/misc-mask-light.png
rename to apps/portal/src/assets/images/pages/misc-mask-light.png
diff --git a/apps/customers/src/assets/images/pages/misc-under-maintenance.png b/apps/portal/src/assets/images/pages/misc-under-maintenance.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/misc-under-maintenance.png
rename to apps/portal/src/assets/images/pages/misc-under-maintenance.png
diff --git a/apps/customers/src/assets/images/pages/puma-shoes.jpeg b/apps/portal/src/assets/images/pages/puma-shoes.jpeg
similarity index 100%
rename from apps/customers/src/assets/images/pages/puma-shoes.jpeg
rename to apps/portal/src/assets/images/pages/puma-shoes.jpeg
diff --git a/apps/customers/src/assets/images/pages/register-multi-step-bg-dark.png b/apps/portal/src/assets/images/pages/register-multi-step-bg-dark.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/register-multi-step-bg-dark.png
rename to apps/portal/src/assets/images/pages/register-multi-step-bg-dark.png
diff --git a/apps/customers/src/assets/images/pages/register-multi-step-bg-light.png b/apps/portal/src/assets/images/pages/register-multi-step-bg-light.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/register-multi-step-bg-light.png
rename to apps/portal/src/assets/images/pages/register-multi-step-bg-light.png
diff --git a/apps/customers/src/assets/images/pages/singing-course-poster.png b/apps/portal/src/assets/images/pages/singing-course-poster.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/singing-course-poster.png
rename to apps/portal/src/assets/images/pages/singing-course-poster.png
diff --git a/apps/customers/src/assets/images/pages/themeselection-qr.png b/apps/portal/src/assets/images/pages/themeselection-qr.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/themeselection-qr.png
rename to apps/portal/src/assets/images/pages/themeselection-qr.png
diff --git a/apps/customers/src/assets/images/pages/tree-pot.png b/apps/portal/src/assets/images/pages/tree-pot.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/tree-pot.png
rename to apps/portal/src/assets/images/pages/tree-pot.png
diff --git a/apps/customers/src/assets/images/pages/user-profile-header-bg.png b/apps/portal/src/assets/images/pages/user-profile-header-bg.png
similarity index 100%
rename from apps/customers/src/assets/images/pages/user-profile-header-bg.png
rename to apps/portal/src/assets/images/pages/user-profile-header-bg.png
diff --git a/apps/customers/src/assets/images/svg/Card.svg b/apps/portal/src/assets/images/svg/Card.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/Card.svg
rename to apps/portal/src/assets/images/svg/Card.svg
diff --git a/apps/customers/src/assets/images/svg/Check.svg b/apps/portal/src/assets/images/svg/Check.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/Check.svg
rename to apps/portal/src/assets/images/svg/Check.svg
diff --git a/apps/customers/src/assets/images/svg/Diamond.svg b/apps/portal/src/assets/images/svg/Diamond.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/Diamond.svg
rename to apps/portal/src/assets/images/svg/Diamond.svg
diff --git a/apps/customers/src/assets/images/svg/Suitcase.svg b/apps/portal/src/assets/images/svg/Suitcase.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/Suitcase.svg
rename to apps/portal/src/assets/images/svg/Suitcase.svg
diff --git a/apps/customers/src/assets/images/svg/Wallet.svg b/apps/portal/src/assets/images/svg/Wallet.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/Wallet.svg
rename to apps/portal/src/assets/images/svg/Wallet.svg
diff --git a/apps/customers/src/assets/images/svg/address.svg b/apps/portal/src/assets/images/svg/address.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/address.svg
rename to apps/portal/src/assets/images/svg/address.svg
diff --git a/apps/customers/src/assets/images/svg/auth-v1-bottom-shape.svg b/apps/portal/src/assets/images/svg/auth-v1-bottom-shape.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/auth-v1-bottom-shape.svg
rename to apps/portal/src/assets/images/svg/auth-v1-bottom-shape.svg
diff --git a/apps/customers/src/assets/images/svg/auth-v1-top-shape.svg b/apps/portal/src/assets/images/svg/auth-v1-top-shape.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/auth-v1-top-shape.svg
rename to apps/portal/src/assets/images/svg/auth-v1-top-shape.svg
diff --git a/apps/customers/src/assets/images/svg/cart.svg b/apps/portal/src/assets/images/svg/cart.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/cart.svg
rename to apps/portal/src/assets/images/svg/cart.svg
diff --git a/apps/customers/src/assets/images/svg/checkbox-checked.svg b/apps/portal/src/assets/images/svg/checkbox-checked.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/checkbox-checked.svg
rename to apps/portal/src/assets/images/svg/checkbox-checked.svg
diff --git a/apps/customers/src/assets/images/svg/checkbox-indeterminate.svg b/apps/portal/src/assets/images/svg/checkbox-indeterminate.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/checkbox-indeterminate.svg
rename to apps/portal/src/assets/images/svg/checkbox-indeterminate.svg
diff --git a/apps/customers/src/assets/images/svg/checkbox-unchecked.svg b/apps/portal/src/assets/images/svg/checkbox-unchecked.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/checkbox-unchecked.svg
rename to apps/portal/src/assets/images/svg/checkbox-unchecked.svg
diff --git a/apps/customers/src/assets/images/svg/discord.svg b/apps/portal/src/assets/images/svg/discord.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/discord.svg
rename to apps/portal/src/assets/images/svg/discord.svg
diff --git a/apps/customers/src/assets/images/svg/gift.svg b/apps/portal/src/assets/images/svg/gift.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/gift.svg
rename to apps/portal/src/assets/images/svg/gift.svg
diff --git a/apps/customers/src/assets/images/svg/home.svg b/apps/portal/src/assets/images/svg/home.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/home.svg
rename to apps/portal/src/assets/images/svg/home.svg
diff --git a/apps/customers/src/assets/images/svg/keyboard.svg b/apps/portal/src/assets/images/svg/keyboard.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/keyboard.svg
rename to apps/portal/src/assets/images/svg/keyboard.svg
diff --git a/apps/customers/src/assets/images/svg/laptop.svg b/apps/portal/src/assets/images/svg/laptop.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/laptop.svg
rename to apps/portal/src/assets/images/svg/laptop.svg
diff --git a/apps/customers/src/assets/images/svg/lightbulb.svg b/apps/portal/src/assets/images/svg/lightbulb.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/lightbulb.svg
rename to apps/portal/src/assets/images/svg/lightbulb.svg
diff --git a/apps/customers/src/assets/images/svg/office.svg b/apps/portal/src/assets/images/svg/office.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/office.svg
rename to apps/portal/src/assets/images/svg/office.svg
diff --git a/apps/customers/src/assets/images/svg/paper-send.svg b/apps/portal/src/assets/images/svg/paper-send.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/paper-send.svg
rename to apps/portal/src/assets/images/svg/paper-send.svg
diff --git a/apps/customers/src/assets/images/svg/payment.svg b/apps/portal/src/assets/images/svg/payment.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/payment.svg
rename to apps/portal/src/assets/images/svg/payment.svg
diff --git a/apps/customers/src/assets/images/svg/radio-checked.svg b/apps/portal/src/assets/images/svg/radio-checked.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/radio-checked.svg
rename to apps/portal/src/assets/images/svg/radio-checked.svg
diff --git a/apps/customers/src/assets/images/svg/radio-unchecked.svg b/apps/portal/src/assets/images/svg/radio-unchecked.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/radio-unchecked.svg
rename to apps/portal/src/assets/images/svg/radio-unchecked.svg
diff --git a/apps/customers/src/assets/images/svg/rocket.svg b/apps/portal/src/assets/images/svg/rocket.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/rocket.svg
rename to apps/portal/src/assets/images/svg/rocket.svg
diff --git a/apps/customers/src/assets/images/svg/trending.svg b/apps/portal/src/assets/images/svg/trending.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/trending.svg
rename to apps/portal/src/assets/images/svg/trending.svg
diff --git a/apps/customers/src/assets/images/svg/user-info.svg b/apps/portal/src/assets/images/svg/user-info.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/user-info.svg
rename to apps/portal/src/assets/images/svg/user-info.svg
diff --git a/apps/customers/src/assets/images/svg/user.svg b/apps/portal/src/assets/images/svg/user.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/user.svg
rename to apps/portal/src/assets/images/svg/user.svg
diff --git a/apps/customers/src/assets/images/svg/wizard-account.svg b/apps/portal/src/assets/images/svg/wizard-account.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/wizard-account.svg
rename to apps/portal/src/assets/images/svg/wizard-account.svg
diff --git a/apps/customers/src/assets/images/svg/wizard-address.svg b/apps/portal/src/assets/images/svg/wizard-address.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/wizard-address.svg
rename to apps/portal/src/assets/images/svg/wizard-address.svg
diff --git a/apps/customers/src/assets/images/svg/wizard-personal.svg b/apps/portal/src/assets/images/svg/wizard-personal.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/wizard-personal.svg
rename to apps/portal/src/assets/images/svg/wizard-personal.svg
diff --git a/apps/customers/src/assets/images/svg/wizard-social-link.svg b/apps/portal/src/assets/images/svg/wizard-social-link.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/wizard-social-link.svg
rename to apps/portal/src/assets/images/svg/wizard-social-link.svg
diff --git a/apps/customers/src/assets/images/svg/wizard-submit.svg b/apps/portal/src/assets/images/svg/wizard-submit.svg
similarity index 100%
rename from apps/customers/src/assets/images/svg/wizard-submit.svg
rename to apps/portal/src/assets/images/svg/wizard-submit.svg
diff --git a/apps/customers/src/assets/styles/styles.scss b/apps/portal/src/assets/styles/styles.scss
similarity index 100%
rename from apps/customers/src/assets/styles/styles.scss
rename to apps/portal/src/assets/styles/styles.scss
diff --git a/apps/customers/src/assets/styles/variables/_template.scss b/apps/portal/src/assets/styles/variables/_template.scss
similarity index 100%
rename from apps/customers/src/assets/styles/variables/_template.scss
rename to apps/portal/src/assets/styles/variables/_template.scss
diff --git a/apps/customers/src/assets/styles/variables/_vuetify.scss b/apps/portal/src/assets/styles/variables/_vuetify.scss
similarity index 100%
rename from apps/customers/src/assets/styles/variables/_vuetify.scss
rename to apps/portal/src/assets/styles/variables/_vuetify.scss
diff --git a/apps/customers/src/components/AppLoadingIndicator.vue b/apps/portal/src/components/AppLoadingIndicator.vue
similarity index 100%
rename from apps/customers/src/components/AppLoadingIndicator.vue
rename to apps/portal/src/components/AppLoadingIndicator.vue
diff --git a/apps/customers/src/components/AppPricing.vue b/apps/portal/src/components/AppPricing.vue
similarity index 100%
rename from apps/customers/src/components/AppPricing.vue
rename to apps/portal/src/components/AppPricing.vue
diff --git a/apps/customers/src/components/AppSearchHeader.vue b/apps/portal/src/components/AppSearchHeader.vue
similarity index 100%
rename from apps/customers/src/components/AppSearchHeader.vue
rename to apps/portal/src/components/AppSearchHeader.vue
diff --git a/apps/customers/src/components/ErrorHeader.vue b/apps/portal/src/components/ErrorHeader.vue
similarity index 100%
rename from apps/customers/src/components/ErrorHeader.vue
rename to apps/portal/src/components/ErrorHeader.vue
diff --git a/apps/band/src/components/dialogs/AddAuthenticatorAppDialog.vue b/apps/portal/src/components/dialogs/AddAuthenticatorAppDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/AddAuthenticatorAppDialog.vue
rename to apps/portal/src/components/dialogs/AddAuthenticatorAppDialog.vue
diff --git a/apps/customers/src/components/dialogs/AddEditAddressDialog.vue b/apps/portal/src/components/dialogs/AddEditAddressDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/AddEditAddressDialog.vue
rename to apps/portal/src/components/dialogs/AddEditAddressDialog.vue
diff --git a/apps/customers/src/components/dialogs/AddEditPermissionDialog.vue b/apps/portal/src/components/dialogs/AddEditPermissionDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/AddEditPermissionDialog.vue
rename to apps/portal/src/components/dialogs/AddEditPermissionDialog.vue
diff --git a/apps/customers/src/components/dialogs/AddEditRoleDialog.vue b/apps/portal/src/components/dialogs/AddEditRoleDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/AddEditRoleDialog.vue
rename to apps/portal/src/components/dialogs/AddEditRoleDialog.vue
diff --git a/apps/band/src/components/dialogs/AddPaymentMethodDialog.vue b/apps/portal/src/components/dialogs/AddPaymentMethodDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/AddPaymentMethodDialog.vue
rename to apps/portal/src/components/dialogs/AddPaymentMethodDialog.vue
diff --git a/apps/band/src/components/dialogs/CardAddEditDialog.vue b/apps/portal/src/components/dialogs/CardAddEditDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/CardAddEditDialog.vue
rename to apps/portal/src/components/dialogs/CardAddEditDialog.vue
diff --git a/apps/customers/src/components/dialogs/ConfirmDialog.vue b/apps/portal/src/components/dialogs/ConfirmDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/ConfirmDialog.vue
rename to apps/portal/src/components/dialogs/ConfirmDialog.vue
diff --git a/apps/customers/src/components/dialogs/CreateAppDialog.vue b/apps/portal/src/components/dialogs/CreateAppDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/CreateAppDialog.vue
rename to apps/portal/src/components/dialogs/CreateAppDialog.vue
diff --git a/apps/customers/src/components/dialogs/EnableOneTimePasswordDialog.vue b/apps/portal/src/components/dialogs/EnableOneTimePasswordDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/EnableOneTimePasswordDialog.vue
rename to apps/portal/src/components/dialogs/EnableOneTimePasswordDialog.vue
diff --git a/apps/customers/src/components/dialogs/PaymentProvidersDialog.vue b/apps/portal/src/components/dialogs/PaymentProvidersDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/PaymentProvidersDialog.vue
rename to apps/portal/src/components/dialogs/PaymentProvidersDialog.vue
diff --git a/apps/band/src/components/dialogs/PricingPlanDialog.vue b/apps/portal/src/components/dialogs/PricingPlanDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/PricingPlanDialog.vue
rename to apps/portal/src/components/dialogs/PricingPlanDialog.vue
diff --git a/apps/band/src/components/dialogs/ReferAndEarnDialog.vue b/apps/portal/src/components/dialogs/ReferAndEarnDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/ReferAndEarnDialog.vue
rename to apps/portal/src/components/dialogs/ReferAndEarnDialog.vue
diff --git a/apps/customers/src/components/dialogs/ShareProjectDialog.vue b/apps/portal/src/components/dialogs/ShareProjectDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/ShareProjectDialog.vue
rename to apps/portal/src/components/dialogs/ShareProjectDialog.vue
diff --git a/apps/customers/src/components/dialogs/TwoFactorAuthDialog.vue b/apps/portal/src/components/dialogs/TwoFactorAuthDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/TwoFactorAuthDialog.vue
rename to apps/portal/src/components/dialogs/TwoFactorAuthDialog.vue
diff --git a/apps/customers/src/components/dialogs/UserInfoEditDialog.vue b/apps/portal/src/components/dialogs/UserInfoEditDialog.vue
similarity index 100%
rename from apps/customers/src/components/dialogs/UserInfoEditDialog.vue
rename to apps/portal/src/components/dialogs/UserInfoEditDialog.vue
diff --git a/apps/band/src/components/dialogs/UserUpgradePlanDialog.vue b/apps/portal/src/components/dialogs/UserUpgradePlanDialog.vue
similarity index 100%
rename from apps/band/src/components/dialogs/UserUpgradePlanDialog.vue
rename to apps/portal/src/components/dialogs/UserUpgradePlanDialog.vue
diff --git a/apps/customers/src/layouts/blank.vue b/apps/portal/src/layouts/blank.vue
similarity index 100%
rename from apps/customers/src/layouts/blank.vue
rename to apps/portal/src/layouts/blank.vue
diff --git a/apps/customers/src/layouts/components/DefaultLayoutWithHorizontalNav.vue b/apps/portal/src/layouts/components/DefaultLayoutWithHorizontalNav.vue
similarity index 100%
rename from apps/customers/src/layouts/components/DefaultLayoutWithHorizontalNav.vue
rename to apps/portal/src/layouts/components/DefaultLayoutWithHorizontalNav.vue
diff --git a/apps/customers/src/layouts/components/DefaultLayoutWithVerticalNav.vue b/apps/portal/src/layouts/components/DefaultLayoutWithVerticalNav.vue
similarity index 100%
rename from apps/customers/src/layouts/components/DefaultLayoutWithVerticalNav.vue
rename to apps/portal/src/layouts/components/DefaultLayoutWithVerticalNav.vue
diff --git a/apps/customers/src/layouts/components/Footer.vue b/apps/portal/src/layouts/components/Footer.vue
similarity index 100%
rename from apps/customers/src/layouts/components/Footer.vue
rename to apps/portal/src/layouts/components/Footer.vue
diff --git a/apps/customers/src/layouts/components/NavBarNotifications.vue b/apps/portal/src/layouts/components/NavBarNotifications.vue
similarity index 100%
rename from apps/customers/src/layouts/components/NavBarNotifications.vue
rename to apps/portal/src/layouts/components/NavBarNotifications.vue
diff --git a/apps/customers/src/layouts/components/NavSearchBar.vue b/apps/portal/src/layouts/components/NavSearchBar.vue
similarity index 100%
rename from apps/customers/src/layouts/components/NavSearchBar.vue
rename to apps/portal/src/layouts/components/NavSearchBar.vue
diff --git a/apps/customers/src/layouts/components/NavbarShortcuts.vue b/apps/portal/src/layouts/components/NavbarShortcuts.vue
similarity index 100%
rename from apps/customers/src/layouts/components/NavbarShortcuts.vue
rename to apps/portal/src/layouts/components/NavbarShortcuts.vue
diff --git a/apps/customers/src/layouts/components/NavbarThemeSwitcher.vue b/apps/portal/src/layouts/components/NavbarThemeSwitcher.vue
similarity index 100%
rename from apps/customers/src/layouts/components/NavbarThemeSwitcher.vue
rename to apps/portal/src/layouts/components/NavbarThemeSwitcher.vue
diff --git a/apps/customers/src/layouts/components/UserProfile.vue b/apps/portal/src/layouts/components/UserProfile.vue
similarity index 100%
rename from apps/customers/src/layouts/components/UserProfile.vue
rename to apps/portal/src/layouts/components/UserProfile.vue
diff --git a/apps/customers/src/layouts/default.vue b/apps/portal/src/layouts/default.vue
similarity index 100%
rename from apps/customers/src/layouts/default.vue
rename to apps/portal/src/layouts/default.vue
diff --git a/apps/customers/src/lib/api-client.ts b/apps/portal/src/lib/api-client.ts
similarity index 58%
rename from apps/customers/src/lib/api-client.ts
rename to apps/portal/src/lib/api-client.ts
index 6714c8a..6b4397c 100644
--- a/apps/customers/src/lib/api-client.ts
+++ b/apps/portal/src/lib/api-client.ts
@@ -1,24 +1,31 @@
import axios from 'axios'
+import { parse } from 'cookie-es'
import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
const apiClient: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: {
'Content-Type': 'application/json',
- 'Accept': 'application/json',
+ Accept: 'application/json',
},
timeout: 30000,
})
-// Request interceptor - add auth token
+function getAccessToken(): string | null {
+ if (typeof document === 'undefined') return null
+ const cookies = parse(document.cookie)
+ const token = cookies.accessToken
+
+ return token ?? null
+}
+
apiClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
- const token = localStorage.getItem('auth_token')
+ const token = getAccessToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
- // Log in development
if (import.meta.env.DEV) {
console.log(`🚀 ${config.method?.toUpperCase()} ${config.url}`, config.data)
}
@@ -28,7 +35,6 @@ apiClient.interceptors.request.use(
error => Promise.reject(error),
)
-// Response interceptor - handle errors
apiClient.interceptors.response.use(
response => {
if (import.meta.env.DEV) {
@@ -39,13 +45,19 @@ apiClient.interceptors.response.use(
},
error => {
if (import.meta.env.DEV) {
- console.error(`❌ ${error.response?.status} ${error.config?.url}`, error.response?.data)
+ console.error(
+ `❌ ${error.response?.status} ${error.config?.url}`,
+ error.response?.data,
+ )
}
- // Handle 401 - redirect to login
if (error.response?.status === 401) {
- localStorage.removeItem('auth_token')
- window.location.href = '/login'
+ document.cookie = 'accessToken=; path=/; max-age=0'
+ document.cookie = 'userData=; path=/; max-age=0'
+ document.cookie = 'userAbilityRules=; path=/; max-age=0'
+ if (window.location.pathname !== '/login') {
+ window.location.href = '/login'
+ }
}
return Promise.reject(error)
@@ -53,4 +65,3 @@ apiClient.interceptors.response.use(
)
export { apiClient }
-
diff --git a/apps/customers/src/main.ts b/apps/portal/src/main.ts
similarity index 62%
rename from apps/customers/src/main.ts
rename to apps/portal/src/main.ts
index 2b4e7b3..dc67339 100644
--- a/apps/customers/src/main.ts
+++ b/apps/portal/src/main.ts
@@ -1,4 +1,5 @@
import { createApp } from 'vue'
+import { VueQueryPlugin } from '@tanstack/vue-query'
import App from '@/App.vue'
import { registerPlugins } from '@core/utils/plugins'
@@ -13,5 +14,13 @@ const app = createApp(App)
// Register plugins
registerPlugins(app)
+app.use(VueQueryPlugin, {
+ queryClientConfig: {
+ defaultOptions: {
+ queries: { staleTime: 1000 * 60 * 5, retry: 1 },
+ },
+ },
+})
+
// Mount vue app
app.mount('#app')
diff --git a/apps/band/src/navigation/horizontal/index.ts b/apps/portal/src/navigation/horizontal/index.ts
similarity index 100%
rename from apps/band/src/navigation/horizontal/index.ts
rename to apps/portal/src/navigation/horizontal/index.ts
diff --git a/apps/customers/src/navigation/horizontal/index.ts b/apps/portal/src/navigation/vertical/index.ts
similarity index 100%
rename from apps/customers/src/navigation/horizontal/index.ts
rename to apps/portal/src/navigation/vertical/index.ts
diff --git a/apps/customers/src/pages/[...error].vue b/apps/portal/src/pages/[...error].vue
similarity index 100%
rename from apps/customers/src/pages/[...error].vue
rename to apps/portal/src/pages/[...error].vue
diff --git a/apps/customers/src/pages/index.vue b/apps/portal/src/pages/index.vue
similarity index 100%
rename from apps/customers/src/pages/index.vue
rename to apps/portal/src/pages/index.vue
diff --git a/apps/customers/src/pages/login.vue b/apps/portal/src/pages/login.vue
similarity index 100%
rename from apps/customers/src/pages/login.vue
rename to apps/portal/src/pages/login.vue
diff --git a/apps/band/src/pages/second-page.vue b/apps/portal/src/pages/second-page.vue
similarity index 100%
rename from apps/band/src/pages/second-page.vue
rename to apps/portal/src/pages/second-page.vue
diff --git a/apps/customers/src/plugins/1.router/index.ts b/apps/portal/src/plugins/1.router/index.ts
similarity index 100%
rename from apps/customers/src/plugins/1.router/index.ts
rename to apps/portal/src/plugins/1.router/index.ts
diff --git a/apps/customers/src/plugins/2.pinia.ts b/apps/portal/src/plugins/2.pinia.ts
similarity index 100%
rename from apps/customers/src/plugins/2.pinia.ts
rename to apps/portal/src/plugins/2.pinia.ts
diff --git a/apps/customers/src/plugins/iconify/build-icons.ts b/apps/portal/src/plugins/iconify/build-icons.ts
similarity index 100%
rename from apps/customers/src/plugins/iconify/build-icons.ts
rename to apps/portal/src/plugins/iconify/build-icons.ts
diff --git a/apps/customers/src/plugins/iconify/icons.css b/apps/portal/src/plugins/iconify/icons.css
similarity index 100%
rename from apps/customers/src/plugins/iconify/icons.css
rename to apps/portal/src/plugins/iconify/icons.css
diff --git a/apps/customers/src/plugins/iconify/index.ts b/apps/portal/src/plugins/iconify/index.ts
similarity index 100%
rename from apps/customers/src/plugins/iconify/index.ts
rename to apps/portal/src/plugins/iconify/index.ts
diff --git a/apps/customers/src/plugins/iconify/package.json b/apps/portal/src/plugins/iconify/package.json
similarity index 100%
rename from apps/customers/src/plugins/iconify/package.json
rename to apps/portal/src/plugins/iconify/package.json
diff --git a/apps/customers/src/plugins/layouts.ts b/apps/portal/src/plugins/layouts.ts
similarity index 100%
rename from apps/customers/src/plugins/layouts.ts
rename to apps/portal/src/plugins/layouts.ts
diff --git a/apps/customers/src/plugins/vuetify/defaults.ts b/apps/portal/src/plugins/vuetify/defaults.ts
similarity index 100%
rename from apps/customers/src/plugins/vuetify/defaults.ts
rename to apps/portal/src/plugins/vuetify/defaults.ts
diff --git a/apps/customers/src/plugins/vuetify/icons.ts b/apps/portal/src/plugins/vuetify/icons.ts
similarity index 100%
rename from apps/customers/src/plugins/vuetify/icons.ts
rename to apps/portal/src/plugins/vuetify/icons.ts
diff --git a/apps/customers/src/plugins/vuetify/index.ts b/apps/portal/src/plugins/vuetify/index.ts
similarity index 100%
rename from apps/customers/src/plugins/vuetify/index.ts
rename to apps/portal/src/plugins/vuetify/index.ts
diff --git a/apps/customers/src/plugins/vuetify/theme.ts b/apps/portal/src/plugins/vuetify/theme.ts
similarity index 100%
rename from apps/customers/src/plugins/vuetify/theme.ts
rename to apps/portal/src/plugins/vuetify/theme.ts
diff --git a/apps/customers/src/plugins/webfontloader.ts b/apps/portal/src/plugins/webfontloader.ts
similarity index 100%
rename from apps/customers/src/plugins/webfontloader.ts
rename to apps/portal/src/plugins/webfontloader.ts
diff --git a/apps/customers/src/utils/constants.ts b/apps/portal/src/utils/constants.ts
similarity index 100%
rename from apps/customers/src/utils/constants.ts
rename to apps/portal/src/utils/constants.ts
diff --git a/apps/customers/src/utils/paginationMeta.ts b/apps/portal/src/utils/paginationMeta.ts
similarity index 100%
rename from apps/customers/src/utils/paginationMeta.ts
rename to apps/portal/src/utils/paginationMeta.ts
diff --git a/apps/customers/src/views/pages/authentication/AuthProvider.vue b/apps/portal/src/views/pages/authentication/AuthProvider.vue
similarity index 100%
rename from apps/customers/src/views/pages/authentication/AuthProvider.vue
rename to apps/portal/src/views/pages/authentication/AuthProvider.vue
diff --git a/apps/customers/themeConfig.ts b/apps/portal/themeConfig.ts
similarity index 100%
rename from apps/customers/themeConfig.ts
rename to apps/portal/themeConfig.ts
diff --git a/apps/customers/tsconfig.json b/apps/portal/tsconfig.json
similarity index 100%
rename from apps/customers/tsconfig.json
rename to apps/portal/tsconfig.json
diff --git a/apps/customers/typed-router.d.ts b/apps/portal/typed-router.d.ts
similarity index 100%
rename from apps/customers/typed-router.d.ts
rename to apps/portal/typed-router.d.ts
diff --git a/apps/customers/vite.config.ts b/apps/portal/vite.config.ts
similarity index 96%
rename from apps/customers/vite.config.ts
rename to apps/portal/vite.config.ts
index e1d4c28..df39a55 100644
--- a/apps/customers/vite.config.ts
+++ b/apps/portal/vite.config.ts
@@ -96,6 +96,12 @@ export default defineConfig({
},
server: {
port: 5175,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8000',
+ changeOrigin: true,
+ },
+ },
},
build: {
chunkSizeWarningLimit: 5000,
diff --git a/docker-compose.yml b/docker-compose.yml
index 06be6b7..b2a3a6c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,4 @@
-# Band Management - Development Services
+# Event Crew - Development Services
# PHP/Node run natively for best Cursor IDE performance
services:
@@ -9,8 +9,8 @@ services:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: band_management
- MYSQL_USER: band_management
+ MYSQL_DATABASE: event_crew
+ MYSQL_USER: event_crew
MYSQL_PASSWORD: secret
volumes:
- mysql_data:/var/lib/mysql
diff --git a/docs/API.md b/docs/API.md
new file mode 100644
index 0000000..d1c3850
--- /dev/null
+++ b/docs/API.md
@@ -0,0 +1,56 @@
+# EventCrew API Contract
+
+# Base: /api/v1/
+
+# Auth: Bearer token (Sanctum)
+
+## Auth
+
+POST /auth/login
+POST /auth/logout
+GET /auth/me
+
+## Organisations
+
+GET /organisations -- lijst (super admin)
+POST /organisations -- aanmaken
+GET /organisations/{org} -- detail
+PUT /organisations/{org} -- bijwerken
+GET /organisations/{org}/members -- leden
+POST /organisations/{org}/invite -- uitnodigen
+
+## Events
+
+GET /organisations/{org}/events
+POST /organisations/{org}/events
+GET /organisations/{org}/events/{event}
+PUT /organisations/{org}/events/{event}
+
+## Festival Sections
+
+GET /events/{event}/sections
+POST /events/{event}/sections
+GET /events/{event}/sections/{section}
+
+## Time Slots
+
+GET /events/{event}/time-slots
+POST /events/{event}/time-slots
+
+## Shifts
+
+GET /events/{event}/sections/{section}/shifts
+POST /events/{event}/sections/{section}/shifts
+PUT /events/{event}/sections/{section}/shifts/{shift}
+POST /events/{event}/sections/{section}/shifts/{shift}/assign
+POST /events/{event}/sections/{section}/shifts/{shift}/claim
+
+## Persons
+
+GET /events/{event}/persons
+POST /events/{event}/persons
+GET /events/{event}/persons/{person}
+PUT /events/{event}/persons/{person}
+POST /events/{event}/persons/{person}/approve
+
+# ... (volledig API contract uitbreiden per module)
diff --git a/docs/SCHEMA.md b/docs/SCHEMA.md
new file mode 100644
index 0000000..7d085e4
--- /dev/null
+++ b/docs/SCHEMA.md
@@ -0,0 +1,1154 @@
+# EventCrew — Core Database Schema
+
+> Source: Design Document v1.3 — Section 3.5
+> All 12 findings from the database review (v1.3) are incorporated.
+> Last updated: March 2026
+
+---
+
+## Primary Key Convention: ULID
+
+> **All tables use ULID (Universally Unique Lexicographically Sortable Identifier) as primary key — NO UUID v4.**
+>
+> **Reason:** UUID v4 is random, causing B-tree index fragmentation in InnoDB on every INSERT. ULID is monotonically increasing (time-ordered) and preserves index locality.
+>
+> - Laravel: use `Str::ulid()` or the `HasUlids` trait
+> - Migrations: `$table->ulid('id')->primary()`
+>
+> Externally visible IDs (URLs, barcodes, API) use ULID. Internal pivot tables may use auto-increment integer PK for join performance.
+
+---
+
+## Table of Contents
+
+1. [3.5.1 Foundation](#351-foundation)
+2. [3.5.2 Locations](#352-locations)
+3. [3.5.3 Festival Sections, Time Slots & Shifts](#353-festival-sections-time-slots--shifts)
+4. [3.5.4 Volunteer Profile & History](#354-volunteer-profile--history)
+5. [3.5.5 Crowd Types, Persons & Crowd Lists](#355-crowd-types-persons--crowd-lists)
+6. [3.5.6 Accreditation Engine](#356-accreditation-engine)
+7. [3.5.7 Artists & Advancing](#357-artists--advancing)
+8. [3.5.8 Communication & Briefings](#358-communication--briefings)
+9. [3.5.9 Forms, Check-In & Operational](#359-forms-check-in--operational)
+10. [3.5.10 Database Design Rules & Index Strategy](#3510-database-design-rules--index-strategy)
+
+---
+
+## 3.5.1 Foundation
+
+### `users`
+
+| Column | Type | Notes |
+| ------------------- | ------------------ | -------------------- |
+| `id` | ULID | PK, `HasUlids` trait |
+| `name` | string | |
+| `email` | string | unique |
+| `password` | string | hashed |
+| `timezone` | string | |
+| `locale` | string | |
+| `avatar` | string nullable | |
+| `email_verified_at` | timestamp nullable | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `belongsToMany` organisations (via `organisation_user`), `belongsToMany` events (via `event_user_roles`)
+**Soft delete:** yes
+
+---
+
+### `organisations`
+
+| Column | Type | Notes |
+| ---------------- | ------------------ | -------------------------------------- |
+| `id` | ULID | PK |
+| `name` | string | |
+| `slug` | string | unique |
+| `billing_status` | string | |
+| `settings` | JSON | Display prefs only — no queryable data |
+| `created_at` | timestamp | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `hasMany` events, crowd_types, accreditation_categories
+**Soft delete:** yes
+
+---
+
+### `organisation_user`
+
+| Column | Type | Notes |
+| ----------------- | ------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `user_id` | ULID FK | → users |
+| `organisation_id` | ULID FK | → organisations |
+| `role` | string | Spatie role via pivot |
+
+**Type:** Pivot table — integer PK
+
+---
+
+### `user_invitations`
+
+| Column | Type | Notes |
+| -------------------- | ---------------- | --------------------------------- |
+| `id` | ULID | PK |
+| `email` | string | |
+| `invited_by_user_id` | ULID FK | → users |
+| `organisation_id` | ULID FK | → organisations |
+| `event_id` | ULID FK nullable | → events |
+| `role` | string | |
+| `token` | ULID | unique — sent in invitation email |
+| `status` | enum | `pending\|accepted\|expired` |
+| `expires_at` | timestamp | |
+
+**Indexes:** `(token)`, `(email, status)`
+**Logic:** On accept: look up existing account by email or create new one.
+
+---
+
+### `events`
+
+| Column | Type | Notes |
+| ----------------- | ------------------ | ------------------------------------------------------------------------- |
+| `id` | ULID | PK |
+| `organisation_id` | ULID FK | → organisations |
+| `name` | string | |
+| `slug` | string | |
+| `start_date` | date | |
+| `end_date` | date | |
+| `timezone` | string | |
+| `status` | enum | `draft\|published\|registration_open\|buildup\|showday\|teardown\|closed` |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `belongsTo` organisation, `hasMany` festival_sections, time_slots, persons, artists, briefings
+**Indexes:** `(organisation_id, status)`
+**Soft delete:** yes
+
+---
+
+### `event_user_roles`
+
+| Column | Type | Notes |
+| ---------- | ------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `user_id` | ULID FK | → users |
+| `event_id` | ULID FK | → events |
+| `role` | string | |
+
+**Type:** Pivot table — integer PK
+
+---
+
+## 3.5.2 Locations
+
+> **New table (resolves review finding #3):** `locations` was referenced by `shifts` but never defined. Locations are event-scoped and reusable across sections.
+
+### `locations`
+
+| Column | Type | Notes |
+| --------------------- | ---------------------- | -------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `address` | string nullable | |
+| `lat` | decimal(10,8) nullable | |
+| `lng` | decimal(11,8) nullable | |
+| `description` | text nullable | |
+| `access_instructions` | text nullable | |
+
+**Indexes:** `(event_id)`
+**Usage:** Referenced by `shifts.location_id`
+
+---
+
+## 3.5.3 Festival Sections, Time Slots & Shifts
+
+> Three-layer Crescat model. Critical improvement: `time_slot_id` denormalised onto `shift_assignments` for DB-enforceable conflict detection (finding #2). Shift swaps split into two tables (finding #10).
+
+### `festival_sections`
+
+| Column | Type | Notes |
+| ------------ | ------------------ | ----------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `sort_order` | int | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `hasMany` shifts
+**Indexes:** `(event_id, sort_order)`
+**Soft delete:** yes
+
+---
+
+### `time_slots`
+
+| Column | Type | Notes |
+| ---------------- | ------- | ----------------------------------------------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `person_type` | enum | `CREW\|VOLUNTEER\|PRESS\|PHOTO\|PARTNER` — controls visibility in registration form |
+| `date` | date | |
+| `start_time` | time | |
+| `end_time` | time | |
+| `duration_hours` | decimal | |
+
+**Relations:** `hasMany` shifts
+**Indexes:** `(event_id, person_type, date)`
+
+---
+
+### `shifts`
+
+| Column | Type | Notes |
+| ------------------------- | ------------------ | --------------------------------------------------------------------- |
+| `id` | ULID | PK |
+| `festival_section_id` | ULID FK | → festival_sections |
+| `time_slot_id` | ULID FK | → time_slots |
+| `location_id` | ULID FK nullable | → locations |
+| `slots_total` | int | |
+| `slots_open_for_claiming` | int | Number of slots visible & claimable in volunteer portal |
+| `assigned_crew_id` | ULID FK nullable | → users |
+| `events_during_shift` | JSON | Array of performance_ids — opaque reference list, no filtering needed |
+| `status` | string | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `belongsTo` festival_section, time_slot, location; `hasMany` shift_assignments
+**Indexes:** `(festival_section_id, time_slot_id)`, `(time_slot_id, status)`
+**Soft delete:** yes
+
+---
+
+### `shift_assignments`
+
+| Column | Type | Notes |
+| ------------------ | ------------------ | -------------------------------------------------------------------- |
+| `id` | ULID | PK |
+| `shift_id` | ULID FK | → shifts |
+| `person_id` | ULID FK | → persons |
+| `time_slot_id` | ULID FK | Denormalised from shifts — enables DB-enforceable conflict detection |
+| `status` | enum | `pending_approval\|approved\|rejected\|cancelled\|completed` |
+| `auto_approved` | bool | |
+| `assigned_by` | ULID FK nullable | → users |
+| `assigned_at` | timestamp nullable | |
+| `approved_by` | ULID FK nullable | → users |
+| `approved_at` | timestamp nullable | |
+| `rejection_reason` | text nullable | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Unique constraint:** `UNIQUE(person_id, time_slot_id)` — DB-enforceable conflict detection
+**Indexes:** `(shift_id, status)`, `(person_id, status)`, `(person_id, time_slot_id)`
+**Soft delete:** yes
+
+---
+
+### `volunteer_availabilities`
+
+| Column | Type | Notes |
+| -------------- | --------- | ------------ |
+| `id` | ULID | PK |
+| `person_id` | ULID FK | → persons |
+| `time_slot_id` | ULID FK | → time_slots |
+| `submitted_at` | timestamp | |
+
+**Purpose:** Volunteer selects available Time Slots — basis for shift matching
+**Unique constraint:** `UNIQUE(person_id, time_slot_id)`
+**Indexes:** `(time_slot_id)`
+
+---
+
+### `shift_absences`
+
+> **New table (finding #10 split)**
+
+| Column | Type | Notes |
+| --------------------- | ------------------ | ----------------------- |
+| `id` | ULID | PK |
+| `shift_assignment_id` | ULID FK | → shift_assignments |
+| `person_id` | ULID FK | → persons |
+| `reason` | enum | `sick\|personal\|other` |
+| `reported_at` | timestamp | |
+| `status` | enum | `open\|filled\|closed` |
+| `closed_at` | timestamp nullable | |
+
+**Purpose:** Volunteer reports absence — shift slot becomes available. Triggers waitlist notification.
+**Indexes:** `(shift_assignment_id)`, `(status)`
+
+---
+
+### `shift_swap_requests`
+
+> **New table (finding #10 split)**
+
+| Column | Type | Notes |
+| -------------------- | ------------------ | --------------------------------------------------- |
+| `id` | ULID | PK |
+| `from_assignment_id` | ULID FK | → shift_assignments |
+| `to_person_id` | ULID FK | → persons |
+| `message` | text nullable | |
+| `status` | enum | `pending\|accepted\|rejected\|cancelled\|completed` |
+| `reviewed_by` | ULID FK nullable | → users |
+| `reviewed_at` | timestamp nullable | |
+| `auto_approved` | bool | |
+
+**Logic:** A asks B to swap. After both agree: coordinator confirms (or auto-approve).
+**Indexes:** `(from_assignment_id)`, `(to_person_id, status)`
+
+---
+
+### `shift_waitlist`
+
+| Column | Type | Notes |
+| ------------- | ------------------ | --------- |
+| `id` | ULID | PK |
+| `shift_id` | ULID FK | → shifts |
+| `person_id` | ULID FK | → persons |
+| `position` | int | |
+| `added_at` | timestamp | |
+| `notified_at` | timestamp nullable | |
+
+**Unique constraint:** `UNIQUE(shift_id, person_id)`
+**Logic:** On vacancy: position 1 is automatically notified.
+**Indexes:** `(shift_id, position)`
+
+---
+
+## 3.5.4 Volunteer Profile & History
+
+### `volunteer_profiles`
+
+| Column | Type | Notes |
+| ------------------------- | --------------- | ------------------------------------- |
+| `id` | ULID | PK |
+| `user_id` | ULID FK unique | → users — 1:1 |
+| `bio` | text nullable | |
+| `photo_url` | string nullable | |
+| `tshirt_size` | string nullable | |
+| `first_aid` | bool | |
+| `driving_licence` | bool | |
+| `allergies` | text nullable | |
+| `access_requirements` | text nullable | |
+| `emergency_contact_name` | string nullable | |
+| `emergency_contact_phone` | string nullable | |
+| `reliability_score` | decimal(3,2) | 0.00–5.00, computed via scheduled job |
+| `is_ambassador` | bool | |
+
+**Unique constraint:** `UNIQUE(user_id)` — platform-wide, 1:1 with users
+
+---
+
+### `volunteer_festival_history`
+
+| Column | Type | Notes |
+| -------------------- | ---------------- | --------------- |
+| `id` | ULID | PK |
+| `user_id` | ULID FK | → users |
+| `event_id` | ULID FK | → events |
+| `organisation_id` | ULID FK | → organisations |
+| `hours_planned` | decimal nullable | |
+| `hours_completed` | decimal nullable | |
+| `no_show_count` | int | |
+| `coordinator_rating` | tinyint | 1–5 |
+| `coordinator_notes` | text nullable | |
+| `would_reinvite` | bool | |
+
+**Note:** Never visible to the volunteer themselves.
+**Unique constraint:** `UNIQUE(user_id, event_id)`
+**Indexes:** `(user_id, event_id)`
+
+---
+
+### `post_festival_evaluations`
+
+| Column | Type | Notes |
+| ------------------------ | ---------------- | --------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `person_id` | ULID FK | → persons |
+| `shift_id` | ULID FK nullable | → shifts |
+| `overall_rating` | tinyint | 1–5 |
+| `shift_rating` | tinyint | 1–5 |
+| `would_return` | bool | |
+| `feedback_text` | text nullable | |
+| `improvement_suggestion` | text nullable | |
+| `submitted_at` | timestamp | |
+| `is_anonymous` | bool | |
+
+**Indexes:** `(event_id, is_anonymous)`, `(person_id)`
+
+---
+
+### `festival_retrospectives`
+
+> **Finding #8:** All KPIs as concrete columns instead of a JSON blob. Enables trend analysis across multiple years.
+
+| Column | Type | Notes |
+| -------------------------- | -------------- | ------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK unique | → events |
+| `generated_at` | timestamp | |
+| `volunteers_planned` | int | |
+| `volunteers_completed` | int | |
+| `no_show_count` | int | |
+| `no_show_pct` | decimal(5,2) | |
+| `avg_overall_satisfaction` | decimal(3,2) | |
+| `avg_shift_satisfaction` | decimal(3,2) | |
+| `would_return_pct` | decimal(5,2) | |
+| `sections_understaffed` | int | |
+| `sections_overstaffed` | int | |
+| `top_feedback` | JSON | Array of strings — free-text feedback |
+| `notes` | text nullable | |
+
+---
+
+## 3.5.5 Crowd Types, Persons & Crowd Lists
+
+> **Finding #1 (identity fragmentation):** `persons` gets nullable `user_id` as canonical link to platform account.
+> **Finding #4:** `crowd_list_persons` pivot added.
+> **Finding #9:** `persons.email` as indexed deduplication key.
+
+### `crowd_types`
+
+| Column | Type | Notes |
+| ----------------- | --------------- | ---------------------------------------------------------- |
+| `id` | ULID | PK |
+| `organisation_id` | ULID FK | → organisations |
+| `name` | string | |
+| `system_type` | enum | `CREW\|GUEST\|ARTIST\|VOLUNTEER\|PRESS\|PARTNER\|SUPPLIER` |
+| `color` | string | hex |
+| `icon` | string nullable | |
+| `is_active` | bool | |
+
+**Indexes:** `(organisation_id, system_type)`
+
+---
+
+### `persons`
+
+| Column | Type | Notes |
+| ---------------- | ------------------ | -------------------------------------------------------------------- |
+| `id` | ULID | PK |
+| `user_id` | ULID FK nullable | → users — nullable: external guests/artists have no platform account |
+| `event_id` | ULID FK | → events |
+| `crowd_type_id` | ULID FK | → crowd_types |
+| `company_id` | ULID FK nullable | → companies |
+| `name` | string | |
+| `email` | string | Indexed deduplication key |
+| `phone` | string nullable | |
+| `status` | enum | `invited\|applied\|pending\|approved\|rejected\|no_show` |
+| `is_blacklisted` | bool | |
+| `admin_notes` | text nullable | |
+| `custom_fields` | JSON | Event-specific fields — not queryable |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Unique constraint:** `UNIQUE(event_id, user_id) WHERE user_id IS NOT NULL`
+**Indexes:** `(event_id, crowd_type_id, status)`, `(email, event_id)`, `(user_id, event_id)`
+**Soft delete:** yes
+
+---
+
+### `companies`
+
+| Column | Type | Notes |
+| ----------------- | ------------------ | ----------------------------------------- |
+| `id` | ULID | PK |
+| `organisation_id` | ULID FK | → organisations |
+| `name` | string | |
+| `type` | enum | `supplier\|partner\|agency\|venue\|other` |
+| `contact_name` | string nullable | |
+| `contact_email` | string nullable | |
+| `contact_phone` | string nullable | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Note:** Shared across events within an organisation.
+**Indexes:** `(organisation_id)`
+**Soft delete:** yes
+
+---
+
+### `crowd_lists`
+
+| Column | Type | Notes |
+| ---------------------- | ---------------- | -------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `crowd_type_id` | ULID FK | → crowd_types |
+| `name` | string | |
+| `type` | enum | `internal\|external` |
+| `recipient_company_id` | ULID FK nullable | → companies |
+| `auto_approve` | bool | |
+| `max_persons` | int nullable | |
+
+**Relations:** `hasMany` persons via `crowd_list_persons` pivot
+**Indexes:** `(event_id, type)`
+
+---
+
+### `crowd_list_persons`
+
+> **New pivot table (finding #4)**
+
+| Column | Type | Notes |
+| ------------------ | ---------------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `crowd_list_id` | ULID FK | → crowd_lists |
+| `person_id` | ULID FK | → persons |
+| `added_at` | timestamp | |
+| `added_by_user_id` | ULID FK nullable | → users |
+
+**Unique constraint:** `UNIQUE(crowd_list_id, person_id)`
+**Indexes:** `(person_id)`
+
+---
+
+## 3.5.6 Accreditation Engine
+
+> **Finding #5:** `event_accreditation_items` activates org-level items per event. Accreditation items are now configured at org level and activated per event with event-specific limits.
+
+### `accreditation_categories`
+
+| Column | Type | Notes |
+| ----------------- | --------------- | -------------------------------------------------------- |
+| `id` | ULID | PK |
+| `organisation_id` | ULID FK | → organisations |
+| `name` | string | e.g. Wristband, Food & Beverage, Communication, Clothing |
+| `sort_order` | int | |
+| `icon` | string nullable | |
+
+**Indexes:** `(organisation_id)`
+
+---
+
+### `accreditation_items`
+
+| Column | Type | Notes |
+| --------------------------- | --------------------- | -------------------------- |
+| `id` | ULID | PK |
+| `accreditation_category_id` | ULID FK | → accreditation_categories |
+| `name` | string | |
+| `is_date_dependent` | bool | |
+| `barcode_type` | enum | `qr\|code128\|ean13` |
+| `ticket_visual_url` | string nullable | |
+| `cost_price` | decimal(8,2) nullable | |
+| `sort_order` | int | |
+
+**Note:** Org-level items, activated per event via `event_accreditation_items`.
+
+---
+
+### `event_accreditation_items`
+
+> **New table (finding #5)**
+
+| Column | Type | Notes |
+| ------------------------- | ------------- | --------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `accreditation_item_id` | ULID FK | → accreditation_items |
+| `max_quantity_per_person` | int nullable | |
+| `total_budget_quantity` | int nullable | |
+| `is_active` | bool | |
+| `notes` | text nullable | |
+
+**Unique constraint:** `UNIQUE(event_id, accreditation_item_id)`
+**Indexes:** `(event_id, is_active)`
+
+---
+
+### `accreditation_assignments`
+
+| Column | Type | Notes |
+| ----------------------- | ------------------ | --------------------------------------------------------- |
+| `id` | ULID | PK |
+| `person_id` | ULID FK | → persons |
+| `accreditation_item_id` | ULID FK | → accreditation_items |
+| `event_id` | ULID FK | → events — FK to event_accreditation_items for validation |
+| `date` | date nullable | For date-dependent items |
+| `quantity` | int | |
+| `is_handed_out` | bool | |
+| `handed_out_at` | timestamp nullable | |
+| `handed_out_by_user_id` | ULID FK nullable | → users |
+
+**Indexes:** `(person_id, event_id)`, `(accreditation_item_id, is_handed_out)`
+
+---
+
+### `access_zones`
+
+| Column | Type | Notes |
+| ------------- | ------------- | ------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | e.g. Backstage, VIP, Main Stage |
+| `zone_code` | varchar(20) | unique per event |
+| `description` | text nullable | |
+
+**Note:** Day-coupling via `access_zone_days` pivot.
+**Indexes:** `(event_id)`
+
+---
+
+### `access_zone_days`
+
+> **New table (finding #8: replaces JSON `days` column)**
+
+| Column | Type | Notes |
+| ---------------- | ------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `access_zone_id` | ULID FK | → access_zones |
+| `day_date` | date | |
+
+**Purpose:** Queryable — which zones are active on date X?
+**Unique constraint:** `UNIQUE(access_zone_id, day_date)`
+**Indexes:** `(day_date)`
+
+---
+
+### `person_access_zones`
+
+| Column | Type | Notes |
+| ---------------- | ----------------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `person_id` | ULID FK | → persons |
+| `access_zone_id` | ULID FK | → access_zones |
+| `valid_from` | datetime | |
+| `valid_to` | datetime nullable | |
+
+**Indexes:** `(person_id)`, `(access_zone_id)`
+
+---
+
+## 3.5.7 Artists & Advancing
+
+> **Finding #8:** `stages.active_days` JSON replaced by `stage_days` pivot. `milestone_flags` JSON remains (opaque toggle-set, never filtered).
+
+### `artists`
+
+| Column | Type | Notes |
+| ------------------- | ------------------ | -------------------------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `booking_status` | enum | `concept\|requested\|option\|confirmed\|contracted\|cancelled` |
+| `star_rating` | tinyint | 1–5 |
+| `project_leader_id` | ULID FK nullable | → users |
+| `milestone_flags` | JSON | Binary toggle-set — OK as JSON |
+| `advance_open_from` | datetime nullable | |
+| `advance_open_to` | datetime nullable | |
+| `portal_token` | ULID unique | Access to artist portal without account |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `hasMany` performances, advance_sections, artist_contacts, artist_riders
+**Soft delete:** yes
+
+---
+
+### `performances`
+
+| Column | Type | Notes |
+| ----------------- | ------- | ------------------------------- |
+| `id` | ULID | PK |
+| `artist_id` | ULID FK | → artists |
+| `stage_id` | ULID FK | → stages |
+| `date` | date | |
+| `start_time` | time | |
+| `end_time` | time | |
+| `booking_status` | string | |
+| `check_in_status` | enum | `expected\|checked_in\|no_show` |
+
+**Note:** B2B detection via overlap query on `stage_id + date + time window`.
+**Indexes:** `(stage_id, date, start_time, end_time)`
+
+---
+
+### `stages`
+
+| Column | Type | Notes |
+| ---------- | ------------ | -------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `color` | string | hex |
+| `capacity` | int nullable | |
+
+**Note:** Day-activation via `stage_days` pivot (finding #8).
+**Relations:** `hasMany` performances
+**Indexes:** `(event_id)`
+
+---
+
+### `stage_days`
+
+> **New table (finding #8: replaces `stages.active_days` JSON)**
+
+| Column | Type | Notes |
+| ---------- | ------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `stage_id` | ULID FK | → stages |
+| `day_date` | date | |
+
+**Unique constraint:** `UNIQUE(stage_id, day_date)`
+
+---
+
+### `advance_sections`
+
+| Column | Type | Notes |
+| ------------ | ----------------- | ------------------------------------------ |
+| `id` | ULID | PK |
+| `artist_id` | ULID FK | → artists |
+| `name` | string | |
+| `type` | enum | `guest_list\|contacts\|production\|custom` |
+| `is_open` | bool | |
+| `open_from` | datetime nullable | |
+| `open_to` | datetime nullable | |
+| `sort_order` | int | |
+
+**Note:** Crescat section model — each section independently submittable.
+**Indexes:** `(artist_id, is_open)`
+
+---
+
+### `advance_submissions`
+
+| Column | Type | Notes |
+| -------------------- | ------------------ | ------------------------------ |
+| `id` | ULID | PK |
+| `advance_section_id` | ULID FK | → advance_sections |
+| `submitted_by_name` | string | |
+| `submitted_by_email` | string | |
+| `submitted_at` | timestamp | |
+| `status` | enum | `pending\|accepted\|declined` |
+| `reviewed_by` | ULID FK nullable | → users |
+| `reviewed_at` | timestamp nullable | |
+| `data` | JSON | Free form data — not queryable |
+
+**Indexes:** `(advance_section_id, status)`
+
+---
+
+### `artist_contacts`
+
+| Column | Type | Notes |
+| -------------------- | --------------- | -------------------------------- |
+| `id` | ULID | PK |
+| `artist_id` | ULID FK | → artists |
+| `name` | string | |
+| `email` | string nullable | |
+| `phone` | string nullable | |
+| `role` | string | e.g. tour manager, agent, booker |
+| `receives_briefing` | bool | |
+| `receives_infosheet` | bool | |
+
+**Indexes:** `(artist_id)`
+
+---
+
+### `artist_riders`
+
+| Column | Type | Notes |
+| ----------- | ------- | ------------------------------------ |
+| `id` | ULID | PK |
+| `artist_id` | ULID FK | → artists |
+| `category` | enum | `technical\|hospitality` |
+| `items` | JSON | Unstructured rider data — OK as JSON |
+
+**Indexes:** `(artist_id, category)`
+
+---
+
+### `itinerary_items`
+
+| Column | Type | Notes |
+| --------------- | --------------- | -------------------------------------------------- |
+| `id` | ULID | PK |
+| `artist_id` | ULID FK | → artists |
+| `type` | enum | `transfer\|pickup\|delivery\|checkin\|performance` |
+| `datetime` | datetime | |
+| `from_location` | string nullable | |
+| `to_location` | string nullable | |
+| `notes` | text nullable | |
+
+**Note:** Flights/hotels: Out of Scope.
+**Indexes:** `(artist_id, datetime)`
+
+---
+
+## 3.5.8 Communication & Briefings
+
+> **Finding #11:** `broadcast_messages` extended with polymorphic `broadcast_message_targets` for flexible audience definition.
+
+### `briefing_templates`
+
+| Column | Type | Notes |
+| ------------ | ------- | ------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `type` | enum | `crowd\|artist\|volunteer\|supplier` |
+| `blocks` | JSON | Drag-and-drop block config — never filtered |
+| `is_default` | bool | |
+
+**Indexes:** `(event_id, type)`
+
+---
+
+### `briefings`
+
+| Column | Type | Notes |
+| ---------------------- | ----------------- | -------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `briefing_template_id` | ULID FK nullable | → briefing_templates |
+| `name` | string | |
+| `target_crowd_types` | JSON | Array of crowd_type IDs |
+| `send_from` | datetime nullable | |
+| `send_until` | datetime nullable | |
+| `status` | enum | `draft\|queued\|sending\|sent\|paused` |
+
+**Indexes:** `(event_id, status)`
+
+---
+
+### `briefing_sends`
+
+| Column | Type | Notes |
+| ------------- | ------------------ | ---------------------------------- |
+| `id` | ULID | PK |
+| `briefing_id` | ULID FK | → briefings |
+| `person_id` | ULID FK | → persons |
+| `status` | enum | `queued\|sent\|opened\|downloaded` |
+| `sent_at` | timestamp nullable | |
+| `opened_at` | timestamp nullable | |
+
+**Note:** Track per person per briefing. No soft delete — audit record.
+**Indexes:** `(status, briefing_id)` — queue processing, `(person_id)`
+
+---
+
+### `communication_campaigns`
+
+| Column | Type | Notes |
+| ----------------- | ----------------- | -------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `type` | enum | `email\|sms\|whatsapp` |
+| `name` | string | |
+| `body` | text | |
+| `recipient_group` | JSON | Target filter description |
+| `status` | enum | `draft\|scheduled\|sending\|sent\|cancelled` |
+| `scheduled_at` | datetime nullable | |
+| `sent_at` | datetime nullable | |
+| `sent_count` | int | |
+| `failed_count` | int | |
+
+**Note:** Bulk campaigns. SMS+WhatsApp via Zender.
+**Indexes:** `(event_id, type, status)`
+
+---
+
+### `messages`
+
+| Column | Type | Notes |
+| --------------------- | ------------------ | ---------------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `sender_user_id` | ULID FK | → users |
+| `recipient_person_id` | ULID FK | → persons |
+| `body` | text | |
+| `urgency` | enum | `normal\|urgent\|emergency` |
+| `channel_used` | enum | `email\|sms\|whatsapp` — determined by ZenderService |
+| `read_at` | timestamp nullable | |
+| `replied_at` | timestamp nullable | |
+| `created_at` | timestamp | |
+
+**Indexes:** `(event_id, recipient_person_id)`, `(recipient_person_id, read_at)`
+
+---
+
+### `message_replies`
+
+| Column | Type | Notes |
+| --------------- | ------------- | --------------------------------- |
+| `id` | ULID | PK |
+| `message_id` | ULID FK | → messages |
+| `person_id` | ULID FK | → persons |
+| `body` | text | |
+| `status_update` | enum nullable | `on_my_way\|arrived\|sick\|other` |
+| `created_at` | timestamp | |
+
+**Note:** Volunteer replies via portal. No soft delete — audit record.
+**Indexes:** `(message_id)`
+
+---
+
+### `broadcast_messages`
+
+| Column | Type | Notes |
+| ----------------- | --------- | --------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `sender_user_id` | ULID FK | → users |
+| `body` | text | |
+| `urgency` | enum | `normal\|urgent\|emergency` |
+| `channel_used` | enum | `email\|sms\|whatsapp` |
+| `sent_at` | timestamp | |
+| `recipient_count` | int | |
+| `read_count` | int | |
+
+**Note:** Group message. Audience defined via `broadcast_message_targets`.
+**Indexes:** `(event_id, sent_at)`
+
+---
+
+### `broadcast_message_targets`
+
+> **New polymorphic table (finding #11)**
+
+| Column | Type | Notes |
+| ---------------------- | ------------- | ------------------------------------------------ |
+| `id` | int AI | PK — integer for join performance |
+| `broadcast_message_id` | ULID FK | → broadcast_messages |
+| `target_type` | enum | `event\|section\|shift\|crowd_type\|custom_list` |
+| `target_id` | ULID nullable | NULL when `target_type = event` (entire event) |
+
+**Note:** Multiple targets per message possible.
+**Indexes:** `(broadcast_message_id)`
+
+---
+
+## 3.5.9 Forms, Check-In & Operational
+
+### `public_forms`
+
+| Column | Type | Notes |
+| ----------------------------- | ------------- | -------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `crowd_type_id` | ULID FK | → crowd_types |
+| `fields` | JSON | Form config — not filtered |
+| `conditional_logic` | JSON | Form config — not filtered |
+| `iframe_token` | ULID unique | |
+| `confirmation_email_template` | text nullable | |
+| `is_active` | bool | |
+
+**Indexes:** `(event_id, crowd_type_id, is_active)`
+
+---
+
+### `form_submissions`
+
+| Column | Type | Notes |
+| ---------------- | --------- | --------------------------------- |
+| `id` | ULID | PK |
+| `public_form_id` | ULID FK | → public_forms |
+| `person_id` | ULID FK | → persons |
+| `data` | JSON | Free form results — not queryable |
+| `submitted_at` | timestamp | |
+
+**Indexes:** `(public_form_id, submitted_at)`, `(person_id)`
+
+---
+
+### `check_ins`
+
+| Column | Type | Notes |
+| -------------------- | ---------------- | ----------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `person_id` | ULID FK | → persons |
+| `scanned_by_user_id` | ULID FK nullable | → users |
+| `scanner_id` | ULID FK nullable | → scanners |
+| `scanned_at` | timestamp | |
+| `location_id` | ULID FK nullable | → locations |
+
+**Note:** Immutable audit record — NO soft delete.
+**Indexes:** `(event_id, person_id, scanned_at)`, `(event_id, scanned_at)`
+
+---
+
+### `show_day_absence_alerts`
+
+| Column | Type | Notes |
+| ----------------- | ------------------ | -------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `shift_id` | ULID FK | → shifts |
+| `person_id` | ULID FK | → persons |
+| `alert_sent_at` | timestamp | |
+| `response_status` | enum | `no_response\|confirmed\|absent\|late` |
+| `resolved_at` | timestamp nullable | |
+
+**Note:** Immutable audit record — NO soft delete.
+**Indexes:** `(shift_id, response_status)`, `(event_id, alert_sent_at)`
+
+---
+
+### `scanners`
+
+| Column | Type | Notes |
+| ---------------- | ------------------ | ---------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | |
+| `type` | enum | `crowd\|zone\|accreditation` |
+| `scope` | JSON | Scanner configuration |
+| `pairing_code` | varchar(8) unique | |
+| `last_active_at` | timestamp nullable | |
+
+**Indexes:** `(event_id)`, `(pairing_code)`
+
+---
+
+### `inventory_items`
+
+| Column | Type | Notes |
+| ----------------------- | ------------------ | ----------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `name` | string | e.g. walkie-talkie, vest, key |
+| `item_code` | varchar(50) | |
+| `assigned_to_person_id` | ULID FK nullable | → persons |
+| `assigned_at` | timestamp nullable | |
+| `returned_at` | timestamp nullable | |
+| `returned_by_user_id` | ULID FK nullable | → users |
+
+**Indexes:** `(event_id, assigned_to_person_id)`, `(item_code)`
+
+---
+
+### `event_info_blocks`
+
+| Column | Type | Notes |
+| -------------- | ------- | ---------------------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `type` | enum | `description\|route\|parking\|contacts\|marketing\|custom` |
+| `title` | string | |
+| `content` | text | |
+| `files` | JSON | Array of file paths |
+| `sort_order` | int | |
+| `is_published` | bool | |
+
+**Note:** Visibility per crowd_type via `event_info_block_crowd_types`.
+**Indexes:** `(event_id, type, is_published)`
+
+---
+
+### `event_info_block_crowd_types`
+
+> **Finding #8: replaces `visible_to_crowd_types` JSON column**
+
+| Column | Type | Notes |
+| --------------------- | ------- | --------------------------------- |
+| `id` | int AI | PK — integer for join performance |
+| `event_info_block_id` | ULID FK | → event_info_blocks |
+| `crowd_type_id` | ULID FK | → crowd_types |
+
+**Unique constraint:** `UNIQUE(event_info_block_id, crowd_type_id)`
+
+---
+
+### `production_requests`
+
+> **New table (finding #3: missing table)**
+
+| Column | Type | Notes |
+| -------------- | ------------------ | --------------------------------------------------------- |
+| `id` | ULID | PK |
+| `event_id` | ULID FK | → events |
+| `company_id` | ULID FK | → companies |
+| `title` | string | |
+| `status` | enum | `draft\|sent\|in_progress\|submitted\|approved\|rejected` |
+| `token` | ULID unique | Portal access without account |
+| `sent_at` | timestamp nullable | |
+| `submitted_at` | timestamp nullable | |
+| `reviewed_by` | ULID FK nullable | → users |
+| `reviewed_at` | timestamp nullable | |
+| `deleted_at` | timestamp nullable | Soft delete |
+
+**Relations:** `hasMany` material_requests
+**Indexes:** `(event_id, status)`, `(company_id)`
+**Soft delete:** yes
+
+---
+
+### `material_requests`
+
+| Column | Type | Notes |
+| ----------------------- | ----------------- | ------------------------------------------ |
+| `id` | ULID | PK |
+| `production_request_id` | ULID FK | → production_requests |
+| `category` | enum | `heavy_equipment\|tools\|vehicles\|other` |
+| `name` | string | |
+| `description` | text nullable | |
+| `quantity` | int | |
+| `period_from` | datetime nullable | |
+| `period_to` | datetime nullable | |
+| `status` | enum | `requested\|approved\|rejected\|fulfilled` |
+| `notes` | text nullable | |
+
+**Indexes:** `(production_request_id, status)`
+
+---
+
+## 3.5.10 Database Design Rules & Index Strategy
+
+### Rule 1 — ULID as Primary Key
+
+- Business tables: `$table->ulid('id')->primary()` + `HasUlids` trait
+- Pure pivot/link tables (no own lifecycle): `$table->id()` (auto-increment integer) for join performance
+- Never UUID v4 — avoids InnoDB B-tree fragmentation
+
+---
+
+### Rule 2 — JSON Columns: When Yes, When No
+
+| ✅ Use JSON for | ❌ Never JSON for |
+| ----------------------------------------------- | ------------------------------------- |
+| Opaque config (blocks, fields, settings, items) | Dates/periods |
+| Toggle-sets (milestone_flags) | Status values |
+| Free-text arrays (top_feedback) | Foreign keys |
+| Unstructured rider data | Boolean flags |
+| | Anything you filter/sort/aggregate on |
+
+**Replaced in v1.3:**
+
+- `access_zone_days` (was `days` JSON)
+- `stage_days` (was `active_days` JSON)
+- `broadcast_message_targets` (was `target` JSON)
+- `event_info_block_crowd_types` (was `visible_to_crowd_types` JSON)
+- `festival_retrospectives` columns (were in `data` JSON blob)
+
+---
+
+### Rule 3 — Soft Delete Strategy
+
+**Soft delete YES:** `organisations`, `events`, `festival_sections`, `shifts`, `shift_assignments`, `persons`, `artists`, `companies`, `production_requests`
+
+**Soft delete NO (immutable audit records):** `check_ins`, `show_day_absence_alerts`, `briefing_sends`, `message_replies`, `audit_log`, `shift_waitlist`, `volunteer_festival_history`
+
+> **Rationale:** Soft deleting audit records creates a false picture of reality.
+
+---
+
+### Rule 4 — Required Indexes (minimum set)
+
+| Table | Indexes |
+| ------------------- | ------------------------------------------------------------------------------- |
+| `persons` | `(event_id, crowd_type_id, status)`, `(email, event_id)`, `(user_id, event_id)` |
+| `shift_assignments` | `UNIQUE(person_id, time_slot_id)`, `(shift_id, status)`, `(person_id, status)` |
+| `check_ins` | `(event_id, person_id, scanned_at)`, `(event_id, scanned_at)` |
+| `briefing_sends` | `(status, briefing_id)` — queue processing |
+| `shift_waitlist` | `(shift_id, position)` |
+| `performances` | `(stage_id, date, start_time, end_time)` — B2B overlap detection |
+
+> Add `EXPLAIN ANALYZE` to queries taking >100ms. Target: all list queries <50ms.
+
+---
+
+### Rule 5 — Multi-Tenancy Scoping
+
+- Every query on event data **MUST** scope on `organisation_id` via Eloquent Global Scope (`OrganisationScope`)
+- Use Laravel policies for authorisation: never direct id-checks in controllers
+- **Audit log:** Spatie `laravel-activitylog` on: `persons`, `accreditation_assignments`, `shift_assignments`, `check_ins`, `production_requests`
diff --git a/docs/SETUP.md b/docs/SETUP.md
index 050a579..ccd06e2 100644
--- a/docs/SETUP.md
+++ b/docs/SETUP.md
@@ -1,6 +1,6 @@
-# Band Management - Setup Guide
+# Event Crew - Setup Guide
-This guide walks you through setting up the Band Management project from scratch.
+This guide walks you through setting up the Event Crew project from scratch.
## Cursor AI Configuration
@@ -60,7 +60,7 @@ docker -v # Should show Docker version
## Step 1: Start Docker Services
```bash
-cd band-management
+cd event-crew
make services
```
@@ -85,8 +85,8 @@ Requirements:
- Set up CORS for localhost:5173, localhost:5174, localhost:5175
- Use MySQL with these credentials:
- Host: 127.0.0.1
- - Database: band_management
- - Username: band_management
+ - Database: event_crew
+ - Username: event_crew
- Password: secret
Follow the conventions in .cursor/rules for code style.
@@ -95,7 +95,7 @@ Follow the conventions in .cursor/rules for code style.
### Manual Alternative
```bash
-cd band-management
+cd event-crew
composer create-project laravel/laravel api
cd api
composer require laravel/sanctum
@@ -107,8 +107,8 @@ Then configure `api/.env`:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
-DB_DATABASE=band_management
-DB_USERNAME=band_management
+DB_DATABASE=event_crew
+DB_USERNAME=event_crew
DB_PASSWORD=secret
SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174,localhost:5175
@@ -164,7 +164,7 @@ cd ../customers && pnpm install
**apps/admin/.env.local**
```env
VITE_API_URL=http://localhost:8000/api/v1
-VITE_APP_NAME="Band Management Admin"
+VITE_APP_NAME="Event Crew Admin"
```
**apps/band/.env.local**
diff --git a/resources/design/EventCrew_Design_Document_v1.3.docx b/resources/design/EventCrew_Design_Document_v1.3.docx
new file mode 100644
index 0000000..5567504
Binary files /dev/null and b/resources/design/EventCrew_Design_Document_v1.3.docx differ
diff --git a/resources/design/EventCrew_Dev_Guide_v1.0.docx b/resources/design/EventCrew_Dev_Guide_v1.0.docx
new file mode 100644
index 0000000..2683997
Binary files /dev/null and b/resources/design/EventCrew_Dev_Guide_v1.0.docx differ
diff --git a/resources/design/EventCrew_Start_Guide_v1.0.docx b/resources/design/EventCrew_Start_Guide_v1.0.docx
new file mode 100644
index 0000000..56de85c
Binary files /dev/null and b/resources/design/EventCrew_Start_Guide_v1.0.docx differ
diff --git a/resources/inspiration/Crescat/01JBVRBYDHK5Y6D74Q0CB08VFB.png b/resources/inspiration/Crescat/01JBVRBYDHK5Y6D74Q0CB08VFB.png
new file mode 100644
index 0000000..6eb5300
Binary files /dev/null and b/resources/inspiration/Crescat/01JBVRBYDHK5Y6D74Q0CB08VFB.png differ
diff --git a/resources/inspiration/Crescat/01JBVS7CC9HC7F0EWHZE1921FZ.png b/resources/inspiration/Crescat/01JBVS7CC9HC7F0EWHZE1921FZ.png
new file mode 100644
index 0000000..c4a903d
Binary files /dev/null and b/resources/inspiration/Crescat/01JBVS7CC9HC7F0EWHZE1921FZ.png differ
diff --git a/resources/inspiration/Crescat/01JBVS9V439K8FANVDWN9BP2DH.png b/resources/inspiration/Crescat/01JBVS9V439K8FANVDWN9BP2DH.png
new file mode 100644
index 0000000..c38f79e
Binary files /dev/null and b/resources/inspiration/Crescat/01JBVS9V439K8FANVDWN9BP2DH.png differ
diff --git a/resources/inspiration/Crescat/01JBVTQBKR7VP1HZRXG1SZ1EKM.png b/resources/inspiration/Crescat/01JBVTQBKR7VP1HZRXG1SZ1EKM.png
new file mode 100644
index 0000000..208c550
Binary files /dev/null and b/resources/inspiration/Crescat/01JBVTQBKR7VP1HZRXG1SZ1EKM.png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 001 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 001 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..48bf3f7
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 001 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 002 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 002 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..ccf3bfa
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 002 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 003 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 003 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..0f86320
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 003 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 004 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 004 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..afabc5a
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 004 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 005 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 005 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..7400480
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 005 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 006 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 006 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..175711f
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 006 - Advance - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 007 - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 007 - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..cccefd6
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 007 - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 008 - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 008 - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..7e690a4
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 008 - Sophia Rivera - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 009 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 009 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png
new file mode 100644
index 0000000..066d514
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 009 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 010 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 010 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png
new file mode 100644
index 0000000..9fb5d6a
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 010 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 011 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 011 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png
new file mode 100644
index 0000000..481e2f5
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 011 - Volunteers Echt Feesten! 2026 2026 - Echt Feesten! 2026 - Crescat_ - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 012 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 012 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..7453bf0
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 012 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 013 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 013 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..9f8c6d8
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 013 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 014 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 014 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..7f57da5
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 014 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/Crescat/FireShot Capture 015 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png b/resources/inspiration/Crescat/FireShot Capture 015 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png
new file mode 100644
index 0000000..7453bf0
Binary files /dev/null and b/resources/inspiration/Crescat/FireShot Capture 015 - Volunteers - Echt Feesten! 2026 - [app.crescat.io].png differ
diff --git a/resources/inspiration/In2event/Artist-1.png b/resources/inspiration/In2event/Artist-1.png
new file mode 100644
index 0000000..9fa305f
Binary files /dev/null and b/resources/inspiration/In2event/Artist-1.png differ
diff --git a/resources/inspiration/In2event/Artist-2-.png b/resources/inspiration/In2event/Artist-2-.png
new file mode 100644
index 0000000..d7011bc
Binary files /dev/null and b/resources/inspiration/In2event/Artist-2-.png differ
diff --git a/resources/inspiration/In2event/Artist-3.png b/resources/inspiration/In2event/Artist-3.png
new file mode 100644
index 0000000..7fc8fca
Binary files /dev/null and b/resources/inspiration/In2event/Artist-3.png differ
diff --git a/resources/inspiration/In2event/Event-Sustainability-2048x1152.jpg b/resources/inspiration/In2event/Event-Sustainability-2048x1152.jpg
new file mode 100644
index 0000000..1d9944a
Binary files /dev/null and b/resources/inspiration/In2event/Event-Sustainability-2048x1152.jpg differ
diff --git a/resources/inspiration/In2event/Guest-1.png b/resources/inspiration/In2event/Guest-1.png
new file mode 100644
index 0000000..f606eeb
Binary files /dev/null and b/resources/inspiration/In2event/Guest-1.png differ
diff --git a/resources/inspiration/In2event/Guest-2.png b/resources/inspiration/In2event/Guest-2.png
new file mode 100644
index 0000000..f7c7eff
Binary files /dev/null and b/resources/inspiration/In2event/Guest-2.png differ
diff --git a/resources/inspiration/In2event/Production-3.1-.png b/resources/inspiration/In2event/Production-3.1-.png
new file mode 100644
index 0000000..e574057
Binary files /dev/null and b/resources/inspiration/In2event/Production-3.1-.png differ
diff --git a/resources/inspiration/In2event/Staff-2.png b/resources/inspiration/In2event/Staff-2.png
new file mode 100644
index 0000000..035eea6
Binary files /dev/null and b/resources/inspiration/In2event/Staff-2.png differ
diff --git a/resources/inspiration/In2event/Staff-3.png b/resources/inspiration/In2event/Staff-3.png
new file mode 100644
index 0000000..b65feb2
Binary files /dev/null and b/resources/inspiration/In2event/Staff-3.png differ
diff --git a/resources/inspiration/In2event/TypeRegister-volunteer.png b/resources/inspiration/In2event/TypeRegister-volunteer.png
new file mode 100644
index 0000000..21d4a6b
Binary files /dev/null and b/resources/inspiration/In2event/TypeRegister-volunteer.png differ
diff --git a/resources/inspiration/In2event/TypeSupplier.png b/resources/inspiration/In2event/TypeSupplier.png
new file mode 100644
index 0000000..d9c9379
Binary files /dev/null and b/resources/inspiration/In2event/TypeSupplier.png differ
diff --git a/resources/inspiration/In2event/Website-Container-13.png b/resources/inspiration/In2event/Website-Container-13.png
new file mode 100644
index 0000000..975ef2a
Binary files /dev/null and b/resources/inspiration/In2event/Website-Container-13.png differ
diff --git a/resources/inspiration/In2event/Website-Container-3.png b/resources/inspiration/In2event/Website-Container-3.png
new file mode 100644
index 0000000..84981ce
Binary files /dev/null and b/resources/inspiration/In2event/Website-Container-3.png differ
diff --git a/resources/inspiration/In2event/Zonder-titel-20-x-15-cm-2.png b/resources/inspiration/In2event/Zonder-titel-20-x-15-cm-2.png
new file mode 100644
index 0000000..29bdf2e
Binary files /dev/null and b/resources/inspiration/In2event/Zonder-titel-20-x-15-cm-2.png differ
diff --git a/resources/inspiration/In2event/Zonder-titel-20-x-15-cm-4.png b/resources/inspiration/In2event/Zonder-titel-20-x-15-cm-4.png
new file mode 100644
index 0000000..db5caed
Binary files /dev/null and b/resources/inspiration/In2event/Zonder-titel-20-x-15-cm-4.png differ
diff --git a/resources/inspiration/In2event/dashboard.webp b/resources/inspiration/In2event/dashboard.webp
new file mode 100644
index 0000000..d4fc5ba
Binary files /dev/null and b/resources/inspiration/In2event/dashboard.webp differ
diff --git a/resources/inspiration/In2event/guest-registration-3-1.png b/resources/inspiration/In2event/guest-registration-3-1.png
new file mode 100644
index 0000000..5cf1e4e
Binary files /dev/null and b/resources/inspiration/In2event/guest-registration-3-1.png differ
diff --git a/resources/inspiration/In2event/homepage-visual-2048x866.png b/resources/inspiration/In2event/homepage-visual-2048x866.png
new file mode 100644
index 0000000..fc4de20
Binary files /dev/null and b/resources/inspiration/In2event/homepage-visual-2048x866.png differ
diff --git a/resources/inspiration/In2event/production-1.png b/resources/inspiration/In2event/production-1.png
new file mode 100644
index 0000000..0b0d99c
Binary files /dev/null and b/resources/inspiration/In2event/production-1.png differ
diff --git a/resources/inspiration/In2event/program-advancing-1-1.png b/resources/inspiration/In2event/program-advancing-1-1.png
new file mode 100644
index 0000000..f99908f
Binary files /dev/null and b/resources/inspiration/In2event/program-advancing-1-1.png differ
diff --git a/resources/inspiration/In2event/program-advancing-2-1.png b/resources/inspiration/In2event/program-advancing-2-1.png
new file mode 100644
index 0000000..94c7507
Binary files /dev/null and b/resources/inspiration/In2event/program-advancing-2-1.png differ
diff --git a/resources/inspiration/In2event/program-advancing-3-1.png b/resources/inspiration/In2event/program-advancing-3-1.png
new file mode 100644
index 0000000..295e4bf
Binary files /dev/null and b/resources/inspiration/In2event/program-advancing-3-1.png differ
diff --git a/resources/inspiration/In2event/program-advancing-hero-2.png b/resources/inspiration/In2event/program-advancing-hero-2.png
new file mode 100644
index 0000000..d0653d3
Binary files /dev/null and b/resources/inspiration/In2event/program-advancing-hero-2.png differ
diff --git a/resources/inspiration/In2event/staff-management-1-1.png b/resources/inspiration/In2event/staff-management-1-1.png
new file mode 100644
index 0000000..8fe0ed4
Binary files /dev/null and b/resources/inspiration/In2event/staff-management-1-1.png differ
diff --git a/resources/inspiration/In2event/staff-management-2-1.png b/resources/inspiration/In2event/staff-management-2-1.png
new file mode 100644
index 0000000..051b8e3
Binary files /dev/null and b/resources/inspiration/In2event/staff-management-2-1.png differ
diff --git a/resources/inspiration/In2event/staff-management-3-1.png b/resources/inspiration/In2event/staff-management-3-1.png
new file mode 100644
index 0000000..2639e2c
Binary files /dev/null and b/resources/inspiration/In2event/staff-management-3-1.png differ
diff --git a/resources/inspiration/In2event/staff-management-4-1.png b/resources/inspiration/In2event/staff-management-4-1.png
new file mode 100644
index 0000000..c787af9
Binary files /dev/null and b/resources/inspiration/In2event/staff-management-4-1.png differ
diff --git a/resources/inspiration/In2event/staff-management-hero-1-3.png b/resources/inspiration/In2event/staff-management-hero-1-3.png
new file mode 100644
index 0000000..d8f09d7
Binary files /dev/null and b/resources/inspiration/In2event/staff-management-hero-1-3.png differ
diff --git a/resources/inspiration/In2event/thumbnail_in2event.png b/resources/inspiration/In2event/thumbnail_in2event.png
new file mode 100644
index 0000000..7cbfec3
Binary files /dev/null and b/resources/inspiration/In2event/thumbnail_in2event.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-add-member.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-add-member.png
new file mode 100644
index 0000000..7945cb5
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-add-member.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-automation.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-automation.png
new file mode 100644
index 0000000..0de815f
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-automation.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-communication-campaign.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-communication-campaign.png
new file mode 100644
index 0000000..c55fa2b
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-communication-campaign.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-communication.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-communication.png
new file mode 100644
index 0000000..5752c2c
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-communication.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-dashboard-mission.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-dashboard-mission.png
new file mode 100644
index 0000000..4233624
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-dashboard-mission.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-dashboard.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-dashboard.png
new file mode 100644
index 0000000..4c22fc5
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-dashboard.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-event.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-event.png
new file mode 100644
index 0000000..f485cbb
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-event.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-form-creation.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-form-creation.png
new file mode 100644
index 0000000..ccb62e0
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-form-creation.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-form.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-form.png
new file mode 100644
index 0000000..35733ee
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-form.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-manager.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-manager.png
new file mode 100644
index 0000000..54b67e0
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-manager.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-campaign.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-campaign.png
new file mode 100644
index 0000000..c787b73
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-campaign.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-definition.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-definition.png
new file mode 100644
index 0000000..de9bab8
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-definition.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-description.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-description.png
new file mode 100644
index 0000000..3017f24
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-description.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-information.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-information.png
new file mode 100644
index 0000000..5e0c321
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-information.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-location.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-location.png
new file mode 100644
index 0000000..8003371
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-location.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-schedule.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-schedule.png
new file mode 100644
index 0000000..1e9ffee
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-mission-schedule.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-planning.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-planning.png
new file mode 100644
index 0000000..1694528
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-planning.png differ
diff --git a/resources/inspiration/WeezCrew/en-gb-weezcrew-statistics.png b/resources/inspiration/WeezCrew/en-gb-weezcrew-statistics.png
new file mode 100644
index 0000000..8757a5e
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gb-weezcrew-statistics.png differ
diff --git a/resources/inspiration/WeezCrew/en-gbp-weezticket-create-event-ticketing.png b/resources/inspiration/WeezCrew/en-gbp-weezticket-create-event-ticketing.png
new file mode 100644
index 0000000..5c5cab9
Binary files /dev/null and b/resources/inspiration/WeezCrew/en-gbp-weezticket-create-event-ticketing.png differ
diff --git a/resources/vuexy-admin-v10.11.1/design-files/figma/vuexy-figma-dashboard-ui-kit-and-builder-v4.zip b/resources/vuexy-admin-v10.11.1/design-files/figma/vuexy-figma-dashboard-ui-kit-and-builder-v4.zip
deleted file mode 100644
index 215a378..0000000
Binary files a/resources/vuexy-admin-v10.11.1/design-files/figma/vuexy-figma-dashboard-ui-kit-and-builder-v4.zip and /dev/null differ
diff --git a/resources/~$entCrew_Design_Document_v1.3.docx b/resources/~$entCrew_Design_Document_v1.3.docx
new file mode 100644
index 0000000..291e916
Binary files /dev/null and b/resources/~$entCrew_Design_Document_v1.3.docx differ
diff --git a/resources/~$entCrew_Dev_Guide_v1.0.docx b/resources/~$entCrew_Dev_Guide_v1.0.docx
new file mode 100644
index 0000000..291e916
Binary files /dev/null and b/resources/~$entCrew_Dev_Guide_v1.0.docx differ
diff --git a/resources/~$entCrew_Start_Guide_v1.0.docx b/resources/~$entCrew_Start_Guide_v1.0.docx
new file mode 100644
index 0000000..291e916
Binary files /dev/null and b/resources/~$entCrew_Start_Guide_v1.0.docx differ