Epic 3 compleet: Backend integratie voor Swift Agenda Planning. Nieuwe endpoints: - GET /api/swift/agenda - Query afspraken op datumrange - POST /api/swift/agenda/create - Nieuwe afspraak aanmaken - POST /api/swift/agenda/cancel - Afspraak annuleren - POST /api/swift/agenda/reschedule - Afspraak verzetten - GET /api/swift/patients/search - Fuzzy patiënt zoeken Alle endpoints bevatten: - Supabase authenticatie + resource ownership checks - Zod validatie met Nederlandse foutmeldingen - Hergebruik van bestaande agenda server actions Test documentatie: docs/swift/test-plan-epic3-backend.md Progress: 3/7 Epics compleet (E0, E1, E2, E3) Story points: 11 SP (E3.S1: 3, E3.S2: 3, E3.S3: 3, E3.S4: 2)
758 lines
32 KiB
Markdown
758 lines
32 KiB
Markdown
# Mission Control - Bouwplan
|
||
|
||
Projectnaam: Swift Agenda Planning Module
|
||
Versie: v1.0
|
||
Datum: 27-12-2025
|
||
Auteur: Colin Lit
|
||
|
||
---
|
||
|
||
## 1. Doel en context
|
||
Doel: een Swift Agenda Planning module bouwen waarmee gebruikers via chat afspraken kunnen opvragen, aanmaken, annuleren en verzetten, met een AgendaBlock artifact als visuele bevestiging.
|
||
Toelichting: dit bouwt voort op het Swift conversatie‑model en hergebruikt de klassieke agenda (`/app/epd/agenda`) als full view. De Swift‑variant focust op snelle queries en quick actions.
|
||
|
||
---
|
||
|
||
## 2. Uitgangspunten
|
||
|
||
### 2.1 Technische Stack
|
||
- Frontend: Next.js App Router + React + TypeScript
|
||
- Styling: Tailwind CSS + shadcn/ui components
|
||
- State: Zustand (Swift store) + React Query for data caching
|
||
- Backend: Next.js route handlers + Supabase (encounters table)
|
||
- AI/Intent: Two-tier system (Local regex <50ms + Claude Haiku fallback ~400ms)
|
||
- Date/Time: date-fns library (al aanwezig)
|
||
- Auth: Supabase Auth (server-side guard via `lib/auth/server.ts`)
|
||
- Voice: Deepgram (existing integration voor voice input)
|
||
|
||
### 2.2 Projectkaders
|
||
- Scope (MVP): agenda_query, create_appointment, cancel_appointment, reschedule_appointment + AgendaBlock.
|
||
- Out of scope: drag-and-drop, recurring, availability, conflict resolution UI, multi-practitioner.
|
||
- Geen nieuwe dependencies zonder akkoord.
|
||
- Geen database migraties in MVP.
|
||
- Reuse bestaande agenda actions waar mogelijk.
|
||
|
||
### 2.3 Programmeer uitgangspunten
|
||
- DRY: hergebruik `app/epd/agenda/actions.ts` (getEncounters, createEncounter, cancelEncounter, rescheduleEncounter).
|
||
- KISS: snelle API routes + simpele AgendaBlock UI.
|
||
- SOC: intent parsing, data fetching en UI gescheiden.
|
||
- YAGNI: alleen P1/P2 uit FO, geen extra planner features.
|
||
|
||
### 2.4 Performance Targets
|
||
| Metric | Target | Actual (Expected) |
|
||
|--------|--------|-------------------|
|
||
| Local pattern match | <50ms | ~10-15ms |
|
||
| Local + entity extraction | <100ms | ~30-50ms |
|
||
| AI fallback (cold) | <800ms | ~400-600ms |
|
||
| AI fallback (warm) | <500ms | ~200-400ms |
|
||
| Total (local path) | <150ms | ~50-80ms |
|
||
| Total (AI path) | <1000ms | ~500-700ms |
|
||
|
||
### 2.5 Confidence Thresholds
|
||
| Confidence | Actie | Voorbeeld |
|
||
|------------|-------|-----------|
|
||
| **>0.9** | Direct artifact openen met prefill | "afspraken vandaag" |
|
||
| **0.7-0.9** | Artifact + bevestigingsvraag | "maak afspraak jan morgen" (tijd ontbreekt) |
|
||
| **0.5-0.7** | Verduidelijkingsvraag in chat | "maak afspraak" (patient/tijd ontbreekt) |
|
||
| **<0.5** | Fallback: "Ik begrijp het niet" | Gibberish input |
|
||
|
||
---
|
||
|
||
## 3. Epics & Stories Overzicht
|
||
| Epic ID | Titel | Doel | Status | Stories | Opmerkingen |
|
||
|---------|-------|------|--------|---------|-------------|
|
||
| E0 | Alignment & scope | MVP afbakenen en keuzes vastleggen | Done | 2 | FO‑based |
|
||
| E1 | Intent & entity layer | Agenda intents + entities toevoegen | Done | 4 | Swift intent stack |
|
||
| E2 | Date/time parsing | NLP‑helpers voor datum/tijd | Done | 3 | Geen nieuwe deps |
|
||
| E3 | Backend integratie | Agenda data APIs + reuse actions | Done | 4 | Auth vereist |
|
||
| E4 | AgendaBlock UI | List/create/cancel/reschedule views | To Do | 5 | Swift artifact |
|
||
| E5 | Chat orchestration | Action routing + prompt update | To Do | 3 | Swift chat API |
|
||
| E6 | QA & docs | Testplan + docs update | To Do | 3 | Manual QA |
|
||
|
||
---
|
||
|
||
## 4. Epics & Stories (Uitwerking)
|
||
|
||
### Epic 0 — Alignment & scope
|
||
Epic doel: MVP scope, UX flows en beslissingen vastleggen.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E0.S1 | Scope + out-of-scope vastleggen | P1/P2 lijst bevestigd, OOS lijst bevestigd | Done | - | 1 |
|
||
| E0.S2 | UX flow beschrijven | Entry/exit, artifact gedrag en fallback flows gedocumenteerd | Done | E0.S1 | 2 |
|
||
|
||
UX flow (MVP):
|
||
- Entry: user is in Swift chat; agenda intents open AgendaBlock when confidence >= 0.7.
|
||
- Exit: close artifact via tab close; for full view use link to `/epd/agenda` from the AgendaBlock footer.
|
||
- Queries: default to today when clear; ask a clarification question when date range is missing or ambiguous.
|
||
- Create: require patient + date + time; use active patient when user says "deze patient"; otherwise ask in chat.
|
||
- Cancel/reschedule: if multiple matches, show disambiguation list in AgendaBlock; confirm before final action.
|
||
- Fallback: if intent confidence < 0.7, keep artifact closed and ask for missing details.
|
||
|
||
---
|
||
|
||
### Epic 1 — Intent & entity layer
|
||
Epic doel: agenda intent types en entities toevoegen aan Swift.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E1.S1 | SwiftIntent uitbreiden | Agenda intent names toegevoegd in types/store | Done | E0.S1 | 2 |
|
||
| E1.S2 | Local intent patterns | Regex patterns voor agenda intents in `intent-classifier.ts` | Done | E1.S1 | 3 |
|
||
| E1.S3 | AI fallback prompt | Prompt in `intent-classifier-ai.ts` uitgebreid met agenda intents | Done | E1.S1 | 3 |
|
||
| E1.S4 | Entities schema | `ExtractedEntities` uitgebreid met date/time/identifier | Done | E1.S1 | 2 |
|
||
|
||
**Technical notes:**
|
||
|
||
**E1.S1 - SwiftIntent type uitbreiding:**
|
||
- File: `lib/swift/types.ts`
|
||
- Houd `SwiftIntent` single source of truth (voorkom duplicatie).
|
||
- SwiftIntent (na uitbreiding):
|
||
```ts
|
||
type SwiftIntent =
|
||
| 'dagnotitie'
|
||
| 'zoeken'
|
||
| 'overdracht'
|
||
| 'agenda_query' // 🆕
|
||
| 'create_appointment' // 🆕
|
||
| 'cancel_appointment' // 🆕
|
||
| 'reschedule_appointment'// 🆕
|
||
| 'unknown';
|
||
```
|
||
|
||
**E1.S2 - Local pattern examples:**
|
||
- File: `lib/swift/intent-classifier.ts`
|
||
- Toevoegen aan `INTENT_PATTERNS`:
|
||
```ts
|
||
agenda_query: [
|
||
{ pattern: /^afspraken?\\b/i, weight: 1.0 },
|
||
{ pattern: /^agenda\\b/i, weight: 1.0 },
|
||
{ pattern: /^(wat|wanneer)\\s+is\\s+(mijn\\s+)?volgende\\s+afspraak/i, weight: 1.0 },
|
||
],
|
||
create_appointment: [
|
||
{ pattern: /^maak\\s+afspraak\\b/i, weight: 1.0 },
|
||
{ pattern: /^plan\\s+(een\\s+)?(intake|afspraak)\\b/i, weight: 1.0 },
|
||
],
|
||
cancel_appointment: [
|
||
{ pattern: /^annuleer\\s+(de\\s+)?afspraak/i, weight: 1.0 },
|
||
],
|
||
reschedule_appointment: [
|
||
{ pattern: /^verzet\\s+(de\\s+)?afspraak/i, weight: 1.0 },
|
||
{ pattern: /^\\d{1,2}:\\d{2}\\s+naar\\s+\\d{1,2}:\\d{2}/i, weight: 0.9 },
|
||
],
|
||
```
|
||
|
||
**E1.S3 - AI System Prompt addition:**
|
||
- File: `lib/swift/intent-classifier-ai.ts`
|
||
- Toevoegen aan `INTENT_CLASSIFIER_SYSTEM_PROMPT`:
|
||
- Agenda intent descriptions + voorbeelden
|
||
- Entity extraction instructies (patientName, dateRange, datetime, type, identifier, newDatetime)
|
||
- Response format met nieuwe entities
|
||
|
||
**E1.S4 - ExtractedEntities uitbreiding:**
|
||
- File: `lib/swift/types.ts`
|
||
- Toevoegen:
|
||
```ts
|
||
interface ExtractedEntities {
|
||
// Bestaande entities
|
||
patientName?: string;
|
||
category?: VerpleegkundigCategory;
|
||
content?: string;
|
||
|
||
// 🆕 Agenda entities
|
||
dateRange?: {
|
||
start: Date;
|
||
end: Date;
|
||
label: 'vandaag' | 'morgen' | 'deze week' | 'volgende week' | 'custom';
|
||
};
|
||
datetime?: {
|
||
date: Date;
|
||
time: string; // "HH:mm" format
|
||
};
|
||
appointmentType?: 'intake' | 'behandeling' | 'follow-up' | 'telefonisch' |
|
||
'huisbezoek' | 'online' | 'crisis' | 'overig';
|
||
location?: 'praktijk' | 'online' | 'thuis';
|
||
identifier?: {
|
||
type: 'patient' | 'time' | 'both';
|
||
patientName?: string;
|
||
patientId?: string;
|
||
time?: string;
|
||
date?: Date;
|
||
encounterId?: string;
|
||
};
|
||
newDatetime?: {
|
||
date: Date;
|
||
time: string;
|
||
};
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Epic 2 — Date/time parsing
|
||
Epic doel: datum/tijd interpretatie uit natuurlijke taal.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E2.S1 | Date parser utility | `lib/swift/date-time-parser.ts` met relatieve datums | Done | E1.S4 | 3 |
|
||
| E2.S2 | Time parser utility | Tijd normalisatie (14:00, half drie) | Done | E2.S1 | 2 |
|
||
| E2.S3 | Entity extraction hook | Entity extractor gebruikt parser output | Done | E2.S2 | 2 |
|
||
|
||
**Technical notes:**
|
||
|
||
**E2.S1 - Date parser implementation:**
|
||
- File: `lib/swift/date-time-parser.ts`
|
||
- Functie: `parseRelativeDate(input: string): Date | DateRange | null`
|
||
- Ondersteunt:
|
||
- "vandaag", "morgen", "overmorgen"
|
||
- "maandag", "dinsdag", ... (next weekday)
|
||
- "deze week", "volgende week" (returns DateRange)
|
||
- Fallback naar date-fns `parse()` voor "30 december", "28-12-2024"
|
||
- Gebruik `date-fns` helpers: `addDays()`, `startOfWeek()`, `endOfWeek()`, `addWeeks()`
|
||
|
||
**E2.S2 - Time parser implementation:**
|
||
- Functie: `parseTime(input: string): string | null`
|
||
- Ondersteunt:
|
||
- "14:00" → "14:00"
|
||
- "14" → "14:00"
|
||
- "twee uur" → "14:00"
|
||
- "half drie" → "14:30"
|
||
- "kwart voor drie" → "14:45"
|
||
- Returns `HH:mm` format string
|
||
|
||
**E2.S3 - Integration:**
|
||
- Parser functies worden aangeroepen in entity extraction flow
|
||
- Local pattern matching extraheert ruwe strings ("morgen", "14:00")
|
||
- Parser functies normaliseren naar Date/time formats
|
||
- AI fallback gebruikt parser voor validation
|
||
|
||
---
|
||
|
||
### Epic 3 — Backend integratie
|
||
Epic doel: agenda data ontsluiten voor Swift blocks.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E3.S1 | Agenda query API | Endpoint voor afspraken op datumrange (auth) | Done | E1.S4 | 3 |
|
||
| E3.S2 | Create appointment API | Endpoint die `createEncounter` aanroept | Done | E3.S1 | 3 |
|
||
| E3.S3 | Cancel/reschedule API | Endpoints die `cancelEncounter`/`rescheduleEncounter` aanroepen | Done | E3.S1 | 3 |
|
||
| E3.S4 | Patient match API | Fuzzy patiënt matching + disambiguation lijst | Done | E1.S4 | 2 |
|
||
|
||
**Technical notes:**
|
||
|
||
**E3.S1 - Agenda query endpoint:**
|
||
- Route: `GET /api/swift/agenda?start=2024-12-27&end=2024-12-27`
|
||
- Auth: via `createClient()` from `lib/auth/server.ts`
|
||
- Hergebruik: `getEncounters()` from `app/epd/agenda/actions.ts`
|
||
- Response: `{ appointments: Encounter[] }`
|
||
- Filters: practitioner_id = current user, status = 'planned', period_start in range
|
||
- Order: chronological (period_start ASC)
|
||
|
||
**E3.S2 - Create appointment endpoint:**
|
||
- Route: `POST /api/swift/agenda/create`
|
||
- Body: `{ patientId, datetime: { date, time }, type, location, notes? }`
|
||
- Validation: Zod schema, Dutch error messages
|
||
- Hergebruik: `createEncounter()` from `app/epd/agenda/actions.ts`
|
||
- Mapping:
|
||
- type → typeCode (FHIR encounter type)
|
||
- location → classCode ('AMB' = praktijk, 'VR' = online, 'HH' = thuis)
|
||
- datetime → periodStart/periodEnd (1 hour duration default)
|
||
- Response: `{ encounterId, success: true }`
|
||
|
||
**E3.S3 - Cancel/Reschedule endpoints:**
|
||
- Route: `POST /api/swift/agenda/cancel`
|
||
- Body: `{ encounterId }`
|
||
- Hergebruik: `cancelEncounter(encounterId)` (soft delete: status → 'cancelled')
|
||
|
||
- Route: `POST /api/swift/agenda/reschedule`
|
||
- Body: `{ encounterId, newDatetime: { date, time } }`
|
||
- Hergebruik: `rescheduleEncounter(encounterId, periodStart, periodEnd)`
|
||
|
||
**E3.S4 - Patient search endpoint:**
|
||
- Route: `GET /api/swift/patients/search?q=jan`
|
||
- Fuzzy match op patient name (ILIKE %query%)
|
||
- Response: `{ patients: Array<{ id, name, bsn }> }`
|
||
- Limit: 10 resultaten
|
||
- Used voor: disambiguation bij meerdere matches
|
||
|
||
---
|
||
|
||
### Epic 4 — AgendaBlock UI
|
||
Epic doel: Swift artifact voor agenda flows.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E4.S1 | AgendaBlock skeleton | Block met tabs/modes (list/create/cancel/reschedule) | To Do | E3.S1 | 3 |
|
||
| E4.S2 | List view | Lijst met afspraken + empty state | To Do | E4.S1 | 3 |
|
||
| E4.S3 | Create form | Prefill + validatie + submit | To Do | E4.S1 | 5 |
|
||
| E4.S4 | Cancel view | Disambiguation + confirm flow | To Do | E4.S1 | 3 |
|
||
| E4.S5 | Reschedule view | Edit form met nieuwe tijd | To Do | E4.S1 | 3 |
|
||
|
||
**Technical notes:**
|
||
|
||
**E4.S1 - AgendaBlock component structure:**
|
||
- File: `components/swift/artifacts/blocks/agenda-block.tsx`
|
||
- Props interface:
|
||
```ts
|
||
interface AgendaBlockProps {
|
||
mode: 'list' | 'create' | 'cancel' | 'reschedule';
|
||
appointments?: Encounter[];
|
||
dateRange?: { start: Date; end: Date; label: string };
|
||
prefillData?: {
|
||
patient?: { id: string; name: string };
|
||
datetime?: { date: Date; time: string };
|
||
type?: EncounterType;
|
||
location?: EncounterLocation;
|
||
};
|
||
disambiguationOptions?: Encounter[];
|
||
onClose?: () => void;
|
||
}
|
||
```
|
||
- Architecture: switch statement op mode → render juiste sub-component
|
||
- Styling: max-width 600px, max-height 80vh, overflow-y auto
|
||
|
||
**E4.S2 - List view:**
|
||
- Component: `<AgendaListView />`
|
||
- Layout:
|
||
- Header: Datum label (e.g., "Afspraken Vandaag - 27 december") + close button
|
||
- Body: Scrollable lijst van appointment cards
|
||
- Footer: Link naar `/epd/agenda` ("📅 Open volledige agenda →")
|
||
- Appointment card bevat:
|
||
- Tijd (09:00 - 10:00)
|
||
- Patient naam (klikbaar → opens PatientContextCard)
|
||
- Type badge met color coding (intake=blauw, behandeling=groen, crisis=rood)
|
||
- Locatie icon (📍 Praktijk, 🌐 Online, 🏠 Thuis)
|
||
- Actions: [Details] [Annuleren]
|
||
- Empty state: "📭 Geen afspraken gevonden" + [Maak nieuwe afspraak] button
|
||
|
||
**E4.S3 - Create form:**
|
||
- Component: `<AgendaCreateForm />`
|
||
- Fields:
|
||
- Patiënt* (autocomplete, pre-filled)
|
||
- Datum* (date picker, pre-filled)
|
||
- Tijd* (time picker 07:00-20:00, pre-filled)
|
||
- Type* (radio buttons: Intake, Behandeling, Vervolg, Telefonisch, Huisbezoek, Crisis)
|
||
- Locatie* (radio buttons: Praktijk, Online, Thuis)
|
||
- Notities (textarea, optioneel, max 500 chars)
|
||
- Validation:
|
||
- Patient required
|
||
- Date cannot be in past
|
||
- Time within 07:00-20:00
|
||
- Conflict warning: "⚠️ Conflict: Je hebt al een afspraak om 14:00 met Marie" (optional feature)
|
||
- Submit: [Annuleren] [✓ Afspraak maken]
|
||
- States: initial → validating → submitting → success/error
|
||
|
||
**E4.S4 - Cancel view:**
|
||
- Component: `<AgendaCancelView />`
|
||
- Submode 1: Disambiguation (multiple matches)
|
||
- Radio list met opties: "○ 28-12-2024 09:00 - Jan de Vries (Intake)"
|
||
- [Annuleren] [Volgende →] buttons
|
||
- Submode 2: Confirmation (single match or after disambiguation)
|
||
- Appointment details card
|
||
- Warning: "⚠️ Deze actie kan niet ongedaan worden gemaakt."
|
||
- [Terug] [✓ Annuleren] buttons
|
||
|
||
**E4.S5 - Reschedule view:**
|
||
- Component: `<AgendaRescheduleForm />`
|
||
- Layout:
|
||
- Afspraak details (readonly)
|
||
- Huidige datum/tijd (readonly, strikethrough styling)
|
||
- Nieuwe datum/tijd* (editable date + time pickers, pre-filled)
|
||
- Conflict check: "✅ Geen conflicten gevonden" or warning
|
||
- [Annuleren] [✓ Verzetten] buttons
|
||
|
||
**Styling specs:**
|
||
- Colors: Teal-700 primary, appointment type badges (zie FO sectie 13.3)
|
||
- Animations: slide-in from right 200ms, fade-out on close
|
||
- Typography: text-sm voor body, text-base voor headers
|
||
- Spacing: 16px padding, 12px card gaps
|
||
|
||
---
|
||
|
||
### Epic 5 — Chat orchestration
|
||
Epic doel: agenda intents laten landen in juiste artifact.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E5.S1 | Action routing | Agenda intents openen AgendaBlock met juiste mode | To Do | E4.S1 | 2 |
|
||
| E5.S2 | Chat prompt update | `/api/swift/chat` prompt bevat agenda sectie + action format | To Do | E1.S3 | 2 |
|
||
| E5.S3 | Error states | User-friendly errors + link naar `/epd/agenda` | To Do | E3.S1 | 2 |
|
||
|
||
**Technical notes:**
|
||
|
||
**E5.S1 - Intent router implementation:**
|
||
- Location: Swift store of artifact manager
|
||
- Functie: `openArtifactForIntent(intent, entities, confidence)`
|
||
- Logic:
|
||
```ts
|
||
if (confidence < 0.7) return null; // Trigger verduidelijkingsvraag
|
||
|
||
switch (intent) {
|
||
case 'agenda_query':
|
||
return { type: 'AgendaBlock', mode: 'list', prefill: { dateRange } };
|
||
case 'create_appointment':
|
||
if (!entities.patientName) return null; // Need patient
|
||
return { type: 'AgendaBlock', mode: 'create', prefill: { patient, datetime, type, location } };
|
||
case 'cancel_appointment':
|
||
return { type: 'AgendaBlock', mode: 'cancel', prefill: { identifier } };
|
||
case 'reschedule_appointment':
|
||
if (!entities.identifier) return null; // Need to know which appointment
|
||
return { type: 'AgendaBlock', mode: 'reschedule', prefill: { identifier, newDatetime } };
|
||
}
|
||
```
|
||
|
||
**E5.S2 - Chat API system prompt addition:**
|
||
- File: `app/api/swift/chat/route.ts`
|
||
- Toevoegen aan system prompt:
|
||
```markdown
|
||
## Agenda & Afspraken Beheer
|
||
|
||
Je helpt gebruikers met agenda-gerelateerde taken.
|
||
|
||
### Intents die je herkent:
|
||
1. **agenda_query** - Afspraken opvragen
|
||
Voorbeelden: "afspraken vandaag", "wat is volgende", "agenda morgen"
|
||
Actie: Toon lijst van afspraken in AgendaBlock
|
||
|
||
2. **create_appointment** - Nieuwe afspraak maken
|
||
Voorbeelden: "maak afspraak met Jan morgen 14:00", "plan intake"
|
||
Required: patient, date, time
|
||
Optional: type (default: behandeling), location (default: praktijk)
|
||
|
||
3. **cancel_appointment** - Afspraak annuleren
|
||
Voorbeelden: "annuleer afspraak Jan", "cancel 14:00"
|
||
Actie: Toon confirmation dialog
|
||
|
||
4. **reschedule_appointment** - Afspraak verzetten
|
||
Voorbeelden: "verzet 14:00 naar 15:00", "verzet Jan naar dinsdag"
|
||
Actie: Toon edit form met oude + nieuwe tijd
|
||
|
||
### Entity Extraction:
|
||
- Patient: extraheer volledige naam, gebruik activePatient als user zegt "deze patiënt"
|
||
- Date/Time: parse relatieve datums ("morgen", "dinsdag"), validate geen verleden
|
||
- Type: intake, behandeling, follow-up, telefonisch (default: behandeling)
|
||
- Location: praktijk, online, thuis (default: praktijk)
|
||
|
||
### Verduidelijkingsvragen:
|
||
Stel vragen bij:
|
||
- Meerdere patiënten met zelfde naam
|
||
- Onduidelijke datum
|
||
- Ontbrekende tijd bij create
|
||
- Meerdere matches bij cancel/reschedule
|
||
```
|
||
|
||
**E5.S3 - Error handling:**
|
||
- Server errors: "Er ging iets mis bij het ophalen van je afspraken. Probeer het opnieuw of [open de volledige agenda](/epd/agenda)."
|
||
- Auth errors: Redirect naar `/login`
|
||
- Validation errors: Dutch messages ("Datum kan niet in het verleden liggen")
|
||
- Network errors: Retry logic + user-friendly message
|
||
|
||
---
|
||
|
||
### Epic 6 — QA & docs
|
||
Epic doel: kwaliteit borgen en documentatie updaten.
|
||
|
||
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|
||
|----------|--------------|---------------------|--------|------------------|--------------|
|
||
| E6.S1 | Manual test checklist | Scenarios uit FO opgenomen | To Do | E5.S3 | 2 |
|
||
| E6.S2 | Docs update | Bouwplan + release note bijgewerkt | To Do | E6.S1 | 1 |
|
||
| E6.S3 | Regression checks | Swift en klassieke agenda blijven werken | To Do | E6.S1 | 2 |
|
||
|
||
---
|
||
|
||
## 5. Kwaliteit & Testplan
|
||
|
||
Test types:
|
||
| Test Type | Scope | Tools | Verantwoordelijke |
|
||
|-----------|-------|-------|-------------------|
|
||
| Lint | App code | pnpm lint | Developer |
|
||
| Type check | TypeScript | pnpm build | Developer |
|
||
| Smoke tests | Agenda intents + artifact flows | Manual checklist (20 scenarios) | Developer/UX |
|
||
| Regression | /epd/agenda klassiek | Manual checklist | Developer |
|
||
|
||
### Manual Test Checklist (uit FO, 20 scenarios):
|
||
|
||
| # | Scenario | Expected Result | Priority | Status |
|
||
|---|----------|-----------------|----------|--------|
|
||
| 1 | "afspraken vandaag" | AgendaBlock list view, toont vandaag's afspraken | P1 | ⬜ |
|
||
| 2 | "wat is volgende afspraak" | Chat response met eerstvolgende, AgendaBlock toont details | P1 | ⬜ |
|
||
| 3 | "maak afspraak jan morgen 14:00" | Create form met prefill, patient gevonden | P1 | ⬜ |
|
||
| 4 | Voice: "maak intake marie vrijdag 10:00" | Transcript correct, create form opent | P2 | ⬜ |
|
||
| 5 | "maak afspraak" (incomplete) | Verduidelijkingsvraag: "Met welke patiënt?" | P1 | ⬜ |
|
||
| 6 | "maak afspraak onbekende patient" | Patient search fallback, suggesties | P2 | ⬜ |
|
||
| 7 | Create form submit → success | Toast, chat confirmation, artifact sluit | P1 | ⬜ |
|
||
| 8 | "annuleer afspraak jan" (1 match) | Confirmation dialog, direct match | P1 | ⬜ |
|
||
| 9 | "annuleer afspraak jan" (3 matches) | Disambiguation radio list | P1 | ⬜ |
|
||
| 10 | Cancel confirm → success | Afspraak status=cancelled, toast | P1 | ⬜ |
|
||
| 11 | "verzet 14:00 naar 15:00" | Reschedule form, tijd pre-filled | P1 | ⬜ |
|
||
| 12 | Reschedule submit → success | Tijd updated, toast confirmation | P1 | ⬜ |
|
||
| 13 | Conflict warning (overlapping) | Warning banner in create form | P2 | ⬜ |
|
||
| 14 | Click patient name in list | PatientContextCard opent | P2 | ⬜ |
|
||
| 15 | Click "Details" button | Navigates to /epd/agenda with filter | P2 | ⬜ |
|
||
| 16 | Empty state (geen afspraken) | Placeholder met "Maak nieuwe afspraak" | P1 | ⬜ |
|
||
| 17 | Server error tijdens query | Error in chat, geen artifact | P1 | ⬜ |
|
||
| 18 | Date in past validation | "Kan geen afspraken in verleden maken" | P1 | ⬜ |
|
||
| 19 | Context-aware: "maak afspraak deze patient" | Gebruikt active patient uit context | P2 | ⬜ |
|
||
| 20 | Multiple artifacts (3 max) | Agenda + Dagnotitie + Context card, tabs | P2 | ⬜ |
|
||
|
||
### Regression Test Checklist:
|
||
|
||
| # | Test | Expected Result | Status |
|
||
|---|------|-----------------|--------|
|
||
| R1 | Klassieke agenda `/epd/agenda` werkt nog | Kan afspraken zien/maken/wijzigen | ⬜ |
|
||
| R2 | Swift dagnotitie intent ongewijzigd | "dagnotitie jan medicatie" opent DagnotatieBlock | ⬜ |
|
||
| R3 | Swift zoeken intent ongewijzigd | "zoek jan" opent ZoekenBlock | ⬜ |
|
||
| R4 | Swift overdracht intent ongewijzigd | "overdracht" opent OverdrachtBlock | ⬜ |
|
||
| R5 | Auth guard op alle endpoints | Geen auth → 401/redirect | ⬜ |
|
||
|
||
### Performance Test Targets:
|
||
|
||
| Metric | Target | Test Method |
|
||
|--------|--------|-------------|
|
||
| Local pattern match | <50ms | Browser DevTools Network tab |
|
||
| Create form open | <150ms total | Lighthouse/manual timing |
|
||
| AI fallback | <1000ms | DevTools, onduidelijke input |
|
||
| List view render (10 items) | <100ms | React DevTools Profiler |
|
||
|
||
---
|
||
|
||
## 6. User Stories (uit FO)
|
||
|
||
### Primaire User Stories (P1 - MVP):
|
||
|
||
| ID | Als | Wil ik | Zodat | Acceptatie |
|
||
|----|-----|--------|-------|------------|
|
||
| US-24 | Verpleegkundige | Snel overzicht afspraken vandaag | Ik weet waar ik moet zijn | "afspraken vandaag" → lijst in AgendaBlock, <3 sec |
|
||
| US-25 | Verpleegkundige | Check volgende afspraak tijdens werk | Ik op tijd ben | "wat is mijn volgende afspraak?" → directe info |
|
||
| US-27 | Verpleegkundige | Snelle afspraak tijdens telefoongesprek | Ik direct kan plannen | "maak afspraak jan morgen 14:00" → prefilled form, <15 sec |
|
||
| US-28 | Verpleegkundige | Context-aware planning | Ik niet steeds naam hoef te typen | "maak afspraak met deze patiënt" → gebruikt actieve patiënt |
|
||
| US-29 | Verpleegkundige | Voice input tijdens consult | Ik hands-free kan werken | Spraak → afspraak maken |
|
||
| US-30 | Verpleegkundige | Annuleren via chat | Ik snel kan annuleren | "annuleer afspraak jan" → confirmation → done |
|
||
| US-31 | Verpleegkundige | Snel verzetten | Ik afspraken flexibel kan aanpassen | "verzet 14:00 naar 15:00" → tijd update |
|
||
|
||
### Secundaire User Stories (P2 - Nice to Have):
|
||
|
||
| ID | Als | Wil ik | Zodat | Acceptatie |
|
||
|----|-----|--------|-------|------------|
|
||
| US-26 | Verpleegkundige | Weekoverzicht bekijken | Ik kan plannen | "agenda deze week" → gefilterde lijst |
|
||
| US-32 | Verpleegkundige | Disambiguation bij meerdere matches | Systeem helpt kiezen | Systeem vraagt "Welke Jan?" → lijst opties |
|
||
|
||
---
|
||
|
||
## 7. File Structuur (na implementatie)
|
||
|
||
```
|
||
lib/swift/
|
||
├── types.ts # SwiftIntent + ExtractedEntities uitbreidingen
|
||
├── intent-classifier.ts # Local patterns voor agenda intents
|
||
├── intent-classifier-ai.ts # AI prompt uitbreidingen
|
||
└── date-time-parser.ts # 🆕 Date/time parsing utilities
|
||
|
||
app/api/swift/
|
||
├── agenda/
|
||
│ ├── route.ts # 🆕 GET agenda query endpoint
|
||
│ ├── create/route.ts # 🆕 POST create appointment
|
||
│ ├── cancel/route.ts # 🆕 POST cancel appointment
|
||
│ └── reschedule/route.ts # 🆕 POST reschedule appointment
|
||
├── patients/
|
||
│ └── search/route.ts # 🆕 GET fuzzy patient search
|
||
└── chat/route.ts # Updated: agenda system prompt
|
||
|
||
components/swift/artifacts/blocks/
|
||
├── agenda-block.tsx # 🆕 Main AgendaBlock component
|
||
├── agenda-list-view.tsx # 🆕 List mode
|
||
├── agenda-create-form.tsx # 🆕 Create mode
|
||
├── agenda-cancel-view.tsx # 🆕 Cancel mode
|
||
└── agenda-reschedule-form.tsx # 🆕 Reschedule mode
|
||
|
||
app/epd/agenda/
|
||
└── actions.ts # ♻️ Hergebruikt (getEncounters, createEncounter, etc.)
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Demo & Presentatieplan
|
||
Doel: korte demo van agenda planning via Swift (5 minuten).
|
||
|
||
**Demo Flow:**
|
||
1. **Agenda query**: "afspraken vandaag" → lijst met 3 afspraken
|
||
2. **Quick create**: "maak afspraak met Jan morgen 14:00" → create form → submit → success toast
|
||
3. **Cancel flow**: "annuleer afspraak Jan" → disambiguation (3 matches) → select → confirm → success
|
||
4. **Reschedule**: "verzet de 14:00 naar 15:00" → edit form → submit → success
|
||
5. **Fallback**: Link naar klassieke agenda tonen via footer
|
||
|
||
**Key Messaging:**
|
||
- Swift = snelle queries en actions (10-15 sec)
|
||
- Klassieke agenda = visuele planning en overzicht
|
||
- Voice input = hands-free werken tijdens consult
|
||
|
||
---
|
||
|
||
## 9. Risico's & Mitigatie
|
||
| Risico | Kans | Impact | Mitigatie | Owner |
|
||
|--------|------|--------|-----------|-------|
|
||
| Intent ambigu | Hoog | Middel | Disambiguation + fallback prompt | UX |
|
||
| Date/time parsing faalt | Middel | Middel | AI fallback + clear prompts | Dev |
|
||
| API auth issues | Laag | Hoog | Central auth guard + error messaging | Dev |
|
||
| Scope creep | Hoog | Middel | MVP guardrails | PO |
|
||
|
||
---
|
||
|
||
## 10. Sprint Planning & Implementatie Volgorde
|
||
|
||
### Voorgestelde Sprint Indeling (3 sprints):
|
||
|
||
**Sprint 1: Foundation (E1 + E2)**
|
||
- Focus: Intent layer + date/time parsing
|
||
- Stories: E1.S1, E1.S2, E1.S3, E1.S4, E2.S1, E2.S2, E2.S3
|
||
- Total: 17 SP
|
||
- Oplevering: Intent detection + entity extraction werkend, nog geen UI
|
||
- Test: Console log check van extracted entities
|
||
|
||
**Sprint 2: Backend + UI Foundation (E3 + E4.S1-S2)**
|
||
- Focus: API endpoints + AgendaBlock basis
|
||
- Stories: E3.S1, E3.S2, E3.S3, E3.S4, E4.S1, E4.S2
|
||
- Total: 14 SP
|
||
- Oplevering: Agenda query werkend met list view
|
||
- Test: "afspraken vandaag" end-to-end
|
||
|
||
**Sprint 3: UI Completion + Integration (E4.S3-S5 + E5 + E6)**
|
||
- Focus: Create/cancel/reschedule forms + orchestration
|
||
- Stories: E4.S3, E4.S4, E4.S5, E5.S1, E5.S2, E5.S3, E6.S1, E6.S2, E6.S3
|
||
- Total: 20 SP
|
||
- Oplevering: MVP compleet
|
||
- Test: Alle 20 test scenarios
|
||
|
||
**Total MVP: 51 SP (~3 sprints à 2 weken = 6 weken)**
|
||
|
||
### Implementatie Volgorde (binnen stories):
|
||
|
||
Voor elke story, volg deze volgorde:
|
||
1. **Types first**: Definieer interfaces/types
|
||
2. **Backend**: API endpoints + server actions
|
||
3. **Frontend**: UI components
|
||
4. **Integration**: Wire backend ↔ frontend
|
||
5. **Test**: Manual test scenario's
|
||
|
||
### Critical Path:
|
||
```
|
||
E1.S1 (types) → E1.S2 (patterns) → E1.S4 (entities) → E2 (parsers) →
|
||
E3.S1 (query API) → E4.S1 (skeleton) → E4.S2 (list view) →
|
||
E5.S1 (routing) → Milestone: Basic Query Werkend
|
||
|
||
E3.S2 (create API) → E4.S3 (create form) → Milestone: Create Werkend
|
||
|
||
E3.S3 (cancel API) → E4.S4 (cancel view) → Milestone: Cancel Werkend
|
||
|
||
E4.S5 (reschedule) → E5.S2 (prompt) → E6 (QA) → MVP Done
|
||
```
|
||
|
||
---
|
||
|
||
## 11. Evaluatie & Lessons Learned
|
||
Te documenteren na oplevering:
|
||
- Welke intents vaak misclassificeren?
|
||
- Hoe snel users afspraken kunnen plannen?
|
||
- Zijn extra agenda features nodig?
|
||
|
||
---
|
||
|
||
## 12. Referenties & Documentatie
|
||
|
||
### Primaire Documenten:
|
||
- **FO Agenda Afspraken**: `docs/swift/fo-agenda-afspraken.md` - Gebruikersgericht FO
|
||
- **FO Agenda Planning**: `docs/swift/fo-swift-agenda-planning.md` - Technisch FO met detailed specs
|
||
- **Swift FO v3**: `docs/swift/fo-swift-medical-scribe-v3.md` - Basis conversational interface
|
||
- **Swift Bouwplan Core**: `docs/swift/bouwplan-swift-standalone-module.md` - Development roadmap
|
||
|
||
### Code Referenties:
|
||
- **Bestaande Agenda**: `app/epd/agenda` - Klassieke kalender (wordt hergebruikt)
|
||
- **Agenda Actions**: `app/epd/agenda/actions.ts` - Server actions (getEncounters, createEncounter, etc.)
|
||
- **Swift Chat API**: `app/api/swift/chat/route.ts` - Conversational endpoint
|
||
- **Intent Classifier**: `lib/swift/intent-classifier.ts` - Local pattern matching
|
||
- **AI Classifier**: `lib/swift/intent-classifier-ai.ts` - Claude Haiku fallback
|
||
- **Swift Types**: `lib/swift/types.ts` - SwiftIntent & ExtractedEntities
|
||
|
||
### Libraries & Dependencies:
|
||
- **date-fns**: Date/time utilities (already installed)
|
||
- **Zod**: API validation schemas (already installed)
|
||
- **React Query**: Data caching (already installed)
|
||
- **shadcn/ui**: UI components (already installed)
|
||
- **Deepgram**: Voice input (already integrated)
|
||
|
||
### External References:
|
||
- [FHIR Encounter Resource](https://www.hl7.org/fhir/encounter.html) - Encounter data model
|
||
- [date-fns Documentation](https://date-fns.org/) - Date manipulation
|
||
- [React Query Best Practices](https://tanstack.com/query/latest) - Caching patterns
|
||
|
||
---
|
||
|
||
## 13. Glossary & Abbreviations
|
||
|
||
| Term | Betekenis |
|
||
|------|-----------|
|
||
| **Epic** | Grote feature of fase, bevat meerdere stories |
|
||
| **Story** | Kleine uitvoerbare taak (user story) |
|
||
| **SP** | Story Points - schatting van complexiteit |
|
||
| **MVP** | Minimum Viable Product |
|
||
| **FO** | Functioneel Ontwerp |
|
||
| **P1/P2** | Priority 1 (must have) / Priority 2 (nice to have) |
|
||
| **SSE** | Server-Sent Events |
|
||
| **RLS** | Row Level Security (Supabase) |
|
||
| **NLP** | Natural Language Processing |
|
||
| **Artifact** | Swift UI component (AgendaBlock, DagnotatieBlock, etc.) |
|
||
| **Intent** | Gebruikersintentie (agenda_query, create_appointment, etc.) |
|
||
| **Entity** | Geëxtraheerde data uit user input (patient, date, time) |
|
||
| **Disambiguation** | Verduidelijking bij meerdere matches |
|
||
| **Confidence** | Zekerheid van intent classificatie (0.0-1.0) |
|
||
| **Two-tier** | Local pattern matching + AI fallback systeem |
|
||
| **FHIR** | Fast Healthcare Interoperability Resources |
|
||
| **Encounter** | Afspraak/contact in FHIR terminologie |
|
||
|
||
### Domain-Specific Terms:
|
||
|
||
| Term | Betekenis |
|
||
|------|-----------|
|
||
| **Klassieke Agenda** | Bestaande visuele kalender in `/epd/agenda` |
|
||
| **Swift Agenda** | Nieuwe conversational agenda interface |
|
||
| **AgendaBlock** | Swift artifact voor agenda functionaliteit |
|
||
| **Verpleegkundige** | Primaire gebruikersrol (nurse/healthcare provider) |
|
||
| **Practitioner** | FHIR term voor zorgverlener |
|
||
| **Quick Create** | Snel afspraak maken via natuurlijke taal |
|
||
| **Hands-free** | Werken met voice input (geen typen nodig) |
|
||
|
||
---
|
||
|
||
## Versiehistorie
|
||
|
||
| Versie | Datum | Auteur | Wijziging |
|
||
|--------|-------|--------|-----------|
|
||
| v1.0 | 27-12-2025 | Colin Lit | Initiele versie - basis epics en stories |
|
||
| v1.1 | 27-12-2025 | Claude Code | Uitgebreid met technische details uit FO's: patterns, entities, API specs, UI components, test scenarios, sprint planning, file structuur |
|
||
|
||
---
|
||
|
||
## Quick Reference Card
|
||
|
||
**Voor Developers - Start Here:**
|
||
1. Lees Epic 0 voor scope
|
||
2. Begin met E1.S1 (types uitbreiden)
|
||
3. Volg implementatie volgorde (sectie 10)
|
||
4. Check test scenarios (sectie 5)
|
||
5. Zie file structuur (sectie 7) voor waar code moet komen
|
||
|
||
**Voor Product Owners:**
|
||
- MVP scope: sectie 2.2
|
||
- User stories: sectie 6
|
||
- Demo plan: sectie 8
|
||
- Risico's: sectie 9
|
||
|
||
**Voor QA:**
|
||
- Test checklist: sectie 5 (20 scenarios)
|
||
- Regression tests: sectie 5
|
||
- Performance targets: sectie 2.4
|
||
|
||
**Voor Designers:**
|
||
- UI components: Epic 4 technical notes
|
||
- Styling specs: E4.S1-E4.S5
|
||
- User flows: FO documenten (zie sectie 12)
|