diff --git a/src/components/doctors/DoctorForm.css b/src/components/doctors/DoctorForm.css new file mode 100644 index 00000000..d5f23798 --- /dev/null +++ b/src/components/doctors/DoctorForm.css @@ -0,0 +1,201 @@ +.doctor-form-container { + padding: 1rem; +} + +.doctor-form-title { + margin-bottom: 1.5rem; + text-align: center; + font-size: 2.5rem; +} + +.form-section { + margin-bottom: 2rem; + padding: 1.5rem; + border: 1px solid #dee2e6; + border-radius: 0.375rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); +} + +.section-header { + margin-bottom: 1rem; + font-size: 1.8rem; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; +} + +.section-toggle { + font-size: 1.25rem; +} + +.form-label { + font-size: 1.1rem; + margin-bottom: 0.5rem; +} + +.form-control-custom { + font-size: 1.1rem; + padding: 0.5rem 0.75rem; +} + +.avatar-container { + display: flex; + align-items: center; + margin-bottom: 1rem; +} + +.avatar-image { + width: 100px; + height: 100px; + border-radius: 50%; + object-fit: cover; +} + +.avatar-placeholder { + width: 100px; + height: 100px; + border-radius: 50%; + background-color: #e0e0e0; + display: flex; + align-items: center; + justify-content: center; + font-size: 3.5rem; + color: #9e9e9e; +} + +.file-input-label { + font-size: 1rem; + padding: 0.5rem 1rem; +} + +.actions-container { + margin-top: 1rem; + text-align: center; +} + +.btn-submit { + font-size: 1.2rem; + padding: 0.75rem 1.5rem; + margin-right: 1rem; +} + +.btn-cancel { + font-size: 1.2rem; + padding: 0.75rem 1.5rem; +} + + +.modal-overlay { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; +} + +.modal-content { + background-color: #fff; + border-radius: 10px; + width: 400px; + max-width: 90%; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + overflow: hidden; +} + +.modal-header { + background-color: #1e3a8a; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-title { + color: #fff; + margin: 0; + font-size: 1.2rem; + font-weight: bold; +} + +.modal-close-btn { + background: none; + border: none; + font-size: 20px; + color: #fff; + cursor: pointer; +} + +.modal-body { + padding: 25px 20px; +} + +.modal-message { + color: #111; + font-size: 1.1rem; + margin: 0 0 15px 0; + font-weight: bold; +} + +.modal-list { + display: flex; + flex-direction: column; + gap: 8px; + margin-left: 10px; +} + +.modal-list-item { + color: #111; + font-size: 1.1rem; + margin: 0; + font-weight: 600; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + padding: 15px 20px; + border-top: 1px solid #ddd; +} + +.modal-confirm-btn { + background-color: #1e3a8a; + color: #fff; + border: none; + padding: 8px 20px; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + font-weight: bold; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .doctor-form-title { + font-size: 2rem; + } + + .section-header { + font-size: 1.5rem; + } + + .form-section { + padding: 1rem; + } + + .avatar-container { + flex-direction: column; + align-items: flex-start; + } + + .btn-submit, + .btn-cancel { + font-size: 1.1rem; + padding: 0.6rem 1.2rem; + } +} \ No newline at end of file diff --git a/src/components/doctors/DoctorForm.jsx b/src/components/doctors/DoctorForm.jsx index 45ec0bf2..8f335a71 100644 --- a/src/components/doctors/DoctorForm.jsx +++ b/src/components/doctors/DoctorForm.jsx @@ -1,5 +1,6 @@ import React, { useState, useRef } from 'react'; import { Link, useNavigate, useLocation } from 'react-router-dom'; +import './DoctorForm.css'; function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { const navigate = useNavigate(); @@ -22,16 +23,13 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { .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); @@ -39,7 +37,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { 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); @@ -47,7 +44,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { resto = 11 - (soma % 11); let digito2 = resto === 10 || resto === 11 ? 0 : resto; - return digito1 === parseInt(cpfLimpo.charAt(9)) && digito2 === parseInt(cpfLimpo.charAt(10)); }; @@ -79,12 +75,10 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { 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(''); } @@ -108,7 +102,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { let cpfFormatado = FormatCPF(value); setFormData(prev => ({ ...prev, [name]: cpfFormatado })); - const cpfLimpo = cpfFormatado.replace(/\D/g, ''); if (cpfLimpo.length === 11) { if (!validarCPF(cpfFormatado)) { @@ -150,7 +143,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { } }; - const scrollToEmptyField = (fieldName) => { let fieldRef = null; @@ -183,7 +175,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { return; } - setTimeout(() => { if (fieldRef.current) { fieldRef.current.scrollIntoView({ @@ -192,11 +183,9 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { }); 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 = ''; @@ -208,7 +197,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { }; const handleSubmit = async () => { - const missingFields = []; if (!formData.full_name) missingFields.push('full_name'); if (!formData.cpf) missingFields.push('cpf'); @@ -221,7 +209,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { setEmptyFields(missingFields); setShowRequiredModal(true); - setTimeout(() => { if (missingFields.length > 0) { scrollToEmptyField(missingFields[0]); @@ -230,7 +217,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { return; } - const cpfLimpo = formData.cpf.replace(/\D/g, ''); if (cpfLimpo.length !== 11) { setShowRequiredModal(true); @@ -240,7 +226,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { return; } - if (!validarCPF(formData.cpf)) { setShowRequiredModal(true); setEmptyFields(['cpf']); @@ -249,12 +234,9 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { return; } - try { await onSave({ ...formData }); - } catch (error) { - throw error; } }; @@ -265,96 +247,43 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { return ( <> - {showRequiredModal && ( -
-
-
-
Atenção
+
+
+
+
Atenção
-
-

+

+

{cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'}

-
+
{cpfError ? ( -

{cpfError}

+

{cpfError}

) : ( <> - {!formData.full_name &&

- Nome

} - {!formData.cpf &&

- CPF

} - {!formData.email &&

- Email

} - {!formData.phone_mobile &&

- Telefone

} - {!formData.crm_uf &&

- Estado do CRM

} - {!formData.crm &&

- CRM

} + {!formData.full_name &&

- Nome

} + {!formData.cpf &&

- CPF

} + {!formData.email &&

- Email

} + {!formData.phone_mobile &&

- Telefone

} + {!formData.crm_uf &&

- Estado do CRM

} + {!formData.crm &&

- CRM

} )}
-
+
@@ -363,48 +292,35 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
)} -
-

MediConnect

+
+

MediConnect

-
-

handleToggleCollapse('dadosPessoais')} - style={{ fontSize: '1.8rem' }}> + {/* DADOS PESSOAIS */} +
+

handleToggleCollapse('dadosPessoais')}> Dados Pessoais - + {collapsedSections.dadosPessoais ? '▲' : '▼'}

-
+
{avatarUrl ? ( Avatar do Médico ) : ( -
+
)}
- + - {formData.foto && {formData.foto.name}} + {formData.foto && {formData.foto.name}}
- +
- - + +
- +
- +
- - @@ -523,105 +439,100 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
-
-

handleToggleCollapse('contato')} - style={{ fontSize: '1.8rem' }}> + {/* CONTATO */} +
+

handleToggleCollapse('contato')}> Contato - + {collapsedSections.contato ? '▲' : '▼'}

- +
- +
- - + +
-
-

handleToggleCollapse('endereco')} - style={{ fontSize: '1.8rem' }}> + {/* ENDEREÇO */} +
+

handleToggleCollapse('endereco')}> Endereço - + {collapsedSections.endereco ? '▲' : '▼'}

- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
-
+ {/* BOTÕES DE AÇÃO */} +
- diff --git a/src/components/patients/PatientForm.css b/src/components/patients/PatientForm.css new file mode 100644 index 00000000..f829d414 --- /dev/null +++ b/src/components/patients/PatientForm.css @@ -0,0 +1,215 @@ +.patient-form-container { + padding: 1rem; +} + +.patient-form-title { + margin-bottom: 1.5rem; + text-align: center; + font-size: 2.5rem; +} + +.form-section { + margin-bottom: 2rem; + padding: 1.5rem; + border: 1px solid #dee2e6; + border-radius: 0.375rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); +} + +.section-header { + margin-bottom: 1rem; + font-size: 1.8rem; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; +} + +.section-toggle { + font-size: 1.25rem; +} + +.form-label { + font-size: 1.1rem; + margin-bottom: 0.5rem; +} + +.form-control-custom { + font-size: 1.1rem; + padding: 0.5rem 0.75rem; +} + +.avatar-container { + display: flex; + align-items: center; + margin-bottom: 1rem; +} + +.avatar-image { + width: 100px; + height: 100px; + border-radius: 50%; + object-fit: cover; +} + +.avatar-placeholder { + width: 100px; + height: 100px; + border-radius: 50%; + background-color: #e0e0e0; + display: flex; + align-items: center; + justify-content: center; + font-size: 3.5rem; + color: #9e9e9e; +} + +.file-input-label { + font-size: 1rem; + padding: 0.5rem 1rem; +} + +.checkbox-custom { + transform: scale(1.2); +} + +.checkbox-label { + font-size: 1.1rem; + margin-left: 0.5rem; +} + +.textarea-custom { + font-size: 1.1rem; + min-height: 100px; +} + +.actions-container { + margin-top: 1rem; + text-align: center; +} + +.btn-submit { + font-size: 1.2rem; + padding: 0.75rem 1.5rem; + margin-right: 1rem; +} + +.btn-cancel { + font-size: 1.2rem; + padding: 0.75rem 1.5rem; +} + + +.modal-overlay { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; +} + +.modal-content { + background-color: #fff; + border-radius: 10px; + width: 400px; + max-width: 90%; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + overflow: hidden; +} + +.modal-header { + background-color: #1e3a8a; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-title { + color: #fff; + margin: 0; + font-size: 1.2rem; + font-weight: bold; +} + +.modal-close-btn { + background: none; + border: none; + font-size: 20px; + color: #fff; + cursor: pointer; +} + +.modal-body { + padding: 25px 20px; +} + +.modal-message { + color: #111; + font-size: 1.1rem; + margin: 0 0 15px 0; + font-weight: bold; +} + +.modal-list { + display: flex; + flex-direction: column; + gap: 8px; + margin-left: 10px; +} + +.modal-list-item { + color: #111; + font-size: 1.1rem; + margin: 0; + font-weight: 600; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + padding: 15px 20px; + border-top: 1px solid #ddd; +} + +.modal-confirm-btn { + background-color: #1e3a8a; + color: #fff; + border: none; + padding: 8px 20px; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + font-weight: bold; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .patient-form-title { + font-size: 2rem; + } + + .section-header { + font-size: 1.5rem; + } + + .form-section { + padding: 1rem; + } + + .avatar-container { + flex-direction: column; + align-items: flex-start; + } + + .btn-submit, + .btn-cancel { + font-size: 1.1rem; + padding: 0.6rem 1.2rem; + } +} \ No newline at end of file diff --git a/src/components/patients/PatientForm.jsx b/src/components/patients/PatientForm.jsx index a8cdd620..fd48d03c 100644 --- a/src/components/patients/PatientForm.jsx +++ b/src/components/patients/PatientForm.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { FormatTelefones, FormatPeso, FormatCPF } from '../utils/Formatar/Format'; +import './PatientForm.css'; function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { const [avatarUrl, setAvatarUrl] = useState(null); @@ -20,17 +21,13 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { const emailRef = useRef(null); const telefoneRef = useRef(null); - 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); @@ -38,7 +35,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { 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); @@ -46,7 +42,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { resto = 11 - (soma % 11); let digito2 = resto === 10 || resto === 11 ? 0 : resto; - return digito1 === parseInt(cpfLimpo.charAt(9)) && digito2 === parseInt(cpfLimpo.charAt(10)); }; @@ -71,12 +66,10 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { 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(''); } @@ -120,7 +113,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { switch (fieldName) { case 'full_name': fieldRef = nomeRef; - setCollapsedSections(prev => ({ ...prev, dadosPessoais: true })); break; case 'cpf': @@ -139,7 +131,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { return; } - setTimeout(() => { if (fieldRef.current) { fieldRef.current.scrollIntoView({ @@ -148,11 +139,9 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { }); 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 = ''; @@ -164,7 +153,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { }; const handleSubmit = async () => { - const missingFields = []; if (!formData.full_name) missingFields.push('full_name'); if (!formData.cpf) missingFields.push('cpf'); @@ -175,17 +163,14 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { setEmptyFields(missingFields); setShowRequiredModal(true); - setTimeout(() => { if (missingFields.length > 0) { scrollToEmptyField(missingFields[0]); } - }, 500); - + }, 500); return; } - const cpfLimpo = formData.cpf.replace(/\D/g, ''); if (cpfLimpo.length !== 11) { setShowRequiredModal(true); @@ -195,7 +180,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { return; } - if (!validarCPF(formData.cpf)) { setShowRequiredModal(true); setEmptyFields(['cpf']); @@ -205,8 +189,10 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(formData.email)) { + throw new Error('Email inválido. Por favor, verifique o email digitado.'); + } - await onSave({ ...formData, bmi: parseFloat(formData.bmi) || null }); }; @@ -215,48 +201,36 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { }; return ( -
-

MediConnect

+
+

MediConnect

{/* DADOS PESSOAIS */} -
-

handleToggleCollapse('dadosPessoais')} style={{ fontSize: '1.8rem' }}> +
+

handleToggleCollapse('dadosPessoais')}> Dados Pessoais - + {collapsedSections.dadosPessoais ? '▲' : '▼'}

{/* AVATAR E INPUT DE FOTO */} -
+
{avatarUrl ? ( Avatar do Paciente ) : ( -
+
)}
- + - {formData.foto && {formData.foto.name}} + {formData.foto && {formData.foto.name}}
{/* CAMPOS OBRIGATÓRIOS */}
- +
- - + +
- +
- +
- + {cpfError && ( @@ -333,12 +303,12 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { )}
- - + +
- - @@ -346,12 +316,12 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
- - + +
- - @@ -361,20 +331,20 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
- - + +
- - + +
- - + +
- - @@ -383,37 +353,37 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
- -
@@ -421,35 +391,34 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { {/* CAMPOS ADICIONAIS */}
- - + +
- +
- + - {formData.anexos ? formData.anexos.name : 'Nenhum arquivo escolhido'} + {formData.anexos ? formData.anexos.name : 'Nenhum arquivo escolhido'}
-
{/* INFORMAÇÕES MÉDICAS */} -
-

handleToggleCollapse('infoMedicas')} style={{ fontSize: '1.8rem' }}> +
+

handleToggleCollapse('infoMedicas')}> Informações Médicas - + {collapsedSections.infoMedicas ? '▲' : '▼'}

- - @@ -462,35 +431,34 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
- - + +
- - + +
- - + +
-
{/* INFORMAÇÕES DE CONVÊNIO */} -
-

handleToggleCollapse('infoConvenio')} style={{ fontSize: '1.8rem' }}> +
+

handleToggleCollapse('infoConvenio')}> Informações de convênio - + {collapsedSections.infoConvenio ? '▲' : '▼'}

- - @@ -499,21 +467,21 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
- - + +
- - + +
- - + +
- -
@@ -521,8 +489,8 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { {/* PACIENTE VIP */}
- -
@@ -532,185 +500,129 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
{/* ENDEREÇO */} -
-

handleToggleCollapse('endereco')} style={{ fontSize: '1.8rem' }}> +
+

handleToggleCollapse('endereco')}> Endereço - + {collapsedSections.endereco ? '▲' : '▼'}

- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
-
-

handleToggleCollapse('contato')} style={{ fontSize: '1.8rem' }}> + {/* CONTATO */} +
+

handleToggleCollapse('contato')}> Contato - + {collapsedSections.contato ? '▲' : '▼'}

- +
- +
- - + +
- - + +
- + {/* MODAL DE VALIDAÇÃO */} {showRequiredModal && ( -
-
- -
-
Atenção
+
+
+
+
Atenção
- -
-

+

+

{cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'}

-
+
{cpfError ? ( -

{cpfError}

+

{cpfError}

) : ( <> - {!formData.full_name &&

- Nome

} - {!formData.cpf &&

- CPF

} - {!formData.email &&

- Email

} - {!formData.phone_mobile &&

- Telefone

} + {!formData.full_name &&

- Nome

} + {!formData.cpf &&

- CPF

} + {!formData.email &&

- Email

} + {!formData.phone_mobile &&

- Telefone

} )}
- -
+
@@ -719,13 +631,13 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
)} - -
- - diff --git a/src/pages/DoctorCadastroManager.jsx b/src/pages/DoctorCadastroManager.jsx index 450e7f75..3d0e11c5 100644 --- a/src/pages/DoctorCadastroManager.jsx +++ b/src/pages/DoctorCadastroManager.jsx @@ -3,6 +3,7 @@ import { useAuth } from '../components/utils/AuthProvider'; import DoctorForm from '../components/doctors/DoctorForm'; import API_KEY from '../components/utils/apiKeys'; import { useNavigate, useLocation } from 'react-router-dom'; +import './style/DoctorCadastroManager.css'; function DoctorCadastroManager() { const [doctorData, setDoctorData] = useState({}); @@ -65,7 +66,6 @@ function DoctorCadastroManager() { if (!response.ok) { let errorMessage = `Erro ao salvar médico (${response.status})`; - const responseText = await response.text(); console.log("Conteúdo da resposta:", responseText); @@ -75,7 +75,6 @@ function DoctorCadastroManager() { console.error("Erro detalhado:", errorData); errorMessage = errorData.message || errorData.details || errorMessage; } catch (jsonError) { - errorMessage = responseText || errorMessage; } } else { @@ -101,7 +100,6 @@ function DoctorCadastroManager() { result = { success: true, status: response.status }; } - setShowSuccessModal(true); } catch (error) { @@ -110,7 +108,7 @@ function DoctorCadastroManager() { let userFriendlyMessage = error.message; if (error.message.includes('doctors_cpf_key') || error.message.includes('duplicate key')) { - userFriendlyMessage = 'Já existe um médico cadastrado com este CPF. Verifique os dados ou edite o médico existente.'; + userFriendlyMessage = 'Já existe um médico cadastrado com este CPF.'; } else if (error.message.includes('Unexpected end of JSON input')) { userFriendlyMessage = 'Erro de comunicação com o servidor. Tente novamente.'; } @@ -124,7 +122,6 @@ function DoctorCadastroManager() { const handleCloseSuccessModal = () => { setShowSuccessModal(false); - const prefixo = location.pathname.split("/")[1]; navigate(`/${prefixo}/medicos`); }; @@ -135,83 +132,35 @@ function DoctorCadastroManager() { return ( <> + {isLoading && ( +
+
+
+ )} {showSuccessModal && ( -
-
-
-
Sucesso
+
+
+
+
Sucesso
-
-

+

+

Médico cadastrado com sucesso!

-
+
@@ -220,82 +169,29 @@ function DoctorCadastroManager() {
)} - {showErrorModal && ( -
-
-
-
Erro
+
+
+
+
Erro
-
-

+

+

{errorMessage}

-
+
diff --git a/src/pages/PatientCadastroManager.jsx b/src/pages/PatientCadastroManager.jsx index 701e6b5b..1504b5b5 100644 --- a/src/pages/PatientCadastroManager.jsx +++ b/src/pages/PatientCadastroManager.jsx @@ -1,70 +1,64 @@ -import {useState} from 'react'; +import { useState } from 'react'; import React from 'react'; import { useNavigate } from 'react-router-dom'; import PatientForm from '../components/patients/PatientForm'; import API_KEY from '../components/utils/apiKeys'; import { useAuth } from '../components/utils/AuthProvider'; +import './style/PatientCadastroManager.css'; -function PatientCadastroManager( {setCurrentPage} ) { +function PatientCadastroManager({ setCurrentPage }) { const navigate = useNavigate() const [showModal, setShowModal] = useState(false); - const [infosModal, setInfosModal] = useState({title:'', message:''}); + const [infosModal, setInfosModal] = useState({ title: '', message: '' }); const [isLoading, setIsLoading] = useState(false); const { getAuthorizationHeader, isAuthenticated } = useAuth(); const [formData, setFormData] = useState({}) - const validarCPF = (cpf) => { cpf = cpf.replace(/\D/g, ''); - + if (cpf.length !== 11) return false; - - + if (/^(\d)\1+$/.test(cpf)) return false; let soma = 0; let resto; - + for (let i = 1; i <= 9; i++) { - soma = soma + parseInt(cpf.substring(i-1, i)) * (11 - i); + soma = soma + parseInt(cpf.substring(i - 1, i)) * (11 - i); } - + resto = (soma * 10) % 11; if ((resto === 10) || (resto === 11)) resto = 0; if (resto !== parseInt(cpf.substring(9, 10))) return false; - + soma = 0; for (let i = 1; i <= 10; i++) { - soma = soma + parseInt(cpf.substring(i-1, i)) * (12 - i); + soma = soma + parseInt(cpf.substring(i - 1, i)) * (12 - i); } - + resto = (soma * 10) % 11; if ((resto === 10) || (resto === 11)) resto = 0; if (resto !== parseInt(cpf.substring(10, 11))) return false; - + return true; } - + const handleSavePatient = async (patientData) => { - console.log(' Iniciando salvamento do paciente:', patientData); setIsLoading(true); try { - - console.log(' Verificando autenticação...'); if (!isAuthenticated) { throw new Error('Usuário não autenticado'); } const authHeader = getAuthorizationHeader(); - console.log(' Header de autorização:', authHeader ? 'Presente' : 'Faltando'); - + if (!authHeader) { throw new Error('Header de autorização não encontrado'); } - const cpfLimpo = patientData.cpf.replace(/\D/g, ''); if (!validarCPF(cpfLimpo)) { throw new Error('CPF inválido. Por favor, verifique o número digitado.'); @@ -76,23 +70,15 @@ function PatientCadastroManager( {setCurrentPage} ) { myHeaders.append("Authorization", authHeader); myHeaders.append("Prefer", "return=representation"); - console.log(' Headers configurados:', { - 'Content-Type': 'application/json', - 'apikey': API_KEY ? 'Presente' : 'Faltando', - 'Authorization': authHeader ? 'Presente' : 'Faltando', - 'Prefer': 'return=representation' - }); - const cleanedData = { full_name: patientData.full_name, cpf: cpfLimpo, email: patientData.email, phone_mobile: patientData.phone_mobile, - birth_date: patientData.birth_date || null, - sex: patientData.sex === 'Masculino' ? 'M' : - patientData.sex === 'Feminino' ? 'F' : - patientData.sex || null, + sex: patientData.sex === 'Masculino' ? 'M' : + patientData.sex === 'Feminino' ? 'F' : + patientData.sex || null, social_name: patientData.social_name || null, rg: patientData.rg || null, blood_type: patientData.blood_type || null, @@ -100,16 +86,31 @@ function PatientCadastroManager( {setCurrentPage} ) { height_m: patientData.height_m ? parseFloat(patientData.height_m) : null, bmi: patientData.bmi ? parseFloat(patientData.bmi) : null, notes: patientData.notes || null, + mother_name: patientData.mother_name || null, + father_name: patientData.father_name || null, + guardian_name: patientData.guardian_name || null, + guardian_cpf: patientData.guardian_cpf ? patientData.guardian_cpf.replace(/\D/g, '') : null, + marital_status: patientData.marital_status || null, + profession: patientData.profession || null, + naturality: patientData.naturality || null, + nationality: patientData.nationality || null, + race: patientData.race || null, + cep: patientData.cep || null, + street: patientData.street || null, + number: patientData.number || null, + complement: patientData.complement || null, + neighborhood: patientData.neighborhood || null, + city: patientData.city || null, + state: patientData.state || null, + phone1: patientData.phone1 || null, + phone2: patientData.phone2 || null }; - console.log(' Dados limpos para envio:', cleanedData); - if (!cleanedData.full_name || !cleanedData.cpf || !cleanedData.email || !cleanedData.phone_mobile) { throw new Error('Dados obrigatórios faltando: nome, CPF, email e telefone são necessários'); } var raw = JSON.stringify(cleanedData); - console.log(' Payload JSON:', raw); var requestOptions = { method: 'POST', @@ -118,37 +119,24 @@ function PatientCadastroManager( {setCurrentPage} ) { redirect: 'follow' }; - console.log(' Fazendo requisição para API...'); const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients", requestOptions); - - console.log(' Status da resposta:', response.status); - console.log(' Response ok:', response.ok); let responseData; try { responseData = await response.json(); - console.log(' Corpo da resposta:', responseData); } catch (jsonError) { - console.log(' Não foi possível parsear JSON da resposta:', jsonError); responseData = { error: 'Resposta inválida da API' }; } - if (!response.ok) { - console.error(' Erro da API - Detalhes:', { - status: response.status, - statusText: response.statusText, - data: responseData - }); - let errorMessage = 'Erro ao salvar paciente'; - + if (response.status === 401) { errorMessage = 'Não autorizado. Verifique suas credenciais.'; } else if (response.status === 403) { errorMessage = 'Acesso proibido. Verifique suas permissões.'; } else if (response.status === 409) { - errorMessage = 'Paciente com este CPF já existe.'; + errorMessage = 'Já existe um paciente cadastrado com este CPF.'; } else if (response.status === 400) { errorMessage = `Dados inválidos: ${responseData.details || responseData.message || 'Verifique os campos'}`; } else if (response.status === 422) { @@ -158,27 +146,22 @@ function PatientCadastroManager( {setCurrentPage} ) { } else { errorMessage = `Erro ${response.status}: ${responseData.message || responseData.error || 'Erro desconhecido'}`; } - + throw new Error(errorMessage); } - console.log(' Paciente salvo com sucesso:', responseData); - - setInfosModal({ title: 'Sucesso', message: 'O cadastro do paciente foi realizado com sucesso.' }); setShowModal(true); - - + setTimeout(() => { setShowModal(false); navigate('/secretaria/pacientes'); }, 2000); - + } catch (error) { - console.error(' Erro completo ao salvar paciente:', error); setInfosModal({ title: 'Erro', message: error.message || 'Não foi possível conectar ao servidor. Verifique sua internet e tente novamente.' @@ -192,76 +175,30 @@ function PatientCadastroManager( {setCurrentPage} ) { return ( <>
- {showModal &&( -
-
- -
-
{infosModal.title}
+ {showModal && ( +
+
+
+
{infosModal.title}
- -
-

+

+

{infosModal.message}

{infosModal.title === 'Erro' && ( -

+

)}
- -
+
diff --git a/src/pages/style/DoctorCadastroManager.css b/src/pages/style/DoctorCadastroManager.css new file mode 100644 index 00000000..3ac82bfe --- /dev/null +++ b/src/pages/style/DoctorCadastroManager.css @@ -0,0 +1,281 @@ +/* Estilos do modal */ +.modal-overlay { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; +} + +.modal-container { + background-color: #fff; + border-radius: 10px; + width: 400px; + max-width: 90%; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + overflow: hidden; + animation: modal-appear 0.3s ease-out; +} + +@keyframes modal-appear { + from { + opacity: 0; + transform: scale(0.9) translateY(-20px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.modal-header { + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header-success { + background-color: #28a745 !important; +} + +.modal-header-error { + background-color: #dc3545 !important; +} + +.modal-title { + color: #fff; + margin: 0; + font-size: 1.2rem; + font-weight: bold; +} + +.modal-close-button { + background: none; + border: none; + font-size: 20px; + color: #fff; + cursor: pointer; + font-weight: bold; + transition: opacity 0.2s ease; +} + +.modal-close-button:hover { + opacity: 0.8; +} + +.modal-body { + padding: 25px 20px; +} + +.modal-message { + color: #111; + font-size: 1.1rem; + margin: 0; + font-weight: 600; + line-height: 1.4; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + padding: 15px 20px; + border-top: 1px solid #ddd; +} + +.modal-button { + border: none; + padding: 8px 20px; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + font-weight: bold; + transition: all 0.2s ease; +} + +.modal-button:hover { + transform: translateY(-1px); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.modal-button-success { + background-color: #28a745; + color: #fff; +} + +.modal-button-success:hover { + background-color: #218838; +} + +.modal-button-error { + background-color: #dc3545; + color: #fff; +} + +.modal-button-error:hover { + background-color: #c82333; +} + +.page-heading { + margin-bottom: 2rem; + padding: 1rem 0; + border-bottom: 1px solid #eaeaea; +} + +.page-heading h3 { + color: #333; + font-size: 1.75rem; + font-weight: 600; + margin: 0; +} + +.page-content { + background-color: #fff; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + padding: 2rem; + margin-bottom: 2rem; +} + +.page-content section { + margin: 0; +} + +.page-content .row { + margin: 0; +} + +.page-content .col-12 { + padding: 0; +} + +.loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.8); + display: flex; + justify-content: center; + align-items: center; + z-index: 9998; +} + +.loading-spinner { + border: 4px solid #f3f3f3; + border-top: 4px solid #3498db; + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.modal-close-button:focus, +.modal-button:focus { + outline: 2px solid #0056b3; + outline-offset: 2px; +} + +/* Garantir que as cores dos cabeçalhos sejam aplicadas */ +.modal-overlay .modal-container .modal-header.modal-header-success { + background-color: #28a745 !important; +} + +.modal-overlay .modal-container .modal-header.modal-header-error { + background-color: #dc3545 !important; +} + +@media (max-width: 768px) { + .modal-container { + width: 95%; + margin: 1rem; + } + + .modal-body { + padding: 20px 15px; + } + + .modal-message { + font-size: 1rem; + } + + .page-heading h3 { + font-size: 1.5rem; + } + + .page-content { + padding: 1.5rem; + margin: 1rem; + } +} + +@media (max-width: 480px) { + .modal-header { + padding: 12px 15px; + } + + .modal-title { + font-size: 1.1rem; + } + + .modal-body { + padding: 15px; + } + + .modal-footer { + padding: 12px 15px; + } + + .page-content { + padding: 1rem; + margin: 0.5rem; + } + + .page-heading { + margin-bottom: 1.5rem; + padding: 0.75rem 0; + } + + .page-heading h3 { + font-size: 1.35rem; + } +} + +@media (prefers-contrast: high) { + .modal-container { + border: 2px solid #000; + } + + .modal-header-success { + background-color: #006400 !important; + } + + .modal-header-error { + background-color: #8b0000 !important; + } +} + +@media (prefers-reduced-motion: reduce) { + .modal-container { + animation: none; + } + + .modal-button:hover { + transform: none; + } + + .loading-spinner { + animation-duration: 2s; + } +} \ No newline at end of file diff --git a/src/pages/style/PatientCadastroManager.css b/src/pages/style/PatientCadastroManager.css new file mode 100644 index 00000000..1c8b2cd7 --- /dev/null +++ b/src/pages/style/PatientCadastroManager.css @@ -0,0 +1,278 @@ +.page-heading { + margin-bottom: 2rem; +} + +.page-heading h3 { + color: #1e3a8a; + font-weight: 600; + margin-bottom: 1rem; +} + +.page-content { + padding: 1rem 0; +} + +.alert-info { + background-color: #d1ecf1; + border-color: #bee5eb; + color: #0c5460; + padding: 0.75rem 1.25rem; + border-radius: 0.375rem; + display: flex; + align-items: center; + font-size: 0.9rem; +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.15em; +} + +.section { + margin-bottom: 2rem; +} + +.row { + display: flex; + flex-wrap: wrap; + margin-right: -0.75rem; + margin-left: -0.75rem; +} + +.col-12 { + flex: 0 0 100%; + max-width: 100%; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +/* Estilos do modal */ +.modal-overlay { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; +} + +.modal-content { + background-color: #fff; + border-radius: 10px; + width: 400px; + max-width: 90%; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + overflow: hidden; + animation: modal-appear 0.3s ease-out; +} + +@keyframes modal-appear { + from { + opacity: 0; + transform: translateY(-20px) scale(0.95); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.modal-header { + background-color: #1e3a8a; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header.success { + background-color: #28a745 !important; +} + +.modal-header.error { + background-color: #dc3545 !important; +} + +.modal-header h5 { + color: #fff; + margin: 0; + font-size: 1.2rem; + font-weight: bold; +} + +.modal-close-button { + background: none; + border: none; + font-size: 20px; + color: #fff; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: background-color 0.2s; +} + +.modal-close-button:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +.modal-body { + padding: 25px 20px; +} + +.modal-message { + color: #111; + font-size: 1.1rem; + margin: 0; + font-weight: 600; + line-height: 1.4; +} + +.modal-submessage { + color: #666; + font-size: 0.9rem; + margin: 10px 0 0 0; + line-height: 1.4; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + padding: 15px 20px; + border-top: 1px solid #ddd; +} + +.modal-confirm-button { + background-color: #1e3a8a; + color: #fff; + border: none; + padding: 8px 20px; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + font-weight: bold; + transition: background-color 0.2s; +} + +.modal-confirm-button:hover { + background-color: #1e40a5; +} + +.modal-confirm-button.success { + background-color: #28a745 !important; +} + +.modal-confirm-button.success:hover { + background-color: #218838 !important; +} + +.modal-confirm-button.error { + background-color: #dc3545 !important; +} + +.modal-confirm-button.error:hover { + background-color: #c82333 !important; +} + +@media (max-width: 768px) { + .page-heading { + margin-bottom: 1.5rem; + } + + .page-heading h3 { + font-size: 1.5rem; + } + + .modal-content { + width: 95%; + margin: 1rem; + } + + .modal-body { + padding: 20px 15px; + } + + .modal-message { + font-size: 1rem; + } +} + +@media (max-width: 480px) { + .page-heading h3 { + font-size: 1.3rem; + } + + .modal-header { + padding: 12px 15px; + } + + .modal-header h5 { + font-size: 1.1rem; + } + + .modal-body { + padding: 15px; + } + + .modal-footer { + padding: 12px 15px; + } + + .modal-confirm-button { + padding: 6px 16px; + font-size: 0.9rem; + } +} + +.fade-in { + animation: fadeIn 0.3s ease-in; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.slide-in { + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + transform: translateY(-10px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.loading-state { + opacity: 0.7; + pointer-events: none; +} + +.loading-state::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.8); + z-index: 10; +} \ No newline at end of file