diff --git a/src/components/doctors/DoctorForm.jsx b/src/components/doctors/DoctorForm.jsx index e7e7bea..5a0befb 100644 --- a/src/components/doctors/DoctorForm.jsx +++ b/src/components/doctors/DoctorForm.jsx @@ -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."); }; }; diff --git a/src/components/doctors/HorariosDisponibilidade.jsx b/src/components/doctors/HorariosDisponibilidade.jsx index 793c161..bc8b83b 100644 --- a/src/components/doctors/HorariosDisponibilidade.jsx +++ b/src/components/doctors/HorariosDisponibilidade.jsx @@ -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) => (
@@ -186,7 +188,12 @@ const HorariosDisponibilidade = ({
@@ -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, }} - > - + > )}
); diff --git a/src/pages/DisponibilidadesDoctorPage.jsx b/src/pages/DisponibilidadesDoctorPage.jsx index a035dfe..e107595 100644 --- a/src/pages/DisponibilidadesDoctorPage.jsx +++ b/src/pages/DisponibilidadesDoctorPage.jsx @@ -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 (
-
+ {/* Cabeçalho */} +

Disponibilidades por Médico

- - { + 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 - + {editando + ? "← Voltar para Tabela" + : gerenciarModo + ? "← Voltar" + : "+ Gerenciar Disponibilidades"} +
-
-
-
- + {/* Campo de busca - ESCONDIDO NO MODO DE EDIÇÃO */} + {!editando && ( +
+
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", + }} />
+ )} -
-
-

- Disponibilidades Encontradas ({disponibilidades.length}) -

+
+
+

+ {editando + ? "Editar Disponibilidade" + : gerenciarModo + ? "Gerenciar Disponibilidades" + : "Disponibilidades Encontradas"}{" "} + ({disponibilidades.length}) +

- {loading ? ( -

Carregando disponibilidades...

- ) : (filtroMedicoNome && disponibilidades.length === 0) ? ( -

- Nenhuma disponibilidade encontrada para o nome buscado. -

- ) : ( - - - - {[ "Dia da Semana", "Início", "Término", "Intervalo", "Tipo Consulta"].map( - (header) => ( -
Carregando...

+ ) : disponibilidades.length === 0 ? ( +

Nenhuma disponibilidade encontrada.

+ ) : editando ? ( + <> + + + + ) : ( + + + + + + + + + {gerenciarModo && } + + + + {disponibilidades.map((disp) => ( + + + + + + + {gerenciarModo && ( + )} - - - {disponibilidades.map((disp, index) => ( - - - - - - - - ))} - -
Dia da SemanaInícioTérminoIntervaloTipo ConsultaAções
{diasDaSemana[disp.weekday]}{formatarDataHora(disp.start_time)}{formatarDataHora(disp.end_time)}{disp.slot_minutes}{disp.appointment_type} + {" "} + +
- {diasDaSemana[disp.weekday] || disp.weekday} - - {formatarDataHora(disp.start_time)} - - {formatarDataHora(disp.end_time)} - {disp.slot_minutes}{disp.appointment_type}
- )} - - - + ))} + +
+ )} +
+
); }; -export default DisponibilidadesDoctorPage; \ No newline at end of file +export default DisponibilidadesDoctorPage;