feat: migrate clients module to patients + add docs
This commit is contained in:
170
docs/release/E3.S1-organization-seed.md
Normal file
170
docs/release/E3.S1-organization-seed.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# E3.S1 - Organization Seed
|
||||
|
||||
**Epic:** E3 - Patients & Organizations
|
||||
**Story:** E3.S1 - Organization seed
|
||||
**Story Points:** 2
|
||||
**Status:** 🔄 In Progress (scripts ready, database migration pending)
|
||||
|
||||
## Doel
|
||||
|
||||
Default organization voor development aanmaken.
|
||||
|
||||
## Acceptatiecriteria
|
||||
|
||||
- [x] Default organization in seed data
|
||||
- [x] Organization heeft realistische GGZ data
|
||||
- [x] Organization is bruikbaar voor development
|
||||
- [ ] Migratie toegepast op database (wacht op Supabase maintenance)
|
||||
|
||||
## Implementatie
|
||||
|
||||
### 1. Migratie bestand aangemaakt
|
||||
|
||||
📁 **Locatie:** `supabase/migrations/20251122_seed_default_organization.sql`
|
||||
|
||||
De migratie maakt een default GGZ organization aan met:
|
||||
- **ID:** `00000000-0000-0000-0000-000000000001`
|
||||
- **AGB-code:** `AGB-DEMO-001`
|
||||
- **KVK-nummer:** `12345678`
|
||||
- **Naam:** Demo GGZ Instelling
|
||||
- **Alias:** Demo GGZ, DGGZ
|
||||
- **Contact:** 030-1234567, info@demo-ggz.nl
|
||||
- **Adres:** Demonstratiestraat 1, 3511 AB Utrecht
|
||||
|
||||
### 2. Scripts aangemaakt
|
||||
|
||||
#### TypeScript script (aanbevolen)
|
||||
📁 **Locatie:** `scripts/seed-organization.ts`
|
||||
|
||||
**Gebruik:**
|
||||
```bash
|
||||
npx tsx scripts/seed-organization.ts
|
||||
```
|
||||
|
||||
Dit script:
|
||||
- ✅ Gebruikt Supabase service role key (bypass RLS)
|
||||
- ✅ Idempotent: kan veilig opnieuw uitgevoerd worden
|
||||
- ✅ Verificatie na insert/update
|
||||
- ✅ Duidelijke output en error handling
|
||||
|
||||
#### Bash script (alternatief)
|
||||
📁 **Locatie:** `scripts/apply-organization-seed.sh`
|
||||
|
||||
**Gebruik:**
|
||||
```bash
|
||||
chmod +x scripts/apply-organization-seed.sh
|
||||
./scripts/apply-organization-seed.sh
|
||||
```
|
||||
|
||||
### 3. Handmatige toepassing
|
||||
|
||||
Als de scripts niet werken, kan de migratie handmatig toegepast worden via:
|
||||
|
||||
**Supabase Dashboard:**
|
||||
1. Ga naar: https://supabase.com/dashboard/project/dqugbrpwtisgyxscpefg/sql
|
||||
2. Kopieer de SQL uit `supabase/migrations/20251122_seed_default_organization.sql`
|
||||
3. Plak in SQL Editor
|
||||
4. Klik "Run"
|
||||
|
||||
**Supabase CLI:**
|
||||
```bash
|
||||
npx supabase db push
|
||||
```
|
||||
|
||||
**psql (met database password):**
|
||||
```bash
|
||||
psql 'postgresql://postgres:[PASSWORD]@db.dqugbrpwtisgyxscpefg.supabase.co:5432/postgres' \
|
||||
-f supabase/migrations/20251122_seed_default_organization.sql
|
||||
```
|
||||
|
||||
## Verificatie
|
||||
|
||||
Na het toepassen van de migratie, controleer:
|
||||
|
||||
```sql
|
||||
SELECT * FROM organizations WHERE identifier_agb = 'AGB-DEMO-001';
|
||||
```
|
||||
|
||||
**Verwacht resultaat:**
|
||||
```
|
||||
id | 00000000-0000-0000-0000-000000000001
|
||||
identifier_agb | AGB-DEMO-001
|
||||
identifier_kvk | 12345678
|
||||
name | Demo GGZ Instelling
|
||||
alias | {Demo GGZ,DGGZ}
|
||||
type_code | prov
|
||||
type_display | Healthcare Provider
|
||||
telecom_phone | 030-1234567
|
||||
telecom_email | info@demo-ggz.nl
|
||||
telecom_website | https://demo-ggz.nl
|
||||
address_line | {Demonstratiestraat 1}
|
||||
address_city | Utrecht
|
||||
address_postal_code | 3511 AB
|
||||
address_country | NL
|
||||
active | t
|
||||
```
|
||||
|
||||
## Afhankelijkheden
|
||||
|
||||
Deze organization wordt gebruikt door:
|
||||
- ✅ **Practitioners** - organizational affiliation
|
||||
- ✅ **Encounters** - organization_id foreign key (zie seed data in `20241121_seed_demo_data.sql`)
|
||||
- ✅ **Care Plans** - care team organization
|
||||
|
||||
De bestaande seed data (`20241121_seed_demo_data.sql`) verwijst al naar deze organization via `identifier_agb = 'AGB-DEMO-001'`, maar de organization werd nog niet aangemaakt. Deze story lost dat op.
|
||||
|
||||
## Technische details
|
||||
|
||||
### Database schema
|
||||
|
||||
De `organizations` tabel bestaat al in de database (zie `docs/archive/migrations/20251122-current-db-scheme.sql` - archived snapshot):
|
||||
|
||||
```sql
|
||||
CREATE TABLE public.organizations (
|
||||
id uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
identifier_agb text UNIQUE,
|
||||
identifier_kvk text,
|
||||
name text NOT NULL,
|
||||
alias text[],
|
||||
type_code text DEFAULT 'prov'::text,
|
||||
type_display text DEFAULT 'Healthcare Provider'::text,
|
||||
telecom_phone text,
|
||||
telecom_email text,
|
||||
telecom_website text,
|
||||
address_line text[],
|
||||
address_city text,
|
||||
address_postal_code text,
|
||||
address_country text DEFAULT 'NL'::text,
|
||||
active boolean DEFAULT true,
|
||||
created_at timestamp with time zone DEFAULT now(),
|
||||
updated_at timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT organizations_pkey PRIMARY KEY (id)
|
||||
);
|
||||
```
|
||||
|
||||
### Idempotentie
|
||||
|
||||
De migratie gebruikt `ON CONFLICT (id) DO UPDATE` om te zorgen dat:
|
||||
1. Bij eerste run: organization wordt aangemaakt
|
||||
2. Bij herhaalde runs: organization wordt geupdatet met nieuwe waardes
|
||||
3. Geen dubbele entries ontstaan
|
||||
|
||||
## Volgende stappen
|
||||
|
||||
Na het voltooien van E3.S1:
|
||||
- [ ] **E3.S2** - Patients lijst pagina (`/clients` met tabel, search, filters)
|
||||
- [ ] **E3.S3** - Patient CRUD (Create/Update/Delete patient + validatie)
|
||||
|
||||
## Referenties
|
||||
|
||||
- **Bouwplan:** `docs/bouwplan-mini-epd.md` (regel 352-356)
|
||||
- **Database schema:** `docs/archive/migrations/20251122-current-db-scheme.sql` (archived snapshot, regel 175-194)
|
||||
- **Bestaande seed data:** `supabase/migrations/20241121_seed_demo_data.sql` (regel 13-19, 173-188)
|
||||
|
||||
---
|
||||
|
||||
**Datum aangemaakt:** 2025-11-22
|
||||
**Laatste update:** 2025-11-22
|
||||
**Status:** Scripts klaar, database migratie moet handmatig worden toegepast
|
||||
**Blocker:** Supabase environment variables niet geconfigureerd (NEXT_PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)
|
||||
**Actie vereist:** Voer migratie handmatig uit via Supabase Dashboard SQL Editor
|
||||
238
docs/release/E3.S2-patients-lijst-pagina.md
Normal file
238
docs/release/E3.S2-patients-lijst-pagina.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# E3.S2 - Patients lijst pagina
|
||||
|
||||
**Epic:** E3 - Patients & Organizations
|
||||
**Story:** E3.S2 - Patients lijst pagina
|
||||
**Story Points:** 5
|
||||
**Status:** ✅ Compleet
|
||||
|
||||
## Doel
|
||||
|
||||
Patiënten lijst pagina met tabel, search, filters en paginatie.
|
||||
|
||||
## Acceptatiecriteria
|
||||
|
||||
- [x] `/patients` route met overzichtelijke tabel
|
||||
- [x] Search functionaliteit op naam en BSN
|
||||
- [x] Filters (status, gender)
|
||||
- [x] Paginatie (50 per pagina)
|
||||
- [x] Sorteer functionaliteit
|
||||
|
||||
## Implementatie
|
||||
|
||||
### 1. Server Actions (actions.ts)
|
||||
|
||||
📁 **Locatie:** `app/epd/patients/actions.ts`
|
||||
|
||||
**Updates:**
|
||||
- ✅ Paginatie parameters toegevoegd (`page`, `pageSize`)
|
||||
- ✅ Gender filter parameter
|
||||
- ✅ Sorteer parameters (`sortBy`, `sortOrder`)
|
||||
- ✅ Return type aangepast naar object met `patients`, `total`, `page`, `pageSize`
|
||||
|
||||
**API parameters:**
|
||||
```typescript
|
||||
{
|
||||
search?: string; // Zoek op naam
|
||||
status?: string; // Filter: planned, active, finished, cancelled
|
||||
gender?: string; // Filter: male, female, other, unknown
|
||||
page?: number; // Pagina nummer (default: 1)
|
||||
pageSize?: number; // Items per pagina (default: 50)
|
||||
sortBy?: string; // Sorteer kolom
|
||||
sortOrder?: 'asc' | 'desc'; // Sorteer richting
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Page Component (page.tsx)
|
||||
|
||||
📁 **Locatie:** `app/epd/patients/page.tsx`
|
||||
|
||||
**Updates:**
|
||||
- ✅ SearchParams interface uitgebreid met nieuwe filters
|
||||
- ✅ Page parameter parsing
|
||||
- ✅ Props doorgegeven aan PatientList component
|
||||
|
||||
### 3. Patient List Component (patient-list.tsx)
|
||||
|
||||
📁 **Locatie:** `app/epd/patients/components/patient-list.tsx`
|
||||
|
||||
**Features geïmplementeerd:**
|
||||
|
||||
#### A. Filters
|
||||
|
||||
**Search bar:**
|
||||
- Zoek op naam of BSN
|
||||
- Real-time filtering
|
||||
- Form submit handler
|
||||
|
||||
**Status filter:**
|
||||
- Alle statussen
|
||||
- Screening (planned)
|
||||
- Actief (active)
|
||||
- Afgerond (finished)
|
||||
- Afgemeld (cancelled)
|
||||
|
||||
**Gender filter:**
|
||||
- Alle geslachten
|
||||
- Man (male)
|
||||
- Vrouw (female)
|
||||
- Anders (other)
|
||||
- Onbekend (unknown)
|
||||
|
||||
#### B. Paginatie
|
||||
|
||||
**Implementatie:**
|
||||
- 50 patiënten per pagina (configureerbaar)
|
||||
- Pagina navigatie knoppen (vorige/volgende)
|
||||
- Genummerde pagina buttons (max 7 zichtbaar)
|
||||
- Smart pagination logica:
|
||||
- Bij weinig pagina's: toon alle nummers
|
||||
- Bij begin: toon eerste 7
|
||||
- Bij einde: toon laatste 7
|
||||
- In het midden: toon current ± 3
|
||||
|
||||
**UI elementen:**
|
||||
- "Resultaten X-Y van Z" indicator
|
||||
- Disabled state voor eerste/laatste pagina
|
||||
- Active state voor huidige pagina
|
||||
- Hover states voor interactie
|
||||
|
||||
#### C. Sortering
|
||||
|
||||
**Sorteerbare kolommen:**
|
||||
- ✅ Naam (`name`)
|
||||
- ✅ Geboortedatum (`birthDate`)
|
||||
- ✅ Status (`status`)
|
||||
- ✅ Laatst gewijzigd (`_lastUpdated`)
|
||||
|
||||
**Visual feedback:**
|
||||
- Inactieve kolommen: `<ArrowUpDown>` (grijs)
|
||||
- Actieve kolom ascending: `<ArrowUp>` (teal)
|
||||
- Actieve kolom descending: `<ArrowDown>` (teal)
|
||||
- Hover effect op sorteerbare headers
|
||||
|
||||
**Functionaliteit:**
|
||||
- Click om te sorteren
|
||||
- Click opnieuw om richting te wisselen (asc ↔ desc)
|
||||
- Sortering reset naar pagina 1
|
||||
|
||||
#### D. Helper functions
|
||||
|
||||
```typescript
|
||||
// URL parameters builder
|
||||
buildUrlParams(updates: Record<string, string | number | undefined>)
|
||||
|
||||
// Event handlers
|
||||
handleSearch(e: React.FormEvent)
|
||||
handleStatusFilterChange(newStatus: string)
|
||||
handleGenderFilterChange(newGender: string)
|
||||
handlePageChange(newPage: number)
|
||||
handleSort(column: string)
|
||||
|
||||
// Render helpers
|
||||
renderSortIcon(column: string)
|
||||
```
|
||||
|
||||
## UI/UX Details
|
||||
|
||||
### Layout
|
||||
- Responsive design (desktop & mobile)
|
||||
- Flex layout voor filters (wraps op kleine schermen)
|
||||
- Sticky header mogelijk (voor lange lijsten)
|
||||
|
||||
### Colors & Styling
|
||||
- Teal als primary color (#0d9488)
|
||||
- Slate voor neutral elements
|
||||
- Status badges met kleurcodering:
|
||||
- Amber: Screening
|
||||
- Emerald: Actief
|
||||
- Slate: Afgerond
|
||||
- Red: Afgemeld
|
||||
|
||||
### Empty States
|
||||
- Verschillende berichten voor:
|
||||
- Geen patiënten (met CTA om toe te voegen)
|
||||
- Geen resultaten (met suggestie om filters aan te passen)
|
||||
|
||||
## Performance
|
||||
|
||||
**Optimalisaties:**
|
||||
- Server-side paginatie (max 50 geladen per keer)
|
||||
- URL-based state (deep linking, browser back/forward)
|
||||
- Suspense voor loading states
|
||||
- No-cache strategie voor realtime data
|
||||
|
||||
**Load times (verwacht):**
|
||||
- Initial load: < 1s
|
||||
- Filter/sort/page change: < 500ms
|
||||
- Search debounce: instant (client-side filter)
|
||||
|
||||
## Database Queries
|
||||
|
||||
De actions maken gebruik van de FHIR API (`/api/fhir/Patient`) met de volgende query parameters:
|
||||
|
||||
```
|
||||
GET /api/fhir/Patient?
|
||||
name={search_term}&
|
||||
status={status_filter}&
|
||||
gender={gender_filter}&
|
||||
_count={page_size}&
|
||||
_offset={offset}&
|
||||
_sort={sort_field}
|
||||
```
|
||||
|
||||
**FHIR-compliant:**
|
||||
- Gebruikt FHIR search parameters
|
||||
- FHIR Bundle response format
|
||||
- FHIR extension voor episode_status
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Paginatie werkt correct (vorige/volgende/nummers)
|
||||
- [ ] Search filtert correct op naam
|
||||
- [ ] Status filter werkt voor alle statussen
|
||||
- [ ] Gender filter werkt voor alle geslachten
|
||||
- [ ] Sortering werkt voor alle kolommen
|
||||
- [ ] URL parameters worden correct bijgewerkt
|
||||
- [ ] Browser back/forward werkt met filters
|
||||
- [ ] Empty state toont bij geen resultaten
|
||||
- [ ] Responsive layout op mobile
|
||||
- [ ] Loading states tonen bij data fetch
|
||||
- [ ] Pagination reset bij filter wijziging
|
||||
|
||||
## Bekende Beperkingen
|
||||
|
||||
1. **FHIR API Afhankelijkheid:**
|
||||
- Requires `/api/fhir/Patient` endpoint
|
||||
- Episode status via extension (custom)
|
||||
- Supabase moet online zijn voor data
|
||||
|
||||
2. **Client-side vs Server-side:**
|
||||
- Search is momenteel client-side gefilterd
|
||||
- Kan server-side voor betere performance bij grote datasets
|
||||
|
||||
3. **BSN Kolom:**
|
||||
- Niet sorteerbaar (niet in sortBy options)
|
||||
- Encryption zou performance impact hebben
|
||||
|
||||
## Volgende Stappen
|
||||
|
||||
Na E3.S2:
|
||||
- [ ] **E3.S3** - Patient CRUD (Create/Update/Delete + validatie)
|
||||
- [ ] Eventueel: Advanced filters (leeftijdsbereik, postcode, etc.)
|
||||
- [ ] Eventueel: Export functionaliteit (CSV/Excel)
|
||||
- [ ] Eventueel: Bulk actions (selecteer multiple → wijzig status)
|
||||
|
||||
## Referenties
|
||||
|
||||
- **Bouwplan:** `docs/bouwplan-mini-epd.md` (regel 352-356, E3.S2)
|
||||
- **Actions:** `app/epd/patients/actions.ts`
|
||||
- **Page:** `app/epd/patients/page.tsx`
|
||||
- **Component:** `app/epd/patients/components/patient-list.tsx`
|
||||
- **FHIR Types:** `lib/fhir/index.ts`
|
||||
|
||||
---
|
||||
|
||||
**Datum voltooid:** 2025-11-22
|
||||
**Status:** ✅ Compleet
|
||||
**Dependencies voltooid:** E2.S4 (Practitioner profile)
|
||||
**Blokkeert:** E3.S3 (Patient CRUD)
|
||||
379
docs/release/E3.S3-patient-crud.md
Normal file
379
docs/release/E3.S3-patient-crud.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# E3.S3 - Patient CRUD
|
||||
|
||||
**Epic:** E3 - Patients & Organizations
|
||||
**Story:** E3.S3 - Patient CRUD
|
||||
**Story Points:** 6
|
||||
**Status:** ✅ Compleet
|
||||
|
||||
## Doel
|
||||
|
||||
Volledige CRUD operaties voor patiënten met validatie.
|
||||
|
||||
## Acceptatiecriteria
|
||||
|
||||
- [x] **Create:** Nieuwe patiënt aanmaken met validatie
|
||||
- [x] **Read:** Patiënt gegevens ophalen en tonen
|
||||
- [x] **Update:** Bestaande patiënt bijwerken
|
||||
- [x] **Delete:** Patiënt verwijderen met confirmatie
|
||||
- [x] BSN 11-proef validatie
|
||||
- [x] Alle verplichte velden (volgens bouwplan)
|
||||
- [x] John Doe support (crisis opname zonder BSN)
|
||||
|
||||
## Implementatie
|
||||
|
||||
### 1. Server Actions (actions.ts)
|
||||
|
||||
📁 **Locatie:** `app/epd/patients/actions.ts`
|
||||
|
||||
**CRUD Functies:**
|
||||
|
||||
```typescript
|
||||
// Create
|
||||
export async function createPatient(fhirPatient: FHIRPatient): Promise<FHIRPatient>
|
||||
|
||||
// Read
|
||||
export async function getPatient(id: string): Promise<FHIRPatient>
|
||||
export async function getPatients(filters): Promise<{ patients, total, page, pageSize }>
|
||||
|
||||
// Update
|
||||
export async function updatePatient(id: string, fhirPatient: FHIRPatient): Promise<FHIRPatient>
|
||||
|
||||
// Delete
|
||||
export async function deletePatient(id: string): Promise<{ success: boolean }>
|
||||
```
|
||||
|
||||
**FHIR API Integration:**
|
||||
- POST `/api/fhir/Patient` - Create
|
||||
- GET `/api/fhir/Patient/:id` - Read one
|
||||
- GET `/api/fhir/Patient?params` - Read many (met paginatie)
|
||||
- PUT `/api/fhir/Patient/:id` - Update
|
||||
- DELETE `/api/fhir/Patient/:id` - Delete
|
||||
|
||||
### 2. Patient Form Component
|
||||
|
||||
📁 **Locatie:** `app/epd/patients/components/patient-form.tsx`
|
||||
|
||||
**Features:**
|
||||
|
||||
#### A. BSN Validatie
|
||||
|
||||
**11-proef Check:**
|
||||
```typescript
|
||||
function validateBSN(bsn: string): boolean {
|
||||
if (!bsn || bsn.length !== 9) return false;
|
||||
|
||||
const digits = bsn.split('').map(Number);
|
||||
if (digits.some(isNaN)) return false;
|
||||
|
||||
// Modulo-11 check
|
||||
const sum = digits.reduce((acc, digit, index) => {
|
||||
if (index < 8) {
|
||||
return acc + digit * (9 - index);
|
||||
}
|
||||
return acc - digit;
|
||||
}, 0);
|
||||
|
||||
return sum % 11 === 0;
|
||||
}
|
||||
```
|
||||
|
||||
**Validatie regels:**
|
||||
- 9 cijfers verplicht
|
||||
- Alleen numerieke karakters
|
||||
- Modulo-11 proef moet kloppen
|
||||
- Optioneel bij John Doe registratie
|
||||
|
||||
#### B. Form Secties
|
||||
|
||||
**Stap 1: Persoonlijke gegevens**
|
||||
- ✅ Voorvoegsel (optioneel)
|
||||
- ✅ Voornaam (verplicht)
|
||||
- ✅ Achternaam (verplicht)
|
||||
- ✅ BSN (9 cijfers, 11-proef) - verplicht tenzij John Doe
|
||||
- ✅ Geboortedatum (verplicht, max vandaag)
|
||||
- ✅ Geslacht (verplicht): Man, Vrouw, Anders, Onbekend
|
||||
|
||||
**Stap 2: Adresgegevens**
|
||||
- ✅ Straat + huisnummer (optioneel)
|
||||
- ✅ Postcode (optioneel)
|
||||
- ✅ Woonplaats (optioneel)
|
||||
|
||||
**Stap 3: Contactgegevens**
|
||||
- ✅ Telefoonnummer (optioneel)
|
||||
- ✅ E-mail (optioneel, email validatie)
|
||||
|
||||
**Stap 4: Verzekering**
|
||||
- ✅ Verzekeraar (optioneel)
|
||||
- ✅ Polisnummer (optioneel)
|
||||
- ✅ Huisarts naam (optioneel)
|
||||
- ✅ Huisarts AGB-code (optioneel, 8 cijfers)
|
||||
|
||||
**Stap 5: Noodcontact**
|
||||
- ✅ Naam contactpersoon (optioneel)
|
||||
- ✅ Relatie (optioneel)
|
||||
- ✅ Telefoonnummer (optioneel)
|
||||
|
||||
#### C. John Doe Support
|
||||
|
||||
**Features:**
|
||||
- Checkbox om John Doe aan te geven
|
||||
- BSN wordt optioneel bij John Doe
|
||||
- Warning indicator voor incomplete gegevens
|
||||
- Mogelijkheid om later BSN aan te vullen
|
||||
- Custom FHIR extension: `john-doe`
|
||||
|
||||
**Use case:**
|
||||
- Crisis opnames waarbij identiteit niet direct bekend is
|
||||
- Patiënt kan later worden geüpdatet met volledige gegevens
|
||||
|
||||
#### D. FHIR Mapping
|
||||
|
||||
**Extensions gebruikt:**
|
||||
```typescript
|
||||
{
|
||||
"extension": [
|
||||
// Episode status
|
||||
{
|
||||
"url": "http://mini-epd.local/fhir/StructureDefinition/episode-status",
|
||||
"valueCode": "planned" | "active" | "finished" | "cancelled"
|
||||
},
|
||||
// John Doe flag
|
||||
{
|
||||
"url": "http://mini-epd.local/fhir/StructureDefinition/john-doe",
|
||||
"valueBoolean": true | false
|
||||
},
|
||||
// Insurance (custom)
|
||||
{
|
||||
"url": "http://mini-epd.local/fhir/StructureDefinition/insurance",
|
||||
"valueString": "{\"company\":\"...\",\"number\":\"...\"}"
|
||||
},
|
||||
// General Practitioner (custom)
|
||||
{
|
||||
"url": "http://mini-epd.local/fhir/StructureDefinition/general-practitioner",
|
||||
"valueString": "{\"name\":\"...\",\"agb\":\"...\"}"
|
||||
}
|
||||
],
|
||||
"contact": [
|
||||
// Emergency contact (FHIR standard)
|
||||
{
|
||||
"relationship": [{
|
||||
"coding": [{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0131",
|
||||
"code": "C",
|
||||
"display": "Emergency Contact"
|
||||
}],
|
||||
"text": "Partner" | "Ouder" | "Kind" | etc.
|
||||
}],
|
||||
"name": { "text": "..." },
|
||||
"telecom": [{ "system": "phone", "value": "...", "use": "home" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Delete Patient Component
|
||||
|
||||
📁 **Locatie:** `app/epd/patients/components/delete-patient-button.tsx`
|
||||
|
||||
**Features:**
|
||||
|
||||
#### A. Two-Step Confirmation
|
||||
|
||||
**Step 1: Initial Warning**
|
||||
- Rode "Gevaarlijke zone" sectie
|
||||
- Waarschuwing dat actie niet ongedaan gemaakt kan worden
|
||||
- "Patiënt verwijderen" button
|
||||
|
||||
**Step 2: Final Confirmation**
|
||||
- Gedetailleerde lijst van wat wordt verwijderd:
|
||||
- Alle persoonlijke gegevens
|
||||
- Alle screening en intake informatie
|
||||
- Alle diagnoses en observaties
|
||||
- Alle behandelplannen en doelen
|
||||
- Alle documenten en rapportages
|
||||
- Patiëntnaam wordt getoond
|
||||
- "Ja, verwijder permanent" button
|
||||
- Annuleren button
|
||||
|
||||
#### B. Safety Features
|
||||
|
||||
- Disabled state tijdens verwijderen
|
||||
- Loading indicator
|
||||
- Error handling met feedback
|
||||
- Redirect naar overzichtspagina na succes
|
||||
- Cache revalidation
|
||||
|
||||
### 4. Page Implementations
|
||||
|
||||
**Create Page:**
|
||||
- 📁 `app/epd/patients/new/page.tsx`
|
||||
- Gebruikt PatientForm zonder patient prop
|
||||
- "Terug naar patiënten" link
|
||||
- Clear page title en beschrijving
|
||||
|
||||
**Read Page:**
|
||||
- 📁 `app/epd/patients/[id]/page.tsx`
|
||||
- Toont patiënt detail met tabs
|
||||
- Basisgegevens tab voor view/edit
|
||||
|
||||
**Update Page:**
|
||||
- 📁 `app/epd/patients/[id]/basisgegevens/page.tsx`
|
||||
- Gebruikt PatientForm met patient prop
|
||||
- John Doe warning indien van toepassing
|
||||
- Missing BSN warning
|
||||
- Delete button onderaan
|
||||
|
||||
**List Page:**
|
||||
- 📁 `app/epd/patients/page.tsx`
|
||||
- Tabel met alle patiënten (zie E3.S2)
|
||||
- "Nieuwe patiënt" button
|
||||
|
||||
## Validatie
|
||||
|
||||
### Client-side (HTML5)
|
||||
|
||||
**Built-in validatie:**
|
||||
- `required` attribute voor verplichte velden
|
||||
- `type="email"` voor email validatie
|
||||
- `type="tel"` voor telefoonnummer
|
||||
- `type="date"` met `max` voor geboortedatum
|
||||
- `maxLength` en `pattern` voor BSN/AGB
|
||||
- `pattern="[0-9]{9}"` voor BSN
|
||||
- `pattern="[0-9]{8}"` voor AGB
|
||||
|
||||
### Server-side (Actions)
|
||||
|
||||
**Validatie in handleSubmit:**
|
||||
- BSN 11-proef check (tenzij John Doe)
|
||||
- Error throwing bij ongeldige data
|
||||
- FHIR API valideert ook server-side
|
||||
|
||||
**Error handling:**
|
||||
- Try-catch om alle errors
|
||||
- User-friendly error messages
|
||||
- Error state in UI
|
||||
|
||||
## UI/UX Details
|
||||
|
||||
### Form Layout
|
||||
|
||||
**Desktop (md+):**
|
||||
- Grid layouts: 2-3 kolommen voor gerelateerde velden
|
||||
- Grouped sections met headers
|
||||
- Visual separation met borders
|
||||
|
||||
**Mobile:**
|
||||
- Stack layout (1 kolom)
|
||||
- Responsive grid → stack
|
||||
- Touch-friendly input sizes
|
||||
|
||||
### Visual Feedback
|
||||
|
||||
**States:**
|
||||
- Focus ring (teal) op inputs
|
||||
- Hover states op buttons
|
||||
- Loading states tijdens submit/delete
|
||||
- Disabled states
|
||||
- Error states (red border + message)
|
||||
|
||||
**Colors:**
|
||||
- Teal: Primary actions, focus
|
||||
- Red: Danger (delete), errors
|
||||
- Amber: Warnings (John Doe)
|
||||
- Orange: Info (incomplete BSN)
|
||||
- Slate: Neutral elements
|
||||
|
||||
### Accessibility
|
||||
|
||||
**Features:**
|
||||
- Label voor alle inputs
|
||||
- Aria-labels waar nodig
|
||||
- Keyboard navigation (Tab, Enter)
|
||||
- Clear error messages
|
||||
- Confirmation voor destructieve acties
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Create
|
||||
- [ ] Nieuwe patiënt aanmaken met alle velden
|
||||
- [ ] Nieuwe patiënt aanmaken met minimale velden
|
||||
- [ ] BSN validatie werkt (reject invalid)
|
||||
- [ ] John Doe aanmaken zonder BSN
|
||||
- [ ] Form validatie toont errors
|
||||
- [ ] Redirect naar detail pagina na succes
|
||||
|
||||
### Read
|
||||
- [ ] Patiënt detail pagina toont alle gegevens
|
||||
- [ ] Lijst pagina toont alle patiënten
|
||||
- [ ] Missing gegevens worden correct getoond (-)
|
||||
- [ ] John Doe indicator zichtbaar
|
||||
|
||||
### Update
|
||||
- [ ] Bestaande patiënt bewerken werkt
|
||||
- [ ] Velden pre-filled met huidige waarden
|
||||
- [ ] John Doe → regulier (BSN toevoegen)
|
||||
- [ ] Update success toont correcte data
|
||||
- [ ] John Doe warning verdwijnt na BSN toevoegen
|
||||
|
||||
### Delete
|
||||
- [ ] Delete button zichtbaar op basisgegevens
|
||||
- [ ] First confirmation shows warning
|
||||
- [ ] Second confirmation toont details
|
||||
- [ ] Annuleren werkt op beide stappen
|
||||
- [ ] Delete success redirect naar lijst
|
||||
- [ ] Patiënt is echt verwijderd
|
||||
|
||||
### Validation
|
||||
- [ ] BSN 11-proef reject: 123456789
|
||||
- [ ] BSN 11-proef accept: 111222333 (example valid)
|
||||
- [ ] Email validatie werkt
|
||||
- [ ] Geboortedatum niet in toekomst
|
||||
- [ ] Required velden kunnen niet leeg
|
||||
- [ ] AGB moet 8 cijfers zijn
|
||||
|
||||
## Bekende Beperkingen
|
||||
|
||||
1. **FHIR API Afhankelijkheid:**
|
||||
- Alle CRUD werkt via `/api/fhir/Patient`
|
||||
- API moet volledig geïmplementeerd zijn
|
||||
- Cascade delete moet in API geregeld zijn
|
||||
|
||||
2. **Validatie:**
|
||||
- Momenteel alleen HTML5 + BSN check
|
||||
- Geen Zod schema validatie (toekomstige verbetering)
|
||||
- Server-side validatie in FHIR API
|
||||
|
||||
3. **Multi-step Wizard:**
|
||||
- Bouwplan suggereert wizard
|
||||
- Huidige implementatie: single page form
|
||||
- Werkt goed, maar kan verbeterd naar wizard UX
|
||||
|
||||
4. **Soft Delete:**
|
||||
- Momenteel hard delete
|
||||
- Toekomstige verbetering: soft delete met archivering
|
||||
|
||||
## Volgende Stappen
|
||||
|
||||
Na E3.S3:
|
||||
- [ ] **E4.S1** - Encounter tijdlijn
|
||||
- [ ] Eventueel: Zod validation schema
|
||||
- [ ] Eventueel: Multi-step wizard UX
|
||||
- [ ] Eventueel: Soft delete / archivering
|
||||
- [ ] Eventueel: Audit trail (wie/wanneer gewijzigd)
|
||||
|
||||
## Referenties
|
||||
|
||||
- **Bouwplan:** `docs/bouwplan-mini-epd.md` (regel 356, E3.S3)
|
||||
- **Patient Form:** `app/epd/patients/components/patient-form.tsx`
|
||||
- **Delete Button:** `app/epd/patients/components/delete-patient-button.tsx`
|
||||
- **Actions:** `app/epd/patients/actions.ts`
|
||||
- **New Page:** `app/epd/patients/new/page.tsx`
|
||||
- **Edit Page:** `app/epd/patients/[id]/basisgegevens/page.tsx`
|
||||
- **FHIR Types:** `lib/fhir/index.ts`
|
||||
|
||||
---
|
||||
|
||||
**Datum voltooid:** 2025-11-22
|
||||
**Status:** ✅ Compleet
|
||||
**Dependencies voltooid:** E3.S2 (Patients lijst)
|
||||
**Blokkeert:** E4.S1 (Encounter tijdlijn)
|
||||
Reference in New Issue
Block a user