diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index e1cc85a..b211975 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,138 +1,271 @@ import React, { useState } from "react"; -import { Link } from "react-router-dom"; -import menuItems from "../data/sidebar-items-medico.json"; // Use "sidebar-items-secretaria.json" para secretaria e "sidebar-items-adm.json" para ADM -import TrocardePerfis from "./TrocardePerfis"; +import { Link, useNavigate } from "react-router-dom"; +import menuItems from "../data/sidebar-items-medico.json"; +import TrocardePerfis from "./TrocardePerfis"; +function Sidebar({ menuItems }) { + const [isActive, setIsActive] = useState(true); + const [openSubmenu, setOpenSubmenu] = useState(null); + const [showLogoutModal, setShowLogoutModal] = useState(false); + const navigate = useNavigate(); -// 1. Recebe 'menuItems' e 'onLogout' como props -function Sidebar({ menuItems, onLogout }) { - const [isActive, setIsActive] = useState(true); - const [openSubmenu, setOpenSubmenu] = useState(null); + const toggleSidebar = () => { + setIsActive(!isActive); + }; - const toggleSidebar = () => { - setIsActive(!isActive); - }; + const handleSubmenuClick = (submenuName) => { + setOpenSubmenu(openSubmenu === submenuName ? null : submenuName); + }; - const handleSubmenuClick = (submenuName) => { - setOpenSubmenu(openSubmenu === submenuName ? null : submenuName); - }; + const handleLogoutClick = () => { + setShowLogoutModal(true); + }; - const renderLink = (item) => { - // Links internos (rotas do React Router) - if (item.url && item.url.startsWith("/")) { - return ( - - {item.icon && } - {item.name} - - ); - } + const handleLogoutConfirm = async () => { + try { + const token = localStorage.getItem('token') || + localStorage.getItem('authToken') || + localStorage.getItem('userToken') || + localStorage.getItem('access_token') || + sessionStorage.getItem('token') || + sessionStorage.getItem('authToken'); - // Links externos - return ( - - {item.icon && } - {item.name} - - ); - }; + if (token) { + const response = await fetch('https://mock.apidog.com/m1/1053378-0-default/auth/v1/logout', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }); - return ( -
+ > + ); } export default Sidebar; \ No newline at end of file diff --git a/src/components/doctors/DoctorForm.jsx b/src/components/doctors/DoctorForm.jsx index 8b4ceb8..45ec0bf 100644 --- a/src/components/doctors/DoctorForm.jsx +++ b/src/components/doctors/DoctorForm.jsx @@ -1,11 +1,10 @@ -import React, { useState } from 'react'; -import { Link,useNavigate, useLocation } from 'react-router-dom'; +import React, { useState, useRef } from 'react'; +import { Link, useNavigate, useLocation } from 'react-router-dom'; -function DoctorForm({ onSave, onCancel, formData, setFormData }) { +function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { const navigate = useNavigate(); const location = useLocation(); - // Funções para formatar telefone e CPF const FormatTelefones = (valor) => { const digits = String(valor).replace(/\D/g, '').slice(0, 11); return digits @@ -23,20 +22,53 @@ function DoctorForm({ onSave, onCancel, formData, setFormData }) { .replace(/(\d{3})(\d{1,2})$/, '$1-$2'); }; + + const validarCPF = (cpf) => { + const cpfLimpo = cpf.replace(/\D/g, ''); + + if (cpfLimpo.length !== 11) return false; + + + if (/^(\d)\1+$/.test(cpfLimpo)) return false; + + + let soma = 0; + for (let i = 0; i < 9; i++) { + soma += parseInt(cpfLimpo.charAt(i)) * (10 - i); + } + let resto = 11 - (soma % 11); + let digito1 = resto === 10 || resto === 11 ? 0 : resto; + + + soma = 0; + for (let i = 0; i < 10; i++) { + soma += parseInt(cpfLimpo.charAt(i)) * (11 - i); + } + resto = 11 - (soma % 11); + let digito2 = resto === 10 || resto === 11 ? 0 : resto; + + + return digito1 === parseInt(cpfLimpo.charAt(9)) && digito2 === parseInt(cpfLimpo.charAt(10)); + }; + const [avatarUrl, setAvatarUrl] = useState(null); + const [showRequiredModal, setShowRequiredModal] = useState(false); + const [emptyFields, setEmptyFields] = useState([]); + const [cpfError, setCpfError] = useState(''); + + const nomeRef = useRef(null); + const cpfRef = useRef(null); + const emailRef = useRef(null); + const telefoneRef = useRef(null); + const crmUfRef = useRef(null); + const crmRef = useRef(null); const [collapsedSections, setCollapsedSections] = useState({ dadosPessoais: true, - infoMedicas: false, - infoConvenio: false, - endereco: false, contato: false, + endereco: false, }); - const [showModal, setShowModal] = useState(false); - const [showSuccessModal, setShowSuccessModal] = useState(false); - const [errorModalMsg, setErrorModalMsg] = useState(''); - const handleToggleCollapse = (section) => { setCollapsedSections(prevState => ({ ...prevState, @@ -47,6 +79,16 @@ function DoctorForm({ onSave, onCancel, formData, setFormData }) { const handleChange = (e) => { const { name, value, type, checked, files } = e.target; + + if (value && emptyFields.includes(name)) { + setEmptyFields(prev => prev.filter(field => field !== name)); + } + + + if (name === 'cpf' && cpfError) { + setCpfError(''); + } + if (type === 'checkbox') { setFormData(prev => ({ ...prev, [name]: checked })); } else if (type === 'file') { @@ -65,6 +107,16 @@ function DoctorForm({ onSave, onCancel, formData, setFormData }) { } else if (name.includes('cpf')) { let cpfFormatado = FormatCPF(value); setFormData(prev => ({ ...prev, [name]: cpfFormatado })); + + + const cpfLimpo = cpfFormatado.replace(/\D/g, ''); + if (cpfLimpo.length === 11) { + if (!validarCPF(cpfFormatado)) { + setCpfError('CPF inválido'); + } else { + setCpfError(''); + } + } } else if (name.includes('phone')) { let telefoneFormatado = FormatTelefones(value); setFormData(prev => ({ ...prev, [name]: telefoneFormatado })); @@ -88,48 +140,133 @@ function DoctorForm({ onSave, onCancel, formData, setFormData }) { state: data.uf || '' })); } else { - setErrorModalMsg('CEP não encontrado!'); - setShowModal(true); + setShowRequiredModal(true); + setEmptyFields(['cep']); } } catch (error) { - setErrorModalMsg('Erro ao buscar o CEP.'); - setShowModal(true); + setShowRequiredModal(true); + setEmptyFields(['cep']); } } }; + + const scrollToEmptyField = (fieldName) => { + let fieldRef = null; + + switch (fieldName) { + case 'full_name': + fieldRef = nomeRef; + setCollapsedSections(prev => ({ ...prev, dadosPessoais: true })); + break; + case 'cpf': + fieldRef = cpfRef; + setCollapsedSections(prev => ({ ...prev, dadosPessoais: true })); + break; + case 'email': + fieldRef = emailRef; + setCollapsedSections(prev => ({ ...prev, contato: true })); + break; + case 'phone_mobile': + fieldRef = telefoneRef; + setCollapsedSections(prev => ({ ...prev, contato: true })); + break; + case 'crm_uf': + fieldRef = crmUfRef; + setCollapsedSections(prev => ({ ...prev, dadosPessoais: true })); + break; + case 'crm': + fieldRef = crmRef; + setCollapsedSections(prev => ({ ...prev, dadosPessoais: true })); + break; + default: + return; + } + + + setTimeout(() => { + if (fieldRef.current) { + fieldRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'center' + }); + fieldRef.current.focus(); + + + fieldRef.current.style.border = '2px solid #dc3545'; + fieldRef.current.style.boxShadow = '0 0 0 0.2rem rgba(220, 53, 69, 0.25)'; + + + setTimeout(() => { + if (fieldRef.current) { + fieldRef.current.style.border = ''; + fieldRef.current.style.boxShadow = ''; + } + }, 3000); + } + }, 300); + }; + const handleSubmit = async () => { - if (!formData.full_name || !formData.cpf || !formData.email || !formData.phone_mobile || !formData.crm_uf || !formData.crm) { - setErrorModalMsg('Por favor, preencha todos os campos obrigatórios.'); - setShowModal(true); + + const missingFields = []; + if (!formData.full_name) missingFields.push('full_name'); + if (!formData.cpf) missingFields.push('cpf'); + if (!formData.email) missingFields.push('email'); + if (!formData.phone_mobile) missingFields.push('phone_mobile'); + if (!formData.crm_uf) missingFields.push('crm_uf'); + if (!formData.crm) missingFields.push('crm'); + + if (missingFields.length > 0) { + setEmptyFields(missingFields); + setShowRequiredModal(true); + + + setTimeout(() => { + if (missingFields.length > 0) { + scrollToEmptyField(missingFields[0]); + } + }, 500); return; } + const cpfLimpo = formData.cpf.replace(/\D/g, ''); if (cpfLimpo.length !== 11) { - setErrorModalMsg('CPF inválido. Por favor, verifique o número digitado.'); - setShowModal(true); + setShowRequiredModal(true); + setEmptyFields(['cpf']); + setCpfError('CPF deve ter 11 dígitos'); + setTimeout(() => scrollToEmptyField('cpf'), 500); return; } + + if (!validarCPF(formData.cpf)) { + setShowRequiredModal(true); + setEmptyFields(['cpf']); + setCpfError('CPF inválido'); + setTimeout(() => scrollToEmptyField('cpf'), 500); + return; + } + + try { await onSave({ ...formData }); - setShowSuccessModal(true); + } catch (error) { - setErrorModalMsg('médico salvo com sucesso'); - setShowModal(true); + + throw error; } }; - const handleCloseSuccessModal = () => { - setShowSuccessModal(false); - const prefixo = location.pathname.split("/")[1]; - navigate(`/${prefixo}/medicos`); + const handleModalClose = () => { + setShowRequiredModal(false); }; return ( <> - {showModal && ( + + {showRequiredModal && (- {errorModalMsg} +
+ {cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'}
+{cpfError}
+ ) : ( + <> + {!formData.full_name &&- Nome
} + {!formData.cpf &&- CPF
} + {!formData.email &&- Telefone
} + {!formData.crm_uf &&- Estado do CRM
} + {!formData.crm &&- CRM
} + > + )} +- Médico salvo com sucesso! -
-- Por favor, preencha: + {cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'}
- Nome
} - {!formData.cpf &&- CPF
} - {!formData.email &&- Telefone
} + {cpfError ? ( +{cpfError}
+ ) : ( + <> + {!formData.full_name &&- Nome
} + {!formData.cpf &&- CPF
} + {!formData.email &&- Telefone
} + > + )}- O cadastro do paciente foi realizado com sucesso. -
-+ Médico cadastrado com sucesso! +
++ {errorMessage} +
+{infosModal.message}
-+ {infosModal.message} +
+ {infosModal.title === 'Erro' && ( ++
+ )} +