// Conversion tracking and form handling class ConversionTracker { constructor() { this.init(); this.setupFormHandlers(); this.trackPageView(); this.setupUTMTracking(); } init() { console.log('Conversion Tracker initialized'); } // Track page view trackPageView() { const data = { event: 'page_view', page: window.location.pathname, language: document.documentElement.lang || 'pt', referrer: document.referrer, timestamp: new Date().toISOString() }; this.sendEvent(data); } // Setup UTM parameter tracking setupUTMTracking() { const urlParams = new URLSearchParams(window.location.search); const utmParams = { source: urlParams.get('utm_source') || this.getSourceFromReferrer(), medium: urlParams.get('utm_medium') || this.getMediumFromReferrer(), campaign: urlParams.get('utm_campaign') || 'organic', term: urlParams.get('utm_term') || '', content: urlParams.get('utm_content') || '' }; // Store in hidden form fields document.querySelectorAll('input[name="source"]').forEach(input => { input.value = utmParams.source; }); document.querySelectorAll('input[name="medium"]').forEach(input => { input.value = utmParams.medium; }); document.querySelectorAll('input[name="campaign"]').forEach(input => { input.value = utmParams.campaign; }); // Store in localStorage for session tracking localStorage.setItem('utm_params', JSON.stringify(utmParams)); } getSourceFromReferrer() { const referrer = document.referrer; if (!referrer) return 'direct'; try { const hostname = new URL(referrer).hostname.toLowerCase(); if (hostname.includes('google')) return 'google'; if (hostname.includes('facebook')) return 'facebook'; if (hostname.includes('instagram')) return 'instagram'; if (hostname.includes('youtube')) return 'youtube'; if (hostname.includes('linkedin')) return 'linkedin'; if (hostname.includes('twitter')) return 'twitter'; if (hostname.includes('tiktok')) return 'tiktok'; return hostname; } catch { return 'unknown'; } } getMediumFromReferrer() { const referrer = document.referrer; if (!referrer) return 'direct'; try { const hostname = new URL(referrer).hostname.toLowerCase(); if (hostname.includes('google')) return 'search'; if (hostname.includes('facebook') || hostname.includes('instagram') || hostname.includes('linkedin') || hostname.includes('twitter')) return 'social'; if (hostname.includes('youtube') || hostname.includes('tiktok')) return 'video'; return 'referral'; } catch { return 'unknown'; } } // Setup form handlers setupFormHandlers() { // Main conversion form const conversionForm = document.getElementById('conversionForm'); if (conversionForm) { conversionForm.addEventListener('submit', (e) => this.handleFormSubmit(e, 'main')); } // Quick form const quickForm = document.getElementById('quickForm'); if (quickForm) { quickForm.addEventListener('submit', (e) => this.handleFormSubmit(e, 'quick')); } // Track form interactions this.trackFormInteractions(); } // Handle form submission async handleFormSubmit(event, formType) { event.preventDefault(); const form = event.target; const submitBtn = form.querySelector('button[type="submit"]'); const btnText = submitBtn.querySelector('.btn-text'); const btnLoading = submitBtn.querySelector('.btn-loading'); // Validate form if (!form.checkValidity()) { form.classList.add('was-validated'); return; } // Show loading state submitBtn.disabled = true; btnText.classList.add('d-none'); btnLoading.classList.remove('d-none'); try { // Collect form data const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); // Add tracking data data.userAgent = navigator.userAgent; data.timestamp = new Date().toISOString(); data.formType = formType; // Track form submission event this.sendEvent({ event: 'form_submit', form_type: formType, page: window.location.pathname, language: data.language || 'pt' }); // Submit form const response = await fetch(form.action, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: JSON.stringify(data) }); if (response.ok) { const result = await response.json(); this.handleSuccess(result, formType); } else { throw new Error('Submission failed'); } } catch (error) { console.error('Form submission error:', error); this.handleError(error, formType); } finally { // Reset button state submitBtn.disabled = false; btnText.classList.remove('d-none'); btnLoading.classList.add('d-none'); } } // Handle successful submission handleSuccess(result, formType) { // Track conversion this.sendEvent({ event: 'conversion', form_type: formType, conversion_id: result.conversionId, page: window.location.pathname, language: document.documentElement.lang || 'pt' }); // Show success modal or redirect if (result.redirectUrl) { window.location.href = result.redirectUrl; } else { const successModal = new bootstrap.Modal(document.getElementById('successModal')); successModal.show(); } // Fire conversion pixels if (result.conversionPixels) { result.conversionPixels.forEach(pixel => { this.firePixel(pixel); }); } // GTM dataLayer push if (window.dataLayer) { window.dataLayer.push({ event: 'conversion', form_type: formType, conversion_value: result.value || 1, conversion_currency: 'BRL' }); } } // Handle submission error handleError(error, formType) { console.error('Conversion error:', error); // Track error this.sendEvent({ event: 'conversion_error', form_type: formType, error: error.message, page: window.location.pathname }); // Show error message this.showError('Ocorreu um erro ao enviar o formulário. Tente novamente.'); } // Track form interactions trackFormInteractions() { // Track form start document.querySelectorAll('form input, form textarea, form select').forEach(field => { let hasInteracted = false; field.addEventListener('focus', () => { if (!hasInteracted) { hasInteracted = true; this.sendEvent({ event: 'form_start', form_id: field.closest('form').id, field_name: field.name, page: window.location.pathname }); } }); // Track field completion field.addEventListener('blur', () => { if (field.value.trim()) { this.sendEvent({ event: 'form_field_complete', form_id: field.closest('form').id, field_name: field.name, page: window.location.pathname }); } }); }); } // Send tracking event async sendEvent(data) { try { await fetch('/api/analytics/track', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); } catch (error) { console.error('Analytics tracking error:', error); } } // Fire conversion pixel firePixel(pixelUrl) { try { const img = new Image(); img.src = pixelUrl; } catch (error) { console.error('Pixel firing error:', error); } } // Show error message showError(message) { // Create or update error alert let errorAlert = document.getElementById('errorAlert'); if (!errorAlert) { errorAlert = document.createElement('div'); errorAlert.id = 'errorAlert'; errorAlert.className = 'alert alert-danger alert-dismissible fade show position-fixed'; errorAlert.style.cssText = 'top: 20px; right: 20px; z-index: 9999; max-width: 400px;'; document.body.appendChild(errorAlert); } errorAlert.innerHTML = ` ${message} `; // Auto-hide after 5 seconds setTimeout(() => { errorAlert.remove(); }, 5000); } } // Scroll animations and interactions class ScrollAnimations { constructor() { this.setupScrollToTop(); this.setupProgressBar(); this.setupParallax(); } setupScrollToTop() { // Create scroll to top button const scrollBtn = document.createElement('button'); scrollBtn.innerHTML = ''; scrollBtn.className = 'btn btn-primary rounded-circle position-fixed'; scrollBtn.style.cssText = 'bottom: 20px; right: 20px; z-index: 1000; width: 50px; height: 50px; opacity: 0; transition: opacity 0.3s;'; scrollBtn.id = 'scrollToTop'; document.body.appendChild(scrollBtn); // Show/hide on scroll window.addEventListener('scroll', () => { if (window.pageYOffset > 300) { scrollBtn.style.opacity = '1'; } else { scrollBtn.style.opacity = '0'; } }); // Smooth scroll to top scrollBtn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); } setupProgressBar() { // Create progress bar const progressBar = document.createElement('div'); progressBar.className = 'scroll-progress'; progressBar.style.cssText = 'position: fixed; top: 0; left: 0; width: 0%; height: 3px; background: linear-gradient(90deg, #667eea, #764ba2); z-index: 9999; transition: width 0.3s;'; document.body.appendChild(progressBar); // Update progress on scroll window.addEventListener('scroll', () => { const scrolled = (window.pageYOffset / (document.documentElement.scrollHeight - window.innerHeight)) * 100; progressBar.style.width = scrolled + '%'; }); } setupParallax() { // Simple parallax effect for hero section const hero = document.querySelector('.hero-section'); if (hero) { window.addEventListener('scroll', () => { const scrolled = window.pageYOffset; const rate = scrolled * -0.5; hero.style.transform = `translateY(${rate}px)`; }); } } } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new ConversionTracker(); new ScrollAnimations(); // Setup Bootstrap form validation const forms = document.querySelectorAll('.needs-validation'); forms.forEach(form => { form.addEventListener('submit', event => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } form.classList.add('was-validated'); }); }); // Phone number formatting const phoneInputs = document.querySelectorAll('input[type="tel"]'); phoneInputs.forEach(input => { input.addEventListener('input', (e) => { let value = e.target.value.replace(/\D/g, ''); if (value.length >= 10) { value = value.replace(/(\d{2})(\d{4,5})(\d{4})/, '($1) $2-$3'); } else if (value.length >= 6) { value = value.replace(/(\d{2})(\d{4})/, '($1) $2'); } else if (value.length >= 2) { value = value.replace(/(\d{2})/, '($1) '); } e.target.value = value; }); }); });