'use client'; /** * Artifact Container Component * * Container die meerdere artifacts kan beheren met tabs. * Max 3 artifacts tegelijk, tabs alleen zichtbaar bij >1 artifact. * * Epic: E4 (Artifact Area & Tabs) * Story: E4.S1 (ArtifactContainer component) */ import { ArtifactTab } from './artifact-tab'; import { AgendaBlock, type AgendaBlockProps } from './blocks/agenda-block'; import { DagnotatieBlock } from '../blocks/dagnotitie-block'; import { ZoekenBlock } from '../blocks/zoeken-block'; import { OverdrachtBlock } from '../blocks/overdracht-block'; import { PatientDashboardBlock } from '../blocks/patient-dashboard-block'; import { FallbackPicker } from '../blocks/fallback-picker'; import { APPOINTMENT_TYPES, type AppointmentTypeCode, type LocationClassCode } from '@/app/epd/agenda/types'; import type { Artifact, BlockType } from '@/stores/cortex-store'; import { parseRelativeDate, isDateRange } from '@/lib/cortex/date-time-parser'; interface ArtifactContainerProps { artifacts: Artifact[]; activeArtifactId: string | null; onSelectArtifact: (id: string) => void; onCloseArtifact: (id: string) => void; } type AgendaIntent = 'agenda_query' | 'create_appointment' | 'cancel_appointment' | 'reschedule_appointment'; type AgendaMode = AgendaBlockProps['mode']; const AGENDA_MODE_MAP: Record = { agenda_query: 'list', create_appointment: 'create', cancel_appointment: 'cancel', reschedule_appointment: 'reschedule', }; const LOCATION_CODE_MAP: Record = { praktijk: 'AMB', online: 'VR', thuis: 'HH', }; function coerceDate(value: unknown): Date | undefined { if (value instanceof Date) return value; if (typeof value === 'string' || typeof value === 'number') { const parsed = new Date(value); if (!Number.isNaN(parsed.getTime())) { return parsed; } } return undefined; } function coerceDateRange(raw: any): AgendaBlockProps['dateRange'] | undefined { const label = typeof raw?.label === 'string' ? raw.label : undefined; if (!label) return undefined; return { start: coerceDate(raw?.start), end: coerceDate(raw?.end), label, }; } function resolveAppointmentType(value: unknown): AppointmentTypeCode | undefined { if (typeof value !== 'string') return undefined; if (value in APPOINTMENT_TYPES) return value as AppointmentTypeCode; return undefined; } function resolveLocation(value: unknown): LocationClassCode | undefined { if (typeof value !== 'string') return undefined; if (value in LOCATION_CODE_MAP) return LOCATION_CODE_MAP[value]; if (value === 'AMB' || value === 'VR' || value === 'HH') return value; return undefined; } /** * Convert date label to Date using central date-time-parser (DRY) */ function resolveDateFromLabel(label?: string): Date | null { if (!label) return null; const parsed = parseRelativeDate(label); if (!parsed) return null; // DateRange → return start date if (isDateRange(parsed)) return parsed.start; return parsed; } function buildAgendaPrefill(prefill: Record | undefined): AgendaBlockProps['prefillData'] { if (!prefill || typeof prefill !== 'object') return undefined; const patient = prefill.patient || (prefill.patientName || prefill.patientId ? { id: prefill.patientId || '', name: prefill.patientName || '', } : undefined); // Try to resolve date from label first (more reliable than AI-generated dates) const dateLabel = prefill?.datetime?.label || prefill?.dateRange?.label; const resolvedDate = resolveDateFromLabel(dateLabel); const datetimeDate = resolvedDate || // Prefer calculated date from label coerceDate(prefill?.datetime?.date) || (prefill?.datetime?.time ? new Date() : undefined); const datetime = datetimeDate ? { date: datetimeDate, time: typeof prefill?.datetime?.time === 'string' ? prefill.datetime.time : '', } : undefined; const appointmentType = resolveAppointmentType(prefill?.appointmentType || prefill?.type); const location = resolveLocation(prefill?.location); // Also resolve newDatetime from label const newDateLabel = prefill?.newDatetime?.label; const resolvedNewDate = resolveDateFromLabel(newDateLabel); const newDatetimeDate = resolvedNewDate || // Prefer calculated date from label coerceDate(prefill?.newDatetime?.date) || (prefill?.newDatetime?.time ? new Date() : undefined); const newDatetime = newDatetimeDate ? { date: newDatetimeDate, time: typeof prefill?.newDatetime?.time === 'string' ? prefill.newDatetime.time : '', } : undefined; return { patient, datetime, type: appointmentType, location, notes: typeof prefill?.notes === 'string' ? prefill.notes : undefined, identifier: prefill?.identifier, newDatetime, }; } /** * Render the appropriate block component based on artifact type */ function renderArtifactBlock(artifact: Artifact, onCloseArtifact: (id: string) => void) { switch (artifact.type) { case 'dagnotitie': return ; case 'zoeken': return ; case 'overdracht': return ; case 'agenda_query': case 'create_appointment': case 'cancel_appointment': case 'reschedule_appointment': { const agendaType = artifact.type as AgendaIntent; const rawPrefill = artifact.prefill as Record; const dateRange = coerceDateRange(rawPrefill?.dateRange); const prefillData = buildAgendaPrefill(rawPrefill); const appointments = Array.isArray(rawPrefill?.appointments) ? rawPrefill.appointments : undefined; const disambiguationOptions = Array.isArray(rawPrefill?.disambiguationOptions) ? rawPrefill.disambiguationOptions : undefined; return ( onCloseArtifact(artifact.id)} /> ); } case 'patient-dashboard': return ; case 'fallback': return ; default: return (
Onbekend artifact type: {artifact.type}
); } } /** * Get user-friendly title for artifact type */ export function getArtifactTitle(type: BlockType, prefill?: any): string { switch (type) { case 'dagnotitie': return prefill?.patientName ? `Dagnotitie - ${prefill.patientName}` : 'Dagnotitie'; case 'zoeken': return 'Patiënt Zoeken'; case 'overdracht': return 'Dienst Overdracht'; case 'agenda_query': return 'Agenda'; case 'create_appointment': return prefill?.patientName ? `Nieuwe afspraak - ${prefill.patientName}` : 'Nieuwe afspraak'; case 'cancel_appointment': return 'Afspraak annuleren'; case 'reschedule_appointment': return 'Afspraak verzetten'; case 'fallback': return 'Kies een actie'; case 'patient-dashboard': return prefill?.patientName ? `Dashboard - ${prefill.patientName}` : 'Patiëntoverzicht'; default: return 'Artifact'; } } export function ArtifactContainer({ artifacts, activeArtifactId, onSelectArtifact, onCloseArtifact, }: ArtifactContainerProps) { // Find active artifact const activeArtifact = artifacts.find((a) => a.id === activeArtifactId); // Show placeholder if no artifacts if (artifacts.length === 0) { return (
📋

Artifacts verschijnen hier

Vraag me iets in de chat om te beginnen!

); } return (
{/* Tabs - alleen tonen bij >1 artifact */} {artifacts.length > 1 && (
{artifacts.map((artifact) => ( ))}
)} {/* Active artifact content */}
{activeArtifact ? (
{renderArtifactBlock(activeArtifact, onCloseArtifact)}
) : (
Selecteer een artifact om te bekijken
)}
); }