import "../../assets/css/index.css" import { Link } from "react-router-dom"; import { useState, useEffect, useRef, useLayoutEffect } from "react"; import { createPortal } from "react-dom"; import { getAccessToken } from "../../utils/auth.js"; import { getUserRole } from "../../utils/userInfo.js"; import { getDoctorId } from "../../utils/userInfo.js"; const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; const supabaseAK = import.meta.env.VITE_SUPABASE_ANON_KEY; // Componente para o dropdown portal function DropdownPortal({ anchorEl, isOpen, onClose, className, children }) { const menuRef = useRef(null); const [stylePos, setStylePos] = useState({ position: "absolute", top: 0, left: 0, visibility: "hidden", zIndex: 1000, }); useLayoutEffect(() => { if (!isOpen || !anchorEl || !menuRef.current) return; const anchorRect = anchorEl.getBoundingClientRect(); const menuRect = menuRef.current.getBoundingClientRect(); const scrollY = window.scrollY || window.pageYOffset; const scrollX = window.scrollX || window.pageXOffset; let left = anchorRect.right + scrollX - menuRect.width; let top = anchorRect.bottom + scrollY; if (left < 0) left = scrollX + 4; if (top + menuRect.height > window.innerHeight + scrollY) { top = anchorRect.top + scrollY - menuRect.height; } setStylePos({ position: "absolute", top: `${Math.round(top)}px`, left: `${Math.round(left)}px`, visibility: "visible", zIndex: 1000, }); }, [isOpen, anchorEl, children]); useEffect(() => { if (!isOpen) return; function handleDocClick(e) { if ( menuRef.current && !menuRef.current.contains(e.target) && anchorEl && !anchorEl.contains(e.target) ) { onClose(); } } function handleScroll() { onClose(); } document.addEventListener("mousedown", handleDocClick); document.addEventListener("scroll", handleScroll, true); return () => { document.removeEventListener("mousedown", handleDocClick); document.removeEventListener("scroll", handleScroll, true); }; }, [isOpen, onClose, anchorEl]); if (!isOpen) return null; return createPortal(
e.stopPropagation()} > {children}
, document.body ); } function AgendaDoctor() { const [agenda, setAgenda] = useState([]); const [medicos, setMedicos] = useState([]); const [openDropdown, setOpenDropdown] = useState(null); const [search, setSearch] = useState(""); const [dayFilter, setDayFilter] = useState(""); const [typeFilter, setTypeFilter] = useState(""); const [deleteId, setDeleteId] = useState(null); const [editId, setEditId] = useState(null); const [editData, setEditData] = useState({ doctor_id: "", weekday: "", start_time: "", end_time: "", slot_minutes: 30, appointment_type: "", active: true, }); const anchorRefs = useRef({}); const role = getUserRole(); const tokenUsuario = getAccessToken(); const requestOptions = { method: "GET", headers: { apikey: supabaseAK, Authorization: `Bearer ${tokenUsuario}`, }, redirect: "follow", }; // Fetch agenda useEffect(() => { if (getUserRole() === 'medico') { fetch( `${supabaseUrl}/rest/v1/doctor_availability?doctor_id=eq.${getDoctorId()}`, requestOptions ) .then((res) => res.json()) .then((result) => setAgenda(Array.isArray(result) ? result : [])) .catch((err) => console.log(err)); } else { fetch( `${supabaseUrl}/rest/v1/doctor_availability`, requestOptions ) .then((res) => res.json()) .then((result) => setAgenda(Array.isArray(result) ? result : [])) .catch((err) => console.log(err)); } }, []); // Fetch médicos useEffect(() => { fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors", requestOptions) .then((res) => res.json()) .then((result) => setMedicos(Array.isArray(result) ? result : [])) .catch((err) => console.log(err)); }, []); const getDoctorName = (id) => { if (!id) return ""; const medico = medicos.find((m) => m.id === id); return medico ? medico.full_name || medico.name || "" : id; }; // DELETE const handleDelete = (id) => setDeleteId(id); const confirmDelete = () => { if (!deleteId) return; fetch( `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability?id=eq.${deleteId}`, { method: "DELETE", headers: { apikey: supabaseAK, Authorization: `Bearer ${tokenUsuario}`, }, } ) .then((res) => { if (!res.ok) throw new Error("Erro ao deletar a agenda"); setAgenda((prev) => prev.filter((a) => a.id !== deleteId)); setDeleteId(null); }) .catch((err) => console.log(err)); }; // EDIT const handleEditClick = (id) => { const agendaItem = agenda.find((a) => a.id === id); if (!agendaItem) return; setEditData({ doctor_id: agendaItem.doctor_id || "", weekday: agendaItem.weekday || "", start_time: agendaItem.start_time || "", end_time: agendaItem.end_time || "", slot_minutes: agendaItem.slot_minutes || 30, appointment_type: agendaItem.appointment_type || "", active: agendaItem.active ?? true, }); setEditId(id); setOpenDropdown(null); }; const handleEditChange = (e) => { const { name, value, type, checked } = e.target; setEditData((prev) => ({ ...prev, [name]: type === "checkbox" ? checked : value, })); }; const submitEdit = () => { if (!editId) return; if (!editData.doctor_id) { alert("Selecione um médico válido."); return; } if (!editData.weekday || !editData.start_time || !editData.end_time || !editData.appointment_type) { alert("Preencha todos os campos obrigatórios."); return; } fetch( `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability?id=eq.${editId}`, { method: "PATCH", headers: { apikey: supabaseAK, Authorization: `Bearer ${tokenUsuario}`, "Content-Type": "application/json", "Prefer": "return=representation", // ESSENCIAL }, body: JSON.stringify(editData), } ) .then((res) => { if (!res.ok) throw new Error("Erro ao salvar alterações"); return res.json(); }) .then((updated) => { setAgenda((prev) => prev.map((a) => (a.id === editId ? { ...a, ...updated[0] } : a)) ); setEditId(null); }) .catch((err) => { console.error(err); alert("Erro ao salvar alterações. Verifique os campos e tente novamente."); }); }; const filteredAgenda = agenda.filter((a) => { if (!a) return false; const q = search.toLowerCase(); // Filtro por texto (nome do médico, dia, tipo) const matchesText = ( (getDoctorName(a.doctor_id) || "").toLowerCase().includes(q) || (a.weekday || "").toLowerCase().includes(q) || (a.appointment_type || "").toLowerCase().includes(q) ); // Filtro por dia da semana const matchesDay = !dayFilter || a.weekday === dayFilter; // Filtro por tipo de consulta const matchesType = !typeFilter || a.appointment_type === typeFilter; return matchesText && matchesDay && matchesType; }); // Paginação const [itemsPerPage1, setItemsPerPage1] = useState(15); const [currentPage1, setCurrentPage1] = useState(1); const indexOfLastAgenda = currentPage1 * itemsPerPage1; const indexOfFirstAgenda = indexOfLastAgenda - itemsPerPage1; const currentAgenda = filteredAgenda.slice(indexOfFirstAgenda, indexOfLastAgenda); const totalPages1 = Math.ceil(filteredAgenda.length / itemsPerPage1); // Reset da paginação quando filtros mudam useEffect(() => { setCurrentPage1(1); }, [search, dayFilter, typeFilter]); const permissoes = { admin: ['nome'], secretaria: ['nome'], medico: [''] }; const pode = (acao) => permissoes[role]?.includes(acao); return (
{/* Header com título e botão */}

Agenda Médica

Adicionar agenda
{/* Todos os filtros em uma única linha */}
{/* Campo de busca */} setSearch(e.target.value)} style={{ minWidth: "300px", maxWidth: "450px", }} /> {/* Filtro por dia da semana */} {/* Filtro por tipo de consulta */}
{/* Tabela */}
{pode('nome') && } {currentAgenda.length > 0 ? ( currentAgenda.map((a) => ( {pode('nome') && } )) ) : ( )}
NomeDias disponíveis Horário disponível Duração (min) Tipo Status Ação
{getDoctorName(a.doctor_id)} {a.weekday === 'monday' ? 'Segunda' : a.weekday === 'tuesday' ? 'Terça' : a.weekday === 'wednesday' ? 'Quarta' : a.weekday === 'thursday' ? 'Quinta' : a.weekday === 'friday' ? 'Sexta' : a.weekday === 'saturday' ? 'Sábado' : a.weekday === 'sunday' ? 'Domingo' : a.weekday} {a.start_time || ""} ás {a.end_time || ""} {a.slot_minutes || 30} {a.appointment_type === 'presencial' ? ( <> Presencial ) : a.appointment_type === 'telemedicina' ? ( <> Telemedicina ) : ( a.appointment_type )} {a.active ? ( <> Ativo ) : ( <> Inativo )}
Nenhuma agenda encontrada
{/* Paginação */}
Total encontrados: {filteredAgenda.length}
{/* Modal de Delete */} {deleteId && (

Tem certeza que deseja deletar esta agenda?

)} {/* Modal de Edit */} {editId && (
Editar Disponibilidade
)}
); } export default AgendaDoctor;