Files
triqura-ecd/docs/swift/archive/swift-prd.md
2025-12-27 13:17:00 +01:00

632 lines
29 KiB
Markdown

# 📄 Product Requirements Document (PRD) — Contextual UI EPD
**Projectnaam:** Contextual UI EPD — "Het Slimme EPD"
**Versie:** v1.0
**Datum:** 23-12-2024
**Auteur:** Colin Lit
---
## 1. Doelstelling
🎯 **Doel van deze sectie:** Beschrijf waarom dit product of prototype wordt gebouwd en wat het beoogde resultaat is.
### Primair doel
Uitbreiden van het bestaande Speedrun EPD met **Contextual UI**: een systeem waarin voorgedefinieerde interface-componenten **automatisch verschijnen op basis van context** — wie je bent, welke patiënt actief is, welk moment van de dag het is, en wat je probeert te doen.
### Het kernprincipe
> **"Pre-built components, smart triggers"**
>
> We genereren geen UI on-the-fly (te onvoorspelbaar voor zorg), maar tonen **geteste bouwblokken op het juiste moment**. De AI zit in het *bepalen wanneer* en het *vullen van content*, niet in het genereren van interface-elementen.
### Het contrast met traditionele EPD's
| Aspect | Traditioneel EPD | Contextual UI EPD |
|--------|------------------|-------------------|
| Navigatie | 47 menu-items, tabbladen, submenu's | Context bepaalt wat je ziet |
| Interface | Altijd alles zichtbaar | Alleen wat nu relevant is |
| Timing | Gebruiker zoekt zelf | Systeem toont proactief |
| Klikken | 12 klikken voor rapportage | 1 zin of automatisch |
| Context | Gebruiker moet onthouden | Systeem begrijpt situatie |
### Secundaire doelen
- Demonstreren van "intelligent interface" concept voor gesprekken met Nedap, Medicore, etc.
- LinkedIn content over next-gen EPD interfaces
- Technische showcase van context-aware React componenten
- Valideren of "proactieve UI" gewaardeerd wordt door zorgprofessionals
### Relatie met Speedrun EPD
Dit is **geen nieuw product** maar een uitbreiding op de bestaande codebase:
- Hergebruik van alle database schemas
- Hergebruik van bestaande API routes en AI-functionaliteit
- Hergebruik van UI componenten (speech recorder, editors, etc.)
- Toevoeging van Context Engine en Trigger System
---
## 2. Doelgroep
🎯 **Doel:** Schets wie de eindgebruikers, stakeholders en testers zijn.
### Primaire gebruikers
| Rol | Behoeften | Pijnpunten vandaag |
|-----|-----------|-------------------|
| **Verpleegkundige** | Snelle notities tussen zorgmomenten | 40% tijd aan administratie, zoeken naar juiste scherm |
| **SPV/Behandelaar** | Rapportage na gesprek, overdracht | 20 min typen na crisis-interventie |
| **Teamleider** | Overzicht, overdracht ontvangen | Informatie verspreid over schermen |
### Secundaire stakeholders
| Rol | Interesse |
|-----|-----------|
| **ICT Manager** | Minder training nodig, snellere adoptie |
| **Product Owner (demo)** | AI-toegevoegde waarde zien |
| **Developer (inspiratie)** | Context-aware UI patterns leren |
### Gebruikerscontext
De interface moet werken in situaties waar:
- Handen bezet zijn (handschoenen, zorghandelingen)
- Tijd schaars is (tussen patiënten door)
- Concentratie elders ligt (na emotioneel gesprek)
- Meerdere patiënten tegelijk aandacht vragen
---
## 3. Kernfunctionaliteiten (MVP-scope)
🎯 **Doel:** Afbakenen van de minimale werkende functies.
### 3.1 Architectuur: De Drie Lagen
```
┌─────────────────────────────────────────────────────────┐
│ LAAG 1: CONTEXT ENGINE │
│ Houdt bij: gebruiker, patiënt, tijd, dienst, recente │
│ acties. Zustand store + Supabase realtime. │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ LAAG 2: TRIGGER SYSTEM │
│ Rules engine die bepaalt wanneer welk component │
│ verschijnt. Combinatie van tijd, events, en intent. │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ LAAG 3: PRE-BUILT COMPONENTS │
│ Geteste UI-blokken die door triggers worden getoond │
│ en automatisch worden gevuld met relevante data. │
└─────────────────────────────────────────────────────────┘
```
### 3.2 MVP Componenten
| # | Component | Trigger | Data/AI | Prioriteit |
|---|-----------|---------|---------|------------|
| 1 | **Command Input** | Altijd zichtbaar (centraal) | Voice + text | Must |
| 2 | **DagnotatieBlock** | Intent "notitie" of voice | Pre-fill patiënt, categorie | Must |
| 3 | **ZoekenBlock** | Intent "zoek" of patiëntnaam | Fuzzy search results | Must |
| 4 | **PatientContextCard** | Patiënt geselecteerd | Laatste rapportages, alerts | Must |
| 5 | **RapportageBlock** | Intent "gesprek" of "rapportage" | AI-samenvatting optie | Should |
| 6 | **OverdrachtPanel** | Tijd = einde dienst OF intent | AI-samenvatting per patiënt | Should |
| 7 | **AgendaContextCard** | Afspraak binnen 15 min | Patiënt + laatste contact | Could |
| 8 | **FallbackPicker** | Lage intent confidence | Grid met alle opties | Must |
### 3.3 Context Engine Specificatie
```typescript
interface ContextState {
// Gebruiker
currentUser: {
id: string;
name: string;
role: 'verpleegkundige' | 'behandelaar' | 'teamleider';
};
// Dienst
currentShift: {
type: 'ochtend' | 'middag' | 'avond' | 'nacht';
startTime: Date;
endTime: Date;
patients: string[]; // IDs van toegewezen patiënten
};
// Actieve patiënt (sticky)
activePatient: {
id: string;
name: string;
lastContact: Date;
alerts: Alert[];
} | null;
// Recente acties
recentActions: Action[]; // Laatste 5-10
// Tijd-gevoelige context
upcomingAppointment: Appointment | null; // Binnen 30 min
shiftEndingSoon: boolean; // < 1 uur tot einde
}
```
### 3.4 Trigger Rules Specificatie
| Trigger Type | Conditie | Actie | Prioriteit |
|--------------|----------|-------|------------|
| **Intent** | Voice/text input geclassificeerd | Open bijbehorend block | 1 (hoogste) |
| **Patient Select** | Patiënt aangeklikt/gevonden | Toon PatientContextCard | 2 |
| **Time: Appointment** | Afspraak binnen 15 min | Toon AgendaContextCard | 3 |
| **Time: Shift End** | < 1 uur tot einde dienst | Suggestie OverdrachtPanel | 4 |
| **Fallback** | Intent confidence < 0.7 | Toon FallbackPicker | 5 (laagste) |
### 3.5 Intent Classification
**Two-tier approach voor snelheid:**
```typescript
// Tier 1: Local keyword matching (< 10ms)
const quickMatch = (input: string): IntentResult | null => {
const patterns = [
{ regex: /notitie|dagnotitie|noteren/i, intent: 'dagnotitie' },
{ regex: /zoek|vind|wie is/i, intent: 'zoeken' },
{ regex: /overdracht|dienst\s*(eindigt|klaar)/i, intent: 'overdracht' },
{ regex: /rapport|gesprek|consult/i, intent: 'rapportage' },
{ regex: /afspraken?|agenda|planning/i, intent: 'agenda' },
];
// ... matching logic
};
// Tier 2: Claude API (fallback, < 500ms)
const aiClassify = async (input: string): Promise<IntentResult> => {
// Alleen aangeroepen als quickMatch null of low confidence
};
```
**Entity Extraction:**
Naast intent ook extraheren:
- `patient_name`: "Jan de Vries", "mevrouw Jansen"
- `category`: "medicatie", "ADL", "incident"
- `time_reference`: "vandaag", "gisteren", "afgelopen dienst"
---
## 4. Gebruikersflows (Demo- of MVP-flows)
🎯 **Doel:** Laten zien hoe de gebruiker stap-voor-stap door het systeem gaat.
### Flow 1: Snelle Dagnotitie via Voice (30 seconden)
```
┌─────────────────────────────────────────────────────────┐
│ 1. Gebruiker opent Command Center │
│ → Ziet: lege input, context "Ochtend | 8 patiënten" │
├─────────────────────────────────────────────────────────┤
│ 2. Spreekt: "Notitie Jan de Vries medicatie gegeven" │
│ → Deepgram transcribeert real-time │
├─────────────────────────────────────────────────────────┤
│ 3. System analyseert: │
│ → Intent: dagnotitie (confidence 0.95) │
│ → Patient: "Jan de Vries" → ID lookup │
│ → Category: "Medicatie" │
│ → Text: "medicatie gegeven" │
├─────────────────────────────────────────────────────────┤
│ 4. DagnotatieBlock verschijnt: │
│ → Patient: Jan de Vries ✓ (pre-filled) │
│ → Categorie: Medicatie ✓ (pre-filled) │
│ → Tekst: "medicatie gegeven" ✓ (pre-filled) │
│ → [Opslaan] knop highlighted │
├─────────────────────────────────────────────────────────┤
│ 5. Gebruiker: review → klik Opslaan │
│ → Toast: "Notitie opgeslagen" │
│ → Block verdwijnt │
│ → Recent badge: "Jan - Medicatie" verschijnt │
└─────────────────────────────────────────────────────────┘
```
### Flow 2: Patiënt Opzoeken + Context (45 seconden)
```
┌─────────────────────────────────────────────────────────┐
│ 1. Gebruiker typt: "Jan" │
│ → Intent: zoeken │
│ → Query: "Jan" │
├─────────────────────────────────────────────────────────┤
│ 2. ZoekenBlock verschijnt met matches: │
│ ┌──────────────────────────────────────────────┐ │
│ │ Jan de Vries │ 15-03-1965 │ Laatst: 2u │ │
│ │ Jan Bakker │ 22-08-1978 │ Laatst: 1d │ │
│ │ Jantine Smit │ 04-11-1990 │ Laatst: 3d │ │
│ └──────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 3. Gebruiker klikt "Jan de Vries" │
│ → activePatient wordt gezet │
│ → ZoekenBlock sluit │
├─────────────────────────────────────────────────────────┤
│ 4. PatientContextCard verschijnt: │
│ ┌──────────────────────────────────────────────┐ │
│ │ JAN DE VRIES (59) │ │
│ │ ───────────────────────────────────────── │ │
│ │ Laatste contact: 2 uur geleden │ │
│ │ │ │
│ │ Recente notities: │ │
│ │ • 09:15 Medicatie uitgereikt │ │
│ │ • Gisteren: Goed gesprek over ontslag │ │
│ │ │ │
│ │ ⚠️ Let op: 2 valincidenten deze week │ │
│ │ │ │
│ │ [Notitie] [Rapportage] [Behandelplan] │ │
│ └──────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 5. Gebruiker klikt [Notitie] │
│ → DagnotatieBlock opent met Jan pre-filled │
└─────────────────────────────────────────────────────────┘
```
### Flow 3: Automatische Overdracht Suggestie (proactief)
```
┌─────────────────────────────────────────────────────────┐
│ Context: Het is 15:15, dienst eindigt om 16:00 │
├─────────────────────────────────────────────────────────┤
│ 1. System detecteert: shiftEndingSoon = true │
│ → Subtiele banner verschijnt: │
│ ┌──────────────────────────────────────────────┐ │
│ │ 🕐 Dienst eindigt over 45 min │ │
│ │ Wil je alvast de overdracht voorbereiden? │ │
│ │ [Start overdracht] [Later] │ │
│ └──────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 2. Gebruiker klikt [Start overdracht] │
│ → OverdrachtPanel opent │
├─────────────────────────────────────────────────────────┤
│ 3. OverdrachtPanel toont: │
│ ┌──────────────────────────────────────────────┐ │
│ │ OVERDRACHT OCHTEND → MIDDAG │ │
│ │ 08:00 - 16:00 | 8 patiënten │ │
│ │ ───────────────────────────────────────── │ │
│ │ │ │
│ │ ▼ Jan de Vries │ │
│ │ AI-samenvatting: Rustige ochtend, medicatie│ │
│ │ uitgereikt zonder problemen. Let op val- │ │
│ │ risico bij toiletbezoek. │ │
│ │ [Bronnen: 3 notities] │ │
│ │ │ │
│ │ ▼ Marie van den Berg │ │
│ │ AI-samenvatting: ... │ │
│ │ │ │
│ │ [Kopieer alles] [Verstuur naar collega] │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### Flow 4: Ambigue Input met Fallback
```
┌─────────────────────────────────────────────────────────┐
│ 1. Gebruiker typt: "medicatie" │
│ → Intent: onduidelijk (dagnotitie? metingen?) │
│ → Confidence: 0.5 │
│ → Geen patiënt gespecificeerd │
├─────────────────────────────────────────────────────────┤
│ 2. FallbackPicker verschijnt: │
│ ┌──────────────────────────────────────────────┐ │
│ │ Wat wil je doen? │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 📝 │ │ 🔍 │ │ 📋 │ │ │
│ │ │ Notitie │ │ Zoeken │ │ Rapport │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 🔄 │ │ 📅 │ │ 💊 │ │ │
│ │ │Overdracht│ │ Agenda │ │ Meting │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 3. Gebruiker klikt [Notitie] │
│ → DagnotatieBlock opent │
│ → "medicatie" ingevuld als tekst │
│ → Vraagt om patiënt te selecteren │
└─────────────────────────────────────────────────────────┘
```
---
## 5. Niet in Scope
🎯 **Doel:** Duidelijk maken wat (nog) niet wordt gebouwd.
### Expliciet uitgesloten (v1)
| Feature | Reden |
|---------|-------|
| **Volledig voice-only navigatie** | Voice is input, niet navigatie. Te foutgevoelig. |
| **AI-gegenereerde UI componenten** | Onvoorspelbaar, niet geschikt voor zorg. Pre-built only. |
| **Ambient listening** | Privacy concerns, v2+ feature |
| **Multi-user realtime** | Complexiteit, niet nodig voor demo |
| **Offline mode** | Complexiteit, later toevoegen |
| **Behandelplan block** | Te complex, lage frequentie, bestaande UI voldoet |
| **Intake block** | Wizard is complex, 1x/maand, lage waarde voor MVP |
| **Metingen block** | Lage waarde-perceptie bij stakeholders |
| **Native mobile app** | Responsive web is voldoende |
| **FHIR/externe EPD integratie** | Post-MVP, apart project |
### Bewust versimpeld (v1)
| Aspect | Versimpeling |
|--------|--------------|
| **Diensten** | Hardcoded tijden (08-16, 16-23, 23-08) |
| **Patiënt-toewijzing** | Alle patiënten zichtbaar, geen restricties |
| **Alerts** | Alleen handmatig toegevoegd, geen automatische detectie |
---
## 6. Succescriteria
🎯 **Doel:** Objectieve meetlat voor een geslaagde oplevering.
### Functionele criteria
| Criterium | Target | Meetmethode |
|-----------|--------|-------------|
| Intent classification accuracy | > 85% | Test set van 50 voorbeelden |
| Voice transcription accuracy | > 90% | Handmatige review sample |
| Pre-fill correctheid | > 90% | Juiste patiënt/categorie |
| Fallback usage | < 25% | Picker clicks / totaal |
| "Notitie Jan medicatie" → save | < 30 sec | Timestamp logging |
### Performance criteria
| Criterium | Target |
|-----------|--------|
| Intent classification | < 500ms |
| Block render | < 200ms |
| Voice transcription latency | < 100ms |
| PatientContextCard laden | < 300ms |
### UX criteria
| Criterium | Target |
|-----------|--------|
| Klikken tot taak compleet | Gemiddeld < 3 |
| Training nodig | Zero (intuïtief) |
| "Dit voelt als magie" feedback | Minimaal 1 test user |
### Business criteria
| Criterium | Target |
|-----------|--------|
| Demo-ready | Ja, 3 scenario's foutloos |
| LinkedIn content | 2+ posts over concept |
| Stakeholder interesse | Minimaal 1 concrete vervolgvraag |
---
## 7. Risico's & Mitigatie
🎯 **Doel:** Risico's vroeg signaleren en plannen hoe ermee om te gaan.
| Risico | Kans | Impact | Mitigatie |
|--------|------|--------|-----------|
| **Intent classification onnauwkeurig** | Medium | Hoog | Fallback picker altijd beschikbaar, local-first matching |
| **Voice niet goed in Nederlands** | Laag | Medium | Deepgram NL model, tekst fallback altijd mogelijk |
| **Pre-fill verkeerde patiënt** | Medium | Hoog | Altijd confirmation tonen, nooit blind opslaan |
| **Context engine te complex** | Medium | Medium | Minimale context in v1, uitbreiden in v2 |
| **Gebruikers missen "overzicht"** | Medium | Medium | Bestaande patiëntenlijst als alternatieve entry |
| **Performance AI calls** | Medium | Medium | Local-first matching, streaming responses, caching |
| **Scope creep** | Hoog | Hoog | Strikte MVP scope, parking lot voor ideeën |
| **Demo deadline druk** | Medium | Hoog | Focus op 3 happy paths, polish later |
---
## 8. Roadmap / Vervolg (Post-MVP)
🎯 **Doel:** Richting geven aan toekomstige uitbreidingen.
### Fase 2: Extended Blocks (Post-MVP)
- **AgendaBlock** - Afspraken beheren, context bij naderende afspraak
- **BehandelplanBlock** - Wrapper rond bestaande functionaliteit
- **MetingenBlock** - Vitale functies invoer met trends
### Fase 3: Smart Triggers
- **Proactieve alerts** - "3 valincidenten, wil je een risico-analyse?"
- **Behandelplan reminder** - "Plan verloopt over 2 weken"
- **Automatische categorisatie** - ML-model voor notitie-types
### Fase 4: Team Features
- **Shift handover** - Gestructureerde overdracht workflow
- **Team dashboard** - Overzicht alle patiënten, alerts
- **Notificaties** - Push bij urgente updates
### Fase 5: Integraties
- **FHIR export** - Standaard zorgdata uitwisseling
- **Externe EPD sync** - Koppeling met Nedap, PinkRoccade
- **Calendar sync** - Google/Outlook afspraken importeren
---
## 9. Technische Architectuur (Overzicht)
### Nieuwe Routes
```
/app
/(app)
/command-center
/page.tsx # Hoofdscherm
/components/
CommandInput.tsx # Text + voice input
BlockContainer.tsx # Generic block wrapper
FallbackPicker.tsx # Block selection grid
RecentActions.tsx # Recent badges
/api
/intent
/classify/route.ts # Intent classification
/context
/route.ts # Get/set user context
/patients
/search/route.ts # Fuzzy patient search
```
### Building Blocks
```
/components/building-blocks/
/dagnotitie/
DagnotatieBlock.tsx
/zoeken/
ZoekenBlock.tsx
PatientCard.tsx
/rapportage/
RapportageBlock.tsx
/overdracht/
OverdrachtPanel.tsx
/context/
PatientContextCard.tsx
AgendaContextCard.tsx
/shared/
BlockContainer.tsx
BlockHeader.tsx
```
### State Management (Zustand)
```typescript
// stores/context-store.ts
interface ContextStore {
// User & shift
currentUser: User;
currentShift: Shift;
// Active patient (sticky)
activePatient: Patient | null;
setActivePatient: (patient: Patient | null) => void;
// Recent actions
recentActions: Action[];
addRecentAction: (action: Action) => void;
// Time-based context
upcomingAppointment: Appointment | null;
shiftEndingSoon: boolean;
}
// stores/command-center-store.ts
interface CommandCenterStore {
// Active block
activeBlock: BlockType | null;
blockPrefill: Record<string, unknown>;
// Input state
inputValue: string;
isListening: boolean;
transcript: string;
// Actions
processInput: (text: string) => Promise<void>;
openBlock: (type: BlockType, prefill?: object) => void;
closeBlock: () => void;
}
```
---
## 10. Fasering & Sprint Planning
### Sprint 1: Foundation (Dag 1-2)
| Taak | Output | Uren |
|------|--------|------|
| Command Center page layout | `/app/command-center/page.tsx` | 2 |
| CommandInput component | Text + submit | 2 |
| Voice integratie | Mic button, Deepgram | 2 |
| Context store setup | Zustand store | 2 |
| BlockContainer wrapper | Generic frame | 1 |
**Deliverable:** Command Center opent, voice werkt, geen blocks nog.
### Sprint 2: Intent & Zoeken (Dag 3-4)
| Taak | Output | Uren |
|------|--------|------|
| Intent API route | `/api/intent/classify` | 3 |
| Local keyword matching | Quick patterns | 2 |
| Entity extraction | Patient name uit tekst | 2 |
| ZoekenBlock | Patient cards + select | 3 |
| FallbackPicker | Grid met icons | 2 |
**Deliverable:** "zoek jan" werkt, patient selectie mogelijk.
### Sprint 3: Dagnotitie Flow (Dag 5-6)
| Taak | Output | Uren |
|------|--------|------|
| DagnotatieBlock | Quick entry form | 3 |
| Pre-fill logic | Patient + categorie | 2 |
| Save flow + toast | Feedback | 1 |
| PatientContextCard | Na selectie tonen | 3 |
| Recent actions strip | Badges | 2 |
**Deliverable:** "notitie jan medicatie" werkt end-to-end.
### Sprint 4: Polish & Demo (Dag 7-8)
| Taak | Output | Uren |
|------|--------|------|
| RapportageBlock | Wrapper rond composer | 3 |
| OverdrachtPanel | AI-samenvatting | 4 |
| Animaties | Transitions | 2 |
| Demo scenario's | 3 happy paths | 2 |
| Bug fixes | Stability | 3 |
**Deliverable:** Demo-ready voor stakeholders.
### Totaal: ~42 uur over 8 dagen
---
## 11. Bijlagen & Referenties
🎯 **Doel:** Bronnen koppelen voor context en consistentie.
### Gerelateerde documenten
| Document | Locatie | Beschrijving |
|----------|---------|--------------|
| Ephemeral UI Research | `Ephemeral_UI_for_Healthcare...md` | Achtergrond contextual interfaces |
| PRD Ephemeral UI EPD | `nextgen-epd-prd-ephemeral-ui-epd.md` | Originele visie document |
| MVP Prioritering | `nextgen-epd-mvp-prioritering-ephemeral-ui.md` | Scope beslissingen |
| Waarde-analyse | `nextgen-epd-waarde-analyse-ephemeral-ui.md` | Klant & PO perspectief |
| FO Mini-ECD v2 | `fo-mini-ecd-v2.md` | Bestaande EPD functionaliteit |
| TO Mini-ECD | `to-mini-ecd-v1_2.md` | Technische basis |
### Bestaande codebase (hergebruik)
| Component | Locatie | Hergebruik |
|-----------|---------|------------|
| Speech Recorder | `components/speech-recorder.tsx` | 100% |
| Deepgram API | `api/deepgram/transcribe/route.ts` | 100% |
| Toast System | `lib/hooks/use-toast.ts` | 100% |
| Command (cmdk) | `components/ui/command.tsx` | 90% |
| Dagregistratie Form | `app/epd/dagregistratie/` | 80% |
| Report Types | `lib/types/report.ts` | 100% |
---
## Wijzigingslog
| Versie | Datum | Wijzigingen |
|--------|-------|-------------|
| 1.0 | 23-12-2024 | Initiële versie |