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.
323 lines
11 KiB
Markdown
323 lines
11 KiB
Markdown
# 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
|
||
<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
|
||
|
||
```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
|
||
<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
|