diff --git a/components/cortex/command-center/command-center.tsx b/components/cortex/command-center/command-center.tsx index 6bdc6a5..c0ea44a 100644 --- a/components/cortex/command-center/command-center.tsx +++ b/components/cortex/command-center/command-center.tsx @@ -17,11 +17,13 @@ */ import { useEffect, useCallback, useRef } from 'react'; +import { useRouter } from 'next/navigation'; import { AnimatePresence } from 'framer-motion'; import { useCortexStore } from '@/stores/cortex-store'; import { cn } from '@/lib/utils'; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import { useMediaQuery } from '@/hooks/use-media-query'; +import { useToast } from '@/hooks/use-toast'; import { ContextBar } from './context-bar'; import { OfflineBanner } from './offline-banner'; import { NudgeToast } from './nudge-toast'; @@ -31,14 +33,30 @@ import { ArtifactArea } from '../artifacts/artifact-area'; import { getArtifactTitle } from '../artifacts/artifact-container'; import { routeIntentToArtifact } from '@/lib/cortex/action-parser'; import { isFeatureEnabled } from '@/lib/config/feature-flags'; +import type { ExtractedEntities } from '@/lib/cortex/types'; + +// Intake tab mapping for navigation +const INTAKE_TAB_PATHS: Record = { + contacts: 'contacts', + kindcheck: 'kindcheck', + risk: 'risk', + anamnese: 'anamnese', + examination: 'examination', + rom: 'rom', + diagnosis: 'diagnosis', + behandeladvies: 'behandeladvies', +}; export function CommandCenter() { + const router = useRouter(); + const { toast } = useToast(); const { closeAllArtifacts, openArtifacts, openArtifact, pendingAction, setPendingAction, + activePatient, // Nudge state (E4) suggestions, acceptSuggestion, @@ -87,11 +105,55 @@ export function CommandCenter() { }, [handleKeyDown]); // E3.S6 + E4.S2: Handle pending actions from chat (artifact opening) + // E5.S2: Handle intake_navigeer navigation useEffect(() => { if (!pendingAction) return; console.log('[CommandCenter] Processing pending action:', pendingAction); + // E5.S2: Handle intake_navigeer intent - navigate instead of opening artifact + if (pendingAction.intent === 'intake_navigeer') { + const entities = pendingAction.entities as ExtractedEntities; + const navigationTarget = entities.navigationTarget; + + if (!activePatient) { + toast({ + variant: 'destructive', + title: 'Geen patiënt geselecteerd', + description: 'Selecteer eerst een patiënt om te navigeren.', + }); + setPendingAction(null); + return; + } + + // For now, we need an intake ID. Try to get it from entities or navigate to intakes list + const tabPath = navigationTarget ? INTAKE_TAB_PATHS[navigationTarget] : null; + + if (tabPath) { + // Navigate to the patient's intakes page with a toast + // Note: Full navigation to specific intake tab requires intakeId + // For MVP, navigate to patient intakes page + const url = `/epd/patients/${activePatient.id}/intakes`; + + toast({ + title: 'Navigeren...', + description: `Naar ${navigationTarget} sectie`, + }); + + console.log('[CommandCenter] Navigating to:', url, 'target:', navigationTarget); + router.push(url); + } else { + toast({ + variant: 'destructive', + title: 'Navigatie mislukt', + description: 'Onbekende sectie. Probeer: risico, diagnose, anamnese.', + }); + } + + setPendingAction(null); + return; + } + // Check if action has artifact data if (pendingAction.artifact) { const { type, prefill } = pendingAction.artifact; @@ -130,7 +192,7 @@ export function CommandCenter() { } setPendingAction(null); - }, [pendingAction, openArtifact, setPendingAction]); + }, [pendingAction, openArtifact, setPendingAction, activePatient, router, toast]); return (
diff --git a/docs/intent/intake-intent-proces/fo-to-bouwplan-intake-blocks-mvp.md b/docs/intent/intake-intent-proces/fo-to-bouwplan-intake-blocks-mvp.md new file mode 100644 index 0000000..7b5f599 --- /dev/null +++ b/docs/intent/intake-intent-proces/fo-to-bouwplan-intake-blocks-mvp.md @@ -0,0 +1,871 @@ +# FO/TO/Bouwplan: Cortex Intake Blocks MVP + +**Projectnaam:** Cortex Intake Blocks - MVP +**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 document beschrijft de **functionele specificaties**, **technische architectuur** en **bouwplan** voor de MVP van Cortex Intake Blocks. Het combineert FO, TO en Bouwplan in één referentiedocument. + +### 1.2 Context + +De Cortex Command Center heeft werkende intents voor dagnotities, zoeken en overdracht. De **26 intake-gerelateerde intents** ontbreken volledig (zie `gap-analyse-intake-cortex.md`). + +Dit document beschrijft de **MVP fase**: de 4 meest waardevolle blocks die het intake-proces via spraak ontsluiten. + +### 1.3 Relatie met andere documenten + +| Document | Relatie | +|----------|---------| +| `intake-process-intents.md` | Volledige intent definities (26 intents) | +| `gap-analyse-intake-cortex.md` | Analyse wat ontbreekt | +| `fo-to-cortex-shared-components.md` | Shared components (gebouwd) | +| `bouwplan-cortex-shared-components.md` | Bouwplan shared components (afgerond) | + +### 1.4 Scope + +**In scope (MVP):** +- `IntakeStatusBlock` - Checklist voortgang +- `RisicoBlock` - Risico's met severity levels +- `DiagnoseBlock` - Diagnoses lijst +- `intake_navigeer` intent - Navigatie naar tabs + +**Buiten scope (Fase 2+):** +- ScreeningBlock, KindcheckBlock, AnamneseBlock +- BehandeladviesBlock, IntakeListBlock +- IntakeSamenvattingBlock (AI) +- Actie-intents (toevoegen via spraak) + +--- + +## 1.5 Roadmap: Alle Intake Fases + +> **Let op:** Dit MVP dekt alleen Fase 3. De overige fases staan hieronder gepland voor toekomstige iteraties. + +### Overzicht Fases + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ INTAKE PROCES ROADMAP │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │ +│ │ FASE 1 │ │ FASE 2 │ │ FASE 3 │ │ FASE 4 │ │ +│ │ SCREENING │───▶│ INTAKE START │───▶│ UITVOERING │───▶│ AFSLUITEN │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ ⏳ Later │ │ ⏳ Later │ │ ✅ MVP │ │ ⏳ Later │ │ +│ │ 4 intents │ │ 2 intents │ │ 4 intents │ │ 3 intents │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ └───────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Fase 1: Screening (⏳ Toekomstig) + +| Intent | Type | Beschrijving | Block | +|--------|------|--------------|-------| +| `screening_query` | Query | Toon screening overzicht | ScreeningBlock | +| `hulpvraag_invoer` | Actie | Hulpvraag invullen | - | +| `screening_besluit` | Actie | Geschikt/niet geschikt | - | +| `screening_activiteit` | Actie | Activiteit toevoegen | - | + +**Prioriteit:** Middel (eenmalige actie per patiënt) + +### Fase 2: Intake Starten (⏳ Toekomstig) + +| Intent | Type | Beschrijving | Block | +|--------|------|--------------|-------| +| `intake_starten` | Actie | Nieuwe intake aanmaken | NewIntakeForm | +| `intake_lijst` | Query | Alle intakes van patiënt | IntakeListBlock | + +**Prioriteit:** Middel (eenmalige actie per intake) + +### Fase 3: Intake Uitvoering (✅ MVP) + +| Intent | Type | Beschrijving | Block | Status | +|--------|------|--------------|-------|--------| +| `intake_status` | Query | Checklist voortgang | IntakeStatusBlock | ✅ MVP | +| `intake_navigeer` | Navigatie | Naar specifieke tab | - | ✅ MVP | +| `risico_query` | Query | Risico's tonen | RisicoBlock | ✅ MVP | +| `diagnose_query` | Query | Diagnoses tonen | DiagnoseBlock | ✅ MVP | +| `kindcheck_query` | Query | Kindcheck status | KindcheckBlock | ⏳ Fase 3b | +| `anamnese_query` | Query | Anamnese overzicht | AnamneseBlock | ⏳ Fase 3b | +| `behandeladvies_query` | Query | Behandeladvies | BehandeladviesBlock | ⏳ Fase 3b | +| `contacten_query` | Query | Contactmomenten | - | ⏳ Fase 3c | +| `onderzoeken_query` | Query | Onderzoeken | - | ⏳ Fase 3c | +| `rom_query` | Query | ROM scores | - | ⏳ Fase 3c | +| `*_toevoegen` (6x) | Actie | Data invoeren via spraak | - | ⏳ Fase 3d | + +### Fase 4: Intake Afsluiten (⏳ Toekomstig) + +| Intent | Type | Beschrijving | Block | +|--------|------|--------------|-------| +| `intake_afsluiten` | Actie | Intake afronden | - | +| `intake_samenvatting` | Query | AI-samenvatting | IntakeSamenvattingBlock | +| `intake_checklist` | Query | Wat nog te doen | IntakeStatusBlock (hergebruik) | + +**Prioriteit:** Middel-Hoog (AI-samenvatting is waardevol) + +### Iteratie Planning + +| Iteratie | Fases | Intents | Geschatte effort | +|----------|-------|---------|------------------| +| **MVP (nu)** | 3a | 4 | 16 uur | +| Iteratie 2 | 3b | +3 | 8 uur | +| Iteratie 3 | 1 + 4 | +7 | 12 uur | +| Iteratie 4 | 2 + 3c | +5 | 10 uur | +| Iteratie 5 | 3d (acties) | +6 | 16 uur | +| **Totaal** | Alle | 26 | ~62 uur | + +--- + +## 2. Overzicht MVP Blocks + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ CORTEX MVP │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ "Wat moet ik nog doen?" "Wat zijn de risico's?" │ +│ ↓ ↓ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ IntakeStatus │ │ RisicoBlock │ │ +│ │ Block │ │ │ │ +│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ +│ │ │ ✅ Algemeen │ │ │ │Suïcide [M] │ │ │ +│ │ │ ❌ Kindcheck│ │ │ │Agressie [L] │ │ │ +│ │ │ ❌ Risico │ │ │ └─────────────┘ │ │ +│ │ │ ✅ Diagnose │ │ │ │ │ +│ │ └─────────────┘ │ │ [Bekijk] [+] │ │ +│ └─────────────────┘ └─────────────────┘ │ +│ │ +│ "Welke diagnoses?" "Ga naar risicotaxatie" │ +│ ↓ ↓ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ DiagnoseBlock │ │ Navigation │ │ +│ │ │ │ (geen block) │ │ +│ │ ┌─────────────┐ │ │ │ │ +│ │ │F32.1 Depr. │ │ │ router.push() │ │ +│ │ │F41.1 Angst │ │ │ naar intake tab │ │ +│ │ └─────────────┘ │ │ │ │ +│ │ [Bekijk] [+] │ └─────────────────┘ │ +│ └─────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 3. User Stories + +| ID | Rol | Doel / Actie | Verwachte waarde | Prioriteit | +|----|-----|--------------|------------------|------------| +| US-IB-01 | Behandelaar | "Wat moet ik nog doen?" vragen | Ziet checklist van intake voortgang | Hoog | +| US-IB-02 | Behandelaar | "Wat zijn de risico's?" vragen | Ziet risico's met severity levels | Hoog | +| US-IB-03 | Behandelaar | "Welke diagnoses heeft Jan?" vragen | Ziet diagnoses met ICD-10 codes | Hoog | +| US-IB-04 | Behandelaar | "Ga naar diagnose" zeggen | Navigeert direct naar diagnose tab | Hoog | +| US-IB-05 | Behandelaar | Op "Bekijk in dossier" klikken | Opent volledige EPD view | Middel | +| US-IB-06 | Behandelaar | Zien welke secties verplicht zijn | Weet wat minimaal ingevuld moet | Middel | + +--- + +## 4. Functionele Specificaties per Block + +### 4.1 IntakeStatusBlock + +**Intent:** `intake_status` +**Trigger voorbeelden:** "Wat moet ik nog doen?", "Is de intake compleet?", "Intake checklist" + +**Functioneel gedrag:** + +1. **Context check:** Block vereist actieve intake (intakeId) +2. **Data laden:** Haal completion status op per sectie +3. **Weergave:** + - Progress bar met percentage + - Lijst van alle 9 secties met status (✅/❌) + - Verplichte secties gemarkeerd + - Onvolledige verplichte secties highlighted (amber) +4. **Interactie:** + - Klik op sectie → navigeer naar die tab in EPD + - "Intake afsluiten" knop (disabled als niet compleet) + +**Visueel:** +``` +┌─────────────────────────────────────────────────────┐ +│ Intake Status - Jan de Vries [X] │ +├─────────────────────────────────────────────────────┤ +│ │ +│ Voortgang 6/9 (67%) │ +│ [████████████░░░░░░] │ +│ │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ ⚠️ Nog te voltooien (3) │ │ +│ │ │ │ +│ │ Kindcheck [>] │ │ +│ │ Risicotaxatie [>] │ │ +│ │ Behandeladvies [>] │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ +│ ✅ Algemeen │ +│ ✅ Contactmomenten │ +│ ❌ Kindcheck [Verplicht] │ +│ ❌ Risicotaxatie [Verplicht] │ +│ ✅ Anamnese │ +│ ✅ Onderzoeken │ +│ ✅ ROM │ +│ ✅ Diagnose │ +│ ❌ Behandeladvies [Verplicht] │ +│ │ +└─────────────────────────────────────────────────────┘ +``` + +**States:** +- Loading: `BlockLoading` met "Status laden..." +- Error: `BlockError` met retry +- No intake: `BlockEmpty` met "Geen actieve intake gevonden" +- Complete: Groene banner "Intake is compleet!" + +--- + +### 4.2 RisicoBlock + +**Intent:** `risico_query` +**Trigger voorbeelden:** "Wat zijn de risico's?", "Risico's van Jan", "Toon risicotaxatie" + +**Functioneel gedrag:** + +1. **Context check:** Block vereist actieve patient (patientId) en intake (intakeId) +2. **Data laden:** Haal risico assessments op +3. **Weergave:** + - Lijst van risico's met type en severity level + - Color-coded badges (groen/oranje/rood) + - Datum en beoordelaar per risico +4. **Interactie:** + - "Bekijk in dossier" → navigeer naar risicotaxatie tab + - "Toevoegen" → navigeer naar risicotaxatie tab (fase 2: inline toevoegen) + +**Risico levels en kleuren:** +| Level | Badge | Kleur | +|-------|-------|-------| +| Laag | `success` | Groen | +| Gemiddeld/Matig | `warning` | Oranje | +| Hoog | `danger` | Rood | +| Zeer hoog | `danger` | Rood | + +**Visueel:** +``` +┌─────────────────────────────────────────────────────┐ +│ Risicotaxatie [X] │ +├─────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ ⚠️ Huidige risico's (3) │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ Suïcidaliteit [Matig] │ │ │ +│ │ │ 12 jan 2026 • Dr. Jansen │ │ │ +│ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ Agressie [Laag] │ │ │ +│ │ │ 10 jan 2026 • Dr. Bakker │ │ │ +│ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ Zelfverwaarlozing [Laag] │ │ │ +│ │ │ 10 jan 2026 • Dr. Bakker │ │ │ +│ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ +│ ───────────────────────────────────────────────── │ +│ [🔗 Bekijk in dossier] [+ Toevoegen] │ +│ │ +└─────────────────────────────────────────────────────┘ +``` + +**States:** +- Loading: `BlockLoading` met "Risico's laden..." +- Error: `BlockError` met retry +- No patient: `BlockEmpty` met "Selecteer eerst een patiënt" +- No risks: `BlockEmpty` met "Geen risico's geregistreerd" + actie + +--- + +### 4.3 DiagnoseBlock + +**Intent:** `diagnose_query` +**Trigger voorbeelden:** "Welke diagnoses?", "Diagnose van Jan", "Toon diagnoses" + +**Functioneel gedrag:** + +1. **Context check:** Block vereist patient en intake +2. **Data laden:** Haal diagnoses op met ICD-10 codes +3. **Weergave:** + - Hoofddiagnose vs bijdiagnoses + - ICD-10 code + omschrijving + - Datum vastgesteld +4. **Interactie:** + - "Bekijk in dossier" → navigeer naar diagnose tab + - "Toevoegen" → navigeer naar diagnose tab + +**Visueel:** +``` +┌─────────────────────────────────────────────────────┐ +│ Diagnoses [X] │ +├─────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ 🏥 Hoofddiagnose │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ F32.1 Depressieve stoornis, matig │ │ │ +│ │ │ 15 jan 2026 │ │ │ +│ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ 📋 Nevendiagnoses (2) │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ F41.1 Gegeneraliseerde angststoornis │ │ │ +│ │ │ 15 jan 2026 │ │ │ +│ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────┐ │ │ +│ │ │ Z73.0 Burnout │ │ │ +│ │ │ 12 jan 2026 │ │ │ +│ │ └─────────────────────────────────────┘ │ │ +│ │ │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ +│ ───────────────────────────────────────────────── │ +│ [🔗 Bekijk in dossier] [+ Toevoegen] │ +│ │ +└─────────────────────────────────────────────────────┘ +``` + +--- + +### 4.4 Intake Navigatie (geen block) + +**Intent:** `intake_navigeer` +**Trigger voorbeelden:** "Ga naar diagnose", "Open kindcheck", "Naar risicotaxatie" + +**Functioneel gedrag:** + +1. **Context check:** Vereist actieve patient en intake +2. **Tab mapping:** Parse target tab uit input +3. **Navigatie:** `router.push()` naar correcte URL +4. **Feedback:** Toast "Navigeren naar [tab]..." + +**Tab mapping:** +| Input keywords | Target tab | URL path | +|----------------|------------|----------| +| algemeen, basis | algemeen | `/intakes/[id]` | +| contact, gesprek | contactmomenten | `/intakes/[id]/contacts` | +| kindcheck, kinderen | kindcheck | `/intakes/[id]/kindcheck` | +| risico, risicotaxatie | risicotaxatie | `/intakes/[id]/risks` | +| anamnese, voorgeschiedenis | anamnese | `/intakes/[id]/anamnese` | +| onderzoek | onderzoeken | `/intakes/[id]/examinations` | +| rom, vragenlijst | rom | `/intakes/[id]/rom` | +| diagnose | diagnose | `/intakes/[id]/diagnosis` | +| behandeladvies, advies | behandeladvies | `/intakes/[id]/advice` | + +--- + +## 5. Technische Architectuur + +### 5.1 Component Structuur + +``` +components/cortex/blocks/ +├── block-container.tsx ✅ Bestaat +├── intake-status-block.tsx 🆕 Nieuw +├── risico-block.tsx 🆕 Nieuw +├── diagnose-block.tsx 🆕 Nieuw +└── ... (bestaande blocks) + +lib/cortex/ +├── types.ts 📝 Update (nieuwe intents) +├── reflex-classifier.ts 📝 Update (nieuwe patterns) +├── handlers/ +│ └── intake-navigator.ts 🆕 Nieuw (navigatie handler) +└── hooks/ + ├── use-block-data.ts ✅ Bestaat (shared components) + └── use-intake-context.ts ✅ Bestaat (shared components) + +app/api/cortex/ +├── intake/ +│ ├── [intakeId]/ +│ │ ├── status/route.ts 🆕 Nieuw +│ │ ├── risks/route.ts 🆕 Nieuw +│ │ └── diagnoses/route.ts 🆕 Nieuw +``` + +### 5.2 Data Flow + +``` +User Input Cortex EPD + │ │ │ + │ "Wat zijn de risico's?" │ │ + │ ─────────────────────────> │ + │ │ │ + │ Reflex Classifier │ + │ intent: risico_query │ + │ confidence: 0.95 │ + │ │ │ + │ Open RisicoBlock │ + │ │ │ + │ │ GET /api/cortex/intake/ │ + │ │ {id}/risks │ + │ │ ─────────────────────────────> + │ │ │ + │ │ getRiskAssessments() │ + │ │ <───────────────────────────── + │ │ │ + │ Render RisicoBlock │ │ + │ <───────────────────────── │ +``` + +### 5.3 Bestaande Data Functies + +Deze server actions bestaan al en kunnen hergebruikt worden: + +| Functie | Bestand | Gebruikt door | +|---------|---------|---------------| +| `getIntakeById()` | `lib/actions/intake-actions.ts` | IntakeStatusBlock | +| `getRiskAssessments()` | `lib/actions/intake-actions.ts` | RisicoBlock | +| `getDiagnoses()` | `lib/actions/intake-actions.ts` | DiagnoseBlock | +| `getKindcheck()` | `lib/actions/intake-actions.ts` | IntakeStatusBlock | +| `getTreatmentAdvice()` | `lib/actions/intake-actions.ts` | IntakeStatusBlock | + +--- + +## 6. API Ontwerp + +### 6.1 Nieuwe Endpoints + +#### GET `/api/cortex/intake/[intakeId]/status` + +**Doel:** Intake completion status voor IntakeStatusBlock + +**Response:** +```typescript +interface IntakeStatusResponse { + intakeId: string; + intakeTitle: string; + patientId: string; + patientName: string; + completedCount: number; + totalCount: number; + sections: Array<{ + key: string; // 'algemeen' | 'kindcheck' | etc. + label: string; // 'Kindcheck' + completed: boolean; + required: boolean; + path: string; // 'kindcheck' (for URL) + }>; +} +``` + +**Logica per sectie:** +| Sectie | Completed wanneer | Verplicht | +|--------|-------------------|-----------| +| algemeen | Intake bestaat | ✅ | +| contactmomenten | ≥1 contact | ❌ | +| kindcheck | `hasChildren !== null` | ✅* | +| risicotaxatie | ≥1 risico assessment | ✅ | +| anamnese | ≥1 anamnese entry | ✅ | +| onderzoeken | ≥1 onderzoek | ❌ | +| rom | ≥1 ROM meting | ❌ | +| diagnose | ≥1 diagnose | ✅ | +| behandeladvies | advice niet leeg | ✅ | + +*Kindcheck wettelijk verplicht bij patiënten met kinderen + +--- + +#### GET `/api/cortex/intake/[intakeId]/risks` + +**Doel:** Risico assessments voor RisicoBlock + +**Response:** +```typescript +interface RisksResponse { + intakeId: string; + patientId: string; + risks: Array<{ + id: string; + type: string; // 'suicidaliteit' | 'agressie' | etc. + typeLabel: string; // 'Suïcidaliteit' + level: string; // 'laag' | 'gemiddeld' | 'hoog' | 'zeer_hoog' + levelLabel: string; // 'Matig' + rationale?: string; + assessedAt: string; // ISO date + assessedBy?: string; // Naam beoordelaar + }>; +} +``` + +--- + +#### GET `/api/cortex/intake/[intakeId]/diagnoses` + +**Doel:** Diagnoses voor DiagnoseBlock + +**Response:** +```typescript +interface DiagnosesResponse { + intakeId: string; + patientId: string; + primaryDiagnosis: Diagnosis | null; + secondaryDiagnoses: Diagnosis[]; +} + +interface Diagnosis { + id: string; + code: string; // ICD-10 code: 'F32.1' + description: string; // 'Depressieve stoornis, matig' + isPrimary: boolean; + diagnosedAt: string; // ISO date + diagnosedBy?: string; +} +``` + +--- + +## 7. Intent & Pattern Configuratie + +### 7.1 Nieuwe Intent Types + +```typescript +// lib/cortex/types.ts - toevoegen aan CortexIntent + +type CortexIntent = + | 'dagnotitie' + | 'zoeken' + | 'overdracht' + | 'agenda_query' + | 'create_appointment' + | 'cancel_appointment' + | 'reschedule_appointment' + // MVP Intake intents + | 'intake_status' // 🆕 + | 'risico_query' // 🆕 + | 'diagnose_query' // 🆕 + | 'intake_navigeer' // 🆕 + | 'unknown'; +``` + +### 7.2 Reflex Patterns + +```typescript +// lib/cortex/reflex-classifier.ts - toevoegen + +// intake_status +{ pattern: /^(wat\s+)?moet\s+ik\s+nog\s+(doen|invullen)/i, intent: 'intake_status', weight: 1.0 }, +{ pattern: /^is\s+(de\s+)?intake\s+compleet/i, intent: 'intake_status', weight: 0.95 }, +{ pattern: /^intake\s+(checklist|status|voortgang)/i, intent: 'intake_status', weight: 0.9 }, +{ pattern: /^welke\s+secties\s+(zijn|nog)/i, intent: 'intake_status', weight: 0.85 }, + +// risico_query +{ pattern: /^(wat\s+zijn\s+)?(de\s+)?risico'?s/i, intent: 'risico_query', weight: 1.0 }, +{ pattern: /^(toon|show)\s+risico/i, intent: 'risico_query', weight: 0.95 }, +{ pattern: /^risicotaxatie/i, intent: 'risico_query', weight: 0.9 }, + +// diagnose_query +{ pattern: /^(welke\s+)?diagnose[ns]?(\s+heeft)?/i, intent: 'diagnose_query', weight: 1.0 }, +{ pattern: /^(toon|show)\s+diagnose/i, intent: 'diagnose_query', weight: 0.95 }, +{ pattern: /^wat\s+is\s+(de\s+)?diagnose/i, intent: 'diagnose_query', weight: 0.9 }, + +// intake_navigeer +{ pattern: /^ga\s+naar\s+(de\s+)?(risico|diagnose|kindcheck|anamnese|rom|behandel)/i, intent: 'intake_navigeer', weight: 1.0 }, +{ pattern: /^open\s+(de\s+)?(risico|diagnose|kindcheck|anamnese)/i, intent: 'intake_navigeer', weight: 0.95 }, +{ pattern: /^naar\s+(risico|diagnose|kindcheck)/i, intent: 'intake_navigeer', weight: 0.9 }, +``` + +### 7.3 Block Configs + +```typescript +// lib/cortex/types.ts - toevoegen aan BLOCK_CONFIGS + +'intake-status': { + type: 'intake-status', + title: 'Intake Status', + size: 'md', + icon: 'ClipboardList', +}, +'risico-query': { + type: 'risico-query', + title: 'Risicotaxatie', + size: 'md', + icon: 'AlertTriangle', +}, +'diagnose-query': { + type: 'diagnose-query', + title: 'Diagnoses', + size: 'md', + icon: 'Stethoscope', +}, +``` + +--- + +## 8. Epics & Stories + +### 8.1 Overzicht + +| Epic ID | Titel | Doel | Stories | Geschat | Status | +|---------|-------|------|---------|---------|--------| +| E0 | Intent Setup | Types, patterns, configs | 3 | 2 uur | ✅ Done | +| E1 | API Routes | Endpoints voor data | 3 | 3 uur | ✅ Done | +| E2 | IntakeStatusBlock | Checklist block | 2 | 3 uur | ✅ Done | +| E3 | RisicoBlock | Risico's block | 2 | 2 uur | ✅ Done | +| E4 | DiagnoseBlock | Diagnoses block | 2 | 2 uur | ✅ Done | +| E5 | Navigatie | intake_navigeer handler | 2 | 2 uur | ✅ Done | +| E6 | Integratie | Canvas-area + testing | 2 | 2 uur | ⚠️ Partial | + +**Totaal geschat:** ~16 uur (2 werkdagen) +**Status:** MVP functioneel (6/7 epics done), E2E testing nog te doen + +--- + +### 8.2 Epic 0 — Intent Setup ✅ + +**Doel:** Cortex kan de nieuwe intents herkennen. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E0.S1 | Intent types toevoegen | 4 nieuwe intents in `CortexIntent` type | ✅ | 1 | +| E0.S2 | Reflex patterns toevoegen | Patterns voor alle 4 intents, confidence ≥0.85 | ✅ | 2 | +| E0.S3 | Block configs toevoegen | BLOCK_CONFIGS voor 3 blocks | ✅ | 1 | + +**Afhankelijkheden:** Geen +**Geïmplementeerd:** `lib/cortex/types.ts`, `reflex-classifier.ts`, `intent-classifier.ts`, `intent-labels.ts` + +--- + +### 8.3 Epic 1 — API Routes ✅ + +**Doel:** Backend endpoints voor block data. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E1.S1 | `/api/cortex/intake/status` | Returns completion status, tested | ✅ | 3 | +| E1.S2 | `/api/cortex/intake/risico` | Returns risks array, tested | ✅ | 2 | +| E1.S3 | `/api/cortex/intake/diagnose` | Returns diagnoses, tested | ✅ | 2 | + +**Afhankelijkheden:** E0 +**Geïmplementeerd:** `app/api/cortex/intake/status/route.ts`, `risico/route.ts`, `diagnose/route.ts` +**Opmerking:** URL structuur gewijzigd naar `?patientId=xxx&intakeId=xxx` ipv `[intakeId]` path param + +--- + +### 8.4 Epic 2 — IntakeStatusBlock ✅ + +**Doel:** Werkend IntakeStatusBlock component. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E2.S1 | Block component bouwen | Gebruikt shared components, alle states | ✅ | 5 | +| E2.S2 | Navigatie naar secties | Klik op sectie opent EPD tab | ✅ | 2 | + +**Afhankelijkheden:** E0, E1.S1 +**Geïmplementeerd:** `components/cortex/blocks/intake-status-block.tsx` +**Features:** CompletionRing (SVG), verplichte/optionele secties, "Naar intake" footer actie + +--- + +### 8.5 Epic 3 — RisicoBlock ✅ + +**Doel:** Werkend RisicoBlock component. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E3.S1 | Block component bouwen | Gebruikt shared components, color-coded badges | ✅ | 3 | +| E3.S2 | Footer acties | "Bekijk in dossier" werkt | ✅ | 1 | + +**Afhankelijkheden:** E0, E1.S2 +**Geïmplementeerd:** `components/cortex/blocks/risico-block.tsx` +**Features:** RiskSummaryCard (suïcide/zelfbeschadiging/agressie flags), severity badges, "Naar risicotaxaties" footer + +--- + +### 8.6 Epic 4 — DiagnoseBlock ✅ + +**Doel:** Werkend DiagnoseBlock component. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E4.S1 | Block component bouwen | Hoofddiagnose + nevendiagnoses gescheiden | ✅ | 3 | +| E4.S2 | Footer acties | "Bekijk in dossier" werkt | ✅ | 1 | + +**Afhankelijkheden:** E0, E1.S3 +**Geïmplementeerd:** `components/cortex/blocks/diagnose-block.tsx` +**Features:** PrimaryDiagnosisCard, clinical status badges, ICD-10 codes, "Naar diagnoses" footer + +--- + +### 8.7 Epic 5 — Navigatie Handler ✅ + +**Doel:** `intake_navigeer` intent werkt. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E5.S1 | Tab parser bouwen | Extraheert target tab uit input | ✅ | 2 | +| E5.S2 | Navigation handler | router.push() naar correcte URL, toast feedback | ✅ | 2 | + +**Afhankelijkheden:** E0 +**Geïmplementeerd:** +- Entity extraction in `lib/cortex/entity-extractor.ts` (INTAKE_TAB_KEYWORDS, extractIntakeNavigeerEntities) +- `navigationTarget` field toegevoegd aan ExtractedEntities type +- `lib/cortex/action-parser.ts` - intake intents toegevoegd aan schema en routing +- `components/cortex/command-center/command-center.tsx` - navigation handler met router.push() en toast feedback +**Opmerking:** Navigeert naar `/epd/patients/{id}/intakes` (intake lijst). Volledige tab-navigatie vereist intakeId selectie. + +--- + +### 8.8 Epic 6 — Integratie ⚠️ + +**Doel:** Alles werkt samen in Cortex. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | SP | +|----------|--------------|---------------------|--------|-----| +| E6.S1 | Canvas-area updaten | Rendert nieuwe blocks op basis van intent | ✅ | 2 | +| E6.S2 | E2E testing | Alle 4 intents werken via voice/text input | ⏳ | 3 | + +**Afhankelijkheden:** E2, E3, E4, E5 +**Geïmplementeerd:** +- `components/cortex/command-center/canvas-area.tsx` - imports en switch cases voor 3 blocks +- `components/cortex/command-center/recent-strip.tsx` - INTENT_CONFIG voor nieuwe intents +**TODO:** E2E testing met echte voice/text input + +--- + +## 9. Implementatie Volgorde + +``` +Week 1: +┌─────────────────────────────────────────────────────────────────┐ +│ Dag 1 │ +│ ├── E0.S1 Intent types │ +│ ├── E0.S2 Reflex patterns │ +│ ├── E0.S3 Block configs │ +│ └── E1.S1 /status API route │ +│ │ +│ Dag 2 │ +│ ├── E1.S2 /risks API route │ +│ ├── E1.S3 /diagnoses API route │ +│ ├── E2.S1 IntakeStatusBlock │ +│ └── E2.S2 Status navigatie │ +│ │ +│ Dag 3 (half) │ +│ ├── E3.S1 RisicoBlock │ +│ ├── E3.S2 Risico footer │ +│ ├── E4.S1 DiagnoseBlock │ +│ └── E4.S2 Diagnose footer │ +│ │ +│ Dag 3 (half) + Buffer │ +│ ├── E5.S1 Tab parser │ +│ ├── E5.S2 Navigation handler │ +│ ├── E6.S1 Canvas-area update │ +│ └── E6.S2 E2E testing │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 10. Dependencies Check + +### 10.1 Bestaande code (hergebruiken) + +| Dependency | Pad | Status | +|------------|-----|--------| +| BlockContainer | `components/cortex/blocks/block-container.tsx` | ✅ | +| BlockLoading, BlockError, BlockEmpty | `components/cortex/shared/block-states.tsx` | ✅ | +| BlockSection, BlockItem, BlockFooter | `components/cortex/shared/` | ✅ | +| useBlockData, useIntakeContext | `lib/cortex/hooks/` | ✅ | +| useCortexStore | `stores/cortex-store.ts` | ✅ | +| safeFetch, getErrorInfo | `lib/cortex/error-handler.ts` | ✅ | +| getRiskAssessments | `lib/actions/intake-actions.ts` | ✅ | +| getDiagnoses | `lib/actions/intake-actions.ts` | ✅ | + +### 10.2 Nieuw te bouwen + +| Item | Type | Epic | +|------|------|------| +| 4 intent types | TypeScript | E0 | +| ~12 reflex patterns | Config | E0 | +| 3 block configs | Config | E0 | +| 3 API routes | Next.js API | E1 | +| IntakeStatusBlock | React component | E2 | +| RisicoBlock | React component | E3 | +| DiagnoseBlock | React component | E4 | +| Navigation handler | TypeScript | E5 | + +--- + +## 11. Risico's & Mitigatie + +| Risico | Kans | Impact | Mitigatie | +|--------|------|--------|-----------| +| Intake context niet beschikbaar | Middel | Hoog | Fallback naar patient-selectie block | +| Meerdere intakes per patient | Middel | Middel | Gebruik meest recente of vraag clarificatie | +| Bestaande actions niet compatible | Laag | Hoog | Wrapper functies voor Cortex API | +| Pattern overlap met bestaande intents | Laag | Middel | Specifieke patterns, hogere weights | + +--- + +## 12. Definition of Done + +**Per Story:** +- [x] Code geschreven volgens specs +- [x] TypeScript compileert zonder errors +- [x] ESLint geen errors +- [x] Component werkt in browser +- [ ] Alle states getest (loading, error, empty, data) + +**Per Epic:** +- [x] E0-E4 stories DONE +- [x] Integratie met andere epics getest +- [ ] Code reviewed + +**Project DONE:** +- [x] 6/7 epics volledig DONE (E0-E5) +- [x] 1/7 epic partial (E6 - testing) +- [x] Alle 4 intents werken (3 blocks + 1 navigatie) +- [x] `pnpm build` succesvol +- [ ] Demo scenario werkt (E2E testing) +- [x] Commit: `b095b1e` feat(cortex): add Intake Blocks MVP + +--- + +## 13. Referenties + +**Project documenten:** +- `docs/intent/intake-intent-proces/intake-process-intents.md` +- `docs/intent/intake-intent-proces/gap-analyse-intake-cortex.md` +- `docs/intent/intake-intent-proces/fo-to-cortex-shared-components.md` + +**Bestaande code:** +- `components/cortex/blocks/` - Block voorbeelden +- `lib/cortex/reflex-classifier.ts` - Pattern voorbeelden +- `lib/actions/intake-actions.ts` - Data functies + +--- + +## Versiehistorie + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 03-02-2026 | Colin Lit | Initiële versie | +| v1.1 | 03-02-2026 | Colin Lit | Status update: E0-E4 done, E5-E6 partial | +| v1.2 | 03-02-2026 | Colin Lit | E5.S2 done: navigation handler in command-center.tsx | diff --git a/lib/cortex/action-parser.ts b/lib/cortex/action-parser.ts index 92bf6c3..790ee9f 100644 --- a/lib/cortex/action-parser.ts +++ b/lib/cortex/action-parser.ts @@ -21,6 +21,11 @@ const ActionSchema = z.object({ 'create_appointment', 'cancel_appointment', 'reschedule_appointment', + // Intake intents (MVP) + 'intake_status', + 'intake_navigeer', + 'risico_query', + 'diagnose_query', 'unknown', ]), entities: z.object({ @@ -77,6 +82,10 @@ const ActionSchema = z.object({ 'create_appointment', 'cancel_appointment', 'reschedule_appointment', + // Intake blocks (MVP) + 'intake_status', + 'risico_query', + 'diagnose_query', 'fallback', 'patient-dashboard', ]), @@ -368,6 +377,33 @@ export function routeIntentToArtifact( prefill: entities, }; + // Intake intents (MVP) + case 'intake_status': + return { + type: 'intake_status', + title: 'Intake Status', + prefill: entities, + }; + + case 'risico_query': + return { + type: 'risico_query', + title: 'Risicotaxatie', + prefill: entities, + }; + + case 'diagnose_query': + return { + type: 'diagnose_query', + title: 'Diagnoses', + prefill: entities, + }; + + case 'intake_navigeer': + // Navigation intent - handled separately in CommandCenter + // Returns null to trigger navigation instead of artifact + return null; + case 'unknown': // Unknown intent - show fallback picker return {