'use client'; /** * Patient Form Component * E2.S2: Nieuwe Cliƫnt Flow met John Doe logica */ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { Save, Loader2, AlertCircle } from 'lucide-react'; import { createPatient, updatePatient } from '../actions'; import type { FHIRPatient } from '@/lib/fhir'; interface PatientFormProps { patient?: FHIRPatient; } // BSN validation (Modulo-11 check) 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; } export function PatientForm({ patient }: PatientFormProps) { const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [isJohnDoe, setIsJohnDoe] = useState( patient?.extension?.find( (ext) => ext.url === 'http://mini-epd.local/fhir/StructureDefinition/john-doe' )?.valueBoolean || false ); const existingName = patient?.name?.[0]; const existingBsn = patient?.identifier?.find( (id) => id.system === 'http://fhir.nl/fhir/NamingSystem/bsn' )?.value; const existingPhone = patient?.telecom?.find((t) => t.system === 'phone')?.value; const existingEmail = patient?.telecom?.find((t) => t.system === 'email')?.value; const existingAddress = patient?.address?.[0]; // Extract insurance data from extension const insuranceExtension = patient?.extension?.find( (ext) => ext.url === 'http://mini-epd.local/fhir/StructureDefinition/insurance' ); let existingInsurance: { company?: string; number?: string } = {}; if (insuranceExtension?.valueString) { try { existingInsurance = JSON.parse(insuranceExtension.valueString); } catch (e) { console.error('Failed to parse insurance extension:', e); } } // Extract GP (huisarts) data from extension const gpExtension = patient?.extension?.find( (ext) => ext.url === 'http://mini-epd.local/fhir/StructureDefinition/general-practitioner' ); let existingGP: { name?: string; agb?: string } = {}; if (gpExtension?.valueString) { try { existingGP = JSON.parse(gpExtension.valueString); } catch (e) { console.error('Failed to parse GP extension:', e); } } // Extract emergency contact data from contact field const emergencyContact = patient?.contact?.find( (c) => c.relationship?.some(r => r.coding?.some(code => code.code === 'C')) ); const emergencyName = emergencyContact?.name?.text || ''; const emergencyRelationship = emergencyContact?.relationship?.[0]?.text || ''; const emergencyPhone = emergencyContact?.telecom?.find(t => t.system === 'phone')?.value || ''; async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setIsSubmitting(true); setError(null); try { const formData = new FormData(e.currentTarget); const bsnValue = formData.get('bsn') as string; // Validate BSN if not John Doe if (!isJohnDoe && bsnValue) { if (!validateBSN(bsnValue)) { throw new Error('Ongeldig BSN nummer. Controleer het nummer en probeer opnieuw.'); } } // Build FHIR Patient resource const fhirPatient: FHIRPatient = { resourceType: 'Patient', identifier: bsnValue ? [ { system: 'http://fhir.nl/fhir/NamingSystem/bsn', value: bsnValue, use: 'official' as const, }, ] : [], name: [ { use: 'official' as const, family: formData.get('family') as string, given: [formData.get('given') as string].filter(Boolean), prefix: formData.get('prefix') ? [(formData.get('prefix') as string)] : undefined, }, ], gender: formData.get('gender') as 'male' | 'female' | 'other' | 'unknown', birthDate: formData.get('birthDate') as string, // Address address: formData.get('addressLine') || formData.get('city') || formData.get('postalCode') ? [ { use: 'home', line: formData.get('addressLine') ? [formData.get('addressLine') as string] : undefined, city: formData.get('city') as string || undefined, postalCode: formData.get('postalCode') as string || undefined, country: 'NL', }, ] : undefined, // Contact telecom: [ formData.get('phone') ? { system: 'phone' as const, value: formData.get('phone') as string, use: 'mobile' as const, } : undefined, formData.get('email') ? { system: 'email' as const, value: formData.get('email') as string, } : undefined, ].filter((t): t is NonNullable => t !== undefined), active: true, // Extensions for status and john_doe extension: [ { url: 'http://mini-epd.local/fhir/StructureDefinition/episode-status', valueCode: 'planned', // Always set to planned for new patients }, isJohnDoe ? { url: 'http://mini-epd.local/fhir/StructureDefinition/john-doe', valueBoolean: true, } : undefined, // Insurance extension (custom) formData.get('insuranceCompany') ? { url: 'http://mini-epd.local/fhir/StructureDefinition/insurance', valueString: JSON.stringify({ company: formData.get('insuranceCompany'), number: formData.get('insuranceNumber'), }), } : undefined, // GP (huisarts) extension formData.get('gpName') ? { url: 'http://mini-epd.local/fhir/StructureDefinition/general-practitioner', valueString: JSON.stringify({ name: formData.get('gpName'), agb: formData.get('gpAgb'), }), } : undefined, ].filter((ext): ext is NonNullable => ext !== undefined), // Emergency contact (FHIR contact field) contact: formData.get('emergencyName') ? [ { relationship: [ { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/v2-0131', code: 'C', display: 'Emergency Contact', }, ], text: formData.get('emergencyRelationship') as string || 'Noodcontact', }, ], name: { text: formData.get('emergencyName') as string, }, telecom: formData.get('emergencyPhone') ? [ { system: 'phone' as const, value: formData.get('emergencyPhone') as string, use: 'home' as const, }, ] : undefined, }, ] : undefined, }; let createdPatient: FHIRPatient; if (patient?.id) { // Update existing patient createdPatient = await updatePatient(patient.id, fhirPatient); } else { // Create new patient createdPatient = await createPatient(fhirPatient); } // Redirect to patient detail page if (createdPatient.id) { router.push(`/epd/patients/${createdPatient.id}`); } else { router.push('/epd/patients'); } router.refresh(); } catch (err) { setError(err instanceof Error ? err.message : 'Er is een fout opgetreden'); setIsSubmitting(false); } } return (
{error && (

{error}

)} {/* John Doe Checkbox */}
{/* John Doe Warning */} {isJohnDoe && (

John Doe registratie

Gegevens kunnen later worden aangevuld. Vul BSN aan zodra deze beschikbaar is.

)} {/* Name Fields */}
{/* BSN and Birth Date */}

9 cijfers, inclusief modulo-11 check

{/* Gender */}
{/* Address Fields */}

Adresgegevens

{/* Contact Information */}

Contactgegevens

{/* Insurance Information */}

Verzekering

{/* General Practitioner (Huisarts) */}

Huisarts

8 cijfers

{/* Emergency Contact */}

Noodcontact

{/* Action Buttons */}
); }