Files
triqura-ecd/components/behandelplan/flat/behandelplan-flat.tsx
colinislit 265d3a971f feat(diagnose+agenda): Diagnose module met ICD-10 en agenda uitbreidingen
Diagnose Module:
- Diagnose overzicht pagina met alle patiënt diagnoses
- Diagnosis manager met ICD-10 combobox zoekfunctie
- Diagnose kaarten met hoofddiagnose markering
- Modal voor nieuwe/bewerkte diagnoses
- ICD-10 GGZ codes dataset (lib/data/)
- Zod schemas voor diagnose validatie
- TypeScript types voor ICD-10 (lib/types/icd10.ts)
- Complete documentatie (PRD, FO, TO, Bouwplan)

Agenda Uitbreidingen:
- Patient context card in afspraak modal
- Rapportage composer direct in afspraak modal
- Rapportage bewerken vanuit gekoppelde rapportages
- Verbeterde focus styling voor inputs

Behandelplan:
- Flat componenten structuur (behandeldoel-card, form, planning)
- Context header component
- Uitgebreide types (lib/types/behandelplan.ts)
- Actions voor behandelplan beheer

UI Componenten:
- Command component (shadcn/ui) voor combobox
- Popover component (shadcn/ui)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 13:29:59 +01:00

291 lines
8.9 KiB
TypeScript

'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { cn } from '@/lib/utils';
import {
type Behandeldoel,
type Behandelstructuur,
type Evaluatiemoment,
type Veiligheidsplan,
type SmartGoal,
type Intervention,
type FhirCarePlanStatus,
FHIR_STATUS_LABELS,
transformToFlat,
createEmptyBehandeldoel,
calculateBehandeldoelenProgress,
} from '@/lib/types/behandelplan';
import { type LifeDomainScore } from '@/lib/types/leefgebieden';
import { ContextHeader } from './context-header';
import { BehandeldoelCard } from './behandeldoel-card';
import { PlanningSection } from './planning-section';
import { Plus, Sparkles, FileText, CheckCircle2 } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
interface Condition {
id: string;
category: string;
code_display: string;
severity_code: string | null;
severity_display: string | null;
}
interface CarePlan {
id: string;
title: string;
status: FhirCarePlanStatus;
version: number | null;
goals: SmartGoal[] | null;
activities: Intervention[] | null;
behandelstructuur: Behandelstructuur | null;
sessie_planning: unknown[] | null;
evaluatiemomenten: Evaluatiemoment[] | null;
veiligheidsplan: Veiligheidsplan | null;
created_at: string | null;
published_at: string | null;
period_start: string | null;
}
interface BehandelplanFlatProps {
patientId: string;
carePlan: CarePlan | null;
condition: Condition | null;
hulpvraag: string | null;
lifeDomainScores: LifeDomainScore[] | null;
// Callbacks
onGenerate?: () => Promise<void>;
onCreateManual?: () => Promise<void>;
onStatusChange?: (status: FhirCarePlanStatus) => Promise<void>;
onSaveBehandeldoel?: (doel: Behandeldoel) => Promise<void>;
onDeleteBehandeldoel?: (doelId: string) => Promise<void>;
className?: string;
}
/**
* BehandelplanFlat - Hoofdcomponent voor plat behandelplan
*
* 3 blokken:
* 1. Context Header (read-only): Diagnose, hulpvraag, leefgebieden
* 2. Behandeldoelen (editable): Cards met inline interventies
* 3. Planning & Evaluatie (collapsed): Evaluaties, sessies, veiligheidsplan
*/
export function BehandelplanFlat({
patientId,
carePlan,
condition,
hulpvraag,
lifeDomainScores,
onGenerate,
onCreateManual,
onStatusChange,
onSaveBehandeldoel,
onDeleteBehandeldoel,
className,
}: BehandelplanFlatProps) {
const [editingDoelId, setEditingDoelId] = useState<string | null>(null);
const [isGenerating, setIsGenerating] = useState(false);
const [isCreating, setIsCreating] = useState(false);
// Transform old structure to flat
const behandeldoelen: Behandeldoel[] = carePlan?.goals && carePlan?.activities
? transformToFlat(carePlan.goals, carePlan.activities)
: [];
const totalProgress = calculateBehandeldoelenProgress(behandeldoelen);
const statusInfo = carePlan?.status ? FHIR_STATUS_LABELS[carePlan.status] : null;
// Handlers
const handleGenerate = async () => {
if (!onGenerate) return;
setIsGenerating(true);
try {
await onGenerate();
} finally {
setIsGenerating(false);
}
};
const handleCreateManual = async () => {
if (!onCreateManual) return;
setIsCreating(true);
try {
await onCreateManual();
} finally {
setIsCreating(false);
}
};
const handleSaveDoel = async (doel: Behandeldoel) => {
if (!onSaveBehandeldoel) return;
await onSaveBehandeldoel(doel);
setEditingDoelId(null);
};
const handleDeleteDoel = async (doelId: string) => {
if (!onDeleteBehandeldoel) return;
await onDeleteBehandeldoel(doelId);
setEditingDoelId(null);
};
const handleAddDoel = () => {
const newDoel = createEmptyBehandeldoel();
// Start editing immediately
setEditingDoelId(newDoel.id);
// We need to save this empty doel first, then edit
// For now, we'll handle this in the parent component
};
// No plan yet - show creation options
if (!carePlan) {
return (
<div className={cn('space-y-4', className)}>
{/* Context header */}
<ContextHeader
condition={condition}
hulpvraag={hulpvraag}
lifeDomainScores={lifeDomainScores}
/>
{/* Creation options */}
<Card className="border-dashed border-2 border-slate-300">
<CardContent className="p-6 text-center space-y-4">
<div className="mx-auto w-12 h-12 rounded-full bg-indigo-100 flex items-center justify-center">
<FileText className="h-6 w-6 text-indigo-600" />
</div>
<div>
<h3 className="font-medium text-slate-900">
Nog geen behandelplan
</h3>
<p className="text-sm text-slate-500 mt-1">
Maak een nieuw behandelplan aan
</p>
</div>
<div className="flex items-center justify-center gap-3">
<Button
onClick={handleGenerate}
disabled={isGenerating}
className="bg-indigo-600 hover:bg-indigo-700"
>
<Sparkles className="h-4 w-4 mr-2" />
{isGenerating ? 'Genereren...' : 'Genereer met AI'}
</Button>
<Button
variant="outline"
onClick={handleCreateManual}
disabled={isCreating}
>
<Plus className="h-4 w-4 mr-2" />
{isCreating ? 'Aanmaken...' : 'Handmatig aanmaken'}
</Button>
</div>
</CardContent>
</Card>
</div>
);
}
return (
<div className={cn('space-y-4', className)}>
{/* Plan header with status */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<h2 className="text-lg font-semibold text-slate-900">
{carePlan.title || 'Behandelplan'}
</h2>
{statusInfo && (
<Badge
variant="outline"
style={{ borderColor: statusInfo.color, color: statusInfo.color }}
>
{statusInfo.label}
</Badge>
)}
{carePlan.version && (
<span className="text-sm text-slate-500">v{carePlan.version}</span>
)}
</div>
<div className="flex items-center gap-3">
{/* Overall progress */}
<div className="flex items-center gap-2 text-sm text-slate-600">
<span>Voortgang:</span>
<div className="w-24">
<Progress value={totalProgress} className="h-2" />
</div>
<span className="font-medium">{totalProgress}%</span>
</div>
{/* Status actions */}
{carePlan.status === 'draft' && onStatusChange && (
<Button
size="sm"
onClick={() => onStatusChange('active')}
className="bg-green-600 hover:bg-green-700"
>
<CheckCircle2 className="h-4 w-4 mr-1" />
Activeren
</Button>
)}
</div>
</div>
{/* Block 1: Context Header */}
<ContextHeader
condition={condition}
hulpvraag={hulpvraag}
lifeDomainScores={lifeDomainScores}
/>
{/* Block 2: Behandeldoelen */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<h3 className="text-sm font-medium text-slate-700 uppercase tracking-wide">
Behandeldoelen ({behandeldoelen.length})
</h3>
<Button
variant="ghost"
size="sm"
onClick={handleAddDoel}
className="text-indigo-600 hover:text-indigo-700"
>
<Plus className="h-4 w-4 mr-1" />
Nieuw doel
</Button>
</div>
{behandeldoelen.length === 0 ? (
<Card className="border-dashed">
<CardContent className="p-6 text-center">
<p className="text-sm text-slate-500">
Nog geen behandeldoelen. Klik op &quot;Nieuw doel&quot; om te beginnen.
</p>
</CardContent>
</Card>
) : (
<div className="space-y-3">
{behandeldoelen.map((doel) => (
<BehandeldoelCard
key={doel.id}
doel={doel}
isEditing={editingDoelId === doel.id}
onEdit={() => setEditingDoelId(doel.id)}
onSave={handleSaveDoel}
onCancel={() => setEditingDoelId(null)}
onDelete={() => handleDeleteDoel(doel.id)}
/>
))}
</div>
)}
</div>
{/* Block 3: Planning & Evaluatie */}
<PlanningSection
behandelstructuur={carePlan.behandelstructuur}
evaluatiemomenten={carePlan.evaluatiemomenten}
veiligheidsplan={carePlan.veiligheidsplan}
/>
</div>
);
}