630 lines
24 KiB
HTML
630 lines
24 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 de Paciente</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0f172a; /* azul-escuro elegante */
|
|
--panel: #111827; /* painel */
|
|
--card: #0b1220; /* cards */
|
|
--text: #f1f5f9;
|
|
--muted: #94a3b8;
|
|
--primary: #3b82f6;
|
|
--danger: #ef4444;
|
|
--success: #22c55e;
|
|
--border: #1f2937;
|
|
--focus: #93c5fd;
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
margin: 0;
|
|
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans,
|
|
Helvetica Neue, Arial, "Apple Color Emoji", "Segoe UI Emoji";
|
|
background: linear-gradient(180deg, var(--bg), #0b1021 60%);
|
|
color: var(--text);
|
|
}
|
|
header {
|
|
padding: 24px 16px;
|
|
border-bottom: 1px solid var(--border);
|
|
background: rgba(17, 24, 39, 0.6);
|
|
position: sticky; top: 0; backdrop-filter: blur(8px);
|
|
}
|
|
.container {
|
|
max-width: 1100px;
|
|
margin: 20px auto;
|
|
padding: 0 16px 60px;
|
|
}
|
|
h1 { margin: 0; font-size: 1.6rem; letter-spacing: 0.4px; }
|
|
h2 { font-size: 1.25rem; margin: 14px 0; color: var(--text); }
|
|
p.helper { color: var(--muted); margin-top: 4px; font-size: 0.9rem; }
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(12, 1fr);
|
|
gap: 14px;
|
|
}
|
|
.card {
|
|
background: linear-gradient(180deg, var(--card), #0a0f1c);
|
|
border: 1px solid var(--border);
|
|
border-radius: 14px;
|
|
padding: 16px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,.25);
|
|
}
|
|
label { display: block; font-weight: 600; margin-bottom: 6px; }
|
|
.row { display: flex; gap: 10px; align-items: center; }
|
|
input[type="text"], input[type="email"], input[type="date"], input[type="number"], select, textarea {
|
|
width: 100%;
|
|
background: #0b1220;
|
|
color: var(--text);
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
padding: 10px 12px;
|
|
outline: none;
|
|
transition: border .15s, box-shadow .15s;
|
|
}
|
|
textarea { min-height: 110px; resize: vertical; }
|
|
input:focus, select:focus, textarea:focus {
|
|
border-color: var(--focus);
|
|
box-shadow: 0 0 0 3px rgba(147, 197, 253, .15);
|
|
}
|
|
.field { grid-column: span 12; }
|
|
.col-6 { grid-column: span 6; }
|
|
.col-4 { grid-column: span 4; }
|
|
.col-3 { grid-column: span 3; }
|
|
|
|
.actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; }
|
|
button {
|
|
padding: 10px 14px;
|
|
border-radius: 12px;
|
|
border: 1px solid var(--border);
|
|
background: #0b1220;
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
}
|
|
.btn-primary { background: var(--primary); border-color: #2563eb; }
|
|
.btn-danger { background: var(--danger); border-color: #b91c1c; }
|
|
|
|
.error { font-size: 0.85rem; color: var(--danger); margin-top: 6px; display: none; }
|
|
.show { display: block; }
|
|
|
|
.avatar {
|
|
width: 120px; height: 120px; border-radius: 12px; overflow: hidden; border: 1px solid var(--border);
|
|
background: #0b1220; display: grid; place-items: center; color: var(--muted);
|
|
}
|
|
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
.file-btn { position: relative; overflow: hidden; }
|
|
.file-btn input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
|
|
|
|
details {
|
|
border: 1px solid var(--border);
|
|
border-radius: 12px;
|
|
background: linear-gradient(180deg, var(--panel), #0b1220);
|
|
padding: 10px 12px;
|
|
}
|
|
summary { cursor: pointer; font-weight: 700; list-style: none; }
|
|
summary::-webkit-details-marker { display:none; }
|
|
|
|
.inline { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: end; }
|
|
.inline-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; align-items: end; }
|
|
|
|
.toggle {
|
|
--h: 28px; --w: 52px;
|
|
width: var(--w); height: var(--h); border-radius: 999px; position: relative; background: #253046; border:1px solid var(--border);
|
|
}
|
|
.toggle input { display: none; }
|
|
.knob { position: absolute; top: 2px; left: 2px; width: calc(var(--h) - 4px); height: calc(var(--h) - 4px); border-radius: 999px; background: #e5e7eb; transition: left .15s, background .15s; }
|
|
.toggle input:checked + .knob { left: calc(var(--w) - var(--h) + 2px); background: #bbf7d0; }
|
|
|
|
.attachments { margin-top: 10px; }
|
|
.att-row { display:flex; align-items:center; justify-content:space-between; padding:8px 10px; border:1px dashed var(--border); border-radius:10px; margin-top:8px; }
|
|
.att-row small { color: var(--muted); }
|
|
.att-row button { padding:6px 10px; }
|
|
|
|
.muted { color: var(--muted); font-size: .9rem; }
|
|
.required:after { content:" *"; color: var(--danger); font-weight: 800; }
|
|
|
|
@media (max-width: 820px) {
|
|
.col-6, .col-4, .col-3 { grid-column: span 12; }
|
|
.inline, .inline-3 { grid-template-columns: 1fr; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>Cadastro de Paciente</h1>
|
|
</header>
|
|
|
|
<div class="container">
|
|
<form id="pacienteForm" novalidate>
|
|
<section class="card">
|
|
<h2>1. Dados pessoais</h2>
|
|
<div class="grid">
|
|
<div class="field col-3">
|
|
<label>Foto</label>
|
|
<div class="row">
|
|
<div class="avatar" id="avatarPreview" aria-label="Pré-visualização da foto">Prévia</div>
|
|
<label class="file-btn btn-primary" style="padding:10px 12px;">
|
|
<span>Carregar</span>
|
|
<input type="file" id="foto" accept="image/*" />
|
|
</label>
|
|
</div>
|
|
<p class="muted">PNG/JPG até 5MB.</p>
|
|
</div>
|
|
|
|
<div class="field col-9">
|
|
<label class="required" for="nome">Nome</label>
|
|
<input id="nome" name="nome" type="text" autocomplete="name" required />
|
|
<div class="error" id="err-nome">Informe o nome completo.</div>
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="nomeSocial">Nome social</label>
|
|
<input id="nomeSocial" name="nomeSocial" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label class="required" for="cpf">CPF</label>
|
|
<input id="cpf" name="cpf" type="text" inputmode="numeric" maxlength="14" placeholder="000.000.000-00" required />
|
|
<div class="error" id="err-cpf">CPF inválido.</div>
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label for="rg">RG</label>
|
|
<input id="rg" name="rg" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="docTipo">Outros documentos</label>
|
|
<div class="inline">
|
|
<select id="docTipo" name="docTipo">
|
|
<option value="">Selecione…</option>
|
|
<option>CNH</option>
|
|
<option>Passaporte</option>
|
|
<option>RNE</option>
|
|
<option>Cart. Profissional</option>
|
|
</select>
|
|
<input id="docNumero" name="docNumero" type="text" placeholder="Número do documento" disabled />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label>Sexo</label>
|
|
<div class="row" role="radiogroup" aria-label="Sexo">
|
|
<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 / Prefiro não informar</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label for="nascimento">Data de nascimento</label>
|
|
<input id="nascimento" name="nascimento" type="date" />
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label for="estadoCivil">Estado civil</label>
|
|
<select id="estadoCivil" name="estadoCivil">
|
|
<option value="">Selecione…</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="field col-4">
|
|
<label for="raca">Raça/Cor (IBGE)</label>
|
|
<select id="raca" name="raca">
|
|
<option value="">Selecione…</option>
|
|
<option>Branca</option>
|
|
<option>Preta</option>
|
|
<option>Parda</option>
|
|
<option>Amarela</option>
|
|
<option>Indígena</option>
|
|
<option>Não informada</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="field col-4">
|
|
<label for="etnia">Etnia (IBGE/autoidentificação)</label>
|
|
<select id="etnia" name="etnia">
|
|
<option value="">Selecione…</option>
|
|
<option>Não se aplica</option>
|
|
<option>Indígena (especificar em observações)</option>
|
|
<option>Quilombola</option>
|
|
<option>Cigana</option>
|
|
<option>Outra / Prefiro não informar</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="field col-4">
|
|
<label for="profissao">Profissão</label>
|
|
<input id="profissao" name="profissao" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="naturalidade">Naturalidade (cidade)</label>
|
|
<input list="cidadesBR" id="naturalidade" name="naturalidade" placeholder="Cidade de nascimento" />
|
|
<datalist id="cidadesBR">
|
|
<option>São Paulo (SP)</option>
|
|
<option>Rio de Janeiro (RJ)</option>
|
|
<option>Belo Horizonte (MG)</option>
|
|
<option>Porto Alegre (RS)</option>
|
|
<option>Salvador (BA)</option>
|
|
<option>Curitiba (PR)</option>
|
|
<option>Fortaleza (CE)</option>
|
|
<option>Recife (PE)</option>
|
|
<option>Manaus (AM)</option>
|
|
<option>Brasília (DF)</option>
|
|
<option>Maceió (AL)</option>
|
|
</datalist>
|
|
</div>
|
|
<div class="field col-6">
|
|
<label for="nacionalidade">Nacionalidade (país)</label>
|
|
<input list="paises" id="nacionalidade" name="nacionalidade" placeholder="Brasil" />
|
|
<datalist id="paises">
|
|
<option>Brasil</option>
|
|
<option>Argentina</option>
|
|
<option>Portugal</option>
|
|
<option>Estados Unidos</option>
|
|
<option>Japão</option>
|
|
<option>Itália</option>
|
|
<option>Espanha</option>
|
|
<option>França</option>
|
|
<option>Alemanha</option>
|
|
<option>Outros</option>
|
|
</datalist>
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="mae">Nome da mãe</label>
|
|
<input id="mae" name="mae" type="text" autocomplete="on" />
|
|
</div>
|
|
<div class="field col-6">
|
|
<label for="profMae">Profissão da mãe</label>
|
|
<input id="profMae" name="profMae" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="pai">Nome do pai</label>
|
|
<input id="pai" name="pai" type="text" />
|
|
</div>
|
|
<div class="field col-6">
|
|
<label for="profPai">Profissão do pai</label>
|
|
<input id="profPai" name="profPai" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="resp">Nome do responsável</label>
|
|
<input id="resp" name="resp" type="text" />
|
|
</div>
|
|
<div class="field col-6">
|
|
<label for="cpfResp">CPF do responsável</label>
|
|
<input id="cpfResp" name="cpfResp" type="text" inputmode="numeric" maxlength="14" placeholder="000.000.000-00" />
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="conjuge">Nome do esposo(a)</label>
|
|
<input id="conjuge" name="conjuge" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label>RN na guia do convênio</label>
|
|
<div class="row" style="gap:12px; align-items:center;">
|
|
<span class="muted">Não</span>
|
|
<label class="toggle">
|
|
<input type="checkbox" id="rnGuia" />
|
|
<span class="knob"></span>
|
|
</label>
|
|
<span class="muted">Sim</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label for="codLegado">Código legado</label>
|
|
<input id="codLegado" name="codLegado" type="text" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<details class="card" open>
|
|
<summary>2. Observações e anexos</summary>
|
|
<div class="grid" style="margin-top:10px;">
|
|
<div class="field col-12">
|
|
<label for="obs">Observações</label>
|
|
<textarea id="obs" name="obs" placeholder="Alergias, restrições, notas…"></textarea>
|
|
</div>
|
|
|
|
<div class="field col-12">
|
|
<label>Anexos do paciente</label>
|
|
<div class="row" style="gap:10px; align-items: center;">
|
|
<label class="file-btn btn-primary">
|
|
<span>Adicionar arquivos</span>
|
|
<input type="file" id="anexos" multiple />
|
|
</label>
|
|
<span class="muted">Cartão de convênio, exames, etc.</span>
|
|
</div>
|
|
<div class="attachments" id="attachmentsList" aria-live="polite"></div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="card" open>
|
|
<summary>3. Contato</summary>
|
|
<div class="grid" style="margin-top:10px;">
|
|
<div class="field col-6">
|
|
<label for="email">E-mail</label>
|
|
<input id="email" name="email" type="email" placeholder="nome@exemplo.com" />
|
|
<div class="error" id="err-email">Formato de e-mail inválido.</div>
|
|
</div>
|
|
<div class="field col-6">
|
|
<label for="celular">Celular</label>
|
|
<input id="celular" name="celular" type="text" inputmode="tel" placeholder="+55 (11) 91234-5678" />
|
|
</div>
|
|
|
|
<div class="field col-6">
|
|
<label for="tel1">Telefone 1</label>
|
|
<input id="tel1" name="tel1" type="text" inputmode="tel" placeholder="+55 (11) 1234-5678" />
|
|
</div>
|
|
<div class="field col-6">
|
|
<label for="tel2">Telefone 2</label>
|
|
<input id="tel2" name="tel2" type="text" inputmode="tel" placeholder="+55 (11) 1234-5678" />
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="card" open>
|
|
<summary>4. Endereço</summary>
|
|
<div class="grid" style="margin-top:10px;">
|
|
<div class="field col-3">
|
|
<label for="cep">CEP</label>
|
|
<input id="cep" name="cep" type="text" inputmode="numeric" maxlength="9" placeholder="00000-000" />
|
|
<div class="error" id="err-cep">CEP não encontrado.</div>
|
|
</div>
|
|
<div class="field col-9">
|
|
<label for="logradouro">Logradouro</label>
|
|
<input id="logradouro" name="logradouro" type="text" />
|
|
</div>
|
|
|
|
<div class="field col-3">
|
|
<label for="numero">Número</label>
|
|
<input id="numero" name="numero" type="text" />
|
|
</div>
|
|
<div class="field col-3">
|
|
<label for="complemento">Complemento</label>
|
|
<input id="complemento" name="complemento" type="text" />
|
|
</div>
|
|
<div class="field col-3">
|
|
<label for="bairro">Bairro</label>
|
|
<input id="bairro" name="bairro" type="text" />
|
|
</div>
|
|
<div class="field col-3">
|
|
<label for="cidade">Cidade</label>
|
|
<input id="cidade" name="cidade" type="text" />
|
|
</div>
|
|
<div class="field col-3">
|
|
<label for="estado">Estado</label>
|
|
<input id="estado" name="estado" type="text" maxlength="2" placeholder="UF" />
|
|
</div>
|
|
<div class="field col-9">
|
|
<label for="referencia">Referência</label>
|
|
<input id="referencia" name="referencia" type="text" placeholder="Ponto de referência (opcional)" />
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<div class="actions">
|
|
<button type="button" class="btn-danger" id="btnCancelar">Cancelar</button>
|
|
<button type="submit" class="btn-primary">Salvar</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
// === Utilidades de máscara ===
|
|
const onlyDigits = (v) => v.replace(/\D+/g, "");
|
|
const maskCPF = (v) => {
|
|
v = onlyDigits(v).slice(0,11);
|
|
return v
|
|
.replace(/(\d{3})(\d)/, '$1.$2')
|
|
.replace(/(\d{3})(\d)/, '$1.$2')
|
|
.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
|
|
};
|
|
const maskCEP = (v) => {
|
|
v = onlyDigits(v).slice(0,8);
|
|
return v.replace(/(\d{5})(\d{1,3})$/, '$1-$2');
|
|
};
|
|
const maskCell = (v) => {
|
|
v = onlyDigits(v).slice(0,13); // 55 + 2 + 9 + 4 = 19, mas guardamos apenas dígitos
|
|
// Formato: +55 (XX) XXXXX-XXXX
|
|
let d = onlyDigits(v);
|
|
if (d.startsWith('55') === false) d = '55' + d;
|
|
d = d.slice(0,13); // 55 + 2 + 9 + 4 (mas sem sinais)
|
|
const cc = d.slice(0,2); // 55
|
|
const dd = d.slice(2,4);
|
|
const p1 = d.slice(4,9);
|
|
const p2 = d.slice(9,13);
|
|
let out = `+${cc}`;
|
|
if (dd) out += ` (${dd})`;
|
|
if (p1) out += ` ${p1}`;
|
|
if (p2) out += `-${p2}`;
|
|
return out;
|
|
};
|
|
const maskPhone = (v) => {
|
|
v = onlyDigits(v);
|
|
if (v.startsWith('55') === false) v = '55' + v;
|
|
v = v.slice(0,12); // 55 + 2 + 8
|
|
const cc = v.slice(0,2); // 55
|
|
const dd = v.slice(2,4);
|
|
const p1 = v.slice(4,8);
|
|
const p2 = v.slice(8,12);
|
|
let out = `+${cc}`;
|
|
if (dd) out += ` (${dd})`;
|
|
if (p1) out += ` ${p1}`;
|
|
if (p2) out += `-${p2}`;
|
|
return out;
|
|
};
|
|
|
|
// === Validação de CPF ===
|
|
function validaCPF(cpf) {
|
|
cpf = onlyDigits(cpf);
|
|
if (!cpf || cpf.length !== 11) return false;
|
|
if (/^(\d)\1+$/.test(cpf)) return false; // evita sequências
|
|
let soma = 0;
|
|
for (let i = 0; i < 9; i++) soma += parseInt(cpf.charAt(i)) * (10 - i);
|
|
let resto = 11 - (soma % 11);
|
|
let dig1 = resto === 10 || resto === 11 ? 0 : resto;
|
|
soma = 0;
|
|
for (let i = 0; i < 10; i++) soma += parseInt(cpf.charAt(i)) * (11 - i);
|
|
resto = 11 - (soma % 11);
|
|
let dig2 = resto === 10 || resto === 11 ? 0 : resto;
|
|
return dig1 === parseInt(cpf.charAt(9)) && dig2 === parseInt(cpf.charAt(10));
|
|
}
|
|
|
|
// === Foto: pré-visualização ===
|
|
const fotoInput = document.getElementById('foto');
|
|
const avatarPreview = document.getElementById('avatarPreview');
|
|
fotoInput.addEventListener('change', () => {
|
|
const file = fotoInput.files?.[0];
|
|
if (!file) return;
|
|
if (!file.type.startsWith('image/')) { alert('Selecione um arquivo de imagem.'); return; }
|
|
if (file.size > 5 * 1024 * 1024) { alert('Imagem acima de 5MB.'); return; }
|
|
const url = URL.createObjectURL(file);
|
|
avatarPreview.innerHTML = `<img alt="Foto do paciente" src="${url}">`;
|
|
});
|
|
|
|
// === Documentos: habilitar número quando tipo selecionado ===
|
|
const docTipo = document.getElementById('docTipo');
|
|
const docNumero = document.getElementById('docNumero');
|
|
docTipo.addEventListener('change', () => {
|
|
docNumero.disabled = !docTipo.value;
|
|
if (!docTipo.value) docNumero.value = '';
|
|
docNumero.focus();
|
|
});
|
|
|
|
// === Máscaras ===
|
|
const cpf = document.getElementById('cpf');
|
|
const cpfResp = document.getElementById('cpfResp');
|
|
const cep = document.getElementById('cep');
|
|
const celular = document.getElementById('celular');
|
|
const tel1 = document.getElementById('tel1');
|
|
const tel2 = document.getElementById('tel2');
|
|
|
|
const applyMask = (el, fn) => {
|
|
el.addEventListener('input', () => el.value = fn(el.value));
|
|
el.addEventListener('blur', () => el.value = fn(el.value));
|
|
};
|
|
applyMask(cpf, maskCPF);
|
|
applyMask(cpfResp, maskCPF);
|
|
applyMask(cep, maskCEP);
|
|
applyMask(celular, maskCell);
|
|
applyMask(tel1, maskPhone);
|
|
applyMask(tel2, maskPhone);
|
|
|
|
// === CEP: auto-preencher via ViaCEP ===
|
|
async function buscaCEP(v) {
|
|
const dig = onlyDigits(v);
|
|
if (dig.length !== 8) return null;
|
|
try {
|
|
const res = await fetch(`https://viacep.com.br/ws/${dig}/json/`);
|
|
if (!res.ok) return null;
|
|
const data = await res.json();
|
|
if (data.erro) return null;
|
|
return data; // {logradouro, bairro, localidade, uf}
|
|
} catch { return null; }
|
|
}
|
|
const logradouro = document.getElementById('logradouro');
|
|
const bairro = document.getElementById('bairro');
|
|
const cidade = document.getElementById('cidade');
|
|
const estado = document.getElementById('estado');
|
|
const errCEP = document.getElementById('err-cep');
|
|
|
|
cep.addEventListener('blur', async () => {
|
|
errCEP.classList.remove('show');
|
|
const data = await buscaCEP(cep.value);
|
|
if (!data) { errCEP.classList.add('show'); return; }
|
|
logradouro.value = data.logradouro || '';
|
|
bairro.value = data.bairro || '';
|
|
cidade.value = data.localidade || '';
|
|
estado.value = data.uf || '';
|
|
});
|
|
|
|
// === Anexos: listar e remover ===
|
|
const anexosInput = document.getElementById('anexos');
|
|
const attachmentsList = document.getElementById('attachmentsList');
|
|
const anexosState = [];
|
|
function renderAnexos() {
|
|
attachmentsList.innerHTML = '';
|
|
anexosState.forEach((f, idx) => {
|
|
const row = document.createElement('div');
|
|
row.className = 'att-row';
|
|
const left = document.createElement('div');
|
|
left.innerHTML = `<strong>${f.name}</strong><br><small>${new Date(f.addedAt).toLocaleString()} — ${(f.size/1024).toFixed(1)} KB</small>`;
|
|
const del = document.createElement('button');
|
|
del.type = 'button';
|
|
del.textContent = 'Excluir';
|
|
del.addEventListener('click', () => { anexosState.splice(idx,1); renderAnexos(); });
|
|
row.append(left, del);
|
|
attachmentsList.appendChild(row);
|
|
});
|
|
}
|
|
anexosInput.addEventListener('change', () => {
|
|
const files = Array.from(anexosInput.files || []);
|
|
files.forEach(f => anexosState.push({ name: f.name, size: f.size, addedAt: Date.now() }));
|
|
anexosInput.value = '';
|
|
renderAnexos();
|
|
});
|
|
|
|
// === Validações no submit ===
|
|
const form = document.getElementById('pacienteForm');
|
|
const errNome = document.getElementById('err-nome');
|
|
const errCpf = document.getElementById('err-cpf');
|
|
const errEmail = document.getElementById('err-email');
|
|
|
|
form.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
let ok = true;
|
|
|
|
// nome obrigatório
|
|
if (!nome.value.trim()) { errNome.classList.add('show'); ok = false; } else errNome.classList.remove('show');
|
|
|
|
// cpf válido
|
|
if (!validaCPF(cpf.value)) { errCpf.classList.add('show'); ok = false; } else errCpf.classList.remove('show');
|
|
|
|
// email (se preenchido) precisa ser válido
|
|
if (email.value && !/^\S+@\S+\.\S+$/.test(email.value)) { errEmail.classList.add('show'); ok = false; } else errEmail.classList.remove('show');
|
|
|
|
if (!ok) { window.scrollTo({ top: 0, behavior: 'smooth' }); return; }
|
|
|
|
// Coleta dos dados (exemplo). Aqui você enviaria para sua API com fetch().
|
|
const dados = Object.fromEntries(new FormData(form).entries());
|
|
dados.cpf = onlyDigits(dados.cpf);
|
|
dados.cpfResp = onlyDigits(dados.cpfResp || '');
|
|
dados.cep = onlyDigits(dados.cep || '');
|
|
dados.anexos = anexosState;
|
|
dados.rnGuia = document.getElementById('rnGuia').checked;
|
|
|
|
console.log('Salvar paciente:', dados);
|
|
alert('Paciente salvo (exemplo). Veja o console do navegador para os dados coletados.');
|
|
form.reset();
|
|
avatarPreview.textContent = 'Prévia';
|
|
attachmentsList.innerHTML = '';
|
|
anexosState.length = 0;
|
|
docNumero.disabled = true;
|
|
});
|
|
|
|
// Cancelar
|
|
document.getElementById('btnCancelar').addEventListener('click', () => {
|
|
if (confirm('Deseja cancelar e voltar à lista? Alterações serão perdidas.')) {
|
|
// Redirecione conforme sua aplicação:
|
|
window.history.back();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|