1/* Form de bloqueio LGPD do CPF na consulta de fatura (dentro do modal de 2 Privacidade da fatura.html). So controla o form: quem abre/fecha o modal 3 continua sendo o fatura.js (data-priv-close). Envia pro endpoint 4 /api/site-admin/public/privacidade (Bloco 1); a resposta do backend e 5 neutra de proposito (nao revela se o documento e de cliente). */ 6(function () { 7 "use strict"; 8 9 var card = document.querySelector("#fatura-priv-modal .fatura-priv-modal__card"); 10 var btnAbrir = document.getElementById("fatura-priv-optout-btn"); 11 var viewForm = document.getElementById("fatura-priv-view-form"); 12 var viewOk = document.getElementById("fatura-priv-view-sucesso"); 13 var form = document.getElementById("fatura-priv-form"); 14 if (!card || !btnAbrir || !viewForm || !viewOk || !form) return; // pagina sem o modal 15 16 var kicker = card.querySelector(".fatura-priv-modal__kicker"); 17 var titulo = document.getElementById("fatura-priv-modal-title"); 18 var inNome = document.getElementById("fatura-priv-nome"); 19 var inDoc = document.getElementById("fatura-priv-doc"); 20 var inEmail = document.getElementById("fatura-priv-email"); 21 var inLgpd = document.getElementById("fatura-priv-lgpd"); 22 var inHp = form.querySelector('input[name="website"]'); 23 var slotTs = document.getElementById("fatura-priv-ts-slot"); 24 var elErro = document.getElementById("fatura-priv-form-erro"); 25 var btnEnviar = document.getElementById("fatura-priv-enviar-btn"); 26 var btnVoltar = document.getElementById("fatura-priv-voltar-btn"); 27 28 // titulos por view (o original volta quando o usuario fecha/volta) 29 var kickerOrig = kicker ? kicker.textContent : ""; 30 var tituloOrig = titulo ? titulo.textContent : ""; 31 32 // filhos diretos do card que formam a view "politica" (h3/p/cookies/opt-out) 33 var politicaEls = card.querySelectorAll( 34 ":scope > .fatura-priv-modal__h, :scope > .fatura-priv-modal__p, " + 35 ":scope > .fatura-priv-modal__cookies, :scope > .fatura-optout" 36 ); 37 38 // ---- Turnstile (mesmo mecanismo do beneficio.js; api.js carrega 1x) ---- 39 var ts = { id: null, token: "" }; 40 var tsLoading = false; 41 function loadTs(cb) { 42 if (window.turnstile && window.turnstile.render) { cb(true); return; } 43 if (tsLoading) { 44 var iv = setInterval(function () { if (window.turnstile && window.turnstile.render) { clearInterval(iv); cb(true); } }, 150); 45 setTimeout(function () { clearInterval(iv); }, 8000); 46 return; 47 } 48 tsLoading = true; 49 var s = document.createElement("script"); 50 s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"; 51 s.async = true; s.defer = true; 52 s.onload = function () { cb(true); }; 53 s.onerror = function () { cb(false); }; 54 document.head.appendChild(s); 55 } 56 function ensureTs() { 57 if (!window.NR_TURNSTILE_REQUIRED || !slotTs || ts.id !== null) return; 58 loadTs(function (ok) { 59 if (!ok || !window.turnstile || ts.id !== null) return; 60 try { 61 ts.id = window.turnstile.render(slotTs, { 62 sitekey: window.NR_TURNSTILE_SITE_KEY, 63 theme: "dark", 64 callback: function (t) { ts.token = t; }, 65 "expired-callback": function () { ts.token = ""; }, 66 "error-callback": function () { ts.token = ""; } 67 }); 68 } catch (e) { /* ignora */ } 69 }); 70 } 71 function resetTs() { 72 ts.token = ""; 73 if (ts.id !== null && window.turnstile) { try { window.turnstile.reset(ts.id); } catch (e) {} } 74 } 75 // reset FORTE (igual beneficio.js): tira o widget do DOM. Sem isso, fechar e 76 // reabrir o form mostrava um "Success" residual do widget antigo. 77 function resetTsHard() { 78 ts.token = ""; 79 if (ts.id !== null && window.turnstile) { try { window.turnstile.remove(ts.id); } catch (e) {} } 80 ts.id = null; 81 if (slotTs) slotTs.innerHTML = ""; 82 } 83 84 // ---- troca de views dentro do modal ---- 85 function mostraPolitica() { 86 politicaEls.forEach(function (el) { el.hidden = false; }); 87 viewForm.hidden = true; 88 viewOk.hidden = true; 89 if (kicker) kicker.textContent = kickerOrig; 90 if (titulo) titulo.textContent = tituloOrig; 91 // saiu do form (Voltar ou fechar): widget fora do DOM, sem Success residual 92 resetTsHard(); 93 } 94 function mostraForm() {
95 politicaEls.forEach(function (el) { el.hidden = true; }); 96 viewForm.hidden = false; 97 viewOk.hidden = true; 98 if (kicker) kicker.textContent = "Bloqueio de CPF"; 99 if (titulo) titulo.textContent = "Solicitar bloqueio na consulta"; 100 setErro(""); 101 // v2: NAO renderiza o Turnstile aqui. O managed do Cloudflare resolvia 102 // sozinho e mostrava "Success" com o form vazio. Agora renderiza so 103 // quando o CPF/CNPJ completa (listener de input abaixo), como no beneficio. 104 resetTsHard(); 105 if (inNome) { try { inNome.focus(); } catch (e) {} } 106 } 107 function mostraSucesso() { 108 politicaEls.forEach(function (el) { el.hidden = true; }); 109 viewForm.hidden = true; 110 viewOk.hidden = false; 111 if (kicker) kicker.textContent = "Bloqueio de CPF"; 112 if (titulo) titulo.textContent = "Pedido enviado"; 113 } 114 115 function setErro(msg) { 116 if (!elErro) return; 117 elErro.textContent = msg || ""; 118 elErro.hidden = !msg; 119 } 120 121 // helpers de validacao (mesmo padrao do beneficio.js) 122 function digits(s) { return (s || "").replace(/\D/g, ""); } 123 function emailValido(v) { return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test((v || "").trim()); } 124 // mascara BR ao vivo: ate 11 digitos formata CPF, acima vira CNPJ 125 function formatDoc(d) { 126 d = d.slice(0, 14); 127 if (d.length <= 11) { 128 if (d.length > 9) return d.replace(/(\d{3})(\d{3})(\d{3})(\d+)/, "$1.$2.$3-$4"); 129 if (d.length > 6) return d.replace(/(\d{3})(\d{3})(\d+)/, "$1.$2.$3"); 130 if (d.length > 3) return d.replace(/(\d{3})(\d+)/, "$1.$2"); 131 return d; 132 } 133 if (d.length > 12) return d.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d+)/, "$1.$2.$3/$4-$5"); 134 return d.replace(/(\d{2})(\d{3})(\d{3})(\d+)/, "$1.$2.$3/$4"); 135 } 136 137 btnAbrir.addEventListener("click", mostraForm); 138 if (btnVoltar) btnVoltar.addEventListener("click", mostraPolitica); 139 140 // Documento: mascara BR ao vivo (so digito entra, pontos automaticos) + 141 // Turnstile SO com 11/14 digitos; se o doc regride, o widget some (reset
142 // forte), pra nao sobrar "Success" com documento invalido. 143 if (inDoc) { 144 inDoc.addEventListener("input", function () { 145 var d = digits(inDoc.value); 146 var fmt = formatDoc(d); 147 if (inDoc.value !== fmt) inDoc.value = fmt; 148 if (d.length === 11 || d.length === 14) { 149 if (ts.id === null) ensureTs(); 150 } else if (ts.id !== null) { 151 resetTsHard(); 152 } 153 }); 154 } 155 156 // E-mail: validacao ao vivo (borda + msg discreta enquanto invalido) 157 function checaEmail(mostraMsg) { 158 if (!inEmail) return; 159 var v = (inEmail.value || "").trim(); 160 var invalido = v !== "" && !emailValido(v); 161 inEmail.style.borderColor = invalido ? "#f87171" : ""; 162 if (invalido && mostraMsg) { 163 setErro("Informe um e-mail válido (com @ e domÃnio)."); 164 } else if (elErro && elErro.textContent.indexOf("e-mail válido") !== -1) { 165 setErro(""); 166 } 167 } 168 if (inEmail) { 169 inEmail.addEventListener("input", function () { checaEmail(false); }); 170 inEmail.addEventListener("blur", function () { checaEmail(true); }); 171 } 172 173 // fechar o modal (qualquer data-priv-close, inclusive backdrop/x do fatura.js): 174 // volta pra politica (que ja faz o reset forte do Turnstile) e limpa os campos 175 document.addEventListener("click", function (e) { 176 if (e.target && e.target.closest && e.target.closest("[data-priv-close]")) { 177 mostraPolitica(); 178 if (form) form.reset(); 179 } 180 }); 181 182 // ---- validacao no cliente (o backend revalida tudo) ---- 183 function valida() { 184 var nome = (inNome && inNome.value ? inNome.value : "").trim(); 185 var nDigits = digits(inDoc && inDoc.value ? inDoc.value : "").length; 186 var email = (inEmail && inEmail.value ? inEmail.value : "").trim(); 187 if (nome.length < 2) return "Informe o seu nome completo."; 188 if (nDigits !== 11 && nDigits !== 14) return "Informe um CPF (11 números) ou CNPJ (14 números)."; 189 if (!emailValido(email)) return "Informe um e-mail válido (com @ e domÃnio)."; 190 if (!inLgpd || !inLgpd.checked) return "Marque a confirmação de titular pra gente processar o pedido."; 191 if (window.NR_TURNSTILE_REQUIRED && !ts.token) return "Complete a verificação de segurança antes de enviar."; 192 return ""; 193 } 194 195 form.addEventListener("submit", function (e) { 196 e.preventDefault(); 197 var problema = valida(); 198 if (problema) { setErro(problema); return; } 199 setErro(""); 200 if (btnEnviar) { btnEnviar.disabled = true; btnEnviar.textContent = "Enviando..."; } 201 202 fetch("/api/site-admin/public/privacidade", { 203 method: "POST", 204 headers: { "Content-Type": "application/json" }, 205 body: JSON.stringify({ 206 nome: inNome.value.trim(), 207 documento: inDoc.value.trim(), 208 email: inEmail.value.trim(), 209 lgpd: "1", 210 website: inHp ? inHp.value : "", 211 cf_turnstile_response: ts.token 212 }) 213 }).then(function (r) { 214 return r.json().then(function (data) { return { http: r.status, data: data }; }); 215 }).then(function (res) { 216 if (res.data && res.data.ok) { 217 mostraSucesso(); 218 form.reset(); 219 return; 220 }
221 // erro de validacao do backend (ex: documento invalido, Turnstile expirado): 222 // mostra a mensagem dele; o Turnstile renova no fim pro proximo envio 223 setErro((res.data && res.data.message) || "Não foi possÃvel enviar agora. Tente de novo ou escreva pra [email protected]."); 224 }).catch(function () { 225 setErro("Não foi possÃvel enviar agora. Tente de novo ou escreva pra [email protected]."); 226 }).then(function () { 227 // roda em qualquer desfecho (sucesso, erro do backend ou rede): 228 // reabilita o botao e renova o token usado 229 if (btnEnviar) { btnEnviar.disabled = false; btnEnviar.textContent = "Solicitar bloqueio"; } 230 resetTs(); 231 }); 232 }); 233})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.