Files
triqura-ecd/docs/specs/agenda/bouwplan-agenda-module-v1.0.md
colinislit e661c97491 feat(agenda): Epic 1 - Calendar views with FullCalendar
- 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>
2025-12-02 17:13:55 +01:00

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.ts per 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 - Intakegesprek
  • behandeling - Behandelsessie
  • follow-up - Vervolggesprek
  • telefonisch - Telefonisch contact
  • huisbezoek - Huisbezoek
  • online - Online consult
  • crisis - Crisiscontact
  • overig - 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:

  • encounters tabel (FHIR Encounter)
  • reports tabel
  • patients tabel
  • practitioners tabel

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