# 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)
