/** * Footer Contact Form Handler (Issue #37) * * Maneja la validación, envío y tracking del formulario de contacto del footer. * Incluye: * - Validaciones de email y WhatsApp * - Envío a webhook * - Google Analytics 4 tracking * - Estados de loading y mensajes de feedback * * @package ROI_Theme * @since 1.0.0 */ (function() { 'use strict'; // Configuración del webhook const WEBHOOK_URL = 'https://hook.us2.make.com/iq8p4q9w50a12crlb58d4h1o6lwu4f47'; const FORM_SOURCE = 'ROI Website - Footer Contact Form'; // Expresiones regulares para validación const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const WHATSAPP_REGEX = /^\+?[\d\s-]{10,15}$/; /** * Inicializar el formulario cuando el DOM está listo */ function init() { const form = document.getElementById('footerContactForm'); if (!form) return; // Agregar event listeners form.addEventListener('submit', handleSubmit); // Validación en tiempo real para campos específicos const emailInput = document.getElementById('footerEmail'); const whatsappInput = document.getElementById('footerWhatsapp'); if (emailInput) { emailInput.addEventListener('blur', function() { validateEmail(this); }); emailInput.addEventListener('input', function() { if (this.classList.contains('is-invalid')) { validateEmail(this); } }); } if (whatsappInput) { whatsappInput.addEventListener('blur', function() { validateWhatsApp(this); }); whatsappInput.addEventListener('input', function() { if (this.classList.contains('is-invalid')) { validateWhatsApp(this); } }); } } /** * Validar email * @param {HTMLInputElement} input - Campo de input * @returns {boolean} - True si es válido */ function validateEmail(input) { const value = input.value.trim(); const isValid = EMAIL_REGEX.test(value); if (value === '') { input.classList.remove('is-valid', 'is-invalid'); return true; // Si está vacío pero no es requerido, es válido } if (isValid) { input.classList.remove('is-invalid'); input.classList.add('is-valid'); } else { input.classList.remove('is-valid'); input.classList.add('is-invalid'); } return isValid; } /** * Validar WhatsApp (10-15 dígitos) * @param {HTMLInputElement} input - Campo de input * @returns {boolean} - True si es válido */ function validateWhatsApp(input) { const value = input.value.trim(); // Remover espacios, guiones y signos + para contar solo dígitos const digitsOnly = value.replace(/[\s\-+]/g, ''); const isValid = WHATSAPP_REGEX.test(value) && digitsOnly.length >= 10 && digitsOnly.length <= 15; if (value === '') { input.classList.remove('is-valid', 'is-invalid'); return false; // WhatsApp es requerido } if (isValid) { input.classList.remove('is-invalid'); input.classList.add('is-valid'); } else { input.classList.remove('is-valid'); input.classList.add('is-invalid'); } return isValid; } /** * Validar todos los campos del formulario * @param {HTMLFormElement} form - Formulario * @returns {boolean} - True si todo es válido */ function validateForm(form) { let isValid = true; // Validar campos requeridos const fullName = form.querySelector('#footerFullName'); const email = form.querySelector('#footerEmail'); const whatsapp = form.querySelector('#footerWhatsapp'); if (fullName && fullName.value.trim() === '') { fullName.classList.add('is-invalid'); isValid = false; } else if (fullName) { fullName.classList.remove('is-invalid'); fullName.classList.add('is-valid'); } if (email && !validateEmail(email)) { isValid = false; } if (whatsapp && !validateWhatsApp(whatsapp)) { isValid = false; } return isValid; } /** * Mostrar mensaje de feedback * @param {string} message - Mensaje a mostrar * @param {string} type - Tipo: 'success', 'danger', 'info' */ function showMessage(message, type) { const messageDiv = document.getElementById('footerFormMessage'); if (!messageDiv) return; messageDiv.className = 'col-12 mt-2 alert alert-' + type; messageDiv.textContent = message; messageDiv.style.display = 'block'; messageDiv.classList.add('show'); // Scroll suave al mensaje messageDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); // Auto-ocultar mensajes de éxito después de 5 segundos if (type === 'success') { setTimeout(function() { hideMessage(); }, 5000); } } /** * Ocultar mensaje */ function hideMessage() { const messageDiv = document.getElementById('footerFormMessage'); if (!messageDiv) return; messageDiv.classList.remove('show'); setTimeout(function() { messageDiv.style.display = 'none'; }, 150); } /** * Trackear evento en Google Analytics 4 * @param {string} eventName - Nombre del evento * @param {Object} params - Parámetros adicionales */ function trackGA4Event(eventName, params) { if (typeof gtag === 'function') { gtag('event', eventName, params); } else if (typeof dataLayer !== 'undefined') { dataLayer.push({ 'event': eventName, ...params }); } } /** * Preparar datos del formulario * @param {HTMLFormElement} form - Formulario * @returns {Object} - Datos del formulario */ function getFormData(form) { return { fullName: form.querySelector('#footerFullName').value.trim(), company: form.querySelector('#footerCompany').value.trim() || 'N/A', whatsapp: form.querySelector('#footerWhatsapp').value.trim(), email: form.querySelector('#footerEmail').value.trim(), comments: form.querySelector('#footerComments').value.trim() || 'Sin comentarios', source: FORM_SOURCE, timestamp: new Date().toISOString(), pageUrl: window.location.href, pageTitle: document.title }; } /** * Enviar datos al webhook * @param {Object} data - Datos a enviar * @returns {Promise} - Promesa de fetch */ async function sendToWebhook(data) { const response = await fetch(WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error('Error en el servidor: ' + response.status); } return response; } /** * Resetear el formulario * @param {HTMLFormElement} form - Formulario */ function resetForm(form) { form.reset(); // Remover todas las clases de validación const inputs = form.querySelectorAll('.form-control'); inputs.forEach(function(input) { input.classList.remove('is-valid', 'is-invalid'); }); } /** * Manejar el envío del formulario * @param {Event} e - Evento de submit */ async function handleSubmit(e) { e.preventDefault(); const form = e.target; const submitBtn = form.querySelector('button[type="submit"]'); // Ocultar mensaje anterior hideMessage(); // Validar formulario if (!validateForm(form)) { showMessage('Por favor, completa todos los campos requeridos correctamente.', 'danger'); // Track error de validación trackGA4Event('form_validation_error', { form_name: 'footer_contact', form_source: FORM_SOURCE }); return; } // Deshabilitar botón y mostrar loading submitBtn.disabled = true; submitBtn.classList.add('loading'); const originalText = submitBtn.innerHTML; // Track inicio de envío trackGA4Event('form_submit_start', { form_name: 'footer_contact', form_source: FORM_SOURCE }); try { // Preparar y enviar datos const formData = getFormData(form); await sendToWebhook(formData); // Éxito showMessage('¡Gracias por tu mensaje! Nos pondremos en contacto contigo pronto.', 'success'); resetForm(form); // Track éxito trackGA4Event('form_submit_success', { form_name: 'footer_contact', form_source: FORM_SOURCE, has_company: formData.company !== 'N/A', has_comments: formData.comments !== 'Sin comentarios' }); // Track conversión trackGA4Event('generate_lead', { currency: 'MXN', value: 1, form_name: 'footer_contact', form_source: FORM_SOURCE }); } catch (error) { console.error('Error al enviar el formulario:', error); showMessage('Hubo un error al enviar tu mensaje. Por favor, intenta nuevamente.', 'danger'); // Track error trackGA4Event('form_submit_error', { form_name: 'footer_contact', form_source: FORM_SOURCE, error_message: error.message }); } finally { // Restaurar botón submitBtn.disabled = false; submitBtn.classList.remove('loading'); submitBtn.innerHTML = originalText; } } // Inicializar cuando el DOM esté listo if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();