513 lines
16 KiB
Markdown
513 lines
16 KiB
Markdown
# MVP & Prioritering Ephemeral UI EPD
|
|
|
|
**Document:** MVP Scope en Implementatie Prioritering
|
|
**Datum:** december 2024
|
|
**Status:** Definitief
|
|
|
|
---
|
|
|
|
## 1. MVP Definitie
|
|
|
|
### 1.1 Eén Zin
|
|
|
|
> **MVP = Een Command Center waarmee je met tekst of voice een dagnotitie of rapportage maakt, met automatische patient herkenning.**
|
|
|
|
### 1.2 MVP Scope
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ MVP SCOPE │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ IN SCOPE (Must Have) │
|
|
│ ───────────────────── │
|
|
│ ✓ Command Center pagina │
|
|
│ ✓ Text input met intent herkenning │
|
|
│ ✓ Voice input (Deepgram - bestaat al) │
|
|
│ ✓ Dagnotitie blok │
|
|
│ ✓ Zoeken blok │
|
|
│ ✓ Patient pre-fill uit input │
|
|
│ ✓ Fallback blok-picker │
|
|
│ │
|
|
│ SHOULD HAVE (Demo Enhanced) │
|
|
│ ─────────────────────────── │
|
|
│ ○ Rapportage blok │
|
|
│ ○ Overdracht blok met AI │
|
|
│ ○ Recent actions tracking │
|
|
│ ○ Context bar (dienst info) │
|
|
│ │
|
|
│ OUT OF SCOPE (v1) │
|
|
│ ───────────────── │
|
|
│ ✗ Behandelplan blok │
|
|
│ ✗ Intake blok │
|
|
│ ✗ Agenda blok │
|
|
│ ✗ Metingen blok │
|
|
│ ✗ Ambient listening │
|
|
│ ✗ Multi-user realtime │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 1.3 Waarom Deze Scope?
|
|
|
|
| Keuze | Rationale |
|
|
|-------|-----------|
|
|
| Dagnotitie eerst | Hoogste frequentie (20x/dag), simpelste form |
|
|
| Zoeken als fundament | Elke actie begint met "wie" - zonder zoeken geen pre-fill |
|
|
| Voice must-have | "Ik draag handschoenen" - zorg context vereist hands-free |
|
|
| Fallback verplicht | Vertrouwen: nooit een doodlopende straat |
|
|
| Geen behandelplan | Te complex, lage frequentie, bestaande UI voldoet |
|
|
|
|
---
|
|
|
|
## 2. Hergebruik Bestaande Code
|
|
|
|
### 2.1 Direct Herbruikbaar
|
|
|
|
| Component | Locatie | Hergebruik |
|
|
|-----------|---------|------------|
|
|
| **Speech Recorder** | `components/speech-recorder.tsx` | 100% - drop-in |
|
|
| **Deepgram API** | `api/deepgram/transcribe/route.ts` | 100% - werkt |
|
|
| **Toast System** | `lib/hooks/use-toast.ts` | 100% - feedback |
|
|
| **Command (cmdk)** | `components/ui/command.tsx` | 90% - zoek basis |
|
|
| **Dagregistratie Form** | `app/epd/dagregistratie/` | 80% - wrap als blok |
|
|
| **Report Types** | `lib/types/report.ts` | 100% - validatie |
|
|
| **Verpleegkundig API** | `api/reports/route.ts` | 100% - save |
|
|
|
|
### 2.2 Gedeeltelijk Herbruikbaar
|
|
|
|
| Component | Locatie | Aanpassing Nodig |
|
|
|-----------|---------|------------------|
|
|
| **Patient List** | `app/epd/verpleegrapportage/` | → PatientCard voor zoeken |
|
|
| **EPD Layout** | `app/epd/layout.tsx` | → Command Center layout |
|
|
| **Report Composer** | `app/epd/patients/[id]/rapportage/` | → RapportageBlock wrapper |
|
|
|
|
### 2.3 Nieuw te Bouwen
|
|
|
|
| Component | Geschatte Effort | Prioriteit |
|
|
|-----------|------------------|------------|
|
|
| `/app/command-center/page.tsx` | 4h | P1 |
|
|
| `/api/intent/classify/route.ts` | 6h | P1 |
|
|
| `CommandInput.tsx` | 3h | P1 |
|
|
| `BlockContainer.tsx` | 2h | P1 |
|
|
| `DagnotitieBlock.tsx` | 3h | P1 |
|
|
| `ZoekenBlock.tsx` | 4h | P1 |
|
|
| `FallbackPicker.tsx` | 2h | P1 |
|
|
| `PatientResolver.ts` | 2h | P1 |
|
|
| `useCommandCenter.ts` (store) | 3h | P1 |
|
|
| **Totaal MVP** | **~29h** | - |
|
|
|
|
---
|
|
|
|
## 3. Prioritering: MoSCoW
|
|
|
|
### 3.1 Must Have (Release Blocker)
|
|
|
|
```
|
|
M1. Command Center Layout
|
|
└── Centrale pagina met input field
|
|
└── Route: /command-center
|
|
|
|
M2. Intent Classification
|
|
└── API endpoint dat input analyseert
|
|
└── Returns: { intent, entities, confidence }
|
|
|
|
M3. Dagnotitie Blok
|
|
└── Simpele form: patient, categorie, tekst
|
|
└── Pre-fill support
|
|
└── 1-click save
|
|
|
|
M4. Zoeken Blok
|
|
└── Patient search met cmdk
|
|
└── PatientCard met quick actions
|
|
└── Set active patient
|
|
|
|
M5. Voice Input
|
|
└── Deepgram integratie in Command input
|
|
└── Live transcript display
|
|
|
|
M6. Fallback Picker
|
|
└── Grid met blok icons
|
|
└── Toont bij lage confidence of onbekende intent
|
|
|
|
M7. Patient Pre-fill
|
|
└── Entity extraction uit input
|
|
└── Patient name → ID resolver
|
|
```
|
|
|
|
### 3.2 Should Have (Demo Value)
|
|
|
|
```
|
|
S1. Rapportage Blok
|
|
└── Wrapper rond bestaande ReportComposer
|
|
└── Voice dictation in editor
|
|
└── AI structurering knop
|
|
|
|
S2. Overdracht Blok
|
|
└── AI samenvatting (API bestaat)
|
|
└── Multi-patient view
|
|
└── Bronverwijzingen
|
|
|
|
S3. Recent Actions
|
|
└── Badge strip onder input
|
|
└── Click to re-open
|
|
|
|
S4. Context Bar
|
|
└── "Ochtend dienst | 8 patiënten"
|
|
└── Shift awareness
|
|
```
|
|
|
|
### 3.3 Could Have (Nice to Have)
|
|
|
|
```
|
|
C1. Categorie Herkenning
|
|
└── "medicatie" → Medicatie category
|
|
└── Keyword mapping
|
|
|
|
C2. Animaties
|
|
└── Block slide-in
|
|
└── Pre-fill highlight
|
|
└── Success celebration
|
|
|
|
C3. Keyboard Shortcuts
|
|
└── Cmd+K → focus input
|
|
└── Enter → submit
|
|
└── Esc → close block
|
|
|
|
C4. Onboarding Hints
|
|
└── "Probeer: notitie jan"
|
|
└── First-time user guidance
|
|
```
|
|
|
|
### 3.4 Won't Have (Explicit Out)
|
|
|
|
```
|
|
W1. Behandelplan Blok - te complex, lage frequentie
|
|
W2. Intake Blok - wizard is complex, 1x/maand
|
|
W3. Agenda Blok - bestaande werkt, lage urgentie
|
|
W4. Metingen Blok - lage waarde-perceptie
|
|
W5. Ambient Listening - v2+ feature
|
|
W6. Offline Mode - v2+ feature
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Implementatie Volgorde
|
|
|
|
### 4.1 Dependency Graph
|
|
|
|
```
|
|
┌─────────────────┐
|
|
│ Command Center │
|
|
│ Layout │
|
|
└────────┬────────┘
|
|
│
|
|
┌──────────────┼──────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
│ Voice │ │ Intent │ │ Fallback │
|
|
│ Input │ │ API │ │ Picker │
|
|
└────┬─────┘ └────┬─────┘ └──────────┘
|
|
│ │
|
|
│ ┌───────┴───────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌──────────────┐ ┌──────────────┐
|
|
│ Zoeken │ │ Dagnotitie │
|
|
│ Blok │ │ Blok │
|
|
└──────┬───────┘ └──────────────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ Pre-fill │
|
|
│ Logic │
|
|
└──────────────┘
|
|
```
|
|
|
|
### 4.2 Sprint Planning
|
|
|
|
#### Sprint 1: Foundation (Dag 1-3)
|
|
|
|
| # | Taak | Afhankelijk van | Output |
|
|
|---|------|-----------------|--------|
|
|
| 1.1 | Command Center page layout | - | `/app/command-center/page.tsx` |
|
|
| 1.2 | CommandInput component | 1.1 | Text input met submit |
|
|
| 1.3 | BlockContainer wrapper | 1.1 | Generic block frame |
|
|
| 1.4 | Zustand store setup | - | `useCommandCenter` hook |
|
|
| 1.5 | Voice integratie | 1.2 | Mic button in input |
|
|
|
|
**Deliverable:** Command Center opent, voice werkt, geen blokken nog.
|
|
|
|
#### Sprint 2: Intent & Zoeken (Dag 4-6)
|
|
|
|
| # | Taak | Afhankelijk van | Output |
|
|
|---|------|-----------------|--------|
|
|
| 2.1 | Intent API route | - | `/api/intent/classify` |
|
|
| 2.2 | Entity extraction | 2.1 | Patient name uit tekst |
|
|
| 2.3 | Patient search API | - | `/api/patients/search` |
|
|
| 2.4 | ZoekenBlock | 1.3, 2.3 | Patient cards + select |
|
|
| 2.5 | Fallback picker | 1.3 | Grid met blok icons |
|
|
|
|
**Deliverable:** "zoek jan" werkt, patient selectie mogelijk.
|
|
|
|
#### Sprint 3: Dagnotitie Flow (Dag 7-9)
|
|
|
|
| # | Taak | Afhankelijk van | Output |
|
|
|---|------|-----------------|--------|
|
|
| 3.1 | DagnotitieBlock | 1.3 | Quick entry form |
|
|
| 3.2 | Pre-fill logic | 2.2, 3.1 | Patient + categorie auto |
|
|
| 3.3 | Save flow | 3.1 | Toast + recent badge |
|
|
| 3.4 | Intent → Block routing | 2.1, 3.1 | "notitie jan" → block |
|
|
|
|
**Deliverable:** "notitie voor jan: medicatie gegeven" werkt end-to-end.
|
|
|
|
#### Sprint 4: Polish & Demo (Dag 10-12)
|
|
|
|
| # | Taak | Afhankelijk van | Output |
|
|
|---|------|-----------------|--------|
|
|
| 4.1 | RapportageBlock | 1.3 | Wrapper rond composer |
|
|
| 4.2 | Recent actions strip | 3.3 | Clickable badges |
|
|
| 4.3 | Animaties | All | Smooth transitions |
|
|
| 4.4 | Demo scenarios | All | 3 happy paths |
|
|
| 4.5 | Bug fixes | All | Stability |
|
|
|
|
**Deliverable:** Demo-ready voor stakeholders.
|
|
|
|
---
|
|
|
|
## 5. Technische Beslissingen
|
|
|
|
### 5.1 State Management
|
|
|
|
**Besluit:** Zustand
|
|
|
|
```typescript
|
|
// stores/command-center.ts
|
|
interface CommandCenterState {
|
|
// Active state
|
|
activeBlock: 'dagnotitie' | 'zoeken' | 'rapportage' | 'overdracht' | null
|
|
activePatient: Patient | null
|
|
|
|
// Input state
|
|
inputValue: string
|
|
isListening: boolean
|
|
transcript: string
|
|
|
|
// Pre-fill data
|
|
prefillData: {
|
|
patientName?: string
|
|
category?: string
|
|
text?: string
|
|
}
|
|
|
|
// History
|
|
recentActions: Action[]
|
|
|
|
// Actions
|
|
processInput: (text: string) => Promise<void>
|
|
openBlock: (type: BlockType, prefill?: object) => void
|
|
closeBlock: () => void
|
|
setActivePatient: (patient: Patient) => void
|
|
}
|
|
```
|
|
|
|
### 5.2 Intent Classification
|
|
|
|
**Besluit:** Two-tier approach
|
|
|
|
```typescript
|
|
// Tier 1: Local keyword matching (instant)
|
|
const quickMatch = (input: string) => {
|
|
if (/notitie|dagnotitie/i.test(input)) return { intent: 'dagnotitie', confidence: 0.9 }
|
|
if (/zoek|vind|wie is/i.test(input)) return { intent: 'zoeken', confidence: 0.9 }
|
|
if (/overdracht|dienst/i.test(input)) return { intent: 'overdracht', confidence: 0.9 }
|
|
if (/rapport|gesprek/i.test(input)) return { intent: 'rapportage', confidence: 0.85 }
|
|
return null
|
|
}
|
|
|
|
// Tier 2: Claude API (fallback)
|
|
const aiClassify = async (input: string) => {
|
|
// Only called if quickMatch returns null or low confidence
|
|
}
|
|
```
|
|
|
|
### 5.3 Patient Resolution
|
|
|
|
**Besluit:** Fuzzy search met Supabase
|
|
|
|
```typescript
|
|
// Fuzzy match op naam
|
|
const searchPatients = async (query: string) => {
|
|
const { data } = await supabase
|
|
.from('patients')
|
|
.select('id, name, birth_date')
|
|
.ilike('name', `%${query}%`)
|
|
.limit(5)
|
|
return data
|
|
}
|
|
|
|
// Als 1 match → auto-select
|
|
// Als >1 match → toon ZoekenBlock
|
|
// Als 0 matches → "Geen patient gevonden"
|
|
```
|
|
|
|
### 5.4 Block Architecture
|
|
|
|
**Besluit:** Wrapper pattern
|
|
|
|
```typescript
|
|
// Elk blok krijgt dezelfde interface
|
|
interface BlockProps {
|
|
prefill?: Record<string, unknown>
|
|
onComplete: (result: unknown) => void
|
|
onClose: () => void
|
|
}
|
|
|
|
// BlockContainer handelt header, minimize, close
|
|
<BlockContainer title="Dagnotitie" icon={FileText}>
|
|
<DagnotitieBlock prefill={prefill} onComplete={handleComplete} />
|
|
</BlockContainer>
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Risico's & Mitigaties
|
|
|
|
| Risico | Kans | Impact | Mitigatie |
|
|
|--------|------|--------|-----------|
|
|
| Intent accuracy <80% | Medium | Hoog | Fallback picker altijd zichtbaar |
|
|
| Voice niet goed in NL | Laag | Medium | Deepgram NL model + tekst fallback |
|
|
| Pre-fill verkeerde patient | Medium | Hoog | Altijd confirmation, nooit blind save |
|
|
| Latency AI calls | Medium | Medium | Local-first matching, AI als backup |
|
|
| Demo deadline druk | Medium | Hoog | Scope strict houden, no feature creep |
|
|
|
|
---
|
|
|
|
## 7. Success Metrics
|
|
|
|
### 7.1 MVP Success (Must Hit)
|
|
|
|
| Metric | Target | Hoe Meten |
|
|
|--------|--------|-----------|
|
|
| "notitie jan" → save | <30 sec | Timestamp logs |
|
|
| Intent accuracy | >85% | Correct block / total |
|
|
| Voice transcription | >90% accuracy | Manual review sample |
|
|
| Fallback usage | <25% | Picker clicks / total |
|
|
|
|
### 7.2 Demo Success
|
|
|
|
| Metric | Target |
|
|
|--------|--------|
|
|
| 3 scenario's foutloos | 100% |
|
|
| Stakeholder "wow" moment | Ja |
|
|
| Concrete vervolgafspraak | Ja |
|
|
|
|
---
|
|
|
|
## 8. Definition of Done
|
|
|
|
### 8.1 Per Sprint
|
|
|
|
- [ ] Alle taken in sprint completed
|
|
- [ ] Geen console errors
|
|
- [ ] Happy path werkt
|
|
- [ ] Code reviewed
|
|
|
|
### 8.2 MVP Done
|
|
|
|
- [ ] Command Center bereikbaar op `/command-center`
|
|
- [ ] Voice input werkt
|
|
- [ ] "notitie voor [patient]" opent DagnotitieBlock
|
|
- [ ] "zoek [naam]" toont PatientCards
|
|
- [ ] Pre-fill werkt voor patient naam
|
|
- [ ] Fallback picker werkt
|
|
- [ ] Save + toast feedback werkt
|
|
- [ ] Geen blocking bugs
|
|
- [ ] 3 demo scenario's gedocumenteerd
|
|
|
|
---
|
|
|
|
## 9. Demo Scenario's
|
|
|
|
### Scenario 1: Snelle Dagnotitie (30 sec)
|
|
|
|
```
|
|
1. Open Command Center
|
|
2. Type of spreek: "notitie voor Jan de Vries: medicatie uitgereikt"
|
|
3. System:
|
|
- Herkent intent: dagnotitie
|
|
- Herkent patient: Jan de Vries
|
|
- Pre-fills categorie: Medicatie
|
|
- Pre-fills tekst: "medicatie uitgereikt"
|
|
4. User: Review → Opslaan
|
|
5. Toast: "Notitie opgeslagen"
|
|
6. Recent badge verschijnt
|
|
```
|
|
|
|
### Scenario 2: Patient Zoeken + Notitie (45 sec)
|
|
|
|
```
|
|
1. Type: "notitie marie"
|
|
2. System: Meerdere "Marie" gevonden
|
|
3. Toont: ZoekenBlock met 3 matches
|
|
4. User: Selecteert "Marie van den Berg"
|
|
5. System: Opent DagnotitieBlock met patient ingevuld
|
|
6. User: Typt notitie → Opslaan
|
|
```
|
|
|
|
### Scenario 3: Voice Flow (40 sec)
|
|
|
|
```
|
|
1. Klik mic button
|
|
2. Spreek: "Mevrouw Jansen heeft goed gegeten en haar medicatie ingenomen"
|
|
3. System:
|
|
- Transcribeert real-time
|
|
- Herkent: patient = Jansen, categorie = ADL + Medicatie
|
|
4. Toont: DagnotitieBlock met alles ingevuld
|
|
5. User: Review → Opslaan
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Appendix: File Structure
|
|
|
|
```
|
|
app/
|
|
├── command-center/
|
|
│ ├── page.tsx # Main Command Center
|
|
│ └── components/
|
|
│ ├── command-input.tsx # Text + voice input
|
|
│ ├── voice-indicator.tsx # Listening state UI
|
|
│ ├── block-container.tsx # Generic block wrapper
|
|
│ ├── fallback-picker.tsx # Block selection grid
|
|
│ └── recent-actions.tsx # Recent badges strip
|
|
|
|
├── api/
|
|
│ └── intent/
|
|
│ └── classify/
|
|
│ └── route.ts # Intent classification API
|
|
|
|
components/
|
|
└── building-blocks/
|
|
├── dagnotitie-block.tsx # Quick entry form
|
|
├── zoeken-block.tsx # Patient search
|
|
├── rapportage-block.tsx # Report composer wrapper
|
|
└── overdracht-block.tsx # Handover summary
|
|
|
|
stores/
|
|
└── command-center.ts # Zustand store
|
|
|
|
lib/
|
|
├── intent/
|
|
│ ├── classifier.ts # Intent classification logic
|
|
│ ├── entity-extractor.ts # Extract patient, category
|
|
│ └── patient-resolver.ts # Name → Patient lookup
|
|
└── types/
|
|
└── command-center.ts # TypeScript types
|
|
```
|
|
|
|
---
|
|
|
|
*Dit document is de single source of truth voor MVP scope en prioritering.*
|