Files
triqura-ecd/docs/swift/archive/bouwplan-swift-v2.md
colinislit 9b85448a43 feat(swift): implementeer intent detection & action parsing (E3.S4)
Epic 3 Story 4 compleet: AI action objects worden geparsed en gevalideerd.

E3.S4 - Intent Detection in Response (5 SP)
- Action parser met JSON extraction uit markdown code blocks
- Zod validatie voor action schema (intent, entities, confidence, artifact)
- Confidence-based artifact opening (≥0.7 threshold)
- Visual feedback met action badges in chat messages
- Console logging voor debugging

Nieuwe Components:
- lib/swift/action-parser.ts (149 regels)
  - parseActionFromResponse(): Extract & validate JSON actions
  - extractJsonBlock(): Regex matching voor ```json blocks
  - removeJsonBlocks(): Clean text zonder JSON
  - shouldOpenArtifact(): Confidence check (≥0.7)
  - getConfidenceLabel(): "Zeer zeker", "Redelijk zeker", etc.
  - validateArtifactType(): Intent/artifact type matching

Components Updated:
- components/swift/chat/chat-panel.tsx
  - Action parsing in onDone callback
  - setPendingAction voor artifact opening (E3.S6)
  - Console logging: "[ChatPanel] Action detected"

- components/swift/chat/chat-message.tsx
  - Action badge met Sparkles icon
  - Intent label (Dagnotitie, Patiënt zoeken, Overdracht)
  - CheckCircle icon voor high confidence (≥0.7)
  - Confidence label display

- stores/swift-store.ts
  - updateLastMessage(): Optional action parameter
  - Type-safe action assignment (null/undefined handling)

Features:
- JSON extraction via regex: /```json\s*\n([\s\S]*?)\n```/
- Zod schema: type, intent, entities, confidence, artifact
- Confidence thresholds: >0.9, 0.7-0.9, 0.5-0.7, <0.5
- Visual feedback: Sparkles + CheckCircle icons
- Cleaned text content (JSON removed from display)
- Action stored in message.action en pendingAction state

Action Schema:
```typescript
{
  type: "action",
  intent: "dagnotitie" | "zoeken" | "overdracht" | "unknown",
  entities: {
    patientName?: string,
    patientId?: string,
    category?: "medicatie" | "adl" | "gedrag" | "incident" | "observatie",
    content?: string,
    query?: string
  },
  confidence: number, // 0-1
  artifact?: {
    type: BlockType,
    prefill: Record<string, any>
  }
}
```

Build Status:
-  pnpm build succesvol (geen type errors)
- Alleen bekende warnings (Supabase realtime, useCallback)

Voortgang: 46 SP / 85 SP (54%) - E3.S4 compleet

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 16:11:43 +01:00

35 KiB

Mission Control — Bouwplan Swift v2.5

Projectnaam: Swift — Contextual UI EPD Versie: v2.5 Datum: 27-12-2024 Auteur: Colin Lit / Development Team Status: 🔄 86% Complete - MVP binnen handbereik!


🎯 Project Status Summary

Metric Waarde Progress
Story Points 62 / 72 SP 86% ██████████████████░░
Epics Compleet 4.75 / 5 E0 E1 E2 E3 E4 E5 🔄
Stories Compleet 27 / 29 93%
Remaining Work 2 SP (1 story) E5.S4 only!
Estimated Time 1-2 uur Voor MVP compleet

🎉 Laatste sprint! Nog 1 story en de MVP is klaar voor demo!


Changelog v2.5

Belangrijke wijzigingen t.o.v. v2.4:

  • E5.S3 compleet: Keyboard shortcuts geverifieerd en uitgebreid
  • ⌘Enter / Ctrl+Enter voor quick submit in CommandInput
  • ⌘Enter / Ctrl+Enter voor quick save in DagnotatieBlock
  • Visual hints toegevoegd (⌘↵ op buttons)
  • Keyboard shortcuts reference document
  • Totalen bijgewerkt: 62 SP done (86%), 10 SP remaining

Changelog v2.4

Belangrijke wijzigingen t.o.v. v2.3:

  • E5.S2 compleet: Error handling geïmplementeerd met gecentraliseerde utilities
  • OfflineBanner component voor offline detection
  • safeFetch wrapper met 30s timeout en retry logic
  • User-friendly Nederlandse error messages voor alle HTTP status codes
  • Retry logic met exponential backoff voor transient errors
  • Alle blocks en command input gebruiken nieuwe error handler
  • Totalen bijgewerkt: 60 SP done (83%), 12 SP remaining

Changelog v2.3

Belangrijke wijzigingen t.o.v. v2.2:

  • E5.S1 compleet: Block animaties geïmplementeerd volgens UX specificatie
  • Slide up/down animaties met fade en scale (200ms)
  • CanvasArea animaties verbeterd: slide down bij sluiten (niet omhoog)
  • BlockContainer animaties consistent gemaakt met CanvasArea
  • Totalen bijgewerkt: 58 SP done (81%), 14 SP remaining

Changelog v2.2

Belangrijke wijzigingen t.o.v. v2.1:

  • Epic 3 compleet: Alle P1 blocks geïmplementeerd (E3.S0-S6)
  • PatientContextCard toegevoegd: Auto-open na patient selectie
  • OverdrachtBlock met AI samenvattingen per patiënt
  • Patient search API geïmplementeerd
  • Technische debt opgelost: BlockContainer animaties, CanvasArea rendering
  • Totalen bijgewerkt: 56 SP done (78%), 16 SP remaining

Changelog v2.0

Belangrijke wijzigingen t.o.v. v1.5:

  • E3.S0 toegevoegd: CanvasArea block rendering (kritieke blokkade)
  • Technische debt sectie toegevoegd (type duplicatie, keyboard shortcuts)
  • Diagnostiek Workflow als uitbreiding opgenomen
  • Sprint planning aangepast aan huidige voortgang
  • Totalen bijgewerkt: 29 stories, 72 SP

1. Doel en context

1.1 Projectdoel

Swift is een Contextual UI interface voor het Mini-EPD systeem. In plaats van navigatie door menu's spreekt of typt de gebruiker een intentie — en het juiste UI-blok verschijnt voorgevuld met relevante data.

Kernbelofte:

Van 12 klikken en 3 minuten naar 1 zin en 15 seconden.

1.2 Business Case

Metric Huidig Met Swift Besparing
Dagnotitie maken 3-5 min 15 sec 95%
Patiënt zoeken 1-2 min 5 sec 95%
Overdracht maken 20-30 min 5 min 80%
Rapportage schrijven 8-15 min 2-3 min 75%

Per verpleegkundige per dag: ~4 uur terug naar zorg

1.3 Relatie met Documentatie

Document Beschrijft Locatie
PRD Product visie, requirements swift-prd.md
FO Functionele flows, blocks swift-fo-ai.md
TO Technische architectuur to-swift-v1.md
UX Visuele specificaties swift-ux-v2.1.md
Diagnostiek FO Behandelaar workflow swift-fo-diagnostiek-workflow.md
Diagnostiek Bouwplan Uitbreiding voor behandelaars bouwplan-swift-diagnostiek-workflow.md

2. Uitgangspunten

2.1 Technische Stack

Bestaand (hergebruiken):

Component Technologie Versie
Framework Next.js 14.2.18
UI Library React 18.3.1
Styling TailwindCSS 3.4.18
Components shadcn/ui -
Command Palette cmdk 1.1.1
Animations framer-motion 12.23.24
Database Supabase 2.81.1
Speech-to-Text Deepgram 4.11.2
AI Claude API -
Validation Zod 4.1.12
Forms react-hook-form 7.66.1

Nieuw toegevoegd:

Component Technologie Versie Status
State Management Zustand 5.0.9 Geïnstalleerd

2.2 Projectkaders

Kader Waarde
Bouwtijd 4 weken (4 sprints)
Team 1 developer
Scope MVP: P1 blocks (dagnotitie, zoeken, overdracht)
Uitbreiding Diagnostiek workflow (optioneel, +22 SP)
Data Bestaande Supabase database
Doel Werkende demo + user testing

2.3 Programmeer Uitgangspunten

Code Quality Principles:

  • DRY (Don't Repeat Yourself)

    • Herbruikbare block componenten
    • Centrale intent classificatie logica
    • Shared hooks voor common patterns
    • Let op: Types importeren uit lib/swift/types.ts, niet dupliceren
  • KISS (Keep It Simple, Stupid)

    • Local-first intent classificatie (regex)
    • AI alleen als fallback
    • Minimale state complexity
  • SOC (Separation of Concerns)

    • UI blocks gescheiden van intent logic
    • API routes gescheiden van business logic
    • Store slices per domein
  • YAGNI (You Aren't Gonna Need It)

    • Alleen P1 blocks in MVP
    • Geen toggle tussen interfaces
    • Geen advanced analytics in v1

Development Practices:

// ✅ Goede structuur voor Swift components
components/
├── swift/
   ├── command-center/
      ├── command-center.tsx      // Main container
      ├── command-input.tsx       // Input component
      ├── canvas-area.tsx         // Block rendering ← KRITIEK
      └── index.ts                // Barrel export
   └── blocks/
       ├── block-container.tsx     // Wrapper met animaties
       ├── dagnotitie-block.tsx
       ├── zoeken-block.tsx
       ├── overdracht-block.tsx
       └── index.ts

// ✅ Store importeert types (geen duplicatie)
stores/
└── swift-store.ts                   // Importeert uit lib/swift/types.ts

// ✅ Intent classification
lib/
└── swift/
    ├── types.ts                     // SINGLE SOURCE OF TRUTH voor types
    ├── intent-classifier.ts         // Local classification
    ├── intent-classifier-ai.ts      // AI fallback
    └── entity-extractor.ts          // Entity parsing

3. Epics & Stories Overzicht

Epic ID Titel Doel Status Stories Effort
E0 Setup & Foundation Zustand, routing, base layout Done 4 8 SP
E1 Command Center Input, voice, context bar Done 5 13 SP
E2 Intent Classification Local + AI fallback + wiring Done 5 12 SP
E3 P1 Blocks Dagnotitie, Zoeken, Overdracht Done 7 23 SP
E4 Navigation & Auth Login keuze, routing, preferences Done 4 8 SP
E5 Polish & Testing Animaties, error handling, tests 🔄 In Progress 4 8 SP

Totaal: 29 stories, 72 story points

Categorie SP
Done (E0 + E1 + E2 + E3 + E4 + E5.S1 + E5.S2 + E5.S3) 62
Remaining 10

Epic Completion Details

Epic Stories SP Status Completion
E0: Setup & Foundation 4/4 8/8 DONE 100% ████████████████████
E1: Command Center 5/5 13/13 DONE 100% ████████████████████
E2: Intent Classification 5/5 12/12 DONE 100% ████████████████████
E3: P1 Blocks 7/7 23/23 DONE 100% ████████████████████
E4: Navigation & Auth 4/4 8/8 DONE 100% ████████████████████
E5: Polish & Testing 3/4 6/8 🔄 IN PROGRESS 75% ███████████████░░░░░
TOTAAL 27/29 62/72 🎯 86% ██████████████████░░

Laatste Sprint:

  • E5.S1 - Block animaties (2 SP)
  • E5.S2 - Error handling (2 SP)
  • E5.S3 - Keyboard shortcuts (2 SP)
  • E5.S4 - Smoke tests (2 SP) ← ALLEEN DIT NOG!

Belangrijk:

  • Bouw per epic en per story, niet alles tegelijk
  • Dependencies installeren: eerst aan Colin melden
  • Database migraties: eerst aan Colin melden

4. Epics & Stories (Uitwerking)

Epic 0 — Setup & Foundation DONE

Epic Doel: Werkende development omgeving met Zustand store en Swift routing.

Story ID Beschrijving Acceptatiecriteria Status Afh. SP
E0.S1 Zustand installeren pnpm add zustand succesvol, import werkt 1
E0.S2 Swift store opzetten stores/swift-store.ts met basis state E0.S1 2
E0.S3 Swift route aanmaken /epd/swift route met eigen layout E0.S2 2
E0.S4 Swift folder structuur components/swift/, lib/swift/ aangemaakt E0.S3 3

Epic 1 — Command Center DONE

Epic Doel: Werkende command center met tekst en voice input.

Story ID Beschrijving Acceptatiecriteria Status Afh. SP
E1.S1 Command Center layout 4-zone layout (context, canvas, recent, input), keyboard shortcuts (⌘K, Escape) E0.S4 3
E1.S2 Context Bar Dienst indicator, patiënt chip, user info E1.S1 2
E1.S3 Command Input Tekst input met placeholder, focus state, send button E1.S1 2
E1.S4 Voice Input integratie Deepgram streaming, waveform visualisatie E1.S3 3
E1.S5 Recent Strip Laatste 5 acties als chips, click-to-repeat E1.S1 3

Technical Notes:

Command Center Layout:
┌─────────────────────────────────────────┐
│ Context Bar (48px)                       │
├─────────────────────────────────────────┤
│                                         │
│ Canvas Area (flex) ← Blocks hier        │
│                                         │
├─────────────────────────────────────────┤
│ Recent Strip (48px)                     │
├─────────────────────────────────────────┤
│ Command Input (64px, fixed bottom)      │
└─────────────────────────────────────────┘

Epic 2 — Intent Classification DONE

Epic Doel: Two-tier intent classificatie (local + AI fallback) + wiring naar blocks.

Story ID Beschrijving Acceptatiecriteria Status Afh. SP
E2.S1 Local classifier Regex patterns voor P1 intents, <50ms E0.S4 3
E2.S2 Entity extraction Patient naam, categorie uit input E2.S1 3
E2.S3 AI fallback Claude Haiku bij confidence <0.8 E2.S2 2
E2.S4 Intent API route POST /api/intent/classify met logging E2.S3 2
E2.S5 Input → Block wiring CommandInput.handleSubmit → API → openBlock E2.S4 2

E2.S5 Technical Notes ( GEÏMPLEMENTEERD):

// components/swift/command-center/command-input.tsx
// IMPLEMENTATIE:
const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  if (!hasValue || isProcessing) return;
  
  const inputText = inputValue.trim();
  setIsProcessing(true);
  
  try {
    const response = await fetch('/api/intent/classify', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ input: inputText }),
    });
    
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({ error: 'Onbekende fout' }));
      throw new Error(errorData.error || `HTTP ${response.status}`);
    }
    
    const { intent, confidence, entities } = await response.json();
    
    if (intent !== 'unknown' && confidence >= 0.5) {
      openBlock(intent as BlockType, entities);
      addRecentAction({ intent, label: inputText.slice(0, 50), patientName: entities.patientName });
      clearInput();
    } else {
      // Tijdelijke fallback naar dagnotitie (wordt vervangen door FallbackPicker in E4.S4)
      openBlock('dagnotitie', { content: inputText });
      addRecentAction({ intent: 'dagnotitie', label: inputText.slice(0, 50) });
      clearInput();
    }
  } catch (error) {
    // Error handling met fallback naar dagnotitie
    openBlock('dagnotitie', { content: inputText });
    addRecentAction({ intent: 'dagnotitie', label: inputText.slice(0, 50) });
    clearInput();
  } finally {
    setIsProcessing(false);
  }
};

Epic 3 — P1 Blocks DONE

Epic Doel: Werkende DagnotatieBlock, ZoekenBlock en OverdrachtBlock.

Story ID Beschrijving Acceptatiecriteria Status Afh. SP
E3.S0 CanvasArea block rendering Switch/case voor block types, prefill doorgeven E2.S5 2
E3.S1 Block Container Animatie wrapper, close button, sizes E1.S1 2
E3.S2 DagnotatieBlock Patient, categorie, tekst, opslaan naar /api/reports E3.S0, E3.S1 5
E3.S3 Patient search API GET /api/patients/search?q= fuzzy search E0.S4 3
E3.S4 ZoekenBlock Input, resultaten, selectie → store E3.S0, E3.S3 3
E3.S5 PatientContextCard Na selectie: notities, vitals, diagnose E3.S4 5
E3.S6 OverdrachtBlock AI samenvatting per patiënt (bestaande API) E3.S0 3

E3.S0 Technical Notes ( GEÏMPLEMENTEERD):

// components/swift/command-center/canvas-area.tsx
// IMPLEMENTATIE:
import { DagnotatieBlock } from '../blocks/dagnotitie-block';
import { ZoekenBlock } from '../blocks/zoeken-block';
import { OverdrachtBlock } from '../blocks/overdracht-block';
import { PatientContextCard } from '../blocks/patient-context-card';

function renderBlock(activeBlock: BlockType, prefillData: BlockPrefillData) {
  switch (activeBlock) {
    case 'dagnotitie':
      return <DagnotatieBlock prefill={prefillData} />;
    case 'zoeken':
      return <ZoekenBlock prefill={prefillData} />;
    case 'overdracht':
      return <OverdrachtBlock prefill={prefillData} />;
    default:
      return null;
  }
}

// In CanvasArea:
{activeBlock ? (
  <AnimatePresence mode="wait">
    <motion.div key={activeBlock} {...blockAnimations}>
      {renderBlock(activeBlock, prefillData)}
    </motion.div>
  </AnimatePresence>
) : activePatient ? (
  <motion.div key="patient-context" {...blockAnimations}>
    <PatientContextCard />
  </motion.div>
) : (
  <EmptyState />
)}

E3.S2 Technical Notes ( GEÏMPLEMENTEERD):

// components/swift/blocks/dagnotitie-block.tsx
// IMPLEMENTATIE:
// 1. Patient search: Debounced search met /api/fhir/Patient?q=...
// 2. Category selector: 5 categorie buttons (medicatie, adl, gedrag, incident, observatie)
// 3. Tekst input: Textarea met character counter (max 500)
// 4. Opslaan: POST /api/reports met type 'verpleegkundig'
// 5. Success toast + auto-close block na 500ms

Epic 4 — Navigation & Auth DONE

Epic Doel: Login pagina met interface keuze, routing naar Swift/Klassiek.

Story ID Beschrijving Acceptatiecriteria Status Afh. SP
E4.S1 Login form uitbreiden Interface selector (Swift/Klassiek) E0.S3 2
E4.S2 Preference opslag user_metadata.preferred_interface E4.S1 2
E4.S3 Redirect middleware /epd → preference route E4.S2 2
E4.S4 Fallback Picker Visuele keuze bij lage confidence E3.S0 2

Epic 5 — Polish & Testing 🔄 IN PROGRESS

Epic Doel: Gepolijste UX met animaties, error handling en tests.

Story ID Beschrijving Acceptatiecriteria Status Afh. SP
E5.S1 Block animaties Slide up/down met framer-motion E3.S0 2
E5.S2 Error handling Network errors, validation, toasts E3.S6 2
E5.S3 Keyboard shortcuts Verificatie + ⌘Enter shortcuts E1.S1 2
E5.S4 Smoke tests Happy flow tests voor alle P1 blocks E5.S2 2

E5.S2 Technical Notes ( GEÏMPLEMENTEERD):

// lib/swift/error-handler.ts
// IMPLEMENTATIE: Gecentraliseerde error handling utility

// 1. Offline detection
export function isOffline(): boolean {
  return typeof navigator !== 'undefined' && !navigator.onLine;
}

// 2. Network error detection
export function isNetworkError(error: unknown): boolean {
  if (error instanceof TypeError) {
    return error.message.includes('fetch') ||
           error.message.includes('network') ||
           error.message.includes('Failed to fetch');
  }
  return false;
}

// 3. User-friendly error messages (Dutch)
export function getErrorInfo(error: unknown, context?: ErrorContext): ErrorInfo {
  if (isOffline()) {
    return {
      title: 'Geen internetverbinding',
      description: 'Controleer je internetverbinding en probeer het opnieuw.',
      retryable: true,
    };
  }

  // HTTP status code mapping
  if (context?.statusCode) {
    switch (context.statusCode) {
      case 401: return { title: 'Niet geautoriseerd', ... };
      case 404: return { title: 'Niet gevonden', ... };
      case 500: return { title: 'Serverfout', ... };
      // etc.
    }
  }
  // ...
}

// 4. Safe fetch wrapper met timeout (30s)
export async function safeFetch(
  url: string,
  options?: RequestInit,
  context?: ErrorContext
): Promise<Response> {
  if (isOffline()) throw new Error('Geen internetverbinding');

  const response = await fetch(url, {
    ...options,
    signal: AbortSignal.timeout(30000), // 30s timeout
  });

  if (!response.ok) {
    const errorData = await parseErrorResponse(response);
    throw new Error(errorData.error);
  }

  return response;
}

// 5. Retry logic met exponential backoff
export async function retryFetch<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  delayMs: number = 1000
): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const errorInfo = getErrorInfo(error);
      if (!errorInfo.retryable || attempt === maxRetries - 1) {
        throw error;
      }
      await new Promise(r => setTimeout(r, delayMs * (attempt + 1)));
    }
  }
}

// components/swift/command-center/offline-banner.tsx
// IMPLEMENTATIE: Offline detection banner
export function OfflineBanner() {
  const [isOffline, setIsOffline] = useState(false);

  useEffect(() => {
    setIsOffline(!navigator.onLine);
    window.addEventListener('online', () => setIsOffline(false));
    window.addEventListener('offline', () => setIsOffline(true));
    // cleanup
  }, []);

  if (!isOffline) return null;

  return (
    <div className="fixed top-0 left-0 right-0 z-[100] bg-amber-500 text-white">
      <WifiOff /> Geen internetverbinding
    </div>
  );
}

// Usage in blocks:
// - safeFetch() voor alle API calls (vervangt raw fetch)
// - getErrorInfo() voor user-friendly toast messages
// - retryFetch() voor dagnotitie/overdracht save operaties (max 3 retries)

E5.S3 Technical Notes ( GEÏMPLEMENTEERD):

// components/swift/command-center/command-input.tsx
// IMPLEMENTATIE: ⌘Enter quick submit

// Keyboard shortcut: Cmd/Ctrl+Enter to submit
useEffect(() => {
  const handleKeyDown = (e: KeyboardEvent) => {
    // Cmd/Ctrl+Enter: submit command
    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
      e.preventDefault();
      handleSubmit();
    }
  };

  window.addEventListener('keydown', handleKeyDown);
  return () => window.removeEventListener('keydown', handleKeyDown);
}, [handleSubmit]);

// components/swift/blocks/dagnotitie-block.tsx
// IMPLEMENTATIE: ⌘Enter quick save

// Keyboard shortcut: Cmd/Ctrl+Enter to save
useEffect(() => {
  const handleKeyDown = (e: KeyboardEvent) => {
    // Cmd/Ctrl+Enter: save dagnotitie
    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
      e.preventDefault();
      handleSave();
    }
  };

  window.addEventListener('keydown', handleKeyDown);
  return () => window.removeEventListener('keydown', handleKeyDown);
}, [handleSave]);

// Visual hint op submit button
<Button type="submit" title="Opslaan (⌘Enter)">
  {isSubmitting ? (
    <>
      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
      Opslaan...
    </>
  ) : (
    <>
      Opslaan
      <span className="ml-2 text-xs opacity-70 hidden sm:inline">⌘↵</span>
    </>
  )}
</Button>

Bestaande shortcuts (E1.S1, E4.S4) geverifieerd:

  • ⌘K / Ctrl+K - Focus command input (CommandCenter)
  • Escape - Close active block (CommandCenter + FallbackPicker)
  • 1-3 - Quick select in FallbackPicker
  • Enter - Native form submit in CommandInput

Nieuwe shortcuts (E5.S3):

  • ⌘Enter / Ctrl+Enter - Quick submit in CommandInput
  • ⌘Enter / Ctrl+Enter - Quick save in DagnotatieBlock

Documentatie:

  • docs/swift/keyboard-shortcuts-reference.md - Complete shortcut reference
  • docs/swift/test-plan-e5-s3-keyboard-shortcuts.md - Test plan

E5.S1 Technical Notes ( GEÏMPLEMENTEERD):

// components/swift/command-center/canvas-area.tsx
// IMPLEMENTATIE volgens UX specificatie (sectie 11.1):
// Openen: Slide up + fade in (200ms), Scale: 0.95 → 1.0
// Sluiten: Slide down + fade out (200ms), Scale: 1.0 → 0.95
const blockAnimations = {
  initial: { opacity: 0, y: 20, scale: 0.95 },
  animate: { 
    opacity: 1, 
    y: 0, 
    scale: 1,
    transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
  },
  exit: { 
    opacity: 0, 
    y: 20, // Slide down (niet omhoog)
    scale: 0.95,
    transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
  },
};

// AnimatePresence met mode="wait" voor soepele transitions
<AnimatePresence mode="wait" initial={false}>
  {activeBlock && (
    <motion.div 
      key={activeBlock}
      initial={blockAnimations.initial}
      animate={blockAnimations.animate}
      exit={blockAnimations.exit}
    >
      {renderBlock(activeBlock, prefillData)}
    </motion.div>
  )}
</AnimatePresence>

Nota: Keyboard shortcuts (⌘K focus, Escape close) zijn al geïmplementeerd in E1.S1. E5.S3 is nu verificatie + eventuele uitbreiding (Enter submit, etc.).


5. Technische Debt & Bekende Issues

5.1 Type Duplicatie (Medium Priority)

Probleem:

// lib/swift/types.ts - BRON
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';

// stores/swift-store.ts - DUPLICAAT (moet verwijderd worden)
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';

Impact: Bij uitbreiding intents (diagnostiek workflow) moet op twee plekken gewijzigd worden.

Oplossing: Verwijder duplicaat uit swift-store.ts, importeer uit lib/swift/types.ts:

// stores/swift-store.ts
import type { SwiftIntent, BlockType, ShiftType } from '@/lib/swift/types';

Wanneer: Voorafgaand aan E-D1.S1 (diagnostiek intent patterns).

5.2 BlockContainer Animaties (RESOLVED)

Probleem: BlockContainer bestaat maar heeft nog geen framer-motion animaties.

Oplossing: Geïmplementeerd in E3.S1. BlockContainer heeft nu volledige animatie support met framer-motion.

5.3 CanvasArea Placeholder (RESOLVED)

Probleem: CanvasArea toont placeholder tekst i.p.v. blocks.

Oplossing: Geïmplementeerd in E3.S0. CanvasArea heeft nu volledige block rendering met switch/case en animaties.


6. Kwaliteit & Testplan

6.1 Test Types

Test Type Scope Tools Wanneer
Unit Tests Intent classifier, entity extraction Vitest E2
Integration Tests API endpoints Vitest + MSW E2, E3
Component Tests Blocks, Command Center React Testing Library E3, E5
E2E Tests Complete flows Playwright (optioneel) E5
Manual Tests Demo scenarios Checklist E5

6.2 Test Coverage Targets

Area Target Reden
Intent classifier 90%+ Kritiek voor UX
API routes 80%+ Data integrity
UI components 60%+ Belangrijkste flows

6.3 Manual Test Checklist (MVP Demo)

Status: 🔄 12/14 scenarios geïmplementeerd (86%)

Happy Flows:

  • User kan inloggen en Swift kiezen (E4)
  • Command input krijgt focus met Cmd+K (E1.S1)
  • ⌘Enter quick submit → block opent (E5.S3) NEW!
  • "notitie jan medicatie" → DagnotatieBlock opent met prefill (E3.S2)
  • Dagnotitie opslaan → toast + block sluit (E3.S2)
  • ⌘Enter in dagnotitie → quick save (E5.S3) NEW!
  • "zoek marie" → ZoekenBlock met resultaten (E3.S4)
  • Patiënt selecteren → PatientContextCard (E3.S5)
  • "overdracht" → OverdrachtBlock met AI samenvatting (E3.S6)
  • Voice input → transcript in command input (E1.S4)

Error Scenarios:

  • Onbekende intent → FallbackPicker (E4.S4)
  • Offline mode → banner + error toast (E5.S2) NEW!
  • Network error → toast met retry (E5.S2)
  • Lege notitie → validation error (E3.S2)
  • Geen zoekresultaten → "Geen patiënten gevonden" (E3.S4)

Keyboard Shortcuts:

  • Escape sluit block (E1.S1)
  • 1-3 in FallbackPicker → quick select (E4.S4)

Te Testen (E5.S4):

  • End-to-end smoke test alle flows
  • Performance check (< 100ms block open)
  • Cross-browser test (Chrome, Safari, Firefox)

7. Demo & Presentatieplan

7.1 Demo Scenario

Duur: 10 minuten Doelgroep: Zorgprofessionals, management Locatie: Live op Vercel

Flow:

1. INTRO (1 min)
   "40% van je tijd gaat naar administratie.
    Wat als je gewoon kon zeggen wat je wilt?"

2. CONTRAST (2 min)
   Klassiek EPD: Dashboard → Patiënten → Jan → Rapportages →
                 Nieuwe → Type → Tekst → Opslaan
   Swift: "notitie jan medicatie gegeven" → Opslaan

3. DAGNOTITIE FLOW (2 min)
   - Typ: "notitie jan medicatie uitgereikt"
   - Block verschijnt voorgevuld
   - Één klik: opgeslagen

4. VOICE DEMO (2 min)
   - Klik microfoon
   - Spreek: "marie had een rustige nacht, goed geslapen"
   - Block verschijnt met transcript

5. OVERDRACHT (2 min)
   - "overdracht"
   - AI genereert samenvatting per patiënt
   - Toon bronverwijzingen

6. AFSLUITING (1 min)
   - Tijdsbesparing recap
   - Vragen

7.2 Backup Plan

Probleem Oplossing
Internet issues Localhost met demo data
Voice niet werkt Type-only demo
AI API down Pre-cached responses
Complete failure Video recording

8. Risico's & Mitigatie

Status Update (27-12-2024): Meeste risico's zijn gemitigeerd!

Risico Kans Impact Mitigatie Status
Voice accuracy NL Middel Hoog Deepgram NL model, fallback naar tekst Gemitigeerd - Voice werkt met transcript fallback
Intent misclassificatie Laag ↓ Middel ↓ Two-tier systeem, FallbackPicker Gemitigeerd - FallbackPicker geïmplementeerd (E4.S4)
AI latency Laag Middel Local-first, Haiku model Gemitigeerd - Local classifier <50ms
User adoption Middel Middel Keuze behouden, geen dwang Gemitigeerd - Interface selector (E4.S1-S3)
Scope creep Laag ↓ Laag ↓ Strict P1-only, backlog voor rest Onder controle - P1 scope behouden
Performance Laag Laag Code splitting, lazy loading, animaties Gemitigeerd - <200ms transitions (E5.S1)
Network errors Laag Laag Offline detection, retry logic Gemitigeerd - Error handler (E5.S2)
CanvasArea blocking Hoog Hoog E3.S0 prioriteit OPGELOST - E3.S0 compleet

Nieuwe Risico's:

Risico Kans Impact Mitigatie Status
Demo preparatie Laag Middel E5.S4 smoke tests, rehearsal In behandeling
Cross-browser issues Laag Laag Test in Chrome/Safari/Firefox E5.S4
Production deployment Laag Middel Vercel deployment check Post-MVP

Conclusie: Project risico's zijn minimaal. MVP is stabiel en klaar voor testing.


9. Sprint Planning (Aangepast)

Huidige Status (27-12-2024)

  • E0: Setup & Foundation (8 SP) — DONE
  • E1: Command Center (13 SP) — DONE
  • E2: Intent Classification (12 SP) — DONE
  • E3: P1 Blocks (23 SP) — DONE
  • E4: Navigation & Auth (8 SP) — DONE
  • 🔄 E5: Polish & Testing (8 SP) — IN PROGRESS (6/8 SP done)
  • E5.S1: Block animaties (2 SP) — DONE
  • E5.S2: Error handling (2 SP) — DONE
  • E5.S3: Keyboard shortcuts (2 SP) — DONE
  • E5.S4: Smoke tests (2 SP) — TO DO

Totaal Done: 62 SP / 72 SP (86%)

Sprint 3 (Voltooid): Core Wiring + Blocks

  • E2.S5: Input → Block wiring (2 SP) — DONE
  • E3.S0: CanvasArea block rendering (2 SP) — DONE
  • E3.S1: Block Container (2 SP) — DONE
  • E3.S2: DagnotatieBlock (5 SP) — DONE
  • Deliverable: "notitie jan" → DagnotatieBlock werkt end-to-end

Sprint 4 (Voltooid): Remaining Blocks

  • E3.S3: Patient search API (3 SP) — DONE
  • E3.S4: ZoekenBlock (3 SP) — DONE
  • E3.S5: PatientContextCard (5 SP) — DONE
  • E3.S6: OverdrachtBlock (3 SP) — DONE
  • Deliverable: Alle P1 blocks werken

Sprint 5: Polish & Ship (In Progress)

  • E4: Navigation & Auth (8 SP) — DONE
  • 🔄 E5: Polish & Testing (8 SP) — IN PROGRESS (6/8 SP done)
    • E5.S1: Block animaties (2 SP) — DONE
    • E5.S2: Error handling (2 SP) — DONE
    • E5.S3: Keyboard shortcuts (2 SP) — DONE
    • E5.S4: Smoke tests (2 SP) — TO DO
  • Technische debt opruimen
  • Deliverable: Demo-ready MVP

Laatste 2 SP voor MVP compleet! 🎯


10. Uitbreidingen (Backlog)

Diagnostiek Workflow (22 SP)

Status: Gepland na MVP Documentatie: bouwplan-swift-diagnostiek-workflow.md

Epic Stories SP Vereisten
E-D1 Afspraak & Rapportage 12 E3 compleet
E-D2 Diagnose Beheer 10 E3 compleet

Pre-requisites:

  1. Swift MVP compleet (E0-E5)
  2. Report type "diagnostiek" toevoegen aan REPORT_TYPES
  3. SwiftIntent uitbreiden met nieuwe types
  4. Entity extraction voor datum/tijd

Zie: bouwplan-swift-diagnostiek-workflow-beoordeling.md voor details.


11. Definition of Done

Een story is Done wanneer:

  • Code geschreven en werkend
  • TypeScript types correct (geen duplicaten)
  • Component responsive (mobile + desktop)
  • Error states afgehandeld
  • Toegankelijkheid basics (focus, labels)
  • Getest in Chrome + Safari
  • PR reviewed (indien team)
  • Gemerged naar main

Een epic is Done wanneer:

  • Alle stories Done
  • Integration test passed
  • Demo scenario werkt

12. Referenties

Project Documenten

  • PRD: docs/swift/swift-prd.md
  • FO: docs/swift/swift-fo-ai.md
  • TO: docs/swift/to-swift-v1.md
  • UX: docs/swift/swift-ux-v2.1.md
  • Diagnostiek FO: docs/swift/swift-fo-diagnostiek-workflow.md
  • Diagnostiek Bouwplan: docs/swift/bouwplan-swift-diagnostiek-workflow.md
  • Beoordeling: docs/swift/bouwplan-swift-diagnostiek-workflow-beoordeling.md

Bestaande Code Referenties

  • Swift Store: stores/swift-store.ts
  • Swift Types: lib/swift/types.ts (SINGLE SOURCE)
  • Intent Classifier: lib/swift/intent-classifier.ts
  • Intent API: app/api/intent/classify/route.ts
  • Command Center: components/swift/command-center/
  • Block Container: components/swift/blocks/block-container.tsx

Bestaande EPD Code (Hergebruik)

  • Command component: components/ui/command.tsx
  • Speech streaming: components/speech-recorder-streaming.tsx
  • Overdracht API: app/api/overdracht/generate/route.ts
  • Report types: lib/types/report.ts
  • Reports API: app/api/reports/route.ts

External


13. Glossary

Term Betekenis
Swift Projectnaam voor Contextual UI EPD
Command Center Hoofdscherm met één input
Block Ephemeral UI component (dagnotitie, zoeken, etc.)
Intent Gebruikersintentie (dagnotitie, zoeken, overdracht)
Entity Geëxtraheerde data (patiëntnaam, categorie)
Prefill Vooraf ingevulde data in block
Klassiek EPD Traditionele menu-gebaseerde interface
P1 Prioriteit 1 (MVP scope)
SP Story Points (Fibonacci: 1, 2, 3, 5, 8)
Wiring Koppeling tussen componenten (input → API → block)

Versiehistorie:

Versie Datum Auteur Wijziging
v1.0 23-12-2024 Colin Lit Initiële versie
v1.1 23-12-2024 Claude E0 + E1 voltooid (21 SP)
v1.2 23-12-2024 Claude E2.S1 + E2.S2 voltooid (27 SP)
v1.3 23-12-2024 Claude E2.S3 AI fallback voltooid (29 SP)
v1.4 23-12-2024 Claude E2.S1-S4 voltooid (31 SP)
v1.5 24-12-2024 Claude E2.S5 toegevoegd: Input → Block wiring (+2 SP)
v2.0 24-12-2024 Claude Major update: E3.S0 toegevoegd, technische debt sectie, diagnostiek workflow referentie, sprint planning aangepast (29 stories, 72 SP)
v2.1 24-12-2024 Claude E2.S5 voltooid: Input → Block wiring geïmplementeerd, Epic 2 compleet (33 SP done, 46%)
v2.2 24-12-2024 Claude Epic 3 compleet: Alle P1 blocks geïmplementeerd (E3.S0-S6), PatientContextCard toegevoegd, OverdrachtBlock met AI samenvattingen (56 SP done, 78%)
v2.3 24-12-2024 Claude E5.S1 compleet: Block animaties geïmplementeerd volgens UX specificatie (slide up/down met fade en scale, 200ms), Epic 4 compleet (58 SP done, 81%)
v2.4 27-12-2024 Claude E5.S2 compleet: Error handling met gecentraliseerde utilities, OfflineBanner, safeFetch, retry logic, Nederlandse error messages (60 SP done, 83%)
v2.5 27-12-2024 Claude E5.S3 compleet: Keyboard shortcuts geverifieerd en uitgebreid, ⌘Enter quick submit/save, visual hints, shortcuts reference (62 SP done, 86%)