diff --git a/docs/architectuur/architectuur-overzicht.md b/docs/architectuur/architectuur-overzicht.md new file mode 100644 index 0000000..4c8e7fb --- /dev/null +++ b/docs/architectuur/architectuur-overzicht.md @@ -0,0 +1,296 @@ +# Architectuur Overzicht — Mini-EPD + +**Versie:** v1.0 +**Datum:** 4 februari 2026 +**Doelgroep:** Product owners, IT consultants, data scientists + +--- + +## 1. Introductie + +**Mini-EPD** is een elektronisch patiëntendossier voor de geestelijke gezondheidszorg. Het systeem combineert klassieke dossiervoering met een AI-gestuurde spraakinterface genaamd **Cortex**. + +> **Elevator pitch:** Een EPD waarin zorgmedewerkers niet hoeven te klikken, maar gewoon zeggen wat ze willen doen. "Maak notitie voor Jan, medicatie gegeven" — en het gebeurt. + +--- + +## 2. Technische Stack + +| Laag | Technologie | Rol | +|------|-------------|-----| +| **Frontend** | Next.js 14, React, Tailwind CSS | Gebruikersinterface | +| **Backend** | Next.js API Routes | Server-logica en API's | +| **Database** | Supabase (PostgreSQL) | Opslag patiëntgegevens | +| **Authenticatie** | Supabase Auth | Inloggen en toegangsbeheer | +| **AI - Taal** | Claude (Anthropic) | Classificatie en samenvatting | +| **AI - Spraak** | Deepgram | Spraak-naar-tekst | +| **Hosting** | Vercel | Deployment en hosting | + +--- + +## 3. Module-overzicht + +Het systeem bestaat uit zes hoofdmodules: + +| Module | Doel | Primaire gebruiker | +|--------|------|-------------------| +| **Cortex** | AI spraak/tekst commando's | Iedereen | +| **Dashboard** | Overzicht werkzaamheden | Behandelaar | +| **Patiënten** | Dossiers en intake | Iedereen | +| **Verpleegrapportage** | Dagnotities en overdracht | Verpleegkundige | +| **Agenda** | Afspraken en planning | Iedereen | +| **Clients** | (Legacy, doorverwijzing) | — | + +### Module-beschrijvingen + +#### Cortex — AI Command Center +Het "brein" van het systeem. Medewerkers geven spraak- of tekstcommando's, Cortex begrijpt de intentie en voert de actie uit. Bijvoorbeeld: "zoek marie" opent direct het zoekscherm met resultaten. + +#### Dashboard +Startpagina met overzicht van caseload, aandachtspunten en aankomende afspraken. Geeft behandelaars in één oogopslag zicht op hun werk. + +#### Patiënten +Beheer van alle patiënten en hun dossiers. Elk dossier bevat: +- Basisgegevens (NAW, verzekering) +- Screening (hulpvraag, documenten) +- Intake (anamnese, diagnoses, risico's) +- Behandelplan (doelen, interventies) +- Rapportage (dagnotities) + +#### Verpleegrapportage +Twee functies: +1. **Rapportage** — Invoer van dagelijkse observaties per patiënt +2. **Overdracht** — AI-samenvatting van alle notities voor shift-wissel + +#### Agenda +Kalenderweergave van alle afspraken. Filtert per patiënt, dag of week. + +--- + +## 4. Cortex — Diepere Uitwerking + +Cortex is het onderscheidende element van dit EPD. Het werkt met drie lagen: + +### Laag 1: Reflex Arc (Snelle herkenning) +- **Wat:** Lokale patroonherkenning zonder AI +- **Snelheid:** <20 milliseconden +- **Wanneer:** Eenvoudige, veelvoorkomende commando's +- **Voorbeeld:** "notitie jan" → herkent direct als "dagnotitie maken" + +### Laag 2: Orchestrator (AI-classificatie) +- **Wat:** Claude AI analyseert complexe invoer +- **Snelheid:** 200-800 milliseconden +- **Wanneer:** Meerdere acties, context nodig, onduidelijke input +- **Voorbeeld:** "Zeg jan af en maak notitie griep" → herkent twee acties + +### Laag 3: Nudge (Proactieve suggesties) +- **Wat:** Suggesties na voltooide acties +- **Wanneer:** Na opslaan van bepaalde notities +- **Voorbeeld:** Notitie met "wond" → suggestie: "Wondcontrole inplannen?" + +### Ondersteunde commando's (intents) + +| Intent | Wat het doet | Voorbeeld | +|--------|-------------|----------| +| `dagnotitie` | Verpleegkundige notitie | "medicatie jan gegeven" | +| `zoeken` | Patiënt zoeken | "zoek marie" | +| `overdracht` | Overdracht openen | "overdracht" | +| `agenda_query` | Afspraken bekijken | "afspraken vandaag" | +| `create_appointment` | Afspraak maken | "plan intake jan morgen 14:00" | +| `cancel_appointment` | Afspraak annuleren | "annuleer afspraak jan" | +| `intake_status` | Intake voortgang | "wat moet ik nog doen?" | + +### Cortex UI-opbouw + +Het scherm is verticaal gesplitst: +- **Links (40%):** Chat — conversatie met Cortex +- **Rechts (60%):** Werkgebied — formulieren en lijsten + +Sneltoetsen: +- `Cmd/Ctrl + K` — Focus op invoerveld +- `Cmd/Ctrl + Enter` — Verstuur commando +- `Esc` — Sluit werkgebied + +--- + +## 5. Data-architectuur + +### Kernentiteiten + +De database is georganiseerd rond de **patiënt** als centrale entiteit: + +| Entiteit | Beschrijving | +|----------|--------------| +| **patients** | Basisgegevens patiënten | +| **encounters** | Contactmomenten (afspraken, bezoeken) | +| **observations** | Meetgegevens (vitals, symptomen) | +| **conditions** | Diagnoses en aandoeningen | +| **reports** | Alle notities en rapportages | +| **intakes** | Intake-trajecten | +| **care_plans** | Behandelplannen | +| **risk_assessments** | Risico-evaluaties | +| **practitioners** | Zorgverleners | + +### Rapportage-types + +Alle notities zitten in één tabel (`reports`) met een type-aanduiding: + +| Type | Gebruik | +|------|---------| +| `verpleegkundig` | Dagelijkse zorgnotities | +| `observatie` | Klinische waarnemingen | +| `incident` | Incidenten/crises | +| `voortgang` | Voortgangsnota's | +| `medicatie` | Medicijnbeheer | +| `contact` | Contactlogboek | + +### API-groepen + +De backend API's zijn logisch gegroepeerd: + +| Groep | Functie | +|-------|---------| +| `/api/patients/*` | Patiëntgegevens | +| `/api/reports/*` | Rapportages CRUD | +| `/api/overdracht/*` | Shift-overdracht + AI-samenvatting | +| `/api/intakes/*` | Intake-beheer | +| `/api/cortex/*` | AI command center | +| `/api/deepgram/*` | Spraakherkenning | + +--- + +## 6. Diagrambeschrijvingen + +Onderstaande beschrijvingen kun je gebruiken om visuele diagrammen te maken. + +### Diagram A: Systeemoverzicht (Container) + +**Componenten:** +1. **Gebruiker** (persoon) — Zorgmedewerker met browser +2. **Frontend** (container) — Next.js React applicatie +3. **API Layer** (container) — Next.js API Routes +4. **Database** (container) — Supabase PostgreSQL +5. **Claude AI** (externe service) — Anthropic API +6. **Deepgram** (externe service) — Spraak-naar-tekst API + +**Verbindingen:** +- Gebruiker → Frontend (HTTPS) +- Frontend → API Layer (REST/SSE) +- API Layer → Database (SQL via Supabase client) +- API Layer → Claude AI (HTTPS, voor classificatie en samenvatting) +- Frontend → Deepgram (WebSocket, voor live spraak) + +--- + +### Diagram B: Cortex Flow (Sequence) + +**Actoren:** Gebruiker, Frontend, Reflex Arc, Orchestrator (Claude), Database + +**Flow:** +1. Gebruiker spreekt/typt commando +2. Frontend stuurt tekst naar API +3. Reflex Arc probeert lokaal te classificeren +4. **Als succesvol:** Retourneer intent + entiteiten +5. **Als niet succesvol:** Escaleer naar Orchestrator +6. Orchestrator (Claude) analyseert en retourneert intent chain +7. Frontend toont juiste werkgebied (formulier/lijst) +8. Gebruiker voltooit actie +9. Data wordt opgeslagen in Database +10. (Optioneel) Nudge evalueert en toont suggestie + +--- + +### Diagram C: Module-relaties (Component) + +**Modules en hun connecties:** + +``` +┌─────────────────────────────────────────────────────┐ +│ CORTEX │ +│ (kan alle andere modules aansturen via commando's) │ +└───────────────────────┬─────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ +┌───────────┐ ┌───────────────┐ ┌────────┐ +│ PATIËNTEN │◄──│ VERPLEEG- │ │ AGENDA │ +│ │ │ RAPPORTAGE │ │ │ +└─────┬─────┘ └───────────────┘ └────────┘ + │ + ▼ +┌─────────────┐ +│ DASHBOARD │ +│ (overzicht) │ +└─────────────┘ +``` + +**Beschrijving:** +- Cortex fungeert als "universele afstandsbediening" +- Patiënten-module is de kern voor dossierdata +- Verpleegrapportage leest en schrijft naar patiëntendossiers +- Agenda toont afspraken gekoppeld aan patiënten +- Dashboard aggregeert data uit alle modules + +--- + +### Diagram D: Data-relaties (ERD vereenvoudigd) + +**Entiteiten en relaties:** + +``` +PATIENT (1) ──────< (N) ENCOUNTER + │ + ├──────< (N) OBSERVATION + │ + ├──────< (N) CONDITION + │ + ├──────< (N) REPORT + │ + └──────< (N) INTAKE + │ + ├──── ANAMNESE + ├──── EXAMINATION + ├──── RISK_ASSESSMENT + └──── CARE_PLAN +``` + +**Leeswijzer:** +- Eén patiënt heeft meerdere contactmomenten (encounters) +- Eén patiënt heeft meerdere observaties, diagnoses en rapportages +- Eén patiënt kan meerdere intake-trajecten doorlopen +- Elk intake-traject bevat anamnese, onderzoek, risico's en behandelplan + +--- + +## 7. Glossary + +| Term | Betekenis | +|------|-----------| +| **EPD** | Elektronisch Patiënten Dossier | +| **Cortex** | AI command center voor spraak/tekst commando's | +| **Intent** | Gedetecteerde bedoeling achter een commando | +| **Reflex Arc** | Snelle, lokale patroonherkenning (geen AI) | +| **Orchestrator** | AI-laag voor complexe classificatie | +| **Nudge** | Proactieve suggestie na een actie | +| **Overdracht** | Shift-wissel met samenvatting van notities | +| **RLS** | Row Level Security — database-beveiliging per gebruiker | +| **Intake** | Opnameproces nieuwe patiënt | +| **Anamnese** | Medische voorgeschiedenis | +| **ROM** | Routine Outcome Monitoring — effectmeting behandeling | +| **FHIR** | Internationale standaard voor zorgdata-uitwisseling | + +--- + +## 8. Contactpunten voor verdieping + +| Onderwerp | Waar te vinden | +|-----------|----------------| +| Functioneel ontwerp Cortex | `docs/swift/` | +| API-documentatie | `app/api/` (code + comments) | +| Database schema | `supabase/migrations/` | +| UI componenten | `components/` | +| Release notes | `docs/releasenotes/` | + +--- + +*Dit document geeft een high-level overzicht. Voor technische implementatiedetails, raadpleeg de broncode of vraag het development team.* diff --git a/docs/architectuur/implementatieplan-nieuwe-intents.md b/docs/architectuur/implementatieplan-nieuwe-intents.md new file mode 100644 index 0000000..7b55abf --- /dev/null +++ b/docs/architectuur/implementatieplan-nieuwe-intents.md @@ -0,0 +1,728 @@ +# Implementatieplan — Nieuwe Intents Toevoegen + +**Versie:** v1.0 +**Datum:** 4 februari 2026 +**Doelgroep:** Product owners, IT consultants, data scientists + +--- + +## 1. Overzicht + +Dit document beschrijft het stappenplan voor het toevoegen van een nieuwe intent aan Cortex. Een intent doorloopt **8 aanraakpunten** in de codebase — elk punt moet correct geconfigureerd zijn. + +### Tijdsindicatie per Intent Type + +| Type | Complexiteit | Bestanden | +|------|--------------|-----------| +| **Query Block** (data tonen) | Laag | 6-7 bestanden | +| **Action Block** (data invoeren) | Middel | 7-8 bestanden | +| **Navigatie Intent** (geen block) | Laag | 4-5 bestanden | + +--- + +## 2. Beslisboom: Welk Type Intent? + +``` + ┌─────────────────────────┐ + │ Wat moet de intent doen?│ + └───────────┬─────────────┘ + │ + ┌───────────────────────┼───────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ +│ Data TONEN │ │ Data INVOEREN │ │ NAVIGEREN │ +│ (read-only) │ │ (formulier) │ │ (route) │ +└───────┬───────┘ └───────┬───────┘ └───────┬───────┘ + │ │ │ + ▼ ▼ ▼ + Query Block Action Block Navigation Intent + + Voorbeelden: Voorbeelden: Voorbeelden: + - risico_query - dagnotitie - intake_navigeer + - diagnose_query - create_appointment + - intake_status +``` + +--- + +## 3. Stappenplan + +### Stap 1: Types Definiëren + +**Bestand:** `lib/cortex/types.ts` + +**Wat te doen:** + +1. Voeg intent toe aan `CortexIntent` type: +```typescript +export type CortexIntent = + | 'dagnotitie' + | 'zoeken' + // ... bestaande intents + | 'nieuwe_intent' // ← NIEUW + | 'unknown'; +``` + +2. Voeg block config toe aan `BLOCK_CONFIGS` (alleen als er een block is): +```typescript +export const BLOCK_CONFIGS: Record = { + // ... bestaande configs + nieuwe_intent: { + type: 'nieuwe_intent', + title: 'Nieuwe Intent Titel', + size: 'md', // 'sm' | 'md' | 'lg' | 'full' + icon: 'IconName', // Lucide icon naam + }, +}; +``` + +3. Voeg eventuele nieuwe entities toe aan `ExtractedEntities`: +```typescript +export interface ExtractedEntities { + // ... bestaande entities + nieuwVeld?: string; +} +``` + +--- + +### Stap 2: Patterns Toevoegen (Reflex Arc) + +**Bestand:** `lib/cortex/reflex-classifier.ts` + +**Wat te doen:** + +Voeg patterns toe aan `INTENT_PATTERNS`: + +```typescript +const INTENT_PATTERNS: Record<...> = { + // ... bestaande patterns + + nieuwe_intent: [ + // Exacte commando's (weight 1.0) + { pattern: /^trigger woord/i, weight: 1.0 }, + { pattern: /^alternatief commando/i, weight: 1.0 }, + + // Sterke matches (weight 0.9) + { pattern: /^toon\s+(de\s+)?nieuwe/i, weight: 0.9 }, + + // Partiele matches (weight 0.7-0.8) + { pattern: /^nieuwe\b/i, weight: 0.7 }, + ], +}; +``` + +**Pattern weight richtlijnen:** + +| Weight | Wanneer | Voorbeeld | +|--------|---------|-----------| +| `1.0` | Exacte, unieke trigger | `"risicotaxatie"` | +| `0.9-0.95` | Sterke indicator | `"toon risico's"` | +| `0.8-0.85` | Goede match | `"bekijk risico"` | +| `0.7` | Partiele match | `"risico"` (kan ook andere dingen zijn) | +| `< 0.7` | Vermijd | Leidt tot escalatie naar AI | + +--- + +### Stap 3: AI Chat Integratie + +**Bestand:** `app/api/cortex/chat/route.ts` + +**Wat te doen:** + +1. Voeg intent toe aan de system prompt (in `buildSystemPrompt()` functie): + +```typescript +// In de intents lijst: +- **nieuwe_intent** — Korte beschrijving wat het doet + - Triggers: "trigger 1", "trigger 2", "trigger 3" + - Entities: veldNaam (type) + - Actie: Beschrijf wat er gebeurt +``` + +2. Voeg een voorbeeld toe: + +```typescript +### Voorbeeld N: Nieuwe Intent + +**User:** +"trigger zin" + +**AI Response:** +"Korte bevestiging van wat je gaat doen. + +\`\`\`json +{ + "type": "action", + "intent": "nieuwe_intent", + "entities": { + "veldNaam": "waarde" + }, + "confidence": 0.95, + "artifact": { + "type": "nieuwe_intent", + "prefill": { + "veldNaam": "waarde" + } + } +} +\`\`\`" +``` + +**Let op:** De AI leert van voorbeelden. Zorg dat: +- Het JSON format exact klopt +- De confidence realistisch is (0.85-0.98) +- De entities overeenkomen met wat je in types.ts hebt gedefinieerd + +--- + +### Stap 4: Validatie Schema + +**Bestand:** `lib/cortex/action-parser.ts` + +**Wat te doen:** + +1. Voeg intent toe aan `ActionSchema`: + +```typescript +const ActionSchema = z.object({ + type: z.literal('action'), + intent: z.enum([ + 'dagnotitie', + 'zoeken', + // ... bestaande intents + 'nieuwe_intent', // ← NIEUW + 'unknown', + ]), + // ... rest van schema +}); +``` + +2. Voeg artifact type toe (als er een block is): + +```typescript +artifact: z.object({ + type: z.enum([ + 'dagnotitie', + 'zoeken', + // ... bestaande types + 'nieuwe_intent', // ← NIEUW + 'fallback', + ]), + prefill: z.record(z.string(), z.any()), +}).optional(), +``` + +--- + +### Stap 5: Routing Configureren + +**Bestand:** `lib/cortex/action-parser.ts` + +**Wat te doen:** + +Voeg case toe aan `routeIntentToArtifact()`: + +```typescript +export function routeIntentToArtifact( + intent: CortexIntent, + entities: Record, + confidence: number +): { type: BlockType; prefill: Record; title: string } | null { + + // ... bestaande cases + + case 'nieuwe_intent': + // Optioneel: check of vereiste entities aanwezig zijn + if (!entities.vereistVeld) { + return null; // Triggert clarification vraag + } + return { + type: 'nieuwe_intent', + title: 'Nieuwe Intent Titel', + prefill: { + veldNaam: entities.veldNaam, + // ... andere prefill data + }, + }; +} +``` + +**Voor navigatie intents:** +```typescript +case 'nieuwe_navigeer': + // Return null → wordt afgehandeld in CommandCenter + return null; +``` + +--- + +### Stap 6: API Route (Indien Nodig) + +**Bestand:** `app/api/cortex/[domain]/route.ts` + +**Wanneer nodig:** Als de block data moet ophalen van de server. + +**Wat te doen:** + +```typescript +// app/api/cortex/nieuwe/route.ts + +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { createClient } from '@/lib/auth/server'; + +const QuerySchema = z.object({ + patientId: z.string().uuid(), + optionalParam: z.string().optional(), // Let op: NIET .nullable() +}); + +export async function GET(request: NextRequest) { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + + if (!user) { + return NextResponse.json({ error: 'Niet ingelogd' }, { status: 401 }); + } + + const searchParams = request.nextUrl.searchParams; + + // BELANGRIJK: Convert null naar undefined voor Zod + const params = QuerySchema.safeParse({ + patientId: searchParams.get('patientId'), + optionalParam: searchParams.get('optionalParam') || undefined, // ← NIET null! + }); + + if (!params.success) { + return NextResponse.json({ error: 'Ongeldige parameters' }, { status: 400 }); + } + + // Data ophalen + const { data, error } = await supabase + .from('tabel_naam') + .select('*') + .eq('patient_id', params.data.patientId); + + if (error) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } + + return NextResponse.json({ data }); +} +``` + +**Let op (Lesson Learned):** +- `searchParams.get()` retourneert `null`, niet `undefined` +- Zod's `.optional()` verwacht `undefined` +- Gebruik altijd `|| undefined` bij optionele params + +--- + +### Stap 7: Block Component + +**Bestand:** `components/cortex/blocks/nieuwe-intent-block.tsx` + +**Wat te doen:** + +```typescript +'use client'; + +/** + * Nieuwe Intent Block + * + * Block voor [beschrijving]. + * Intent: nieuwe_intent + */ + +import { useCortexStore, type BlockPrefillData } from '@/stores/cortex-store'; +import { BlockContainer } from './block-container'; +import { BlockLoading, BlockError, BlockEmpty } from '../shared/block-states'; +import { useBlockData } from '@/lib/cortex/hooks/use-block-data'; +import { BLOCK_CONFIGS } from '@/lib/cortex/types'; +import { IconName } from 'lucide-react'; + +interface NieuweIntentBlockProps { + prefill?: BlockPrefillData; +} + +interface NieuweIntentData { + items: Array<{ + id: string; + // ... velden + }>; +} + +export function NieuweIntentBlock({ prefill }: NieuweIntentBlockProps) { + const config = BLOCK_CONFIGS['nieuwe_intent']; + const { activePatient } = useCortexStore(); + + const patientId = prefill?.patientId || activePatient?.id; + + // Data ophalen + const { data, isLoading, error, refetch } = useBlockData({ + endpoint: '/api/cortex/nieuwe', + params: { patientId: patientId || '' }, + enabled: Boolean(patientId), + }); + + // State: Geen context + if (!patientId) { + return ( + + + + ); + } + + // State: Loading + if (isLoading) { + return ( + + + + ); + } + + // State: Error + if (error) { + return ( + + + + ); + } + + // State: Data + return ( + +
+ {/* Render je data hier */} + {data?.items.map((item) => ( +
+ {/* Item content */} +
+ ))} +
+
+ ); +} +``` + +**Shared components beschikbaar:** +- `BlockContainer` — Wrapper met header en close button +- `BlockLoading` — Spinner met message +- `BlockError` — Foutmelding met retry button +- `BlockEmpty` — Lege state met icon en actie +- `BlockSection` — Sectie met header +- `BlockItem` — Lijst-item met badge +- `BlockFooter` — Footer met acties + +--- + +### Stap 8: Artifact Container Updaten + +**Bestand:** `components/cortex/artifacts/artifact-container.tsx` + +**Wat te doen (3 plekken):** + +1. **Import toevoegen:** +```typescript +import { NieuweIntentBlock } from '../blocks/nieuwe-intent-block'; +``` + +2. **Render case toevoegen:** +```typescript +function renderArtifactBlock(artifact: Artifact) { + switch (artifact.type) { + // ... bestaande cases + + case 'nieuwe_intent': + return ; + } +} +``` + +3. **Titel toevoegen:** +```typescript +function getArtifactTitle(type: string): string { + switch (type) { + // ... bestaande cases + + case 'nieuwe_intent': + return 'Nieuwe Intent Titel'; + } +} +``` + +--- + +### Stap 9 (Optioneel): Navigatie Handler + +**Alleen voor navigatie intents (zonder block)** + +**Bestand:** `components/cortex/command-center/command-center.tsx` + +**Wat te doen:** + +In de `useEffect` die `pendingAction` afhandelt: + +```typescript +useEffect(() => { + if (!pendingAction) return; + + // ... bestaande handlers + + if (pendingAction.intent === 'nieuwe_navigeer') { + const target = pendingAction.entities.navigationTarget; + + // Navigeer naar juiste pagina + router.push(`/epd/patients/${patientId}/path/${target}`); + + // Feedback tonen + toast({ + title: 'Navigeren...', + description: `Naar ${target}`, + }); + + // Cleanup + setPendingAction(null); + return; + } +}, [pendingAction]); +``` + +--- + +## 4. Checklist + +Gebruik deze checklist bij het toevoegen van een nieuwe intent: + +### Voorbereiding +- [ ] Intent type bepaald (Query/Action/Navigation) +- [ ] Intent naam gekozen (lowercase, underscore) +- [ ] Entities gedefinieerd +- [ ] Trigger woorden verzameld + +### Types & Patterns +- [ ] `lib/cortex/types.ts` — CortexIntent type +- [ ] `lib/cortex/types.ts` — BLOCK_CONFIGS (als block nodig) +- [ ] `lib/cortex/types.ts` — ExtractedEntities (als nieuwe entities) +- [ ] `lib/cortex/reflex-classifier.ts` — INTENT_PATTERNS + +### AI Integratie +- [ ] `app/api/cortex/chat/route.ts` — Intent in system prompt +- [ ] `app/api/cortex/chat/route.ts` — Voorbeeld met JSON + +### Validatie & Routing +- [ ] `lib/cortex/action-parser.ts` — ActionSchema intent enum +- [ ] `lib/cortex/action-parser.ts` — ActionSchema artifact type (als block) +- [ ] `lib/cortex/action-parser.ts` — routeIntentToArtifact() case + +### API (indien nodig) +- [ ] `app/api/cortex/[domain]/route.ts` — Nieuwe route +- [ ] Query params: `|| undefined` voor optionele params! + +### UI Component +- [ ] `components/cortex/blocks/[intent]-block.tsx` — Block component +- [ ] `components/cortex/artifacts/artifact-container.tsx` — Import +- [ ] `components/cortex/artifacts/artifact-container.tsx` — Render case +- [ ] `components/cortex/artifacts/artifact-container.tsx` — Titel + +### Navigatie (indien van toepassing) +- [ ] `components/cortex/command-center/command-center.tsx` — Handler + +### Testen +- [ ] Lokale classificatie testen (Reflex Arc) +- [ ] AI classificatie testen (Chat) +- [ ] Block rendering testen +- [ ] Error states testen +- [ ] Voice input testen + +--- + +## 5. Veelgemaakte Fouten + +### Fout 1: Zod + null vs undefined + +**Probleem:** +```typescript +// Dit faalt! +const param = searchParams.get('optionalParam'); // Returns null +``` + +**Oplossing:** +```typescript +const param = searchParams.get('optionalParam') || undefined; +``` + +### Fout 2: Intent niet in AI prompt + +**Symptoom:** Chat AI vraagt "Wil je het dossier opzoeken?" ipv de juiste actie. + +**Oorzaak:** Intent ontbreekt in system prompt. + +**Oplossing:** Voeg intent + voorbeeld toe aan `buildSystemPrompt()`. + +### Fout 3: Block wordt niet gerenderd + +**Symptoom:** Console log toont "Opening artifact: intent" maar niets verschijnt. + +**Oorzaak:** Drie plekken in artifact-container.tsx niet bijgewerkt. + +**Oplossing:** Check import, render case, én titel. + +### Fout 4: Escalatie bij elke invoer + +**Symptoom:** Alles gaat naar AI, zelfs simpele commando's. + +**Oorzaak:** Pattern weight te laag (< 0.7). + +**Oplossing:** Verhoog weights of voeg sterkere patterns toe. + +--- + +## 6. Diagram: Bestandenflow + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ BESTANDEN PER STAP │ +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ lib/cortex/ │ +│ ├── types.ts ────────────────────────┬─── Stap 1: Types │ +│ ├── reflex-classifier.ts ────────────┼─── Stap 2: Patterns │ +│ └── action-parser.ts ────────────────┼─── Stap 4-5: Validatie+Route │ +│ │ │ +│ app/api/cortex/ │ │ +│ ├── chat/route.ts ───────────────────┼─── Stap 3: AI Prompt │ +│ └── [domain]/route.ts ───────────────┼─── Stap 6: API (optioneel) │ +│ │ │ +│ components/cortex/ │ │ +│ ├── blocks/[intent]-block.tsx ───────┼─── Stap 7: Block Component │ +│ ├── artifacts/artifact-container.tsx ┼─── Stap 8: Rendering │ +│ └── command-center/command-center.tsx┴─── Stap 9: Navigatie │ +│ │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 7. Voorbeeld: Nieuwe Intent "kindcheck_query" + +Stel we willen een intent toevoegen voor "Toon kindcheck status". + +### Stap 1: types.ts +```typescript +// CortexIntent +| 'kindcheck_query' + +// BLOCK_CONFIGS +kindcheck_query: { + type: 'kindcheck_query', + title: 'Kindcheck', + size: 'md', + icon: 'Baby', +}, +``` + +### Stap 2: reflex-classifier.ts +```typescript +kindcheck_query: [ + { pattern: /^kindcheck/i, weight: 1.0 }, + { pattern: /^(toon|bekijk)\s+(de\s+)?kindcheck/i, weight: 0.95 }, + { pattern: /^zijn\s+er\s+kinderen/i, weight: 0.9 }, + { pattern: /^kinderen\s+in\s+beeld/i, weight: 0.85 }, +], +``` + +### Stap 3: chat/route.ts +```typescript +// In prompt: +- **kindcheck_query** — Kindcheck status opvragen + - Triggers: "kindcheck", "zijn er kinderen?", "kinderen in beeld?" + - Entities: geen + - Actie: Toont kindcheck status block + +// Voorbeeld: +### Voorbeeld 13: Kindcheck + +**User:** +"kindcheck" + +**AI Response:** +"Ik toon de kindcheck status. + +\`\`\`json +{ + "type": "action", + "intent": "kindcheck_query", + "entities": {}, + "confidence": 0.98, + "artifact": { + "type": "kindcheck_query", + "prefill": {} + } +} +\`\`\`" +``` + +### Stap 4-5: action-parser.ts +```typescript +// ActionSchema intent enum +'kindcheck_query', + +// ActionSchema artifact type +'kindcheck_query', + +// routeIntentToArtifact +case 'kindcheck_query': + return { + type: 'kindcheck_query', + title: 'Kindcheck', + prefill: entities, + }; +``` + +### Stap 6: API route +```typescript +// app/api/cortex/intake/kindcheck/route.ts +// ... (vergelijkbaar met risico/route.ts) +``` + +### Stap 7: Block component +```typescript +// components/cortex/blocks/kindcheck-block.tsx +// ... (vergelijkbaar met risico-block.tsx) +``` + +### Stap 8: artifact-container.tsx +```typescript +import { KindcheckBlock } from '../blocks/kindcheck-block'; + +// render case +case 'kindcheck_query': + return ; + +// titel +case 'kindcheck_query': + return 'Kindcheck'; +``` + +--- + +## 8. Gerelateerde Documentatie + +| Document | Locatie | +|----------|---------| +| Intent Overzicht | `docs/architectuur/intent-overzicht.md` | +| Block Template Pattern | `docs/intent/intake-intent-proces/block-template-pattern.md` | +| Session Log (Lessons Learned) | `docs/intent/intake-intent-proces/session-log-2026-02-03.md` | +| Architectuur Overzicht | `docs/architectuur/architectuur-overzicht.md` | + +--- + +*Bij vragen of problemen, raadpleeg de session logs voor bekende issues en oplossingen.* diff --git a/docs/architectuur/intent-overzicht.md b/docs/architectuur/intent-overzicht.md new file mode 100644 index 0000000..6af95e7 --- /dev/null +++ b/docs/architectuur/intent-overzicht.md @@ -0,0 +1,511 @@ +# Intent Overzicht — Cortex + +**Versie:** v1.0 +**Datum:** 4 februari 2026 +**Doelgroep:** Product owners, IT consultants, data scientists + +--- + +## 1. Wat is een Intent? + +Een **intent** is de gedetecteerde bedoeling achter een gebruikerscommando. Wanneer een zorgmedewerker zegt "zoek jan", herkent Cortex de intent `zoeken` met de entity `patientName: "jan"`. + +**Voorbeeld flow:** + +``` +Gebruikersinvoer → Intent → Actie +──────────────────────────────────────────────────────── +"notitie jan" → dagnotitie → Open notitie-formulier +"wat zijn risico's" → risico_query → Toon risico-overzicht +"agenda vandaag" → agenda_query → Toon afspraken +``` + +--- + +## 2. Alle Intents in Kaart + +### 2.1 Basis Intents (Productie) + +| Intent | Trigger voorbeelden | Wat het doet | Block | +|--------|---------------------|--------------|-------| +| `dagnotitie` | "notitie jan", "medicatie gegeven" | Verpleegkundige notitie maken | DagnotitieBlock | +| `zoeken` | "zoek marie", "wie is jan" | Patiënt opzoeken | ZoekenBlock | +| `overdracht` | "overdracht", "einde dienst" | Shift-overdracht bekijken | OverdrachtBlock | + +### 2.2 Agenda Intents (Productie) + +| Intent | Trigger voorbeelden | Wat het doet | Block | +|--------|---------------------|--------------|-------| +| `agenda_query` | "agenda", "afspraken vandaag" | Afspraken bekijken | AgendaBlock | +| `create_appointment` | "plan afspraak jan morgen" | Nieuwe afspraak maken | CreateAppointmentBlock | +| `cancel_appointment` | "annuleer afspraak jan" | Afspraak annuleren | CancelAppointmentBlock | +| `reschedule_appointment` | "verzet 14:00 naar 15:00" | Afspraak verzetten | RescheduleAppointmentBlock | + +### 2.3 Intake Intents (MVP) + +| Intent | Trigger voorbeelden | Wat het doet | Block | +|--------|---------------------|--------------|-------| +| `intake_status` | "wat moet ik nog doen?", "intake checklist" | Intake voortgang tonen | IntakeStatusBlock | +| `risico_query` | "wat zijn de risico's?", "risicotaxatie" | Risico's weergeven | RisicoBlock | +| `diagnose_query` | "welke diagnoses?", "toon diagnose" | Diagnoses weergeven | DiagnoseBlock | +| `intake_navigeer` | "ga naar risico", "open anamnese" | Naar intake-sectie navigeren | *(geen block, directe navigatie)* | + +### 2.4 Speciale Intents + +| Intent | Wanneer | Wat het doet | +|--------|---------|--------------| +| `unknown` | Niet herkend | Toon fallback-keuzemenu | + +--- + +## 3. Anatomie van een Intent + +Elke intent heeft meerdere "aanraakpunten" in de codebase: + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ INTENT LEVENSCYCLUS │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. TYPES lib/cortex/types.ts │ +│ └── Intent naam in CortexIntent type │ +│ └── Block config in BLOCK_CONFIGS │ +│ └── Entities in ExtractedEntities │ +│ │ +│ 2. HERKENNING lib/cortex/reflex-classifier.ts │ +│ └── Regex patterns voor lokale classificatie │ +│ │ +│ 3. AI PROMPT app/api/cortex/chat/route.ts │ +│ └── Intent beschrijving in system prompt │ +│ └── Voorbeelden met JSON output │ +│ │ +│ 4. VALIDATIE lib/cortex/action-parser.ts │ +│ └── Zod schema voor intent validatie │ +│ └── Artifact type mapping │ +│ │ +│ 5. ROUTING lib/cortex/action-parser.ts │ +│ └── routeIntentToArtifact() switch case │ +│ │ +│ 6. UI BLOCK components/cortex/blocks/[intent]-block.tsx │ +│ └── React component voor weergave │ +│ │ +│ 7. RENDERING components/cortex/artifacts/artifact- │ +│ container.tsx │ +│ └── Import statement │ +│ └── Switch case in renderArtifactBlock() │ +│ └── Titel in getArtifactTitle() │ +│ │ +│ 8. API (optioneel) app/api/cortex/[domain]/route.ts │ +│ └── Endpoint voor data ophalen │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. Bestandsoverzicht per Intent + +### 4.1 `dagnotitie` + +| Aspect | Bestand | Regel/Sectie | +|--------|---------|--------------| +| Type definitie | `lib/cortex/types.ts` | `CortexIntent` type | +| Block config | `lib/cortex/types.ts` | `BLOCK_CONFIGS.dagnotitie` | +| Patterns | `lib/cortex/reflex-classifier.ts` | `INTENT_PATTERNS.dagnotitie` | +| AI prompt | `app/api/cortex/chat/route.ts` | System prompt sectie | +| Validatie | `lib/cortex/action-parser.ts` | `ActionSchema` | +| Routing | `lib/cortex/action-parser.ts` | `routeIntentToArtifact()` case | +| UI Block | `components/cortex/blocks/dagnotitie-block.tsx` | Hele bestand | +| Rendering | `components/cortex/artifacts/artifact-container.tsx` | Import + switch | +| API | `app/api/reports/route.ts` | POST voor opslaan | + +**Entities:** +```typescript +{ + patientName?: string; // "jan" + patientId?: string; // UUID + category?: 'medicatie' | 'adl' | 'gedrag' | 'incident' | 'observatie'; + content?: string; // "medicatie gegeven" +} +``` + +--- + +### 4.2 `zoeken` + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/zoeken-block.tsx` | +| API | `app/api/cortex/patients/search/route.ts` | + +**Entities:** +```typescript +{ + query?: string; // Zoekterm + patientName?: string; // Directe naam +} +``` + +--- + +### 4.3 `overdracht` + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/overdracht-block.tsx` | +| API | `app/api/overdracht/route.ts` | + +**Entities:** +```typescript +{ + // Geen specifieke entities +} +``` + +--- + +### 4.4 `agenda_query` + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/agenda-block.tsx` | +| API | `app/api/cortex/agenda/route.ts` | + +**Entities:** +```typescript +{ + dateRange?: { + start: Date; + end: Date; + label: 'vandaag' | 'morgen' | 'deze week' | 'volgende week' | 'custom'; + }; +} +``` + +--- + +### 4.5 `create_appointment` + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/agenda-block.tsx` (create mode) | +| API | `app/api/cortex/agenda/create/route.ts` | + +**Entities:** +```typescript +{ + patientName?: string; + patientId?: string; + datetime?: { + date: Date; + time: string; // "HH:mm" + }; + appointmentType?: 'intake' | 'behandeling' | 'follow-up' | 'telefonisch' | 'huisbezoek' | 'online' | 'crisis' | 'overig'; + location?: 'praktijk' | 'online' | 'thuis'; +} +``` + +--- + +### 4.6 `intake_status` (MVP) + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/intake-status-block.tsx` | +| API | `app/api/cortex/intake/status/route.ts` | + +**Entities:** +```typescript +{ + patientId?: string; + intakeId?: string; +} +``` + +**Trigger patterns:** +- "wat moet ik nog doen?" +- "is de intake compleet?" +- "intake checklist" +- "intake status" + +--- + +### 4.7 `risico_query` (MVP) + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/risico-block.tsx` | +| API | `app/api/cortex/intake/risico/route.ts` | + +**Entities:** +```typescript +{ + patientId?: string; + intakeId?: string; +} +``` + +**Trigger patterns:** +- "wat zijn de risico's?" +- "risicotaxatie" +- "toon risico's" + +--- + +### 4.8 `diagnose_query` (MVP) + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Block config | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| UI Block | `components/cortex/blocks/diagnose-block.tsx` | +| API | `app/api/cortex/intake/diagnose/route.ts` | + +**Entities:** +```typescript +{ + patientId?: string; + intakeId?: string; +} +``` + +**Trigger patterns:** +- "welke diagnoses?" +- "toon diagnose" +- "wat is de diagnose?" + +--- + +### 4.9 `intake_navigeer` (MVP) + +| Aspect | Bestand | +|--------|---------| +| Type definitie | `lib/cortex/types.ts` | +| Patterns | `lib/cortex/reflex-classifier.ts` | +| Handler | `components/cortex/command-center/command-center.tsx` | + +**Let op:** Deze intent heeft geen block — het navigeert direct naar een EPD-pagina. + +**Entities:** +```typescript +{ + navigationTarget?: 'contacts' | 'kindcheck' | 'risk' | 'anamnese' | + 'examination' | 'rom' | 'diagnosis' | 'behandeladvies'; +} +``` + +**Trigger patterns:** +- "ga naar risico" +- "open diagnose" +- "naar anamnese" + +--- + +## 5. Data Flow Diagram + +### 5.1 Van Invoer naar Actie + +``` +┌──────────────┐ +│ GEBRUIKER │ +│ spreekt/ │ +│ typt │ +└──────┬───────┘ + │ "notitie jan medicatie" + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ LAAG 1: REFLEX ARC │ +│ lib/cortex/reflex-classifier.ts │ +│ │ +│ • Pattern matching: /^notitie\s+\w+/i │ +│ • Confidence: 0.95 │ +│ • Escalatie check: geen multi-intent, geen context nodig │ +│ │ +│ Output: { intent: "dagnotitie", confidence: 0.95 } │ +└──────────────────────────┬───────────────────────────────────┘ + │ + ┌────────────────┴────────────────┐ + │ Confidence >= 0.7? │ + └────────────────┬────────────────┘ + │ Ja + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ ROUTING │ +│ lib/cortex/action-parser.ts → routeIntentToArtifact() │ +│ │ +│ Input: intent="dagnotitie", entities={patientName:"jan"} │ +│ Output: { type: "dagnotitie", prefill: {...}, title: "..." } │ +└──────────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ UI RENDERING │ +│ components/cortex/artifacts/artifact-container.tsx │ +│ │ +│ • Switch op artifact.type │ +│ • Rendert │ +└──────────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ BLOCK │ +│ components/cortex/blocks/dagnotitie-block.tsx │ +│ │ +│ • Toont formulier met prefilled data │ +│ • Gebruiker vult aan en klikt "Opslaan" │ +│ • POST naar /api/reports │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 5.2 Escalatie naar AI (Laag 2) + +Wanneer escaleert de Reflex Arc naar de AI Orchestrator? + +| Trigger | Voorbeeld | Reden | +|---------|-----------|-------| +| **Lage confidence** | "blah blah" | Geen pattern match | +| **Ambiguïteit** | "jan" | Kan zoeken of notitie zijn | +| **Multi-intent** | "zeg jan af en maak notitie" | Twee acties in één zin | +| **Context nodig** | "maak notitie voor hem" | Wie is "hem"? | +| **Relatieve tijd** | "morgen om 14:00" | Datum moet berekend worden | + +``` +┌──────────────────────────────────────────────────────────────┐ +│ LAAG 2: ORCHESTRATOR (bij escalatie) │ +│ app/api/cortex/chat/route.ts │ +│ │ +│ • Stuurt context + input naar Claude AI │ +│ • AI retourneert IntentChain met 1+ actions │ +│ • Kan clarification vragen ("Met welke patiënt?") │ +└──────────────────────────────────────────────────────────────┘ +``` + +--- + +## 6. Entity Extractie + +### 6.1 Hoe worden entities geëxtraheerd? + +**Lokaal (Reflex Arc):** +- Eenvoudige regex voor bekende patronen +- Voorbeeld: `/^notitie\s+(\w+)/` → extraheert patiëntnaam + +**AI (Orchestrator):** +- Claude analyseert volledige zin +- Extraheert alle relevante entities +- Kan context gebruiken (actieve patiënt, agenda) + +### 6.2 Entity Types + +| Entity | Type | Voorbeeld | Gebruikt door | +|--------|------|-----------|---------------| +| `patientName` | string | "jan de vries" | Alle intents | +| `patientId` | UUID | "abc-123..." | Alle intents | +| `category` | enum | "medicatie" | dagnotitie | +| `content` | string | "medicatie gegeven" | dagnotitie | +| `query` | string | "jan" | zoeken | +| `dateRange` | object | { start, end, label } | agenda_query | +| `datetime` | object | { date, time } | create_appointment | +| `appointmentType` | enum | "intake" | create_appointment | +| `location` | enum | "praktijk" | create_appointment | +| `navigationTarget` | enum | "risk" | intake_navigeer | + +--- + +## 7. Block Types + +### 7.1 Block Categorieën + +| Type | Doel | Voorbeeld | +|------|------|-----------| +| **Query Block** | Data tonen (read-only) | RisicoBlock, DiagnoseBlock | +| **Action Block** | Data invoeren/wijzigen | DagnotitieBlock | +| **Status Block** | Voortgang/checklist tonen | IntakeStatusBlock | +| **Navigation Block** | Direct navigeren | *(intake_navigeer)* | + +### 7.2 Block Structuur + +Alle blocks volgen hetzelfde patroon: + +``` +┌─────────────────────────────────────────────────────┐ +│ [Icon] Titel [Sluiten] │ +├─────────────────────────────────────────────────────┤ +│ │ +│ Loading state → Error state → Data state │ +│ (spinner) (foutmelding) (content) │ +│ │ +│ ─────────────────────────────────────────────── │ +│ [Secundaire actie] [Primaire actie] │ +└─────────────────────────────────────────────────────┘ +``` + +--- + +## 8. Confidence & Escalatie + +### 8.1 Drempelwaarden + +| Waarde | Betekenis | Actie | +|--------|-----------|-------| +| `>= 0.9` | Zeer zeker | Direct uitvoeren | +| `0.7 - 0.9` | Redelijk zeker | Uitvoeren met bevestiging | +| `< 0.7` | Onzeker | Escaleer naar AI of vraag verduidelijking | + +### 8.2 Escalatie Redenen + +```typescript +type EscalationReason = + | 'low_confidence' // Confidence < 0.7 + | 'ambiguous' // Top-2 intents liggen dicht bij elkaar + | 'multi_intent_detected' // Meerdere acties gedetecteerd + | 'needs_context' // Voornaamwoorden zoals "hij", "haar" + | 'relative_time'; // "morgen", "volgende week" +``` + +--- + +## 9. Gerelateerde Documentatie + +| Document | Locatie | Inhoud | +|----------|---------|--------| +| Block Template Pattern | `docs/intent/intake-intent-proces/block-template-pattern.md` | Technisch patroon voor blocks | +| Session Log | `docs/intent/intake-intent-proces/session-log-2026-02-03.md` | Bug fixes en lessons learned | +| Implementatieplan | `docs/architectuur/implementatieplan-nieuwe-intents.md` | Stappenplan nieuwe intents | + +--- + +## 10. Glossary + +| Term | Betekenis | +|------|-----------| +| **Intent** | Gedetecteerde bedoeling achter een commando | +| **Entity** | Geëxtraheerd gegeven uit de invoer (naam, datum, etc.) | +| **Block** | UI component dat een intent visueel afhandelt | +| **Artifact** | Container voor blocks in het werkgebied | +| **Confidence** | Zekerheidsgraad van classificatie (0-1) | +| **Escalatie** | Doorverwijzing naar AI voor complexe invoer | +| **Prefill** | Vooringevulde data in een formulier | + +--- + +*Voor het toevoegen van nieuwe intents, zie: `implementatieplan-nieuwe-intents.md`* diff --git a/docs/intent/intake-intent-proces/block-template-pattern.md b/docs/intent/intake-intent-proces/block-template-pattern.md new file mode 100644 index 0000000..3b89ccf --- /dev/null +++ b/docs/intent/intake-intent-proces/block-template-pattern.md @@ -0,0 +1,894 @@ +# Cortex Block Template & Patterns + +**Datum:** 03-02-2026 +**Status:** Ontwerp +**Doel:** Herbruikbaar patroon voor alle intake-gerelateerde Cortex blocks + +--- + +## 1. Anatomie van een Cortex Block + +``` +┌─────────────────────────────────────────────────────────────┐ +│ [Icon] Titel [Close] │ ← Header (via BlockContainer) +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Loading State │ │ ← State: Loading +│ │ [Spinner] Data laden... │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Error State │ │ ← State: Error +│ │ [!] Foutmelding + [Retry] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Empty State │ │ ← State: Empty +│ │ [Icon] Geen data + [Actie] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Data State │ │ ← State: Data +│ │ │ │ +│ │ Section 1: [Icon] Label │ │ +│ │ ├── Item 1 │ │ +│ │ └── Item 2 │ │ +│ │ │ │ +│ │ Section 2: [Icon] Label │ │ +│ │ └── Content │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ───────────────────────────────────────────────────────── │ +│ [Secondary Action] [Primary Action] │ ← Footer Actions +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. Block Types + +### 2.1 Query Block (Read-only) + +**Doel:** Data tonen, navigeren naar EPD voor bewerken + +```typescript +// Voorbeeld: RisicoQueryBlock +// Input: "Wat zijn de risico's?" +// Output: Lijst risico's met levels + +interface QueryBlockPattern { + // Data ophalen + fetch: () => Promise; + + // Tonen + render: (data: Data) => JSX.Element; + + // Navigatie + onViewInEPD: () => void; // "Bekijk in dossier" +} +``` + +### 2.2 Action Block (Create/Update) + +**Doel:** Snelle invoer, bevestiging, opslaan + +```typescript +// Voorbeeld: RisicoToevoegenBlock +// Input: "Risico suïcidaliteit matig" +// Output: Prefilled form, confirm, save + +interface ActionBlockPattern { + // Prefill van intent entities + prefill: ExtractedEntities; + + // Form state + form: FormState; + + // Opslaan + onSubmit: () => Promise; + + // Bevestiging + confirmationRequired: boolean; +} +``` + +### 2.3 Status Block (Progress/Checklist) + +**Doel:** Voortgang tonen, navigeren naar onvolledige items + +```typescript +// Voorbeeld: IntakeStatusBlock +// Input: "Wat moet ik nog doen?" +// Output: Checklist met links + +interface StatusBlockPattern { + // Status per onderdeel + status: Record; + + // Navigatie naar onderdeel + onNavigate: (section: string) => void; +} +``` + +### 2.4 Navigation Block (Route) + +**Doel:** Direct navigeren naar EPD pagina + +```typescript +// Voorbeeld: IntakeNavigatieBlock +// Input: "Ga naar diagnose" +// Output: Router.push() of openArtifact() + +interface NavigationBlockPattern { + // Doel bepalen + destination: string; + + // Navigeren + onNavigate: () => void; +} +``` + +--- + +## 3. Shared Components + +### 3.1 Block States + +```typescript +// components/cortex/shared/block-states.tsx + +interface BlockLoadingProps { + message?: string; +} + +export function BlockLoading({ message = 'Laden...' }: BlockLoadingProps) { + return ( +
+ + {message} +
+ ); +} + +interface BlockErrorProps { + message: string; + onRetry?: () => void; +} + +export function BlockError({ message, onRetry }: BlockErrorProps) { + return ( +
+ +

{message}

+ {onRetry && ( + + )} +
+ ); +} + +interface BlockEmptyProps { + icon: LucideIcon; + message: string; + action?: { + label: string; + onClick: () => void; + }; +} + +export function BlockEmpty({ icon: Icon, message, action }: BlockEmptyProps) { + return ( +
+ +

{message}

+ {action && ( + + )} +
+ ); +} +``` + +### 3.2 Block Section + +```typescript +// components/cortex/shared/block-section.tsx + +interface BlockSectionProps { + icon: LucideIcon; + iconColor?: string; + title: string; + count?: number; + children: ReactNode; +} + +export function BlockSection({ + icon: Icon, + iconColor = 'text-slate-600', + title, + count, + children +}: BlockSectionProps) { + return ( +
+
+ +

{title}

+ {count !== undefined && ( + ({count}) + )} +
+ {children} +
+ ); +} +``` + +### 3.3 Block Item (List Row) + +```typescript +// components/cortex/shared/block-item.tsx + +interface BlockItemProps { + title: string; + subtitle?: string; + badge?: { + label: string; + variant: 'default' | 'success' | 'warning' | 'danger'; + }; + onClick?: () => void; +} + +const BADGE_STYLES = { + default: 'bg-slate-100 text-slate-700', + success: 'bg-green-50 text-green-700', + warning: 'bg-amber-50 text-amber-700', + danger: 'bg-red-50 text-red-700', +}; + +export function BlockItem({ title, subtitle, badge, onClick }: BlockItemProps) { + const Wrapper = onClick ? 'button' : 'div'; + + return ( + +
+

{title}

+ {subtitle && ( +

{subtitle}

+ )} +
+ {badge && ( + + {badge.label} + + )} +
+ ); +} +``` + +### 3.4 Block Footer + +```typescript +// components/cortex/shared/block-footer.tsx + +interface BlockFooterProps { + secondaryAction?: { + label: string; + icon?: LucideIcon; + onClick: () => void; + }; + primaryAction?: { + label: string; + icon?: LucideIcon; + onClick: () => void; + loading?: boolean; + }; +} + +export function BlockFooter({ secondaryAction, primaryAction }: BlockFooterProps) { + return ( +
+ {secondaryAction ? ( + + ) :
} + + {primaryAction && ( + + )} +
+ ); +} +``` + +--- + +## 4. Custom Hook Pattern + +### 4.1 useBlockData (Generic Data Fetching) + +```typescript +// lib/cortex/hooks/use-block-data.ts + +interface UseBlockDataOptions { + endpoint: string; + params?: Record; + enabled?: boolean; + onError?: (error: Error) => void; +} + +interface UseBlockDataResult { + data: T | null; + isLoading: boolean; + error: string | null; + refetch: () => Promise; +} + +export function useBlockData({ + endpoint, + params, + enabled = true, + onError, +}: UseBlockDataOptions): UseBlockDataResult { + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(enabled); + const [error, setError] = useState(null); + const { toast } = useToast(); + + const fetchData = useCallback(async () => { + setIsLoading(true); + setError(null); + + try { + const url = new URL(endpoint, window.location.origin); + if (params) { + Object.entries(params).forEach(([key, value]) => { + url.searchParams.set(key, value); + }); + } + + const response = await safeFetch(url.toString()); + const result = await response.json(); + setData(result); + } catch (err) { + const errorInfo = getErrorInfo(err); + setError(errorInfo.description); + onError?.(err as Error); + toast({ + variant: 'destructive', + title: errorInfo.title, + description: errorInfo.description, + }); + } finally { + setIsLoading(false); + } + }, [endpoint, params, onError, toast]); + + useEffect(() => { + if (enabled) { + fetchData(); + } + }, [enabled, fetchData]); + + return { data, isLoading, error, refetch: fetchData }; +} +``` + +### 4.2 useIntakeContext (Intake-specific) + +```typescript +// lib/cortex/hooks/use-intake-context.ts + +interface UseIntakeContextResult { + patientId: string | null; + intakeId: string | null; + patientName: string | null; + hasContext: boolean; +} + +export function useIntakeContext(prefill?: BlockPrefillData): UseIntakeContextResult { + const { activePatient } = useCortexStore(); + + // TODO: Add activeIntake to cortex-store + // For now, we need intakeId from prefill or URL + + const patientId = prefill?.patientId || activePatient?.id || null; + const patientName = prefill?.patientName || + (activePatient ? formatPatientName(activePatient) : null); + const intakeId = prefill?.intakeId || null; + + return { + patientId, + intakeId, + patientName, + hasContext: Boolean(patientId), + }; +} +``` + +--- + +## 5. Template: Query Block + +```typescript +// components/cortex/blocks/[name]-query-block.tsx + +'use client'; + +/** + * [Name] Query Block + * + * Block voor het tonen van [beschrijving]. + * Intent: [intent_name] + */ + +import { useEffect } from 'react'; +import { useCortexStore } from '@/stores/cortex-store'; +import { useToast } from '@/hooks/use-toast'; +import { BlockContainer } from './block-container'; +import { BlockLoading, BlockError, BlockEmpty } from '../shared/block-states'; +import { BlockSection } from '../shared/block-section'; +import { BlockItem } from '../shared/block-item'; +import { BlockFooter } from '../shared/block-footer'; +import { useBlockData } from '@/lib/cortex/hooks/use-block-data'; +import { useIntakeContext } from '@/lib/cortex/hooks/use-intake-context'; +import type { BlockPrefillData } from '@/stores/cortex-store'; +import { BLOCK_CONFIGS } from '@/lib/cortex/types'; +import { [Icon], ExternalLink } from 'lucide-react'; + +// ============================================================================ +// Types +// ============================================================================ + +interface [Name]QueryBlockProps { + prefill?: BlockPrefillData; +} + +interface [Name]Data { + items: Array<{ + id: string; + // ... fields + }>; +} + +// ============================================================================ +// Component +// ============================================================================ + +export function [Name]QueryBlock({ prefill }: [Name]QueryBlockProps) { + const config = BLOCK_CONFIGS['[block-type]']; + const { closeBlock } = useCortexStore(); + const { patientId, intakeId, patientName, hasContext } = useIntakeContext(prefill); + + // Fetch data + const { data, isLoading, error, refetch } = useBlockData<[Name]Data>({ + endpoint: `/api/cortex/intake/${intakeId}/[endpoint]`, + enabled: Boolean(intakeId), + }); + + // Handle no context + if (!hasContext) { + return ( + + + + ); + } + + // Handle loading + if (isLoading) { + return ( + + + + ); + } + + // Handle error + if (error) { + return ( + + + + ); + } + + // Handle empty + if (!data?.items?.length) { + return ( + + { + // Navigate to EPD + window.location.href = `/epd/patients/${patientId}/intakes/${intakeId}/[tab]`; + }, + }} + /> + + ); + } + + // Render data + return ( + +
+ +
+ {data.items.map((item) => ( + + ))} +
+
+ + { + window.location.href = `/epd/patients/${patientId}/intakes/${intakeId}/[tab]`; + }, + }} + primaryAction={{ + label: 'Toevoegen', + onClick: () => { + // Open action block or navigate + }, + }} + /> +
+
+ ); +} + +// ============================================================================ +// Helpers +// ============================================================================ + +function getBadgeVariant(status: string): 'default' | 'success' | 'warning' | 'danger' { + switch (status) { + case 'laag': return 'success'; + case 'gemiddeld': return 'warning'; + case 'hoog': + case 'zeer_hoog': return 'danger'; + default: return 'default'; + } +} +``` + +--- + +## 6. Template: Status Block + +```typescript +// components/cortex/blocks/intake-status-block.tsx + +'use client'; + +/** + * Intake Status Block + * + * Block voor het tonen van intake voortgang/checklist. + * Intent: intake_status + */ + +import { useCortexStore } from '@/stores/cortex-store'; +import { BlockContainer } from './block-container'; +import { BlockLoading, BlockError, BlockEmpty } from '../shared/block-states'; +import { useBlockData } from '@/lib/cortex/hooks/use-block-data'; +import { useIntakeContext } from '@/lib/cortex/hooks/use-intake-context'; +import type { BlockPrefillData } from '@/stores/cortex-store'; +import { BLOCK_CONFIGS } from '@/lib/cortex/types'; +import { + CheckCircle2, + Circle, + ClipboardList, + ChevronRight, +} from 'lucide-react'; +import { cn } from '@/lib/utils'; + +// ============================================================================ +// Types +// ============================================================================ + +interface IntakeStatusBlockProps { + prefill?: BlockPrefillData; +} + +interface IntakeStatusData { + intakeId: string; + intakeTitle: string; + completedCount: number; + totalCount: number; + sections: Array<{ + key: string; + label: string; + completed: boolean; + required: boolean; + path: string; + }>; +} + +// ============================================================================ +// Constants +// ============================================================================ + +const SECTION_ORDER = [ + { key: 'algemeen', label: 'Algemeen', required: true }, + { key: 'contactmomenten', label: 'Contactmomenten', required: false }, + { key: 'kindcheck', label: 'Kindcheck', required: true }, + { key: 'risicotaxatie', label: 'Risicotaxatie', required: true }, + { key: 'anamnese', label: 'Anamnese', required: true }, + { key: 'onderzoeken', label: 'Onderzoeken', required: false }, + { key: 'rom', label: 'ROM', required: false }, + { key: 'diagnose', label: 'Diagnose', required: true }, + { key: 'behandeladvies', label: 'Behandeladvies', required: true }, +]; + +// ============================================================================ +// Component +// ============================================================================ + +export function IntakeStatusBlock({ prefill }: IntakeStatusBlockProps) { + const config = BLOCK_CONFIGS['intake-status']; // Need to add this + const { patientId, intakeId, hasContext } = useIntakeContext(prefill); + + const { data, isLoading, error, refetch } = useBlockData({ + endpoint: `/api/cortex/intake/${intakeId}/status`, + enabled: Boolean(intakeId), + }); + + if (!hasContext) { + return ( + + + + ); + } + + if (isLoading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + ); + } + + if (!data) { + return ( + + + + ); + } + + const progress = Math.round((data.completedCount / data.totalCount) * 100); + const incompleteSections = data.sections.filter(s => !s.completed && s.required); + + return ( + +
+ {/* Progress Bar */} +
+
+ Voortgang + + {data.completedCount}/{data.totalCount} ({progress}%) + +
+
+
+
+
+ + {/* Incomplete Items (priority) */} + {incompleteSections.length > 0 && ( +
+

+ Nog te voltooien ({incompleteSections.length}) +

+
+ {incompleteSections.map((section) => ( + + ))} +
+
+ )} + + {/* All Sections */} +
+ {data.sections.map((section) => ( + + ))} +
+ + {/* Complete Message */} + {progress === 100 && ( +
+ +

+ Intake is compleet! +

+

+ Je kunt de intake nu afsluiten +

+
+ )} +
+ + ); +} +``` + +--- + +## 7. Checklist voor nieuwe Block + +Bij het bouwen van een nieuwe block: + +- [ ] **Types definiëren** - Props interface, Data interface +- [ ] **Intent toevoegen** aan `lib/cortex/types.ts` +- [ ] **Patterns toevoegen** aan `lib/cortex/reflex-classifier.ts` +- [ ] **Block config toevoegen** aan `BLOCK_CONFIGS` +- [ ] **API route maken** (indien nodig) in `app/api/cortex/` +- [ ] **Block component bouwen** met shared components +- [ ] **Canvas-area updaten** om block te renderen +- [ ] **Testen** met voice input en prefill + +--- + +## 8. Bestandsstructuur + +``` +components/cortex/ +├── blocks/ +│ ├── block-container.tsx # Wrapper (bestaat) +│ ├── dagnotitie-block.tsx # Bestaat +│ ├── zoeken-block.tsx # Bestaat +│ ├── overdracht-block.tsx # Bestaat +│ ├── patient-dashboard-block.tsx # Bestaat +│ │ +│ ├── # NIEUW - Intake Blocks +│ ├── intake-status-block.tsx # Checklist +│ ├── risico-query-block.tsx # Risico's tonen +│ ├── diagnose-query-block.tsx # Diagnoses tonen +│ ├── kindcheck-query-block.tsx # Kindcheck status +│ └── screening-query-block.tsx # Screening overzicht +│ +├── shared/ +│ ├── patient-list-item.tsx # Bestaat +│ ├── linked-evidence.tsx # Bestaat +│ │ +│ ├── # NIEUW - Shared Block Components +│ ├── block-states.tsx # Loading/Error/Empty +│ ├── block-section.tsx # Section wrapper +│ ├── block-item.tsx # List item +│ └── block-footer.tsx # Footer actions +│ +lib/cortex/ +├── hooks/ +│ ├── use-patient-search.ts # Bestaat +│ ├── use-patient-selection.ts # Bestaat +│ │ +│ ├── # NIEUW +│ ├── use-block-data.ts # Generic data fetching +│ └── use-intake-context.ts # Intake context +``` + +--- + +## 9. Volgende Stappen + +1. **Shared components bouwen** (`block-states.tsx`, etc.) +2. **Hooks bouwen** (`use-block-data.ts`, `use-intake-context.ts`) +3. **Eerste block:** `IntakeStatusBlock` (meest waardevolle quick win) +4. **API route:** `/api/cortex/intake/[id]/status` +5. **Intent + patterns** toevoegen voor `intake_status` diff --git a/docs/intent/intake-intent-proces/bouwplan-cortex-shared-components.md b/docs/intent/intake-intent-proces/bouwplan-cortex-shared-components.md new file mode 100644 index 0000000..96e1ebe --- /dev/null +++ b/docs/intent/intake-intent-proces/bouwplan-cortex-shared-components.md @@ -0,0 +1,365 @@ +# 🚀 Mission Control — Bouwplan Cortex Shared Components + +**Projectnaam:** Cortex Intake Intent System - Shared Components +**Versie:** v1.0 +**Datum:** 03-02-2026 +**Auteur:** Colin Lit + +--- + +## 1. Doel en Context + +🎯 **Doel:** +Bouwen van herbruikbare shared components en hooks voor alle Cortex Intake Blocks. Deze componenten vormen de foundation voor de intake-gerelateerde intents. + +📘 **Context:** +De Cortex Command Center heeft momenteel 7 werkende intents, maar geen van de 26 intake-gerelateerde intents is geïmplementeerd. De shared components zijn de eerste stap om dit te realiseren. Ze worden hergebruikt door alle nieuwe intake blocks. + +**Relatie met andere documenten:** +| Document | Relatie | +|----------|---------| +| `fo-to-cortex-shared-components.md` | Functionele en technische specificaties | +| `intake-process-intents.md` | Intent definities die deze components gebruiken | +| `gap-analyse-intake-cortex.md` | Identificeert de behoefte aan deze components | + +--- + +## 2. Uitgangspunten + +### 2.1 Technische Stack + +| Component | Technologie | Reden | +|-----------|-------------|-------| +| **Framework** | Next.js 14 (App Router) | Bestaande stack | +| **UI Library** | React 18 + TypeScript | Type safety, DX | +| **Styling** | Tailwind CSS | Utility-first, consistent met project | +| **Icons** | Lucide React | Bestaande icon library | +| **Components** | shadcn/ui | Button, etc. al in gebruik | +| **State** | Zustand (cortex-store) | Bestaande state management | +| **Animations** | Framer Motion | Optioneel, al in project | + +### 2.2 Projectkaders + +| Kader | Waarde | +|-------|--------| +| **Tijd** | 1 dag bouwtijd | +| **Scope** | 4 components + 2 hooks | +| **Team** | 1 developer | +| **Data** | Geen nieuwe data, alleen UI | +| **Doel** | Foundation voor intake blocks | + +### 2.3 Programmeer Uitgangspunten + +**Code Quality Principles:** +- **DRY** - Herbruikbare components en utility functions +- **KISS** - Eenvoudige oplossingen, geen premature optimization +- **SOC** - UI gescheiden van business logic, hooks voor data +- **YAGNI** - Alleen bouwen wat nu nodig is + +**Development Practices:** +- TypeScript strict mode +- Props interfaces geëxporteerd voor hergebruik +- Consistente naamgeving (`Block*` prefix) +- JSDoc comments voor public API +- Tree-shakeable exports via index.ts + +--- + +## 3. Epics & Stories Overzicht + +| Epic ID | Titel | Doel | Status | Stories | Geschat | +|---------|-------|------|--------|---------|---------| +| E0 | Block States | Loading/Error/Empty states | ⏳ To Do | 3 | 2 uur | +| E1 | Block Layout | Section/Item/Footer components | ⏳ To Do | 3 | 2 uur | +| E2 | Custom Hooks | Data fetching en context hooks | ⏳ To Do | 2 | 2 uur | +| E3 | Integration | Exports en documentatie | ⏳ To Do | 2 | 1 uur | + +**Totaal geschat:** ~7 uur (1 werkdag) + +--- + +## 4. Epics & Stories (Uitwerking) + +### Epic 0 — Block States + +**Epic Doel:** Consistente states voor alle blocks (loading, error, empty). + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP | +|----------|--------------|---------------------|--------|------------------|----| +| E0.S1 | `BlockLoading` component | Spinner + message, centered, animatie | ⏳ | — | 1 | +| E0.S2 | `BlockError` component | Error icon + message + retry button | ⏳ | — | 1 | +| E0.S3 | `BlockEmpty` component | Custom icon + message + action button | ⏳ | — | 1 | + +**Technical Notes:** +```typescript +// Verwachte exports +export { BlockLoading } from './block-states'; +export { BlockError } from './block-states'; +export { BlockEmpty } from './block-states'; +``` + +**Acceptatiecriteria E0:** +- [ ] Alle 3 states renderen correct +- [ ] Props zijn fully typed met interfaces +- [ ] Responsive op alle block sizes (sm/md/lg) +- [ ] Consistent met bestaande design (slate colors, tailwind) + +--- + +### Epic 1 — Block Layout + +**Epic Doel:** Herbruikbare layout components voor block content. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP | +|----------|--------------|---------------------|--------|------------------|----| +| E1.S1 | `BlockSection` component | Icon + title + count + children wrapper | ⏳ | — | 1 | +| E1.S2 | `BlockItem` component | Title + subtitle + badge, clickable optie | ⏳ | — | 2 | +| E1.S3 | `BlockFooter` component | Secondary + primary action buttons | ⏳ | E1.S2 | 1 | + +**Technical Notes:** +```typescript +// Badge variants voor BlockItem +type BadgeVariant = 'default' | 'success' | 'warning' | 'danger'; + +// Helper functie +export function getRiskBadgeVariant(level: string): BadgeVariant; +``` + +**Acceptatiecriteria E1:** +- [ ] Section toont icon met configureerbare kleur +- [ ] Item is klikbaar wanneer onClick meegegeven +- [ ] Item hover state alleen bij klikbaar +- [ ] Footer acties correct aligned (left/right) +- [ ] Footer toont loading spinner op primary action + +--- + +### Epic 2 — Custom Hooks + +**Epic Doel:** Hooks voor data fetching en context management. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP | +|----------|--------------|---------------------|--------|------------------|----| +| E2.S1 | `useBlockData` hook | Generic fetch met loading/error states, refetch | ⏳ | error-handler.ts | 3 | +| E2.S2 | `useIntakeContext` hook | Patient + Intake context van store/prefill | ⏳ | cortex-store | 2 | + +**Technical Notes:** +```typescript +// useBlockData interface +interface UseBlockDataResult { + data: T | null; + isLoading: boolean; + error: string | null; + refetch: () => Promise; +} + +// useIntakeContext interface +interface UseIntakeContextResult { + patientId: string | null; + intakeId: string | null; + patientName: string | null; + hasPatientContext: boolean; + hasIntakeContext: boolean; +} +``` + +**Acceptatiecriteria E2:** +- [ ] useBlockData handelt errors via bestaande error-handler +- [ ] useBlockData toont toast bij error +- [ ] useBlockData supported enabled flag voor conditional fetching +- [ ] useIntakeContext valt terug op activePatient uit store +- [ ] useIntakeContext is memoized om re-renders te voorkomen + +--- + +### Epic 3 — Integration + +**Epic Doel:** Exports, documentatie en integratie met bestaande code. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP | +|----------|--------------|---------------------|--------|------------------|----| +| E3.S1 | Index exports | Barrel file met alle exports | ⏳ | E0, E1, E2 | 1 | +| E3.S2 | Update bestaande blocks | Refactor ZoekenBlock naar shared components | ⏳ | E3.S1 | 2 | + +**Technical Notes:** +```typescript +// components/cortex/shared/index.ts +// Block States +export { BlockLoading, BlockError, BlockEmpty } from './block-states'; + +// Block Layout +export { BlockSection } from './block-section'; +export { BlockItem, getRiskBadgeVariant, type BadgeVariant } from './block-item'; +export { BlockFooter } from './block-footer'; + +// Existing (unchanged) +export { PatientListItem, PatientListEmpty, PatientListLoading } from './patient-list-item'; +export { LinkedEvidence } from './linked-evidence'; +``` + +**Acceptatiecriteria E3:** +- [ ] Alle components importeerbaar via `@/components/cortex/shared` +- [ ] Hooks importeerbaar via `@/lib/cortex/hooks` +- [ ] Geen breaking changes voor bestaande code +- [ ] ZoekenBlock werkt nog na refactor (smoke test) + +--- + +## 5. Implementatie Volgorde + +``` +E0.S1 BlockLoading ──┐ +E0.S2 BlockError ──┼──▶ E0 Complete +E0.S3 BlockEmpty ──┘ + │ +E1.S1 BlockSection ──┐ │ +E1.S2 BlockItem ──┼──▶ E1 Complete +E1.S3 BlockFooter ──┘ │ + │ +E2.S1 useBlockData ──┬──▶ E2 Complete +E2.S2 useIntakeContext ──┘ │ + │ +E3.S1 Index exports ──┬──▶ E3 Complete ──▶ ✅ DONE +E3.S2 Refactor test ──┘ +``` + +--- + +## 6. Bestandsstructuur (Deliverables) + +``` +components/cortex/shared/ +├── block-states.tsx 🆕 E0.S1-S3 +├── block-section.tsx 🆕 E1.S1 +├── block-item.tsx 🆕 E1.S2 +├── block-footer.tsx 🆕 E1.S3 +├── index.ts 🆕 E3.S1 (update) +├── patient-list-item.tsx ✅ Bestaat +└── linked-evidence.tsx ✅ Bestaat + +lib/cortex/hooks/ +├── use-block-data.ts 🆕 E2.S1 +├── use-intake-context.ts 🆕 E2.S2 +├── use-patient-search.ts ✅ Bestaat +└── use-patient-selection.ts ✅ Bestaat +``` + +--- + +## 7. Kwaliteit & Testplan + +### Test Types + +| Test Type | Scope | Hoe | Status | +|-----------|-------|-----|--------| +| Type Check | Alle files | `pnpm types:check` | ⏳ | +| Lint | Alle files | `pnpm lint` | ⏳ | +| Visual | Components | Browser inspection | ⏳ | +| Integration | ZoekenBlock | Manual smoke test | ⏳ | + +### Manual Test Checklist + +**Block States:** +- [ ] BlockLoading toont spinner + tekst +- [ ] BlockError toont error + retry knop werkt +- [ ] BlockEmpty toont icon + message + action werkt + +**Block Layout:** +- [ ] BlockSection toont icon in juiste kleur +- [ ] BlockSection toont count badge +- [ ] BlockItem is klikbaar met hover state +- [ ] BlockItem toont badge in juiste kleur +- [ ] BlockFooter toont beide acties +- [ ] BlockFooter primary loading state werkt + +**Hooks:** +- [ ] useBlockData fetcht data correct +- [ ] useBlockData toont loading state +- [ ] useBlockData toont error bij failure +- [ ] useBlockData refetch werkt +- [ ] useIntakeContext leest activePatient +- [ ] useIntakeContext leest prefill data + +**Integration:** +- [ ] Alle exports werken via index.ts +- [ ] ZoekenBlock werkt nog na changes +- [ ] Geen TypeScript errors +- [ ] Geen console errors + +--- + +## 8. Definition of Done + +Een story is **DONE** wanneer: +- [ ] Code is geschreven volgens FO/TO spec +- [ ] TypeScript compileert zonder errors +- [ ] ESLint toont geen errors +- [ ] Component/hook werkt in browser +- [ ] Props zijn gedocumenteerd met JSDoc +- [ ] Export is toegevoegd aan index.ts + +Het **project** is **DONE** wanneer: +- [ ] Alle 10 stories zijn DONE +- [ ] Manual test checklist is 100% ✅ +- [ ] ZoekenBlock smoke test passed +- [ ] `pnpm build` slaagt + +--- + +## 9. Risico's & Mitigatie + +| Risico | Kans | Impact | Mitigatie | +|--------|------|--------|-----------| +| Breaking changes bestaande blocks | Middel | Hoog | Backward compatible exports, smoke test | +| Performance issues hooks | Laag | Middel | useMemo, useCallback waar nodig | +| Inconsistente styling | Laag | Laag | Tailwind design tokens hergebruiken | +| Scope creep (extra features) | Middel | Middel | Strict aan FO/TO houden, YAGNI | + +--- + +## 10. Dependencies Check + +**Bestaande dependencies (geen installatie nodig):** +- ✅ `lucide-react` - Icons +- ✅ `@/components/ui/button` - shadcn Button +- ✅ `@/lib/utils` - cn() helper +- ✅ `@/lib/cortex/error-handler` - safeFetch, getErrorInfo +- ✅ `@/stores/cortex-store` - useCortexStore +- ✅ `@/hooks/use-toast` - Toast notifications + +**Geen nieuwe dependencies nodig** ✅ + +--- + +## 11. Referenties + +**Project Documents:** +- `docs/intent/intake-intent-proces/fo-to-cortex-shared-components.md` - FO/TO +- `docs/intent/intake-intent-proces/intake-process-intents.md` - Intents +- `docs/intent/intake-intent-proces/gap-analyse-intake-cortex.md` - Gap analyse +- `docs/intent/intake-intent-proces/block-template-pattern.md` - Patterns + +**Bestaande Code (referentie):** +- `components/cortex/shared/patient-list-item.tsx` - Pattern voorbeeld +- `components/cortex/blocks/zoeken-block.tsx` - Block voorbeeld +- `lib/cortex/hooks/use-patient-search.ts` - Hook voorbeeld + +--- + +## 12. Glossary + +| Term | Betekenis | +|------|-----------| +| Block | UI component in Cortex canvas-area | +| Shared Component | Herbruikbare UI component voor blocks | +| Hook | React custom hook voor logic/state | +| Prefill | Data meegegeven aan block bij openen | +| Intent | Gebruikers intentie (spraak/tekst) | + +--- + +**Versiehistorie:** + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 03-02-2026 | Colin Lit | Initiële versie | diff --git a/docs/intent/intake-intent-proces/fo-to-cortex-shared-components.md b/docs/intent/intake-intent-proces/fo-to-cortex-shared-components.md new file mode 100644 index 0000000..9aee35f --- /dev/null +++ b/docs/intent/intake-intent-proces/fo-to-cortex-shared-components.md @@ -0,0 +1,1087 @@ +# Functioneel & Technisch Ontwerp: Cortex Shared Components + +**Projectnaam:** Cortex Intake Intent System - Shared Components +**Versie:** v1.0 +**Datum:** 03-02-2026 +**Auteur:** Colin Lit (met AI-assistentie) + +--- + +## 1. Doel en Context + +### 1.1 Doel van dit document + +Dit gecombineerde FO/TO beschrijft de **shared components** die worden hergebruikt door alle Cortex Blocks. Het document combineert: +- **Functioneel (FO):** Wat de gebruiker ziet en ervaart +- **Technisch (TO):** Hoe de componenten worden gebouwd + +### 1.2 Relatie met andere documenten + +| Document | Relatie | +|----------|---------| +| `intake-process-intents.md` | Beschrijft de intents die deze components gebruiken | +| `gap-analyse-intake-cortex.md` | Identificeert welke components ontbreken | +| `block-template-pattern.md` | Beschrijft het patroon waarop deze components gebaseerd zijn | + +### 1.3 Scope + +**In scope:** +- 4 nieuwe shared components voor Cortex Blocks +- 2 nieuwe custom hooks voor data fetching en context + +**Buiten scope:** +- Individuele intake blocks (apart document) +- Bestaande components (`patient-list-item.tsx`, `linked-evidence.tsx`) + +--- + +## 2. Overzicht Componenten + +### 2.1 Component Hiërarchie + +``` +components/cortex/ +├── blocks/ +│ └── [intake-block].tsx ← Gebruikt shared components +│ +├── shared/ +│ ├── patient-list-item.tsx ✅ Bestaat +│ ├── linked-evidence.tsx ✅ Bestaat +│ │ +│ ├── block-states.tsx 🆕 NIEUW (dit document) +│ ├── block-section.tsx 🆕 NIEUW (dit document) +│ ├── block-item.tsx 🆕 NIEUW (dit document) +│ └── block-footer.tsx 🆕 NIEUW (dit document) + +lib/cortex/hooks/ +├── use-patient-search.ts ✅ Bestaat +├── use-patient-selection.ts ✅ Bestaat +│ +├── use-block-data.ts 🆕 NIEUW (dit document) +└── use-intake-context.ts 🆕 NIEUW (dit document) +``` + +### 2.2 Component Overzicht + +| Component | Type | Functie | Gebruikt door | +|-----------|------|---------|---------------| +| `BlockLoading` | State | Laad-indicator tijdens data fetch | Alle blocks | +| `BlockError` | State | Foutmelding met retry optie | Alle blocks | +| `BlockEmpty` | State | Lege staat met actie | Alle blocks | +| `BlockSection` | Layout | Sectie met icon en titel | Query blocks | +| `BlockItem` | Layout | Lijst-item met badge | Query blocks | +| `BlockFooter` | Layout | Footer met acties | Alle blocks | + +--- + +## 3. User Stories + +### 3.1 Algemene User Stories + +| ID | Rol | Doel / Actie | Verwachte waarde | Prioriteit | +|----|-----|--------------|------------------|------------| +| US-SC-01 | Gebruiker | Zien dat data wordt geladen | Weet dat systeem bezig is | Hoog | +| US-SC-02 | Gebruiker | Foutmelding zien bij problemen | Kan retry doen of hulp zoeken | Hoog | +| US-SC-03 | Gebruiker | Lege staat zien met suggestie | Weet wat te doen als geen data | Middel | +| US-SC-04 | Gebruiker | Snel navigeren naar EPD | Kan details bekijken in dossier | Hoog | +| US-SC-05 | Gebruiker | Risico niveau direct zien | Beoordeelt urgentie in één oogopslag | Hoog | + +### 3.2 Developer User Stories + +| ID | Rol | Doel / Actie | Verwachte waarde | Prioriteit | +|----|-----|--------------|------------------|------------| +| US-DEV-01 | Developer | Consistente states hergebruiken | Sneller nieuwe blocks bouwen | Hoog | +| US-DEV-02 | Developer | Generieke data hook gebruiken | Minder boilerplate code | Hoog | +| US-DEV-03 | Developer | Intake context automatisch hebben | Geen prop drilling nodig | Middel | + +--- + +## 4. Functionele Specificaties per Component + +### 4.1 BlockLoading + +**Doel:** Toon visuele feedback tijdens het laden van data. + +**Functioneel gedrag:** +- Toont geanimeerde spinner (rotatie) +- Toont optionele tekst (bijv. "Risico's laden...") +- Centreert verticaal en horizontaal in block +- Verdwijnt zodra data geladen is + +**Visueel:** +``` +┌─────────────────────────────────────┐ +│ │ +│ │ +│ [◌ spinner] │ +│ Risico's laden... │ +│ │ +│ │ +└─────────────────────────────────────┘ +``` + +**Props:** +| Prop | Type | Default | Beschrijving | +|------|------|---------|--------------| +| `message` | `string` | `"Laden..."` | Tekst onder spinner | + +--- + +### 4.2 BlockError + +**Doel:** Toon foutmelding met optie om opnieuw te proberen. + +**Functioneel gedrag:** +- Toont rood/oranje waarschuwingsicoon +- Toont foutmelding (gebruikersvriendelijk, Nederlands) +- Optionele "Opnieuw proberen" knop +- Verdwijnt bij retry of bij sluiten block + +**Visueel:** +``` +┌─────────────────────────────────────┐ +│ │ +│ [⚠️ icon] │ +│ │ +│ Kon risico's niet ophalen. │ +│ Controleer je internetverbinding. │ +│ │ +│ [🔄 Opnieuw proberen] │ +│ │ +└─────────────────────────────────────┘ +``` + +**Props:** +| Prop | Type | Default | Beschrijving | +|------|------|---------|--------------| +| `message` | `string` | - | Foutmelding tekst | +| `onRetry` | `() => void` | `undefined` | Callback voor retry (toont knop indien aanwezig) | + +--- + +### 4.3 BlockEmpty + +**Doel:** Toon informatieve lege staat met optionele actie. + +**Functioneel gedrag:** +- Toont context-relevant icoon (configureerbaar) +- Toont melding waarom leeg +- Optionele actie-knop (bijv. "Toevoegen in dossier") +- Visueel subtiel (niet alarmerend) + +**Visueel:** +``` +┌─────────────────────────────────────┐ +│ │ +│ [📋 icon faded] │ +│ │ +│ Geen risico's geregistreerd │ +│ │ +│ [+ Toevoegen in dossier] │ +│ │ +└─────────────────────────────────────┘ +``` + +**Props:** +| Prop | Type | Default | Beschrijving | +|------|------|---------|--------------| +| `icon` | `LucideIcon` | - | Icoon component | +| `message` | `string` | - | Hoofdboodschap | +| `action` | `{ label: string; onClick: () => void }` | `undefined` | Optionele actie knop | + +--- + +### 4.4 BlockSection + +**Doel:** Groepeer gerelateerde content met visuele header. + +**Functioneel gedrag:** +- Toont icoon + titel + optionele count +- Bevat children content +- Consistente styling (witte achtergrond, border, padding) +- Optioneel inklapbaar (v2) + +**Visueel:** +``` +┌─────────────────────────────────────┐ +│ [⚠️] Risicotaxatie (3) │ ← Header +├─────────────────────────────────────┤ +│ │ +│ [Content / children] │ ← Children +│ │ +└─────────────────────────────────────┘ +``` + +**Props:** +| Prop | Type | Default | Beschrijving | +|------|------|---------|--------------| +| `icon` | `LucideIcon` | - | Icoon component | +| `iconColor` | `string` | `"text-slate-600"` | Tailwind kleur class | +| `title` | `string` | - | Sectie titel | +| `count` | `number` | `undefined` | Optionele count badge | +| `children` | `ReactNode` | - | Sectie inhoud | + +--- + +### 4.5 BlockItem + +**Doel:** Toon een enkel item in een lijst met consistente styling. + +**Functioneel gedrag:** +- Toont titel + optionele subtitle +- Toont status badge (kleur-gecodeerd) +- Klikbaar indien `onClick` meegegeven +- Hover state bij klikbaar +- Geen hover bij niet-klikbaar + +**Badge varianten:** +| Variant | Kleur | Gebruik | +|---------|-------|---------| +| `default` | Grijs | Neutraal, geen status | +| `success` | Groen | Laag risico, compleet | +| `warning` | Oranje | Gemiddeld risico, aandacht | +| `danger` | Rood | Hoog risico, urgent | + +**Visueel:** +``` +┌─────────────────────────────────────────────────────┐ +│ [Avatar?] Suïcidaliteit [Matig] │ ← Klikbaar +│ 12 jan 2026 • Dr. Jansen │ +└─────────────────────────────────────────────────────┘ +``` + +**Props:** +| Prop | Type | Default | Beschrijving | +|------|------|---------|--------------| +| `title` | `string` | - | Hoofdtekst | +| `subtitle` | `string` | `undefined` | Subtekst (datum, auteur, etc.) | +| `badge` | `{ label: string; variant: BadgeVariant }` | `undefined` | Status badge | +| `onClick` | `() => void` | `undefined` | Klik handler (maakt item klikbaar) | + +--- + +### 4.6 BlockFooter + +**Doel:** Consistente footer met acties onderaan een block. + +**Functioneel gedrag:** +- Links: secundaire actie (ghost button) +- Rechts: primaire actie (solid button) +- Horizontale scheidingslijn boven footer +- Loading state op primaire actie + +**Visueel:** +``` +───────────────────────────────────────── +[🔗 Bekijk in dossier] [+ Toevoegen] + (secundair) (primair) +``` + +**Props:** +| Prop | Type | Default | Beschrijving | +|------|------|---------|--------------| +| `secondaryAction` | `{ label, icon?, onClick }` | `undefined` | Linker actie | +| `primaryAction` | `{ label, icon?, onClick, loading? }` | `undefined` | Rechter actie | + +--- + +## 5. UI Overzicht + +### 5.1 Component Compositie in een Block + +``` +┌─────────────────────────────────────────────────────────────┐ +│ [Icon] Risicotaxatie [X Close] │ ← BlockContainer +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌───────────────────────────────────────────────────────┐ │ +│ │ [⚠️] Huidige risico's (3) │ │ ← BlockSection +│ ├───────────────────────────────────────────────────────┤ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────────────────┐ │ │ +│ │ │ Suïcidaliteit [Matig] │ │ │ ← BlockItem +│ │ │ 12 jan 2026 │ │ │ +│ │ └─────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────────────────┐ │ │ +│ │ │ Agressie [Laag] │ │ │ ← BlockItem +│ │ │ 10 jan 2026 │ │ │ +│ │ └─────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────────┘ │ +│ │ +│ ───────────────────────────────────────────────────────── │ +│ [🔗 Bekijk in dossier] [+ Toevoegen] │ ← BlockFooter +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 5.2 State Flows + +``` +Block Lifecycle: + + ┌─────────┐ data? ┌─────────┐ + │ Loading │───────────▶│ Data │ + └─────────┘ yes └─────────┘ + │ │ + │ error │ empty + ▼ ▼ + ┌─────────┐ ┌─────────┐ + │ Error │ │ Empty │ + └─────────┘ └─────────┘ + │ │ + │ retry │ action + ▼ ▼ + ┌─────────┐ ┌─────────┐ + │ Loading │ │ EPD │ + └─────────┘ │ Navigatie│ + └─────────┘ +``` + +--- + +## 6. Technische Architectuur + +### 6.1 Component Structuur + +``` +components/cortex/shared/ +├── block-states.tsx # BlockLoading, BlockError, BlockEmpty +├── block-section.tsx # BlockSection +├── block-item.tsx # BlockItem +├── block-footer.tsx # BlockFooter +└── index.ts # Re-exports + +lib/cortex/hooks/ +├── use-block-data.ts # Generic data fetching hook +└── use-intake-context.ts # Intake context hook +``` + +### 6.2 Dependencies + +``` +Externe dependencies: +├── lucide-react # Icons +├── framer-motion # Animaties (optioneel) +└── @/components/ui/button # shadcn Button component + +Interne dependencies: +├── @/lib/utils # cn() helper +├── @/lib/cortex/error-handler # safeFetch, getErrorInfo +├── @/stores/cortex-store # useCortexStore +└── @/hooks/use-toast # Toast notifications +``` + +--- + +## 7. Technische Specificaties + +### 7.1 block-states.tsx + +```typescript +// components/cortex/shared/block-states.tsx + +'use client'; + +import { Loader2, AlertCircle, RefreshCw } from 'lucide-react'; +import type { LucideIcon } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + +// ============================================================================ +// BlockLoading +// ============================================================================ + +interface BlockLoadingProps { + /** Tekst onder de spinner */ + message?: string; + /** Extra CSS classes */ + className?: string; +} + +export function BlockLoading({ + message = 'Laden...', + className +}: BlockLoadingProps) { + return ( +
+ + {message} +
+ ); +} + +// ============================================================================ +// BlockError +// ============================================================================ + +interface BlockErrorProps { + /** Foutmelding tekst */ + message: string; + /** Callback voor retry knop (toont knop indien aanwezig) */ + onRetry?: () => void; + /** Extra CSS classes */ + className?: string; +} + +export function BlockError({ + message, + onRetry, + className +}: BlockErrorProps) { + return ( +
+ +

{message}

+ {onRetry && ( + + )} +
+ ); +} + +// ============================================================================ +// BlockEmpty +// ============================================================================ + +interface BlockEmptyProps { + /** Icoon component */ + icon: LucideIcon; + /** Hoofdboodschap */ + message: string; + /** Optionele actie knop */ + action?: { + label: string; + onClick: () => void; + }; + /** Extra CSS classes */ + className?: string; +} + +export function BlockEmpty({ + icon: Icon, + message, + action, + className +}: BlockEmptyProps) { + return ( +
+ +

{message}

+ {action && ( + + )} +
+ ); +} +``` + +### 7.2 block-section.tsx + +```typescript +// components/cortex/shared/block-section.tsx + +'use client'; + +import type { ReactNode } from 'react'; +import type { LucideIcon } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface BlockSectionProps { + /** Icoon component */ + icon: LucideIcon; + /** Tailwind kleur class voor icoon */ + iconColor?: string; + /** Sectie titel */ + title: string; + /** Optionele count badge */ + count?: number; + /** Sectie inhoud */ + children: ReactNode; + /** Extra CSS classes */ + className?: string; +} + +export function BlockSection({ + icon: Icon, + iconColor = 'text-slate-600', + title, + count, + children, + className, +}: BlockSectionProps) { + return ( +
+ {/* Header */} +
+ +

{title}

+ {count !== undefined && ( + ({count}) + )} +
+ + {/* Content */} + {children} +
+ ); +} +``` + +### 7.3 block-item.tsx + +```typescript +// components/cortex/shared/block-item.tsx + +'use client'; + +import { cn } from '@/lib/utils'; + +// ============================================================================ +// Types +// ============================================================================ + +export type BadgeVariant = 'default' | 'success' | 'warning' | 'danger'; + +interface BlockItemProps { + /** Hoofdtekst */ + title: string; + /** Subtekst (datum, auteur, etc.) */ + subtitle?: string; + /** Status badge */ + badge?: { + label: string; + variant: BadgeVariant; + }; + /** Klik handler (maakt item klikbaar) */ + onClick?: () => void; + /** Extra CSS classes */ + className?: string; +} + +// ============================================================================ +// Styling +// ============================================================================ + +const BADGE_STYLES: Record = { + default: 'bg-slate-100 text-slate-700 border-slate-200', + success: 'bg-green-50 text-green-700 border-green-200', + warning: 'bg-amber-50 text-amber-700 border-amber-200', + danger: 'bg-red-50 text-red-700 border-red-200', +}; + +// ============================================================================ +// Component +// ============================================================================ + +export function BlockItem({ + title, + subtitle, + badge, + onClick, + className +}: BlockItemProps) { + const isClickable = Boolean(onClick); + const Component = isClickable ? 'button' : 'div'; + + return ( + + {/* Content */} +
+

+ {title} +

+ {subtitle && ( +

+ {subtitle} +

+ )} +
+ + {/* Badge */} + {badge && ( + + {badge.label} + + )} +
+ ); +} + +// ============================================================================ +// Helper: Get variant from risk level +// ============================================================================ + +export function getRiskBadgeVariant(level: string): BadgeVariant { + switch (level.toLowerCase()) { + case 'laag': + return 'success'; + case 'gemiddeld': + return 'warning'; + case 'hoog': + case 'zeer_hoog': + return 'danger'; + default: + return 'default'; + } +} +``` + +### 7.4 block-footer.tsx + +```typescript +// components/cortex/shared/block-footer.tsx + +'use client'; + +import type { LucideIcon } from 'lucide-react'; +import { Loader2 } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + +interface ActionConfig { + /** Knop label */ + label: string; + /** Optioneel icoon */ + icon?: LucideIcon; + /** Click handler */ + onClick: () => void; + /** Loading state (alleen voor primary) */ + loading?: boolean; + /** Disabled state */ + disabled?: boolean; +} + +interface BlockFooterProps { + /** Linker actie (ghost button) */ + secondaryAction?: ActionConfig; + /** Rechter actie (solid button) */ + primaryAction?: ActionConfig; + /** Extra CSS classes */ + className?: string; +} + +export function BlockFooter({ + secondaryAction, + primaryAction, + className +}: BlockFooterProps) { + // Don't render if no actions + if (!secondaryAction && !primaryAction) { + return null; + } + + return ( +
+ {/* Secondary Action (left) */} + {secondaryAction ? ( + + ) : ( +
// Spacer + )} + + {/* Primary Action (right) */} + {primaryAction && ( + + )} +
+ ); +} +``` + +### 7.5 use-block-data.ts + +```typescript +// lib/cortex/hooks/use-block-data.ts + +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { useToast } from '@/hooks/use-toast'; +import { safeFetch, getErrorInfo } from '@/lib/cortex/error-handler'; + +// ============================================================================ +// Types +// ============================================================================ + +interface UseBlockDataOptions { + /** API endpoint (relatief pad) */ + endpoint: string; + /** Query parameters */ + params?: Record; + /** Of data moet worden opgehaald */ + enabled?: boolean; + /** Callback bij error */ + onError?: (error: Error) => void; + /** Operatie naam voor error messages */ + operationName?: string; +} + +interface UseBlockDataResult { + /** Opgehaalde data */ + data: T | null; + /** Loading state */ + isLoading: boolean; + /** Error message */ + error: string | null; + /** Refetch functie */ + refetch: () => Promise; +} + +// ============================================================================ +// Hook +// ============================================================================ + +export function useBlockData({ + endpoint, + params, + enabled = true, + onError, + operationName = 'Data laden', +}: UseBlockDataOptions): UseBlockDataResult { + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(enabled); + const [error, setError] = useState(null); + const { toast } = useToast(); + + const fetchData = useCallback(async () => { + if (!enabled) return; + + setIsLoading(true); + setError(null); + + try { + // Build URL with params + const url = new URL(endpoint, window.location.origin); + if (params) { + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined) { + url.searchParams.set(key, value); + } + }); + } + + const response = await safeFetch( + url.toString(), + undefined, + { operation: operationName } + ); + + const result = await response.json(); + setData(result); + } catch (err) { + const errorInfo = getErrorInfo(err, { operation: operationName }); + setError(errorInfo.description); + onError?.(err as Error); + + toast({ + variant: 'destructive', + title: errorInfo.title, + description: errorInfo.description, + }); + } finally { + setIsLoading(false); + } + }, [endpoint, JSON.stringify(params), enabled, onError, operationName, toast]); + + useEffect(() => { + if (enabled) { + fetchData(); + } + }, [fetchData, enabled]); + + return { data, isLoading, error, refetch: fetchData }; +} +``` + +### 7.6 use-intake-context.ts + +```typescript +// lib/cortex/hooks/use-intake-context.ts + +'use client'; + +import { useMemo } from 'react'; +import { useCortexStore } from '@/stores/cortex-store'; +import type { BlockPrefillData } from '@/stores/cortex-store'; +import { formatPatientName } from '@/lib/fhir/patient-mapper'; + +// ============================================================================ +// Types +// ============================================================================ + +interface UseIntakeContextResult { + /** Patient ID (van prefill of activePatient) */ + patientId: string | null; + /** Intake ID (van prefill) */ + intakeId: string | null; + /** Patient naam (voor display) */ + patientName: string | null; + /** Of er voldoende context is */ + hasPatientContext: boolean; + /** Of er intake context is */ + hasIntakeContext: boolean; +} + +// ============================================================================ +// Hook +// ============================================================================ + +export function useIntakeContext( + prefill?: BlockPrefillData +): UseIntakeContextResult { + const { activePatient } = useCortexStore(); + + return useMemo(() => { + // Patient context: van prefill of activePatient + const patientId = prefill?.patientId || activePatient?.id || null; + const patientName = prefill?.patientName || + (activePatient ? formatPatientName(activePatient) : null); + + // Intake context: alleen van prefill (TODO: activeIntake in store) + const intakeId = prefill?.intakeId || null; + + return { + patientId, + intakeId, + patientName, + hasPatientContext: Boolean(patientId), + hasIntakeContext: Boolean(intakeId), + }; + }, [prefill, activePatient]); +} +``` + +### 7.7 Index Export + +```typescript +// components/cortex/shared/index.ts + +// Block States +export { BlockLoading, BlockError, BlockEmpty } from './block-states'; + +// Block Layout +export { BlockSection } from './block-section'; +export { BlockItem, getRiskBadgeVariant, type BadgeVariant } from './block-item'; +export { BlockFooter } from './block-footer'; + +// Existing +export { PatientListItem, PatientListEmpty, PatientListLoading } from './patient-list-item'; +export { LinkedEvidence } from './linked-evidence'; +``` + +--- + +## 8. API Ontwerp + +### 8.1 Geen nieuwe API endpoints + +De shared components maken gebruik van bestaande endpoints en de nieuwe intake endpoints (beschreven in apart document). De hooks abstraheren de API calls. + +### 8.2 Error Handling + +Alle API errors worden afgehandeld via de bestaande `error-handler.ts`: + +```typescript +// Voorbeeld error flow +try { + const response = await safeFetch('/api/cortex/intake/123/risks'); + // ... +} catch (err) { + const errorInfo = getErrorInfo(err, { operation: 'Risico's laden' }); + // errorInfo.title = "Laden mislukt" + // errorInfo.description = "Kon risico's niet ophalen. Probeer opnieuw." +} +``` + +--- + +## 9. Gebruikersrollen en Rechten + +De shared components zijn UI-only en hebben geen eigen rechtenmodel. Rechten worden afgedwongen door: +1. **API routes** - RLS policies in Supabase +2. **Store** - `activePatient` alleen beschikbaar na selectie +3. **Blocks** - Tonen alleen data waartoe gebruiker toegang heeft + +--- + +## 10. Performance & Accessibility + +### 10.1 Performance + +| Aspect | Target | Implementatie | +|--------|--------|---------------| +| Render tijd | < 16ms | Geen zware berekeningen in render | +| Bundle size | < 5KB per component | Tree-shakeable exports | +| Re-renders | Minimaal | `useMemo` voor derived state | + +### 10.2 Accessibility + +| Aspect | Implementatie | +|--------|---------------| +| Screen readers | `aria-label` op interactieve elementen | +| Keyboard nav | `button` elements met `onClick` | +| Focus visible | Tailwind `focus-visible:ring` | +| Color contrast | WCAG AA compliant kleuren | +| Loading state | `aria-busy="true"` | + +--- + +## 11. Testing Strategie + +### 11.1 Unit Tests + +```typescript +// __tests__/components/cortex/shared/block-item.test.tsx + +describe('BlockItem', () => { + it('renders title and subtitle', () => { + render(); + expect(screen.getByText('Test')).toBeInTheDocument(); + expect(screen.getByText('Sub')).toBeInTheDocument(); + }); + + it('renders badge with correct variant', () => { + render(); + const badge = screen.getByText('Hoog'); + expect(badge).toHaveClass('bg-red-50'); + }); + + it('is clickable when onClick provided', () => { + const onClick = jest.fn(); + render(); + fireEvent.click(screen.getByRole('button')); + expect(onClick).toHaveBeenCalled(); + }); + + it('is not clickable when onClick not provided', () => { + render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); +}); +``` + +### 11.2 Visual Tests + +Storybook stories voor elke component variant: + +```typescript +// stories/BlockItem.stories.tsx + +export default { + title: 'Cortex/Shared/BlockItem', + component: BlockItem, +}; + +export const Default = () => ; +export const WithSubtitle = () => ; +export const WithBadge = () => ; +export const Clickable = () => alert('Clicked!')} />; +``` + +--- + +## 12. Implementatie Volgorde + +| # | Component | Effort | Dependencies | +|---|-----------|--------|--------------| +| 1 | `block-states.tsx` | S | Geen | +| 2 | `block-section.tsx` | S | Geen | +| 3 | `block-item.tsx` | S | Geen | +| 4 | `block-footer.tsx` | S | `@/components/ui/button` | +| 5 | `use-block-data.ts` | M | `error-handler.ts` | +| 6 | `use-intake-context.ts` | S | `cortex-store` | +| 7 | `index.ts` exports | S | 1-6 | + +**Totaal geschat:** ~1 dag + +--- + +## 13. Risico's en Mitigatie + +| Risico | Impact | Mitigatie | +|--------|--------|-----------| +| Inconsistente styling | Middel | Design tokens in Tailwind config | +| Over-engineering | Laag | YAGNI - alleen bouwen wat nodig is | +| Bundle bloat | Laag | Tree-shakeable exports | +| Breaking changes | Middel | Semantic versioning, deprecation warnings | + +--- + +## 14. Bijlagen & Referenties + +**Projectdocumenten:** +- `docs/intent/intake-intent-proces/intake-process-intents.md` - Intent specificaties +- `docs/intent/intake-intent-proces/gap-analyse-intake-cortex.md` - Gap analyse +- `docs/intent/intake-intent-proces/block-template-pattern.md` - Template patterns + +**Bestaande code:** +- `components/cortex/shared/patient-list-item.tsx` - Referentie voor patterns +- `components/cortex/blocks/block-container.tsx` - Parent container +- `lib/cortex/error-handler.ts` - Error handling utilities + +**Tech documentatie:** +- Lucide Icons: https://lucide.dev/icons +- Tailwind CSS: https://tailwindcss.com/docs +- shadcn/ui Button: https://ui.shadcn.com/docs/components/button diff --git a/docs/intent/intake-intent-proces/gap-analyse-intake-cortex.md b/docs/intent/intake-intent-proces/gap-analyse-intake-cortex.md new file mode 100644 index 0000000..ed5ddd8 --- /dev/null +++ b/docs/intent/intake-intent-proces/gap-analyse-intake-cortex.md @@ -0,0 +1,379 @@ +# Gap Analyse: Intake Proces × Cortex + +**Datum:** 03-02-2026 +**Status:** Analyse +**Bron:** `docs/intent/intake-process-intents.md` + +--- + +## 1. Executive Summary + +| Laag | Status | Conclusie | +|------|--------|-----------| +| **EPD Backend** | ✅ 90% compleet | Server actions, database, API's zijn uitgebreid aanwezig | +| **EPD UI** | ✅ 95% compleet | Alle 9 intake tabs + screening hebben werkende componenten | +| **Cortex Intents** | ❌ 0% | Geen van de 26 intake intents bestaat | +| **Cortex Blocks** | ❌ 0% | Geen intake-specifieke blocks | +| **Cortex API** | ❌ 0% | Geen /api/cortex/intake/* routes | + +**Conclusie:** De EPD-functionaliteit is robuust gebouwd. De koppeling met Cortex ontbreekt volledig. + +--- + +## 2. Wat Bestaat (✅) + +### 2.1 Cortex Core (7 intents) + +```typescript +// lib/cortex/types.ts +type CortexIntent = + | 'dagnotitie' // ✅ Block + patterns + | 'zoeken' // ✅ Block + patterns + | 'overdracht' // ✅ Block + patterns + | 'agenda_query' // ✅ Patterns (geen dedicated block) + | 'create_appointment' // ✅ Patterns + | 'cancel_appointment' // ✅ Patterns + | 'reschedule_appointment'// ✅ Patterns + | 'unknown'; +``` + +### 2.2 Cortex Blocks (6 stuks) + +| Block | Pad | Functie | +|-------|-----|---------| +| `dagnotitie-block.tsx` | `components/cortex/blocks/` | Notitie invoer | +| `zoeken-block.tsx` | `components/cortex/blocks/` | Patiënt zoeken | +| `overdracht-block.tsx` | `components/cortex/blocks/` | Overdracht samenvatting | +| `patient-dashboard-block.tsx` | `components/cortex/blocks/` | Patiënt overzicht | +| `patient-context-card.tsx` | `components/cortex/blocks/` | Context weergave | +| `fallback-picker.tsx` | `components/cortex/blocks/` | Unknown intent handler | + +### 2.3 Cortex API Routes + +| Route | Methode | Functie | +|-------|---------|---------| +| `/api/cortex/classify` | POST | Intent classificatie | +| `/api/cortex/chat` | POST | Streaming chat | +| `/api/cortex/context` | GET | Context ophalen | +| `/api/cortex/agenda` | GET | Agenda query | +| `/api/cortex/agenda/create` | POST | Afspraak maken | +| `/api/cortex/agenda/cancel` | POST | Afspraak annuleren | +| `/api/cortex/agenda/reschedule` | POST | Afspraak verzetten | +| `/api/cortex/patients/search` | GET | Patiënt zoeken | + +### 2.4 EPD Screening (Volledig) + +**UI Componenten:** +| Component | Status | Functie | +|-----------|--------|---------| +| `HelpRequestCard` | ✅ | Hulpvraag invoer (textarea) | +| `DecisionCard` | ✅ | Besluit: geschikt/niet_geschikt | +| `DocumentCard` | ✅ | Document upload | +| `ActivityLog` | ✅ | Activiteiten timeline | + +**Server Actions:** +| Action | Status | Functie | +|--------|--------|---------| +| `getScreeningSummary()` | ✅ | Screening data ophalen | +| `saveHelpRequest()` | ✅ | Hulpvraag opslaan | +| `saveScreeningDecision()` | ✅ | Besluit opslaan | +| `addScreeningActivity()` | ✅ | Activiteit toevoegen | + +### 2.5 EPD Intake (Volledig) + +**9 Tabs met UI + Actions:** + +| Tab | UI Component | GET Action | CREATE Action | +|-----|--------------|------------|---------------| +| Algemeen | `IntakePage` | `getIntakeById()` | `createIntake()` | +| Contactmomenten | `ContactManager` | `getContactMoments()` | `createContactMoment()` | +| Kindcheck | `KindcheckForm` | `getKindcheck()` | `saveKindcheck()` | +| Risicotaxatie | `RiskManager` | `getRiskAssessments()` | `createRiskAssessment()` | +| Anamnese | `AnamneseManager` | `getAnamneses()` | `createAnamnese()` | +| Onderzoeken | `ExaminationManager` | `getExaminations()` | `createExamination()` | +| ROM | `ExaminationManager` | `getExaminations()` | `createExamination()` | +| Diagnose | `DiagnosisManager` | `getDiagnoses()` | `createDiagnosis()` | +| Behandeladvies | `TreatmentAdviceForm` | `getTreatmentAdvice()` | `saveTreatmentAdvice()` | + +--- + +## 3. Wat Ontbreekt (❌) + +### 3.1 Intents (26 ontbreken) + +Geen van de intake-gerelateerde intents uit `intake-process-intents.md` bestaat: + +#### Screening Intents (4) +| Intent | Type | Patterns nodig | +|--------|------|----------------| +| `screening_query` | Query | `^(toon\s+)?screening`, `wat is de hulpvraag` | +| `hulpvraag_invoer` | Actie | `hulpvraag:`, `noteer hulpvraag` | +| `screening_besluit` | Actie | `geschikt voor behandeling`, `niet geschikt` | +| `screening_activiteit` | Actie | `gebeld met`, `verwijsbrief ontvangen` | + +#### Intake Start Intents (2) +| Intent | Type | Patterns nodig | +|--------|------|----------------| +| `intake_starten` | Actie | `start intake`, `nieuwe intake` | +| `intake_lijst` | Query | `toon intakes`, `welke intakes` | + +#### Intake Navigatie (3) +| Intent | Type | Patterns nodig | +|--------|------|----------------| +| `intake_navigeer` | Navigatie | `ga naar risico`, `open kindcheck` | +| `intake_volgende` | Navigatie | `volgende stap`, `ga verder` | +| `intake_vorige` | Navigatie | `vorige`, `terug` | + +#### Intake Tab Intents (14) +| Intent | Type | Tab | +|--------|------|-----| +| `contact_toevoegen` | Actie | Contactmomenten | +| `contacten_query` | Query | Contactmomenten | +| `kindcheck_invullen` | Actie | Kindcheck | +| `kindcheck_query` | Query | Kindcheck | +| `risico_toevoegen` | Actie | Risicotaxatie | +| `risico_query` | Query | Risicotaxatie | +| `anamnese_toevoegen` | Actie | Anamnese | +| `anamnese_query` | Query | Anamnese | +| `onderzoek_toevoegen` | Actie | Onderzoeken | +| `onderzoeken_query` | Query | Onderzoeken | +| `rom_toevoegen` | Actie | ROM | +| `rom_query` | Query | ROM | +| `diagnose_toevoegen` | Actie | Diagnose | +| `diagnose_query` | Query | Diagnose | +| `behandeladvies_invoer` | Actie | Behandeladvies | +| `behandeladvies_query` | Query | Behandeladvies | + +#### Intake Afsluiten (3) +| Intent | Type | Patterns nodig | +|--------|------|----------------| +| `intake_afsluiten` | Actie | `sluit intake af`, `intake afronden` | +| `intake_samenvatting` | Query | `samenvatting intake` | +| `intake_checklist` | Query | `wat moet ik nog doen`, `is intake compleet` | + +### 3.2 Cortex Blocks (9 ontbreken) + +| Block | Doel | Data bron | +|-------|------|-----------| +| `ScreeningBlock` | Screening overzicht | `getScreeningSummary()` | +| `IntakeListBlock` | Lijst intakes van patiënt | `getIntakesByPatientId()` | +| `IntakeStatusBlock` | Voortgang/checklist | Nieuwe functie nodig | +| `KindcheckBlock` | Kindcheck samenvatting | `getKindcheck()` | +| `RisicoBlock` | Risico's met levels | `getRiskAssessments()` | +| `AnamneseBlock` | Anamnese samenvatting | `getAnamneses()` | +| `DiagnoseBlock` | Diagnoses lijst | `getDiagnoses()` | +| `BehandeladviesBlock` | Behandeladvies tonen | `getTreatmentAdvice()` | +| `IntakeSamenvattingBlock` | AI samenvatting | Nieuwe AI functie nodig | + +### 3.3 Cortex API Routes (ontbreken) + +| Route | Methode | Functie | +|-------|---------|---------| +| `/api/cortex/screening` | GET | Screening data voor Cortex | +| `/api/cortex/intake` | GET | Intake lijst | +| `/api/cortex/intake/[id]` | GET | Intake details | +| `/api/cortex/intake/[id]/status` | GET | Intake voortgang/checklist | +| `/api/cortex/intake/[id]/risks` | GET | Risico's van intake | +| `/api/cortex/intake/[id]/diagnoses` | GET | Diagnoses van intake | +| `/api/cortex/intake/[id]/summary` | GET | AI samenvatting | + +### 3.4 Reflex Patterns (ontbreken) + +In `lib/cortex/reflex-classifier.ts` moeten patterns worden toegevoegd voor alle 26 intents. + +--- + +## 4. Architectuur Gap + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ HUIDIGE SITUATIE │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ CORTEX EPD │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 7 Intents │ │ Screening UI │ │ +│ │ 6 Blocks │ ❌ │ Intake UI (9) │ │ +│ │ 8 API routes │ ─ ─ ─ ─ ─ ─ │ Server Actions │ │ +│ └─────────────────┘ GEEN LINK └─────────────────┘ │ +│ │ +├─────────────────────────────────────────────────────────────────────────┤ +│ GEWENSTE SITUATIE │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ CORTEX EPD │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 7 + 26 Intents │ │ Screening UI │ │ +│ │ 6 + 9 Blocks │ ✅ │ Intake UI (9) │ │ +│ │ 8 + 7 API routes│ ◀──────────▶ │ Server Actions │ │ +│ └─────────────────┘ GEKOPPELD └─────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 5. Implementatie Roadmap + +### Fase 1: Foundation (Query Intents) + +**Doel:** Informatie opvragen via spraak + +| # | Taak | Effort | Prioriteit | +|---|------|--------|------------| +| 1.1 | Intent types toevoegen aan `types.ts` | S | 🔴 | +| 1.2 | Patterns toevoegen aan `reflex-classifier.ts` | M | 🔴 | +| 1.3 | `IntakeStatusBlock` bouwen | M | 🔴 | +| 1.4 | `RisicoBlock` bouwen | S | 🔴 | +| 1.5 | `DiagnoseBlock` bouwen | S | 🟡 | +| 1.6 | `/api/cortex/intake/*` routes | M | 🔴 | + +**Resultaat:** "Wat zijn de risico's?", "Is de intake compleet?" werken + +### Fase 2: Navigation (Navigatie Intents) + +**Doel:** Navigeren via spraak + +| # | Taak | Effort | Prioriteit | +|---|------|--------|------------| +| 2.1 | `intake_navigeer` patterns | S | 🔴 | +| 2.2 | Navigation handler in orchestrator | M | 🔴 | +| 2.3 | Router integratie | M | 🟡 | + +**Resultaat:** "Ga naar diagnose", "Open kindcheck" werken + +### Fase 3: Actions (Actie Intents) + +**Doel:** Data invoeren via spraak + +| # | Taak | Effort | Prioriteit | +|---|------|--------|------------| +| 3.1 | `intake_starten` intent + handler | M | 🟡 | +| 3.2 | `risico_toevoegen` intent + handler | L | 🟡 | +| 3.3 | `diagnose_toevoegen` intent + handler | L | 🟡 | +| 3.4 | Confirmatie dialogen | M | 🟡 | + +**Resultaat:** "Start intake", "Risico suïcidaliteit matig" werken + +### Fase 4: Intelligence (AI Features) + +**Doel:** Slimme AI functionaliteit + +| # | Taak | Effort | Prioriteit | +|---|------|--------|------------| +| 4.1 | `IntakeSamenvattingBlock` met AI | L | 🔵 | +| 4.2 | Intake-gerelateerde nudges | M | 🔵 | +| 4.3 | Context-aware suggestions | L | 🔵 | + +**Resultaat:** "Samenvatting intake", proactieve suggesties werken + +--- + +## 6. Effort Schatting + +| Fase | Items | Effort | +|------|-------|--------| +| Fase 1: Foundation | 6 taken | ~3-4 dagen | +| Fase 2: Navigation | 3 taken | ~1-2 dagen | +| Fase 3: Actions | 4 taken | ~3-4 dagen | +| Fase 4: Intelligence | 3 taken | ~2-3 dagen | +| **Totaal** | **16 taken** | **~10-13 dagen** | + +--- + +## 7. Quick Wins + +Snel te implementeren met hoge waarde: + +### 7.1 `intake_status` Query (1 dag) + +```typescript +// Input: "Wat moet ik nog doen?" / "Is de intake compleet?" +// Output: Checklist met voltooide/openstaande tabs + +// Nieuwe functie nodig: +async function getIntakeCompletionStatus(intakeId: string) { + const intake = await getIntakeById(intakeId); + const kindcheck = await getKindcheck(intakeId); + const risks = await getRiskAssessments(intakeId); + const diagnoses = await getDiagnoses(intakeId); + // etc... + + return { + algemeen: !!intake, + kindcheck: !!kindcheck.hasChildren !== undefined, + risicotaxatie: risks.length > 0, + diagnose: diagnoses.length > 0, + // ... + }; +} +``` + +### 7.2 `risico_query` (0.5 dag) + +```typescript +// Input: "Wat zijn de risico's?" +// Output: RisicoBlock met bestaande getRiskAssessments() + +// Block template: + +``` + +### 7.3 `intake_navigeer` (0.5 dag) + +```typescript +// Input: "Ga naar diagnose" +// Output: router.push() naar juiste tab + +// Pattern matching: +{ pattern: /^ga\s+naar\s+(risico|diagnose|kindcheck|anamnese)/i, weight: 1.0 } +``` + +--- + +## 8. Dependencies + +``` +types.ts (intent definitions) + ↓ +reflex-classifier.ts (patterns) + ↓ +/api/cortex/intake/* (data routes) + ↓ +blocks/*.tsx (UI components) + ↓ +canvas-area.tsx (block rendering) +``` + +--- + +## 9. Risico's + +| Risico | Impact | Mitigatie | +|--------|--------|-----------| +| Intake context niet beschikbaar | Hoog | ActiveIntake store toevoegen | +| Meerdere intakes per patiënt | Middel | Intent clarification bij ambiguïteit | +| Performance bij grote datasets | Laag | Pagination in queries | +| Pattern overlap met bestaande intents | Middel | Goede escalation logic | + +--- + +## 10. Aanbeveling + +**Start met Fase 1 (Query Intents)** omdat: +1. Hergebruikt bestaande server actions +2. Geen destructieve acties (veilig) +3. Direct waarde voor gebruiker +4. Legt foundation voor actie intents + +**Eerste implementatie:** +1. `intake_status` - "Wat moet ik nog doen?" +2. `risico_query` - "Wat zijn de risico's?" +3. `intake_navigeer` - "Ga naar diagnose" + +Deze 3 intents dekken de belangrijkste use case: snel navigeren en informatie opvragen tijdens een intake sessie. diff --git a/docs/intent/intake-intent-proces/intake-process-image-prompts.md b/docs/intent/intake-intent-proces/intake-process-image-prompts.md new file mode 100644 index 0000000..28db38b --- /dev/null +++ b/docs/intent/intake-intent-proces/intake-process-image-prompts.md @@ -0,0 +1,234 @@ +# Text-to-Image Prompts: Intake Proces Visualisatie + +**Doel:** Prompts voor het genereren van visualisaties van het Cortex intake proces + +--- + +## 1. Overzichtsprompt (Hero Image) + +### Voor: Midjourney / DALL-E 3 + +``` +A clean, modern healthcare software interface showing an AI-powered patient intake workflow. + +The image displays a split view: +- Left side: A vertical process flow with connected nodes showing stages: "Screening" → "Intake Start" → "Assessment" → "Diagnosis" → "Treatment Plan", each node glowing softly in teal/cyan +- Right side: A sleek dashboard interface with a voice input bar at the top showing a waveform, and card-based UI components below displaying patient information + +The design aesthetic is minimal, professional healthcare software with a white background, subtle shadows, and accent colors in teal (#0d9488) and slate gray. Small microphone icon indicates voice control. + +Style: Clean UI mockup, flat design, healthcare technology, professional, minimalist +Aspect ratio: 16:9 +``` + +--- + +## 2. Process Flow Diagram + +### Voor: Midjourney / Ideogram + +``` +A horizontal workflow diagram for a medical intake process, infographic style. + +Four connected phases flowing left to right: +1. "SCREENING" - icon of clipboard with checkmark, showing "Hulpvraag" and "Besluit" +2. "INTAKE" - icon of folder opening, showing 9 small tab icons stacked +3. "ASSESSMENT" - icon of stethoscope and brain, showing "Risico", "Diagnose", "Anamnese" +4. "BEHANDELPLAN" - icon of document with heart, showing "Doelen" and "Interventies" + +Arrows connect each phase. Below each phase, small cards show the key data points. + +Color scheme: Teal (#0d9488) for primary, soft grays for backgrounds, white cards with subtle shadows. Modern healthcare aesthetic, clean lines, professional. + +Style: Process infographic, flat icons, medical software, flowchart +Aspect ratio: 21:9 (ultrawide) +``` + +--- + +## 3. Voice Intent Interactie + +### Voor: DALL-E 3 / Midjourney + +``` +A modern healthcare software interface demonstrating voice-controlled AI assistant interaction. + +Center focus: A floating command bar with a glowing microphone icon and the Dutch text "Wat zijn de risico's van Jan?" displayed as voice input with a subtle sound waveform. + +Below the command bar: Three response cards appearing in a staggered layout: +- Card 1: "Suïcidaliteit - Matig" with orange indicator +- Card 2: "Agressie - Laag" with green indicator +- Card 3: "Zelfverwaarlozing - Laag" with green indicator + +The interface has a soft blur effect on the background showing a patient dashboard. A small AI assistant avatar or brain icon indicates the system is processing. + +Style: Voice UI, conversational interface, healthcare app, glassmorphism, modern +Aspect ratio: 4:3 +``` + +--- + +## 4. Intake Tabs UI + +### Voor: Midjourney / Stable Diffusion + +``` +A detailed healthcare software interface showing a patient intake workflow with 9 horizontal tabs. + +The tab bar displays: "Algemeen | Contacten | Kindcheck | Risico | Anamnese | Onderzoek | ROM | Diagnose | Behandeladvies" + +The "Risico" tab is highlighted/active in teal. Below shows a risk assessment form with: +- Risk type dropdown showing "Suïcidaliteit" +- Severity scale: Laag / Matig / Hoog / Zeer hoog (Matig selected) +- Notes textarea +- Action items checklist + +Left sidebar shows patient info card with name, age, photo placeholder. + +Clean, professional medical software aesthetic. White background, teal accents, clear typography, organized layout. + +Style: Healthcare EHR interface, medical software UI, dashboard design, professional +Aspect ratio: 16:10 +``` + +--- + +## 5. Cortex Command Center + +### Voor: DALL-E 3 + +``` +A futuristic but professional healthcare AI command center interface called "Cortex". + +Top section: A prominent voice/text input bar with placeholder "Vraag iets of geef een opdracht..." and a microphone button, subtle glow effect. + +Main area shows three "action blocks" in a grid: +1. "Intake Status" card - circular progress indicator showing 6/9 complete, checklist of remaining items +2. "Risicotaxatie" card - risk assessment summary with colored severity badges +3. "Volgende Actie" card - AI suggestion "Kindcheck invullen?" with accept/dismiss buttons + +Bottom: Recent actions timeline showing "Diagnose toegevoegd", "Risico bijgewerkt" with timestamps. + +Right sidebar: Active patient context showing "Jan de Vries" with key info. + +Color palette: Teal primary (#0d9488), white backgrounds, slate text, subtle gradients. Modern healthcare technology feel. + +Style: AI dashboard, healthcare technology, command center UI, modern minimal +Aspect ratio: 16:9 +``` + +--- + +## 6. Intent Classification Diagram + +### Voor: Ideogram / Midjourney + +``` +A technical diagram showing AI intent classification for healthcare voice commands. + +Center: A brain/neural network icon labeled "Cortex AI" + +Three incoming arrows from the left showing voice inputs: +- "Start intake voor Jan" +- "Wat zijn de risico's?" +- "Ga naar diagnose" + +Three outgoing arrows to the right showing classified intents with icons: +- "intake_starten" → folder icon → "Actie" +- "risico_query" → search icon → "Query" +- "intake_navigeer" → arrow icon → "Navigatie" + +Below: A confidence meter showing "Reflex: 0.9" and "AI Fallback" path for low confidence. + +Style: Technical flowchart, AI/ML diagram, clean infographic, developer documentation +Aspect ratio: 16:9 +``` + +--- + +## 7. Mobile Voice Assistant + +### Voor: DALL-E 3 / Midjourney + +``` +A healthcare professional's hand holding a tablet device showing a voice-activated patient intake assistant. + +The tablet screen displays: +- Top: Patient header "Marie Jansen - Intake #2024-0042" +- Center: Large circular voice activation button with pulsing animation rings +- Below: Recent voice command "Kindcheck: geen kinderen" with checkmark +- Bottom: Suggested next actions as pill-shaped buttons: "Risicotaxatie", "Anamnese", "Diagnose" + +Background: Soft-focus hospital/clinic environment with warm lighting. + +The overall feel is modern healthcare technology that's approachable and easy to use. + +Style: Healthcare technology, tablet UI, medical app, professional photography composite +Aspect ratio: 3:4 (portrait) +``` + +--- + +## 8. Before/After Comparison + +### Voor: Midjourney + +``` +A split-screen comparison image showing healthcare workflow transformation. + +LEFT SIDE labeled "Traditioneel": +- Cluttered desktop with multiple windows open +- Paper forms scattered +- Mouse clicking through endless menus +- Stressed healthcare worker +- Gray, busy, overwhelming + +RIGHT SIDE labeled "Met Cortex": +- Clean single interface +- Voice waveform indicating speech input +- Organized card-based results +- Calm, focused healthcare worker +- Bright, organized, efficient + +A diagonal dividing line separates the two sides. The contrast emphasizes simplicity vs complexity. + +Style: Comparison infographic, before/after, healthcare technology marketing +Aspect ratio: 2:1 +``` + +--- + +## Prompt Tips + +### Stijl Keywords +- Healthcare software / Medical EHR / Clinical interface +- Clean UI / Minimal design / Professional +- Voice interface / AI assistant / Conversational UI +- Dashboard / Card-based layout / Modern web app + +### Kleurenpalet +- Primary: Teal `#0d9488` +- Background: White `#ffffff`, Slate `#f1f5f9` +- Text: Slate `#1e293b` +- Accents: Amber (warning), Red (high risk), Green (success) + +### Vermijden +- Geen stockfoto-achtige healthcare beelden (stethoscopen, witte jassen) +- Geen te futuristische sci-fi elementen +- Geen drukke, overladen interfaces +- Geen generieke "AI brain" clichés + +--- + +## Aanbevolen Model per Prompt + +| Prompt | Best Model | Reden | +|--------|------------|-------| +| 1. Hero Image | DALL-E 3 | Beste voor UI mockups | +| 2. Process Flow | Ideogram | Sterk in diagrammen | +| 3. Voice Intent | DALL-E 3 | Goed met tekst in beeld | +| 4. Tabs UI | Midjourney | Gedetailleerde interfaces | +| 5. Command Center | DALL-E 3 | Complexe UI compositie | +| 6. Intent Diagram | Ideogram | Technische diagrammen | +| 7. Mobile | Midjourney | Realistische composities | +| 8. Before/After | Midjourney | Conceptuele vergelijkingen | diff --git a/docs/intent/intake-intent-proces/intake-process-intents.md b/docs/intent/intake-intent-proces/intake-process-intents.md new file mode 100644 index 0000000..54be5cc --- /dev/null +++ b/docs/intent/intake-intent-proces/intake-process-intents.md @@ -0,0 +1,330 @@ +# Intake Proces: Flow, Intents & UI + +**Datum:** 03-02-2026 +**Status:** Ontwerp +**Doel:** Documentatie van het intake proces met bijbehorende Cortex intents en UI componenten + +--- + +## 1. Procesoverzicht + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ INTAKE PROCES │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ +│ │ SCREENING │ ──▶ │ INTAKE START │ ──▶ │ INTAKE UITVOERING │ │ +│ │ │ │ │ │ │ │ +│ │ • Hulpvraag │ │ • Titel │ │ ┌────────────────────┐ │ │ +│ │ • Documenten │ │ • Afdeling │ │ │ 1. Algemeen │ │ │ +│ │ • Activiteit │ │ • Startdatum │ │ │ 2. Contactmomenten │ │ │ +│ │ • Besluit │ │ │ │ │ 3. Kindcheck │ │ │ +│ └──────────────┘ └──────────────┘ │ │ 4. Risicotaxatie │ │ │ +│ │ │ │ │ 5. Anamnese │ │ │ +│ ▼ │ │ │ 6. Onderzoeken │ │ │ +│ geschikt? ───▶ NEE ───▶ STOP │ │ 7. ROM │ │ │ +│ │ │ │ 8. Diagnose │ │ │ +│ JA │ │ 9. Behandeladvies │ │ │ +│ └────────────────────┘ │ └────────────────────┘ │ │ +│ │ │ │ │ +│ └─────────────┼────────────┘ │ +│ ▼ │ +│ ┌──────────────────────────┐ │ +│ │ INTAKE AFSLUITEN │ │ +│ │ │ │ +│ │ • Status: afgerond │ │ +│ │ • Einddatum │ │ +│ │ → Start Behandelplan │ │ +│ └──────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. Fase 1: Screening + +### 2.1 Procesbeschrijving + +De screening is de eerste beoordeling of een patiënt geschikt is voor behandeling. Hier wordt de hulpvraag vastgelegd, documenten verzameld (verwijsbrief), en een besluit genomen. + +### 2.2 UI Componenten + +| Component | Pad | Beschrijving | +|-----------|-----|--------------| +| `ScreeningPage` | `app/epd/patients/[id]/screening/page.tsx` | Container pagina | +| `HelpRequestCard` | `screening/components/help-request-card.tsx` | Hulpvraag invoer (textarea) | +| `DecisionCard` | `screening/components/decision-card.tsx` | Besluit: geschikt/niet_geschikt + afdeling | +| `DocumentCard` | `screening/components/document-card.tsx` | Upload verwijsbrief en documenten | +| `ActivityLog` | `screening/components/activity-log.tsx` | Chronologisch activiteitenlog | + +### 2.3 Intents & Queries + +| Intent | Type | Input Voorbeelden | Output/Actie | +|--------|------|-------------------|--------------| +| `screening_query` | Query | "Toon screening van Jan"
"Wat is de hulpvraag?" | `ScreeningBlock` - overzicht hulpvraag, besluit, status | +| `hulpvraag_invoer` | Actie | "Hulpvraag: angstklachten en slaapproblemen"
"Noteer hulpvraag: depressieve klachten" | Vult `HelpRequestCard` in | +| `screening_besluit` | Actie | "Jan is geschikt voor behandeling"
"Niet geschikt, doorverwijzen" | Vult `DecisionCard` in | +| `screening_activiteit` | Actie | "Gebeld met huisarts over verwijzing"
"Verwijsbrief ontvangen" | Voegt toe aan `ActivityLog` | + +### 2.4 Nudges + +| Trigger | Nudge | Priority | +|---------|-------|----------| +| Hulpvraag ingevuld, geen verwijsbrief | "Verwijsbrief uploaden?" | medium | +| Verwijsbrief + hulpvraag compleet | "Screeningsbesluit nemen?" | medium | +| Besluit = geschikt | "Intake starten voor deze patiënt?" | low | + +--- + +## 3. Fase 2: Intake Starten + +### 3.1 Procesbeschrijving + +Na een positief screeningsbesluit wordt een nieuwe intake aangemaakt met basisgegevens. + +### 3.2 UI Componenten + +| Component | Pad | Beschrijving | +|-----------|-----|--------------| +| `NewIntakePage` | `intakes/new/page.tsx` | Formulier voor nieuwe intake | +| `NewIntakeForm` | `intakes/components/new-intake-form.tsx` | Titel, afdeling, startdatum | +| `IntakeList` | `intakes/components/intake-list.tsx` | Overzicht alle intakes van patiënt | + +### 3.3 Intents & Queries + +| Intent | Type | Input Voorbeelden | Output/Actie | +|--------|------|-------------------|--------------| +| `intake_starten` | Actie | "Start intake voor Jan"
"Nieuwe intake afdeling Volwassenen" | Opent `NewIntakeForm` met prefill | +| `intake_lijst` | Query | "Toon intakes van Jan"
"Welke intakes heeft Marie?" | `IntakeListBlock` - lijst met status | + +--- + +## 4. Fase 3: Intake Uitvoering + +### 4.1 Overzicht Tabs + +| # | Tab | Verplicht | Component | Beschrijving | +|---|-----|-----------|-----------|--------------| +| 1 | Algemeen | ✅ | `IntakePage` | Basisgegevens intake | +| 2 | Contactmomenten | ❌ | `ContactManager` | Gesprekken, telefoontjes | +| 3 | Kindcheck | ⚠️* | `KindcheckForm` | Kinderen in beeld, zorgen, acties | +| 4 | Risicotaxatie | ✅ | `RiskManager` | Suïcidaliteit, agressie, etc. | +| 5 | Anamnese | ✅ | `AnamneseManager` | Psychiatrisch, sociaal, somatisch | +| 6 | Onderzoeken | ❌ | `ExaminationManager` | Lichamelijk, psychologisch onderzoek | +| 7 | ROM | ❌ | `ExaminationManager` (isRom) | Vragenlijsten (OQ-45, etc.) | +| 8 | Diagnose | ✅ | `DiagnosisManager` | ICD-10 classificaties | +| 9 | Behandeladvies | ✅ | `TreatmentAdviceForm` | Advies, programma, behandelaar | + +*Kindcheck is wettelijk verplicht bij patiënten met kinderen + +### 4.2 Tab-specifieke Intents + +#### 4.2.1 Algemeen + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `intake_algemeen` | Query | "Toon intake gegevens"
"Wanneer is de intake gestart?" | Toont algemene info | +| `intake_status` | Query | "Wat is de status van de intake?"
"Is de intake compleet?" | Status + checklist onvolledige secties | + +#### 4.2.2 Contactmomenten + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `contact_toevoegen` | Actie | "Intakegesprek gehad met Jan"
"Telefonisch contact: besproken wachttijd" | Voegt contactmoment toe | +| `contacten_query` | Query | "Welke contactmomenten zijn er geweest?" | Lijst contactmomenten | + +#### 4.2.3 Kindcheck + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `kindcheck_invullen` | Actie | "Kindcheck: 2 kinderen, geen zorgen"
"Geen kinderen in beeld" | Vult kindcheck formulier | +| `kindcheck_query` | Query | "Is de kindcheck gedaan?"
"Hoeveel kinderen heeft Jan?" | Status + samenvatting | + +#### 4.2.4 Risicotaxatie + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `risico_toevoegen` | Actie | "Risico suïcidaliteit: matig"
"Agressierisico laag" | Voegt risico toe aan `RiskManager` | +| `risico_query` | Query | "Wat zijn de risico's van Jan?"
"Toon risicotaxatie" | Risico overzicht met levels | + +#### 4.2.5 Anamnese + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `anamnese_toevoegen` | Actie | "Psychiatrische anamnese: eerste depressie 2019"
"Sociale anamnese: woont alleen" | Voegt anamnese sectie toe | +| `anamnese_query` | Query | "Wat is de voorgeschiedenis?"
"Toon anamnese" | Anamnese overzicht | + +#### 4.2.6 Onderzoeken + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `onderzoek_toevoegen` | Actie | "Psychologisch onderzoek aangevraagd"
"Lab: bloedonderzoek normaal" | Voegt onderzoek toe | +| `onderzoeken_query` | Query | "Welke onderzoeken zijn gedaan?" | Lijst onderzoeken + resultaten | + +#### 4.2.7 ROM + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `rom_toevoegen` | Actie | "ROM: OQ-45 score 82"
"PHQ-9 afgenomen, score 14" | Voegt ROM-meting toe | +| `rom_query` | Query | "Wat zijn de ROM scores?"
"Toon vragenlijsten" | ROM overzicht met scores | + +#### 4.2.8 Diagnose + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `diagnose_toevoegen` | Actie | "Diagnose: depressieve stoornis"
"Hoofddiagnose F32.1" | Opent ICD-10 zoeken + voegt toe | +| `diagnose_query` | Query | "Wat zijn de diagnoses?"
"Welke diagnose heeft Jan?" | Diagnose overzicht | + +#### 4.2.9 Behandeladvies + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `behandeladvies_invoer` | Actie | "Behandeladvies: CGT bij angst"
"Advies: EMDR voor trauma" | Vult behandeladvies formulier | +| `behandeladvies_query` | Query | "Wat is het behandeladvies?"
"Toon advies" | Behandeladvies overzicht | + +### 4.3 Navigatie Intents + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `intake_navigeer` | Navigatie | "Ga naar risicotaxatie"
"Open kindcheck"
"Naar diagnose" | Navigeert naar specifieke tab | +| `intake_volgende` | Navigatie | "Volgende stap"
"Ga verder" | Navigeert naar volgende tab | +| `intake_vorige` | Navigatie | "Vorige"
"Terug" | Navigeert naar vorige tab | + +--- + +## 5. Fase 4: Intake Afsluiten + +### 5.1 Procesbeschrijving + +Wanneer alle verplichte secties zijn ingevuld, kan de intake worden afgesloten. + +### 5.2 Intents + +| Intent | Type | Input | Output | +|--------|------|-------|--------| +| `intake_afsluiten` | Actie | "Sluit intake af"
"Intake afronden" | Zet status op 'afgerond' + einddatum | +| `intake_samenvatting` | Query | "Samenvatting intake"
"Geef overzicht van de intake" | AI-gegenereerde samenvatting | +| `intake_checklist` | Query | "Wat moet ik nog doen?"
"Is de intake compleet?" | Checklist onvolledige secties | + +### 5.3 Nudges + +| Trigger | Nudge | Priority | +|---------|-------|----------| +| Alle verplichte secties ingevuld | "Intake afsluiten?" | medium | +| Intake afgesloten | "Behandelplan opstellen?" | low | +| Intake > 4 weken open | "Intake nog actueel? Afsluiten of voortzetten?" | low | + +--- + +## 6. Intent Matrix (Totaaloverzicht) + +### 6.1 Alle Intents + +| # | Intent ID | Type | Fase | Priority | +|---|-----------|------|------|----------| +| 1 | `screening_query` | Query | Screening | 🔴 Hoog | +| 2 | `hulpvraag_invoer` | Actie | Screening | 🟡 Middel | +| 3 | `screening_besluit` | Actie | Screening | 🟡 Middel | +| 4 | `screening_activiteit` | Actie | Screening | 🔵 Laag | +| 5 | `intake_starten` | Actie | Start | 🔴 Hoog | +| 6 | `intake_lijst` | Query | Start | 🟡 Middel | +| 7 | `intake_navigeer` | Navigatie | Uitvoering | 🔴 Hoog | +| 8 | `intake_status` | Query | Uitvoering | 🔴 Hoog | +| 9 | `contact_toevoegen` | Actie | Uitvoering | 🟡 Middel | +| 10 | `kindcheck_invullen` | Actie | Uitvoering | 🟡 Middel | +| 11 | `kindcheck_query` | Query | Uitvoering | 🟡 Middel | +| 12 | `risico_toevoegen` | Actie | Uitvoering | 🔴 Hoog | +| 13 | `risico_query` | Query | Uitvoering | 🔴 Hoog | +| 14 | `anamnese_toevoegen` | Actie | Uitvoering | 🟡 Middel | +| 15 | `anamnese_query` | Query | Uitvoering | 🟡 Middel | +| 16 | `onderzoek_toevoegen` | Actie | Uitvoering | 🔵 Laag | +| 17 | `onderzoeken_query` | Query | Uitvoering | 🔵 Laag | +| 18 | `rom_toevoegen` | Actie | Uitvoering | 🟡 Middel | +| 19 | `rom_query` | Query | Uitvoering | 🟡 Middel | +| 20 | `diagnose_toevoegen` | Actie | Uitvoering | 🔴 Hoog | +| 21 | `diagnose_query` | Query | Uitvoering | 🔴 Hoog | +| 22 | `behandeladvies_invoer` | Actie | Uitvoering | 🔴 Hoog | +| 23 | `behandeladvies_query` | Query | Uitvoering | 🔴 Hoog | +| 24 | `intake_afsluiten` | Actie | Afsluiten | 🟡 Middel | +| 25 | `intake_samenvatting` | Query | Afsluiten | 🟡 Middel | +| 26 | `intake_checklist` | Query | Afsluiten | 🟡 Middel | + +### 6.2 Verdeling + +| Type | Aantal | +|------|--------| +| Query | 13 | +| Actie | 11 | +| Navigatie | 2 | +| **Totaal** | **26** | + +| Priority | Aantal | +|----------|--------| +| 🔴 Hoog | 10 | +| 🟡 Middel | 13 | +| 🔵 Laag | 3 | + +--- + +## 7. UI Blocks (Nieuw te bouwen) + +Voor Cortex moeten we nieuwe blocks bouwen die de intent resultaten tonen: + +| Block | Toont | Bron Data | +|-------|-------|-----------| +| `ScreeningBlock` | Hulpvraag, besluit, status | `getScreeningSummary()` | +| `IntakeStatusBlock` | Checklist tabs, voortgang | Nieuwe functie nodig | +| `IntakeListBlock` | Lijst intakes van patiënt | `getIntakesByPatient()` | +| `KindcheckBlock` | Samenvatting kindcheck | `getKindcheck()` | +| `RisicoBlock` | Risico's met levels | `getRiskAssessments()` | +| `AnamneseBlock` | Anamnese samenvatting | `getAnamneses()` | +| `DiagnoseBlock` | Diagnoses lijst | `getDiagnoses()` | +| `BehandeladviesBlock` | Behandeladvies | `getTreatmentAdvice()` | +| `IntakeSamenvattingBlock` | AI-samenvatting hele intake | Nieuwe AI functie nodig | + +--- + +## 8. Implementatie Prioriteit + +### 8.1 MVP (Fase 1) + +Focus op de meest waardevolle intents: + +1. **`intake_status`** - "Wat moet ik nog doen?" → Checklist +2. **`intake_navigeer`** - "Ga naar risicotaxatie" → Direct navigeren +3. **`risico_query`** - "Wat zijn de risico's?" → Risico overzicht +4. **`diagnose_query`** - "Welke diagnoses?" → Diagnose overzicht +5. **`intake_samenvatting`** - "Samenvatting intake" → AI samenvatting + +### 8.2 Fase 2 + +6. **`screening_query`** - Screening overzicht +7. **`kindcheck_query`** - Kindcheck status +8. **`behandeladvies_query`** - Behandeladvies tonen +9. **`intake_starten`** - Nieuwe intake via spraak + +### 8.3 Fase 3 (Actie intents) + +10. **`risico_toevoegen`** - Risico toevoegen via spraak +11. **`diagnose_toevoegen`** - Diagnose toevoegen +12. **`kindcheck_invullen`** - Kindcheck invullen +13. Overige actie intents... + +--- + +## 9. Open Vragen + +1. **Contextbeheer**: Hoe weet Cortex welke intake actief is als een patiënt meerdere intakes heeft? +2. **Navigatie vs Blocks**: Navigeren we naar bestaande pagina's of tonen we data in Cortex blocks? +3. **Actie confirmatie**: Welke acties vereisen bevestiging voordat ze uitgevoerd worden? +4. **Integratie behandelplan**: Hoe koppelen we intake-afsluiting aan behandelplan-start? + +--- + +## Versiehistorie + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v0.1 | 03-02-2026 | Colin Lit | Initieel ontwerp | diff --git a/docs/intent/intake-intent-proces/prompt-intake-procesflow-intents.md b/docs/intent/intake-intent-proces/prompt-intake-procesflow-intents.md new file mode 100644 index 0000000..a8e4199 --- /dev/null +++ b/docs/intent/intake-intent-proces/prompt-intake-procesflow-intents.md @@ -0,0 +1,221 @@ +# Text-to-Image Prompt: Intake Procesflow met Intents + +**Doel:** Visualisatie van het complete intake proces met spraak-intents en EPD schermen + +--- + +## Prompt (DALL-E 3 / Ideogram) + +``` +A detailed horizontal process flow diagram for a Dutch healthcare EPD (Electronic Patient Dossier) system showing voice intents triggering UI screens. + +The diagram flows LEFT to RIGHT through 4 main phases, each in a distinct colored zone: + +PHASE 1 - "SCREENING" (light blue zone): +┌─────────────────────────────────────────┐ +│ Voice intents (speech bubbles): │ +│ • "Wat is de hulpvraag?" │ +│ • "Jan is geschikt voor behandeling" │ +│ ↓ │ +│ EPD Screens (UI cards): │ +│ [HelpRequestCard] [DecisionCard] │ +│ [DocumentCard] [ActivityLog] │ +└─────────────────────────────────────────┘ + +Arrow → to next phase + +PHASE 2 - "INTAKE START" (light green zone): +┌─────────────────────────────────────────┐ +│ Voice intents: │ +│ • "Start intake voor Jan" │ +│ • "Nieuwe intake Volwassenen" │ +│ ↓ │ +│ EPD Screens: │ +│ [NewIntakeForm] │ +│ - Titel │ +│ - Afdeling │ +│ - Startdatum │ +└─────────────────────────────────────────┘ + +Arrow → to next phase + +PHASE 3 - "INTAKE UITVOERING" (light teal zone, largest section): +┌─────────────────────────────────────────────────────────────┐ +│ 9 TABS displayed as a horizontal tab bar: │ +│ [Algemeen|Contact|Kindcheck|Risico|Anamnese|Onderzoek|ROM|Diagnose|Advies] │ +│ │ +│ Below: 3 columns of intent→screen mappings: │ +│ │ +│ Column 1: Column 2: Column 3: │ +│ "Ga naar risico" "Wat zijn de risico's?" "Diagnose: depressie" │ +│ ↓ ↓ ↓ │ +│ [RiskManager] [RisicoBlock] [DiagnosisManager] │ +│ │ +│ "Kindcheck: geen kinderen" "Is intake compleet?" "Behandeladvies CGT" │ +│ ↓ ↓ ↓ │ +│ [KindcheckForm] [IntakeStatusBlock] [TreatmentAdviceForm] │ +└─────────────────────────────────────────────────────────────┘ + +Arrow → to next phase + +PHASE 4 - "AFSLUITEN" (light purple zone): +┌─────────────────────────────────────────┐ +│ Voice intents: │ +│ • "Sluit intake af" │ +│ • "Samenvatting intake" │ +│ ↓ │ +│ EPD Screens: │ +│ [IntakeSummaryBlock] │ +│ [Status: Afgerond ✓] │ +│ ↓ │ +│ "Start behandelplan" → │ +└─────────────────────────────────────────┘ + +LEGEND at bottom: +🎤 = Voice Intent (input) +📱 = EPD Screen/Component (output) +→ = Process flow +↓ = Intent triggers screen + +Design style: Clean technical flowchart, software architecture diagram, white background with subtle colored zones for each phase. Speech bubbles for intents, rounded rectangles for UI components. Arrows showing relationships. Professional documentation style. + +Typography: Sans-serif, clear hierarchy, Dutch language labels. +Colors: Screening=#e0f2fe, Start=#dcfce7, Uitvoering=#ccfbf1, Afsluiten=#f3e8ff, Arrows=#0d9488 +Aspect ratio: 21:9 (ultrawide) or 3:1 +``` + +--- + +## Alternatief: Verticale Flow (voor A4/portrait) + +``` +A vertical process flow diagram for a Dutch healthcare intake system showing voice commands and their corresponding EPD screens. + +TOP TO BOTTOM layout with 4 stacked sections: + +SECTION 1 - SCREENING +├── Intent bubble: "Wat is de hulpvraag van Jan?" +├── Arrow down +└── Screen cards: [HelpRequestCard] [DecisionCard] + +SECTION 2 - INTAKE START +├── Intent bubble: "Start intake voor Jan" +├── Arrow down +└── Screen card: [NewIntakeForm] + +SECTION 3 - INTAKE TABS (expanded section) +├── Tab bar showing 9 tabs +├── Three example flows side by side: +│ ├── "Ga naar risico" → [RiskManager] +│ ├── "Wat zijn diagnoses?" → [DiagnoseBlock] +│ └── "Kindcheck geen kinderen" → [KindcheckForm] + +SECTION 4 - AFSLUITEN +├── Intent bubble: "Sluit intake af" +├── Arrow down +└── Screen cards: [IntakeSummaryBlock] → Behandelplan + +Visual style: Technical documentation, swim lane diagram, clear sections with subtle background colors, speech bubbles for voice input, device frames for screens. + +Aspect ratio: 9:16 or A4 portrait +``` + +--- + +## Simpele Versie (voor snelle generatie) + +``` +A software flowchart showing voice commands triggering healthcare screens. + +Four colored columns left to right: +1. SCREENING (blue): "Hulpvraag?" → HelpRequestCard +2. INTAKE START (green): "Start intake" → NewIntakeForm +3. UITVOERING (teal): "Risico's?" → RiskManager, "Diagnose?" → DiagnosisManager +4. AFSLUITEN (purple): "Samenvatting" → IntakeSummaryBlock + +Speech bubbles connect to UI component boxes with arrows. +Clean, minimal, technical diagram style. +White background, colored section headers. + +Aspect ratio: 16:9 +``` + +--- + +## Mermaid Diagram (voor technische docs) + +Als je liever een exacte diagram wilt die je zelf kunt renderen: + +```mermaid +flowchart LR + subgraph SCREENING["🔵 SCREENING"] + I1["🎤 Wat is de hulpvraag?"] + I2["🎤 Jan is geschikt"] + S1["📱 HelpRequestCard"] + S2["📱 DecisionCard"] + I1 --> S1 + I2 --> S2 + end + + subgraph START["🟢 INTAKE START"] + I3["🎤 Start intake voor Jan"] + S3["📱 NewIntakeForm"] + I3 --> S3 + end + + subgraph UITVOERING["🔷 INTAKE UITVOERING"] + direction TB + TABS["9 Tabs: Algemeen | Contact | Kindcheck | Risico | Anamnese | Onderzoek | ROM | Diagnose | Advies"] + + I4["🎤 Ga naar risico"] + I5["🎤 Wat zijn de risico's?"] + I6["🎤 Diagnose: depressie"] + I7["🎤 Kindcheck: geen kinderen"] + + S4["📱 RiskManager"] + S5["📱 RisicoBlock"] + S6["📱 DiagnosisManager"] + S7["📱 KindcheckForm"] + + I4 --> S4 + I5 --> S5 + I6 --> S6 + I7 --> S7 + end + + subgraph AFSLUITEN["🟣 AFSLUITEN"] + I8["🎤 Sluit intake af"] + I9["🎤 Samenvatting intake"] + S8["📱 IntakeSummaryBlock"] + S9["📱 Status: Afgerond ✓"] + I8 --> S9 + I9 --> S8 + end + + SCREENING --> START + START --> UITVOERING + UITVOERING --> AFSLUITEN + AFSLUITEN -->|"Start behandelplan"| BP["📱 BehandelplanView"] +``` + +--- + +## Prompt Parameters + +| Parameter | Waarde | +|-----------|--------| +| **Model** | DALL-E 3, Ideogram, of Midjourney | +| **Style** | Technical diagram / Software flowchart | +| **Aspect** | 21:9 (ultrawide) voor horizontaal, 9:16 voor verticaal | +| **Kleuren** | Fase-gebonden: blauw→groen→teal→paars | +| **Taal** | Nederlands (intent teksten) | + +--- + +## Tips voor beste resultaat + +1. **DALL-E 3**: Beste voor tekst in afbeeldingen, maar kan moeite hebben met complexe diagrammen +2. **Ideogram**: Zeer sterk in diagrammen en flowcharts met tekst +3. **Midjourney**: Mooi visueel maar minder nauwkeurig met tekst + +**Aanbeveling**: Gebruik Ideogram voor dit type technische procesflow, of maak het diagram in Figma/Miro en gebruik AI alleen voor styling. diff --git a/docs/swift/architectuur-visual-spec.json b/docs/swift/architectuur-visual-spec.json new file mode 100644 index 0000000..10a3dc3 --- /dev/null +++ b/docs/swift/architectuur-visual-spec.json @@ -0,0 +1,627 @@ +{ + "meta": { + "title": "Intent-Driven EPD - Architectuur Visual", + "version": "1.0", + "created": "2025-01-05", + "purpose": "Conceptuele architectuurplaat voor LinkedIn post en presentaties", + "target_audience": ["C-level", "Zorgprofessionals", "ICT-managers"], + "language": "Nederlands", + "tagline": "Het systeem begrijpt wat je wilt, niet alleen wat je typt" + }, + "format": { + "dimensions": { + "width": 1200, + "height": 1500, + "unit": "px" + }, + "aspect_ratio": "4:5", + "export_formats": ["PNG", "SVG", "PDF"], + "linkedin_optimized": true + }, + "brand": { + "colors": { + "primary": { + "teal_700": "#0F766E", + "teal_600": "#0D9488", + "teal_50": "#F0FDFA" + }, + "accent": { + "amber_500": "#F59E0B", + "amber_50": "#FFFBEB" + }, + "success": { + "green_600": "#16A34A", + "green_50": "#ECFDF5" + }, + "neutral": { + "slate_900": "#0F172A", + "slate_600": "#475569", + "slate_400": "#94A3B8", + "slate_200": "#E2E8F0", + "slate_50": "#F8FAFC", + "white": "#FFFFFF" + } + }, + "typography": { + "font_family": "Inter, system-ui, sans-serif", + "sizes": { + "title": "32px", + "subtitle": "18px", + "section_header": "20px", + "body": "14px", + "caption": "12px", + "label": "11px" + }, + "weights": { + "bold": 700, + "semibold": 600, + "medium": 500, + "regular": 400 + } + }, + "spacing": { + "xs": 4, + "sm": 8, + "md": 16, + "lg": 24, + "xl": 32, + "xxl": 48 + }, + "border_radius": { + "sm": 4, + "md": 8, + "lg": 12, + "xl": 16 + }, + "shadows": { + "sm": "0 1px 2px rgba(15,23,42,0.06)", + "md": "0 4px 12px rgba(15,23,42,0.08)", + "lg": "0 8px 24px rgba(15,23,42,0.12)" + } + }, + "layout": { + "structure": "vertical_flow", + "sections": [ + "header", + "user_input", + "intent_layer", + "jit_layer", + "action_divider", + "nudge_layer", + "footer_summary" + ], + "padding": { + "outer": 40, + "section_gap": 24 + } + }, + "sections": { + "header": { + "height": 100, + "background": "#FFFFFF", + "content": { + "title": { + "text": "INTENT-DRIVEN EPD", + "style": { + "font_size": "32px", + "font_weight": 700, + "color": "#0F172A", + "letter_spacing": "0.05em", + "text_align": "center" + } + }, + "subtitle": { + "text": "Het systeem begrijpt wat je wilt, niet alleen wat je typt", + "style": { + "font_size": "16px", + "font_weight": 400, + "color": "#475569", + "text_align": "center", + "margin_top": 8 + } + } + } + }, + "user_input": { + "height": 80, + "content": { + "icon": { + "name": "user-round", + "source": "lucide", + "size": 32, + "color": "#0F766E" + }, + "label": { + "text": "ZORGVERLENER", + "style": { + "font_size": "11px", + "font_weight": 600, + "color": "#475569", + "letter_spacing": "0.1em" + } + }, + "speech_bubble": { + "text": "\"Notitie jan medicatie\"", + "style": { + "font_size": "16px", + "font_weight": 500, + "color": "#0F172A", + "font_style": "italic", + "background": "#F8FAFC", + "border": "1px solid #E2E8F0", + "border_radius": 8, + "padding": "12px 20px" + } + } + }, + "connector": { + "type": "arrow_down", + "color": "#CBD5E1", + "length": 32 + } + }, + "intent_layer": { + "height": 280, + "box": { + "background": "#F0FDFA", + "border": "2px solid #0F766E", + "border_radius": 16, + "padding": 24 + }, + "header": { + "badge": { + "text": "INTENT", + "style": { + "font_size": "14px", + "font_weight": 700, + "color": "#FFFFFF", + "background": "#0F766E", + "padding": "6px 16px", + "border_radius": 20, + "letter_spacing": "0.1em" + } + } + }, + "content": { + "description": { + "text": "Natuurlijke taal → Gestructureerde intentie", + "style": { + "font_size": "14px", + "font_weight": 500, + "color": "#0F766E" + } + }, + "transformation": { + "input": { + "text": "\"Notitie jan medicatie\"", + "style": { + "font_size": "15px", + "color": "#475569", + "font_style": "italic" + } + }, + "arrow": { + "icon": "arrow-down", + "color": "#0D9488", + "size": 20 + }, + "output": { + "boxes": [ + { + "label": "WIE", + "value": "Jan", + "color": "#0F766E", + "background": "#FFFFFF" + }, + { + "label": "WAT", + "value": "Notitie", + "color": "#0F766E", + "background": "#FFFFFF" + }, + { + "label": "CONTEXT", + "value": "Medicatie", + "color": "#0F766E", + "background": "#FFFFFF" + } + ], + "box_style": { + "width": 100, + "height": 60, + "border_radius": 8, + "border": "1px solid #0D9488", + "text_align": "center", + "label_size": "10px", + "value_size": "16px", + "value_weight": 600 + } + } + }, + "multi_intent_example": { + "text": "Complexe intenties: \"Zeg jan af en maak notitie griep\" → 2 acties, automatisch gesplitst", + "style": { + "font_size": "12px", + "color": "#475569", + "margin_top": 16, + "padding": "8px 12px", + "background": "#FFFFFF", + "border_radius": 6 + } + } + }, + "connector": { + "type": "arrow_down", + "color": "#CBD5E1", + "length": 24 + } + }, + "jit_layer": { + "height": 320, + "box": { + "background": "#FFFBEB", + "border": "2px solid #F59E0B", + "border_radius": 16, + "padding": 24 + }, + "header": { + "badge": { + "text": "JIT-INTERFACE", + "style": { + "font_size": "14px", + "font_weight": 700, + "color": "#FFFFFF", + "background": "#F59E0B", + "padding": "6px 16px", + "border_radius": 20, + "letter_spacing": "0.05em" + } + }, + "subtitle": { + "text": "Just-In-Time Blocks", + "style": { + "font_size": "12px", + "color": "#92400E", + "margin_left": 8 + } + } + }, + "content": { + "description": { + "text": "Het juiste scherm verschijnt. Vooringevuld. Klaar voor actie.", + "style": { + "font_size": "14px", + "font_weight": 500, + "color": "#92400E" + } + }, + "block_preview": { + "title": { + "icon": "file-text", + "text": "DAGNOTITIE", + "style": { + "font_size": "12px", + "font_weight": 600, + "color": "#475569" + } + }, + "fields": [ + { + "label": "Patient", + "value": "Jan", + "status": "prefilled", + "checkmark": true + }, + { + "label": "Categorie", + "value": "Medicatie", + "status": "prefilled", + "checkmark": true + }, + { + "label": "Inhoud", + "value": "", + "status": "cursor", + "placeholder": "cursor knippert" + } + ], + "style": { + "background": "#FFFFFF", + "border": "1px solid #E2E8F0", + "border_radius": 12, + "padding": 16, + "shadow": "0 4px 12px rgba(15,23,42,0.08)", + "field_height": 36, + "checkmark_color": "#16A34A" + } + }, + "other_blocks": { + "label": "Andere blocks (laden wanneer nodig):", + "blocks": [ + { + "icon": "search", + "label": "Zoeken" + }, + { + "icon": "calendar", + "label": "Agenda" + }, + { + "icon": "clipboard-list", + "label": "Overdracht" + }, + { + "icon": "layout-dashboard", + "label": "Dashboard" + } + ], + "style": { + "icon_size": 16, + "background": "#FFFFFF", + "border": "1px solid #E2E8F0", + "border_radius": 8, + "padding": "8px 12px", + "gap": 8, + "color": "#475569", + "font_size": "11px" + } + } + }, + "connector": { + "type": "action_button", + "content": { + "text": "OPSLAAN", + "icon": "check", + "style": { + "background": "#0F766E", + "color": "#FFFFFF", + "font_size": "12px", + "font_weight": 600, + "padding": "8px 24px", + "border_radius": 6 + } + }, + "arrow_after": { + "type": "arrow_down", + "color": "#CBD5E1", + "length": 24 + } + } + }, + "nudge_layer": { + "height": 260, + "box": { + "background": "#ECFDF5", + "border": "2px solid #16A34A", + "border_radius": 16, + "padding": 24 + }, + "header": { + "badge": { + "text": "NUDGE", + "style": { + "font_size": "14px", + "font_weight": 700, + "color": "#FFFFFF", + "background": "#16A34A", + "padding": "6px 16px", + "border_radius": 20, + "letter_spacing": "0.1em" + } + }, + "subtitle": { + "text": "Proactieve Suggesties", + "style": { + "font_size": "12px", + "color": "#166534", + "margin_left": 8 + } + } + }, + "content": { + "description": { + "text": "Het systeem denkt vooruit. Op basis van protocollen.", + "style": { + "font_size": "14px", + "font_weight": 500, + "color": "#166534" + } + }, + "nudge_card": { + "icon": { + "name": "lightbulb", + "color": "#F59E0B", + "size": 20 + }, + "title": "SUGGESTIE", + "message": { + "line1": "Medicatie gewijzigd voor Jan.", + "line2": "Evaluatie inplannen over 1 week?" + }, + "source": { + "icon": "book-open", + "text": "Bron: Zorgstandaard" + }, + "buttons": [ + { + "text": "Ja, plannen", + "style": "primary", + "background": "#16A34A", + "color": "#FFFFFF" + }, + { + "text": "Niet nu", + "style": "secondary", + "background": "transparent", + "color": "#475569", + "border": "1px solid #E2E8F0" + } + ], + "style": { + "background": "#FFFFFF", + "border": "1px solid #E2E8F0", + "border_radius": 12, + "padding": 16, + "shadow": "0 4px 12px rgba(15,23,42,0.08)" + } + }, + "footnote": { + "text": "Niet dwingend. Wel onderbouwd. Jij beslist.", + "style": { + "font_size": "12px", + "color": "#166534", + "font_style": "italic", + "margin_top": 12 + } + } + } + }, + "footer_summary": { + "height": 140, + "background": "#FFFFFF", + "border_top": "1px solid #E2E8F0", + "padding_top": 24, + "content": { + "columns": [ + { + "icon": "target", + "icon_color": "#0F766E", + "title": "INTENT", + "description": "Begrijpt wat je wilt", + "color": "#0F766E" + }, + { + "icon": "layout", + "icon_color": "#F59E0B", + "title": "JIT-INTERFACE", + "description": "Toont wat je nodig hebt", + "color": "#F59E0B" + }, + { + "icon": "lightbulb", + "icon_color": "#16A34A", + "title": "NUDGE", + "description": "Suggereert wat je kan vergeten", + "color": "#16A34A" + } + ], + "column_style": { + "width": 180, + "text_align": "center", + "icon_size": 24, + "title_size": "13px", + "title_weight": 700, + "description_size": "12px", + "description_color": "#475569" + }, + "tagline": { + "text": "Jij zorgt. Het EPD regelt de rest.", + "style": { + "font_size": "16px", + "font_weight": 600, + "color": "#0F172A", + "text_align": "center", + "margin_top": 24 + } + } + } + } + }, + "icons": { + "source": "lucide-react", + "mapping": { + "user": "user-round", + "note": "file-text", + "search": "search", + "calendar": "calendar", + "clipboard": "clipboard-list", + "dashboard": "layout-dashboard", + "lightbulb": "lightbulb", + "book": "book-open", + "check": "check", + "arrow_down": "arrow-down", + "target": "target", + "layout": "layout" + } + }, + "connectors": { + "style": { + "stroke_width": 2, + "stroke_color": "#CBD5E1", + "arrow_head_size": 8 + }, + "positions": [ + { + "from": "user_input", + "to": "intent_layer", + "type": "arrow_down" + }, + { + "from": "intent_layer", + "to": "jit_layer", + "type": "arrow_down" + }, + { + "from": "jit_layer", + "to": "nudge_layer", + "type": "button_then_arrow", + "button_label": "OPSLAAN" + } + ] + }, + "accessibility": { + "alt_text": "Architectuurdiagram van het Intent-Driven EPD Cortex. Toont drie lagen: 1) Intent - natuurlijke taal wordt omgezet naar gestructureerde intentie met wie, wat en context; 2) JIT-Interface - het juiste invoerscherm verschijnt automatisch met vooringevulde velden; 3) Nudge - proactieve suggesties op basis van zorgprotocollen. Tagline: Jij zorgt, het EPD regelt de rest.", + "contrast_ratio": { + "text_on_white": "7:1 minimum", + "text_on_colored": "4.5:1 minimum" + } + }, + "export_variants": { + "linkedin_post": { + "dimensions": "1200x1500", + "format": "PNG", + "quality": "high", + "notes": "Optimaal voor LinkedIn feed" + }, + "linkedin_article": { + "dimensions": "1920x1080", + "format": "PNG", + "notes": "16:9 voor artikel header" + }, + "presentation": { + "dimensions": "1920x1080", + "format": "SVG", + "notes": "Vector voor schaling" + }, + "print_a4": { + "dimensions": "210x297mm", + "format": "PDF", + "dpi": 300, + "notes": "Voor handouts" + } + }, + "design_tools": { + "figma": { + "setup": [ + "Create frame 1200x1500px", + "Set background #FFFFFF", + "Use Auto Layout for sections", + "Import Lucide icons as components" + ], + "components_to_create": [ + "Layer Box (variant: intent/jit/nudge)", + "Badge (variant: teal/amber/green)", + "Field Row (variant: prefilled/empty)", + "Block Thumbnail", + "Summary Column" + ] + }, + "canva": { + "setup": [ + "Custom size 1200x1500", + "Use brand colors", + "Elements > Lines for connectors" + ] + } + } +} diff --git a/docs/swift/architectuur.md b/docs/swift/architectuur.md new file mode 100644 index 0000000..376869c --- /dev/null +++ b/docs/swift/architectuur.md @@ -0,0 +1,471 @@ +# Mini-EPD Prototype - Architectuur + +## Executive Summary (C-Level) + +**Mini-EPD** is een moderne, AI-gestuurde elektronische patiëntendossier (EPD) oplossing voor de Nederlandse zorgsector. Het systeem combineert spraakherkenning met intelligente intentieherkenning, waardoor zorgverleners hands-free kunnen rapporteren en navigeren. + +### Kernwaarden + +| Aspect | Waarde | +|--------|--------| +| **Tijdsbesparing** | 70% van commando's verwerkt in <20ms door lokale AI | +| **Gebruiksgemak** | Natuurlijke taal en spraak als primaire input | +| **Compliance** | GDPR-vriendelijk met soft deletes en audit trail | +| **Schaalbaarheid** | Serverless architectuur, horizontaal schaalbaar | +| **Integratie** | FHIR-geïnspireerd datamodel voor interoperabiliteit | + +### Strategische Voordelen + +1. **Lagere administratielast** - Zorgverleners dicteren notities in natuurlijke taal; AI classificeert en structureert automatisch +2. **Proactieve ondersteuning** - Systeem suggereert vervolgacties op basis van klinische protocollen +3. **Snelle implementatie** - Cloud-native stack (Vercel + Supabase) zonder on-premise infrastructuur +4. **Toekomstbestendig** - Modulaire opzet maakt toevoeging van nieuwe functionaliteit eenvoudig + +### Risico's & Mitigatie + +| Risico | Mitigatie | +|--------|-----------| +| AI-hallucinaties | Drie-lagen architectuur met confidence scoring; fallback naar menselijke verificatie | +| Data privacy | Row Level Security op database niveau; geen PII in logs | +| Vendor lock-in | Open standaarden (FHIR, PostgreSQL); migratiepaden beschikbaar | + +--- + +## Technische Architectuur + +### Tech Stack + +| Component | Technologie | Motivatie | +|-----------|-------------|-----------| +| Frontend | Next.js 14 (App Router) | Server-side rendering, optimale SEO, moderne DX | +| Database | Supabase (PostgreSQL) | Managed database met ingebouwde auth en RLS | +| Auth | Supabase Auth + JWT | Industrie-standaard, SSR-compatibel | +| AI Classification | Claude API (Anthropic) | State-of-the-art NLP voor Nederlands | +| Speech-to-Text | Deepgram | Lage latency, hoge nauwkeurigheid | +| State Management | Zustand | Lightweight, TypeScript-native | +| Styling | Tailwind CSS + shadcn/ui | Consistente UI, snelle ontwikkeling | + +--- + +### Cortex: Drie-Lagen AI Architectuur + +Het hart van het systeem is **Cortex**, een intelligent command center dat natuurlijke taal omzet naar gestructureerde acties. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ GEBRUIKER INPUT │ +│ "Maak een dagnotitie voor Jan Bakker" │ +└─────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────┐ +│ LAYER 1: REFLEX ARC │ +│ Latency: <20ms │ +├─────────────────────────────────────────────────────────────────┤ +│ • Regex-gebaseerde pattern matching │ +│ • Weighted confidence scoring │ +│ • Verwerkt 70%+ van alle commando's lokaal │ +│ • Escaleert bij: confidence <0.7, multi-intent, ambiguïteit │ +└─────────────────────────────────────────────────────────────────┘ + ↓ (indien nodig) +┌─────────────────────────────────────────────────────────────────┐ +│ LAYER 2: ORCHESTRATOR │ +│ Model: Claude 3.5 Haiku │ +├─────────────────────────────────────────────────────────────────┤ +│ • Multi-intent detectie ("annuleer afspraak en maak notitie") │ +│ • Pronoun resolutie met actieve patiënt context │ +│ • Relatieve tijd parsing ("morgen", "volgende week") │ +│ • Context-aware entity extraction │ +└─────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────┐ +│ LAYER 3: NUDGE │ +│ Proactieve Suggesties │ +├─────────────────────────────────────────────────────────────────┤ +│ • Post-actie aanbevelingen op basis van V&VN protocollen │ +│ • Voorbeeld: Na wondnotitie → suggestie voor controle-interval │ +│ • Niet-intrusief: toast notifications met dismiss optie │ +└─────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────┐ +│ UI ARTIFACT BLOCKS │ +│ DagnotatieBlock / ZoekenBlock / OverdrachtBlock │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Intent Types + +| Intent | Beschrijving | Voorbeeld | +|--------|--------------|-----------| +| `dagnotitie` | Dagelijkse rapportage aanmaken | "Noteer dat mevrouw Jansen goed geslapen heeft" | +| `zoeken` | Patiënt opzoeken | "Zoek Jan Bakker" | +| `overdracht` | Handover bekijken | "Toon overdracht van gisteren" | +| `agenda_query` | Agenda raadplegen | "Wat staat er vandaag gepland?" | +| `create_appointment` | Afspraak inplannen | "Plan een intake voor morgen 14:00" | +| `cancel_appointment` | Afspraak annuleren | "Annuleer de afspraak van vrijdag" | +| `reschedule_appointment` | Afspraak verzetten | "Verzet de afspraak naar volgende week" | + +--- + +### Directory Structuur + +``` +mini-epd-prototype/ +├── app/ # Next.js App Router +│ ├── api/ # Backend API Routes +│ │ ├── cortex/ # AI Command Center +│ │ │ ├── classify/ # Intent classificatie +│ │ │ ├── chat/ # Streaming conversatie +│ │ │ ├── patients/ # Patiënt context +│ │ │ └── agenda/ # Agenda operaties +│ │ ├── reports/ # Rapportage CRUD +│ │ ├── overdracht/ # Handover + AI summaries +│ │ ├── patients/ # Patiënt data +│ │ ├── deepgram/ # Speech-to-text +│ │ └── fhir/ # FHIR endpoints +│ │ +│ ├── epd/ # EPD Modules (Protected) +│ │ ├── dashboard/ # Cortex Command Center +│ │ ├── verpleegrapportage/ # Nursing reports +│ │ │ └── rapportage/ # Timeline invoer +│ │ ├── patients/[id]/ # Patiënt dossier +│ │ ├── agenda/ # Kalender (FullCalendar) +│ │ └── clients/ # Cliëntenbeheer +│ │ +│ └── auth/ # Authenticatie +│ ├── login/ +│ └── reset-password/ +│ +├── lib/ # Shared Business Logic +│ ├── cortex/ # Drie-lagen AI systeem +│ │ ├── types.ts # Type definities +│ │ ├── reflex-classifier.ts # Layer 1: Pattern matching +│ │ ├── orchestrator.ts # Layer 2: Claude classificatie +│ │ ├── nudge.ts # Layer 3: Suggesties +│ │ ├── entity-extractor.ts # Entity extraction +│ │ ├── date-time-parser.ts # Datum/tijd parsing +│ │ └── hooks/ # React hooks +│ │ +│ ├── auth/ # Auth utilities +│ │ ├── server.ts # Server-side (API routes) +│ │ └── client.ts # Client-side +│ │ +│ ├── supabase/ # Database layer +│ │ ├── database.types.ts # Auto-generated types +│ │ ├── client.ts # Browser client +│ │ └── server.ts # SSR client +│ │ +│ └── types/ # Domain types +│ ├── report.ts # Rapportage types +│ └── overdracht.ts # Handover types +│ +├── components/ # React Components +│ ├── cortex/ # Cortex UI +│ │ ├── command-center/ # Main container +│ │ │ ├── command-center.tsx +│ │ │ ├── command-input.tsx # Voice/text input +│ │ │ ├── context-bar.tsx # Actieve patiënt +│ │ │ ├── canvas-area.tsx # Artifact rendering +│ │ │ └── offline-banner.tsx +│ │ ├── blocks/ # Intent-specifieke UI +│ │ │ ├── dagnotitie-block.tsx +│ │ │ ├── zoeken-block.tsx +│ │ │ └── overdracht-block.tsx +│ │ └── chat/ # Chat componenten +│ │ +│ └── ui/ # shadcn/ui (29 componenten) +│ ├── button.tsx +│ ├── dialog.tsx +│ └── ... +│ +├── stores/ # State Management +│ └── cortex-store.ts # Zustand store +│ +├── supabase/ # Database +│ └── migrations/ # SQL migrations +│ +└── docs/ # Documentatie + └── swift/ # Cortex specificaties +``` + +--- + +### Data Model + +#### Reports Table (Unified) + +Alle rapportages worden opgeslagen in één tabel met type-discriminatie: + +```sql +CREATE TABLE reports ( + id UUID PRIMARY KEY, + patient_id UUID REFERENCES patients(id), + type report_type NOT NULL, -- enum + content TEXT, + structured_data JSONB, -- type-specifieke velden + created_at TIMESTAMPTZ, + created_by UUID REFERENCES auth.users(id), + shift_date DATE, -- berekend: vóór 07:00 = vorige dag + deleted_at TIMESTAMPTZ, -- soft delete + ai_confidence FLOAT, + ai_reasoning TEXT +); +``` + +**Report Types:** +- `voortgang` - Voortgangsrapportage +- `observatie` - Klinische observatie +- `incident` - Incident melding +- `medicatie` - Medicatie gerelateerd +- `contact` - Contact met derden +- `crisis` - Crisis interventie +- `intake` - Intake notities +- `behandeladvies` - Behandeladvies +- `vrije_notitie` - Vrije tekst +- `verpleegkundig` - Verpleegkundige notitie (met categorie) + +**Verpleegkundig Categories:** +- `medicatie`, `adl`, `gedrag`, `incident`, `observatie` + +#### Shift Logic + +Rapporten aangemaakt vóór 07:00 worden toegewezen aan de vorige dag (nachtdienst handover): + +```typescript +function calculateShiftDate(createdAt: Date): Date { + const hour = createdAt.getHours(); + if (hour < 7) { + return subDays(createdAt, 1); + } + return createdAt; +} +``` + +**Diensten:** +| Dienst | Tijdvak | +|--------|---------| +| Nacht | 00:00 - 07:00 | +| Ochtend | 07:00 - 12:00 | +| Middag | 12:00 - 17:00 | +| Avond | 17:00 - 24:00 | + +--- + +### API Endpoints + +#### Cortex APIs + +| Method | Endpoint | Beschrijving | +|--------|----------|--------------| +| POST | `/api/cortex/classify` | Intent classificatie (Reflex + Orchestrator) | +| POST | `/api/cortex/chat` | Streaming chat (SSE) | +| GET | `/api/cortex/context` | Huidige gebruikerscontext | +| GET | `/api/cortex/patients/search` | Patiënt zoeken voor context | +| GET | `/api/cortex/agenda` | Dagagenda ophalen | +| POST | `/api/cortex/agenda/create` | Afspraak aanmaken | +| POST | `/api/cortex/agenda/cancel` | Afspraak annuleren | +| POST | `/api/cortex/agenda/reschedule` | Afspraak verzetten | + +#### Report APIs + +| Method | Endpoint | Beschrijving | +|--------|----------|--------------| +| GET | `/api/reports` | Lijst rapporten (filters: type, datum, patiënt) | +| POST | `/api/reports` | Rapport aanmaken | +| GET | `/api/reports/[id]` | Enkel rapport ophalen | +| PUT | `/api/reports/[id]` | Rapport bijwerken | +| DELETE | `/api/reports/[id]` | Soft delete | + +#### Overdracht APIs + +| Method | Endpoint | Beschrijving | +|--------|----------|--------------| +| GET | `/api/overdracht/patients` | Patiënten voor handover | +| GET | `/api/overdracht/[patientId]` | Handover data per patiënt | +| POST | `/api/overdracht/generate` | AI-gegenereerde samenvatting | + +--- + +### State Management + +**Zustand Store** (`stores/cortex-store.ts`): + +```typescript +interface CortexStore { + // Chat state + messages: ChatMessage[]; + pendingAction: IntentAction | null; + + // Context + activePatient: Patient | null; + currentShift: 'nacht' | 'ochtend' | 'middag' | 'avond'; + + // UI + openArtifacts: ArtifactInstance[]; + suggestions: NudgeSuggestion[]; + patientSidebarOpen: boolean; + + // Actions + addMessage(msg: ChatMessage): void; + setActivePatient(patient: Patient): void; + openArtifact(artifact: ArtifactInstance): void; + acceptSuggestion(id: string): void; + dismissSuggestion(id: string): void; +} +``` + +--- + +### Security + +#### Row Level Security (RLS) + +Alle tabellen hebben RLS policies die data toegang beperken tot geautoriseerde gebruikers: + +```sql +-- Voorbeeld: users kunnen alleen hun eigen patiënten zien +CREATE POLICY "Users can view assigned patients" + ON patients FOR SELECT + USING (auth.uid() IN ( + SELECT user_id FROM patient_assignments + WHERE patient_id = patients.id + )); +``` + +#### Authentication Flow + +``` +1. Login via /auth/login +2. Supabase Auth valideert credentials +3. JWT token in httpOnly cookie +4. Middleware refresht sessie bij elk request +5. API routes valideren via createClient() +6. RLS filtert data op database niveau +``` + +#### Input Validation + +Alle API endpoints gebruiken Zod schemas: + +```typescript +const CreateReportSchema = z.object({ + patient_id: z.string().uuid(), + type: z.enum(['voortgang', 'observatie', ...]), + content: z.string().min(20).max(5000), + structured_data: z.object({...}).optional() +}); +``` + +--- + +### Performance + +| Aspect | Implementatie | Target | +|--------|---------------|--------| +| Intent classificatie | Layer 1 Reflex | <20ms voor 70%+ requests | +| Chat responses | SSE streaming | First token <500ms | +| Patient search | Debounced input | 300ms debounce | +| Build optimization | Code splitting | Three.js in apart chunk | +| Database queries | Selective columns | Geen overbodige data | + +--- + +### Monitoring & Audit + +**AI Events Table:** + +```sql +CREATE TABLE ai_events ( + id UUID PRIMARY KEY, + kind TEXT, -- 'classify', 'summarize', 'chat' + request JSONB, + response JSONB, + duration_ms INTEGER, + created_at TIMESTAMPTZ, + user_id UUID +); +``` + +Elk AI-verzoek wordt gelogd voor: +- Compliance en audit trail +- Performance monitoring +- Model fine-tuning data + +--- + +### Feature Flags + +```typescript +// lib/config/feature-flags.ts +CORTEX_V2_ENABLED // Drie-lagen architectuur +CORTEX_MULTI_INTENT // Multi-intent detectie +CORTEX_NUDGE // Proactieve suggesties +CORTEX_LOGGING // Debug logging (dev only) +``` + +--- + +## Deployment + +### Infrastructuur + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Vercel │────▶│ Supabase │────▶│ PostgreSQL │ +│ (Frontend + │ │ (Auth) │ │ (Database) │ +│ API Routes) │ └─────────────────┘ └─────────────────┘ +└─────────────────┘ + │ + ▼ +┌─────────────────┐ ┌─────────────────┐ +│ Claude API │ │ Deepgram │ +│ (Anthropic) │ │ (Speech-to-Text)│ +└─────────────────┘ └─────────────────┘ +``` + +### Environment Variables + +```bash +# Supabase +NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... + +# AI Services +ANTHROPIC_API_KEY=sk-ant-... +DEEPGRAM_API_KEY=... + +# Feature Flags +NEXT_PUBLIC_CORTEX_V2=true +``` + +--- + +## Extensibility + +### Nieuw Intent Toevoegen + +1. Voeg toe aan `CortexIntent` type in `lib/cortex/types.ts` +2. Voeg Reflex patterns toe in `lib/cortex/reflex-classifier.ts` +3. Maak artifact block in `components/cortex/blocks/` +4. Update Orchestrator prompt (indien AI-driven) +5. Maak API endpoint (indien nodig) + +### Nieuw Report Type Toevoegen + +1. Voeg toe aan `REPORT_TYPES` enum in `lib/types/report.ts` +2. Voeg Zod schema toe voor validatie +3. Update UI componenten voor nieuw type +4. Voeg database migration toe indien nodig + +--- + +## Conclusie + +De Mini-EPD architectuur is ontworpen voor: + +- **Snelheid**: Drie-lagen AI met <20ms lokale verwerking +- **Schaalbaarheid**: Serverless, horizontaal schaalbaar +- **Veiligheid**: RLS, JWT, Zod validatie op alle lagen +- **Uitbreidbaarheid**: Modulaire opzet met duidelijke interfaces +- **Compliance**: Audit trail, soft deletes, Nederlandse foutmeldingen + +Het systeem is production-ready voor MVP deployment met duidelijke paden voor doorontwikkeling. diff --git a/docs/swift/prompt-architecture-diagram.json b/docs/swift/prompt-architecture-diagram.json new file mode 100644 index 0000000..bfb184f --- /dev/null +++ b/docs/swift/prompt-architecture-diagram.json @@ -0,0 +1,187 @@ +{ + "id": "architecture-diagram", + "name": "Architectuur Diagram — Drie-Lagen Intelligentie", + "version": "1.0", + "created": "2025-01-05", + "purpose": "Technische geloofwaardigheid voor ICT-managers en EPD-leveranciers", + "primary_audience": ["ICT-managers", "EPD-leveranciers"], + "core_message": "Doordacht, schaalbaar, veilig", + "format": { + "aspect_ratio": "4:3", + "resolution": "1920x1440px", + "file_type": "Vector (SVG/Figma)", + "alternatives": ["A4 landscape", "1920x1080px for slides"], + "use_cases": ["Technical documentation", "Whitepaper", "Integration guide", "Pitch deck"] + }, + "brand_colors": { + "layer1_reflex": "#0F766E", + "layer2_orchestrator": "#F59E0B", + "layer3_nudge": "#16A34A", + "background": "#F8FAFC", + "surface": "#FFFFFF", + "text_primary": "#0F172A", + "text_secondary": "#475569", + "border": "#E2E8F0" + }, + "diagram_spec": { + "layout": "vertical_flow_top_to_bottom", + "layers": [ + { + "id": "input", + "name": "Input Layer", + "name_nl": "Invoer", + "description": "Spraak en tekst invoer van zorgprofessional", + "example": "\"Maak notitie voor Jan\"", + "icons": ["microphone", "keyboard"], + "color": "neutral", + "height": "80px" + }, + { + "id": "reflex", + "name": "Layer 1: Reflex Arc", + "name_nl": "Laag 1: Reflex", + "tagline": "Snelle lokale verwerking", + "description": "Lokale pattern matching zonder AI-calls", + "coverage": "70% van alle requests", + "latency": "<20ms", + "color": "#0F766E", + "icon": "zap", + "intents": [ + { "id": "notitie", "label": "Notitie", "pattern": "notitie|noteer|schrijf op" }, + { "id": "zoeken", "label": "Zoeken", "pattern": "zoek|vind|waar is" }, + { "id": "agenda", "label": "Agenda", "pattern": "agenda|afspraak|planning" }, + { "id": "overdracht", "label": "Overdracht", "pattern": "overdracht|handover" } + ], + "escalation": { + "condition": "confidence < 0.7", + "target": "orchestrator", + "label": "Escaleer naar Layer 2" + }, + "height": "160px" + }, + { + "id": "orchestrator", + "name": "Layer 2: Orchestrator", + "name_nl": "Laag 2: Orchestrator", + "tagline": "Claude AI voor complexe gevallen", + "description": "Context-aware intent classificatie", + "model": "Claude 3.5 Haiku", + "color": "#F59E0B", + "icon": "brain", + "capabilities": [ + { + "id": "multi_intent", + "label": "Multi-intent", + "example": "\"annuleer afspraak én maak notitie\"", + "icon": "layers" + }, + { + "id": "pronoun_resolution", + "label": "Pronoun resolutie", + "example": "\"voor hem\" → actieve patiënt", + "icon": "user" + }, + { + "id": "relative_time", + "label": "Relatieve tijd", + "example": "\"morgen 14:00\" → 2025-01-06T14:00", + "icon": "calendar" + } + ], + "height": "180px" + }, + { + "id": "nudge", + "name": "Layer 3: Nudge", + "name_nl": "Laag 3: Nudge", + "tagline": "Proactieve suggesties", + "description": "Post-actie aanbevelingen op basis van zorgprotocollen", + "color": "#16A34A", + "icon": "lightbulb", + "example": { + "trigger": "Na wondnotitie", + "suggestion": "Plan wondcontrole over 3 dagen?", + "source": "V&VN richtlijnen" + }, + "height": "120px" + }, + { + "id": "output", + "name": "Output: Gestructureerde Zorgdata", + "name_nl": "Uitvoer: Gestructureerde Data", + "description": "FHIR-compatibele data naar EPD", + "color": "neutral", + "icon": "database", + "output_card": { + "fields": [ + { "label": "Patient", "value": "Jan Bakker" }, + { "label": "Type", "value": "Verpleegkundige notitie" }, + { "label": "Categorie", "value": "ADL" }, + { "label": "Inhoud", "value": "Goed geslapen, zelfstandig ontbeten" }, + { "label": "Dienst", "value": "Ochtend" }, + { "label": "Datum", "value": "2025-01-05" } + ] + }, + "integration": "EPD / FHIR", + "height": "160px" + } + ], + "connectors": { + "style": "arrow_down", + "color": "#CBD5E1", + "width": "2px" + } + }, + "style": { + "corners": "8px rounded", + "shadows": "0 2px 6px rgba(15,23,42,0.08)", + "typography": { + "headings": "font-weight: 600", + "body": "font-weight: 400", + "code": "font-family: monospace" + }, + "spacing": { + "layer_gap": "24px", + "inner_padding": "20px" + }, + "iconography": "Lucide or Phosphor icons", + "language": "Dutch labels" + }, + "platforms": { + "figma": { + "instructions": "Use Auto Layout for each layer. Create components for reusable elements. Use brand colors from variables.", + "template_structure": [ + "Frame: Architecture Diagram (1920x1440)", + " ├── Component: Input Layer", + " ├── Connector Arrow", + " ├── Component: Reflex Layer", + " ├── Connector Arrow (conditional)", + " ├── Component: Orchestrator Layer", + " ├── Connector Arrow", + " ├── Component: Nudge Layer", + " ├── Connector Arrow", + " └── Component: Output Layer" + ] + }, + "mermaid": { + "code": "flowchart TD\n A[\"🎤 Spraak / ⌨️ Tekst\"] --> B\n B[\"Layer 1: Reflex Arc\\n⚡ <20ms | 70% requests\"] --> C{Confidence < 0.7?}\n C -->|Nee| E\n C -->|Ja| D[\"Layer 2: Orchestrator\\n🧠 Claude AI\"]\n D --> E[\"Layer 3: Nudge\\n💡 Proactieve suggesties\"]\n E --> F[\"📋 Gestructureerde Zorgdata\\nEPD / FHIR\"]" + }, + "excalidraw": { + "style": "Hand-drawn for workshops", + "instructions": "Use rounded rectangles, hand-drawn arrows, sticky note aesthetic" + }, + "draw_io": { + "instructions": "Use flowchart shapes, custom colors from brand palette" + } + }, + "text_content": { + "title": "Cortex Drie-Lagen Architectuur", + "subtitle": "Van natuurlijke taal naar gestructureerde zorgdata", + "layer_descriptions": { + "reflex": "Snelle, lokale verwerking voor 70% van alle commando's. Geen externe API-calls, <20ms latency.", + "orchestrator": "Claude AI voor complexe gevallen: multi-intent, context-resolutie, tijdsparsing.", + "nudge": "Proactieve suggesties na acties, gebaseerd op V&VN zorgprotocollen." + }, + "footer": "FHIR-compatibel | WCAG AA toegankelijk | AVG-compliant" + } +} diff --git a/docs/swift/prompt-hero-visual.json b/docs/swift/prompt-hero-visual.json new file mode 100644 index 0000000..d7e3ce9 --- /dev/null +++ b/docs/swift/prompt-hero-visual.json @@ -0,0 +1,50 @@ +{ + "id": "hero-visual", + "name": "Hero Visual — Het Intelligente Zorgbrein", + "version": "1.0", + "created": "2025-01-05", + "purpose": "Eerste indruk op website/pitch deck. Moet vertrouwen wekken.", + "primary_audience": ["Iedereen"], + "core_message": "Technologie die zorg ondersteunt, niet vervangt", + "format": { + "aspect_ratio": "16:9", + "resolution": "1920x1080px", + "use_cases": ["Website hero", "Pitch deck opening", "LinkedIn banner"] + }, + "brand_colors": { + "primary": "#0F766E", + "accent": "#F59E0B", + "background": "#F8FAFC" + }, + "prompt_text": "A serene, professional healthcare visualization showing the concept of an 'intelligent layer' between caregivers and patient data. Center: A subtle, abstract neural network pattern in teal (#0F766E) forming a gentle arc or bridge shape. Left side: Soft silhouettes of healthcare professionals (nurse, doctor) speaking naturally, with subtle sound wave lines emanating from them. Right side: Clean, organized patient data cards and medical icons flowing into structured columns. The neural bridge transforms chaotic voice input into organized output. Style: Clean, minimal, Scandinavian healthcare aesthetic. Color palette: Teal primary (#0F766E), warm amber accents (#F59E0B), soft gray backgrounds (#F8FAFC). No screens or devices visible — focus on human-AI collaboration. Subtle depth with soft shadows, not flat design. Professional enough for hospital boardroom presentation. Mood: Calm, trustworthy, efficient, human-centered technology.", + "negative_prompt": "Robots, glowing brains, Matrix-style code, overly futuristic elements, stock photo aesthetics, cluttered, dark, dystopian, screens, devices, text, watermarks", + "composition": { + "center": "Abstract neural network pattern in teal forming gentle arc/bridge", + "left": "Healthcare professional silhouettes speaking, sound wave lines", + "right": "Organized patient data cards and medical icons in columns", + "transformation": "Chaotic voice input → organized structured output" + }, + "style": { + "aesthetic": "Clean, minimal, Scandinavian healthcare", + "depth": "Subtle shadows, not flat design", + "quality": "Hospital boardroom presentation quality" + }, + "mood": ["Calm", "Trustworthy", "Efficient", "Human-centered"], + "platforms": { + "midjourney_v6": { + "suffix": "--ar 16:9 --style raw --s 200 --c 15", + "full_prompt": "A serene, professional healthcare visualization showing the concept of an 'intelligent layer' between caregivers and patient data. Center: A subtle, abstract neural network pattern in teal (#0F766E) forming a gentle arc or bridge shape. Left side: Soft silhouettes of healthcare professionals speaking naturally, with subtle sound wave lines. Right side: Clean, organized patient data cards flowing into structured columns. Clean, minimal, Scandinavian healthcare aesthetic. Teal primary, warm amber accents, soft gray backgrounds. No screens or devices. Subtle depth with soft shadows. Calm, trustworthy, human-centered technology. --ar 16:9 --style raw --s 200 --c 15" + }, + "dalle_3": { + "style": "natural", + "full_prompt": "A serene, professional healthcare visualization showing an 'intelligent layer' between caregivers and patient data. In the center, a subtle abstract neural network pattern in teal (#0F766E) forms a gentle arc. On the left, soft silhouettes of healthcare professionals speak naturally with subtle sound waves. On the right, clean organized patient data cards flow into structured columns. Scandinavian healthcare aesthetic with teal primary color, warm amber accents (#F59E0B), and soft gray backgrounds (#F8FAFC). No screens or devices visible. Subtle shadows, not flat. Professional, calm, trustworthy mood." + }, + "stable_diffusion": { + "steps": 30, + "cfg_scale": 7, + "sampler": "DPM++ 2M Karras", + "full_prompt": "professional healthcare visualization, intelligent AI layer concept, abstract neural network pattern in teal, healthcare workers silhouettes speaking, sound waves, organized patient data cards, clean minimal scandinavian aesthetic, teal and amber color scheme, soft gray background, subtle shadows, professional medical, calm trustworthy mood, high quality, detailed", + "negative": "robots, glowing brains, matrix code, futuristic, stock photo, cluttered, dark, dystopian, screens, devices, text, watermark, low quality" + } + } +} diff --git a/docs/swift/prompt-value-flow.json b/docs/swift/prompt-value-flow.json new file mode 100644 index 0000000..f786130 --- /dev/null +++ b/docs/swift/prompt-value-flow.json @@ -0,0 +1,223 @@ +{ + "id": "value-flow", + "name": "Value Flow — Van Stem naar Structuur", + "version": "1.0", + "created": "2025-01-05", + "purpose": "Snel begrip voor zorgdirectie en zorgprofessionals. 'Ik snap het in 5 seconden.'", + "primary_audience": ["Zorgdirectie", "Zorgprofessionals"], + "core_message": "Van 2 minuten typen naar 8 seconden spreken", + "format": { + "aspect_ratio": "3:1", + "resolution": "1920x640px", + "variants": { + "static": { + "format": "PNG/SVG", + "use": "Website banner, email header" + }, + "animated": { + "format": "MP4/GIF", + "duration": "10 seconds loop", + "use": "Trade shows, LinkedIn, waiting room displays" + }, + "print": { + "format": "PDF", + "size": "A3 landscape", + "use": "Posters, handouts" + } + } + }, + "brand_colors": { + "step1": { + "primary": "#F59E0B", + "background": "#FFFBEB", + "description": "Warm, human, organic" + }, + "step2": { + "primary": "#0F766E", + "background": "#F0FDFA", + "description": "Teal, neural, processing" + }, + "step3": { + "primary": "#16A34A", + "background": "#ECFDF5", + "description": "Green, success, organized" + }, + "connectors": "#CBD5E1", + "text_primary": "#0F172A", + "text_secondary": "#475569" + }, + "key_metrics": [ + { + "value": "8 sec", + "comparison": "vs 2 min typen", + "meaning": "Tijdsbesparing per notitie", + "placement": "Step 1" + }, + { + "value": "<20ms", + "label": "verwerking", + "meaning": "Verwerkingssnelheid Cortex", + "placement": "Step 2" + }, + { + "value": "70%", + "label": "minder administratie", + "meaning": "Reductie administratieve last", + "placement": "Step 3" + } + ], + "steps": [ + { + "number": 1, + "id": "input", + "title": "Inspreken", + "subtitle": "Natuurlijke taal", + "icon": "user-voice", + "icon_alternatives": ["mic", "message-circle"], + "visual": "Healthcare worker silhouette speaking, sound waves emanating", + "example_input": { + "text": "\"Mevrouw Jansen heeft goed geslapen en zelfstandig ontbeten\"", + "context": "Verpleegkundige tijdens ochtendronde" + }, + "metric": { + "primary": "8 seconden", + "comparison": "vs 2 min typen", + "icon": "clock" + }, + "benefits": [ + "Geen formulieren invullen", + "Hands-free tijdens zorg", + "Natuurlijke workflow" + ], + "color_zone": { + "style": "warm, human, organic shapes", + "gradient": "from amber-50 to white" + } + }, + { + "number": 2, + "id": "processing", + "title": "Verwerking", + "subtitle": "Cortex AI", + "icon": "cpu", + "icon_alternatives": ["brain", "sparkles"], + "visual": "Abstract neural pattern, processing indicators, data extraction visualization", + "extracted_data": { + "patient": "Jansen", + "type": "Observatie", + "category": "ADL", + "shift": "Ochtend", + "sentiment": "Positief" + }, + "metric": { + "primary": "<20ms", + "label": "verwerking", + "icon": "zap" + }, + "benefits": [ + "Automatische classificatie", + "Geen handmatige invoer", + "Context-aware herkenning" + ], + "color_zone": { + "style": "teal, neural, technological but approachable", + "gradient": "teal-50 center glow" + } + }, + { + "number": 3, + "id": "output", + "title": "Resultaat", + "subtitle": "Gestructureerd in EPD", + "icon": "clipboard-check", + "icon_alternatives": ["file-check", "check-circle"], + "visual": "Clean data card with checkmark, organized fields, success state", + "output_card": { + "header": "Verpleegkundige notitie", + "fields": [ + { "label": "Patient", "value": "Mw. Jansen" }, + { "label": "Type", "value": "ADL Observatie" }, + { "label": "Dienst", "value": "Ochtend" }, + { "label": "Status", "value": "✓ Opgeslagen" } + ] + }, + "metric": { + "primary": "70%", + "label": "minder administratie", + "icon": "trending-down" + }, + "benefits": [ + "Direct klaar voor overdracht", + "Automatische rapportages", + "Data voor analytics" + ], + "color_zone": { + "style": "clean, organized, success", + "gradient": "to green-50" + } + } + ], + "connectors": { + "style": "curved arrows with gradient", + "animation": { + "type": "flow", + "direction": "left to right", + "duration": "1.5s per connector", + "easing": "ease-in-out" + }, + "labels": { + "1_to_2": "Cortex luistert", + "2_to_3": "Automatisch gestructureerd" + } + }, + "animation_spec": { + "total_duration": "10 seconds", + "loop": true, + "timeline": [ + { "time": "0-2s", "action": "Step 1 appears, sound waves animate" }, + { "time": "2-3s", "action": "Connector 1→2 animates, text flows" }, + { "time": "3-5s", "action": "Step 2 processing animation, data extraction" }, + { "time": "5-6s", "action": "Connector 2→3 animates" }, + { "time": "6-8s", "action": "Step 3 card assembles, checkmark appears" }, + { "time": "8-10s", "action": "Hold complete state, metrics pulse" } + ] + }, + "platforms": { + "figma": { + "instructions": "Create 3 frames for steps, use Smart Animate for prototype. Auto Layout horizontal with 48px gap.", + "components": [ + "Step Card (variant for each step)", + "Connector Arrow (with optional label)", + "Metric Badge", + "Benefit List" + ] + }, + "after_effects": { + "instructions": "Use shape layers for cards, animate along motion path for connectors. Lottie export for web.", + "effects": [ + "Sound wave: repeating sine wave path animation", + "Processing: rotating gradient, particle system", + "Success: scale bounce on checkmark" + ] + }, + "canva": { + "instructions": "Use brand kit for colors. Animate elements with built-in animations. Export as MP4 for social.", + "template": "Infographic horizontal layout" + }, + "html_css": { + "instructions": "CSS Grid 3-column layout. CSS animations for connectors. Intersection Observer for scroll-triggered animation.", + "framework": "Framer Motion or GSAP for React" + } + }, + "text_content": { + "headline": "Van Stem naar Structuur", + "subheadline": "Cortex transformeert natuurlijke taal naar gestructureerde zorgdata", + "cta": "Bekijk demo →", + "footer_stats": "8 sec inspreken • <20ms verwerking • 70% minder administratie" + }, + "accessibility": { + "alt_text": "Infographic die laat zien hoe Cortex spraak omzet naar gestructureerde EPD-data in drie stappen: 1) Zorgverlener spreekt notitie in (8 seconden), 2) Cortex AI verwerkt en classificeert (<20ms), 3) Gestructureerde data verschijnt in EPD (70% minder administratie)", + "contrast": "All text meets WCAG AA (4.5:1 minimum)", + "reduced_motion": "Provide static fallback for prefers-reduced-motion" + } +} diff --git a/docs/swift/visualisatie-prompts.json b/docs/swift/visualisatie-prompts.json new file mode 100644 index 0000000..8471d9f --- /dev/null +++ b/docs/swift/visualisatie-prompts.json @@ -0,0 +1,302 @@ +{ + "meta": { + "title": "Cortex EPD Visualisatie Prompts", + "version": "1.0", + "created": "2025-01-05", + "author": "Mini-EPD Team", + "purpose": "AI image generation prompts voor marketing en presentaties", + "target_audience": [ + "ICT-managers", + "Zorgdirectie", + "EPD-leveranciers", + "Zorgprofessionals" + ] + }, + "brand": { + "colors": { + "primary": { + "teal_700": "#0F766E", + "teal_800": "#115E59", + "teal_600": "#0D9488", + "teal_50": "#F0FDFA" + }, + "ai": { + "amber_500": "#F59E0B", + "amber_600": "#D97706", + "amber_50": "#FFFBEB" + }, + "success": { + "green_600": "#16A34A", + "green_50": "#ECFDF5" + }, + "neutral": { + "background": "#F8FAFC", + "surface": "#FFFFFF", + "text_primary": "#0F172A", + "text_secondary": "#475569" + } + }, + "tone": { + "do": [ + "Professioneel", + "Betrouwbaar", + "Evidence-based", + "Menselijk", + "Toegankelijk" + ], + "avoid": [ + "Hype-taal (revolutionair, disruptief)", + "Sci-fi aesthetiek", + "Robots of androiden", + "Matrix-style code", + "Stock photo look" + ] + } + }, + "prompts": [ + { + "id": "hero-visual", + "name": "Hero Visual — Het Intelligente Zorgbrein", + "purpose": "Eerste indruk op website/pitch deck. Moet vertrouwen wekken.", + "primary_audience": ["Iedereen"], + "core_message": "Technologie die zorg ondersteunt, niet vervangt", + "format": { + "aspect_ratio": "16:9", + "resolution": "1920x1080px", + "use_cases": ["Website hero", "Pitch deck opening", "LinkedIn banner"] + }, + "prompt": { + "scene": "A serene, professional healthcare visualization showing the concept of an 'intelligent layer' between caregivers and patient data.", + "composition": { + "center": "A subtle, abstract neural network pattern in teal (#0F766E) forming a gentle arc or bridge shape", + "left": "Soft silhouettes of healthcare professionals (nurse, doctor) speaking naturally, with subtle sound wave lines emanating from them", + "right": "Clean, organized patient data cards and medical icons flowing into structured columns", + "transformation": "The neural bridge transforms chaotic voice input into organized output" + }, + "style": { + "aesthetic": "Clean, minimal, Scandinavian healthcare", + "colors": "Teal primary (#0F766E), warm amber accents (#F59E0B), soft gray backgrounds (#F8FAFC)", + "elements": "No screens or devices visible — focus on human-AI collaboration", + "depth": "Subtle depth with soft shadows, not flat design", + "quality": "Professional enough for hospital boardroom presentation" + }, + "mood": ["Calm", "Trustworthy", "Efficient", "Human-centered"], + "negative_prompt": "Robots, glowing brains, Matrix-style code, overly futuristic elements, stock photo aesthetics, cluttered, dark, dystopian" + }, + "platforms": { + "midjourney_v6": "--ar 16:9 --style raw --s 200 --c 15", + "dalle_3": "Style: natural, photorealistic illustration", + "stable_diffusion": "Steps: 30, CFG: 7, Sampler: DPM++ 2M Karras" + } + }, + { + "id": "architecture-diagram", + "name": "Architectuur Diagram — Drie-Lagen Intelligentie", + "purpose": "Technische geloofwaardigheid voor ICT-managers en EPD-leveranciers", + "primary_audience": ["ICT-managers", "EPD-leveranciers"], + "core_message": "Doordacht, schaalbaar, veilig", + "format": { + "aspect_ratio": "4:3", + "resolution": "1920x1440px or A4 landscape", + "file_type": "Vector (SVG/Figma)", + "use_cases": ["Technical documentation", "Whitepaper", "Integration guide"] + }, + "prompt": { + "type": "technical_diagram", + "layout": "vertical_flow", + "layers": [ + { + "id": "input", + "name": "Input Layer", + "description": "Spraak en tekst invoer", + "example": "\"Maak notitie voor Jan\"", + "icons": ["microphone", "keyboard"], + "color": "neutral" + }, + { + "id": "reflex", + "name": "Layer 1: Reflex Arc", + "description": "Lokale pattern matching, geen AI-calls, 70% van requests", + "latency": "<20ms", + "color": "#0F766E", + "icon": "lightning", + "intents": ["notitie", "zoeken", "agenda", "overdracht"], + "escalation_rule": "Confidence < 0.7 → Escaleer naar Layer 2" + }, + { + "id": "orchestrator", + "name": "Layer 2: Orchestrator", + "description": "Complexe intent classificatie met Claude AI", + "color": "#F59E0B", + "icon": "brain", + "capabilities": [ + "Multi-intent: \"annuleer afspraak én maak notitie\"", + "Pronoun resolutie: \"voor hem\" → actieve patiënt", + "Relatieve tijd: \"morgen 14:00\" → ISO datetime" + ] + }, + { + "id": "nudge", + "name": "Layer 3: Nudge", + "description": "Post-actie suggesties op basis van zorgprotocollen", + "color": "#16A34A", + "icon": "lightbulb", + "example": "Na wondnotitie → \"Plan wondcontrole over 3 dagen?\"", + "source": "V&VN richtlijnen" + }, + { + "id": "output", + "name": "Output: Gestructureerde Zorgdata", + "description": "FHIR-compatibele data naar EPD", + "color": "neutral", + "fields": ["Patient", "Type", "Categorie", "Inhoud", "Dienst", "Datum"], + "integration": "EPD / FHIR" + } + ], + "style": { + "corners": "rounded (8px)", + "shadows": "subtle, professional", + "typography": "clean sans-serif", + "language": "Dutch labels, technical terms where appropriate", + "iconography": "Lucide or Phosphor icons" + } + }, + "platforms": { + "figma": "Use Auto Layout, Components for each layer", + "mermaid": "flowchart TD with custom styling", + "excalidraw": "Hand-drawn style for workshops" + } + }, + { + "id": "value-flow", + "name": "Value Flow — Van Stem naar Structuur", + "purpose": "Snel begrip voor zorgdirectie en zorgprofessionals", + "primary_audience": ["Zorgdirectie", "Zorgprofessionals"], + "core_message": "Van 2 minuten typen naar 8 seconden spreken", + "format": { + "aspect_ratio": "3:1", + "resolution": "1920x640px", + "variants": { + "static": "PNG/SVG for website banner", + "animated": "10-second loop for trade shows / LinkedIn", + "print": "A3 landscape for posters" + } + }, + "prompt": { + "type": "infographic", + "layout": "horizontal_3_step", + "steps": [ + { + "number": 1, + "title": "Inspreken", + "subtitle": "Natuurlijke taal", + "icon": "healthcare_worker_speaking", + "example_input": "\"Mevrouw Jansen heeft goed geslapen en zelfstandig ontbeten\"", + "metric": { + "value": "8 seconden", + "comparison": "vs 2 min typen" + }, + "benefits": [ + "Geen formulieren", + "Hands-free", + "Tijdens zorg" + ], + "color_zone": "warm, human, organic shapes" + }, + { + "number": 2, + "title": "Verwerking", + "subtitle": "Cortex AI", + "icon": "brain_processing", + "extracted_data": { + "patient": "Jansen", + "type": "Observatie", + "category": "ADL", + "shift": "Ochtend" + }, + "metric": { + "value": "<20ms", + "label": "verwerking" + }, + "benefits": [ + "Automatische classificatie", + "Geen handmatige invoer", + "Context-aware" + ], + "color_zone": "teal center (#0F766E), neural aesthetic" + }, + { + "number": 3, + "title": "Resultaat", + "subtitle": "Gestructureerd in EPD", + "icon": "clipboard_check", + "output_card": { + "fields": ["Patient", "Type", "Categorie", "Status"], + "status": "success_checkmark" + }, + "metric": { + "value": "70%", + "label": "minder administratie" + }, + "benefits": [ + "Klaar voor overdracht", + "Rapportages", + "Analytics" + ], + "color_zone": "clean, organized, success green (#16A34A)" + } + ], + "connectors": { + "style": "arrows with gradient", + "animation": "subtle flow animation for motion version" + }, + "style": { + "gradient": "warm (left) → teal (center) → cool/green (right)", + "typography": "large, readable at 50% zoom", + "data_viz": "highlight key metrics prominently" + } + }, + "key_metrics": [ + { + "metric": "8 sec vs 2 min", + "meaning": "Tijdsbesparing per notitie" + }, + { + "metric": "<20ms", + "meaning": "Verwerkingssnelheid" + }, + { + "metric": "70%", + "meaning": "Reductie administratieve last" + } + ], + "platforms": { + "after_effects": "Use shape layers, animate along path", + "figma": "Smart Animate for prototype", + "canva": "Use brand kit colors" + } + } + ], + "usage_guidelines": { + "website": { + "hero": "hero-visual", + "how_it_works": "value-flow", + "technical_page": "architecture-diagram" + }, + "pitch_deck": { + "slide_1": "hero-visual (title slide)", + "slide_3": "value-flow (the problem we solve)", + "slide_5": "architecture-diagram (how it works)" + }, + "trade_show": { + "banner": "hero-visual (large format)", + "screen": "value-flow (animated loop)", + "handout": "architecture-diagram (A4 print)" + }, + "linkedin": { + "company_banner": "hero-visual (1128x191 crop)", + "post_image": "value-flow (1200x627)", + "article_header": "architecture-diagram (simplified)" + } + } +} diff --git a/docs/templates/aispeedrun-manifesto.html b/docs/templates/aispeedrun-manifesto.html deleted file mode 100644 index fe0226b..0000000 --- a/docs/templates/aispeedrun-manifesto.html +++ /dev/null @@ -1,756 +0,0 @@ - - - - - - AI Speedrun - Het Software Manifesto - - - - - - - - - - - - - -
-
-

- Software
- is
- Kapot. -

-

- En iedereen weet het. -

-
-
↓ Scroll
-
- - -
-
-
-

- Ik werk al 15 jaar in de GGZ. -

-

- PinkRoccade, Nedap, Zorgdomein, Caress—ik heb ze allemaal voorbij zien komen. -

-
- -
-
-

De Belofte

-
    -
  • €100.000/jaar licentiekosten
  • -
  • 6 maanden implementatie
  • -
  • €50.000 setup & training
  • -
  • 5-jarig contract
  • -
  • "Alles wat je nodig hebt"
  • -
-
- -
- -
-

De Realiteit

-
    -
  • Behandelaren haten de workflow
  • -
  • IT worstelt met integraties
  • -
  • 200 features, 20 gebruikt
  • -
  • 18 maanden voor een "kleine" aanpassing
  • -
  • Vendor lock-in tot in de eeuwigheid
  • -
-
-
- -
-
€500.000+
-

Over 5 jaar. Voor software die niet past.

-
-
-
- - -
-
-

- Als behandelaar vulde ik formulieren in die niemand las. -

-

- Als product owner probeerde ik systemen te fixen die fundamenteel broken waren. -

-

- Als consultant zag ik organisaties worstelen met exact dezelfde problemen. -

- -
- "Dit moet toch sneller kunnen?" -
- -

- Telkens dezelfde vraag. Telkens hetzelfde antwoord: "Nee, zo werkt het nu eenmaal." -

- -

- Bullshit. -

-
-
- - -
-
-

NU KAN HET WEL

-

- 4 weken. €50/maand. Klaar. -

-
- -
-
- 1 -

AI-Powered Intake

-

45 minuten gesprek → 2 minuten samenvatting

- WEEK 1 -
- -
- 2 -

DSM-5 Suggesties

-

Automatische diagnose-ondersteuning

- WEEK 2 -
- -
- 3 -

Behandelplannen

-

AI genereert, jij verfijnt

- WEEK 3 -
- -
- 4 -

Live & Werkend

-

Geen jarenlange implementatie

- WEEK 4 -
-
-
- - -
-
-

- Build in Public -

-

- Geen geheimen. Alles op LinkedIn. Live. -

- -
-
-
01
-

Setup

-

Database, Auth, UI

-
-
-
02
-

AI Core

-

Claude integratie

-
-
-
03
-

Workflows

-

Intake → Plan

-
-
-
04
-

Launch

-

Live demo

-
-
-
-
- - -
-
-

- Stop met
- wachten op
- verandering -

- -

- Bouw hem gewoon zelf. In 4 weken. -

- - -
-
- - -
- -
- - - - diff --git a/public/images/87e5e820-ff45-4aab-901d-3d7cc778e7ac.jpeg b/public/images/87e5e820-ff45-4aab-901d-3d7cc778e7ac.jpeg new file mode 100644 index 0000000..92883cb Binary files /dev/null and b/public/images/87e5e820-ff45-4aab-901d-3d7cc778e7ac.jpeg differ