diff --git a/components/behandelplan/index.ts b/components/behandelplan/index.ts
new file mode 100644
index 0000000..7b15f0c
--- /dev/null
+++ b/components/behandelplan/index.ts
@@ -0,0 +1,14 @@
+/**
+ * Behandelplan Components
+ *
+ * Export all behandelplan-related components
+ */
+
+// Leefgebieden (Life Domains)
+export { LeefgebiedenBadge, LeefgebiedenBadgeGroup } from './leefgebieden-badge';
+export {
+ LeefgebiedenScores,
+ LeefgebiedenScoresCard,
+ LeefgebiedenScoreBar,
+} from './leefgebieden-scores';
+export { LeefgebiedenForm, LeefgebiedenQuickForm } from './leefgebieden-form';
diff --git a/components/behandelplan/leefgebieden-badge.tsx b/components/behandelplan/leefgebieden-badge.tsx
new file mode 100644
index 0000000..f74dbeb
--- /dev/null
+++ b/components/behandelplan/leefgebieden-badge.tsx
@@ -0,0 +1,77 @@
+'use client';
+
+import { Badge } from '@/components/ui/badge';
+import { cn } from '@/lib/utils';
+import { type LifeDomain, LIFE_DOMAIN_META } from '@/lib/types/leefgebieden';
+
+interface LeefgebiedenBadgeProps {
+ domain: LifeDomain;
+ showEmoji?: boolean;
+ size?: 'sm' | 'md' | 'lg';
+ className?: string;
+}
+
+/**
+ * Colored badge for a life domain
+ * Uses the domain's specific color from the meta definition
+ */
+export function LeefgebiedenBadge({
+ domain,
+ showEmoji = true,
+ size = 'md',
+ className,
+}: LeefgebiedenBadgeProps) {
+ const meta = LIFE_DOMAIN_META[domain];
+
+ const sizeClasses = {
+ sm: 'text-xs px-1.5 py-0.5',
+ md: 'text-sm px-2 py-0.5',
+ lg: 'text-base px-3 py-1',
+ };
+
+ return (
+
+ {showEmoji && {meta.emoji}}
+ {meta.shortLabel}
+
+ );
+}
+
+interface LeefgebiedenBadgeGroupProps {
+ domains: LifeDomain[];
+ showEmoji?: boolean;
+ size?: 'sm' | 'md' | 'lg';
+ className?: string;
+}
+
+/**
+ * Group of life domain badges
+ */
+export function LeefgebiedenBadgeGroup({
+ domains,
+ showEmoji = true,
+ size = 'sm',
+ className,
+}: LeefgebiedenBadgeGroupProps) {
+ if (domains.length === 0) return null;
+
+ return (
+
+ {domains.map((domain) => (
+
+ ))}
+
+ );
+}
diff --git a/components/behandelplan/leefgebieden-form.tsx b/components/behandelplan/leefgebieden-form.tsx
new file mode 100644
index 0000000..446d5cb
--- /dev/null
+++ b/components/behandelplan/leefgebieden-form.tsx
@@ -0,0 +1,368 @@
+'use client';
+
+import { useState, useCallback } from 'react';
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
+import { Label } from '@/components/ui/label';
+import { Slider } from '@/components/ui/slider';
+import { Textarea } from '@/components/ui/textarea';
+import { Button } from '@/components/ui/button';
+import { cn } from '@/lib/utils';
+import {
+ type LifeDomainScore,
+ type LifeDomain,
+ type Priority,
+ LIFE_DOMAIN_META,
+ LIFE_DOMAINS,
+ createDefaultLifeDomainScores,
+ getScoreColor,
+} from '@/lib/types/leefgebieden';
+
+interface DomainFormRowProps {
+ score: LifeDomainScore;
+ onChange: (score: LifeDomainScore) => void;
+ expanded?: boolean;
+ onToggleExpand?: () => void;
+}
+
+const SCORE_LABELS: Record = {
+ 1: 'Zeer laag',
+ 2: 'Laag',
+ 3: 'Gemiddeld',
+ 4: 'Goed',
+ 5: 'Uitstekend',
+};
+
+const PRIORITY_OPTIONS: { value: Priority; label: string; color: string }[] = [
+ { value: 'laag', label: 'Laag', color: 'bg-gray-200 text-gray-700' },
+ { value: 'middel', label: 'Middel', color: 'bg-blue-100 text-blue-700' },
+ { value: 'hoog', label: 'Hoog', color: 'bg-orange-100 text-orange-700' },
+];
+
+/**
+ * Single domain row in the form
+ */
+function DomainFormRow({
+ score,
+ onChange,
+ expanded = false,
+ onToggleExpand,
+}: DomainFormRowProps) {
+ const meta = LIFE_DOMAIN_META[score.domain];
+
+ const handleBaselineChange = (values: number[]) => {
+ onChange({ ...score, baseline: values[0], current: values[0] });
+ };
+
+ const handleTargetChange = (values: number[]) => {
+ onChange({ ...score, target: values[0] });
+ };
+
+ const handlePriorityChange = (priority: Priority) => {
+ onChange({ ...score, priority });
+ };
+
+ const handleNotesChange = (notes: string) => {
+ onChange({ ...score, notes });
+ };
+
+ return (
+
+ {/* Header Row */}
+
+
+
+ {meta.emoji}
+
+
+
{meta.label}
+
{meta.description}
+
+
+
+
+
+
+ {score.baseline}
+
+ β
+
+ {score.target}
+
+
+
+ {SCORE_LABELS[score.baseline]}
+
+
+
+
+
+
+ {/* Expanded Content */}
+ {expanded && (
+
+ {/* Baseline Score Slider */}
+
+
+
+ {score.baseline}
+
+
+
+ Zeer laag
+ Uitstekend
+
+
+
+ {/* Target Score Slider */}
+
+
+
+ {score.target}
+
+
+
+ Zeer laag
+ Uitstekend
+
+
+
+ {/* Priority Selection */}
+
+
+
+ {PRIORITY_OPTIONS.map((option) => (
+
+ ))}
+
+
+
+ {/* Notes */}
+
+
+
+
+ )}
+
+ );
+}
+
+interface LeefgebiedenFormProps {
+ initialScores?: LifeDomainScore[];
+ onSave: (scores: LifeDomainScore[]) => void;
+ onCancel?: () => void;
+ isSaving?: boolean;
+}
+
+/**
+ * Complete form for entering life domain scores during intake
+ */
+export function LeefgebiedenForm({
+ initialScores,
+ onSave,
+ onCancel,
+ isSaving = false,
+}: LeefgebiedenFormProps) {
+ const [scores, setScores] = useState(
+ initialScores || createDefaultLifeDomainScores()
+ );
+ const [expandedDomain, setExpandedDomain] = useState(null);
+
+ const handleScoreChange = useCallback((updatedScore: LifeDomainScore) => {
+ setScores((prev) =>
+ prev.map((s) => (s.domain === updatedScore.domain ? updatedScore : s))
+ );
+ }, []);
+
+ const handleToggleExpand = useCallback((domain: LifeDomain) => {
+ setExpandedDomain((prev) => (prev === domain ? null : domain));
+ }, []);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ onSave(scores);
+ };
+
+ const highPriorityCount = scores.filter((s) => s.priority === 'hoog').length;
+
+ return (
+
+ );
+}
+
+interface LeefgebiedenQuickFormProps {
+ initialScores?: LifeDomainScore[];
+ onChange: (scores: LifeDomainScore[]) => void;
+}
+
+/**
+ * Compact version of the form for inline editing
+ */
+export function LeefgebiedenQuickForm({
+ initialScores,
+ onChange,
+}: LeefgebiedenQuickFormProps) {
+ const [scores, setScores] = useState(
+ initialScores || createDefaultLifeDomainScores()
+ );
+
+ const handleScoreChange = useCallback(
+ (domain: LifeDomain, value: number) => {
+ const updated = scores.map((s) =>
+ s.domain === domain ? { ...s, baseline: value, current: value } : s
+ );
+ setScores(updated);
+ onChange(updated);
+ },
+ [scores, onChange]
+ );
+
+ return (
+
+ {LIFE_DOMAINS.map((domain) => {
+ const score = scores.find((s) => s.domain === domain)!;
+ const meta = LIFE_DOMAIN_META[domain];
+ return (
+
+
{meta.emoji}
+
+
+ {meta.shortLabel}
+
+ {score.baseline}
+
+
+
handleScoreChange(domain, v[0])}
+ min={1}
+ max={5}
+ step={1}
+ className="w-full"
+ />
+
+
+ );
+ })}
+
+ );
+}
diff --git a/components/behandelplan/leefgebieden-scores.tsx b/components/behandelplan/leefgebieden-scores.tsx
new file mode 100644
index 0000000..e590445
--- /dev/null
+++ b/components/behandelplan/leefgebieden-scores.tsx
@@ -0,0 +1,186 @@
+'use client';
+
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
+import { cn } from '@/lib/utils';
+import {
+ type LifeDomainScore,
+ type LifeDomain,
+ LIFE_DOMAIN_META,
+ LIFE_DOMAINS,
+ getScoreColor,
+ getAverageScore,
+} from '@/lib/types/leefgebieden';
+import { LeefgebiedenBadge } from './leefgebieden-badge';
+
+interface LeefgebiedenScoreBarProps {
+ score: LifeDomainScore;
+ showTarget?: boolean;
+ compact?: boolean;
+}
+
+/**
+ * Single life domain score as a progress bar
+ */
+export function LeefgebiedenScoreBar({
+ score,
+ showTarget = true,
+ compact = false,
+}: LeefgebiedenScoreBarProps) {
+ const meta = LIFE_DOMAIN_META[score.domain];
+ const percentage = (score.current / 5) * 100;
+ const targetPercentage = (score.target / 5) * 100;
+
+ return (
+
+
+
+ {meta.emoji}
+
+ {meta.shortLabel}
+
+ {score.priority === 'hoog' && (
+
+ Prioriteit
+
+ )}
+
+
+
+ {score.current}
+
+ {showTarget && (
+ <>
+ β
+ {score.target}
+ >
+ )}
+
+
+
+ {/* Custom progress bar with domain-specific color */}
+
+ {showTarget && (
+
+ )}
+
+
+ );
+}
+
+interface LeefgebiedenScoresProps {
+ scores: LifeDomainScore[];
+ showTarget?: boolean;
+ compact?: boolean;
+ showSummary?: boolean;
+ className?: string;
+}
+
+/**
+ * Display all 7 life domain scores
+ */
+export function LeefgebiedenScores({
+ scores,
+ showTarget = true,
+ compact = false,
+ showSummary = true,
+ className,
+}: LeefgebiedenScoresProps) {
+ // Ensure we have all 7 domains in the correct order
+ const orderedScores = LIFE_DOMAINS.map((domain) => {
+ const found = scores.find((s) => s.domain === domain);
+ return (
+ found || {
+ domain,
+ baseline: 3,
+ current: 3,
+ target: 4,
+ notes: '',
+ priority: 'middel' as const,
+ }
+ );
+ });
+
+ const avgCurrent = getAverageScore(orderedScores, 'current');
+ const avgTarget = getAverageScore(orderedScores, 'target');
+ const highPriority = orderedScores.filter((s) => s.priority === 'hoog');
+
+ return (
+
+ {showSummary && (
+
+
+
+ Gemiddelde score:{' '}
+ {avgCurrent}
+ {showTarget && (
+ β {avgTarget}
+ )}
+
+
+ {highPriority.length > 0 && (
+
+ Prioriteiten:
+ {highPriority.map((s) => (
+
+ ))}
+
+ )}
+
+ )}
+
+
+ {orderedScores.map((score) => (
+
+ ))}
+
+
+ );
+}
+
+interface LeefgebiedenScoresCardProps {
+ scores: LifeDomainScore[];
+ title?: string;
+ showTarget?: boolean;
+ className?: string;
+}
+
+/**
+ * Life domain scores in a Card wrapper
+ */
+export function LeefgebiedenScoresCard({
+ scores,
+ title = 'Leefgebieden',
+ showTarget = true,
+ className,
+}: LeefgebiedenScoresCardProps) {
+ return (
+
+
+
+ π
+ {title}
+
+
+
+
+
+
+ );
+}
diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx
new file mode 100644
index 0000000..e87d62b
--- /dev/null
+++ b/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
new file mode 100644
index 0000000..cabfbfc
--- /dev/null
+++ b/components/ui/card.tsx
@@ -0,0 +1,76 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/components/ui/label.tsx b/components/ui/label.tsx
new file mode 100644
index 0000000..5341821
--- /dev/null
+++ b/components/ui/label.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/components/ui/progress.tsx b/components/ui/progress.tsx
new file mode 100644
index 0000000..4fc3b47
--- /dev/null
+++ b/components/ui/progress.tsx
@@ -0,0 +1,28 @@
+"use client"
+
+import * as React from "react"
+import * as ProgressPrimitive from "@radix-ui/react-progress"
+
+import { cn } from "@/lib/utils"
+
+const Progress = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, value, ...props }, ref) => (
+
+
+
+))
+Progress.displayName = ProgressPrimitive.Root.displayName
+
+export { Progress }
diff --git a/components/ui/slider.tsx b/components/ui/slider.tsx
new file mode 100644
index 0000000..ab19d57
--- /dev/null
+++ b/components/ui/slider.tsx
@@ -0,0 +1,28 @@
+"use client"
+
+import * as React from "react"
+import * as SliderPrimitive from "@radix-ui/react-slider"
+
+import { cn } from "@/lib/utils"
+
+const Slider = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+
+))
+Slider.displayName = SliderPrimitive.Root.displayName
+
+export { Slider }
diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx
new file mode 100644
index 0000000..0f4caeb
--- /dev/null
+++ b/components/ui/tabs.tsx
@@ -0,0 +1,55 @@
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx
new file mode 100644
index 0000000..e56b0af
--- /dev/null
+++ b/components/ui/textarea.tsx
@@ -0,0 +1,22 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Textarea = React.forwardRef<
+ HTMLTextAreaElement,
+ React.ComponentProps<"textarea">
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/docs/specs/behandelplan/bouwplan-behandelplan-v1.md b/docs/specs/behandelplan/bouwplan-behandelplan-v1.md
new file mode 100644
index 0000000..2f68cf5
--- /dev/null
+++ b/docs/specs/behandelplan/bouwplan-behandelplan-v1.md
@@ -0,0 +1,438 @@
+# Mission Control β Bouwplan Behandelplan Module
+
+**Projectnaam:** Mini-EPD Prototype - Behandelplan Module
+**Versie:** v1.0
+**Datum:** 03-12-2024
+**Auteur:** Colin Lit
+
+---
+
+## 1. Doel en context
+
+**Doel:**
+Een werkende MVP Behandelplan module bouwen die demonstreert hoe AI:
+- **Tijdsbesparing** realiseert: van 30+ minuten naar 2-5 minuten
+- **Kwaliteitsverbetering** biedt: SMART-doelen, evidence-based interventies
+- **Transparantie** creΓ«ert: cliΓ«nt kan eigen plan begrijpen (B1-taal)
+- **Praktische workflow** ondersteunt: intake β diagnose β behandelplan
+
+**Context:**
+Deze module is onderdeel van de AI Speedrun LinkedIn Serie. Het prototype demonstreert AI-toegevoegde waarde voor zorgprofessionals, product owners en developers.
+
+**Gerelateerde documenten:**
+- [PRD Behandelplan v2.0](./prd-behandelplan-v2-final.md)
+- [FO Behandelplan v1.0](./fo-behandelplan-v1.md)
+- [TO Behandelplan v1.0](./to-behandelplan-v1.md)
+
+---
+
+## 2. Uitgangspunten
+
+### 2.1 Technische Stack
+
+| Component | Technologie | Status |
+|-----------|-------------|--------|
+| **Frontend** | Next.js 14.2 + TailwindCSS + shadcn/ui | β
Bestaand |
+| **Backend** | Next.js API Routes + Server Actions | β
Bestaand |
+| **Database** | Supabase PostgreSQL + RLS | β
Bestaand |
+| **AI/ML** | Claude 3.5 Sonnet (Anthropic) | β
Bestaand |
+| **Hosting** | Vercel | β
Bestaand |
+| **Auth** | Supabase Auth | β
Bestaand |
+| **Icons** | Lucide React | β
Bestaand |
+| **Editor** | TipTap | β
Bestaand |
+| **Validation** | Zod | β
Bestaand |
+| **Charts** | Recharts | β³ Stretch goal |
+
+### 2.2 Projectkaders
+
+| Kader | Waarde |
+|-------|--------|
+| **Tijd** | 14-19 uur bouwtijd voor MVP |
+| **Budget** | β¬0 extra (bestaande API keys) |
+| **Team** | 1 developer + AI assistentie |
+| **Data** | Fictieve demo-data (geen productiegegevens) |
+| **Doel** | Werkende demo voor LinkedIn serie |
+
+### 2.3 Gekozen Aanpak
+
+- **Foundation first**: Types en database migraties eerst, dan UI
+- **Simpele visualisatie**: Progress bars i.p.v. radar chart (stretch)
+- **Simple JSON**: Geen streaming, enkele API call met loading state
+
+### 2.4 Programmeer Uitgangspunten
+
+**Code Quality Principles:**
+
+- **DRY (Don't Repeat Yourself)**
+ - Herbruikbare leefgebieden componenten
+ - Centrale types voor behandelplan structuur
+ - Utility functions voor score berekeningen
+
+- **KISS (Keep It Simple, Stupid)**
+ - Simple JSON API (geen streaming complexity)
+ - Progress bars i.p.v. radar chart
+ - Bestaande UI patterns hergebruiken
+
+- **SOC (Separation of Concerns)**
+ - Types in `/lib/types/behandelplan.ts`
+ - AI prompts in `/lib/ai/behandelplan-prompt.ts`
+ - Server actions in `/app/epd/patients/[id]/behandelplan/actions.ts`
+ - UI components in `/components/behandelplan/`
+
+- **YAGNI (You Aren't Gonna Need It)**
+ - Geen versie-diff view (stretch)
+ - Geen real-time collaboration
+ - Geen notificaties/reminders
+
+**Development Practices:**
+
+- **Error Handling**
+ - Try-catch op alle AI calls
+ - Fallback naar manual mode bij AI failure
+ - User-friendly foutmeldingen
+
+- **Security**
+ - ANTHROPIC_API_KEY in environment
+ - RLS policies op care_plans
+ - Zod validation op alle inputs
+
+- **Performance**
+ - AI response < 8 seconden
+ - Skeleton loaders tijdens generatie
+ - Optimistic updates voor status
+
+---
+
+## 3. Epics & Stories Overzicht
+
+| Epic ID | Titel | Doel | Status | Stories | Geschat |
+|---------|-------|------|--------|---------|---------|
+| E0 | Foundation | Types, database schema | β³ To Do | 3 | 2-3 uur |
+| E1 | Leefgebieden | Intake formulier + score weergave | β³ To Do | 3 | 3-4 uur |
+| E2 | AI Generatie | Claude API endpoint + prompts | β³ To Do | 3 | 3-4 uur |
+| E3 | Behandelplan UI | Pagina + componenten | β³ To Do | 5 | 6-8 uur |
+| E4 | Stretch | Micro-regeneratie, radar chart | β³ Optioneel | 3 | 3-5 uur |
+
+**Totaal MVP (E0-E3):** 14-19 uur
+**Totaal met Stretch:** 17-24 uur
+
+---
+
+## 4. Epics & Stories (Uitwerking)
+
+### Epic 0 β Foundation (Types & Database)
+
+**Epic Doel:** Solide basis met TypeScript types en database schema uitbreiding.
+
+| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
+|----------|--------------|---------------------|--------|------------------|----|
+| E0.S1 | Leefgebieden types maken | `lib/types/leefgebieden.ts` met LifeDomain, LifeDomainScore types | β³ | β | 1 |
+| E0.S2 | Behandelplan types maken | `lib/types/behandelplan.ts` met SmartGoal, Intervention, GeneratedPlan types + Zod schemas | β³ | E0.S1 | 2 |
+| E0.S3 | Database migratie | `care_plans` uitgebreid met version, behandelstructuur, evaluatiemomenten; `intakes` met life_domains | β³ | E0.S2 | 2 |
+
+**Technical Notes:**
+```typescript
+// lib/types/leefgebieden.ts
+export type LifeDomain = 'dlv' | 'wonen' | 'werk' | 'sociaal' | 'vrijetijd' | 'financien' | 'gezondheid'
+
+export interface LifeDomainScore {
+ domain: LifeDomain
+ baseline: number // 1-5
+ current: number // 1-5
+ target: number // 1-5
+ notes: string
+ priority: 'laag' | 'middel' | 'hoog'
+}
+```
+
+**Deliverables:**
+- [ ] `lib/types/leefgebieden.ts`
+- [ ] `lib/types/behandelplan.ts`
+- [ ] Database migratie via Supabase MCP (cloud-only, geen lokale instantie)
+- [ ] Gegenereerde database types via `mcp__supabase__generate_typescript_types`
+
+---
+
+### Epic 1 β Leefgebieden Componenten
+
+**Epic Doel:** Formulier voor intake + visuele weergave van scores.
+
+| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
+|----------|--------------|---------------------|--------|------------------|----|
+| E1.S1 | Leefgebieden formulier | 7 sliders (1-5), toelichting velden, prioriteit dropdowns | β³ | E0.S3 | 3 |
+| E1.S2 | Leefgebieden scores weergave | 7 progress bars met kleuren, baseline vs current indicator | β³ | E0.S1 | 2 |
+| E1.S3 | Leefgebieden badge component | Gekleurde tag per domein met emoji | β³ | E0.S1 | 1 |
+
+**Technical Notes:**
+```typescript
+// Kleuren per leefgebied
+const DOMAIN_COLORS = {
+ dlv: '#8b5cf6', // paars
+ wonen: '#ec4899', // roze
+ werk: '#f59e0b', // oranje
+ sociaal: '#3b82f6', // blauw
+ vrijetijd: '#10b981', // groen
+ financien: '#eab308', // geel
+ gezondheid: '#ef4444', // rood
+}
+```
+
+**Deliverables:**
+- [ ] `components/behandelplan/leefgebieden-form.tsx`
+- [ ] `components/behandelplan/leefgebieden-scores.tsx`
+- [ ] `components/behandelplan/leefgebieden-badge.tsx`
+
+---
+
+### Epic 2 β AI Generatie
+
+**Epic Doel:** Claude API endpoint voor behandelplan generatie.
+
+| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
+|----------|--------------|---------------------|--------|------------------|----|
+| E2.S1 | Prompt engineering | System prompt + user prompt templates, evidence-based mapping | β³ | E0.S2 | 2 |
+| E2.S2 | Generate endpoint | `POST /api/behandelplan/generate` retourneert JSON, < 8 sec response | β³ | E2.S1 | 3 |
+| E2.S3 | AI event logging | Calls loggen naar `ai_events` tabel | β³ | E2.S2 | 1 |
+
+**Technical Notes:**
+```typescript
+// Prompt structuur
+const messages = [
+ { role: 'system', content: BEHANDELPLAN_SYSTEM_PROMPT },
+ { role: 'user', content: buildUserPrompt(context) },
+];
+
+// AI settings
+const settings = {
+ model: 'claude-3-5-sonnet-20240620',
+ max_tokens: 4096,
+ temperature: 0.3, // Consistent maar niet robotisch
+};
+```
+
+**Deliverables:**
+- [ ] `lib/ai/behandelplan-prompt.ts`
+- [ ] `lib/ai/intervention-mapping.ts`
+- [ ] `app/api/behandelplan/generate/route.ts`
+
+---
+
+### Epic 3 β Behandelplan UI
+
+**Epic Doel:** Werkende behandelplan pagina met alle componenten.
+
+| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
+|----------|--------------|---------------------|--------|------------------|----|
+| E3.S1 | Behandelplan pagina | Placeholder vervangen, data loading, status weergave | β³ | E2.S2 | 2 |
+| E3.S2 | Generate button + flow | Button triggert AI, loading state, resultaat weergave | β³ | E3.S1 | 2 |
+| E3.S3 | SMART doelen sectie | Goal cards met progress, status, leefgebied badge | β³ | E3.S1, E1.S3 | 3 |
+| E3.S4 | Interventies sectie | Intervention cards met gekoppelde doelen | β³ | E3.S3 | 2 |
+| E3.S5 | Server actions | Create, update, delete, publish behandelplan | β³ | E3.S1 | 2 |
+
+**Technical Notes:**
+```
+/app/epd/patients/[id]/behandelplan/
+βββ page.tsx # Server component
+βββ actions.ts # Server actions
+βββ components/
+ βββ behandelplan-view.tsx
+ βββ generate-button.tsx
+ βββ goals-section.tsx
+ βββ goal-card.tsx
+ βββ interventions-section.tsx
+```
+
+**Deliverables:**
+- [ ] `app/epd/patients/[id]/behandelplan/page.tsx` (vervangen)
+- [ ] `app/epd/patients/[id]/behandelplan/actions.ts`
+- [ ] `components/behandelplan/behandelplan-view.tsx`
+- [ ] `components/behandelplan/generate-button.tsx`
+- [ ] `components/behandelplan/goals-section.tsx`
+- [ ] `components/behandelplan/goal-card.tsx`
+- [ ] `components/behandelplan/interventions-section.tsx`
+
+---
+
+### Epic 4 β Stretch Goals (Optioneel)
+
+**Epic Doel:** Extra features indien tijd beschikbaar.
+
+| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
+|----------|--------------|---------------------|--------|------------------|----|
+| E4.S1 | Micro-regeneratie | Per doel [β»] knop, modal met instructie, nieuw voorstel | β³ | E3.S3 | 3 |
+| E4.S2 | Radar chart | Recharts installeren, 3-lijn spindiagram | β³ | E1.S2 | 3 |
+| E4.S3 | Sessie-planning | Tabel met 8-12 sessies, status per sessie | β³ | E3.S1 | 2 |
+
+**Technical Notes:**
+- Recharts: `pnpm add recharts`
+- Regenerate endpoint: `POST /api/behandelplan/regenerate-section`
+
+---
+
+## 5. Kwaliteit & Testplan
+
+### Test Types
+
+| Test Type | Scope | Tools | Status |
+|-----------|-------|-------|--------|
+| Unit Tests | Types, utilities | Vitest | β³ Nice to have |
+| Integration | API endpoints | Manual + curl | β
Required |
+| Smoke Tests | Happy flow | Manual checklist | β
Required |
+| Performance | AI response time | Network tab | β
Required |
+
+### Manual Test Checklist (Demo)
+
+**Pre-conditions:**
+- [ ] Patient bestaat met intake data
+- [ ] Diagnose/probleemprofiel is ingevuld
+- [ ] Leefgebieden scores zijn ingevuld
+
+**Happy Flow:**
+- [ ] Behandelplan pagina laadt zonder errors
+- [ ] "Genereer Behandelplan" knop is zichtbaar
+- [ ] AI genereert plan binnen 8 seconden
+- [ ] 2-4 SMART doelen worden getoond
+- [ ] Doelen hebben leefgebied badges
+- [ ] Interventies zijn gekoppeld aan doelen
+- [ ] B1-taal versie is beschikbaar per doel
+- [ ] Plan kan worden opgeslagen
+- [ ] Status kan worden gewijzigd (concept β actief)
+
+**Error Scenarios:**
+- [ ] Geen intake data β "Vul eerst intake in" melding
+- [ ] AI API error β "AI niet beschikbaar" melding + retry optie
+- [ ] Validatie error β Inline error messages
+
+---
+
+## 6. Demo & Presentatieplan
+
+### Demo Scenario
+
+**Duur:** 5-7 minuten
+**Doelgroep:** LinkedIn audience (product owners, developers, zorgprofessionals)
+
+**Flow:**
+
+1. **Context** (30 sec)
+ - "Behandelplan schrijven kost 30+ minuten"
+ - "AI kan dit reduceren naar 2-5 minuten"
+
+2. **Leefgebieden invullen** (1 min)
+ - Toon 7 domeinen met sliders
+ - Prioriteiten instellen
+ - Opslaan
+
+3. **AI Generatie** (2 min)
+ - Klik op "Genereer Behandelplan"
+ - Toon loading state met timer
+ - Resultaat verschijnt (< 5 sec)
+
+4. **Resultaat bekijken** (2 min)
+ - SMART doelen met leefgebied tags
+ - B1-taal versie voor cliΓ«nt
+ - Evidence-based interventies
+ - Behandelstructuur
+
+5. **Aanpassen** (1 min)
+ - Doel bewerken
+ - Status wijzigen
+ - Publiceren
+
+**Key Highlights:**
+- Tijdsbesparing: 30 min β 5 min
+- Kwaliteit: SMART-criteria automatisch
+- Transparantie: B1-taal voor cliΓ«nt
+
+---
+
+## 7. Risico's & Mitigatie
+
+| Risico | Kans | Impact | Mitigatie | Owner |
+|--------|------|--------|-----------|-------|
+| AI genereert invalide JSON | Middel | Hoog | Zod validation, retry (2x), fallback message | Dev |
+| AI response > 8 seconden | Laag | Middel | Timeout handling, loading feedback | Dev |
+| Leefgebieden UI te complex | Middel | Middel | Simpele sliders, geen radar chart (MVP) | Dev |
+| Database migratie faalt | Laag | Hoog | Test lokaal eerst, rollback script | Dev |
+| Prompt geeft slechte output | Middel | Hoog | Itereren op prompt, few-shot examples | Dev |
+| Scope creep | Hoog | Middel | Strikte MVP scope, stretch als optioneel | Dev |
+
+---
+
+## 8. Implementatie Volgorde
+
+```
+Week 1 (14-19 uur totaal)
+βββ Dag 1: E0 - Foundation (2-3 uur)
+β βββ E0.S1: Leefgebieden types
+β βββ E0.S2: Behandelplan types
+β βββ E0.S3: Database migratie
+β
+βββ Dag 2: E1 - Leefgebieden (3-4 uur)
+β βββ E1.S1: Formulier component
+β βββ E1.S2: Scores weergave
+β βββ E1.S3: Badge component
+β
+βββ Dag 3: E2 - AI Generatie (3-4 uur)
+β βββ E2.S1: Prompt engineering
+β βββ E2.S2: Generate endpoint
+β βββ E2.S3: Event logging
+β
+βββ Dag 4-5: E3 - UI (6-8 uur)
+ βββ E3.S1: Behandelplan pagina
+ βββ E3.S2: Generate button
+ βββ E3.S3: Doelen sectie
+ βββ E3.S4: Interventies sectie
+ βββ E3.S5: Server actions
+
+Optioneel (indien tijd):
+βββ E4 - Stretch Goals
+ βββ E4.S1: Micro-regeneratie
+ βββ E4.S2: Radar chart
+ βββ E4.S3: Sessie-planning
+```
+
+---
+
+## 9. Referenties
+
+### Mission Control Documents
+- **PRD** β [prd-behandelplan-v2-final.md](./prd-behandelplan-v2-final.md)
+- **FO** β [fo-behandelplan-v1.md](./fo-behandelplan-v1.md)
+- **TO** β [to-behandelplan-v1.md](./to-behandelplan-v1.md)
+- **UX/UI** β [ux-stylesheet.md](../ux-stylesheet.md)
+
+### Bestaande Code
+- API pattern: `/app/api/reports/classify/route.ts`
+- Server actions: `/app/epd/patients/[id]/intakes/[intakeId]/actions.ts`
+- Types pattern: `/lib/types/report.ts`
+- AI integration: `/app/api/docs/chat/route.ts`
+
+### External Resources
+- Repository: `github.com/[org]/15-mini-epd-prototype`
+- Anthropic Docs: https://docs.anthropic.com/claude/reference
+- Supabase Docs: https://supabase.com/docs
+
+---
+
+## 10. Glossary & Abbreviations
+
+| Term | Betekenis |
+|------|-----------|
+| SMART | Specifiek, Meetbaar, Acceptabel, Realistisch, Tijdgebonden |
+| B1-taal | Taalniveau begrijpelijk voor algemeen publiek |
+| Leefgebieden | 7 levensdomeinen volgens herstelgerichte zorg |
+| DSM | Diagnostic and Statistical Manual (diagnose classificatie) |
+| RLS | Row Level Security (Supabase) |
+| FHIR | Fast Healthcare Interoperability Resources |
+| Care Plan | FHIR resource voor behandelplan |
+| SP | Story Points |
+| MVP | Minimum Viable Product |
+
+---
+
+**Versiehistorie:**
+
+| Versie | Datum | Auteur | Wijziging |
+|--------|-------|--------|-----------|
+| v1.0 | 03-12-2024 | Colin Lit | InitiΓ«le versie |
diff --git a/docs/specs/behandelplan/fo-behandelplan-v1.md b/docs/specs/behandelplan/fo-behandelplan-v1.md
new file mode 100644
index 0000000..e3f683c
--- /dev/null
+++ b/docs/specs/behandelplan/fo-behandelplan-v1.md
@@ -0,0 +1,566 @@
+# Functioneel Ontwerp (FO) β Behandelplan Module
+
+**Projectnaam:** Mini-EPD Prototype - AI Speedrun
+**Versie:** v1.0
+**Datum:** 03-12-2024
+**Auteur:** Colin Lit
+
+---
+
+## 1. Doel en relatie met het PRD
+
+**Doel van dit document:**
+Dit Functioneel Ontwerp beschrijft **hoe** de Behandelplan module uit het PRD functioneel werkt β wat de behandelaar ziet, doet en ervaart bij het genereren en beheren van behandelplannen met AI-ondersteuning.
+
+**Relatie met PRD:**
+- PRD: `prd-behandelplan-v2-final.md` β beschrijft *wat* en *waarom*
+- FO (dit document): beschrijft *hoe* dit in de praktijk werkt
+
+**Scope:**
+- MVP-functionaliteit (Fase 1-4 uit implementatieplan)
+- Foundation first aanpak (types β componenten β AI β UI)
+- Simpele leefgebieden visualisatie (progress bars, geen radar chart)
+- Simple JSON API (geen streaming)
+
+---
+
+## 2. Overzicht van de belangrijkste onderdelen
+
+De Behandelplan module bestaat uit de volgende onderdelen:
+
+| # | Onderdeel | Beschrijving |
+|---|-----------|--------------|
+| 1 | **Leefgebieden Intake** | Formulier voor 7 levensdomeinen met scores en prioriteiten |
+| 2 | **AI Generatie** | Knop om behandelplan te laten genereren op basis van intake + diagnose |
+| 3 | **Behandelplan Overzicht** | Hoofdpagina met structuur, doelen, interventies |
+| 4 | **SMART Doelen** | Lijst van 2-4 behandeldoelen met voortgang |
+| 5 | **Interventies** | Evidence-based interventies gekoppeld aan doelen |
+| 6 | **Sessie-planning** | Tabel met geplande sessies (stretch) |
+| 7 | **Evaluatiemomenten** | Tussentijdse en eindevaluatie (stretch) |
+
+---
+
+## 3. User Stories
+
+### Primaire User Stories (MVP)
+
+| ID | Rol | Doel / Actie | Verwachte waarde | Prioriteit |
+|----|-----|--------------|------------------|------------|
+| US-01 | Behandelaar | Leefgebieden scores invullen bij intake | Gestructureerd beeld van cliΓ«ntsituatie | Hoog |
+| US-02 | Behandelaar | AI behandelplan laten genereren | Van 30 min naar 2-5 min tijdsbesparing | Hoog |
+| US-03 | Behandelaar | SMART doelen bekijken en aanpassen | Kwaliteitsverbetering, passend bij cliΓ«nt | Hoog |
+| US-04 | Behandelaar | Interventies koppelen aan doelen | Evidence-based behandeling | Hoog |
+| US-05 | Behandelaar | Specifiek doel laten regenereren | Fijnafstelling zonder alles opnieuw | Middel |
+| US-06 | Behandelaar | Plan publiceren (concept β actief) | CliΓ«nt kan plan inzien | Middel |
+
+### Secundaire User Stories (Stretch)
+
+| ID | Rol | Doel / Actie | Verwachte waarde | Prioriteit |
+|----|-----|--------------|------------------|------------|
+| US-07 | Behandelaar | Sessie-planning invullen | Overzicht behandeltraject | Laag |
+| US-08 | Behandelaar | Evaluatiemoment vastleggen | Voortgang meten en bijsturen | Laag |
+| US-09 | CliΓ«nt | Eigen behandelplan bekijken (B1-taal) | Transparantie en begrip | Laag |
+
+---
+
+## 4. Functionele werking per onderdeel
+
+### 4.1 Leefgebieden Intake
+
+**Locatie:** Onderdeel van intake-flow of aparte tab binnen cliΓ«ntdossier
+
+**Functionaliteit:**
+- Formulier met 7 levensdomeinen (leefgebieden)
+- Per domein:
+ - **Score slider:** 1-5 (1 = zeer problematisch, 5 = goed)
+ - **Toelichting:** Vrij tekstveld voor context
+ - **Prioriteit:** Dropdown (Laag / Middel / Hoog)
+
+**De 7 Leefgebieden:**
+
+| # | Domein | Emoji | Kleur | Voorbeeldvragen |
+|---|--------|-------|-------|-----------------|
+| 1 | Dagelijkse Levensverrichtingen (DLV) | π | `#8b5cf6` | Zelfzorg, structuur, dagritme |
+| 2 | Wonen | π‘ | `#ec4899` | Woonsituatie, veiligheid thuis |
+| 3 | Werk/Dagbesteding | πΌ | `#f59e0b` | Baan, opleiding, vrijwilligerswerk |
+| 4 | Sociaal netwerk | π₯ | `#3b82f6` | Familie, vrienden, relaties |
+| 5 | Vrijetijd/Zingeving | π― | `#10b981` | Hobby's, levensdoel, spiritualiteit |
+| 6 | FinanciΓ«n | π° | `#eab308` | Schulden, inkomen, budgettering |
+| 7 | Lichamelijke gezondheid | π | `#ef4444` | Slaap, beweging, voeding |
+
+**Gedrag:**
+- Opslaan: Data wordt opgeslagen als JSONB in intake/care_plan record
+- Validatie: Alle 7 domeinen moeten een score hebben
+- Weergave: Na opslaan worden scores getoond als progress bars met kleuren
+
+**States:**
+- **Leeg:** "Vul de leefgebieden in om een compleet beeld te krijgen"
+- **Gedeeltelijk:** Waarschuwing bij minder dan 7 domeinen
+- **Compleet:** Groen vinkje, klaar voor behandelplan generatie
+
+---
+
+### 4.2 AI Behandelplan Generatie
+
+**Locatie:** Behandelplan tab binnen cliΓ«ntdossier
+
+**Trigger:** Knop `[β‘ Genereer Behandelplan]`
+
+**Voorwaarden:**
+- Intake notities aanwezig (uit rich text editor)
+- Diagnose/probleemprofiel ingevuld (DSM-categorie + severity)
+- Leefgebieden scores ingevuld (7 domeinen)
+
+**Input naar AI:**
+```
+- Intake tekst (samenvatting of volledige notities)
+- DSM-categorie (bijv. "Angststoornissen")
+- Severity niveau (Laag / Middel / Hoog)
+- Leefgebieden scores met prioriteiten
+- Optioneel: extra instructies van behandelaar
+```
+
+**AI Processing:**
+- Model: Claude 3.5 Sonnet
+- Response tijd: < 5 seconden
+- Output: Gestructureerde JSON
+
+**Output van AI:**
+1. **Behandelstructuur:** Duur, frequentie, aantal sessies, vorm
+2. **SMART Doelen:** 2-4 doelen verdeeld over leefgebieden
+3. **Interventies:** Evidence-based, gekoppeld aan doelen
+4. **Sessie-planning:** Grove indeling (8-12 sessies)
+5. **Evaluatiemomenten:** Tussentijds + eind
+6. **Veiligheidsplan:** Alleen bij severity "Hoog"
+
+**UI tijdens generatie:**
+```
+βββββββββββββββββββββββββββββββββββββββ
+β β‘ Behandelplan wordt gegenereerd...β
+β β
+β [βββββββββββββββββββ] 75% β
+β β
+β Even geduld, dit duurt ~5 seconden β
+βββββββββββββββββββββββββββββββββββββββ
+```
+
+**Na generatie:**
+- Plan verschijnt in bewerkbare vorm
+- Status: "Concept" (niet gepubliceerd)
+- Behandelaar kan reviewen en aanpassen
+
+---
+
+### 4.3 Behandelplan Overzicht (Hoofdpagina)
+
+**Locatie:** `/epd/patients/[id]/behandelplan`
+
+**Layout:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β HEADER β
+β Behandelplan v1 Status: β Concept β
+β [Bewerken] [Publiceer] [Nieuwe Versie] β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
+β β
+β π BEHANDELSTRUCTUUR β
+β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β Duur: 8 weken | Frequentie: Wekelijks | Sessies: 8 ββ
+β β Vorm: Individueel ββ
+β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β
+β π LEEFGEBIEDEN OVERZICHT β
+β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β DLV ββββββββββ 4/5 Baseline: 3 ββ
+β β Wonen ββββββββββ 4/5 Baseline: 4 ββ
+β β Werk β οΈ ββββββββββ 2/5 Baseline: 2 [Prioriteit] ββ
+β β Sociaal β οΈ ββββββββββ 2/5 Baseline: 2 [Prioriteit] ββ
+β β Vrijetijd ββββββββββ 3/5 Baseline: 3 ββ
+β β FinanciΓ«n ββββββββββ 3/5 Baseline: 3 ββ
+β β Gezondheid ββββββββββ 4/5 Baseline: 4 ββ
+β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β
+β π― SMART DOELEN (3) β
+β [Doel cards - zie 4.4] β
+β β
+β π‘ INTERVENTIES (2) β
+β [Interventie cards - zie 4.5] β
+β β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+**Acties:**
+- `[Bewerken]`: Opent inline editing modus
+- `[Publiceer]`: Wijzigt status naar "Actief", zichtbaar voor cliΓ«nt
+- `[Nieuwe Versie]`: Maakt v2 aan op basis van huidige versie
+
+**Status indicatoren:**
+- π΅ Concept - Bewerkbaar, niet zichtbaar voor cliΓ«nt
+- π’ Actief - Gepubliceerd, zichtbaar voor cliΓ«nt
+- π‘ In evaluatie - Evaluatiemoment gepland
+- β« Afgerond - Behandeling afgerond
+
+---
+
+### 4.4 SMART Doelen
+
+**Weergave per doel:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β πΌ Werk Prioriteitβ
+β [Hoog] β
+β Terugkeer naar 4 werkdagen per week β
+β β
+β "Ik werk weer 4 dagen zonder paniek te krijgen" β
+β (cliΓ«nt-versie) β
+β β
+β Voortgang: ββββββββββ 60% β
+β Status: Bezig | Deadline: 8 weken β
+β β
+β Meetbaarheid: Aantal werkdagen per week bijhouden β
+β β
+β [Bewerk] [β» Regenereer] [Details βΌ] β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+**Velden per doel:**
+| Veld | Type | Beschrijving |
+|------|------|--------------|
+| Titel | Tekst | Korte beschrijving (1 zin) |
+| Beschrijving | Tekst | SMART-uitwerking (2-3 zinnen) |
+| CliΓ«nt-versie | Tekst | B1-taal versie voor cliΓ«nt |
+| Leefgebied | Tag | DLV/Wonen/Werk/Sociaal/etc. |
+| Prioriteit | Dropdown | Hoog/Middel/Laag |
+| Meetbaarheid | Tekst | Hoe meten we vooruitgang? |
+| Tijdslijn | Getal | Binnen X weken |
+| Status | Dropdown | Niet gestart/Bezig/Gehaald/Bijgesteld |
+| Voortgang | Slider | 0-100% |
+
+**Acties:**
+- `[Bewerk]`: Inline editing van alle velden
+- `[β» Regenereer]`: AI genereert alternatief doel (zie 4.6)
+- `[Details βΌ]`: Uitklappen voor SMART-details
+- `[+]`: Handmatig doel toevoegen
+- `[ποΈ]`: Doel verwijderen
+
+**AI-gedrag bij generatie:**
+- Focust op leefgebieden met prioriteit "Hoog"
+- Verdeelt doelen over minimaal 2 verschillende domeinen
+- Maakt concrete, meetbare doelen (geen vage termen)
+- Genereert automatisch B1-taal cliΓ«nt-versie
+
+---
+
+### 4.5 Interventies
+
+**Weergave per interventie:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β π§ Cognitieve Gedragstherapie (CGT) β
+β β
+β Beschrijving: β
+β Identificeren en uitdagen van negatieve gedachtenpatronen β
+β die angst en vermijding in stand houden. β
+β β
+β Rationale: β
+β CGT is de eerste keuze behandeling bij angststoornissen β
+β met sterke evidentie voor effectiviteit. β
+β β
+β Gekoppeld aan: [πΌ Doel 1] [π₯ Doel 2] β
+β β
+β [Bewerk] [Details βΌ] β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+**Velden per interventie:**
+| Veld | Type | Beschrijving |
+|------|------|--------------|
+| Naam | Tekst | CGT, Exposure, EMDR, ACT, etc. |
+| Beschrijving | Tekst | Uitleg van de interventie |
+| Rationale | Tekst | Waarom past dit bij deze cliΓ«nt? |
+| Gekoppelde doelen | Multi-select | Welke doelen worden benaderd? |
+
+**AI-mapping (evidence-based):**
+| DSM-Categorie | Primaire Interventies | Sessies bij Hoog |
+|---------------|----------------------|------------------|
+| Angststoornissen | CGT, Exposure, ACT | 12-16 sessies |
+| Stemmingsklachten | CGT, IPT, Gedragsactivatie | 8-12 sessies |
+| Trauma/PTSS | EMDR, Narratieve therapie | 12+ sessies |
+| Persoonlijkheid | Schematherapie, MBT | 20+ sessies |
+
+---
+
+### 4.6 Micro-regeneratie (Stretch)
+
+**Trigger:** Klik op `[β» Regenereer]` bij specifiek doel of interventie
+
+**Flow:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β» Doel regenereren [X] β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
+β β
+β Huidige doel: β
+β "Terugkeer naar 4 werkdagen per week" β
+β β
+β Extra instructie voor AI (optioneel): β
+β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β Maak meer gefocust op geleidelijke opbouw ββ
+β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β β
+β [Annuleren] [β» Regenereer] β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+**Na regeneratie:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β Nieuw voorstel: β
+β β
+β "Stapsgewijze opbouw naar 4 werkdagen via 2β3β4 schema" β
+β β
+β [Behoud origineel] [β Accepteer nieuw voorstel] β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+**Gedrag:**
+- AI behoudt context van rest van plan
+- Alleen het specifieke onderdeel wordt vervangen
+- Toast notification bij succes: "Doel bijgewerkt"
+
+---
+
+### 4.7 Publicatie Workflow
+
+**Statussen:**
+```
+Concept βββ Actief βββ In evaluatie βββ Afgerond
+ β β
+ ββββ Gearchiveerd ββββββββββββ
+ (bij nieuwe versie)
+```
+
+**Validatie voor publicatie:**
+- β Minimaal 1 doel ingevuld
+- β Minimaal 1 interventie gekoppeld
+- β Behandelstructuur compleet (duur, frequentie)
+- β Evaluatiemomenten gepland (tussentijds + eind)
+
+**Publicatie actie:**
+1. Behandelaar klikt `[Publiceer]`
+2. Systeem valideert compleetheid
+3. Bij succes: status β "Actief", publicatiedatum vastgelegd
+4. Toast: "Behandelplan gepubliceerd"
+5. Plan zichtbaar in cliΓ«ntportaal
+
+**Versie-beheer:**
+- Nummering: v1, v2, v3, etc.
+- Bij "Nieuwe Versie": huidige β "Gearchiveerd", nieuwe kopie β "Concept"
+- Oude versies blijven zichtbaar (read-only)
+
+---
+
+## 5. UI-overzicht (visuele structuur)
+
+### Behandelplan Pagina Layout
+
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β HEADER β
+β Mini-EPD Logo [CliΓ«ntnaam βΌ] [Zoek...] [Profiel] β
+βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββ€
+β SIDEBAR β MAIN CONTENT β
+β β β
+β β CliΓ«nten β βββββββββββββββββββββββββββββββββββββββββββββ β
+β βββββββββ β β Behandelplan v1 Status: Concept β β
+β β‘ Dashboard β β [Bewerken] [Publiceer] [Print] β β
+β β‘ Intake β βββββββββββββββββββββββββββββββββββββββββββββ β
+β β‘ Diagnose β β
+β β Behandelplan β βββ Behandelstructuur βββββββββββββββββββββββ β
+β β‘ Rapportage β β Duur: 8 weken | Freq: Wekelijks | 8 sess β β
+β β‘ Agenda β βββββββββββββββββββββββββββββββββββββββββββββ β
+β β β
+β β βββ Leefgebieden βββββββββββββββββββββββββββββ β
+β β β [Progress bars met scores per domein] β β
+β β βββββββββββββββββββββββββββββββββββββββββββββ β
+β β β
+β β βββ SMART Doelen βββββββββββββββββββββββββββββ β
+β β β [Doel 1 - Werk] β β
+β β β [Doel 2 - Sociaal] β β
+β β β [Doel 3 - DLV] β β
+β β β [+ Doel toevoegen] β β
+β β βββββββββββββββββββββββββββββββββββββββββββββ β
+β β β
+β β βββ Interventies βββββββββββββββββββββββββββββ β
+β β β [CGT] [Exposure] β β
+β β βββββββββββββββββββββββββββββββββββββββββββββ β
+β β β
+βββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββ€
+β FOOTER: Auto-saved 2 sec ago β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+### Responsive (Tablet)
+
+```
+βββββββββββββββββββββββββββββββββββββββββββββββ
+β [β°] Mini-EPD CliΓ«ntnaam [Zoek] β
+βββββββββββββββββββββββββββββββββββββββββββββββ€
+β β
+β Behandelplan v1 β
+β Status: β Concept β
+β [Bewerken] [Publiceer] β
+β β
+β βββ Behandelstructuur ββββββββββββββββββββββ
+β β Duur: 8 weken | Wekelijks | 8 sessies ββ
+β ββββββββββββββββββββββββββββββββββββββββββββ
+β β
+β βββ Leefgebieden βββββββββββββββββββββββββββ
+β β [Compacte progress bars] ββ
+β ββββββββββββββββββββββββββββββββββββββββββββ
+β β
+β βββ Doelen βββββββββββββββββββββββββββββββββ
+β β [Gestapelde doel cards] ββ
+β ββββββββββββββββββββββββββββββββββββββββββββ
+β β
+βββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+---
+
+## 6. Interacties met AI (functionele beschrijving)
+
+| Locatie | AI-actie | Trigger | Input | Output |
+|---------|----------|---------|-------|--------|
+| Behandelplan tab | Genereer plan | Klik `[β‘ Genereer]` | Intake + diagnose + leefgebieden | Compleet behandelplan (JSON) |
+| Doel card | Regenereer doel | Klik `[β» Regenereer]` | Context plan + instructie | Alternatief doel |
+| Interventie card | Regenereer interventie | Klik `[β» Regenereer]` | Context plan + instructie | Alternatieve interventie |
+| Doel card | Genereer cliΓ«nt-versie | Automatisch bij nieuw doel | Behandelaar-tekst | B1-taal versie |
+
+### AI Response Format
+
+```typescript
+interface AIGeneratedPlan {
+ behandelstructuur: {
+ duur: string // "8 weken"
+ frequentie: string // "Wekelijks"
+ aantalSessies: number // 8
+ vorm: string // "Individueel"
+ }
+ doelen: Array<{
+ id: string
+ title: string
+ description: string // SMART uitwerking
+ clientVersion: string // B1-taal
+ lifeDomain: string // "werk" | "sociaal" | etc.
+ priority: string // "hoog" | "middel" | "laag"
+ measurability: string
+ timelineWeeks: number
+ }>
+ interventies: Array<{
+ name: string
+ description: string
+ rationale: string
+ linkedGoalIds: string[]
+ }>
+ evaluatiemomenten: Array<{
+ type: string // "tussentijds" | "eind"
+ weekNumber: number
+ }>
+ veiligheidsplan?: { // Alleen bij severity "Hoog"
+ waarschuwingssignalen: string[]
+ copingStrategieen: string[]
+ contacten: string[]
+ }
+}
+```
+
+---
+
+## 7. Gebruikersrollen en rechten
+
+| Rol | Toegang tot | Acties | Beperkingen |
+|-----|------------|--------|-------------|
+| Behandelaar | Eigen cliΓ«ntdossiers | Volledig CRUD, AI generatie | Alleen eigen cliΓ«nten |
+| Behandelaar (collega) | Gedeelde cliΓ«nten | Lezen, commentaar | Geen bewerken |
+| CliΓ«nt | Eigen behandelplan | Alleen lezen | Ziet B1-versie, geen edit |
+| Demo-user | Alle fictieve data | Lezen + AI testen | Geen opslaan |
+
+---
+
+## 8. States en Foutafhandeling
+
+### Empty States
+
+**Geen behandelplan:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββ
+β π β
+β β
+β Nog geen behandelplan β
+β β
+β Vul eerst de intake en diagnose in, β
+β dan kan AI een behandelplan genereren. β
+β β
+β [Naar Intake] [Naar Diagnose] β
+βββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+**Incomplete voorwaarden:**
+```
+βββββββββββββββββββββββββββββββββββββββββββββ
+β β οΈ Nog niet klaar voor behandelplan β
+β β
+β β‘ Intake notities β β
+β β‘ Diagnose/probleemprofiel β β
+β β‘ Leefgebieden scores β β
+β β
+β Vul de ontbrekende onderdelen in. β
+βββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+### Error States
+
+| Situatie | Bericht | Actie |
+|----------|---------|-------|
+| AI niet beschikbaar | "AI tijdelijk niet beschikbaar" | Retry knop, handmatig alternatief |
+| Validatie fout | Inline error onder veld | Focus op fout veld |
+| Netwerk error | Toast: "Verbinding verloren" | Auto-retry, lokale opslag |
+| Rate limit | "Even wachten..." | Countdown timer |
+
+### Loading States
+
+**AI generatie:**
+```
+β‘ Behandelplan wordt gegenereerd...
+[βββββββββββββββββββ] 75%
+Even geduld, dit duurt ~5 seconden
+```
+
+**Auto-save:**
+- Tijdens typen: "Opslaan..."
+- Na succes: "β Opgeslagen 2 sec geleden"
+
+---
+
+## 9. Bijlagen & Referenties
+
+### Interne Documenten
+- [PRD Behandelplan v2.0](./prd-behandelplan-v2-final.md) β Requirements
+- [Implementatieplan](~/.claude/plans/) β Technische aanpak
+- [UX Stylesheet](../ux-stylesheet.md) β Kleuren, typography
+
+### Technische Specificaties
+- Database: `treatment_plans` tabel met JSONB structuur
+- API: `/api/behandelplan/generate` (POST, JSON response)
+- AI Model: Claude 3.5 Sonnet
+
+### Externe Bronnen
+- [GGZ Richtlijnen](https://www.ggzrichtlijnen.nl/) β Evidence-based interventies
+- [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/) β Accessibility
+
+---
+
+**Document Status:** v1.0 Draft
+**Volgende Review:** Na implementatie Fase 1-2
+**Eigenaar:** Colin Lit
\ No newline at end of file
diff --git a/docs/specs/behandelplan/prd-behandelplan-v2-final.md b/docs/specs/behandelplan/prd-behandelplan-v2-final.md
index be313e0..1b74040 100644
--- a/docs/specs/behandelplan/prd-behandelplan-v2-final.md
+++ b/docs/specs/behandelplan/prd-behandelplan-v2-final.md
@@ -3,7 +3,7 @@
**Projectnaam:** Mini-ECD Prototype - AI Speedrun
**Versie:** v2.0 (volgens template, incl. Leefgebieden)
**Datum:** 2 december 2024
-**Auteur:** Colin van Zeeland
+**Auteur:** Colin Lit
**Changelog:**
- v2.0: Herstructurering volgens PRD template, duidelijke MVP/post-MVP scheiding, expliciete UX sectie
diff --git a/docs/specs/behandelplan/to-behandelplan-v1.md b/docs/specs/behandelplan/to-behandelplan-v1.md
new file mode 100644
index 0000000..da372c8
--- /dev/null
+++ b/docs/specs/behandelplan/to-behandelplan-v1.md
@@ -0,0 +1,719 @@
+# Technisch Ontwerp (TO) β Behandelplan Module
+
+**Projectnaam:** Mini-EPD Prototype - AI Speedrun
+**Versie:** v1.0
+**Datum:** 03-12-2024
+**Auteur:** Colin Lit
+
+---
+
+## 1. Doel en relatie met PRD en FO
+
+**Doel van dit document:**
+Dit Technisch Ontwerp beschrijft **hoe** de Behandelplan module technisch wordt gebouwd. Het PRD beschrijft het *wat*, het FO het *hoe functioneel*, en dit TO de *technische implementatie*.
+
+**Gerelateerde documenten:**
+- PRD: `prd-behandelplan-v2-final.md`
+- FO: `fo-behandelplan-v1.md`
+- Implementatieplan: `~/.claude/plans/effervescent-toasting-beaver.md`
+
+**Scope:**
+- Foundation first: Types β Database β Components β AI β UI
+- Simple JSON API (geen streaming)
+- Simpele leefgebieden visualisatie (progress bars, geen radar chart)
+
+---
+
+## 2. Technische Architectuur Overzicht
+
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β FRONTEND (Next.js 14) β
+β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββββββββββββββ β
+β β Behandelplan β β Leefgebieden β β SMART Doelen β β
+β β Page β β Components β β Components β β
+β β (Server Comp) β β (Client Comp) β β (Client Comp) β β
+β ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ ββββββββββββββββ¬βββββββββββββββ β
+β β β β β
+β ββββββββββββββββββββββ΄βββββββββββββββββββββββββββ β
+β β β
+β ββββββββββββΌβββββββββββ β
+β β Server Actions β β
+β β (behandelplan/ β β
+β β actions.ts) β β
+β ββββββββββββ¬βββββββββββ β
+ββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
+ β
+ββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
+β API ROUTES β
+β βββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ β
+β β /api/behandelplan/generate β β
+β β (POST - AI Generation) β β
+β βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ β
+β β β
+β βββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ β
+β β /api/behandelplan/regenerate-section β β
+β β (POST - Micro-regeneration) β β
+β βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ β
+ββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
+ β
+ββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββ
+β EXTERNAL SERVICES β
+β βββββββββββββββββββ β βββββββββββββββββββββββ β
+β β Supabase ββββββββββββββββ΄βββββββββββββββΊ Claude API β β
+β β (PostgreSQL) β β (Anthropic) β β
+β β - care_plans β β - claude-sonnet β β
+β β - patients β β - JSON response β β
+β β - intakes β β β β
+β βββββββββββββββββββ βββββββββββββββββββββββ β
+ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+```
+
+---
+
+## 3. Techstack Selectie
+
+### Bestaande Stack (hergebruiken)
+
+| Component | Technologie | Status | Argumentatie |
+|-----------|-------------|--------|--------------|
+| Frontend | Next.js 14.2.18 | β
Bestaand | React framework, SSR, App Router |
+| Backend | Next.js API Routes | β
Bestaand | Co-located, TypeScript |
+| Database | Supabase PostgreSQL | β
Bestaand | RLS, FHIR-compliant schema |
+| AI | Claude Sonnet | β
Bestaand | API key geconfigureerd |
+| Styling | TailwindCSS 3.4 | β
Bestaand | Utility-first, shadcn/ui |
+| Icons | Lucide React | β
Bestaand | Consistent icon set |
+| Editor | TipTap | β
Bestaand | Rich text editor |
+| Validation | Zod | β
Bestaand | Schema validation |
+
+### Nieuwe Dependencies
+
+| Component | Technologie | Nodig voor | Alternatief |
+|-----------|-------------|------------|-------------|
+| Charts | Recharts 2.x | Radar chart (stretch) | β Later toevoegen |
+
+**Conclusie:** Geen nieuwe dependencies nodig voor MVP. Recharts alleen bij stretch goal.
+
+---
+
+## 4. Datamodel
+
+### 4.1 Bestaande Tabellen (hergebruiken)
+
+```sql
+-- FHIR CarePlan (hoofdtabel voor behandelplannen)
+care_plans (
+ id UUID PRIMARY KEY,
+ patient_id UUID REFERENCES patients(id),
+ title TEXT,
+ status careplan_status, -- draft | active | completed | revoked
+ intent TEXT,
+ goals JSONB, -- Array van doelen
+ activities JSONB, -- Array van interventies
+ based_on_intake_id UUID,
+ based_on_anamneses UUID[],
+ based_on_examinations UUID[],
+ based_on_risk_assessments UUID[],
+ care_team_ids UUID[],
+ author_id UUID,
+ period_start DATE,
+ period_end DATE,
+ created_at TIMESTAMP,
+ updated_at TIMESTAMP
+)
+
+-- Conditions (diagnoses - input voor AI)
+conditions (
+ id UUID,
+ patient_id UUID,
+ code TEXT, -- DSM-5 code
+ code_system TEXT,
+ display_text TEXT,
+ category TEXT,
+ clinical_status TEXT,
+ severity TEXT, -- laag | middel | hoog
+ encounter_id UUID
+)
+
+-- Intakes (bron voor AI context)
+intakes (
+ id UUID,
+ patient_id UUID,
+ status intake_status,
+ treatment_advice JSONB,
+ kindcheck_data JSONB,
+ notes TEXT
+)
+```
+
+### 4.2 Nieuwe Velden / Migratie
+
+```sql
+-- Migratie: Leefgebieden toevoegen aan intakes
+ALTER TABLE intakes
+ADD COLUMN life_domains JSONB;
+
+-- Migratie: Behandelplan specifieke velden aan care_plans
+ALTER TABLE care_plans
+ADD COLUMN version INTEGER DEFAULT 1,
+ADD COLUMN published_at TIMESTAMP,
+ADD COLUMN behandelstructuur JSONB,
+ADD COLUMN evaluatiemomenten JSONB,
+ADD COLUMN veiligheidsplan JSONB;
+
+-- Constraint voor versie-beheer
+ALTER TABLE care_plans
+ADD CONSTRAINT unique_patient_version UNIQUE (patient_id, version);
+```
+
+### 4.3 JSONB Structuren
+
+**life_domains (in intakes):**
+```typescript
+interface LifeDomainScore {
+ domain: 'dlv' | 'wonen' | 'werk' | 'sociaal' | 'vrijetijd' | 'financien' | 'gezondheid'
+ baseline: number // 1-5
+ current: number // 1-5
+ target: number // 1-5
+ notes: string
+ priority: 'laag' | 'middel' | 'hoog'
+}
+
+// life_domains: LifeDomainScore[]
+```
+
+**goals (in care_plans):**
+```typescript
+interface SmartGoal {
+ id: string
+ title: string
+ description: string
+ clientVersion: string // B1-taal
+ lifeDomain: LifeDomain
+ priority: 'hoog' | 'middel' | 'laag'
+ measurability: string
+ timelineWeeks: number
+ status: 'niet_gestart' | 'bezig' | 'gehaald' | 'bijgesteld'
+ progress: number // 0-100
+}
+```
+
+**activities (in care_plans):**
+```typescript
+interface Intervention {
+ id: string
+ name: string
+ description: string
+ rationale: string
+ linkedGoalIds: string[]
+}
+```
+
+**behandelstructuur:**
+```typescript
+interface Behandelstructuur {
+ duur: string // "8 weken"
+ frequentie: string // "Wekelijks"
+ aantalSessies: number // 8
+ vorm: string // "Individueel"
+}
+```
+
+**evaluatiemomenten:**
+```typescript
+interface Evaluatiemoment {
+ id: string
+ type: 'tussentijds' | 'eind' | 'crisis'
+ weekNumber: number
+ plannedDate: string
+ actualDate?: string
+ status: 'gepland' | 'afgerond' | 'overgeslagen'
+ outcome?: string
+ lifeDomainUpdates?: LifeDomainScore[]
+}
+```
+
+### 4.4 ERD
+
+```
+patients β1:Nβ intakes β1:1β life_domains (JSONB)
+ β β
+ β βββββββββββ anamneses ββββββββββ
+ β βββββββββββ examinations βββββββ€
+ β βββββββββββ risk_assessments βββ€
+ β β
+ ββ1:Nβ care_plans βββββββββββββββββββββββββββββ
+ β (based_on_*)
+ βββ goals (JSONB)
+ βββ activities (JSONB)
+ βββ behandelstructuur (JSONB)
+ βββ evaluatiemomenten (JSONB)
+ βββ veiligheidsplan (JSONB)
+
+ ββ1:Nβ conditions (diagnoses - input voor AI)
+```
+
+---
+
+## 5. API Ontwerp
+
+### 5.1 Endpoints Overzicht
+
+| Endpoint | Method | Input | Output | Auth |
+|----------|--------|-------|--------|------|
+| `/api/behandelplan/generate` | POST | GenerateInput | GeneratedPlan | Required |
+| `/api/behandelplan/regenerate-section` | POST | RegenerateInput | RegeneratedSection | Required |
+
+### 5.2 POST /api/behandelplan/generate
+
+**Request:**
+```typescript
+interface GenerateInput {
+ patientId: string // UUID
+ intakeId: string // UUID
+ conditionId?: string // UUID (optioneel, haalt anders laatste op)
+ extraInstructions?: string // Optionele aanvullende instructies
+}
+```
+
+**Response:**
+```typescript
+interface GeneratedPlan {
+ behandelstructuur: Behandelstructuur
+ doelen: SmartGoal[]
+ interventies: Intervention[]
+ evaluatiemomenten: Evaluatiemoment[]
+ veiligheidsplan?: Veiligheidsplan // Alleen bij severity "Hoog"
+}
+```
+
+**Error Responses:**
+- `400`: Validation error (missing fields, invalid UUIDs)
+- `401`: Unauthorized
+- `404`: Patient/Intake/Condition not found
+- `422`: Insufficient data for generation (no intake notes, no diagnosis)
+- `500`: AI API error
+- `503`: AI service unavailable
+
+### 5.3 POST /api/behandelplan/regenerate-section
+
+**Request:**
+```typescript
+interface RegenerateInput {
+ patientId: string
+ carePlanId: string
+ sectionType: 'goal' | 'intervention'
+ sectionId: string
+ instruction?: string // Extra instructie voor AI
+ currentPlan: GeneratedPlan // Context van huidige plan
+}
+```
+
+**Response:**
+```typescript
+interface RegeneratedSection {
+ type: 'goal' | 'intervention'
+ original: SmartGoal | Intervention
+ regenerated: SmartGoal | Intervention
+}
+```
+
+### 5.4 Zod Validation Schemas
+
+```typescript
+// lib/types/behandelplan.ts
+
+export const GenerateInputSchema = z.object({
+ patientId: z.string().uuid(),
+ intakeId: z.string().uuid(),
+ conditionId: z.string().uuid().optional(),
+ extraInstructions: z.string().max(500).optional(),
+});
+
+export const RegenerateInputSchema = z.object({
+ patientId: z.string().uuid(),
+ carePlanId: z.string().uuid(),
+ sectionType: z.enum(['goal', 'intervention']),
+ sectionId: z.string().uuid(),
+ instruction: z.string().max(200).optional(),
+ currentPlan: GeneratedPlanSchema,
+});
+```
+
+---
+
+## 6. Security & Compliance
+
+### 6.1 Security Checklist
+
+- [x] **Authentication:** Supabase Auth (bestaand)
+- [x] **Authorization:** Row Level Security op care_plans
+- [x] **Data Encryption:** At rest (PostgreSQL), in transit (HTTPS)
+- [x] **Input Validation:** Zod schemas op alle endpoints
+- [ ] **Rate Limiting:** Toe te voegen op AI endpoints (10 req/min)
+- [x] **CORS:** Restrictive origins (bestaand)
+- [x] **Secrets:** Environment variables (ANTHROPIC_API_KEY)
+
+### 6.2 RLS Policies voor care_plans
+
+```sql
+-- Bestaande RLS policy uitbreiden
+ALTER TABLE care_plans ENABLE ROW LEVEL SECURITY;
+
+-- Behandelaars kunnen alleen eigen patiΓ«nten zien
+CREATE POLICY "Users can view care plans for their patients"
+ON care_plans FOR SELECT
+USING (
+ auth.uid() IN (
+ SELECT practitioner_id FROM patient_practitioners
+ WHERE patient_id = care_plans.patient_id
+ )
+);
+
+-- Behandelaars kunnen care plans maken voor eigen patiΓ«nten
+CREATE POLICY "Users can create care plans for their patients"
+ON care_plans FOR INSERT
+WITH CHECK (
+ auth.uid() = author_id
+);
+
+-- Behandelaars kunnen eigen care plans updaten
+CREATE POLICY "Users can update their care plans"
+ON care_plans FOR UPDATE
+USING (auth.uid() = author_id);
+```
+
+### 6.3 AVG/GDPR Overwegingen
+
+- **Data minimalisatie:** Alleen noodzakelijke velden in AI prompt
+- **Geen BSN/identificerende data naar AI:** Alleen intake notities en scores
+- **Audit trail:** Bestaande `ai_events` tabel loggen van AI calls
+- **Consent:** AI-gebruik gedekt onder behandelrelatie
+
+---
+
+## 7. AI/LLM Integratie
+
+### 7.1 AI Stack
+
+| Component | Waarde |
+|-----------|--------|
+| Provider | Anthropic |
+| Model | claude-3-5-sonnet-20240620 (of claude-sonnet-4) |
+| Library | Native fetch (geen SDK nodig) |
+| Caching | Geen (elke generatie is uniek) |
+| Fallback | Error message + manual mode optie |
+
+### 7.2 Prompt Template
+
+```typescript
+// lib/ai/behandelplan-prompt.ts
+
+export const BEHANDELPLAN_SYSTEM_PROMPT = `
+Je bent een ervaren GGZ-behandelaar die behandelplannen opstelt.
+Je maakt SMART doelen die recovery-gericht en evidence-based zijn.
+
+INSTRUCTIES:
+1. Genereer 2-4 SMART doelen gebaseerd op de intake en diagnose
+2. Focus op leefgebieden met prioriteit "Hoog"
+3. Verdeel doelen over minimaal 2 verschillende leefgebieden
+4. Maak concrete, meetbare doelen (geen vage termen)
+5. Genereer voor elk doel een B1-taal versie (cliΓ«nt-vriendelijk)
+6. Kies evidence-based interventies passend bij de DSM-categorie
+7. Plan 8-12 sessies afhankelijk van severity
+8. Voeg veiligheidsplan toe alleen bij severity "Hoog"
+
+OUTPUT FORMAT:
+Retourneer ALLEEN valide JSON volgens het volgende schema:
+{
+ "behandelstructuur": {
+ "duur": "8 weken",
+ "frequentie": "Wekelijks",
+ "aantalSessies": 8,
+ "vorm": "Individueel"
+ },
+ "doelen": [...],
+ "interventies": [...],
+ "evaluatiemomenten": [...],
+ "veiligheidsplan": null | {...}
+}
+`;
+
+export function buildUserPrompt(context: PlanContext): string {
+ return `
+CLIΓNT CONTEXT:
+- Intake notities: ${context.intakeNotes}
+- DSM-categorie: ${context.dsmCategory}
+- Severity: ${context.severity}
+
+LEEFGEBIEDEN SCORES:
+${context.lifeDomains.map(d =>
+ `- ${d.domain}: ${d.baseline}/5 (prioriteit: ${d.priority})`
+).join('\n')}
+
+${context.extraInstructions ? `EXTRA INSTRUCTIES:\n${context.extraInstructions}` : ''}
+
+Genereer nu een behandelplan.
+`;
+}
+```
+
+### 7.3 Evidence-Based Mapping
+
+```typescript
+// lib/ai/intervention-mapping.ts
+
+export const INTERVENTION_MAPPING: Record = {
+ 'angststoornissen': [
+ { name: 'CGT', sessions: { laag: 8, middel: 10, hoog: 14 } },
+ { name: 'Exposure therapie', sessions: { laag: 6, middel: 8, hoog: 12 } },
+ { name: 'ACT', sessions: { laag: 8, middel: 10, hoog: 12 } },
+ ],
+ 'stemmingsklachten': [
+ { name: 'CGT', sessions: { laag: 8, middel: 10, hoog: 14 } },
+ { name: 'IPT', sessions: { laag: 8, middel: 12, hoog: 16 } },
+ { name: 'Gedragsactivatie', sessions: { laag: 6, middel: 8, hoog: 10 } },
+ ],
+ 'trauma_ptss': [
+ { name: 'EMDR', sessions: { laag: 6, middel: 10, hoog: 16 } },
+ { name: 'Narratieve therapie', sessions: { laag: 8, middel: 12, hoog: 16 } },
+ ],
+ 'persoonlijkheid': [
+ { name: 'Schematherapie', sessions: { laag: 16, middel: 24, hoog: 40 } },
+ { name: 'MBT', sessions: { laag: 16, middel: 24, hoog: 40 } },
+ ],
+};
+```
+
+### 7.4 API Call Implementatie
+
+```typescript
+// app/api/behandelplan/generate/route.ts
+
+export async function POST(request: NextRequest) {
+ // 1. Validate input
+ const body = await request.json();
+ const input = GenerateInputSchema.parse(body);
+
+ // 2. Load context from database
+ const context = await loadPlanContext(input);
+
+ // 3. Build prompt
+ const messages = [
+ { role: 'system', content: BEHANDELPLAN_SYSTEM_PROMPT },
+ { role: 'user', content: buildUserPrompt(context) },
+ ];
+
+ // 4. Call Claude API
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-api-key': process.env.ANTHROPIC_API_KEY!,
+ 'anthropic-version': '2023-06-01',
+ },
+ body: JSON.stringify({
+ model: 'claude-3-5-sonnet-20240620',
+ max_tokens: 4096,
+ temperature: 0.3,
+ messages,
+ }),
+ });
+
+ // 5. Parse and validate response
+ const result = await response.json();
+ const plan = GeneratedPlanSchema.parse(
+ JSON.parse(result.content[0].text)
+ );
+
+ // 6. Log to ai_events
+ await logAIEvent('behandelplan_generate', input, plan);
+
+ return NextResponse.json(plan);
+}
+```
+
+---
+
+## 8. Performance & Scalability
+
+### 8.1 Performance Targets
+
+| Metric | Target | Huidige Baseline |
+|--------|--------|------------------|
+| Page load (FCP) | < 1.5s | ~1s (andere pagina's) |
+| API response | < 500ms | ~300ms (intakes) |
+| AI generation | < 8s | N/A (nieuw) |
+| Auto-save | < 500ms | ~300ms (reports) |
+
+### 8.2 Optimalisaties
+
+**Frontend:**
+- Server Components voor initial load (geen client JS voor data)
+- Skeleton loaders tijdens AI generatie
+- Optimistic updates voor status wijzigingen
+
+**Backend:**
+- Parallel database queries voor context loading
+- Geen caching van AI responses (elke generatie uniek)
+- Connection pooling via Supabase (bestaand)
+
+**AI:**
+- Max tokens: 4096 (voldoende voor plan JSON)
+- Temperature: 0.3 (consistent maar niet robotisch)
+- Retry logic: 2x met exponential backoff
+
+---
+
+## 9. Deployment & CI/CD
+
+### 9.1 Omgevingen (bestaand)
+
+| Omgeving | URL | Database |
+|----------|-----|----------|
+| Development | localhost:3000 | Local Supabase |
+| Preview | Vercel preview | Supabase preview branch |
+| Production | [main domain] | Supabase production |
+
+### 9.2 Migratie Workflow (Cloud-only)
+
+```bash
+# Geen lokale Supabase instantie - direct naar cloud
+
+# Optie 1: Via Supabase MCP tool
+mcp__supabase__apply_migration(name, query)
+
+# Optie 2: Via Supabase CLI
+npx supabase db push --linked
+
+# Types genereren na migratie
+mcp__supabase__generate_typescript_types
+```
+
+**Let op:** Geen `supabase db reset` mogelijk - migraties zijn direct productie.
+
+### 9.3 Deployment Checklist
+
+- [ ] Environment variables in Vercel dashboard
+- [ ] Database migraties toegepast
+- [ ] TypeScript types gegenereerd (`supabase gen types typescript`)
+- [ ] Build succesvol (`pnpm build`)
+- [ ] Smoke test op preview environment
+
+---
+
+## 10. Monitoring & Logging
+
+### 10.1 AI Event Logging (bestaand)
+
+```sql
+-- Bestaande ai_events tabel
+ai_events (
+ id UUID,
+ kind TEXT, -- 'behandelplan_generate' | 'behandelplan_regenerate'
+ request JSONB, -- Input parameters
+ response JSONB, -- Generated plan
+ duration_ms INTEGER,
+ created_at TIMESTAMP
+)
+```
+
+### 10.2 Metrics te Tracken
+
+| Metric | Doel | Actie bij Overschrijding |
+|--------|------|--------------------------|
+| AI success rate | > 95% | Check prompts, input validation |
+| AI response time p95 | < 8s | Optimize prompt size |
+| Error rate | < 2% | Alert + investigate |
+
+---
+
+## 11. Risico's & Technische Mitigatie
+
+| Risico | Impact | Kans | Mitigatie |
+|--------|--------|------|-----------|
+| AI genereert invalide JSON | Hoog | Middel | Zod validation, retry logic, fallback |
+| AI API down/rate limited | Hoog | Laag | Error message, manual mode optie |
+| Grote intake teksten (token limit) | Middel | Middel | Truncate/summarize intake eerst |
+| Inconsistente B1-taal kwaliteit | Middel | Middel | Post-processing, behandelaar review |
+| Performance bij grote plannen | Laag | Laag | Pagination, lazy loading |
+
+---
+
+## 12. Implementatie Volgorde
+
+### Stap 1: Types & Database (2-3 uur)
+
+**Bestanden:**
+```
+lib/types/
+βββ behandelplan.ts # Nieuwe types + Zod schemas
+βββ leefgebieden.ts # Life domain types
+
+supabase/migrations/
+βββ xxx_add_behandelplan_fields.sql
+```
+
+### Stap 2: Leefgebieden Componenten (3-4 uur)
+
+**Bestanden:**
+```
+components/behandelplan/
+βββ leefgebieden-form.tsx # Intake formulier (7 sliders)
+βββ leefgebieden-scores.tsx # Progress bar weergave
+βββ leefgebieden-badge.tsx # Domain tag/badge
+```
+
+### Stap 3: AI Generatie (3-4 uur)
+
+**Bestanden:**
+```
+lib/ai/
+βββ behandelplan-prompt.ts # System + user prompts
+βββ intervention-mapping.ts # Evidence-based mapping
+
+app/api/behandelplan/
+βββ generate/route.ts # POST endpoint
+βββ regenerate-section/route.ts # Micro-regeneratie
+```
+
+### Stap 4: Behandelplan UI (6-8 uur)
+
+**Bestanden:**
+```
+app/epd/patients/[id]/behandelplan/
+βββ page.tsx # Server component (vervang placeholder)
+βββ actions.ts # Server actions (CRUD)
+βββ components/
+ βββ behandelplan-view.tsx
+ βββ goals-section.tsx
+ βββ goal-card.tsx
+ βββ interventions-section.tsx
+ βββ generate-button.tsx
+```
+
+---
+
+## 13. Bijlagen & Referenties
+
+### Projectdocumenten
+- [PRD Behandelplan v2.0](./prd-behandelplan-v2-final.md)
+- [FO Behandelplan v1.0](./fo-behandelplan-v1.md)
+- [UX Stylesheet](../ux-stylesheet.md)
+
+### Tech Documentatie
+- Next.js: https://nextjs.org/docs
+- Supabase: https://supabase.com/docs
+- Anthropic Claude: https://docs.anthropic.com/claude/reference
+
+### Bestaande Code Referenties
+- API pattern: `/app/api/reports/classify/route.ts`
+- Server actions: `/app/epd/patients/[id]/intakes/[intakeId]/actions.ts`
+- Types pattern: `/lib/types/report.ts`
+- AI integration: `/app/api/docs/chat/route.ts`
+
+---
+
+**Document Status:** v1.0 Draft
+**Volgende Review:** Na implementatie Stap 1-2
+**Eigenaar:** Colin van Zeeland
diff --git a/docs/templates/bouwplan_template.md b/docs/templates/bouwplan_template.md
index 4adaebf..87b1089 100644
--- a/docs/templates/bouwplan_template.md
+++ b/docs/templates/bouwplan_template.md
@@ -9,7 +9,7 @@ Afhankelijk van de **complexiteit van je software** bepaal je zelf hoe gedetaill
**Projectnaam:** _[vul in]_
**Versie:** _v1.0_
**Datum:** _[dd-mm-jjjj]_
-**Auteur:** _[naam]_
+**Auteur:** _[Colin Lit]_
---
@@ -155,6 +155,8 @@ export const loadData = async () => {
| E3 | AI-integratie | AI endpoints en prompt engineering | β³ To Do | 3 | Test met Gemini model |
| E4 | Testing & Deploy | QA, demo prep en deployment | β³ To Do | 3 | |
+**Belangrijk:** Voer niet in 1x het volledige plan uit. Bouw per epic en per story.
+
---
## 4. Epics & Stories (Uitwerking)
diff --git a/docs/templates/fo_template.md b/docs/templates/fo_template.md
index ef3c75b..c8a9f92 100644
--- a/docs/templates/fo_template.md
+++ b/docs/templates/fo_template.md
@@ -3,7 +3,7 @@
**Projectnaam:** _[vul in]_
**Versie:** _v1.0_
**Datum:** _[dd-mm-jjjj]_
-**Auteur:** _[naam]_
+**Auteur:** Colin Lit
---
diff --git a/docs/templates/prd_template.md b/docs/templates/prd_template.md
index 5f5488f..132ccb4 100644
--- a/docs/templates/prd_template.md
+++ b/docs/templates/prd_template.md
@@ -3,7 +3,7 @@
**Projectnaam:** _[vul in]_
**Versie:** _v1.0_
**Datum:** _[dd-mm-jjjj]_
-**Auteur:** _[naam]_
+**Auteur:** Colin Lit
---
diff --git a/lib/types/behandelplan.ts b/lib/types/behandelplan.ts
new file mode 100644
index 0000000..88e5a6e
--- /dev/null
+++ b/lib/types/behandelplan.ts
@@ -0,0 +1,370 @@
+/**
+ * Behandelplan (Treatment Plan) Types
+ *
+ * Types voor AI-gegenereerde behandelplannen
+ * Gebaseerd op FHIR CarePlan met GGZ-specifieke uitbreidingen
+ */
+
+import { z } from 'zod';
+import { type LifeDomain, type LifeDomainScore, LIFE_DOMAINS, PRIORITIES } from './leefgebieden';
+
+// =============================================================================
+// STATUS TYPES
+// =============================================================================
+
+/**
+ * Status van een behandelplan
+ */
+export const PLAN_STATUSES = ['concept', 'actief', 'in_evaluatie', 'afgerond', 'gearchiveerd'] as const;
+export type PlanStatus = typeof PLAN_STATUSES[number];
+
+/**
+ * Status van een doel
+ */
+export const GOAL_STATUSES = ['niet_gestart', 'bezig', 'gehaald', 'bijgesteld'] as const;
+export type GoalStatus = typeof GOAL_STATUSES[number];
+
+/**
+ * Type evaluatiemoment
+ */
+export const EVALUATION_TYPES = ['tussentijds', 'eind', 'crisis'] as const;
+export type EvaluationType = typeof EVALUATION_TYPES[number];
+
+/**
+ * Status evaluatiemoment
+ */
+export const EVALUATION_STATUSES = ['gepland', 'afgerond', 'overgeslagen'] as const;
+export type EvaluationStatus = typeof EVALUATION_STATUSES[number];
+
+// =============================================================================
+// CORE TYPES
+// =============================================================================
+
+/**
+ * Behandelstructuur - algemene parameters van het plan
+ */
+export interface Behandelstructuur {
+ duur: string; // bijv. "8 weken"
+ frequentie: string; // bijv. "Wekelijks"
+ aantalSessies: number; // bijv. 8
+ vorm: string; // bijv. "Individueel"
+}
+
+/**
+ * SMART Doel
+ */
+export interface SmartGoal {
+ id: string;
+ title: string; // Korte beschrijving (1 zin)
+ description: string; // SMART-uitwerking (2-3 zinnen)
+ clientVersion: string; // B1-taal versie voor cliΓ«nt
+ lifeDomain: LifeDomain; // Gekoppeld leefgebied
+ priority: 'hoog' | 'middel' | 'laag';
+ measurability: string; // Hoe meten we vooruitgang?
+ timelineWeeks: number; // Binnen X weken
+ status: GoalStatus;
+ progress: number; // 0-100
+}
+
+/**
+ * Evidence-based Interventie
+ */
+export interface Intervention {
+ id: string;
+ name: string; // bijv. "CGT", "EMDR", "ACT"
+ description: string; // Uitleg van de interventie
+ rationale: string; // Waarom past dit bij deze cliΓ«nt?
+ linkedGoalIds: string[]; // Welke doelen worden hiermee benaderd?
+}
+
+/**
+ * Evaluatiemoment
+ */
+export interface Evaluatiemoment {
+ id: string;
+ type: EvaluationType;
+ weekNumber: number;
+ plannedDate: string; // ISO date string
+ actualDate?: string; // Ingevuld na uitvoering
+ status: EvaluationStatus;
+ outcome?: string; // Vrije tekst resultaat
+ lifeDomainUpdates?: LifeDomainScore[]; // Nieuwe scores
+}
+
+/**
+ * Veiligheidsplan (alleen bij severity "Hoog")
+ */
+export interface Veiligheidsplan {
+ waarschuwingssignalen: string[]; // 3-5 items
+ copingStrategieen: string[]; // 3-5 items
+ contacten: {
+ naam: string;
+ rol: string;
+ telefoon: string;
+ }[];
+ restricties?: string[]; // bijv. "Geen alcohol tijdens behandeling"
+}
+
+/**
+ * Sessie in de planning
+ */
+export interface Sessie {
+ id: string;
+ nummer: number;
+ focus: string;
+ datum?: string; // ISO date string
+ status: 'gepland' | 'afgerond' | 'no_show' | 'verzet' | 'geannuleerd';
+ gekoppeldeDoelIds: string[];
+ notities?: string;
+}
+
+// =============================================================================
+// GENERATED PLAN (AI OUTPUT)
+// =============================================================================
+
+/**
+ * Volledig door AI gegenereerd behandelplan
+ */
+export interface GeneratedPlan {
+ behandelstructuur: Behandelstructuur;
+ doelen: SmartGoal[];
+ interventies: Intervention[];
+ sessiePlanning: Sessie[];
+ evaluatiemomenten: Evaluatiemoment[];
+ veiligheidsplan?: Veiligheidsplan; // Alleen bij severity "Hoog"
+}
+
+// =============================================================================
+// API INPUT/OUTPUT TYPES
+// =============================================================================
+
+/**
+ * Input voor behandelplan generatie
+ */
+export interface GenerateBehandelplanInput {
+ patientId: string;
+ intakeId: string;
+ conditionId?: string; // Optioneel, haalt anders laatste op
+ extraInstructions?: string; // Optionele aanvullende instructies
+}
+
+/**
+ * Input voor micro-regeneratie
+ */
+export interface RegenerateSectionInput {
+ patientId: string;
+ carePlanId: string;
+ sectionType: 'goal' | 'intervention';
+ sectionId: string;
+ instruction?: string; // Extra instructie voor AI
+ currentPlan: GeneratedPlan; // Context van huidige plan
+}
+
+/**
+ * Output van micro-regeneratie
+ */
+export interface RegeneratedSection {
+ type: 'goal' | 'intervention';
+ original: SmartGoal | Intervention;
+ regenerated: SmartGoal | Intervention;
+}
+
+// =============================================================================
+// ZOD SCHEMAS
+// =============================================================================
+
+export const BehandelstructuurSchema = z.object({
+ duur: z.string(),
+ frequentie: z.string(),
+ aantalSessies: z.number().min(1).max(52),
+ vorm: z.string(),
+});
+
+export const SmartGoalSchema = z.object({
+ id: z.string(),
+ title: z.string().min(5).max(200),
+ description: z.string().min(10).max(500),
+ clientVersion: z.string().min(5).max(300),
+ lifeDomain: z.enum(LIFE_DOMAINS),
+ priority: z.enum(PRIORITIES),
+ measurability: z.string().min(5).max(200),
+ timelineWeeks: z.number().min(1).max(52),
+ status: z.enum(GOAL_STATUSES),
+ progress: z.number().min(0).max(100),
+});
+
+export const InterventionSchema = z.object({
+ id: z.string(),
+ name: z.string().min(2).max(100),
+ description: z.string().min(10).max(500),
+ rationale: z.string().min(10).max(500),
+ linkedGoalIds: z.array(z.string()),
+});
+
+export const EvaluatiemomentSchema = z.object({
+ id: z.string(),
+ type: z.enum(EVALUATION_TYPES),
+ weekNumber: z.number().min(1).max(52),
+ plannedDate: z.string(),
+ actualDate: z.string().optional(),
+ status: z.enum(EVALUATION_STATUSES),
+ outcome: z.string().optional(),
+ lifeDomainUpdates: z.array(z.any()).optional(), // Simplified for now
+});
+
+export const VeiligheidsplanSchema = z.object({
+ waarschuwingssignalen: z.array(z.string()).min(1).max(10),
+ copingStrategieen: z.array(z.string()).min(1).max(10),
+ contacten: z.array(z.object({
+ naam: z.string(),
+ rol: z.string(),
+ telefoon: z.string(),
+ })),
+ restricties: z.array(z.string()).optional(),
+});
+
+export const SessieSchema = z.object({
+ id: z.string(),
+ nummer: z.number().min(1),
+ focus: z.string(),
+ datum: z.string().optional(),
+ status: z.enum(['gepland', 'afgerond', 'no_show', 'verzet', 'geannuleerd']),
+ gekoppeldeDoelIds: z.array(z.string()),
+ notities: z.string().optional(),
+});
+
+export const GeneratedPlanSchema = z.object({
+ behandelstructuur: BehandelstructuurSchema,
+ doelen: z.array(SmartGoalSchema).min(1).max(6),
+ interventies: z.array(InterventionSchema).min(1).max(5),
+ sessiePlanning: z.array(SessieSchema),
+ evaluatiemomenten: z.array(EvaluatiemomentSchema).min(1),
+ veiligheidsplan: VeiligheidsplanSchema.optional(),
+});
+
+export const GenerateBehandelplanInputSchema = z.object({
+ patientId: z.string().uuid(),
+ intakeId: z.string().uuid(),
+ conditionId: z.string().uuid().optional(),
+ extraInstructions: z.string().max(500).optional(),
+});
+
+export const RegenerateSectionInputSchema = z.object({
+ patientId: z.string().uuid(),
+ carePlanId: z.string().uuid(),
+ sectionType: z.enum(['goal', 'intervention']),
+ sectionId: z.string(),
+ instruction: z.string().max(200).optional(),
+ currentPlan: GeneratedPlanSchema,
+});
+
+// =============================================================================
+// HELPER FUNCTIONS
+// =============================================================================
+
+/**
+ * Genereer een nieuw UUID-achtig ID
+ */
+export function generateId(): string {
+ return crypto.randomUUID();
+}
+
+/**
+ * Maak een nieuw leeg SMART doel
+ */
+export function createEmptyGoal(lifeDomain: LifeDomain = 'dlv'): SmartGoal {
+ return {
+ id: generateId(),
+ title: '',
+ description: '',
+ clientVersion: '',
+ lifeDomain,
+ priority: 'middel',
+ measurability: '',
+ timelineWeeks: 8,
+ status: 'niet_gestart',
+ progress: 0,
+ };
+}
+
+/**
+ * Maak een nieuwe lege interventie
+ */
+export function createEmptyIntervention(): Intervention {
+ return {
+ id: generateId(),
+ name: '',
+ description: '',
+ rationale: '',
+ linkedGoalIds: [],
+ };
+}
+
+/**
+ * Bereken totale voortgang van alle doelen
+ */
+export function calculateTotalProgress(goals: SmartGoal[]): number {
+ if (goals.length === 0) return 0;
+ const sum = goals.reduce((acc, goal) => acc + goal.progress, 0);
+ return Math.round(sum / goals.length);
+}
+
+/**
+ * Krijg doelen per leefgebied
+ */
+export function getGoalsByDomain(goals: SmartGoal[]): Record {
+ const result = {} as Record;
+ for (const domain of LIFE_DOMAINS) {
+ result[domain] = goals.filter((g) => g.lifeDomain === domain);
+ }
+ return result;
+}
+
+/**
+ * Check of plan klaar is voor publicatie
+ */
+export function canPublish(plan: GeneratedPlan): { valid: boolean; errors: string[] } {
+ const errors: string[] = [];
+
+ if (plan.doelen.length === 0) {
+ errors.push('Minimaal 1 doel is vereist');
+ }
+
+ if (plan.interventies.length === 0) {
+ errors.push('Minimaal 1 interventie is vereist');
+ }
+
+ if (!plan.behandelstructuur.duur || !plan.behandelstructuur.frequentie) {
+ errors.push('Behandelstructuur moet compleet zijn');
+ }
+
+ if (plan.evaluatiemomenten.length < 2) {
+ errors.push('Minimaal 2 evaluatiemomenten zijn vereist');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ };
+}
+
+/**
+ * Status label voor UI
+ */
+export const PLAN_STATUS_LABELS: Record = {
+ concept: { label: 'Concept', color: '#60a5fa' }, // blauw
+ actief: { label: 'Actief', color: '#10b981' }, // groen
+ in_evaluatie: { label: 'In evaluatie', color: '#f59e0b' }, // oranje
+ afgerond: { label: 'Afgerond', color: '#6b7280' }, // grijs
+ gearchiveerd: { label: 'Gearchiveerd', color: '#9ca3af' }, // lichtgrijs
+};
+
+/**
+ * Goal status label voor UI
+ */
+export const GOAL_STATUS_LABELS: Record = {
+ niet_gestart: { label: 'Niet gestart', color: '#9ca3af' },
+ bezig: { label: 'Bezig', color: '#3b82f6' },
+ gehaald: { label: 'Gehaald', color: '#10b981' },
+ bijgesteld: { label: 'Bijgesteld', color: '#f59e0b' },
+};
diff --git a/lib/types/leefgebieden.ts b/lib/types/leefgebieden.ts
new file mode 100644
index 0000000..dc48066
--- /dev/null
+++ b/lib/types/leefgebieden.ts
@@ -0,0 +1,167 @@
+/**
+ * Leefgebieden (Life Domains) Types
+ *
+ * 7 levensdomeinen volgens herstelgerichte GGZ-methodiek
+ * Gebruikt voor intake-assessment en behandelplan doelen
+ */
+
+import { z } from 'zod';
+
+/**
+ * De 7 leefgebieden (levensdomeinen)
+ */
+export const LIFE_DOMAINS = [
+ 'dlv', // Dagelijkse Levensverrichtingen
+ 'wonen', // Wonen
+ 'werk', // Werk/Dagbesteding
+ 'sociaal', // Sociaal netwerk
+ 'vrijetijd', // Vrijetijd/Zingeving
+ 'financien', // FinanciΓ«n
+ 'gezondheid', // Lichamelijke gezondheid
+] as const;
+
+export type LifeDomain = typeof LIFE_DOMAINS[number];
+
+/**
+ * Prioriteit niveau
+ */
+export const PRIORITIES = ['laag', 'middel', 'hoog'] as const;
+export type Priority = typeof PRIORITIES[number];
+
+/**
+ * Leefgebied metadata (labels, kleuren, emoji's)
+ */
+export const LIFE_DOMAIN_META: Record = {
+ dlv: {
+ label: 'Dagelijkse Levensverrichtingen',
+ shortLabel: 'DLV',
+ emoji: 'π ',
+ color: '#8b5cf6', // paars
+ description: 'Zelfzorg, structuur, dagritme',
+ },
+ wonen: {
+ label: 'Wonen',
+ shortLabel: 'Wonen',
+ emoji: 'π‘',
+ color: '#ec4899', // roze
+ description: 'Woonsituatie, veiligheid thuis',
+ },
+ werk: {
+ label: 'Werk/Dagbesteding',
+ shortLabel: 'Werk',
+ emoji: 'πΌ',
+ color: '#f59e0b', // oranje
+ description: 'Baan, opleiding, vrijwilligerswerk',
+ },
+ sociaal: {
+ label: 'Sociaal netwerk',
+ shortLabel: 'Sociaal',
+ emoji: 'π₯',
+ color: '#3b82f6', // blauw
+ description: 'Familie, vrienden, relaties',
+ },
+ vrijetijd: {
+ label: 'Vrijetijd/Zingeving',
+ shortLabel: 'Vrijetijd',
+ emoji: 'π―',
+ color: '#10b981', // groen
+ description: "Hobby's, levensdoel, spiritualiteit",
+ },
+ financien: {
+ label: 'FinanciΓ«n',
+ shortLabel: 'FinanciΓ«n',
+ emoji: 'π°',
+ color: '#eab308', // geel
+ description: 'Schulden, inkomen, budgettering',
+ },
+ gezondheid: {
+ label: 'Lichamelijke gezondheid',
+ shortLabel: 'Gezondheid',
+ emoji: 'π',
+ color: '#ef4444', // rood
+ description: 'Slaap, beweging, voeding',
+ },
+};
+
+/**
+ * Score per leefgebied (1-5 schaal)
+ */
+export interface LifeDomainScore {
+ domain: LifeDomain;
+ baseline: number; // 1-5, score bij intake
+ current: number; // 1-5, huidige score (voor evaluatie)
+ target: number; // 1-5, doelscore
+ notes: string; // Toelichting
+ priority: Priority; // Prioriteit voor behandeling
+}
+
+/**
+ * Zod schema voor validatie
+ */
+export const LifeDomainScoreSchema = z.object({
+ domain: z.enum(LIFE_DOMAINS),
+ baseline: z.number().min(1).max(5),
+ current: z.number().min(1).max(5),
+ target: z.number().min(1).max(5),
+ notes: z.string().default(''),
+ priority: z.enum(PRIORITIES),
+});
+
+export const LifeDomainScoresSchema = z.array(LifeDomainScoreSchema).length(7);
+
+/**
+ * Type voor het volledige leefgebieden formulier
+ */
+export type LifeDomainScores = LifeDomainScore[];
+
+/**
+ * Default waarden voor nieuw formulier
+ */
+export function createDefaultLifeDomainScores(): LifeDomainScores {
+ return LIFE_DOMAINS.map((domain) => ({
+ domain,
+ baseline: 3,
+ current: 3,
+ target: 4,
+ notes: '',
+ priority: 'middel' as Priority,
+ }));
+}
+
+/**
+ * Helper: krijg leefgebieden met hoge prioriteit
+ */
+export function getHighPriorityDomains(scores: LifeDomainScores): LifeDomainScore[] {
+ return scores.filter((s) => s.priority === 'hoog');
+}
+
+/**
+ * Helper: krijg leefgebieden met lage scores (problematisch)
+ */
+export function getLowScoreDomains(scores: LifeDomainScores, threshold = 2): LifeDomainScore[] {
+ return scores.filter((s) => s.baseline <= threshold);
+}
+
+/**
+ * Helper: bereken gemiddelde score
+ */
+export function getAverageScore(scores: LifeDomainScores, type: 'baseline' | 'current' | 'target' = 'baseline'): number {
+ if (scores.length === 0) return 0;
+ const sum = scores.reduce((acc, s) => acc + s[type], 0);
+ return Math.round((sum / scores.length) * 10) / 10;
+}
+
+/**
+ * Helper: krijg kleur voor score (groen/oranje/rood)
+ */
+export function getScoreColor(score: number): string {
+ if (score >= 4) return '#10b981'; // groen
+ if (score >= 3) return '#f59e0b'; // oranje
+ return '#ef4444'; // rood
+}
diff --git a/package.json b/package.json
index dcaf338..73eba78 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,11 @@
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-icons": "^1.3.2",
+ "@radix-ui/react-label": "^2.1.8",
+ "@radix-ui/react-progress": "^1.1.8",
+ "@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
+ "@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toast": "^1.2.15",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.4.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 03e9e27..7b0b78d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,9 +41,21 @@ importers:
'@radix-ui/react-icons':
specifier: ^1.3.2
version: 1.3.2(react@18.3.1)
+ '@radix-ui/react-label':
+ specifier: ^2.1.8
+ version: 2.1.8(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-progress':
+ specifier: ^1.1.8
+ version: 1.1.8(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slider':
+ specifier: ^1.3.6
+ version: 1.3.6(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slot':
specifier: ^1.2.4
version: 1.2.4(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-tabs':
+ specifier: ^1.1.13
+ version: 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-toast':
specifier: ^1.2.15
version: 1.2.15(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -553,6 +565,9 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
+ '@radix-ui/number@1.1.1':
+ resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
+
'@radix-ui/primitive@1.1.3':
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
@@ -613,6 +628,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-context@1.1.3':
+ resolution: {integrity: sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-dialog@1.1.15':
resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
peerDependencies:
@@ -697,6 +721,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-label@2.1.8':
+ resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-menu@2.1.16':
resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==}
peerDependencies:
@@ -762,6 +799,32 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-primitive@2.1.4':
+ resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-progress@1.1.8':
+ resolution: {integrity: sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-roving-focus@1.1.11':
resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
peerDependencies:
@@ -775,6 +838,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-slider@1.3.6':
+ resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-slot@1.2.3':
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
peerDependencies:
@@ -793,6 +869,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-tabs@1.1.13':
+ resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-toast@1.2.15':
resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==}
peerDependencies:
@@ -851,6 +940,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-previous@1.1.1':
+ resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-rect@1.1.1':
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
peerDependencies:
@@ -4104,6 +4202,8 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
+ '@radix-ui/number@1.1.1': {}
+
'@radix-ui/primitive@1.1.3': {}
'@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@@ -4153,6 +4253,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.27
+ '@radix-ui/react-context@1.1.3(@types/react@18.3.27)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.27
+
'@radix-ui/react-dialog@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -4237,6 +4343,15 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.27
+ '@radix-ui/react-label@2.1.8(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.27
+ '@types/react-dom': 18.3.7(@types/react@18.3.27)
+
'@radix-ui/react-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -4310,6 +4425,25 @@ snapshots:
'@types/react': 18.3.27
'@types/react-dom': 18.3.7(@types/react@18.3.27)
+ '@radix-ui/react-primitive@2.1.4(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.4(@types/react@18.3.27)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.27
+ '@types/react-dom': 18.3.7(@types/react@18.3.27)
+
+ '@radix-ui/react-progress@1.1.8(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.3(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.27
+ '@types/react-dom': 18.3.7(@types/react@18.3.27)
+
'@radix-ui/react-roving-focus@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -4327,6 +4461,25 @@ snapshots:
'@types/react': 18.3.27
'@types/react-dom': 18.3.7(@types/react@18.3.27)
+ '@radix-ui/react-slider@1.3.6(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.27)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.27
+ '@types/react-dom': 18.3.7(@types/react@18.3.27)
+
'@radix-ui/react-slot@1.2.3(@types/react@18.3.27)(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1)
@@ -4341,6 +4494,22 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.27
+ '@radix-ui/react-tabs@1.1.13(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.1(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@18.3.27)(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.27)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.27
+ '@types/react-dom': 18.3.7(@types/react@18.3.27)
+
'@radix-ui/react-toast@1.2.15(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -4395,6 +4564,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.27
+ '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.27)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.27
+
'@radix-ui/react-use-rect@1.1.1(@types/react@18.3.27)(react@18.3.1)':
dependencies:
'@radix-ui/rect': 1.1.1
@@ -5580,8 +5755,8 @@ snapshots:
'@typescript-eslint/parser': 8.47.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1)
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1)
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.5(eslint@8.57.1)
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1)
@@ -5600,7 +5775,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1):
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
@@ -5611,22 +5786,22 @@ snapshots:
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.47.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1)
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -5637,7 +5812,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3