Files
triqura-ecd/lib/cortex/intent-labels.ts
colinislit 42d64761ec feat(cortex): Epic 3 - UI Components complete
Epic 3 delivers the visual layer for multi-intent flows and clarification:

E3.S1 - ActionChainCard (3 SP)
- Container for multi-intent chain display
- Header with AI/Local source badge and action count
- Original user input context
- Stacked ActionItems with Framer Motion animations
- Collapsible AI reasoning section
- Status-dependent styling (pending/executing/completed/partial/failed)

E3.S2 - ActionItem (2 SP)
- 6 status icons (pending, confirming, executing, success, failed, skipped)
- Dutch intent labels via shared intent-labels.ts
- Confidence badge with color coding (green ≥90%, amber ≥70%, red <70%)
- Entity summary formatting
- Confirmation buttons for confirming status
- Retry button for failed + recoverable actions

E3.S3 - ClarificationCard (2 SP)
- Amber-themed styling for question context
- Header with HelpCircle icon
- Original user input display
- Responsive options grid (2 cols desktop, 1 col mobile)
- Dismiss button and cancel link
- Framer Motion entry/exit animations

E3.S4 - ProcessingIndicator (1 SP)
- 3 variants: spinner, skeleton, pulse
- 3 sizes: sm, md, lg
- Configurable message
- InlineSpinner export for buttons

Integration in ChatPanel (v4.0):
- V2 store hooks for activeChain, pendingClarification
- Event handlers for confirm/skip/retry/dismiss
- Conditional rendering with AnimatePresence

New files:
- lib/cortex/intent-labels.ts
- components/cortex/chat/action-chain-card.tsx
- components/cortex/chat/action-item.tsx
- components/cortex/chat/clarification-card.tsx
- components/cortex/chat/processing-indicator.tsx

Modified:
- components/cortex/chat/chat-panel.tsx (v3.0 → v4.0)
- docs/intent/bouwplan-cortex-v2.md (v1.2 → v1.3)

Progress: 35/48 SP (73%) - E0, E1, E2, E3 complete

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 12:02:15 +01:00

183 lines
4.1 KiB
TypeScript

/**
* Intent Labels & Status Styles
*
* Shared Dutch labels and styling for Cortex V2 UI components.
* Used by ActionChainCard, ActionItem, and ClarificationCard.
*
* Epic: E3 (UI Components)
*/
import type { CortexIntent, IntentActionStatus, NudgePriority, ExtractedEntities } from './types';
/**
* Dutch labels for intent types
*/
export const INTENT_LABELS: Record<CortexIntent, string> = {
dagnotitie: 'Dagnotitie',
zoeken: 'Patiënt zoeken',
overdracht: 'Overdracht',
agenda_query: 'Agenda bekijken',
create_appointment: 'Afspraak maken',
cancel_appointment: 'Afspraak annuleren',
reschedule_appointment: 'Afspraak verzetten',
unknown: 'Onbekend',
};
/**
* Status styling for IntentAction items
*/
export const STATUS_STYLES: Record<
IntentActionStatus,
{ bg: string; border: string; text: string; icon: string }
> = {
pending: {
bg: 'bg-slate-50',
border: 'border-slate-200',
text: 'text-slate-700',
icon: 'text-slate-300',
},
confirming: {
bg: 'bg-amber-50',
border: 'border-amber-200',
text: 'text-amber-700',
icon: 'text-amber-500',
},
executing: {
bg: 'bg-blue-50',
border: 'border-blue-200',
text: 'text-blue-700',
icon: 'text-blue-500',
},
success: {
bg: 'bg-green-50',
border: 'border-green-200',
text: 'text-green-700',
icon: 'text-green-500',
},
failed: {
bg: 'bg-red-50',
border: 'border-red-200',
text: 'text-red-700',
icon: 'text-red-500',
},
skipped: {
bg: 'bg-slate-50',
border: 'border-slate-200',
text: 'text-slate-400',
icon: 'text-slate-400',
},
};
/**
* Get confidence badge styling based on confidence score
* >= 0.9: green (high confidence)
* >= 0.7: amber (medium confidence)
* < 0.7: red (low confidence)
*/
export function getConfidenceStyle(confidence: number): {
bg: string;
text: string;
label: string;
} {
if (confidence >= 0.9) {
return {
bg: 'bg-green-50',
text: 'text-green-700',
label: 'Hoog',
};
}
if (confidence >= 0.7) {
return {
bg: 'bg-amber-50',
text: 'text-amber-700',
label: 'Gemiddeld',
};
}
return {
bg: 'bg-red-50',
text: 'text-red-700',
label: 'Laag',
};
}
/**
* Priority styling for NudgeSuggestion toasts
*/
export const PRIORITY_STYLES: Record<
NudgePriority,
{ bg: string; border: string; text: string }
> = {
low: {
bg: 'bg-blue-50',
border: 'border-blue-200',
text: 'text-blue-700',
},
medium: {
bg: 'bg-amber-50',
border: 'border-amber-200',
text: 'text-amber-700',
},
high: {
bg: 'bg-red-50',
border: 'border-red-200',
text: 'text-red-700',
},
};
/**
* Format entity summary for display in ActionItem
*/
export function formatEntitySummary(
intent: CortexIntent,
entities: ExtractedEntities
): string {
const parts: string[] = [];
// Patient name
if (entities.patientName) {
parts.push(`Patiënt: ${entities.patientName}`);
}
// Content/query
if (entities.content && typeof entities.content === 'string') {
const truncated =
entities.content.length > 50
? `${entities.content.slice(0, 50)}...`
: entities.content;
parts.push(truncated);
}
if (entities.query && typeof entities.query === 'string') {
parts.push(`Zoekterm: "${entities.query}"`);
}
// Date/time
if (entities.datetime) {
const dt = entities.datetime;
// datetime.date is a Date object, format it
const dateStr = dt.date ? dt.date.toLocaleDateString('nl-NL') : null;
if (dateStr && dt.time) {
parts.push(`${dateStr} om ${dt.time}`);
} else if (dateStr) {
parts.push(dateStr);
} else if (dt.time) {
parts.push(`om ${dt.time}`);
}
}
// Category for dagnotitie
if (intent === 'dagnotitie' && entities.category) {
const categoryLabels: Record<string, string> = {
medicatie: 'Medicatie',
adl: 'ADL',
gedrag: 'Gedrag',
incident: 'Incident',
observatie: 'Observatie',
};
const label = categoryLabels[entities.category as string] || entities.category;
parts.push(`Categorie: ${label}`);
}
return parts.join(' • ') || 'Geen details';
}