Resolução dos problemas na logica da pagina de Disponibiidades

This commit is contained in:
pedrofedericoo 2025-10-30 10:46:22 -03:00
commit e11d86db67
3 changed files with 270 additions and 163 deletions

View File

@ -126,12 +126,14 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}
};
const handleAvailabilityUpdate = useCallback(
(newAvailability) => {
setFormData((prev) => ({ ...prev, availability: newAvailability }));
},
[setFormData]
);
const handleAvailabilityUpdate = useCallback((newAvailability) => {
setFormData((prev) => {
if (JSON.stringify(prev.availability) !== JSON.stringify(newAvailability)) {
return { ...prev, availability: newAvailability };
}
return prev;
});
}, []);
const handleCepBlur = async () => {
const cep = formData.cep?.replace(/\D/g, "");
@ -229,25 +231,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}
};
const handlePatchAvailability = async (id, updatedAvailability) => {
try {
const response = await fetch(`${ENDPOINT_AVAILABILITY}?id=${id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updatedAvailability),
});
const data = await response.json();
console.log("Disponibilidade atualizada:", data);
alert("Disponibilidade atualizada com sucesso!");
} catch (error) {
console.error("Erro ao atualizar disponibilidade:", error);
alert("Erro ao atualizar disponibilidade.");
}
};
const handleSubmit = async () => {
const missingFields = [];
if (!formData.full_name) missingFields.push("full_name");
@ -290,20 +273,12 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
await onSave({ ...formData });
if (formData.availability && formData.availability.length > 0) {
if (formData.availabilityId) {
await handlePatchAvailability(
formData.availabilityId,
formData.availability
);
} else {
await handleCreateAvailability(formData.availability);
}
}
alert("Médico salvo e disponibilidade enviada ao mock com sucesso!");
alert("Médico salvo com sucesso!");
} catch (error) {
console.error("Erro ao salvar médico ou disponibilidade:", error);
alert("Erro ao salvar médico ou disponibilidade.");
console.error("Erro ao salvar médico:", error);
alert("Erro ao salvar médico.");
};
};

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useCallback, useRef } from "react";
import { Clock } from "lucide-react";
const initialBlockTemplate = {
@ -21,21 +21,19 @@ const emptyAvailabilityTemplate = [
const HorariosDisponibilidade = ({
initialAvailability = emptyAvailabilityTemplate,
onUpdate,
onCancel,
}) => {
const [availability, setAvailability] = useState(initialAvailability);
const isFirstRun = useRef(true);
useEffect(() => {
if (initialAvailability !== emptyAvailabilityTemplate) {
if (initialAvailability && initialAvailability.length > 0) {
setAvailability(initialAvailability);
} else {
setAvailability(emptyAvailabilityTemplate);
}
}, [initialAvailability]);
useEffect(() => {
if (onUpdate) {
onUpdate(availability);
}
}, [availability, onUpdate]);
const handleDayCheck = useCallback((dayIndex, currentIsChecked) => {
const isChecked = !currentIsChecked;
@ -110,6 +108,10 @@ const HorariosDisponibilidade = ({
);
}, []);
const handleSave = useCallback(() => {
if (onUpdate) onUpdate(availability);
}, [availability, onUpdate]);
const renderTimeBlock = (dayIndex, bloco) => (
<div
key={bloco.id}
@ -165,7 +167,7 @@ const HorariosDisponibilidade = ({
width: "100%",
boxSizing: "border-box",
outline: "none",
fontSize: "13px"
fontSize: "13px",
}}
step="300"
/>
@ -186,7 +188,12 @@ const HorariosDisponibilidade = ({
<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
<label
htmlFor={`termino-${dayIndex}-${bloco.id}`}
style={{ fontWeight: 500, color: "#4b5563", width: "56px", fontSize: "13px" }}
style={{
fontWeight: 500,
color: "#4b5563",
width: "56px",
fontSize: "13px",
}}
>
Término:
</label>
@ -199,13 +206,13 @@ const HorariosDisponibilidade = ({
handleTimeChange(dayIndex, bloco.id, "termino", e.target.value)
}
style={{
padding: "4px 6px",
border: "1px solid #d1d5db",
borderRadius: "6px",
width: "100%",
boxSizing: "border-box",
outline: "none",
fontSize: "13px",
padding: "4px 6px",
border: "1px solid #d1d5db",
borderRadius: "6px",
width: "100%",
boxSizing: "border-box",
outline: "none",
fontSize: "13px",
}}
step="300"
/>
@ -258,8 +265,7 @@ const HorariosDisponibilidade = ({
marginLeft: window.innerWidth < 640 ? "0" : "16px",
fontWeight: 500,
}}
>
</span>
></span>
)}
</div>
);

View File

@ -1,75 +1,68 @@
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";
import HorariosDisponibilidade from "../components/doctors/HorariosDisponibilidade";
const ENDPOINT =
"https://mock.apidog.com/m1/1053378-0-default/rest/v1/doctor_availability";
const MEDICOS_MOCKADOS = [
{ id: 53, nome: " João Silva" },
{ id: 19, nome: " Ana Costa" },
{ id: 11, nome: " Pedro Santos" },
{ id: 53, nome: "João Silva" },
{ id: 19, nome: "Ana Costa" },
{ id: 11, nome: "Pedro Santos" },
];
const diasDaSemana = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
const formatarDataHora = (isoString) => {
if (!isoString) return "N/A";
try {
const data = new Date(isoString);
return data.toLocaleTimeString("pt-BR", { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' });
} catch (error) {
// Usa o toLocaleTimeString para extrair hora e minuto
return data.toLocaleTimeString("pt-BR", {
hour: "2-digit",
minute: "2-digit",
timeZone: "UTC",
});
} catch {
return "Data Inválida";
}
};
const DisponibilidadesDoctorPage = () => {
const [disponibilidades, setDisponibilidades] = useState([]);
const [loading, setLoading] = useState(false);
const [filtroMedicoNome, setFiltroMedicoNome] = useState("");
const [medicoEncontradoId, setMedicoEncontradoId] = useState(null);
const [gerenciarModo, setGerenciarModo] = useState(false);
const [editando, setEditando] = useState(null); // ID da disponibilidade sendo editada
const encontrarMedicoIdPorNome = (nome) => {
if (!nome) return null;
const termoBusca = nome.toLowerCase();
const medico = MEDICOS_MOCKADOS.find(m =>
m.nome.toLowerCase().includes(termoBusca)
);
return medico ? medico.id : null;
if (!nome) return null;
const termo = nome.toLowerCase();
const medico = MEDICOS_MOCKADOS.find((m) =>
m.nome.toLowerCase().includes(termo)
);
return medico ? medico.id : null;
};
const fetchDisponibilidades = useCallback(async (nome) => {
setLoading(true);
setDisponibilidades([]);
setMedicoEncontradoId(null);
const doctorId = encontrarMedicoIdPorNome(nome);
if (!doctorId) {
setLoading(false);
return;
setLoading(false);
return;
}
const url = `${ENDPOINT_LISTAR}?select=*&doctor_id=eq.${doctorId}`;
try {
const response = await fetch(url);
const result = await response.json();
let dados = Array.isArray(result) ? result : [];
const res = await fetch(`${ENDPOINT}?doctor_id=eq.${doctorId}`);
const data = await res.json();
setDisponibilidades(dados);
setMedicoEncontradoId(doctorId);
} catch (error) {
setDisponibilidades(
Array.isArray(data)
? data
: data && Array.isArray(data.items)
? data.items
: []
);
} catch (e) {
console.error("Erro ao buscar disponibilidades:", e);
setDisponibilidades([]);
} finally {
setLoading(false);
@ -77,111 +70,244 @@ const DisponibilidadesDoctorPage = () => {
}, []);
useEffect(() => {
if (!gerenciarModo && editando) {
setEditando(null);
}
}, [gerenciarModo]);
useEffect(() => {
if (editando) return;
if (filtroMedicoNome) {
const timer = setTimeout(() => {
fetchDisponibilidades(filtroMedicoNome);
}, 300);
}, 300);
return () => clearTimeout(timer);
} else {
setDisponibilidades([]);
setMedicoEncontradoId(null);
setDisponibilidades([]);
}
}, [filtroMedicoNome, fetchDisponibilidades]);
}, [filtroMedicoNome, fetchDisponibilidades, editando]);
const rotaGerenciar = medicoEncontradoId
? `../medicos/${medicoEncontradoId}/edit`
: `../medicos/novo/edit`;
const atualizarDisponibilidade = async (id, novoIntervalo) => {
try {
const res = await fetch(`${ENDPOINT}?id=eq.${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ slot_minutes: novoIntervalo }),
});
if (res.ok) {
alert("Disponibilidade atualizada com sucesso!");
setEditando(null);
fetchDisponibilidades(filtroMedicoNome);
} else {
alert("Erro ao atualizar disponibilidade");
}
} catch {
alert("Falha ao conectar com o servidor");
}
};
const deletarDisponibilidade = async (id) => {
if (!window.confirm("Deseja realmente excluir esta disponibilidade?"))
return;
try {
const res = await fetch(`${ENDPOINT}?id=eq.${id}`, { method: "DELETE" });
if (res.ok) {
alert("Disponibilidade excluída!");
setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
} else {
alert("Erro ao excluir disponibilidade");
}
} catch {
alert("Erro ao conectar com o servidor");
}
};
const disponibilidadeParaEdicao = editando
? disponibilidades.find((d) => d.id === editando)
: null;
const initialAvailabilityParaEdicao = diasDaSemana.map((dia, weekdayIndex) => {
const blocosDoDia = disponibilidades
.filter(d => d.weekday === weekdayIndex)
.map(d => ({
id: d.id,
inicio: d.start_time
? new Date(d.start_time).toISOString().substring(11, 16)
: "07:00",
termino: d.end_time
? new Date(d.end_time).toISOString().substring(11, 16)
: "17:00",
isNew: false,
slot_minutes: d.slot_minutes,
}));
return {
dia,
isChecked: blocosDoDia.length > 0,
blocos: blocosDoDia,
};
});
const handleUpdateHorarios = (horariosAtualizados) => {
console.log("Horários editados:", horariosAtualizados);
setEditando(null);
fetchDisponibilidades(filtroMedicoNome);
};
return (
<div id="main-content">
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
{/* Cabeçalho */}
<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={rotaGerenciar}
{/* Botão Voltar/Gerenciar */}
<button
onClick={() => {
if (editando) {
setEditando(null); // Se está editando, volta para a tabela
} else {
setGerenciarModo((m) => !m); // Senão, alterna o modo de gerenciamento
}
}}
className="btn-primary"
style={{
padding: "10px 20px",
fontSize: "14px",
whiteSpace: "nowrap",
textDecoration: "none",
display: "inline-block",
}}
>
+ Gerenciar Disponibilidades
</Link>
{editando
? "← Voltar para Tabela"
: gerenciarModo
? "← Voltar"
: "+ Gerenciar Disponibilidades"}
</button>
</div>
<div className="atendimento-eprocura">
<div className="busca-atendimento">
<div style={{ marginRight: '10px' }}>
<i className="fa-solid fa-user-doctor"></i>
{/* Campo de busca - ESCONDIDO NO MODO DE EDIÇÃO */}
{!editando && (
<div className="atendimento-eprocura">
<div className="busca-atendimento">
<input
type="text"
placeholder="Filtrar por Nome do Médico..."
value={filtroMedicoNome}
onChange={(e) => setFiltroMedicoNome(e.target.value)}
style={{ border: "1px solid #ccc", borderRadius: "4px", padding: "5px" }}
style={{
border: "1px solid #ccc",
borderRadius: "4px",
padding: "5px",
marginTop: "10px",
marginBottom: "10px",
}}
/>
</div>
</div>
)}
<section className="calendario-ou-filaespera">
<div className="fila-container">
<h2 className="fila-titulo">
Disponibilidades Encontradas ({disponibilidades.length})
</h2>
<section className="calendario-ou-filaespera">
<div className="fila-container">
<h2 className="fila-titulo">
{editando
? "Editar Disponibilidade"
: gerenciarModo
? "Gerenciar Disponibilidades"
: "Disponibilidades Encontradas"}{" "}
({disponibilidades.length})
</h2>
{loading ? (
<p className="text-center py-10">Carregando disponibilidades...</p>
) : (filtroMedicoNome && disponibilidades.length === 0) ? (
<p className="text-center py-10">
Nenhuma disponibilidade encontrada para o nome buscado.
</p>
) : (
<table className="fila-tabela" style={{ width: "100%", borderCollapse: "collapse" }}>
<thead>
<tr>
{[ "Dia da Semana", "Início", "Término", "Intervalo", "Tipo Consulta"].map(
(header) => (
<th
key={header}
style={{ padding: "10px", borderBottom: "2px solid #ddd", textAlign: "left" }}
{loading ? (
<p>Carregando...</p>
) : disponibilidades.length === 0 ? (
<p>Nenhuma disponibilidade encontrada.</p>
) : editando ? (
<>
<HorariosDisponibilidade
initialAvailability={initialAvailabilityParaEdicao}
onUpdate={handleUpdateHorarios}
/>
<button
onClick={() =>
handleUpdateHorarios(initialAvailabilityParaEdicao)
}
style={{
marginTop: "20px",
padding: "10px 20px",
fontSize: "16px",
fontWeight: "bold",
borderRadius: "8px",
backgroundColor: "#3b82f6",
color: "white",
border: "none",
cursor: "pointer",
}}
>
Salvar Alterações
</button>
</>
) : (
<table style={{ width: "100%", borderCollapse: "collapse" }}>
<thead>
<tr>
<th>Dia da Semana</th>
<th>Início</th>
<th>Término</th>
<th>Intervalo</th>
<th>Tipo Consulta</th>
{gerenciarModo && <th>Ações</th>}
</tr>
</thead>
<tbody>
{disponibilidades.map((disp) => (
<tr key={disp.id}>
<td>{diasDaSemana[disp.weekday]}</td>
<td>{formatarDataHora(disp.start_time)}</td>
<td>{formatarDataHora(disp.end_time)}</td>
<td>{disp.slot_minutes}</td>
<td>{disp.appointment_type}</td>
{gerenciarModo && (
<td>
<button
onClick={() => setEditando(disp.id)}
style={{
backgroundColor: "#10b981",
color: "white",
borderRadius: "6px",
}}
>
{header}
</th>
)
Editar
</button>{" "}
<button
onClick={() => deletarDisponibilidade(disp.id)}
style={{
backgroundColor: "#c72f2f",
color: "white",
borderRadius: "6px",
}}
>
Excluir
</button>
</td>
)}
</tr>
</thead>
<tbody>
{disponibilidades.map((disp, index) => (
<tr key={disp.id || index} style={{ borderBottom: "1px solid #eee" }}>
<td style={{ padding: "10px", fontSize: "0.9em" }}>
{diasDaSemana[disp.weekday] || disp.weekday}
</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>
{formatarDataHora(disp.start_time)}
</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>
{formatarDataHora(disp.end_time)}
</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.slot_minutes}</td>
<td style={{ padding: "10px", fontSize: "0.9em" }}>{disp.appointment_type}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</section>
</div>
))}
</tbody>
</table>
)}
</div>
</section>
</div>
);
};
export default DisponibilidadesDoctorPage;
export default DisponibilidadesDoctorPage;