# NS.E5 — Integration & Demo Polish **Casus:** Cortex No Show Afhandeling **Epic doel:** De volledige end-to-end flow werkend krijgen, edge cases afvangen, en de demo valideren aan de succescriteria uit het PRD. **Geschatte tijd:** ~2 uur **Afhankelijkheden:** NS.E1 t/m NS.E4 volledig afgerond --- ## Context Na NS.E1–NS.E4 zijn alle individuele onderdelen gebouwd: - Intent wordt herkend (E1) - Nudges triggeren en lopen sequentieel (E2) - API routes werken (E3) - Artifact component toont de brief (E4) Dit epic zorgt dat alles correct samenwerkt, fouten netjes worden afgevangen, en de demo vloeiend loopt. --- ## NS.E5.S1 — Volledige `chat-panel.tsx` integratie valideren **Bestand:** `components/cortex/chat/chat-panel.tsx` ### Checklist: zijn alle functies aanwezig en correct verbonden? Na NS.E2 en NS.E4 moeten de volgende functies bestaan in `ChatPanel`: | Functie | Aangemaakt in | Doet | |---|---|---| | `handleNoShowCancelStep` | NS.E2.S3 | Cancel API → context check → nudge 2 of done | | `handleNoShowRescriptStep` | NS.E4.S3 | Rescript API → openArtifact | | `handleAcceptNudge` (uitgebreid) | NS.E2.S3 | Brancht op `trigger.intent` en `suggestion.rationale` | ### Verbinding `handleAcceptNudge` → `handleNoShowRescriptStep` Controleer dat de stap-5 check correct is geïmplementeerd: ```typescript const handleAcceptNudge = useCallback(async ( suggestionId: string, suggestion: ChatMessageType['nudge'] ) => { acceptSuggestion(suggestionId); if (!suggestion) return; // Stap 2→3: declarabiliteitscheck geaccepteerd → annuleer + check brief if (suggestion.trigger.intent === 'register_no_show') { await handleNoShowCancelStep(suggestion); return; } // Stap 4→5: brief-check geaccepteerd → herschrijf brief if (suggestion.suggestion.rationale === 'noshow-brief-check') { await handleNoShowRescriptStep(); return; } // Generieke flow voor alle andere nudges const artifact = routeIntentToArtifact( suggestion.suggestion.intent, suggestion.suggestion.entities, 0.9 ); if (artifact) { openArtifact({ type: artifact.type, prefill: artifact.prefill, title: artifact.title }); } }, [ acceptSuggestion, openArtifact, handleNoShowCancelStep, handleNoShowRescriptStep, ]); ``` **Belangrijk:** `handleNoShowCancelStep` en `handleNoShowRescriptStep` moeten in de `useCallback` dependency array staan. Als ze er niet in staan, kan je stale closures krijgen. ### `onSend` handler: no-show nudge trigger Controleer dat de `onDone` callback de no-show nudge triggert. Dit blok moet aanwezig zijn in de `onDone`: ```typescript // No-show nudge trigger na register_no_show classificatie if (parsed.action?.intent === 'register_no_show' && isFeatureEnabled('CORTEX_NUDGE')) { const suggestions = evaluateNudge({ intent: 'register_no_show', actionId: crypto.randomUUID(), entities: parsed.action.entities, content: message, }); suggestions.forEach((suggestion) => { addChatMessage({ type: 'nudge', content: suggestion.suggestion.message, nudge: suggestion, }); }); } ``` ### Done criteria - Volledige flow doorloopbaar zonder errors in de browser console - `[ChatPanel]` log berichten verschijnen op de juiste momenten - Geen `TypeError` of `undefined is not a function` errors --- ## NS.E5.S2 — Loading states en dubbele submits voorkomen **Bestand:** `components/cortex/chat/chat-panel.tsx` + `stores/cortex-store.ts` ### Probleem Tussen de API calls (cancel → context → rescript) zit latentie. Als de gebruiker snel klikt, kunnen er dubbele calls ontstaan. Ook moet de UI duidelijk maken dat er iets "bezig" is. ### Oplossing A — `isNoShowProcessing` flag in store Voeg toe aan store (in `CortexStore` interface, `initialState`, en actions): ```typescript // In interface: isNoShowProcessing: boolean; // In initialState: isNoShowProcessing: false, // Actions: setNoShowProcessing: (processing: boolean) => void; ``` Implementatie: ```typescript setNoShowProcessing: (processing) => set({ isNoShowProcessing: processing }, false, 'setNoShowProcessing'), ``` ### Oplossing B — Gebruik in `handleNoShowCancelStep` en `handleNoShowRescriptStep` Wrap elke async functie: ```typescript const handleNoShowCancelStep = useCallback(async (...) => { const { isNoShowProcessing } = useCortexStore.getState(); if (isNoShowProcessing) return; // Dubbele klik preventie setNoShowProcessing(true); try { // ... bestaande logica } finally { setNoShowProcessing(false); } }, [..., setNoShowProcessing]); ``` ### Oplossing C — Nudge knop disablen tijdens processing In `NudgeChatMessage` is de accept/dismiss knop al aanwezig. We moeten de accept knop disablen tijdens `isNoShowProcessing`: **Bestand:** `components/cortex/chat/nudge-chat-message.tsx` Voeg toe: ```typescript const isNoShowProcessing = useCortexStore((s) => s.isNoShowProcessing); ``` Voeg `disabled={isNoShowProcessing}` toe aan de accept-knop als de nudge een no-show nudge is (`suggestion.trigger.intent === 'register_no_show'` of `suggestion.suggestion.rationale === 'noshow-brief-check'`). ### Done criteria - Snel twee keer klikken op `[Ja, annuleer]` triggert de API maar één keer - Knop is visueel disabled tijdens verwerking - Na voltooiing is de knop weer actief (relevant voor de "Nee" knop) --- ## NS.E5.S3 — Cleanup bij unmount **Bestand:** `components/cortex/chat/chat-panel.tsx` Als de gebruiker weg navigeert tijdens een lopende no-show flow, moet de state worden opgeruimd. Voeg toe in `ChatPanel`: ```typescript const resetNoShowFlow = useCortexStore((s) => s.resetNoShowFlow); // Cleanup bij unmount useEffect(() => { return () => { // Reset no-show flow als component unmount tijdens actieve flow const { noShowFlow } = useCortexStore.getState(); if (noShowFlow.step !== 'idle' && noShowFlow.step !== 'done') { resetNoShowFlow(); } }; }, [resetNoShowFlow]); ``` ### Done criteria - Navigeren weg van het dashboard tijdens een actieve no-show flow reset de state - Bij terugkeren: flow begint opnieuw (geen "stuck" state) --- ## NS.E5.S4 — Demo Validatie Checklist Doorloop de volledige flow handmatig en vink elk punt af: ### Classificatie - [ ] Input `"patiënt is niet verschenen"` → `register_no_show` intent herkend (zichtbaar in console) - [ ] Input `"no show vandaag"` → `register_no_show` intent herkend - [ ] Input `"cliënt was er niet"` → AI classificeert als `register_no_show` - [ ] Input `"patiënt niet verschenen volgende week"` → escaleert naar AI (relative_time) ### Nudge 1 — Declarabiliteitscheck - [ ] Na no-show input: nudge bubble verschijnt met declarabiliteits-waarschuwing - [ ] Nudge heeft prioriteit badge `high` (rode kleur) - [ ] `[Nee]` klik: nudge verdwijnt, geen verdere actie, flow eindigt - [ ] `[Ja, annuleer]` klik: loading state actief (knop disabled) ### Stap 3 — Annulering - [ ] Cancel API aangeroepen: `POST /api/cortex/noshow/cancel` → 200 response - [ ] Context API aangeroepen: `GET /api/cortex/noshow/context` → `hasConceptBrief: true` - [ ] Chat toont "Bezig met annuleren..." tijdens verwerking - [ ] Na voltooiing: nudge 2 verschijnt met brief-vraag ### Nudge 2 — Brief check - [ ] Nudge 2 toont correcte message over conceptbrief - [ ] `[Nee]` klik: nudge verdwijnt, chat bevestigt annulering - [ ] `[Ja, pas brief aan]` klik: rescript API aangeroepen ### Stap 4 — Rescript - [ ] Rescript API aangeroepen: `POST /api/cortex/noshow/rescript` → herschreven inhoud - [ ] Artifact paneel opent met `NoShowDocumentBlock` - [ ] Chat toont "Huisartsbrief is aangepast. Kijk of je hiermee akkoord bent." - [ ] Herschreven brief is NIET alleen de originele tekst met "no show" erachter geplakt ### Stap 5 — Accordering - [ ] Textarea is bewerkbaar — gebruiker kan aanpassingen maken - [ ] "Originele brief bekijken" toggle werkt - [ ] `[Akkoord & Verzendklaar]` roept dispatch API aan - [ ] Na dispatch: knop verandert naar "✓ Verzendklaar" - [ ] Chat ontvangt bevestigingsbericht ### Technische criteria - [ ] Volledige flow doorloopbaar in < 30 seconden (excl. LLM latency ~3-5s) - [ ] Geen schermwissels nodig gedurende de hele flow - [ ] `pnpm build` slaagt zonder errors - [ ] `pnpm lint` slaagt zonder errors - [ ] Geen rode console errors in de browser ### Edge cases - [ ] Offline/netwerk fout tijdens cancel → error message in chat, flow herstelbaar - [ ] LLM fout in rescript → originele brief getoond + gele waarschuwingsbalk - [ ] Dubbele klik op `[Ja, annuleer]` → slechts één API call --- ## NS.E5.S5 — Commit strategie Commitvolgorde die logisch werkt voor code review: ``` feat(cortex): NS.E1 — register_no_show intent type + reflex patterns feat(cortex): NS.E2.S1 — no-show flow state in cortex store feat(cortex): NS.E2.S2 — nudge rule: declarabiliteitscheck feat(cortex): NS.E2.S3 — chat panel: no-show nudge trigger + handlers feat(cortex): NS.E3 — no-show API routes (cancel, context, rescript, dispatch) feat(cortex): NS.E4.S1 — NoShowDocumentBlock component feat(cortex): NS.E4.S2 — artifact-container: register_no_show case feat(cortex): NS.E4.S3 — chat panel: rescript step + artifact koppeling feat(cortex): NS.E5 — loading states, cleanup, demo polish ``` Elke commit moet `pnpm build` laten slagen. --- ## Overzicht gewijzigde bestanden (totaal) | Bestand | Wijziging | |---|---| | `lib/cortex/types.ts` | `register_no_show` in union + BLOCK_CONFIGS | | `lib/cortex/reflex-classifier.ts` | Patronen voor register_no_show | | `app/api/cortex/chat/route.ts` | System prompt uitgebreid | | `lib/cortex/nudge.ts` | Nudge rule 1 toegevoegd | | `stores/cortex-store.ts` | NoShowFlowState + actions + isNoShowProcessing | | `lib/cortex/mock-data/noshow.ts` | **Nieuw** | | `app/api/cortex/noshow/cancel/route.ts` | **Nieuw** | | `app/api/cortex/noshow/context/route.ts` | **Nieuw** | | `app/api/cortex/noshow/rescript/route.ts` | **Nieuw** | | `app/api/cortex/noshow/dispatch/route.ts` | **Nieuw** | | `components/cortex/blocks/noshow-document-block.tsx` | **Nieuw** | | `components/cortex/artifacts/artifact-container.tsx` | Case + import + getArtifactTitle | | `components/cortex/chat/chat-panel.tsx` | Nudge trigger + handlers + cleanup | | `components/cortex/chat/nudge-chat-message.tsx` | isNoShowProcessing check | **Totaal: 14 bestanden** — 5 nieuw, 9 aangepast