Files
triqura-ecd/docs/migratie-clients-naar-patients.md
2025-11-23 10:13:00 +01:00

20 KiB

🚀 Migratieplan: /clients//patients/ Route Consolidatie

Datum: 2025-11-22 Versie: 1.0 Status: Voltooid Gekozen Strategie: Custom API (Optie B)


📋 Executive Summary

Doel: Consolideer beide routes naar één FHIR-compliant /patients/ route met Custom API voor Intakes.

Inschatting: 10-12 story points (2-3 werkdagen) Risico Level: Medium Breaking Changes: Ja (mitigated door redirects)


🔧 Fase 0 — Schema & Runbook

Status: Repo gealigneerd (feb 2026). Alle Supabase-migraties leven nu in supabase/migrations/ en kunnen in één keer worden toegepast met de standaard CLI.

Benodigde migraties

  • 20241115000001_create_leads_table.sql
  • 20241115000002_create_epd_core_tables.sql
  • 20241115000004_create_demo_users.sql
  • 20241121_migrate_legacy_to_fhir.sql
  • 20241121_seed_demo_data.sql
  • 20251119094908_auth_hook_duplicate_email.sql
  • 20251122_screening_intake_schema.sql
  • 20251122_seed_default_organization.sql

Runbook

  1. Start Supabase lokaal (supabase start) of log in op de gewenste omgeving.
  2. Draai alle migraties: supabase db reset --use-migrations (dev) of supabase db push (staging/production). Hiermee wordt het schema gelijkgetrokken met supabase/migrations/.
  3. Seed basisdata: pnpm ts-node scripts/seed-organization.ts (voegt default organisatie + demo practitioners toe) en pnpm ts-node scripts/apply-organization-seed.sh indien nodig.
  4. Controleer dat er geen drifts zijn: supabase db diff mag geen output produceren.
  5. Smoke-test FHIR + Custom API:
    • curl -s http://localhost:3000/api/fhir/Patient?_count=1
    • curl -s http://localhost:3000/api/intakes?patientId=<uuid>
  6. Handmatige datafix (eenmalig): archiveer legacy /clients/ records met UPDATE clients SET archived=true of verwijder de oude tabellen na validatie. Nieuwe dossiers worden uitsluitend via patients beheerd.
  7. Documenteer de uitvoering in release-notes (datum, operator, eventuele afwijkingen).

Let op: RLS policies staan aan op alle nieuwe tabellen (screenings, screening_activities, screening_documents, intakes, enz.). Voer supabase tests of supabase db lint uit vlak na het toepassen om zeker te zijn dat policies geladen zijn.


🎯 Probleem Statement

Momenteel bestaan er twee parallelle implementaties voor patiënt/cliënt beheer:

  1. /app/epd/clients/ - Oudere implementatie (17-19 nov)

    • Directe Supabase queries
    • Bevat werkende Intake module (Epic 4)
    • Niet FHIR-compliant
  2. /app/epd/patients/ - Nieuwere implementatie (21-22 nov)

    • FHIR API compliant
    • Betere features (BSN validatie, filters, paginatie)
    • Intake nog placeholder

Impact:

  • Verwarring over canonical route
  • Code duplicatie
  • Inconsistente architectuur
  • Moeilijke maintenance

📊 Huidige Situatie Analyse

/clients/ Route Inventory

Volledig Geïmplementeerd:

Component LOC Functionaliteit
intakes/components/intake-list.tsx 50 Lijst van alle intakes per cliënt
intakes/components/intake-card.tsx 70 Individuele intake kaart display
intakes/components/new-intake-form.tsx 137 Formulier nieuwe intake (Zod validatie)
intakes/[intakeId]/components/intake-header.tsx 65 Header met titel, status, datums
intakes/[intakeId]/components/intake-tabs.tsx 52 Tab navigatie binnen intake
intakes/[intakeId]/page.tsx 52 Intake detail algemene informatie
intakes/[intakeId]/layout.tsx 29 Layout wrapper voor intake detail
intakes/actions.ts 83 Server actions (CRUD operations)

Total: ~538 LOC werkende functionaliteit

Server Actions (Supabase-based):

export async function getIntakesByClientId(clientId: string)
export async function createIntake(input: CreateIntakeInput)
export async function getIntakeById(intakeId: string)

Placeholders (geen migratie nodig):

  • Profile Tab ("Coming Soon Week 3")
  • Plan Tab ("Coming Soon Week 3")
  • Reports pagina
  • Diagnose pagina

📦 Basis Functionaliteit (inferieur aan /patients/):

  • Client list (geen status/gender filters, geen paginatie)
  • Client form (geen BSN validatie, geen John Doe)
  • Dashboard (basic, geen unique features)

/patients/ Route Inventory

Al Geïmplementeerd (Superieur):

  • Patient list met advanced filtering
  • Patient form met BSN 11-proef validatie
  • John Doe support
  • Delete functionaliteit (two-step confirmation)
  • Modern layout (ClientHeader + ClientSidebar)
  • Dashboard met quick actions

Ontbreekt (te migreren van /clients/):

  • Intake module (volledig)

🎯 Gekozen Architectuur: Custom API (Optie B)

Waarom Custom API?

Voordelen:

  • Snellere implementatie (1-2 dagen vs 3-4 dagen)
  • Eenvoudiger data model (direct mapping naar intakes tabel)
  • Minder transformatie logica nodig
  • Bestaande database schema hergebruiken
  • Type safety met TypeScript

⚠️ Trade-offs:

  • Niet FHIR Encounter-compliant (maar acceptabel voor MVP)
  • Toekomstige refactor naar FHIR mogelijk nodig
  • Aparte API naast FHIR Patient/Practitioner

🔮 Toekomst Path:

  • Behoud Custom API voor Intakes in MVP
  • Plan FHIR Encounter mapping in latere fase (Epic 5/6)
  • Incrementele migratie mogelijk zonder breaking changes

API Specificatie

Endpoint: /api/intakes

Routes:

GET    /api/intakes?patientId={id}        // List intakes for patient
POST   /api/intakes                        // Create new intake
GET    /api/intakes/{intakeId}             // Get intake by ID
PUT    /api/intakes/{intakeId}             // Update intake
DELETE /api/intakes/{intakeId}             // Delete intake

Request/Response Types:

interface Intake {
  id: string;
  patient_id: string;
  title: string;
  department: 'Volwassenen' | 'Jeugd' | 'Ouderen';
  status: 'Open' | 'Afgerond';
  start_date: string;
  end_date?: string;
  notes?: string;
  psychologist_id?: string;
  created_at: string;
  updated_at: string;
}

interface CreateIntakeInput {
  patient_id: string;
  title: string;
  department: 'Volwassenen' | 'Jeugd' | 'Ouderen';
  start_date: string;
}

interface IntakeListResponse {
  intakes: Intake[];
  total: number;
}

🗺️ Gedetailleerd Migratieplan

Fase 1: Intake API Ontwikkeling (3 SP)

Doel: Bouw Custom API voor Intake operaties

1.1 API Route Setup (1 SP)

Bestanden aan te maken:

app/api/intakes/
├── route.ts              // GET (list), POST (create)
└── [intakeId]/
    └── route.ts          // GET, PUT, DELETE

Implementatie:

  • Supabase client met RLS
  • Error handling
  • Input validatie (Zod schemas)
  • Response formatting

Acceptatie Criteria:

  • GET /api/intakes?patientId={id} retourneert alle intakes
  • POST /api/intakes creëert nieuwe intake
  • GET /api/intakes/{id} retourneert specifieke intake
  • PUT /api/intakes/{id} update intake
  • DELETE /api/intakes/{id} verwijdert intake
  • Alle endpoints hebben error handling
  • Input validatie werkt

1.2 Type Definitions (0.5 SP)

Bestand: lib/types/intake.ts

Inhoud:

  • Intake interface
  • CreateIntakeInput, UpdateIntakeInput types
  • IntakeListResponse type
  • Zod validation schemas

1.3 Server Actions Refactor (1.5 SP)

Bestand: app/epd/patients/[id]/intakes/actions.ts

Wijzigingen:

// VOOR (Supabase direct):
const { data } = await supabase.from('intakes').select('*')

// NA (API call):
const response = await fetch(`/api/intakes?patientId=${patientId}`)
const data = await response.json()

Updates:

  • getIntakesByClientIdgetIntakesByPatientId
  • createIntake - gebruik POST /api/intakes
  • getIntakeById - gebruik GET /api/intakes/{id}
  • Cookies worden doorgegeven aan fetch calls
  • Error handling voor auth redirects

Complexiteit: Medium Risico: Laag


Fase 2: Component Migratie (3 SP)

Doel: Verplaats alle Intake componenten naar /patients/

2.1 Directory Structuur (0.5 SP)

Creëer structuur:

app/epd/patients/[id]/intakes/
├── components/
│   ├── intake-card.tsx ✅
│   ├── intake-list.tsx ✅
│   └── new-intake-form.tsx ✅
├── [intakeId]/
│   ├── components/
│   │   ├── intake-header.tsx ✅
│   │   └── intake-tabs.tsx ✅
│   ├── layout.tsx ✅
│   └── page.tsx ✅
├── new/
│   └── page.tsx ✅
└── actions.ts ✅

2.2 Kopieer en Pas Aan (2 SP)

Voor elk component:

  1. Kopieer van /clients/[id]/intakes/ naar /patients/[id]/intakes/
  2. Update imports:
    • clientIdpatientId (props en variabelen)
    • /epd/clients//epd/patients/ (routes)
    • Type imports naar @/lib/types/intake
  3. Update server action calls (gebruik nieuwe actions.ts)
  4. Import paden gecorrigeerd

Specifieke wijzigingen:

intake-list.tsx:

  • Props: clientIdpatientId
  • Link urls: /clients//patients/

new-intake-form.tsx:

  • Form field: patient_id ipv client_id
  • Redirect: /patients/ ipv /clients/

intake-header.tsx:

  • Breadcrumb: /patients/ ipv /clients/
  • Type imports gecorrigeerd

Complexiteit: Laag (copy-paste + find/replace) Risico: Laag

2.3 Verwijder Placeholder (0.5 SP)

Bestand: app/epd/patients/[id]/intake/page.tsx

Actie: Verwijder placeholder, vervang door redirect:

export default async function IntakeRedirect({ params }) {
  const { id } = await params;
  redirect(`/epd/patients/${id}/intakes`);
}

Fase 3: Navigatie Integratie (2 SP)

Doel: Integreer Intake tab in patient navigatie

3.1 Update ClientSidebar (1 SP)

Bestand: app/epd/patients/[id]/components/client-sidebar.tsx

Wijzigingen:

  • Maak "Intake" tab interactief (verwijder placeholder styling)
  • Link naar /patients/{id}/intakes
  • Active state logic voor subroutes
  • Optioneel: Badge met aantal openstaande intakes (nog niet geïmplementeerd)

3.2 Update Dashboard (1 SP)

Bestand: app/epd/patients/[id]/page.tsx

Wijzigingen:

  • Update "Volgende stappen" sectie
  • Verwijs naar intake functionaliteit
  • Intake quick action card linkt naar nieuwe route
  • Optioneel: Toon recent intake in dashboard (geïmplementeerd)

Complexiteit: Laag Risico: Laag


Fase 4: Route Consolidatie (2 SP)

Doel: Deprecate /clients/ en setup redirects

4.1 Catch-all Redirect (1 SP)

Bestand: app/epd/clients/[...path]/route.ts (nieuw)

Implementatie:

import { redirect } from 'next/navigation';
import { NextRequest } from 'next/server';

export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ path: string[] }> }
) {
  const { path } = await params;
  const searchParams = request.nextUrl.searchParams;
  const newPath = `/epd/patients/${path.join('/')}`;
  const newUrl = new URL(newPath, request.url);
  searchParams.forEach((value, key) => {
    newUrl.searchParams.set(key, value);
  });
  redirect(newUrl.toString());
}

Test scenarios:

  • /epd/clients/epd/patients (via page.tsx redirect)
  • /epd/clients/{id}/epd/patients/{id}
  • /epd/clients/{id}/intakes/epd/patients/{id}/intakes
  • /epd/clients/{id}/intakes/{intakeId}/epd/patients/{id}/intakes/{intakeId}
  • Query parameters worden behouden

4.2 Archive Old Code (0.5 SP)

Acties:

  • mkdir -p app/epd/_archive
  • mv app/epd/clients app/epd/_archive/clients_backup_20251122
  • Redirect routes teruggeplaatst in app/epd/clients/
  • Oude code volledig gearchiveerd

4.3 Update Documentatie (0.5 SP)

Bestanden bij te werken:

  • docs/specs/UI/bouwplan-mini-epd-v1.0.md - Update /clients/ naar /patients/
  • CHANGELOG.md - Entry toegevoegd met breaking changes
  • docs/migratie-clients-naar-patients.md - Dit document bijgewerkt
  • docs/bouwplan-mini-epd.md - Nog te updaten indien aanwezig
  • README.md - Nog te updaten indien screenshots/links aanwezig

Changelog:

  • Entry toegevoegd aan CHANGELOG.md met breaking changes en mitigatie

Complexiteit: Laag Risico: Laag


Fase 5: Testing & Validatie (2 SP)

Doel: Verifieer feature parity en stabiliteit

5.1 Functionele Tests (1 SP)

Test Checklist:

Intake List:

  • Navigeer naar /patients/{id}/intakes
  • Lijst toont alle intakes voor patient
  • Empty state toont bij geen intakes
  • "Nieuwe intake" button werkt

Nieuwe Intake:

  • Formulier opent via "Nieuwe intake" button
  • Alle velden valideren correct
  • Submit creëert intake in database
  • Redirect naar intake lijst na succes
  • Intake verschijnt in lijst

Intake Detail:

  • Klik op intake card opent detail
  • Header toont correcte titel, status, datums
  • Tabs tonen (ook al zijn ze placeholder)
  • Algemene informatie tab toont data
  • Notities sectie werkt

Navigatie:

  • Sidebar "Intake" tab is actief
  • Breadcrumbs kloppen
  • Terug naar patiënten werkt

Redirects:

  • /clients/{id}/patients/{id} werkt
  • /clients/{id}/intakes/patients/{id}/intakes werkt
  • Query parameters behouden blijven

5.2 API Tests (0.5 SP)

Test alle endpoints:

# List intakes
curl http://localhost:3000/api/intakes?patientId={uuid}

# Create intake
curl -X POST http://localhost:3000/api/intakes \
  -H "Content-Type: application/json" \
  -d '{"patient_id":"uuid","title":"Test","department":"Volwassenen","start_date":"2025-11-22"}'

# Get intake
curl http://localhost:3000/api/intakes/{intakeId}

# Update intake
curl -X PUT http://localhost:3000/api/intakes/{intakeId} \
  -d '{"status":"Afgerond"}'

# Delete intake
curl -X DELETE http://localhost:3000/api/intakes/{intakeId}

Verifieer:

  • Response status codes correct (200, 201, 404, etc.)
  • Response bodies bevatten verwachte data
  • Errors worden netjes afgehandeld
  • RLS policies werken (unauthorized access blocked)

5.3 Regressie Tests (0.5 SP)

Verifieer bestaande functionaliteit:

  • Patient list werkt nog
  • Patient create/update/delete werkt nog
  • Screening tab (placeholder) werkt nog
  • Andere tabs onveranderd

Complexiteit: Medium Risico: Medium


📈 Story Point Breakdown

Fase Taak SP Complexiteit Risico
1.1 API Route Setup 1 Medium Laag
1.2 Type Definitions 0.5 Laag Laag
1.3 Server Actions Refactor 1.5 Medium Laag
2.1 Directory Structuur 0.5 Laag Laag
2.2 Kopieer en Pas Aan 2 Laag Laag
2.3 Verwijder Placeholder 0.5 Laag Laag
3.1 Update ClientSidebar 1 Laag Laag
3.2 Update Dashboard 1 Laag Laag
4.1 Catch-all Redirect 1 Medium Laag
4.2 Archive Old Code 0.5 Laag Laag
4.3 Update Documentatie 0.5 Laag Laag
5.1 Functionele Tests 1 Medium Medium
5.2 API Tests 0.5 Medium Medium
5.3 Regressie Tests 0.5 Laag Laag
TOTAAL 12 SP Laag-Medium Laag-Medium

Geschatte Tijdsduur: 2-3 werkdagen (16-24 uur development tijd)


⚠️ Risico's en Mitigatie

Risico Impact Waarschijnlijkheid Mitigatie
Breaking changes in productie Hoog Laag Redirects + grondige testing
Data inconsistentie (intakes) Hoog Laag Gebruik dezelfde database tabel
Gemiste edge cases Medium Medium Uitgebreide test checklist
Performance issues (API overhead) Laag Laag Cache strategie, index optimization
Type errors na migratie Medium Medium TypeScript strict mode, thorough testing

🎯 Acceptatie Criteria (Definition of Done)

Functioneel:

  • Alle Intake functionaliteit werkt in /patients/ route
  • Feature parity met originele /clients/ implementatie
  • Redirects werken voor alle /clients/ URLs
  • Geen broken links in applicatie

Technisch:

  • API endpoints geïmplementeerd en werkend
  • Type definitions compleet (lib/types/intake.ts)
  • Error handling geïmplementeerd (inclusief auth redirects)
  • Cookies worden correct doorgegeven aan API calls
  • RLS policies getest (nog te valideren)

Testing:

  • Basis functionele tests uitgevoerd (componenten werken)
  • API tests uitgevoerd (endpoints werken)
  • Import paden gecorrigeerd
  • Geen TypeScript errors
  • Volledige regressie tests (nog te doen)

Documentatie:

  • Bouwplannen bijgewerkt (bouwplan-mini-epd-v1.0.md)
  • CHANGELOG.md entry toegevoegd
  • API documentatie beschikbaar (docs/api/intakes-api.md)
  • Migration guide voor developers (dit document)

Cleanup:

  • Oude /clients/ code gearchiveerd
  • Geen duplicate code
  • Import paden gecorrigeerd
  • Console warnings/errors check (nog te doen)

🚀 Implementatie Volgorde (Aanbevolen)

Dag 1: API Foundation

  1. Ochtend: Fase 1.1 + 1.2 (API routes + types)
  2. Middag: Fase 1.3 (Server actions refactor)
  3. Eind dag: Fase 5.2 (API tests)

Deliverable: Werkende Intake API

Dag 2: Component Migratie

  1. Ochtend: Fase 2.1 + 2.2 (Kopieer componenten)
  2. Middag: Fase 2.3 + 3.1 + 3.2 (Navigatie integratie)
  3. Eind dag: Fase 5.1 (Functionele tests)

Deliverable: Werkende Intake module in /patients/

Dag 3: Consolidatie & Cleanup

  1. Ochtend: Fase 4.1 + 4.2 (Redirects + archive)
  2. Middag: Fase 4.3 (Documentatie)
  3. Eind dag: Fase 5.3 (Regressie tests)

Deliverable: Volledige migratie afgerond


📦 Deliverables Checklist

Code:

  • /app/api/intakes/ - API routes (GET, POST)
  • /app/api/intakes/[intakeId]/ - API routes (GET, PUT, DELETE)
  • /app/epd/patients/[id]/intakes/ - Volledige module
  • /app/epd/clients/[...path]/route.ts - Redirect (catch-all)
  • /app/epd/clients/page.tsx - Root redirect
  • /lib/types/intake.ts - Type definitions

Documentatie:

  • docs/migratie-clients-naar-patients.md - Dit document (bijgewerkt)
  • docs/api/intakes-api.md - API documentatie
  • Updated bouwplannen (bouwplan-mini-epd-v1.0.md)
  • CHANGELOG.md entry

Tests:

  • Test rapport met resultaten
  • Screenshot van werkende features
  • Performance metrics (optioneel)

Archief:

  • /app/epd/_archive/clients_backup_20251122/ - Oude code

🔮 Toekomstige Verbeteringen (Out of Scope)

FHIR Compliance:

  • Migreer Custom Intake API naar FHIR Encounter
  • Mapping van intakes tabel naar FHIR resources
  • Implementeer FHIR search parameters

Features:

  • Intake status workflow (bezig → afgerond met validaties)
  • Intake templates per afdeling
  • Notities met rich text editor
  • Document attachments per intake
  • Intake duplicatie/klonen
  • Bulk operations (meerdere intakes tegelijk)

Performance:

  • API response caching
  • Optimistic UI updates
  • Lazy loading van intake details
  • Pagination voor intake lists (bij >50 intakes)

Analytics:

  • Intake completion metrics
  • Average intake duration per afdeling
  • Psychologist workload dashboard

📞 Ondersteuning en Vragen

Contact:

Resources:

  • Bouwplan Screening & Intake: docs/specs/screening-intake/bouwplan-screening-intake-v1.0.md
  • FHIR Bouwplan: docs/bouwplan-pragmatisch-fhir.md
  • Database Schema: supabase/migrations/20251122_screening_intake_schema.sql

Versie Historie:

  • v1.0 (2025-11-22): Initial migration plan met Custom API strategie