import {
addDays,
subDays,
addWeeks,
subWeeks,
addMonths,
subMonths,
endOfWeek,
format,
startOfWeek,
} from 'date-fns'
import { ptBR } from 'date-fns/locale'
import { useState } from 'react'
import { AgendaDailyView } from '../components/calendar/AgendaDailyView.jsx'
import { AgendaWeeklyView } from '../components/calendar/AgendaWeeklyView.jsx'
import { AgendaMonthlyView } from '../components/calendar/AgendaMonthlyView.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' },
]
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']
export function AgendaPage({ navigate }) {
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,
setModalOpen,
form,
updateForm,
handleCreate,
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 = (() => {
const query = normalizeSearch(modalPatientSearch)
if (!query) return patients
return patients.filter((patient) =>
[patient.name, patient.full_name, patient.nome, patient.cpf, patient.email]
.filter(Boolean)
.join(' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.includes(query),
)
})()
const filteredProfessionals = (() => {
const query = normalizeSearch(modalDoctorSearch)
if (!query) return professionals
return professionals.filter((professional) =>
[professional.name, professional.email, professional.unit]
.filter(Boolean)
.join(' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.includes(query),
)
})()
const selectedPatient = patients.find((patient) => String(patient.id) === String(form.patientId))
const selectedProfessional = professionals.find((professional) => String(professional.id) === String(form.professionalId))
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' && (
navigate(`/pacientes/${appointment.patientId}`)}
/>
)}
{activeView === 'Mes' && (
{
setBaseDate(day)
setActiveView('Dia')
}}
/>
)}
{activeView === 'Dia' && (
navigate(`/pacientes/${appointment.patientId}`)}
/>
)}
)}
setModalOpen(false)} open={modalOpen} title="Novo agendamento">
)
}
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 normalizeSearch(value) {
return String(value || '')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.trim()
.toLowerCase()
}