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 */}
setDayFilter(e.target.value)}
>
Todos os dias
Segunda-feira
Terça-feira
Quarta-feira
Quinta-feira
Sexta-feira
Sábado
Domingo
{/* Filtro por tipo de consulta */}
setTypeFilter(e.target.value)}
>
Todos os tipos
Presencial
Telemedicina
{/* Tabela */}
{pode('nome') && Nome }
Dias disponíveis
Horário disponível
Duração (min)
Tipo
Status
Ação
{currentAgenda.length > 0 ? (
currentAgenda.map((a) => (
{pode('nome') && {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
>
)}
handleEditClick(a.id)}
title="Editar agenda"
>
handleDelete(a.id)}
title="Excluir agenda"
>
))
) : (
Nenhuma agenda encontrada
)}
{/* Paginação */}
Total encontrados: {filteredAgenda.length}
{
setItemsPerPage1(Number(e.target.value));
setCurrentPage1(1);
}}
title="Itens por página"
>
10 por página
15 por página
20 por página
30 por página
{/* Primeira página */}
setCurrentPage1(1)}>
{"<<"}
{/* Página anterior */}
setCurrentPage1(prev => Math.max(prev - 1, 1))}>
<
{/* Número da página atual */}
{currentPage1}
{/* Próxima página */}
setCurrentPage1(prev => Math.min(prev + 1, totalPages1))}>
>
{/* Última página */}
setCurrentPage1(totalPages1)}>
{">>"}
{/* Modal de Delete */}
{deleteId && (
Tem certeza que deseja deletar esta agenda?
setDeleteId(null)}
>
Fechar
Deletar
)}
{/* Modal de Edit */}
{editId && (
Editar Disponibilidade
setEditId(null)}>
setEditId(null)}>
Fechar
Salvar
)}
);
}
export default AgendaDoctor;