import { useState, useEffect, useMemo } from 'react' import { isSameDay } from 'date-fns' import { appointmentRepository } from '../repositories/appointmentRepository.js' import { availabilityRepository } from '../repositories/availabilityRepository.js' import { patientRepository } from '../repositories/patientRepository.js' import { professionalRepository } from '../repositories/professionalRepository.js' import { profileRepository } from '../repositories/profileRepository.js' import { formatLocalDateInput, parseLocalDate, sortAppointmentsByTime } from '../utils/agendaDate.js' export function useAgenda() { const [patients, setPatients] = useState([]) const [professionals, setProfessionals] = useState([]) const [currentProfessional, setCurrentProfessional] = useState(null) const [viewerProfile, setViewerProfile] = useState(null) const [localAppointments, setLocalAppointments] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [availableSlots, setAvailableSlots] = useState([]) const [slotsLoading, setSlotsLoading] = useState(false) const [slotsError, setSlotsError] = useState('') const [activeView, setActiveView] = useState('Dia') const [baseDate, setBaseDate] = useState(new Date()) const [status, setStatus] = useState('Todos') const [doctorFilter, setDoctorFilter] = useState('Todos') const [doctorSearch, setDoctorSearch] = useState('') const [unitFilter, setUnitFilter] = useState('') const [modalOpen, setModalOpen] = useState(false) const [form, setForm] = useState({ patientId: '', professionalId: '', type: 'Retorno', time: '15:30', mode: 'Teleconsulta', }) const agendaScope = viewerProfile?.isDoctor ? 'doctor' : 'global' const canCreateAppointment = agendaScope === 'doctor' ? Boolean(currentProfessional?.id) : professionals.length > 0 useEffect(() => { let active = true async function loadAgendaContext() { try { setError('') const [patientsData, professionalsData, currentProfile] = await Promise.all([ patientRepository.getAll(), professionalRepository.getAll(), profileRepository.getCurrentUserProfile(), ]) if (!active) return const agendaScope = currentProfile?.isDoctor ? 'doctor' : 'global' const resolvedProfessional = professionalRepository.resolveCurrentProfessional(currentProfile, professionalsData) const initialProfessionalId = agendaScope === 'doctor' ? resolvedProfessional?.id || '' : professionalsData?.[0]?.id || '' setViewerProfile(currentProfile) setPatients(patientsData || []) setCurrentProfessional(resolvedProfessional) setProfessionals(professionalsData || []) setForm((current) => ({ ...current, patientId: patientsData?.length ? patientsData[0].id : '', professionalId: initialProfessionalId, })) if (agendaScope === 'doctor' && !resolvedProfessional) { setLocalAppointments([]) setError('Não foi possível vincular o médico logado a um profissional da base.') return } const appointmentsData = await appointmentRepository.getAll({ doctorId: agendaScope === 'doctor' ? resolvedProfessional?.id : undefined, }) if (!active) return setLocalAppointments( agendaScope === 'doctor' && resolvedProfessional ? filterAppointmentsByProfessional(appointmentsData || [], resolvedProfessional.id) : sortAppointmentsByTime(appointmentsData || []), ) } catch (loadError) { if (!active) return console.error(loadError) setError(loadError.message || 'Erro ao carregar agenda.') } finally { if (active) { setLoading(false) } } } loadAgendaContext() return () => { active = false } }, []) useEffect(() => { if (!modalOpen) return const targetProfessionalId = agendaScope === 'doctor' ? currentProfessional?.id : form.professionalId let active = true async function loadAvailableSlots() { if (!targetProfessionalId) { setAvailableSlots([]) setSlotsError('') return } setSlotsLoading(true) setSlotsError('') try { const slots = await availabilityRepository.getAvailableSlots({ doctorId: targetProfessionalId, date: formatLocalDateInput(baseDate), appointmentType: form.mode, }) if (!active) return const activeSlots = slots.filter((slot) => slot.available) setAvailableSlots(activeSlots) if (activeSlots.length) { setForm((current) => activeSlots.some((slot) => slot.time === current.time) ? current : { ...current, time: activeSlots[0].time }, ) } } catch (loadError) { if (!active) return setAvailableSlots([]) setSlotsError(loadError.message || 'Não foi possível calcular horários disponíveis.') } finally { if (active) setSlotsLoading(false) } } loadAvailableSlots() return () => { active = false } }, [agendaScope, baseDate, currentProfessional?.id, form.mode, form.professionalId, modalOpen]) const visibleAppointments = useMemo(() => { let filtered = localAppointments if (status !== 'Todos') { filtered = filtered.filter((appointment) => appointment.status === status) } if (agendaScope !== 'doctor' && doctorFilter !== 'Todos') { filtered = filterAppointmentsByProfessional(filtered, doctorFilter) } if (agendaScope !== 'doctor') { const normalizedDoctorSearch = normalizeValue(doctorSearch) const normalizedUnit = normalizeValue(unitFilter) if (normalizedDoctorSearch || normalizedUnit) { filtered = filtered.filter((appointment) => { const professional = professionals.find( (item) => normalizeValue(item.id) === normalizeValue(appointment.professionalId), ) const professionalName = normalizeValue(professional?.name || appointment.professional) const professionalUnit = normalizeValue(professional?.unit || appointment.unit) return ( (!normalizedDoctorSearch || professionalName.includes(normalizedDoctorSearch)) && (!normalizedUnit || professionalUnit === normalizedUnit) ) }) } } if (activeView === 'Dia') { filtered = filtered.filter((appointment) => { if (!appointment.date) return false const appointmentDate = parseLocalDate(appointment.date) if (!appointmentDate) return false return isSameDay(appointmentDate, baseDate) }) } return sortAppointmentsByTime(filtered) }, [localAppointments, status, agendaScope, doctorFilter, doctorSearch, unitFilter, professionals, activeView, baseDate]) function updateForm(field, value) { setForm((current) => ({ ...current, [field]: value })) } async function handleCreate(event) { event.preventDefault() if (!form.patientId) { alert('Selecione um paciente para criar o agendamento.') return } const targetProfessionalId = agendaScope === 'doctor' ? currentProfessional?.id : form.professionalId if (!targetProfessionalId) { alert('Não foi possível identificar o profissional da consulta.') return } const dateStr = formatLocalDateInput(baseDate) try { const created = await appointmentRepository.create({ patientId: form.patientId, date: dateStr, time: form.time, type: form.type, mode: form.mode, room: form.mode === 'Teleconsulta' ? 'Virtual' : 'Consultório 1', professionalId: targetProfessionalId, }) setLocalAppointments((current) => sortAppointmentsByTime([...current, created])) setModalOpen(false) } catch (createError) { alert(createError.message || 'Erro ao criar agendamento.') } } return { patients, professionals, currentProfessional, viewerProfile, agendaScope, loading, error, canCreateAppointment, activeView, setActiveView, baseDate, setBaseDate, status, setStatus, doctorFilter, setDoctorFilter, doctorSearch, setDoctorSearch, unitFilter, setUnitFilter, modalOpen, setModalOpen, form, updateForm, handleCreate, visibleAppointments, availableSlots, slotsLoading, slotsError, } } function filterAppointmentsByProfessional(appointments, professionalId) { const normalizedProfessionalId = normalizeValue(professionalId) return sortAppointmentsByTime( appointments.filter((appointment) => normalizeValue(appointment.professionalId) === normalizedProfessionalId), ) } function normalizeValue(value) { return String(value || '').trim().toLowerCase() }