/** * Modal de Contacto - JavaScript * * Carga dinámica del modal, validaciones y envío a webhook * Compatible con Bootstrap 5.3.2 * * @package ROI_Theme * @since 1.0.0 */ (function() { 'use strict'; // ========================================================================= // CONFIGURACIÓN // ========================================================================= /** * URL del webhook para envío de formulario * IMPORTANTE: Reemplaza con tu URL real de webhook * * Servicios recomendados: * - Make (Integromat): https://www.make.com * - Zapier: https://zapier.com * - Webhook.site (testing): https://webhook.site * - n8n (self-hosted): https://n8n.io * - Pipedream: https://pipedream.com */ const WEBHOOK_URL = 'https://webhook.site/tu-url-aqui'; /** * Expresión regular para validación de email */ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; /** * Expresión regular para validación de WhatsApp * Acepta 10-15 dígitos con o sin espacios/guiones */ const WHATSAPP_REGEX = /^[\+]?[(]?[0-9]{1,4}[)]?[-\s\.]?[(]?[0-9]{1,4}[)]?[-\s\.]?[0-9]{1,4}[-\s\.]?[0-9]{1,5}$/; // ========================================================================= // INICIALIZACIÓN // ========================================================================= /** * Inicializa el modal cuando el DOM está listo */ document.addEventListener('DOMContentLoaded', function() { loadContactModal(); }); // ========================================================================= // CARGA DINÁMICA DEL MODAL // ========================================================================= /** * Carga el HTML del modal dinámicamente desde archivo externo */ function loadContactModal() { // Verificar si ya existe el contenedor del modal let modalContainer = document.getElementById('modalContainer'); if (!modalContainer) { // Crear contenedor si no existe modalContainer = document.createElement('div'); modalContainer.id = 'modalContainer'; document.body.appendChild(modalContainer); } // Obtener la URL del tema desde WordPress const themeUrl = typeof roiheme !== 'undefined' && rroieme.themeUrl ? roiheme.themeUrl : '/wp-content/themes/roitheme'; // Cargar el HTML del modal fetch(themeUrl + '/modal-contact.html') .then(response => { if (!response.ok) { throw new Error('Error al cargar el modal: ' + response.status); } return response.text(); }) .then(html => { modalContainer.innerHTML = html; // Inicializar el formulario después de cargar el HTML initContactForm(); console.log('Modal de contacto cargado exitosamente'); }) .catch(error => { console.error('Error cargando el modal:', error); }); } // ========================================================================= // VALIDACIONES // ========================================================================= /** * Valida el campo de nombre completo * @param {string} fullName - Nombre a validar * @returns {Object} - {valid: boolean, message: string} */ function validateFullName(fullName) { if (!fullName || fullName.trim().length === 0) { return { valid: false, message: 'Por favor ingresa tu nombre completo' }; } if (fullName.trim().length < 3) { return { valid: false, message: 'El nombre debe tener al menos 3 caracteres' }; } return { valid: true, message: '' }; } /** * Valida el campo de email * @param {string} email - Email a validar * @returns {Object} - {valid: boolean, message: string} */ function validateEmail(email) { if (!email || email.trim().length === 0) { return { valid: false, message: 'Por favor ingresa tu correo electrónico' }; } if (!EMAIL_REGEX.test(email.trim())) { return { valid: false, message: 'Por favor ingresa un correo electrónico válido' }; } return { valid: true, message: '' }; } /** * Valida el campo de WhatsApp * @param {string} whatsapp - Número de WhatsApp a validar * @returns {Object} - {valid: boolean, message: string} */ function validateWhatsApp(whatsapp) { if (!whatsapp || whatsapp.trim().length === 0) { return { valid: false, message: 'Por favor ingresa tu número de WhatsApp' }; } // Remover todos los caracteres no numéricos excepto el + const cleanNumber = whatsapp.replace(/[^\d+]/g, ''); const digitsOnly = cleanNumber.replace(/\+/g, ''); if (digitsOnly.length < 10 || digitsOnly.length > 15) { return { valid: false, message: 'El número debe tener entre 10 y 15 dígitos' }; } if (!WHATSAPP_REGEX.test(whatsapp)) { return { valid: false, message: 'Por favor ingresa un número de WhatsApp válido' }; } return { valid: true, message: '' }; } /** * Marca un campo como válido o inválido visualmente * @param {HTMLElement} input - Elemento input * @param {boolean} isValid - Si es válido o no * @param {string} message - Mensaje de error (opcional) */ function setFieldValidation(input, isValid, message = '') { if (isValid) { input.classList.remove('is-invalid'); input.classList.add('is-valid'); } else { input.classList.remove('is-valid'); input.classList.add('is-invalid'); // Actualizar mensaje de error const feedback = input.nextElementSibling; if (feedback && feedback.classList.contains('invalid-feedback')) { feedback.textContent = message; } } } /** * Limpia las validaciones visuales de un campo * @param {HTMLElement} input - Elemento input */ function clearFieldValidation(input) { input.classList.remove('is-valid', 'is-invalid'); } // ========================================================================= // FORMULARIO // ========================================================================= /** * Inicializa el formulario de contacto con validación y envío a webhook */ function initContactForm() { const contactForm = document.getElementById('contactForm'); if (!contactForm) { console.error('Formulario de contacto no encontrado'); return; } // Validación en tiempo real const fullNameInput = document.getElementById('fullName'); const emailInput = document.getElementById('email'); const whatsappInput = document.getElementById('whatsapp'); if (fullNameInput) { fullNameInput.addEventListener('blur', function() { const validation = validateFullName(this.value); setFieldValidation(this, validation.valid, validation.message); }); fullNameInput.addEventListener('input', function() { if (this.classList.contains('is-invalid')) { const validation = validateFullName(this.value); if (validation.valid) { setFieldValidation(this, true); } } }); } if (emailInput) { emailInput.addEventListener('blur', function() { const validation = validateEmail(this.value); setFieldValidation(this, validation.valid, validation.message); }); emailInput.addEventListener('input', function() { if (this.classList.contains('is-invalid')) { const validation = validateEmail(this.value); if (validation.valid) { setFieldValidation(this, true); } } }); } if (whatsappInput) { whatsappInput.addEventListener('blur', function() { const validation = validateWhatsApp(this.value); setFieldValidation(this, validation.valid, validation.message); }); whatsappInput.addEventListener('input', function() { if (this.classList.contains('is-invalid')) { const validation = validateWhatsApp(this.value); if (validation.valid) { setFieldValidation(this, true); } } }); } // Manejo del envío del formulario contactForm.addEventListener('submit', handleFormSubmit); } /** * Maneja el envío del formulario * @param {Event} e - Evento de submit */ async function handleFormSubmit(e) { e.preventDefault(); // Obtener elementos del formulario const fullNameInput = document.getElementById('fullName'); const companyInput = document.getElementById('company'); const whatsappInput = document.getElementById('whatsapp'); const emailInput = document.getElementById('email'); const commentsInput = document.getElementById('comments'); const submitButton = e.target.querySelector('button[type="submit"]'); // Validar todos los campos const fullNameValidation = validateFullName(fullNameInput.value); const emailValidation = validateEmail(emailInput.value); const whatsappValidation = validateWhatsApp(whatsappInput.value); // Marcar campos inválidos setFieldValidation(fullNameInput, fullNameValidation.valid, fullNameValidation.message); setFieldValidation(emailInput, emailValidation.valid, emailValidation.message); setFieldValidation(whatsappInput, whatsappValidation.valid, whatsappValidation.message); // Si algún campo es inválido, detener el envío if (!fullNameValidation.valid || !emailValidation.valid || !whatsappValidation.valid) { showFormMessage('Por favor corrige los errores en el formulario', 'danger'); // Hacer foco en el primer campo inválido if (!fullNameValidation.valid) { fullNameInput.focus(); } else if (!emailValidation.valid) { emailInput.focus(); } else if (!whatsappValidation.valid) { whatsappInput.focus(); } return; } // Preparar datos del formulario const formData = { fullName: fullNameInput.value.trim(), company: companyInput.value.trim(), whatsapp: whatsappInput.value.trim(), email: emailInput.value.trim(), comments: commentsInput.value.trim(), timestamp: new Date().toISOString(), source: 'APU Website - Modal Contact Form' }; // Deshabilitar botón y mostrar spinner const originalButtonText = submitButton.innerHTML; submitButton.disabled = true; submitButton.innerHTML = 'Enviando...'; try { // Enviar datos al webhook const response = await fetch(WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), mode: 'no-cors' // Permite envío sin CORS (no podremos leer la respuesta) }); // Como usamos no-cors, asumimos que el envío fue exitoso si no hay error showFormMessage('¡Mensaje enviado exitosamente! Nos pondremos en contacto pronto.', 'success'); // Resetear formulario e.target.reset(); // Limpiar validaciones visuales clearFieldValidation(fullNameInput); clearFieldValidation(emailInput); clearFieldValidation(whatsappInput); // Tracking de Google Analytics 4 if (typeof gtag !== 'undefined') { gtag('event', 'form_submission', { event_category: 'Contact Form', event_label: 'Modal Contact Form Submitted', value: 1 }); } // Cerrar modal después de 2 segundos setTimeout(() => { const modalElement = document.getElementById('contactModal'); if (modalElement && typeof bootstrap !== 'undefined') { const modal = bootstrap.Modal.getInstance(modalElement); if (modal) { modal.hide(); } } }, 2000); } catch (error) { console.error('Error al enviar el formulario:', error); showFormMessage('Hubo un error al enviar el mensaje. Por favor intenta nuevamente.', 'danger'); } finally { // Rehabilitar botón submitButton.disabled = false; submitButton.innerHTML = originalButtonText; } } /** * Muestra un mensaje de feedback en el formulario * @param {string} message - Mensaje a mostrar * @param {string} type - Tipo de alerta (success, danger, warning, info) */ function showFormMessage(message, type) { const messageDiv = document.getElementById('formMessage'); if (!messageDiv) { console.error('Contenedor de mensajes no encontrado'); return; } messageDiv.className = `mt-3 alert alert-${type}`; messageDiv.textContent = message; messageDiv.style.display = 'block'; messageDiv.setAttribute('role', 'alert'); // Anunciar mensaje a lectores de pantalla messageDiv.setAttribute('aria-live', 'polite'); // Ocultar mensaje después de 5 segundos (excepto mensajes de éxito) if (type !== 'success') { setTimeout(() => { messageDiv.style.display = 'none'; }, 5000); } } // ========================================================================= // EVENTOS DEL MODAL // ========================================================================= /** * Limpia el formulario cuando se cierra el modal */ document.addEventListener('hidden.bs.modal', function (event) { if (event.target.id === 'contactModal') { const contactForm = document.getElementById('contactForm'); const messageDiv = document.getElementById('formMessage'); if (contactForm) { contactForm.reset(); // Limpiar validaciones visuales const inputs = contactForm.querySelectorAll('.form-control'); inputs.forEach(input => clearFieldValidation(input)); } if (messageDiv) { messageDiv.style.display = 'none'; } } }); /** * Tracking cuando se abre el modal */ document.addEventListener('shown.bs.modal', function (event) { if (event.target.id === 'contactModal') { // Hacer foco en el primer campo const fullNameInput = document.getElementById('fullName'); if (fullNameInput) { setTimeout(() => fullNameInput.focus(), 100); } // Google Analytics 4 tracking if (typeof gtag !== 'undefined') { gtag('event', 'modal_open', { event_category: 'Contact Form', event_label: 'Contact Modal Opened', }); } } }); })();