# UX-SPEC: Festival hiërarchie in de GUI > **Status:** Definitief ontwerp — goedgekeurd door product owner > **Datum:** April 2026 > **Doel:** Specificatie voor de weergave van tijdsloten, secties en shifts > binnen de festival/sub-event hiërarchie in `apps/app/` --- ## 1. Ontwerpprincipes ### 1.1 Gebruik de namen van de coördinator, niet de onze Groepslabels in dropdowns en lijsten zijn altijd de **daadwerkelijke namen** van het festival en de programmaonderdelen (sub-events). Nooit technisch jargon als "overkoepelend", "cross_event", "parent", of "festival-level". De coördinator heeft deze namen zelf ingesteld en herkent ze altijd — ongeacht of sub-events dagen, locaties, tracks of edities vertegenwoordigen. ### 1.2 Context-behoud boven technische zuiverheid Als een coördinator vanuit sub-event "Vrijdag" op sectie "EHBO" klikt, blijft de UI in de Vrijdag-context. De coördinator ziet EHBO-diensten relevant voor Vrijdag, met een link om uit te zoomen naar het totaaloverzicht. ### 1.3 Progressieve onthulling via info-tooltips De interface is standaard schoon. Op drie vaste plekken staan info-iconen (ⓘ) met contextuele uitleg en concrete voorbeelden. Wie het concept snapt, klikt er nooit op. Wie het niet snapt, leest één keer en begrijpt het. ### 1.4 Eén patroon, overal herkenbaar Het visuele patroon is identiek op elk scherm: eigen items bovenaan (prominent), festival-items eronder (iets gedimpt, met festivalnaam als groepslabel). Eén patroon leren = overal begrijpen. ### 1.5 Flat events = geen complexiteit tonen Bij een enkelvoudig evenement (geen sub-events) worden alle groepslabels, info-tooltips en hiërarchie-elementen weggelaten. De dropdown is een platte lijst. Geen uitleg nodig — het spreekt voor zich. --- ## 2. Vier dropdown-scenario's ### 2.1 Scenario A — Standaard sectie op een sub-event (meest voorkomend) **Context:** Bijv. "Bar Schirmbar" binnen "Dag 1 — Vrijdag" van "Echt Feesten 2026" **Dropdown-inhoud:** 1. Groepslabel: **[sub-event naam]** (bijv. "Dag 1 — Vrijdag") 2. Tijdsloten van het sub-event — normale opacity, gesorteerd op start_time 3. Groepslabel: **[festival naam]** (bijv. "Echt Feesten 2026") 4. Tijdsloten van het festival — **opacity: 0.65**, gesorteerd op datum + start_time **API-call:** `GET /events/{sub_event_id}/time-slots?include_parent=true` **Info-tooltip tekst:** > Kies het tijdvenster voor deze dienst. Je ziet de tijdsloten van > **[sub-event naam]** en de algemene tijdsloten van **[festival naam]**. > > **Tip:** voor een reguliere dienst kies je een tijdslot van [sub-event naam]. > Voor een opbouw- of afbraakdienst kies je een festivaltijdslot. ### 2.2 Scenario B — Festival-brede sectie (cross_event) **Context:** Bijv. "EHBO" (type=cross_event) bekeken vanuit "Dag 1 — Vrijdag" **Dropdown-inhoud:** 1. Groepslabel: **[festival naam]** (bijv. "Echt Feesten 2026") 2. Festival-level tijdsloten — normale opacity 3. Groepslabel: **[sub-event 1 naam]** (bijv. "Dag 1 — Vrijdag") 4. Tijdsloten van sub-event 1 5. Groepslabel: **[sub-event 2 naam]** (bijv. "Dag 2 — Zaterdag") 6. Tijdsloten van sub-event 2 7. *(herhaal voor alle sub-events)* **API-call:** Festival-level tijdsloten via `GET /events/{festival_id}/time-slots` plus tijdsloten van alle sub-events. Dit vereist een nieuwe API-optie of een frontend-aggregatie van meerdere calls. **Aandachtspunt voor implementatie:** de dropdown kan lang worden bij grote festivals (3 festival-slots + 4 slots × 5 sub-events = 23 items). De groepslabels maken het scanbaar. Overweeg bij > 20 items een zoekfilter in de dropdown (Vuetify `v-autocomplete` i.p.v. `v-select`). **Navigatie-indicator:** toon achter de sectienaam in de breadcrumb een chip "festival-breed" zodat de coördinator weet dat deze sectie anders werkt dan standaard secties. **Info-tooltip tekst:** > **[sectie naam]** is een festival-brede sectie — actief bij elk > programmaonderdeel. Je kunt tijdsloten kiezen van **[festival naam]** > en van alle programmaonderdelen. > > **Tip:** plan diensten per programmaonderdeel (bijv. een showavond) > of festival-breed (bijv. opbouw, nachtsecurity). ### 2.3 Scenario C — Flat event (geen sub-events) **Context:** Bijv. "Braderie Dorpstown 2026" — geen hiërarchie. **Dropdown-inhoud:** platte lijst, geen groepslabels, geen info-tooltip. **API-call:** `GET /events/{event_id}/time-slots` (geen include_parent) ### 2.4 Scenario D — Locatie-/track-gebaseerde sub-events **Context:** Bijv. "Cultuurnacht Rotterdam" met locaties als sub-events. **Gedrag:** identiek aan scenario A. Het patroon werkt automatisch omdat de sub-event namen de locatienamen zijn (bijv. "Theater Walhalla"). De groepslabels in de dropdown tonen dan locatienamen i.p.v. dagnamen. --- ## 3. Secties-overzicht ### 3.1 Op een sub-event pagina Festival-brede secties (type=cross_event) tonen de **festivalnaam** als context achter de sectienaam. Standaard secties tonen niets extra. ``` EHBO · Echt Feesten 2026 4 diensten · 12/16 Security · Echt Feesten 2026 6 diensten · 18/24 Bar Schirmbar 8 diensten · 20/24 Podium crew 3 diensten · 6/9 ``` De festivalnaam achter de sectienaam is voldoende context. Geen badges, geen groepen, geen kleuren. **Info-tooltip (ⓘ bij de secties-header):** > Sommige secties zijn **festival-breed**: ze zijn bij elk > programmaonderdeel actief en worden centraal beheerd. Je herkent ze > aan de festivalnaam achter de sectienaam. > > **Voorbeeld:** EHBO en Security zijn festival-breed — ze staan bij elk > programmaonderdeel. Bar Schirmbar hoort alleen bij [sub-event naam]. ### 3.2 Op de festival-pagina Alle secties getoond: standard secties (operationeel) + cross_event secties. Geen speciale markering nodig — alles hoort hier. ### 3.3 Op een flat event Gewoon een platte lijst. Geen contextlabels nodig. --- ## 4. Tijdsloten-pagina ### 4.1 Op een sub-event pagina Eén lijst, geen tabs. Boven: eigen tijdsloten (bewerkbaar, CRUD-acties). Onder: festival-tijdsloten, gescheiden door een stippellijn met slotje. ``` [Eigen tijdsloten — bewerkbaar] Vrijdag avond — vrijwilliger 18:00 – 02:00 [Bewerken] [Verwijderen] Vrijdag avond — crew 17:00 – 03:00 [Bewerken] [Verwijderen] ──── 🔒 Echt Feesten 2026 — alleen-lezen ──── Opbouw vrijdag vr 08:00 – 18:00 Nachtsecurity vr→za vr 23:00 – za 07:00 → Beheer tijdsloten van Echt Feesten 2026 ``` ### 4.2 Op de festival-pagina Gewone CRUD-lijst met alle festival-level tijdsloten. Geen read-only sectie — alles is bewerkbaar op dit niveau. ### 4.3 Op een flat event Gewone CRUD-lijst. Geen scheiding, geen read-only sectie. --- ## 5. Navigatiegedrag bij festival-brede secties ### 5.1 Klikken op een festival-brede sectie vanuit sub-event context De coördinator blijft in de sub-event context. Bovenaan de sectie-view staat een context-banner: ``` Je bekijkt EHBO vanuit Dag 1 — Vrijdag [Bekijk alle diensten →] ``` De shift-lijst toont: - Diensten met een tijdslot van dit sub-event - Diensten met een festival-level tijdslot - NIET: diensten met tijdsloten van andere sub-events "Bekijk alle diensten" navigeert naar de sectie op festival-niveau, waar alle diensten over alle programmaonderdelen zichtbaar zijn. ### 5.2 Festival-brede sectie op festival-niveau Alle diensten, gegroepeerd per programmaonderdeel (sub-event naam) + een groep voor festival-level diensten. Dit is het "totaaloverzicht" voor de EHBO-coördinator. --- ## 6. Implementatie-aantekeningen ### 6.1 Backend **Bestaande API die werkt:** - `GET /events/{event}/time-slots?include_parent=true` — retourneert eigen + parent festival tijdsloten, elk met `source` veld (`own` | `festival`) en `event_name` **Nieuwe API nodig voor scenario B (cross_event sectie):** - Optie 1: Frontend doet meerdere calls (festival + alle sub-events) - Optie 2: Nieuw endpoint `GET /events/{festival}/time-slots?include_children=true` dat alle tijdsloten van festival + alle sub-events retourneert - **Aanbeveling:** optie 2, met `source` = `own` | `{sub_event_id}` en `event_name` per tijdslot ### 6.2 Frontend — v-autocomplete configuratie ```vue ``` ### 6.3 Frontend — bepalen welk scenario actief is ```typescript const dropdownScenario = computed(() => { const event = currentEvent.value const section = currentSection.value // Flat event — scenario C if (!event?.parent_event_id && !event?.children?.length) { return 'flat' } // Cross_event section — scenario B if (section?.type === 'cross_event') { return 'cross_event' } // Standard section on sub-event — scenario A (and D) if (event?.parent_event_id) { return 'sub_event_standard' } // Standard section on festival — festival-level, no sub-event slots return 'festival_standard' }) ``` ### 6.4 Seeder-vereisten De DevSeeder moet consistent zijn met deze hiërarchie: - Festival-level tijdsloten op het parent event (opbouw, afbraak, etc.) - Sub-event tijdsloten op elk sub-event (programma-specifiek) - Shifts in standard secties → tijdsloten van hun eigen event - Shifts in cross_event secties → tijdsloten van het parent festival - Geen cross-references naar andere sub-events vanuit standard secties ### 6.5 Info-tooltip component Herbruikbaar component `InfoTooltip.vue`: ```vue ``` --- ## 7. Toekomstige verbeteringen (niet in scope) - **ARCH-08:** Recurrence voor tijdsloten ("kopieer naar ander sub-event") - Zoekfilter in de dropdown bij > 20 tijdsloten - Cross_event sectie per-sub-event activatie toggle (B1 — pas bij vraag) - Drag-and-drop herordening van tijdsloten in de dropdown