feat(cortex): Epic 4 - Nudge MVP (Layer 3) complete

Proactive suggestions after successful actions - proof of concept.

E4.S1 - Protocol Rules (lib/cortex/nudge.ts)
- ProtocolRule and ProtocolCondition types
- Condition operators: equals, contains, matches, exists
- 2 hardcoded rules: wondzorg-controle, medicatie-controle

E4.S2 - evaluateNudge Function
- evaluateNudge(input) returns NudgeSuggestion[]
- checkCondition() helper for rule matching
- Priority sorting (high → medium → low)

E4.S3 - NudgeToast Component
- Countdown progress bar with auto-dismiss (5 min)
- Priority-based styling (low/medium/high)
- Accept and dismiss buttons
- Framer Motion animations

E4.S4 - Integration
- NudgeToast rendered in CommandCenter (fixed position)
- Trigger added in DagnotatieBlock after successful save
- Accept opens corresponding artifact with prefilled data

Demo flow:
1. Create dagnotitie with "wond" in content
2. Save → NudgeToast appears
3. Click "Ja, inplannen" → Appointment artifact opens

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
colinislit
2026-01-01 12:42:49 +01:00
parent 42d64761ec
commit 4b759c9b3d
6 changed files with 506 additions and 17 deletions

View File

@@ -25,6 +25,7 @@ import { Button } from '@/components/ui/button';
import { Loader2, Search, User, RefreshCw } from 'lucide-react';
import { cn } from '@/lib/utils';
import { safeFetch, getErrorInfo, retryFetch } from '@/lib/cortex/error-handler';
import { evaluateNudge } from '@/lib/cortex/nudge';
interface DagnotitieBlockProps {
prefill?: BlockPrefillData;
@@ -39,7 +40,7 @@ interface Patient {
export function DagnotatieBlock({ prefill }: DagnotitieBlockProps) {
const config = BLOCK_CONFIGS.dagnotitie;
const { closeBlock } = useCortexStore();
const { closeBlock, addSuggestion } = useCortexStore();
const { toast } = useToast();
// Form state
@@ -221,6 +222,23 @@ export function DagnotatieBlock({ prefill }: DagnotitieBlockProps) {
description: `Notitie voor ${patientName} is opgeslagen`,
});
// E4: Evaluate nudge after successful save
const nudges = evaluateNudge({
intent: 'dagnotitie',
actionId: data.id || `dagnotitie-${Date.now()}`,
entities: {
patientName,
category,
},
content: content.trim(),
});
// Add nudge suggestions to store
nudges.forEach((nudge) => {
addSuggestion(nudge);
console.log('[DagnotatieBlock] Nudge triggered:', nudge.suggestion.message);
});
// Close block after short delay
setTimeout(() => {
closeBlock();
@@ -241,7 +259,7 @@ export function DagnotatieBlock({ prefill }: DagnotitieBlockProps) {
} finally {
setIsSubmitting(false);
}
}, [patientId, content, category, includeInHandover, patientName, toast, closeBlock]);
}, [patientId, content, category, includeInHandover, patientName, toast, closeBlock, addSuggestion]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();