feat(agenda): Epic 0 - Database schema for agenda module

- 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>
This commit is contained in:
colinislit
2025-12-02 16:44:31 +01:00
parent aacada2197
commit 43c7cb898b
8 changed files with 4371 additions and 37 deletions

View File

@@ -0,0 +1,506 @@
# 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 | ⏳ To Do | 3 |
| E1 | Calendar Views | Dag/week/werkdagen weergaven | ⏳ To Do | 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:**
```sql
-- 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:**
```bash
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:**
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
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:**
- [FullCalendar React](https://fullcalendar.io/docs/react)
- [shadcn/ui Calendar](https://ui.shadcn.com/docs/components/calendar)
- [date-fns](https://date-fns.org/)
**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 |