PageSourceSearch

https://www.farmaciasbrava.com.br/staticfiles/js/login.js

js farmaciasbrava.com.br collected 2026-09-24 10:42:38 UTC 76,535 bytes, 2,015 lines download raw bytes

1
2  function obterUrlPosLogin() {
3    try {
4      const next = new URLSearchParams(window.location.search).get('next');
5      if (next && next.startsWith('/') && !next.startsWith('//')) {
6        return next;
7      }
8    } catch (_) {}
9    return '/';
10  }
11
12  function scrollAndFocus(id, offset = 200) {
13      const el = document.getElementById(id);
14      if (!el) return;
15      const top = el.getBoundingClientRect().top + window.scrollY - offset;
16      window.scrollTo({ top, behavior: 'smooth' });
17      setTimeout(() => el.focus(), 500);
18    }
19
20  function isCampoVisivel(campo) {
21    if (!campo || campo.disabled) return false;
22    if (campo.closest('.hidden, [hidden]')) return false;
23
24    const estilo = window.getComputedStyle(campo);
25    if (estilo.display === 'none' || estilo.visibility === 'hidden') return false;
26
27    return campo.offsetParent !== null || estilo.position === 'fixed';
28  }
29
30  // Rosario: login com CPF ou CNPJ alfanumérico (data-login-cpf-cnpj no form_cpf).
31  function isLoginCpfCnpjHabilitado() {
32    const cpfCnpj = document.getElementById('form_cpf')?.dataset?.loginCpfCnpj;
33
34    if (cpfCnpj === 'true') {
35      return true;
36    }
37    return false;
38  }
39
40  function normalizarDocumentoLogin(valor) {
41    return (valor || '').replace(/[.\-\/\s]/g, '').toUpperCase();
42  }
43
44  function obterDocumentoLogin(valor) {
45    if (isLoginCpfCnpjHabilitado()) {
46      return normalizarDocumentoLogin(valor);
47    }
48    return (valor || '').replace(/\D/g, '');
49  }
50
51  function validarDocumentoLogin(valor) {
52    const documento = obterDocumentoLogin(valor);
53    if (/^\d{11}$/.test(documento)) {
54      return validarCPF(documento);
55    }
56    if (isLoginCpfCnpjHabilitado() && /^[A-Z0-9]{14}$/.test(documento)) {
57      return validarCNPJ(documento);
58    }
59    return false;
60  }
61
62  function destacarPrimeiroCampoInvalido(form) {
63    if (!form) return;
64    const campos = form.querySelectorAll('input, select, textarea');
65    for (const campo of campos) {
66      if (campo.type === 'hidden' || campo.disabled) continue;
67      if (campo.checkValidity()) continue;
68
69      campo.classList.add('invalid');
70      campo.classList.remove('valid');
71
72      const feedback = campo.parentElement?.nextElementSibling;
73      if (feedback?.classList?.contains('invalid-feedback')) {
74        feedback.classList.add('show');
75      }
76
77      if (campo.id) {
78        scrollAndFocus(campo.id);
79      } else {
80        campo.scrollIntoView({ behavior: 'smooth', block: 'center' });
81        campo.focus();
82      }
83      return;
84    }
85  }
86
87  function resetValidation(formId) {
88    const form = document.getElementById(formId);
89    if (!form) return;
90
91    // limpa classes de validação nos inputs/selects/textarea
92    form.querySelectorAll('.valid, .invalid')
93        .forEach(el => el.classList.remove('valid','invalid'));
94
95    // limpa mensagens/customValidity e feedbacks
96    form.querySelectorAll('input, select, textarea').forEach(el => {
97      el.setCustomValidity('');
98      el.removeAttribute('aria-invalid');
99    });
100    form.querySelectorAll('.invalid-feedback, .valid-feedback')
101        .forEach(fb => fb.classList.remove('show'));
102  }
103
104  let cadastroData = {};
105  let loginData = {};
106
107  // Estado do fluxo de token no cadastro.
108  // A decisão final é feita pelo retorno de "solicitar/token":
109  // status false -> pula step de token e vai para criação de senha.
110  const LOGIN_FLOW_FLAGS = {
111    enableSignupTokenStep: true,
112  };
113
114  function isSignupTokenStepEnabled() {
115    return !!LOGIN_FLOW_FLAGS.enableSignupTokenStep;
116  }
117
118  function disableFieldsInForm(formId, disabled) {
119      const form = document.getElementById(formId);
120      if (!form) return;
121      const fields = form.querySelectorAll('input, select, textarea, button');
122      fields.forEach((field) => {
123          if (field.id === 'btn-verificar-cpf') return; // mantém ação do botão principal
124          field.disabled = !!disabled;
125      });
126    }
127
128  function getFormObject(formId) {
129      const form = document.getElementById(formId);
130      if (!form) return {};
131      const fields = form.querySelectorAll('input, select, textarea');
132      const data = {};
133      fields.forEach((field) => {
134          const name = field.name;
135          if (!name || field.disabled) return;
136          if (field.type === 'radio') {
137              if (field.checked) data[name] = field.value;
138          } else if (field.type === 'checkbox') {
139              data[name] = field.checked;
140          } else {
141              data[name] = field.value;
142          }
143      });
144      return data;
145    }
146
147  function mostrarFormRetornaCadastroCpf() {
148    document.getElementById('form_cpf').classList.remove('hidden');
149    document.querySelectorAll('#form_cadastro_encontrado, #form_cadastro_nao_encontrado, #form_valida_token, #redefinir-senha')
150        .forEach(el => el.classList.add('hidden'));
151    document.getElementById('email-mascarado').innerHTML = '';
152    document.getElementById('btn-verificar-cpf').classList.remove('hidden');
153    disableFieldsInForm('form_cadastro_encontrado', true);
154    disableFieldsInForm('form_cadastro_nao_encontrado', true);
155    disableFieldsInForm('form_valida_token', true);
156    disableFieldsInForm('redefinir-senha', true);
157    disableFieldsInForm('form_redefinir_senha', true);
158    disableFieldsInForm('form_finaliza_cadastro', true);
159  }
160
161  function mostrarFormCadastroEncontrado() {
162      document.getElementById('form_cpf').classList.remove('hidden');
163      document.getElementById('form_cadastro_encontrado').classList.remove('hidden');
164      document.querySelectorAll('#form_cadastro_nao_encontrado, #form_valida_token, #form_finaliza_cadastro, #form_redefinir_senha')
165        .forEach(el => el.classList.add('hidden'));
166      disableFieldsInForm('form_cadastro_encontrado', false);
167      disableFieldsInForm('form_cadastro_nao_encontrado', true);
168      disableFieldsInForm('form_valida_token', true);
169      disableFieldsInForm('form_redefinir_senha', true);
170      disableFieldsInForm('form_finaliza_cadastro', true);
171    }
172    
173  function mostrarFormCadastroNaoEncontrado() {
174      document.getElementById('form_cpf').classList.remove('hidden');
175      document.getElementById('form_cadastro_nao_encontrado').classList.remove('hidden');
176      document.querySelectorAll('#form_cadastro_encontrado, #form_valida_token, #redefinir-senha, #form_finaliza_cadastro, #form_redefinir_senha')
177        .forEach(el => el.classList.add('hidden'));
178      disableFieldsInForm('form_cadastro_encontrado', true);
179      disableFieldsInForm('form_cadastro_nao_encontrado', false);
180      disableFieldsInForm('form_valida_token', true);
181      disableFieldsInForm('redefinir-senha', true);
182      disableFieldsInForm('form_finaliza_cadastro', true);
183      disableFieldsInForm('form_redefinir_senha', true);
184      disableFieldsInForm('form_finaliza_cadastro', true);
185  }
186
187  function mostrarFormToken(is_reset = false) {
188      TokenCtx.setFlow(is_reset ? 'reset' : 'cadastro');
189      document.getElementById('form_cpf').focus()
190      document.querySelectorAll('#form_cpf, #form_cadastro_encontrado, #form_cadastro_nao_encontrado, #redefinir-senha, #form_finaliza_cadastro, #form_redefinir_senha')
191      .forEach(el => el.classList.add('hidden'));
192      document.getElementById('form_valida_token').classList.remove('hidden')
193      document.getElementById('form_valida_token').classList.remove('valid')
194      disableFieldsInForm('form_cadastro_encontrado', true);
195      disableFieldsInForm('form_cadastro_nao_encontrado', true);
196      disableFieldsInForm('form_valida_token', false);
197      disableFieldsInForm('redefinir-senha', true);
198      disableFieldsInForm('form_redefinir_senha', true);
199      disableFieldsInForm('form_finaliza_cadastro', true);
200      if (is_reset) {
201        document.getElementById('enviar-token-celular').classList.add('hidden')
202        const email = document.getElementById('email-redefinir').value;
203        const emailMask = document.getElementById('email-mascarado');
204        if (emailMask) emailMask.textContent = mascararEmail(email);
205      }
206      scrollToTop();
207      iniciarTimer();
208  }
209
210  function mostrarFormCadastraSenha(is_reset = false) {
211    document.getElementById('form_finaliza_cadastro').reset();
212    document.getElementById('form_redefinir_senha').reset();
213    document.querySelectorAll('#form_cpf, #form_cadastro_encontrado, #form_cadastro_nao_encontrado, #redefinir-senha, #form_valida_token, #form_finaliza_cadastro, #form_redefinir_senha')
214      .forEach(el => el.classList.add('hidden'));
215    disableFieldsInForm('form_cadastro_encontrado', true);
216    disableFieldsInForm('form_cadastro_nao_encontrado', true);
217    disableFieldsInForm('redefinir-senha', true);
218    disableFieldsInForm('form_valida_token', true);
219    disableFieldsInForm('form_finaliza_cadastro', true);
220    disableFieldsInForm('form_redefinir_senha', true);
221    if(is_reset) {
222      document.getElementById('form_redefinir_senha').classList.remove('hidden')
223      disableFieldsInForm('form_redefinir_senha', false);
224    } else {
225      document.getElementById('form_finaliza_cadastro').classList.remove('hidden')
226      disableFieldsInForm('form_finaliza_cadastro', false);
227    }
228    scrollToTop();
229  }
230
231  function navegarPara(formId, is_reset = false) {
232      document.getElementById('btn-verificar-cpf').classList.add('hidden')
233      switch (formId) {
234          case 'form_cadastro_encontrado':
235              mostrarFormCadastroEncontrado();
236              document.getElementById('retorna_cadastro_cpf').classList.remove('hidden')
237              break;
238          case 'form_cadastro_nao_encontrado':
239              mostrarFormCadastroNaoEncontrado();
240              document.getElementById('retorna_cadastro_cpf').classList.remove('hidden')
241              break;
242          case 'form_valida_token':
243              document.getElementById('enviar-token').classList.add('hidden')
244              mostrarFormToken(is_reset);
245              break;
246          case 'form_finaliza_cadastro':
247              mostrarFormCadastraSenha();
248              break;
249          default:
250              console.warn(`Formulário desconhecido: ${formId}`);
251      }
252  }
253
254  const TokenCtx = (() => {
255    let tokenId = null;     
256    let flow   = null;      // 'reset' | 'signup'
257
258    return {
259      setFlow: (t) => flow = t,
260      getFlow: () => flow,
261      setTokenId: (id) => tokenId = id,
262      getTokenId: () => tokenId,
263      takeTokenId: () => { const t = tokenId; tokenId = null; return t; },
264      clear: () => { tokenId = null; flow = null; }
265    };
266  })();
267
268  function getCode6() {
269    return Array.from(document.querySelectorAll('.codigo-token'))
270      .map(i => (i.value || '').replace(/\D/g,''))
271      .join('');
272  }
273
274  //Chama funcao global
275  async function executarRecaptchaGoogle(action = 'LOGIN') {
276    if (!window.SecurityRecaptcha || typeof window.SecurityRecaptcha.execute !== 'function') {
277      if (window.toastr) toastr.error('Verificação de segurança indisponível. Atualize a página e tente novamente.');
278      return null;
279    }
280    return window.SecurityRecaptcha.execute(action);
281  }
282
283  async function enviarToken(tipo_envio, dat_nasc = false) {
284
285    let celular;
286    let email;
287
288      if(tipo_envio === 'celular') {
289          const telEl = document.getElementById('celular');
290          celular = (telEl ? telEl.value : '').replace(/\D/g, '');
291      } else if (tipo_envio === 'email') {
292          const emailEl = document.getElementById('email');
293          const emailRedef = document.getElementById('email-redefinir');
294          email = emailEl.value ? emailEl.value : emailRedef.value ? emailRedef.value : '';
295          
296          if (!validarEmail(email)) {
297              exibirErroCampoEmail(emailEl || emailRedef, 'Por favor, insira um email válido.');
298              return;
299          }
300      } else {
301          return;
302      }
303      const recaptchaToken = await executarRecaptchaGoogle('LOGIN');
304      if (recaptchaToken === null) return;
305
306      const data_nascimento = document.getElementById('dat_nasc').value;
307
308      load('on', document.body);
309
310      let payload = {};
311      if (email) payload = { email, tipo: TokenCtx.getFlow() };
312      if (celular) payload = { celular, tipo: TokenCtx.getFlow() };
313      if (TokenCtx.getFlow() === 'cadastro' && dat_nasc && data_nascimento) payload = { email, tipo: TokenCtx.getFlow(), dat_nasc: data_nascimento };
314      if (recaptchaToken) payload.recaptcha = recaptchaToken;
315
316      const resp = await fetch("solicitar/token", { 
317        method: "POST",
318        headers: {
319          "Content-Type": "application/json",
320          "X-CSRFToken": window.CSRF_TOKEN(),
321        },
322        body: JSON.stringify(payload),
323      });
324
325      const data = await resp.json();
326
327      const verificarAuth = (
328        TokenCtx.getFlow() === 'cadastro' &&
329        tipo_envio === 'email' &&
330        resp.ok &&
331        data &&
332        data.dual_auth !== true
333      );
334
335      if (verificarAuth) {
336        LOGIN_FLOW_FLAGS.enableSignupTokenStep = false;
337        TokenCtx.setTokenId(null);
338        //if (window.toastr) toastr.info(data.descricao || 'Cadastro sem validação por token.');
339        navegarPara('form_finaliza_cadastro');
340        load('off', document.body);
341        return;
342      }
343
344      const ok  = resp.ok && data.status && data.token_id;
345      if(ok) {
346        LOGIN_FLOW_FLAGS.enableSignupTokenStep = true;
347        TokenCtx.setTokenId(data.token_id);
348      }
349
350      if (ok) {
351          if(tipo_envio === 'celular') {
352              if (window.toastr) toastr.success('Verifique seu celular e informe o código no campo sinalizado.', 'Token enviado com sucesso');
353              const avisoToken = document.getElementById('aviso_token');
354              avisoToken.innerHTML = 'Enviamos um código para o celular: <span id="telefone-mascarado"></span>';
355              avisoToken.classList.add('mb-2');
356              const telMask = document.getElementById('telefone-mascarado');
357              if (telMask) telMask.textContent = mascararTelefone(celular);
358              const lixo = document.getElementById('aviso_lixo_eletronico');
359              if (lixo) lixo.style.display = 'none';
360          } else {
361              if (window.toastr) toastr.success('Verifique seu e-mail e informe o código no campo sinalizado.', 'Token enviado com sucesso');
362              const avisoToken = document.getElementById('aviso_token');
363              avisoToken.innerHTML = 'Enviamos um código para o e-mail: <span id="email-mascarado"></span>';
364              avisoToken.classList.remove('mb-2');
365              const emailMask = document.getElementById('email-mascarado');
366              if (emailMask) emailMask.textContent = mascararEmail(email);
367              const lixo = document.getElementById('aviso_lixo_eletronico');
368              if (lixo) lixo.style.display = '';
369          }
370          navegarPara('form_valida_token')
371      } else {
372          if (data.idade_minima) {
373            document.getElementById('min-data').classList.add('show');
374            document.getElementById('dat_nasc').classList.add('invalid');
375            document.getElementById('dat_nasc').classList.remove('valid');
376            document.getElementById('min-data').textContent = data.idade_minima;
377            scrollAndFocus('dat_nasc');
378          }
379          if (tipo_envio === 'email') {
380            const emailElErro = document.getElementById('email') || document.getElementById('email-redefinir');
381            const mensagemErroEmail = data.email || data.descricao;
382            if (mensagemErroEmail) {
383              exibirErroCampoEmail(emailElErro, mensagemErroEmail);
384            }
385          }
386          if (window.toastr) toastr.error(data.descricao ? data.descricao : 'Erro ao enviar token', 'Erro');
387      }
388
389      load('off', document.body);
390  }
391
392  async function validarCodigoToken() {
393    const token = getCode6();
394    if (token.length !== 6) {
395      toastr?.error('Informe os 6 dígitos.');
396      return;
397    }
398    const email = document.getElementById('email').value || document.getElementById('email-redefinir').value;
399    const celular = document.getElementById('celular').value || '';
400
401    load('on', document.body);
402    try {
403      const resp = await fetch("confirmar/token", {
404        method: "POST",
405        headers: { "Content-Type": "application/json", "X-CSRFToken": window.CSRF_TOKEN() },
406        body: JSON.stringify({ token, email, celular, tipo: TokenCtx.getFlow() })
407      });
408      const data = await resp.json();
409
410      if (resp.ok && data.status) {
411        //Campos de senha conforme o fluxo:
412        if (TokenCtx.getFlow() === 'reset') {
413          mostrarFormCadastraSenha(true);
414        } else {
415          mostrarFormCadastraSenha(false);
416        }
417      } else {
418        toastr?.error(data.descricao || 'Código inválido ou expirado.');
419      }
420    } finally {
421      document.querySelectorAll('.codigo-token').forEach(i => i.value = '');
422      load('off', document.body);
423    }
424  }
425
426  document.getElementById('form_valida_token')?.addEventListener('submit', (e) => {
427    e.preventDefault();
428    e.stopPropagation();
429    validarCodigoToken();
430  });
431
432  document.getElementById('form_redefinir_senha')?.addEventListener('submit', async (e) => {
433    e.preventDefault();
434
435    const form = e.currentTarget;
436    const novaSenhaEl = document.getElementById('senha_redefinir');
437    const confirmaEl  = document.getElementById('senha_redefinir_confirmacao');
438    const novaSenha   = novaSenhaEl?.value || '';
439    const confirma    = confirmaEl?.value || '';
440
441    // força de senha e confirmação
442    exibeErrosSenha(novaSenha, true);
443    if (confirmaEl) {
444      const ok = validaSenhaConfirmacao(novaSenha, confirma);
445      confirmaEl.setCustomValidity(ok ? '' : 'As senhas não conferem!');
446    }
447
448    if(form && form.checkValidity()) {
449      const tokenId   = TokenCtx.getTokenId();
450      if (!tokenId) {
451        toastr?.error('Código expirado. Reenvie o token.');
452        return;
453      }
454
455      const recaptchaToken = await executarRecaptchaGoogle('LOGIN');
456      if (recaptchaToken === null) {
457        return;
458      }
459
460      const redefinirPayload = { nova_senha: novaSenha, token: tokenId };
461      if (recaptchaToken) redefinirPayload.recaptcha = recaptchaToken;
462      redefinir_senha(redefinirPayload);
463
464      //if (!tokenId) { toastr?.error('Código expirado. Reenvie o token.'); return; }
465      
466    } else {
467      toastr?.error('Verifique os campos em destaque.');
468    }
469  });
470
471  async function redefinir_senha(dados) {
472    load('on', document.body);
473      try {
474        const resp = await fetch("alterar/senha", {
475          method: "POST",
476          headers: { "Content-Type": "application/json", "X-CSRFToken": window.CSRF_TOKEN() },
477          body: JSON.stringify(dados)
478        });
479        const data = await resp.json();
480
481        if (resp.ok && data.status) {
482          TokenCtx.clear();
483          toastr?.success('Senha redefinida com sucesso!');
484          //TokenCtx.clear();
485          window.location.href = '/login';
486        } else {
487          toastr?.error(data.descricao || 'Não foi possível redefinir a senha.');
488        }
489      } finally {
490        load('off', document.body);
491      }
492    
493  }
494
495  document.getElementById('form_cadastro_nao_encontrado')?.addEventListener('submit', async function(event){
496      const cpfverificado = document.getElementById('username')
497      const cpf = document.getElementById('documento');
498      cpf.value = cpfverificado.value;
499      
500      var sexo = document.getElementsByName('sexo');
501      var data = document.getElementsByName('dat_nasc')[0];
502      var tel = document.getElementById('celular');
503      const emailCadastro = document.getElementById('email');
504      const form = document.getElementById('form_cadastro_nao_encontrado');
505      const is_data_visivel = isCampoVisivel(data);
506
507      var aux = 0;
508      sexo.forEach((value) => {
509          if (value.checked) {
510              aux++
511          }
512      });
513      // garantir obrigatoriedade do grupo e refletir no validity
514      Array.from(sexo).forEach(r => r.required = true);
515      if (aux > 0) {
516          document.getElementsByName('sexo').forEach(value => value.classList.remove('invalid'));
517          Array.from(sexo).forEach(r => r.setCustomValidity(''));
518      } else {
519          document.getElementsByName('sexo').forEach(value => value.classList.add('invalid'));
520          if (sexo[0]) sexo[0].setCustomValidity('Selecione uma opção');
521      }
522      if (!validarCampoCelular(tel)) {
523        tel.focus();
524      }
525      if(validarDocumentoLogin(cpf.value)){
526        cpf.setCustomValidity('');
527      } else {
528        document.getElementById('invalid_cpf').classList.add('show');
529        cpf.classList.add('invalid');
530        cpf.classList.remove('valid');
531      }
532      
533      // Exige data de nascimento apenas quando o campo estiver visível.
534      if (data) {
535        data.required = is_data_visivel;
536      }
537      if (is_data_visivel) {
538        if (compareDates(data.value) && data.value && data.value.length === 10) {
539          document.getElementById('invalid-data').classList.remove('show');
540          data.classList.remove('invalid');
541          data.classList.add('valid');
542        } else {
543          if(data.value) {
544            document.getElementById('invalid-data').innerHTML = 'Informe uma data válida';
545            document.getElementById('invalid-data').classList.add('show');
546          }
547          data.classList.add('invalid');
548          data.classList.remove('valid');
549          data.focus();
550        }
551      } else if (data) {
552        document.getElementById('invalid-data')?.classList.remove('show');
553        data.classList.remove('invalid');
554        data.classList.remove('valid');
555        if (!data.value) {
556          data.value = '01/01/2000';
557        }
558      }
559      if (!validarCampoEmail(emailCadastro)) {
560        scrollAndFocus('email');
561      }
562      if (this.checkValidity() === false) {
563          event.preventDefault();
564          event.stopPropagation();
565          destacarPrimeiroCampoInvalido(form);
566          toastr?.error('Verifique os campos em destaque.');
567      } else {
568          event.preventDefault();
569          event.stopPropagation();
570          cadastroData = Object.assign({}, getFormObject('form_cpf'), getFormObject('form_cadastro_nao_encontrado'));
571          if (!is_data_visivel) {
572            cadastroData.dat_nasc = '01/01/2000';
573          }
574          TokenCtx.setFlow('cadastro');
575          LOGIN_FLOW_FLAGS.enableSignupTokenStep = true;
576          enviarToken('email', true);
577      }
578      this.classList.add('was-validated');
579      
580  });
581
582  document.getElementById('btn-sair')?.addEventListener('click', function (event) {
583      event.preventDefault();
584      logout();
585  });
586  document.getElementById('btn-reenviar-token')?.addEventListener('click', function (event) {
587      event.preventDefault();
588      enviarToken('email');
589  });
590  document.getElementById('retorna_cadastro')?.addEventListener('click', function (event) {
591      event.preventDefault();
592      if(TokenCtx.getFlow() === 'reset') {
593        document.getElementById('form_cpf')?.classList.remove('hidden');
594        document.getElementById('btn-verificar-cpf')?.classList.remove('hidden');
595        document.getElementById('form_valida_token')?.classList.add('hidden');
596        document.getElementById('form_cpf')?.focus();
597      } else {
598        navegarPara('form_cadastro_nao_encontrado')
599      }
600  });
601  document.getElementById('retorna_cadastro2')?.addEventListener('click', function (event) {
602      event.preventDefault();
603      navegarPara('form_cadastro_nao_encontrado')
604    });
605    document.getElementById('retorna_redefinir')?.addEventListener('click', function (event) {
606      event.preventDefault();
607      TokenCtx.clear();
608      document.getElementById('redefinir-senha')?.classList.add('hidden');
609      document.getElementById('form_redefinir_senha')?.classList.add('hidden');
610      document.getElementById('form_valida_token')?.classList.add('hidden');
611      mostrarFormRetornaCadastroCpf();
612      document.getElementById('form_cpf')?.classList.remove('hidden');
613      document.getElementById('retorna_cadastro_cpf')?.classList.add('hidden');
614
615      // document.getElementById('btn-verificar-cpf')?.classList.remove('hidden');
616      // document.getElementById('form_cpf')?.focus();
617  });
618
619  document.getElementById('retorna_cadastro_cpf')?.addEventListener('click', function (event) {
620      event.preventDefault();
621      cadastroData = {};
622      loginData = {};
623      document.getElementById('form_cpf')?.classList.remove('valid');
624      document.getElementById('form_cadastro_nao_encontrado')?.classList.remove('valid');
625      document.getElementById('form_cadastro_encontrado')?.classList.remove('valid');
626      document.getElementById('form_valida_token')?.classList.remove('valid');
627      document.querySelectorAll('#form_cadastro_encontrado, #form_cadastro_nao_encontrado, #form_valida_token, #btn-reenviar-token, #enviar-token, #redefinir-senha')
628      .forEach(el => el.classList.add('hidden'));
629      
630      const telMask = document.getElementById('telefone-mascarado');
631      if (telMask) telMask.textContent = '';
632      const emailMask = document.getElementById('email-mascarado');
633      if (emailMask) emailMask.textContent = '';
634      document.getElementById('btn-verificar-cpf')?.classList.remove('hidden');
635      document.getElementById('form_cpf')?.classList.remove('hidden');
636      document.getElementById('retorna_cadastro_cpf')?.classList.add('hidden');
637  });
638
639  const tempoEnvio = 60;
640  let timerEnvio;
641
642  document.getElementById('btn-enviar-token-telefone')?.addEventListener('click', function (event) {
643      event.preventDefault();
644      enviarToken('celular');
645      const enviarCel = document.getElementById('enviar-token-celular');
646      const timerCel = document.getElementById('timer-token-celular');
647      enviarCel && enviarCel.classList.add('hidden');
648      timerCel && timerCel.classList.remove('hidden');
649      let contador = tempoEnvio;
650      const contadorEl = document.getElementById('contador-token');
651      if (contadorEl) contadorEl.textContent = String(contador);
652      timerEnvio = setInterval(() => {
653          contador--;
654          if (contadorEl) contadorEl.textContent = String(contador);
655          if (contador <= 0) {
656              clearInterval(timerEnvio);
657              timerCel && timerCel.classList.add('hidden');
658              enviarCel && enviarCel.classList.remove('hidden');
659          }
660      }, 1000);
661  });
662
663  function limparCamposToken() {
664      document.querySelectorAll('.codigo-token').forEach(el => el.value = '');
665      const hidden = document.getElementById('token_hidden');
666      if (hidden) hidden.value = '';
667      const focus = document.getElementById('focus_token');
668      focus && focus.focus();
669  }
670
671  document.getElementById('form_finaliza_cadastro')?.addEventListener('submit', async function(event){
672    event.preventDefault();
673    event.stopPropagation();
674
675    const cpfUsername = (getFormObject('form_cpf') || {}).username;
676
677    // força de senha e confirmação antes de checar validade do form
678    const senhaEl = document.getElementById('senha_cadastro');
679    const confirmaEl = document.getElementById('senha_cadastro_confirmacao');
680    const senha = senhaEl?.value || '';
681    const confirma = confirmaEl?.value || '';
682    exibeErrosSenha(senha, false);
683    if (confirmaEl) {
684      const ok = validaSenhaConfirmacao(senha, confirma);
685      confirmaEl.setCustomValidity(ok ? '' : 'As senhas não conferem!');
686      if(!ok) toastr?.error('Verifique as senhas e tente novamente.', 'Erro');
687    }
688
689    if (this.checkValidity() === true) {
690      disableFieldsInForm('form_cadastro_nao_encontrado', false);
691      let payload = Object.assign({}, getFormObject('form_finaliza_cadastro'), getFormObject('form_cadastro_nao_encontrado'));
692      const tokenId = TokenCtx.takeTokenId();
693
694      if (isSignupTokenStepEnabled()) {
695        if (!tokenId) {
696          toastr?.error('Código expirado. Reenvie o token.');
697          return;
698        }
699        payload.token_id = tokenId;
700      }
701
702      const recaptchaToken = await executarRecaptchaGoogle('LOGIN');
703      if (recaptchaToken === null) {
704        return;
705      }
706
707      if (recaptchaToken) payload.recaptcha = recaptchaToken;
708      cadastrar_usuario(payload);
709      //TokenCtx.clear();
710    }
711  });
712
713  function validarEmail(email) {
714      const valor = (email || '').trim().toLowerCase();
715      if (!valor || valor.length > 254) return false;
716
717      const regex = /^[a-z0-9._%+-]+@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,6}$/;
718      if (!regex.test(valor)) return false;
719
720      const partes = valor.split('@')[1].split('.');
721      const tld = partes[partes.length - 1];
722
723      if (partes.length === 2) {
724        if (tld.length < 3) return false;
725        if (tld.length >= 4 && !/[aeiou]/.test(tld)) return false;
726      }
727
728      return true;
729  }
730
731  function buscarFeedbackServidorEmail(inputEl) {
732    if (!inputEl) return null;
733    if (inputEl.id === 'email-redefinir') {
734      return document.getElementById('invalid-email-redefinir');
735    }
736    return document.getElementById('invalid-email');
737  }
738
739  function exibirErroCampoEmail(inputEl, mensagem, feedbackServidorEl = null) {
740    if (!inputEl) return;
741
742    const feedbackServidor = feedbackServidorEl || buscarFeedbackServidorEmail(inputEl);
743    const feedbackGenerico = inputEl.parentElement?.nextElementSibling;
744
745    inputEl.classList.add('invalid');
746    inputEl.classList.remove('valid');
747    inputEl.setCustomValidity(mensagem || 'Erro no e-mail');
748
749    if (feedbackGenerico?.classList?.contains('invalid-feedback') && feedbackGenerico !== feedbackServidor) {
750      feedbackGenerico.classList.remove('show');
751    }
752
753    if (feedbackServidor) {
754      feedbackServidor.textContent = mensagem || '';
755      feedbackServidor.classList.add('show');
756    }
757
758    if (inputEl.id) {
759      scrollAndFocus(inputEl.id);
760    } else {
761      inputEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
762      inputEl.focus();
763    }
764  }
765
766  function limparErroServidorEmail(inputEl) {
767    const feedbackServidor = buscarFeedbackServidorEmail(inputEl);
768    if (feedbackServidor) {
769      feedbackServidor.classList.remove('show');
770      feedbackServidor.textContent = '';
771    }
772  }
773
774  function validarCampoEmail(inputEl, feedbackEl = null) {
775    if (!inputEl) return true;
776    const valor = (inputEl.value || '').trim();
777    const valido = !!valor && validarEmail(valor);
778    const feedbackPadrao = feedbackEl || inputEl.parentElement?.nextElementSibling;
779    const feedbackServidor = buscarFeedbackServidorEmail(inputEl);
780
781    if (valido) {
782      inputEl.classList.remove('invalid');
783      inputEl.classList.add('valid');
784      inputEl.setCustomValidity('');
785      limparErroServidorEmail(inputEl);
786      if (feedbackPadrao?.classList?.contains('invalid-feedback')) {
787        feedbackPadrao.classList.remove('show');
788      }
789    } else {
790      inputEl.classList.add('invalid');
791      inputEl.classList.remove('valid');
792      inputEl.setCustomValidity('Informe um e-mail válido');
793      if (feedbackPadrao?.classList?.contains('invalid-feedback')) {
794        feedbackPadrao.textContent = 'Informe um e-mail válido';
795        feedbackPadrao.classList.add('show');
796      }
797      if (feedbackServidor && feedbackServidor !== feedbackPadrao) {
798        feedbackServidor.classList.remove('show');
799        feedbackServidor.textContent = '';
800      }
801    }
802
803    return valido;
804  }
805
806  function validarCampoCelular(inputEl) {
807    if (!inputEl) return true;
808    const valido = (inputEl.value || '').length >= 15;
809    const feedback = inputEl.parentElement?.nextElementSibling;
810
811    if (valido) {
812      inputEl.classList.remove('invalid');
813      inputEl.classList.add('valid');
814      inputEl.setCustomValidity('');
815      if (feedback?.classList?.contains('invalid-feedback')) {
816        feedback.classList.remove('show');
817      }
818    } else {
819      inputEl.classList.add('invalid');
820      inputEl.classList.remove('valid');
821      inputEl.setCustomValidity('Informe um celular válido');
822      if (feedback?.classList?.contains('invalid-feedback')) {
823        feedback.classList.add('show');
824      }
825    }
826
827    return valido;
828  }
829
830  document.addEventListener('input', function(e) {
831      if (e.target && e.target.classList.contains('codigo-token')) {
832          const el = e.target;
833          el.value = (el.value || '').replace(/\D/g, '');
834          if (el.value.length === 1) {
835              const next = el.nextElementSibling;
836              if (next && next.classList.contains('codigo-token')) next.focus();
837          }
838          atualizarTokenHidden();
839      }
840  });
841
842  document.addEventListener('keydown', function(e) {
843      if (e.target && e.target.classList.contains('codigo-token')) {
844          const el = e.target;
845          if (e.key === 'Backspace' && !el.value) {
846              const prev = el.previousElementSibling;
847              if (prev && prev.classList.contains('codigo-token')) prev.focus();
848          }
849          // Quando pressionar Enter no campo de token, submeter apenas o formulário de validação de token
850          if (e.key === 'Enter') {
851              e.preventDefault();
852              e.stopPropagation();
853              const formToken = document.getElementById('form_valida_token');
854              if (formToken && !formToken.classList.contains('hidden')) {
855                  // Verifica se todos os 6 dígitos foram preenchidos antes de submeter
856                  const token = getCode6();
857                  if (token.length === 6) {
858                      formToken.requestSubmit();
859                  } else {
860                      // Se não estiver completo, apenas move para o próximo campo
861                      const next = el.nextElementSibling;
862                      if (next && next.classList.contains('codigo-token')) {
863                          next.focus();
864                      }
865                  }
866              }
867          }
868      }
869  });
870
871  function atualizarTokenHidden() {
872      let token = '';
873      document.querySelectorAll('.codigo-token').forEach(inp => token += (inp.value || ''));
874      const hidden = document.getElementById('token_hidden');
875      if (hidden) hidden.value = token;
876  }
877
878  document.addEventListener('paste', function(e) {
879      if (e.target && e.target.classList.contains('codigo-token')) {
880          e.preventDefault();
881          const pasted = (e.clipboardData || window.clipboardData).getData('text').replace(/\D/g, '');
882          const inputs = Array.from(document.querySelectorAll('.codigo-token'));
883          inputs.forEach((inp, i) => inp.value = pasted[i] || '');
884          if (pasted.length === 6 && inputs[5]) inputs[5].focus();
885          atualizarTokenHidden();
886      }
887  });
888
889  let intervalo; 
890
891  function iniciarTimer() {
892      let tempo = 59;
893      const botao = document.getElementById('enviar-token');
894      const timerDisplay = document.getElementById('timer');
895      if (botao) botao.classList.add('hidden');
896      if (intervalo) clearInterval(intervalo);
897      intervalo = setInterval(function() {
898          if (tempo > 0) {
899              if (timerDisplay) timerDisplay.textContent = 'Reenviar em ' + tempo + 's';
900              tempo--;
901          } else {
902              if (botao) botao.classList.remove('hidden');
903              if (timerDisplay) timerDisplay.textContent = '';
904              clearInterval(intervalo);
905              intervalo = null;
906          }
907      }, 1000);
908  }
909
910  function mascararEmail(email) {
911      const [usuario, dominio] = email.split('@');
912
913      if (usuario.length <= 3) {
914        return `${usuario}@${dominio}`;
915      }
916
917      const visiveis = usuario.slice(0, 3);
918      const mascarados = '*'.repeat(usuario.length - 3);
919
920      return `${visiveis}${mascarados}@${dominio}`;
921  }
922
923  function mascararTelefone(telefone) {
924      const ddd = telefone.slice(0, 2);
925      const primeiroDigito = telefone.slice(2, 3);
926      const ultimosDigitos = telefone.slice(-2);
927      const mascarados = '*'.repeat(telefone.length - 2 - 3); 
928
929      return `(${ddd}) ${primeiroDigito}${mascarados}${ultimosDigitos}`;
930  }
931
932  function scrollToTop(){
933    window.scrollTo({ top: 0, behavior: 'smooth' });
934  }
935
936  // document.querySelectorAll('.input-busca').forEach(inp => {
937  //     inp.addEventListener('focus', function(){ onoffbody && onoffbody('off'); });
938  //     inp.addEventListener('blur', function(){ onoffbody && onoffbody('on'); });
939  // });
940
941  document.getElementById('email-redefinicao')?.addEventListener('submit', async function (event) {
942    event.preventDefault();
943
944    const form  = this;
945    const input = document.getElementById('email-redefinir');
946    const email = (input?.value || '').trim();
947
948    if (!validarEmail(email)) {
949       input.classList.add('invalid');
950       input.classList.remove('valid');
951       document.getElementById('invalid-email-redefinir').classList.add('show');
952      return;
953    }
954    document.getElementById('invalid-email-redefinir').classList.remove('show');
955    input.classList.remove('invalid');
956    input.classList.add('valid');
957
958    const recaptchaToken = await executarRecaptchaGoogle('LOGIN');
959    if (recaptchaToken === null) return;
960
961    load('on', document.body);
962
963    const resp = await fetch("solicitar/token", { 
964      method: "POST",
965      headers: {
966        "Content-Type": "application/json",
967        "X-CSRFToken": window.CSRF_TOKEN(),
968      },
969      body: JSON.stringify(recaptchaToken ? { email, tipo: 'reset', recaptcha: recaptchaToken } : { email, tipo: 'reset' }),
970    });
971
972    const data = await resp.json();
973
974    load('off', document.body);
975    const ok  = resp.ok && data.status && data.token_id;
976    if(ok) {
977      TokenCtx.setFlow('reset');
978      TokenCtx.setTokenId(data.token_id);
979      toastr.success(data.descricao || 'E-mail enviado com sucesso! Verifique sua caixa de entrada.');
980      if (data.dual_auth !== true) {
981        mostrarFormCadastraSenha(true);
982      } else {
983        navegarPara('form_valida_token', true);
984      }
985    } else {
986      document.getElementById('email-redefinir').classList.add('invalid');
987      document.getElementById('email-redefinir').classList.remove('valid');
988      document.getElementById('nao-encontrado-email-redefinir').classList.add('show');
989      toastr.error(data.descricao || 'Erro ao enviar e-mail. Verifique se o e-mail está cadastrado.');
990    }
991  });
992
993  document.getElementById('celular')?.addEventListener('blur', function () {
994    validarCampoCelular(this);
995  });
996
997  document.getElementById('celular')?.addEventListener('input', function () {
998    const valor = (this.value || '').trim();
999    if (!valor) {
1000      this.classList.remove('invalid', 'valid');
1001      this.setCustomValidity('');
1002      const feedback = this.parentElement?.nextElementSibling;
1003      if (feedback?.classList?.contains('invalid-feedback')) {
1004        feedback.classList.remove('show');
1005      }
1006      return;
1007    }
1008    validarCampoCelular(this);
1009  });
1010
1011  document.getElementById('email')?.addEventListener('blur', function () {
1012    validarCampoEmail(this);
1013  });
1014
1015  document.getElementById('email')?.addEventListener('input', function () {
1016    limparErroServidorEmail(this);
1017    const valor = (this.value || '').trim();
1018    if (!valor) {
1019      this.classList.remove('invalid', 'valid');
1020      this.setCustomValidity('');
1021      return;
1022    }
1023    validarCampoEmail(this);
1024  });
1025
1026  document.getElementById('email-redefinir')?.addEventListener('blur', function () {
1027    validarCampoEmail(this, document.getElementById('invalid-email-redefinir'));
1028  });
1029
1030  document.getElementById('email-redefinir')?.addEventListener('input', function () {
1031    const valor = (this.value || '').trim();
1032    if (!valor) {
1033      this.classList.remove('invalid', 'valid');
1034      this.setCustomValidity('');
1035      document.getElementById('invalid-email-redefinir')?.classList.remove('show');
1036      return;
1037    }
1038    validarCampoEmail(this, document.getElementById('invalid-email-redefinir'));
1039  });
1040
1041
1042  function fechaAlert() {
1043      const el = document.getElementById('alert_envio-email');
1044      if (!el) return;
1045      el.remove();
1046  }
1047
1048
1049  const campos = document.getElementsByName('sexo'); // NodeList
1050
1051  Array.from(campos).forEach((el) => {
1052    el.addEventListener('change', () => {
1053      Array.from(campos).forEach((e) => e.classList.remove('is-invalid'));
1054    });
1055  });
1056
1057  function resetLoginValidity(campos = ['username','senha_login']) {
1058    for (const id of campos) document.getElementById(id)?.setCustomValidity('');
1059  }
1060  
1061  function markLoginInvalid({ descricao = 'Credenciais inválidas', campos = ['username','senha_login'] } = {}) {
1062    if (window.toastr) toastr.error(descricao, 'Erro no Login');
1063  
1064    const inputCpf   = document.getElementById('username');
1065    const inputSenha = document.getElementById('senha_login');
1066    document.getElementById('invalid_senha_login').classList.add('show');
1067    // marca erro uma vez
1068    for (const id of campos) document.getElementById(id)?.classList.add('invalid');
1069  
1070    // mensagens de feedback (se existirem)
1071    const fbCpf   = inputCpf && inputCpf.parentNode && inputCpf.parentNode.children[2];
1072    const fbSenha = inputSenha && inputSenha.parentNode && inputSenha.parentNode.children[2];
1073    if (fbCpf)   fbCpf.innerHTML   = descricao;
1074    if (fbSenha) fbSenha.innerHTML = '';
1075  
1076    // limpar erro quando o usuário voltar a digitar
1077    for (const name of campos) {
1078      const el = document.getElementsByName(name)[0];
1079      if (!el) continue;
1080      el.addEventListener('input', function clearValidity() {
1081        el.setCustomValidity('');
1082        el.removeEventListener('input', clearValidity);
1083      }, { once: true });
1084    }
1085  
1086    inputCpf?.focus();
1087  }
1088
1089  document.getElementById('form_cadastro_encontrado')?.addEventListener('submit', async function(event){
1090    event.preventDefault();
1091    event.stopPropagation();
1092
1093    const form = this;
1094
1095    // limpa erros customizados remanescentes
1096    resetLoginValidity();
1097
1098    // se quiser evitar campos required ocultos travando, desabilite-os:
1099    // form.querySelectorAll('[required]').forEach(el => {
1100    //   const hidden = el.offsetParent === null;
1101    //   if (hidden) el.setAttribute('disabled','disabled');
1102    //   else el.removeAttribute('disabled');
1103    // });
1104
1105    if (!form.checkValidity()) {
1106      form.classList.add('was-validated');
1107      form.reportValidity();
1108      return;
1109    }
1110    document.getElementById('username').classList.remove('invalid');
1111    document.getElementById('senha_login').classList.remove('invalid');
1112    document.getElementById('senha_login').classList.add('valid')
1113    document.getElementById('username').classList.add('valid');
1114    const username = obterDocumentoLogin(document.getElementById('username')?.value || '');
1115    const password = document.getElementById('senha_login')?.value || '';
1116
1117    const recaptchaToken = await executarRecaptchaGoogle('LOGIN');
1118    if (recaptchaToken === null) return;
1119
1120    const loginPayload = { username, password };
1121    if (recaptchaToken) loginPayload.recaptcha = recaptchaToken;
1122    logar_usuario(loginPayload);
1123  });
1124
1125  function compareDates(date) {
1126      let parts = date.split('/'); // separa a data pelo caracter '/'
1127      let today = new Date();      // pega a data atual
1128      date = new Date(parts[2], parts[1] - 1, parts[0]); // formata 'date'
1129      // compara se a data informada é maior que a data atual
1130      // e retorna true ou false
1131      return date >= today ? false : true
1132  }
1133
1134  async function cadastrar_usuario(dados) {
1135    load('on', document.body);
1136
1137        const device_id = localStorage.getItem('fmcToken');
1138        if (device_id) {
1139            dados.device_id = device_id;
1140            dados.location = localStorage.getItem('location');
1141        }
1142
1143        const resp = await fetch("cadastrar/cliente", { 
1144          method: 'POST', 
1145          headers: {
1146          "Content-Type": "application/json",
1147          "X-CSRFToken": window.CSRF_TOKEN(),
1148        },
1149          body: JSON.stringify(dados),
1150        });
1151        const data = await resp.json();
1152
1153        if (data.status) {
1154            try { if (typeof filtroParaDadosAnaliticos === 'function') filtroParaDadosAnaliticos('CompleteRegistration', { method: 'site_credentials' }); } catch(_){}
1155
1156            if (window.toastr) toastr.success('Cadastro realizado com sucesso!');
1157            setTimeout(function(){
1158                if (document.referrer !== '') {
1159                    window.location = document.referrer;
1160                    console.log('logou'); 
1161                    console.log('cliente_id:', dados.documento.replace(/\D/g,''));
1162                    console.log('NM_PRIMEIRO:', dados.nome.split(' ')[0].trim());
1163                } else {
1164                    window.location = '/';
1165                }
1166                //atualizar_melhor();
1167            }, 1000);
1168        } else {
1169            if (data.campos && data.campos.length > 0) {
1170                navegarPara('form_cadastro_nao_encontrado')
1171                const input = document.getElementsByName(data.campos[0])[0];
1172                if (input) {
1173                    input.setCustomValidity('Erro input');
1174                    const feedback = input.parentNode && input.parentNode.children[2];
1175                    if (feedback) feedback.innerHTML = data.descricao;
1176                    input.focus();
1177                }
1178            } else {
1179                limparCamposToken();
1180                if (window.toastr) toastr.error(data.descricao, 'Erro ao cadastrar cliente');
1181            }
1182        }
1183
1184        load('off', document.body);
1185  }
1186
1187
1188  async function logar_usuario(dados) {
1189    const recaptchaConfigurado = !!(window.recaptcha_site_key && window.recaptcha_site_key());
1190    if (recaptchaConfigurado && !dados.recaptcha) {
1191      if (window.toastr) toastr.error('Verificação de segurança obrigatória. Tente novamente.');
1192      return;
1193    }
1194    load('on', document.body);
1195    try {
1196      const device_id = localStorage.getItem('fmcToken');
1197      if (device_id) {
1198        dados.device_id = device_id;
1199        dados.location  = localStorage.getItem('location');
1200      }
1201
1202      const resp  = await fetch('login', {
1203        method: 'POST',
1204        headers: {
1205          'Content-Type': 'application/json',
1206          'X-CSRFToken': window.CSRF_TOKEN(),
1207        },
1208        body: JSON.stringify(dados),
1209      });
1210  
1211      const data = await resp.json();
1212  
1213      if (data.status) {
1214        document.getElementById('invalid_senha_login').classList.remove('show');
1215        document.getElementById('username').classList.remove('invalid');
1216        document.getElementById('username').classList.add('valid');
1217        document.getElementById('senha_login').classList.remove('invalid');
1218        document.getElementById('senha_login').classList.add('valid');
1219        
1220        try { if (typeof filtroParaDadosAnaliticos === 'function') filtroParaDadosAnaliticos('Login', { method: 'site_credentials' }); } catch(_){}
1221        
1222        console.log('logou');
1223        console.log('cliente_id:', dados.username);
1224        console.log('NM_PRIMEIRO:', data.nm_primeiro);
1225
1226        window.location = obterUrlPosLogin();
1227        return true;
1228      } else {
1229        // 👇 um único lugar cuidando dos erros de credencial
1230        markLoginInvalid({ descricao: data.descricao, campos: data.campos });
1231        return false;
1232      }
1233    } catch (e) {
1234      if (e && e.statusText !== 'error') {
1235        window.toastr && toastr.error('Ocorreu um erro inesperado ao fazer login.', 'Erro ao enviar');
1236      }
1237      return false;
1238    } finally {
1239      load('off', document.body);
1240    }
1241  }
1242  
1243
1244  // TODO: Verificar novos eventos.
1245  // function formAnalytics(dados){
1246  //     for (let dado in dados) {
1247  //         if (dado) {
1248  //             console.log(dado.value);
1249              
1250  //             filtroParaDadosAnaliticos('MenuAndCallToAction', {
1251  //                 event: 'select_content',
1252  //                 content_type: 'form',
1253  //                 content_id: 'form:' + dado,
1254  //             });
1255  //         }
1256  //     }
1257  //     filtroParaDadosAnaliticos('GenerateLead', {
1258  //         event: 'generate_lead',
1259  //         lead_source: 'pagina_cadastro',
1260  //     });
1261    
1262  // } 
1263
1264  // async function atualizar_melhor() {
1265  //     try {
1266  //         const resp = await fetch("ecommerce/carrinho/verificar_melhor", { method: 'POST', credentials: 'same-origin' });
1267  //         if (resp.ok) {
1268  //             if (typeof atualizarCarrinho === 'function') atualizarCarrinho('inicio');
1269  //         } else {
1270  //             if (window.toastr) toastr.error('Ocorreu um erro inesperado ao verificar o melhor carrinho.', 'Erro ao enviar');
1271  //         }
1272  //     } catch(e) {
1273  //         if (window.toastr) toastr.error('Ocorreu um erro inesperado ao verificar o melhor carrinho.', 'Erro ao enviar');
1274  //     }
1275  // }
1276
1277  /* Função para validar o cadastro */
1278
1279
1280  async function validarCadastroCPF(documento) {
1281      const f1 = document.getElementById('form_cadastro_encontrado');
1282      const f2 = document.getElementById('form_valida_token');
1283      const f3 = document.getElementById('form_cadastro_nao_encontrado');
1284      f1 && f1.reset();
1285      f2 && f2.reset();
1286      f3 && f3.reset();
1287
1288      load('on', document.body);
1289
1290          const csrf = window.CSRF_TOKEN();
1291          const body = new URLSearchParams();
1292          if (csrf) body.set('csrfmiddlewaretoken', csrf);
1293          body.set('documento', documento);
1294          const resp = await fetch("verificar/cadastro", { 
1295            method: "POST",
1296            headers: {
1297              "Content-Type": "application/json",
1298              "X-CSRFToken": window.CSRF_TOKEN(),
1299            },
1300            body: JSON.stringify({
1301              documento: documento,
1302            }),
1303          });
1304          const data = await resp.json();
1305          if (data.status) {
1306              if (data.is_existe) {
1307                  document.getElementById('btn-verificar-cpf')?.classList.add('hidden');
1308                  navegarPara('form_cadastro_encontrado');
1309                  document.getElementById('cadastro_nao_econtrado')?.classList.add('hidden');
1310                  document.getElementById('cadastro_econtrado')?.classList.remove('hidden');
1311                  document.getElementById('senha_login')?.focus();
1312              } else {
1313                  resetValidation('form_cadastro_nao_encontrado');
1314
1315                  document.getElementById('btn-verificar-cpf')?.classList.add('hidden');
1316                  navegarPara('form_cadastro_nao_encontrado');
1317                  document.getElementById('cadastro_econtrado')?.classList.add('hidden');
1318                  document.getElementById('cadastro_nao_econtrado')?.classList.remove('hidden');
1319              }
1320              const emailRedef = document.getElementById('email-redefinir');
1321              if (emailRedef) emailRedef.value = '';
1322              fechaAlert();
1323          } else {
1324              document.getElementById('btn-verificar-cpf')?.classList.remove('hidden');
1325              document.getElementById('cadastro_nao_econtrado')?.classList.add('hidden');
1326              document.getElementById('cadastro_econtrado')?.classList.add('hidden');
1327              const errCpf = document.getElementById('passwordErrorsCpf');
1328              const errSenha = document.getElementById('passwordErrorsSenha');
1329              const errCad = document.getElementById('passwordErrorsCadastro');
1330              if (errCpf) { errCpf.innerHTML = ''; errCpf.classList.remove('text-danger'); }
1331              if (errSenha) { errSenha.innerHTML = ''; errSenha.classList.remove('text-danger'); }
1332              if (errCad) { errCad.innerHTML = ''; errCad.classList.remove('text-danger'); }
1333              // {% if not request|is_mobile and not request|is_tablet %}
1334              //     const bg = document.querySelector('.bg-fundo');
1335              //     if (bg) bg.style.paddingBottom = '0px';
1336              // {% endif %}
1337          }
1338      
1339          load('off', document.body);
1340
1341  }
1342
1343  document.getElementById('link-redefinir-senha')?.addEventListener('click', function (event) {
1344    document.getElementById('email-redefinir').removeAttribute('disabled');
1345    document.getElementById('btn-enviar-email-redefinir').removeAttribute('disabled')
1346  });
1347
1348  document.getElementById('btn-verificar-cpf')?.addEventListener('click', function (event) {
1349      event.preventDefault();
1350      event.stopPropagation();
1351      const form = document.getElementById('form_cpf');
1352      const cpf = document.getElementById('username')
1353      document.getElementById('invalid_cpf').classList.remove('show');
1354      cpf.classList.remove('invalid');
1355      cpf.classList.remove('valid');
1356
1357      let validacao = validarDocumentoLogin(cpf.value);
1358      
1359      if(validacao) {
1360        validarForm(form);
1361        validarCadastroCPF(obterDocumentoLogin(cpf.value))
1362      } else {
1363        document.getElementById('invalid_cpf').classList.add('show');
1364        cpf.classList.add('invalid');
1365        cpf.classList.remove('valid');
1366      }
1367  });
1368
1369  let typingTimer;
1370  const typingDelay = 1000;
1371
1372  document.getElementById('username').addEventListener('input', function(){
1373    const cpfEl = document.getElementById('username');
1374    const form = document.getElementById('form_cpf');
1375    const btn = document.getElementById('btn-verificar-cpf');
1376    if (btn && btn.offsetParent !== null) return;
1377    clearTimeout(typingTimer);
1378    typingTimer = setTimeout(function(){
1379      if(validarDocumentoLogin(cpfEl.value)){
1380        validarForm(form);
1381        validarCadastroCPF(obterDocumentoLogin(cpfEl.value))
1382      }
1383    }, typingDelay);
1384  });
1385
1386  document.getElementById('form_cpf')?.addEventListener('submit', (e) => {
1387    e.preventDefault();
1388    const cpfEl = document.getElementById('username');
1389    const form  = document.getElementById('form_cpf');
1390    const btn   = document.getElementById('btn-verificar-cpf');
1391    //if (btn && btn.offsetParent !== null) return;
1392    clearTimeout(typingTimer);
1393    const documentoLogin = obterDocumentoLogin(cpfEl.value);
1394    if (validarDocumentoLogin(documentoLogin)) {
1395      validarForm(form);
1396      validarCadastroCPF(documentoLogin);
1397    }
1398  });
1399
1400  // (function(){
1401  //     const cpfEl = document.getElementById('username');
1402  //     if (!cpfEl) return;
1403  //     let typingTimer;
1404  //     const typingDelay = 1000;
1405  //     cpfEl.addEventListener('input', function(){
1406  //         const btn = document.getElementById('btn-verificar-cpf');
1407  //         if (btn && btn.offsetParent !== null) return;
1408  //         clearTimeout(typingTimer);
1409  //         typingTimer = setTimeout(function(){
1410  //             validarCPF(cpfEl);
1411  //         }, typingDelay);
1412  //     });
1413  // })();
1414
1415function show(el)  { el?.classList.remove('hidden'); }
1416function hide(el)  { el?.classList.add('hidden'); }
1417
1418function setEnabledRequired(el, enabled) {
1419  if (!el) return;
1420  el.disabled = !enabled;
1421  if (enabled) el.setAttribute('required', 'required');
1422  else el.removeAttribute('required');
1423}
1424
1425function atualizarProfSaude() {
1426  const checked  = document.querySelector('input[name="is_profissional_saude"]:checked');
1427  const isProf   = checked?.value === 'sim';
1428
1429  const bloco    = document.getElementById('profissionais_saude'); 
1430  const ufSelect = document.getElementById('uf_id');
1431  const crSelect = document.getElementById('cr');
1432  const codigoCR = document.getElementById('codigo_cr');
1433
1434  if (isProf) {
1435    show(bloco);
1436    setEnabledRequired(ufSelect,  true);
1437    setEnabledRequired(crSelect,  true);
1438    setEnabledRequired(codigoCR,  true);
1439    if (ufSelect?.querySelector('option[value="41"]')) ufSelect.value = '41';
1440    if (crSelect) {
1441      const crmOpt = Array.from(crSelect.options || []).find(
1442        (o) => (o?.value || '').toLowerCase() === 'crm'
1443      );
1444      if (crmOpt) crSelect.value = crmOpt.value;
1445    }
1446    requestAnimationFrame(() => {
1447      codigoCR?.classList.remove('is-valid','is-invalid');
1448      codigoCR?.focus();
1449      codigoCR?.setSelectionRange(0, codigoCR.value.length);
1450    });
1451  } else {
1452    hide(bloco);
1453    // Limpa valores e desabilita/retira required
1454    if (ufSelect) ufSelect.value = '';
1455    if (crSelect) crSelect.value = '';
1456    if (codigoCR) codigoCR.value = '';
1457    setEnabledRequired(ufSelect,  false);
1458    setEnabledRequired(crSelect,  false);
1459    setEnabledRequired(codigoCR,  false);
1460  }
1461}
1462
1463document.addEventListener('change', (e) => {
1464  if (e.target.matches('input[name="is_profissional_saude"]')) {
1465    atualizarProfSaude();
1466  }
1467});
1468
1469document.addEventListener('DOMContentLoaded', atualizarProfSaude);
1470
1471
1472/* Função para validar a força da senha e igualdade entre as senhas */
1473  
1474  function validaSenhaConfirmacao(password, confirm) {
1475      let senha = password;
1476      let confirmacao = confirm;
1477
1478      if (senha !== confirmacao) {
1479          return false;
1480      } else {
1481          return true;
1482      }
1483  }
1484
1485  let reduceErrorsCadastro = [];
1486
1487  function validaSenhaForca(password) {
1488      let senha = password;
1489      let errorsSenha = [];
1490
1491      // Regras de validação
1492      if (senha.length < 8) {
1493        errorsSenha.push("Pelo menos 8 caracteres");
1494      }
1495      if (!/[A-Z]/.test(senha)) {
1496        errorsSenha.push("Pelo menos 1 caractere maiúsculo");
1497      }
1498      if (!/[a-z]/.test(senha)) {
1499        errorsSenha.push("Pelo menos 1 caractere minúsculo");
1500      }
1501      if (!/[0-9]/.test(senha)) {
1502        errorsSenha.push("Pelo menos 1 número");
1503      }
1504      if (!/[$*&@#!?.,;:_\-+=%|/\\(){}[\]]/.test(senha)) {
1505        errorsSenha.push(`Pelo menos 1 caractere especial (Ex: $*&@#!?.,;:_-+=%|/\\(){}[])`);
1506      } else if (senha.length === 0) {
1507          errorsSenha = [];
1508      }	
1509
1510      // Usar reduce para retornar apenas mensagens únicas
1511      let reduceErrorsCadastro = errorsSenha.reduce((acc, current) => {
1512          if (!acc.includes(current)) {
1513              acc.push(current);
1514          }
1515          return acc;
1516      }, []);
1517
1518      return reduceErrorsCadastro;
1519  }
1520
1521  function exibeErrosSenha(password, is_reset = false) {
1522      let errors = validaSenhaForca(password);
1523      const error_length = is_reset ? 'req-length-reset' : 'req-length';
1524      const error_uppercase = is_reset ? 'req-uppercase-reset' : 'req-uppercase';
1525      const error_lowercase = is_reset ? 'req-lowercase-reset' : 'req-lowercase';
1526      const error_number = is_reset ? 'req-number-reset' : 'req-number';
1527      const error_special = is_reset ? 'req-special-reset' : 'req-special';
1528      
1529      // Atualizar a lista de requisitos
1530      if (password.length >= 8) {
1531          document.getElementById(error_length).classList.remove('text-danger');
1532          document.getElementById(error_length).classList.add('text-success');
1533      } else {
1534          document.getElementById(error_length).classList.remove('text-success');
1535          document.getElementById(error_length).classList.add('text-danger');
1536      }
1537
1538      if (/[A-Z]/.test(password)) {
1539          document.getElementById(error_uppercase).classList.remove('text-danger');
1540          document.getElementById(error_uppercase).classList.add('text-success');
1541      } else {
1542          document.getElementById(error_uppercase).classList.remove('text-success');
1543          document.getElementById(error_uppercase).classList.add('text-danger');
1544      }
1545
1546      if (/[a-z]/.test(password)) {
1547          document.getElementById(error_lowercase).classList.remove('text-danger');
1548          document.getElementById(error_lowercase).classList.add('text-success');
1549      } else {
1550          document.getElementById(error_lowercase).classList.remove('text-success');
1551          document.getElementById(error_lowercase).classList.add('text-danger');
1552      }
1553
1554      if (/[0-9]/.test(password)) {
1555          document.getElementById(error_number).classList.remove('text-danger');
1556          document.getElementById(error_number).classList.add('text-success');
1557      } else {
1558          document.getElementById(error_number).classList.remove('text-success');
1559          document.getElementById(error_number).classList.add('text-danger');
1560      }
1561
1562      if (/[$*&@#!?.,;:_\-+=%|/\\(){}[\]]/.test(password)) {
1563          document.getElementById(error_special).classList.remove('text-danger');
1564          document.getElementById(error_special).classList.add('text-success');
1565      } else {
1566          document.getElementById(error_special).classList.remove('text-success');
1567          document.getElementById(error_special).classList.add('text-danger');
1568      }
1569
1570      // // Exibir erros gerais
1571      if (errors.length > 0) {
1572          if (is_reset) {
1573            document.getElementById('senha_redefinir').classList.add('invalid');
1574            document.getElementById('senha_redefinir').classList.remove('valid');
1575          } else {
1576            document.getElementById('senha_cadastro').classList.add('invalid');
1577            document.getElementById('senha_cadastro').classList.remove('valid');
1578          }
1579      } else {
1580          if (is_reset) {
1581            document.getElementById('senha_redefinir').classList.remove('invalid');
1582            document.getElementById('senha_redefinir').classList.add('valid');
1583          } else {
1584            document.getElementById('senha_cadastro').classList.remove('invalid');
1585            document.getElementById('senha_cadastro').classList.add('valid');
1586          }
1587      }
1588
1589      // refletir no validity do campo
1590      const inputSenha = is_reset ? document.getElementById('senha_redefinir') : document.getElementById('senha_cadastro');
1591      if (inputSenha) inputSenha.setCustomValidity(errors.length > 0 ? 'A senha não atende os requisitos.' : '');
1592  }
1593  document.querySelectorAll('[name="senha"]').forEach((el) => {
1594    let timer; // cada input tem seu próprio timer
1595    el.addEventListener('input', () => {
1596      clearTimeout(timer);
1597      timer = setTimeout(() => {
1598        const password          = document.getElementById('senha_cadastro')?.value || '';
1599        const passwordRedefinir = document.getElementById('senha_redefinir')?.value || '';
1600        exibeErrosSenha(password || passwordRedefinir, !password); // segundo arg true = redefinição
1601      }, 1000);
1602    });
1603  });
1604  
1605  function atualizarStatusConfirmacao(is_reset = false) {
1606    const senhaId = is_reset ? 'senha_redefinir' : 'senha_cadastro';
1607    const confirmaId = is_reset ? 'senha_redefinir_confirmacao' : 'senha_cadastro_confirmacao';
1608    const msgId = is_reset ? 'passwordErrorsConfirmacaoReset' : 'passwordErrorsConfirmacao';
1609
1610    const senha = document.getElementById(senhaId)?.value || '';
1611    const confirmacao = document.getElementById(confirmaId)?.value || '';
1612    const msg = document.getElementById(msgId);
1613    const inp = document.getElementById(confirmaId);
1614    if (!inp || !msg) return;
1615
1616    if (!confirmacao) {
1617      msg.textContent = '';
1618      msg.classList.remove('text-danger', 'text-success');
1619      inp.classList.remove('invalid', 'valid');
1620      inp.setCustomValidity('');
1621      return;
1622    }
1623
1624    const isConfirmada = validaSenhaConfirmacao(senha, confirmacao);
1625    if (!isConfirmada) {
1626      msg.textContent = 'As senhas não conferem!';
1627      msg.classList.remove('text-success');
1628      msg.classList.add('text-danger');
1629      inp.classList.add('invalid');
1630      inp.classList.remove('valid');
1631      inp.setCustomValidity('As senhas não conferem!');
1632    } else {
1633      msg.textContent = '';
1634      msg.classList.remove('text-danger');
1635      msg.classList.add('text-success');
1636      inp.classList.remove('invalid');
1637      inp.classList.add('valid');
1638      inp.setCustomValidity('');
1639    }
1640  }
1641
1642  // CONFIRMAÇÃO (revalida também quando a senha principal muda)
1643  [
1644    'senha_cadastro',
1645    'senha_cadastro_confirmacao',
1646    'senha_redefinir',
1647    'senha_redefinir_confirmacao'
1648  ].forEach((id) => {
1649    const el = document.getElementById(id);
1650    if (!el) return;
1651    let timer;
1652    el.addEventListener('input', () => {
1653      clearTimeout(timer);
1654      timer = setTimeout(() => {
1655        atualizarStatusConfirmacao(false);
1656        atualizarStatusConfirmacao(true);
1657      }, 300);
1658    });
1659  });
1660
1661  function initIMask(){
1662    // Codigo que troca o CPF para texto livre caso email seja preenchido
1663
1664    // const el = document.getElementById('username');
1665    //   const mask = IMask(el, {
1666    //     mask: [
1667    //       { mask: '000.000.000-00', overwrite: true, lazy: true }, // CPF
1668    //       { mask: /[\s\S]*/ }                                      // texto livre
1669    //     ],
1670    //     dispatch: (appended, ctx) => {
1671    //       const v = (ctx.value + appended);
1672    //       const digits = v.replace(/\D/g,'');
1673    //       const soNumOuPont = /^[\d.\-\s]*$/.test(v);
1674    //       return (!/[A-Za-z@]/.test(v) && soNumOuPont && digits.length > 0)
1675    //         ? ctx.compiledMasks[0] // CPF
1676    //         : ctx.compiledMasks[1]; // texto
1677    //     }
1678    //   });
1679    //   mask.on('accept', () => {
1680    //       const isCPF = mask.masked.currentMask && mask.masked.currentMask.mask === '000.000.000-00';
1681    //       if (isCPF) {
1682    //         el.setAttribute('inputmode','numeric');
1683    //         el.setAttribute('maxlength','14');
1684    //       } else {
1685    //         el.setAttribute('inputmode','text');
1686    //         el.removeAttribute('maxlength');
1687    //       }
1688    //     });
1689        const tel = document.querySelector('#celular, [name="celular"]');
1690        const dat_nasc = document.querySelector('#dat_nasc, [name="dat_nasc"]');
1691        const username = document.querySelector('#username, [name="username"]');
1692        if(tel) {
1693          IMask(tel, { mask: '(00) 00000-0000' })
1694        }
1695        if(dat_nasc) {
1696          IMask(dat_nasc, { mask: '00/00/0000' })
1697        }
1698        if(username) {
1699          if (isLoginCpfCnpjHabilitado()) {
1700            // Máscara CPF/CNPJ alfanumérico — ativa só com data-login-cpf-cnpj (Rosario).
1701            IMask(username, {
1702              mask: [
1703                {
1704                  mask: '000.000.000-00',
1705                  regex: '^\\d{0,11}$'
1706                },
1707                {
1708                  mask: 'SS.SSS.SSS/SSSS-00',
1709                  regex: '^(?:[A-Z]|\\d{12,})[0-9A-Z]*$',
1710                  definitions: {
1711                    S: /[0-9A-Z]/i
1712                  }
1713                }
1714              ],
1715              prepare: (str) => str.toUpperCase(),
1716              dispatch: (appended, dynamicMasked) => {
1717                const base = (dynamicMasked.value + appended)
1718                  .replace(/[^0-9A-Za-z]/g, '')
1719                  .toUpperCase();
1720                return dynamicMasked.compiledMasks.find(
1721                  m => m.regex && base.match(new RegExp(m.regex))
1722                ) || dynamicMasked.compiledMasks[1];
1723              }
1724            });
1725          } else {
1726            IMask(username, { mask: '000.000.000-00' })
1727          }
1728
1729          const usernameInput = document.querySelector('#username, [name="username"]');
1730          usernameInput?.addEventListener('keyup', () => {
1731            console.log('keyup');
1732          })
1733          usernameInput?.addEventListener('change', () => {
1734            console.log('change');
1735          })
1736          usernameInput?.addEventListener('input', () => {
1737            console.log('input');
1738          })
1739          usernameInput?.addEventListener('paste', () => {
1740            console.log('paste');
1741          })
1742        }
1743
1744    }
1745
1746
1747
1748
1749  if (window._iMaskCarregado) {
1750    initIMask();
1751  } else {
1752    window.addEventListener('imask:ready', initIMask, { once: true });
1753  }
1754
1755  document.addEventListener('DOMContentLoaded', async () => {
1756
1757  });
1758
1759
1760  document.addEventListener('DOMContentLoaded', function () {
1761    // captura todos os gatilhos com data-toggle="collapse"
1762    const triggers = document.querySelectorAll('[data-toggle="collapse"]');
1763
1764    triggers.forEach(function (trigger) {
1765      trigger.addEventListener('click', function (e) {
1766        e.preventDefault();
1767
1768        const selector = trigger.getAttribute('data-target');
1769        const panel = document.querySelector(selector);
1770        if (!panel) return;
1771
1772        const isOpen = !panel.classList.contains('hidden');
1773
1774        // toggle visibilidade
1775        panel.classList.toggle('hidden', isOpen);
1776
1777        // acessibilidade
1778        trigger.setAttribute('aria-expanded', String(!isOpen));
1779        panel.setAttribute('aria-hidden', String(isOpen));
1780
1781        // foco no input ao abrir
1782        if (!isOpen) {
1783          const firstInput = panel.querySelector('input[type="email"], input[type="text"]');
1784          if (firstInput) setTimeout(() => firstInput.focus(), 50);
1785        }
1786      });
1787    });
1788  });
1789
1790
1791
1792
1793  // Toggle de visibilidade de senha (olho mágico)
1794  (function initPasswordToggle(){
1795    const ICON_EYE = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8Z"/><circle cx="12" cy="12" r="3"/></svg>';
1796    const ICON_EYE_OFF = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.94 10.94 0 0 1 12 20c-7 0-11-8-11-8a20.3 20.3 0 0 1 5.06-6.94"/><path d="M1 1l22 22"/><path d="M10.58 10.58a2 2 0 1 0 2.83 2.83"/><path d="M9.88 4.26A10.94 10.94 0 0 1 12 4c7 0 11 8 11 8a20.3 20.3 0 0 1-3.12 4.88"/></svg>';
1797
1798    function setIcon(el, isVisible){
1799      if (!el) return;
1800      el.innerHTML = isVisible ? ICON_EYE_OFF : ICON_EYE;
1801      el.setAttribute('aria-pressed', isVisible ? 'true' : 'false');
1802      el.setAttribute('aria-label', isVisible ? 'Ocultar senha' : 'Mostrar senha');
1803    }
1804
1805    function getPasswordInputForToggle(toggleEl){
1806      if (!toggleEl) return null;
1807      // estrutura esperada: <div class="relative"> <input.senha /> <span.mostra-senha /> <label /> </div>
1808      const prev = toggleEl.previousElementSibling;
1809      if (prev && prev.matches('input.senha')) return prev;
1810      const container = toggleEl.closest('.relative') || toggleEl.parentElement;
1811      return container ? container.querySelector('input.senha') : null;
1812    }
1813
1814    function toggleVisibility(toggleEl){
1815      const input = getPasswordInputForToggle(toggleEl);
1816      if (!input) return;
1817      const toText = input.getAttribute('type') === 'password';
1818      input.setAttribute('type', toText ? 'text' : 'password');
1819      setIcon(toggleEl, toText);
1820      // manter foco no input após alternar, para melhor UX
1821      input.focus({ preventScroll: true });
1822      // reposiciona cursor ao final
1823      const val = input.value;
1824      input.setSelectionRange(val.length, val.length);
1825    }
1826
1827    function initOne(toggleEl){
1828      if (!toggleEl || toggleEl._pwInit) return;
1829      toggleEl._pwInit = true;
1830      toggleEl.setAttribute('role', 'button');
1831      toggleEl.setAttribute('tabindex', '0');
1832      toggleEl.style.userSelect = 'none';
1833      setIcon(toggleEl, false);
1834      toggleEl.addEventListener('click', (e) => {
1835        e.preventDefault();
1836        toggleVisibility(toggleEl);
1837      });
1838      toggleEl.addEventListener('keydown', (e) => {
1839        if (e.key === 'Enter' || e.key === ' ') {
1840          e.preventDefault();
1841          toggleVisibility(toggleEl);
1842        }
1843      });
1844    }
1845
1846    function initAll(){
1847      document.querySelectorAll('.mostra-senha').forEach(initOne);
1848    }
1849
1850    if (document.readyState === 'loading') {
1851      document.addEventListener('DOMContentLoaded', initAll, { once: true });
1852    } else {
1853      initAll();
1854    }
1855  })();
1856
1857
1858  // Abrir modal de login integrado voai
1859  function abrirLoginIntegrado(modal) {
1860    openModal(modal);
1861    document.getElementById('form-voai').reset();
1862    document.getElementById('invalid_cpf_voai').innerHTML = '';
1863    document.getElementById('invalid_password_voai').innerHTML = '';
1864    document.getElementById('cpf_voai').classList.remove('valid');
1865    document.getElementById('password_voai').classList.remove('valid');
1866    document.getElementById('invalid_cpf_voai').classList.remove('show');
1867    document.getElementById('invalid_password_voai').classList.remove('show');
1868    document.getElementById('cpf_voai').classList.remove('invalid');
1869    document.getElementById('password_voai').classList.remove('invalid');
1870    document.getElementById('cpf_voai').focus();
1871    aplicarIMask();
1872    
1873  }
1874
1875  function aplicarIMask() {
1876      const cpf = document.querySelector('#cpf_voai, [name="cpf_voai"]');
1877      if(cpf) {
1878        IMask(cpf, { mask: '000.000.000-00' })
1879      }
1880    }
1881
1882  function logarVoai() {
1883    const form = document.getElementById('form-voai');
1884    const cpfInput = document.getElementById('cpf_voai');
1885    const senhaInput = document.getElementById('password_voai');
1886    const cpfFb = document.getElementById('invalid_cpf_voai');
1887    const senhaFb = document.getElementById('invalid_password_voai');
1888
1889    // limpa feedbacks e estados
1890    cpfFb.innerHTML = '';
1891    senhaFb.innerHTML = '';
1892    cpfFb.classList.remove('show');
1893    senhaFb.classList.remove('show');
1894    cpfInput.classList.remove('invalid');
1895    senhaInput.classList.remove('invalid');
1896
1897    const cpfDigits = (cpfInput.value || '').replace(/\D/g, '');
1898    let hasError = false;
1899
1900    if (!cpfDigits || cpfDigits.length !== 11) {
1901      cpfFb.innerHTML = 'Informe um CPF válido';
1902      cpfFb.classList.add('show');
1903      cpfInput.classList.add('invalid');
1904      hasError = true;
1905    }
1906    if (!senhaInput.value) {
1907      senhaFb.innerHTML = 'Informe uma senha válida';
1908      senhaFb.classList.add('show');
1909      senhaInput.classList.add('invalid');
1910      hasError = true;
1911    }
1912
1913    if (hasError) {
1914      if (cpfInput.classList.contains('invalid')) cpfInput.focus();
1915      else senhaInput.focus();
1916      form?.classList.add('invalid');
1917      return;
1918    }
1919
1920    load('on', document.body);
1921
1922    fetch('auth/voai/login', {
1923      method: 'POST',
1924      headers: {
1925        'Content-Type': 'application/json',
1926        'X-CSRFToken': window.CSRF_TOKEN(),
1927      },
1928      body: JSON.stringify({ username: cpfDigits, password: senhaInput.value }),
1929    }).then(response => response.json()).then(data => {
1930      console.log(data);
1931      if(data.status) {
1932        toastr.success('Login realizado com sucesso!');
1933        closeModal();
1934        window.location.href = obterUrlPosLogin();
1935      } else {
1936        toastr.error(data.descricao);
1937        cpfInput.focus();
1938      }
1939    }).catch(error => {
1940      console.error('Erro ao fazer login:', error);
1941    }).finally(() => {
1942      load('off', document.body);
1943    });
1944  }
1945
1946  document.getElementById('form-voai')?.addEventListener('submit', function(e){
1947    e.preventDefault();
1948    e.stopPropagation();
1949    logarVoai();
1950  });
1951
1952  // feedback imediato ao digitar
1953  document.getElementById('cpf_voai')?.addEventListener('input', () => {
1954    const el = document.getElementById('cpf_voai');
1955    const fb = document.getElementById('invalid_cpf_voai');
1956    el.classList.remove('invalid');
1957    fb.classList.remove('show');
1958    fb.innerHTML = '';
1959  });
1960  document.getElementById('password_voai')?.addEventListener('input', () => {
1961    const el = document.getElementById('password_voai');
1962    const fb = document.getElementById('invalid_password_voai');
1963    el.classList.remove('invalid');
1964    fb.classList.remove('show');
1965    fb.innerHTML = '';
1966  });
1967
1968  document.getElementById('form-voai')?.addEventListener('keydown', (e) => {
1969    if (e.key === 'Enter') {
1970      e.preventDefault();
1971      const form = e.currentTarget;
1972      if (form && typeof form.requestSubmit === 'function') {
1973        form.requestSubmit();
1974      } else {
1975        logarVoai();
1976      }
1977    }
1978  });
1979
1980  // Google OAuth: redireciona para a rota do backend que fará o redirect para o Google
1981  (function() {
1982    const originalFetch = window.fetch;
1983    window.fetch = function(...args) {
1984      const url = args[0];
1985      if (typeof url === 'string' && url.includes('accounts.google.com/o/oauth2')) {
1986        console.warn('Bloqueada tentativa de fetch para Google OAuth. Redirecionando corretamente...');
1987        window.location.href = '/auth/google/redirect';
1988        return Promise.reject(new Error('OAuth deve usar redirect, não fetch'));
1989      }
1990      return originalFetch.apply(this, args);
1991    };
1992
1993    const btnGoogle = document.getElementById('btn-entrar-com-google');
1994    if (btnGoogle) {
1995      const newBtn = btnGoogle.cloneNode(true);
1996      btnGoogle.parentNode.replaceChild(newBtn, btnGoogle);
1997
1998      newBtn.addEventListener('click', function (e) {
1999        e.preventDefault();
2000        e.stopPropagation();
2001        e.stopImmediatePropagation();
2002        
2003        if (this.disabled) return;
2004        this.disabled = true;
2005
2006        const redirectUrl = '/auth/google/redirect';
2007
2008        window.location.href = redirectUrl;
2009        return false;
2010      }, true);
2011    }
2012  })();
2013
2014
2015

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.