Organização da estilização

This commit is contained in:
Caio Miguel Lima Nunes 2025-10-15 13:30:43 -03:00
parent 28f12b467c
commit b6bda9945b
8 changed files with 1262 additions and 640 deletions

View File

@ -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;
}
}

View File

@ -1,5 +1,6 @@
import React, { useState, useRef } from 'react'; import React, { useState, useRef } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom'; import { Link, useNavigate, useLocation } from 'react-router-dom';
import './DoctorForm.css';
function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) { function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
const navigate = useNavigate(); const navigate = useNavigate();
@ -22,16 +23,13 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
.replace(/(\d{3})(\d{1,2})$/, '$1-$2'); .replace(/(\d{3})(\d{1,2})$/, '$1-$2');
}; };
const validarCPF = (cpf) => { const validarCPF = (cpf) => {
const cpfLimpo = cpf.replace(/\D/g, ''); const cpfLimpo = cpf.replace(/\D/g, '');
if (cpfLimpo.length !== 11) return false; if (cpfLimpo.length !== 11) return false;
if (/^(\d)\1+$/.test(cpfLimpo)) return false; if (/^(\d)\1+$/.test(cpfLimpo)) return false;
let soma = 0; let soma = 0;
for (let i = 0; i < 9; i++) { for (let i = 0; i < 9; i++) {
soma += parseInt(cpfLimpo.charAt(i)) * (10 - 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 resto = 11 - (soma % 11);
let digito1 = resto === 10 || resto === 11 ? 0 : resto; let digito1 = resto === 10 || resto === 11 ? 0 : resto;
soma = 0; soma = 0;
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
soma += parseInt(cpfLimpo.charAt(i)) * (11 - i); soma += parseInt(cpfLimpo.charAt(i)) * (11 - i);
@ -47,7 +44,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
resto = 11 - (soma % 11); resto = 11 - (soma % 11);
let digito2 = resto === 10 || resto === 11 ? 0 : resto; let digito2 = resto === 10 || resto === 11 ? 0 : resto;
return digito1 === parseInt(cpfLimpo.charAt(9)) && digito2 === parseInt(cpfLimpo.charAt(10)); 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 handleChange = (e) => {
const { name, value, type, checked, files } = e.target; const { name, value, type, checked, files } = e.target;
if (value && emptyFields.includes(name)) { if (value && emptyFields.includes(name)) {
setEmptyFields(prev => prev.filter(field => field !== name)); setEmptyFields(prev => prev.filter(field => field !== name));
} }
if (name === 'cpf' && cpfError) { if (name === 'cpf' && cpfError) {
setCpfError(''); setCpfError('');
} }
@ -108,7 +102,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
let cpfFormatado = FormatCPF(value); let cpfFormatado = FormatCPF(value);
setFormData(prev => ({ ...prev, [name]: cpfFormatado })); setFormData(prev => ({ ...prev, [name]: cpfFormatado }));
const cpfLimpo = cpfFormatado.replace(/\D/g, ''); const cpfLimpo = cpfFormatado.replace(/\D/g, '');
if (cpfLimpo.length === 11) { if (cpfLimpo.length === 11) {
if (!validarCPF(cpfFormatado)) { if (!validarCPF(cpfFormatado)) {
@ -150,7 +143,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
} }
}; };
const scrollToEmptyField = (fieldName) => { const scrollToEmptyField = (fieldName) => {
let fieldRef = null; let fieldRef = null;
@ -183,7 +175,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return; return;
} }
setTimeout(() => { setTimeout(() => {
if (fieldRef.current) { if (fieldRef.current) {
fieldRef.current.scrollIntoView({ fieldRef.current.scrollIntoView({
@ -192,11 +183,9 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}); });
fieldRef.current.focus(); fieldRef.current.focus();
fieldRef.current.style.border = '2px solid #dc3545'; fieldRef.current.style.border = '2px solid #dc3545';
fieldRef.current.style.boxShadow = '0 0 0 0.2rem rgba(220, 53, 69, 0.25)'; fieldRef.current.style.boxShadow = '0 0 0 0.2rem rgba(220, 53, 69, 0.25)';
setTimeout(() => { setTimeout(() => {
if (fieldRef.current) { if (fieldRef.current) {
fieldRef.current.style.border = ''; fieldRef.current.style.border = '';
@ -208,7 +197,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
const missingFields = []; const missingFields = [];
if (!formData.full_name) missingFields.push('full_name'); if (!formData.full_name) missingFields.push('full_name');
if (!formData.cpf) missingFields.push('cpf'); if (!formData.cpf) missingFields.push('cpf');
@ -221,7 +209,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
setEmptyFields(missingFields); setEmptyFields(missingFields);
setShowRequiredModal(true); setShowRequiredModal(true);
setTimeout(() => { setTimeout(() => {
if (missingFields.length > 0) { if (missingFields.length > 0) {
scrollToEmptyField(missingFields[0]); scrollToEmptyField(missingFields[0]);
@ -230,7 +217,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return; return;
} }
const cpfLimpo = formData.cpf.replace(/\D/g, ''); const cpfLimpo = formData.cpf.replace(/\D/g, '');
if (cpfLimpo.length !== 11) { if (cpfLimpo.length !== 11) {
setShowRequiredModal(true); setShowRequiredModal(true);
@ -240,7 +226,6 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return; return;
} }
if (!validarCPF(formData.cpf)) { if (!validarCPF(formData.cpf)) {
setShowRequiredModal(true); setShowRequiredModal(true);
setEmptyFields(['cpf']); setEmptyFields(['cpf']);
@ -249,12 +234,9 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return; return;
} }
try { try {
await onSave({ ...formData }); await onSave({ ...formData });
} catch (error) { } catch (error) {
throw error; throw error;
} }
}; };
@ -265,96 +247,43 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return ( return (
<> <>
{showRequiredModal && ( {showRequiredModal && (
<div <div className="modal-overlay">
style={{ <div className="modal-content">
display: "flex", <div className="modal-header">
justifyContent: "center", <h5 className="modal-title">Atenção</h5>
alignItems: "center",
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 9999,
}}
>
<div
style={{
backgroundColor: "#fff",
borderRadius: "10px",
width: "400px",
maxWidth: "90%",
boxShadow: "0 5px 15px rgba(0,0,0,0.3)",
overflow: "hidden",
}}
>
<div
style={{
backgroundColor: "#1e3a8a",
padding: "15px 20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<h5 style={{ color: "#fff", margin: 0, fontSize: "1.2rem", fontWeight: "bold" }}>Atenção</h5>
<button <button
onClick={handleModalClose} onClick={handleModalClose}
style={{ className="modal-close-btn"
background: "none",
border: "none",
fontSize: "20px",
color: "#fff",
cursor: "pointer",
}}
> >
× ×
</button> </button>
</div> </div>
<div style={{ padding: "25px 20px" }}> <div className="modal-body">
<p style={{ color: "#111", fontSize: "1.1rem", margin: "0 0 15px 0", fontWeight: "bold" }}> <p className="modal-message">
{cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'} {cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'}
</p> </p>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginLeft: '10px' }}> <div className="modal-list">
{cpfError ? ( {cpfError ? (
<p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>{cpfError}</p> <p className="modal-list-item">{cpfError}</p>
) : ( ) : (
<> <>
{!formData.full_name && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Nome</p>} {!formData.full_name && <p className="modal-list-item">- Nome</p>}
{!formData.cpf && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- CPF</p>} {!formData.cpf && <p className="modal-list-item">- CPF</p>}
{!formData.email && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Email</p>} {!formData.email && <p className="modal-list-item">- Email</p>}
{!formData.phone_mobile && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Telefone</p>} {!formData.phone_mobile && <p className="modal-list-item">- Telefone</p>}
{!formData.crm_uf && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Estado do CRM</p>} {!formData.crm_uf && <p className="modal-list-item">- Estado do CRM</p>}
{!formData.crm && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- CRM</p>} {!formData.crm && <p className="modal-list-item">- CRM</p>}
</> </>
)} )}
</div> </div>
</div> </div>
<div <div className="modal-footer">
style={{
display: "flex",
justifyContent: "flex-end",
padding: "15px 20px",
borderTop: "1px solid #ddd",
}}
>
<button <button
onClick={handleModalClose} onClick={handleModalClose}
style={{ className="modal-confirm-btn"
backgroundColor: "#1e3a8a",
color: "#fff",
border: "none",
padding: "8px 20px",
borderRadius: "6px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
> >
Fechar Fechar
</button> </button>
@ -363,48 +292,35 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
)} )}
<div className="card p-3 shadow-sm"> <div className="card doctor-form-container shadow-sm">
<h3 className="mb-4 text-center" style={{ fontSize: '2.5rem' }}>MediConnect</h3> <h3 className="doctor-form-title">MediConnect</h3>
<div className="mb-5 p-4 border rounded shadow-sm"> {/* DADOS PESSOAIS */}
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" <div className="form-section">
onClick={() => handleToggleCollapse('dadosPessoais')} <h4 className="section-header" onClick={() => handleToggleCollapse('dadosPessoais')}>
style={{ fontSize: '1.8rem' }}>
Dados Pessoais Dados Pessoais
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.dadosPessoais ? '▲' : '▼'} {collapsedSections.dadosPessoais ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.dadosPessoais ? ' show' : ''}`}> <div className={`collapse${collapsedSections.dadosPessoais ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-6 mb-3 d-flex align-items-center"> <div className="col-md-6 mb-3 avatar-container">
<div className="me-3"> <div className="me-3">
{avatarUrl ? ( {avatarUrl ? (
<img <img
src={avatarUrl} src={avatarUrl}
alt="Avatar do Médico" alt="Avatar do Médico"
style={{ width: '100px', height: '100px', borderRadius: '50%', objectFit: 'cover' }} className="avatar-image"
/> />
) : ( ) : (
<div <div className="avatar-placeholder">
style={{
width: '100px',
height: '100px',
borderRadius: '50%',
backgroundColor: '#e0e0e0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '3.5rem',
color: '#9e9e9e'
}}
>
&#x2624; &#x2624;
</div> </div>
)} )}
</div> </div>
<div> <div>
<label htmlFor="foto-input" className="btn btn-primary" style={{ fontSize: '1rem' }}>Carregar Foto</label> <label htmlFor="foto-input" className="btn btn-primary file-input-label">Carregar Foto</label>
<input <input
type="file" type="file"
className="form-control d-none" className="form-control d-none"
@ -413,31 +329,31 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
accept="image/*" accept="image/*"
/> />
{formData.foto && <span className="ms-2" style={{ fontSize: '1rem' }}>{formData.foto.name}</span>} {formData.foto && <span className="ms-2 form-label">{formData.foto.name}</span>}
</div> </div>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome: *</label> <label className="form-label">Nome: *</label>
<input <input
ref={nomeRef} ref={nomeRef}
type="text" type="text"
className="form-control" className="form-control form-control-custom"
name="full_name" name="full_name"
value={formData.full_name || ''} value={formData.full_name || ''}
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Data de nascimento:</label> <label className="form-label">Data de nascimento:</label>
<input type="date" className="form-control" name="birth_date" value={formData.birth_date || ''} onChange={handleChange} min="1900-01-01" max="2025-09-24" /> <input type="date" className="form-control form-control-custom" name="birth_date" value={formData.birth_date || ''} onChange={handleChange} min="1900-01-01" max="2025-09-24" />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CPF: *</label> <label className="form-label">CPF: *</label>
<input <input
ref={cpfRef} ref={cpfRef}
type="text" type="text"
className={`form-control ${cpfError ? 'is-invalid' : ''}`} className={`form-control form-control-custom ${cpfError ? 'is-invalid' : ''}`}
name="cpf" name="cpf"
value={formData.cpf || ''} value={formData.cpf || ''}
onChange={handleChange} onChange={handleChange}
@ -450,10 +366,10 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado do CRM: *</label> <label className="form-label">Estado do CRM: *</label>
<select <select
ref={crmUfRef} ref={crmUfRef}
className="form-control" className="form-control form-control-custom"
name="crm_uf" name="crm_uf"
value={formData.crm_uf || ''} value={formData.crm_uf || ''}
onChange={handleChange} onChange={handleChange}
@ -489,11 +405,11 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CRM: *</label> <label className="form-label">CRM: *</label>
<input <input
ref={crmRef} ref={crmRef}
type="text" type="text"
className="form-control" className="form-control form-control-custom"
name="crm" name="crm"
value={formData.crm || ''} value={formData.crm || ''}
onChange={handleChange} onChange={handleChange}
@ -501,8 +417,8 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Especialização:</label> <label className="form-label">Especialização:</label>
<select className="form-control" name="specialty" value={formData.specialty || ''} onChange={handleChange}> <select className="form-control form-control-custom" name="specialty" value={formData.specialty || ''} onChange={handleChange}>
<option value="">Selecione</option> <option value="">Selecione</option>
<option value="Clínica Geral">Clínica médica (clínico geral)</option> <option value="Clínica Geral">Clínica médica (clínico geral)</option>
<option value="Pediatria">Pediatria</option> <option value="Pediatria">Pediatria</option>
@ -523,105 +439,100 @@ function DoctorForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
</div> </div>
<div className="mb-5 p-4 border rounded shadow-sm"> {/* CONTATO */}
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" <div className="form-section">
onClick={() => handleToggleCollapse('contato')} <h4 className="section-header" onClick={() => handleToggleCollapse('contato')}>
style={{ fontSize: '1.8rem' }}>
Contato Contato
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.contato ? '▲' : '▼'} {collapsedSections.contato ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.contato ? ' show' : ''}`}> <div className={`collapse${collapsedSections.contato ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Email: *</label> <label className="form-label">Email: *</label>
<input <input
ref={emailRef} ref={emailRef}
type="email" type="email"
className="form-control" className="form-control form-control-custom"
name="email" name="email"
value={formData.email || ''} value={formData.email || ''}
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone: *</label> <label className="form-label">Telefone: *</label>
<input <input
ref={telefoneRef} ref={telefoneRef}
type="text" type="text"
className="form-control" className="form-control form-control-custom"
name="phone_mobile" name="phone_mobile"
value={formData.phone_mobile || ''} value={formData.phone_mobile || ''}
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone 2:</label> <label className="form-label">Telefone 2:</label>
<input type="text" className="form-control" name="phone2" value={formData.phone2 || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="phone2" value={formData.phone2 || ''} onChange={handleChange} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="mb-5 p-4 border rounded shadow-sm"> {/* ENDEREÇO */}
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" <div className="form-section">
onClick={() => handleToggleCollapse('endereco')} <h4 className="section-header" onClick={() => handleToggleCollapse('endereco')}>
style={{ fontSize: '1.8rem' }}>
Endereço Endereço
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.endereco ? '▲' : '▼'} {collapsedSections.endereco ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.endereco ? ' show' : ''}`}> <div className={`collapse${collapsedSections.endereco ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label>CEP:</label> <label className="form-label">CEP:</label>
<input type="text" className="form-control" name="cep" value={formData.cep || ''} onChange={handleChange} onBlur={handleCepBlur} /> <input type="text" className="form-control form-control-custom" name="cep" value={formData.cep || ''} onChange={handleChange} onBlur={handleCepBlur} />
</div> </div>
<div className="col-md-8 mb-3"> <div className="col-md-8 mb-3">
<label>Rua:</label> <label className="form-label">Rua:</label>
<input type="text" className="form-control" name="street" value={formData.street || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="street" value={formData.street || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label>Bairro:</label> <label className="form-label">Bairro:</label>
<input type="text" className="form-control" name="neighborhood" value={formData.neighborhood || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="neighborhood" value={formData.neighborhood || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label>Cidade:</label> <label className="form-label">Cidade:</label>
<input type="text" className="form-control" name="city" value={formData.city || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="city" value={formData.city || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-2 mb-3"> <div className="col-md-2 mb-3">
<label>Estado:</label> <label className="form-label">Estado:</label>
<input type="text" className="form-control" name="state" value={formData.state || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="state" value={formData.state || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label>Número:</label> <label className="form-label">Número:</label>
<input type="text" className="form-control" name="number" value={formData.number || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="number" value={formData.number || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-8 mb-3"> <div className="col-md-8 mb-3">
<label>Complemento:</label> <label className="form-label">Complemento:</label>
<input type="text" className="form-control" name="complement" value={formData.complement || ''} onChange={handleChange} /> <input type="text" className="form-control form-control-custom" name="complement" value={formData.complement || ''} onChange={handleChange} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="mt-3 text-center"> {/* BOTÕES DE AÇÃO */}
<div className="actions-container">
<button <button
className="btn btn-success me-3" className="btn btn-success btn-submit"
onClick={handleSubmit} onClick={handleSubmit}
disabled={isLoading} disabled={isLoading}
style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}
> >
{isLoading ? 'Salvando...' : 'Salvar Médico'} {isLoading ? 'Salvando...' : 'Salvar Médico'}
</button> </button>
<Link to={`/${location.pathname.split("/")[1]}/medicos`}> <Link to={`/${location.pathname.split("/")[1]}/medicos`}>
<button <button className="btn btn-light btn-cancel">
className="btn btn-light"
style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}
>
Cancelar Cancelar
</button> </button>
</Link> </Link>

View File

@ -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;
}
}

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { FormatTelefones, FormatPeso, FormatCPF } from '../utils/Formatar/Format'; import { FormatTelefones, FormatPeso, FormatCPF } from '../utils/Formatar/Format';
import './PatientForm.css';
function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) { function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
const [avatarUrl, setAvatarUrl] = useState(null); const [avatarUrl, setAvatarUrl] = useState(null);
@ -20,17 +21,13 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
const emailRef = useRef(null); const emailRef = useRef(null);
const telefoneRef = useRef(null); const telefoneRef = useRef(null);
const validarCPF = (cpf) => { const validarCPF = (cpf) => {
const cpfLimpo = cpf.replace(/\D/g, ''); const cpfLimpo = cpf.replace(/\D/g, '');
if (cpfLimpo.length !== 11) return false; if (cpfLimpo.length !== 11) return false;
if (/^(\d)\1+$/.test(cpfLimpo)) return false; if (/^(\d)\1+$/.test(cpfLimpo)) return false;
let soma = 0; let soma = 0;
for (let i = 0; i < 9; i++) { for (let i = 0; i < 9; i++) {
soma += parseInt(cpfLimpo.charAt(i)) * (10 - 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 resto = 11 - (soma % 11);
let digito1 = resto === 10 || resto === 11 ? 0 : resto; let digito1 = resto === 10 || resto === 11 ? 0 : resto;
soma = 0; soma = 0;
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
soma += parseInt(cpfLimpo.charAt(i)) * (11 - i); soma += parseInt(cpfLimpo.charAt(i)) * (11 - i);
@ -46,7 +42,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
resto = 11 - (soma % 11); resto = 11 - (soma % 11);
let digito2 = resto === 10 || resto === 11 ? 0 : resto; let digito2 = resto === 10 || resto === 11 ? 0 : resto;
return digito1 === parseInt(cpfLimpo.charAt(9)) && digito2 === parseInt(cpfLimpo.charAt(10)); 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 handleChange = (e) => {
const { name, value, type, checked, files } = e.target; const { name, value, type, checked, files } = e.target;
if (value && emptyFields.includes(name)) { if (value && emptyFields.includes(name)) {
setEmptyFields(prev => prev.filter(field => field !== name)); setEmptyFields(prev => prev.filter(field => field !== name));
} }
if (name === 'cpf' && cpfError) { if (name === 'cpf' && cpfError) {
setCpfError(''); setCpfError('');
} }
@ -120,7 +113,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
switch (fieldName) { switch (fieldName) {
case 'full_name': case 'full_name':
fieldRef = nomeRef; fieldRef = nomeRef;
setCollapsedSections(prev => ({ ...prev, dadosPessoais: true })); setCollapsedSections(prev => ({ ...prev, dadosPessoais: true }));
break; break;
case 'cpf': case 'cpf':
@ -139,7 +131,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return; return;
} }
setTimeout(() => { setTimeout(() => {
if (fieldRef.current) { if (fieldRef.current) {
fieldRef.current.scrollIntoView({ fieldRef.current.scrollIntoView({
@ -148,11 +139,9 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}); });
fieldRef.current.focus(); fieldRef.current.focus();
fieldRef.current.style.border = '2px solid #dc3545'; fieldRef.current.style.border = '2px solid #dc3545';
fieldRef.current.style.boxShadow = '0 0 0 0.2rem rgba(220, 53, 69, 0.25)'; fieldRef.current.style.boxShadow = '0 0 0 0.2rem rgba(220, 53, 69, 0.25)';
setTimeout(() => { setTimeout(() => {
if (fieldRef.current) { if (fieldRef.current) {
fieldRef.current.style.border = ''; fieldRef.current.style.border = '';
@ -164,7 +153,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
const missingFields = []; const missingFields = [];
if (!formData.full_name) missingFields.push('full_name'); if (!formData.full_name) missingFields.push('full_name');
if (!formData.cpf) missingFields.push('cpf'); if (!formData.cpf) missingFields.push('cpf');
@ -175,17 +163,14 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
setEmptyFields(missingFields); setEmptyFields(missingFields);
setShowRequiredModal(true); setShowRequiredModal(true);
setTimeout(() => { setTimeout(() => {
if (missingFields.length > 0) { if (missingFields.length > 0) {
scrollToEmptyField(missingFields[0]); scrollToEmptyField(missingFields[0]);
} }
}, 500); }, 500);
return; return;
} }
const cpfLimpo = formData.cpf.replace(/\D/g, ''); const cpfLimpo = formData.cpf.replace(/\D/g, '');
if (cpfLimpo.length !== 11) { if (cpfLimpo.length !== 11) {
setShowRequiredModal(true); setShowRequiredModal(true);
@ -195,7 +180,6 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
return; return;
} }
if (!validarCPF(formData.cpf)) { if (!validarCPF(formData.cpf)) {
setShowRequiredModal(true); setShowRequiredModal(true);
setEmptyFields(['cpf']); setEmptyFields(['cpf']);
@ -205,7 +189,9 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
} }
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 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 }); await onSave({ ...formData, bmi: parseFloat(formData.bmi) || null });
}; };
@ -215,48 +201,36 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
}; };
return ( return (
<div className="card p-3"> <div className="card patient-form-container">
<h3 className="mb-4 text-center" style={{ fontSize: '2.5rem' }}>MediConnect</h3> <h3 className="patient-form-title">MediConnect</h3>
{/* DADOS PESSOAIS */} {/* DADOS PESSOAIS */}
<div className="mb-5 p-4 border rounded shadow-sm"> <div className="form-section">
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('dadosPessoais')} style={{ fontSize: '1.8rem' }}> <h4 className="section-header" onClick={() => handleToggleCollapse('dadosPessoais')}>
Dados Pessoais Dados Pessoais
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.dadosPessoais ? '▲' : '▼'} {collapsedSections.dadosPessoais ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.dadosPessoais ? ' show' : ''}`}> <div className={`collapse${collapsedSections.dadosPessoais ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
{/* AVATAR E INPUT DE FOTO */} {/* AVATAR E INPUT DE FOTO */}
<div className="col-md-6 mb-3 d-flex align-items-center"> <div className="col-md-6 mb-3 avatar-container">
<div className="me-3"> <div className="me-3">
{avatarUrl ? ( {avatarUrl ? (
<img <img
src={avatarUrl} src={avatarUrl}
alt="Avatar do Paciente" alt="Avatar do Paciente"
style={{ width: '100px', height: '100px', borderRadius: '50%', objectFit: 'cover' }} className="avatar-image"
/> />
) : ( ) : (
<div <div className="avatar-placeholder">
style={{
width: '100px',
height: '100px',
borderRadius: '50%',
backgroundColor: '#e0e0e0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '3.5rem',
color: '#9e9e9e'
}}
>
&#x2624; &#x2624;
</div> </div>
)} )}
</div> </div>
<div> <div>
<label htmlFor="foto-input" className="btn btn-primary" style={{ fontSize: '1rem' }}>Carregar Foto</label> <label htmlFor="foto-input" className="btn btn-primary file-input-label">Carregar Foto</label>
<input <input
type="file" type="file"
className="form-control d-none" className="form-control d-none"
@ -265,48 +239,45 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
onChange={handleChange} onChange={handleChange}
accept="image/*" accept="image/*"
/> />
{formData.foto && <span className="ms-2" style={{ fontSize: '1rem' }}>{formData.foto.name}</span>} {formData.foto && <span className="ms-2 form-label">{formData.foto.name}</span>}
</div> </div>
</div> </div>
{/* CAMPOS OBRIGATÓRIOS */} {/* CAMPOS OBRIGATÓRIOS */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome: *</label> <label className="form-label">Nome: *</label>
<input <input
ref={nomeRef} ref={nomeRef}
type="text" type="text"
className="form-control" className="form-control form-control-custom"
name="full_name" name="full_name"
value={formData.full_name || ''} value={formData.full_name || ''}
onChange={handleChange} onChange={handleChange}
style={{ fontSize: '1.1rem' }}
required required
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome social:</label> <label className="form-label">Nome social:</label>
<input type="text" className="form-control" name="social_name" value={formData.social_name || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="social_name" value={formData.social_name || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Data de nascimento:</label> <label className="form-label">Data de nascimento:</label>
<input <input
type="date" type="date"
className="form-control" className="form-control form-control-custom"
name="birth_date" name="birth_date"
value={formData.birth_date || ''} value={formData.birth_date || ''}
onChange={handleChange} onChange={handleChange}
style={{ fontSize: '1.1rem' }}
min="1900-01-01" max="2025-09-24" min="1900-01-01" max="2025-09-24"
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Gênero:</label> <label className="form-label">Gênero:</label>
<select <select
className="form-control" className="form-control form-control-custom"
name="sex" name="sex"
value={formData.sex || ''} value={formData.sex || ''}
onChange={handleChange} onChange={handleChange}
style={{ fontSize: '1.1rem' }}
> >
<option value="">Selecione</option> <option value="">Selecione</option>
<option value="Masculino">Masculino</option> <option value="Masculino">Masculino</option>
@ -315,15 +286,14 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CPF: *</label> <label className="form-label">CPF: *</label>
<input <input
ref={cpfRef} ref={cpfRef}
type="text" type="text"
className={`form-control ${cpfError ? 'is-invalid' : ''}`} className={`form-control form-control-custom ${cpfError ? 'is-invalid' : ''}`}
name="cpf" name="cpf"
value={formData.cpf || ''} value={formData.cpf || ''}
onChange={handleChange} onChange={handleChange}
style={{ fontSize: '1.1rem' }}
required required
/> />
{cpfError && ( {cpfError && (
@ -333,12 +303,12 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
)} )}
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>RG:</label> <label className="form-label">RG:</label>
<input type="text" className="form-control" name="rg" value={formData.rg || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="rg" value={formData.rg || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Outros documentos:</label> <label className="form-label">Outros documentos:</label>
<select className="form-control" name="document_type" value={formData.document_type || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }}> <select className="form-control form-control-custom" name="document_type" value={formData.document_type || ''} onChange={handleChange}>
<option value="">Selecione</option> <option value="">Selecione</option>
<option value="CNH">CNH</option> <option value="CNH">CNH</option>
<option value="Passaporte">Passaporte</option> <option value="Passaporte">Passaporte</option>
@ -346,12 +316,12 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Número do documento:</label> <label className="form-label">Número do documento:</label>
<input type="text" className="form-control" name="document_number" value={formData.document_number || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="document_number" value={formData.document_number || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Etnia e Raça:</label> <label className="form-label">Etnia e Raça:</label>
<select className="form-control" name="race" value={formData.race || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }}> <select className="form-control form-control-custom" name="race" value={formData.race || ''} onChange={handleChange}>
<option value="">Selecione</option> <option value="">Selecione</option>
<option value="Branca">Branca</option> <option value="Branca">Branca</option>
<option value="Preta">Preta</option> <option value="Preta">Preta</option>
@ -361,20 +331,20 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Naturalidade:</label> <label className="form-label">Naturalidade:</label>
<input type="text" className="form-control" name="naturality" value={formData.naturalidade || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="naturality" value={formData.naturalidade || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nacionalidade:</label> <label className="form-label">Nacionalidade:</label>
<input type="text" className="form-control" name="nationality" value={formData.nationality || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="nationality" value={formData.nationality || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Profissão:</label> <label className="form-label">Profissão:</label>
<input type="text" className="form-control" name="profession" value={formData.profession || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="profession" value={formData.profession || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado civil:</label> <label className="form-label">Estado civil:</label>
<select className="form-control" name="marital_status" value={formData.marital_status || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }}> <select className="form-control form-control-custom" name="marital_status" value={formData.marital_status || ''} onChange={handleChange}>
<option value="" disabled>Selecione</option> <option value="" disabled>Selecione</option>
<option value="Solteiro">Solteiro(a)</option> <option value="Solteiro">Solteiro(a)</option>
<option value="Casado">Casado(a)</option> <option value="Casado">Casado(a)</option>
@ -383,37 +353,37 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome da Mãe:</label> <label className="form-label">Nome da Mãe:</label>
<input type="text" className="form-control" name="mother_name" value={formData.mother_name || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="mother_name" value={formData.mother_name || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Profissão da mãe:</label> <label className="form-label">Profissão da mãe:</label>
<input type="text" className="form-control" name="mother_profession" value={formData.mother_profession || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="mother_profession" value={formData.mother_profession || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome do Pai:</label> <label className="form-label">Nome do Pai:</label>
<input type="text" className="form-control" name="father_name" value={formData.father_name || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="father_name" value={formData.father_name || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Profissão do pai:</label> <label className="form-label">Profissão do pai:</label>
<input type="text" className="form-control" name="father_profession" value={formData.father_profession || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="father_profession" value={formData.father_profession || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome do responsável:</label> <label className="form-label">Nome do responsável:</label>
<input type="text" className="form-control" name="guardian_name" value={formData.guardian_name || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="guardian_name" value={formData.guardian_name || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CPF do responsável:</label> <label className="form-label">CPF do responsável:</label>
<input type="text" className="form-control" name="guardian_cpf" value={formData.guardian_cpf || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="guardian_cpf" value={formData.guardian_cpf || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Identificador de outro sistema:</label> <label className="form-label">Identificador de outro sistema:</label>
<input type="text" className="form-control" name="legacy_code" value={formData.legacy_code || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="legacy_code" value={formData.legacy_code || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-12 mb-3"> <div className="col-md-12 mb-3">
<div className="form-check"> <div className="form-check">
<input className="form-check-input" type="checkbox" name="rn_in_insurance" checked={formData.rn_in_insurance || false} onChange={handleChange} id="rn_in_insurance" style={{ transform: 'scale(1.2)' }} /> <input className="form-check-input checkbox-custom" type="checkbox" name="rn_in_insurance" checked={formData.rn_in_insurance || false} onChange={handleChange} id="rn_in_insurance" />
<label className="form-check-label ms-2" htmlFor="rn_in_insurance" style={{ fontSize: '1.1rem' }}> <label className="form-check-label checkbox-label" htmlFor="rn_in_insurance">
RN na Guia do convênio RN na Guia do convênio
</label> </label>
</div> </div>
@ -421,35 +391,34 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
{/* CAMPOS ADICIONAIS */} {/* CAMPOS ADICIONAIS */}
<div className="col-md-12 mb-3 mt-3"> <div className="col-md-12 mb-3 mt-3">
<label style={{ fontSize: '1.1rem' }}>Observações:</label> <label className="form-label">Observações:</label>
<textarea className="form-control" name="notes" value={formData.notes || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} placeholder='alergias, doenças crônicas, informações sobre porteses ou marca-passo, etc'></textarea> <textarea className="form-control textarea-custom" name="notes" value={formData.notes || ''} onChange={handleChange} placeholder='alergias, doenças crônicas, informações sobre porteses ou marca-passo, etc'></textarea>
</div> </div>
<div className="col-md-12 mb-3"> <div className="col-md-12 mb-3">
<label style={{ fontSize: '1.1rem' }}>Anexos do Paciente:</label> <label className="form-label">Anexos do Paciente:</label>
<div> <div>
<label htmlFor="anexos-input" className="btn btn-secondary" style={{ fontSize: '1.1rem' }}>Escolher arquivo</label> <label htmlFor="anexos-input" className="btn btn-secondary file-input-label">Escolher arquivo</label>
<input type="file" className="form-control d-none" name="anexos" id="anexos-input" onChange={handleChange} /> <input type="file" className="form-control d-none" name="anexos" id="anexos-input" onChange={handleChange} />
<span className="ms-2" style={{ fontSize: '1.1rem' }}>{formData.anexos ? formData.anexos.name : 'Nenhum arquivo escolhido'}</span> <span className="ms-2 form-label">{formData.anexos ? formData.anexos.name : 'Nenhum arquivo escolhido'}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* INFORMAÇÕES MÉDICAS */} {/* INFORMAÇÕES MÉDICAS */}
<div className="mb-5 p-4 border rounded shadow-sm"> <div className="form-section">
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('infoMedicas')} style={{ fontSize: '1.8rem' }}> <h4 className="section-header" onClick={() => handleToggleCollapse('infoMedicas')}>
Informações Médicas Informações Médicas
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.infoMedicas ? '▲' : '▼'} {collapsedSections.infoMedicas ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.infoMedicas ? ' show' : ''}`}> <div className={`collapse${collapsedSections.infoMedicas ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Tipo Sanguíneo:</label> <label className="form-label">Tipo Sanguíneo:</label>
<select className="form-control" name="blood_type" value={formData.blood_type || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }}> <select className="form-control form-control-custom" name="blood_type" value={formData.blood_type || ''} onChange={handleChange}>
<option value="">Selecione</option> <option value="">Selecione</option>
<option value="A+">A+</option> <option value="A+">A+</option>
<option value="A-">A-</option> <option value="A-">A-</option>
@ -462,35 +431,34 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
<div className="col-md-2 mb-3"> <div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>Peso (kg):</label> <label className="form-label">Peso (kg):</label>
<input type="text" step="0.1" className="form-control" name="weight_kg" value={formData.weight_kg || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" step="0.1" className="form-control form-control-custom" name="weight_kg" value={formData.weight_kg || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-2 mb-3"> <div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>Altura (m):</label> <label className="form-label">Altura (m):</label>
<input type="text" step="0.01" className="form-control" name="height_m" value={formData.height_m || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" step="0.01" className="form-control form-control-custom" name="height_m" value={formData.height_m || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-2 mb-3"> <div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>IMC (kg/):</label> <label className="form-label">IMC (kg/):</label>
<input type="text" className="form-control" name="bmi" value={formData.bmi || ''} readOnly disabled style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="bmi" value={formData.bmi || ''} readOnly disabled />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* INFORMAÇÕES DE CONVÊNIO */} {/* INFORMAÇÕES DE CONVÊNIO */}
<div className="mb-5 p-4 border rounded shadow-sm"> <div className="form-section">
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('infoConvenio')} style={{ fontSize: '1.8rem' }}> <h4 className="section-header" onClick={() => handleToggleCollapse('infoConvenio')}>
Informações de convênio Informações de convênio
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.infoConvenio ? '▲' : '▼'} {collapsedSections.infoConvenio ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.infoConvenio ? ' show' : ''}`}> <div className={`collapse${collapsedSections.infoConvenio ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Convênio:</label> <label className="form-label">Convênio:</label>
<select className="form-control" name="convenio" value={formData.convenio || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }}> <select className="form-control form-control-custom" name="convenio" value={formData.convenio || ''} onChange={handleChange}>
<option value="">Selecione</option> <option value="">Selecione</option>
<option value="Amil">Amil</option> <option value="Amil">Amil</option>
<option value="Bradesco Saúde">Bradesco Saúde</option> <option value="Bradesco Saúde">Bradesco Saúde</option>
@ -499,21 +467,21 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</select> </select>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Plano:</label> <label className="form-label">Plano:</label>
<input type="text" className="form-control" name="plano" value={formData.plano || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="plano" value={formData.plano || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}> de matrícula:</label> <label className="form-label"> de matrícula:</label>
<input type="text" className="form-control" name="numeroMatricula" value={formData.numeroMatricula || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="numeroMatricula" value={formData.numeroMatricula || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Validade da Carteira:</label> <label className="form-label">Validade da Carteira:</label>
<input type="date" className="form-control" name="validadeCarteira" value={formData.validadeCarteira || ''} onChange={handleChange} disabled={formData.validadeIndeterminada} style={{ fontSize: '1.1rem' }} /> <input type="date" className="form-control form-control-custom" name="validadeCarteira" value={formData.validadeCarteira || ''} onChange={handleChange} disabled={formData.validadeIndeterminada} />
</div> </div>
<div className="col-md-2 d-flex align-items-end mb-3"> <div className="col-md-2 d-flex align-items-end mb-3">
<div className="form-check"> <div className="form-check">
<input className="form-check-input" type="checkbox" name="validadeIndeterminada" checked={formData.validadeIndeterminada || false} onChange={handleChange} id="validadeIndeterminada" style={{ transform: 'scale(1.2)' }} /> <input className="form-check-input checkbox-custom" type="checkbox" name="validadeIndeterminada" checked={formData.validadeIndeterminada || false} onChange={handleChange} id="validadeIndeterminada" />
<label className="form-check-label ms-2" htmlFor="validadeIndeterminada" style={{ fontSize: '1.1rem' }}> <label className="form-check-label checkbox-label" htmlFor="validadeIndeterminada">
Validade indeterminada Validade indeterminada
</label> </label>
</div> </div>
@ -521,8 +489,8 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
{/* PACIENTE VIP */} {/* PACIENTE VIP */}
<div className="col-md-12 mb-3 mt-3"> <div className="col-md-12 mb-3 mt-3">
<div className="form-check"> <div className="form-check">
<input className="form-check-input" type="checkbox" name="vip" checked={formData.vip || false} onChange={handleChange} id="vip" style={{ transform: 'scale(1.2)' }} /> <input className="form-check-input checkbox-custom" type="checkbox" name="vip" checked={formData.vip || false} onChange={handleChange} id="vip" />
<label className="form-check-label ms-2" htmlFor="vip" style={{ fontSize: '1.1rem' }}> <label className="form-check-label checkbox-label" htmlFor="vip">
Paciente VIP Paciente VIP
</label> </label>
</div> </div>
@ -532,185 +500,129 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
{/* ENDEREÇO */} {/* ENDEREÇO */}
<div className="mb-5 p-4 border rounded shadow-sm"> <div className="form-section">
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('endereco')} style={{ fontSize: '1.8rem' }}> <h4 className="section-header" onClick={() => handleToggleCollapse('endereco')}>
Endereço Endereço
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.endereco ? '▲' : '▼'} {collapsedSections.endereco ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.endereco ? ' show' : ''}`}> <div className={`collapse${collapsedSections.endereco ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>CEP:</label> <label className="form-label">CEP:</label>
<input type="text" className="form-control" name="cep" value={formData.cep || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="cep" value={formData.cep || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-8 mb-3"> <div className="col-md-8 mb-3">
<label style={{ fontSize: '1.1rem' }}>Rua:</label> <label className="form-label">Rua:</label>
<input type="text" className="form-control" name="street" value={formData.street || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="street" value={formData.street || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Bairro:</label> <label className="form-label">Bairro:</label>
<input type="text" className="form-control" name="neighborhood" value={formData.neighborhood || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="neighborhood" value={formData.neighborhood || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Cidade:</label> <label className="form-label">Cidade:</label>
<input type="text" className="form-control" name="city" value={formData.city || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="city" value={formData.city || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-2 mb-3"> <div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado:</label> <label className="form-label">Estado:</label>
<input type="text" className="form-control" name="state" value={formData.state || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="state" value={formData.state || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Número:</label> <label className="form-label">Número:</label>
<input type="text" className="form-control" name="number" value={formData.number || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="number" value={formData.number || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-8 mb-3"> <div className="col-md-8 mb-3">
<label style={{ fontSize: '1.1rem' }}>Complemento:</label> <label className="form-label">Complemento:</label>
<input type="text" className="form-control" name="complement" value={formData.complement || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="complement" value={formData.complement || ''} onChange={handleChange} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="mb-5 p-4 border rounded shadow-sm"> {/* CONTATO */}
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('contato')} style={{ fontSize: '1.8rem' }}> <div className="form-section">
<h4 className="section-header" onClick={() => handleToggleCollapse('contato')}>
Contato Contato
<span className="fs-5"> <span className="section-toggle">
{collapsedSections.contato ? '▲' : '▼'} {collapsedSections.contato ? '▲' : '▼'}
</span> </span>
</h4> </h4>
<div className={`collapse${collapsedSections.contato ? ' show' : ''}`}> <div className={`collapse${collapsedSections.contato ? ' show' : ''}`}>
<div className="row mt-3"> <div className="row mt-3">
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Email: *</label> <label className="form-label">Email: *</label>
<input <input
ref={emailRef} ref={emailRef}
type="email" type="email"
className="form-control" className="form-control form-control-custom"
name="email" name="email"
value={formData.email || ''} value={formData.email || ''}
onChange={handleChange} onChange={handleChange}
style={{ fontSize: '1.1rem' }}
required required
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone: *</label> <label className="form-label">Telefone: *</label>
<input <input
ref={telefoneRef} ref={telefoneRef}
type="text" type="text"
className="form-control" className="form-control form-control-custom"
name="phone_mobile" name="phone_mobile"
value={formData.phone_mobile || ''} value={formData.phone_mobile || ''}
onChange={handleChange} onChange={handleChange}
style={{ fontSize: '1.1rem' }}
required required
/> />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone 2:</label> <label className="form-label">Telefone 2:</label>
<input type="text" className="form-control" name="phone1" value={formData.phone1 || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="phone1" value={formData.phone1 || ''} onChange={handleChange} />
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone 3:</label> <label className="form-label">Telefone 3:</label>
<input type="text" className="form-control" name="phone2" value={formData.phone2 || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} /> <input type="text" className="form-control form-control-custom" name="phone2" value={formData.phone2 || ''} onChange={handleChange} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* MODAL DE VALIDAÇÃO */}
{showRequiredModal && ( {showRequiredModal && (
<div <div className="modal-overlay">
style={{ <div className="modal-content">
display: "flex", <div className="modal-header">
justifyContent: "center", <h5 className="modal-title">Atenção</h5>
alignItems: "center",
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 9999,
}}
>
<div
style={{
backgroundColor: "#fff",
borderRadius: "10px",
width: "400px",
maxWidth: "90%",
boxShadow: "0 5px 15px rgba(0,0,0,0.3)",
overflow: "hidden",
}}
>
<div
style={{
backgroundColor: "#1e3a8a",
padding: "15px 20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<h5 style={{ color: "#fff", margin: 0, fontSize: "1.2rem", fontWeight: "bold" }}>Atenção</h5>
<button <button
onClick={handleModalClose} onClick={handleModalClose}
style={{ className="modal-close-btn"
background: "none",
border: "none",
fontSize: "20px",
color: "#fff",
cursor: "pointer",
}}
> >
× ×
</button> </button>
</div> </div>
<div className="modal-body">
<div style={{ padding: "25px 20px" }}> <p className="modal-message">
<p style={{ color: "#111", fontSize: "1.1rem", margin: "0 0 15px 0", fontWeight: "bold" }}>
{cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'} {cpfError ? 'Problema com o CPF:' : 'Por favor, preencha:'}
</p> </p>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginLeft: '10px' }}> <div className="modal-list">
{cpfError ? ( {cpfError ? (
<p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>{cpfError}</p> <p className="modal-list-item">{cpfError}</p>
) : ( ) : (
<> <>
{!formData.full_name && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Nome</p>} {!formData.full_name && <p className="modal-list-item">- Nome</p>}
{!formData.cpf && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- CPF</p>} {!formData.cpf && <p className="modal-list-item">- CPF</p>}
{!formData.email && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Email</p>} {!formData.email && <p className="modal-list-item">- Email</p>}
{!formData.phone_mobile && <p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>- Telefone</p>} {!formData.phone_mobile && <p className="modal-list-item">- Telefone</p>}
</> </>
)} )}
</div> </div>
</div> </div>
<div className="modal-footer">
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "15px 20px",
borderTop: "1px solid #ddd",
}}
>
<button <button
onClick={handleModalClose} onClick={handleModalClose}
style={{ className="modal-confirm-btn"
backgroundColor: "#1e3a8a",
color: "#fff",
border: "none",
padding: "8px 20px",
borderRadius: "6px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
> >
Fechar Fechar
</button> </button>
@ -719,13 +631,13 @@ function PatientForm({ onSave, onCancel, formData, setFormData, isLoading }) {
</div> </div>
)} )}
{/* BOTÕES DE AÇÃO */}
<div className="mt-3 text-center"> <div className="actions-container">
<button className="btn btn-success me-3" onClick={handleSubmit} disabled={isLoading} style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}> <button className="btn btn-success btn-submit" onClick={handleSubmit} disabled={isLoading}>
{isLoading ? 'Salvando...' : 'Salvar Paciente'} {isLoading ? 'Salvando...' : 'Salvar Paciente'}
</button> </button>
<Link to='/secretaria/pacientes'> <Link to='/secretaria/pacientes'>
<button className="btn btn-light" style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}> <button className="btn btn-light btn-cancel">
Cancelar Cancelar
</button> </button>
</Link> </Link>

View File

@ -3,6 +3,7 @@ import { useAuth } from '../components/utils/AuthProvider';
import DoctorForm from '../components/doctors/DoctorForm'; import DoctorForm from '../components/doctors/DoctorForm';
import API_KEY from '../components/utils/apiKeys'; import API_KEY from '../components/utils/apiKeys';
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation } from 'react-router-dom';
import './style/DoctorCadastroManager.css';
function DoctorCadastroManager() { function DoctorCadastroManager() {
const [doctorData, setDoctorData] = useState({}); const [doctorData, setDoctorData] = useState({});
@ -65,7 +66,6 @@ function DoctorCadastroManager() {
if (!response.ok) { if (!response.ok) {
let errorMessage = `Erro ao salvar médico (${response.status})`; let errorMessage = `Erro ao salvar médico (${response.status})`;
const responseText = await response.text(); const responseText = await response.text();
console.log("Conteúdo da resposta:", responseText); console.log("Conteúdo da resposta:", responseText);
@ -75,7 +75,6 @@ function DoctorCadastroManager() {
console.error("Erro detalhado:", errorData); console.error("Erro detalhado:", errorData);
errorMessage = errorData.message || errorData.details || errorMessage; errorMessage = errorData.message || errorData.details || errorMessage;
} catch (jsonError) { } catch (jsonError) {
errorMessage = responseText || errorMessage; errorMessage = responseText || errorMessage;
} }
} else { } else {
@ -101,7 +100,6 @@ function DoctorCadastroManager() {
result = { success: true, status: response.status }; result = { success: true, status: response.status };
} }
setShowSuccessModal(true); setShowSuccessModal(true);
} catch (error) { } catch (error) {
@ -110,7 +108,7 @@ function DoctorCadastroManager() {
let userFriendlyMessage = error.message; let userFriendlyMessage = error.message;
if (error.message.includes('doctors_cpf_key') || error.message.includes('duplicate key')) { 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')) { } else if (error.message.includes('Unexpected end of JSON input')) {
userFriendlyMessage = 'Erro de comunicação com o servidor. Tente novamente.'; userFriendlyMessage = 'Erro de comunicação com o servidor. Tente novamente.';
} }
@ -124,7 +122,6 @@ function DoctorCadastroManager() {
const handleCloseSuccessModal = () => { const handleCloseSuccessModal = () => {
setShowSuccessModal(false); setShowSuccessModal(false);
const prefixo = location.pathname.split("/")[1]; const prefixo = location.pathname.split("/")[1];
navigate(`/${prefixo}/medicos`); navigate(`/${prefixo}/medicos`);
}; };
@ -135,83 +132,35 @@ function DoctorCadastroManager() {
return ( return (
<> <>
{isLoading && (
<div className="loading-overlay">
<div className="loading-spinner"></div>
</div>
)}
{showSuccessModal && ( {showSuccessModal && (
<div <div className="modal-overlay">
style={{ <div className="modal-container">
display: "flex", <div className="modal-header modal-header-success">
justifyContent: "center", <h5 className="modal-title">Sucesso</h5>
alignItems: "center",
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 9999,
}}
>
<div
style={{
backgroundColor: "#fff",
borderRadius: "10px",
width: "400px",
maxWidth: "90%",
boxShadow: "0 5px 15px rgba(0,0,0,0.3)",
overflow: "hidden",
}}
>
<div
style={{
backgroundColor: "#28a745",
padding: "15px 20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<h5 style={{ color: "#fff", margin: 0, fontSize: "1.2rem", fontWeight: "bold" }}>Sucesso</h5>
<button <button
onClick={handleCloseSuccessModal} onClick={handleCloseSuccessModal}
style={{ className="modal-close-button"
background: "none",
border: "none",
fontSize: "20px",
color: "#fff",
cursor: "pointer",
fontWeight: "bold",
}}
> >
× ×
</button> </button>
</div> </div>
<div style={{ padding: "25px 20px" }}> <div className="modal-body">
<p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}> <p className="modal-message">
Médico cadastrado com sucesso! Médico cadastrado com sucesso!
</p> </p>
</div> </div>
<div <div className="modal-footer">
style={{
display: "flex",
justifyContent: "flex-end",
padding: "15px 20px",
borderTop: "1px solid #ddd",
}}
>
<button <button
onClick={handleCloseSuccessModal} onClick={handleCloseSuccessModal}
style={{ className="modal-button modal-button-success"
backgroundColor: "#28a745",
color: "#fff",
border: "none",
padding: "8px 20px",
borderRadius: "6px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
> >
Fechar Fechar
</button> </button>
@ -220,82 +169,29 @@ function DoctorCadastroManager() {
</div> </div>
)} )}
{showErrorModal && ( {showErrorModal && (
<div <div className="modal-overlay">
style={{ <div className="modal-container">
display: "flex", <div className="modal-header modal-header-error">
justifyContent: "center", <h5 className="modal-title">Erro</h5>
alignItems: "center",
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 9999,
}}
>
<div
style={{
backgroundColor: "#fff",
borderRadius: "10px",
width: "400px",
maxWidth: "90%",
boxShadow: "0 5px 15px rgba(0,0,0,0.3)",
overflow: "hidden",
}}
>
<div
style={{
backgroundColor: "#dc3545",
padding: "15px 20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<h5 style={{ color: "#fff", margin: 0, fontSize: "1.2rem", fontWeight: "bold" }}>Erro</h5>
<button <button
onClick={handleCloseErrorModal} onClick={handleCloseErrorModal}
style={{ className="modal-close-button"
background: "none",
border: "none",
fontSize: "20px",
color: "#fff",
cursor: "pointer",
}}
> >
× ×
</button> </button>
</div> </div>
<div style={{ padding: "25px 20px" }}> <div className="modal-body">
<p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}> <p className="modal-message">
{errorMessage} {errorMessage}
</p> </p>
</div> </div>
<div <div className="modal-footer">
style={{
display: "flex",
justifyContent: "flex-end",
padding: "15px 20px",
borderTop: "1px solid #ddd",
}}
>
<button <button
onClick={handleCloseErrorModal} onClick={handleCloseErrorModal}
style={{ className="modal-button modal-button-error"
backgroundColor: "#dc3545",
color: "#fff",
border: "none",
padding: "8px 20px",
borderRadius: "6px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
> >
Fechar Fechar
</button> </button>

View File

@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
import PatientForm from '../components/patients/PatientForm'; import PatientForm from '../components/patients/PatientForm';
import API_KEY from '../components/utils/apiKeys'; import API_KEY from '../components/utils/apiKeys';
import { useAuth } from '../components/utils/AuthProvider'; import { useAuth } from '../components/utils/AuthProvider';
import './style/PatientCadastroManager.css';
function PatientCadastroManager({ setCurrentPage }) { function PatientCadastroManager({ setCurrentPage }) {
const navigate = useNavigate() const navigate = useNavigate()
@ -14,13 +15,11 @@ function PatientCadastroManager( {setCurrentPage} ) {
const { getAuthorizationHeader, isAuthenticated } = useAuth(); const { getAuthorizationHeader, isAuthenticated } = useAuth();
const [formData, setFormData] = useState({}) const [formData, setFormData] = useState({})
const validarCPF = (cpf) => { const validarCPF = (cpf) => {
cpf = cpf.replace(/\D/g, ''); cpf = cpf.replace(/\D/g, '');
if (cpf.length !== 11) return false; if (cpf.length !== 11) return false;
if (/^(\d)\1+$/.test(cpf)) return false; if (/^(\d)\1+$/.test(cpf)) return false;
let soma = 0; let soma = 0;
@ -47,24 +46,19 @@ function PatientCadastroManager( {setCurrentPage} ) {
} }
const handleSavePatient = async (patientData) => { const handleSavePatient = async (patientData) => {
console.log(' Iniciando salvamento do paciente:', patientData);
setIsLoading(true); setIsLoading(true);
try { try {
console.log(' Verificando autenticação...');
if (!isAuthenticated) { if (!isAuthenticated) {
throw new Error('Usuário não autenticado'); throw new Error('Usuário não autenticado');
} }
const authHeader = getAuthorizationHeader(); const authHeader = getAuthorizationHeader();
console.log(' Header de autorização:', authHeader ? 'Presente' : 'Faltando');
if (!authHeader) { if (!authHeader) {
throw new Error('Header de autorização não encontrado'); throw new Error('Header de autorização não encontrado');
} }
const cpfLimpo = patientData.cpf.replace(/\D/g, ''); const cpfLimpo = patientData.cpf.replace(/\D/g, '');
if (!validarCPF(cpfLimpo)) { if (!validarCPF(cpfLimpo)) {
throw new Error('CPF inválido. Por favor, verifique o número digitado.'); throw new Error('CPF inválido. Por favor, verifique o número digitado.');
@ -76,19 +70,11 @@ function PatientCadastroManager( {setCurrentPage} ) {
myHeaders.append("Authorization", authHeader); myHeaders.append("Authorization", authHeader);
myHeaders.append("Prefer", "return=representation"); 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 = { const cleanedData = {
full_name: patientData.full_name, full_name: patientData.full_name,
cpf: cpfLimpo, cpf: cpfLimpo,
email: patientData.email, email: patientData.email,
phone_mobile: patientData.phone_mobile, phone_mobile: patientData.phone_mobile,
birth_date: patientData.birth_date || null, birth_date: patientData.birth_date || null,
sex: patientData.sex === 'Masculino' ? 'M' : sex: patientData.sex === 'Masculino' ? 'M' :
patientData.sex === 'Feminino' ? 'F' : patientData.sex === 'Feminino' ? 'F' :
@ -100,16 +86,31 @@ function PatientCadastroManager( {setCurrentPage} ) {
height_m: patientData.height_m ? parseFloat(patientData.height_m) : null, height_m: patientData.height_m ? parseFloat(patientData.height_m) : null,
bmi: patientData.bmi ? parseFloat(patientData.bmi) : null, bmi: patientData.bmi ? parseFloat(patientData.bmi) : null,
notes: patientData.notes || 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) { 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'); throw new Error('Dados obrigatórios faltando: nome, CPF, email e telefone são necessários');
} }
var raw = JSON.stringify(cleanedData); var raw = JSON.stringify(cleanedData);
console.log(' Payload JSON:', raw);
var requestOptions = { var requestOptions = {
method: 'POST', method: 'POST',
@ -118,29 +119,16 @@ function PatientCadastroManager( {setCurrentPage} ) {
redirect: 'follow' redirect: 'follow'
}; };
console.log(' Fazendo requisição para API...');
const response = await fetch("https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients", requestOptions); 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; let responseData;
try { try {
responseData = await response.json(); responseData = await response.json();
console.log(' Corpo da resposta:', responseData);
} catch (jsonError) { } catch (jsonError) {
console.log(' Não foi possível parsear JSON da resposta:', jsonError);
responseData = { error: 'Resposta inválida da API' }; responseData = { error: 'Resposta inválida da API' };
} }
if (!response.ok) { if (!response.ok) {
console.error(' Erro da API - Detalhes:', {
status: response.status,
statusText: response.statusText,
data: responseData
});
let errorMessage = 'Erro ao salvar paciente'; let errorMessage = 'Erro ao salvar paciente';
if (response.status === 401) { if (response.status === 401) {
@ -148,7 +136,7 @@ function PatientCadastroManager( {setCurrentPage} ) {
} else if (response.status === 403) { } else if (response.status === 403) {
errorMessage = 'Acesso proibido. Verifique suas permissões.'; errorMessage = 'Acesso proibido. Verifique suas permissões.';
} else if (response.status === 409) { } 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) { } else if (response.status === 400) {
errorMessage = `Dados inválidos: ${responseData.details || responseData.message || 'Verifique os campos'}`; errorMessage = `Dados inválidos: ${responseData.details || responseData.message || 'Verifique os campos'}`;
} else if (response.status === 422) { } else if (response.status === 422) {
@ -162,23 +150,18 @@ function PatientCadastroManager( {setCurrentPage} ) {
throw new Error(errorMessage); throw new Error(errorMessage);
} }
console.log(' Paciente salvo com sucesso:', responseData);
setInfosModal({ setInfosModal({
title: 'Sucesso', title: 'Sucesso',
message: 'O cadastro do paciente foi realizado com sucesso.' message: 'O cadastro do paciente foi realizado com sucesso.'
}); });
setShowModal(true); setShowModal(true);
setTimeout(() => { setTimeout(() => {
setShowModal(false); setShowModal(false);
navigate('/secretaria/pacientes'); navigate('/secretaria/pacientes');
}, 2000); }, 2000);
} catch (error) { } catch (error) {
console.error(' Erro completo ao salvar paciente:', error);
setInfosModal({ setInfosModal({
title: 'Erro', title: 'Erro',
message: error.message || 'Não foi possível conectar ao servidor. Verifique sua internet e tente novamente.' message: error.message || 'Não foi possível conectar ao servidor. Verifique sua internet e tente novamente.'
@ -193,75 +176,29 @@ function PatientCadastroManager( {setCurrentPage} ) {
<> <>
<div className="page-heading"> <div className="page-heading">
{showModal && ( {showModal && (
<div <div className="modal-overlay fade-in">
style={{ <div className="modal-content slide-in">
display: "flex", <div className={`modal-header ${infosModal.title === 'Sucesso' ? 'success' : 'error'}`}>
justifyContent: "center", <h5>{infosModal.title}</h5>
alignItems: "center",
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 9999,
}}
>
<div
style={{
backgroundColor: "#fff",
borderRadius: "10px",
width: "400px",
maxWidth: "90%",
boxShadow: "0 5px 15px rgba(0,0,0,0.3)",
overflow: "hidden",
}}
>
<div
style={{
backgroundColor: infosModal.title === 'Sucesso' ? "#28a745" : "#dc3545",
padding: "15px 20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<h5 style={{ color: "#fff", margin: 0, fontSize: "1.2rem", fontWeight: "bold" }}>{infosModal.title}</h5>
<button <button
onClick={() => setShowModal(false)} onClick={() => setShowModal(false)}
style={{ className="modal-close-button"
background: "none",
border: "none",
fontSize: "20px",
color: "#fff",
cursor: "pointer",
}}
> >
× ×
</button> </button>
</div> </div>
<div className="modal-body">
<div style={{ padding: "25px 20px" }}> <p className="modal-message">
<p style={{ color: "#111", fontSize: "1.1rem", margin: 0, fontWeight: "600" }}>
{infosModal.message} {infosModal.message}
</p> </p>
{infosModal.title === 'Erro' && ( {infosModal.title === 'Erro' && (
<p style={{ color: "#666", fontSize: "0.9rem", margin: "10px 0 0 0" }}> <p className="modal-submessage">
</p> </p>
)} )}
</div> </div>
<div className="modal-footer">
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "15px 20px",
borderTop: "1px solid #ddd",
}}
>
<button <button
onClick={() => { onClick={() => {
setShowModal(false); setShowModal(false);
@ -269,16 +206,7 @@ function PatientCadastroManager( {setCurrentPage} ) {
navigate('/secretaria/pacientes'); navigate('/secretaria/pacientes');
} }
}} }}
style={{ className={`modal-confirm-button ${infosModal.title === 'Sucesso' ? 'success' : 'error'}`}
backgroundColor: "#1e3a8a",
color: "#fff",
border: "none",
padding: "8px 20px",
borderRadius: "6px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
> >
Fechar Fechar
</button> </button>

View File

@ -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;
}
}

View File

@ -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;
}