404 lines
13 KiB
JavaScript
404 lines
13 KiB
JavaScript
// 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}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
|
|
// 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 = '<i class="fas fa-chevron-up"></i>';
|
|
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;
|
|
});
|
|
});
|
|
}); |