(function () { 'use strict'; /* ========================= BOOTSTRAP SEGURO (GTM / SPA) ========================= */ if (window.__NPW_LOADED__) return; window.__NPW_LOADED__ = true; /* ========================= CONFIGURAÇÃO GLOBAL ========================= */ var NPW = { config: { defaultWhatsLink: "https://api.whatsapp.com/send/?phone=5511999954200&text=Estou no site da Dra. Estrela Machado e gostaria de mais informações", apiURL: "https://wapi.bernardo.app.br/v1/estrelatricologia", clientId: "estrelatricologia", interceptSelector: 'a.bnd-whats', minNameLength: 3, phoneDigits: 11 }, state: { pendingUrl: null, submitting: false } }; /* ========================= READY ========================= */ function onReady(fn) { if (document.readyState !== 'loading') fn(); else document.addEventListener('DOMContentLoaded', fn); } onReady(initNPW); /* ========================= INIT ========================= */ function initNPW() { if (!document.body || document.getElementById('npw-root')) return; createShadowRoot(); bindEvents(); observeWhatsLinks(); } /* ========================= SHADOW DOM - ISOLAMENTO COMPLETO ========================= */ function createShadowRoot() { // Container raiz com Shadow DOM var root = document.createElement('div'); root.id = 'npw-root'; document.body.appendChild(root); // Criar Shadow DOM var shadow = root.attachShadow({ mode: 'closed' }); // CSS ISOLADO (não afeta a página) var style = document.createElement('style'); style.textContent = ` :host { all: initial; display: block; font-family: 'Segoe UI', Tahoma, sans-serif; } /* Reset dentro do Shadow DOM */ * { box-sizing: border-box; margin: 0; padding: 0; } #npw-btn { display: flex; align-items: center; gap: 8px; position: fixed; bottom: 20px; left: 20px; z-index: 10000; background: #854d0e; color: #fff; border: 0; padding: 12px 20px; border-radius: 8px; font-weight: 600; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,.15); font-size: 16px; font-family: inherit; } #npw-btn img { width: 16px; height: 16px; display: block; } #npw-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.5); display: none; justify-content: center; align-items: center; z-index: 99999; animation: fadeIn 0.2s ease; } #npw-overlay.open { display: flex; } #npw-modal { background: #fff; padding: 24px; border-radius: 12px; max-width: 400px; width: 90%; display: flex; flex-direction: column; gap: 14px; position: relative; box-shadow: 0 10px 40px rgba(0,0,0,.2); animation: slideUp 0.3s ease; } #npw-modal h2 { margin: 0; font-size: 17px; color: #333; font-weight: 600; font-family: inherit; } #npw-modal input { padding: 10px; border-radius: 8px; border: 1px solid #ccc; font-size: 15px; font-family: inherit; transition: border-color 0.2s; width: 100%; outline: none; } #npw-modal input:focus { border-color: #854d0e; } #npw-modal input.error { border-color: #e74c3c; } #npw-modal button { background: #854d0e; color: #fff; border: 0; padding: 10px; border-radius: 8px; font-weight: 700; cursor: pointer; font-size: 15px; font-family: inherit; transition: opacity 0.2s, transform 0.1s; } #npw-modal button:hover { opacity: 0.9; } #npw-modal button:active { transform: scale(0.98); } #npw-modal button:disabled { opacity: 0.6; cursor: not-allowed; } #npw-error { color: #e74c3c; font-size: 14px; min-height: 18px; font-family: inherit; } /* Animações */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } /* Mobile Responsivo */ @media (max-width: 480px) { #npw-btn { bottom: 10px; left: 10px; padding: 10px 16px; font-size: 14px; } #npw-modal { padding: 20px; width: 95%; } } `; // HTML ISOLADO var template = document.createElement('template'); template.innerHTML = `

Informe seus dados e fale com nossa equipe

`; shadow.appendChild(style); shadow.appendChild(template.content.cloneNode(true)); // Salvar referências NPW.shadow = shadow; NPW.root = root; NPW.elements = { btn: shadow.getElementById('npw-btn'), overlay: shadow.getElementById('npw-overlay'), modal: shadow.getElementById('npw-modal'), name: shadow.getElementById('npw-name'), phone: shadow.getElementById('npw-phone'), submit: shadow.getElementById('npw-submit'), error: shadow.getElementById('npw-error') }; } /* ========================= EVENT BINDING MELHORADO ========================= */ function bindEvents() { var el = NPW.elements; // Salvar HTML original do botão NPW.state.buttonHTML = el.btn.innerHTML; // Máscara do telefone el.phone.addEventListener('input', maskPhone); // Toggle modal el.btn.addEventListener('click', toggleModal); // Fechar ao clicar no overlay el.overlay.addEventListener('click', function(e) { if (e.target === el.overlay) closeModal(); }); // Submit el.submit.addEventListener('click', function() { if (!NPW.state.submitting) handleSubmit(); }); // Enter key el.name.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); el.phone.focus(); } }); el.phone.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); if (!NPW.state.submitting) handleSubmit(); } }); // ESC global (melhorado) document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeModal(); }); } /* ========================= MODAL - MELHORADO ========================= */ function toggleModal() { var overlay = NPW.elements.overlay; var btn = NPW.elements.btn; var isOpen = overlay.classList.contains('open'); if (isOpen) { closeModal(); } else { openModalUI(); } } function openModalUI() { var overlay = NPW.elements.overlay; var btn = NPW.elements.btn; overlay.classList.add('open'); btn.innerHTML = 'Fechar'; setTimeout(function() { NPW.elements.name.focus(); }, 100); clearError(); } function closeModal() { var overlay = NPW.elements.overlay; var btn = NPW.elements.btn; overlay.classList.remove('open'); btn.innerHTML = NPW.state.buttonHTML; NPW.state.pendingUrl = null; NPW.state.submitting = false; clearError(); NPW.elements.name.value = ''; NPW.elements.phone.value = ''; } function showError(msg, input) { var errorDiv = NPW.elements.error; if (errorDiv) errorDiv.textContent = msg; if (input) { input.classList.add('error'); input.focus(); } } function clearError() { var errorDiv = NPW.elements.error; if (errorDiv) errorDiv.textContent = ''; NPW.elements.name.classList.remove('error'); NPW.elements.phone.classList.remove('error'); } /* ========================= WHATS LINKS - EVENT DELEGATION (MELHOR PARA REACT) ========================= */ function observeWhatsLinks() { // Usar event delegation em vez de listeners individuais document.addEventListener('click', handleLinkClick); // Observer para links dinâmicos (apenas para manter compatibilidade) new MutationObserver(function() { // Não precisa fazer nada, o event delegation já cuida }).observe(document.body, { childList: true, subtree: true }); } function handleLinkClick(e) { var link = e.target.closest(NPW.config.interceptSelector); if (!link) return; // Ignorar se for React Router ou navegação interna if (link.getAttribute('data-npw-ignore') !== null) return; if (link.target === '_blank') return; // Verificar se é um link do WhatsApp var href = link.href || ''; if (!href.includes('whatsapp.com') && !href.includes('wa.me')) return; e.preventDefault(); e.stopPropagation(); NPW.state.pendingUrl = href; openModalUI(); } /* ========================= FORM - MELHORADO ========================= */ function getBodyData(name, phoneDigits) { try { var params = new URLSearchParams(window.location.search); // Lista completa de parâmetros de tracking var trackingParams = { number: phoneDigits, name: name, id: NPW.config.clientId, page: location.href, gclid: '', braid: '', gbraid: '', wbraid: '', utm_source: '', utm_medium: '', utm_campaign: '', utm_content: '', utm_term: '', fbclid: '', msclkid: '', li_fat_id: '', integration: 'whatsapp' }; // Preenche com valores da URL ou sessionStorage for (var key in trackingParams) { if (key === 'number' || key === 'name' || key === 'id' || key === 'page' || key === 'integration') continue; var value = params.get(key); if (value) { sessionStorage.setItem(key, value); trackingParams[key] = value; } else { trackingParams[key] = sessionStorage.getItem(key) || ''; } } // Constrói a string var bodyParts = []; for (var key in trackingParams) { bodyParts.push(key + '=' + encodeURIComponent(trackingParams[key])); } return bodyParts.join('&'); } catch (error) { // Fallback em caso de erro return 'number=' + encodeURIComponent(phoneDigits) + '&name=' + encodeURIComponent(name) + '&id=' + encodeURIComponent(NPW.config.clientId) + '&page=' + encodeURIComponent(location.href); } } function maskPhone(e) { var v = e.target.value.replace(/\D/g, '').slice(0, 11); v = v.replace(/^(\d{2})(\d)/, '($1) $2'); v = v.replace(/(\d{5})(\d)/, '$1-$2'); e.target.value = v; } function handleSubmit() { var name = NPW.elements.name.value.trim(); var phoneDigits = NPW.elements.phone.value.replace(/\D/g, ''); var submit = NPW.elements.submit; clearError(); if (name.length < NPW.config.minNameLength) { return showError('Informe seu nome completo.', NPW.elements.name); } if (phoneDigits.length !== NPW.config.phoneDigits) { return showError('Número de WhatsApp inválido. Use DDD + 9 dígitos.', NPW.elements.phone); } // Popup var popup = window.open('about:blank', '_blank'); NPW.state.submitting = true; submit.disabled = true; submit.textContent = 'Enviando...'; // Enviar para API fetch(NPW.config.apiURL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: getBodyData(name, phoneDigits) }) .catch(function(err) { showError('Erro ao enviar. Tente novamente.', null); }) .finally(function() { // DataLayer (GTM) if (window.dataLayer) { window.dataLayer.push({ event: 'whatsapp_lead_submit', leadsUserData: { phone_number: '+55' + phoneDigits, address: { first_name: name, country: 'BR' } } }); } // Redirecionar para WhatsApp var finalUrl = NPW.state.pendingUrl || NPW.config.defaultWhatsLink; if (popup) { popup.location.href = finalUrl; } else { window.open(finalUrl, '_blank'); } closeModal(); NPW.state.submitting = false; submit.disabled = false; submit.textContent = 'Enviar mensagem'; }); } })();