380 lines
9.7 KiB
Markdown
380 lines
9.7 KiB
Markdown
# 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)
|