PageSourceSearch

https://nrconexoes.com.br/assets/js/privacidade.js?v=4

js nrconexoes.com.br collected 2026-10-08 12:29:05 UTC 10,075 bytes, 233 lines download raw bytes

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.