import React, { useState, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import API_KEY from '../components/utils/apiKeys.js'; import AgendamentoCadastroManager from '../pages/AgendamentoCadastroManager.jsx'; 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 { ChevronLeft, ChevronRight, Edit, Trash2 } from 'lucide-react'; import "../pages/style/Agendamento.css"; import '../pages/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 { getAuthorizationHeader, user } = useAuth(); const [isLoading, setIsLoading] = useState(true); const [DictAgendamentosOrganizados, setDictAgendamentosOrganizados] = useState({}); const [filaEsperaData, setFilaDeEsperaData] = useState([]); const [FiladeEspera, setFiladeEspera] = useState(false); const [PageNovaConsulta, setPageConsulta] = useState(false); const [currentDate, setCurrentDate] = useState(dayjs()); const [selectedDay, setSelectedDay] = useState(dayjs()); const [quickJump, setQuickJump] = useState({ month: currentDate.month(), year: currentDate.year() }); const [searchConsultas, setSearchConsultas] = useState(''); const [searchFilaEspera, setSearchFilaEspera] = useState(''); const [waitPage, setWaitPage] = useState(1); const [waitPerPage, setWaitPerPage] = useState(10); const [waitSortKey, setWaitSortKey] = useState(null); const [waitSortDir, setWaitSortDir] = useState('asc'); const [isCancelModalOpen, setIsCancelModalOpen] = useState(false); const [appointmentToCancel, setAppointmentToCancel] = useState(null); const [cancellationReason, setCancellationReason] = useState(''); const authHeader = useMemo(() => getAuthorizationHeader(), [getAuthorizationHeader]); useEffect(() => { const carregarDados = async () => { const patientId = user?.patient_id || "6e7f8829-0574-42df-9290-8dbb70f75ada"; if (!authHeader) { console.warn("Header de autorização não disponível."); setIsLoading(false); return; } setIsLoading(true); try { const myHeaders = new Headers({ "Authorization": authHeader, "apikey": API_KEY }); const requestOptions = { method: 'GET', headers: myHeaders }; const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select=*,doctors(full_name)&patient_id=eq.${patientId}`, requestOptions); if (!response.ok) throw new Error(`Erro na requisição: ${response.statusText}`); const consultasBrutas = await response.json() || []; const newDict = {}; const newFila = []; for (const agendamento of consultasBrutas) { const agendamentoMelhorado = { ...agendamento, medico_nome: agendamento.doctors?.full_name || 'Médico não informado' }; 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)); } setDictAgendamentosOrganizados(newDict); setFilaDeEsperaData(newFila); } catch (err) { console.error('Falha ao buscar ou processar agendamentos:', err); setDictAgendamentosOrganizados({}); setFilaDeEsperaData([]); } finally { setIsLoading(false); } }; carregarDados(); }, [authHeader, user]); const updateAppointmentStatus = async (id, updates) => { const myHeaders = new Headers({ "Authorization": authHeader, "apikey": API_KEY, "Content-Type": "application/json" }); 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) throw new Error('Falha ao atualizar o status.'); return true; } catch (error) { console.error('Erro de rede/servidor:', error); return false; } }; const handleCancelClick = (appointmentId) => { setAppointmentToCancel(appointmentId); setCancellationReason(''); setIsCancelModalOpen(true); }; const executeCancellation = async () => { if (!appointmentToCancel) return; setIsLoading(true); const motivo = cancellationReason.trim() || "Cancelado pelo paciente (motivo não especificado)"; const success = await updateAppointmentStatus(appointmentToCancel, { status: "cancelled", cancellation_reason: motivo, updated_at: new Date().toISOString() }); setIsCancelModalOpen(false); setAppointmentToCancel(null); setCancellationReason(''); if (success) { alert("Solicitação cancelada com sucesso!"); setDictAgendamentosOrganizados(prev => { const newDict = { ...prev }; for (const date in newDict) { newDict[date] = newDict[date].filter(app => app.id !== appointmentToCancel); } return newDict; }); setFilaDeEsperaData(prev => prev.filter(item => item.agendamento.id !== appointmentToCancel)); } else { alert("Falha ao cancelar a solicitação."); } setIsLoading(false); }; const handleQuickJumpChange = (type, value) => setQuickJump(prev => ({ ...prev, [type]: Number(value) })); const applyQuickJump = () => { const newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); setCurrentDate(newDate); setSelectedDay(newDate); }; const dateGrid = useMemo(() => { 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; }, [currentDate]); const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; const handleDateClick = (day) => setSelectedDay(day); const consultasDoDiaFiltradas = useMemo(() => { const consultas = DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')] || []; if (!searchConsultas.trim()) return consultas; const term = searchConsultas.toLowerCase(); return consultas.filter(app => app.medico_nome?.toLowerCase().includes(term) || app.status?.toLowerCase().includes(term) ); }, [DictAgendamentosOrganizados, selectedDay, searchConsultas]); const filaEsperaFiltrada = useMemo(() => { if (!searchFilaEspera.trim()) return filaEsperaData; const term = searchFilaEspera.toLowerCase(); return filaEsperaData.filter(item => item.Infos?.medico_nome?.toLowerCase().includes(term) ); }, [filaEsperaData, searchFilaEspera]); const applySortingWaitlist = (arr) => { if (!Array.isArray(arr) || !waitSortKey) return arr; const copy = [...arr]; if (waitSortKey === 'medico') { copy.sort((a, b) => (a?.Infos?.medico_nome || '').localeCompare(b?.Infos?.medico_nome || '')); } else if (waitSortKey === 'data') { copy.sort((a, b) => new Date(a?.agendamento?.created_at || 0) - new Date(b?.agendamento?.created_at || 0)); } if (waitSortDir === 'desc') copy.reverse(); return copy; }; const filaEsperaOrdenada = applySortingWaitlist(filaEsperaFiltrada); // Paginação const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; const waitIndiceInicial = (waitPage - 1) * waitPerPage; const waitIndiceFinal = waitIndiceInicial + waitPerPage; const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); 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); }, [searchFilaEspera, waitSortKey, waitSortDir]); const activeButtonStyle = { backgroundColor: '#1B2A41', color: 'white', padding: '6px 12px', fontSize: '0.875rem', borderRadius: '8px', border: '1px solid white', display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }; const inactiveButtonStyle = { backgroundColor: '#1B2A41', color: 'white', padding: '6px 12px', fontSize: '0.875rem', borderRadius: '8px', border: '1px solid #1B2A41', display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }; if (isLoading) { return (
); } return (

Minhas consultas

{!PageNovaConsulta ? (
{!FiladeEspera ? (
{selectedDay.format('MMM')}{selectedDay.format('DD')}

{selectedDay.format('dddd')}

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

setSearchConsultas(e.target.value)} />

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

{(consultasDoDiaFiltradas.length > 0) ? ( consultasDoDiaFiltradas.map(app => (
{dayjs(app.scheduled_at).format('HH:mm')}
Consulta com Dr(a). {app.medico_nome}
{app.status !== 'cancelled' && dayjs(app.scheduled_at).isAfter(dayjs()) && ( )}
)) ) : (

Nenhuma consulta agendada para esta data.

)}
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}
}
); })}
) : (

Minhas Solicitações em Fila de Espera

Filtros
setSearchFilaEspera(e.target.value)} /> Digite o 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) => ( ))) : ( )}
Médico Solicitado Data da Solicitação Ações
Dr(a). {item.Infos?.medico_nome} {dayjs(item.agendamento.created_at).format('DD/MM/YYYY HH:mm')}
Nenhuma solicitação na fila de espera.
{filaEsperaFiltrada.length > 0 && (
Itens por página:
Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
)}
)}
) : ( )} {isCancelModalOpen && (
Confirmação de Cancelamento

Qual o motivo do cancelamento?

)}
) } export default Agendamento;