import React, { useState, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import API_KEY from '../components/utils/apiKeys.js'; import AgendamentoCadastroManager from './AgendamentoCadastroManager.jsx'; import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient.js'; import TabelaAgendamentoDia from '../components/AgendarConsulta/TabelaAgendamentoDia'; import TabelaAgendamentoSemana from '../components/AgendarConsulta/TabelaAgendamentoSemana'; import TabelaAgendamentoMes from '../components/AgendarConsulta/TabelaAgendamentoMes'; import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta'; import { GetAllDoctors, GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor.js'; import { useAuth } from '../components/utils/AuthProvider.js'; import dayjs from 'dayjs'; import 'dayjs/locale/pt-br'; import isBetween from 'dayjs/plugin/isBetween'; import localeData from 'dayjs/plugin/localeData'; import { Search, ChevronLeft, ChevronRight, Edit, Trash2 } from 'lucide-react'; import CalendarComponent from '../components/AgendarConsulta/CalendarComponent.jsx'; import "./style/Agendamento.css"; import './style/FilaEspera.css'; import Spinner from '../components/Spinner.jsx'; dayjs.locale('pt-br'); dayjs.extend(isBetween); dayjs.extend(localeData); const Agendamento = ({ setDictInfo }) => { const navigate = useNavigate(); const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]); const [selectedID, setSelectedId] = useState('0'); const [filaEsperaData, setFilaEsperaData] = useState([]); const [FiladeEspera, setFiladeEspera] = useState(false); const [PageNovaConsulta, setPageConsulta] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const { getAuthorizationHeader } = useAuth(); const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({}); const [showDeleteModal, setShowDeleteModal] = useState(false); const [ListaDeMedicos, setListaDeMedicos] = useState([]); const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]); const [searchTermDoctor, setSearchTermDoctor] = useState(''); const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" }); const [cacheAgendamentos, setCacheAgendamentos] = useState([]); const [appointmentToEdit, setAppointmentToEdit] = useState(null); const [showConfirmModal, setShowConfirmModal] = useState(false); const [motivoCancelamento, setMotivoCancelamento] = useState(""); const [showSpinner, setShowSpinner] = useState(true); const [waitlistSearch, setWaitlistSearch] = useState(''); const [waitSortKey, setWaitSortKey] = useState(null); const [waitSortDir, setWaitSortDir] = useState('asc'); const [waitPage, setWaitPage] = useState(1); const [waitPerPage, setWaitPerPage] = useState(10); const authHeader = getAuthorizationHeader(); const cacheMedicos = useMemo(() => ({}), []); const cachePacientes = useMemo(() => ({}), []); const [currentDate, setCurrentDate] = useState(dayjs()); const [selectedDay, setSelectedDay] = useState(dayjs()); const [editingAppointmentId, setEditingAppointmentId] = useState(null); const [quickJump, setQuickJump] = useState({ month: currentDate.month(), year: currentDate.year() }); const fetchAppointments = async () => { const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' }; try { const res = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*", requestOptions); const data = await res.json(); setListaTodosAgendamentos(data); } catch (err) { console.error('Erro ao buscar agendamentos', err); } }; const updateAppointmentStatus = async (id, updates) => { const myHeaders = new Headers(); myHeaders.append("Authorization", authHeader); myHeaders.append("apikey", API_KEY); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Prefer", "return=representation"); const requestOptions = { method: 'PATCH', headers: myHeaders, body: JSON.stringify(updates) }; try { const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${id}`, requestOptions); if (response.ok) { await fetchAppointments(); return true; } else { console.error('Erro ao atualizar agendamento:', await response.text()); return false; } } catch (error) { console.error('Erro de rede/servidor:', error); return false; } }; const deleteConsulta = async (id) => { setShowSpinner(true); const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: motivoCancelamento, updated_at: new Date().toISOString() }); setShowSpinner(false); if (success) { setShowDeleteModal(false); setMotivoCancelamento(""); setSelectedId('0'); } else { alert("Falha ao cancelar a consulta."); } }; const confirmConsulta = async (id) => { setShowSpinner(true); const success = await updateAppointmentStatus(id, { status: "agendado", cancellation_reason: null, updated_at: new Date().toISOString() }); setShowSpinner(false); if (success) { setShowConfirmModal(false); setSelectedId('0'); } else { alert("Falha ao reverter o cancelamento."); } }; const handleQuickJumpChange = (type, value) => { setQuickJump(prev => ({ ...prev, [type]: Number(value) })); }; useEffect(() => { setQuickJump({ month: currentDate.month(), year: currentDate.year() }); }, [currentDate]); const applyQuickJump = () => { let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); setCurrentDate(newDate); setSelectedDay(newDate); }; useEffect(() => { if (!listaTodosAgendamentos.length) { setShowSpinner(false); return; } setShowSpinner(true); const fetchDados = async () => { const newDict = {}; const newFila = []; for (const agendamento of listaTodosAgendamentos) { if (!agendamento.doctor_id || !agendamento.patient_id) continue; if (!cacheMedicos[agendamento.doctor_id]) { cacheMedicos[agendamento.doctor_id] = (await GetDoctorByID(agendamento.doctor_id, authHeader))[0]; } if (!cachePacientes[agendamento.patient_id]) { cachePacientes[agendamento.patient_id] = (await GetPatientByID(agendamento.patient_id, authHeader))[0]; } const medico = cacheMedicos[agendamento.doctor_id]; const paciente = cachePacientes[agendamento.patient_id]; const agendamentoMelhorado = { ...agendamento, medico_nome: medico?.full_name, paciente_nome: paciente?.full_name, paciente_cpf: paciente?.cpf }; <<<<<<< HEAD if (agendamento.status === "requested") { newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado }); } else { const DiaAgendamento = dayjs(agendamento.scheduled_at).format("YYYY-MM-DD"); if (newDict[DiaAgendamento]) { newDict[DiaAgendamento].push(agendamentoMelhorado); } else { newDict[DiaAgendamento] = [agendamentoMelhorado]; } } } for (const key in newDict) { newDict[key].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at)); } setAgendamentosOrganizados(newDict); setFilaEsperaData(newFila); setShowSpinner(false); ======= useEffect(() => { fetchAppointments(); GetAllDoctors(authHeader).then(docs => setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id })))); }, [authHeader]); const handleSearchMedicos = (term) => { setSearchTermDoctor(term); if (term.trim() === '') { setFiltredTodosMedicos([]); setMedicoFiltrado({ id: "vazio" }); return; } const filtered = ListaDeMedicos.filter(medico => medico.nomeMedico.toLowerCase().includes(term.toLowerCase()) ); setFiltredTodosMedicos(filtered); }; const filaEsperaFiltrada = useMemo(() => { if (!waitlistSearch.trim()) return filaEsperaData; const term = waitlistSearch.toLowerCase(); return filaEsperaData.filter(item => (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) || (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) || (item?.Infos?.nome_medico?.toLowerCase() || '').includes(term)); }, [waitlistSearch, filaEsperaData]); const applySortingWaitlist = (arr) => { if (!Array.isArray(arr) || !waitSortKey) return arr; const copy = [...arr]; if (waitSortKey === 'paciente') { copy.sort((a, b) => (a?.Infos?.paciente_nome || '').localeCompare((b?.Infos?.paciente_nome || ''))); } else if (waitSortKey === 'medico') { copy.sort((a, b) => (a?.Infos?.nome_medico || '').localeCompare((b?.Infos?.nome_medico || ''))); } else if (waitSortKey === 'data') { copy.sort((a, b) => new Date(a?.agendamento?.scheduled_at || 0) - new Date(b?.agendamento?.scheduled_at || 0)); } if (waitSortDir === 'desc') copy.reverse(); return copy; >>>>>>> alteracoes-modais-tabela }; fetchDados(); }, [listaTodosAgendamentos, authHeader, cacheMedicos, cachePacientes]); <<<<<<< HEAD useEffect(() => { fetchAppointments(); GetAllDoctors(authHeader).then(docs => setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id }))) ======= const generateDateGrid = () => { const grid = []; const startOfMonth = currentDate.startOf('month'); let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day'); for (let i = 0; i < 42; i++) { grid.push(currentDay); currentDay = currentDay.add(1, 'day'); } return grid; }; const dateGrid = useMemo(() => generateDateGrid(), [currentDate]); const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; const handleDateClick = (day) => setSelectedDay(day); const DeleteModal = () => (
Confirmação de Cancelamento

Qual o motivo do cancelamento?

>>>>>>> alteracoes-modais-tabela ); }, [authHeader]); const handleSearchMedicos = (term) => { }; const filaEsperaFiltrada = useMemo(() => { if (!waitlistSearch.trim()) return filaEsperaData; const term = waitlistSearch.toLowerCase(); return filaEsperaData.filter(item => (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) || (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) || (item?.Infos?.nome_medico?.toLowerCase() || '').includes(term) ); }, [waitlistSearch, filaEsperaData]); const applySortingWaitlist = (arr) => { if (!Array.isArray(arr) || !waitSortKey) return arr; const copy = [...arr]; if (waitSortKey === 'paciente') { copy.sort((a, b) => (a?.Infos?.paciente_nome || '').localeCompare((b?.Infos?.paciente_nome || ''))); } else if (waitSortKey === 'medico') { copy.sort((a, b) => (a?.Infos?.nome_medico || '').localeCompare((b?.Infos?.nome_medico || ''))); } else if (waitSortKey === 'data') { copy.sort((a, b) => new Date(a?.agendamento?.scheduled_at || 0) - new Date(b?.agendamento?.scheduled_at || 0) ); } if (waitSortDir === 'desc') copy.reverse(); return copy; }; const filaEsperaOrdenada = applySortingWaitlist(filaEsperaFiltrada); const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; const waitIndiceInicial = (waitPage - 1) * waitPerPage; const waitIndiceFinal = waitIndiceInicial + waitPerPage; const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); <<<<<<< HEAD const gerarNumerosWaitPages = () => { const paginas = []; const paginasParaMostrar = 5; let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); inicio = Math.max(1, fim - paginasParaMostrar + 1); for (let i = inicio; i <= fim; i++) { paginas.push(i); } return paginas; }; useEffect(() => { setWaitPage(1); }, [waitlistSearch, waitSortKey, waitSortDir]); const generateDateGrid = () => { const grid = []; const startOfMonth = currentDate.startOf('month'); let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day'); for (let i = 0; i < 42; i++) { grid.push(currentDay); currentDay = currentDay.add(1, 'day'); } return grid; }; const dateGrid = useMemo(() => generateDateGrid(), [currentDate]); const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; const handleDateClick = (day) => setSelectedDay(day); const DeleteModal = () => (
Confirmação de Cancelamento

Qual o motivo do cancelamento?

); const ConfirmEditModal = () => (
Confirmação de edição

Tem certeza que deseja retirar o cancelamento?

Isso reverterá o status do agendamento para Agendado.
); return (

Agendar nova consulta

{!PageNovaConsulta ? (
handleSearchMedicos(e.target.value)} />
{searchTermDoctor && FiltredTodosMedicos.length > 0 && (
{FiltredTodosMedicos.map((medico) => (
{ setSearchTermDoctor(medico.nomeMedico); setFiltredTodosMedicos([]); setMedicoFiltrado(medico); }} >

{medico.nomeMedico}

))}
)}
{/* ABA + BOTÕES NA MESMA BARRA */}
{FiladeEspera === false ? (
{selectedDay.format('MMM')} {selectedDay.format('DD')}

{selectedDay.format('dddd')}

{selectedDay.format('D [de] MMMM [de] YYYY')}

Consultas para {selectedDay.format('DD/MM')}

{showSpinner ? ( ) : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? ( DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')].map(app => (
{dayjs(app.scheduled_at).format('HH:mm')}
{app.paciente_nome} Dr(a). {app.medico_nome}
{app.status === 'cancelled' ? ( ) : ( <> )}
)) ) : (

Nenhuma consulta agendada.

)}
======= return (

Agendar nova consulta

{!PageNovaConsulta ? (
{!FiladeEspera && (
Filtrar por Médico
handleSearchMedicos(e.target.value)} /> Filtre os agendamentos por médico
{searchTermDoctor && FiltredTodosMedicos.length > 0 && (
{FiltredTodosMedicos.map((medico) => ( ))}
)} {MedicoFiltrado.id !== "vazio" && (
Filtrando por: {searchTermDoctor}
)}
)}
{FiladeEspera === false ? (
{selectedDay.format('MMM')}{selectedDay.format('DD')}

{selectedDay.format('dddd')}

{selectedDay.format('D [de] MMMM [de] YYYY')}

Consultas para {selectedDay.format('DD/MM')}

{showSpinner ? : (DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]?.length > 0) ? ( DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')] .filter(app => { if (MedicoFiltrado.id === "vazio") return true; return app.doctor_id === MedicoFiltrado.idMedico; }) .map(app => (
{dayjs(app.scheduled_at).format('HH:mm')}
{app.paciente_nome}Dr(a). {app.medico_nome}
{app.status === 'cancelled' ? ( ) : ( <> )}
))) : (

Nenhuma consulta agendada.

)}
Realizado
Confirmado
Agendado
Cancelado

{currentDate.format('MMMM [de] YYYY')}

{weekDays.map(day =>
{day}
)} {dateGrid.map((day, index) => { const appointmentsOnDay = DictAgendamentosOrganizados[day.format('YYYY-MM-DD')] || []; const cellClasses = `day-cell ${day.isSame(currentDate, 'month') ? 'current-month' : 'other-month'} ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${day.isSame(selectedDay, 'day') ? 'selected' : ''}`; return (
handleDateClick(day)}>{day.format('D')}{appointmentsOnDay.length > 0 &&
{appointmentsOnDay.length}
}
); })}
) : (

Fila de Espera

Filtros
setWaitlistSearch(e.target.value)} />Digite o nome do paciente, CPF ou nome do médico
Ordenar por: {(() => { const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; return ();})()}
{filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
{filaEsperaPaginada.length > 0 ? (filaEsperaPaginada.map((item, index) => ( ))) : ()}
Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções
{item?.Infos?.paciente_nome}{item?.Infos?.paciente_cpf}{item?.Infos?.nome_medico}{dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')}
{showSpinner ? : (<>

Nenhuma solicitação encontrada.

)}
{filaEsperaFiltrada.length > 0 && (
Itens por página:
Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
)}
)}
>>>>>>> alteracoes-modais-tabela
Realizado
Confirmado
Agendado
Cancelado

{currentDate.format('MMMM [de] YYYY')}

{weekDays.map(day => (
{day}
))} {dateGrid.map((day, index) => { const appointmentsOnDay = DictAgendamentosOrganizados[day.format('YYYY-MM-DD')] || []; const cellClasses = `day-cell ${ day.isSame(currentDate, 'month') ? 'current-month' : 'other-month' } ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${ day.isSame(selectedDay, 'day') ? 'selected' : '' }`; return (
handleDateClick(day)} > {day.format('D')} {appointmentsOnDay.length > 0 && (
{appointmentsOnDay.length}
)}
); })}
) : (

Fila de Espera

Filtros
setWaitlistSearch(e.target.value)} /> Digite o nome do paciente, CPF ou nome do médico
Ordenar por: {(() => { const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; return ( ); })()}
{filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
{filaEsperaPaginada.length > 0 ? ( filaEsperaPaginada.map((item, index) => ( )) ) : ( )}
Nome do Paciente CPF Médico Solicitado Data da Solicitação Ações
{item?.Infos?.paciente_nome} {item?.Infos?.paciente_cpf} {item?.Infos?.nome_medico} {dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')}
{showSpinner ? ( ) : ( <>

Nenhuma solicitação encontrada.

)}
{filaEsperaFiltrada.length > 0 && (
Itens por página:
Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
)}
)}
) : ( )} {showDeleteModal && } {showConfirmModal && }
); }; export default Agendamento;