- E1.S1: FullCalendar setup with React wrapper - E1.S2: Day view (timeGridDay) with hourly slots - E1.S3: Week view (timeGridWeek) with 7-day grid - E1.S4: Workweek view (timeGridWorkWeek, Mon-Fri) Features: - View switcher toolbar (Dag/Week/Werkweek) - Date navigation (prev/next/today) - Dutch localization (nl) - Business hours highlighting (08:00-18:00) - Color-coded appointments by type - Drag-and-drop rescheduling support - Server actions for encounter CRUD 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
20 KiB
Bouwplan — Agenda Module
Projectnaam: Mini-EPD Agenda Module Versie: v1.0 Datum: 02-12-2024 Auteur: Colin
1. Doel en context
Doel: Een volledige agendafunctionaliteit bouwen voor het Mini-EPD systeem waarmee behandelaars afspraken kunnen plannen, beheren en koppelen aan EPD-documenten.
Toelichting: De agenda is een kernfunctionaliteit binnen elk EPD-systeem. Momenteel bestaat er alleen een placeholder pagina op /epd/agenda. Deze module bouwt voort op de bestaande encounters tabel (FHIR-compliant) en breidt deze uit met volledige kalenderfunctionaliteit en bidirectionele koppelingen met rapportages.
Kernfunctionaliteiten:
- Kalenderweergaven (dag/week/werkdagen)
- Afspraakbeheer (maken, verzetten, annuleren)
- Patiënt-selectie bij afspraken
- Koppeling afspraak ↔ verslag (bidirectioneel)
2. Uitgangspunten
2.1 Technische Stack
| Component | Technologie |
|---|---|
| Frontend | Next.js 14 (App Router) + React 18 + TypeScript |
| UI Components | shadcn/ui (Radix primitives) + Tailwind CSS |
| Calendar Library | FullCalendar (@fullcalendar/react) |
| Datepicker | shadcn/ui Calendar (bestaand) |
| Database | Supabase (PostgreSQL) |
| Date Handling | date-fns (al in project) |
| State Management | React Server Components + Server Actions |
2.2 Projectkaders
| Aspect | Waarde |
|---|---|
| Scope | MVP met uitbreidingsmogelijkheden |
| Data | Bestaande encounters tabel + uitbreiding reports |
| Integratie | Naadloos met bestaande patient/intake flows |
| Gebruikers | Behandelaars (practitioners) |
2.3 Programmeer Uitgangspunten
Bestaande patronen volgen:
- Server Actions voor mutaties (
actions.tsper route) - API routes voor complexe queries
- FHIR-compliant datastructuren
- shadcn/ui component styling
Code Quality:
- TypeScript strict mode
- Zod validatie voor alle inputs
- Error boundaries voor UI failures
- Optimistic updates waar mogelijk
3. Epics & Stories Overzicht
| Epic ID | Titel | Doel | Status | Stories |
|---|---|---|---|---|
| E0 | Database & Types | Schema uitbreiden, types genereren | ✅ Done | 3 |
| E1 | Calendar Views | Dag/week/werkdagen weergaven | ✅ Done | 4 |
| E2 | Afspraak CRUD | Maken, bewerken, annuleren | ⏳ To Do | 5 |
| E3 | Patiënt Integratie | Selectie, zoeken, quick-create | ⏳ To Do | 3 |
| E4 | EPD Koppeling | Verslag ↔ Afspraak bidirectioneel | ⏳ To Do | 4 |
| E5 | Polish & Testing | UX verfijning, edge cases | ⏳ To Do | 3 |
4. Epics & Stories (Uitwerking)
Epic 0 — Database & Types
Epic Doel: Database schema uitbreiden voor report-encounter koppeling en TypeScript types updaten.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E0.S1 | Migration: encounter_id toevoegen aan reports | encounter_id en intake_id kolommen bestaan, foreign keys werken |
✅ | — | 2 |
| E0.S2 | Index toevoegen voor performance | idx_reports_encounter, idx_encounters_period indices bestaan |
✅ | E0.S1 | 1 |
| E0.S3 | TypeScript types regenereren | database.types.ts bevat nieuwe kolommen |
✅ | E0.S2 | 1 |
Technical Notes:
-- Migration E0.S1
ALTER TABLE reports
ADD COLUMN encounter_id UUID REFERENCES encounters(id) ON DELETE SET NULL,
ADD COLUMN intake_id UUID REFERENCES intakes(id) ON DELETE SET NULL;
-- Migration E0.S2
CREATE INDEX idx_reports_encounter ON reports(encounter_id);
CREATE INDEX idx_reports_intake ON reports(intake_id);
CREATE INDEX idx_encounters_period ON encounters(period_start, period_end);
CREATE INDEX idx_encounters_practitioner ON encounters(practitioner_id);
Epic 1 — Calendar Views
Epic Doel: Interactieve kalenderweergaven met dag, week en werkdagen views.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E1.S1 | FullCalendar installatie & setup | Library geïnstalleerd, basis component rendert | ✅ | E0.S3 | 2 |
| E1.S2 | Dag view implementeren | Uurblokken 08:00-18:00, afspraken zichtbaar | ✅ | E1.S1 | 3 |
| E1.S3 | Week view implementeren | 7-dagen grid, drag-resize werkt | ✅ | E1.S2 | 3 |
| E1.S4 | Werkdagen view (ma-vr) | Filter voor weekend, business hours highlight | ✅ | E1.S3 | 2 |
Technical Notes:
pnpm add @fullcalendar/react @fullcalendar/daygrid @fullcalendar/timegrid @fullcalendar/interaction
Component Structuur:
app/epd/agenda/
├── page.tsx # Server component, data fetching
├── components/
│ ├── agenda-calendar.tsx # FullCalendar wrapper (client)
│ ├── agenda-toolbar.tsx # View switcher, date nav
│ ├── agenda-sidebar.tsx # Mini calendar + filters
│ └── appointment-card.tsx # Event rendering
├── actions.ts # Server actions
└── types.ts # Agenda-specific types
Styling:
- FullCalendar CSS overschrijven met Tailwind
- Consistent met shadcn/ui design tokens
- Dark mode support (later)
Epic 2 — Afspraak CRUD
Epic Doel: Volledige afspraakbeheer functionaliteit.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E2.S1 | Afspraak aanmaken modal | Form met alle velden, validatie, opslaan werkt | ⏳ | E1.S2 | 5 |
| E2.S2 | Afspraak bewerken | Click op event → edit modal, wijzigingen opslaan | ⏳ | E2.S1 | 3 |
| E2.S3 | Afspraak verzetten (drag-drop) | Drag event naar nieuwe tijd, confirm dialog | ⏳ | E2.S2 | 3 |
| E2.S4 | Afspraak annuleren | Soft delete (status=cancelled), confirm dialog | ⏳ | E2.S2 | 2 |
| E2.S5 | Afspraak details view | Click voor volledige info, quick actions | ⏳ | E2.S2 | 2 |
Appointment Modal Fields:
| Veld | Type | Verplicht | Bron |
|---|---|---|---|
| Patiënt | Patient selector | Ja | patients tabel |
| Datum | Date picker | Ja | — |
| Starttijd | Time picker | Ja | — |
| Eindtijd | Time picker | Nee | — |
| Type | Select | Ja | encounter types |
| Locatie | Select | Nee | class_code |
| Behandelaar | Select | Ja | practitioners |
| Notities | Textarea | Nee | — |
Appointment Types (type_code):
intake- Intakegesprekbehandeling- Behandelsessiefollow-up- Vervolggesprektelefonisch- Telefonisch contacthuisbezoek- Huisbezoekonline- Online consultcrisis- Crisiscontactoverig- Overig
Status Flow:
planned → in-progress → completed
↓
cancelled
Epic 3 — Patiënt Integratie
Epic Doel: Naadloze patiëntselectie bij het maken van afspraken.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E3.S1 | Patient search component | Zoeken op naam, BSN, clientnummer | ⏳ | E2.S1 | 3 |
| E3.S2 | Recent patients dropdown | Laatste 5 patiënten snel selecteren | ⏳ | E3.S1 | 2 |
| E3.S3 | Quick patient info | Naam, geboortedatum, actieve intake tonen | ⏳ | E3.S2 | 2 |
Patient Selector Component:
interface PatientSelectorProps {
value?: string; // patient_id
onChange: (patientId: string) => void;
showRecentPatients?: boolean;
allowCreate?: boolean; // Future: quick create
}
Epic 4 — EPD Koppeling
Epic Doel: Bidirectionele koppeling tussen afspraken en verslagen.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E4.S1 | Verslag maken vanuit afspraak | Button op appointment → report composer met encounter_id | ⏳ | E2.S5 | 3 |
| E4.S2 | Afspraak koppelen vanuit verslag | In rapportage: link naar bestaande/nieuwe afspraak | ⏳ | E4.S1 | 3 |
| E4.S3 | Gekoppelde items tonen | In afspraak details: linked reports zichtbaar | ⏳ | E4.S2 | 2 |
| E4.S4 | Navigatie tussen afspraak ↔ verslag | Click-through links beide kanten | ⏳ | E4.S3 | 2 |
Integration Points:
┌─────────────────┐ ┌─────────────────┐
│ Agenda │ │ Rapportage │
│ │ │ │
│ [Afspraak] │───────────────▶│ [Verslag] │
│ └─ + Verslag │ │ └─ encounter │
│ │◀───────────────│ └─ + Koppel │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────┐
│ encounters │
│ id, patient_id, period_start, period_end, ... │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ reports │
│ id, patient_id, encounter_id, intake_id, ... │
└─────────────────────────────────────────────────────┘
Report Composer Updates:
- Nieuwe prop:
encounterId?: string - Pre-fill patient_id vanuit encounter
- Toon encounter info (datum, type) in composer
Epic 5 — Polish & Testing
Epic Doel: UX verfijning en edge case handling.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E5.S1 | Loading states & skeletons | Alle async operaties hebben loading feedback | ⏳ | E4.S4 | 2 |
| E5.S2 | Error handling | User-friendly errors, retry mogelijkheden | ⏳ | E5.S1 | 2 |
| E5.S3 | Responsive design | Mobile-friendly, touch gestures | ⏳ | E5.S2 | 3 |
5. Kwaliteit & Testplan
Test Types
| Test Type | Scope | Tools |
|---|---|---|
| Unit Tests | Date utilities, validators | Vitest |
| Integration | API endpoints, database | Playwright |
| Smoke Tests | Kritieke flows | Manual |
| Visual | UI consistency | Manual |
Manual Test Checklist
Afspraak Flow:
- Nieuwe afspraak aanmaken met alle velden
- Afspraak bewerken (tijd, type, notities)
- Afspraak verzetten via drag-drop
- Afspraak annuleren met bevestiging
- Patiënt zoeken en selecteren
Calendar Views:
- Dag view toont correcte uren
- Week view toont 7 dagen
- Werkdagen view filtert weekend
- Navigatie (vorige/volgende) werkt
- Vandaag button springt naar huidige dag
EPD Koppeling:
- Verslag maken vanuit afspraak
- Afspraak koppelen vanuit verslag
- Gekoppelde items zijn zichtbaar
- Navigatie werkt beide kanten
6. Data Model
Encounters (uitgebreid)
interface Encounter {
id: string;
identifier: string;
status: 'planned' | 'in-progress' | 'on-hold' | 'completed' | 'cancelled';
class_code: string; // AMB, VR, HH (home health)
class_display: string;
type_code: string; // intake, behandeling, follow-up, etc.
type_display: string;
patient_id: string;
practitioner_id: string;
organization_id?: string;
period_start: string; // ISO datetime
period_end?: string;
notes?: string;
intake_id?: string;
created_at: string;
updated_at: string;
}
Reports (uitgebreid)
interface Report {
id: string;
patient_id: string;
created_by?: string;
type: 'behandeladvies' | 'vrije_notitie' | 'intake' | 'voortgang' | 'crisis' | 'contact';
content: string;
encounter_id?: string; // NEW: koppeling met afspraak
intake_id?: string; // NEW: koppeling met intake
// ... existing fields
}
Calendar Event (voor FullCalendar)
interface CalendarEvent {
id: string;
title: string; // Patient naam
start: Date;
end?: Date;
extendedProps: {
encounter: Encounter;
patient: Patient;
linkedReports: Report[];
};
backgroundColor?: string; // Based on type
borderColor?: string;
}
7. API Endpoints
Nieuwe Endpoints
| Methode | Endpoint | Beschrijving |
|---|---|---|
| GET | /api/agenda/encounters |
Haal afspraken op (met filters) |
| POST | /api/agenda/encounters |
Nieuwe afspraak aanmaken |
| PUT | /api/agenda/encounters/[id] |
Afspraak bewerken |
| PATCH | /api/agenda/encounters/[id]/cancel |
Afspraak annuleren |
| GET | /api/agenda/encounters/[id]/reports |
Gekoppelde verslagen |
Query Parameters (GET encounters)
interface EncounterFilters {
start_date: string; // ISO date
end_date: string;
practitioner_id?: string;
patient_id?: string;
status?: string[];
type_code?: string[];
}
8. Risico's & Mitigatie
| Risico | Kans | Impact | Mitigatie |
|---|---|---|---|
| FullCalendar styling conflicten | Middel | Middel | CSS isolation, custom theme |
| Performance bij veel afspraken | Laag | Hoog | Pagination, date range filtering |
| Timezone issues | Middel | Hoog | Altijd UTC opslaan, lokaal tonen |
| Drag-drop UX op mobile | Middel | Middel | Touch-friendly alternatives |
| Concurrent edits | Laag | Middel | Optimistic locking, conflict resolution |
9. UI/UX Specificaties
Agenda Layout
┌────────────────────────────────────────────────────────────────┐
│ EPD Header [User] │
├────────┬───────────────────────────────────────────────────────┤
│ │ Agenda [+ Nieuwe Afspraak] │
│ Nav │───────────────────────────────────────────────────────│
│ │ [< Vorige] [Vandaag] [Volgende >] [Dag|Week|Werk] │
│ │───────────────────────────────────────────────────────│
│ │ ┌──────────┐ │
│ │ │ December │ ┌─────────────────────────────────────┐│
│ │ │ 2024 │ │ Ma │ Di │ Wo │ Do │ Vr ││
│ │ │ [Cal] │ │──────┼──────┼──────┼──────┼────────││
│ │ └──────────┘ │ 08:00│ │ │ │ ││
│ │ │──────│ [Pnt]│ │ │ ││
│ │ Filters: │ 09:00│ │ [Int]│ │ ││
│ │ □ Intakes │──────│ │ │ │ ││
│ │ □ Behandeling │ 10:00│ │ │ [Beh]│ ││
│ │ □ Telefonisch │──────│ │ │ │ ││
│ │ │ ... │ │ │ │ ││
│ │ └─────────────────────────────────────┘│
└────────┴───────────────────────────────────────────────────────┘
Afspraak Card (in kalender)
┌─────────────────────┐
│ 09:00 - 10:00 │
│ ● Jan de Vries │
│ Intakegesprek │
│ [📝] [✏️] │
└─────────────────────┘
Afspraak Modal
┌─────────────────────────────────────────┐
│ Nieuwe Afspraak [X] │
├─────────────────────────────────────────┤
│ │
│ Patiënt * │
│ ┌─────────────────────────────────────┐ │
│ │ 🔍 Zoek patiënt... │ │
│ └─────────────────────────────────────┘ │
│ │
│ Datum * Tijd * │
│ ┌──────────────┐ ┌──────┐ - ┌──────┐│
│ │ 02-12-2024 │ │09:00 │ │10:00 ││
│ └──────────────┘ └──────┘ └──────┘│
│ │
│ Type afspraak * │
│ ┌─────────────────────────────────────┐ │
│ │ Intakegesprek ▼ │ │
│ └─────────────────────────────────────┘ │
│ │
│ Locatie │
│ ┌─────────────────────────────────────┐ │
│ │ Praktijk ▼ │ │
│ └─────────────────────────────────────┘ │
│ │
│ Notities │
│ ┌─────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────┘ │
│ │
│ [Annuleren] [Opslaan] │
└─────────────────────────────────────────┘
10. Referenties
Interne Documenten:
- FO Mini-EPD v1.2
- TO Mini-EPD v1.2
- Screening-Intake Bouwplan
Libraries:
Database:
encounterstabel (FHIR Encounter)reportstabelpatientstabelpractitionerstabel
11. Glossary
| Term | Betekenis |
|---|---|
| Encounter | FHIR term voor een contact/afspraak moment |
| period_start | Starttijd van een afspraak (ISO datetime) |
| class_code | Type locatie (AMB=ambulant, VR=virtueel, HH=thuis) |
| type_code | Type afspraak (intake, behandeling, etc.) |
| Bidirectioneel | Koppeling werkt beide kanten op |
Versiehistorie:
| Versie | Datum | Auteur | Wijziging |
|---|---|---|---|
| v1.0 | 02-12-2024 | Colin | Initiële versie |