22 KiB
Analyse Ephemeral UI EPD - Multi-Perspectief Review
Document: Analyse PRD Ephemeral UI EPD v1.0 Datum: december 2024 Reviewers: Lead Developer, UX Designer, Frontend Developer, Product Owner
1. Lead Developer Perspectief
1.1 Technische Haalbaarheid
Verdict: ✅ Haalbaar met bestaande stack
De PRD vraagt om functionaliteit die grotendeels al bestaat in de codebase. De technische uitdaging zit niet in de bouwblokken zelf, maar in de orchestratielaag.
Wat we al hebben:
| Component | Status | Kwaliteit |
|---|---|---|
| Next.js 14 App Router | ✅ | Production-ready |
| Supabase Auth + RLS | ✅ | Volledig geconfigureerd |
| Claude API integratie | ✅ | Werkt voor behandelplan + overdracht |
| Deepgram STT | ✅ | Streaming + batch transcriptie |
| TipTap Rich Text | ✅ | Met voice-insert support |
| shadcn/ui + Tailwind | ✅ | 20+ componenten |
Wat nieuw gebouwd moet worden:
Prioriteit 1 (Kritiek):
├── /api/intent/classify # AI intent classificatie
├── /app/command-center # Nieuwe entry point
└── stores/commandCenter.ts # State management
Prioriteit 2 (Bouwblok wrappers):
├── components/building-blocks/BlockContainer.tsx
├── components/building-blocks/PatientResolver.tsx
└── components/building-blocks/EntityExtractor.tsx
Prioriteit 3 (Nieuwe blokken):
├── ZoekenBlock.tsx # Patient search UI
└── MetingenBlock.tsx # Vitals input form
1.2 Architectuurbeslissingen
State Management: Aanbeveling: Zustand boven Context API
- Command Center heeft complexe state (activeBlock, activePatient, transcript, recentActions)
- Zustand is al impliciet beschikbaar via React patterns in codebase
- Geen prop drilling, geen provider nesting
// Voorgestelde store structuur
interface CommandCenterStore {
// UI State
activeBlock: BlockType | null;
isListening: boolean;
transcript: string;
// Context
activePatient: Patient | null;
recentActions: Action[];
shiftInfo: ShiftInfo;
// Pre-fill data extracted from intent
prefillData: Record<string, unknown>;
}
Intent Classification: Aanbeveling: Streaming response met confidence threshold
// Twee-staps flow
1. Quick classification (<100ms): regex + keyword matching
2. AI fallback (>100ms): Claude voor ambigue input
// Voorbeeld
"notitie jan" → Quick match: intent=dagnotitie, patient="jan"
"ik heb net iets besproken" → AI needed: wat? met wie?
API Design:
POST /api/intent/classify
Input: { text: string, context: { activePatient?, shift? } }
Output: { intent, confidence, entities, clarification? }
GET /api/context
Output: { user, shift, patients, pendingItems }
1.3 Risico's & Mitigaties
| Risico | Impact | Mitigatie |
|---|---|---|
| Intent misclassificatie | Hoog | Fallback naar blok-selector UI |
| Latency AI calls | Medium | Local-first matching, AI als fallback |
| Entity extraction fout | Medium | "Bedoelde je X?" confirmation flow |
| State sync issues | Medium | Optimistic UI + server reconciliation |
| Voice in lawaaierige omgeving | Medium | Push-to-talk, geen continuous listening |
1.4 Technische Schuld Risico
Laag risico - We bouwen bovenop bestaande patterns:
- Bestaande API routes blijven werken
- Bouwblokken zijn wrappers rond bestaande componenten
- Geen database schema wijzigingen nodig
- Geen breaking changes voor huidige EPD flows
1.5 Aanbeveling
Go/No-Go: ✅ GO
Start met Command Center + Intent API + 2 blokken (Rapportage, Dagnotitie). Itereer op basis van intent accuracy metrics voordat we alle 8 blokken bouwen.
2. UX Designer Perspectief
2.1 Concept Evaluatie
De belofte: Van 12 klikken naar 1 zin.
Dit is een fundamentele paradigma-shift. Geen menu's, geen navigatie-leren, geen "waar zit dat ook alweer?" De gebruiker spreekt intentie, het systeem reageert.
Sterktes van het concept:
- Cognitive load reductie - Zorgverleners hoeven geen mentaal model van het EPD te hebben
- Context-awareness - Systeem weet wie je bent, welke dienst, welke patiënten
- Hands-free potentieel - Voice-first past bij zorg (handschoenen, hygiëne)
- Ephemeral = focus - Alleen wat je nu nodig hebt, geen afleiding
Zorgen:
- Discoverability - Hoe weet de gebruiker wat mogelijk is?
- Error recovery - Wat als het systeem verkeerd begrijpt?
- Power users - Willen sommigen toch sneltoetsen/directe toegang?
- Trust - "Heeft het systeem mijn notitie wel opgeslagen?"
2.2 Interaction Design Analyse
Command Center Flow:
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 🎤 Wat wil je doen? │ │
│ │ ____________________________________________ │ │
│ │ │ │
│ │ 💡 Voorbeelden: "notitie voor Jan", "overdracht", │ │
│ │ "mijn afspraken", "zoek Marie" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Context: Ochtend dienst · 8 patiënten · 2 actiepunten │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [ Actief bouwblok verschijnt hier ] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Recent: [Jan - Notitie ✓] [Overdracht 14:00] │
│ │
└─────────────────────────────────────────────────────────────┘
Kritieke UX Momenten:
| Moment | Risico | Design Oplossing |
|---|---|---|
| Eerste gebruik | "Wat kan ik zeggen?" | Voorbeelden tonen, onboarding hints |
| Ambigue input | Frustratie bij verkeerde interpretatie | "Bedoelde je..." met opties |
| Mid-task switch | Context verlies | "Opslaan als concept?" modal |
| Na voltooiing | Onzekerheid of het werkte | Duidelijke bevestiging + undo optie |
| Geen match | Doodlopende straat | Fallback naar visuele blok-picker |
2.3 Microinteracties
Voice Input Feedback:
[Idle] → Grijze microfoon
[Listening] → Pulserende blauwe ring + live transcript
[Processing]→ Spinner + "Even denken..."
[Matched] → Groene check + smooth transition naar blok
[Unclear] → Oranje indicator + clarificatie vraag
Blok Transities:
- In: Slide-up met fade (200ms)
- Minimize: Collapse naar badge in "Recent" row
- Close: Fade-out met success toast
Pre-fill Animatie: Wanneer systeem velden invult op basis van intent:
- Velden highlighten kort (geel flash)
- Sequential fill (niet alles tegelijk)
- "Automatisch ingevuld" label bij pre-filled velden
2.4 Accessibility Overwegingen
| Aspect | Vereiste | Implementatie |
|---|---|---|
| Keyboard-only | Moet volledig werken zonder voice | Tab navigation, Enter to submit |
| Screen readers | Blok-wissels aangekondigd | ARIA live regions |
| Motor impairments | Grote touch targets | Min 44x44px buttons |
| Cognitieve load | Niet te veel tegelijk | Max 1 actief blok |
2.5 Fallback Strategie
De "Noodrem": Als conversational interface faalt, moet er altijd een visuele fallback zijn.
┌─────────────────────────────────────────────────────────────┐
│ Ik begreep dat niet helemaal. Wat wil je doen? │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 📝 │ │ 👤 │ │ 📋 │ │ 🔄 │ │
│ │Notitie │ │ Intake │ │ Plan │ │Overdracht│ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 🔍 │ │ 📅 │ │ 💊 │ │ 📊 │ │
│ │ Zoeken │ │ Agenda │ │Medicatie│ │Metingen │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────┘
2.6 Aanbeveling
Go/No-Go: ✅ GO met voorwaarden
- Bouw de fallback picker EERST - dit is je vangnet
- Investeer in microinteracties - ze maken of breken de "magie"
- User testing na eerste 2 blokken - niet na alle 8
- Metrics verzamelen: intent accuracy, tijd-tot-taak, fallback usage
3. Frontend Developer Perspectief
3.1 Component Architectuur
Huidige staat: Goed gestructureerd, maar bouwblok-specifiek
We moeten bestaande componenten wrappen om ze "ephemeral" te maken. Dit vereist een abstractielaag.
Voorgestelde structuur:
components/
├── building-blocks/
│ ├── core/
│ │ ├── BlockContainer.tsx # Wrapper met header, minimize, close
│ │ ├── BlockHeader.tsx # Titel + acties
│ │ ├── BlockFooter.tsx # Save/Cancel buttons
│ │ └── PrefilledField.tsx # Highlight voor auto-filled
│ │
│ ├── rapportage/
│ │ ├── RapportageBlock.tsx # Wrapper rond ReportComposer
│ │ └── RapportageBlock.types.ts
│ │
│ ├── dagnotitie/
│ │ ├── DagnotitieBlock.tsx # Quick entry form
│ │ └── DagnotitieBlock.types.ts
│ │
│ ├── zoeken/
│ │ ├── ZoekenBlock.tsx # cmdk-based search
│ │ ├── PatientCard.tsx # Search result card
│ │ └── ZoekenBlock.types.ts
│ │
│ └── ... (andere blokken)
│
├── command-center/
│ ├── CommandInput.tsx # Text + voice input
│ ├── VoiceIndicator.tsx # Listening state UI
│ ├── ContextBar.tsx # Shift info, patient count
│ ├── RecentActions.tsx # Minimized blocks
│ ├── BlockPicker.tsx # Fallback grid
│ └── ClarificationDialog.tsx # "Bedoelde je...?"
3.2 Hergebruik Analyse
Direct herbruikbaar (copy):
// Volledig herbruikbaar
import { RichTextEditor } from '@/components/rich-text-editor'
import { SpeechRecorder } from '@/components/speech-recorder'
import { Timeline } from '@/components/ui/timeline'
import { Command } from '@/components/ui/command' // voor zoeken
// Bestaande forms met minimale aanpassing
ReportComposer → RapportageBlock (wrap + simplify)
BehandelplanView → BehandelplanBlock (read-only + edit mode toggle)
AgendaCalendar → AgendaBlock (date-filtered view)
Moet gerefactored worden:
// Te gekoppeld aan page-specifieke logica
IntakeTabs → Moet ontkoppeld van [intakeId] routing
VitalsBlock → Alleen display, geen input form
PatientList → Moet naar PatientCard + search results
Nieuw te bouwen:
// Helemaal nieuw
CommandInput.tsx // ~150 lines
BlockContainer.tsx // ~100 lines
ZoekenBlock.tsx // ~200 lines
MetingenBlock.tsx // ~150 lines
DagnotitieBlock.tsx // ~120 lines (simplified from ReportComposer)
ClarificationDialog.tsx // ~80 lines
3.3 State Management Implementatie
Keuze: Zustand
// stores/command-center-store.ts
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
interface CommandCenterState {
// Block state
activeBlock: BlockType | null
blockData: Record<string, unknown>
minimizedBlocks: MinimizedBlock[]
// Context
activePatient: Patient | null
shiftInfo: ShiftInfo | null
// Voice
isListening: boolean
transcript: string
interimTranscript: string
// Recent
recentActions: Action[]
// Actions
openBlock: (type: BlockType, prefill?: Record<string, unknown>) => void
closeBlock: () => void
minimizeBlock: () => void
setActivePatient: (patient: Patient | null) => void
setTranscript: (text: string) => void
addRecentAction: (action: Action) => void
}
export const useCommandCenter = create<CommandCenterState>()(
devtools(
persist(
(set, get) => ({
// ... implementation
}),
{ name: 'command-center' }
)
)
)
3.4 Intent Handling Flow
// hooks/use-intent.ts
export function useIntent() {
const { openBlock, setActivePatient, activePatient } = useCommandCenter()
const processInput = async (text: string) => {
// 1. Quick local match
const quickMatch = quickClassify(text)
if (quickMatch.confidence > 0.9) {
return handleIntent(quickMatch)
}
// 2. AI classification
const result = await fetch('/api/intent/classify', {
method: 'POST',
body: JSON.stringify({
text,
context: { activePatient }
})
}).then(r => r.json())
// 3. Handle result
if (result.clarification_needed) {
return { type: 'clarify', question: result.clarification_question }
}
if (result.entities.patient_name && !activePatient) {
const patient = await resolvePatient(result.entities.patient_name)
if (patient.length > 1) {
return { type: 'select_patient', options: patient }
}
setActivePatient(patient[0])
}
openBlock(result.intent, result.entities)
return { type: 'success', block: result.intent }
}
return { processInput }
}
3.5 Performance Optimalisatie
Bundle splitting:
// Lazy load blokken
const RapportageBlock = dynamic(
() => import('@/components/building-blocks/rapportage/RapportageBlock'),
{ loading: () => <BlockSkeleton /> }
)
const BehandelplanBlock = dynamic(
() => import('@/components/building-blocks/behandelplan/BehandelplanBlock'),
{ loading: () => <BlockSkeleton /> }
)
Prefetching:
// Prefetch meest gebruikte blokken bij mount
useEffect(() => {
import('@/components/building-blocks/rapportage/RapportageBlock')
import('@/components/building-blocks/dagnotitie/DagnotitieBlock')
}, [])
Voice optimization:
// Reuse Deepgram connection
const deepgramRef = useRef<DeepgramConnection | null>(null)
// Start listening immediately on mic click (no API call delay)
// Token already fetched at mount
3.6 Testing Strategy
// Kritieke test scenarios
describe('CommandCenter', () => {
it('opens RapportageBlock for "notitie voor jan"')
it('asks clarification for ambiguous input')
it('pre-fills patient when mentioned by name')
it('falls back to BlockPicker on unknown intent')
it('minimizes block and shows in recent')
it('restores minimized block on click')
})
describe('Intent Classification', () => {
it('handles Dutch medical vocabulary')
it('extracts patient name from natural speech')
it('returns low confidence for gibberish')
})
3.7 Aanbeveling
Go/No-Go: ✅ GO
Geschatte effort:
| Component | Uren | Complexiteit |
|---|---|---|
| CommandCenter layout | 4h | Low |
| CommandInput + Voice | 6h | Medium |
| BlockContainer abstraction | 4h | Medium |
| RapportageBlock wrapper | 3h | Low |
| DagnotitieBlock (new) | 5h | Medium |
| ZoekenBlock (new) | 6h | Medium |
| Intent API route | 4h | Medium |
| Zustand store | 3h | Low |
| Animations/transitions | 4h | Low |
| Totaal MVP | ~40h | - |
4. Product Owner Perspectief
4.1 Business Case Analyse
Doelstelling: Demo-ready voor Nedap gesprek (7 jan)
Dit is een showcase project met twee doelen:
- Thought leadership positionering
- Concrete gespreksstarter voor enterprise partnerships
ROI Potentieel:
| Metric | Traditioneel EPD | Ephemeral UI | Impact |
|---|---|---|---|
| Tijd per rapportage | 3-5 min | <1 min | 70% reductie |
| Klikken per actie | 8-15 | 1-3 | 80% reductie |
| Training tijd | 2-4 dagen | 1 uur | 90% reductie |
| Error rate (verkeerde scherm) | 15% | <5% | 70% reductie |
4.2 Scope Prioritering
Must Have (Demo MVP):
- Command Center met text input
- Voice input (Deepgram werkt al)
- 2 werkende blokken: Rapportage + Dagnotitie
- Intent classification (happy path)
- Pre-fill van patient naam
- Visuele fallback (blok picker)
Should Have (Demo Enhanced):
- Overdracht blok met AI samenvatting
- Zoeken blok
- Recent actions tracking
- Context bar (dienst info)
- Animaties en polish
Could Have (Post-Demo):
- Behandelplan blok
- Agenda blok
- Metingen blok
- Intake blok
- Multi-patient context switching
Won't Have (v1):
- Offline mode
- Mobile native app
- Multi-user realtime
- FHIR integratie
- Full ambient listening
4.3 Risico Assessment
| Risico | Waarschijnlijkheid | Impact | Mitigatie |
|---|---|---|---|
| Demo niet klaar 7 jan | Medium | Hoog | Focus op 2 blokken, polish later |
| Intent accuracy <85% | Medium | Hoog | Fallback UI prominent aanwezig |
| Voice werkt niet live | Laag | Medium | Tekst input als backup |
| Nedap niet geïnteresseerd | Medium | Medium | Parallel outreach naar andere partijen |
| "Speeltje" perceptie | Medium | Medium | Focus op tijdsbesparing metrics |
4.4 Stakeholder Waarde
Voor Zorgverleners:
- Minder administratie, meer tijd voor zorg
- Geen menu-navigatie stress
- Voice input tijdens handeling
Voor Zorgorganisaties:
- Lagere training kosten
- Hogere EPD adoptie
- Minder documentatie-fouten
Voor IT/Beheer:
- Moderne tech stack (Next.js, Supabase)
- AI-first architectuur
- Schaalbaar en maintainable
4.5 Demo Scenario's
Scenario 1: De Drukke Ochtend (2 min)
Verpleegkundige start dienst → Command Center opent
"Mijn patiënten vandaag" → Overzicht met prioriteiten
"Notitie voor Jan de Vries: medicatie uitgereikt, geen bijzonderheden"
→ Dagnotitie pre-filled, 1-click save
Scenario 2: Na een Gesprek (1 min)
Behandelaar na sessie → Voice inspreken
"Ik heb net een gesprek gehad met mevrouw Jansen over haar angstklachten"
→ Rapportage blok opent, patient pre-filled
→ AI structureert de dictatie
→ Save
Scenario 3: Overdracht (1 min)
Dienst eindigt → "Overdracht"
→ AI genereert samenvatting van de dag
→ Aandachtspunten gemarkeerd
→ Klaar voor collega
4.6 Success Metrics
Demo Success:
- 3 scenario's foutloos uitvoeren
- <5 seconden van input tot blok open
- "Wow" reactie van stakeholders
- Concrete vervolgafspraak
Product Success (post-launch):
- Intent accuracy >85%
- Fallback usage <20%
- Tijd-tot-taak 50% lager dan traditioneel
- User satisfaction >4/5
4.7 Go-to-Market
Fase 1: Internal Demo (Week 1-2)
- Bouwen MVP
- Interne testing
Fase 2: Stakeholder Demo (Week 3)
- Nedap presentatie (7 jan)
- LinkedIn content (4 posts gepland)
Fase 3: Pilot (Q1 2025)
- 1 zorginstelling
- Real user feedback
- Iteratie op intent accuracy
Fase 4: Scale (Q2 2025)
- Meerdere instellingen
- Enterprise features
- Mogelijke partnership/overname gesprekken
4.8 Aanbeveling
Go/No-Go: ✅ GO
Voorwaarden:
- Scope beperken tot 2-3 blokken voor demo
- Fallback UI is verplicht (geen "het werkt alleen met AI")
- Realistische demo verwachtingen (happy path)
- LinkedIn content parallel voorbereiden
5. Gezamenlijke Conclusie
Consensus: ✅ GO
Alle vier de perspectieven zijn positief, met de volgende gedeelde voorwaarden:
Kritieke Succesfactoren:
- Fallback first - Bouw de visuele blok-picker voordat je intent bouwt
- Scope discipline - 2 blokken voor demo, niet 8
- Intent accuracy - Meet en optimaliseer continue
- User testing vroeg - Niet wachten tot alles "af" is
Gedeelde Risico's:
- Intent misclassificatie (mitigatie: fallback UI)
- Demo deadline druk (mitigatie: scope beperken)
- "Speeltje" perceptie (mitigatie: focus op metrics)
Next Steps:
- Command Center basis layout (Frontend)
- Intent classification API (Lead Dev)
- UX mockups voor microinteracties (UX)
- Demo scenario scripts (PO)
Appendix: Decision Log
| Beslissing | Optie Gekozen | Rationale |
|---|---|---|
| State management | Zustand | Lightweight, geen provider nesting |
| Intent approach | Local + AI fallback | Snelheid + accuracy balans |
| Voice library | Deepgram (bestaand) | Al geïntegreerd, werkt goed |
| Blok architectuur | Wrapper pattern | Maximaal hergebruik bestaande code |
| Demo scope | 2-3 blokken | Realistische deadline |
Document gegenereerd op basis van PRD analyse en codebase review.