Disponibilidades Atualizadas
This commit is contained in:
parent
63121d6702
commit
5aaaf7d3e5
31
package-lock.json
generated
31
package-lock.json
generated
@ -31871,23 +31871,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
|
||||||
"version": "2.8.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
|
||||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
|
||||||
"prettier": "bin-prettier.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.13.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pretty-bytes": {
|
"node_modules/pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||||
@ -33013,20 +32996,6 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-scripts/node_modules/yaml": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
|
||||||
"license": "ISC",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
|
||||||
"yaml": "bin.mjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-toastify": {
|
"node_modules/react-toastify": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
||||||
|
|||||||
@ -199,3 +199,14 @@
|
|||||||
padding: 0.6rem 1.2rem;
|
padding: 0.6rem 1.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.doctor-form-container { padding: 0.75rem; }
|
||||||
|
.doctor-form-title { font-size: 1.75rem; }
|
||||||
|
.form-section { padding: 0.75rem; }
|
||||||
|
.section-header { font-size: 1.25rem; }
|
||||||
|
.form-label { font-size: 1rem; }
|
||||||
|
.form-control-custom { font-size: 1rem; }
|
||||||
|
.btns-container { display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
.btn-submit, .btn-cancel { width: 100%; margin-right: 0; }
|
||||||
|
}
|
||||||
@ -135,14 +135,11 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAvailabilityUpdate = useCallback((newAvailability) => {
|
const handleAvailabilityUpdate = useCallback((newAvailability) => {
|
||||||
setFormData((prev) => {
|
setFormData((prev) => {
|
||||||
if (JSON.stringify(prev.availability) !== JSON.stringify(newAvailability)) {
|
|
||||||
return { ...prev, availability: newAvailability };
|
return { ...prev, availability: newAvailability };
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
});
|
});
|
||||||
}, []);
|
}, [setFormData]);
|
||||||
|
|
||||||
const handleCepBlur = async () => {
|
const handleCepBlur = async () => {
|
||||||
const cep = formData.cep?.replace(/\D/g, "");
|
const cep = formData.cep?.replace(/\D/g, "");
|
||||||
@ -326,17 +323,9 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const savedDoctor = await onSave({ ...formData });
|
// Chama a função onSave (handleSave no DoctorEditPage) com o formData completo.
|
||||||
|
// A lógica de salvamento do médico e da disponibilidade é responsabilidade do componente pai.
|
||||||
if (formData.availability && formData.availability.length > 0 && savedDoctor.id) {
|
await onSave({ ...formData });
|
||||||
if (formData.availabilityId) {
|
|
||||||
|
|
||||||
await handlePatchAvailability(formData.availabilityId, formData.availability);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
await handleCreateAvailability(savedDoctor.id, formData.availability);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao salvar médico ou disponibilidade:", error);
|
console.error("Erro ao salvar médico ou disponibilidade:", error);
|
||||||
|
|||||||
@ -152,3 +152,17 @@
|
|||||||
.btn-add:hover {
|
.btn-add:hover {
|
||||||
background-color: #059669;
|
background-color: #059669;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.horarios-container { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.horarios-container { grid-template-columns: 1fr; gap: 10px; }
|
||||||
|
.day-card { height: auto; }
|
||||||
|
.day-header label { font-size: 13px; }
|
||||||
|
.time-inputs { flex-direction: column; }
|
||||||
|
.input-wrapper input { font-size: 12px; }
|
||||||
|
.btn-add { font-size: 12px; }
|
||||||
|
.btn-remove { font-size: 12px; }
|
||||||
|
}
|
||||||
@ -10,13 +10,13 @@ const initialBlockTemplate = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const emptyAvailabilityTemplate = [
|
const emptyAvailabilityTemplate = [
|
||||||
{ dia: "Segunda-feira", isChecked: false, blocos: [] },
|
{ dia: "Domingo", weekday: 0, isChecked: false, blocos: [] },
|
||||||
{ dia: "Terça-feira", isChecked: false, blocos: [] },
|
{ dia: "Segunda-feira", weekday: 1, isChecked: false, blocos: [] },
|
||||||
{ dia: "Quarta-feira", isChecked: false, blocos: [] },
|
{ dia: "Terça-feira", weekday: 2, isChecked: false, blocos: [] },
|
||||||
{ dia: "Quinta-feira", isChecked: false, blocos: [] },
|
{ dia: "Quarta-feira", weekday: 3, isChecked: false, blocos: [] },
|
||||||
{ dia: "Sexta-feira", isChecked: false, blocos: [] },
|
{ dia: "Quinta-feira", weekday: 4, isChecked: false, blocos: [] },
|
||||||
{ dia: "Sábado", isChecked: false, blocos: [] },
|
{ dia: "Sexta-feira", weekday: 5, isChecked: false, blocos: [] },
|
||||||
{ dia: "Domingo", isChecked: false, blocos: [] },
|
{ dia: "Sábado", weekday: 6, isChecked: false, blocos: [] },
|
||||||
];
|
];
|
||||||
|
|
||||||
const HorariosDisponibilidade = ({
|
const HorariosDisponibilidade = ({
|
||||||
@ -35,10 +35,18 @@ const HorariosDisponibilidade = ({
|
|||||||
}
|
}
|
||||||
}, [initialAvailability]);
|
}, [initialAvailability]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFirstRun.current) {
|
||||||
|
isFirstRun.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (onUpdate) onUpdate(availability);
|
||||||
|
}, [availability, onUpdate]);
|
||||||
|
|
||||||
const handleDayCheck = useCallback((dayIndex, currentIsChecked) => {
|
const handleDayCheck = useCallback((dayIndex, currentIsChecked) => {
|
||||||
const isChecked = !currentIsChecked;
|
const isChecked = !currentIsChecked;
|
||||||
setAvailability((prev) =>
|
setAvailability((prev) => {
|
||||||
prev.map((day, i) =>
|
const updated = prev.map((day, i) =>
|
||||||
i === dayIndex
|
i === dayIndex
|
||||||
? {
|
? {
|
||||||
...day,
|
...day,
|
||||||
@ -56,8 +64,10 @@ const HorariosDisponibilidade = ({
|
|||||||
: [],
|
: [],
|
||||||
}
|
}
|
||||||
: day
|
: day
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
console.log('handleDayCheck - updated availability:', updated);
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleAddBlock = useCallback((dayIndex) => {
|
const handleAddBlock = useCallback((dayIndex) => {
|
||||||
@ -107,9 +117,7 @@ const HorariosDisponibilidade = ({
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSave = useCallback(() => {
|
|
||||||
if (onUpdate) onUpdate(availability);
|
|
||||||
}, [availability, onUpdate]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="horarios-container">
|
<div className="horarios-container">
|
||||||
|
|||||||
@ -7,9 +7,29 @@ import "./style/DisponibilidadesDoctorPage.css";
|
|||||||
const ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
|
const ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
|
||||||
const DOCTORS_ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors";
|
const DOCTORS_ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors";
|
||||||
|
|
||||||
const diasDaSemana = ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"];
|
const diasDaSemana = [
|
||||||
|
"Domingo",
|
||||||
|
"Segunda",
|
||||||
|
"Terça",
|
||||||
|
"Quarta",
|
||||||
|
"Quinta",
|
||||||
|
"Sexta",
|
||||||
|
"Sábado"
|
||||||
|
];
|
||||||
|
const weekdayNumToStr = {
|
||||||
|
0: "sunday",
|
||||||
|
1: "monday",
|
||||||
|
2: "tuesday",
|
||||||
|
3: "wednesday",
|
||||||
|
4: "thursday",
|
||||||
|
5: "friday",
|
||||||
|
6: "saturday",
|
||||||
|
};
|
||||||
|
const weekdayStrToNum = Object.fromEntries(
|
||||||
|
Object.entries(weekdayNumToStr).map(([num, str]) => [str, Number(num)])
|
||||||
|
);
|
||||||
|
|
||||||
const DisponibilidadesDoctorPage = ( ) => {
|
const DisponibilidadesDoctorPage = () => {
|
||||||
const { getAuthorizationHeader } = useAuth();
|
const { getAuthorizationHeader } = useAuth();
|
||||||
const [disponibilidades, setDisponibilidades] = useState([]);
|
const [disponibilidades, setDisponibilidades] = useState([]);
|
||||||
const [doctors, setDoctors] = useState([]);
|
const [doctors, setDoctors] = useState([]);
|
||||||
@ -17,6 +37,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
const [editando, setEditando] = useState(null);
|
const [editando, setEditando] = useState(null);
|
||||||
const [expandedDoctors, setExpandedDoctors] = useState({});
|
const [expandedDoctors, setExpandedDoctors] = useState({});
|
||||||
const [showSuggestions, setShowSuggestions] = useState(false);
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||||
|
const [availabilityEdit, setAvailabilityEdit] = useState([]);
|
||||||
|
|
||||||
const getHeaders = () => {
|
const getHeaders = () => {
|
||||||
const myHeaders = new Headers();
|
const myHeaders = new Headers();
|
||||||
@ -24,6 +45,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
if (authHeader) myHeaders.append("Authorization", authHeader);
|
if (authHeader) myHeaders.append("Authorization", authHeader);
|
||||||
myHeaders.append("Content-Type", "application/json");
|
myHeaders.append("Content-Type", "application/json");
|
||||||
if (API_KEY) myHeaders.append("apikey", API_KEY);
|
if (API_KEY) myHeaders.append("apikey", API_KEY);
|
||||||
|
myHeaders.append("Prefer", "return=representation");
|
||||||
return myHeaders;
|
return myHeaders;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,15 +53,13 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
const fetchDoctors = async () => {
|
const fetchDoctors = async () => {
|
||||||
try {
|
try {
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: getHeaders(),
|
headers: getHeaders(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(DOCTORS_ENDPOINT, requestOptions);
|
const response = await fetch(DOCTORS_ENDPOINT, requestOptions);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
setDoctors(Array.isArray(result) ? result : []);
|
setDoctors(Array.isArray(result) ? result : []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao carregar médicos:", error);
|
|
||||||
setDoctors([]);
|
setDoctors([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -49,16 +69,12 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchDisponibilidades = async () => {
|
const fetchDisponibilidades = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(ENDPOINT, {
|
const res = await fetch(ENDPOINT, { method: "GET", headers: getHeaders() });
|
||||||
method: "GET",
|
|
||||||
headers: getHeaders()
|
|
||||||
});
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setDisponibilidades(Array.isArray(data) ? data : []);
|
setDisponibilidades(Array.isArray(data) ? data : []);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao buscar disponibilidades:", error);
|
|
||||||
setDisponibilidades([]);
|
setDisponibilidades([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -66,35 +82,24 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
}, [getAuthorizationHeader]);
|
}, [getAuthorizationHeader]);
|
||||||
|
|
||||||
const toggleExpandDoctor = (doctorId) => {
|
const toggleExpandDoctor = (doctorId) => {
|
||||||
setExpandedDoctors((prev) => ({
|
setExpandedDoctors((prev) => ({ ...prev, [doctorId]: !prev[doctorId] }));
|
||||||
...prev,
|
|
||||||
[doctorId]: !prev[doctorId],
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const salvarTodasDisponibilidades = async (doctorId, horariosAtualizados) => {
|
const salvarTodasDisponibilidades = async (doctorId, horariosAtualizados) => {
|
||||||
try {
|
try {
|
||||||
const headers = getHeaders();
|
const headers = getHeaders();
|
||||||
|
const promises = [];
|
||||||
// 1. Obter as disponibilidades existentes para este médico
|
const currentIds = new Set();
|
||||||
const existingDisponibilidadesRes = await fetch(`${ENDPOINT}?doctor_id=eq.${String(doctorId)}`, {
|
|
||||||
method: "GET",
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
const existingDisponibilidades = existingDisponibilidadesRes.ok ? await existingDisponibilidadesRes.json() : [];
|
|
||||||
|
|
||||||
const disponibilidadesParaManter = new Set();
|
|
||||||
|
|
||||||
for (const dia of horariosAtualizados) {
|
for (const dia of horariosAtualizados) {
|
||||||
if (dia.isChecked && dia.blocos.length > 0) {
|
if (dia.isChecked && dia.blocos.length > 0) {
|
||||||
// Processar blocos de horários
|
|
||||||
for (const bloco of dia.blocos) {
|
for (const bloco of dia.blocos) {
|
||||||
const inicio = bloco.inicio.includes(":") ? bloco.inicio : bloco.inicio + ":00";
|
const inicio = bloco.inicio.includes(":") ? bloco.inicio : bloco.inicio + ":00";
|
||||||
const termino = bloco.termino.includes(":") ? bloco.termino : bloco.termino + ":00";
|
const termino = bloco.termino.includes(":") ? bloco.termino : bloco.termino + ":00";
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
doctor_id: doctorId,
|
doctor_id: doctorId,
|
||||||
weekday: dia.weekday,
|
weekday: weekdayNumToStr[dia.weekday],
|
||||||
start_time: inicio,
|
start_time: inicio,
|
||||||
end_time: termino,
|
end_time: termino,
|
||||||
slot_minutes: bloco.slot_minutes || 30,
|
slot_minutes: bloco.slot_minutes || 30,
|
||||||
@ -103,82 +108,74 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (bloco.id && !bloco.isNew) {
|
if (bloco.id && !bloco.isNew) {
|
||||||
// Atualizar disponibilidade existente
|
currentIds.add(bloco.id);
|
||||||
await fetch(`${ENDPOINT}?id=eq.${bloco.id}`, {
|
promises.push(
|
||||||
|
fetch(`${ENDPOINT}?id=eq.${bloco.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
}).then(() => ({ type: 'PATCH', id: bloco.id }))
|
||||||
disponibilidadesParaManter.add(bloco.id);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Criar nova disponibilidade
|
promises.push(
|
||||||
const postRes = await fetch(ENDPOINT, {
|
fetch(ENDPOINT, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
})
|
||||||
if (postRes.ok) {
|
.then(res => res.json())
|
||||||
const newDisp = await postRes.json();
|
.then(data => {
|
||||||
// Adicionar o ID da nova disponibilidade para evitar exclusão acidental
|
const createdItem = Array.isArray(data) ? data[0] : data;
|
||||||
if (newDisp && newDisp.length > 0) {
|
return { type: 'POST', id: createdItem?.id };
|
||||||
disponibilidadesParaManter.add(newDisp[0].id);
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Excluir disponibilidades antigas que não foram mantidas
|
|
||||||
const disponibilidadesParaExcluir = existingDisponibilidades.filter(
|
|
||||||
(disp) => !disponibilidadesParaManter.has(disp.id)
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
for (const disp of disponibilidadesParaExcluir) {
|
}
|
||||||
await fetch(`${ENDPOINT}?id=eq.${disp.id}`, {
|
}
|
||||||
method: "DELETE",
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alert("Horários atualizados com sucesso!");
|
const results = await Promise.all(promises);
|
||||||
setEditando(null);
|
|
||||||
|
|
||||||
const res = await fetch(ENDPOINT, {
|
results.forEach(res => {
|
||||||
method: "GET",
|
if (res.type === 'POST' && res.id) currentIds.add(res.id);
|
||||||
headers: getHeaders()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const existingRes = await fetch(`${ENDPOINT}?doctor_id=eq.${String(doctorId)}`, {
|
||||||
|
method: "GET", headers
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingRes.ok) {
|
||||||
|
const existingData = await existingRes.json();
|
||||||
|
const deletePromises = existingData
|
||||||
|
.filter(dbItem => !currentIds.has(dbItem.id))
|
||||||
|
.map(dbItem =>
|
||||||
|
fetch(`${ENDPOINT}?id=eq.${dbItem.id}`, { method: "DELETE", headers })
|
||||||
|
);
|
||||||
|
await Promise.all(deletePromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditando(null);
|
||||||
|
setAvailabilityEdit([]);
|
||||||
|
const res = await fetch(ENDPOINT, { method: "GET", headers: getHeaders() });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setDisponibilidades(Array.isArray(data) ? data : []);
|
setDisponibilidades(Array.isArray(data) ? data : []);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao salvar disponibilidades:", error);
|
console.error(error);
|
||||||
alert("Erro ao salvar os horários");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deletarDisponibilidade = async (id) => {
|
const deletarDisponibilidade = async (id) => {
|
||||||
if (!window.confirm("Deseja realmente excluir esta disponibilidade?")) return;
|
if (!window.confirm("Deseja realmente excluir esta disponibilidade?")) return;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${ENDPOINT}?id=eq.${id}`, {
|
const res = await fetch(`${ENDPOINT}?id=eq.${id}`, { method: "DELETE", headers: getHeaders() });
|
||||||
method: "DELETE",
|
if (res.ok) setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
|
||||||
headers: getHeaders(),
|
} catch (error) {}
|
||||||
});
|
|
||||||
if (res.ok) {
|
|
||||||
alert("Disponibilidade excluída com sucesso!");
|
|
||||||
setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erro:", error);
|
|
||||||
alert("Erro ao conectar com o servidor");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const disponibilidadesAgrupadas = useMemo(() => {
|
const disponibilidadesAgrupadas = useMemo(() => {
|
||||||
const agrupadas = {};
|
const agrupadas = {};
|
||||||
|
|
||||||
doctors.forEach((doctor) => {
|
doctors.forEach((doctor) => {
|
||||||
agrupadas[doctor.id] = {
|
agrupadas[doctor.id] = {
|
||||||
doctor_id: doctor.id,
|
doctor_id: doctor.id,
|
||||||
@ -186,13 +183,9 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
disponibilidades: [],
|
disponibilidades: [],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
disponibilidades.forEach((disp) => {
|
disponibilidades.forEach((disp) => {
|
||||||
if (agrupadas[disp.doctor_id]) {
|
if (agrupadas[disp.doctor_id]) agrupadas[disp.doctor_id].disponibilidades.push(disp);
|
||||||
agrupadas[disp.doctor_id].disponibilidades.push(disp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.values(agrupadas).forEach((grupo) => {
|
Object.values(agrupadas).forEach((grupo) => {
|
||||||
if (grupo.disponibilidades.length === 0) {
|
if (grupo.disponibilidades.length === 0) {
|
||||||
grupo.disponibilidades.push({
|
grupo.disponibilidades.push({
|
||||||
@ -203,15 +196,8 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let resultado = Object.values(agrupadas);
|
let resultado = Object.values(agrupadas);
|
||||||
|
if (searchTerm) resultado = resultado.filter((grupo) => grupo.doctor_name.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||||
if (searchTerm) {
|
|
||||||
resultado = resultado.filter(grupo =>
|
|
||||||
grupo.doctor_name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultado;
|
return resultado;
|
||||||
}, [disponibilidades, doctors, searchTerm]);
|
}, [disponibilidades, doctors, searchTerm]);
|
||||||
|
|
||||||
@ -237,60 +223,66 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
friday: "Sexta",
|
friday: "Sexta",
|
||||||
saturday: "Sábado",
|
saturday: "Sábado",
|
||||||
};
|
};
|
||||||
|
|
||||||
const key = typeof weekday === "string" ? weekday.toLowerCase() : weekday;
|
const key = typeof weekday === "string" ? weekday.toLowerCase() : weekday;
|
||||||
return dias[key] || "Desconhecido";
|
return dias[key] || "Desconhecido";
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialAvailabilityParaEdicao = useMemo(() => {
|
const initialAvailabilityParaEdicao = useMemo(() => {
|
||||||
if (!editando) return [];
|
if (!editando) return [];
|
||||||
|
const disponibilidadesMedico = disponibilidades.filter((d) => String(d.doctor_id) === String(editando));
|
||||||
const disponibilidadesMedico = disponibilidades.filter(
|
const blocosPorDia = {};
|
||||||
(d) => String(d.doctor_id) === String(editando)
|
disponibilidadesMedico.forEach((d) => {
|
||||||
);
|
const num = typeof d.weekday === "string" ? weekdayStrToNum[d.weekday.toLowerCase()] : d.weekday;
|
||||||
|
if (num === undefined) return;
|
||||||
return [1, 2, 3, 4, 5, 6, 0].map((weekday) => {
|
if (!blocosPorDia[num]) blocosPorDia[num] = [];
|
||||||
const blocosDoDia = disponibilidadesMedico
|
if (d.active !== false) {
|
||||||
.filter((d) => d.weekday === weekday && d.active !== false)
|
blocosPorDia[num].push({
|
||||||
.map((d) => ({
|
|
||||||
id: d.id,
|
id: d.id,
|
||||||
inicio: formatTime(d.start_time) || "07:00",
|
inicio: formatTime(d.start_time) || "07:00",
|
||||||
termino: formatTime(d.end_time) || "17:00",
|
termino: formatTime(d.end_time) || "17:00",
|
||||||
slot_minutes: d.slot_minutes || 30,
|
slot_minutes: d.slot_minutes || 30,
|
||||||
appointment_type: d.appointment_type || "presencial",
|
appointment_type: d.appointment_type || "presencial",
|
||||||
isNew: false,
|
isNew: false,
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const resultado = [1, 2, 3, 4, 5, 6, 0].map((weekday) => {
|
||||||
|
const blocosDoDia = blocosPorDia[weekday] || [];
|
||||||
return {
|
return {
|
||||||
dia: diasDaSemana[weekday],
|
dia: diasDaSemana[weekday],
|
||||||
weekday: weekday,
|
weekday: weekday,
|
||||||
isChecked: blocosDoDia.length > 0,
|
isChecked: blocosDoDia.length > 0,
|
||||||
blocos: blocosDoDia.length > 0 ? blocosDoDia : [{
|
blocos:
|
||||||
|
blocosDoDia.length > 0
|
||||||
|
? blocosDoDia
|
||||||
|
: [
|
||||||
|
{
|
||||||
id: null,
|
id: null,
|
||||||
inicio: "07:00",
|
inicio: "07:00",
|
||||||
termino: "17:00",
|
termino: "17:00",
|
||||||
slot_minutes: 30,
|
slot_minutes: 30,
|
||||||
appointment_type: "presencial",
|
appointment_type: "presencial",
|
||||||
isNew: true,
|
isNew: true,
|
||||||
}],
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
return resultado;
|
||||||
}, [disponibilidades, editando]);
|
}, [disponibilidades, editando]);
|
||||||
|
|
||||||
const handleUpdateHorarios = (horariosAtualizados) => {
|
const handleUpdateHorarios = (horariosAtualizados) => {
|
||||||
if (!editando) return;
|
if (!editando) return;
|
||||||
salvarTodasDisponibilidades(editando, horariosAtualizados);
|
setAvailabilityEdit(horariosAtualizados || []);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredDoctors = useMemo(() => {
|
const filteredDoctors = useMemo(() => {
|
||||||
if (!searchTerm) return doctors;
|
if (!searchTerm) return doctors;
|
||||||
return doctors.filter((doc) =>
|
return doctors.filter((doc) => (doc.full_name || doc.name).toLowerCase().includes(searchTerm.toLowerCase()));
|
||||||
(doc.full_name || doc.name).toLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
);
|
|
||||||
}, [doctors, searchTerm]);
|
}, [doctors, searchTerm]);
|
||||||
|
|
||||||
const handleCancelarEdicao = () => {
|
const handleCancelarEdicao = () => {
|
||||||
setEditando(null);
|
setEditando(null);
|
||||||
|
setAvailabilityEdit([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDoctorSelect = (doctor) => {
|
const handleDoctorSelect = (doctor) => {
|
||||||
@ -317,9 +309,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="disponibilidades-container">
|
<div className="disponibilidades-container">
|
||||||
<h1 className="disponibilidades-title">
|
<h1 className="disponibilidades-title">Disponibilidades dos Médicos</h1>
|
||||||
Disponibilidades dos Médicos
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div className="search-container">
|
<div className="search-container">
|
||||||
<div className="search-input-container">
|
<div className="search-input-container">
|
||||||
@ -335,10 +325,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
className="search-input"
|
className="search-input"
|
||||||
/>
|
/>
|
||||||
{searchTerm && (
|
{searchTerm && (
|
||||||
<button
|
<button onClick={handleClearSearch} className="clear-search-btn">
|
||||||
onClick={handleClearSearch}
|
|
||||||
className="clear-search-btn"
|
|
||||||
>
|
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@ -347,11 +334,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
{showSuggestions && searchTerm && filteredDoctors.length > 0 && (
|
{showSuggestions && searchTerm && filteredDoctors.length > 0 && (
|
||||||
<div className="suggestions-dropdown">
|
<div className="suggestions-dropdown">
|
||||||
{filteredDoctors.map((doc) => (
|
{filteredDoctors.map((doc) => (
|
||||||
<div
|
<div key={doc.id} onClick={() => handleDoctorSelect(doc)} className="suggestion-item">
|
||||||
key={doc.id}
|
|
||||||
onClick={() => handleDoctorSelect(doc)}
|
|
||||||
className="suggestion-item"
|
|
||||||
>
|
|
||||||
{doc.full_name || doc.name}
|
{doc.full_name || doc.name}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -361,9 +344,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
|
|
||||||
<section className="calendario-ou-filaespera">
|
<section className="calendario-ou-filaespera">
|
||||||
<div className="fila-container">
|
<div className="fila-container">
|
||||||
<h2 className="section-title">
|
<h2 className="section-title">{editando ? `Editar Horários` : "Lista de Disponibilidades"}</h2>
|
||||||
{editando ? `Editar Horários` : "Lista de Disponibilidades"}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{doctors.length === 0 ? (
|
{doctors.length === 0 ? (
|
||||||
<p className="loading-text">Carregando médicos...</p>
|
<p className="loading-text">Carregando médicos...</p>
|
||||||
@ -371,13 +352,7 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
<>
|
<>
|
||||||
<div className="edit-container">
|
<div className="edit-container">
|
||||||
{initialAvailabilityParaEdicao.length > 0 ? (
|
{initialAvailabilityParaEdicao.length > 0 ? (
|
||||||
<HorariosDisponibilidade
|
<HorariosDisponibilidade initialAvailability={initialAvailabilityParaEdicao} onUpdate={handleUpdateHorarios} onCancel={handleCancelarEdicao} />
|
||||||
initialAvailability={initialAvailabilityParaEdicao}
|
|
||||||
onUpdate={handleUpdateHorarios}
|
|
||||||
readOnly={false}
|
|
||||||
showHeader={false}
|
|
||||||
compact={false}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<p className="loading-text">Carregando horários para edição...</p>
|
<p className="loading-text">Carregando horários para edição...</p>
|
||||||
)}
|
)}
|
||||||
@ -385,16 +360,15 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
|
|
||||||
<div className="disp-buttons-container">
|
<div className="disp-buttons-container">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleUpdateHorarios(initialAvailabilityParaEdicao)}
|
onClick={() =>
|
||||||
|
salvarTodasDisponibilidades(editando, availabilityEdit.length > 0 ? availabilityEdit : initialAvailabilityParaEdicao)
|
||||||
|
}
|
||||||
className="disp-btn-primary"
|
className="disp-btn-primary"
|
||||||
>
|
>
|
||||||
Salvar Alterações
|
Salvar Alterações
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button onClick={handleCancelarEdicao} className="disp-btn-danger">
|
||||||
onClick={handleCancelarEdicao}
|
|
||||||
className="disp-btn-danger"
|
|
||||||
>
|
|
||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -405,33 +379,20 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
<p className="no-results">Nenhum médico encontrado</p>
|
<p className="no-results">Nenhum médico encontrado</p>
|
||||||
) : (
|
) : (
|
||||||
disponibilidadesAgrupadas.map((grupo) => (
|
disponibilidadesAgrupadas.map((grupo) => (
|
||||||
<div
|
<div key={grupo.doctor_id} className={`doctor-group ${expandedDoctors[grupo.doctor_id] ? "expanded" : ""}`}>
|
||||||
key={grupo.doctor_id}
|
<div className="doctor-header" onClick={() => toggleExpandDoctor(grupo.doctor_id)}>
|
||||||
className={`doctor-group ${expandedDoctors[grupo.doctor_id] ? 'expanded' : ''}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="doctor-header"
|
|
||||||
onClick={() => toggleExpandDoctor(grupo.doctor_id)}
|
|
||||||
>
|
|
||||||
<h3 className="doctor-name">
|
<h3 className="doctor-name">
|
||||||
{grupo.doctor_name}
|
{grupo.doctor_name}
|
||||||
<span className="doctor-hours">
|
<span className="doctor-hours">({grupo.disponibilidades.filter((d) => !d.is_empty).length} horários)</span>
|
||||||
({grupo.disponibilidades.filter(d => !d.is_empty).length} horários)
|
|
||||||
</span>
|
|
||||||
</h3>
|
</h3>
|
||||||
<span className={`expand-icon ${expandedDoctors[grupo.doctor_id] ? 'expanded' : ''}`}>
|
<span className={`expand-icon ${expandedDoctors[grupo.doctor_id] ? "expanded" : ""}`}>▼</span>
|
||||||
▼
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{expandedDoctors[grupo.doctor_id] && (
|
{expandedDoctors[grupo.doctor_id] && (
|
||||||
<div className="doctor-content">
|
<div className="doctor-content">
|
||||||
<div className="edit-btn-container">
|
<div className="edit-btn-container">
|
||||||
<button
|
<button onClick={() => setEditando(grupo.doctor_id)} className="disp-btn-edit">
|
||||||
onClick={() => setEditando(grupo.doctor_id)}
|
{grupo.disponibilidades.some((d) => !d.is_empty) ? "Editar" : "Cadastrar Horários"}
|
||||||
className="disp-btn-edit"
|
|
||||||
>
|
|
||||||
{grupo.disponibilidades.some(d => !d.is_empty) ? "Editar" : "Cadastrar Horários"}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -451,36 +412,15 @@ const DisponibilidadesDoctorPage = ( ) => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{grupo.disponibilidades.map((disp) => (
|
{grupo.disponibilidades.map((disp) => (
|
||||||
<tr key={disp.id}>
|
<tr key={disp.id}>
|
||||||
|
<td>{disp.is_empty ? "Nenhum horário cadastrado" : getDiaSemana(disp.weekday)}</td>
|
||||||
|
<td>{disp.is_empty ? "-" : formatTime(disp.start_time)}</td>
|
||||||
|
<td>{disp.is_empty ? "-" : formatTime(disp.end_time)}</td>
|
||||||
|
<td>{disp.is_empty ? "-" : disp.slot_minutes || 30}</td>
|
||||||
|
<td>{disp.is_empty ? "-" : disp.appointment_type || "presencial"}</td>
|
||||||
<td>
|
<td>
|
||||||
{disp.is_empty ? "Nenhum horário cadastrado" : getDiaSemana(disp.weekday)}
|
<span className={getStatusBadgeClass(disp)}>{getStatusText(disp)}</span>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{disp.is_empty ? "-" : formatTime(disp.start_time)}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{disp.is_empty ? "-" : formatTime(disp.end_time)}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{disp.is_empty ? "-" : disp.slot_minutes || 30}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{disp.is_empty ? "-" : disp.appointment_type || "presencial"}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span className={getStatusBadgeClass(disp)}>
|
|
||||||
{getStatusText(disp)}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{!disp.is_empty && (
|
|
||||||
<button
|
|
||||||
onClick={() => deletarDisponibilidade(disp.id)}
|
|
||||||
className="disp-btn-delete"
|
|
||||||
>
|
|
||||||
Excluir
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</td>
|
</td>
|
||||||
|
<td>{!disp.is_empty && <button onClick={() => deletarDisponibilidade(disp.id)} className="disp-btn-delete">Excluir</button>}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -1,104 +1,333 @@
|
|||||||
import React, { useEffect, useState, useCallback } from "react";
|
import React, { useState, useEffect, useMemo } from "react";
|
||||||
import { useParams, useSearchParams } from "react-router-dom";
|
import { useParams, useNavigate, useLocation } from "react-router-dom";
|
||||||
import { GetDoctorByID } from "../components/utils/Functions-Endpoints/Doctor";
|
|
||||||
import DoctorForm from "../components/doctors/DoctorForm";
|
import DoctorForm from "../components/doctors/DoctorForm";
|
||||||
import { useAuth } from "../components/utils/AuthProvider";
|
import { useAuth } from "../components/utils/AuthProvider";
|
||||||
import API_KEY from "../components/utils/apiKeys";
|
import API_KEY from "../components/utils/apiKeys";
|
||||||
|
|
||||||
const ENDPOINT_AVAILABILITY =
|
const ENDPOINT = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors";
|
||||||
"https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
|
const ENDPOINT_AVAILABILITY = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_availability";
|
||||||
|
|
||||||
const DoctorEditPage = ({DictInfo}) => {
|
const diasDaSemana = ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"];
|
||||||
|
const weekdayNumToStr = {
|
||||||
|
0: "sunday",
|
||||||
|
1: "monday",
|
||||||
|
2: "tuesday",
|
||||||
|
3: "wednesday",
|
||||||
|
4: "thursday",
|
||||||
|
5: "friday",
|
||||||
|
6: "saturday",
|
||||||
|
};
|
||||||
|
const weekdayStrToNum = Object.fromEntries(
|
||||||
|
Object.entries(weekdayNumToStr).map(([num, str]) => [str, Number(num)])
|
||||||
|
);
|
||||||
|
|
||||||
|
const EditDoctorPage = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
const { getAuthorizationHeader } = useAuth();
|
const { getAuthorizationHeader } = useAuth();
|
||||||
const [DoctorToPUT, setDoctorPUT] = useState({});
|
|
||||||
|
|
||||||
const Parametros = useParams();
|
const [doctor, setDoctor] = useState(null);
|
||||||
const [searchParams] = useSearchParams();
|
const [availability, setAvailability] = useState([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
const DoctorID = "b24c88b2-1d51-4c04-8fe8-e75c3f2817d1";
|
const effectiveId = id;
|
||||||
const availabilityId = searchParams.get("availabilityId");
|
|
||||||
|
|
||||||
const [availabilityToPATCH, setAvailabilityToPATCH] = useState(null);
|
const getHeaders = () => {
|
||||||
const [mode, setMode] = useState("doctor");
|
const myHeaders = new Headers();
|
||||||
console.log("teste", DictInfo)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setDoctorPUT(DictInfo)
|
|
||||||
}, [DictInfo]);
|
|
||||||
|
|
||||||
const HandlePutDoctor = async () => {
|
|
||||||
const authHeader = getAuthorizationHeader();
|
const authHeader = getAuthorizationHeader();
|
||||||
|
if (authHeader) myHeaders.append("Authorization", authHeader);
|
||||||
var myHeaders = new Headers();
|
|
||||||
myHeaders.append("apikey", API_KEY);
|
|
||||||
myHeaders.append("Authorization", authHeader);
|
|
||||||
myHeaders.append("Content-Type", "application/json");
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
if (API_KEY) myHeaders.append("apikey", API_KEY);
|
||||||
var raw = JSON.stringify(DoctorToPUT);
|
myHeaders.append("Prefer", "return=representation");
|
||||||
|
return myHeaders;
|
||||||
console.log("Enviando médico para atualização (PUT):", DoctorToPUT);
|
|
||||||
|
|
||||||
var requestOptions = {
|
|
||||||
method: "PUT",
|
|
||||||
headers: myHeaders,
|
|
||||||
body: raw,
|
|
||||||
redirect: "follow",
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?id=eq.${DictInfo.id}`,requestOptions)
|
|
||||||
.then(response => console.log(response))
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const salvarDisponibilidades = async (doctorId, horariosAtualizados) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const headers = getHeaders();
|
||||||
`${ENDPOINT_AVAILABILITY}?id=eq.${availabilityId}`,
|
const promises = [];
|
||||||
requestOptions
|
const currentIds = new Set();
|
||||||
|
|
||||||
|
for (const dia of horariosAtualizados) {
|
||||||
|
if (dia.isChecked && dia.blocos.length > 0) {
|
||||||
|
for (const bloco of dia.blocos) {
|
||||||
|
const inicio = bloco.inicio.includes(":") ? bloco.inicio : bloco.inicio + ":00";
|
||||||
|
const termino = bloco.termino.includes(":") ? bloco.termino : bloco.termino + ":00";
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
doctor_id: doctorId,
|
||||||
|
weekday: weekdayNumToStr[dia.weekday],
|
||||||
|
start_time: inicio,
|
||||||
|
end_time: termino,
|
||||||
|
slot_minutes: bloco.slot_minutes || 30,
|
||||||
|
appointment_type: bloco.appointment_type || "presencial",
|
||||||
|
active: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bloco.id && !bloco.isNew) {
|
||||||
|
currentIds.add(bloco.id);
|
||||||
|
promises.push(
|
||||||
|
fetch(`${ENDPOINT_AVAILABILITY}?id=eq.${bloco.id}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
}).then((res) => {
|
||||||
|
if (!res.ok) throw new Error(`Erro no PATCH: ${res.status}`);
|
||||||
|
return { type: "PATCH", id: bloco.id };
|
||||||
|
})
|
||||||
);
|
);
|
||||||
console.log("Resposta PATCH Disponibilidade:", response);
|
} else {
|
||||||
alert("Disponibilidade atualizada com sucesso!");
|
promises.push(
|
||||||
// Opcional: Redirecionar de volta para a lista de disponibilidades
|
fetch(ENDPOINT_AVAILABILITY, {
|
||||||
// navigate('/disponibilidades');
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
const createdItem = Array.isArray(data) ? data[0] : data;
|
||||||
|
if (createdItem && createdItem.id) {
|
||||||
|
return { type: "POST", id: createdItem.id };
|
||||||
|
}
|
||||||
|
return { type: "POST", id: null };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
|
||||||
|
results.forEach((res) => {
|
||||||
|
if (res.type === "POST" && res.id) currentIds.add(res.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingDisponibilidadesRes = await fetch(
|
||||||
|
`${ENDPOINT_AVAILABILITY}?doctor_id=eq.${String(doctorId)}`,
|
||||||
|
{ method: "GET", headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingDisponibilidadesRes.ok) {
|
||||||
|
const existingDisponibilidades = await existingDisponibilidadesRes.json();
|
||||||
|
|
||||||
|
const deletePromises = existingDisponibilidades
|
||||||
|
.filter((disp) => !currentIds.has(disp.id))
|
||||||
|
.map((disp) =>
|
||||||
|
fetch(`${ENDPOINT_AVAILABILITY}?id=eq.${disp.id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(deletePromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedResponse = await fetch(
|
||||||
|
`${ENDPOINT_AVAILABILITY}?doctor_id=eq.${doctorId}&order=weekday.asc,start_time.asc`,
|
||||||
|
{ method: "GET", headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (updatedResponse.ok) {
|
||||||
|
const updatedData = await updatedResponse.json();
|
||||||
|
setAvailability(updatedData);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao atualizar disponibilidade:", error);
|
|
||||||
alert("Erro ao atualizar disponibilidade.");
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeAvailabilityForForm = (availabilityData) => {
|
||||||
|
if (!Array.isArray(availabilityData)) return [];
|
||||||
|
|
||||||
|
const disponibilidadesMedico = availabilityData.filter((d) =>
|
||||||
|
String(d.doctor_id) === String(effectiveId) && d.active !== false
|
||||||
|
);
|
||||||
|
const blocosPorDia = {};
|
||||||
|
|
||||||
|
disponibilidadesMedico.forEach((d) => {
|
||||||
|
const num = typeof d.weekday === "string" ? weekdayStrToNum[d.weekday.toLowerCase()] : d.weekday;
|
||||||
|
if (num === undefined || num === null) return;
|
||||||
|
if (!blocosPorDia[num]) blocosPorDia[num] = [];
|
||||||
|
blocosPorDia[num].push({
|
||||||
|
id: d.id,
|
||||||
|
inicio: d.start_time?.substring(0, 5) || "07:00",
|
||||||
|
termino: d.end_time?.substring(0, 5) || "17:00",
|
||||||
|
slot_minutes: d.slot_minutes || 30,
|
||||||
|
appointment_type: d.appointment_type || "presencial",
|
||||||
|
isNew: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultado = [1, 2, 3, 4, 5, 6, 0].map((weekday) => {
|
||||||
|
const blocosDoDia = blocosPorDia[weekday] || [];
|
||||||
|
return {
|
||||||
|
dia: diasDaSemana[weekday],
|
||||||
|
weekday: weekday,
|
||||||
|
isChecked: blocosDoDia.length > 0,
|
||||||
|
blocos:
|
||||||
|
blocosDoDia.length > 0
|
||||||
|
? blocosDoDia
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: null,
|
||||||
|
inicio: "07:00",
|
||||||
|
termino: "17:00",
|
||||||
|
slot_minutes: 30,
|
||||||
|
appointment_type: "presencial",
|
||||||
|
isNew: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return resultado;
|
||||||
|
};
|
||||||
|
|
||||||
|
const availabilityFormatted = useMemo(() => {
|
||||||
|
return normalizeAvailabilityForForm(availability);
|
||||||
|
}, [availability, effectiveId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchDoctorData = async () => {
|
||||||
|
if (!effectiveId || effectiveId === "edit") {
|
||||||
|
alert("ID do médico não encontrado");
|
||||||
|
navigate("/secretaria/medicos");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const doctorResponse = await fetch(`${ENDPOINT}?id=eq.${effectiveId}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: getHeaders(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!doctorResponse.ok) {
|
||||||
|
throw new Error("Erro ao carregar dados do médico");
|
||||||
|
}
|
||||||
|
|
||||||
|
const doctorData = await doctorResponse.json();
|
||||||
|
if (doctorData.length === 0) {
|
||||||
|
throw new Error("Médico não encontrado");
|
||||||
|
}
|
||||||
|
|
||||||
|
setDoctor(doctorData[0]);
|
||||||
|
|
||||||
|
const availabilityResponse = await fetch(
|
||||||
|
`${ENDPOINT_AVAILABILITY}?doctor_id=eq.${effectiveId}&order=weekday.asc,start_time.asc`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: getHeaders(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (availabilityResponse.ok) {
|
||||||
|
const availabilityData = await availabilityResponse.json();
|
||||||
|
setAvailability(availabilityData);
|
||||||
|
} else {
|
||||||
|
setAvailability([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
alert("Erro ao carregar dados do médico");
|
||||||
|
navigate("/secretaria/medicos");
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (effectiveId) {
|
||||||
|
fetchDoctorData();
|
||||||
|
}
|
||||||
|
}, [effectiveId, navigate]);
|
||||||
|
|
||||||
|
const handleSave = async (formData) => {
|
||||||
|
const { availability: updatedAvailability, ...doctorDataToSave } = formData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsSaving(true);
|
||||||
|
|
||||||
|
const response = await fetch(`${ENDPOINT}?id=eq.${effectiveId}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: getHeaders(),
|
||||||
|
body: JSON.stringify(doctorDataToSave),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Erro ao salvar dados do médico");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedAvailability && updatedAvailability.length > 0) {
|
||||||
|
await salvarDisponibilidades(effectiveId, updatedAvailability);
|
||||||
|
}
|
||||||
|
|
||||||
|
alert("Médico e horários atualizados com sucesso!");
|
||||||
|
navigate("/secretaria/medicos");
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
alert(`Erro ao salvar dados: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
|
console.log('Horários a serem salvos:', updatedAvailability);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
navigate("/secretaria/medicos");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="container mt-4">
|
||||||
|
<div className="d-flex justify-content-center">
|
||||||
|
<div className="spinner-border" role="status">
|
||||||
|
<span className="visually-hidden">Carregando...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-center mt-2">
|
||||||
|
Carregando dados do médico ID: {effectiveId || "..."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doctor) {
|
||||||
|
if (!isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="container mt-4">
|
||||||
|
<div className="alert alert-danger">
|
||||||
|
Médico não encontrado
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = {
|
||||||
|
...doctor,
|
||||||
|
availability: (doctor && doctor.availability) ? doctor.availability : availabilityFormatted,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mt-4">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<h1>Editar Médico</h1>
|
||||||
<DoctorForm
|
<DoctorForm
|
||||||
onSave={
|
formData={formData}
|
||||||
mode === "availability" ? HandlePatchAvailability : HandlePutDoctor
|
setFormData={setDoctor}
|
||||||
}
|
onSave={handleSave}
|
||||||
formData={mode === "availability" ? availabilityToPATCH : DoctorToPUT}
|
onCancel={handleCancel}
|
||||||
setFormData={
|
isLoading={isSaving}
|
||||||
mode === "availability" ? setAvailabilityToPATCH : setDoctorPUT
|
isEditing={true}
|
||||||
}
|
|
||||||
isEditingAvailability={mode === "availability"}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DoctorEditPage;
|
export default EditDoctorPage;
|
||||||
@ -147,7 +147,7 @@ function TableDoctor({setDictInfo}) {
|
|||||||
return resultado;
|
return resultado;
|
||||||
}) : [];
|
}) : [];
|
||||||
|
|
||||||
// Aplica ordenação rápida
|
|
||||||
const applySorting = (arr) => {
|
const applySorting = (arr) => {
|
||||||
if (!Array.isArray(arr) || !sortKey) return arr;
|
if (!Array.isArray(arr) || !sortKey) return arr;
|
||||||
const copy = [...arr];
|
const copy = [...arr];
|
||||||
@ -437,13 +437,13 @@ function TableDoctor({setDictInfo}) {
|
|||||||
<td>{medico.email || 'Não informado'}</td>
|
<td>{medico.email || 'Não informado'}</td>
|
||||||
<td>
|
<td>
|
||||||
<div className="d-flex gap-2">
|
<div className="d-flex gap-2">
|
||||||
<Link to={`details`}>
|
<Link to={`details/${medico.id}`}>
|
||||||
<button className="btn btn-sm btn-view" onClick={() => setDictInfo({...medico})}>
|
<button className="btn btn-sm btn-view" onClick={() => setDictInfo({...medico})}>
|
||||||
<i className="bi bi-eye me-1"></i> Ver Detalhes
|
<i className="bi bi-eye me-1"></i> Ver Detalhes
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to={`edit`}>
|
<Link to={`edit/${medico.id}`}>
|
||||||
<button className="btn btn-sm btn-edit" onClick={() => setDictInfo({...medico})}>
|
<button className="btn btn-sm btn-edit" onClick={() => setDictInfo({...medico})}>
|
||||||
<i className="bi bi-pencil me-1"></i> Editar
|
<i className="bi bi-pencil me-1"></i> Editar
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -223,3 +223,20 @@
|
|||||||
.btn-action.btn-delete:hover {
|
.btn-action.btn-delete:hover {
|
||||||
background-color: #C53030;
|
background-color: #C53030;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.unidade-selecionarprofissional { flex-direction: column; align-items: stretch; gap: 12px; }
|
||||||
|
.calendar-wrapper { flex-direction: column; padding: 16px; }
|
||||||
|
.calendar-info-panel { flex: 0 0 auto; border-right: none; border-bottom: 1px solid #E2E8F0; padding-right: 0; padding-bottom: 16px; }
|
||||||
|
.calendar-grid { grid-template-columns: repeat(4, 1fr); }
|
||||||
|
.calendar-controls { flex-direction: column; align-items: flex-start; gap: 8px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.calendar-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.date-indicator h2 { font-size: 1.25rem; }
|
||||||
|
.legend-item { font-size: 0.75rem; padding: 4px 8px; }
|
||||||
|
.appointment-item { flex-direction: column; align-items: stretch; gap: 8px; }
|
||||||
|
.appointment-actions { width: 100%; }
|
||||||
|
.btn-action { width: 100%; }
|
||||||
|
}
|
||||||
|
|||||||
@ -218,6 +218,30 @@
|
|||||||
background-color: #27ae60;
|
background-color: #27ae60;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.disponibilidades-container { padding: 12px; }
|
||||||
|
.disponibilidades-title { font-size: 1.25rem; }
|
||||||
|
.search-container { padding: 12px; }
|
||||||
|
.doctor-header { padding: 12px; }
|
||||||
|
.doctor-name { font-size: 1rem; }
|
||||||
|
.doctor-hours { display: none; }
|
||||||
|
.expand-icon { font-size: 0.9rem; }
|
||||||
|
.table-container { padding: 0 6px 6px 6px; }
|
||||||
|
.disponibilidades-table thead th { padding: 6px 8px; }
|
||||||
|
.disponibilidades-table td { padding: 10px 8px; font-size: 0.8125rem; }
|
||||||
|
.disponibilidades-table thead th:nth-child(4),
|
||||||
|
.disponibilidades-table thead th:nth-child(5),
|
||||||
|
.disponibilidades-table thead th:nth-child(7),
|
||||||
|
.disponibilidades-table tbody td:nth-child(4),
|
||||||
|
.disponibilidades-table tbody td:nth-child(5),
|
||||||
|
.disponibilidades-table tbody td:nth-child(7) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.disp-buttons-container { flex-direction: column; gap: 10px; }
|
||||||
|
.disp-btn-primary, .disp-btn-danger { width: 100%; }
|
||||||
|
.suggestions-dropdown { width: calc(100% - 30px); left: 15px; right: 15px; }
|
||||||
|
}
|
||||||
|
|
||||||
.status-inactive {
|
.status-inactive {
|
||||||
background-color: #e74c3c;
|
background-color: #e74c3c;
|
||||||
}
|
}
|
||||||
@ -304,7 +328,7 @@
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: #95a5a6;
|
background-color: #fa273c;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -312,7 +336,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.disp-btn-danger:hover {
|
.disp-btn-danger:hover {
|
||||||
background-color: #7f8c8d;
|
background-color: #f41936;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Section Titles */
|
/* Section Titles */
|
||||||
|
|||||||
@ -256,6 +256,15 @@ html, body {
|
|||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.unidade-selecionarprofissional { flex-direction: column; gap: 10px; }
|
||||||
|
.unidade-selecionarprofissional input,
|
||||||
|
.unidade-selecionarprofissional select { width: 100%; margin-left: 0; }
|
||||||
|
.busca-fila-espera { position: static; width: 100%; margin-bottom: 8px; }
|
||||||
|
.fila-header { height: auto; flex-direction: column; gap: 8px; }
|
||||||
|
.btns-e-legenda-container { flex-direction: column; gap: 10px; }
|
||||||
|
.legenda-tabela { justify-content: center; flex-wrap: wrap; }
|
||||||
|
}
|
||||||
.fila-header {
|
.fila-header {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -160,6 +160,17 @@
|
|||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.table-doctor-card .card-header { padding: 0.75rem 1rem; }
|
||||||
|
.table-doctor-table th, .table-doctor-table td { padding: 8px 6px; }
|
||||||
|
.table-doctor-table thead th:nth-child(2),
|
||||||
|
.table-doctor-table thead th:nth-child(4),
|
||||||
|
.table-doctor-table tbody td:nth-child(2),
|
||||||
|
.table-doctor-table tbody td:nth-child(4) { display: none; }
|
||||||
|
.filter-buttons-container { width: 100%; }
|
||||||
|
.filter-btn { width: 100%; }
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.table-doctor-table {
|
.table-doctor-table {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|||||||
@ -338,3 +338,13 @@
|
|||||||
padding: 0.4em 0.65em;
|
padding: 0.4em 0.65em;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.table-paciente-card .card-header { padding: 0.75rem 1rem; }
|
||||||
|
.table-paciente-table th, .table-paciente-table td { padding: 8px 6px; }
|
||||||
|
.table-paciente-table thead th:nth-child(2),
|
||||||
|
.table-paciente-table thead th:nth-child(4),
|
||||||
|
.table-paciente-table tbody td:nth-child(2),
|
||||||
|
.table-paciente-table tbody td:nth-child(4) { display: none; }
|
||||||
|
.table-paciente-filters .btn-sm { width: 100%; }
|
||||||
|
}
|
||||||
@ -34,8 +34,8 @@ function PerfilSecretaria({ onLogout }) {
|
|||||||
<Route path="medicos" element={<DoctorTable setDictInfo={setDictInfo} />} />
|
<Route path="medicos" element={<DoctorTable setDictInfo={setDictInfo} />} />
|
||||||
<Route path="pacientes/details" element={<Details DictInfo={DictInfo}/>} />
|
<Route path="pacientes/details" element={<Details DictInfo={DictInfo}/>} />
|
||||||
<Route path="pacientes/edit" element={<EditPage DictInfo={DictInfo}/>} />
|
<Route path="pacientes/edit" element={<EditPage DictInfo={DictInfo}/>} />
|
||||||
<Route path="medicos/details" element={<DoctorDetails doctor={DictInfo} />} />
|
<Route path="medicos/details/:id" element={<DoctorDetails doctor={DictInfo} />} />
|
||||||
<Route path="medicos/edit" element={<DoctorEditPage DictInfo={DictInfo} />} />
|
<Route path="medicos/edit/:id" element={<DoctorEditPage DictInfo={DictInfo} />} />
|
||||||
<Route path="agendamento" element={<Agendamento setDictInfo={setDictInfo}/>} />
|
<Route path="agendamento" element={<Agendamento setDictInfo={setDictInfo}/>} />
|
||||||
<Route path="agendamento/edit" element={<AgendamentoEditPage setDictInfo={setDictInfo} DictInfo={DictInfo}/>} />
|
<Route path="agendamento/edit" element={<AgendamentoEditPage setDictInfo={setDictInfo} DictInfo={DictInfo}/>} />
|
||||||
<Route path="laudo" element={<LaudoManager />} />
|
<Route path="laudo" element={<LaudoManager />} />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user