riseup-squad23/src/components/medico/FormExcecaoDisponibilidade.jsx
2025-12-04 09:56:19 -03:00

408 lines
18 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from "react";
import { useAuth } from "../../_assets/utils/AuthProvider";
import { GetAllDoctors, GetDoctorByName } from '../../_assets/utils/Functions-Endpoints/Doctor';
import API_KEY from "../../_assets/utils/apiKeys";
import "../../_assets/css/components/agendamento/FormAgendamento.css";
const ENDPOINT_CRIAR_EXCECAO = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctor_exceptions";
const FormCriarExcecao = ({ onCancel, doctorID }) => {
const { getAuthorizationHeader, user, getUserInfo } = useAuth();
const [dadosAtendimento, setDadosAtendimento] = useState({
profissional: doctorID || '',
crm: '',
tipoAtendimento: '',
dataAtendimento: '',
inicio: '',
termino: '',
motivo: ''
});
const [todosProfissionais, setTodosProfissionais] = useState([]);
const [profissionaisFiltrados, setProfissionaisFiltrados] = useState([]);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [doctorSearchName, setDoctorSearchName] = useState('');
const [searchingDoctor, setSearchingDoctor] = useState(false);
const handleAtendimentoChange = (e) => {
const { value, name } = e.target;
setDadosAtendimento(prev => ({
...prev,
[name]: value
}));
};
// Preencher automaticamente usando GetDoctorByName (se disponível) ou dados do usuário como fallback
useEffect(() => {
let cancelled = false;
const loggedDoctorId = doctorID || user?.doctor_id || user?.id || null;
const userName = user?.full_name || user?.name || user?.username || '';
const userCrm = user?.crm || user?.crm_number || user?.crmFormatted || '';
const tryFill = async () => {
// se não há nome para buscar, usa fallback local
if (!userName) {
setDadosAtendimento(prev => ({
...prev,
profissional: prev.profissional || loggedDoctorId || '',
crm: prev.crm || userCrm || ''
}));
setDoctorSearchName(prev => prev || '');
return;
}
try {
const authHeader = getAuthorizationHeader ? getAuthorizationHeader() : '';
const doctor = await GetDoctorByName(userName, authHeader);
if (cancelled) return;
if (doctor) {
const crmValue = doctor.crm || doctor.crm_number || doctor.crmFormatted || userCrm || '';
const idValue = doctor.id || loggedDoctorId || '';
setDadosAtendimento(prev => ({
...prev,
profissional: prev.profissional || idValue,
crm: prev.crm || crmValue
}));
setDoctorSearchName(prev => prev || (doctor.full_name || userName));
} else {
// fallback para dados do usuário quando busca não retorna resultado
setDadosAtendimento(prev => ({
...prev,
profissional: prev.profissional || loggedDoctorId || '',
crm: prev.crm || userCrm || ''
}));
setDoctorSearchName(prev => prev || userName);
}
} catch (err) {
console.warn('GetDoctorByName falhou, usando fallback do usuário:', err);
if (cancelled) return;
setDadosAtendimento(prev => ({
...prev,
profissional: prev.profissional || loggedDoctorId || '',
crm: prev.crm || userCrm || ''
}));
setDoctorSearchName(prev => prev || userName);
}
};
tryFill();
return () => { cancelled = true; };
}, [doctorID, user, getAuthorizationHeader]);
useEffect(() => {
const loadDoctors = async () => {
setSearchingDoctor(true);
let authHeader = '';
try { authHeader = getAuthorizationHeader ? getAuthorizationHeader() : ''; } catch {}
try {
const Medicos = await GetAllDoctors(authHeader);
const medicosArray = Array.isArray(Medicos) ? Medicos : [];
setTodosProfissionais(medicosArray);
// preencher automaticamente com médico logado (se encontrado na lista)
const loggedDoctorId = doctorID || user?.doctor_id || user?.id || null;
if (loggedDoctorId) {
const match = medicosArray.find(d =>
String(d.id) === String(loggedDoctorId) ||
String(d.doctor_id || '').toLowerCase() === String(loggedDoctorId).toLowerCase()
);
if (match) {
const crmValue = match.crm || match.crm_number || match.crmFormatted || '';
setDadosAtendimento(prev => ({
...prev,
profissional: prev.profissional || match.id,
crm: prev.crm || crmValue
}));
setDoctorSearchName(prev => prev || (match.full_name || ''));
} else {
// se não encontrou na lista mas user tem crm, preenche crm
const userCrm = user?.crm || user?.crm_number || '';
if (userCrm) {
setDadosAtendimento(prev => ({ ...prev, crm: prev.crm || userCrm }));
}
}
} else {
// se não há loggedDoctorId, mas user tem crm, preenche crm
const userCrm = user?.crm || user?.crm_number || '';
if (userCrm) {
setDadosAtendimento(prev => ({ ...prev, crm: prev.crm || userCrm }));
}
}
} catch (err) {
console.error('Erro ao carregar médicos:', err);
setTodosProfissionais([]);
} finally {
setSearchingDoctor(false);
}
};
loadDoctors();
}, [getAuthorizationHeader, doctorID, user]);
const handleSearchProfissional = (e) => {
const term = e.target.value;
setDoctorSearchName(term);
if (term.trim() === '') {
setProfissionaisFiltrados([]);
setIsDropdownOpen(false);
return;
}
const filtered = todosProfissionais.filter(p =>
((p.full_name || '').toLowerCase().includes(term.toLowerCase()) ||
(String(p.crm || p.crm_number || '').toLowerCase().includes(term.toLowerCase())))
);
setProfissionaisFiltrados(filtered);
setIsDropdownOpen(filtered.length > 0);
};
const handleSelectProfissional = (profissional) => {
const crmValue = profissional.crm || profissional.crm_number || profissional.crmFormatted || '';
setDadosAtendimento(prev => ({ ...prev, profissional: profissional.id, crm: crmValue }));
setDoctorSearchName(profissional.full_name || '');
setProfissionaisFiltrados([]);
setIsDropdownOpen(false);
};
// lista simples de valores permitidos
const ALLOWED_KINDS = ['disponibilidade_extra', 'bloqueio'];
const handleSubmitExcecao = async (e) => {
e.preventDefault();
console.log("Tentando criar Exceção.");
let { profissional, crm, dataAtendimento, tipoAtendimento, inicio, termino, motivo } = dadosAtendimento;
// agora a validação principal é pelo CRM (campo obrigatório)
if (!crm || !dataAtendimento || !tipoAtendimento || !motivo) {
alert("Por favor, preencha o CRM do médico, Data, Tipo e Motivo.");
return;
}
// se o ID do profissional não foi selecionado, tenta resolver pelo CRM usando lista carregada
if (!profissional) {
const match = todosProfissionais.find(d => {
const dcrm = String(d.crm || d.crm_number || d.crmFormatted || '').trim();
return dcrm && dcrm.toLowerCase() === String(crm).toLowerCase();
});
if (match) {
profissional = match.id;
} else {
alert("Médico com o CRM informado não encontrado. Selecione a partir da lista ou verifique o CRM.");
return;
}
} else {
// se ID estiver setado, opcional: conferir se CRM coincide com o cadastrado (se tiver)
const sel = todosProfissionais.find(d => d.id === profissional);
if (sel) {
const dcrm = String(sel.crm || sel.crm_number || sel.crmFormatted || '').trim();
if (dcrm && dcrm.toLowerCase() !== String(crm).toLowerCase()) {
// apenas alerta, não bloqueia — mantém ID para envio como pedido
if (!window.confirm("O CRM informado não corresponde ao CRM do médico selecionado. Deseja continuar?")) {
return;
}
}
}
}
// usa diretamente o value selecionado (já definido no <select>) e valida
const mappedKind = tipoAtendimento;
if (!ALLOWED_KINDS.includes(mappedKind)) {
alert(`Tipo inválido: "${tipoAtendimento}". Tipos aceitos: ${ALLOWED_KINDS.join(', ')}`);
return;
}
const startTime = inicio ? inicio + ":00" : null;
const endTime = termino ? termino + ":00" : null;
let authHeader = "";
try {
authHeader = getAuthorizationHeader ? getAuthorizationHeader() : "";
} catch (err) {
console.warn("Não foi possível obter Authorization header via useAuth()", err);
}
let createdBy = user?.id || null;
if (!createdBy && typeof getUserInfo === "function") {
try {
const info = await getUserInfo();
createdBy = info?.id || info?.profile?.id || null;
} catch (err) {
console.warn("getUserInfo falhou:", err);
}
}
if (!createdBy) {
try {
const stored = localStorage.getItem("user");
if (stored) {
const parsed = JSON.parse(stored);
createdBy = parsed?.id || parsed?.user?.id || null;
}
} catch {}
}
const raw = JSON.stringify({
doctor_id: profissional,
date: dataAtendimento,
kind: mappedKind,
start_time: startTime,
end_time: endTime,
reason: motivo,
created_by: createdBy
});
var myHeaders = new Headers();
if (authHeader) myHeaders.append("Authorization", authHeader);
myHeaders.append("Content-Type", "application/json");
if (API_KEY) myHeaders.append("apikey", API_KEY);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
try {
const response = await fetch(ENDPOINT_CRIAR_EXCECAO, requestOptions);
const resultText = await response.text();
let result;
try {
result = JSON.parse(resultText);
} catch {
result = { message: resultText || 'Sucesso, mas resposta não é JSON.' };
}
if (response.ok || response.status === 201) {
console.log("Exceção criada com sucesso:", result);
alert(`Exceção criada! Detalhes: ${result.id || JSON.stringify(result)}`);
onCancel(true);
} else {
console.error("Erro ao criar exceção:", result);
alert(`Erro ao criar exceção. Status: ${response.status}. Detalhes: ${result.message || JSON.stringify(result)}`);
}
} catch (error) {
console.error("Erro na requisição para criar exceção:", error);
alert("Erro de comunicação com o servidor.");
}
};
return (
<div className="form-container">
<form className="form-agendamento" onSubmit={handleSubmitExcecao}>
<h2 className="section-title">Informações da Nova Exceção</h2>
<div className="campo-informacoes-atendimento">
{/* Busca por nome usando filtragem local */}
<div className="campo-de-input campo-de-input-container">
<label>Nome do médico</label>
<input
type="text"
name="doctorSearchName"
placeholder="Digite o nome do médico"
value={doctorSearchName}
onChange={handleSearchProfissional}
autoComplete="off"
/>
{isDropdownOpen && profissionaisFiltrados.length > 0 && (
<div className="dropdown-profissionais">
{profissionaisFiltrados.map(p => (
<div key={p.id} className="dropdown-item" onClick={() => handleSelectProfissional(p)}>
{p.full_name}
</div>
))}
</div>
)}
{searchingDoctor && <small>Carregando médicos...</small>}
</div>
{/* CRM do profissional (agora campo obrigatório de validação) */}
<div className="campo-de-input">
<label>CRM do médico *</label>
<input
type="text"
name="crm"
required
placeholder="Ex: 12345-SP"
value={dadosAtendimento.crm}
onChange={handleAtendimentoChange}
/>
</div>
<div className="campo-de-input">
<label>Tipo de exceção *</label>
<select name="tipoAtendimento" onChange={handleAtendimentoChange} value={dadosAtendimento.tipoAtendimento} required>
<option value="" disabled>Selecione o tipo de exceção</option>
<option value="disponibilidade_extra" >Liberação</option>
<option value="bloqueio" >Bloqueio</option>
</select>
</div>
</div>
<section id="informacoes-atendimento-segunda-linha">
<section id="informacoes-atendimento-segunda-linha-esquerda">
<div className="campo-informacoes-atendimento">
<div className="campo-de-input">
<label>Data *</label>
<input
type="date"
name="dataAtendimento"
required
value={dadosAtendimento.dataAtendimento}
onChange={handleAtendimentoChange}
/>
</div>
</div>
<div className="campo-informacoes-atendimento">
<div className="campo-de-input">
<label>Início (Opcional)</label>
<input
type="time"
name="inicio"
value={dadosAtendimento.inicio}
onChange={handleAtendimentoChange}
/>
</div>
<div className="campo-de-input">
<label>Término (Opcional)</label>
<input
type="time"
name="termino"  
value={dadosAtendimento.termino}
onChange={handleAtendimentoChange}
/>
</div>
</div>
</section>
<section className="informacoes-atendimento-segunda-linha-direita">
<div className="campo-de-input">
<label>Motivo da exceção *</label>
<textarea
name="motivo"
rows="4"
cols="1"
required
value={dadosAtendimento.motivo}
onChange={handleAtendimentoChange}
></textarea>
</div>
</section>
</section>
<div className="form-actions">
<button type="submit" className="btn-primary">Criar Exceção</button>
<button type="button" className="btn-cancel" onClick={() => onCancel(false)}>Cancelar</button>
</div>
</form>
</div>
);
};
export default FormCriarExcecao;