)}
- {/* Notificação simples: Sem permissão (exibe sem backdrop escuro) - centralizada */}
- {showNoPermission && (
-
- )}
-
- {/* Confirm delete modal (simples: Sim / Não) */}
- {showConfirmDelete && toDelete && (
-
@@ -283,4 +294,4 @@ const Details = (DictInfo) => {
);
};
-export default Details;
+export default Details;
\ No newline at end of file
diff --git a/src/pages/secretaria/DisponibilidadesMedico.jsx b/src/pages/secretaria/DisponibilidadesMedico.jsx
index c32cb75..a6c1e57 100644
--- a/src/pages/secretaria/DisponibilidadesMedico.jsx
+++ b/src/pages/secretaria/DisponibilidadesMedico.jsx
@@ -1,359 +1,440 @@
//DisponibilidadesDoctorPage.jsx
-import { useState, useEffect, useCallback, useMemo } from "react";
-import { GetAllDoctors } from "../../_assets/utils/Functions-Endpoints/Doctor";
+import { useState, useEffect, useMemo } from "react";
import { useAuth } from "../../_assets/utils/AuthProvider";
import API_KEY from "../../_assets/utils/apiKeys";
import HorariosDisponibilidade from "../../components/medico/HorariosDisponibilidade";
+// import "./style/DisponibilidadesDoctorPage.css";
-const ENDPOINT =
- "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
+const ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
+const DOCTORS_ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors";
-const diasDaSemana = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
+const diasDaSemana = [
+ "Domingo",
+ "Segunda",
+ "Terça",
+ "Quarta",
+ "Quinta",
+ "Sexta",
+ "Sábado"
+];
+const weekdayNumToStr = {
+ 0: "sunday",
+ 1: "monday",
+ 2: "tuesday",
+ 3: "wednesday",
+ 4: "thursday",
+ 5: "friday",
+ 6: "saturday",
+};
+const weekdayStrToNum = Object.fromEntries(
+ Object.entries(weekdayNumToStr).map(([num, str]) => [str, Number(num)])
+);
const DisponibilidadesDoctorPage = () => {
const { getAuthorizationHeader } = useAuth();
const [disponibilidades, setDisponibilidades] = useState([]);
- const [loading, setLoading] = useState(false);
const [doctors, setDoctors] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
- const [selectedDoctor, setSelectedDoctor] = useState(null);
const [editando, setEditando] = useState(null);
- const [doctorsLoading, setDoctorsLoading] = useState(true);
+ const [expandedDoctors, setExpandedDoctors] = useState({});
+ const [showSuggestions, setShowSuggestions] = useState(false);
+ const [availabilityEdit, setAvailabilityEdit] = useState([]);
+
+ const getHeaders = () => {
+ const myHeaders = new Headers();
+ const authHeader = getAuthorizationHeader();
+ if (authHeader) myHeaders.append("Authorization", authHeader);
+ myHeaders.append("Content-Type", "application/json");
+ if (API_KEY) myHeaders.append("apikey", API_KEY);
+ myHeaders.append("Prefer", "return=representation");
+ return myHeaders;
+ };
useEffect(() => {
const fetchDoctors = async () => {
try {
- setDoctorsLoading(true);
- const data = await GetAllDoctors();
- console.log("Médicos recebidos:", data);
- setDoctors(Array.isArray(data) ? data : []);
+ const requestOptions = {
+ method: "GET",
+ headers: getHeaders(),
+ };
+ const response = await fetch(DOCTORS_ENDPOINT, requestOptions);
+ const result = await response.json();
+ setDoctors(Array.isArray(result) ? result : []);
} catch (error) {
- console.error("Erro ao carregar médicos:", error);
setDoctors([]);
- } finally {
- setDoctorsLoading(false);
}
};
fetchDoctors();
- }, []);
-
- const resolveAuthHeader = () => {
- try {
- const h = getAuthorizationHeader();
- return h || "";
- } catch {
- return "";
- }
- };
-
- const getHeaders = () => {
- const myHeaders = new Headers();
- const authHeader = resolveAuthHeader();
- if (authHeader) myHeaders.append("Authorization", authHeader);
- myHeaders.append("Content-Type", "application/json");
- if (API_KEY) myHeaders.append("apikey", API_KEY);
- return myHeaders;
- };
-
- const fetchDisponibilidades = useCallback(async (doctorId = null) => {
- setLoading(true);
- let url = ENDPOINT;
- if (doctorId) {
- url += `?doctor_id=eq.${doctorId}&select=*&order=weekday.asc,start_time.asc`;
- } else {
- url += `?select=*&order=doctor_id.asc,weekday.asc,start_time.asc`;
- }
- try {
- const res = await fetch(url, { method: "GET", headers: getHeaders() });
- if (!res.ok) throw new Error(`Erro HTTP: ${res.status}`);
- const data = await res.json();
- setDisponibilidades(Array.isArray(data) ? data : []);
- } catch (e) {
- console.error("Erro ao buscar disponibilidades:", e);
- alert("Erro ao carregar disponibilidades");
- setDisponibilidades([]);
- } finally {
- setLoading(false);
- }
- }, []);
+ }, [getAuthorizationHeader]);
useEffect(() => {
- if (selectedDoctor) {
- fetchDisponibilidades(selectedDoctor.id);
- } else {
- fetchDisponibilidades(null);
- }
- }, [selectedDoctor, fetchDisponibilidades]);
+ const fetchDisponibilidades = async () => {
+ try {
+ const res = await fetch(ENDPOINT, { method: "GET", headers: getHeaders() });
+ if (res.ok) {
+ const data = await res.json();
+ setDisponibilidades(Array.isArray(data) ? data : []);
+ }
+ } catch (error) {
+ setDisponibilidades([]);
+ }
+ };
+ fetchDisponibilidades();
+ }, [getAuthorizationHeader]);
- const atualizarDisponibilidade = async (id, dadosAtualizados) => {
+ const toggleExpandDoctor = (doctorId) => {
+ setExpandedDoctors((prev) => ({ ...prev, [doctorId]: !prev[doctorId] }));
+ };
+
+ const salvarTodasDisponibilidades = async (doctorId, horariosAtualizados) => {
try {
- const res = await fetch(`${ENDPOINT}?id=eq.${id}`, {
- method: "PATCH",
- headers: getHeaders(),
- body: JSON.stringify(dadosAtualizados),
+ const headers = getHeaders();
+ const promises = [];
+ const currentIds = new Set();
+
+ for (const dia of horariosAtualizados) {
+ if (dia.isChecked && dia.blocos.length > 0) {
+ for (const bloco of dia.blocos) {
+ const inicio = bloco.inicio.includes(":") ? bloco.inicio : bloco.inicio + ":00";
+ const termino = bloco.termino.includes(":") ? bloco.termino : bloco.termino + ":00";
+
+ const payload = {
+ doctor_id: doctorId,
+ weekday: weekdayNumToStr[dia.weekday],
+ start_time: inicio,
+ end_time: termino,
+ slot_minutes: bloco.slot_minutes || 30,
+ appointment_type: bloco.appointment_type || "presencial",
+ active: true,
+ };
+
+ if (bloco.id && !bloco.isNew) {
+ currentIds.add(bloco.id);
+ promises.push(
+ fetch(`${ENDPOINT}?id=eq.${bloco.id}`, {
+ method: "PATCH",
+ headers,
+ body: JSON.stringify(payload),
+ }).then(() => ({ type: 'PATCH', id: bloco.id }))
+ );
+ } else {
+ promises.push(
+ fetch(ENDPOINT, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(payload),
+ })
+ .then(res => res.json())
+ .then(data => {
+ const createdItem = Array.isArray(data) ? data[0] : data;
+ return { type: 'POST', id: createdItem?.id };
+ })
+ );
+ }
+ }
+ }
+ }
+
+ const results = await Promise.all(promises);
+
+ results.forEach(res => {
+ if (res.type === 'POST' && res.id) currentIds.add(res.id);
});
+
+ const existingRes = await fetch(`${ENDPOINT}?doctor_id=eq.${String(doctorId)}`, {
+ method: "GET", headers
+ });
+
+ if (existingRes.ok) {
+ const existingData = await existingRes.json();
+ const deletePromises = existingData
+ .filter(dbItem => !currentIds.has(dbItem.id))
+ .map(dbItem =>
+ fetch(`${ENDPOINT}?id=eq.${dbItem.id}`, { method: "DELETE", headers })
+ );
+ await Promise.all(deletePromises);
+ }
+
+ setEditando(null);
+ setAvailabilityEdit([]);
+ const res = await fetch(ENDPOINT, { method: "GET", headers: getHeaders() });
if (res.ok) {
- alert("Disponibilidade atualizada com sucesso!");
- setEditando(null);
- if (selectedDoctor) fetchDisponibilidades(selectedDoctor.id);
- else fetchDisponibilidades();
- } else {
- const errorData = await res.json();
- console.error("Erro na resposta:", errorData);
- alert("Erro ao atualizar disponibilidade");
+ const data = await res.json();
+ setDisponibilidades(Array.isArray(data) ? data : []);
}
} catch (error) {
- console.error("Erro:", error);
- alert("Falha ao conectar com o servidor");
+ console.error(error);
}
};
const deletarDisponibilidade = async (id) => {
- if (!window.confirm("Deseja realmente excluir esta disponibilidade?"))
- return;
+ if (!window.confirm("Deseja realmente excluir esta disponibilidade?")) return;
try {
- const res = await fetch(`${ENDPOINT}?id=eq.${id}`, {
- method: "DELETE",
- headers: getHeaders(),
- });
- if (res.ok) {
- alert("Disponibilidade excluída com sucesso!");
- setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
- } else {
- const errorData = await res.json();
- console.error("Erro na resposta:", errorData);
- alert("Erro ao excluir disponibilidade");
- }
- } catch (error) {
- console.error("Erro:", error);
- alert("Erro ao conectar com o servidor");
- }
+ const res = await fetch(`${ENDPOINT}?id=eq.${id}`, { method: "DELETE", headers: getHeaders() });
+ if (res.ok) setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
+ } catch (error) {}
};
- const initialAvailabilityParaEdicao = useMemo(
- () =>
- diasDaSemana.map((dia, weekdayIndex) => {
- const blocosDoDia = disponibilidades
- .filter((d) => d.weekday === weekdayIndex && d.active !== false)
- .map((d) => ({
- id: d.id,
- inicio: d.start_time ? d.start_time.substring(0, 5) : "07:00",
- termino: d.end_time ? d.end_time.substring(0, 5) : "17:00",
- isNew: false,
- slot_minutes: d.slot_minutes || 30,
- appointment_type: d.appointment_type || "presencial",
- active: d.active !== false,
- }));
- return {
- dia,
- weekday: weekdayIndex,
- isChecked: blocosDoDia.length > 0,
- blocos: blocosDoDia,
- };
- }),
- [disponibilidades]
- );
+ const disponibilidadesAgrupadas = useMemo(() => {
+ const agrupadas = {};
+ doctors.forEach((doctor) => {
+ agrupadas[doctor.id] = {
+ doctor_id: doctor.id,
+ doctor_name: doctor.full_name || doctor.name,
+ disponibilidades: [],
+ };
+ });
+ disponibilidades.forEach((disp) => {
+ if (agrupadas[disp.doctor_id]) agrupadas[disp.doctor_id].disponibilidades.push(disp);
+ });
+ Object.values(agrupadas).forEach((grupo) => {
+ if (grupo.disponibilidades.length === 0) {
+ grupo.disponibilidades.push({
+ id: `empty-${grupo.doctor_id}`,
+ doctor_id: grupo.doctor_id,
+ doctor_name: grupo.doctor_name,
+ is_empty: true,
+ });
+ }
+ });
+ let resultado = Object.values(agrupadas);
+ if (searchTerm) resultado = resultado.filter((grupo) => grupo.doctor_name.toLowerCase().includes(searchTerm.toLowerCase()));
+ return resultado;
+ }, [disponibilidades, doctors, searchTerm]);
+
+ const formatTime = (timeString) => {
+ if (!timeString) return "";
+ return timeString.includes(":") ? timeString.substring(0, 5) : timeString;
+ };
+
+ const getDiaSemana = (weekday) => {
+ const dias = {
+ 0: "Domingo",
+ 1: "Segunda",
+ 2: "Terça",
+ 3: "Quarta",
+ 4: "Quinta",
+ 5: "Sexta",
+ 6: "Sábado",
+ sunday: "Domingo",
+ monday: "Segunda",
+ tuesday: "Terça",
+ wednesday: "Quarta",
+ thursday: "Quinta",
+ friday: "Sexta",
+ saturday: "Sábado",
+ };
+ const key = typeof weekday === "string" ? weekday.toLowerCase() : weekday;
+ return dias[key] || "Desconhecido";
+ };
+
+ const initialAvailabilityParaEdicao = useMemo(() => {
+ if (!editando) return [];
+ const disponibilidadesMedico = disponibilidades.filter((d) => String(d.doctor_id) === String(editando));
+ const blocosPorDia = {};
+ disponibilidadesMedico.forEach((d) => {
+ const num = typeof d.weekday === "string" ? weekdayStrToNum[d.weekday.toLowerCase()] : d.weekday;
+ if (num === undefined) return;
+ if (!blocosPorDia[num]) blocosPorDia[num] = [];
+ if (d.active !== false) {
+ blocosPorDia[num].push({
+ id: d.id,
+ inicio: formatTime(d.start_time) || "07:00",
+ termino: formatTime(d.end_time) || "17:00",
+ slot_minutes: d.slot_minutes || 30,
+ appointment_type: d.appointment_type || "presencial",
+ isNew: false,
+ });
+ }
+ });
+ const resultado = [1, 2, 3, 4, 5, 6, 0].map((weekday) => {
+ const blocosDoDia = blocosPorDia[weekday] || [];
+ return {
+ dia: diasDaSemana[weekday],
+ weekday: weekday,
+ isChecked: blocosDoDia.length > 0,
+ blocos:
+ blocosDoDia.length > 0
+ ? blocosDoDia
+ : [
+ {
+ id: null,
+ inicio: "07:00",
+ termino: "17:00",
+ slot_minutes: 30,
+ appointment_type: "presencial",
+ isNew: true,
+ },
+ ],
+ };
+ });
+ return resultado;
+ }, [disponibilidades, editando]);
const handleUpdateHorarios = (horariosAtualizados) => {
- const bloco = horariosAtualizados
- .flatMap((d) => d.blocos)
- .find((b) => b.id === editando);
- if (!bloco) return alert("Bloco não encontrado.");
- const dadosAtualizados = {
- start_time: bloco.inicio + ":00",
- end_time: bloco.termino + ":00",
- slot_minutes: bloco.slot_minutes,
- appointment_type: bloco.appointment_type,
- active: bloco.active,
- };
- atualizarDisponibilidade(editando, dadosAtualizados);
+ if (!editando) return;
+ setAvailabilityEdit(horariosAtualizados || []);
};
const filteredDoctors = useMemo(() => {
if (!searchTerm) return doctors;
- return doctors.filter((doc) =>
- doc.name.toLowerCase().includes(searchTerm.toLowerCase())
- );
+ return doctors.filter((doc) => (doc.full_name || doc.name).toLowerCase().includes(searchTerm.toLowerCase()));
}, [doctors, searchTerm]);
- return (
-
-
- Disponibilidades dos Médicos
-
+ const handleCancelarEdicao = () => {
+ setEditando(null);
+ setAvailabilityEdit([]);
+ };
-
-
{
- setSearchTerm(e.target.value);
- setSelectedDoctor(null);
- }}
- style={{
- border: "1px solid #ccc",
- borderRadius: "4px",
- padding: "6px",
- width: "300px",
- }}
- />
- {searchTerm && (
-
{
+ setSearchTerm(doctor.full_name || doctor.name);
+ setShowSuggestions(false);
+ };
+
+ const handleClearSearch = () => {
+ setSearchTerm("");
+ setShowSuggestions(false);
+ };
+
+ const getStatusBadgeClass = (disp) => {
+ if (disp.is_empty) return "status-badge status-not-configured";
+ if (disp.active === false) return "status-badge status-inactive";
+ return "status-badge status-active";
+ };
+
+ const getStatusText = (disp) => {
+ if (disp.is_empty) return "Não configurado";
+ if (disp.active === false) return "Inativa";
+ return "Ativa";
+ };
+
+ return (
+
+
Disponibilidades dos Médicos
+
+
+
+ {
+ setSearchTerm(e.target.value);
+ setShowSuggestions(true);
}}
- >
- {filteredDoctors.length > 0 ? (
- filteredDoctors.map((doc) => (
-
- {
- setSelectedDoctor(doc);
- setSearchTerm(doc.name);
- }}
- style={{
- padding: "6px 8px",
- cursor: "pointer",
- borderBottom: "1px solid #eee",
- }}
- >
- {doc.name}
-
- ))
- ) : (
- -
- Nenhum médico encontrado
-
- )}
-
+ onFocus={() => setShowSuggestions(true)}
+ className="search-input"
+ />
+ {searchTerm && (
+
+ )}
+
+
+ {showSuggestions && searchTerm && filteredDoctors.length > 0 && (
+
+ {filteredDoctors.map((doc) => (
+
handleDoctorSelect(doc)} className="suggestion-item">
+ {doc.full_name || doc.name}
+
+ ))}
+
)}
-
- {editando ? "Editar Disponibilidade" : "Lista de Disponibilidades"}{" "}
- ({disponibilidades.length})
-
+
{editando ? `Editar Horários` : "Lista de Disponibilidades"}
- {loading ? (
-
Carregando...
- ) : disponibilidades.length === 0 ? (
-
Nenhuma disponibilidade encontrada.
+ {doctors.length === 0 ? (
+
Carregando médicos...
) : editando ? (
<>
-
-
+
+ {initialAvailabilityParaEdicao.length > 0 ? (
+
+ ) : (
+
Carregando horários para edição...
+ )}
+
+
+
+
+
+
+
>
) : (
-
-
-
- | Médico |
- Dia da Semana |
- Início |
- Término |
- Intervalo (min) |
- Tipo |
- Status |
- Ações |
-
-
-
- {disponibilidades.map((disp) => {
- const medico = doctors.find((d) => d.id === disp.doctor_id);
- return (
-
- | {medico ? medico.name : disp.doctor_id} |
- {diasDaSemana[disp.weekday]} |
- {disp.start_time} |
- {disp.end_time} |
- {disp.slot_minutes || 30} |
- {disp.appointment_type || "presencial"} |
-
-
- {disp.active === false ? "Inativa" : "Ativa"}
-
- |
-
-
-
+
+ {disponibilidadesAgrupadas.length === 0 ? (
+ Nenhum médico encontrado
+ ) : (
+ disponibilidadesAgrupadas.map((grupo) => (
+
+ toggleExpandDoctor(grupo.doctor_id)}>
+
+ {grupo.doctor_name}
+ ({grupo.disponibilidades.filter((d) => !d.is_empty).length} horários)
+
+ ▼
+
-
+ {expandedDoctors[grupo.doctor_id] && (
+
+
+
+
+
+
+
+
+
+ | Dia da Semana |
+ Início |
+ Término |
+ Intervalo (min) |
+ Tipo |
+ Status |
+ Ações |
+
+
+
+ {grupo.disponibilidades.map((disp) => (
+
+ | {disp.is_empty ? "Nenhum horário cadastrado" : getDiaSemana(disp.weekday)} |
+ {disp.is_empty ? "-" : formatTime(disp.start_time)} |
+ {disp.is_empty ? "-" : formatTime(disp.end_time)} |
+ {disp.is_empty ? "-" : disp.slot_minutes || 30} |
+ {disp.is_empty ? "-" : disp.appointment_type || "presencial"} |
+
+ {getStatusText(disp)}
+ |
+ {!disp.is_empty && } |
+
+ ))}
+
+
+
- |
-
- );
- })}
-
-
+ )}
+
+ ))
+ )}
+
)}
@@ -361,4 +442,4 @@ const DisponibilidadesDoctorPage = () => {
);
};
-export default DisponibilidadesDoctorPage;
+export default DisponibilidadesDoctorPage;
\ No newline at end of file
diff --git a/src/pages/secretaria/EditarMedico.jsx b/src/pages/secretaria/EditarMedico.jsx
index cf2908c..1a43cbe 100644
--- a/src/pages/secretaria/EditarMedico.jsx
+++ b/src/pages/secretaria/EditarMedico.jsx
@@ -1,150 +1,337 @@
//DoctorEditPage.jsx
//Nesta página falta: mudar nomes
-import { useEffect, useState } from "react";
-import { useParams, useSearchParams } from "react-router-dom";
-import { GetDoctorByID } from "../../_assets/utils/Functions-Endpoints/Doctor";
+import { useState, useEffect, useMemo } from "react";
+import { useParams, useNavigate, useLocation } from "react-router-dom";
import { useAuth } from "../../_assets/utils/AuthProvider";
import API_KEY from "../../_assets/utils/apiKeys";
import DoctorForm from "../../components/medico/FormCadastroMedico";
-const ENDPOINT_AVAILABILITY =
- "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
+const ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors";
+const ENDPOINT_AVAILABILITY = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
-const DoctorEditPage = () => {
+const diasDaSemana = ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"];
+const weekdayNumToStr = {
+ 0: "sunday",
+ 1: "monday",
+ 2: "tuesday",
+ 3: "wednesday",
+ 4: "thursday",
+ 5: "friday",
+ 6: "saturday",
+};
+const weekdayStrToNum = Object.fromEntries(
+ Object.entries(weekdayNumToStr).map(([num, str]) => [str, Number(num)])
+);
+
+const EditDoctorPage = () => {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const location = useLocation();
const { getAuthorizationHeader } = useAuth();
- const [DoctorToPUT, setDoctorPUT] = useState({});
+
+ const [doctor, setDoctor] = useState(null);
+ const [availability, setAvailability] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isSaving, setIsSaving] = useState(false);
- const Parametros = useParams();
- const [searchParams] = useSearchParams();
- const DoctorID = Parametros.id;
- const availabilityId = searchParams.get("availabilityId");
+ const effectiveId = id;
- const [availabilityToPATCH, setAvailabilityToPATCH] = useState(null);
- const [mode, setMode] = useState("doctor");
-
- useEffect(() => {
+ const getHeaders = () => {
+ const myHeaders = new Headers();
const authHeader = getAuthorizationHeader();
-
- if (availabilityId) {
- setMode("availability");
-
- fetch(`${ENDPOINT_AVAILABILITY}?id=eq.${availabilityId}&select=*`, {
- method: "GET",
- headers: {
- apikey: API_KEY,
- Authorization: authHeader,
- },
- })
- .then((res) => res.json())
- .then((data) => {
- if (data && data.length > 0) {
- setAvailabilityToPATCH(data[0]);
- console.log("Disponibilidade vinda da API:", data[0]);
- }
- })
- .catch((err) => console.error("Erro ao buscar disponibilidade:", err));
- } else {
- setMode("doctor");
- GetDoctorByID(DoctorID, authHeader)
- .then((data) => {
- console.log(data, "médico vindo da API");
- setDoctorPUT(data[0]);
- })
- .catch((err) => console.error("Erro ao buscar paciente:", err));
- }
- }, [DoctorID, availabilityId, getAuthorizationHeader]);
-
- const HandlePutDoctor = async () => {
- const authHeader = getAuthorizationHeader();
-
- var myHeaders = new Headers();
- myHeaders.append("apikey", API_KEY);
- myHeaders.append("Authorization", authHeader);
+ if (authHeader) myHeaders.append("Authorization", authHeader);
myHeaders.append("Content-Type", "application/json");
+ if (API_KEY) myHeaders.append("apikey", API_KEY);
+ myHeaders.append("Prefer", "return=representation");
+ return myHeaders;
+ };
- var raw = JSON.stringify(DoctorToPUT);
-
- console.log("Enviando médico para atualização (PUT):", DoctorToPUT);
-
- var requestOptions = {
- method: "PUT",
- headers: myHeaders,
- body: raw,
- redirect: "follow",
- };
-
+ const salvarDisponibilidades = async (doctorId, horariosAtualizados) => {
try {
- const response = await fetch(
- `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?id=eq.${DoctorID}`,
- requestOptions
+ const headers = getHeaders();
+ const promises = [];
+ const currentIds = new Set();
+
+ for (const dia of horariosAtualizados) {
+ if (dia.isChecked && dia.blocos.length > 0) {
+ for (const bloco of dia.blocos) {
+ const inicio = bloco.inicio.includes(":") ? bloco.inicio : bloco.inicio + ":00";
+ const termino = bloco.termino.includes(":") ? bloco.termino : bloco.termino + ":00";
+
+ const payload = {
+ doctor_id: doctorId,
+ weekday: weekdayNumToStr[dia.weekday],
+ start_time: inicio,
+ end_time: termino,
+ slot_minutes: bloco.slot_minutes || 30,
+ appointment_type: bloco.appointment_type || "presencial",
+ active: true,
+ };
+
+ if (bloco.id && !bloco.isNew) {
+ currentIds.add(bloco.id);
+ promises.push(
+ fetch(`${ENDPOINT_AVAILABILITY}?id=eq.${bloco.id}`, {
+ method: "PATCH",
+ headers,
+ body: JSON.stringify(payload),
+ }).then((res) => {
+ if (!res.ok) throw new Error(`Erro no PATCH: ${res.status}`);
+ return { type: "PATCH", id: bloco.id };
+ })
+ );
+ } else {
+ promises.push(
+ fetch(ENDPOINT_AVAILABILITY, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(payload),
+ })
+ .then((res) => res.json())
+ .then((data) => {
+ const createdItem = Array.isArray(data) ? data[0] : data;
+ if (createdItem && createdItem.id) {
+ return { type: "POST", id: createdItem.id };
+ }
+ return { type: "POST", id: null };
+ })
+ );
+ }
+ }
+ }
+ }
+
+ const results = await Promise.all(promises);
+
+ results.forEach((res) => {
+ if (res.type === "POST" && res.id) currentIds.add(res.id);
+ });
+
+ const existingDisponibilidadesRes = await fetch(
+ `${ENDPOINT_AVAILABILITY}?doctor_id=eq.${String(doctorId)}`,
+ { method: "GET", headers }
);
- console.log("Resposta PUT Doutor:", response);
- alert("Dados do médico atualizados com sucesso!");
+
+ if (existingDisponibilidadesRes.ok) {
+ const existingDisponibilidades = await existingDisponibilidadesRes.json();
+
+ const deletePromises = existingDisponibilidades
+ .filter((disp) => !currentIds.has(disp.id))
+ .map((disp) =>
+ fetch(`${ENDPOINT_AVAILABILITY}?id=eq.${disp.id}`, {
+ method: "DELETE",
+ headers,
+ })
+ );
+
+ await Promise.all(deletePromises);
+ }
+
+ const updatedResponse = await fetch(
+ `${ENDPOINT_AVAILABILITY}?doctor_id=eq.${doctorId}&order=weekday.asc,start_time.asc`,
+ { method: "GET", headers }
+ );
+
+ if (updatedResponse.ok) {
+ const updatedData = await updatedResponse.json();
+ setAvailability(updatedData);
+ }
} catch (error) {
- console.error("Erro ao atualizar médico:", error);
- alert("Erro ao atualizar dados do médico.");
throw error;
}
};
- // 2. Função para Atualizar DISPONIBILIDADE (PATCH)
- const HandlePatchAvailability = async (data) => {
- const authHeader = getAuthorizationHeader();
+ const normalizeAvailabilityForForm = (availabilityData) => {
+ if (!Array.isArray(availabilityData)) return [];
- var myHeaders = new Headers();
- myHeaders.append("apikey", API_KEY);
- myHeaders.append("Authorization", authHeader);
- myHeaders.append("Content-Type", "application/json");
+ const disponibilidadesMedico = availabilityData.filter((d) =>
+ String(d.doctor_id) === String(effectiveId) && d.active !== false
+ );
+ const blocosPorDia = {};
+
+ disponibilidadesMedico.forEach((d) => {
+ const num = typeof d.weekday === "string" ? weekdayStrToNum[d.weekday.toLowerCase()] : d.weekday;
+ if (num === undefined || num === null) return;
+ if (!blocosPorDia[num]) blocosPorDia[num] = [];
+ blocosPorDia[num].push({
+ id: d.id,
+ inicio: d.start_time?.substring(0, 5) || "07:00",
+ termino: d.end_time?.substring(0, 5) || "17:00",
+ slot_minutes: d.slot_minutes || 30,
+ appointment_type: d.appointment_type || "presencial",
+ isNew: false,
+ });
+ });
+
+ const resultado = [1, 2, 3, 4, 5, 6, 0].map((weekday) => {
+ const blocosDoDia = blocosPorDia[weekday] || [];
+ return {
+ dia: diasDaSemana[weekday],
+ weekday: weekday,
+ isChecked: blocosDoDia.length > 0,
+ blocos:
+ blocosDoDia.length > 0
+ ? blocosDoDia
+ : [
+ {
+ id: null,
+ inicio: "07:00",
+ termino: "17:00",
+ slot_minutes: 30,
+ appointment_type: "presencial",
+ isNew: true,
+ },
+ ],
+ };
+ });
+
+ return resultado;
+ };
- var raw = JSON.stringify(data);
+ const availabilityFormatted = useMemo(() => {
+ return normalizeAvailabilityForForm(availability);
+ }, [availability, effectiveId]);
- console.log("Enviando disponibilidade para atualização (PATCH):", data);
+ useEffect(() => {
+ const fetchDoctorData = async () => {
+ if (!effectiveId || effectiveId === "edit") {
+ alert("ID do médico não encontrado");
+ navigate("/secretaria/medicos");
+ return;
+ }
- var requestOptions = {
- method: "PATCH",
- headers: myHeaders,
- body: raw,
- redirect: "follow",
+ try {
+ const doctorResponse = await fetch(`${ENDPOINT}?id=eq.${effectiveId}`, {
+ method: "GET",
+ headers: getHeaders(),
+ });
+
+ if (!doctorResponse.ok) {
+ throw new Error("Erro ao carregar dados do médico");
+ }
+
+ const doctorData = await doctorResponse.json();
+ if (doctorData.length === 0) {
+ throw new Error("Médico não encontrado");
+ }
+
+ setDoctor(doctorData[0]);
+
+ const availabilityResponse = await fetch(
+ `${ENDPOINT_AVAILABILITY}?doctor_id=eq.${effectiveId}&order=weekday.asc,start_time.asc`,
+ {
+ method: "GET",
+ headers: getHeaders(),
+ }
+ );
+
+ if (availabilityResponse.ok) {
+ const availabilityData = await availabilityResponse.json();
+ setAvailability(availabilityData);
+ } else {
+ setAvailability([]);
+ }
+
+ } catch (error) {
+ alert("Erro ao carregar dados do médico");
+ navigate("/secretaria/medicos");
+ } finally {
+ setIsLoading(false);
+ }
};
- try {
- const response = await fetch(
- `${ENDPOINT_AVAILABILITY}?id=eq.${availabilityId}`,
- requestOptions
- );
- console.log("Resposta PATCH Disponibilidade:", response);
- alert("Disponibilidade atualizada com sucesso!");
- // Opcional: Redirecionar de volta para a lista de disponibilidades
- // navigate('/disponibilidades');
- } catch (error) {
- console.error("Erro ao atualizar disponibilidade:", error);
- alert("Erro ao atualizar disponibilidade.");
- throw error;
+ if (effectiveId) {
+ fetchDoctorData();
}
+ }, [effectiveId, navigate]);
+
+ const handleSave = async (formData) => {
+ const { availability: updatedAvailability, ...doctorDataToSave } = formData;
+
+ try {
+ setIsSaving(true);
+
+ const response = await fetch(`${ENDPOINT}?id=eq.${effectiveId}`, {
+ method: "PATCH",
+ headers: getHeaders(),
+ body: JSON.stringify(doctorDataToSave),
+ });
+
+ if (!response.ok) {
+ throw new Error("Erro ao salvar dados do médico");
+ }
+
+ if (updatedAvailability && updatedAvailability.length > 0) {
+ await salvarDisponibilidades(effectiveId, updatedAvailability);
+ }
+
+ alert("Médico e horários atualizados com sucesso!");
+ navigate("/secretaria/medicos");
+
+ } catch (error) {
+ alert(`Erro ao salvar dados: ${error.message}`);
+ } finally {
+ setIsSaving(false);
+ }
+ console.log('Horários a serem salvos:', updatedAvailability);
+ };
+
+ const handleCancel = () => {
+ navigate("/secretaria/medicos");
+ };
+
+ if (isLoading) {
+ return (
+
+
+
+ Carregando dados do médico ID: {effectiveId || "..."}
+
+
+ );
+ }
+
+ if (!doctor) {
+ if (!isLoading) {
+ return (
+
+
+ Médico não encontrado
+
+
+ );
+ }
+ return null;
+ }
+
+ const formData = {
+ ...doctor,
+ availability: (doctor && doctor.availability) ? doctor.availability : availabilityFormatted,
};
return (
-
-
- {mode === "availability"
- ? `Editar Horário Disponível (ID: ${availabilityId.substring(0, 8)})`
- : `Editar Médico (ID: ${DoctorID})`}
-
-
-
+
);
};
-export default DoctorEditPage;
+export default EditDoctorPage;
\ No newline at end of file
diff --git a/src/pages/secretaria/EditarPaciente.jsx b/src/pages/secretaria/EditarPaciente.jsx
index 1cf7c84..68c83b2 100644
--- a/src/pages/secretaria/EditarPaciente.jsx
+++ b/src/pages/secretaria/EditarPaciente.jsx
@@ -9,34 +9,16 @@ import API_KEY from '../../_assets/utils/apiKeys'
import PatientForm from '../../components/paciente/FormCadastroPaciente'
-
-const EditPage = (DictInfo) => {
+const EditPage = ({DictInfo}) => {
const navigate = useNavigate()
- const Parametros = useParams()
const [PatientToPUT, setPatientPUT] = useState({})
const { getAuthorizationHeader, isAuthenticated } = useAuth();
-console.log(DictInfo, "usuario vindo do set")
-const PatientID = Parametros.id
-
-
useEffect(() => {
- const authHeader = getAuthorizationHeader()
- console.log(DictInfo.DictInfo.id, "id do cabra")
-
- GetPatientByID(DictInfo.DictInfo.id, authHeader)
- .then((data) => {
- console.log(data[0], "paciente vindo da API");
- setPatientPUT(data[0]); // supabase retorna array
- })
- .catch((err) => console.error("Erro ao buscar paciente:", err));
-
-
-
+ setPatientPUT(DictInfo)
}, [DictInfo])
-
const HandlePutPatient = async () => {
const authHeader = getAuthorizationHeader()
@@ -46,9 +28,9 @@ const HandlePutPatient = async () => {
myHeaders.append("Authorization", authHeader);
myHeaders.append("Content-Type", "application/json");
- var raw = JSON.stringify(PatientToPUT);
+ var raw = JSON.stringify({...PatientToPUT, bmi:Number(PatientToPUT) || null});
- console.log("Enviando paciente para atualização:", PatientToPUT);
+ console.log("Enviando atualização:", PatientToPUT);
var requestOptions = {
method: 'PATCH',
@@ -57,26 +39,11 @@ const HandlePutPatient = async () => {
redirect: 'follow'
};
- try {
- const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?id=eq.${PatientID}`,requestOptions);
- console.log(response)
-
+ fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?id=eq.${PatientToPUT.id}`,requestOptions)
+ .then(response => console.log(response))
+ .then(result => console.log(result))
+ .catch(console.log("erro"))
- if(response.ok === false){
- console.error("Erro ao atualizar paciente:");
- }
- else{
- console.log("ATUALIZADO COM SUCESSO");
- navigate('/secretaria/pacientes')
- }
-
- return response;
- } catch (error) {
- console.error("Erro ao atualizar paciente:", error);
- throw error;
- }
-
-
};
return (
@@ -93,4 +60,4 @@ const HandlePutPatient = async () => {
)
}
-export default EditPage
+export default EditPage
\ No newline at end of file
diff --git a/src/pages/secretaria/ListaAgendamentos.jsx b/src/pages/secretaria/ListaAgendamentos.jsx
index 238225f..2022e82 100644
--- a/src/pages/secretaria/ListaAgendamentos.jsx
+++ b/src/pages/secretaria/ListaAgendamentos.jsx
@@ -3,7 +3,7 @@
import { useState, useMemo, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
-// Importação de endpoints para lógica da Fila de Espera e Médicos (versão main)
+import { Search, ChevronLeft, ChevronRight, Edit, Trash2 } from 'lucide-react';
import { GetPatientByID } from '../../_assets/utils/Functions-Endpoints/Patient.js';
import { GetAllDoctors, GetDoctorByID } from '../../_assets/utils/Functions-Endpoints/Doctor.js';
import { useAuth } from '../../_assets/utils/AuthProvider.js';
@@ -13,818 +13,455 @@ import AgendamentoCadastroManager from '../secretaria/CadastroAgendamento.jsx';
import TabelaAgendamentoDia from '../../components/agendamento/TabelaAgendamentoDia.jsx';
import TabelaAgendamentoSemana from '../../components/agendamento/TabelaAgendamentoSemana.jsx';
import TabelaAgendamentoMes from '../../components/agendamento/TabelaAgendamentoMes.jsx';
-import AgendamentosMes from '../../components/agendamento/DadosConsultasMock.js';
+import FormNovaConsulta from '../../components/agendamento/FormNovaConsulta.jsx';
+import CalendarComponent from '../../components/agendamento/Calendario.jsx';
import Spinner from '../../components/Spinner.jsx';
+// import "./style/Agendamento.css";
+// import './style/FilaEspera.css';
+
import dayjs from 'dayjs';
-//import "./style/Agendamento.css";
-//import './style/FilaEspera.css';
+import 'dayjs/locale/pt-br';
+import isBetween from 'dayjs/plugin/isBetween';
+import localeData from 'dayjs/plugin/localeData';
+
+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 [tabela, setTabela] = useState('diario');
- const [PageNovaConsulta, setPageConsulta] = useState(false);
- const [searchTerm, setSearchTerm] = useState('');
- const [agendamentos, setAgendamentos] = useState()
- const { getAuthorizationHeader } = useAuth()
- const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({})
-
- const [showDeleteModal, setShowDeleteModal] = useState(false)
- const [AgendamentoFiltrado, setAgendamentoFiltrado] = useState()
-
- const [ListaDeMedicos, setListaDeMedicos] = useState([])
- const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([])
- const [searchTermDoctor, setSearchTermDoctor] = useState('');
-
- const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" })
-
-
- const [cacheAgendamentos, setCacheAgendamentos] = useState([])
-
- const [showConfirmModal, setShowConfirmModal] = useState(false)
- const [motivoCancelamento, setMotivoCancelamento] = useState("")
-
- const [listaConsultasID, setListaConsultaID] = useState([])
- const [coresConsultas,setCoresConsultas] = useState([])
-
- const [showSpinner,setShowSpinner] = useState(true)
-
- // Estados para Fila de Espera (padrão TablePaciente)
- const [waitlistSearch, setWaitlistSearch] = useState('');
- const [waitSortKey, setWaitSortKey] = useState(null); // 'paciente' | 'medico' | 'data' | null
- const [waitSortDir, setWaitSortDir] = useState('asc'); // 'asc' | 'desc'
- const [waitPage, setWaitPage] = useState(1);
- const [waitPerPage, setWaitPerPage] = useState(10);
+ 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());
- let authHeader = getAuthorizationHeader()
-
-const cacheMedicos = {};
-const cachePacientes = {};
-
- useEffect(() => {
- if (!listaTodosAgendamentos.length) return;
- console.log("recarregando")
- const DictAgendamentosOrganizados = {};
- const ListaFilaDeEspera = [];
-
- const fetchDados = async () => {
- for (const agendamento of listaTodosAgendamentos) {
- // Cache de médico e paciente
- if (!cacheMedicos[agendamento.doctor_id]) {
- cacheMedicos[agendamento.doctor_id] = await GetDoctorByID(agendamento.doctor_id, authHeader);
- }
- if (!cachePacientes[agendamento.patient_id]) {
- cachePacientes[agendamento.patient_id] = await GetPatientByID(agendamento.patient_id, authHeader);
- }
-
- const medico = cacheMedicos[agendamento.doctor_id];
- const paciente = cachePacientes[agendamento.patient_id];
-
- if (agendamento.status === "requested") {
- ListaFilaDeEspera.push({
- agendamento,
- Infos: {
- nome_medico: medico[0]?.full_name,
- doctor_id: medico[0]?.id,
- patient_id: paciente[0]?.id,
- paciente_nome: paciente[0]?.full_name,
- paciente_cpf: paciente[0]?.cpf,
- },
- });
- } else {
- const DiaAgendamento = agendamento.scheduled_at.split("T")[0];
-
- let agendamentoMelhorado = {...agendamento, medico_nome: medico[0]?.full_name,
- doctor_id: medico[0]?.id,
- patient_id: paciente[0]?.id,
- paciente_nome: paciente[0]?.full_name,
- paciente_cpf: paciente[0]?.cpf, }
-
- if (DiaAgendamento in DictAgendamentosOrganizados) {
- DictAgendamentosOrganizados[DiaAgendamento].push(agendamentoMelhorado);
- } else {
- DictAgendamentosOrganizados[DiaAgendamento] = [agendamentoMelhorado];
- }
- }
- }
-
- // Ordenar por data
- for (const DiaAgendamento in DictAgendamentosOrganizados) {
- DictAgendamentosOrganizados[DiaAgendamento].sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at));
- }
-
- const chavesOrdenadas = Object.keys(DictAgendamentosOrganizados).sort();
-
- const DictAgendamentosFinal = {};
- for (const data of chavesOrdenadas) {
- DictAgendamentosFinal[data] = DictAgendamentosOrganizados[data];
- }
-
- setAgendamentosOrganizados(DictAgendamentosFinal);
- setFilaEsperaData(ListaFilaDeEspera);
- };
-
- fetchDados();
- }, [listaTodosAgendamentos]); // 👉 só recalcula quando a lista muda
-
-
- useEffect(() => {
- var myHeaders = new Headers();
- myHeaders.append("Authorization", authHeader);
- myHeaders.append("apikey", API_KEY)
-
- var requestOptions = {
- method: 'GET',
- headers: myHeaders,
- redirect: 'follow'
- };
-
- fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select&doctor_id&patient_id&status&scheduled_at&order&limit&offset", requestOptions)
- .then(response => response.json())
- .then(result => { setListaTodosAgendamentos(result); console.log(result) })
- .catch(error => console.log('error', error));
-
- const PegarTodosOsMedicos = async () => {
- let lista = []
- const TodosOsMedicos = await GetAllDoctors(authHeader)
-
- for (let d = 0; TodosOsMedicos.length > d; d++) {
- lista.push({ nomeMedico: TodosOsMedicos[d].full_name, idMedico: TodosOsMedicos[d].id })
- }
- setListaDeMedicos(lista)
- }
- PegarTodosOsMedicos()
-
- }, [])
-
-
- const deleteConsulta = (selectedPatientId) => {
- var myHeaders = new Headers();
- myHeaders.append("Content-Type", "application/json");
- myHeaders.append('apikey', API_KEY)
- myHeaders.append("authorization", authHeader)
-
- var raw = JSON.stringify({
- "status":"cancelled",
- "cancellation_reason": motivoCancelamento
+ const [editingAppointmentId, setEditingAppointmentId] = useState(null);
+
+ const [quickJump, setQuickJump] = useState({
+ month: currentDate.month(),
+ year: currentDate.year()
});
- var requestOptions = {
- method: 'PATCH',
- headers: myHeaders,
- body: raw,
- redirect: 'follow'
+
+ 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);
+ }
};
- fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
- .then(response => {if(response.status !== 200)(console.log(response))})
- .then(result => console.log(result))
- .catch(error => console.log('error', error));
- }
- const confirmConsulta = (selectedPatientId) => {
- var myHeaders = new Headers();
- myHeaders.append("Content-Type", "application/json");
- myHeaders.append('apikey', API_KEY)
- myHeaders.append("authorization", authHeader)
-
- var raw = JSON.stringify({ "status":"confirmed" });
-
- var requestOptions = {
- method: 'PATCH',
- headers: myHeaders,
- body: raw,
- redirect: 'follow'
- };
-
- fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
- .then(response => {if(response.status !== 200)(console.log(response))})
- .then(result => console.log(result))
- .catch(error => console.log('error', error));
- }
-
-
-
- const filteredAgendamentos = useMemo(() => {
- if (!searchTerm.trim()) {
- return AgendamentosMes;
- }
- const lowerCaseSearchTerm = searchTerm.toLowerCase();
- const filteredData = {};
-
- for (const semana in AgendamentosMes) {
- filteredData[semana] = {};
- for (const dia in AgendamentosMes[semana]) {
- filteredData[semana][dia] = AgendamentosMes[semana][dia].filter(agendamento =>
- agendamento.status === 'vazio' ||
- (agendamento.paciente && agendamento.paciente.toLowerCase().includes(lowerCaseSearchTerm))
- );
- }
- }
- return filteredData;
- }, [searchTerm]);
-
- const ListarDiasdoMes = (ano, mes) => {
- let segundas = []; let tercas = []; let quartas = []; let quintas = []; let sextas = []
- const base = dayjs(`${ano}-${mes}-01`)
- const DiasnoMes = base.daysInMonth()
- for (let d = 1; d <= DiasnoMes; d++) {
- const data = dayjs(`${ano}-${mes}-${d}`)
- const dia = data.format('dddd')
- switch (dia) {
- case 'Monday': segundas.push(d); break
- case 'Tuesday': tercas.push(d); break
- case 'Wednesday': quartas.push(d); break
- case 'Thursday': quintas.push(d); break
- case 'Friday': sextas.push(d); break
- default: break
- }
- }
- let ListaDiasDatas = { segundas: segundas, tercas: tercas, quartas: quartas, quintas: quintas, sextas: sextas }
- return ListaDiasDatas
- }
-
-
- useEffect(() => {
- console.log("mudou FiltredTodosMedicos:", FiltredTodosMedicos);
- if (MedicoFiltrado.id != "vazio" ) {
- const unicoMedico = MedicoFiltrado;
- console.log(unicoMedico)
- const idMedicoFiltrado = unicoMedico.idMedico;
- console.log(`Médico único encontrado: ${unicoMedico.nomeMedico}. ID: ${idMedicoFiltrado}`);
-
- const agendamentosDoMedico = filtrarAgendamentosPorMedico(
- DictAgendamentosOrganizados,
- idMedicoFiltrado
- );
- console.log(`Total de agendamentos filtrados para este médico: ${agendamentosDoMedico.length}`);
- console.log("Lista completa de Agendamentos do Médico:", agendamentosDoMedico);
-
- //FiltrarAgendamentos(agendamentosDoMedico)
-
- setListaTodosAgendamentos(agendamentosDoMedico)
-
- }
- }, [FiltredTodosMedicos, MedicoFiltrado]);
-
- const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => {
- setCacheAgendamentos(DictAgendamentosOrganizados);
-
- const todasAsListasDeAgendamentos = Object.values(dictAgendamentos);
- const todosOsAgendamentos = todasAsListasDeAgendamentos.flat();
-
- const agendamentosFiltrados = todosOsAgendamentos.filter(agendamento =>
- agendamento.doctor_id === idMedicoFiltrado
- );
-
- return agendamentosFiltrados;
- };
-
- const handleSearchMedicos = (term) => {
- setSearchTermDoctor(term);
- if (term.trim() === '') {
- if (MedicoFiltrado.id !== "vazio") {
- console.log("Medico escolhido, mas vai ser apagado")
- console.log(cacheAgendamentos, "cache ")
-
- }
-
-
- setFiltredTodosMedicos([]);
- setMedicoFiltrado({ id: "vazio" })
-
- //2 FiltrarAgendamentos()
- return;
- }
- if (FiltredTodosMedicos.length === 1) {
- setMedicoFiltrado({ ...FiltredTodosMedicos[0] })
- }
-
- const filtered = ListaDeMedicos.filter(medico =>
- medico.nomeMedico.toLowerCase().includes(term.toLowerCase())
- );
- setFiltredTodosMedicos(filtered);
-};
-
-useEffect(() => {
- setShowSpinner(false)
-},[filaEsperaData])
-
- // Filtrar Fila de Espera
- const filaEsperaFiltrada = useMemo(() => {
- if (!waitlistSearch.trim()) return filaEsperaData;
- const term = waitlistSearch.toLowerCase();
- return filaEsperaData.filter(item => {
- const paciente = item?.Infos?.paciente_nome?.toLowerCase() || '';
- const cpf = item?.Infos?.paciente_cpf?.toLowerCase() || '';
- const medico = item?.Infos?.nome_medico?.toLowerCase() || '';
- return paciente.includes(term) || cpf.includes(term) || medico.includes(term);
- });
- }, [waitlistSearch, filaEsperaData]);
-
- // Ordenar Fila de Espera
- 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 || ''), undefined, { sensitivity: 'base' }));
- } else if (waitSortKey === 'medico') {
- copy.sort((a, b) => (a?.Infos?.nome_medico || '').localeCompare((b?.Infos?.nome_medico || ''), undefined, { sensitivity: 'base' }));
- } 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);
-
- // Paginação Fila de Espera
- const waitTotalPages = Math.ceil(filaEsperaFiltrada.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;
- };
-
- // Reset página quando filtros/ordenação mudarem
- useEffect(() => {
- setWaitPage(1);
- }, [waitlistSearch, waitSortKey, waitSortDir]);
-
- const limparFiltrosWaitlist = () => {
- setWaitlistSearch('');
- setWaitSortKey(null);
- setWaitSortDir('asc');
- setWaitPage(1);
- };
-
- return (
-
-
-
Agendar nova consulta
-
-
-
-
-
-
-
-
-
- {!PageNovaConsulta ? (
-
-
-
- {/* Bloco de busca por médico */}
-
-
-
-
-
- handleSearchMedicos(e.target.value)}
- />
-
-
-
- {/* DROPDOWN (RENDERIZAÇÃO CONDICIONAL) */}
- {searchTermDoctor && FiltredTodosMedicos.length > 0 && (
-
- {FiltredTodosMedicos.map((medico) => (
-
{
- setSearchTermDoctor(medico.nomeMedico);
- setFiltredTodosMedicos([]);
- setMedicoFiltrado(medico)
-
- }}
- >
-
{medico.nomeMedico}
-
- ))}
-
- )}
-
-
-
-
-
-
-
-
-
-
-
- {FiladeEspera === false ?
- (
-
-
-
-
-
-
-
-
-
-
Realizado
-
Confirmado
-
Agendado
-
Cancelado
-
-
-
- {/* Componentes de Tabela - Adicionado props de delete da main */}
- {tabela === "diario" &&
}
- {tabela === 'semanal' &&
}
- {tabela === 'mensal' && }
-
-
- )
- :
- (
-
-
-
-
-
-
Fila de Espera
-
-
- {/* Filtros */}
-
-
- Filtros
-
-
-
- setWaitlistSearch(e.target.value)}
- />
-
- Digite o nome do paciente, CPF ou nome do médico
-
-
-
-
- {/* Ordenação rápida */}
-
- Ordenar por:
- {(() => {
- const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : '';
- return (
-
- );
- })()}
-
-
-
-
-
- {filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
-
-
-
-
-
-
-
- | Nome do Paciente |
- CPF |
- Médico Solicitado |
- Data da Solicitação |
- Ações |
-
-
-
- {filaEsperaPaginada.length > 0 ? (
- filaEsperaPaginada.map((item, index) => (
-
- | {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.
- >
- )}
-
- |
-
- )}
-
-
-
- {/* Paginação */}
- {filaEsperaFiltrada.length > 0 && (
-
-
- Itens por página:
-
-
-
-
-
- Página {waitPage} de {waitTotalPages} •
- Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
-
-
-
-
-
- )}
-
-
-
-
-
-
- )
+ 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;
+ }
+ };
- {/* Modal de Confirmação de Exclusão */}
- {showDeleteModal && (
-
- e.target.classList.contains("modal") && setShowDeleteModal(false)
- }
- >
-
-
-
-
- Confirmação de Cancelamento
-
-
-
+ 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.");
+ }
+ };
-
-
- Qual o motivo do cancelamento?
-
-
-
)}
-
- {showConfirmModal &&(
-
- e.target.classList.contains("modal") && setShowDeleteModal(false)
- }
- >
-
-
-
-
-
- Confirmação de edição
-
-
-
-
-
-
- Tem certeza que deseja retirar o 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
+
+ {/* LIMPA O OBJETO DE EDIÇÃO AO CLICAR EM "ADICIONAR" */}
+
+
+
+
+ {!PageNovaConsulta ? (
+
+
+
+
+
+
+ handleSearchMedicos(e.target.value)} />
+
+
+ {searchTermDoctor && FiltredTodosMedicos.length > 0 && (
{FiltredTodosMedicos.map((medico) => (
{ setSearchTermDoctor(medico.nomeMedico); setFiltredTodosMedicos([]); setMedicoFiltrado(medico); }}>
{medico.nomeMedico}
))}
)}
+
+
+
+
+
+
+
+
+ {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' ? (
+
+ ) : (
+ <>
+ {/* MUDANÇA: PASSA O OBJETO COMPLETO 'app' PARA O ESTADO DE EDIÇÃO */}
+
+ {/* Botão de Cancelar */}
+
+ >
+ )}
+
+
+ ))) : (
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
+
+
+
+ | Nome do Paciente | CPF | Médico Solicitado | Data da Solicitação | Ações |
+
+ {filaEsperaPaginada.length > 0 ? (filaEsperaPaginada.map((item, index) => (
+ | {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;
\ No newline at end of file
diff --git a/src/pages/secretaria/ListaMedicos.jsx b/src/pages/secretaria/ListaMedicos.jsx
index 4a7e733..2223f28 100644
--- a/src/pages/secretaria/ListaMedicos.jsx
+++ b/src/pages/secretaria/ListaMedicos.jsx
@@ -5,9 +5,10 @@ import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useAuth } from "../../_assets/utils/AuthProvider";
import API_KEY from "../../_assets/utils/apiKeys";
+
//import "./style/TableDoctor.css";
-function TableDoctor() {
+function TableDoctor({setDictInfo}) {
const { getAuthorizationHeader, isAuthenticated } = useAuth();
const [medicos, setMedicos] = useState([]);
@@ -150,7 +151,7 @@ function TableDoctor() {
return resultado;
}) : [];
- // Aplica ordenação rápida
+
const applySorting = (arr) => {
if (!Array.isArray(arr) || !sortKey) return arr;
const copy = [...arr];
@@ -216,7 +217,7 @@ function TableDoctor() {
-
+
Médicos Cadastrados
@@ -440,14 +441,14 @@ function TableDoctor() {
{medico.email || 'Não informado'} |
-
- |