Reorganização das Disponibilidades

This commit is contained in:
pedrofedericoo 2025-10-21 09:59:24 -03:00
parent 4d661dd973
commit ce3c5b8f22
6 changed files with 1109 additions and 449 deletions

View File

@ -7,6 +7,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const FormatTelefones = (valor) => { const FormatTelefones = (valor) => {
const digits = String(valor).replace(/\D/g, "").slice(0, 11); const digits = String(valor).replace(/\D/g, "").slice(0, 11);
return digits return digits
@ -28,7 +29,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
const cpfLimpo = cpf.replace(/\D/g, ""); const cpfLimpo = cpf.replace(/\D/g, "");
if (cpfLimpo.length !== 11) return false; if (cpfLimpo.length !== 11) return false;
if (/^(\d)\1+$/.test(cpfLimpo)) return false; if (/^(\d)\1+$/.test(cpfLimpo)) return false;
let soma = 0; let soma = 0;
@ -51,6 +51,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
); );
}; };
const [avatarUrl, setAvatarUrl] = useState(null); const [avatarUrl, setAvatarUrl] = useState(null);
const [showRequiredModal, setShowRequiredModal] = useState(false); const [showRequiredModal, setShowRequiredModal] = useState(false);
const [emptyFields, setEmptyFields] = useState([]); const [emptyFields, setEmptyFields] = useState([]);
@ -122,6 +123,10 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
} }
}; };
const handleAvailabilityUpdate = (newAvailability) => {
setFormData((prev) => ({ ...prev, availability: newAvailability }));
};
const handleCepBlur = async () => { const handleCepBlur = async () => {
const cep = formData.cep?.replace(/\D/g, ""); const cep = formData.cep?.replace(/\D/g, "");
if (cep && cep.length === 8) { if (cep && cep.length === 8) {
@ -250,8 +255,10 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
setShowRequiredModal(false); setShowRequiredModal(false);
}; };
return ( return (
<> <>
{/* Modal de Alerta */}
{showRequiredModal && ( {showRequiredModal && (
<div className="modal-overlay"> <div className="modal-overlay">
<div className="modal-content"> <div className="modal-content">
@ -299,6 +306,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
)} )}
{/* Formulário Principal */}
<div className="card doctor-form-container shadow-sm"> <div className="card doctor-form-container shadow-sm">
<h3 className="doctor-form-title">MediConnect</h3> <h3 className="doctor-form-title">MediConnect</h3>
@ -319,6 +327,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}`} }`}
> >
<div className="row mt-3"> <div className="row mt-3">
{/* Foto / Avatar */}
<div className="col-md-6 mb-3 avatar-container"> <div className="col-md-6 mb-3 avatar-container">
<div className="me-3"> <div className="me-3">
{avatarUrl ? ( {avatarUrl ? (
@ -354,6 +363,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
</div> </div>
{/* Nome Completo */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Nome: *</label> <label className="form-label">Nome: *</label>
<input <input
@ -365,6 +375,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Data de Nascimento */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Data de nascimento:</label> <label className="form-label">Data de nascimento:</label>
<input <input
@ -377,6 +388,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
max="2025-09-24" max="2025-09-24"
/> />
</div> </div>
{/* CPF */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">CPF: *</label> <label className="form-label">CPF: *</label>
<input <input
@ -399,6 +411,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
)} )}
</div> </div>
{/* Estado do CRM */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Estado do CRM: *</label> <label className="form-label">Estado do CRM: *</label>
<select <select
@ -438,6 +451,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
{/* CRM */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">CRM: *</label> <label className="form-label">CRM: *</label>
<input <input
@ -450,6 +464,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
/> />
</div> </div>
{/* Especialização */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Especialização:</label> <label className="form-label">Especialização:</label>
<select <select
@ -497,6 +512,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
className={`collapse${collapsedSections.contato ? " show" : ""}`} className={`collapse${collapsedSections.contato ? " show" : ""}`}
> >
<div className="row mt-3"> <div className="row mt-3">
{/* Email */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Email: *</label> <label className="form-label">Email: *</label>
<input <input
@ -508,6 +524,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Telefone 1 (Principal) */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Telefone: *</label> <label className="form-label">Telefone: *</label>
<input <input
@ -519,6 +536,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Telefone 2 (Opcional) */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Telefone 2:</label> <label className="form-label">Telefone 2:</label>
<input <input
@ -548,6 +566,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
className={`collapse${collapsedSections.endereco ? " show" : ""}`} className={`collapse${collapsedSections.endereco ? " show" : ""}`}
> >
<div className="row mt-3"> <div className="row mt-3">
{/* CEP */}
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label className="form-label">CEP:</label> <label className="form-label">CEP:</label>
<input <input
@ -559,6 +578,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onBlur={handleCepBlur} onBlur={handleCepBlur}
/> />
</div> </div>
{/* Rua */}
<div className="col-md-8 mb-3"> <div className="col-md-8 mb-3">
<label className="form-label">Rua:</label> <label className="form-label">Rua:</label>
<input <input
@ -569,6 +589,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Bairro */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label className="form-label">Bairro:</label> <label className="form-label">Bairro:</label>
<input <input
@ -579,6 +600,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Cidade */}
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label className="form-label">Cidade:</label> <label className="form-label">Cidade:</label>
<input <input
@ -589,6 +611,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Estado */}
<div className="col-md-2 mb-3"> <div className="col-md-2 mb-3">
<label className="form-label">Estado:</label> <label className="form-label">Estado:</label>
<input <input
@ -599,6 +622,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Número */}
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label className="form-label">Número:</label> <label className="form-label">Número:</label>
<input <input
@ -609,6 +633,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{/* Complemento */}
<div className="col-md-8 mb-3"> <div className="col-md-8 mb-3">
<label className="form-label">Complemento:</label> <label className="form-label">Complemento:</label>
<input <input
@ -629,23 +654,27 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
className="section-header" className="section-header"
onClick={() => handleToggleCollapse("horarios")} onClick={() => handleToggleCollapse("horarios")}
> >
Horários Horários de Atendimento
<span className="section-toggle"> <span className="section-toggle">
{collapsedSections.horarios ? "▲" : "▼"} {collapsedSections.horarios ? "▲" : "▼"}
</span> </span>
</h4> </h4>
<div
<div className={`collapse${collapsedSections.horarios ? " show" : ""}`}> className={`collapse${collapsedSections.horarios ? " show" : ""}`}
>
<div className="row mt-3">
<div className="col-12 mb-3">
<p className="form-label text-muted">
Defina seus horários de atendimento para cada dia da semana.
Marque um dia para começar a adicionar blocos de tempo.
</p>
<HorariosDisponibilidade <HorariosDisponibilidade
onChange={(dados) => { initialAvailability={formData.availability}
console.log("Disponibilidades atualizadas:", dados); onUpdate={handleAvailabilityUpdate}
// Se quiser salvar no formData:
// setFormData(prev => ({ ...prev, disponibilidades: dados }));
}}
/> />
</div> </div>
</div> </div>
</div>
</div> </div>
{/* BOTÕES DE AÇÃO */} {/* BOTÕES DE AÇÃO */}
@ -662,6 +691,7 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
<button className="btn btn-light btn-cancel">Cancelar</button> <button className="btn btn-light btn-cancel">Cancelar</button>
</Link> </Link>
</div> </div>
</div>
</> </>
); );
} }

View File

@ -1,106 +1,401 @@
import React, { useState } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { Clock } from "lucide-react";
const diasDaSemana = [ const initialBlockTemplate = {
"segunda", id: null,
"terca", inicio: "09:00",
"quarta", termino: "17:00",
"quinta", isNew: true,
"sexta", };
"sabado",
"domingo" const emptyAvailabilityTemplate = [
{ dia: "Segunda-feira", isChecked: false, blocos: [] },
{ dia: "Terça-feira", isChecked: false, blocos: [] },
{ dia: "Quarta-feira", isChecked: false, blocos: [] },
{ dia: "Quinta-feira", isChecked: false, blocos: [] },
{ dia: "Sexta-feira", isChecked: false, blocos: [] },
{ dia: "Sábado", isChecked: false, blocos: [] },
{ dia: "Domingo", isChecked: false, blocos: [] },
]; ];
export default function HorariosDisponibilidade({ onChange }) { const HorariosDisponibilidade = ({
const [disponibilidades, setDisponibilidades] = useState( initialAvailability = emptyAvailabilityTemplate,
diasDaSemana.map(dia => ({ onUpdate,
weekday: dia, }) => {
slots: [{ start_time: "09:00", end_time: "17:00" }], const [availability, setAvailability] = useState(initialAvailability);
ativo: false,
})) useEffect(() => {
if (initialAvailability !== emptyAvailabilityTemplate) {
setAvailability(initialAvailability);
}
}, [initialAvailability]);
useEffect(() => {
if (onUpdate) {
onUpdate(availability);
}
}, [availability, onUpdate]);
const handleDayCheck = useCallback((dayIndex, currentIsChecked) => {
const isChecked = !currentIsChecked;
setAvailability((prev) =>
prev.map((day, i) =>
i === dayIndex
? {
...day,
isChecked,
blocos: isChecked
? day.blocos.length === 0
? [
{
...initialBlockTemplate,
id: Date.now() + Math.random(),
isNew: true,
},
]
: day.blocos
: [],
}
: day
)
); );
}, []);
function handleToggleDia(index) { const handleAddBlock = useCallback((dayIndex) => {
const updated = [...disponibilidades]; const tempId = Date.now() + Math.random();
updated[index].ativo = !updated[index].ativo; const newBlock = { ...initialBlockTemplate, id: tempId, isNew: true };
setDisponibilidades(updated);
onChange?.(updated);
}
function handleSlotChange(indexDia, indexSlot, campo, valor) { setAvailability((prev) =>
const updated = [...disponibilidades]; prev.map((day, i) =>
updated[indexDia].slots[indexSlot][campo] = valor; i === dayIndex
setDisponibilidades(updated); ? {
onChange?.(updated); ...day,
blocos: [...day.blocos, newBlock],
isChecked: true,
} }
: day
)
);
}, []);
function handleAddSlot(indexDia) { const handleRemoveBlock = useCallback((dayIndex, blockId) => {
const updated = [...disponibilidades]; setAvailability((prev) =>
updated[indexDia].slots.push({ start_time: "", end_time: "" }); prev.map((day, i) => {
setDisponibilidades(updated); if (i === dayIndex) {
onChange?.(updated); const newBlocos = day.blocos.filter((bloco) => bloco.id !== blockId);
return {
...day,
blocos: newBlocos,
isChecked: newBlocos.length > 0,
};
} }
return day;
})
);
}, []);
function handleRemoveSlot(indexDia, indexSlot) { const handleTimeChange = useCallback((dayIndex, blockId, field, value) => {
const updated = [...disponibilidades]; setAvailability((prev) =>
updated[indexDia].slots.splice(indexSlot, 1); prev.map((day, i) =>
setDisponibilidades(updated); i === dayIndex
onChange?.(updated); ? {
...day,
blocos: day.blocos.map((bloco) =>
bloco.id === blockId ? { ...bloco, [field]: value } : bloco
),
} }
: day
)
);
}, []);
const renderTimeBlock = (dayIndex, bloco) => (
<div
key={bloco.id}
style={{
display: "flex",
flexDirection: window.innerWidth < 640 ? "column" : "row",
alignItems: window.innerWidth < 640 ? "flex-start" : "center",
justifyContent: "space-between",
padding: "16px",
marginBottom: "16px",
borderRadius: "12px",
boxShadow:
"0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
transition: "all 0.3s",
backgroundColor: bloco.isNew ? "#eef2ff" : "#ffffff",
border: bloco.isNew ? "2px solid #6366f1" : "1px solid #e5e7eb",
}}
>
<div
style={{
display: "flex",
flexDirection: window.innerWidth < 640 ? "column" : "row",
gap: window.innerWidth < 640 ? "0" : "32px",
width: window.innerWidth < 640 ? "100%" : "auto",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
marginBottom: window.innerWidth < 640 ? "8px" : "0",
}}
>
<label
htmlFor={`inicio-${dayIndex}-${bloco.id}`}
style={{ fontWeight: 500, color: "#4b5563", width: "64px" }}
>
Início:
</label>
<div style={{ position: "relative" }}>
<input
id={`inicio-${dayIndex}-${bloco.id}`}
type="time"
value={bloco.inicio}
onChange={(e) =>
handleTimeChange(dayIndex, bloco.id, "inicio", e.target.value)
}
style={{
padding: "8px",
border: "1px solid #d1d5db",
borderRadius: "8px",
width: "100%",
boxSizing: "border-box",
outline: "none",
}}
step="300"
/>
<Clock
size={16}
style={{
position: "absolute",
right: "12px",
top: "50%",
transform: "translateY(-50%)",
color: "#9ca3af",
pointerEvents: "none",
}}
/>
</div>
</div>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<label
htmlFor={`termino-${dayIndex}-${bloco.id}`}
style={{ fontWeight: 500, color: "#4b5563", width: "64px" }}
>
Término:
</label>
<div style={{ position: "relative" }}>
<input
id={`termino-${dayIndex}-${bloco.id}`}
type="time"
value={bloco.termino}
onChange={(e) =>
handleTimeChange(dayIndex, bloco.id, "termino", e.target.value)
}
style={{
padding: "8px",
border: "1px solid #d1d5db",
borderRadius: "8px",
width: "100%",
boxSizing: "border-box",
outline: "none",
}}
step="300"
/>
<Clock
size={16}
style={{
position: "absolute",
right: "12px",
top: "50%",
transform: "translateY(-50%)",
color: "#9ca3af",
pointerEvents: "none",
}}
/>
</div>
</div>
</div>
<button
onClick={() => handleRemoveBlock(dayIndex, bloco.id)}
style={{
marginTop: window.innerWidth < 640 ? "16px" : "0",
padding: "8px 24px",
backgroundColor: "#ef4444",
color: "white",
fontWeight: 600,
borderRadius: "12px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
transition: "all 0.2s",
width: window.innerWidth < 640 ? "100%" : "auto",
cursor: "pointer",
border: "none",
opacity: 1,
}}
onMouseEnter={(e) =>
(e.currentTarget.style.backgroundColor = "#dc2626")
}
onMouseLeave={(e) =>
(e.currentTarget.style.backgroundColor = "#ef4444")
}
>
Remover Bloco
</button>
{bloco.isNew && (
<span
style={{
fontSize: "12px",
color: "#6366f1",
marginTop: "8px",
marginLeft: window.innerWidth < 640 ? "0" : "16px",
fontWeight: 500,
}}
>
(Novo)
</span>
)}
</div>
);
return ( return (
<div className="space-y-4"> <div
{disponibilidades.map((dia, i) => ( style={{
<div key={dia.weekday} className="border p-3 rounded-md bg-gray-50"> maxWidth: "960px",
<div className="flex items-center justify-between"> margin: "0 auto",
<label className="font-semibold capitalize"> fontFamily: "Inter, sans-serif",
{dia.weekday} }}
</label> >
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
{availability.map((day, dayIndex) => {
const isChecked = day.isChecked;
const dayHeaderStyle = {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "12px 0",
borderBottom: "1px solid #e5e7eb",
marginBottom: "16px",
backgroundColor: isChecked ? "#1f2937" : "#f9fafb",
borderRadius: "8px",
paddingLeft: "16px",
paddingRight: "16px",
cursor: "pointer",
transition: "background-color 0.2s",
};
return (
<div
key={day.dia}
style={{
backgroundColor: "#f9fafb",
padding: "20px",
borderRadius: "12px",
border: "1px solid #e5e7eb",
}}
>
<div
style={{
...dayHeaderStyle,
backgroundColor: isChecked ? "#1f2937" : "#f9fafb",
borderBottom: isChecked
? "1px solid #4b5563"
: "1px solid #e5e7eb",
}}
onClick={() => handleDayCheck(dayIndex, isChecked)}
>
<label
style={{
fontSize: "18px",
fontWeight: "bold",
color: isChecked ? "white" : "#1f2937",
display: "flex",
alignItems: "center",
gap: "12px",
cursor: "pointer",
}}
>
<span>{day.dia}</span>
<input <input
type="checkbox" type="checkbox"
checked={dia.ativo} checked={isChecked}
onChange={() => handleToggleDia(i)} onChange={() => {}}
style={{
width: "20px",
height: "20px",
accentColor: isChecked ? "#3b82f6" : "#9ca3af",
marginLeft: "8px",
}}
/> />
</label>
</div> </div>
{dia.ativo && ( {isChecked && (
<div className="mt-2 space-y-2"> <div style={{ marginTop: "16px" }}>
{dia.slots.map((slot, j) => ( {day.blocos.length === 0 && (
<div key={j} className="flex gap-2 items-center"> <p
<input style={{
type="time" color: "#6b7280",
value={slot.start_time} fontStyle: "italic",
onChange={e => handleSlotChange(i, j, "start_time", e.target.value)} marginBottom: "16px",
className="border rounded p-1" }}
/>
<span>até</span>
<input
type="time"
value={slot.end_time}
onChange={e => handleSlotChange(i, j, "end_time", e.target.value)}
className="border rounded p-1"
/>
{dia.slots.length > 1 && (
<button
type="button"
onClick={() => handleRemoveSlot(i, j)}
className="text-red-600 hover:underline"
> >
Remover Nenhum bloco de horário definido.
</button> </p>
)}
<div
style={{
display: "flex",
flexDirection: "column",
gap: "16px",
}}
>
{day.blocos.map((bloco) =>
renderTimeBlock(dayIndex, bloco)
)} )}
</div> </div>
))}
<button <button
type="button" onClick={() => handleAddBlock(dayIndex)}
onClick={() => handleAddSlot(i)} style={{
className="text-blue-600 hover:underline mt-2" marginTop: "24px",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "12px 24px",
backgroundColor: "#10b981",
color: "white",
fontWeight: "bold",
borderRadius: "12px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
transition: "all 0.3s",
cursor: "pointer",
border: "none",
}}
onMouseEnter={(e) =>
(e.currentTarget.style.backgroundColor = "#059669")
}
onMouseLeave={(e) =>
(e.currentTarget.style.backgroundColor = "#10b981")
}
> >
+ Adicionar pausa/bloco + Adicionar novo bloco
</button> </button>
</div> </div>
)} )}
</div> </div>
))}
</div>
); );
} })}
</div>
</div>
);
};
export default HorariosDisponibilidade;

View File

@ -1,46 +1,44 @@
import React, { useState, useMemo, useEffect } from 'react'; import React, { useState, useMemo, useEffect } from "react";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from "react-router-dom";
import API_KEY from '../components/utils/apiKeys.js'; import API_KEY from "../components/utils/apiKeys.js";
import AgendamentoCadastroManager from './AgendamentoCadastroManager.jsx'; import AgendamentoCadastroManager from "./AgendamentoCadastroManager.jsx";
import TabelaAgendamentoDia from '../components/AgendarConsulta/TabelaAgendamentoDia'; import TabelaAgendamentoDia from "../components/AgendarConsulta/TabelaAgendamentoDia";
import TabelaAgendamentoSemana from '../components/AgendarConsulta/TabelaAgendamentoSemana'; import TabelaAgendamentoSemana from "../components/AgendarConsulta/TabelaAgendamentoSemana";
import TabelaAgendamentoMes from '../components/AgendarConsulta/TabelaAgendamentoMes'; import TabelaAgendamentoMes from "../components/AgendarConsulta/TabelaAgendamentoMes";
import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta'; import FormNovaConsulta from "../components/AgendarConsulta/FormNovaConsulta";
import { GetAllDoctors } from '../components/utils/Functions-Endpoints/Doctor.js'; import { GetAllDoctors } from "../components/utils/Functions-Endpoints/Doctor.js";
import { useAuth } from '../components/utils/AuthProvider.js'; import { useAuth } from "../components/utils/AuthProvider.js";
// NOVO: Caminho de importação corrigido com base na sua estrutura de pastas // NOVO: Caminho de importação corrigido com base na sua estrutura de pastas
import AgendamentosMes from '../components/AgendarConsulta/DadosConsultasMock.js'; import AgendamentosMes from "../components/AgendarConsulta/DadosConsultasMock.js";
import dayjs from "dayjs";
import dayjs from 'dayjs';
import "./style/Agendamento.css"; import "./style/Agendamento.css";
import './style/FilaEspera.css'; import "./style/FilaEspera.css";
import { Search } from 'lucide-react'; import { Search } from "lucide-react";
const Agendamento = () => { const Agendamento = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [FiladeEspera, setFiladeEspera] = useState(false); const [FiladeEspera, setFiladeEspera] = useState(false);
const [tabela, setTabela] = useState('diario'); const [tabela, setTabela] = useState("diario");
const [PageNovaConsulta, setPageConsulta] = useState(false); const [PageNovaConsulta, setPageConsulta] = useState(false);
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState("");
const [agendamentos, setAgendamentos] = useState() const [agendamentos, setAgendamentos] = useState();
const {getAuthorizationHeader} = useAuth() const { getAuthorizationHeader } = useAuth();
const [DictAgendamentosOrganizados, setAgendamentosOrganizados ] = useState({}) const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState(
{}
);
const [showDeleteModal, setShowDeleteModal] = useState(false) const [showDeleteModal, setShowDeleteModal] = useState(false);
const [AgendamentoFiltrado, setAgendamentoFiltrado] = useState() const [AgendamentoFiltrado, setAgendamentoFiltrado] = useState();
const [ListaDeMedicos, setListaDeMedicos] = useState([]) const [ListaDeMedicos, setListaDeMedicos] = useState([]);
const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]) const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]);
const [searchTermDoctor, setSearchTermDoctor] = useState(''); const [searchTermDoctor, setSearchTermDoctor] = useState("");
let authHeader = getAuthorizationHeader();
let authHeader = getAuthorizationHeader()
const FiltrarAgendamentos = (listaTodosAgendamentos) => { const FiltrarAgendamentos = (listaTodosAgendamentos) => {
let DictAgendamentosOrganizados = {}; let DictAgendamentosOrganizados = {};
@ -60,55 +58,59 @@ const Agendamento = () => {
} }
} }
setAgendamentosOrganizados(DictAgendamentosOrganizados); setAgendamentosOrganizados(DictAgendamentosOrganizados);
};
}
// Requisição inicial para mostrar os agendamentos do banco de dados // Requisição inicial para mostrar os agendamentos do banco de dados
useEffect(() => { useEffect(() => {
var myHeaders = new Headers(); var myHeaders = new Headers();
myHeaders.append("Authorization", authHeader); myHeaders.append("Authorization", authHeader);
myHeaders.append("apikey", API_KEY) myHeaders.append("apikey", API_KEY);
var requestOptions = { var requestOptions = {
method: 'GET', method: "GET",
headers: myHeaders, headers: myHeaders,
redirect: 'follow' redirect: "follow",
}; };
fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select&doctor_id&patient_id&status&scheduled_at&order&limit&offset", requestOptions) fetch(
.then(response => response.json()) "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?select&doctor_id&patient_id&status&scheduled_at&order&limit&offset",
.then(result => {FiltrarAgendamentos(result);}) requestOptions
.catch(error => console.log('error', error)); )
.then((response) => response.json())
.then((result) => {
FiltrarAgendamentos(result);
})
.catch((error) => console.log("error", error));
const PegarTodosOsMedicos = async () => { const PegarTodosOsMedicos = async () => {
let lista = [] let lista = [];
const TodosOsMedicos = await GetAllDoctors(authHeader) const TodosOsMedicos = await GetAllDoctors(authHeader);
//console.log(TodosOsMedicos, "tentativa") //console.log(TodosOsMedicos, "tentativa")
for (let d = 0; TodosOsMedicos.length > d; d++) { for (let d = 0; TodosOsMedicos.length > d; d++) {
lista.push({nomeMedico: TodosOsMedicos[d].full_name, idMedico: TodosOsMedicos[d].id })} lista.push({
setListaDeMedicos(lista) nomeMedico: TodosOsMedicos[d].full_name,
idMedico: TodosOsMedicos[d].id,
});
} }
PegarTodosOsMedicos() setListaDeMedicos(lista);
};
}, []) PegarTodosOsMedicos();
}, []);
useEffect(() => { useEffect(() => {
console.log("mudou FiltredTodosMedicos:", FiltredTodosMedicos); console.log("mudou FiltredTodosMedicos:", FiltredTodosMedicos);
if (FiltredTodosMedicos.length === 1) { if (FiltredTodosMedicos.length === 1) {
const unicoMedico = FiltredTodosMedicos[0]; const unicoMedico = FiltredTodosMedicos[0];
console.log(unicoMedico) console.log(unicoMedico);
const idMedicoFiltrado = unicoMedico.idMedico; const idMedicoFiltrado = unicoMedico.idMedico;
console.log(`Médico único encontrado: ${unicoMedico.nomeMedico}. ID: ${idMedicoFiltrado}`); console.log(
`Médico único encontrado: ${unicoMedico.nomeMedico}. ID: ${idMedicoFiltrado}`
);
const agendamentosDoMedico = filtrarAgendamentosPorMedico( const agendamentosDoMedico = filtrarAgendamentosPorMedico(
DictAgendamentosOrganizados, DictAgendamentosOrganizados,
@ -117,25 +119,24 @@ const Agendamento = () => {
// ========================================================================= // =========================================================================
console.log(`Total de agendamentos filtrados para este médico: ${agendamentosDoMedico.length}`); console.log(
console.log("Lista completa de Agendamentos do Médico:", agendamentosDoMedico); `Total de agendamentos filtrados para este médico: ${agendamentosDoMedico.length}`
FiltrarAgendamentos(agendamentosDoMedico) );
console.log(
"Lista completa de Agendamentos do Médico:",
agendamentosDoMedico
);
FiltrarAgendamentos(agendamentosDoMedico);
// AQUI VOCÊ PODE APLICAR SUA LÓGICA FINAL: // AQUI VOCÊ PODE APLICAR SUA LÓGICA FINAL:
// Ex: setar um novo estado com os agendamentos filtrados, se for necessário: // Ex: setar um novo estado com os agendamentos filtrados, se for necessário:
// setAgendamentosFiltrados(agendamentosDoMedico); // setAgendamentosFiltrados(agendamentosDoMedico);
} else { } else {
// Opcional: Limpar a lista filtrada se a busca não for mais única // Opcional: Limpar a lista filtrada se a busca não for mais única
// setAgendamentosFiltrados([]); // setAgendamentosFiltrados([]);
} }
}, [FiltredTodosMedicos]); }, [FiltredTodosMedicos]);
/** /**
* Filtra todos os agendamentos em um objeto aninhado (data -> [agendamentos]) * Filtra todos os agendamentos em um objeto aninhado (data -> [agendamentos])
* com base no ID do médico. * com base no ID do médico.
@ -145,36 +146,81 @@ const Agendamento = () => {
* @returns {Array} Um array contendo todos os agendamentos que correspondem ao idMedicoFiltrado. * @returns {Array} Um array contendo todos os agendamentos que correspondem ao idMedicoFiltrado.
*/ */
const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => { const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => {
// O corpo da função deve usar esses nomes de variáveis: // O corpo da função deve usar esses nomes de variáveis:
const todasAsListasDeAgendamentos = Object.values(dictAgendamentos); const todasAsListasDeAgendamentos = Object.values(dictAgendamentos);
const todosOsAgendamentos = todasAsListasDeAgendamentos.flat(); const todosOsAgendamentos = todasAsListasDeAgendamentos.flat();
const agendamentosFiltrados = todosOsAgendamentos.filter(agendamento => const agendamentosFiltrados = todosOsAgendamentos.filter(
agendamento.doctor_id === idMedicoFiltrado (agendamento) => agendamento.doctor_id === idMedicoFiltrado
); );
return agendamentosFiltrados; return agendamentosFiltrados;
}; };
// Dados da fila de espera (sem alteração) // Dados da fila de espera (sem alteração)
const filaEsperaData = [ const filaEsperaData = [
{ nome: 'Ricardo Pereira', email: 'ricardo.pereira@gmail.com', cpf: '444.777.666-55', telefone: '(79) 99123-4567', entrada: '25/09/2025 às 08:00' }, {
{ nome: 'Ana Costa', email: 'ana.costa@gmail.com', cpf: '321.654.987-00', telefone: '(79) 97777-3333', entrada: '25/09/2025 às 08:30' }, nome: "Ricardo Pereira",
{ nome: 'Lucas Martins', email: 'lucas.martins@gmail.com', cpf: '777.666.555-33', telefone: '(79) 99654-3210', entrada: '25/09/2025 às 09:00' }, email: "ricardo.pereira@gmail.com",
{ nome: 'João Souza', email: 'joao.souza@gmail.com', cpf: '987.654.321-00', telefone: '(79) 98888-2222', entrada: '25/09/2025 às 14:00' }, cpf: "444.777.666-55",
{ nome: 'Maria Silva', email: 'maria.silva@gmail.com', cpf: '123.456.789-00', telefone: '(79) 99999-1111', entrada: '25/09/2025 às 14:30' }, telefone: "(79) 99123-4567",
{ nome: 'Fernanda Lima', email: 'fernanda.lima@gmail.com', cpf: '888.999.000-22', telefone: '(79) 98877-6655', entrada: '26/09/2025 às 09:30' }, entrada: "25/09/2025 às 08:00",
{ nome: 'Carlos Andrade', email: 'carlos.andrade@gmail.com', cpf: '222.555.888-11', telefone: '(79) 99876-5432', entrada: '26/09/2025 às 10:00' }, },
{ nome: 'Juliana Oliveira', email: 'juliana.o@gmail.com', cpf: '111.222.333-44', telefone: '(79) 98765-1234', entrada: '26/09/2025 às 11:30' }, {
nome: "Ana Costa",
email: "ana.costa@gmail.com",
cpf: "321.654.987-00",
telefone: "(79) 97777-3333",
entrada: "25/09/2025 às 08:30",
},
{
nome: "Lucas Martins",
email: "lucas.martins@gmail.com",
cpf: "777.666.555-33",
telefone: "(79) 99654-3210",
entrada: "25/09/2025 às 09:00",
},
{
nome: "João Souza",
email: "joao.souza@gmail.com",
cpf: "987.654.321-00",
telefone: "(79) 98888-2222",
entrada: "25/09/2025 às 14:00",
},
{
nome: "Maria Silva",
email: "maria.silva@gmail.com",
cpf: "123.456.789-00",
telefone: "(79) 99999-1111",
entrada: "25/09/2025 às 14:30",
},
{
nome: "Fernanda Lima",
email: "fernanda.lima@gmail.com",
cpf: "888.999.000-22",
telefone: "(79) 98877-6655",
entrada: "26/09/2025 às 09:30",
},
{
nome: "Carlos Andrade",
email: "carlos.andrade@gmail.com",
cpf: "222.555.888-11",
telefone: "(79) 99876-5432",
entrada: "26/09/2025 às 10:00",
},
{
nome: "Juliana Oliveira",
email: "juliana.o@gmail.com",
cpf: "111.222.333-44",
telefone: "(79) 98765-1234",
entrada: "26/09/2025 às 11:30",
},
]; ];
// Filtro da fila de espera (sem alteração) // Filtro da fila de espera (sem alteração)
const filteredFila = filaEsperaData.filter(item => const filteredFila = filaEsperaData.filter(
(item) =>
item.nome.toLowerCase().includes(searchTerm.toLowerCase()) || item.nome.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.email.toLowerCase().includes(searchTerm.toLowerCase()) || item.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.cpf.includes(searchTerm) || item.cpf.includes(searchTerm) ||
@ -192,9 +238,11 @@ const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => {
for (const semana in AgendamentosMes) { for (const semana in AgendamentosMes) {
filteredData[semana] = {}; filteredData[semana] = {};
for (const dia in AgendamentosMes[semana]) { for (const dia in AgendamentosMes[semana]) {
filteredData[semana][dia] = AgendamentosMes[semana][dia].filter(agendamento => filteredData[semana][dia] = AgendamentosMes[semana][dia].filter(
agendamento.status === 'vazio' || (agendamento) =>
(agendamento.paciente && agendamento.paciente.toLowerCase().includes(lowerCaseSearchTerm)) agendamento.status === "vazio" ||
(agendamento.paciente &&
agendamento.paciente.toLowerCase().includes(lowerCaseSearchTerm))
); );
} }
} }
@ -202,65 +250,98 @@ const filtrarAgendamentosPorMedico = (dictAgendamentos, idMedicoFiltrado) => {
}, [searchTerm]); }, [searchTerm]);
const ListarDiasdoMes = (ano, mes) => { const ListarDiasdoMes = (ano, mes) => {
let segundas = []; let tercas = []; let quartas = []; let quintas = []; let sextas = [] let segundas = [];
const base = dayjs(`${ano}-${mes}-01`) let tercas = [];
const DiasnoMes = base.daysInMonth() let quartas = [];
let quintas = [];
let sextas = [];
const base = dayjs(`${ano}-${mes}-01`);
const DiasnoMes = base.daysInMonth();
for (let d = 1; d <= DiasnoMes; d++) { for (let d = 1; d <= DiasnoMes; d++) {
const data = dayjs(`${ano}-${mes}-${d}`) const data = dayjs(`${ano}-${mes}-${d}`);
const dia = data.format('dddd') const dia = data.format("dddd");
switch (dia) { switch (dia) {
case 'Monday': segundas.push(d); break case "Monday":
case 'Tuesday': tercas.push(d); break segundas.push(d);
case 'Wednesday': quartas.push(d); break break;
case 'Thursday': quintas.push(d); break case "Tuesday":
case 'Friday': sextas.push(d); break tercas.push(d);
default: break 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} let ListaDiasDatas = {
return ListaDiasDatas segundas: segundas,
} tercas: tercas,
quartas: quartas,
const handleClickAgendamento = (agendamento) => { quintas: quintas,
if (agendamento.status !== 'vazio') return sextas: sextas,
else setPageConsulta(true) };
return ListaDiasDatas;
}; };
const handleClickAgendamento = (agendamento) => {
if (agendamento.status !== "vazio") return;
else setPageConsulta(true);
};
const handleSearchMedicos = (term) => { const handleSearchMedicos = (term) => {
setSearchTermDoctor(term); setSearchTermDoctor(term);
if (term.trim() === '') { if (term.trim() === "") {
setFiltredTodosMedicos([]); setFiltredTodosMedicos([]);
return; return;
} }
// Lógica simples de filtragem: // Lógica simples de filtragem:
const filtered = ListaDeMedicos.filter(medico => const filtered = ListaDeMedicos.filter((medico) =>
medico.nomeMedico.toLowerCase().includes(term.toLowerCase()) medico.nomeMedico.toLowerCase().includes(term.toLowerCase())
); );
setFiltredTodosMedicos(filtered); setFiltredTodosMedicos(filtered);
}; };
const handleClickCancel = () => setPageConsulta(false);
const handleClickCancel = () => setPageConsulta(false)
return ( return (
<div> <div>
<h1>Agendar nova consulta</h1> <h1>Agendar nova consulta</h1>
<button className='manage-button btn' onClick={() => navigate('/secretaria/excecoes-disponibilidade')}> <div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
<div>
<button
className="manage-button btn"
onClick={() => navigate("/secretaria/excecoes-disponibilidade")}
>
<i className="bi bi-gear-fill me-1"></i>
Gerenciar Exceções
</button>
</div>
<div>
<button
className="manage-button btn"
onClick={() => navigate("/secretaria/disponibilidade")}
>
<i className="bi bi-gear-fill me-1"></i> <i className="bi bi-gear-fill me-1"></i>
Mudar Disponibilidade Mudar Disponibilidade
</button> </button>
</div>
</div>
{!PageNovaConsulta ? ( {!PageNovaConsulta ? (
<div className='atendimento-eprocura'> <div className="atendimento-eprocura">
<div className="busca-atendimento-container">
<div className='busca-atendimento-container'> <div className="input-e-dropdown-wrapper">
<div className="busca-atendimento">
<div className='input-e-dropdown-wrapper'>
<div className='busca-atendimento'>
<div> <div>
<i className="fa-solid fa-calendar-day"></i> <i className="fa-solid fa-calendar-day"></i>
<input <input
@ -274,11 +355,11 @@ const handleSearchMedicos = (term) => {
{/* DROPDOWN (RENDERIZAÇÃO CONDICIONAL) */} {/* DROPDOWN (RENDERIZAÇÃO CONDICIONAL) */}
{searchTermDoctor && FiltredTodosMedicos.length > 0 && ( {searchTermDoctor && FiltredTodosMedicos.length > 0 && (
<div className='dropdown-medicos'> <div className="dropdown-medicos">
{FiltredTodosMedicos.map((medico) => ( {FiltredTodosMedicos.map((medico) => (
<div <div
key={medico.id} key={medico.id}
className='dropdown-item' className="dropdown-item"
onClick={() => { onClick={() => {
// Ação ao selecionar o médico // Ação ao selecionar o médico
setSearchTermDoctor(medico.nomeMedico); // Preenche o input setSearchTermDoctor(medico.nomeMedico); // Preenche o input
@ -294,71 +375,113 @@ const handleSearchMedicos = (term) => {
</div> </div>
</div> </div>
<div className="unidade-selecionarprofissional">
<div className='unidade-selecionarprofissional'>
<select> <select>
<option value="" disabled selected >Unidade</option> <option value="" disabled selected>
Unidade
</option>
<option value="">Unidade Central</option> <option value="">Unidade Central</option>
<option value="">Unidade Zona Norte</option> <option value="">Unidade Zona Norte</option>
<option value="">Unidade Zona Oeste</option> <option value="">Unidade Zona Oeste</option>
</select> </select>
<input type="text" placeholder='Selecionar profissional' /> <input type="text" placeholder="Selecionar profissional" />
</div> </div>
<div className='container-btns-agenda-fila_esepera'> <div className="container-btns-agenda-fila_esepera">
<button <button
className={`btn-agenda ${FiladeEspera === false ? "opc-agenda-ativo" : ""}`} className={`btn-agenda ${
FiladeEspera === false ? "opc-agenda-ativo" : ""
}`}
onClick={() => { onClick={() => {
setFiladeEspera(false); setFiladeEspera(false);
setSearchTerm(''); setSearchTerm("");
}} }}
> >
Agenda Agenda
</button> </button>
<button <button
className={`btn-fila-espera ${FiladeEspera === true ? "opc-filaespera-ativo" : ""}`} className={`btn-fila-espera ${
FiladeEspera === true ? "opc-filaespera-ativo" : ""
}`}
onClick={() => { onClick={() => {
setFiladeEspera(true); setFiladeEspera(true);
setSearchTerm(''); setSearchTerm("");
}} }}
> >
Fila de espera Fila de espera
</button> </button>
</div> </div>
<section className='calendario-ou-filaespera'> <section className="calendario-ou-filaespera">
{FiladeEspera === false ? {FiladeEspera === false ? (
( <div className="calendario">
<div className='calendario'>
<div> <div>
<section className='btns-e-legenda-container'> <section className="btns-e-legenda-container">
<div> <div>
<button className={`btn-selecionar-tabeladia ${tabela === "diario" ? "ativo" : ""}`} onClick={() => setTabela("diario")}> <button
className={`btn-selecionar-tabeladia ${
tabela === "diario" ? "ativo" : ""
}`}
onClick={() => setTabela("diario")}
>
<i className="fa-solid fa-calendar-day"></i> Dia <i className="fa-solid fa-calendar-day"></i> Dia
</button> </button>
<button className={`btn-selecionar-tabelasemana ${tabela === 'semanal' ? 'ativo' : ""}`} onClick={() => setTabela("semanal")}> <button
className={`btn-selecionar-tabelasemana ${
tabela === "semanal" ? "ativo" : ""
}`}
onClick={() => setTabela("semanal")}
>
<i className="fa-solid fa-calendar-day"></i> Semana <i className="fa-solid fa-calendar-day"></i> Semana
</button> </button>
<button className={`btn-selecionar-tabelames ${tabela === 'mensal' ? 'ativo' : ''}`} onClick={() => setTabela("mensal")}> <button
className={`btn-selecionar-tabelames ${
tabela === "mensal" ? "ativo" : ""
}`}
onClick={() => setTabela("mensal")}
>
<i className="fa-solid fa-calendar-day"></i> Mês <i className="fa-solid fa-calendar-day"></i> Mês
</button> </button>
</div> </div>
<div className='legenda-tabela'> <div className="legenda-tabela">
<div className='legenda-item-realizado'><span>Realizado</span></div> <div className="legenda-item-realizado">
<div className='legenda-item-confirmado'><span>Confirmado</span></div> <span>Realizado</span>
<div className='legenda-item-agendado'><span>Agendado</span></div> </div>
<div className='legenda-item-cancelado'><span>Cancelado</span></div> <div className="legenda-item-confirmado">
<span>Confirmado</span>
</div>
<div className="legenda-item-agendado">
<span>Agendado</span>
</div>
<div className="legenda-item-cancelado">
<span>Cancelado</span>
</div>
</div> </div>
</section> </section>
{tabela === "diario" && <TabelaAgendamentoDia handleClickAgendamento={handleClickAgendamento} agendamentos={DictAgendamentosOrganizados} setShowDeleteModal={setShowDeleteModal} />} {tabela === "diario" && (
{tabela === 'semanal' && <TabelaAgendamentoSemana agendamentos={DictAgendamentosOrganizados} ListarDiasdoMes={ListarDiasdoMes}/>} <TabelaAgendamentoDia
{tabela === 'mensal' && <TabelaAgendamentoMes ListarDiasdoMes={ListarDiasdoMes} aplicarCores={true} agendamentos={DictAgendamentosOrganizados} />} handleClickAgendamento={handleClickAgendamento}
agendamentos={DictAgendamentosOrganizados}
setShowDeleteModal={setShowDeleteModal}
/>
)}
{tabela === "semanal" && (
<TabelaAgendamentoSemana
agendamentos={DictAgendamentosOrganizados}
ListarDiasdoMes={ListarDiasdoMes}
/>
)}
{tabela === "mensal" && (
<TabelaAgendamentoMes
ListarDiasdoMes={ListarDiasdoMes}
aplicarCores={true}
agendamentos={DictAgendamentosOrganizados}
/>
)}
</div> </div>
</div> </div>
) ) : (
:
(
<div className="fila-container"> <div className="fila-container">
<div className="fila-header"> <div className="fila-header">
<input <input
@ -393,8 +516,7 @@ const handleSearchMedicos = (term) => {
</tbody> </tbody>
</table> </table>
</div> </div>
) )}
}
</section> </section>
</div> </div>
) : ( ) : (
@ -415,7 +537,6 @@ const handleSearchMedicos = (term) => {
> >
<div className="modal-dialog modal-dialog-centered"> <div className="modal-dialog modal-dialog-centered">
<div className="modal-content"> <div className="modal-content">
<div className="modal-header bg-danger bg-opacity-25"> <div className="modal-header bg-danger bg-opacity-25">
<h5 className="modal-title text-danger"> <h5 className="modal-title text-danger">
Confirmação de Exclusão Confirmação de Exclusão
@ -434,7 +555,6 @@ const handleSearchMedicos = (term) => {
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button <button
type="button" type="button"
className="btn btn-primary" className="btn btn-primary"
@ -443,7 +563,6 @@ const handleSearchMedicos = (term) => {
Cancelar Cancelar
</button> </button>
<button <button
type="button" type="button"
className="btn btn-danger" className="btn btn-danger"
@ -454,11 +573,10 @@ const handleSearchMedicos = (term) => {
</div> </div>
</div> </div>
</div> </div>
</div>)}
</div> </div>
) )}
} </div>
);
};
export default Agendamento; export default Agendamento;

View File

@ -0,0 +1,145 @@
import React, { useState, useEffect, useCallback } from "react";
import { Link } from "react-router-dom";
const ENDPOINT_LISTAR = "https://mock.apidog.com/m1/1053378-0-default/rest/v1/doctor_availability";
const DisponibilidadesDoctorPage = () => {
const [disponibilidades, setDisponibilidades] = useState([]);
const [loading, setLoading] = useState(false);
const [filtroMedicoId, setFiltroMedicoId] = useState("");
const [filtroActive, setFiltroActive] = useState("true");
const [medicoValido, setMedicoValido] = useState(false);
const fetchDisponibilidades = useCallback(async (doctorId, activeStatus) => {
setLoading(true);
let url = `${ENDPOINT_LISTAR}?select=*`;
if (doctorId) url += `&doctor_id=eq.${doctorId}`;
if (activeStatus === "true" || activeStatus === "false") url += `&active=eq.${activeStatus}`;
try {
const response = await fetch(url);
const result = await response.json();
setDisponibilidades(Array.isArray(result) ? result : []);
setMedicoValido(Array.isArray(result) && result.length > 0);
} catch (error) {
setDisponibilidades([]);
setMedicoValido(false);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
if (filtroMedicoId) {
fetchDisponibilidades(filtroMedicoId, filtroActive);
} else {
setDisponibilidades([]);
setMedicoValido(false);
}
}, [filtroMedicoId, filtroActive, fetchDisponibilidades]);
return (
<div id="main-content">
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<h1 style={{ fontSize: "1.5rem", fontWeight: "bold", color: "#333" }}>
Disponibilidades por Médico
</h1>
<Link
to={medicoValido ? `../medicos/${filtroMedicoId}/edit` : "#"}
className="btn-primary"
style={{
padding: "10px 20px",
fontSize: "14px",
whiteSpace: "nowrap",
textDecoration: "none",
display: "inline-block",
opacity: medicoValido ? 1 : 0.6,
pointerEvents: medicoValido ? "auto" : "none",
}}
>
+ Gerenciar Disponibilidades
</Link>
</div>
<div className="atendimento-eprocura">
<div className="busca-atendimento">
<div>
<i className="fa-solid fa-user-doctor"></i>
<input
type="text"
placeholder="Filtrar por ID do Médico..."
value={filtroMedicoId}
onChange={(e) => setFiltroMedicoId(e.target.value)}
style={{ border: "1px solid #ccc", borderRadius: "4px", padding: "5px" }}
/>
</div>
<div>
<i className="fa-solid fa-check-circle"></i>
<select
value={filtroActive}
onChange={(e) => setFiltroActive(e.target.value)}
style={{ padding: "8px", border: "1px solid #ccc", borderRadius: "4px" }}
>
<option value="true">Ativas</option>
<option value="false">Inativas</option>
<option value="">Todas</option>
</select>
</div>
</div>
<section className="calendario-ou-filaespera">
<div className="fila-container">
<h2 className="fila-titulo">
Disponibilidades Encontradas ({disponibilidades.length})
</h2>
{loading ? (
<p className="text-center py-10">Carregando disponibilidades...</p>
) : disponibilidades.length === 0 ? (
<p className="text-center py-10">
Nenhuma disponibilidade encontrada para os filtros aplicados.
</p>
) : (
<table className="fila-tabela" style={{ width: "100%", borderCollapse: "collapse" }}>
<thead>
<tr>
{["ID", "ID Médico", "Dia da Semana", "Início", "Término", "Intervalo (min)", "Tipo Consulta", "Status"].map(
(header) => (
<th
key={header}
style={{ padding: "10px", borderBottom: "2px solid #ddd", textAlign: "left" }}
>
{header}
</th>
)
)}
</tr>
</thead>
<tbody>
{disponibilidades.map((disp, index) => (
<tr key={disp.id || index} style={{ borderBottom: "1px solid #eee" }}>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.id || "N/A"}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.doctor_id}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.weekday}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.start_time || "N/A"}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.end_time || "N/A"}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.slot_minutes}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.appointment_type}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>
<span className={`status-tag ${disp.active ? "legenda-item-realizado" : "legenda-item-cancelado"}`}>
{disp.active ? "Ativa" : "Inativa"}
</span>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</section>
</div>
</div>
);
};
export default DisponibilidadesDoctorPage;

View File

@ -1,77 +1,146 @@
import React from 'react' import React, { useEffect, useState, useCallback } from "react";
import { GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor' import { useParams, useSearchParams } from "react-router-dom";
import DoctorForm from '../components/doctors/DoctorForm' import { GetDoctorByID } from "../components/utils/Functions-Endpoints/Doctor";
import { useAuth } from '../components/utils/AuthProvider' import DoctorForm from "../components/doctors/DoctorForm";
import {useEffect, useState} from 'react' import { useAuth } from "../components/utils/AuthProvider";
import { useParams } from 'react-router-dom' import API_KEY from "../components/utils/apiKeys";
import API_KEY from '../components/utils/apiKeys'
const ENDPOINT_AVAILABILITY =
"https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
const DoctorEditPage = () => { const DoctorEditPage = () => {
const {getAuthorizationHeader, isAuthenticated} = useAuth(); const { getAuthorizationHeader } = useAuth();
const [DoctorToPUT, setDoctorPUT] = useState({}) const [DoctorToPUT, setDoctorPUT] = useState({});
const Parametros = useParams() const Parametros = useParams();
const [searchParams] = useSearchParams();
const DoctorID = Parametros.id;
const availabilityId = searchParams.get("availabilityId");
const DoctorID = Parametros.id const [availabilityToPATCH, setAvailabilityToPATCH] = useState(null);
const [mode, setMode] = useState("doctor");
useEffect(() => { useEffect(() => {
const authHeader = getAuthorizationHeader();
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) GetDoctorByID(DoctorID, authHeader)
.then((data) => { .then((data) => {
console.log(data, "médico vindo da API"); console.log(data, "médico vindo da API");
setDoctorPUT(data[0]) setDoctorPUT(data[0]);
; // supabase retorna array
}) })
.catch((err) => console.error("Erro ao buscar paciente:", err)); .catch((err) => console.error("Erro ao buscar paciente:", err));
}
}, [DoctorID, availabilityId, getAuthorizationHeader]);
}, [])
const HandlePutDoctor = async () => { const HandlePutDoctor = async () => {
const authHeader = getAuthorizationHeader() const authHeader = getAuthorizationHeader();
var myHeaders = new Headers(); var myHeaders = new Headers();
myHeaders.append('apikey', API_KEY) myHeaders.append("apikey", API_KEY);
myHeaders.append("Authorization", authHeader); myHeaders.append("Authorization", authHeader);
myHeaders.append("Content-Type", "application/json"); myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify(DoctorToPUT); var raw = JSON.stringify(DoctorToPUT);
console.log("Enviando médico para atualização:", DoctorToPUT); console.log("Enviando médico para atualização (PUT):", DoctorToPUT);
var requestOptions = { var requestOptions = {
method: 'PUT', method: "PUT",
headers: myHeaders, headers: myHeaders,
body: raw, body: raw,
redirect: 'follow' redirect: "follow",
}; };
try { try {
const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?id=eq.${DoctorID}`,requestOptions); const response = await fetch(
console.log(response) `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?id=eq.${DoctorID}`,
requestOptions
);
console.log("Resposta PUT Doutor:", response);
alert("Dados do médico atualizados com sucesso!");
} catch (error) { } catch (error) {
console.error("Erro ao atualizar paciente:", error); console.error("Erro ao atualizar médico:", error);
alert("Erro ao atualizar dados do médico.");
throw error; throw error;
} }
};
// 2. Função para Atualizar DISPONIBILIDADE (PATCH)
const HandlePatchAvailability = async (data) => {
const authHeader = getAuthorizationHeader();
var myHeaders = new Headers();
myHeaders.append("apikey", API_KEY);
myHeaders.append("Authorization", authHeader);
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify(data);
console.log("Enviando disponibilidade para atualização (PATCH):", data);
var requestOptions = {
method: "PATCH",
headers: myHeaders,
body: raw,
redirect: "follow",
};
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;
} }
};
return ( return (
<div> <div>
<h1 className="text-2xl font-bold mb-4">
{mode === "availability"
? `Editar Horário Disponível (ID: ${availabilityId.substring(0, 8)})`
: `Editar Médico (ID: ${DoctorID})`}
</h1>
<DoctorForm <DoctorForm
onSave={HandlePutDoctor} onSave={
mode === "availability" ? HandlePatchAvailability : HandlePutDoctor
formData={DoctorToPUT}
setFormData={setDoctorPUT}
/>
</div>
)
} }
formData={mode === "availability" ? availabilityToPATCH : DoctorToPUT}
setFormData={
mode === "availability" ? setAvailabilityToPATCH : setDoctorPUT
}
isEditingAvailability={mode === "availability"}
/>
</div>
);
};
export default DoctorEditPage export default DoctorEditPage;

View File

@ -2,7 +2,7 @@
import { Routes, Route } from "react-router-dom"; import { Routes, Route } from "react-router-dom";
import Sidebar from "../../components/Sidebar"; import Sidebar from "../../components/Sidebar";
import FinanceiroDashboard from "../../pages/FinanceiroDashboard"; import HorariosDisponibilidade from "../../components/doctors/HorariosDisponibilidade";
import SecretariaItems from "../../data/sidebar-items-secretaria.json"; import SecretariaItems from "../../data/sidebar-items-secretaria.json";
import Inicio from "../../pages/Inicio"; import Inicio from "../../pages/Inicio";
import TablePaciente from "../../pages/TablePaciente"; import TablePaciente from "../../pages/TablePaciente";
@ -16,6 +16,7 @@ import EditPage from "../../pages/EditPage";
import DoctorDetails from "../../pages/DoctorDetails"; import DoctorDetails from "../../pages/DoctorDetails";
import DoctorEditPage from "../../pages/DoctorEditPage"; import DoctorEditPage from "../../pages/DoctorEditPage";
import ExcecoesDisponibilidade from "../../pages/ExcecoesDisponibilidade"; import ExcecoesDisponibilidade from "../../pages/ExcecoesDisponibilidade";
import DisponibilidadesDoctorPage from "../../pages/DisponibilidadesDoctorPage"
import AgendamentoEditPage from "../../pages/AgendamentoEditPage"; import AgendamentoEditPage from "../../pages/AgendamentoEditPage";
function PerfilSecretaria({ onLogout }) { function PerfilSecretaria({ onLogout }) {
@ -37,8 +38,10 @@ function PerfilSecretaria({ onLogout }) {
<Route path="agendamento" element={<Agendamento />} /> <Route path="agendamento" element={<Agendamento />} />
<Route path="agendamento/:id/edit" element={<AgendamentoEditPage/>} /> <Route path="agendamento/:id/edit" element={<AgendamentoEditPage/>} />
<Route path="laudo" element={<LaudoManager />} /> <Route path="laudo" element={<LaudoManager />} />
<Route path="*" element={<h2>Página não encontrada</h2>} /> <Route path="disponibilidade" element={<DisponibilidadesDoctorPage />} />
<Route path="horarios" element={<HorariosDisponibilidade/>}/>
<Route path="excecoes-disponibilidade" element={<ExcecoesDisponibilidade />} /> <Route path="excecoes-disponibilidade" element={<ExcecoesDisponibilidade />} />
<Route path="*" element={<h2>Página não encontrada</h2>} />
</Routes> </Routes>
</div> </div>
</div> </div>