feat: complete E2.S5 Input → Block wiring

- Implementeer handleSubmit met API call naar /api/intent/classify
- Voeg error handling en fallback naar dagnotitie toe
- Integreer openBlock en addRecentAction
- Update bouwplan: Epic 2 compleet (33 SP done, 46%)
This commit is contained in:
colinislit
2025-12-24 08:44:40 +01:00
parent db036a3d92
commit 3e94271827
7 changed files with 3449 additions and 5 deletions

View File

@@ -17,6 +17,7 @@
import { forwardRef, useState, useEffect, useRef } from 'react'; import { forwardRef, useState, useEffect, useRef } from 'react';
import { useSwiftStore } from '@/stores/swift-store'; import { useSwiftStore } from '@/stores/swift-store';
import { useSwiftVoice } from '@/lib/swift/use-swift-voice'; import { useSwiftVoice } from '@/lib/swift/use-swift-voice';
import type { BlockType } from '@/lib/swift/types';
import { Mic, MicOff, Send, Loader2 } from 'lucide-react'; import { Mic, MicOff, Send, Loader2 } from 'lucide-react';
export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_, ref) { export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_, ref) {
@@ -27,6 +28,8 @@ export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_
activePatient, activePatient,
activeBlock, activeBlock,
isVoiceActive, isVoiceActive,
openBlock,
addRecentAction,
} = useSwiftStore(); } = useSwiftStore();
const { const {
@@ -115,10 +118,63 @@ export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_
stopRecording(); stopRecording();
} }
const inputText = inputValue.trim();
setIsProcessing(true); setIsProcessing(true);
try { try {
// TODO: Process intent (E2) // Call intent classification API
console.log('Submit:', inputValue); const response = await fetch('/api/intent/classify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input: inputText }),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Onbekende fout' }));
throw new Error(errorData.error || `HTTP ${response.status}`);
}
const result = await response.json();
const { intent, confidence, entities } = result;
// Check if we have a valid intent with sufficient confidence
if (intent !== 'unknown' && confidence >= 0.5) {
// Open the appropriate block with prefill data
// Type assertion: intent is BlockType after 'unknown' check
openBlock(intent as BlockType, entities);
// Add to recent actions
addRecentAction({
intent,
label: inputText.slice(0, 50), // Truncate for display
patientName: entities.patientName,
});
clearInput();
} else {
// Low confidence or unknown intent - temporary fallback to dagnotitie
// TODO: Replace with FallbackPicker in E4.S4
openBlock('dagnotitie', { content: inputText });
addRecentAction({
intent: 'dagnotitie',
label: inputText.slice(0, 50),
});
clearInput();
}
} catch (error) {
console.error('Error processing intent:', error);
// On error, fallback to dagnotitie with the input as content
// This ensures the user's input is not lost
openBlock('dagnotitie', { content: inputText });
addRecentAction({
intent: 'dagnotitie',
label: inputText.slice(0, 50),
});
clearInput(); clearInput();
} finally { } finally {
setIsProcessing(false); setIsProcessing(false);

View File

@@ -0,0 +1,700 @@
# 📋 Beoordeling Bouwplan Swift: Diagnostiek Workflow
**Datum:** 23-12-2024
**Beoordelaar:** AI Code Review
**Versie:** v1.0
---
## Executive Summary
**Algemene Beoordeling:****HAALBAAR met enkele kritieke aanvullingen**
Het bouwplan is goed gestructureerd en bouwt slim voort op bestaande componenten. De geschatte 21 story points voor 2 weken zijn realistisch, maar er zijn enkele belangrijke technische hiaten die eerst opgelost moeten worden voordat de implementatie kan starten.
**Kritieke Bevindingen:**
1.**Report type "diagnostiek" ontbreekt** in `REPORT_TYPES` enum
2.**Swift intent types** moeten uitgebreid worden met nieuwe intents
3. ⚠️ **Entity extraction** voor datum/tijd parsing ontbreekt in plan
4. ⚠️ **Intake selectie** voor diagnoses niet duidelijk uitgewerkt
5.**Hergebruik componenten** is goed geanalyseerd en realistisch
**Blokkerende Bevindingen (Swift Foundation - 24-12-2024):**
6. 🔴 **E2.S5 Input → Block wiring ontbreekt** - handleSubmit is placeholder
7. 🔴 **CanvasArea block rendering ontbreekt** - Blocks worden niet gerenderd
8. 🔴 **P1 Blocks (E3) niet geïmplementeerd** - DagnotatieBlock, ZoekenBlock, OverdrachtBlock bestaan niet
9. ⚠️ **Type duplicatie** - SwiftIntent in zowel types.ts als swift-store.ts
---
## 1. Compleetheid Analyse
### 1.1 ✅ Sterke Punten
**Goed Gedocumenteerd:**
- Duidelijke epic/story breakdown met acceptatiecriteria
- Realistische effort schattingen (21 SP voor 2 weken)
- Goede referenties naar bestaande code
- Testplan met concrete checklists
- Risico analyse met mitigaties
**Technische Analyse:**
- Correcte identificatie van hergebruikbare componenten
- Wrapper pattern goed uitgelegd
- API routes strategie is logisch
### 1.2 ❌ Ontbrekende Elementen
#### Kritiek: Report Type "diagnostiek"
**Probleem:**
Het bouwplan noemt het toevoegen van report type "diagnostiek" (regel 179), maar dit type bestaat niet in de `REPORT_TYPES` enum.
**Huidige situatie:**
```typescript
// lib/types/report.ts
export const REPORT_TYPES = [
'voortgang', 'observatie', 'incident', 'medicatie', 'contact',
'crisis', 'intake', 'behandeladvies', 'vrije_notitie', 'verpleegkundig'
] as const;
```
**Impact:**
- Database validatie zal falen bij POST `/api/reports` met type "diagnostiek"
- Zod schema moet uitgebreid worden
- Mogelijk database constraint check nodig
**Aanbeveling:**
Voeg story toe: **E-D1.S0: Report type "diagnostiek" toevoegen** (1 SP)
- Update `REPORT_TYPES` enum
- Update Zod schema `CreateReportSchema`
- Database migratie indien nodig (check constraints)
#### Kritiek: Swift Intent Types Uitbreiding
**Probleem:**
Het bouwplan beschrijft nieuwe intent patterns, maar de `SwiftIntent` type definitie moet uitgebreid worden.
**Huidige situatie:**
```typescript
// lib/swift/types.ts
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';
```
**Nodig:**
```typescript
export type SwiftIntent =
| 'dagnotitie'
| 'zoeken'
| 'overdracht'
| 'afspraak_maken' // NIEUW
| 'rapportage' // NIEUW
| 'diagnose_bekijken' // NIEUW
| 'diagnose_toevoegen' // NIEUW
| 'diagnose_wijzigen' // NIEUW
| 'unknown';
```
**Impact:**
- TypeScript compile errors zonder deze uitbreiding
- Block configs moeten uitgebreid worden
- Intent classifier moet nieuwe types ondersteunen
**Aanbeveling:**
Voeg toe aan E-D1.S1 en E-D2.S1:
- Update `SwiftIntent` type
- Update `BLOCK_CONFIGS` met nieuwe block types
- Update `BlockType` type (exclude 'unknown')
#### Waarschuwing: Entity Extraction voor Datum/Tijd
**Probleem:**
Het bouwplan beschrijft intent patterns die datum/tijd moeten extraheren (bijv. "morgen 10:00"), maar er is geen plan voor entity extraction van deze waarden.
**Voorbeeld uit plan:**
```
/^afspraak\s+(\w+)\s+(morgen|vandaag)\s+(\d{1,2}:\d{2})/i
```
**Ontbrekend:**
- Functie om "morgen" → Date object te converteren
- Functie om "10:00" → tijd te parseren
- Validatie van datum/tijd combinaties
- Fallback naar date picker bij onduidelijke input
**Huidige situatie:**
`lib/swift/entity-extractor.ts` bestaat, maar bevat alleen patient name extraction.
**Aanbeveling:**
Voeg toe aan E-D1.S1:
- `extractDateTime(input: string): { date?: Date; time?: string }`
- Integratie met date-fns voor Nederlandse datum parsing
- Fallback logica voor onduidelijke input
#### Waarschuwing: Intake Selectie voor Diagnoses
**Probleem:**
Het bouwplan beschrijft diagnose toevoegen, maar `DiagnosisDetailForm` vereist een `intakeId` (encounter_id). Het plan beschrijft niet hoe dit wordt bepaald vanuit Swift context.
**Huidige situatie:**
```typescript
// app/epd/patients/[id]/diagnose/components/diagnosis-detail-form.tsx
// Vereist: intakes array en selectedIntakeId
```
**Vragen:**
- Moet Swift automatisch de laatste intake selecteren?
- Moet Swift een intake selector tonen?
- Kan diagnose zonder intake (direct encounter koppeling)?
**Aanbeveling:**
Voeg toe aan E-D2.S3:
- Beslissing: automatisch laatste intake of selector tonen
- Documenteer in acceptatiecriteria
- Update DiagnoseFormBlock implementatie
### 1.3 ⚠️ Onduidelijkheden
#### Appointment Modal Hergebruik
**Vraag:**
Het plan zegt "80% hergebruik" van `AppointmentModal`, maar deze component is een Dialog met veel interne state. Hoe wordt dit geïntegreerd in Swift blocks?
**Huidige situatie:**
- `AppointmentModal` is een volledig Dialog component
- Swift blocks gebruiken `BlockContainer` (geen Dialog)
- Dark theme styling moet aangepast worden
**Aanbeveling:**
Clarificeer in E-D1.S2:
- Option A: Extract form logica naar shared component, wrapper in beide contexts
- Option B: Hergebruik AppointmentModal maar wrap in BlockContainer (mogelijk styling issues)
- Option C: Nieuwe Swift-specifieke component met shared business logic
#### Encounter ID Callback Pattern
**Vraag:**
Het plan zegt "encounter_id teruggeven via callback", maar hoe wordt dit gebruikt voor de volgende stap (rapportage)?
**Scenario:**
1. User: "afspraak diagnostiek jan morgen 10:00"
2. AfspraakBlock → encounter_id = "abc-123"
3. User: "rapportage diagnostiek gesprek met jan"
4. Hoe weet RapportageBlock dat encounter_id "abc-123" moet gebruiken?
**Aanbeveling:**
Clarificeer:
- Option A: Swift store houdt laatste encounter_id bij per patient
- Option B: User moet encounter expliciet selecteren
- Option C: AI fallback om encounter te matchen op datum/tijd
---
## 2. Haalbaarheid Analyse
### 2.1 ✅ Realistische Schattingen
**Story Points Breakdown:**
- E-D1.S1: 2 SP (intent patterns) → **Realistisch**
- E-D1.S2: 5 SP (AfspraakBlock) → **Realistisch** (met hergebruik)
- E-D1.S3: 4 SP (RapportageBlock) → **Realistisch**
- E-D2.S1: 2 SP (intent patterns) → **Realistisch**
- E-D2.S2: 3 SP (DiagnoseBlock) → **Realistisch**
- E-D2.S3: 5 SP (DiagnoseFormBlock) → **Realistisch**
**Totaal: 21 SP voor 2 weken = ~10 SP/week**
Dit is haalbaar voor 1 developer met goede focus.
### 2.2 ⚠️ Risico's op Vertraging
**Hoog Risico:**
1. **Appointment Modal Integratie** (E-D1.S2)
- Hergebruik Dialog component in Block context kan complex zijn
- Styling aanpassingen kunnen meer tijd kosten dan geschat
- **Mitigatie:** Start met proof-of-concept, pas schatting aan indien nodig
2. **Datum/Tijd Parsing** (E-D1.S1)
- Nederlandse datum parsing ("morgen", "volgende week") kan edge cases hebben
- Tijdzone handling voor afspraken
- **Mitigatie:** Gebruik date-fns met Nederlandse locale, test grondig
**Middel Risico:**
1. **ICD-10 Zoeker Verbetering** (E-D2.S3)
- Plan zegt "fuzzy search verbeteren" maar geeft geen specificaties
- Bestaande zoeker werkt al redelijk goed
- **Mitigatie:** Eerst testen of verbetering nodig is, anders scope verkleinen
2. **Encounter Koppeling** (E-D1.S3)
- Automatische koppeling tussen afspraak en rapportage kan complex zijn
- **Mitigatie:** Start met optionele koppeling, voeg automatische matching later toe
### 2.3 ✅ Goede Foundation
**Bestaande Componenten:**
- ✅ AppointmentModal bestaat en is goed gestructureerd
- ✅ ReportComposer bestaat en ondersteunt al `linkedEncounterId`
- ✅ DiagnosisDetailForm bestaat met ICD-10 zoeker
- ✅ Actions bestaan en zijn herbruikbaar
- ✅ ICD-10 zoeker werkt al met fuzzy search
**Bestaande Infrastructuur:**
- ✅ Swift foundation (Command Center, Intent Classification) werkt
- ✅ Block system bestaat (`BlockContainer`)
- ✅ API routes pattern is duidelijk
- ✅ Supabase RLS policies zijn al geïmplementeerd
---
## 3. Technische Aanbevelingen
### 3.1 Kritieke Toevoegingen
#### Story E-D1.S0: Report Type "diagnostiek" (NIEUW - 1 SP)
**Beschrijving:**
Voeg report type "diagnostiek" toe aan het systeem.
**Acceptatiecriteria:**
- [ ] `REPORT_TYPES` enum bevat "diagnostiek"
- [ ] Zod schema `CreateReportSchema` accepteert "diagnostiek"
- [ ] Database constraint check (indien nodig)
- [ ] QuickActions component toont "diagnostiek" optie (indien van toepassing)
**Technical Notes:**
```typescript
// lib/types/report.ts
export const REPORT_TYPES = [
// ... bestaande types
'diagnostiek', // NIEUW
] as const;
```
#### Story E-D1.S1 Uitbreiding: Entity Extraction
**Toevoegen aan E-D1.S1:**
- [ ] `extractDateTime(input: string)` functie
- [ ] Nederlandse datum parsing ("morgen", "vandaag", "volgende week")
- [ ] Tijd parsing ("10:00", "14:30")
- [ ] Fallback naar date picker bij onduidelijke input
**Technical Notes:**
```typescript
// lib/swift/entity-extractor.ts
export function extractDateTime(input: string): {
date?: Date;
time?: string;
confidence: number;
} {
// Parse "morgen 10:00" → { date: tomorrow, time: "10:00" }
// Parse "vandaag 14:30" → { date: today, time: "14:30" }
// Return confidence voor fallback beslissing
}
```
#### Story E-D2.S3 Uitbreiding: Intake Selectie
**Clarificatie nodig:**
- [ ] Beslissing: automatisch laatste intake of selector?
- [ ] Documenteer in acceptatiecriteria
- [ ] Implementeer gekozen aanpak
**Aanbeveling:**
Automatisch laatste intake selecteren, met optie om te wijzigen:
```typescript
// DiagnoseFormBlock
const intakes = await getPatientIntakes(patientId);
const defaultIntakeId = intakes[0]?.id; // Laatste intake
// Toon dropdown indien meerdere intakes beschikbaar
```
### 3.2 Verbeteringen
#### Intent Patterns Verbeteren
**Huidige patterns zijn te specifiek:**
```typescript
// Te specifiek - mist veel variaties
/^afspraak\s+(diagnostiek|behandeling)\s+(\w+)/i
```
**Aanbeveling:**
Voeg meer variaties toe:
```typescript
afspraak_maken: [
// Basis patterns
{ pattern: /^afspraak\s+(diagnostiek|behandeling)\s+(\w+)/i, weight: 1.0 },
{ pattern: /^plan\s+(diagnostiek|behandeling)\s+(\w+)/i, weight: 0.95 },
// Met datum/tijd
{ pattern: /^afspraak\s+(\w+)\s+(morgen|vandaag)\s+(\d{1,2}:\d{2})/i, weight: 1.0 },
{ pattern: /^plan\s+(\w+)\s+(morgen|vandaag)\s+(\d{1,2}:\d{2})/i, weight: 0.95 },
// Zonder type (default diagnostiek)
{ pattern: /^afspraak\s+(\w+)\s+(morgen|vandaag)/i, weight: 0.85 },
// Alleen "afspraak" met patient naam
{ pattern: /^afspraak\s+(\w+)/i, weight: 0.7 },
],
```
#### API Routes Specificatie
**Huidige beschrijving is te vaag:**
```typescript
// app/api/appointments/route.ts
export async function POST(request: NextRequest) {
// Wrapper rond app/epd/agenda/actions.ts createEncounter
// Retourneert encounter_id voor volgende stap
}
```
**Aanbeveling:**
Specificeer volledige API contract:
```typescript
// POST /api/appointments
// Request body:
{
patientId: string;
periodStart: string; // ISO 8601
periodEnd?: string;
typeCode: 'diagnostiek' | 'behandeling' | ...;
typeDisplay: string;
classCode: 'AMB' | 'HH' | 'VR';
classDisplay: string;
notes?: string;
}
// Response:
{
success: boolean;
data?: {
id: string; // encounter_id
// ... andere encounter velden
};
error?: string;
}
```
---
## 4. Testplan Verbeteringen
### 4.1 Ontbrekende Test Cases
**Entity Extraction Tests:**
- [ ] "morgen 10:00" → correcte datum + tijd
- [ ] "vandaag 14:30" → correcte datum + tijd
- [ ] "volgende week maandag" → correcte datum
- [ ] "afspraak jan" → alleen patient, geen datum → fallback date picker
- [ ] "afspraak jan morgen" → patient + datum, geen tijd → fallback time picker
**Encounter Koppeling Tests:**
- [ ] Afspraak aanmaken → encounter_id opgeslagen
- [ ] Rapportage met encounter_id → correct gekoppeld
- [ ] Rapportage zonder encounter_id → optioneel (geen error)
- [ ] Meerdere encounters opzelfde dag → juiste selectie
**Diagnose Intake Tests:**
- [ ] Patiënt met 1 intake → automatisch geselecteerd
- [ ] Patiënt met meerdere intakes → laatste geselecteerd
- [ ] Patiënt zonder intakes → error/fallback
### 4.2 Integration Test Scenarios
**End-to-End Flow:**
```
1. "afspraak diagnostiek jan morgen 10:00"
→ AfspraakBlock opent
→ Patient "jan" gevonden
→ Datum: morgen
→ Tijd: 10:00
→ Type: diagnostiek
→ Opslaan → encounter_id = "abc-123"
2. "rapportage diagnostiek gesprek met jan"
→ RapportageBlock opent
→ Patient "jan" gevonden
→ Type: diagnostiek
→ Encounter: "abc-123" (laatste encounter van jan)
→ Content invoeren
→ Opslaan → report gekoppeld aan encounter
3. "diagnose jan"
→ DiagnoseBlock opent
→ Overzicht diagnoses van jan
→ Filter: Actief
4. "diagnose toevoegen jan F41.1"
→ DiagnoseFormBlock opent
→ Patient: jan
→ ICD-10: F41.1 (pre-filled)
→ Intake: laatste intake (automatisch)
→ Type: nevendiagnose (default)
→ Opslaan → diagnose toegevoegd
```
---
## 5. Swift Foundation Status (24-12-2024)
> **BELANGRIJK:** De diagnostiek workflow bouwt voort op de Swift foundation (bouwplan-swift-v1.md).
> De foundation is nog niet compleet, waardoor de diagnostiek workflow nog niet kan starten.
### 5.1 Foundation Status Overzicht
| Epic | Status | Impact op Diagnostiek |
|------|--------|----------------------|
| E0 Setup & Foundation | ✅ Done | Geen blokkade |
| E1 Command Center | ✅ Done | Geen blokkade |
| E2 Intent Classification | ⏳ In Progress | 🔴 **BLOKKADE** |
| E3 P1 Blocks | ⏳ To Do | 🔴 **BLOKKADE** |
| E4 Navigation & Auth | ⏳ To Do | Geen blokkade |
| E5 Polish & Testing | ⏳ To Do | Geen blokkade |
### 5.2 Blokkerende Issues
#### 🔴 Blokkade 1: E2.S5 Input → Block Wiring
**Huidige situatie:**
```typescript
// components/swift/command-center/command-input.tsx:120
const handleSubmit = async (e: React.FormEvent) => {
// TODO: Process intent (E2)
console.log('Submit:', inputValue);
clearInput();
};
```
**Probleem:**
De intent classificatie API bestaat (`/api/intent/classify`), maar wordt niet aangeroepen.
Het resultaat wordt niet gebruikt om `openBlock()` aan te roepen.
**Impact:**
Zonder deze wiring kan geen enkel block geopend worden via tekst/spraak input.
**Story toegevoegd aan bouwplan-swift-v1.md (v1.5):**
```markdown
| E2.S5 | Input → Block wiring | CommandInput submit → API → openBlock | ⏳ | E2.S4 | 2 |
```
#### 🔴 Blokkade 2: CanvasArea Block Rendering
**Huidige situatie:**
```typescript
// components/swift/command-center/canvas-area.tsx:18
{activeBlock ? (
<div className="text-slate-400">Block: {activeBlock}</div> // ← Placeholder!
) : (
<EmptyState />
)}
```
**Probleem:**
De CanvasArea toont alleen placeholder tekst, niet de daadwerkelijke block componenten.
Er is geen switch/case of dynamic import voor de block types.
**Impact:**
Zelfs als E2.S5 werkt, worden blocks niet zichtbaar.
**Aanbeveling:**
Voeg story E3.S0 toe aan bouwplan-swift-v1.md:
```markdown
| E3.S0 | CanvasArea block rendering | Switch/case voor block types, prefill doorgeven | ⏳ | E2.S5 | 2 |
```
#### 🔴 Blokkade 3: P1 Blocks Bestaan Niet
**Huidige situatie:**
```typescript
// components/swift/blocks/index.ts
export { BlockContainer } from './block-container';
// Commented out - bestaan niet:
// export { DagnotatieBlock } from './dagnotitie-block';
// export { ZoekenBlock } from './zoeken-block';
// export { OverdrachtBlock } from './overdracht-block';
```
**Probleem:**
De diagnostiek workflow voegt nieuwe blocks toe (AfspraakBlock, RapportageBlock, DiagnoseBlock),
maar de basis P1 blocks waar deze op voortbouwen bestaan nog niet.
**Impact:**
- DagnotatieBlock is nodig als referentie voor RapportageBlock
- ZoekenBlock is nodig voor patient selectie in alle flows
- BlockContainer bestaat wel, maar is nog niet getest met echte content
**Aanbeveling:**
Implementeer eerst E3.S2 (DagnotatieBlock) als proof-of-concept voordat diagnostiek workflow start.
#### ⚠️ Waarschuwing: Type Duplicatie
**Huidige situatie:**
```typescript
// lib/swift/types.ts
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';
// stores/swift-store.ts (DUPLICAAT)
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';
```
**Impact:**
Bij uitbreiding van intents voor diagnostiek workflow moet dit op twee plekken bijgewerkt worden.
Vergeten leidt tot TypeScript errors.
**Aanbeveling:**
Verwijder duplicaat uit `swift-store.ts`, importeer uit `types.ts`.
### 5.3 Dependency Chain voor Diagnostiek
```
Swift Foundation (moet eerst af)
├── E2.S5 Input → Block wiring ← BLOKKADE
│ └── handleSubmit() roept API aan
│ └── openBlock() bij succes
├── E3.S0 CanvasArea block rendering ← BLOKKADE (ontbreekt in plan)
│ └── Switch/case voor block types
│ └── Prefill data doorgeven
├── E3.S2 DagnotatieBlock ← BLOKKADE
│ └── Referentie voor RapportageBlock
│ └── Test case voor block rendering
└── E3.S3 Patient Search API ← BLOKKADE
└── Nodig voor patient selectie
Diagnostiek Workflow (kan dan starten)
├── E-D1.S0 Report type "diagnostiek"
├── E-D1.S1 Intent patterns uitbreiden
├── E-D1.S2 AfspraakBlock
├── E-D1.S3 RapportageBlock
├── E-D2.S1 Diagnose intent patterns
├── E-D2.S2 DiagnoseBlock
└── E-D2.S3 DiagnoseFormBlock
```
### 5.4 Geschatte Effort voor Foundation Completion
| Story | SP | Status |
|-------|----|----|
| E2.S5 Input → Block wiring | 2 | ⏳ Nieuw toegevoegd |
| E3.S0 CanvasArea rendering | 2 | ⏳ Aanbevolen |
| E3.S2 DagnotatieBlock | 5 | ⏳ Gepland |
| E3.S3 Patient Search API | 3 | ⏳ Gepland |
| **Subtotaal** | **12** | |
**Totale effort voor werkende demo:**
- Swift Foundation completion: 12 SP
- Diagnostiek Workflow: 22 SP
- **Totaal: 34 SP**
---
## 6. Conclusie & Aanbevelingen
### 6.1 Algemene Beoordeling
**Compleetheid Diagnostiek Plan:** ⚠️ **7/10**
- Goede structuur en breakdown
- Enkele kritieke technische details ontbreken
- Entity extraction niet volledig uitgewerkt
**Compleetheid Swift Foundation:** ⚠️ **5/10**
- E0-E2.S4 solide geïmplementeerd
- E2.S5 wiring ontbreekt (kritiek)
- E3 blocks niet gebouwd (kritiek)
- CanvasArea rendering niet in plan
**Haalbaarheid:** ⚠️ **6/10** (was 8/10)
- Foundation moet eerst af voordat diagnostiek kan starten
- Extra 12 SP nodig voor foundation completion
- Totale effort: 34 SP i.p.v. 22 SP
### 6.2 Aanbevelingen voor Start
**Voor Start:**
1. ✅ Voeg E-D1.S0 toe: Report type "diagnostiek" (1 SP)
2. ✅ Uitbreid E-D1.S1: Entity extraction voor datum/tijd (extra 1 SP)
3. ✅ Clarificeer E-D2.S3: Intake selectie strategie
4. ✅ Update `SwiftIntent` type definitie
5. ✅ Proof-of-concept AppointmentModal integratie
**Tijdens Implementatie:**
1. Start met E-D1.S0 (report type) - basis voor alles
2. Test entity extraction grondig voordat je verder gaat
3. Itereer op AppointmentModal integratie (mogelijk meer tijd nodig)
4. Houd rekening met edge cases in datum parsing
**Na Implementatie:**
1. Uitgebreide end-to-end tests
2. Performance testen (intent classification snelheid)
3. UX feedback verzamelen
4. Documentatie updaten
### 6.3 Aangepaste Story Breakdown
**Swift Foundation (moet eerst af):**
- E2.S5: Input → Block wiring (2 SP) ⭐ NIEUW
- E3.S0: CanvasArea block rendering (2 SP) ⭐ AANBEVOLEN
- E3.S2: DagnotatieBlock (5 SP)
- E3.S3: Patient Search API (3 SP)
- **Subtotaal Foundation: 12 SP**
**Epic D1 — Afspraak & Rapportage (12 SP totaal):**
- E-D1.S0: Report type "diagnostiek" toevoegen (1 SP) ⭐ NIEUW
- E-D1.S1: Intent patterns + entity extraction (3 SP) ⬆️ +1 SP
- E-D1.S2: AfspraakBlock (5 SP)
- E-D1.S3: RapportageBlock uitbreiden (4 SP)
**Epic D2 — Diagnose Beheer (10 SP totaal):**
- E-D2.S1: Intent patterns uitbreiden (2 SP)
- E-D2.S2: DiagnoseBlock (3 SP)
- E-D2.S3: DiagnoseFormBlock (5 SP)
**Totalen:**
- Swift Foundation completion: 12 SP
- Diagnostiek Workflow: 22 SP
- **Totaal: 34 SP**
### 6.4 Finale Oordeel
⚠️ **HOLD - Foundation eerst afronden**
Het diagnostiek bouwplan is goed gestructureerd, maar kan nog niet starten omdat de Swift foundation incompleet is.
**Kritieke blokkades:**
1. E2.S5 (handleSubmit wiring) - toegevoegd aan bouwplan v1.5
2. E3.S0 (CanvasArea rendering) - moet nog toegevoegd worden
3. E3.S2 (DagnotatieBlock) - moet gebouwd worden als referentie
4. E3.S3 (Patient Search API) - nodig voor alle patient selectie
**Aanbevolen volgorde:**
```
Week 1: Foundation completion (12 SP)
├── E2.S5 Input → Block wiring
├── E3.S0 CanvasArea rendering
├── E3.S2 DagnotatieBlock
└── E3.S3 Patient Search API
Week 2-3: Diagnostiek Workflow (22 SP)
├── E-D1.S0 Report type
├── E-D1.S1 Intent patterns
├── E-D1.S2 AfspraakBlock
├── E-D1.S3 RapportageBlock
├── E-D2.S1 Diagnose patterns
├── E-D2.S2 DiagnoseBlock
└── E-D2.S3 DiagnoseFormBlock
```
**GO voor implementatie** zodra foundation stories zijn afgerond.
---
**Versiehistorie:**
| Versie | Datum | Auteur | Wijziging |
|--------|-------|--------|-----------|
| v1.0 | 23-12-2024 | AI Review | Initiële beoordeling |
| v1.1 | 24-12-2024 | Claude | Swift Foundation status toegevoegd (sectie 5), blokkerende issues geïdentificeerd, finale oordeel aangepast naar HOLD |

View File

@@ -0,0 +1,458 @@
# 🚀 Mission Control — Bouwplan Swift: Diagnostiek Workflow
**Projectnaam:** Swift — Diagnostiek Workflow
**Versie:** v1.0
**Datum:** 23-12-2024
**Auteur:** Colin Lit / Development Team
---
## 1. Doel en context
🎯 **Doel:**
Implementeren van de complete diagnostiek workflow in Swift: van het plannen van een diagnostiek-afspraak, via het schrijven van een rapportage, tot het bekijken en stellen van diagnoses. Deze workflow stelt behandelaars in staat om een volledig diagnostiek-traject door te lopen via natuurlijke taal zonder menu-navigatie.
**Kernbelofte:**
> Een behandelaar kan een volledig diagnostiek-traject doorlopen: "afspraak diagnostiek jan morgen 10:00" → "rapportage diagnostiek gesprek met jan" → "diagnose toevoegen jan F41.1" — alles in één vloeiende flow.
📘 **Context:**
Dit bouwplan beschrijft de implementatie van de diagnostiek workflow als uitbreiding op Swift. Het bouwt voort op de Swift foundation (Command Center, Intent Classification) en voegt specifieke blocks toe voor behandelaars.
**Relatie met documentatie:**
- **FO Diagnostiek:** `swift-fo-diagnostiek-workflow.md` — Complete use case beschrijving
- **FO Algemeen:** `swift-fo-ai.md` — Algemene Swift functionaliteit
- **Bouwplan Swift:** `bouwplan-swift-v1.md` — Hoofd Swift bouwplan
- **UX/UI:** `swift-ux-v2.1.md` — Visuele specificaties
---
## 2. Uitgangspunten
### 2.1 Technische Stack
**Bestaand (hergebruiken):**
| Component | Technologie | Status | Hergebruik |
|-----------|-------------|--------|------------|
| Swift Foundation | Command Center, Intent Classification | ✅ | Basis voor alle blocks |
| Appointment Modal | `app/epd/agenda/components/appointment-modal.tsx` | ✅ | 80% hergebruik |
| Rapportage Composer | `app/epd/patients/[id]/rapportage/components/report-composer.tsx` | ✅ | 80% hergebruik |
| Diagnose Form | `app/epd/patients/[id]/diagnose/components/diagnosis-detail-form.tsx` | ✅ | 80% hergebruik |
| Diagnose Actions | `app/epd/patients/[id]/diagnose/actions.ts` | ✅ | 90% hergebruik |
| FHIR Patient API | `/api/fhir/Patient` | ✅ | Patient search |
| Reports API | `/api/reports` | ✅ | Rapportage opslaan |
**Nieuw te bouwen:**
| Component | Technologie | Reden |
|-----------|-------------|-------|
| AfspraakBlock | React component | Swift wrapper rond appointment modal |
| RapportageBlock | React component | Swift wrapper rond report composer |
| DiagnoseBlock | React component | Overzicht component |
| DiagnoseFormBlock | React component | Swift wrapper rond diagnose form |
| Appointments API | Next.js API route | Encounter CRUD voor Swift |
| Diagnoses API | Next.js API route | Diagnose CRUD voor Swift |
### 2.2 Projectkaders
| Kader | Waarde |
|-------|--------|
| **Bouwtijd** | 2 weken (2 sprints) |
| **Team** | 1 developer |
| **Scope** | Diagnostiek workflow: Afspraak → Rapportage → Diagnose |
| **Data** | Bestaande Supabase database (encounters, conditions, reports) |
| **Doel** | Werkende behandelaar workflow voor demo |
### 2.3 Programmeer Uitgangspunten
**Code Quality Principles:**
- **DRY (Don't Repeat Yourself)**
- Hergebruik bestaande componenten (appointment-modal, report-composer, diagnose-form)
- Centrale API routes voor encounters en diagnoses
- Shared hooks voor common patterns
- **KISS (Keep It Simple, Stupid)**
- Swift blocks zijn wrappers rond bestaande componenten
- Minimale aanpassingen aan bestaande code
- Hergebruik bestaande validatie en error handling
- **SOC (Separation of Concerns)**
- Swift blocks gescheiden van klassieke EPD componenten
- API routes als wrapper rond bestaande actions
- Business logic blijft in bestaande actions
- **YAGNI (You Aren't Gonna Need It)**
- Alleen diagnostiek workflow in scope
- Geen andere behandelaar workflows (intake, behandelplan, etc.)
- Geen advanced features (herhalende afspraken, etc.)
**Development Practices:**
- **Hergebruik Strategie**
```typescript
// ✅ Hergebruik bestaande componenten
// components/swift/blocks/afspraak-block.tsx
import { AppointmentModal } from '@/app/epd/agenda/components/appointment-modal';
// Aanpassen voor Swift context:
// - Prefill vanuit intent
// - Encounter_id teruggeven na opslaan
// - Swift styling (dark theme)
// ✅ Hergebruik bestaande actions
// app/api/appointments/route.ts
import { createEncounter, updateEncounter } from '@/app/epd/agenda/actions';
// Wrapper rond bestaande logica met Swift-specifieke validatie
```
- **Error Handling**
- Hergebruik bestaande error handling uit actions
- Nederlandse foutmeldingen consistent met Swift
- Toast notifications voor user feedback
- **Security**
- Hergebruik Supabase RLS policies
- Authenticatie via bestaande Supabase Auth
- Input validation met Zod (bestaande schemas)
---
## 3. Epics & Stories Overzicht
| Epic ID | Titel | Doel | Status | Stories | Effort | Opmerkingen |
|---------|-------|------|--------|---------|--------|-------------|
| E-D1 | Afspraak & Rapportage | Afspraak plannen + rapportage koppelen | ⏳ To Do | 3 | 11 SP | Hergebruik appointment modal |
| E-D2 | Diagnose Beheer | Diagnose bekijken, toevoegen, bijstellen | ⏳ To Do | 3 | 10 SP | Hergebruik diagnose form |
**Totaal: 6 stories, 21 story points**
**Belangrijk:**
- Bouw per epic en per story
- Hergebruik bestaande componenten waar mogelijk
- Minimale aanpassingen aan bestaande code
- Database migraties: eerst aan Colin melden
---
## 4. Epics & Stories (Uitwerking)
### Epic D1 — Afspraak & Rapportage
**Epic Doel:** Werkende flow van diagnostiek-afspraak plannen tot rapportage schrijven met encounter koppeling.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|----------|--------------|---------------------|--------|------------------|--------------|
| E-D1.S1 | Intent patterns uitbreiden | Afspraak en rapportage intents herkend | ⏳ To Do | Swift foundation | 2 |
| E-D1.S2 | AfspraakBlock | Patiënt, datum/tijd, type, encounter_id terug | ⏳ To Do | E-D1.S1 | 5 |
| E-D1.S3 | RapportageBlock uitbreiden | Encounter_id koppeling, type "diagnostiek" | ⏳ To Do | E-D1.S2 | 4 |
**Technical Notes:**
```typescript
// E-D1.S1: Intent patterns toevoegen aan lib/swift/intent-classifier.ts
afspraak_maken: [
/^afspraak\s+(diagnostiek|behandeling)\s+(\w+)/i,
/^plan\s+(\w+)\s+(morgen|vandaag|volgende week)/i,
/^afspraak\s+(\w+)\s+(morgen|vandaag)\s+(\d{1,2}:\d{2})/i,
],
rapportage: [
/^rapportage\s+(diagnostiek|behandeling)\s+(\w+)/i,
/^gesprek gehad met\s+(\w+)/i,
/^verslag\s+(\w+)/i,
],
// E-D1.S2: AfspraakBlock
// components/swift/blocks/afspraak-block.tsx
// Hergebruik: app/epd/agenda/components/appointment-modal.tsx
// Aanpassingen:
// - Prefill vanuit intent (patient, date, time, type)
// - Dark theme styling (Swift context)
// - Encounter_id teruggeven via callback
// - Vereenvoudigde UI (geen linked reports sectie)
// E-D1.S3: RapportageBlock uitbreiden
// components/swift/blocks/rapportage-block.tsx
// Hergebruik: app/epd/patients/[id]/rapportage/components/report-composer.tsx
// Aanpassingen:
// - Encounter_id in prefill
// - Type "diagnostiek" toevoegen aan report types
// - Encounter link tonen indien gekoppeld
```
**API Routes:**
```typescript
// E-D1.S2: Appointments API
// app/api/appointments/route.ts
export async function POST(request: NextRequest) {
// Wrapper rond app/epd/agenda/actions.ts createEncounter
// Retourneert encounter_id voor volgende stap
}
// E-D1.S3: Reports API uitbreiden
// app/api/reports/route.ts (bestaand)
// Uitbreiden met encounter_id parameter
```
---
### Epic D2 — Diagnose Beheer
**Epic Doel:** Werkende diagnose overzicht en diagnose aanmaken/bijstellen met ICD-10 zoeker.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points |
|----------|--------------|---------------------|--------|------------------|--------------|
| E-D2.S1 | Intent patterns uitbreiden | Diagnose intents herkend | ⏳ To Do | Swift foundation | 2 |
| E-D2.S2 | DiagnoseBlock | Overzicht diagnoses, filter actief/inactief | ⏳ To Do | E-D2.S1 | 3 |
| E-D2.S3 | DiagnoseFormBlock | ICD-10 zoeker, type, status, ernst | ⏳ To Do | E-D2.S2 | 5 |
**Technical Notes:**
```typescript
// E-D2.S1: Intent patterns toevoegen
diagnose_bekijken: [
/^diagnose\s+(\w+)/i,
/^diagnoses van\s+(\w+)/i,
/^wat zijn de diagnoses/i,
],
diagnose_toevoegen: [
/^diagnose toevoegen\s+(\w+)\s+([A-Z]\d+\.\d+)/i,
/^(\w+)\s+heeft\s+([A-Z]\d+\.\d+)/i,
/^diagnose\s+(\w+)\s+([A-Z]\d+\.\d+)/i,
],
diagnose_wijzigen: [
/^diagnose wijzigen\s+(\w+)/i,
/^diagnose bijstellen/i,
],
// E-D2.S2: DiagnoseBlock
// components/swift/blocks/diagnose-block.tsx
// Hergebruik logica van: app/epd/patients/[id]/diagnose/page.tsx
// Aanpassingen:
// - Swift styling (dark theme)
// - Filter tabs: Actief / Inactief / Alle
// - Klik op diagnose → DiagnoseFormBlock (edit mode)
// - "Nieuwe diagnose" knop → DiagnoseFormBlock (create mode)
// E-D2.S3: DiagnoseFormBlock
// components/swift/blocks/diagnose-form-block.tsx
// Hergebruik: app/epd/patients/[id]/diagnose/components/diagnosis-detail-form.tsx
// Aanpassingen:
// - ICD-10 zoeker met fuzzy search (verbeteren)
// - Swift styling
// - Prefill vanuit intent (ICD-10 code)
// - Encounter_id koppeling (optioneel)
```
**API Routes:**
```typescript
// E-D2.S2 + E-D2.S3: Diagnoses API
// app/api/diagnoses/route.ts
export async function GET(request: NextRequest) {
// GET /api/diagnoses/:patientId
// Wrapper rond app/epd/patients/[id]/diagnose/actions.ts getPatientDiagnoses
}
export async function POST(request: NextRequest) {
// POST /api/diagnoses
// Wrapper rond app/epd/patients/[id]/diagnose/actions.ts createPatientDiagnosis
}
export async function PATCH(request: NextRequest) {
// PATCH /api/diagnoses/:id
// Wrapper rond app/epd/patients/[id]/diagnose/actions.ts updatePatientDiagnosis
}
```
---
## 5. Kwaliteit & Testplan
### 5.1 Test Types
| Test Type | Scope | Tools | Wanneer | Verantwoordelijke |
|-----------|-------|-------|---------|-------------------|
| Unit Tests | Intent patterns, entity extraction | Vitest | E-D1.S1, E-D2.S1 | Developer |
| Integration Tests | API endpoints | Vitest + MSW | E-D1.S2, E-D1.S3, E-D2.S3 | Developer |
| Component Tests | Blocks | React Testing Library | E-D1.S2, E-D1.S3, E-D2.S2, E-D2.S3 | Developer |
| Manual Tests | Complete workflow | Checklist | E-D2.S3 | QA / Developer |
### 5.2 Test Coverage Targets
| Area | Target | Reden |
|------|--------|-------|
| Intent patterns | 85%+ | Correcte intent herkenning |
| Entity extraction | 80%+ | Pre-fill correctheid |
| API routes | 80%+ | Data integrity |
| Block components | 60%+ | Belangrijkste flows |
### 5.3 Manual Test Checklist (Diagnostiek Workflow)
**Happy Flow:**
- [ ] "afspraak diagnostiek met jan morgen 10:00" → AfspraakBlock opent
- [ ] Afspraak opslaan → encounter_id teruggegeven
- [ ] "rapportage diagnostiek gesprek met jan" → RapportageBlock met encounter koppeling
- [ ] Rapportage opslaan → gekoppeld aan encounter
- [ ] "diagnose jan" → DiagnoseBlock met overzicht
- [ ] "diagnose toevoegen jan F41.1" → DiagnoseFormBlock met ICD-10 pre-filled
- [ ] Diagnose opslaan → toegevoegd aan overzicht
- [ ] Klik op diagnose → DiagnoseFormBlock (edit mode)
- [ ] Status wijzigen → diagnose bijgewerkt
**Error Scenarios:**
- [ ] Geen patiënt gevonden → ZoekenBlock
- [ ] Ongeldige datum → validatie fout
- [ ] ICD-10 code niet gevonden → validatie fout
- [ ] Encounter niet gevonden → rapportage zonder koppeling
- [ ] Network error → toast met retry
---
## 6. Demo & Presentatieplan
### 6.1 Demo Scenario
**Duur:** 5 minuten
**Doelgroep:** Behandelaars, psychologen, psychiaters
**Locatie:** Live op Vercel
**Flow:**
```
1. INTRO (30 sec)
"Behandelaars besteden veel tijd aan administratie.
Swift maakt diagnostiek-trajecten sneller."
2. AFSPRAAK PLANNEN (1 min)
- Typ: "afspraak diagnostiek met jan morgen 10:00"
- AfspraakBlock verschijnt voorgevuld
- Opslaan → afspraak aangemaakt
3. RAPPORTAGE SCHRIJVEN (1.5 min)
- Typ: "rapportage diagnostiek gesprek met jan"
- RapportageBlock verschijnt met encounter koppeling
- Schrijf/dicteer verslag
- Opslaan → gekoppeld aan afspraak
4. DIAGNOSE STELLEN (2 min)
- Typ: "diagnose jan"
- DiagnoseBlock toont overzicht
- Typ: "diagnose toevoegen jan F41.1"
- DiagnoseFormBlock met ICD-10 pre-filled
- Vul type, status, ernst in
- Opslaan → diagnose toegevoegd
5. AFSLUITING (20 sec)
- Complete flow in 5 minuten
- Vragen
```
### 6.2 Backup Plan
| Probleem | Oplossing |
|----------|-----------|
| Internet issues | Localhost met demo data |
| AI API down | Pre-cached responses |
| Encounter niet gevonden | Demo met pre-made encounter |
| Complete failure | Video recording |
---
## 7. Risico's & Mitigatie
| Risico | Kans | Impact | Mitigatie | Owner |
|--------|------|--------|-----------|-------|
| Hergebruik componenten complex | Middel | Hoog | Stapsgewijs aanpassen, tests per stap | Dev |
| ICD-10 zoeker niet accuraat | Middel | Middel | Fuzzy search verbeteren, externe API optie | Dev |
| Encounter koppeling faalt | Laag | Middel | Duidelijke UX, optionele koppeling | Dev |
| Datum parsing onnauwkeurig | Middel | Laag | Fallback naar date picker | Dev |
| Bestaande code breaking changes | Laag | Hoog | Wrapper pattern, geen directe wijzigingen | Dev |
---
## 8. Sprint Planning
### Sprint 1 (Week 1): Afspraak & Rapportage
- E-D1.S1: Intent patterns uitbreiden (2 SP)
- E-D1.S2: AfspraakBlock (5 SP)
- E-D1.S3: RapportageBlock uitbreiden (4 SP)
- **Deliverable:** Afspraak en rapportage flow werkend
### Sprint 2 (Week 2): Diagnose Beheer
- E-D2.S1: Intent patterns uitbreiden (2 SP)
- E-D2.S2: DiagnoseBlock (3 SP)
- E-D2.S3: DiagnoseFormBlock (5 SP)
- Bug fixes + demo prep
- **Deliverable:** Complete diagnostiek workflow werkend
---
## 9. Definition of Done
Een story is **Done** wanneer:
- [ ] Code geschreven en werkend
- [ ] TypeScript types correct
- [ ] Component responsive (mobile + desktop)
- [ ] Error states afgehandeld
- [ ] Hergebruik bestaande code geïmplementeerd
- [ ] Geen breaking changes in bestaande code
- [ ] Getest in Chrome + Safari
- [ ] Demo scenario werkt
Een epic is **Done** wanneer:
- [ ] Alle stories Done
- [ ] Integration test passed
- [ ] Complete workflow werkt end-to-end
---
## 10. Referenties
### Project Documenten
- FO Diagnostiek: `docs/swift/swift-fo-diagnostiek-workflow.md`
- FO Algemeen: `docs/swift/swift-fo-ai.md`
- Bouwplan Swift: `docs/swift/bouwplan-swift-v1.md`
- UX: `docs/swift/swift-ux-v2.1.md`
### Bestaande Code Referenties (Hergebruik)
| Component | Locatie | Hergebruik % | Aanpassingen |
|-----------|---------|--------------|-------------|
| Appointment Modal | `app/epd/agenda/components/appointment-modal.tsx` | 80% | Prefill, dark theme, encounter_id callback |
| Report Composer | `app/epd/patients/[id]/rapportage/components/report-composer.tsx` | 80% | Encounter koppeling, type "diagnostiek" |
| Diagnose Page | `app/epd/patients/[id]/diagnose/page.tsx` | 70% | Overzicht logica, Swift styling |
| Diagnose Form | `app/epd/patients/[id]/diagnose/components/diagnosis-detail-form.tsx` | 80% | ICD-10 zoeker verbeteren, Swift styling |
| Diagnose Actions | `app/epd/patients/[id]/diagnose/actions.ts` | 90% | Direct hergebruik via API wrapper |
| Encounter Actions | `app/epd/agenda/actions.ts` | 90% | Direct hergebruik via API wrapper |
### External
- ICD-10 codes: https://www.who.int/standards/classifications/classification-of-diseases
- FHIR Encounter: https://www.hl7.org/fhir/encounter.html
- FHIR Condition: https://www.hl7.org/fhir/condition.html
---
## 11. Glossary & Abbreviations
| Term | Betekenis |
|------|-----------|
| Encounter | Afspraak/contactmoment (FHIR term) |
| Condition | Diagnose (FHIR term) |
| ICD-10 | International Classification of Diseases versie 10 |
| Hoofddiagnose | Primaire diagnose |
| Nevendiagnose | Secundaire diagnose |
| Clinical Status | Status van diagnose (actief, inactief, resolved, etc.) |
| Severity | Ernst van diagnose (mild, matig, ernstig) |
---
**Versiehistorie:**
| Versie | Datum | Auteur | Wijziging |
|--------|-------|--------|-----------|
| v1.0 | 23-12-2024 | Claude | Initiële versie - Apart bouwplan voor diagnostiek workflow |

View File

@@ -131,12 +131,12 @@ lib/
|---------|-------|------|--------|---------|--------| |---------|-------|------|--------|---------|--------|
| E0 | Setup & Foundation | Zustand, routing, base layout | ✅ Done | 4 | 8 SP | | E0 | Setup & Foundation | Zustand, routing, base layout | ✅ Done | 4 | 8 SP |
| E1 | Command Center | Input, voice, context bar | ✅ Done | 5 | 13 SP | | E1 | Command Center | Input, voice, context bar | ✅ Done | 5 | 13 SP |
| E2 | Intent Classification | Local + AI fallback | ✅ Done | 4 | 10 SP | | E2 | Intent Classification | Local + AI fallback + wiring | ⏳ In Progress | 5 | 12 SP |
| E3 | P1 Blocks | Dagnotitie, Zoeken, Overdracht | ⏳ To Do | 6 | 21 SP | | E3 | P1 Blocks | Dagnotitie, Zoeken, Overdracht | ⏳ To Do | 6 | 21 SP |
| E4 | Navigation & Auth | Login keuze, routing, preferences | ⏳ To Do | 4 | 8 SP | | E4 | Navigation & Auth | Login keuze, routing, preferences | ⏳ To Do | 4 | 8 SP |
| E5 | Polish & Testing | Animaties, error handling, tests | ⏳ To Do | 4 | 8 SP | | E5 | Polish & Testing | Animaties, error handling, tests | ⏳ To Do | 4 | 8 SP |
**Totaal: 27 stories, 68 story points (31 SP done, 37 SP remaining)** **Totaal: 28 stories, 70 story points (31 SP done, 39 SP remaining)**
**Belangrijk:** **Belangrijk:**
- Bouw per epic en per story, niet alles tegelijk - Bouw per epic en per story, niet alles tegelijk
@@ -225,6 +225,7 @@ Command Center Layout:
| E2.S2 | Entity extraction | Patient naam, categorie uit input | ✅ | E2.S1 | 3 | | E2.S2 | Entity extraction | Patient naam, categorie uit input | ✅ | E2.S1 | 3 |
| E2.S3 | AI fallback | Claude Haiku bij confidence <0.8 | ✅ | E2.S2 | 2 | | E2.S3 | AI fallback | Claude Haiku bij confidence <0.8 | ✅ | E2.S2 | 2 |
| E2.S4 | Intent API route | POST /api/intent/classify | ✅ | E2.S3 | 2 | | E2.S4 | Intent API route | POST /api/intent/classify | ✅ | E2.S3 | 2 |
| E2.S5 | Input → Block wiring | CommandInput submit → API → openBlock | ⏳ | E2.S4 | 2 |
**Technical Notes:** **Technical Notes:**
```typescript ```typescript
@@ -246,6 +247,22 @@ const INTENT_PATTERNS = {
/^wat moet ik weten/i, /^wat moet ik weten/i,
], ],
}; };
// E2.S5: Input → Block wiring
// In CommandInput.handleSubmit:
const handleSubmit = async () => {
const response = await fetch('/api/intent/classify', {
method: 'POST',
body: JSON.stringify({ input: inputValue }),
});
const { intent, confidence, entities } = await response.json();
if (intent !== 'unknown' && confidence >= 0.5) {
openBlock(intent, entities); // Open juiste block met prefill
} else {
// Toon FallbackPicker (E4.S4)
}
};
``` ```
--- ---
@@ -533,4 +550,5 @@ Een epic is **Done** wanneer:
| v1.1 | 23-12-2024 | Claude | E0 + E1 voltooid (21 SP) | | v1.1 | 23-12-2024 | Claude | E0 + E1 voltooid (21 SP) |
| v1.2 | 23-12-2024 | Claude | E2.S1 + E2.S2 voltooid (27 SP) | | v1.2 | 23-12-2024 | Claude | E2.S1 + E2.S2 voltooid (27 SP) |
| v1.3 | 23-12-2024 | Claude | E2.S3 AI fallback voltooid (29 SP) | | v1.3 | 23-12-2024 | Claude | E2.S3 AI fallback voltooid (29 SP) |
| v1.4 | 23-12-2024 | Claude | E2 Intent Classification voltooid (31 SP) | | v1.4 | 23-12-2024 | Claude | E2.S1-S4 voltooid (31 SP) |
| v1.5 | 24-12-2024 | Claude | E2.S5 toegevoegd: Input → Block wiring (+2 SP) |

View File

@@ -0,0 +1,658 @@
# Mission Control — Bouwplan Swift v2.0
**Projectnaam:** Swift — Contextual UI EPD
**Versie:** v2.0
**Datum:** 24-12-2024
**Auteur:** Colin Lit / Development Team
---
## Changelog v2.0
> **Belangrijke wijzigingen t.o.v. v1.5:**
> - E3.S0 toegevoegd: CanvasArea block rendering (kritieke blokkade)
> - Technische debt sectie toegevoegd (type duplicatie, keyboard shortcuts)
> - Diagnostiek Workflow als uitbreiding opgenomen
> - Sprint planning aangepast aan huidige voortgang
> - Totalen bijgewerkt: 29 stories, 72 SP
---
## 1. Doel en context
### 1.1 Projectdoel
Swift is een **Contextual UI** interface voor het Mini-EPD systeem. In plaats van navigatie door menu's spreekt of typt de gebruiker een intentie — en het juiste UI-blok verschijnt voorgevuld met relevante data.
**Kernbelofte:**
> Van 12 klikken en 3 minuten naar 1 zin en 15 seconden.
### 1.2 Business Case
| Metric | Huidig | Met Swift | Besparing |
|--------|--------|-----------|-----------|
| Dagnotitie maken | 3-5 min | 15 sec | 95% |
| Patiënt zoeken | 1-2 min | 5 sec | 95% |
| Overdracht maken | 20-30 min | 5 min | 80% |
| Rapportage schrijven | 8-15 min | 2-3 min | 75% |
**Per verpleegkundige per dag: ~4 uur terug naar zorg**
### 1.3 Relatie met Documentatie
| Document | Beschrijft | Locatie |
|----------|------------|---------|
| PRD | Product visie, requirements | `swift-prd.md` |
| FO | Functionele flows, blocks | `swift-fo-ai.md` |
| TO | Technische architectuur | `to-swift-v1.md` |
| UX | Visuele specificaties | `swift-ux-v2.1.md` |
| **Diagnostiek FO** | Behandelaar workflow | `swift-fo-diagnostiek-workflow.md` |
| **Diagnostiek Bouwplan** | Uitbreiding voor behandelaars | `bouwplan-swift-diagnostiek-workflow.md` |
---
## 2. Uitgangspunten
### 2.1 Technische Stack
**Bestaand (hergebruiken):**
| Component | Technologie | Versie |
|-----------|-------------|--------|
| Framework | Next.js | 14.2.18 |
| UI Library | React | 18.3.1 |
| Styling | TailwindCSS | 3.4.18 |
| Components | shadcn/ui | - |
| Command Palette | cmdk | 1.1.1 |
| Animations | framer-motion | 12.23.24 |
| Database | Supabase | 2.81.1 |
| Speech-to-Text | Deepgram | 4.11.2 |
| AI | Claude API | - |
| Validation | Zod | 4.1.12 |
| Forms | react-hook-form | 7.66.1 |
**Nieuw toegevoegd:**
| Component | Technologie | Versie | Status |
|-----------|-------------|--------|--------|
| State Management | Zustand | 5.0.9 | ✅ Geïnstalleerd |
### 2.2 Projectkaders
| Kader | Waarde |
|-------|--------|
| **Bouwtijd** | 4 weken (4 sprints) |
| **Team** | 1 developer |
| **Scope** | MVP: P1 blocks (dagnotitie, zoeken, overdracht) |
| **Uitbreiding** | Diagnostiek workflow (optioneel, +22 SP) |
| **Data** | Bestaande Supabase database |
| **Doel** | Werkende demo + user testing |
### 2.3 Programmeer Uitgangspunten
**Code Quality Principles:**
- **DRY (Don't Repeat Yourself)**
- Herbruikbare block componenten
- Centrale intent classificatie logica
- Shared hooks voor common patterns
- **Let op:** Types importeren uit `lib/swift/types.ts`, niet dupliceren
- **KISS (Keep It Simple, Stupid)**
- Local-first intent classificatie (regex)
- AI alleen als fallback
- Minimale state complexity
- **SOC (Separation of Concerns)**
- UI blocks gescheiden van intent logic
- API routes gescheiden van business logic
- Store slices per domein
- **YAGNI (You Aren't Gonna Need It)**
- Alleen P1 blocks in MVP
- Geen toggle tussen interfaces
- Geen advanced analytics in v1
**Development Practices:**
```typescript
// ✅ Goede structuur voor Swift components
components/
swift/
command-center/
command-center.tsx // Main container
command-input.tsx // Input component
canvas-area.tsx // Block rendering ← KRITIEK
index.ts // Barrel export
blocks/
block-container.tsx // Wrapper met animaties
dagnotitie-block.tsx
zoeken-block.tsx
overdracht-block.tsx
index.ts
// ✅ Store importeert types (geen duplicatie)
stores/
swift-store.ts // Importeert uit lib/swift/types.ts
// ✅ Intent classification
lib/
swift/
types.ts // SINGLE SOURCE OF TRUTH voor types
intent-classifier.ts // Local classification
intent-classifier-ai.ts // AI fallback
entity-extractor.ts // Entity parsing
```
---
## 3. Epics & Stories Overzicht
| Epic ID | Titel | Doel | Status | Stories | Effort |
|---------|-------|------|--------|---------|--------|
| E0 | Setup & Foundation | Zustand, routing, base layout | ✅ Done | 4 | 8 SP |
| E1 | Command Center | Input, voice, context bar | ✅ Done | 5 | 13 SP |
| E2 | Intent Classification | Local + AI fallback + wiring | ✅ Done | 5 | 12 SP |
| E3 | P1 Blocks | Dagnotitie, Zoeken, Overdracht | ⏳ To Do | 7 | 23 SP |
| E4 | Navigation & Auth | Login keuze, routing, preferences | ⏳ To Do | 4 | 8 SP |
| E5 | Polish & Testing | Animaties, error handling, tests | ⏳ To Do | 4 | 8 SP |
**Totaal: 29 stories, 72 story points**
| Categorie | SP |
|-----------|----:|
| ✅ Done (E0 + E1 + E2) | 33 |
| ⏳ Remaining | 39 |
**Belangrijk:**
- Bouw per epic en per story, niet alles tegelijk
- Dependencies installeren: eerst aan Colin melden
- Database migraties: eerst aan Colin melden
---
## 4. Epics & Stories (Uitwerking)
### Epic 0 — Setup & Foundation ✅ DONE
**Epic Doel:** Werkende development omgeving met Zustand store en Swift routing.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP |
|----------|--------------|---------------------|--------|------|----|
| E0.S1 | Zustand installeren | `pnpm add zustand` succesvol, import werkt | ✅ | — | 1 |
| E0.S2 | Swift store opzetten | `stores/swift-store.ts` met basis state | ✅ | E0.S1 | 2 |
| E0.S3 | Swift route aanmaken | `/epd/swift` route met eigen layout | ✅ | E0.S2 | 2 |
| E0.S4 | Swift folder structuur | `components/swift/`, `lib/swift/` aangemaakt | ✅ | E0.S3 | 3 |
---
### Epic 1 — Command Center ✅ DONE
**Epic Doel:** Werkende command center met tekst en voice input.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP |
|----------|--------------|---------------------|--------|------|----|
| E1.S1 | Command Center layout | 4-zone layout (context, canvas, recent, input), keyboard shortcuts (⌘K, Escape) | ✅ | E0.S4 | 3 |
| E1.S2 | Context Bar | Dienst indicator, patiënt chip, user info | ✅ | E1.S1 | 2 |
| E1.S3 | Command Input | Tekst input met placeholder, focus state, send button | ✅ | E1.S1 | 2 |
| E1.S4 | Voice Input integratie | Deepgram streaming, waveform visualisatie | ✅ | E1.S3 | 3 |
| E1.S5 | Recent Strip | Laatste 5 acties als chips, click-to-repeat | ✅ | E1.S1 | 3 |
**Technical Notes:**
```
Command Center Layout:
┌─────────────────────────────────────────┐
│ Context Bar (48px) │
├─────────────────────────────────────────┤
│ │
│ Canvas Area (flex) ← Blocks hier │
│ │
├─────────────────────────────────────────┤
│ Recent Strip (48px) │
├─────────────────────────────────────────┤
│ Command Input (64px, fixed bottom) │
└─────────────────────────────────────────┘
```
---
### Epic 2 — Intent Classification ✅ DONE
**Epic Doel:** Two-tier intent classificatie (local + AI fallback) + wiring naar blocks.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP |
|----------|--------------|---------------------|--------|------|----|
| E2.S1 | Local classifier | Regex patterns voor P1 intents, <50ms | ✅ | E0.S4 | 3 |
| E2.S2 | Entity extraction | Patient naam, categorie uit input | ✅ | E2.S1 | 3 |
| E2.S3 | AI fallback | Claude Haiku bij confidence <0.8 | ✅ | E2.S2 | 2 |
| E2.S4 | Intent API route | POST /api/intent/classify met logging | ✅ | E2.S3 | 2 |
| E2.S5 | Input → Block wiring | CommandInput.handleSubmit → API → openBlock | ✅ | E2.S4 | 2 |
**E2.S5 Technical Notes (✅ GEÏMPLEMENTEERD):**
```typescript
// components/swift/command-center/command-input.tsx
// IMPLEMENTATIE:
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!hasValue || isProcessing) return;
const inputText = inputValue.trim();
setIsProcessing(true);
try {
const response = await fetch('/api/intent/classify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input: inputText }),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Onbekende fout' }));
throw new Error(errorData.error || `HTTP ${response.status}`);
}
const { intent, confidence, entities } = await response.json();
if (intent !== 'unknown' && confidence >= 0.5) {
openBlock(intent as BlockType, entities);
addRecentAction({ intent, label: inputText.slice(0, 50), patientName: entities.patientName });
clearInput();
} else {
// Tijdelijke fallback naar dagnotitie (wordt vervangen door FallbackPicker in E4.S4)
openBlock('dagnotitie', { content: inputText });
addRecentAction({ intent: 'dagnotitie', label: inputText.slice(0, 50) });
clearInput();
}
} catch (error) {
// Error handling met fallback naar dagnotitie
openBlock('dagnotitie', { content: inputText });
addRecentAction({ intent: 'dagnotitie', label: inputText.slice(0, 50) });
clearInput();
} finally {
setIsProcessing(false);
}
};
```
---
### Epic 3 — P1 Blocks ⏳ TO DO
**Epic Doel:** Werkende DagnotatieBlock, ZoekenBlock en OverdrachtBlock.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP |
|----------|--------------|---------------------|--------|------|----|
| E3.S0 | CanvasArea block rendering | Switch/case voor block types, prefill doorgeven | ⏳ | E2.S5 | 2 |
| E3.S1 | Block Container | Animatie wrapper, close button, sizes | ⏳ | E1.S1 | 2 |
| E3.S2 | DagnotatieBlock | Patient, categorie, tekst, opslaan naar /api/reports | ⏳ | E3.S0, E3.S1 | 5 |
| E3.S3 | Patient search API | GET /api/patients/search?q= fuzzy search | ⏳ | E0.S4 | 3 |
| E3.S4 | ZoekenBlock | Input, resultaten, selectie → store | ⏳ | E3.S0, E3.S3 | 3 |
| E3.S5 | PatientContextCard | Na selectie: notities, vitals, diagnose | ⏳ | E3.S4 | 5 |
| E3.S6 | OverdrachtBlock | AI samenvatting per patiënt (bestaande API) | ⏳ | E3.S0 | 3 |
**E3.S0 Technical Notes (KRITIEK - NIEUW):**
```typescript
// components/swift/command-center/canvas-area.tsx
// HUIDIGE SITUATIE (placeholder):
{activeBlock ? (
<div className="text-slate-400">Block: {activeBlock}</div>
) : (
<EmptyState />
)}
// MOET WORDEN:
import { DagnotatieBlock } from '../blocks/dagnotitie-block';
import { ZoekenBlock } from '../blocks/zoeken-block';
import { OverdrachtBlock } from '../blocks/overdracht-block';
function renderBlock(activeBlock: BlockType, prefillData: BlockPrefillData) {
switch (activeBlock) {
case 'dagnotitie':
return <DagnotatieBlock prefill={prefillData} />;
case 'zoeken':
return <ZoekenBlock prefill={prefillData} />;
case 'overdracht':
return <OverdrachtBlock prefill={prefillData} />;
default:
return <EmptyState />;
}
}
// In CanvasArea:
{activeBlock ? (
<AnimatePresence mode="wait">
<motion.div key={activeBlock} {...blockAnimations}>
{renderBlock(activeBlock, prefillData)}
</motion.div>
</AnimatePresence>
) : (
<EmptyState />
)}
```
**E3.S2 Technical Notes:**
```typescript
// components/swift/blocks/dagnotitie-block.tsx
interface DagnotitieBlockProps {
prefill?: {
patientId?: string;
patientName?: string;
category?: VerpleegkundigCategory;
content?: string;
};
}
// 1. Patient lookup: patientName → patientId via E3.S3 API
// 2. Category selector: medicatie | adl | gedrag | incident | observatie
// 3. Tekst input: textarea of rich editor
// 4. Opslaan: POST /api/reports met type 'verpleegkundig'
```
---
### Epic 4 — Navigation & Auth ⏳ TO DO
**Epic Doel:** Login pagina met interface keuze, routing naar Swift/Klassiek.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP |
|----------|--------------|---------------------|--------|------|----|
| E4.S1 | Login form uitbreiden | Interface selector (Swift/Klassiek) | ⏳ | E0.S3 | 2 |
| E4.S2 | Preference opslag | user_metadata.preferred_interface | ⏳ | E4.S1 | 2 |
| E4.S3 | Redirect middleware | /epd → preference route | ⏳ | E4.S2 | 2 |
| E4.S4 | Fallback Picker | Visuele keuze bij lage confidence | ⏳ | E3.S0 | 2 |
---
### Epic 5 — Polish & Testing ⏳ TO DO
**Epic Doel:** Gepolijste UX met animaties, error handling en tests.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP |
|----------|--------------|---------------------|--------|------|----|
| E5.S1 | Block animaties | Slide up/down met framer-motion | ⏳ | E3.S0 | 2 |
| E5.S2 | Error handling | Network errors, validation, toasts | ⏳ | E3.S6 | 2 |
| E5.S3 | Keyboard shortcuts | Verificatie bestaande shortcuts werken | ⏳ | E1.S1 | 2 |
| E5.S4 | Smoke tests | Happy flow tests voor alle P1 blocks | ⏳ | E5.S2 | 2 |
**Nota:** Keyboard shortcuts (⌘K focus, Escape close) zijn al geïmplementeerd in E1.S1.
E5.S3 is nu verificatie + eventuele uitbreiding (Enter submit, etc.).
---
## 5. Technische Debt & Bekende Issues
### 5.1 Type Duplicatie (Medium Priority)
**Probleem:**
```typescript
// lib/swift/types.ts - BRON
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';
// stores/swift-store.ts - DUPLICAAT (moet verwijderd worden)
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';
```
**Impact:** Bij uitbreiding intents (diagnostiek workflow) moet op twee plekken gewijzigd worden.
**Oplossing:** Verwijder duplicaat uit `swift-store.ts`, importeer uit `lib/swift/types.ts`:
```typescript
// stores/swift-store.ts
import type { SwiftIntent, BlockType, ShiftType } from '@/lib/swift/types';
```
**Wanneer:** Voorafgaand aan E-D1.S1 (diagnostiek intent patterns).
### 5.2 BlockContainer Animaties (Low Priority)
**Probleem:** BlockContainer bestaat maar heeft nog geen framer-motion animaties.
**Huidige situatie:**
```typescript
// components/swift/blocks/block-container.tsx
// Geen AnimatePresence of motion.div
```
**Oplossing:** Implementeren in E5.S1 of integreren in E3.S0.
### 5.3 CanvasArea Placeholder (High Priority - RESOLVED)
**Probleem:** CanvasArea toont placeholder tekst i.p.v. blocks.
**Oplossing:** Story E3.S0 toegevoegd. Dit is de hoogste prioriteit na E2.S5.
---
## 6. Kwaliteit & Testplan
### 6.1 Test Types
| Test Type | Scope | Tools | Wanneer |
|-----------|-------|-------|---------|
| Unit Tests | Intent classifier, entity extraction | Vitest | E2 |
| Integration Tests | API endpoints | Vitest + MSW | E2, E3 |
| Component Tests | Blocks, Command Center | React Testing Library | E3, E5 |
| E2E Tests | Complete flows | Playwright (optioneel) | E5 |
| Manual Tests | Demo scenarios | Checklist | E5 |
### 6.2 Test Coverage Targets
| Area | Target | Reden |
|------|--------|-------|
| Intent classifier | 90%+ | Kritiek voor UX |
| API routes | 80%+ | Data integrity |
| UI components | 60%+ | Belangrijkste flows |
### 6.3 Manual Test Checklist (MVP Demo)
**Happy Flows:**
- [ ] User kan inloggen en Swift kiezen
- [ ] Command input krijgt focus met Cmd+K
- [ ] "notitie jan medicatie" → DagnotatieBlock opent met prefill
- [ ] Dagnotitie opslaan → toast + block sluit
- [ ] "zoek marie" → ZoekenBlock met resultaten
- [ ] Patiënt selecteren → PatientContextCard
- [ ] "overdracht" → OverdrachtBlock met AI samenvatting
- [ ] Voice input → transcript in command input
**Error Scenarios:**
- [ ] Onbekende intent → FallbackPicker
- [ ] Network error → toast met retry
- [ ] Lege notitie → validation error
- [ ] Geen zoekresultaten → "Geen patiënten gevonden"
---
## 7. Demo & Presentatieplan
### 7.1 Demo Scenario
**Duur:** 10 minuten
**Doelgroep:** Zorgprofessionals, management
**Locatie:** Live op Vercel
**Flow:**
```
1. INTRO (1 min)
"40% van je tijd gaat naar administratie.
Wat als je gewoon kon zeggen wat je wilt?"
2. CONTRAST (2 min)
Klassiek EPD: Dashboard → Patiënten → Jan → Rapportages →
Nieuwe → Type → Tekst → Opslaan
Swift: "notitie jan medicatie gegeven" → Opslaan
3. DAGNOTITIE FLOW (2 min)
- Typ: "notitie jan medicatie uitgereikt"
- Block verschijnt voorgevuld
- Één klik: opgeslagen
4. VOICE DEMO (2 min)
- Klik microfoon
- Spreek: "marie had een rustige nacht, goed geslapen"
- Block verschijnt met transcript
5. OVERDRACHT (2 min)
- "overdracht"
- AI genereert samenvatting per patiënt
- Toon bronverwijzingen
6. AFSLUITING (1 min)
- Tijdsbesparing recap
- Vragen
```
### 7.2 Backup Plan
| Probleem | Oplossing |
|----------|-----------|
| Internet issues | Localhost met demo data |
| Voice niet werkt | Type-only demo |
| AI API down | Pre-cached responses |
| Complete failure | Video recording |
---
## 8. Risico's & Mitigatie
| Risico | Kans | Impact | Mitigatie | Owner |
|--------|------|--------|-----------|-------|
| Voice accuracy NL | Middel | Hoog | Deepgram NL model, fallback naar tekst | Dev |
| Intent misclassificatie | Middel | Hoog | Two-tier systeem, FallbackPicker | Dev |
| AI latency | Laag | Middel | Local-first, Haiku model | Dev |
| User adoption | Middel | Middel | Keuze behouden, geen dwang | Product |
| Scope creep | Hoog | Hoog | Strict P1-only, backlog voor rest | Dev |
| Performance | Laag | Middel | Code splitting, lazy loading | Dev |
| **CanvasArea blocking** | Hoog | Hoog | E3.S0 prioriteit na E2.S5 | Dev |
---
## 9. Sprint Planning (Aangepast)
### Huidige Status (24-12-2024)
- ✅ E0: Setup & Foundation (8 SP) — DONE
- ✅ E1: Command Center (13 SP) — DONE
- ✅ E2: Intent Classification (12 SP) — DONE
- ⏳ E3-E5: Remaining (39 SP) — TO DO
**Totaal Done: 33 SP / 72 SP (46%)**
### Sprint 3 (Huidige Sprint): Core Wiring + First Block
- ✅ E2.S5: Input → Block wiring (2 SP) — DONE
- E3.S0: CanvasArea block rendering (2 SP) ← **KRITIEK**
- E3.S1: Block Container (2 SP)
- E3.S2: DagnotatieBlock (5 SP)
- **Deliverable:** "notitie jan" → DagnotatieBlock werkt end-to-end
### Sprint 4: Remaining Blocks
- E3.S3: Patient search API (3 SP)
- E3.S4: ZoekenBlock (3 SP)
- E3.S5: PatientContextCard (5 SP)
- E3.S6: OverdrachtBlock (3 SP)
- **Deliverable:** Alle P1 blocks werken
### Sprint 5: Polish & Ship
- E4: Navigation & Auth (8 SP)
- E5: Polish & Testing (8 SP)
- Technische debt opruimen
- **Deliverable:** Demo-ready MVP
---
## 10. Uitbreidingen (Backlog)
### Diagnostiek Workflow (22 SP)
**Status:** Gepland na MVP
**Documentatie:** `bouwplan-swift-diagnostiek-workflow.md`
| Epic | Stories | SP | Vereisten |
|------|---------|----:|-----------|
| E-D1 | Afspraak & Rapportage | 12 | E3 compleet |
| E-D2 | Diagnose Beheer | 10 | E3 compleet |
**Pre-requisites:**
1. Swift MVP compleet (E0-E5)
2. Report type "diagnostiek" toevoegen aan REPORT_TYPES
3. SwiftIntent uitbreiden met nieuwe types
4. Entity extraction voor datum/tijd
**Zie:** `bouwplan-swift-diagnostiek-workflow-beoordeling.md` voor details.
---
## 11. Definition of Done
Een story is **Done** wanneer:
- [ ] Code geschreven en werkend
- [ ] TypeScript types correct (geen duplicaten)
- [ ] Component responsive (mobile + desktop)
- [ ] Error states afgehandeld
- [ ] Toegankelijkheid basics (focus, labels)
- [ ] Getest in Chrome + Safari
- [ ] PR reviewed (indien team)
- [ ] Gemerged naar main
Een epic is **Done** wanneer:
- [ ] Alle stories Done
- [ ] Integration test passed
- [ ] Demo scenario werkt
---
## 12. Referenties
### Project Documenten
- PRD: `docs/swift/swift-prd.md`
- FO: `docs/swift/swift-fo-ai.md`
- TO: `docs/swift/to-swift-v1.md`
- UX: `docs/swift/swift-ux-v2.1.md`
- Diagnostiek FO: `docs/swift/swift-fo-diagnostiek-workflow.md`
- Diagnostiek Bouwplan: `docs/swift/bouwplan-swift-diagnostiek-workflow.md`
- Beoordeling: `docs/swift/bouwplan-swift-diagnostiek-workflow-beoordeling.md`
### Bestaande Code Referenties
- Swift Store: `stores/swift-store.ts`
- Swift Types: `lib/swift/types.ts` (SINGLE SOURCE)
- Intent Classifier: `lib/swift/intent-classifier.ts`
- Intent API: `app/api/intent/classify/route.ts`
- Command Center: `components/swift/command-center/`
- Block Container: `components/swift/blocks/block-container.tsx`
### Bestaande EPD Code (Hergebruik)
- Command component: `components/ui/command.tsx`
- Speech streaming: `components/speech-recorder-streaming.tsx`
- Overdracht API: `app/api/overdracht/generate/route.ts`
- Report types: `lib/types/report.ts`
- Reports API: `app/api/reports/route.ts`
### External
- Zustand: https://zustand-demo.pmnd.rs/
- cmdk: https://cmdk.paco.me/
- Deepgram: https://developers.deepgram.com/docs
- Claude API: https://docs.anthropic.com/
- Framer Motion: https://www.framer.com/motion/
---
## 13. Glossary
| Term | Betekenis |
|------|-----------|
| Swift | Projectnaam voor Contextual UI EPD |
| Command Center | Hoofdscherm met één input |
| Block | Ephemeral UI component (dagnotitie, zoeken, etc.) |
| Intent | Gebruikersintentie (dagnotitie, zoeken, overdracht) |
| Entity | Geëxtraheerde data (patiëntnaam, categorie) |
| Prefill | Vooraf ingevulde data in block |
| Klassiek EPD | Traditionele menu-gebaseerde interface |
| P1 | Prioriteit 1 (MVP scope) |
| SP | Story Points (Fibonacci: 1, 2, 3, 5, 8) |
| Wiring | Koppeling tussen componenten (input → API → block) |
---
**Versiehistorie:**
| Versie | Datum | Auteur | Wijziging |
|--------|-------|--------|-----------|
| v1.0 | 23-12-2024 | Colin Lit | Initiële versie |
| v1.1 | 23-12-2024 | Claude | E0 + E1 voltooid (21 SP) |
| v1.2 | 23-12-2024 | Claude | E2.S1 + E2.S2 voltooid (27 SP) |
| v1.3 | 23-12-2024 | Claude | E2.S3 AI fallback voltooid (29 SP) |
| v1.4 | 23-12-2024 | Claude | E2.S1-S4 voltooid (31 SP) |
| v1.5 | 24-12-2024 | Claude | E2.S5 toegevoegd: Input → Block wiring (+2 SP) |
| **v2.0** | **24-12-2024** | **Claude** | **Major update: E3.S0 toegevoegd, technische debt sectie, diagnostiek workflow referentie, sprint planning aangepast (29 stories, 72 SP)** |
| **v2.1** | **24-12-2024** | **Claude** | **E2.S5 voltooid: Input → Block wiring geïmplementeerd, Epic 2 compleet (33 SP done, 46%)** |

View File

@@ -0,0 +1,725 @@
# 🧩 Functioneel Ontwerp (FO) — Swift: Diagnostiek Workflow
**Projectnaam:** Swift — Contextual UI EPD
**Versie:** v1.0
**Datum:** 23-12-2024
**Auteur:** Colin Lit
---
## 1. Doel en relatie met het PRD
🎯 **Doel van dit document:**
Dit Functioneel Ontwerp beschrijft **hoe** de diagnostiek workflow in Swift functioneel werkt — van het plannen van een diagnostiek-afspraak tot het stellen en bijstellen van diagnoses. Dit document focust specifiek op de complete flow die behandelaars doorlopen tijdens een diagnostiek-traject.
📘 **Relatie met andere documenten:**
- **PRD:** `swift-prd.md` — Product visie en requirements
- **FO Algemeen:** `swift-fo-ai.md` — Algemene Swift functionaliteit
- **Bouwplan:** `bouwplan-swift-v1.md` — Technische implementatie planning
- **UX/UI:** `swift-ux-v2.1.md` — Visuele specificaties
**Kernprincipe:**
> Een behandelaar kan een volledig diagnostiek-traject doorlopen via natuurlijke taal: van afspraak plannen, via rapportage schrijven, tot diagnose stellen — alles in één vloeiende flow zonder menu-navigatie.
---
## 2. Overzicht van de belangrijkste onderdelen
🎯 **Doel:** Overzicht van de modules en blocks binnen de diagnostiek workflow.
### 2.1 Workflow Componenten
| # | Component | Beschrijving | Type |
|---|-----------|--------------|------|
| 1 | **AfspraakBlock** | Diagnostiek-afspraak plannen | Block |
| 2 | **RapportageBlock** | Verslag schrijven na afspraak | Block |
| 3 | **DiagnoseBlock** | Overzicht van diagnoses | Block |
| 4 | **DiagnoseFormBlock** | Diagnose aanmaken/bijstellen | Block |
### 2.2 Workflow Flow
```
Afspraak Plannen → Rapportage Schrijven → Diagnose Bekijken → Diagnose Aanmaken/Bijstellen
```
### 2.3 Relatie met Bestaande Blocks
| Block | Relatie met Diagnostiek Workflow |
|-------|----------------------------------|
| **ZoekenBlock** | Wordt gebruikt voor patiëntselectie |
| **PatientContextCard** | Toont actieve diagnoses in overzicht |
| **AgendaBlock** | Toont geplande diagnostiek-afspraken |
---
## 3. User Stories
🎯 **Doel:** Beschrijven wat behandelaars moeten kunnen doen tijdens een diagnostiek-traject.
### 3.1 Diagnostiek Workflow Stories
| ID | Rol | Doel / Actie | Verwachte waarde | Prio |
|----|-----|--------------|------------------|------|
| US-D01 | Behandelaar | Diagnostiek-afspraak plannen via spraak/tekst | Afspraak in < 30 sec zonder menu-navigatie | 🟡 P2 |
| US-D02 | Behandelaar | Rapportage schrijven en koppelen aan afspraak | Verslag automatisch gekoppeld aan encounter | 🟡 P2 |
| US-D03 | Behandelaar | Alle diagnoses van patiënt bekijken | Overzicht in één oogopslag | 🟡 P2 |
| US-D04 | Behandelaar | Nieuwe diagnose toevoegen met ICD-10 zoeker | Diagnose toegevoegd met correcte code | 🟡 P2 |
| US-D05 | Behandelaar | Bestaande diagnose bijstellen (status, ernst) | Wijzigingen direct zichtbaar | 🟡 P2 |
### 3.2 User Story Details
**US-D01: Diagnostiek-afspraak plannen**
> Als behandelaar wil ik een diagnostiek-afspraak kunnen plannen door te zeggen "afspraak diagnostiek met Jan morgen 10:00" zodat ik snel kan plannen zonder door menu's te navigeren.
**US-D02: Rapportage koppelen aan afspraak**
> Als behandelaar wil ik een rapportage kunnen schrijven die automatisch gekoppeld wordt aan de diagnostiek-afspraak zodat ik niet handmatig hoef te koppelen.
**US-D03: Diagnoses bekijken**
> Als behandelaar wil ik alle diagnoses van een patiënt kunnen bekijken door te zeggen "diagnose Jan" zodat ik snel een overzicht heb zonder te navigeren.
**US-D04: Diagnose toevoegen**
> Als behandelaar wil ik een nieuwe diagnose kunnen toevoegen met een ICD-10 zoeker zodat ik de juiste code kan vinden zonder handmatig te zoeken.
**US-D05: Diagnose bijstellen**
> Als behandelaar wil ik een bestaande diagnose kunnen bijstellen (status, ernst) zodat ik diagnoses kan actualiseren na behandeling.
---
## 4. Functionele werking per onderdeel
🎯 **Doel:** Per component beschrijven wat de gebruiker kan doen en wat het systeem doet.
### 4.1 AfspraakBlock
**Functie:** Diagnostiek-afspraak plannen via natuurlijke taal.
**Trigger patterns:**
- "afspraak diagnostiek met [patient] [datum] [tijd]"
- "plan diagnostiek voor [patient] morgen"
- "afspraak [patient] volgende week dinsdag 10:00"
**Pre-fill logica:**
| Extracted | Pre-fill |
|-----------|----------|
| patient_name → match | Patiënt selector |
| "diagnostiek" keyword | Type = Diagnostiek |
| "morgen", "vandaag", datum | Datum picker |
| Tijd (10:00, 14:30) | Starttijd |
| Geen tijd | Default 09:00 |
**Form velden:**
| Veld | Type | Verplicht | Default |
|------|------|-----------|---------|
| Patiënt | Dropdown + search | Ja | Pre-filled of ZoekenBlock |
| Datum | Date picker | Ja | Pre-filled of vandaag |
| Starttijd | Time picker | Ja | Pre-filled of 09:00 |
| Eindtijd | Time picker | Nee | Starttijd + 1 uur |
| Type | Dropdown | Ja | Diagnostiek (pre-selected) |
| Locatie | Dropdown | Nee | AMB |
**Afspraak types:**
- Diagnostiek
- Behandeling
- Evaluatie
- Consult
- Overig
**Acties:**
| Knop | Actie | Keyboard |
|------|-------|----------|
| Opslaan | POST naar API, sluit block, retourneert encounter_id | `⌘Enter` |
| Annuleren | Sluit block zonder opslaan | `Escape` |
**Na opslaan:**
1. Toast: "✓ Afspraak diagnostiek met Jan de Vries aangemaakt voor morgen 10:00"
2. Block verdwijnt (200ms animatie)
3. Recent strip: badge "[📅 Jan - Diagnostiek]"
4. Encounter_id wordt opgeslagen in context voor volgende stap
**API:**
```
POST /api/appointments
Body: {
patient_id: string,
period_start: datetime,
period_end: datetime,
type_code: 'diagnostiek',
class_code: 'AMB',
notes?: string
}
Response: {
id: string (encounter_id),
success: boolean
}
```
---
### 4.2 RapportageBlock (Uitgebreid)
**Functie:** Verslag schrijven na diagnostiek-afspraak, gekoppeld aan encounter.
**Trigger patterns:**
- "rapportage diagnostiek gesprek met [patient]"
- "verslag van diagnostiek afspraak [patient]"
- "rapportage [patient]" (als recente diagnostiek-afspraak bestaat)
**Pre-fill logica:**
| Extracted | Pre-fill |
|-----------|----------|
| patient_name → match | Patiënt selector |
| "diagnostiek" keyword | Type = Diagnostiek |
| Recente encounter (diagnostiek) | Encounter_id gekoppeld |
| Geen encounter | Geen koppeling, suggestie tonen |
**Form velden:**
| Veld | Type | Verplicht | Default |
|------|------|-----------|---------|
| Patiënt | Dropdown | Ja | Pre-filled |
| Gekoppeld aan | Link naar encounter | Nee | Recente diagnostiek-afspraak |
| Type | Button group | Ja | Diagnostiek (pre-selected) |
| Inhoud | Rich text editor | Ja | Leeg |
| Datum/tijd | DateTime | Ja | Nu |
**Rapportage types:**
- Diagnostiek
- Gesprek
- Evaluatie
- Telefonisch
- Consult
**AI acties:**
| Actie | Beschrijving | Output |
|-------|--------------|--------|
| ✨ Samenvatten | Bullets van kernpunten | Zijpaneel |
| 📖 B1-niveau | Herschrijf leesbaar | Zijpaneel |
| 🔍 Problemen | Extraheer klinische issues | Zijpaneel |
**Acties:**
| Knop | Actie | Keyboard |
|------|-------|----------|
| Opslaan | POST naar API met encounter_id, sluit block | `⌘Enter` |
| Annuleren | Sluit block zonder opslaan | `Escape` |
**Na opslaan:**
1. Toast: "✓ Rapportage opgeslagen en gekoppeld aan afspraak"
2. Block verdwijnt
3. Recent strip: badge "[📋 Jan - Diagnostiek]"
**API:**
```
POST /api/reports
Body: {
patient_id: string,
encounter_id: string, // Nieuwe: koppeling aan afspraak
type: 'diagnostiek',
content: string (HTML),
timestamp: datetime
}
```
---
### 4.3 DiagnoseBlock
**Functie:** Overzicht van alle diagnoses van een patiënt.
**Trigger patterns:**
- "diagnose [patient]"
- "diagnoses van [patient]"
- "wat zijn de diagnoses van [patient]"
- Klik op "Diagnoses" in PatientContextCard
**Layout:**
```
┌─────────────────────────────────────────────────────────────┐
│ 🏥 Diagnoses van Jan de Vries [×] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Filter: [Actief ✓] [Inactief] [Alle] │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ F41.1 Gegeneraliseerde angststoornis │ │
│ │ Status: Actief | Ernst: Matig │ │
│ │ Toegevoegd: 15 nov 2024 | Intake: Intake 1 │ │
│ │ [Bewerken] [Details] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ F32.1 Depressieve stoornis │ │
│ │ Status: Actief | Ernst: Mild │ │
│ │ Toegevoegd: 20 dec 2024 | Intake: Intake 2 │ │
│ │ [Bewerken] [Details] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ [+ Nieuwe diagnose toevoegen] │
│ │
└─────────────────────────────────────────────────────────────┘
```
**Gedrag:**
- Toont lijst met diagnoses, gesorteerd op datum (nieuwste eerst)
- Filter tabs: Actief / Inactief / Alle
- Per diagnose: code, omschrijving, status, ernst, datum, intake
- Klik op diagnose → DiagnoseFormBlock (edit mode)
- Klik "Nieuwe diagnose" → DiagnoseFormBlock (create mode)
**Empty state:**
"Geen diagnoses gevonden voor [patient]. [+ Nieuwe diagnose toevoegen]"
**API:**
```
GET /api/diagnoses/:patientId
Response: {
diagnoses: [
{
id: string,
code_code: string,
code_display: string,
clinical_status: 'active' | 'inactive' | 'resolved',
severity_display: string,
recorded_date: datetime,
encounter_id: string,
intake?: {
id: string,
title: string
}
}
]
}
```
---
### 4.4 DiagnoseFormBlock
**Functie:** Diagnose aanmaken of bijstellen met ICD-10 zoeker.
**Trigger patterns:**
- "diagnose toevoegen [patient] [ICD-10 code]"
- "diagnose wijzigen [patient]"
- "diagnose bijstellen [patient]"
- Klik op diagnose in DiagnoseBlock
- Klik "Nieuwe diagnose" in DiagnoseBlock
**Pre-fill logica (create mode):**
| Extracted | Pre-fill |
|-----------|----------|
| patient_name → match | Patiënt selector (read-only) |
| ICD-10 code (F41.1) | Code + omschrijving via zoeker |
| Geen code | ICD-10 zoeker open |
**Pre-fill logica (edit mode):**
| Veld | Pre-fill |
|------|----------|
| Patiënt | Read-only, huidige waarde |
| ICD-10 code | Huidige code |
| Omschrijving | Huidige omschrijving |
| Type | Huidige type (hoofd/neven) |
| Status | Huidige status |
| Ernst | Huidige ernst |
| Intake | Huidige intake koppeling |
| Toelichting | Huidige toelichting |
**Form velden:**
| Veld | Type | Verplicht | Default |
|------|------|-----------|---------|
| Patiënt | Read-only | Ja | Pre-filled |
| ICD-10 zoeker | Search + dropdown | Ja | Leeg of pre-filled |
| Code | Text (read-only na selectie) | Ja | Uit zoeker |
| Omschrijving | Text (read-only na selectie) | Ja | Uit zoeker |
| Type | Radio buttons | Ja | Hoofddiagnose |
| Status | Dropdown | Ja | Actief |
| Ernst | Dropdown | Nee | Geen |
| Intake koppeling | Dropdown | Nee | Geen |
| Toelichting | Textarea | Nee | Leeg |
**ICD-10 zoeker gedrag:**
- Fuzzy search tijdens typen (minimaal 2 karakters)
- Resultaten dropdown met code + omschrijving
- Bij selectie: code en omschrijving worden ingevuld
- Zoek op code (F41.1) of omschrijving (angst)
**Status opties:**
- Actief
- Inactief
- Resolved
- Remission
- Recurrence
- Relapse
**Ernst opties:**
- Geen
- Mild
- Matig
- Ernstig
**Type opties:**
- Hoofddiagnose
- Nevendiagnose
**Acties:**
| Knop | Actie | Keyboard |
|------|-------|----------|
| Opslaan | POST/PATCH naar API, sluit block | `⌘Enter` |
| Annuleren | Sluit block zonder opslaan | `Escape` |
| Verwijderen | Bevestigingsdialog → soft delete | - |
**Na opslaan:**
1. Toast: "✓ Diagnose F41.1 - Gegeneraliseerde angststoornis toegevoegd" (of "bijgewerkt")
2. Block verdwijnt
3. DiagnoseBlock wordt automatisch getoond met nieuwe/bijgewerkte diagnose
**API:**
```
POST /api/diagnoses
Body: {
patient_id: string,
encounter_id?: string,
code_code: string,
code_display: string,
code_system: 'ICD-10',
clinical_status: 'active' | 'inactive' | 'resolved',
severity_display?: string,
category: 'primary-diagnosis' | 'encounter-diagnosis',
note?: string
}
PATCH /api/diagnoses/:id
Body: {
code_code?: string,
code_display?: string,
clinical_status?: string,
severity_display?: string,
category?: string,
note?: string
}
```
---
## 5. UI-overzicht (visuele structuur)
🎯 **Doel:** Globale schermopbouw voor diagnostiek workflow blocks.
### 5.1 AfspraakBlock Layout
```
┌─────────────────────────────────────────────────────────────┐
│ 📅 Nieuwe Afspraak [] [×] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Patiënt * │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Jan de Vries ✓ │ │
│ │ 59 jaar • Kamer 12B [← Auto] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Datum * Van * Tot │
│ ┌─────────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 2024-12-24 │ │ 10:00 │ │ 11:00 │ │
│ └─────────────┘ └─────────┘ └─────────┘ │
│ │
│ Type afspraak * │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ [Diagnostiek ▼] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Locatie │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ [AMB ▼] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Notities (optioneel) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ [Annuleren] [Opslaan] │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 5.2 RapportageBlock Layout (met encounter koppeling)
```
┌─────────────────────────────────────────────────────────────┐
│ 📋 Rapportage [] [×] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Patiënt: Jan de Vries [Wijzig] │
│ │
│ Gekoppeld aan: Afspraak diagnostiek - 24 dec 2024 10:00 │
│ │
│ Type │
│ [Diagnostiek ✓] [Gesprek] [Evaluatie] [Telefonisch] │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ [B] [I] [•] [1.] ["] 🎤 Dicteer │ │
│ ├───────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Verslag van diagnostiek gesprek... │ │
│ │ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ AI-acties │
│ [✨ Samenvatten] [📖 B1-niveau] [🔍 Problemen extraheren] │
│ │
│ [Annuleren] [Opslaan] │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 5.3 DiagnoseBlock Layout
```
┌─────────────────────────────────────────────────────────────┐
│ 🏥 Diagnoses van Jan de Vries [] [×] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Filter: [Actief ✓] [Inactief] [Alle] │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ F41.1 Gegeneraliseerde angststoornis │ │
│ │ Status: Actief | Ernst: Matig │ │
│ │ Toegevoegd: 15 nov 2024 | Intake: Intake 1 │ │
│ │ │ │
│ │ [Bewerken] [Details] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ F32.1 Depressieve stoornis │ │
│ │ Status: Actief | Ernst: Mild │ │
│ │ Toegevoegd: 20 dec 2024 | Intake: Intake 2 │ │
│ │ │ │
│ │ [Bewerken] [Details] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ [+ Nieuwe diagnose toevoegen] │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 5.4 DiagnoseFormBlock Layout
```
┌─────────────────────────────────────────────────────────────┐
│ 🏥 Nieuwe Diagnose [] [×] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Patiënt: Jan de Vries (read-only) │
│ │
│ ICD-10 Code * │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 🔍 Zoek ICD-10 code of omschrijving... │ │
│ │ │ │
│ │ Resultaten: │ │
│ │ • F41.1 Gegeneraliseerde angststoornis │ │
│ │ • F41.0 Paniekstoornis │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Code: F41.1 (read-only na selectie) │
│ Omschrijving: Gegeneraliseerde angststoornis (read-only) │
│ │
│ Type * │
│ ○ Hoofddiagnose ● Nevendiagnose │
│ │
│ Status * │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ [Actief ▼] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Ernst │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ [Geen ▼] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Intake koppeling │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ [Geen ▼] │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ Toelichting (optioneel) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ [Annuleren] [Opslaan] │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## 6. Interacties met AI (functionele beschrijving)
🎯 **Doel:** Waar AI in de diagnostiek workflow voorkomt.
| Locatie | AI-actie | Trigger | Output |
|---------|----------|---------|--------|
| RapportageBlock | Samenvatten | Klik knop "✨ Samenvatten" | Bullets van kernpunten in zijpaneel |
| RapportageBlock | B1-niveau | Klik knop "📖 B1-niveau" | Herschreven tekst in zijpaneel |
| RapportageBlock | Extract problemen | Klik knop "🔍 Problemen" | Gestructureerde lijst met categorie + severity |
| DiagnoseFormBlock | ICD-10 suggestie | Typ in zoeker | Fuzzy search resultaten met relevante codes |
**AI Response Handling:**
- Alle AI outputs tonen in dedicated preview area (zijpaneel)
- Gebruiker moet expliciet accepteren/invoegen
- Bewerken altijd mogelijk
- Annuleren zonder gevolgen
---
## 7. Complete Workflow Flow
🎯 **Doel:** Stap-voor-stap beschrijving van de complete diagnostiek workflow.
### Flow 1: Van Afspraak tot Diagnose (Happy Path)
```
┌─────────────────────────────────────────────────────────────┐
│ STAP 1: AFSPRAAK PLANNEN │
│ │
│ Behandelaar: "afspraak diagnostiek met Jan morgen 10:00" │
│ → AfspraakBlock verschijnt met pre-fill │
│ → Behandelaar controleert → klikt Opslaan │
│ → Encounter aangemaakt, encounter_id opgeslagen │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STAP 2: RAPPORTAGE SCHRIJVEN (na afspraak) │
│ │
│ Behandelaar: "rapportage diagnostiek gesprek met Jan" │
│ → RapportageBlock verschijnt │
│ → Encounter_id automatisch gekoppeld │
│ → Behandelaar schrijft/dicteert verslag │
│ → Optioneel: AI samenvatten │
│ → Klikt Opslaan → Rapportage gekoppeld aan encounter │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STAP 3: DIAGNOSE BEKIJKEN │
│ │
│ Behandelaar: "diagnose Jan" │
│ → DiagnoseBlock verschijnt met lijst diagnoses │
│ → Filter: Actief (default) │
│ → Behandelaar ziet overzicht │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STAP 4: DIAGNOSE TOEVOEGEN │
│ │
│ Behandelaar: "diagnose toevoegen Jan F41.1" │
│ → DiagnoseFormBlock verschijnt │
│ → ICD-10 code F41.1 pre-filled │
│ → Behandelaar vult type, status, ernst in │
│ → Koppelt aan intake (optioneel) │
│ → Klikt Opslaan → Diagnose toegevoegd │
│ → DiagnoseBlock wordt getoond met nieuwe diagnose │
└─────────────────────────────────────────────────────────────┘
```
### Flow 2: Diagnose Bijstellen
```
┌─────────────────────────────────────────────────────────────┐
│ Behandelaar: "diagnose wijzigen Jan" │
│ → DiagnoseBlock verschijnt │
│ → Behandelaar klikt op diagnose F41.1 │
│ → DiagnoseFormBlock verschijnt (edit mode) │
│ → Alle velden pre-filled met huidige waarden │
│ → Behandelaar wijzigt status: Actief → Resolved │
│ → Behandelaar wijzigt ernst: Matig → Mild │
│ → Voegt toelichting toe │
│ → Klikt Opslaan → Diagnose bijgewerkt │
│ → DiagnoseBlock wordt getoond met bijgewerkte diagnose │
└─────────────────────────────────────────────────────────────┘
```
---
## 8. Edge Cases & Alternatieve Flows
### Edge Case 1: Meerdere patiënten metzelfde naam
- **Situatie:** Input "diagnose Jan" → meerdere matches
- **Gedrag:** ZoekenBlock verschijnt met resultaten
- **Actie:** Behandelaar selecteert juiste patiënt
- **Vervolg:** DiagnoseBlock voor geselecteerde patiënt
### Edge Case 2: Geen recente afspraak voor rapportage
- **Situatie:** Input "rapportage Jan" → geen encounter gevonden
- **Gedrag:** RapportageBlock opent zonder encounter-koppeling
- **Suggestie:** "Geen recente diagnostiek-afspraak gevonden. Wil je een afspraak koppelen?"
- **Actie:** Optioneel AfspraakBlock openen
### Edge Case 3: ICD-10 code niet gevonden
- **Situatie:** Input "diagnose toevoegen Jan F99.9" → code bestaat niet
- **Gedrag:** Validatie fout: "ICD-10 code F99.9 niet gevonden"
- **Actie:** ICD-10 zoeker blijft open voor correctie
### Edge Case 4: Diagnose al bestaat
- **Situatie:** Input "diagnose toevoegen Jan F41.1" → diagnose bestaat al
- **Gedrag:** Waarschuwing: "Diagnose F41.1 bestaat al. Wil je deze bijwerken?"
- **Actie:** Optioneel DiagnoseFormBlock openen in edit mode
### Edge Case 5: Onvolledige input
- **Situatie:** Input "afspraak diagnostiek" (geen patiënt/datum)
- **Gedrag:** Systeem vraagt om ontbrekende informatie
- **Actie:** ZoekenBlock voor patiënt, datum/tijd picker voor planning
---
## 9. Success Criteria
De diagnostiek workflow is succesvol wanneer:
1. ✅ Behandelaar kan diagnostiek-afspraak plannen in < 30 seconden
2. ✅ Rapportage kan worden geschreven en automatisch gekoppeld aan afspraak
3. ✅ Alle diagnoses van een patiënt zijn in één overzicht zichtbaar
4. ✅ Nieuwe diagnose kan worden toegevoegd met ICD-10 zoeker
5. ✅ Bestaande diagnose kan worden bijgewerkt (status, ernst, etc.)
6. ✅ Alle acties zijn traceerbaar (wie, wanneer, wat)
7. ✅ Workflow kan volledig worden doorlopen zonder menu-navigatie
---
## 10. Bijlagen & Referenties
**Projectdocumenten:**
- PRD: `swift-prd.md`
- FO Algemeen: `swift-fo-ai.md`
- Bouwplan: `bouwplan-swift-v1.md`
- UX/UI: `swift-ux-v2.1.md`
- Taken analyse: `taken-en-vragen-analyse.md`
**Bestaande Code Referenties:**
- Diagnose pagina: `app/epd/patients/[id]/diagnose/page.tsx`
- Diagnose actions: `app/epd/patients/[id]/diagnose/actions.ts`
- Appointment modal: `app/epd/agenda/components/appointment-modal.tsx`
- Rapportage workspace: `app/epd/patients/[id]/rapportage/components/rapportage-workspace-v2.tsx`
**Database Schema:**
- `conditions` tabel voor diagnoses
- `encounters` tabel voor afspraken
- `reports` tabel voor rapportages
---
## Wijzigingslog
| Versie | Datum | Wijzigingen |
|--------|-------|-------------|
| 1.0 | 23-12-2024 | Initiële versie - Diagnostiek workflow beschrijving |

View File

@@ -0,0 +1,829 @@
# 🎨 UX/UI Design Document — Swift: Diagnostiek Workflow
**Projectnaam:** Swift — Diagnostiek Workflow
**Versie:** v1.0
**Datum:** 23-12-2024
**Auteur:** Colin Lit
---
## 1. Visie: Behandelaar Workflow via Natuurlijke Taal
### 1.1 Het Probleem voor Behandelaars
```
TRADITIONEEL EPD - DIAGNOSTIEK WORKFLOW
┌──────────────────────────────────────────────────────────────────┐
│ Menu → Patiënten → Jan → Agenda → Nieuwe Afspraak → │
│ Type: Diagnostiek → Datum/Tijd → Opslaan │
│ │
│ Menu → Patiënten → Jan → Rapportages → Nieuw → │
│ Type: Diagnostiek → Koppel Afspraak → Zoek → Selecteer → │
│ Schrijf verslag → Opslaan │
│ │
│ Menu → Patiënten → Jan → Diagnoses → Nieuw → │
│ Zoek ICD-10 → Selecteer → Vul formulier → Opslaan │
└──────────────────────────────────────────────────────────────────┘
Resultaat: 15-20 klikken, 10-15 minuten per diagnostiek-traject
```
### 1.2 De Swift Oplossing
```
SWIFT - DIAGNOSTIEK WORKFLOW
┌─────────────────────────────────────────────────────────────────┐
│ │
│ "afspraak diagnostiek met jan morgen 10:00" │
│ → AfspraakBlock verschijnt voorgevuld │
│ → Opslaan (1 klik) │
│ │
│ "rapportage diagnostiek gesprek met jan" │
│ → RapportageBlock verschijnt met encounter koppeling │
│ → Schrijf/dicteer → Opslaan (1 klik) │
│ │
│ "diagnose toevoegen jan F41.1" │
│ → DiagnoseFormBlock verschijnt met ICD-10 pre-filled │
│ → Vul aan → Opslaan (1 klik) │
│ │
└─────────────────────────────────────────────────────────────────┘
Resultaat: 3 zinnen, 3 klikken, 3-5 minuten per traject
```
### 1.3 Core Design Principles (Diagnostiek Workflow)
| Principe | Betekenis voor Diagnostiek |
|----------|---------------------------|
| **Conversational** | "afspraak diagnostiek jan morgen 10:00" werkt direct |
| **Contextual** | Systeem onthoudt encounter_id tussen stappen |
| **Ephemeral** | Blocks verschijnen wanneer nodig, verdwijnen na opslaan |
| **Complete Flow** | Van afspraak tot diagnose in één vloeiende flow |
---
## 2. Intent Mapping voor Diagnostiek Workflow
### 2.1 Diagnostiek Intents
| Intent | Trigger patterns | Block | Prio | Freq |
|--------|-----------------|-------|------|------|
| `afspraak_maken` | "afspraak diagnostiek met [patient] [datum] [tijd]"<br>"plan diagnostiek voor [patient] morgen"<br>"afspraak [patient] volgende week dinsdag 10:00" | `AfspraakBlock` | 🟡 P2 | 2-3x/week |
| `rapportage` | "rapportage diagnostiek gesprek met [patient]"<br>"verslag van diagnostiek afspraak [patient]"<br>"rapportage [patient]" (als recente afspraak) | `RapportageBlock` | 🟡 P2 | 3-5x/week |
| `diagnose_bekijken` | "diagnose [patient]"<br>"diagnoses van [patient]"<br>"wat zijn de diagnoses van [patient]" | `DiagnoseBlock` | 🟡 P2 | 3-5x/week |
| `diagnose_toevoegen` | "diagnose toevoegen [patient] [ICD-10]"<br>"[patient] heeft [ICD-10]"<br>"diagnose [patient] [ICD-10]" | `DiagnoseFormBlock` | 🟡 P2 | 1-2x/week |
| `diagnose_wijzigen` | "diagnose wijzigen [patient]"<br>"diagnose bijstellen [patient]" | `DiagnoseFormBlock` (edit) | 🟡 P2 | 1x/week |
### 2.2 Context-Triggered UI (Proactief)
| Trigger | Conditie | Wat verschijnt | Prio |
|---------|----------|----------------|------|
| **Recente afspraak** | Na afspraak opslaan | Suggestie: "Rapportage schrijven?" | 🟡 P2 |
| **Rapportage zonder diagnose** | Na rapportage opslaan | Suggestie: "Diagnose toevoegen?" | 🟡 P2 |
| **Diagnose verouderd** | Diagnose > 6 maanden oud | Suggestie: "Diagnose bijwerken?" | 🟢 P3 |
---
## 3. Screen Architecture
### 3.1 Diagnostiek Blocks in Swift Layout
Alle diagnostiek blocks verschijnen in de **Canvas Area** van Swift:
```
┌─────────────────────────────────────────────────────────────────┐
│ 🕐 Ochtend | 8 ptn Jan de Vries ▼ 👤 SV │
│ CONTEXT BAR │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ │
│ │ │ │
│ │ DIAGNOSTIEK BLOCK │ │
│ │ (AfspraakBlock / │ │
│ │ RapportageBlock / │ │
│ │ DiagnoseBlock / │ │
│ │ DiagnoseFormBlock) │ │
│ │ │ │
│ └─────────────────────┘ │
│ │
│ CANVAS AREA │
│ │
├─────────────────────────────────────────────────────────────────┤
│ Recent: [📅 Jan-Diag] [📋 Jan-Rapp] [🏥 Jan-Diag] │
│ RECENT STRIP │
├─────────────────────────────────────────────────────────────────┤
│ 🎤 Typ of spreek wat je wilt doen... ⌘K │
│ COMMAND INPUT │
└─────────────────────────────────────────────────────────────────┘
```
### 3.2 Block Sizes voor Diagnostiek
| Block | Size | Max-width | Reden |
|-------|------|-----------|-------|
| AfspraakBlock | Medium | 640px | Form met meerdere velden |
| RapportageBlock | Large | 900px | Rich text editor + AI acties |
| DiagnoseBlock | Medium | 640px | Overzicht lijst |
| DiagnoseFormBlock | Medium | 640px | Form met ICD-10 zoeker |
---
## 4. Component Specifications
### 4.1 AfspraakBlock
**Functie:** Diagnostiek-afspraak plannen met pre-fill vanuit intent.
```
┌─────────────────────────────────────────────────────────────────┐
│ 📅 Nieuwe Afspraak [] [×] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Patiënt * │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Jan de Vries ✓ │ │
│ │ 59 jaar • Kamer 12B [← Auto] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Datum * Van * Tot │
│ ┌─────────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 2024-12-24 │ │ 10:00 │ │ 11:00 │ │
│ └─────────────┘ └─────────┘ └─────────┘ │
│ │
│ Type afspraak * │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [Diagnostiek ▼] │ │
│ │ • Diagnostiek │ │
│ │ • Behandeling │ │
│ │ • Evaluatie │ │
│ │ • Consult │ │
│ │ • Overig │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Locatie │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [AMB ▼] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Notities (optioneel) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [Annuleren] [💾 Opslaan] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Pre-fill Indicatoren:**
- Patiënt naam: **Bold** + checkmark
- Datum: **Highlighted** achtergrond
- Tijd: **Highlighted** achtergrond
- Type: **Pre-selected** in dropdown
**Keyboard Shortcuts:**
- `⌘Enter` / `Ctrl+Enter`: Opslaan
- `Escape`: Annuleren
- `Tab`: Navigeer tussen velden
**Na Opslaan:**
1. Toast: "✓ Afspraak diagnostiek met Jan de Vries aangemaakt voor morgen 10:00"
2. Block verdwijnt (200ms slide-down animatie)
3. Recent strip: badge "[📅 Jan - Diagnostiek]"
4. Encounter_id opgeslagen in Swift store voor volgende stap
---
### 4.2 RapportageBlock (Uitgebreid)
**Functie:** Verslag schrijven na diagnostiek-afspraak met encounter koppeling.
```
┌─────────────────────────────────────────────────────────────────┐
│ 📋 Rapportage [] [×] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Patiënt: Jan de Vries [Wijzig] │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 🔗 Gekoppeld aan: Afspraak diagnostiek │ │
│ │ 24 dec 2024 10:00 - 11:00 │ │
│ │ [Ontkoppelen] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Type │
│ [Diagnostiek ✓] [Gesprek] [Evaluatie] [Telefonisch] [Consult]│
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [B] [I] [•] [1.] ["] 🎤 Dicteer │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Verslag van diagnostiek gesprek met Jan de Vries... │ │
│ │ │ │
│ │ [Rich text editor met formatting toolbar] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ AI-acties │
│ [✨ Samenvatten] [📖 B1-niveau] [🔍 Problemen extraheren] │
│ │
│ [Annuleren] [💾 Opslaan] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Encounter Koppeling:**
- **Zichtbaar:** Als recente diagnostiek-afspraak bestaat
- **Styling:** Link badge met encounter details
- **Actie:** Klik "Ontkoppelen" om koppeling te verwijderen
- **Empty state:** "Geen recente diagnostiek-afspraak gevonden. [+ Koppel afspraak]"
**AI Acties (Zijpaneel):**
- **✨ Samenvatten:** Bullets van kernpunten
- **📖 B1-niveau:** Herschreven tekst (leesbaar voor patiënt)
- **🔍 Problemen:** Gestructureerde lijst met categorie + severity
**Keyboard Shortcuts:**
- `⌘Enter` / `Ctrl+Enter`: Opslaan
- `Escape`: Annuleren
- `⌘B` / `Ctrl+B`: Bold
- `⌘I` / `Ctrl+I`: Italic
- `Space` (leeg): Start dicteer
**Na Opslaan:**
1. Toast: "✓ Rapportage opgeslagen en gekoppeld aan afspraak"
2. Block verdwijnt
3. Recent strip: badge "[📋 Jan - Diagnostiek]"
4. Suggestie: "Diagnose toevoegen?" (als nog geen diagnose)
---
### 4.3 DiagnoseBlock
**Functie:** Overzicht van alle diagnoses van een patiënt.
```
┌─────────────────────────────────────────────────────────────────┐
│ 🏥 Diagnoses van Jan de Vries [] [×] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Filter: [Actief ✓] [Inactief] [Alle] │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ F41.1 Gegeneraliseerde angststoornis │ │
│ │ Status: ● Actief | Ernst: Matig │ │
│ │ Toegevoegd: 15 nov 2024 | Intake: Intake 1 │ │
│ │ │ │
│ │ [✏️ Bewerken] [📄 Details] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ F32.1 Depressieve stoornis │ │
│ │ Status: ● Actief | Ernst: Mild │ │
│ │ Toegevoegd: 20 dec 2024 | Intake: Intake 2 │ │
│ │ │ │
│ │ [✏️ Bewerken] [📄 Details] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ F41.0 Paniekstoornis │ │
│ │ Status: ○ Inactief | Ernst: - │ │
│ │ Toegevoegd: 10 sep 2024 | Intake: Intake 1 │ │
│ │ │ │
│ │ [✏️ Bewerken] [📄 Details] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [+ Nieuwe diagnose toevoegen] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Filter Tabs:**
- **Actief:** Alleen diagnoses met status "active" (default)
- **Inactief:** Alleen diagnoses met status "inactive" of "resolved"
- **Alle:** Alle diagnoses
**Diagnose Card:**
- **Code:** ICD-10 code (F41.1) in monospace font
- **Omschrijving:** Volledige naam in bold
- **Status:** Dot indicator (● = actief, ○ = inactief)
- **Ernst:** Badge met kleur (Mild = groen, Matig = geel, Ernstig = rood)
- **Metadata:** Datum + Intake link
**Empty State:**
```
┌─────────────────────────────────────────────────────────────┐
│ Geen diagnoses gevonden voor Jan de Vries │
│ │
│ [+ Nieuwe diagnose toevoegen] │
└─────────────────────────────────────────────────────────────┘
```
**Keyboard Shortcuts:**
- `Escape`: Sluit block
- `Enter` (op diagnose): Open DiagnoseFormBlock (edit mode)
- `N`: Nieuwe diagnose toevoegen
---
### 4.4 DiagnoseFormBlock
**Functie:** Diagnose aanmaken of bijstellen met ICD-10 zoeker.
```
┌─────────────────────────────────────────────────────────────────┐
│ 🏥 Nieuwe Diagnose [] [×] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Patiënt: Jan de Vries (read-only) │
│ │
│ ICD-10 Code * │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 🔍 Zoek ICD-10 code of omschrijving... │ │
│ │ │ │
│ │ Resultaten (bij typen): │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ • F41.1 Gegeneraliseerde angststoornis │ │ │
│ │ │ • F41.0 Paniekstoornis │ │ │
│ │ │ • F41.2 Gemengde angst- en depressieve stoornis│ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Code: F41.1 (read-only na selectie) │
│ Omschrijving: Gegeneraliseerde angststoornis (read-only) │
│ │
│ Type * │
│ ○ Hoofddiagnose ● Nevendiagnose │
│ │
│ Status * │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [Actief ▼] │ │
│ │ • Actief │ │
│ │ • Inactief │ │
│ │ • Resolved │ │
│ │ • Remission │ │
│ │ • Recurrence │ │
│ │ • Relapse │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Ernst │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [Geen ▼] │ │
│ │ • Geen │ │
│ │ • Mild │ │
│ │ • Matig │ │
│ │ • Ernstig │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Intake koppeling (optioneel) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [Geen ▼] │ │
│ │ • Geen │ │
│ │ • Intake 1 - 15 nov 2024 │ │
│ │ • Intake 2 - 20 dec 2024 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Toelichting (optioneel) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [Annuleren] [💾 Opslaan] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**ICD-10 Zoeker Gedrag:**
- **Trigger:** Typ minimaal 2 karakters
- **Search:** Fuzzy search op code (F41.1) of omschrijving (angst)
- **Results:** Dropdown met max 10 resultaten
- **Selectie:** Klik op resultaat → code + omschrijving worden ingevuld (read-only)
- **Pre-fill:** Als ICD-10 code in intent → automatisch zoeken en invullen
**Status Kleuren:**
- **Actief:** Groen dot (●)
- **Inactief:** Grijs dot (○)
- **Resolved:** Blauw dot (●)
**Ernst Badges:**
- **Geen:** Geen badge
- **Mild:** Groen badge
- **Matig:** Geel badge
- **Ernstig:** Rood badge
**Edit Mode:**
- Alle velden pre-filled met huidige waarden
- Patiënt read-only
- Code + omschrijving read-only (wijzig via zoeker)
- Status, ernst, type, toelichting bewerkbaar
**Keyboard Shortcuts:**
- `⌘Enter` / `Ctrl+Enter`: Opslaan
- `Escape`: Annuleren
- `Tab`: Navigeer tussen velden
- `↑` `↓` (in zoeker): Navigeer resultaten
- `Enter` (in zoeker): Selecteer resultaat
**Na Opslaan:**
1. Toast: "✓ Diagnose F41.1 - Gegeneraliseerde angststoornis toegevoegd"
2. Block verdwijnt
3. DiagnoseBlock wordt automatisch getoond met nieuwe/bijgewerkte diagnose
---
## 5. Interaction Flows
### 5.1 Happy Path: Complete Diagnostiek Workflow (3-5 minuten)
```
┌─────────────────────────────────────────────────────────────────┐
│ STAP 1: AFSPRAAK PLANNEN (30 sec) │
│ │
│ Behandelaar: "afspraak diagnostiek met jan morgen 10:00" │
│ → AfspraakBlock verschijnt met pre-fill │
│ ✓ Patiënt: Jan de Vries [highlighted] │
│ ✓ Datum: morgen [highlighted] │
│ ✓ Tijd: 10:00 [highlighted] │
│ ✓ Type: Diagnostiek [pre-selected] │
│ → Behandelaar review → klikt Opslaan │
│ → Toast: "✓ Afspraak aangemaakt" │
│ → Encounter_id opgeslagen │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ STAP 2: RAPPORTAGE SCHRIJVEN (2 min) │
│ │
│ Behandelaar: "rapportage diagnostiek gesprek met jan" │
│ → RapportageBlock verschijnt │
│ ✓ Patiënt: Jan de Vries │
│ ✓ Gekoppeld aan: Afspraak diagnostiek - morgen 10:00 │
│ ✓ Type: Diagnostiek [pre-selected] │
│ → Behandelaar schrijft/dicteert verslag │
│ → Optioneel: AI samenvatten │
│ → Klikt Opslaan │
│ → Toast: "✓ Rapportage gekoppeld aan afspraak" │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ STAP 3: DIAGNOSE BEKIJKEN (30 sec) │
│ │
│ Behandelaar: "diagnose jan" │
│ → DiagnoseBlock verschijnt met overzicht │
│ → Filter: Actief (default) │
│ → Behandelaar ziet bestaande diagnoses │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ STAP 4: DIAGNOSE TOEVOEGEN (1 min) │
│ │
│ Behandelaar: "diagnose toevoegen jan F41.1" │
│ → DiagnoseFormBlock verschijnt │
│ ✓ Patiënt: Jan de Vries [read-only] │
│ ✓ ICD-10: F41.1 [pre-filled] │
│ ✓ Omschrijving: Gegeneraliseerde angststoornis [pre-filled] │
│ → Behandelaar vult type, status, ernst in │
│ → Koppelt aan intake (optioneel) │
│ → Klikt Opslaan │
│ → Toast: "✓ Diagnose toegevoegd" │
│ → DiagnoseBlock wordt getoond met nieuwe diagnose │
└─────────────────────────────────────────────────────────────────┘
TOTAAL: ~4 minuten (was: 10-15 minuten)
```
### 5.2 Diagnose Bijstellen Flow
```
┌─────────────────────────────────────────────────────────────────┐
│ Behandelaar: "diagnose wijzigen jan" │
│ → DiagnoseBlock verschijnt │
│ → Behandelaar klikt op diagnose F41.1 │
│ → DiagnoseFormBlock verschijnt (edit mode) │
│ ✓ Alle velden pre-filled met huidige waarden │
│ → Behandelaar wijzigt: │
│ - Status: Actief → Resolved │
│ - Ernst: Matig → Mild │
│ - Toelichting: "Na behandeling verbeterd" │
│ → Klikt Opslaan │
│ → Toast: "✓ Diagnose bijgewerkt" │
│ → DiagnoseBlock wordt getoond met bijgewerkte diagnose │
└─────────────────────────────────────────────────────────────────┘
```
### 5.3 Proactieve Suggesties Flow
```
┌─────────────────────────────────────────────────────────────────┐
│ Na Afspraak opslaan: │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 💡 Rapportage schrijven voor deze afspraak? │ │
│ │ [Ja] [Later] [×] │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ Na Rapportage opslaan (zonder diagnose): │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 💡 Diagnose toevoegen voor Jan de Vries? │ │
│ │ [Ja] [Later] [×] │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 6. Visual Design Tokens
### 6.1 Block Colors (Dark Theme - Swift Context)
| Element | Hex | Gebruik |
|---------|-----|---------|
| Block Background | `#1E293B` | Block achtergrond |
| Block Border | `#334155` | Block rand |
| Text Primary | `#F1F5F9` | Hoofdtekst |
| Text Secondary | `#94A3B8` | Subtekst |
| Accent | `#3B82F6` | Primaire acties |
| Success | `#10B981` | Bevestigingen |
| Warning | `#F59E0B` | Waarschuwingen |
| Error | `#EF4444` | Fouten |
### 6.2 Status Colors
| Status | Dot | Badge | Hex |
|--------|-----|-------|-----|
| Actief | ● | - | `#10B981` |
| Inactief | ○ | - | `#64748B` |
| Resolved | ● | - | `#3B82F6` |
### 6.3 Ernst Badges
| Ernst | Badge | Hex |
|-------|-------|-----|
| Geen | - | - |
| Mild | Badge | `#10B981` |
| Matig | Badge | `#F59E0B` |
| Ernstig | Badge | `#EF4444` |
### 6.4 ICD-10 Code Styling
| Element | Font | Size | Color |
|---------|------|------|-------|
| Code | Monospace | 14px | `#F1F5F9` |
| Omschrijving | Sans-serif | 16px | `#F1F5F9` |
---
## 7. Keyboard Navigation
### 7.1 AfspraakBlock
| Key | Action |
|-----|--------|
| `⌘Enter` / `Ctrl+Enter` | Opslaan |
| `Escape` | Annuleren |
| `Tab` | Navigeer tussen velden |
| `↑` `↓` | Navigeer dropdown opties |
### 7.2 RapportageBlock
| Key | Action |
|-----|--------|
| `⌘Enter` / `Ctrl+Enter` | Opslaan |
| `Escape` | Annuleren |
| `⌘B` / `Ctrl+B` | Bold |
| `⌘I` / `Ctrl+I` | Italic |
| `Space` (leeg) | Start dicteer |
### 7.3 DiagnoseBlock
| Key | Action |
|-----|--------|
| `Escape` | Sluit block |
| `Enter` (op diagnose) | Open DiagnoseFormBlock (edit) |
| `N` | Nieuwe diagnose |
| `↑` `↓` | Navigeer diagnoses |
### 7.4 DiagnoseFormBlock
| Key | Action |
|-----|--------|
| `⌘Enter` / `Ctrl+Enter` | Opslaan |
| `Escape` | Annuleren |
| `Tab` | Navigeer tussen velden |
| `↑` `↓` (in zoeker) | Navigeer resultaten |
| `Enter` (in zoeker) | Selecteer resultaat |
---
## 8. Error States & Edge Cases
### 8.1 AfspraakBlock Errors
**Geen patiënt gevonden:**
```
┌─────────────────────────────────────────────────────────────┐
│ ⚠️ Patiënt "Jan" niet gevonden │
│ │
│ [🔍 Zoek patiënt] │
└─────────────────────────────────────────────────────────────┘
```
**Ongeldige datum:**
```
┌─────────────────────────────────────────────────────────────┐
│ Datum * │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ morgen ❌ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ⚠️ Ongeldige datum. Gebruik formaat: DD-MM-YYYY │
└─────────────────────────────────────────────────────────────┘
```
### 8.2 RapportageBlock Errors
**Geen encounter gevonden:**
```
┌─────────────────────────────────────────────────────────────┐
│ Geen recente diagnostiek-afspraak gevonden │
│ │
│ [+ Koppel afspraak] │
└─────────────────────────────────────────────────────────────┘
```
**Lege rapportage:**
```
┌─────────────────────────────────────────────────────────────┐
│ ⚠️ Rapportage mag niet leeg zijn │
└─────────────────────────────────────────────────────────────┘
```
### 8.3 DiagnoseFormBlock Errors
**ICD-10 code niet gevonden:**
```
┌─────────────────────────────────────────────────────────────┐
│ ICD-10 Code * │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ F99.9 ❌ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ⚠️ ICD-10 code F99.9 niet gevonden │
└─────────────────────────────────────────────────────────────┘
```
**Diagnose bestaat al:**
```
┌─────────────────────────────────────────────────────────────┐
│ ⚠️ Diagnose F41.1 bestaat al voor Jan de Vries │
│ │
│ [✏️ Bestaande diagnose bewerken] [✕ Annuleren] │
└─────────────────────────────────────────────────────────────┘
```
---
## 9. Responsive Design
### 9.1 Mobile (< 768px)
**AfspraakBlock:**
- Full-width block
- Stacked form velden
- Date/time pickers full-width
- Bottom sheet voor dropdowns
**RapportageBlock:**
- Full-width block
- Rich text editor full-width
- AI acties als buttons onder editor
- Bottom sheet voor encounter koppeling
**DiagnoseBlock:**
- Full-width block
- Diagnose cards stacked
- Filter tabs als chips
- Swipe to edit
**DiagnoseFormBlock:**
- Full-width block
- Form velden stacked
- ICD-10 zoeker full-width
- Bottom sheet voor dropdowns
### 9.2 Tablet (768px - 1024px)
- Blocks blijven medium/large size
- Form velden kunnen naast elkaar (waar logisch)
- Dropdowns blijven inline
---
## 10. Accessibility
### 10.1 Screen Reader Support
- Alle form velden hebben labels
- Status changes worden aangekondigd
- Error messages zijn toegankelijk
- Keyboard navigation volledig ondersteund
### 10.2 Focus Management
- Focus blijft in block na openen
- Focus naar eerste veld bij nieuwe block
- Focus naar error veld bij validatie fout
- Focus naar recent strip na sluiten
### 10.3 Color Contrast
- Alle tekst voldoet aan WCAG AA (4.5:1)
- Status indicators hebben tekst labels
- Error states hebben icon + tekst
---
## 11. Animation & Transitions
### 11.1 Block Animations
**Openen:**
- Slide up + fade in (200ms)
- Scale: 0.95 → 1.0
**Sluiten:**
- Slide down + fade out (200ms)
- Scale: 1.0 → 0.95
**Pre-fill Highlight:**
- Pulse animatie (2x) bij pre-filled velden
- Duration: 600ms
### 11.2 Toast Notifications
- Slide in from bottom (300ms)
- Auto-dismiss na 3 seconden
- Hover: pause auto-dismiss
---
## 12. Component Checklist
### 🟡 P2: Diagnostiek Workflow Blocks
- [ ] AfspraakBlock
- [ ] Pre-fill vanuit intent
- [ ] Encounter_id teruggeven
- [ ] Dark theme styling
- [ ] Keyboard shortcuts
- [ ] Error states
- [ ] RapportageBlock (uitgebreid)
- [ ] Encounter koppeling
- [ ] Type "diagnostiek"
- [ ] Rich text editor
- [ ] AI acties
- [ ] Dark theme styling
- [ ] DiagnoseBlock
- [ ] Overzicht diagnoses
- [ ] Filter tabs (Actief/Inactief/Alle)
- [ ] Diagnose cards
- [ ] Empty state
- [ ] Dark theme styling
- [ ] DiagnoseFormBlock
- [ ] ICD-10 zoeker (fuzzy search)
- [ ] Pre-fill vanuit intent
- [ ] Edit mode
- [ ] Status + ernst selectie
- [ ] Dark theme styling
---
## 13. Summary: De Transformatie voor Behandelaars
```
VAN: NAAR:
────────────────────────────────────────────────────────────────
15-20 klikken per traject → 3 zinnen + 3 klikken
10-15 minuten per traject → 3-5 minuten
Menu navigatie → Natuurlijke taal
Handmatige koppeling → Automatische koppeling
ICD-10 handmatig zoeken → Fuzzy search + pre-fill
Vergeten rapportage → Proactieve suggesties
TIJDSBESPARING:
─────────────────────────────────────────────────────────────────
Afspraak plannen: 2-3 min → 30 sec (85% sneller)
Rapportage schrijven: 5-8 min → 2 min (75% sneller)
Diagnose toevoegen: 3-5 min → 1 min (80% sneller)
Per behandelaar per week: ~2 uur terug naar patiëntcontact
```
---
## Wijzigingslog
| Versie | Datum | Wijzigingen |
|--------|-------|-------------|
| 1.0 | 23-12-2024 | Initiële versie - UX/UI specificaties voor diagnostiek workflow |