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>
This commit is contained in:
290
components/behandelplan/flat/behandelplan-flat.tsx
Normal file
290
components/behandelplan/flat/behandelplan-flat.tsx
Normal file
@@ -0,0 +1,290 @@
|
||||
'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 "Nieuw doel" 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user