diff --git a/.gitignore b/.gitignore index 0ec42e1..1defa31 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,7 @@ coverage/ # Design / assets temp files (e.g. Illustrator) resources/**/*.tmp + +# VitePress +docs/.vitepress/dist +docs/.vitepress/cache diff --git a/CLAUDE.md b/CLAUDE.md index 4954121..d15f0f1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Crewli is a multi-tenant SaaS platform for event and festival management. Built for a professional volunteer organisation, with potential to expand as SaaS. -Design document: `/resources/design/design-document.md` +Design document: `/dev-docs/design-document.md` ## Tech stack @@ -130,3 +130,38 @@ Configure three frontend origins in both Laravel (`config/cors.php` via env) and 11. Pinia store if cross-component state is needed 12. Vue page component 13. Add route in Vue Router + +## User Documentation (VitePress) + +End-user documentation lives in /docs/ as a VitePress site. +Developer documentation (SCHEMA.md, API.md, etc.) lives in /dev-docs/. + +### When to write docs + +When completing a feature that introduces or changes **user-facing behaviour**, +create or update the corresponding documentation page under /docs/. + +### How to write docs + +1. Read /docs/.templates/style-guide.md for terminology, tone, and structure rules +2. Use /docs/.templates/feature-page.md or concept-page.md as your starting template +3. Every page MUST have frontmatter with: title, description, tags +4. Use Dutch language for all content (informal "je/jij") +5. Use Crewli terminology from the style guide — never English equivalents in user docs + +### What to include + +- What the feature does (2-3 sentences) +- Step-by-step instructions from the user's perspective +- Which roles have access (table format) +- Screenshot placeholders where visual guidance helps: `![Beschrijving](./images/placeholder.png)` +- Links to related pages + +### File placement + +Match the existing structure under /docs/: + +- Organizer features → /docs/organizer/[category]/ +- Volunteer features → /docs/volunteer/ +- Portal features → /docs/portal/ +- General concepts → /docs/guide/ diff --git a/dev-docs/API.md b/dev-docs/API.md new file mode 100644 index 0000000..fd415fc --- /dev/null +++ b/dev-docs/API.md @@ -0,0 +1,165 @@ +# Crewli API Contract + +Base path: `/api/v1/` + +Auth: Bearer token (Sanctum) + +## Auth + +- `POST /auth/login` +- `POST /auth/logout` +- `GET /auth/me` + +## Organisations + +- `GET /organisations` — list (super admin) +- `POST /organisations` — create +- `GET /organisations/{org}` — show +- `PUT /organisations/{org}` — update +- `GET /organisations/{org}/members` — members +- `POST /organisations/{org}/invite` — invite user + +## Events + +- `GET /organisations/{org}/events` — list (top-level only by default) +- `GET /organisations/{org}/events?include_children=true` — include sub-events nested in response +- `GET /organisations/{org}/events?type=festival` — filter by event_type (festival|series|event) +- `POST /organisations/{org}/events` — create (supports `parent_event_id` for sub-events) +- `GET /organisations/{org}/events/{event}` — detail (includes children and parent if loaded) +- `PUT /organisations/{org}/events/{event}` — update (does NOT accept `status` — use transition endpoint) +- `POST /organisations/{org}/events/{event}/transition` — change event status via state machine (see below) +- `GET /organisations/{org}/events/{event}/children` — list sub-events of a festival/series + +### Event Status Transitions + +`POST /organisations/{org}/events/{event}/transition` + +Body: `{ "status": "published" }` + +Enforces a state machine: only valid forward (and select backward) transitions are allowed. +Returns 422 with `errors`, `current_status`, `requested_status`, and `allowed_transitions` when the transition is invalid or prerequisites are missing. + +**Prerequisites checked:** +- `→ published`: name, start_date, end_date required +- `→ registration_open`: at least one time slot and one section required + +**Festival cascade:** Transitioning a festival parent to `showday`, `teardown`, or `closed` automatically cascades to all children in an earlier status. + +**EventResource** includes `allowed_transitions` (array of valid next statuses) so the frontend knows which buttons to show. + +## Crowd Types + +- `GET /organisations/{org}/crowd-types` +- `POST /organisations/{org}/crowd-types` +- `PUT /organisations/{org}/crowd-types/{type}` +- `DELETE /organisations/{org}/crowd-types/{type}` + +## Companies + +- `GET /organisations/{org}/companies` +- `POST /organisations/{org}/companies` +- `PUT /organisations/{org}/companies/{company}` +- `DELETE /organisations/{org}/companies/{company}` + +## Section Categories + +- `GET /organisations/{org}/section-categories` — distinct categories used across the organisation's events (for autocomplete). Returns `{ "data": ["Bar", "Podium", ...] }` + +## Festival Sections + +- `GET /events/{event}/sections` +- `POST /events/{event}/sections` +- `PUT /events/{event}/sections/{section}` +- `DELETE /events/{event}/sections/{section}` +- `POST /events/{event}/sections/reorder` + +> **Festival context:** `{event}` can be a festival parent or a sub-event. +> On a festival parent, sections are for operational planning (build-up, teardown). +> For sub-events, `GET` automatically includes `cross_event` sections from the parent festival. +> Shifts on cross_event sections must use the **parent festival's event_id** in API calls, +> since the section's `event_id` points to the parent. + +## Time Slots + +- `GET /events/{event}/time-slots` +- `POST /events/{event}/time-slots` +- `PUT /events/{event}/time-slots/{timeSlot}` +- `DELETE /events/{event}/time-slots/{timeSlot}` + +> **Festival context:** `{event}` can be a festival parent or a sub-event. +> Festival-level time slots (operational: build-up, teardown, transitions) are separate +> from sub-event time slots (program-specific). +> +> `GET /events/{event}/time-slots` returns only the specified event's own time slots by +> default. For sub-events, pass `?include_parent=true` to also include the parent festival's +> time slots — each time slot is marked with a `source` field (`sub_event` or `festival`) +> and includes `event_name` for display grouping. This parameter has no effect on festivals +> or flat events. +> +> Shifts on sub-event sections may reference parent festival time slots (e.g. for build-up +> shifts). The `time_slot_id` validation accepts time slots from the sub-event itself or +> its parent festival. + +## Shifts + +- `GET /events/{event}/sections/{section}/shifts` +- `POST /events/{event}/sections/{section}/shifts` +- `PUT /events/{event}/sections/{section}/shifts/{shift}` +- `DELETE /events/{event}/sections/{section}/shifts/{shift}` +- `POST /events/{event}/sections/{section}/shifts/{shift}/assign` +- `POST /events/{event}/sections/{section}/shifts/{shift}/claim` + +> **Festival context:** When managing shifts on a `cross_event` section, the `{event}` +> in the URL must be the parent festival's ID (matching `section.event_id`), not the +> sub-event's ID. + +## 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` +- `DELETE /events/{event}/persons/{person}` + +## Crowd Lists + +- `GET /events/{event}/crowd-lists` +- `POST /events/{event}/crowd-lists` +- `PUT /events/{event}/crowd-lists/{list}` +- `DELETE /events/{event}/crowd-lists/{list}` +- `POST /events/{event}/crowd-lists/{list}/persons` +- `DELETE /events/{event}/crowd-lists/{list}/persons/{person}` + +## Locations + +- `GET /events/{event}/locations` +- `POST /events/{event}/locations` +- `PUT /events/{event}/locations/{location}` +- `DELETE /events/{event}/locations/{location}` + +## Person Tags (Organisation Settings) + +- `GET /organisations/{org}/person-tags` — list active tags (ordered) +- `POST /organisations/{org}/person-tags` — create tag +- `PUT /organisations/{org}/person-tags/{tag}` — update tag +- `DELETE /organisations/{org}/person-tags/{tag}` — deactivate tag (soft: sets `is_active = false`) +- `GET /organisations/{org}/person-tag-categories` — distinct categories for autocomplete + +## User Tag Assignments + +- `GET /organisations/{org}/users/{user}/tags` — list all tags for user in organisation +- `POST /organisations/{org}/users/{user}/tags` — assign a tag +- `DELETE /organisations/{org}/users/{user}/tags/{tagAssignment}` — remove assignment +- `PUT /organisations/{org}/users/{user}/tags/sync` — sync tags by source + +> **Sync endpoint:** Replaces tags of the specified `source` only. +> Body: `{ "tag_ids": ["ulid1", "ulid2"], "source": "self_reported" }` +> Removes `self_reported` tags not in the list, adds new ones, leaves `organiser_assigned` untouched (and vice versa). + +### Person list tag filtering + +- `GET /events/{event}/persons?tag={person_tag_id}` — filter persons by single tag +- `GET /events/{event}/persons?tags=ulid1,ulid2` — filter persons by multiple tags (AND logic: must have all) + +_(Extend this contract per module as endpoints are implemented.)_ diff --git a/docs/BACKLOG.md b/dev-docs/BACKLOG.md similarity index 78% rename from docs/BACKLOG.md rename to dev-docs/BACKLOG.md index ddc41bb..cf9ab41 100644 --- a/docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -84,6 +84,25 @@ op basis van: reliability score, aantal uren al ingepland, aanmeldvolgorde. --- +### ARCH-06 — Locatie-gebaseerd shift-overzicht + +Cross sub-event filter op location_id. Toont alle shifts op een fysieke locatie +ongeacht programmaonderdeel. +**Schema:** `locations` tabel en `shifts.location_id` bestaan al. +**Prioriteit:** Laag + +--- + +### ARCH-07 — Accreditatie-templates per sectie/dag combinatie + +MUST-HAVE bij accreditatie build. Templates worden primaire toewijzingsmethode. +Per crowd_type + sectie + dag → automatisch voorgestelde accreditatie-items. +Handmatige per-persoon toewijzing is de uitzondering, niet de norm. +**Schema:** Nieuwe tabel `accreditation_templates` nodig. +**Prioriteit:** Hoog — direct meebouwen bij accreditatie-module + +--- + ## Fase 3 — Communicatie & Notificaties ### COMM-01 — Real-time WebSocket notificaties @@ -380,5 +399,87 @@ realistische testdata nodig met parent/child events. --- +### ~~TECH-04 — EventController.store() redundante ternary~~ ✅ OPGELOST + +--- + +## Opgeloste items (april 2026) + +De volgende items zijn geïmplementeerd en afgerond: + +- ~~TECH-04: EventController.store() redundante ternary~~ ✅ +- ~~Auth race condition (CTRL+R fix)~~ ✅ +- ~~Section edit dialog bug~~ ✅ +- ~~Time slot duplicate button~~ ✅ +- ~~Browser autocomplete disabled op dialog form fields~~ ✅ +- ~~Category + icon fields op festival_sections~~ ✅ +- ~~IconPicker component~~ ✅ +- ~~Crowd Types beheer-UI~~ ✅ +- ~~Companies CRUD~~ ✅ +- ~~Person tags backend (person_tags + user_organisation_tags)~~ ✅ +- ~~Event status state machine (dedicated transition endpoint, prerequisites, festival cascade)~~ ✅ +- ~~Festival tab-navigatie (uniform tabs, Programmaonderdelen tab)~~ ✅ +- ~~SectionsShiftsPanel extractie als herbruikbaar component~~ ✅ +- ~~Cross-event section auto-redirect~~ ✅ + +--- + +## Nieuwe backlog items + +### ARCH-06 — Locatie-gebaseerd shift-overzicht + +Cross sub-event filter op location_id. Toont alle shifts op een fysieke locatie +ongeacht programmaonderdeel. +**Prioriteit:** Laag + +--- + +### ARCH-07 — Accreditatie-templates per sectie/dag combinatie + +MUST-HAVE bij accreditatie build. Templates worden primaire toewijzingsmethode. +Per crowd_type + sectie + dag → automatisch voorgestelde accreditatie-items. +**Prioriteit:** Hoog — direct meebouwen bij accreditatie-module + +--- + +### ARCH-08 — Recurrence voor time slots + +Herhalingsfunctie: "genereer 5 time slots in één keer" voor opbouwdagen etc. +**Prioriteit:** Middel + +--- + +### ART-03 — Artist profile met cross-event rider defaults + +Organisatie-niveau artiest-profiel dat rider-defaults, contacten en interne +notities opslaat over events heen. "Importeer van vorig jaar" functie. +**Prioriteit:** Laag + +--- + +### UX-01 — Festival setup checklist / onboarding wizard + +Checklist widget op festival dashboard die door de configuratiestappen leidt. +Items worden groen als ze zijn afgerond. +**Prioriteit:** Middel + +--- + +### UX-02 — Aandachtsmatrix dashboard + +Dashboard widget: hoeveel personen approved maar zonder shift? Hoeveel +shift-claims wachten op goedkeuring? Hoeveel pending identity matches? +**Prioriteit:** Middel + +--- + +### UX-03 — Personen-tab op sub-event niveau + +Gefilterde view: alleen personen met shifts in dit programmaonderdeel. +Met link "Bekijk alle personen op festival-niveau". +**Prioriteit:** Middel + +--- + _Laatste update: April 2026_ -_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-_ +_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-, UX-_ diff --git a/docs/SCHEMA.md b/dev-docs/SCHEMA.md similarity index 91% rename from docs/SCHEMA.md rename to dev-docs/SCHEMA.md index 34fc3fe..256d7d8 100644 --- a/docs/SCHEMA.md +++ b/dev-docs/SCHEMA.md @@ -41,7 +41,8 @@ 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) +10. [3.5.10 Person Tags & Skills](#3510-person-tags--skills) +11. [3.5.11 Database Design Rules & Index Strategy](#3511-database-design-rules--index-strategy) --- @@ -162,6 +163,33 @@ **Indexes:** `(organisation_id, status)`, `(parent_event_id)`, `UNIQUE(organisation_id, slug)` **Soft delete:** yes +**Status state machine:** + +``` +draft → published → registration_open → buildup → showday → teardown → closed + ↑ ↓ ↑ + └── (back) ────┘ │ + (back) ────┘ +``` + +Allowed transitions: + +| From | To | +| ------------------- | --------------------------- | +| `draft` | `published` | +| `published` | `registration_open`, `draft`| +| `registration_open` | `buildup`, `published` | +| `buildup` | `showday` | +| `showday` | `teardown` | +| `teardown` | `closed` | +| `closed` | _(terminal — no transitions)_ | + +**Prerequisites:** +- `→ published`: name, start_date, and end_date must be set +- `→ registration_open`: at least one time slot and one section must exist + +**Festival cascade:** When a festival parent transitions to `showday`, `teardown`, or `closed`, all children in an earlier status are automatically updated to the same status. Earlier statuses (`draft → published`) do NOT cascade. + **Helper scopes (Laravel):** ```php @@ -254,6 +282,8 @@ scopeFestivals() // WHERE event_type IN ('festival', 'series') | `id` | ULID | PK | | `event_id` | ULID FK | → events | | `name` | string | e.g. Horeca, Backstage, Overig, Entertainment | +| `category` | string nullable | **v1.8** Free-text grouping label (e.g. Bar, Podium, Operationeel)| +| `icon` | string nullable | **v1.8** Tabler icon name (e.g. tabler-beer, tabler-microphone-2) | | `type` | enum | `standard\|cross_event` — cross_event for EHBO, verkeersregelaars | | `sort_order` | int | default: 0 | | `crew_need` | int nullable | **v1.5** Total crew needed for this section (Crescat: Crew need) | @@ -267,7 +297,7 @@ scopeFestivals() // WHERE event_type IN ('festival', 'series') | `deleted_at` | timestamp nullable | Soft delete | **Relations:** `hasMany` shifts -**Indexes:** `(event_id, sort_order)` +**Indexes:** `(event_id, sort_order)`, `(event_id, category)` **Soft delete:** yes **Default values:** @@ -279,6 +309,16 @@ scopeFestivals() // WHERE event_type IN ('festival', 'series') - `responder_self_checkin`: true - `timed_accreditations`: false +> **Note:** "Overkoepelende" sections (shared across all sub-events of a festival) +> are identified by `type = 'cross_event'`. There is no separate `is_shared` boolean +> column — the `type` enum distinguishes standard sections from cross-event sections. + +**Festival context:** On a festival parent event, `standard` type sections are festival-only +operational sections (e.g. Terreinploeg, Bouwploeg). `cross_event` sections appear in all +sub-events (e.g. EHBO, Security, Backstage). On sub-events, all sections are `standard` +(program-specific). When querying sections for a sub-event, the API automatically includes +`cross_event` sections from the parent festival. + --- ### `time_slots` @@ -287,6 +327,15 @@ scopeFestivals() // WHERE event_type IN ('festival', 'series') > This naturally handles "generic shifts" — multiple sections referencing one Time Slot share the same time framework. > Per-shift time overrides are handled by `shifts.actual_start_time` / `actual_end_time`. +**Festival context:** Time slots on a festival parent event are for operational scheduling +(build-up, teardown, transitions). Time slots on sub-events are for program-specific +scheduling. When querying time slots for a sub-event with `?include_parent=true`, the parent +festival's time slots are included (marked with `source='festival'`) so that local sections +can create build-up, teardown, and transition shifts. Without this parameter, only the +sub-event's own time slots are returned. Festival-level queries never include sub-event +time slots. The `Event::getAllRelevantTimeSlots()` method can retrieve time slots across +levels for planning purposes. + | Column | Type | Notes | | ---------------- | --------------------- | --------------------------------------------------------------------- | | `id` | ULID | PK | @@ -1271,7 +1320,68 @@ $effectiveDate = $shift->end_date ?? $shift->timeSlot->date; --- -## 3.5.10 Database Design Rules & Index Strategy +## 3.5.10 Person Tags & Skills + +> Tag-based skills/competencies system for volunteers and crew. +> Tags are defined per organisation, assigned to users at the organisation level +> (persistent across events), and come from two sources: self-reported by +> volunteers during registration, or assigned by organisers based on experience. + +### `person_tags` + +| Column | Type | Notes | +| ----------------- | --------------- | ---------------------------------------- | +| `id` | ULID | PK, `HasUlids` trait | +| `organisation_id` | ULID FK | → organisations | +| `name` | string(50) | e.g. "Tapper", "EHBO", "Duits" | +| `category` | string(50) null | e.g. "Vaardigheid", "Taal", "Certificaat"| +| `icon` | string(50) null | Tabler icon name | +| `color` | string(7) null | Hex color | +| `is_active` | bool | default: true | +| `sort_order` | int | default: 0 | +| `created_at` | timestamp | | +| `updated_at` | timestamp | | + +**Relations:** `belongsTo` Organisation +**Indexes:** `(organisation_id, is_active, sort_order)` +**Unique constraint:** `UNIQUE(organisation_id, name)` +**No soft deletes** — tags are deactivated via `is_active = false` + +--- + +### `user_organisation_tags` + +> Tag assignments linking a user to a tag within an organisation. +> Persistent across events — tags live on user+org level, not event level. +> Users without a `user_id` (external guests, artists) cannot have tags. + +| Column | Type | Notes | +| -------------------- | --------------- | ---------------------------------------------- | +| `id` | int AI | PK — integer for join performance (pivot table) | +| `user_id` | ULID FK | → users | +| `organisation_id` | ULID FK | → organisations | +| `person_tag_id` | ULID FK | → person_tags | +| `source` | enum | `self_reported\|organiser_assigned` | +| `assigned_by_user_id`| ULID FK null | → users (who assigned, for organiser_assigned) | +| `proficiency` | enum null | `beginner\|experienced\|expert` | +| `notes` | text null | Organiser-only notes, never shown to volunteer | +| `assigned_at` | timestamp | | + +**Relations:** `belongsTo` User, Organisation, PersonTag, AssignedBy (→ User) +**Unique constraint:** `UNIQUE(user_id, organisation_id, person_tag_id, source)` + — allows both `self_reported` AND `organiser_assigned` for the same tag +**Indexes:** `(user_id, organisation_id)`, `(person_tag_id)`, `(organisation_id, person_tag_id, proficiency)` + +**Design notes:** + +- Tags are scoped to `organisation_id` — organisation A's "Tapper" tag is independent of organisation B's. +- Tags link to `user_id`, NOT `person_id` — this makes them persistent across events. +- The unique constraint includes `source` — a volunteer can self-report "Tapper" AND the organiser can independently assign "Tapper expert". Both coexist. +- **Sync behaviour:** The `PUT .../tags/sync` endpoint replaces tags of the specified `source` only. Syncing `self_reported` tags removes self_reported tags not in the new list and adds new ones, while leaving all `organiser_assigned` tags untouched. + +--- + +## 3.5.11 Database Design Rules & Index Strategy ### Rule 1 — ULID as Primary Key diff --git a/docs/SETUP.md b/dev-docs/SETUP.md similarity index 100% rename from docs/SETUP.md rename to dev-docs/SETUP.md diff --git a/docs/TEST_SCENARIO.md b/dev-docs/TEST_SCENARIO.md similarity index 100% rename from docs/TEST_SCENARIO.md rename to dev-docs/TEST_SCENARIO.md diff --git a/resources/design/design-document.md b/dev-docs/design-document.md similarity index 84% rename from resources/design/design-document.md rename to dev-docs/design-document.md index e1c819d..1018618 100644 --- a/resources/design/design-document.md +++ b/dev-docs/design-document.md @@ -4,7 +4,7 @@ Product Design & Technical Specification Full Stack SaaS — Event & Festival Management Platform -**Version:** 1.3 | **Datum:** Maart 2026 | **Status:** Concept | **Tech Stack:** Laravel 12 + Vue 3 +**Version:** 1.8 | **Datum:** April 2026 | **Status:** Concept | **Tech Stack:** Laravel 12 + Vue 3 # 1. Product Vision & Scope @@ -42,7 +42,11 @@ De volgende termen worden door het hele document en de codebase consistent gehan | **Term** | **Definitie** | |----|----| | Organisation | Een klant/organisatie die Crewli gebruikt. Heeft eigen evenementen, teamleden en instellingen. Equivalent aan 'Launchpad' in In2Event. | -| Event | Een specifiek evenement of festival binnen een organisatie. Alle operationele data is event-scoped. | +| Event | Een specifiek evenement of festival binnen een organisatie. Alle operationele data is event-scoped. Kan een enkelvoudig event, festival of serie zijn (zie event type model, sectie 3.4.1). | +| Festival | Een event van type 'festival' dat programmaonderdelen (sub-events) bevat. Heeft zelf geen secties of shifts — die zitten op de programmaonderdelen. | +| Serie | Vergelijkbaar met Festival, maar voor terugkerende events (edities). Zelfde parent-child structuur. | +| Programmaonderdeel | Een sub-event binnen een festival of serie. Heeft eigen secties, time slots, shifts, personen en artiesten. De benaming is configureerbaar per festival (Dag, Programmaonderdeel, Editie, Locatie, Ronde). | +| Overkoepelende sectie | Een festival section met type 'cross_event' die actief is over alle programmaonderdelen van een festival (bijv. EHBO, Security). | | Festival Section | Een operationele eenheid binnen een event: Bar, Hospitality, Technical, Security, etc. Heeft eigen Crew, Volunteers en Shifts. | | Crowd Type | Classificatie van deelnemers: Crew, Volunteer, Artist, Guest, Press, Partner, Supplier. Configureerbaar per organisatie. | | Person | Individuele deelnemer gekoppeld aan een event via een Crowd Type. | @@ -232,6 +236,83 @@ Elk evenement doorloopt een vaste lifecycle. De dashboard UI en beschikbare acti | Tear-Down | Afbouwdagen. Inventaris terugname. Afsluiting van shifts. | | Closed | Evenement afgerond. Read-only. Rapporten beschikbaar. Data wordt gearchiveerd. | +### 3.4.1 Event Type Model (v1.7) + +Crewli ondersteunt drie event-modi via het universele event model: + +**Flat event** (`event_type = 'event'`, geen parent, geen children) — een standaard enkelvoudig evenement. Alle modules (secties, time slots, shifts, personen, artiesten) zitten direct op het event. + +**Festival** (`event_type = 'festival'`, geen parent, heeft children) — een container met programmaonderdelen. Het festival heeft twee planningslagen: operationele planning op festival-niveau (opbouw, afbraak, transitie) en programma-planning op de programmaonderdelen (sub-events). Personen worden op festival-niveau beheerd en kunnen per programmaonderdeel geactiveerd worden. De benaming van programmaonderdelen is configureerbaar via `sub_event_label`: Dag, Programmaonderdeel, Editie, Locatie, Ronde, of een eigen naam. + +**Serie** (`event_type = 'series'`, geen parent, heeft children) — functioneel identiek aan een festival, maar bedoeld voor terugkerende events (edities/rondes). Gebruikt dezelfde parent-child structuur. + +Sub-events (`parent_event_id` ingevuld) zijn de operationele eenheden binnen een festival of serie. Elk sub-event heeft een eigen event-record met eigen secties, time slots, shifts en artiesten. Overkoepelende secties (bijv. EHBO, Security) worden aangemaakt met type `cross_event` en zijn actief over alle programmaonderdelen. + +**Cross-event sectie auto-redirect (v1.8):** Wanneer een gebruiker een `cross_event` sectie aanmaakt op een sub-event, wordt deze automatisch aangemaakt op het parent festival (met redirect-meta in de response). Zo verschijnt de sectie direct in alle programmaonderdelen. Op een flat event is `cross_event` type niet toegestaan (422). + +**Cross-level time slot gebruik (v1.8):** Shifts op sub-event secties mogen verwijzen naar time slots van zowel het eigen sub-event als het parent festival. Dit maakt opbouw-shifts mogelijk op programma-specifieke secties. De omgekeerde richting (festival-shifts verwijzen naar sub-event time slots) is niet toegestaan. + +| | | +|:--:|----| +| **UI GEDRAG** | Als een event geen children heeft, worden alle tabs op event-niveau getoond (flat mode). Zodra children worden toegevoegd, wordt het event een festival-container en krijgen de children de operationele tabs. | + +### Festival-level operationele planning + +Een festival (parent event) heeft twee planningslagen: + +1. **Operationele planning** — direct op het festival: + opbouw, afbraak, nachtsecurity, transitieperiodes tussen programmaonderdelen. + Gebruikt festival-level time slots en festival-level secties. + +2. **Programma-planning** — op de programmaonderdelen (sub-events): + show-specifieke secties, programma-specifieke time slots en shifts. + +Overkoepelende secties (`type = cross_event`) zijn zichtbaar in zowel de +festival-level operationele planning als in alle programmaonderdelen. +Standaard secties op festival-niveau (bijv. "Terreinploeg") zijn alleen +zichtbaar op festival-niveau. + +| Sectie-type | Op festival-parent | Zichtbaar in sub-events? | +|---|---|---| +| `standard` | Operationele secties (Terreinploeg, Bouwploeg) | Nee — alleen op festival-niveau | +| `cross_event` | Overkoepelend (EHBO, Security, Backstage) | Ja — in alle programmaonderdelen | +| `standard` (op sub-event) | Programma-specifiek (Bar Schirmbar) | Alleen in eigen sub-event | + +Festival-level time slots (bijv. "Opbouw dag 1", "Afbraak") worden **niet** getoond +wanneer time slots voor een sub-event worden opgevraagd. Elk niveau heeft zijn eigen +tijdvensters. + +### 3.4.2 Event Status State Machine (v1.8) + +Statuswijzigingen op events worden afgedwongen via een state machine. Alleen gedefinieerde transities zijn toegestaan — de reguliere PUT /events/{event} endpoint accepteert **geen** `status` veld. Statuswijzigingen gaan uitsluitend via het dedicated endpoint. + +**Endpoint:** `POST /organisations/{org}/events/{event}/transition` +**Body:** `{ "status": "published" }` + +**Transitiekaart:** + +| Van | Naar | +|---------------------|------------------------------| +| `draft` | `published` | +| `published` | `registration_open`, `draft` | +| `registration_open` | `buildup`, `published` | +| `buildup` | `showday` | +| `showday` | `teardown` | +| `teardown` | `closed` | +| `closed` | _(terminal — geen transities)_ | + +Beperkte achterwaartse stappen: `published → draft` (voor correcties) en `registration_open → published` (om registratie te sluiten). + +**Prerequisites per transitie:** +- `→ published`: name, start_date en end_date moeten ingevuld zijn +- `→ registration_open`: minimaal 1 time slot en 1 sectie moeten bestaan + +Bij een ongeldige transitie of ontbrekende prerequisites retourneert de endpoint 422 met `errors`, `current_status`, `requested_status` en `allowed_transitions`. + +**EventResource** bevat altijd een `allowed_transitions` array zodat de frontend weet welke statusknoppen beschikbaar zijn. + +**Festival cascade:** Wanneer een festival parent naar `showday`, `teardown` of `closed` transitiërt, worden alle children in een eerdere status automatisch meegezet naar dezelfde status. Eerdere statuswijzigingen (`draft → published`) cascaden **niet** — sub-events kunnen op een eigen planning zitten. + ## 3.5 Core Database Schema Onderstaand het volledige, productie-waardige datamodel. Alle 12 bevindingen uit de database review (v1.3) zijn verwerkt. Tabellen zijn gegroepeerd per domein. @@ -248,7 +329,7 @@ Onderstaand het volledige, productie-waardige datamodel. Alle 12 bevindingen uit | **organisations** | id (ULID), name, slug, billing_status, settings (JSON: display prefs only), created_at, deleted_at | hasMany events, crowd_types, accreditation_categories. settings JSON alleen voor opaque UI-config, niet voor queryable data. Soft delete: ja. | | **organisation_user** | id (int AI), user_id, organisation_id, role | Pivot. Integer PK voor join-performance. FK: users, organisations. Spatie role via pivot. | | **user_invitations** | id (ULID), email, invited_by_user_id, organisation_id, event_id (nullable), role, token (ULID, unique), status (pending\|accepted\|expired), expires_at | Token in uitnodigingsmail. Bij accept: zoek bestaand account op email of maak nieuw aan. INDEX: (token), (email, status). | -| **events** | id (ULID), organisation_id, name, slug, start_date, end_date, timezone, status (draft\|published\|registration_open\|buildup\|showday\|teardown\|closed), deleted_at | belongsTo organisation. hasMany festival_sections, time_slots, persons, artists, briefings. Soft delete: ja. INDEX: (organisation_id, status). | +| **events** | id (ULID), organisation_id, parent_event_id (ULID FK nullable → events, nullOnDelete), name, slug, start_date, end_date, timezone, status (draft\|published\|registration_open\|buildup\|showday\|teardown\|closed), event_type (enum: event\|festival\|series, default: event), event_type_label (string nullable), sub_event_label (string nullable), is_recurring (bool, default: false), recurrence_rule (string nullable), recurrence_exceptions (JSON nullable), deleted_at | belongsTo organisation. belongsTo event as parent (parent_event_id). hasMany events as children (parent_event_id). hasMany festival_sections, time_slots, persons, artists, briefings. Soft delete: ja. INDEX: (organisation_id, status), (parent_event_id), UNIQUE(organisation_id, slug). Zie sectie 3.4.1 voor event type model. | | **event_user_roles** | id (int AI), user_id, event_id, role | Pivot. Integer PK. FK: users, events. | ### 3.5.2 Locaties (nieuw — oplossing probleem 3) @@ -265,7 +346,7 @@ Drielaags Crescat-model. Kritieke verbetering: time_slot_id gedenormaliseerd naa | **Tabel** | **Belangrijkste kolommen** | **Relaties, constraints & opmerkingen** | |----|----|----| -| **festival_sections** | id (ULID), event_id, name, sort_order, deleted_at | hasMany shifts. Soft delete: ja. INDEX: (event_id, sort_order). | +| **festival_sections** | id (ULID), event_id, name, category (string nullable — v1.8), icon (string nullable — v1.8), type (standard\|cross_event), sort_order, crew_need, crew_auto_accepts, crew_invited_to_events, added_to_timeline, responder_self_checkin, crew_accreditation_level, public_form_accreditation_level, timed_accreditations, deleted_at | hasMany shifts. category: vrije tekst groepslabel met autocomplete vanuit eerder gebruikte waarden binnen de organisatie (endpoint: GET /organisations/{org}/section-categories). icon: Tabler icon naam, geselecteerd via IconPicker component. Soft delete: ja. INDEX: (event_id, sort_order), (event_id, category). | | **time_slots** | id (ULID), event_id, name, person_type (CREW\|VOLUNTEER\|PRESS\|PHOTO\|PARTNER), date, start_time, end_time, duration_hours | hasMany shifts. person_type bepaalt zichtbaarheid in registratieformulier. INDEX: (event_id, person_type, date). | | **shifts** | id (ULID), festival_section_id, time_slot_id, location_id, slots_total, slots_open_for_claiming, assigned_crew_id, events_during_shift (JSON: array of performance_ids), status, deleted_at | belongsTo festival_section, time_slot, location. hasMany shift_assignments. JSON hier OK: performance_ids zijn opaque referentie-lijst zonder filtering. INDEX: (festival_section_id, time_slot_id), (time_slot_id, status). Soft delete: ja. | | **shift_assignments** | id (ULID), shift_id, person_id, time_slot_id (FK: gedenormaliseerd), status (pending_approval\|approved\|rejected\|cancelled\|completed), auto_approved, assigned_by, assigned_at, approved_by, approved_at, rejection_reason, deleted_at | UNIEK constraint: UNIQUE(person_id, time_slot_id) — DB-afdwingbare conflictdetectie. time_slot_id gedenormaliseerd van shifts voor deze constraint. Soft delete: ja. INDEX: (shift_id, status), (person_id, status), (person_id, time_slot_id). | @@ -295,6 +376,37 @@ Oplossing probleem 1 (identiteitsfragmentatie): persons krijgt user_id (nullable | **crowd_lists** | id (ULID), event_id, crowd_type_id, name, type (internal\|external), recipient_company_id (nullable), auto_approve (bool), max_persons (int nullable) | hasMany persons via crowd_list_persons pivot. INDEX: (event_id, type). | | **crowd_list_persons** | id (int AI), crowd_list_id, person_id, added_at, added_by_user_id | Nieuw pivot (oplossing probleem 4). Koppelt person aan crowd_list. UNIQUE(crowd_list_id, person_id). INDEX: (person_id). | +### 3.5.5a Person Tags & Vaardigheden (v1.8) + +Tag-gebaseerd vaardigheden-/competentiesysteem voor vrijwilligers en crew. Tags worden per organisatie gedefinieerd en aan gebruikers toegewezen op organisatieniveau — persistent over events heen. Een vrijwilliger die vorig jaar als Tapper is geregistreerd, behoudt deze tag bij het volgende festival. + +| **Tabel** | **Belangrijkste kolommen** | **Relaties, constraints & opmerkingen** | +|----|----|----| +| **person_tags** | id (ULID), organisation_id, name (string 50), category (string 50 nullable), icon (string 50 nullable), color (hex string 7 nullable), is_active (bool), sort_order (int) | Organisatie-level tagdefinities. Bijv. "Tapper", "EHBO", "Duits". Deactivatie via `is_active = false` (geen soft delete). UNIQUE(organisation_id, name). INDEX: (organisation_id, is_active, sort_order). | +| **user_organisation_tags** | id (int AI), user_id, organisation_id, person_tag_id, source (self_reported\|organiser_assigned), assigned_by_user_id (nullable), proficiency (beginner\|experienced\|expert, nullable), notes (text nullable), assigned_at | Koppelt user aan tag binnen een organisatie. Twee bronnen coëxisteren voor dezelfde tag: self_reported (vrijwilliger kiest bij registratie) en organiser_assigned (coördinator kent toe op basis van ervaring). Sync endpoint vervangt alleen de opgegeven bron. UNIQUE(user_id, organisation_id, person_tag_id, source). INDEX: (user_id, organisation_id), (person_tag_id), (organisation_id, person_tag_id, proficiency). | + +PersonResource wordt verrijkt met tags wanneer `user_id` is ingevuld. Filter endpoints: `?tag={id}` (enkelvoudig) en `?tags=id1,id2` (AND-logica: moet alle tags hebben). + +### 3.5.5b Identiteitsmatching: Person ↔ User (v1.8 — ontwerp) + +Enterprise-grade identiteitsresolutie met drie stappen: detectie → suggestie → bevestiging. Er vindt nooit stilzwijgend automatische koppeling plaats. + +**Detectie-triggers:** +- Wanneer een person wordt aangemaakt met een e-mailadres dat overeenkomt met een bestaande user → systeem maakt een pending match aan in `person_identity_matches` +- Wanneer een nieuw user account wordt aangemaakt → systeem detecteert alle ongekoppelde persons met hetzelfde e-mailadres en maakt pending matches aan + +**Workflow:** +- Organisator reviewt matches: bevestigen (koppelt `person.user_id`) of afwijzen (wordt niet meer getoond) +- Volledige audit trail: wie heeft wanneer bevestigd/afgewezen +- Bulk-bevestigingsendpoint voor efficiëntie +- Pending matches worden inline getoond op PersonResource en als aandachtspunten op het dashboard + +| **Tabel** | **Belangrijkste kolommen** | **Relaties, constraints & opmerkingen** | +|----|----|----| +| **person_identity_matches** | id (ULID), person_id, user_id, match_type (email), confidence (high\|medium), status (pending\|confirmed\|dismissed), confirmed_by_user_id (nullable), confirmed_at (timestamp nullable), dismissed_by_user_id (nullable), dismissed_at (timestamp nullable), created_at | Respecteert UNIQUE(event_id, user_id) op persons — geen dubbele koppeling mogelijk. INDEX: (person_id, status), (user_id, status), (status). | + +**Status:** Ontwerp afgerond, nog niet geïmplementeerd. Tabel wordt aangemaakt bij bouw van de identity matching module. + ### 3.5.6 Accreditatie Engine Oplossing probleem 5: event_accreditation_items activeert org-level items per event. Accreditatie-items zijn nu org-level geconfigureerd en per event geactiveerd met event-specifieke limieten. @@ -401,6 +513,13 @@ Het event dashboard is de landing page voor elke event-context. Het past zijn pr Festival Sections zijn de operationele eenheden binnen een evenement. Elke sectie heeft een eigen tabblad-gebaseerde interface met: Dashboard, Shifts, Scheduler, Crew, Volunteers, Info Section, Accreditation. +> **Festival context:** Secties bestaan op twee niveaus. Op een festival parent event: +> `standard` secties zijn festival-only operationele secties (bijv. Terreinploeg, Bouwploeg); +> `cross_event` secties verschijnen overal (bijv. Backstage, EHBO, Security). +> Op sub-events zijn alle secties `standard` en programma-specifiek (bijv. Bar Schirmbar). +> Bij het ophalen van secties voor een sub-event worden de `cross_event` secties van het +> parent festival automatisch meegeleverd. + ### Sectie dashboard (per section): - Filled Shifts teller: X/Y shifts gevuld. Opgesplitst Crew en Volunteers. @@ -425,6 +544,14 @@ Festival Sections zijn de operationele eenheden binnen een evenement. Elke secti Dit is een van de meest onderscheidende architecturale keuzes van Crewli, gebaseerd op de Crescat aanpak zoals geanalyseerd uit de screenshots. +> **Festival context:** Time slots bestaan op twee niveaus. Op een festival parent event +> worden time slots gebruikt voor operationele planning (opbouw, afbraak, transitie). +> Op sub-events worden time slots gebruikt voor programma-specifieke planning (showdagen). +> Festival-level time slots worden **niet** getoond bij het opvragen van time slots voor +> een sub-event — elk niveau heeft zijn eigen tijdvensters. Het Event model biedt +> `getAllRelevantTimeSlots()` om voor planningsdoeleinden alle relevante time slots over +> niveaus heen op te halen. + | | | |:--:|----| | **HET MODEL IN EEN ZIN** | Time Slots = het WAN-NEER (event-niveau, per persoonscategorie). Shifts = het WAT+WAAR+HOEVEEL (sectie-niveau, met capaciteit). Vrijwilligers kiezen Time Slots. Organisator maakt Shifts per Time Slot per sectie. | @@ -633,6 +760,10 @@ Op de dag van het festival heeft niemand tijd voor complexe navigatie. Show Day - Access Zones: Backstage, VIP, Main Stage, Production, Parking — zone_code voor scanners. +| | | +|:--:|---| +| **ARCH-07 BESLUIT (v1.8)** | Accreditatie-templates worden gebouwd als integraal onderdeel van de accreditatie-module, niet als separate toevoeging. Templates zijn de PRIMAIRE toewijzingsmethode; handmatige per-persoon toewijzing is de uitzondering. Een template definieert: voor crowd_type X werkend in sectie Y op dag Z, automatisch deze accreditatie-items voorstellen. Zie BACKLOG ARCH-07. | + ### Check-in modal (Mission Control): - Zoek op naam of scan barcode. @@ -758,11 +889,13 @@ REST API, WebSockets, externe koppelingen |----|----| | Auth | POST /auth/login, POST /auth/logout, POST /auth/refresh | | Organisations | GET\|POST /organisations, GET\|PUT\|DELETE /organisations/{id} | -| Events | GET\|POST /organisations/{org}/events, PUT /events/{id}/status | +| Events | GET\|POST /organisations/{org}/events, GET\|PUT /organisations/{org}/events/{event}, GET /organisations/{org}/events/{event}/children, POST /organisations/{org}/events/{event}/transition (v1.8 — status state machine). Query params: ?type=festival\|series\|event, ?include_children=true | | Festival Sections | GET\|POST /events/{event}/sections, GET /sections/{id}/dashboard | | Time Slots | GET\|POST /events/{event}/time-slots, PUT\|DELETE /time-slots/{id} | | Shifts | GET\|POST /sections/{section}/shifts, PUT /shifts/{id}, POST /shifts/{id}/assign | -| Persons | GET\|POST /events/{event}/persons, POST /persons/{id}/approve, POST /persons/{id}/checkin | +| Section Categories | GET /organisations/{org}/section-categories (v1.8 — autocomplete) | +| Person Tags | GET\|POST\|PUT\|DELETE /organisations/{org}/person-tags, GET /organisations/{org}/person-tag-categories, GET\|POST\|PUT\|DELETE /organisations/{org}/users/{user}/tags (v1.8) | +| Persons | GET\|POST /events/{event}/persons, POST /persons/{id}/approve, POST /persons/{id}/checkin. Filter: ?tag={id}, ?tags=id1,id2 (v1.8) | | Artists | GET\|POST /events/{event}/artists, GET /artists/{id}/advance-portal | | Advance Sections | GET\|POST /artists/{id}/sections, POST /sections/{id}/submit | | Volunteers | GET /events/{event}/volunteers, GET /volunteers/portal/{token} | @@ -850,11 +983,15 @@ Scheiding tussen de interne beheerapplicatie (app/) en externe portals (portal/) Externe portals hebben event-branding (logo, kleuren) maar bieden een vereenvoudigde, rol-beperkte weergave. Ze zijn PWA-geschikt voor mobiel gebruik. -## 6.5 Inline validatie met 'Missing Items' blok +## 6.5 Autocomplete-gedrag in dialogen (v1.8) + +Alle niet-contactgegevens formuliervelden in dialogen gebruiken `autocomplete="one-time-code"` om browser-autofill interferentie te voorkomen. Persoon-dialoog contactvelden (naam, e-mail, telefoon) behouden bewust browser autocomplete — dit versnelt het invullen van bekende contactgegevens. + +## 6.6 Inline validatie met 'Missing Items' blok Formuliervalidatie sectie-voor-sectie. Toon een 'Missing Items' samenvatting onderin elke sectie die persistente zichtbaarheid heeft totdat alle vereiste velden zijn ingevuld. Geen fouten alleen bij submit. -## 6.6 Kaartintegratie in crew-documenten +## 6.7 Kaartintegratie in crew-documenten Per locatie/shift is een adres, coördinaten en kaartpreview (Google Maps of Leaflet embed) configureerbaar. De kaart wordt opgenomen in de server-side gegenereerde allocatiesheet PDF. Dit is essentieel voor grote outdoor festivallocaties. @@ -1109,6 +1246,8 @@ Alle termen gebruikt in dit document en de codebase | Crowd List | Gestructureerde lijst van personen. Intern (org-gevuld) of extern (partner-gevuld). | | E-ticket | Digitaal ticket gegenereerd voor goedgekeurde personen met accreditatiebarcodes. | | Event Health | Dashboard widget die configuratieproblemen en openstaande acties toont als klikbare items. | +| Event type model | Universeel event model (v1.7) met drie modi: flat event, festival (met programmaonderdelen) en serie (met edities). Zie sectie 3.4.1. | +| Festival | Een event van type 'festival' dat programmaonderdelen (sub-events) bevat als container. Secties en shifts zitten op sub-events. | | Festival Section | Operationele eenheid binnen een event: Bar, Hospitality, Technical, Security, etc. | | Fill rate | Percentage gevulde slots ten opzichte van het totaal voor een shift of accreditatieitem. | | For Sale (Crescat) | Aantal shift-slots claimbaar via vrijwilligersportaal. In Crewli: 'slots_open_for_claiming'. | @@ -1119,12 +1258,15 @@ Alle termen gebruikt in dit document en de codebase | Mission Control | Real-time operationele hub op show-dag voor check-in en tracking. | | Open for Claiming | Aantal shift-slots dat zichtbaar en claimbaar is in het vrijwilligersportaal. | | Organisation | Een klant/organisatie die Crewli gebruikt. Bevat meerdere evenementen. | +| Overkoepelende sectie | Festival section met type 'cross_event' die actief is over alle programmaonderdelen van een festival (bijv. EHBO, Security). | | Person | Individuele deelnemer gekoppeld aan een event via een Crowd Type. | | Production Request | Digitaal intakeformulier gestuurd naar leveranciers voor het verzamelen van logistieke vereisten. | +| Programmaonderdeel | Sub-event binnen een festival of serie. Benaming configureerbaar: Dag, Programmaonderdeel, Editie, Locatie, Ronde. | | Rider | Lijst van technische en hospitality vereisten van een artiest. | | RSVP | Bevestigingsstroom waarbij deelnemers een uitnodiging accepteren of weigeren. | | Scanner | Geconfigureerd scanstation (browser of hardware) gekoppeld aan specifieke crowd types/zones. | | Segment | Onderverdeling van een crowd type voor granulaire communicatie en rapportage. | +| Serie | Een event van type 'series' voor terugkerende events (edities/rondes). Zelfde parent-child structuur als festival. | | Shift | Sectie-specifieke toewijzing binnen een Time Slot. Koppelt sectie + tijdvenster + capaciteit. | | Slot (capacity) | Het aantal personen dat een Shift kan bevatten. | | Stage | Podiumnaam binnen een event. Heeft performances op specifieke dagen. | @@ -1146,4 +1288,4 @@ Alle termen gebruikt in dit document en de codebase Crewli Product Design & Technical Specification — Vertrouwelijk -Versie 1.3 — Maart 2026 — Gebaseerd op analyse van In2Event, Crescat & WeezCrew +Versie 1.8 — April 2026 — Gebaseerd op analyse van In2Event, Crescat & WeezCrew diff --git a/resources/design/dev-guide.md b/dev-docs/dev-guide.md similarity index 98% rename from resources/design/dev-guide.md rename to dev-docs/dev-guide.md index b9831e1..5140653 100644 --- a/resources/design/dev-guide.md +++ b/dev-docs/dev-guide.md @@ -253,9 +253,9 @@ PHP 8.2 + Laravel 12 | TypeScript + Vue 3 + Vuexy/Vuetify | Pinia + TanStack Que - PHPUnit Feature Test per controller, minimaal: 200 + 401 + 403 ``` -### 3.3 docs/SCHEMA.md — Levend schema-document +### 3.3 dev-docs/SCHEMA.md — Levend schema-document -Maak een Markdown bestand aan in /docs/ dat de tabel-definitie bevat als platte tekst. Claude Code gebruikt dit als primaire referentie bij het genereren van migraties. +Maak een Markdown bestand aan in /dev-docs/ dat de tabel-definitie bevat als platte tekst. Claude Code gebruikt dit als primaire referentie bij het genereren van migraties. ``` # Crewli Database Schema @@ -409,7 +409,7 @@ Gebruik deze prompts letterlijk of als basis. De meest effectieve prompts zijn: > **Module genereren — Shifts als voorbeeld** > -> Lees /CLAUDE.md en /docs/SCHEMA.md voor de shifts tabel definitie. +> Lees /CLAUDE.md en /dev-docs/SCHEMA.md voor de shifts tabel definitie. > > Bouw het volledige Shifts module in de volgorde uit CLAUDE.md sectie 'Volgorde bij elke nieuwe module'. > @@ -427,7 +427,7 @@ Gebruik deze prompts letterlijk of als basis. De meest effectieve prompts zijn: > **Migration genereren** > -> Genereer een Laravel migratie voor de tabel [TABELNAAM] op basis van /docs/SCHEMA.md. +> Genereer een Laravel migratie voor de tabel [TABELNAAM] op basis van /dev-docs/SCHEMA.md. > > Gebruik $table->ulid('id')->primary() als PK. > @@ -560,7 +560,7 @@ Gebruik deze prompts letterlijk of als basis. De meest effectieve prompts zijn: > > Backend (in volgorde): > -> 1. Migrations voor alle tabellen uit /docs/SCHEMA.md sectie [X.X] +> 1. Migrations voor alle tabellen uit /dev-docs/SCHEMA.md sectie [X.X] > 2. Models met alle relaties, scopes en accessors > 3. Factories > 4. Policies @@ -668,13 +668,13 @@ Claude Code heeft een beperkt context window. Bij grote taken verliest het de co - Begin elke nieuwe sessie met: 'Lees /CLAUDE.md voor je begint.' -- Verwijs expliciet naar relevante bestanden: 'Zie /docs/SCHEMA.md voor de tabel definitie.' +- Verwijs expliciet naar relevante bestanden: 'Zie /dev-docs/SCHEMA.md voor de tabel definitie.' - Splits grote modules: 'Bouw eerst alleen de backend (stappen 1-9). Stop dan.' - Na een context-verlies: geef een samenvatting: 'We bouwen Crewli. Tot nu toe klaar: auth, organisations, events. Nu: Shifts module backend.' -- Gebruik /docs/API.md als levend document — houd bij wat er al gebouwd is. +- Gebruik /dev-docs/API.md als levend document — houd bij wat er al gebouwd is. ## 7. Tips, Valkuilen & Best Practices diff --git a/resources/design/start-guide.md b/dev-docs/start-guide.md similarity index 99% rename from resources/design/start-guide.md rename to dev-docs/start-guide.md index c1d673e..3a274f5 100644 --- a/resources/design/start-guide.md +++ b/dev-docs/start-guide.md @@ -338,7 +338,7 @@ Je bent klaar om de eerste module te laten genereren - ☐ **Plak deze prompt als eerste bericht:** ``` - Lees eerst volledig /CLAUDE.md en /docs/SCHEMA.md. + Lees eerst volledig /CLAUDE.md en /dev-docs/SCHEMA.md. Bouw daarna Fase 1 — Foundation — in deze volgorde: diff --git a/docs/.templates/concept-page.md b/docs/.templates/concept-page.md new file mode 100644 index 0000000..be74a79 --- /dev/null +++ b/docs/.templates/concept-page.md @@ -0,0 +1,26 @@ +--- +title: [Concept naam] +description: [Eén zin uitleg van het concept] +tags: [] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +## In het kort + +[2-3 zinnen die het concept uitleggen alsof je het aan iemand zonder technische kennis vertelt.] + +## Hoe past dit in Crewli? + +[Beschrijf de context: waar kom je dit concept tegen, en waarom is het belangrijk?] + +## Voorbeeld + +[Concreet voorbeeld uit de praktijk van festivalbeheer.] + +## Gerelateerde concepten + +- [Concept A](/guide/concepts#...) +- [Concept B](/guide/concepts#...) diff --git a/docs/.templates/feature-page.md b/docs/.templates/feature-page.md new file mode 100644 index 0000000..a092eb0 --- /dev/null +++ b/docs/.templates/feature-page.md @@ -0,0 +1,41 @@ +--- +title: [Feature naam] +description: [Eén zin die beschrijft wat de feature doet] +tags: [] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +## Wat is dit? + +[Korte uitleg in 2-3 zinnen: wat doet deze feature en waarom is het nuttig?] + +## Hoe werkt het? + +### Stap 1 — [Actie] + +[Beschrijving met eventueel een screenshot placeholder: `![Beschrijving](./images/placeholder.png)`] + +### Stap 2 — [Actie] + +[Beschrijving] + +## Rollen en toegang + +| Rol | Toegang | +|-----|---------| +| Organisatie Admin | Volledige toegang | +| Event Manager | [Specificeer] | +| Vrijwilliger | [Specificeer] | + +## Veelgestelde vragen + +### [Vraag 1]? + +[Antwoord] + +## Gerelateerde pagina's + +- [Gerelateerde feature](/organizer/...) diff --git a/docs/.templates/style-guide.md b/docs/.templates/style-guide.md new file mode 100644 index 0000000..143b5d3 --- /dev/null +++ b/docs/.templates/style-guide.md @@ -0,0 +1,65 @@ +# Crewli Documentatie Stijlgids + +## Doelgroep + +Crewli-documentatie is geschreven voor mensen die evenementen en festivals organiseren. +Zij zijn praktisch ingesteld, hebben weinig tijd, en willen snel weten hoe iets werkt. +Ga NIET uit van technische kennis. + +## Toon en stijl + +- Schrijf in het **Nederlands** (informeel "je/jij", niet "u") +- Wees **direct en bondig** — geen inleidingen van drie alinea's +- Gebruik **actieve zinnen**: "Klik op Opslaan" in plaats van "Er kan op Opslaan geklikt worden" +- Vermijd jargon — als een term nodig is, leg hem uit of link naar de woordenlijst + +## Vaste terminologie + +Gebruik ALTIJD deze termen — geen synoniemen: + +| Term in Crewli | NIET gebruiken | +|----------------|----------------| +| Evenement | Event (in user-facing docs) | +| Sectie | Afdeling, area, zone (tenzij Access Zone) | +| Dienst | Shift (in user-facing docs) | +| Tijdslot | Tijdblok, slot | +| Vrijwilliger | Volunteer (in user-facing docs) | +| Persoon | Deelnemer, gast (tenzij specifieke crowd type) | +| Crowd type | Bezoekerstype, categorie | +| Crowd lijst | Gastenlijst (alleen als het crowd type 'guest' is) | +| Accreditatie | Badge, pas | +| Access zone | Toegangszone | +| Advancing | Rider, tech spec | +| Briefing | Instructie, info | +| Festival serie | Festival reeks, parent event | + +## Paginastructuur + +1. **Titel** — wat het IS (niet wat het doet) +2. **Eerste alinea** — wat en waarom, max 3 zinnen +3. **Hoe werkt het** — stappen met screenshots +4. **Rollen en toegang** — wie mag wat +5. **FAQ** — alleen als er echte vragen zijn, geen opvulling +6. **Gerelateerde pagina's** — links naar verwante features + +## Screenshots + +- Gebruik placeholders tijdens ontwikkeling: `![Alt tekst](./images/placeholder.png)` +- Naamgeving: `feature-naam-stap-nummer.png` (bijv. `event-aanmaken-stap-2.png`) +- Bewaar in een `images/` map binnen de betreffende sectie +- Geen persoonlijke data zichtbaar in screenshots + +## Frontmatter + +Elke pagina MOET deze frontmatter hebben: + +```yaml +--- +title: Paginatitel +description: Eén zin beschrijving +tags: [relevante, tags, voor, zoeken] +--- +``` + +Tags gebruiken de Crewli-terminologie (zie tabel hierboven). +Gebruik minimaal 2 tags, maximaal 6. diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts new file mode 100644 index 0000000..4999fef --- /dev/null +++ b/docs/.vitepress/config.ts @@ -0,0 +1,162 @@ +import { defineConfig } from "vitepress"; + +export default defineConfig({ + title: "Crewli Docs", + description: "Documentatie voor het Crewli event management platform", + lang: "nl-NL", + + head: [["link", { rel: "icon", href: "/favicon.ico" }]], + + themeConfig: { + logo: "/logo.svg", + + nav: [ + { text: "Handleiding", link: "/guide/getting-started" }, + { text: "Organisator", link: "/organizer/" }, + { text: "Vrijwilliger", link: "/volunteer/" }, + { text: "Portal", link: "/portal/" }, + ], + + sidebar: { + "/guide/": [ + { + text: "Aan de slag", + items: [ + { text: "Introductie", link: "/guide/getting-started" }, + { text: "Kernconcepten", link: "/guide/concepts" }, + { text: "Woordenlijst", link: "/guide/glossary" }, + ], + }, + ], + "/organizer/": [ + { + text: "Organisatie", + items: [ + { text: "Overzicht", link: "/organizer/" }, + { + text: "Organisatie inrichten", + link: "/organizer/organisation-setup", + }, + { text: "Teamleden beheren", link: "/organizer/team-members" }, + ], + }, + { + text: "Evenementen", + items: [ + { text: "Event aanmaken", link: "/organizer/events/create-event" }, + { + text: "Festival series", + link: "/organizer/events/festival-series", + }, + { + text: "Event statussen", + link: "/organizer/events/event-statuses", + }, + ], + }, + { + text: "Secties & Diensten", + items: [ + { text: "Secties beheren", link: "/organizer/shifts/sections" }, + { text: "Tijdslots", link: "/organizer/shifts/time-slots" }, + { text: "Diensten plannen", link: "/organizer/shifts/planning" }, + { text: "Toewijzingen", link: "/organizer/shifts/assignments" }, + ], + }, + { + text: "Personen & Crowd", + items: [ + { text: "Personen beheren", link: "/organizer/persons/manage" }, + { text: "Crowd types", link: "/organizer/persons/crowd-types" }, + { text: "Crowd lijsten", link: "/organizer/persons/crowd-lists" }, + { text: "Tags", link: "/organizer/persons/tags" }, + ], + }, + { + text: "Accreditatie", + items: [ + { + text: "Accreditatie overzicht", + link: "/organizer/accreditation/overview", + }, + { + text: "Access zones", + link: "/organizer/accreditation/access-zones", + }, + { text: "Templates", link: "/organizer/accreditation/templates" }, + ], + }, + { + text: "Artiesten", + items: [ + { text: "Artiesten beheren", link: "/organizer/artists/manage" }, + { + text: "Advancing workflow", + link: "/organizer/artists/advancing", + }, + { text: "Programma", link: "/organizer/artists/programme" }, + ], + }, + { + text: "Communicatie", + items: [ + { text: "Briefings", link: "/organizer/communication/briefings" }, + { text: "Berichten", link: "/organizer/communication/messages" }, + ], + }, + ], + "/volunteer/": [ + { + text: "Vrijwilliger", + items: [ + { text: "Overzicht", link: "/volunteer/" }, + { text: "Registreren", link: "/volunteer/registration" }, + { text: "Diensten kiezen", link: "/volunteer/shift-signup" }, + { text: "Mijn paspoort", link: "/volunteer/passport" }, + { text: "Check-in", link: "/volunteer/check-in" }, + ], + }, + ], + "/portal/": [ + { + text: "Artiestenportaal", + items: [ + { text: "Overzicht", link: "/portal/" }, + { text: "Advancing invullen", link: "/portal/artist-advancing" }, + ], + }, + { + text: "Leveranciersportaal", + items: [{ text: "Toegang", link: "/portal/supplier-access" }], + }, + { + text: "Persportaal", + items: [ + { + text: "Accreditatie aanvragen", + link: "/portal/press-accreditation", + }, + ], + }, + ], + }, + + search: { + provider: "local", + }, + + socialLinks: [ + // { icon: 'github', link: 'https://github.com/...' }, + ], + + footer: { + message: "Crewli — Event Management Platform", + }, + + editLink: { + pattern: + "https://gitea.hausmans.cloud/bert.hausmans/crewli/_edit/main/docs/:path", + text: "Bewerk deze pagina", + }, + }, +}); diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index e244a84..0000000 --- a/docs/API.md +++ /dev/null @@ -1,92 +0,0 @@ -# Crewli API Contract - -Base path: `/api/v1/` - -Auth: Bearer token (Sanctum) - -## Auth - -- `POST /auth/login` -- `POST /auth/logout` -- `GET /auth/me` - -## Organisations - -- `GET /organisations` — list (super admin) -- `POST /organisations` — create -- `GET /organisations/{org}` — show -- `PUT /organisations/{org}` — update -- `GET /organisations/{org}/members` — members -- `POST /organisations/{org}/invite` — invite user - -## Events - -- `GET /organisations/{org}/events` -- `POST /organisations/{org}/events` -- `GET /organisations/{org}/events/{event}` -- `PUT /organisations/{org}/events/{event}` - -## Crowd Types - -- `GET /organisations/{org}/crowd-types` -- `POST /organisations/{org}/crowd-types` -- `PUT /organisations/{org}/crowd-types/{type}` -- `DELETE /organisations/{org}/crowd-types/{type}` - -## Companies - -- `GET /organisations/{org}/companies` -- `POST /organisations/{org}/companies` -- `PUT /organisations/{org}/companies/{company}` -- `DELETE /organisations/{org}/companies/{company}` - -## Festival Sections - -- `GET /events/{event}/sections` -- `POST /events/{event}/sections` -- `PUT /events/{event}/sections/{section}` -- `DELETE /events/{event}/sections/{section}` -- `POST /events/{event}/sections/reorder` - -## Time Slots - -- `GET /events/{event}/time-slots` -- `POST /events/{event}/time-slots` -- `PUT /events/{event}/time-slots/{timeSlot}` -- `DELETE /events/{event}/time-slots/{timeSlot}` - -## Shifts - -- `GET /events/{event}/sections/{section}/shifts` -- `POST /events/{event}/sections/{section}/shifts` -- `PUT /events/{event}/sections/{section}/shifts/{shift}` -- `DELETE /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` -- `DELETE /events/{event}/persons/{person}` - -## Crowd Lists - -- `GET /events/{event}/crowd-lists` -- `POST /events/{event}/crowd-lists` -- `PUT /events/{event}/crowd-lists/{list}` -- `DELETE /events/{event}/crowd-lists/{list}` -- `POST /events/{event}/crowd-lists/{list}/persons` -- `DELETE /events/{event}/crowd-lists/{list}/persons/{person}` - -## Locations - -- `GET /events/{event}/locations` -- `POST /events/{event}/locations` -- `PUT /events/{event}/locations/{location}` -- `DELETE /events/{event}/locations/{location}` - -_(Extend this contract per module as endpoints are implemented.)_ diff --git a/docs/guide/concepts.md b/docs/guide/concepts.md new file mode 100644 index 0000000..1a9bdff --- /dev/null +++ b/docs/guide/concepts.md @@ -0,0 +1,11 @@ +--- +title: Kernconcepten +description: De belangrijkste begrippen en concepten binnen Crewli uitgelegd. +tags: ['concepten', 'uitleg', 'basis'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina legt de kernconcepten van Crewli uit: organisaties, evenementen, secties, diensten, personen en meer. Begin hier als je voor het eerst met Crewli werkt. diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md new file mode 100644 index 0000000..bf778f2 --- /dev/null +++ b/docs/guide/getting-started.md @@ -0,0 +1,11 @@ +--- +title: Aan de slag met Crewli +description: Leer de basis van Crewli en begin met het beheren van je evenementen. +tags: ['introductie', 'aan de slag', 'basis'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina helpt je op weg met je eerste stappen in Crewli: een organisatie aanmaken, je team uitnodigen, en je eerste evenement opzetten. diff --git a/docs/guide/glossary.md b/docs/guide/glossary.md new file mode 100644 index 0000000..1e25f08 --- /dev/null +++ b/docs/guide/glossary.md @@ -0,0 +1,11 @@ +--- +title: Woordenlijst +description: Alle Crewli-termen op een rij met uitleg. +tags: ['woordenlijst', 'terminologie', 'begrippen'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Een alfabetisch overzicht van alle termen die je in Crewli tegenkomt, met een korte uitleg per begrip. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..c4bfdb9 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,31 @@ +--- +layout: home +title: Crewli Documentatie +description: Leer hoe je Crewli gebruikt voor je evenementen en festivals + +hero: + name: Crewli Docs + text: Documentatie voor het Crewli platform + tagline: Alles wat je nodig hebt om je evenementen en festivals te beheren + actions: + - theme: brand + text: Aan de slag + link: /guide/getting-started + - theme: alt + text: Organisator handleiding + link: /organizer/ + +features: + - title: Evenementen beheren + details: Maak evenementen aan, plan festival series, en beheer je volledige evenementportfolio. + link: /organizer/events/create-event + - title: Diensten plannen + details: Richt secties in, maak tijdslots aan, en plan diensten voor je crew. + link: /organizer/shifts/planning + - title: Vrijwilligers + details: Registratie, dienstkeuze, check-in en het vrijwilligerspaspoort. + link: /volunteer/ + - title: Artiesten & Advancing + details: Beheer je line-up en verzamel technische specificaties via het portaal. + link: /organizer/artists/advancing +--- diff --git a/docs/organizer/accreditation/access-zones.md b/docs/organizer/accreditation/access-zones.md new file mode 100644 index 0000000..8bf530b --- /dev/null +++ b/docs/organizer/accreditation/access-zones.md @@ -0,0 +1,11 @@ +--- +title: Access zones +description: Definieer toegangszones en koppel ze aan accreditaties. +tags: ['access zone', 'accreditatie', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je access zones instelt voor je evenement en hoe je ze koppelt aan accreditaties om de toegang te regelen. diff --git a/docs/organizer/accreditation/overview.md b/docs/organizer/accreditation/overview.md new file mode 100644 index 0000000..239cafc --- /dev/null +++ b/docs/organizer/accreditation/overview.md @@ -0,0 +1,11 @@ +--- +title: Accreditatie overzicht +description: Beheer accreditaties voor je evenement met access zones en templates. +tags: ['accreditatie', 'overzicht', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina geeft een overzicht van het accreditatiesysteem in Crewli: hoe je accreditaties instelt, toewijst en beheert. diff --git a/docs/organizer/accreditation/templates.md b/docs/organizer/accreditation/templates.md new file mode 100644 index 0000000..a86f3a7 --- /dev/null +++ b/docs/organizer/accreditation/templates.md @@ -0,0 +1,11 @@ +--- +title: Accreditatie templates +description: Maak herbruikbare templates voor accreditaties met vooraf ingestelde access zones. +tags: ['accreditatie', 'template', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je accreditatie templates aanmaakt en gebruikt om snel accreditaties toe te wijzen met de juiste toegangsrechten. diff --git a/docs/organizer/artists/advancing.md b/docs/organizer/artists/advancing.md new file mode 100644 index 0000000..4504b17 --- /dev/null +++ b/docs/organizer/artists/advancing.md @@ -0,0 +1,11 @@ +--- +title: Advancing workflow +description: Verzamel technische specificaties van artiesten via het advancing-formulier. +tags: ['advancing', 'artiest', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft de advancing workflow: hoe je formulieren verstuurt naar artiesten, antwoorden bijhoudt, en technische specificaties verzamelt. diff --git a/docs/organizer/artists/manage.md b/docs/organizer/artists/manage.md new file mode 100644 index 0000000..1ca0f90 --- /dev/null +++ b/docs/organizer/artists/manage.md @@ -0,0 +1,11 @@ +--- +title: Artiesten beheren +description: Voeg artiesten toe aan je evenement en beheer hun gegevens. +tags: ['artiest', 'beheer', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je artiesten toevoegt, hun gegevens beheert, en ze koppelt aan je programmering. diff --git a/docs/organizer/artists/programme.md b/docs/organizer/artists/programme.md new file mode 100644 index 0000000..d692ceb --- /dev/null +++ b/docs/organizer/artists/programme.md @@ -0,0 +1,11 @@ +--- +title: Programma +description: Stel het programma samen met artiesten, tijden en podia. +tags: ['programma', 'artiest', 'planning', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je het programma van je evenement opbouwt: artiesten toewijzen aan podia en tijden, en het programmaoverzicht beheren. diff --git a/docs/organizer/communication/briefings.md b/docs/organizer/communication/briefings.md new file mode 100644 index 0000000..2232772 --- /dev/null +++ b/docs/organizer/communication/briefings.md @@ -0,0 +1,11 @@ +--- +title: Briefings +description: Stuur briefings naar je crew met alle informatie die ze nodig hebben. +tags: ['briefing', 'communicatie', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je briefings aanmaakt, verstuurt naar specifieke groepen, en bijhoudt wie de briefing heeft gelezen. diff --git a/docs/organizer/communication/messages.md b/docs/organizer/communication/messages.md new file mode 100644 index 0000000..aa22e16 --- /dev/null +++ b/docs/organizer/communication/messages.md @@ -0,0 +1,11 @@ +--- +title: Berichten +description: Stuur berichten naar personen en groepen binnen je evenement. +tags: ['berichten', 'communicatie', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je berichten verstuurt, ontvangt en beheert binnen Crewli. diff --git a/docs/organizer/events/create-event.md b/docs/organizer/events/create-event.md new file mode 100644 index 0000000..769297f --- /dev/null +++ b/docs/organizer/events/create-event.md @@ -0,0 +1,11 @@ +--- +title: Evenement aanmaken +description: Maak een nieuw evenement aan binnen je organisatie. +tags: ['evenement', 'aanmaken', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft stap voor stap hoe je een nieuw evenement aanmaakt, het type kiest, en de basisgegevens invult. diff --git a/docs/organizer/events/event-statuses.md b/docs/organizer/events/event-statuses.md new file mode 100644 index 0000000..cc2249c --- /dev/null +++ b/docs/organizer/events/event-statuses.md @@ -0,0 +1,11 @@ +--- +title: Evenement statussen +description: Begrijp de verschillende statussen van een evenement en wat ze betekenen. +tags: ['evenement', 'status', 'workflow', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft de levenscyclus van een evenement: van concept tot afgerond, en welke acties bij elke status horen. diff --git a/docs/organizer/events/festival-series.md b/docs/organizer/events/festival-series.md new file mode 100644 index 0000000..5fe263d --- /dev/null +++ b/docs/organizer/events/festival-series.md @@ -0,0 +1,11 @@ +--- +title: Festival series +description: Beheer terugkerende festivals als een serie met gedeelde instellingen. +tags: ['festival serie', 'evenement', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe festival series werken: hoe je een parent-evenement aanmaakt, edities toevoegt, en instellingen deelt tussen edities. diff --git a/docs/organizer/index.md b/docs/organizer/index.md new file mode 100644 index 0000000..d26c8ab --- /dev/null +++ b/docs/organizer/index.md @@ -0,0 +1,11 @@ +--- +title: Organisator overzicht +description: Alles wat je als organisator moet weten over het beheren van evenementen in Crewli. +tags: ['organisator', 'overzicht', 'beheer'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Als organisator beheer je evenementen, secties, diensten, personen en communicatie. Dit overzicht helpt je navigeren naar de juiste handleiding. diff --git a/docs/organizer/organisation-setup.md b/docs/organizer/organisation-setup.md new file mode 100644 index 0000000..34f74f4 --- /dev/null +++ b/docs/organizer/organisation-setup.md @@ -0,0 +1,11 @@ +--- +title: Organisatie inrichten +description: Stel je organisatie in met naam, logo en basisinstellingen. +tags: ['organisatie', 'instellingen', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je je organisatie inricht in Crewli: basisgegevens instellen, logo uploaden, en je organisatie klaar maken voor gebruik. diff --git a/docs/organizer/persons/crowd-lists.md b/docs/organizer/persons/crowd-lists.md new file mode 100644 index 0000000..9cb4177 --- /dev/null +++ b/docs/organizer/persons/crowd-lists.md @@ -0,0 +1,11 @@ +--- +title: Crowd lijsten +description: Beheer lijsten van personen per crowd type voor je evenement. +tags: ['crowd lijst', 'persoon', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je crowd lijsten bekijkt, filtert en exporteert, en hoe ze samenhangen met crowd types en accreditatie. diff --git a/docs/organizer/persons/crowd-types.md b/docs/organizer/persons/crowd-types.md new file mode 100644 index 0000000..94ad0fd --- /dev/null +++ b/docs/organizer/persons/crowd-types.md @@ -0,0 +1,11 @@ +--- +title: Crowd types +description: Definieer categorieën voor verschillende soorten bezoekers en deelnemers. +tags: ['crowd type', 'persoon', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je crowd types aanmaakt en gebruikt om personen te categoriseren, zoals vrijwilligers, artiesten, pers en leveranciers. diff --git a/docs/organizer/persons/manage.md b/docs/organizer/persons/manage.md new file mode 100644 index 0000000..a715b07 --- /dev/null +++ b/docs/organizer/persons/manage.md @@ -0,0 +1,11 @@ +--- +title: Personen beheren +description: Voeg personen toe, bewerk gegevens en beheer je personenbestand. +tags: ['persoon', 'beheer', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je personen toevoegt, bewerkt en verwijdert, en hoe het personenbestand samenhangt met crowd types en tags. diff --git a/docs/organizer/persons/tags.md b/docs/organizer/persons/tags.md new file mode 100644 index 0000000..9d6803e --- /dev/null +++ b/docs/organizer/persons/tags.md @@ -0,0 +1,11 @@ +--- +title: Tags +description: Gebruik tags om personen flexibel te labelen en te filteren. +tags: ['tag', 'persoon', 'filter', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je tags aanmaakt, toewijst aan personen, en gebruikt om snel de juiste mensen te vinden. diff --git a/docs/organizer/shifts/assignments.md b/docs/organizer/shifts/assignments.md new file mode 100644 index 0000000..ce3e575 --- /dev/null +++ b/docs/organizer/shifts/assignments.md @@ -0,0 +1,11 @@ +--- +title: Toewijzingen +description: Wijs personen toe aan diensten en beheer de dienstlijst. +tags: ['toewijzing', 'dienst', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je personen toewijst aan diensten, hoe de wachtlijst werkt, en hoe je toewijzingen beheert. diff --git a/docs/organizer/shifts/planning.md b/docs/organizer/shifts/planning.md new file mode 100644 index 0000000..982df61 --- /dev/null +++ b/docs/organizer/shifts/planning.md @@ -0,0 +1,11 @@ +--- +title: Diensten plannen +description: Plan diensten door secties, tijdslots en personen aan elkaar te koppelen. +tags: ['dienst', 'planning', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft de complete workflow voor het plannen van diensten: van het opzetten van secties tot het toewijzen van personen. diff --git a/docs/organizer/shifts/sections.md b/docs/organizer/shifts/sections.md new file mode 100644 index 0000000..b6e085f --- /dev/null +++ b/docs/organizer/shifts/sections.md @@ -0,0 +1,11 @@ +--- +title: Secties beheren +description: Maak secties aan en organiseer je evenement in logische onderdelen. +tags: ['sectie', 'organisator', 'beheer'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je secties aanmaakt, hernoemt, herordent en verwijdert binnen je evenement. diff --git a/docs/organizer/shifts/time-slots.md b/docs/organizer/shifts/time-slots.md new file mode 100644 index 0000000..5227e56 --- /dev/null +++ b/docs/organizer/shifts/time-slots.md @@ -0,0 +1,11 @@ +--- +title: Tijdslots +description: Definieer tijdslots waarbinnen diensten gepland worden. +tags: ['tijdslot', 'planning', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je tijdslots aanmaakt en beheert, en hoe ze samenhangen met secties en diensten. diff --git a/docs/organizer/team-members.md b/docs/organizer/team-members.md new file mode 100644 index 0000000..3b490ea --- /dev/null +++ b/docs/organizer/team-members.md @@ -0,0 +1,11 @@ +--- +title: Teamleden beheren +description: Nodig teamleden uit en beheer hun rollen binnen je organisatie. +tags: ['teamleden', 'rollen', 'uitnodigen', 'organisator'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je teamleden uitnodigt, rollen toewijst, en toegang beheert binnen je organisatie. diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 0000000..d469261 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,2514 @@ +{ + "name": "docs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "docs", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "vitepress": "^1.6.4" + } + }, + "node_modules/@algolia/abtesting": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.16.1.tgz", + "integrity": "sha512-Xxk4l00pYI+jE0PNw8y0MvsQWh5278WRtZQav8/BMMi3HKi2xmeuqe11WJ3y8/6nuBHdv39w76OpJb09TMfAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.50.1.tgz", + "integrity": "sha512-4peZlPXMwTOey9q1rQKMdCnwZb/E95/1e+7KujXpLLSh0FawJzg//U2NM+r4AiJy4+naT2MTBhj0K30yshnVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.50.1.tgz", + "integrity": "sha512-i+aWHHG8NZvGFHtPeMZkxL2Loc6Fm7iaRo15lYSMx8gFL+at9vgdWxhka7mD1fqxkrxXsQstUBCIsSY8FvkEOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.50.1.tgz", + "integrity": "sha512-Hw52Fwapyk/7hMSV/fI4+s3H9MGZEUcRh4VphyXLAk2oLYdndVUkc6KBi0zwHSzwPAr+ZBwFPe2x6naUt9mZGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.50.1.tgz", + "integrity": "sha512-Bn/wtwhJ7p1OD/6pY+Zzn+zlu2N/SJnH46md/PAbvqIzmjVuwjNwD4y0vV5Ov8naeukXdd7UU9v550+v8+mtlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.50.1.tgz", + "integrity": "sha512-0V4Tu0RWR8YxkgI9EPVOZHGE4K5pEIhkLNN0CTkP/rnPsqaaSQpNMYW3/mGWdiKOWbX0iVmwLB9QESk3H0jS5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.1.tgz", + "integrity": "sha512-jofcWNYMXJDDr87Z2eivlWY6o71Zn7F7aOvQCXSDAo9QTlyf7BhXEsZymLUvF0O1yU9Q9wvrjAWn8uVHYnAvgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.50.1.tgz", + "integrity": "sha512-OteRb8WubcmEvU0YlMJwCXs3Q6xrdkb0v50/qZBJP1TF0CvujFZQM++9BjEkTER/Jr9wbPHvjSFKnbMta0b4dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.50.1.tgz", + "integrity": "sha512-0GmfSgDQK6oiIVXnJvGxtNFOfosBspRTR7csCOYCTL1P8QtxX2vDCIKwTM7xdSAEbJaZ43QlWg25q0Qdsndz8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.50.1.tgz", + "integrity": "sha512-ySuigKEe4YjYV3si8NVk9BHQpFj/1B+ON7DhhvTvbrZJseHQQloxzq0yHwKmznSdlO6C956fx4pcfOKkZClsyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.50.1.tgz", + "integrity": "sha512-Cp8T/B0gVmjFlzzp6eP47hwKh5FGyeqQp1N48/ANDdvdiQkPqLyFHQVDwLBH0LddfIPQE+yqmZIgmKc82haF4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.1.tgz", + "integrity": "sha512-XKdGGLikfrlK66ZSXh/vWcXZZ8Vg3byDFbJD8pwEvN1FoBRGxhxya476IY2ohoTymLa4qB5LBRlIa+2TLHx3Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.50.1.tgz", + "integrity": "sha512-mBAU6WyVsDwhHyGM+nodt1/oebHxgvuLlOAoMGbj/1i6LygDHZWDgL1t5JEs37x9Aywv7ZGhqbM1GsfZ54sU6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.50.1.tgz", + "integrity": "sha512-qmo1LXrNKLHvJE6mdQbLnsZAoZvj7VyF2ft4xmbSGWI2WWm87fx/CjUX4kEExt4y0a6T6nEts6ofpUfH5TEE1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.77", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.77.tgz", + "integrity": "sha512-oaENvo6C3BkAEWMlcQA3XemxU9v2SFOTlApSUCODAkIu1haeLCjzrmH3HgmGqjRnJjM+LevO8sA+MgdMHBFBDA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz", + "integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.32", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz", + "integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.32", + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz", + "integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.32", + "@vue/compiler-dom": "3.5.32", + "@vue/compiler-ssr": "3.5.32", + "@vue/shared": "3.5.32", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.8", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz", + "integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.32", + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.32.tgz", + "integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.32.tgz", + "integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.32", + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz", + "integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.32", + "@vue/runtime-core": "3.5.32", + "@vue/shared": "3.5.32", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.32.tgz", + "integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.32", + "@vue/shared": "3.5.32" + }, + "peerDependencies": { + "vue": "3.5.32" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz", + "integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/algoliasearch": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.50.1.tgz", + "integrity": "sha512-/bwdue1/8LWELn/DBalGRfuLsXBLXULJo/yOeavJtDu8rBwxIzC6/Rz9Jg19S21VkJvRuZO1k8CZXBMS73mYbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.16.1", + "@algolia/client-abtesting": "5.50.1", + "@algolia/client-analytics": "5.50.1", + "@algolia/client-common": "5.50.1", + "@algolia/client-insights": "5.50.1", + "@algolia/client-personalization": "5.50.1", + "@algolia/client-query-suggestions": "5.50.1", + "@algolia/client-search": "5.50.1", + "@algolia/ingestion": "1.50.1", + "@algolia/monitoring": "1.50.1", + "@algolia/recommend": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.29.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", + "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz", + "integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.32", + "@vue/compiler-sfc": "3.5.32", + "@vue/runtime-dom": "3.5.32", + "@vue/server-renderer": "3.5.32", + "@vue/shared": "3.5.32" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..ed432bc --- /dev/null +++ b/docs/package.json @@ -0,0 +1,13 @@ +{ + "name": "crewli-docs", + "private": true, + "type": "module", + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + }, + "devDependencies": { + "vitepress": "^1.6.4" + } +} diff --git a/docs/portal/artist-advancing.md b/docs/portal/artist-advancing.md new file mode 100644 index 0000000..bdaade4 --- /dev/null +++ b/docs/portal/artist-advancing.md @@ -0,0 +1,11 @@ +--- +title: Advancing invullen +description: Vul als artiest je technische specificaties en wensen in via het portaal. +tags: ['portal', 'advancing', 'artiest'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je als artiest het advancing-formulier invult: technische specificaties, cateringwensen, en overige informatie. diff --git a/docs/portal/index.md b/docs/portal/index.md new file mode 100644 index 0000000..c7c7d33 --- /dev/null +++ b/docs/portal/index.md @@ -0,0 +1,11 @@ +--- +title: Portal overzicht +description: Toegang tot Crewli voor artiesten, leveranciers en pers via het externe portaal. +tags: ['portal', 'overzicht', 'extern'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Het Crewli portaal biedt externe partijen — artiesten, leveranciers en pers — toegang tot de informatie en formulieren die ze nodig hebben. diff --git a/docs/portal/press-accreditation.md b/docs/portal/press-accreditation.md new file mode 100644 index 0000000..6a7e2d9 --- /dev/null +++ b/docs/portal/press-accreditation.md @@ -0,0 +1,11 @@ +--- +title: Persaccreditatie aanvragen +description: Vraag als persfotograaf of journalist accreditatie aan voor een evenement. +tags: ['portal', 'pers', 'accreditatie'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe pers accreditatie aanvraagt via het portaal, welke gegevens nodig zijn, en hoe de beoordeling werkt. diff --git a/docs/portal/supplier-access.md b/docs/portal/supplier-access.md new file mode 100644 index 0000000..b203304 --- /dev/null +++ b/docs/portal/supplier-access.md @@ -0,0 +1,11 @@ +--- +title: Leverancierstoegang +description: Krijg als leverancier toegang tot relevante evenementinformatie via het portaal. +tags: ['portal', 'leverancier', 'toegang'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe leveranciers toegang krijgen tot het portaal en welke informatie er beschikbaar is. diff --git a/docs/volunteer/check-in.md b/docs/volunteer/check-in.md new file mode 100644 index 0000000..db1c786 --- /dev/null +++ b/docs/volunteer/check-in.md @@ -0,0 +1,11 @@ +--- +title: Check-in +description: Check in bij je dienst met je QR-code of handmatig. +tags: ['vrijwilliger', 'check-in', 'dienst'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe de check-in werkt: je QR-code tonen, handmatig inchecken, en wat er gebeurt als je incheckt. diff --git a/docs/volunteer/index.md b/docs/volunteer/index.md new file mode 100644 index 0000000..9758a79 --- /dev/null +++ b/docs/volunteer/index.md @@ -0,0 +1,11 @@ +--- +title: Vrijwilliger overzicht +description: Alles wat je als vrijwilliger moet weten over het gebruik van Crewli. +tags: ['vrijwilliger', 'overzicht'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Als vrijwilliger gebruik je Crewli om je te registreren voor een evenement, diensten te kiezen, je paspoort te bekijken en in te checken. diff --git a/docs/volunteer/passport.md b/docs/volunteer/passport.md new file mode 100644 index 0000000..8392e89 --- /dev/null +++ b/docs/volunteer/passport.md @@ -0,0 +1,11 @@ +--- +title: Mijn paspoort +description: Bekijk je persoonlijke vrijwilligerspaspoort met je diensten en accreditatie. +tags: ['vrijwilliger', 'paspoort', 'accreditatie'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft het vrijwilligerspaspoort: wat je er vindt, hoe je het gebruikt, en hoe het samenhangt met je diensten en accreditatie. diff --git a/docs/volunteer/registration.md b/docs/volunteer/registration.md new file mode 100644 index 0000000..ac49d71 --- /dev/null +++ b/docs/volunteer/registration.md @@ -0,0 +1,11 @@ +--- +title: Registreren +description: Meld je aan als vrijwilliger voor een evenement. +tags: ['vrijwilliger', 'registratie', 'aanmelden'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je je registreert als vrijwilliger, je profiel invult, en toegang krijgt tot het evenement. diff --git a/docs/volunteer/shift-signup.md b/docs/volunteer/shift-signup.md new file mode 100644 index 0000000..ac7b3dc --- /dev/null +++ b/docs/volunteer/shift-signup.md @@ -0,0 +1,11 @@ +--- +title: Diensten kiezen +description: Bekijk beschikbare diensten en schrijf je in. +tags: ['vrijwilliger', 'dienst', 'inschrijven'] +--- + +# {{ $frontmatter.title }} + +{{ $frontmatter.description }} + +Deze pagina beschrijft hoe je beschikbare diensten bekijkt, je voorkeuren aangeeft, en je inschrijft voor diensten. diff --git a/resources/research/De beste E2E-teststack voor Vue 3 + Laravel in 2026.md b/resources/research/De beste E2E-teststack voor Vue 3 + Laravel in 2026.md new file mode 100644 index 0000000..06dd829 --- /dev/null +++ b/resources/research/De beste E2E-teststack voor Vue 3 + Laravel in 2026.md @@ -0,0 +1,119 @@ +# De beste E2E-teststack voor Vue 3 + Laravel in 2026 + +**Playwright met Pest 4 vormt de optimale, gratis testcombinatie voor een Vue 3 + Laravel SaaS-applicatie.** Playwright domineert inmiddels de markt met **37 miljoen wekelijkse npm-downloads** (5× meer dan Cypress), terwijl Laravel zelf Pest 4 — dat Playwright onder de motorkap gebruikt — officieel aanbeveelt boven Laravel Dusk. Voor een startup die snel en goedkoop wil testen, levert deze combinatie cross-browser testing, gratis parallelle uitvoering en diepe Laravel-integratie op zonder een cent aan toollicenties. AI-gestuurde tools zoals Playwright MCP voegen daar gratis testgeneratie aan toe, terwijl commerciële opties pas bij groei relevant worden. + +## Playwright wint de vergelijking op alle fronten + +De strijd tussen Playwright en Cypress is in 2025-2026 beslecht. Playwright passeerde Cypress in juni 2024 qua npm-downloads en het verschil groeit exponentieel. In de State of JS 2024-enquête scoorde Playwright hoger op zowel **tevredenheid als gebruiksgroei**, en circa 50% van QA-professionals gebruikt nu Playwright tegenover 14% voor Cypress. + +De architecturale voordelen zijn structureel. Playwright draait buiten de browser via het DevTools Protocol, waardoor het **multi-tab, multi-origin en cross-browser testing** (inclusief Safari/WebKit) native ondersteunt. Cypress draait in de browser zelf, wat een uitstekende debugging-ervaring oplevert maar fundamentele beperkingen met zich meebrengt. Qua snelheid toonde een BigBinary-casestudy dat een authenticatieflow van ~2 minuten in Cypress in minder dan **20 seconden** draaide in Playwright — een verbetering van 88%. + +| Criterium | Playwright | Cypress | +|-----------|-----------|---------| +| **Wekelijkse downloads** | ~37M | ~7.3M | +| **Cross-browser** | Chromium, Firefox, WebKit | Chromium, Firefox, beperkt WebKit | +| **Parallelle uitvoering** | Gratis (sharding) | Vereist Cypress Cloud ($67+/maand) | +| **Multi-tab/origin** | Volledig ondersteund | Beperkt door same-origin beleid | +| **API-mocking** | `page.route()` — zeer fijnmazig | `cy.intercept()` — beperkter | +| **Talen** | JS, TS, Python, Java, C# | Alleen JS/TS | +| **Kosten** | **$0** | $0 basis, $67-267/maand voor Cloud | + +Cypress behoudt één niche-voordeel: de **time-travel debugger** in de browser is ongeëvenaard voor interactieve debugging. Voor teams die developer experience boven alles prioriteren en geen Safari-testing nodig hebben, blijft Cypress verdedigbaar. De officiële Vue.js-documentatie beveelt beide tools aan voor E2E-testing, maar noemt Playwright eerst. + +## Pest 4 vervangt Laravel Dusk als de standaard + +De belangrijkste ontwikkeling voor Laravel-ontwikkelaars is de release van **Pest 4 in augustus 2025**. Laravel's eigen documentatie bevat nu een expliciete aanbeveling bovenaan de Dusk-pagina: *"Pest 4 now includes automated browser testing which offers significant performance and usability improvements compared to Laravel Dusk. For new projects, we recommend using Pest for browser testing."* + +Pest 4 gebruikt Playwright onder de motorkap maar biedt een **PHP-native API**. Dit elimineert het grootste pijnpunt van een gescheiden frontend/backend teststack: context-switching tussen PHP en JavaScript. Een Pest 4 browser-test ziet er zo uit: + +```php +it('kan een project aanmaken via het dashboard', function () { + $user = User::factory()->create(); + + visit(route('login')) + ->fill('email', $user->email) + ->fill('password', 'password') + ->click('Login') + ->assertSee('Dashboard') + ->click('Nieuw Project') + ->fill('name', 'Mijn SaaS') + ->click('Aanmaken') + ->assertSee('Mijn SaaS'); + + $this->assertDatabaseHas('projects', ['name' => 'Mijn SaaS']); + $this->assertAuthenticated(); +}); +``` + +De cruciale voordelen ten opzichte van Dusk zijn: **RefreshDatabase werkt gewoon** in browser-tests (bij Dusk onmogelijk vanwege het aparte HTTP-proces), multi-browser support via `--browser`, ingebouwde **visuele regressie-testing**, test-sharding voor CI, en een unified test suite waar unit-, feature- en browser-tests samenleven. De installatie is simpel: `composer require pestphp/pest-plugin-browser --dev` gevolgd door `npx playwright install`. + +Laravel Dusk (v8.3.6) blijft onderhouden en werkt met Laravel 11-13, maar is voor nieuwe projecten niet langer de aangeraden keuze. Bestaande Dusk-tests kunnen geleidelijk gemigreerd worden — Pest is gebouwd op PHPUnit, dus alles blijft compatibel. + +## Vuetify-componenten vereisen specifieke teststrategieën + +Het testen van Vuetify-componenten met E2E-tools kent **bekende uitdagingen** die je vooraf moet kennen. Het meest gedocumenteerde probleem betreft `v-select`: Vuetify's interne `
` onderschept click-events, waardoor Playwright's standaard `.click()` faalt. Een Vuetify-maintainer bevestigde dit als een architecturaal kenmerk, niet een bug. + +De betrouwbaarste workaround is het klikken op het dropdown-icoon in plaats van het select-element zelf: + +```typescript +// Stabiele helper voor Vuetify v-select +async function vuetifySelect(page: Page, testId: string, optionText: string) { + await page.locator(`[data-testid="${testId}"] i`).click(); + await page.getByRole('option', { name: optionText }).click(); +} +``` + +Vergelijkbare patronen gelden voor `v-dialog` (rendered in een overlay buiten de component-tree), `v-autocomplete` (vereist eerst typen, dan selecteren uit dynamische dropdown) en `v-data-table` checkboxes (nested child-elements). Vuetify propageert **geen `data-testid` naar interne sub-elementen** — een feature request hiervoor staat al jaren open. + +Voor een Vuexy-template specifiek geldt: er wordt geen testinfrastructuur meegeleverd. Bouw een Page Object Model voor de standaard Vuexy-layouts (sidebar, app bar, data tables) en voeg systematisch `data-testid`-attributen toe aan je aangepaste templates. Gebruik `data-testid` als primaire selectorstrategie, `getByRole` als secundair, en vermijd class-based selectors volledig — die breken bij Vuetify-updates. + +## AI-testtools: gratis mogelijkheden versus dure platforms + +Het AI-testlandschap in 2026 is sterk gesegmenteerd tussen gratis open-source opties en dure enterprise-platforms. Voor een startup is de **Playwright MCP-server** (Model Context Protocol) de meest relevante innovatie: een gratis, open-source brug tussen AI-modellen (Claude, GitHub Copilot, Cursor) en Playwright's browser-automatisering. MCP gebruikt de **accessibility tree** van een pagina om betrouwbaar met UI te interacteren en kan tests genereren vanuit natuurlijke taal. + +| Tool | Type | Kosten | Geschiktheid startup | +|------|------|--------|---------------------| +| **Playwright MCP** | Open-source AI-brug | Gratis (+ LLM API-kosten) | ★★★★★ Uitstekend | +| **BugBug** | Low-code recorder | Gratis tier (onbeperkt), Pro €189/maand | ★★★★★ Uitstekend | +| **Katalon Studio** | All-in-one platform | Gratis tier, Premium €67/seat/maand | ★★★★ Goed | +| **Testim (Tricentis)** | AI smart locators | Gratis community tier, enterprise op aanvraag | ★★★ Matig | +| **Autify** | No-code AI | Starter €99/maand | ★★★ Matig | +| **mabl** | AI-native platform | Vanaf ~€499/maand | ★★ Duur | +| **Applitools** | Visuele AI | Vanaf ~€969/maand | ★★ Duur | +| **QA Wolf** | Managed dienst | ~€7.500/maand | ★ Onbetaalbaar | + +**BugBug** verdient speciale aandacht voor startups: de gratis tier biedt onbeperkte tests, onbeperkte lokale runs en onbeperkte gebruikers. De Pro-versie (€189/maand) voegt cloud runs en CI/CD-integratie toe. Het is geoptimaliseerd voor Chromium en mist geavanceerde AI self-healing, maar de eenvoud is een pluspunt. + +Voor visuele regressie-testing: begin met Playwright's ingebouwde `toHaveScreenshot()` (gratis). Wordt dit onvoldoende door false positives, stap dan over naar **Percy** (5.000 gratis screenshots/maand). Applitools is pas relevant bij complexe UI's op schaal. + +## GitHub Actions: een betaalbare CI/CD-pipeline + +Playwright integreert naadloos met GitHub Actions en biedt **gratis parallelle uitvoering** via sharding — het grootste kostenvoordeel ten opzichte van Cypress, dat hiervoor een Cloud-abonnement vereist. Een complete pipeline voor Vue 3 + Laravel ziet er als volgt uit: + +**Fase 1** (parallel): Backend-tests met Pest/PHPUnit + Frontend unit-tests met Vitest. **Fase 2** (na succes): E2E browser-tests met Playwright, geshard over 4 runners. Dit fail-fast patroon voorkomt dat dure browser-tests draaien wanneer basis-tests falen. + +De kosten zijn minimaal. GitHub biedt **2.000 gratis minuten per maand** op het Free-plan (3.000 op Pro). Een typische E2E-suite met 4 shards kost ~€0,16 per run op Linux. Bij 20 runs per werkdag komt dit neer op **€28-70 per maand** — en voor publieke repositories zijn alle standaard-runners gratis. Gebruik Playwright's Docker-image (`mcr.microsoft.com/playwright:v1.58.2-noble`) om ~30 seconden browserinstallatie per run te besparen, en cache `node_modules` tussen runs. + +```yaml +# Playwright sharding in GitHub Actions +strategy: + matrix: + shardIndex: [1, 2, 3, 4] + shardTotal: [4] +steps: + - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} +``` + +## De aanbevolen teststack voor een startup + +De optimale, kostenefficiënte testpiramide voor een Vue 3 + Laravel SaaS-applicatie in 2026 bestaat uit drie lagen. **60-70% backend**: Pest/PHPUnit feature-tests voor API-endpoints, models, services en policies — met `RefreshDatabase` en factories. **15-20% frontend**: Vitest + Vue Test Utils voor component-tests van Vue 3-componenten, Pinia stores en composables. **10-15% E2E**: Pest 4 browser-testing (Playwright) voor kritieke gebruikersflows zoals login, registratie, betaling en onboarding. + +Het concrete advies hangt af van je team: + +- **PHP-first team** → Pest 4 met browser-plugin. Eén taal, één test suite, maximale Laravel-integratie. Officieel aanbevolen door Laravel. +- **JavaScript-first team** → Standalone Playwright met `@hyvor/laravel-playwright` voor Laravel-integratie (artisan commands, factories, database-operaties vanuit tests). +- **Bestaand Dusk-project** → Houd werkende Dusk-tests, schrijf nieuwe tests in Pest 4, migreer flakey tests eerst. + +Voeg **Playwright MCP** toe aan je IDE (Cursor, VS Code met Copilot) voor AI-gestuurde testgeneratie vanuit natuurlijke taal — dit is gratis en versnelt het schrijven van tests aanzienlijk. Gebruik `data-testid`-attributen overal in je Vuetify/Vuexy-templates en bouw herbruikbare helper-functies voor lastige Vuetify-componenten zoals `v-select` en `v-autocomplete`. + +**Totale maandelijkse kosten: €0-189.** Playwright, Pest 4, Vitest en GitHub Actions (binnen gratis limiet) kosten niets. Alleen BugBug Pro (optioneel, voor niet-technische teamleden) of extra GitHub Actions-minuten bij intensief gebruik genereren kosten. Geen enkele commerciële AI-tool is noodzakelijk voor een startup — de open-source stack is in 2026 compleet genoeg om professioneel te testen. \ No newline at end of file