import {
addDays,
addMonths,
addWeeks,
endOfWeek,
format,
startOfWeek,
subDays,
subMonths,
subWeeks,
} from 'date-fns'
import { ptBR } from 'date-fns/locale'
import { useState } from 'react'
import { AgendaDailyView } from '../components/calendar/AgendaDailyView.jsx'
import { AgendaMonthlyView } from '../components/calendar/AgendaMonthlyView.jsx'
import { AgendaWeeklyView } from '../components/calendar/AgendaWeeklyView.jsx'
import { useAgenda } from '../hooks/useAgenda.js'
import { formatLocalDateInput, parseLocalDate } from '../utils/agendaDate.js'
const statusFilters = [
{ label: 'Todos', value: 'Todos' },
{ label: 'Confirmadas', value: 'Confirmada' },
{ label: 'Em triagem', value: 'Em triagem' },
{ label: 'Aguardando', value: 'Aguardando' },
{ label: 'Canceladas', value: 'Cancelada' },
]
const viewFilters = [
{ label: 'Dia', value: 'Dia' },
{ label: 'Semana', value: 'Semana' },
{ label: 'Mês', value: 'Mes' },
]
const appointmentTypeOptions = ['Retorno', 'Primeira consulta', 'Exame', 'Avaliação pre-op']
const appointmentStatusOptions = ['Confirmada', 'Em triagem', 'Aguardando']
export function AgendaPage() {
const [modalPatientSearch, setModalPatientSearch] = useState('')
const [modalDoctorSearch, setModalDoctorSearch] = useState('')
const {
patients,
professionals,
currentProfessional,
viewerProfile,
agendaScope,
loading,
error,
canCreateAppointment,
activeView,
setActiveView,
baseDate,
setBaseDate,
status,
setStatus,
setDoctorFilter,
doctorSearch,
setDoctorSearch,
unitFilter,
setUnitFilter,
modalOpen,
editingAppointment,
form,
updateForm,
openCreateModal,
openAppointmentModal,
closeAppointmentModal,
handleSubmitAppointment,
handleCancelAppointment,
visibleAppointments,
availableSlots,
slotsLoading,
slotsError,
} = useAgenda()
if (loading) {
return (
)
}
const weekStart = startOfWeek(baseDate, { weekStartsOn: 0 })
const weekEnd = endOfWeek(baseDate, { weekStartsOn: 0 })
const isDoctorScope = agendaScope === 'doctor'
const unitOptions = [
...new Set(professionals.map((professional) => professional.unit).filter(Boolean)),
].sort((a, b) => a.localeCompare(b, 'pt-BR'))
const filteredPatients = filterBySearch(patients, modalPatientSearch, (patient) => [
patient.name,
patient.full_name,
patient.nome,
patient.cpf,
patient.email,
])
const filteredProfessionals = filterBySearch(professionals, modalDoctorSearch, (professional) => [
professional.name,
professional.email,
professional.unit,
])
const selectedPatient = patients.find((patient) => String(patient.id) === String(form.patientId))
const selectedProfessional = professionals.find((professional) => String(professional.id) === String(form.professionalId))
const timeOptions = getTimeOptions(form.time, availableSlots)
function openCreate(options = {}) {
setModalPatientSearch('')
setModalDoctorSearch('')
openCreateModal(options)
}
function openManage(appointment) {
setModalPatientSearch('')
setModalDoctorSearch('')
openAppointmentModal(appointment)
}
function closeModal() {
setModalPatientSearch('')
setModalDoctorSearch('')
closeAppointmentModal()
}
return (
Agenda
Perfil atual: {viewerProfile?.role || (isDoctorScope ? 'Médico' : 'Usuário')}
{activeView === 'Dia' && format(baseDate, "dd 'de' MMM", { locale: ptBR })}
{activeView === 'Semana' &&
`${format(weekStart, 'dd MMM', { locale: ptBR })} - ${format(weekEnd, 'dd MMM', { locale: ptBR })}`}
{activeView === 'Mes' && format(baseDate, 'MMMM yyyy', { locale: ptBR })}
{error ? (
Não foi possível liberar a agenda
{error}
Enquanto esse vínculo não existir na API, a tela fica bloqueada para evitar exibir consultas de outro médico.
) : (
{format(baseDate, "EEEE, dd 'de' MMMM", { locale: ptBR })}
Visualização: {activeView.toLowerCase()} | {visibleAppointments.length} registros visíveis
{viewFilters.map((view) => (
))}
{statusFilters.map((filter) => (
))}
{!isDoctorScope ? (
) : null}
{!isDoctorScope && (
Perfil atual: {viewerProfile?.role || 'Administrador'}
)}
{activeView === 'Semana' && (
)}
{activeView === 'Mes' && (
{
setBaseDate(day)
setActiveView('Dia')
}}
/>
)}
{activeView === 'Dia' && (
openCreate({ time })}
/>
)}
)}
)
}
function DarkField({ children, label }) {
return (
)
}
function DarkModal({ children, onClose, open, title }) {
if (!open) return null
return (
)
}
function SearchResults({ emptyText, getDescription, getLabel, items, onSelect, selectedId }) {
return (
{items.length ? (
items.map((item) => {
const isSelected = String(item.id) === String(selectedId)
return (
)
})
) : (
{emptyText}
)}
)
}
function getPatientLabel(patient) {
return patient?.name || patient?.full_name || patient?.nome || ''
}
function filterBySearch(items, search, getValues) {
const query = normalizeSearch(search)
if (!query) return items
return items.filter((item) =>
getValues(item)
.filter(Boolean)
.join(' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.includes(query),
)
}
function getTimeOptions(selectedTime, slots) {
return [
...new Set([
selectedTime,
...slots.map((slot) => slot.time),
].filter(Boolean)),
].sort()
}
function normalizeSearch(value) {
return String(value || '')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.trim()
.toLowerCase()
}