- E0.S1: Add encounter_id and intake_id columns to reports table - E0.S2: Add performance indices for reports and encounters - E0.S3: Regenerate TypeScript types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
52 KiB
🧩 Functioneel Ontwerp (FO) – Agenda Module
Projectnaam: Mini-EPD Agenda Module Versie: v1.1 Datum: 02-12-2024 Auteur: Colin
1. Doel en relatie met het PRD
🎯 Doel van dit document: Dit Functioneel Ontwerp beschrijft hoe de Agenda Module uit het PRD functioneel zal werken — wat de behandelaar ziet, doet en ervaart. Waar het PRD uitlegt wat en waarom, laat dit FO zien hoe dit in de praktijk werkt.
📘 Relatie met PRD:
- PRD-referentie:
prd-agenda-module-v1_0.md - Dit FO is de functionele uitwerking van PRD secties 3 (Kernfunctionaliteiten) en 4 (Gebruikersflows)
- Scope is afgebakend op MVP-features; nice-to-have items zijn apart gemarkeerd
Kernprincipe:
De agenda is het kloppend hart van het EPD. Zonder agenda geen dagelijkse workflow, zonder workflow geen AI-integratie in de praktijk.
2. Overzicht van de belangrijkste onderdelen
🎯 Doel: Kort overzicht van schermen en componenten binnen de Agenda Module.
MVP Onderdelen (Must-Have)
| # | Onderdeel | Beschrijving | Prioriteit |
|---|---|---|---|
| 1 | Kalender View | Hoofd agenda-weergave met dag/week/werkdagen toggle | 🔴 Kritiek |
| 2 | Afspraak Modal | Formulier voor maken/bewerken afspraken | 🔴 Kritiek |
| 3 | Patient Zoeken | Autocomplete voor patient selectie | 🔴 Kritiek |
| 4 | Conflict Dialog | Waarschuwing bij dubbele boekingen | 🔴 Kritiek |
| 5 | Afspraak Detail Popup | Quick-view met acties (verslag maken, bewerken) | 🔴 Kritiek |
| 6 | Toolbar | Datum navigatie, view switcher | 🟡 Hoog |
Stretch/Nice-to-Have Onderdelen
| # | Onderdeel | Beschrijving | Prioriteit |
|---|---|---|---|
| 7 | Mini Kalender Sidebar | Maandoverzicht met navigatie | 🟢 Stretch |
| 8 | Recente Patiënten | Quick-select laatste 5 patiënten | 🟢 Stretch |
| 9 | Kleurcodering | Visuele type-indicator (intake/behandeling/follow-up) | 🟢 Stretch |
Niet in Scope (Post-MVP)
| Onderdeel | Reden exclusie |
|---|---|
| Multi-practitioner view | Te complex voor MVP, single-user focus |
| Recurring appointments | Beperkte demo-waarde, hoge complexiteit |
| Email/SMS notificaties | Aparte infrastructuur nodig |
| Wachtlijstbeheer | Buiten scope AI Speedrun |
| Video call integratie | Geen core EPD functionaliteit |
3. User Stories
🎯 Doel: Beschrijven wat gebruikers moeten kunnen doen, vanuit hun perspectief.
MVP User Stories
| ID | Als... | Wil ik... | Zodat... | Prio |
|---|---|---|---|---|
| AG-US01 | Behandelaar | Mijn agenda bekijken in dag/week view | Ik weet welke patiënten vandaag/deze week komen | 🔴 |
| AG-US02 | Behandelaar | Snel een nieuwe afspraak maken door op een tijdslot te klikken | Ik direct kan inplannen tijdens telefoongesprek | 🔴 |
| AG-US03 | Behandelaar | Een patient zoeken tijdens afspraak maken | De afspraak gekoppeld is aan het juiste dossier | 🔴 |
| AG-US04 | Behandelaar | Een waarschuwing zien bij dubbele boeking | Ik niet per ongeluk overlap creëer | 🔴 |
| AG-US05 | Behandelaar | De waarschuwing kunnen overrulen | Ik flexibel kan plannen wanneer nodig | 🔴 |
| AG-US06 | Behandelaar | Een afspraak verzetten via drag-drop | Wijzigingen snel zijn doorgevoerd | 🟡 |
| AG-US07 | Behandelaar | Vanuit een afspraak direct een verslag starten | AI-verslaglegging naadloos in mijn workflow past | 🔴 |
| AG-US08 | Behandelaar | Op een afspraak klikken om details te zien | Ik snel patient info kan checken | 🟡 |
| AG-US09 | Behandelaar | Een afspraak annuleren | Geannuleerde afspraken geregistreerd blijven | 🟡 |
Stretch User Stories
| ID | Als... | Wil ik... | Zodat... | Prio |
|---|---|---|---|---|
| AG-US10 | Behandelaar | Recente patiënten snel selecteren | Repeat-afspraken extra snel gaan | 🟢 |
| AG-US11 | Behandelaar | Afspraken zien met kleurcodering per type | Ik visueel onderscheid kan maken | 🟢 |
| AG-US12 | Behandelaar | Een mini-kalender in de sidebar zien | Ik snel naar andere weken kan navigeren | 🟢 |
4. Functionele werking per onderdeel
🎯 Doel: Per component beschrijven wat de gebruiker kan doen en wat het systeem doet.
4.1 Kalender View (MVP)
Locatie: /epd/agenda
Context: Level 1 navigatie (behandelaar-breed, niet patient-specifiek)
Functioneel gedrag
Views beschikbaar:
| View | Beschrijving | Standaard |
|---|---|---|
| Dag | Enkele dag, uren 08:00-18:00 verticaal | Nee |
| Week | Ma-Zo, 7 kolommen, uren verticaal | Ja ✓ |
| Werkdagen | Ma-Vr, 5 kolommen | Nee |
Weergave-elementen:
- Tijdslots: 30 minuten interval, visueel onderscheiden met subtle gridlijnen
- Afspraak blokken: Patient naam (bold), tijd, type indicator (kleur dot)
- Huidige tijd indicator: Rode lijn op "nu" moment + auto-scroll naar nu bij laden
- Grijze zones: Tijden buiten werktijden (vóór 08:00, na 18:00) licht gearceerd
- Vandaag markering: Huidige dag kolom heeft licht gekleurde achtergrond
Visuele details kalender grid:
Ma 2 Di 3 Wo 4 (vandaag) Do 5
─────────────────────────────────────────────────────
08:00 │ │ │ ░░░░░░░░░░░░░ │ │
│ │ │ ░░░░░░░░░░░░░ │ │
09:00 │ ┌────────┐ │ │ ░░░░░░░░░░░░░ │ │
│ │Jan de V│ │ │ ░─────────────│────────────│ ← Rode "nu" lijn
10:00 │ │Intake │ │ │ ░░░░░░░░░░░░░ │ ┌────────┐ │
│ └────────┘ │ │ ░░░░░░░░░░░░░ │ │Maria J │ │
11:00 │ │ ┌────────┐ │ ░░░░░░░░░░░░░ │ │Behandel│ │
│ │ │Piet K. │ │ ░░░░░░░░░░░░░ │ └────────┘ │
Vandaag (wo 4) heeft lichte achtergrondkleur (░)
Interacties:
| Actie | Resultaat | Feedback |
|---|---|---|
| Klik op leeg tijdslot | Opent Afspraak Modal met datum/tijd pre-filled | Slot krijgt kort highlight |
| Klik op afspraak blok | Opent Afspraak Detail Popup | Blok krijgt ring focus |
| Drag afspraak naar ander slot | Opent Confirm Dialog voor verzetten | Ghost preview op nieuwe locatie |
| Hover op afspraak | Cursor pointer + lichte lift | 100ms delay |
| Hover op leeg slot | Subtle "+" icon verschijnt in slot | Fade-in |
| Double-click leeg slot | Direct modal openen (sneller dan single click) | — |
States:
| State | Weergave |
|---|---|
| Laden | Skeleton loading met shimmer effect |
| Leeg (week/dag) | Illustratie + "Geen afspraken" + prominent CTA |
| Leeg (eerste gebruik) | Onboarding card met tips |
| Gevuld | Afspraak blokken in grid |
| Fout | Inline error met retry button |
| Offline | Banner: "Je bent offline. Wijzigingen worden gesynchroniseerd." |
Scroll & Navigatie:
- Bij pagina laden: auto-scroll naar huidige tijd (met 1 uur marge boven)
- Sticky uur-labels aan linkerkant bij horizontaal scrollen (mobile)
- Sticky dag-headers bij verticaal scrollen
4.2 Afspraak Modal (MVP)
Trigger: Klik op leeg tijdslot OF "Nieuwe afspraak" button OF bewerk-actie
Formulier velden
| Veld | Type | Verplicht | Pre-fill | Validatie |
|---|---|---|---|---|
| Patient | Autocomplete search | ✓ Ja | Nee (of recent als repeat) | Moet bestaande patient zijn |
| Datum | Date picker | ✓ Ja | Geklikte datum of vandaag | Warn bij verleden |
| Starttijd | Time select (08:00-18:00) | ✓ Ja | Geklikte tijd of volgende vrije | 30 min intervals |
| Duur | Select dropdown | ✓ Ja | Automatisch obv type | 15/30/45/60/90/120 min |
| Type | Radio buttons | ✓ Ja | Slim obv patient historie | Intake/Behandeling/Follow-up |
| Notities | Textarea (collapsed) | Nee | Leeg | Max 500 karakters |
Smart Defaults
Type → Duur koppeling (automatisch bij wijziging type):
| Type geselecteerd | Duur wordt |
|---|---|
| Intake | 60 min |
| Behandeling | 45 min |
| Follow-up | 30 min |
User kan duur nog steeds handmatig wijzigen na auto-fill
Type suggestie obv patient historie:
| Patient situatie | Voorgeselecteerd type |
|---|---|
| Nieuwe patient (geen afspraken) | Intake |
| Had intake, geen behandeling | Behandeling |
| Had 3+ behandelingen | Follow-up |
| Laatste afspraak > 6 maanden | Intake (herintake) |
Formulier Layout (Progressive Disclosure)
┌──────────────────────────────────────────┐
│ Nieuwe afspraak ✕ │
├──────────────────────────────────────────┤
│ │
│ Patient * │
│ ┌──────────────────────────────────────┐ │
│ │ 🔍 Zoek patient... │ │ ← Auto-focus hier
│ └──────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📅 Do 5 dec │ │ 🕐 14:00 ▼ │ │ ← Compact: datum + tijd
│ └─────────────────┘ └─────────────────┘ │
│ │
│ Duur: ○ 30 ● 45 ○ 60 ○ 90 min │ ← Inline radio (niet dropdown)
│ │
│ Type: ○ Intake ● Behandeling ○ Follow │ ← Inline radio
│ │
│ [+ Notitie toevoegen] │ ← Collapsed by default
│ │
├──────────────────────────────────────────┤
│ [Annuleren] [✓ Opslaan] │
└──────────────────────────────────────────┘
Bij klik "+ Notitie toevoegen" → expand naar textarea
Inline Validatie
| Veld | Wanneer valideren | Feedback |
|---|---|---|
| Patient | On blur + na 500ms idle | Rode border + "Selecteer een patient" |
| Datum | On change | Gele border + "Let op: verleden datum" |
| Tijd | On change | Check conflict realtime, toon inline warning |
| Notities | On input | Character counter: "234/500" |
Conflict preview (inline, vóór submit):
┌──────────────────────────────────────────┐
│ ⚠️ Overlap met: Jan de Vries 14:00 │ ← Inline warning
│ [Andere tijd kiezen] of [Negeren] │
└──────────────────────────────────────────┘
Functionaliteit
Validatie flow (optimized):
1. Bij elk veld change: inline validatie
2. Conflict check: real-time bij datum/tijd wijziging
3. Bij "Opslaan":
a. Final validation sweep
b. Als conflict: toon inline warning (niet aparte dialog voor simpele cases)
c. Als meerdere conflicts: toon Conflict Dialog
d. Opslaan → optimistic update → toast
Acties:
| Knop | Gedrag | State |
|---|---|---|
| Opslaan | Valideer → Sla op → Sluit → Toast | Disabled tijdens save, spinner |
| Annuleren | Check unsaved → Sluit modal | — |
| Verwijderen (edit) | Inline confirm → Soft delete | Rode tekst, niet prominent |
Keyboard shortcuts:
Cmd/Ctrl + Enter= Opslaan (ook vanuit textarea)Escape= Annuleren (met unsaved check)Tab= Volgende veld↓/↑in dropdowns = Navigate options
4.3 Patient Zoeken (MVP)
Locatie: Autocomplete input in Afspraak Modal
Functioneel gedrag
Zoeklogica:
- Minimaal 2 karakters voor zoekstart
- Zoekt op: achternaam, voornaam, BSN (laatste 4 cijfers), geboortedatum
- Resultaten: Max 10, gesorteerd op relevantie (exacte match eerst)
- Debounce: 300ms na laatste toetsaanslag
Resultaat weergave:
┌──────────────────────────────────────────────┐
│ 🔍 "jan de" ⟳ │
├──────────────────────────────────────────────┤
│ ⭐ Recente patiënten │ ← Sectie header (subtle)
├──────────────────────────────────────────────┤
│ Jan de Vries ↵ │ ← Recent + keyboard hint
│ ♂ 12-03-1985 (39 jaar) · Laatste: 28 nov │
├──────────────────────────────────────────────┤
│ 🔍 Zoekresultaten │ ← Sectie header
├──────────────────────────────────────────────┤
│ Jantine de Boer │
│ ♀ 05-08-1972 (52 jaar) │
├──────────────────────────────────────────────┤
│ Jan de Groot │
│ ♂ 23-07-1990 (34 jaar) │
├──────────────────────────────────────────────┤
│ + Nieuwe patient aanmaken │ ← Altijd onderaan
└──────────────────────────────────────────────┘
States:
| State | Weergave |
|---|---|
| Leeg (focus) | Toon recente patiënten (max 5) als suggestie |
| Typen (< 2 chars) | "Typ minimaal 2 karakters..." |
| Zoeken | Spinner rechts in input |
| Resultaten | Recente patiënten bovenaan, dan zoekresultaten |
| Geen resultaat | "Geen patiënt gevonden" + link naar patient aanmaken |
| Geselecteerd | Chip met patient naam + X om te wissen |
| Keyboard navigatie | Highlighted row met subtle achtergrond |
UX verbeteringen:
- Recente patiënten: Bij focus zonder input, toon laatste 5 bezochte patiënten
- Match highlighting: Zoekterm bold in resultaten ("Jan de Vries")
- Keyboard hints: Toon "↵" icon bij geselecteerde row
- Laatste afspraak: Toon "Laatste: 28 nov" voor context
- Focus management: Auto-focus op input bij modal open
4.4 Conflict Dialog (MVP)
Trigger: Automatisch bij opslaan afspraak met overlap
Functioneel gedrag
Detectie logica:
- Check: Nieuwe afspraak
[start, eind]overlapt met bestaande afspraak(en)? - Overlap = enige overlap in tijd (inclusief aangrenzend)
- Zelfde behandelaar (huidige user) is criterium
Dialog inhoud:
┌──────────────────────────────────────────────┐
│ ⚠️ Dubbele boeking gedetecteerd │
├──────────────────────────────────────────────┤
│ │
│ Je hebt al een afspraak op dit tijdstip: │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ 📅 Donderdag 5 dec, 14:00-15:00 │ │
│ │ 👤 Jan de Vries │ │
│ │ 📋 Intake │ │
│ └────────────────────────────────────────┘ │
│ │
│ Wil je de tijd aanpassen of toch inplannen? │
│ │
├──────────────────────────────────────────────┤
│ [Wijzigen] [Toch inplannen] │
└──────────────────────────────────────────────┘
Acties:
| Knop | Gedrag |
|---|---|
| Wijzigen | Sluit dialog, terug naar modal met tijd veld gefocust |
| Toch inplannen | Bypass conflict, sla op, sluit alles, toast met waarschuwing |
Business rule:
Systeem waarschuwt maar blokkeert NIET. Behandelaars hebben valide redenen voor overlap (bijv. administratie-tijd, korte telefoon tussendoor).
4.5 Afspraak Detail Popup (MVP)
Trigger: Klik op afspraak blok in kalender
Functioneel gedrag
Popup inhoud:
┌──────────────────────────────────────────────┐
│ Jan de Vries ✕ │
│ ♂ 39 jaar · Intake #3 │
├──────────────────────────────────────────────┤
│ 📅 Do 5 dec 2024 │
│ 🕐 14:00 - 15:00 (60 min) │
│ ● Intakegesprek │ ← Kleur dot voor type
├──────────────────────────────────────────────┤
│ Notities: │
│ Eerste gesprek na verwijzing huisarts. │
│ Verwacht: angstklachten, slaapproblemen. │
├──────────────────────────────────────────────┤
│ │
│ [📝 Maak verslag] [✏️ Bewerken]│
│ │
│ ─────────────────────────────────────── │
│ [👤 Naar dossier] [🗑️ Annuleren] │
│ │
└──────────────────────────────────────────────┘
Acties:
| Knop | Gedrag | Visuele prioriteit |
|---|---|---|
| 📝 Maak verslag | Navigeer naar rapportage met pre-fills | Primary (solid button) |
| ✏️ Bewerken | Open Afspraak Modal in edit mode | Secondary (outline) |
| 👤 Naar dossier | Navigeer naar patient dossier | Tertiary (text link) |
| 🗑️ Annuleren | Confirm dialog → soft delete | Tertiary (text link, rode tekst) |
Contextuele CTA (slimme primary actie):
| Afspraak status | Primary actie |
|---|---|
| Gepland (toekomst) | "Bewerken" prominent |
| Vandaag/nu | "Maak verslag" prominent (pulsing) |
| Verleden zonder verslag | "Maak verslag" prominent + badge "!" |
| Verleden met verslag | "Bekijk verslag" + "Bewerken" |
Status indicatoren:
| Status | Badge | Extra visueel |
|---|---|---|
planned |
🔵 Blauw | — |
| Vandaag | 🟢 Groen | Subtle glow effect |
in-progress |
🟢 Groen pulsing | "Nu bezig" label |
on-hold |
🟠 Oranje | Pauze icon |
completed |
⚪ Grijs | Check icon |
cancelled |
🔴 Rood | Doorgestreepte tekst |
UX verbeteringen:
- Quick-action keyboard:
V= Verslag maken,E= Edit,D= Delete - Positie: Popup verschijnt naast afspraak blok, niet centered (context behouden)
- Click-outside: Sluit popup
- Animatie: Fade-in vanuit afspraak blok positie
- Verslag indicator: Als verslag bestaat, toon "📝 Verslag (28 nov)" link in popup
4.6 Toolbar (MVP)
Locatie: Boven kalender grid, sticky bij scrollen
Functioneel gedrag
Desktop layout:
┌──────────────────────────────────────────────────────────────────────────┐
│ [◀] [Vandaag] [▶] 📅 Week 49 · 2-8 december 2024 [Dag|Week|Werk] │
│ [+ Nieuwe afspraak] │
└──────────────────────────────────────────────────────────────────────────┘
Tablet layout (768-1024px):
┌─────────────────────────────────────────────────────┐
│ [◀] [Vandaag] [▶] Week 49 [D|W|5] [+ Nieuw] │
└─────────────────────────────────────────────────────┘
Mobile layout (<768px):
┌─────────────────────────────────┐
│ [◀] Do 5 december [▶] [+] │ ← Compact, alleen dag
└─────────────────────────────────┘
| Element | Gedrag | Keyboard |
|---|---|---|
| ◀ / ▶ | Navigeer 1 periode terug/vooruit | ← / → |
| Vandaag | Spring naar huidige datum + scroll naar nu | T |
| Datum label | Klik → open date picker voor directe navigatie | — |
| View toggle | Switch tussen views | 1/2/3 |
| + Nieuwe afspraak | Open modal zonder pre-fill datum | N |
View toggle states:
┌─────────────────────────────────┐
│ [Dag] Week Werkdagen │ ← Dag geselecteerd
└─────────────────────────────────┘
↓ na klik op Week
┌─────────────────────────────────┐
│ Dag [Week] Werkdagen │ ← Week geselecteerd, animated underline
└─────────────────────────────────┘
Extra UX details:
- Vandaag button: Highlight als niet op vandaag (subtle pulsing dot)
- Datum klik: Opent kleine kalender picker voor snelle week-jump
- Navigatie animatie: Smooth slide transition bij periode wisseling
- Loading tijdens navigatie: Spinner in datum label, grid blijft staan
4.7 Mini Kalender Sidebar (Stretch)
Locatie: Linker sidebar op desktop
Functioneel gedrag (indien gebouwd)
┌────────────────────┐
│ December 2024 │
│ Ma Di Wo Do Vr Za Zo│
│ 1 │
│ 2 3 4 5• 6 7 8│
│ 9 10 11 12 13 14 15│
│ 16 17 18 19 20 21 22│
│ 23 24 25 26 27 28 29│
│ 30 31 │
└────────────────────┘
- Bullet (•): Dagen met afspraken
- Highlight: Geselecteerde dag/week
- Klik op dag: Navigeer naar die dag in main view
5. UI-overzicht (visuele structuur)
🎯 Doel: Globale schermopbouw communiceren.
5.1 Desktop Layout (≥1024px)
┌──────────────────────────────────────────────────────────────────────┐
│ EPD Sidebar │ Agenda Module │
│ │ │
│ ┌─────────┐ │ ┌─────────────────────────────────────────────────────┐│
│ │Dashboard│ │ │ Toolbar: [◀][Vandaag][▶] Week 49 [Dag|Week|Werk] ││
│ │─────────│ │ ├─────────────────────────────────────────────────────┤│
│ │Cliënten │ │ │ ││
│ │─────────│ │ │ Ma Di Wo Do Vr Za Zo ││
│ │►Agenda │ │ │ ───────────────────────────────────────── ││
│ │─────────│ │ │ 08:00 ││
│ │Rapportage│ │ │ 09:00 ┌────┐ ││
│ └─────────┘ │ │ 10:00 │Jan │ ┌────┐ ││
│ │ │ 11:00 │de V│ │Mari│ ││
│ │ │ 12:00 └────┘ │ │ ││
│ │ │ 13:00 └────┘ ││
│ │ │ 14:00 ┌────┐ ││
│ │ │ 15:00 │Piet│ ││
│ │ │ 16:00 └────┘ ││
│ │ │ 17:00 ││
│ │ │ 18:00 ││
│ │ └─────────────────────────────────────────────────────┤│
└─────────────┴───────────────────────────────────────────────────────┘
5.2 Mobile Layout (<768px)
┌─────────────────────────┐
│ ≡ Agenda + Nieuw │
├─────────────────────────┤
│ ◀ Vandaag ▶ │
│ Do 5 december 2024 │
├─────────────────────────┤
│ 08:00 ──────────────────│
│ 09:00 ┌───────────────┐ │
│ │ Jan de Vries │ │
│ 10:00 │ Intake │ │
│ └───────────────┘ │
│ 11:00 ──────────────────│
│ 12:00 ──────────────────│
│ 13:00 ┌───────────────┐ │
│ │ Maria Jansen │ │
│ 14:00 │ Behandeling │ │
│ └───────────────┘ │
│ 15:00 ──────────────────│
│ ... │
└─────────────────────────┘
Mobile aanpassingen:
- Alleen dag-view standaard
- Geen drag-drop (gebruik edit button)
- Swipe links/rechts voor dag navigatie
- Hamburger menu voor EPD sidebar
5.3 Afspraak Modal (Responsive)
┌──────────────────────────────────────────┐
│ Nieuwe afspraak ✕ │
├──────────────────────────────────────────┤
│ │
│ Patient * │
│ ┌──────────────────────────────────────┐ │
│ │ 🔍 Zoek patient... │ │
│ └──────────────────────────────────────┘ │
│ │
│ Datum * Starttijd * │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ 📅 05-12-2024 │ │ 14:00 ▼ │ │
│ └────────────────┘ └────────────────┘ │
│ │
│ Duur * │
│ ┌──────────────────────────────────────┐ │
│ │ 60 minuten ▼ │ │
│ └──────────────────────────────────────┘ │
│ │
│ Type afspraak * │
│ ○ Intake ● Behandeling ○ Follow-up │
│ │
│ Notities │
│ ┌──────────────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ └──────────────────────────────────────┘ │
│ │
├──────────────────────────────────────────┤
│ [Annuleren] [Opslaan] │
└──────────────────────────────────────────┘
5.5 UX Richtlijnen & Micro-interacties
🎯 Doel: Definiëren van de kleine details die de ervaring vloeiend maken.
5.5.1 Design Principes
| Principe | Toepassing in Agenda |
|---|---|
| Snelheid boven alles | Afspraak maken < 10 sec, geen onnodige stappen |
| Visuele feedback | Elke actie toont direct resultaat |
| Foutpreventie > foutcorrectie | Slim valideren vóór submit |
| Flexibiliteit | Waarschuwen, niet blokkeren |
| Context behouden | Na actie terug naar waar je was |
5.5.2 Micro-interacties & Animaties
Afspraak blokken:
| Interactie | Visuele feedback | Timing |
|---|---|---|
| Hover | Lichte schaduw + cursor pointer | Instant |
| Klik | Scale 0.98 + donkerder | 100ms ease |
| Drag start | Opacity 0.7 + verhoogde schaduw | 150ms |
| Drag over slot | Doelslot krijgt highlight border | Instant |
| Drop | Bounce-in animatie naar nieuwe positie | 200ms spring |
Modals & Dialogs:
| Actie | Animatie | Timing |
|---|---|---|
| Open | Fade-in + slide-up | 200ms ease-out |
| Sluiten | Fade-out + slide-down | 150ms ease-in |
| Achtergrond | Blur + dim (opacity 0.5) | 200ms |
Feedback elementen:
| Element | Gedrag |
|---|---|
| Toast success | Slide-in rechtsonder, auto-dismiss 3s |
| Toast warning | Slide-in, persist tot dismiss |
| Loading spinner | Pulse animatie, centered in context |
| Skeleton | Shimmer effect links→rechts |
5.5.3 Optimistic Updates
Principe: UI update direct, rollback bij fout.
| Actie | Optimistic gedrag | Bij fout |
|---|---|---|
| Afspraak opslaan | Direct in kalender tonen | Remove + toast "Opslaan mislukt, probeer opnieuw" |
| Afspraak verzetten | Direct naar nieuwe positie | Terug naar origineel + toast |
| Afspraak annuleren | Direct doorstrepen (visueel) | Herstel + toast |
Implementatie voorbeeld:
1. User klikt "Opslaan"
2. Modal sluit, afspraak verschijnt in kalender (grijs/loading state)
3. API call in background
4. Success → afspraak krijgt normale styling
5. Error → afspraak verdwijnt, toast met retry optie
5.5.4 Smart Defaults & Auto-fill
Tijdslot klikken:
| Context | Auto-fill |
|---|---|
| Klik op 14:00 | Datum + tijd pre-filled |
| Klik na laatste afspraak | Suggereer aansluitend tijdslot |
| Klik op zelfde dag als recente afspraak | Suggereer zelfde patient |
Type selectie:
| Laatste afspraak patient | Suggestie |
|---|---|
| Geen eerdere afspraken | Intake (standaard) |
| Had intake | Behandeling |
| Had 3+ behandelingen | Follow-up |
Duur selectie:
| Type | Standaard duur |
|---|---|
| Intake | 60 min |
| Behandeling | 45 min |
| Follow-up | 30 min |
5.5.5 Keyboard Navigatie
Globale shortcuts (wanneer op agenda pagina):
| Shortcut | Actie |
|---|---|
N |
Nieuwe afspraak (opent modal) |
T |
Ga naar vandaag |
← / → |
Vorige/volgende periode |
1 / 2 / 3 |
Switch naar Dag/Week/Werkdagen |
Esc |
Sluit modal/popup |
In Afspraak Modal:
| Shortcut | Actie |
|---|---|
Tab |
Volgende veld |
Shift+Tab |
Vorige veld |
Enter |
Opslaan (als geen textarea focus) |
Esc |
Annuleren (met unsaved changes check) |
↓ / ↑ |
Navigeer zoekresultaten |
In Patient zoeken:
| Shortcut | Actie |
|---|---|
↓ |
Eerste/volgende resultaat selecteren |
↑ |
Vorige resultaat |
Enter |
Bevestig selectie |
Esc |
Sluit dropdown, behoud huidige selectie |
5.5.6 Empty States & Onboarding
Lege agenda (geen afspraken deze week):
┌─────────────────────────────────────────────────┐
│ │
│ 📅 │
│ │
│ Geen afspraken deze week │
│ │
│ Klik op een tijdslot om je eerste │
│ afspraak in te plannen, of gebruik: │
│ │
│ [+ Nieuwe afspraak] │
│ │
│ 💡 Tip: Druk 'N' voor snelle invoer │
│ │
└─────────────────────────────────────────────────┘
Eerste keer agenda openen (onboarding hint):
- Subtle pulse animatie op "+ Nieuwe afspraak" button
- Tooltip: "Begin hier met je eerste afspraak"
- Verdwijnt na eerste afspraak of dismiss
5.5.7 Error Handling & Recovery
Graceful degradation:
| Fout scenario | User ziet |
|---|---|
| API timeout | "Even geduld..." → na 5s: "Kon niet laden. [Opnieuw proberen]" |
| Conflict check faalt | Sla op zonder conflict check + warning toast |
| Patient search faalt | "Zoeken niet beschikbaar. Typ volledige naam." |
Undo functionaliteit:
| Actie | Undo mogelijkheid |
|---|---|
| Afspraak verwijderd | Toast met "Ongedaan maken" link (5 sec window) |
| Afspraak verzet | Toast met "Ongedaan maken" link (5 sec window) |
| Modal gesloten met unsaved | "Je hebt onopgeslagen wijzigingen. Sluiten?" |
Unsaved changes detection:
┌──────────────────────────────────────────┐
│ Wijzigingen niet opgeslagen │
├──────────────────────────────────────────┤
│ │
│ Je hebt wijzigingen die nog niet zijn │
│ opgeslagen. Wil je deze verliezen? │
│ │
├──────────────────────────────────────────┤
│ [Terug naar formulier] [Niet opslaan] │
└──────────────────────────────────────────┘
5.5.8 Visual Hierarchy in Kalender
Tijdslot visuele staten:
| Staat | Visuele behandeling |
|---|---|
| Beschikbaar | Wit/licht, hover highlight |
| Bezet | Afspraak blok |
| Verleden | Licht grijs, minder contrast |
| Buiten werktijd | Gestreept/gearceerd patroon |
| Nu (huidige tijd) | Rode lijn indicator |
| Geselecteerd slot | Blue outline |
Afspraak blok informatie hiërarchie:
┌────────────────────────┐
│ Jan de Vries 📝 │ ← Naam (bold) + verslag icon
│ 14:00 - 15:00 │ ← Tijd (secondary)
│ ● Intake │ ← Type met kleur dot
└────────────────────────┘
Bij weinig ruimte (korte afspraken 15-30 min):
┌────────────────────────┐
│ J. de Vries 14:00 ● │ ← Compacte weergave
└────────────────────────┘
5.5.9 Touch & Mobile Specifiek
Touch targets:
- Minimum 44x44px voor alle interactieve elementen
- Afspraak blokken: volledige breedte van kolom
Gestures:
| Gesture | Actie |
|---|---|
| Swipe links/rechts | Volgende/vorige dag |
| Long press op afspraak | Open context menu (bewerken/verwijderen) |
| Pull down | Refresh agenda data |
| Tap op leeg slot | Toon "+" button → tap = nieuwe afspraak |
Mobile-specifieke UI:
- Geen drag-drop (te foutgevoelig op touch)
- Bottom sheet ipv centered modal
- Sticky header met datum bij scrollen
5.5.10 Loading States per Component
| Component | Loading state |
|---|---|
| Agenda grid | Skeleton met 3-4 placeholder blokken |
| Patient search | Spinner in input rechts |
| Modal opslaan | Button disabled + spinner + "Opslaan..." |
| View switch | Fade-out → skeleton → fade-in |
| Datum navigatie | Kort skeleton (200ms max) |
Perceived performance:
- Prefetch volgende/vorige week data bij idle
- Cache huidige week, invalidate bij mutatie
- Stale-while-revalidate pattern
6. Interacties met AI (functionele beschrijving)
🎯 Doel: Uitleggen waar AI in de agenda flow voorkomt.
6.1 AI-integratiepunten in Agenda Module
| Locatie | AI-actie | Trigger | Output |
|---|---|---|---|
| Afspraak Detail | → Maak verslag | Klik op "Maak verslag" button | Navigatie naar Rapportage met pre-fills |
| Rapportage (na navigatie) | Transcriptie | Klik "Opname starten" | Audio → tekst via Deepgram |
| Rapportage (na navigatie) | Verslag genereren | Klik "AI genereer" | Gestructureerd verslag op basis van transcriptie |
6.2 Afspraak → Verslag Flow (Kernintegratie)
Dit is de killer feature van de agenda integratie:
Stap-voor-stap:
1. Behandelaar klikt op afspraak in agenda (bijv. "Jan de Vries - 10:00 Intake")
2. Detail popup opent met "📝 Maak verslag" button
3. Behandelaar klikt "Maak verslag"
4. Systeem navigeert naar: /epd/patients/[jan-id]/rapportage?encounter=[afspraak-id]
5. Rapportage module opent met:
- Patient: Jan de Vries (pre-filled, read-only)
- Type: Intake verslag (pre-selected)
- Afspraak referentie: 05-12-2024 10:00 (gelinkt)
6. Behandelaar kan direct:
- Audio opnemen → AI transcribeert
- AI verslag laten genereren
- Handmatig schrijven
7. Bij opslaan: verslag automatisch gekoppeld aan afspraak
8. In agenda: afspraak toont nu 📝 icon (verslag aanwezig)
Pre-fill mapping:
| Rapportage veld | Bron uit Afspraak |
|---|---|
patient_id |
appointment.patient_id |
encounter_id |
appointment.id |
report_type |
Gebaseerd op appointment.type (intake → "intake_verslag") |
title |
"Verslag {type} - {patient_naam} - {datum}" |
6.3 Bidirectionele Linking
Van Agenda naar Dossier:
- Afspraak detail popup bevat link naar patient dossier
- Klik "Naar dossier" →
/epd/patients/[id]
Van Dossier naar Agenda:
- In patient timeline: afspraken weergeven
- Klik op afspraak → terug naar agenda met die afspraak geselecteerd
Van Afspraak naar Verslag:
- Afspraak icon toont 📝 als verslag bestaat
- Klik op icon → direct naar verslag
Van Verslag naar Afspraak:
- Verslag header toont "Gekoppeld aan afspraak: Do 5 dec 14:00"
- Klik → terug naar afspraak detail
7. Gebruikersrollen en rechten
🎯 Doel: Beschrijven welke rollen toegang hebben.
MVP: Single-User Model
In de MVP is er geen rechtenbeheer. Alle functionaliteit is beschikbaar voor de ingelogde behandelaar.
| Rol | Toegang | Beperkingen |
|---|---|---|
| Behandelaar (enige rol) | Volledige agenda | Alleen eigen afspraken zichtbaar |
Data Scoping
- Afspraken: Gefilterd op
practitioner_id = current_user.id - Patiënten: Alle patiënten in systeem doorzoekbaar (MVP simplificatie)
- Verslagen: Alleen eigen verslagen
Post-MVP: Multi-User Model
| Rol | Toegang | Beperkingen |
|---|---|---|
| Behandelaar | Eigen agenda | Alleen eigen afspraken bewerken |
| Assistent | Alle agenda's | Inplannen voor anderen, niet verwijderen |
| Manager | Alle agenda's (readonly) | Geen bewerkingen, wel rapportages |
8. Data Model (Functionele Entiteiten)
🎯 Doel: Functioneel overzicht van data-entiteiten.
8.1 Afspraak (Encounter)
| Attribuut | Type | Beschrijving |
|---|---|---|
id |
UUID | Unieke identifier |
patient_id |
UUID | Gekoppelde patient |
practitioner_id |
UUID | Behandelaar (current user) |
period_start |
DateTime | Starttijd afspraak |
period_end |
DateTime | Eindtijd afspraak |
type_code |
Enum | intake / treatment / followup |
type_display |
String | "Intakegesprek", "Behandeling", etc. |
status |
Enum | planned / in-progress / on-hold / completed / cancelled |
notes |
Text | Vrije notities |
created_at |
DateTime | Aanmaakdatum |
updated_at |
DateTime | Laatste wijziging |
8.2 Afspraak Types
| Code | Display (NL) | Standaard duur | Kleur (stretch) |
|---|---|---|---|
intake |
Intakegesprek | 60 min | 🔵 Blauw |
treatment |
Behandeling | 45 min | 🟢 Groen |
followup |
Follow-up | 30 min | 🟡 Geel |
8.3 Status Lifecycle
planned → in-progress → completed
↓ ↓
on-hold cancelled (soft delete)
| Status | Betekenis | Automatisch |
|---|---|---|
planned |
Ingepland, nog niet gestart | Bij aanmaken |
in-progress |
Momenteel bezig | Bij starttijd? (post-MVP) |
on-hold |
Tijdelijk gepauzeerd | Handmatig (post-MVP) |
completed |
Afgerond | Handmatig of na verslag opslaan |
cancelled |
Geannuleerd | Bij "Verwijderen" actie |
9. Flows in Detail
🎯 Doel: Gedetailleerde stap-voor-stap beschrijvingen.
9.1 Flow: Nieuwe Afspraak (Happy Path)
Scenario: Behandelaar plant afspraak in tijdens telefoongesprek
sequenceDiagram
actor B as Behandelaar
participant K as Kalender
participant M as Afspraak Modal
participant S as Systeem
participant DB as Database
B->>K: Klikt op leeg tijdslot (Do 14:00)
K->>M: Opent modal met pre-fill
Note over M: Datum: 05-12-2024<br/>Tijd: 14:00
B->>M: Typt "jan de" in patient zoek
M->>S: Search request (debounced)
S->>DB: Query patients WHERE name LIKE 'jan de%'
DB-->>S: [Jan de Vries, Jantine de Boer]
S-->>M: Resultaten
B->>M: Selecteert "Jan de Vries"
B->>M: Selecteert type "Behandeling"
B->>M: Klikt "Opslaan"
M->>S: Valideer + conflict check
S->>DB: Check overlap voor practitioner
DB-->>S: Geen conflict
S->>DB: INSERT encounter
DB-->>S: Success
S-->>M: Opslaan OK
M-->>K: Sluit modal
K->>K: Refresh + toon nieuwe afspraak
K-->>B: Toast: "Afspraak opgeslagen"
9.2 Flow: Dubbele Boeking
Scenario: Behandelaar probeert afspraak te maken op bezette tijd
1. Behandelaar klikt op 14:00 (waar al afspraak staat met Jan de Vries)
2. Modal opent, behandelaar vult Maria Jansen in, klikt Opslaan
3. Systeem detecteert overlap:
- Nieuwe: Maria 14:00-15:00
- Bestaand: Jan 14:00-15:00
4. Conflict Dialog opent:
"⚠️ Je hebt al een afspraak: Jan de Vries - 14:00 (Intake)"
5a. Behandelaar kiest "Wijzigen":
→ Terug naar modal, tijd veld gefocust
→ Kiest 15:00, opslaan succesvol
5b. Behandelaar kiest "Toch inplannen":
→ Beide afspraken op 14:00
→ Toast: "Afspraak opgeslagen (dubbele boeking)"
9.3 Flow: Afspraak → Verslag
Scenario: Direct na intake verslag maken via AI
1. Behandelaar klikt op afspraak "Jan de Vries - 10:00 Intake" (zojuist afgelopen)
2. Detail popup opent
3. Behandelaar klikt "📝 Maak verslag"
4. Navigatie naar /epd/patients/[jan-id]/rapportage?encounter=[enc-id]
5. Rapportage module laadt met:
- Patient: Jan de Vries (locked)
- Type: Intake verslag
- Reference: "Afspraak 05-12-2024 10:00"
6. Behandelaar klikt "🎤 Start opname"
7. Behandelaar dicteert verslag van gesprek
8. Behandelaar klikt "Stop"
9. Deepgram transcribeert audio → tekst
10. Behandelaar klikt "AI Genereer verslag"
11. Claude genereert gestructureerd verslag
12. Behandelaar reviewt, past aan, klikt "Opslaan"
13. Verslag opgeslagen met encounter_id link
14. Terug naar agenda: afspraak toont 📝 icon
9.4 Flow: Drag & Drop Verzetten
Scenario: Patient belt, moet naar andere tijd
1. Behandelaar ziet afspraak "Maria - Di 14:00" in week view
2. Behandelaar klikt en houdt vast op afspraak card
3. Visuele feedback: card krijgt schaduw, wordt semi-transparant
4. Behandelaar sleept naar "Wo 15:00"
5. Drop → Confirm dialog:
"Afspraak verzetten van di 14:00 naar wo 15:00?"
[Annuleren] [Bevestigen]
6. Behandelaar klikt "Bevestigen"
7. Systeem checkt conflict (geen)
8. Database update: period_start = wo 15:00
9. Kalender refresh
10. Toast: "Afspraak verzet naar wo 15:00"
10. Validaties & Business Rules
🎯 Doel: Overzicht van validatieregels.
10.1 Formulier Validaties
| Veld | Regel | Foutmelding |
|---|---|---|
| Patient | Verplicht, moet bestaan | "Selecteer een patient" |
| Datum | Verplicht, format dd-mm-yyyy | "Vul een datum in" |
| Datum | Niet in verleden | "Datum ligt in het verleden" (warning, niet block) |
| Starttijd | Verplicht, 08:00-18:00 | "Kies een tijd binnen werktijden" |
| Duur | Verplicht, 15-120 min | "Kies een geldige duur" |
| Type | Verplicht | "Kies een type afspraak" |
| Notities | Max 500 karakters | "Notities te lang (max 500)" |
10.2 Business Rules
| Regel | Type | Gedrag |
|---|---|---|
| Dubbele boeking | Warning | Toon dialog, sta override toe |
| Werktijden | Soft | Waarschuw bij buiten 08:00-18:00, maar allow |
| Verleden datum | Soft | Waarschuw, maar allow (correcties achteraf) |
| Eigen afspraken | Hard | Kan alleen eigen afspraken zien/bewerken |
| Soft delete | Hard | Cancelled afspraken blijven in DB, niet verwijderd |
| Verslag koppeling | Auto | Verslag maakt automatisch link naar encounter |
10.3 Conflict Detectie Logica
isConflict =
(new.start < existing.end) AND (new.end > existing.start)
AND (new.practitioner_id = existing.practitioner_id)
AND (existing.status != 'cancelled')
11. Prioritering MVP vs Post-MVP
🎯 Doel: Duidelijke cut-off voor eerste oplevering.
MVP (Must Ship)
| Feature | Reden |
|---|---|
| Week view kalender | Core navigatie |
| Dag view kalender | Alternatieve weergave |
| Nieuwe afspraak maken | Core functie |
| Patient zoeken (autocomplete) | Koppeling nodig |
| Afspraak bewerken | Wijzigingen nodig |
| Afspraak annuleren (soft delete) | Status management |
| Dubbele boeking warning | Foutpreventie |
| Conflict override | Flexibiliteit |
| "Maak verslag" navigatie | AI-integratie showcase |
| Basic drag-drop (desktop) | Snelle wijzigingen |
| Responsive mobile view | Demo-proof |
Stretch (Nice-to-Have voor Demo)
| Feature | Reden |
|---|---|
| Werkdagen view | Extra convenience |
| Mini kalender sidebar | Navigatie gemak |
| Recente patiënten dropdown | Sneller repeat |
| Kleurcodering per type | Visueel |
| Verslag icon op afspraak | Status indicator |
Post-MVP (Later)
| Feature | Wanneer |
|---|---|
| Recurring appointments | Na klantfeedback |
| Multi-practitioner | Bij team gebruik |
| Email/SMS notifications | Bij productie |
| Calendar sync (Google/Outlook) | Enterprise |
| Wachtlijstbeheer | Productie versie |
| Audit logging | Compliance |
12. Bijlagen & Referenties
🎯 Doel: Links naar gerelateerde documenten.
Interne Documenten
| Document | Beschrijving | Status |
|---|---|---|
| PRD Agenda Module v1.0 | Productvisie en scope | ✅ Approved |
| TO Agenda Module (volgt) | Technische implementatie | 📋 Te schrijven |
| UX Stylesheet | Design tokens en componenten | ✅ Beschikbaar |
| Database Types | Supabase schema definities | ✅ Gegenereerd |
Externe Referenties
| Bron | Gebruik |
|---|---|
| FullCalendar Docs | Kalender library |
| shadcn/ui | UI componenten |
| date-fns | Datum manipulatie |
| FHIR Encounter | Data model referentie |
Bestaande Code Patronen
Referentie naar bestaande implementaties in codebase:
| Pattern | Locatie | Hergebruik voor |
|---|---|---|
| Patient list + search | app/epd/patients/ |
Patient zoeken component |
| Form validation | app/epd/patients/[id]/intakes/ |
Afspraak formulier |
| Modal dialogs | components/ui/dialog.tsx |
Afspraak modal |
| Toast notifications | components/ui/toaster.tsx |
Feedback |
| Server actions | app/epd/patients/[id]/intakes/actions.ts |
CRUD operaties |
| Type definitions | lib/types/intake.ts |
Appointment types |
Changelog
| Versie | Datum | Auteur | Wijziging |
|---|---|---|---|
| v1.0 | 02-12-2024 | Colin | Initieel FO op basis van PRD v1.0 |
| v1.1 | 02-12-2024 | Colin | UX verbeteringen: micro-interacties, smart defaults, keyboard nav, optimistic updates, inline validatie, responsive toolbar, recente patiënten, contextuele CTAs |
Document Status: ✅ Klaar voor review Volgende stap: → Technisch Ontwerp (TO) Agenda Module
Appendix: UX Checklist voor Implementatie
Snelle checklist voor developers om UX verbeteringen niet te vergeten:
Must-Have UX (MVP)
- Auto-scroll naar "nu" bij pagina laden
- Vandaag kolom highlight
- Skeleton loading states
- Optimistic updates met rollback
- Inline validatie (niet alleen op submit)
- Smart defaults: type → duur koppeling
- Keyboard shortcuts: N, T, ←, →, Esc
- Focus management: auto-focus patient search in modal
- Toast feedback bij alle acties
- Unsaved changes warning
- Recente patiënten bij lege search
- Contextuele primary button (verslag maken prominent na afspraak)
Nice-to-Have UX (Stretch)
- Double-click op slot voor snelle afspraak
- Hover "+" icon op lege slots
- Match highlighting in zoekresultaten
- Slide animaties bij view/datum wisseling
- Undo toast na delete/verzetten (5 sec window)
- Prefetch volgende/vorige week
- Mobile swipe gestures
- Onboarding hints bij eerste gebruik