/** * VIRAXIS — form.js * Validación en cliente + envío vía fetch al endpoint PHP. * La validación real y la protección anti-spam definitivas viven * en api/contact.php; esto solo mejora la experiencia del usuario. */ (function () { 'use strict'; document.addEventListener('DOMContentLoaded', function () { var form = document.getElementById('contact-form'); if (!form) return; var submitBtn = document.getElementById('contact-submit'); var feedback = document.getElementById('form-feedback'); var validators = { name: function (v) { return v.trim().length >= 2; }, email: function (v) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.trim()); }, service: function (v) { return v.trim().length > 0; }, message: function (v) { return v.trim().length >= 10; }, }; var errorMessages = { name: 'Escribe tu nombre completo.', email: 'Escribe un correo electrónico válido.', service: 'Selecciona el servicio que te interesa.', message: 'Cuéntanos un poco más (mínimo 10 caracteres).', }; function setFieldError(fieldName, message) { var field = form.querySelector('[name="' + fieldName + '"]'); var errorEl = document.getElementById('error-' + fieldName); if (!field) return; var wrap = field.closest('.form-field'); if (message) { if (wrap) wrap.classList.add('has-error'); if (errorEl) errorEl.textContent = message; } else { if (wrap) wrap.classList.remove('has-error'); if (errorEl) errorEl.textContent = ''; } } function validateForm() { var valid = true; Object.keys(validators).forEach(function (fieldName) { var field = form.querySelector('[name="' + fieldName + '"]'); if (!field) return; var isValid = validators[fieldName](field.value || ''); setFieldError(fieldName, isValid ? null : errorMessages[fieldName]); if (!isValid) valid = false; }); return valid; } // Limpia el error de un campo en cuanto el usuario corrige Object.keys(validators).forEach(function (fieldName) { var field = form.querySelector('[name="' + fieldName + '"]'); if (!field) return; field.addEventListener('input', function () { if (validators[fieldName](field.value || '')) { setFieldError(fieldName, null); } }); }); function showFeedback(message, type) { feedback.textContent = message; feedback.classList.remove('is-success', 'is-error'); feedback.classList.add(type === 'success' ? 'is-success' : 'is-error'); } form.addEventListener('submit', function (e) { e.preventDefault(); // Honeypot: si el campo invisible tiene contenido, es un bot. // No mostramos error: simplemente fingimos éxito y no enviamos nada. var honeypot = form.querySelector('[name="website"]'); if (honeypot && honeypot.value.trim() !== '') { showFeedback('¡Gracias! Tu solicitud fue enviada correctamente.', 'success'); form.reset(); return; } if (!validateForm()) { showFeedback('Revisa los campos marcados antes de continuar.', 'error'); return; } submitBtn.classList.add('is-loading'); submitBtn.disabled = true; feedback.textContent = ''; feedback.classList.remove('is-success', 'is-error'); fetch(form.action, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: new FormData(form), }) .then(function (response) { return response.json(); }) .then(function (data) { if (data && data.success) { showFeedback(data.message || '¡Gracias! Tu solicitud fue enviada correctamente.', 'success'); form.reset(); document.querySelectorAll('.request-type-btn').forEach(function (b, i) { b.classList.toggle('is-active', i === 0); b.setAttribute('aria-checked', String(i === 0)); }); document.getElementById('request_type').value = 'Cotización'; } else { if (data && data.errors) { Object.keys(data.errors).forEach(function (fieldName) { setFieldError(fieldName, data.errors[fieldName]); }); } showFeedback((data && data.message) || 'No pudimos enviar tu solicitud. Intenta de nuevo.', 'error'); } }) .catch(function () { showFeedback('Hubo un problema de conexión. Intenta de nuevo en unos minutos.', 'error'); }) .finally(function () { submitBtn.classList.remove('is-loading'); submitBtn.disabled = false; }); }); }); })();