'use client'; import { useEffect, useMemo, useState } from 'react'; import { useTranslations } from 'next-intl'; import { Button } from '@/components/ui/shared/Button'; import { ToastStack } from '@/components/ui/shared/Toast'; import { patientsApi } from '@/lib/api/patients'; import { getUserFacingError } from '@/components/shared/formatApiError'; import { useAuth } from '@/lib/hooks/useAuth'; import { useToast } from '@/lib/hooks/useToast'; import { hasPermission } from '@/components/shared/permissions'; import { CreatePatientInput, Patient } 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 { PatientAppointmentHistory } from '@/components/ui/patient/PatientAppointmentHistory'; const EMPTY_PATIENT_FORM: CreatePatientInput = { firstName: '', lastName: '', mobile: '', email: '', }; export default function PatientsPage() { const t = useTranslations('patients'); const tErrors = useTranslations('errors'); const tCommon = useTranslations('common'); const { currentOrganization } = useAuth(); const toast = useToast(); const [search, setSearch] = useState(''); const [patients, setPatients] = useState([]); const [selectedPatient, setSelectedPatient] = useState(); const [loadingPatients, setLoadingPatients] = useState(false); const [isCreateOpen, setIsCreateOpen] = useState(false); const [savingPatient, setSavingPatient] = 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(getUserFacingError(error, tErrors, t('errorLoadPatients'))); } finally { setLoadingPatients(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); if (response.existing) { toast.showInfo( t('patientAlreadyExists', { firstName: response.data.firstName, lastName: response.data.lastName, }), ); } else { toast.showSuccess( t('successPatientSaved', { firstName: response.data.firstName, lastName: response.data.lastName, }), ); } } catch (error: unknown) { toast.showError(getUserFacingError(error, tErrors, t('errorSavePatient'))); } finally { setSavingPatient(false); } } return (

{t('title')}

{isCreateOpen && ( setPatientForm((prev) => ({ ...prev, ...patch }))} onSubmit={() => void handleCreatePatient()} onClose={() => { setIsCreateOpen(false); setPatientForm(EMPTY_PATIENT_FORM); }} loading={savingPatient} /> )}
{selectedPatient ? ( ) : null}
); }