Captures the approved UX specification for the festival hierarchy: four dropdown scenarios (standard sub-event, cross_event section, flat event, location-based), context-preservation on navigation, and info-tooltip placement rules. This document has been referenced in implementation work since April but was never committed.
11 KiB
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:
- Groepslabel: [sub-event naam] (bijv. "Dag 1 — Vrijdag")
- Tijdsloten van het sub-event — normale opacity, gesorteerd op start_time
- Groepslabel: [festival naam] (bijv. "Echt Feesten 2026")
- 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:
- Groepslabel: [festival naam] (bijv. "Echt Feesten 2026")
- Festival-level tijdsloten — normale opacity
- Groepslabel: [sub-event 1 naam] (bijv. "Dag 1 — Vrijdag")
- Tijdsloten van sub-event 1
- Groepslabel: [sub-event 2 naam] (bijv. "Dag 2 — Zaterdag")
- Tijdsloten van sub-event 2
- (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 metsourceveld (own|festival) enevent_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=truedat alle tijdsloten van festival + alle sub-events retourneert - Aanbeveling: optie 2, met
source=own|{sub_event_id}enevent_nameper tijdslot
6.2 Frontend — v-autocomplete configuratie
<v-autocomplete
v-model="form.time_slot_id"
:items="groupedTimeSlots"
item-title="label"
item-value="id"
label="Tijdslot"
:loading="isLoading"
>
<!-- Groepslabels via Vuetify's group rendering -->
<!-- Custom item template met tijd rechts -->
<template #item="{ props: itemProps, item }">
<v-list-subheader v-if="item.raw.isGroupHeader">
{{ item.raw.groupName }}
</v-list-subheader>
<v-list-item v-else v-bind="itemProps">
<template #append>
<span class="text-caption text-medium-emphasis">
{{ item.raw.timeRange }}
</span>
</template>
</v-list-item>
</template>
<template #selection="{ item }">
{{ item.raw.name }} · {{ item.raw.timeRange }}
</template>
</v-autocomplete>
6.3 Frontend — bepalen welk scenario actief is
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:
<template>
<v-tooltip location="bottom" max-width="320">
<template #activator="{ props: tooltipProps }">
<v-icon
v-bind="tooltipProps"
icon="tabler-info-circle"
size="16"
color="medium-emphasis"
class="cursor-help"
/>
</template>
<div>
<slot />
</div>
</v-tooltip>
</template>
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