564 lines
25 KiB
HTML
564 lines
25 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Cadastro/Edição de Paciente</title>
|
|
<style>
|
|
:root{
|
|
--bg:#0b1221;--panel:#0f172a;--muted:#111827;--border:#263244;--text:#e5e7eb;--dim:#9ca3af;--accent:#22d3ee;--accent2:#60a5fa;--danger:#ef4444;--ok:#22c55e
|
|
}
|
|
*{box-sizing:border-box}
|
|
html,body{height:100%}
|
|
body{margin:0;background:linear-gradient(180deg,#08101e,var(--bg));color:var(--text);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
|
|
.container{max-width:1100px;margin:24px auto;padding:0 16px}
|
|
header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
|
|
.title{font-weight:800;font-size:clamp(18px,3.6vw,28px)}
|
|
.subtitle{color:var(--dim);font-size:14px}
|
|
.card{background:rgba(17,24,39,.7);border:1px solid var(--border);border-radius:16px;padding:16px;margin-bottom:12px;backdrop-filter:blur(6px)}
|
|
.row{display:grid;grid-template-columns:repeat(12,1fr);gap:12px}
|
|
.col-12{grid-column:span 12}.col-9{grid-column:span 9}.col-8{grid-column:span 8}.col-6{grid-column:span 6}.col-4{grid-column:span 4}.col-3{grid-column:span 3}.col-2{grid-column:span 2}
|
|
label{display:block;color:var(--dim);font-size:12px;margin:2px 0 6px}
|
|
input[type=text],input[type=date],input[type=datetime-local],input[type=number],input[type=email],input[type=tel],select,textarea{width:100%;border:1px solid var(--border);border-radius:12px;background:#0b1221;color:var(--text);padding:10px 12px}
|
|
textarea{resize:vertical}
|
|
.btn{border:none;border-radius:12px;padding:10px 14px;font-weight:700;cursor:pointer}
|
|
.btn.primary{background:linear-gradient(180deg,var(--accent),var(--accent2));color:#04121c;box-shadow:0 10px 24px rgba(34,211,238,.2)}
|
|
.btn.secondary{background:#0b1221;border:1px solid var(--border);color:var(--text)}
|
|
.btn.ghost{background:transparent;border:1px dashed var(--border);color:var(--text)}
|
|
.btn.danger{background:var(--danger);color:white}
|
|
.toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
|
|
.switch{display:inline-flex;align-items:center;gap:8px}
|
|
.hint{color:var(--dim);font-size:12px}
|
|
.thumb{width:96px;height:96px;border-radius:12px;border:1px dashed var(--border);display:flex;align-items:center;justify-content:center;overflow:hidden;background:#0b1221}
|
|
.thumb img{width:100%;height:100%;object-fit:cover}
|
|
.collapse{border-radius:16px;overflow:hidden;border:1px solid var(--border);margin-bottom:12px}
|
|
.collapse summary{list-style:none;cursor:pointer;padding:12px 16px;background:#0b1221;color:var(--text);display:flex;align-items:center;justify-content:space-between}
|
|
.collapse summary::-webkit-details-marker{display:none}
|
|
.collapse .content{padding:16px;background:rgba(17,24,39,.7)}
|
|
.tag{display:inline-flex;gap:6px;align-items:center;border:1px solid var(--border);border-radius:999px;padding:6px 10px;font-size:12px;color:var(--dim)}
|
|
.table{width:100%;border-collapse:collapse}
|
|
.table th,.table td{padding:10px;border-bottom:1px solid var(--border);text-align:left}
|
|
.right{display:flex;justify-content:flex-end;gap:8px}
|
|
.toast{position:fixed;right:16px;bottom:16px;background:#0b1221;border:1px solid var(--border);padding:12px 16px;border-radius:12px;box-shadow:0 12px 30px rgba(0,0,0,.35);display:none}
|
|
.error{color:#fecaca}
|
|
.ok{color:#bbf7d0}
|
|
|
|
@media(max-width:920px){.col-9{grid-column:span 12}.col-8{grid-column:span 12}.col-6{grid-column:span 12}.col-4{grid-column:span 12}.col-3{grid-column:span 12}.col-2{grid-column:span 12}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<div>
|
|
<div class="title">Formulário de Paciente</div>
|
|
<div class="subtitle">Cadastro e edição com validações, anexos, máscara de telefone e busca de CEP</div>
|
|
</div>
|
|
<div class="toolbar">
|
|
<button class="btn secondary" id="btnCancelar">Cancelar</button>
|
|
<button class="btn primary" id="btnSalvar">Salvar</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- DADOS PESSOAIS -->
|
|
<section class="card" aria-labelledby="secDadosPessoais">
|
|
<h2 id="secDadosPessoais" style="margin:0 0 8px">1. Dados pessoais</h2>
|
|
<div class="row">
|
|
<div class="col-3">
|
|
<label>Foto</label>
|
|
<div class="row" style="align-items:end">
|
|
<div class="thumb" id="fotoPreview" aria-label="Miniatura da foto">📷</div>
|
|
<div class="col-12" style="margin-top:8px">
|
|
<input type="file" id="fotoInput" accept="image/*" />
|
|
<div class="hint">Clique em Carregar e selecione uma imagem</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-9">
|
|
<div class="row">
|
|
<div class="col-8">
|
|
<label>Nome <span class="error">*</span></label>
|
|
<input type="text" id="nome" required />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Nome social</label>
|
|
<input type="text" id="nomeSocial" />
|
|
</div>
|
|
<div class="col-3">
|
|
<label>CPF</label>
|
|
<input type="text" id="cpf" inputmode="numeric" placeholder="000.000.000-00" />
|
|
</div>
|
|
<div class="col-3">
|
|
<label>RG</label>
|
|
<input type="text" id="rg" />
|
|
</div>
|
|
<div class="col-3">
|
|
<label>Outros documentos</label>
|
|
<select id="docTipo">
|
|
<option value="">Selecionar…</option>
|
|
<option>CNH</option>
|
|
<option>Passaporte</option>
|
|
<option>RNE</option>
|
|
<option>CRM</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-3">
|
|
<label>Número do documento</label>
|
|
<input type="text" id="docNumero" placeholder="Preencha após escolher o tipo" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Sexo</label>
|
|
<div class="switch">
|
|
<label><input type="radio" name="sexo" value="M"> Masculino</label>
|
|
<label><input type="radio" name="sexo" value="F"> Feminino</label>
|
|
<label><input type="radio" name="sexo" value="O"> Outro</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Data de nascimento</label>
|
|
<input type="date" id="nascimento" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Estado civil</label>
|
|
<select id="estadoCivil">
|
|
<option value="">Selecionar…</option>
|
|
<option>Solteiro(a)</option>
|
|
<option>Casado(a)</option>
|
|
<option>Divorciado(a)</option>
|
|
<option>Viúvo(a)</option>
|
|
<option>União estável</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Raça/Cor (IBGE)</label>
|
|
<select id="racaIbge">
|
|
<option value="">Selecionar…</option>
|
|
<option>Branca</option>
|
|
<option>Preta</option>
|
|
<option>Parda</option>
|
|
<option>Amarela</option>
|
|
<option>Indígena</option>
|
|
<option>Não declarada</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Etnia (se indígena)</label>
|
|
<input type="text" id="etniaIbge" placeholder="Povo / Etnia" disabled />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Profissão</label>
|
|
<input type="text" id="profissao" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Naturalidade (Cidade/UF)</label>
|
|
<input type="text" id="naturalidade" placeholder="Ex.: Maceió/AL" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Nacionalidade</label>
|
|
<select id="nacionalidade">
|
|
<option>Brasil</option>
|
|
<option>Argentina</option>
|
|
<option>Portugal</option>
|
|
<option>Estados Unidos</option>
|
|
<option>Outra</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Nome da mãe</label>
|
|
<input type="text" id="mae" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Profissão da mãe</label>
|
|
<input type="text" id="profMae" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Nome do pai</label>
|
|
<input type="text" id="pai" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Profissão do pai</label>
|
|
<input type="text" id="profPai" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Nome do responsável</label>
|
|
<input type="text" id="responsavel" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>CPF do responsável</label>
|
|
<input type="text" id="cpfResponsavel" placeholder="000.000.000-00" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Nome do(a) esposo(a) (opcional)</label>
|
|
<input type="text" id="conjuge" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>RN na Guia do convênio</label>
|
|
<label class="switch"><input type="checkbox" id="rnConvenio"/> <span>Sim</span></label>
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Código legado</label>
|
|
<input type="text" id="codigoLegado" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- OBSERVAÇÕES E ANEXOS -->
|
|
<details class="collapse" open>
|
|
<summary>
|
|
<span>2. Observações e anexos</span>
|
|
<span class="hint">Clique para expandir/retrair</span>
|
|
</summary>
|
|
<div class="content">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<label>Observações</label>
|
|
<textarea id="observacoes" rows="4" placeholder="Alergias, restrições, uso de medicamentos, etc."></textarea>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="toolbar" style="justify-content:space-between">
|
|
<div class="tag">📎 Anexos do paciente</div>
|
|
<div>
|
|
<input type="file" id="anexoInput" multiple />
|
|
<button class="btn secondary" id="btnAddAnexo">Adicionar</button>
|
|
</div>
|
|
</div>
|
|
<div style="overflow:auto;border:1px solid var(--border);border-radius:12px;margin-top:8px">
|
|
<table class="table" id="anexosTable" aria-label="Lista de anexos">
|
|
<thead>
|
|
<tr><th>Nome</th><th>Tamanho</th><th>Data</th><th style="width:1%"></th></tr>
|
|
</thead>
|
|
<tbody id="anexosBody"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="hint">Arquivos ficam somente no navegador (localStorage) para este exemplo.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- CONTATO -->
|
|
<section class="card" aria-labelledby="secContato">
|
|
<h2 id="secContato" style="margin:0 0 8px">3. Contato</h2>
|
|
<div class="row">
|
|
<div class="col-6">
|
|
<label>E-mail</label>
|
|
<input type="email" id="email" placeholder="nome@dominio.com" />
|
|
</div>
|
|
<div class="col-6">
|
|
<label>Celular</label>
|
|
<input type="tel" id="celular" placeholder="+55 (82) 98888-8888" />
|
|
</div>
|
|
<div class="col-6">
|
|
<label>Telefone 1</label>
|
|
<input type="tel" id="tel1" placeholder="(82) 3333-3333" />
|
|
</div>
|
|
<div class="col-6">
|
|
<label>Telefone 2</label>
|
|
<input type="tel" id="tel2" placeholder="(82) 3444-4444" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ENDEREÇO -->
|
|
<details class="collapse" open>
|
|
<summary>
|
|
<span>4. Endereço</span>
|
|
<span class="hint">Com busca de CEP automática</span>
|
|
</summary>
|
|
<div class="content">
|
|
<div class="row">
|
|
<div class="col-3">
|
|
<label>CEP</label>
|
|
<input type="text" id="cep" placeholder="00000-000" inputmode="numeric" />
|
|
</div>
|
|
<div class="col-7">
|
|
<label>Logradouro</label>
|
|
<input type="text" id="logradouro" />
|
|
</div>
|
|
<div class="col-2">
|
|
<label>Número</label>
|
|
<input type="text" id="numero" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Complemento</label>
|
|
<input type="text" id="complemento" />
|
|
</div>
|
|
<div class="col-4">
|
|
<label>Bairro</label>
|
|
<input type="text" id="bairro" />
|
|
</div>
|
|
<div class="col-3">
|
|
<label>Cidade</label>
|
|
<input type="text" id="cidade" />
|
|
</div>
|
|
<div class="col-1">
|
|
<label>UF</label>
|
|
<input type="text" id="uf" maxlength="2" />
|
|
</div>
|
|
<div class="col-12">
|
|
<label>Referência</label>
|
|
<input type="text" id="referencia" placeholder="Ponto de referência (opcional)" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<div class="right" style="margin-top:16px">
|
|
<button class="btn secondary" id="btnCancelar2">Cancelar</button>
|
|
<button class="btn primary" id="btnSalvar2">Salvar</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toast" id="toast"></div>
|
|
|
|
<script>
|
|
// ===== Utilidades =====
|
|
const fmtBytes = (n)=>{
|
|
if (n<1024) return n+" B"; if (n<1024**2) return (n/1024).toFixed(1)+" KB"; if (n<1024**3) return (n/1024**2).toFixed(1)+" MB"; return (n/1024**3).toFixed(1)+" GB";
|
|
}
|
|
const showToast=(msg,type='ok')=>{ const t=document.getElementById('toast'); t.textContent=msg; t.style.display='block'; t.className = 'toast '+type; setTimeout(()=>t.style.display='none', 2600); }
|
|
|
|
// Máscaras simples
|
|
const onlyDigits = (s)=> (s||'').replace(/\D/g,'');
|
|
const maskCPF = (s)=>{
|
|
const d=onlyDigits(s).slice(0,11);
|
|
return d.replace(/(\d{3})(\d)/,'$1.$2').replace(/(\d{3})(\d)/,'$1.$2').replace(/(\d{3})(\d{1,2})$/,'$1-$2');
|
|
}
|
|
const maskCEP = s=>{
|
|
const d=onlyDigits(s).slice(0,8);
|
|
return d.replace(/(\d{5})(\d{0,3})/,'$1-$2');
|
|
}
|
|
const maskPhoneBR = s=>{
|
|
const d=onlyDigits(s).slice(0,13); // inclui 55
|
|
// +55 (82) 98888-8888 ou (82) 3333-3333
|
|
let out = d;
|
|
if (out.startsWith('55')) out = '+'+out.slice(0,2)+' '+out.slice(2);
|
|
out = out.replace(/^(\+55)\s?(\d{0,2})/, (m,p1,ddd)=> p1 + (ddd?` (${ddd}) `:' '));
|
|
const rest = onlyDigits(out.replace(/\+55|\(|\)|\s|-/g,''));
|
|
if (rest.length<=10) return out.split(' ').length>1? out.split(' ')[0]+" ("+rest.slice(0,2)+") "+rest.slice(2,6)+(rest.length>6?"-"+rest.slice(6):'') : out;
|
|
return `+55 (${rest.slice(0,2)}) ${rest.slice(2,7)}-${rest.slice(7,11)}`;
|
|
}
|
|
const maskPhoneLocal = s=>{
|
|
const d=onlyDigits(s).slice(0,11);
|
|
if (d.length<=10) return d.replace(/(\d{2})(\d{4})(\d{0,4})/, '($1) $2-$3');
|
|
return d.replace(/(\d{2})(\d{5})(\d{0,4})/, '($1) $2-$3');
|
|
}
|
|
|
|
// Validador de CPF
|
|
function isValidCPF(v){
|
|
const d = onlyDigits(v);
|
|
if (d.length!==11) return false;
|
|
if (/^(\d)\1{10}$/.test(d)) return false; // repetidos
|
|
let sum=0; for(let i=0;i<9;i++) sum += parseInt(d[i]) * (10-i);
|
|
let r = 11 - (sum % 11); const d1 = (r>=10)?0:r;
|
|
sum=0; for(let i=0;i<10;i++) sum += parseInt(d[i]) * (11-i);
|
|
r = 11 - (sum % 11); const d2 = (r>=10)?0:r;
|
|
return d1==d[9] && d2==d[10];
|
|
}
|
|
|
|
// Elementos
|
|
const fotoInput = document.getElementById('fotoInput');
|
|
const fotoPreview = document.getElementById('fotoPreview');
|
|
const nome = document.getElementById('nome');
|
|
const nomeSocial = document.getElementById('nomeSocial');
|
|
const cpf = document.getElementById('cpf');
|
|
const rg = document.getElementById('rg');
|
|
const docTipo = document.getElementById('docTipo');
|
|
const docNumero = document.getElementById('docNumero');
|
|
const nascimento = document.getElementById('nascimento');
|
|
const racaIbge = document.getElementById('racaIbge');
|
|
const etniaIbge = document.getElementById('etniaIbge');
|
|
const profissao = document.getElementById('profissao');
|
|
const naturalidade = document.getElementById('naturalidade');
|
|
const nacionalidade = document.getElementById('nacionalidade');
|
|
const mae = document.getElementById('mae');
|
|
const profMae = document.getElementById('profMae');
|
|
const pai = document.getElementById('pai');
|
|
const profPai = document.getElementById('profPai');
|
|
const responsavel = document.getElementById('responsavel');
|
|
const cpfResponsavel = document.getElementById('cpfResponsavel');
|
|
const conjuge = document.getElementById('conjuge');
|
|
const rnConvenio = document.getElementById('rnConvenio');
|
|
const codigoLegado = document.getElementById('codigoLegado');
|
|
|
|
const observacoes = document.getElementById('observacoes');
|
|
const anexoInput = document.getElementById('anexoInput');
|
|
const btnAddAnexo = document.getElementById('btnAddAnexo');
|
|
const anexosBody = document.getElementById('anexosBody');
|
|
|
|
const email = document.getElementById('email');
|
|
const celular = document.getElementById('celular');
|
|
const tel1 = document.getElementById('tel1');
|
|
const tel2 = document.getElementById('tel2');
|
|
|
|
const cep = document.getElementById('cep');
|
|
const logradouro = document.getElementById('logradouro');
|
|
const numero = document.getElementById('numero');
|
|
const complemento = document.getElementById('complemento');
|
|
const bairro = document.getElementById('bairro');
|
|
const cidade = document.getElementById('cidade');
|
|
const uf = document.getElementById('uf');
|
|
const referencia = document.getElementById('referencia');
|
|
|
|
const btnSalvar = document.getElementById('btnSalvar');
|
|
const btnSalvar2 = document.getElementById('btnSalvar2');
|
|
const btnCancelar = document.getElementById('btnCancelar');
|
|
const btnCancelar2 = document.getElementById('btnCancelar2');
|
|
|
|
// Estado de anexos (mantido no navegador)
|
|
let ANEXOS = []; // {name,size,ts,dataURL}
|
|
|
|
// Foto upload
|
|
fotoInput.addEventListener('change', async (e)=>{
|
|
const f = e.target.files?.[0];
|
|
if (!f) return;
|
|
const url = URL.createObjectURL(f);
|
|
fotoPreview.innerHTML = `<img alt="foto" src="${url}">`;
|
|
});
|
|
|
|
// Habilita etnia apenas quando raça=Indígena
|
|
racaIbge.addEventListener('change', ()=>{
|
|
if (racaIbge.value === 'Indígena'){ etniaIbge.disabled = false; etniaIbge.placeholder = 'Informe o povo/etnia'; }
|
|
else { etniaIbge.disabled = true; etniaIbge.value=''; etniaIbge.placeholder = 'Povo / Etnia'; }
|
|
});
|
|
|
|
// Doc. número habilita apenas se tipo escolhido
|
|
docTipo.addEventListener('change', ()=>{ docNumero.disabled = !docTipo.value; if (!docTipo.value) docNumero.value=''; });
|
|
|
|
// Máscaras
|
|
const applyMask = (el, fn) => el.addEventListener('input', ()=> { const pos=el.selectionStart; const before=el.value; el.value = fn(el.value); if (document.activeElement===el && before!==el.value) el.setSelectionRange(el.value.length, el.value.length); });
|
|
applyMask(cpf, maskCPF);
|
|
applyMask(cpfResponsavel, maskCPF);
|
|
applyMask(cep, maskCEP);
|
|
applyMask(celular, maskPhoneBR);
|
|
applyMask(tel1, maskPhoneLocal);
|
|
applyMask(tel2, maskPhoneLocal);
|
|
|
|
// CEP -> ViaCEP
|
|
async function fetchCEP(v){
|
|
const d = onlyDigits(v);
|
|
if (d.length!==8) return;
|
|
try{
|
|
const res = await fetch(`https://viacep.com.br/ws/${d}/json/`);
|
|
const j = await res.json();
|
|
if (j.erro){ showToast('CEP não encontrado','error'); return; }
|
|
logradouro.value = j.logradouro||''; bairro.value=j.bairro||''; cidade.value=j.localidade||''; uf.value=j.uf||'';
|
|
}catch(err){ console.error(err); showToast('Erro ao consultar CEP','error'); }
|
|
}
|
|
cep.addEventListener('change', ()=> fetchCEP(cep.value));
|
|
|
|
// Anexos: adicionar e listar
|
|
btnAddAnexo.addEventListener('click', async ()=>{
|
|
const files = Array.from(anexoInput.files||[]);
|
|
if (!files.length){ showToast('Selecione arquivos para anexar','error'); return; }
|
|
for (const f of files){
|
|
const dataURL = await new Promise(res=>{ const r=new FileReader(); r.onload=()=>res(r.result); r.readAsDataURL(f); });
|
|
ANEXOS.push({ name:f.name, size:f.size, ts:Date.now(), dataURL });
|
|
}
|
|
anexoInput.value='';
|
|
renderAnexos();
|
|
});
|
|
|
|
function renderAnexos(){
|
|
anexosBody.innerHTML='';
|
|
ANEXOS.sort((a,b)=>b.ts-a.ts).forEach((a,idx)=>{
|
|
const tr=document.createElement('tr');
|
|
tr.innerHTML = `<td><a download="${a.name}" href="${a.dataURL}">${a.name}</a></td><td>${fmtBytes(a.size)}</td><td>${new Date(a.ts).toLocaleString('pt-BR')}</td>`;
|
|
const td=document.createElement('td');
|
|
const del=document.createElement('button'); del.className='btn danger'; del.textContent='Excluir'; del.addEventListener('click',()=>{ ANEXOS.splice(idx,1); renderAnexos(); });
|
|
td.appendChild(del); tr.appendChild(td); anexosBody.appendChild(tr);
|
|
});
|
|
if (!ANEXOS.length){
|
|
const tr=document.createElement('tr'); tr.innerHTML = '<td colspan="4" class="hint">Nenhum anexo adicionado.</td>'; anexosBody.appendChild(tr);
|
|
}
|
|
}
|
|
renderAnexos();
|
|
|
|
// Validações básicas
|
|
function validate(){
|
|
const errors=[];
|
|
if (!nome.value.trim()) errors.push('Nome é obrigatório.');
|
|
if (cpf.value.trim() && !isValidCPF(cpf.value)) errors.push('CPF inválido.');
|
|
if (cpfResponsavel.value.trim() && !isValidCPF(cpfResponsavel.value)) errors.push('CPF do responsável inválido.');
|
|
if (email.value.trim() && !/^\S+@\S+\.\S+$/.test(email.value)) errors.push('E-mail em formato inválido.');
|
|
if (docTipo.value && !docNumero.value.trim()) errors.push('Informe o número do documento selecionado.');
|
|
return errors;
|
|
}
|
|
|
|
function getSexo(){ const r = document.querySelector('input[name="sexo"]:checked'); return r? r.value : ''; }
|
|
|
|
// Salvar no localStorage como exemplo
|
|
function collectData(){
|
|
return {
|
|
foto: fotoPreview.querySelector('img')?.src || null,
|
|
nome: nome.value.trim(),
|
|
nomeSocial: nomeSocial.value.trim(),
|
|
cpf: cpf.value.trim(),
|
|
rg: rg.value.trim(),
|
|
docTipo: docTipo.value,
|
|
docNumero: docNumero.value.trim(),
|
|
sexo: getSexo(),
|
|
nascimento: nascimento.value||null,
|
|
racaIbge: racaIbge.value,
|
|
etniaIbge: etniaIbge.value.trim(),
|
|
profissao: profissao.value.trim(),
|
|
naturalidade: naturalidade.value.trim(),
|
|
nacionalidade: nacionalidade.value,
|
|
mae: mae.value.trim(),
|
|
profMae: profMae.value.trim(),
|
|
pai: pai.value.trim(),
|
|
profPai: profPai.value.trim(),
|
|
responsavel: responsavel.value.trim(),
|
|
cpfResponsavel: cpfResponsavel.value.trim(),
|
|
conjuge: conjuge.value.trim(),
|
|
rnConvenio: rnConvenio.checked,
|
|
codigoLegado: codigoLegado.value.trim(),
|
|
observacoes: observacoes.value,
|
|
anexos: ANEXOS,
|
|
contato: { email: email.value.trim(), celular: celular.value.trim(), tel1: tel1.value.trim(), tel2: tel2.value.trim() },
|
|
endereco: { cep: cep.value.trim(), logradouro: logradouro.value.trim(), numero: numero.value.trim(), complemento: complemento.value.trim(), bairro: bairro.value.trim(), cidade: cidade.value.trim(), uf: uf.value.trim().toUpperCase(), referencia: referencia.value.trim() },
|
|
updatedAt: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
function save(){
|
|
const errs = validate();
|
|
if (errs.length){ alert('Corrija os seguintes erros:\n\n- '+errs.join('\n- ')); return; }
|
|
const data = collectData();
|
|
const listKey = 'lista_pacientes_data_v1';
|
|
const exists = JSON.parse(localStorage.getItem(listKey)||'[]');
|
|
// Se já existir um registro com mesmo CPF, atualiza; senão cria novo
|
|
const idx = data.cpf ? exists.findIndex(p=> (p.doc||'') === data.cpf) : -1;
|
|
const toSave = {
|
|
id: idx>=0 ? exists[idx].id : crypto.randomUUID(),
|
|
nome: data.nome || '(sem nome)',
|
|
doc: data.cpf || '',
|
|
vip: exists[idx]?.vip || false,
|
|
telefone: exists[idx]?.telefone || '',
|
|
cidade: exists[idx]?.cidade || '',
|
|
uf: exists[idx]?.uf || '',
|
|
convenio: exists[idx]?.convenio || 'Particular',
|
|
idade: exists[idx]?.idade || null,
|
|
nascimento: data.nascimento,
|
|
ultimo: exists[idx]?.ultimo || null,
|
|
proximo: exists[idx]?.proximo || null,
|
|
obs: data.observacoes || ''
|
|
};
|
|
if (idx>=0) exists[idx] = toSave; else exists.unshift(toSave);
|
|
localStorage.setItem(listKey, JSON.stringify(exists));
|
|
localStorage.setItem('paciente_detalhe', JSON.stringify(data)); // dump completo
|
|
showToast('Paciente salvo com sucesso','ok');
|
|
}
|
|
|
|
function cancel(){
|
|
if (confirm('Deseja cancelar? Alterações não salvas serão perdidas.')){
|
|
window.location.href = 'lista-pacientes.html'; // se existir; caso contrário, permanece
|
|
}
|
|
}
|
|
|
|
btnSalvar.addEventListener('click', save); btnSalvar2.addEventListener('click', save);
|
|
btnCancelar.addEventListener('click', cancel); btnCancelar2.addEventListener('click', cancel);
|
|
</script>
|
|
</body>
|
|
</html> |