# 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
