'use client'; import { useEffect, useMemo, useState } from 'react'; import { Button } from '@/components/ui/common/Button'; import { ToastStack } from '@/components/ui/common/Toast'; import { patientsApi } from '@/lib/api/patients'; import { formatApiErrorMessage } from '@/lib/formatApiError'; import { useAuth } from '@/lib/hooks/useAuth'; import { useToast } from '@/lib/hooks/useToast'; import { hasPermission } from '@/shared/permissions'; import { CreatePatientInput, CreateTreatmentHistoryInput, Patient, TreatmentHistoryItem, } from '@/types/patient'; import { PatientSearchSelect } from '../../../components/ui/patient/PatientSearchSelect'; import { CreatePatientModal } from '../../../components/ui/patient/CreatePatientModal'; import { PatientSummaryCard } from '../../../components/ui/patient/PatientSummaryCard'; import { TreatmentHistoryPreview } from '../../../components/ui/patient/TreatmentHistoryPreview'; const EMPTY_PATIENT_FORM: CreatePatientInput = { firstName: '', lastName: '', phone: '', email: '', }; export default function PatientsPage() { const { currentOrganization } = useAuth(); const toast = useToast(); const [search, setSearch] = useState(''); const [patients, setPatients] = useState([]); const [selectedPatient, setSelectedPatient] = useState(); const [treatments, setTreatments] = useState([]); const [loadingPatients, setLoadingPatients] = useState(false); const [loadingTreatments, setLoadingTreatments] = useState(false); const [isCreateOpen, setIsCreateOpen] = useState(false); const [savingPatient, setSavingPatient] = useState(false); const [savingTreatment, setSavingTreatment] = useState(false); const [patientForm, setPatientForm] = useState(EMPTY_PATIENT_FORM); const canEditPatients = hasPermission(currentOrganization, 'TAB_PATIENTS_EDIT'); const sortedPatients = useMemo( () => [...patients].sort((a, b) => `${a.firstName} ${a.lastName}`.localeCompare(`${b.firstName} ${b.lastName}`), ), [patients], ); useEffect(() => { const timeout = setTimeout(() => { void loadPatients(search); }, 300); return () => clearTimeout(timeout); }, [search]); useEffect(() => { void loadPatients(''); }, []); async function loadPatients(q: string) { setLoadingPatients(true); toast.setError(''); try { const response = await patientsApi.list({ q, page: 1, limit: 25 }); const items = response.data.items; setPatients(items); if (selectedPatient) { const freshSelected = items.find((item) => item.id === selectedPatient.id); setSelectedPatient(freshSelected); } } catch (error: unknown) { toast.showError(formatApiErrorMessage(error, 'Failed to load patients.')); } finally { setLoadingPatients(false); } } async function loadTreatments(patientId: string) { setLoadingTreatments(true); toast.setError(''); try { const response = await patientsApi.listTreatments(patientId); setTreatments(response.data); } catch (error: unknown) { toast.showError(formatApiErrorMessage(error, 'Failed to load treatment history.')); } finally { setLoadingTreatments(false); } } async function handleCreatePatient() { setSavingPatient(true); toast.setError(''); try { const response = await patientsApi.create(patientForm); setIsCreateOpen(false); setPatientForm(EMPTY_PATIENT_FORM); await loadPatients(search); setSelectedPatient(response.data); await loadTreatments(response.data.id); toast.showSuccess( `Patient ${response.data.firstName} ${response.data.lastName} was saved successfully.`, ); } catch (error: unknown) { toast.showError(formatApiErrorMessage(error, 'Failed to save patient.')); } finally { setSavingPatient(false); } } async function handleQuickAddTreatment() { if (!selectedPatient) { return; } const payload: CreateTreatmentHistoryInput = { title: 'Initial consultation', status: 'scheduled', treatmentAt: new Date().toISOString(), notes: 'Created from quick action on patients page.', }; setSavingTreatment(true); toast.setError(''); try { await patientsApi.addTreatment(selectedPatient.id, payload); await loadTreatments(selectedPatient.id); toast.showSuccess('Treatment entry added successfully.'); } catch (error: unknown) { toast.showError(formatApiErrorMessage(error, 'Failed to add treatment entry.')); } finally { setSavingTreatment(false); } } return (

Patients

{isCreateOpen && ( setPatientForm((prev) => ({ ...prev, ...patch }))} onSubmit={() => void handleCreatePatient()} onClose={() => setIsCreateOpen(false)} loading={savingPatient} /> )}
{ setSelectedPatient(patient); void loadTreatments(patient.id); }} loading={loadingPatients} />
); }