// 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;
});
});
});