diff --git a/src/PagesMedico/DoctorAgendamentoManager.jsx b/src/PagesMedico/DoctorAgendamentoManager.jsx index 25a8cae..d89a68f 100644 --- a/src/PagesMedico/DoctorAgendamentoManager.jsx +++ b/src/PagesMedico/DoctorAgendamentoManager.jsx @@ -10,50 +10,50 @@ import isBetween from 'dayjs/plugin/isBetween'; import localeData from 'dayjs/plugin/localeData'; import { Search, ChevronLeft, ChevronRight, Edit, Trash2, CheckCircle } from 'lucide-react'; import "../pages/style/Agendamento.css"; -import '../pages/style/FilaEspera.css'; -import Spinner from '../components/Spinner.jsx'; +import "../pages/style/FilaEspera.css"; +import Spinner from "../components/Spinner.jsx"; - -dayjs.locale('pt-br'); +dayjs.locale("pt-br"); dayjs.extend(isBetween); dayjs.extend(localeData); - const Agendamento = () => { - const navigate = useNavigate(); - const { getAuthorizationHeader, user } = useAuth(); - const authHeader = getAuthorizationHeader(); + const navigate = useNavigate(); + const { getAuthorizationHeader, user } = useAuth(); + const authHeader = getAuthorizationHeader(); const ID_MEDICO_ESPECIFICO = "078d2a67-b4c1-43c8-ae32-c1e75bb5b3df"; - const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]); - const [selectedID, setSelectedId] = useState('0'); - const [filaEsperaData, setFilaEsperaData] = useState([]); - const [FiladeEspera, setFiladeEspera] = useState(false); - const [PageNovaConsulta, setPageConsulta] = useState(false); - 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 [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 [cacheMedicos, setCacheMedicos] = useState({}); - const [cachePacientes, setCachePacientes] = useState({}); - const [currentDate, setCurrentDate] = useState(dayjs()); - const [selectedDay, setSelectedDay] = useState(dayjs()); - const [agendamentoParaEdicao, setAgendamentoParaEdicao] = useState(null); - const [quickJump, setQuickJump] = useState({ - month: currentDate.month(), - year: currentDate.year() - }); + const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]); + const [selectedID, setSelectedId] = useState("0"); + const [filaEsperaData, setFilaEsperaData] = useState([]); + const [FiladeEspera, setFiladeEspera] = useState(false); + const [PageNovaConsulta, setPageConsulta] = useState(false); + 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 [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 [cacheMedicos, setCacheMedicos] = useState({}); + const [cachePacientes, setCachePacientes] = useState({}); + const [currentDate, setCurrentDate] = useState(dayjs()); + const [selectedDay, setSelectedDay] = useState(dayjs()); + const [agendamentoParaEdicao, setAgendamentoParaEdicao] = useState(null); + const [quickJump, setQuickJump] = useState({ + month: currentDate.month(), + year: currentDate.year(), + }); const fetchAppointments = useCallback(async () => { @@ -67,65 +67,89 @@ const Agendamento = () => { const apiUrl = `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?doctor_id=eq.${ID_MEDICO_ESPECIFICO}&select=*`; - try { - const res = await fetch(apiUrl, requestOptions); - const data = await res.json(); - setListaTodosAgendamentos(data || []); - } catch (err) { - console.error('Erro ao buscar agendamentos', err); - setListaTodosAgendamentos([]); - } finally { - setShowSpinner(false); - } - }, [authHeader, ID_MEDICO_ESPECIFICO]); + try { + const res = await fetch(apiUrl, requestOptions); + const data = await res.json(); + setListaTodosAgendamentos(data || []); + } catch (err) { + console.error("Erro ao buscar agendamentos", err); + setListaTodosAgendamentos([]); + } finally { + setShowSpinner(false); + } + }, [authHeader, ID_MEDICO_ESPECIFICO]); - - const updateAppointmentStatus = useCallback(async (id, updates) => { - setShowSpinner(true); - 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; - } finally { - setShowSpinner(false); - } - }, [authHeader, fetchAppointments]); - - - const deleteConsulta = useCallback(async (id) => { - const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: motivoCancelamento, updated_at: new Date().toISOString() }); - if (success) { - setShowDeleteModal(false); - setMotivoCancelamento(""); - setSelectedId('0'); + const updateAppointmentStatus = useCallback( + async (id, updates) => { + setShowSpinner(true); + 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 { - alert("Falha ao cancelar a consulta."); + console.error( + "Erro ao atualizar agendamento:", + await response.text() + ); + return false; } - }, [motivoCancelamento, updateAppointmentStatus]); + } catch (error) { + console.error("Erro de rede/servidor:", error); + return false; + } finally { + setShowSpinner(false); + } + }, + [authHeader, fetchAppointments] + ); + const deleteConsulta = useCallback( + async (id) => { + const success = await updateAppointmentStatus(id, { + status: "cancelled", + cancellation_reason: motivoCancelamento, + updated_at: new Date().toISOString(), + }); + if (success) { + setShowDeleteModal(false); + setMotivoCancelamento(""); + setSelectedId("0"); + } else { + alert("Falha ao cancelar a consulta."); + } + }, + [motivoCancelamento, updateAppointmentStatus] + ); - const confirmConsulta = useCallback(async (id) => { - const success = await updateAppointmentStatus(id, { status: "agendado", cancellation_reason: null, updated_at: new Date().toISOString() }); - if (success) { - setSelectedId('0'); - } else { - alert("Falha ao reverter o cancelamento."); - } - }, [updateAppointmentStatus]); + const confirmConsulta = useCallback( + async (id) => { + const success = await updateAppointmentStatus(id, { + status: "agendado", + cancellation_reason: null, + updated_at: new Date().toISOString(), + }); + if (success) { + setSelectedId("0"); + } else { + alert("Falha ao reverter o cancelamento."); + } + }, + [updateAppointmentStatus] + ); useEffect(() => { if(authHeader) { @@ -150,24 +174,24 @@ const Agendamento = () => { return; } - setShowSpinner(true); + setShowSpinner(true); const appointmentsToShow = listaTodosAgendamentos; - const patientIdsToFetch = new Set(); - const doctorIdsToFetch = new Set(); + const patientIdsToFetch = new Set(); + const doctorIdsToFetch = new Set(); - appointmentsToShow.forEach(ag => { - if (ag.patient_id && !cachePacientes[ag.patient_id]) { - patientIdsToFetch.add(ag.patient_id); - } - if (ag.doctor_id && !cacheMedicos[ag.doctor_id]) { - doctorIdsToFetch.add(ag.doctor_id); - } - }); + appointmentsToShow.forEach((ag) => { + if (ag.patient_id && !cachePacientes[ag.patient_id]) { + patientIdsToFetch.add(ag.patient_id); + } + if (ag.doctor_id && !cacheMedicos[ag.doctor_id]) { + doctorIdsToFetch.add(ag.doctor_id); + } + }); - const fetchPromises = []; + const fetchPromises = []; if (patientIdsToFetch.size > 0) { const query = `id=in.(${Array.from(patientIdsToFetch).join(',')})`; @@ -180,95 +204,106 @@ const Agendamento = () => { fetchPromises.push(Promise.resolve(null)); } - if (doctorIdsToFetch.size > 0) { - const query = `id=in.(${Array.from(doctorIdsToFetch).join(',')})`; - fetchPromises.push( - fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?${query}&select=id,full_name`, { - headers: { apikey: API_KEY, Authorization: authHeader } - }).then(res => res.json()) - ); - } else { - fetchPromises.push(Promise.resolve(null)); + if (doctorIdsToFetch.size > 0) { + const query = `id=in.(${Array.from(doctorIdsToFetch).join(",")})`; + fetchPromises.push( + fetch( + `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?${query}&select=id,full_name`, + { + headers: { apikey: API_KEY, Authorization: authHeader }, } - - const [newPatients, newDoctors] = await Promise.all(fetchPromises); + ).then((res) => res.json()) + ); + } else { + fetchPromises.push(Promise.resolve(null)); + } - const updatedPatientCache = { ...cachePacientes }; - if (newPatients) newPatients.forEach(p => updatedPatientCache[p.id] = p); + const [newPatients, newDoctors] = await Promise.all(fetchPromises); - const updatedDoctorCache = { ...cacheMedicos }; - if (newDoctors) newDoctors.forEach(d => updatedDoctorCache[d.id] = d); - - setCachePacientes(updatedPatientCache); - setCacheMedicos(updatedDoctorCache); + const updatedPatientCache = { ...cachePacientes }; + if (newPatients) + newPatients.forEach((p) => (updatedPatientCache[p.id] = p)); - const newDict = {}; - const newFila = []; + const updatedDoctorCache = { ...cacheMedicos }; + if (newDoctors) newDoctors.forEach((d) => (updatedDoctorCache[d.id] = d)); - for (const agendamento of appointmentsToShow) { - const medico = updatedDoctorCache[agendamento.doctor_id]; - const paciente = updatedPatientCache[agendamento.patient_id]; + setCachePacientes(updatedPatientCache); + setCacheMedicos(updatedDoctorCache); - if (!medico || !paciente) continue; + const newDict = {}; + const newFila = []; - const agendamentoMelhorado = { - ...agendamento, - medico_nome: medico.full_name || 'N/A', - paciente_nome: paciente.full_name || 'N/A', - paciente_cpf: paciente.cpf || 'N/A' - }; + for (const agendamento of appointmentsToShow) { + const medico = updatedDoctorCache[agendamento.doctor_id]; + const paciente = updatedPatientCache[agendamento.patient_id]; - 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] = []; - newDict[DiaAgendamento].push(agendamentoMelhorado); - } - } + if (!medico || !paciente) continue; - for (const key in newDict) { - newDict[key].sort((a, b) => new Date(a.scheduled_at) - new Date(b.scheduled_at)); - } - - setAgendamentosOrganizados(newDict); - setFilaEsperaData(newFila); - setShowSpinner(false); + const agendamentoMelhorado = { + ...agendamento, + medico_nome: medico.full_name || "N/A", + paciente_nome: paciente.full_name || "N/A", + paciente_cpf: paciente.cpf || "N/A", }; + 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] = []; + newDict[DiaAgendamento].push(agendamentoMelhorado); + } + } + + for (const key in newDict) { + newDict[key].sort( + (a, b) => new Date(a.scheduled_at) - new Date(b.scheduled_at) + ); + } + + setAgendamentosOrganizados(newDict); + setFilaEsperaData(newFila); + setShowSpinner(false); + }; + processData(); }, [listaTodosAgendamentos, authHeader]); - const handleEditConsulta = (agendamento) => { - setAgendamentoParaEdicao(agendamento); - setPageConsulta(true); - }; + const handleEditConsulta = (agendamento) => { + setAgendamentoParaEdicao(agendamento); + setPageConsulta(true); + }; - const handleSearchMedicos = (term) => { - setSearchTermDoctor(term); - if (term.trim()) { - const filtered = ListaDeMedicos.filter(medico => - medico.nomeMedico.toLowerCase().includes(term.toLowerCase()) - ); - setFiltredTodosMedicos(filtered); - } else { - setFiltredTodosMedicos([]); - setMedicoFiltrado({ id: "vazio" }); - } - }; - - 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 handleSearchMedicos = (term) => { + setSearchTermDoctor(term); + if (term.trim()) { + const filtered = ListaDeMedicos.filter((medico) => + medico.nomeMedico.toLowerCase().includes(term.toLowerCase()) + ); + setFiltredTodosMedicos(filtered); + } else { + setFiltredTodosMedicos([]); + setMedicoFiltrado({ id: "vazio" }); + } + }; + + 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']; @@ -293,68 +328,86 @@ const DeleteModal = () => ( -); + ); + useEffect(() => { + setQuickJump({ + month: currentDate.month(), + year: currentDate.year(), + }); + }, [currentDate]); - useEffect(() => { - setQuickJump({ - month: currentDate.month(), - year: currentDate.year() - }); - }, [currentDate]); - - 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?.medico_nome?.toLowerCase() || '').includes(term) - ); - }, [waitlistSearch, filaEsperaData]); + 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?.medico_nome?.toLowerCase() || "").includes(term) + ); + }, [waitlistSearch, filaEsperaData]); - const applySortingWaitlist = useCallback((arr) => { - if (!Array.isArray(arr) || !waitSortKey) return arr; - const copy = [...arr]; - const key = waitSortKey; - const dir = waitSortDir === 'asc' ? 1 : -1; - copy.sort((a, b) => { - const valA = key === 'data' ? new Date(a.agendamento.scheduled_at) : (a.Infos?.[`${key}_nome`] || ''); - const valB = key === 'data' ? new Date(b.agendamento.scheduled_at) : (b.Infos?.[`${key}_nome`] || ''); - if (valA < valB) return -1 * dir; - if (valA > valB) return 1 * dir; - return 0; - }); - return copy; - }, [waitSortKey, waitSortDir]); + const applySortingWaitlist = useCallback( + (arr) => { + if (!Array.isArray(arr) || !waitSortKey) return arr; + const copy = [...arr]; + const key = waitSortKey; + const dir = waitSortDir === "asc" ? 1 : -1; + copy.sort((a, b) => { + const valA = + key === "data" + ? new Date(a.agendamento.scheduled_at) + : a.Infos?.[`${key}_nome`] || ""; + const valB = + key === "data" + ? new Date(b.agendamento.scheduled_at) + : b.Infos?.[`${key}_nome`] || ""; + if (valA < valB) return -1 * dir; + if (valA > valB) return 1 * dir; + return 0; + }); + return copy; + }, + [waitSortKey, waitSortDir] + ); - const filaEsperaOrdenada = useMemo(() => applySortingWaitlist(filaEsperaFiltrada), [filaEsperaFiltrada, applySortingWaitlist]); - const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1; - const waitIndiceInicial = (waitPage - 1) * waitPerPage; - const waitIndiceFinal = waitIndiceInicial + waitPerPage; - const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); + const filaEsperaOrdenada = useMemo( + () => applySortingWaitlist(filaEsperaFiltrada), + [filaEsperaFiltrada, applySortingWaitlist] + ); + 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; - }; + 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]); + useEffect(() => { + setWaitPage(1); + }, [waitlistSearch, waitSortKey, waitSortDir]); - const handleQuickJumpChange = (type, value) => { - setQuickJump(prev => ({ ...prev, [type]: Number(value) })); - }; + const handleQuickJumpChange = (type, value) => { + setQuickJump((prev) => ({ ...prev, [type]: Number(value) })); + }; - const applyQuickJump = () => { - let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); - setCurrentDate(newDate); - setSelectedDay(newDate); - }; + const applyQuickJump = () => { + let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1); + setCurrentDate(newDate); + setSelectedDay(newDate); + }; return (