import { useEffect, useMemo, useState } from 'react' import { hasCapability } from '../config/permissions.js' import { patientRepository } from '../repositories/patientRepository.js' const ITEMS_PER_PAGE = 25 const darkInput = 'h-10 w-full rounded-lg border border-[#404040] bg-[#1a1a1a] px-3 text-sm text-[#e5e5e5] outline-none transition placeholder:text-[#737373] focus:border-[#3b82f6] focus:ring-1 focus:ring-[#3b82f6]' const darkLabel = 'mb-1.5 block text-xs font-medium text-[#e5e5e5]' const darkCard = 'rounded-2xl border border-[#404040] bg-[#262626] p-6 shadow-sm' const patientTabs = [ { label: 'Resumo', value: 'resumo' }, { label: 'Consultas', value: 'consultas' }, { label: 'Documentos', value: 'documentos' }, ] export function PatientsPage({ navigate, role }) { const [rows, setRows] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [saving, setSaving] = useState(false) const [view, setView] = useState('list') const [editingId, setEditingId] = useState(null) const [search, setSearch] = useState('') const [insurance, setInsurance] = useState('') const [vip, setVip] = useState('') const [birthday, setBirthday] = useState('') const [city, setCity] = useState('') const [state, setState] = useState('') const [ageMin, setAgeMin] = useState('') const [ageMax, setAgeMax] = useState('') const [lastVisitSince, setLastVisitSince] = useState('') const [advancedOpen, setAdvancedOpen] = useState(false) const [openMenuId, setOpenMenuId] = useState(null) const [page, setPage] = useState(1) useEffect(() => { buildPatientRows() .then((data) => setRows(data)) .catch((err) => setError(err.message)) .finally(() => setLoading(false)) }, []) const editingPatient = rows.find((patient) => patient.id === editingId) const insuranceOptions = useMemo(() => [...new Set(rows.map((patient) => patient.insurance).filter(Boolean))], [rows]) const stateOptions = useMemo(() => [...new Set(rows.map((patient) => patient.state).filter(Boolean))], [rows]) const hasAdvancedFilters = city || state || ageMin || ageMax || lastVisitSince const canEditPatients = hasCapability(role, 'canEditPatients') const canHardDeletePatients = hasCapability(role, 'hardDeletePatients') const filteredPatients = useMemo(() => { return rows.filter((patient) => { const haystack = [patient.name, patient.cpf, patient.document, patient.insurance, patient.phone] .filter(Boolean) .join(' ') .toLowerCase() if (search && !haystack.includes(search.toLowerCase())) { return false } if (insurance && patient.insurance !== insurance) { return false } if (vip === 'Sim' && !patient.vip) { return false } if (vip === 'Não' && patient.vip) { return false } if (birthday === 'Hoje' && patient.birthday !== '07/04') { return false } if (birthday === 'Neste mes' && !patient.birthday?.endsWith('/04')) { return false } if (city && !patient.city.toLowerCase().includes(city.toLowerCase())) { return false } if (state && patient.state !== state) { return false } if (ageMin && patient.age < Number(ageMin)) { return false } if (ageMax && patient.age > Number(ageMax)) { return false } if (lastVisitSince && patient.lastVisitIso && patient.lastVisitIso < lastVisitSince) { return false } return true }) }, [ageMax, ageMin, birthday, city, insurance, lastVisitSince, rows, search, state, vip]) const totalPages = Math.max(1, Math.ceil(filteredPatients.length / ITEMS_PER_PAGE)) const currentPage = Math.min(page, totalPages) const startIndex = (currentPage - 1) * ITEMS_PER_PAGE const paginatedPatients = filteredPatients.slice(startIndex, startIndex + ITEMS_PER_PAGE) function resetAdvancedFilters() { setCity('') setState('') setAgeMin('') setAgeMax('') setLastVisitSince('') setAdvancedOpen(false) setPage(1) } function openForm(patientId = null) { if (!canEditPatients) return setEditingId(patientId) setOpenMenuId(null) setView('form') } async function savePatient(patient) { if (!canEditPatients) { window.alert('Você não tem permissão para salvar pacientes.') return } const isNew = !rows.some((item) => item.id === patient.id) setSaving(true) try { if (isNew) { const [created] = await patientRepository.create(patient) const newRow = { ...patient, id: created.id, detailId: created.id, name: created.full_name || patient.name, phone: created.phone_mobile || patient.phone, } setRows((currentRows) => [newRow, ...currentRows]) } else { await patientRepository.update(patient.id, patient) setRows((currentRows) => currentRows.map((item) => (item.id === patient.id ? patient : item)) ) } } catch (err) { window.alert(`Erro ao salvar paciente: ${err.message}`) return } finally { setSaving(false) } setEditingId(null) setPage(1) setView('list') } async function deletePatient(patientId) { if (!canHardDeletePatients) { window.alert('Você não tem permissão para excluir pacientes.') return } if (window.confirm('Tem certeza que deseja excluir este paciente?')) { try { await patientRepository.remove(patientId) setRows((currentRows) => currentRows.filter((patient) => patient.id !== patientId)) } catch (err) { window.alert(`Erro ao excluir paciente: ${err.message}`) } setOpenMenuId(null) setPage(1) } } function openDetail(patient) { setOpenMenuId(null) if (patient.detailId) { navigate(`/pacientes/${patient.detailId}`) return } openForm(patient.id) } if (loading) { return

Carregando pacientes...

} if (error) { return

Erro ao carregar pacientes: {error}

} if (view === 'form') { return ( patient.id)} onCancel={() => { setEditingId(null) setView('list') }} onSave={savePatient} patient={editingPatient} saving={saving} /> ) } return (

Pacientes

Gerencie as informações de seus pacientes

{canEditPatients ? ( ) : null}
{ setSearch(event.target.value) setPage(1) }} placeholder="Buscar por nome ou documento..." value={search} />
{ setInsurance(value) setPage(1) }} options={insuranceOptions} value={insurance} /> { setVip(value) setPage(1) }} options={['Sim', 'Não']} value={vip} />
{ setBirthday(value) setPage(1) }} options={['Hoje', 'Neste mes']} value={birthday} />
{hasAdvancedFilters ? (
Filtros ativos: {city ? setCity('')} /> : null} {state ? setState('')} /> : null} {ageMin ? setAgeMin('')} /> : null} {ageMax ? setAgeMax('')} /> : null} {lastVisitSince ? ( setLastVisitSince('')} /> ) : null}
) : null}
{paginatedPatients.length ? ( paginatedPatients.map((patient) => ( )) ) : ( )}
Nome Telefone Cidade Estado Ultimo atendimento Proximo atendimento Ações
{patient.phone} {patient.city} {patient.state} {patient.lastVisit || 'Ainda não houve atendimento'} {patient.nextVisit || 'Nenhum atendimento agendado'} {openMenuId === patient.id ? ( <>
Nenhum paciente encontrado.

Mostrando {filteredPatients.length ? startIndex + 1 : 0}- {Math.min(startIndex + ITEMS_PER_PAGE, filteredPatients.length)} de {filteredPatients.length} pacientes

setPage(currentPage - 1)}> {Array.from({ length: totalPages }, (_, index) => index + 1).map((pageNumber) => ( ))} setPage(currentPage + 1)}>
{advancedOpen ? ( { setPage(1) setAdvancedOpen(false) }} onClear={resetAdvancedFilters} onClose={() => setAdvancedOpen(false)} setAgeMax={setAgeMax} setAgeMin={setAgeMin} setCity={setCity} setLastVisitSince={setLastVisitSince} setState={setState} state={state} stateOptions={stateOptions} /> ) : null}
) } function PatientEditor({ existingIds, onCancel, onSave, patient, saving }) { const [formData, setFormData] = useState(() => ({ id: patient?.id || '', detailId: patient?.detailId || null, name: patient?.name || '', cpf: patient?.cpf || '', phone: patient?.phone || '', email: patient?.email || '', city: patient?.city || '', state: patient?.state || '', insurance: patient?.insurance || '', plan: patient?.plan || '', age: patient?.age || '', condition: patient?.condition || '', birthday: patient?.birthday || '', vip: Boolean(patient?.vip), lastVisit: patient?.lastVisit || null, nextVisit: patient?.nextVisit || null, lastVisitIso: patient?.lastVisitIso || null, })) const [attachmentsOpen, setAttachmentsOpen] = useState(false) function handleChange(event) { const { checked, name, type, value } = event.target let nextValue = type === 'checkbox' ? checked : value if (name === 'cpf') { nextValue = maskCPF(value) } if (name === 'phone') { nextValue = maskPhone(value) } setFormData((currentData) => ({ ...currentData, [name]: nextValue })) } function handleSubmit(event) { event.preventDefault() if (!formData.name.trim()) { window.alert('O nome e obrigatorio.') return } onSave({ ...formData, id: formData.id || uniqueSlug(formData.name, existingIds), age: Number(formData.age) || 0, birthday: formData.birthday || '07/04', city: formData.city || 'Cidade não informada', document: formData.cpf ? `CPF ${formData.cpf}` : 'CPF não informado', insurance: formData.insurance || 'Particular', lastVisit: formData.lastVisit || 'Ainda não houve atendimento', nextVisit: formData.nextVisit || null, phone: formData.phone || 'Telefone não informado', plan: formData.insurance || formData.plan || 'Particular', state: formData.state || 'UF', }) } return (

Paciente

Gerencie as informações de seus pacientes

Dados do Paciente