vueloenglobo.mx/public/assets/js/vuelo-seleccionado.js

460 lines
14 KiB
JavaScript

/**
* Módulo de gestión del formulario de cotización de vuelos
* @version 2.0.0
*/
// Variables globales del módulo
let itiPhoneInput = null;
let currentSubmitLock = false;
/**
* Inicializa el módulo cuando el DOM está listo
*/
document.addEventListener("DOMContentLoaded", () => {
initPhoneInput();
initFormValidation();
initShareButtons();
initScrollAnimations();
});
/**
* Inicializa el input de teléfono internacional
*/
function initPhoneInput() {
const phoneInput = document.querySelector("#telefono");
if (phoneInput && typeof intlTelInput !== "undefined") {
itiPhoneInput = window.intlTelInput(phoneInput, {
initialCountry: "es",
preferredCountries: ["es", "mx", "co", "ar", "pe", "cl"],
separateDialCode: true,
utilsScript:
"https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.min.js",
autoPlaceholder: "aggressive",
formatOnDisplay: true,
});
// Limpiar error al escribir
phoneInput.addEventListener("input", () => {
clearFieldError("telefono");
});
}
}
/**
* Inicializa la validación del formulario
*/
function initFormValidation() {
const form = document.getElementById("cotizacionForm");
if (!form) return;
// Agregar listener al formulario
form.addEventListener("submit", submitForm);
// Validación en tiempo real
const inputs = ["nombre", "email", "fecha"];
inputs.forEach((fieldId) => {
const input = document.getElementById(fieldId);
if (input) {
input.addEventListener("blur", () => validateField(fieldId));
input.addEventListener("input", () => {
clearFieldError(fieldId);
});
}
});
}
/**
* Valida un campo específico
* @param {string} fieldId - ID del campo a validar
* @returns {boolean} - True si es válido
*/
function validateField(fieldId) {
const input = document.getElementById(fieldId);
if (!input) return true;
let isValid = true;
let errorMessage = "";
switch (fieldId) {
case "nombre":
const nombre = input.value.trim();
if (!nombre) {
isValid = false;
errorMessage = "El nombre es requerido";
} else if (nombre.length < 3) {
isValid = false;
errorMessage = "El nombre debe tener al menos 3 caracteres";
} else if (!/^[a-zA-ZáéíóúñÁÉÍÓÚÑ\s]+$/.test(nombre)) {
isValid = false;
errorMessage = "El nombre solo puede contener letras";
}
break;
case "email":
const email = input.value.trim();
const emailRegex = /^[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,}$/;
if (!email) {
isValid = false;
errorMessage = "El correo electrónico es requerido";
} else if (!emailRegex.test(email)) {
isValid = false;
errorMessage = "Ingresa un correo electrónico válido";
}
break;
case "fecha":
const fecha = input.value;
if (!fecha) {
isValid = false;
errorMessage = "La fecha es requerida";
} else {
const selectedDate = new Date(fecha);
const today = new Date();
today.setHours(0, 0, 0, 0);
if (selectedDate < today) {
isValid = false;
errorMessage = "La fecha no puede ser anterior a hoy";
}
}
break;
}
if (!isValid) {
showFieldError(fieldId, errorMessage);
} else {
clearFieldError(fieldId);
}
return isValid;
}
/**
* Muestra error en un campo
* @param {string} fieldId - ID del campo
* @param {string} message - Mensaje de error
*/
function showFieldError(fieldId, message) {
const input = document.getElementById(fieldId);
const errorDiv = document.getElementById(`${fieldId}-error`);
if (input) {
input.classList.add("error");
}
if (errorDiv) {
errorDiv.textContent = message;
errorDiv.classList.add("show");
}
}
/**
* Limpia el error de un campo
* @param {string} fieldId - ID del campo
*/
function clearFieldError(fieldId) {
const input = document.getElementById(fieldId);
const errorDiv = document.getElementById(`${fieldId}-error`);
if (input) {
input.classList.remove("error");
}
if (errorDiv) {
errorDiv.classList.remove("show");
errorDiv.textContent = "";
}
}
/**
* Valida todo el formulario
* @returns {boolean} - True si el formulario es válido
*/
function validateForm() {
let isValid = true;
const fields = ["nombre", "email", "fecha"];
for (const field of fields) {
if (!validateField(field)) {
isValid = false;
}
}
// Validar teléfono
const telefono = getFullPhoneNumber();
if (!telefono || telefono.trim() === "") {
showFieldError("telefono", "El número de teléfono es requerido");
isValid = false;
} else if (telefono.length < 8) {
showFieldError("telefono", "Ingresa un número de teléfono válido");
isValid = false;
} else {
clearFieldError("telefono");
}
return isValid;
}
/**
* Obtiene el número de teléfono completo con código de país
* @returns {string} - Número completo
*/
function getFullPhoneNumber() {
if (itiPhoneInput) {
const number = itiPhoneInput.getNumber();
return number || "";
}
const phoneInput = document.getElementById("telefono");
return phoneInput ? phoneInput.value.trim() : "";
}
/**
* Maneja el envío del formulario
* @param {Event} event - Evento del formulario
*/
async function submitForm(event) {
if (event) {
event.preventDefault();
}
// Evitar envíos múltiples
if (currentSubmitLock) {
return;
}
// Validar formulario
if (!validateForm()) {
const firstError = document.querySelector(".form-control.error");
if (firstError) {
firstError.scrollIntoView({
behavior: "smooth",
block: "center",
});
firstError.focus();
}
return;
}
currentSubmitLock = true;
const form = document.getElementById("cotizacionForm");
const submitBtn = document.getElementById("submitCotizacion");
// Deshabilitar botón y mostrar loading
const originalText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.classList.add("btn-loading");
submitBtn.innerHTML = "<span>Enviando...</span>";
try {
const telefonoCompleto = getFullPhoneNumber();
const formData = {
_token: document.querySelector('input[name="_token"]').value,
id_vuelo: document.querySelector('input[name="id_vuelo"]').value,
nombre_vuelo: document.querySelector('input[name="nombre_vuelo"]')
.value,
slug: document.querySelector('input[name="slug"]').value,
nombre: document.getElementById("nombre").value.trim(),
email: document.getElementById("email").value.trim(),
telefono: telefonoCompleto,
pasajeros: document.getElementById("pasajeros").value,
fecha: document.getElementById("fecha").value,
};
const slug = formData.slug;
const url = `/cotizar-vuelo/${slug}`;
const response = await axios.post(url, formData, {
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": formData._token,
},
timeout: 30000, // 30 segundos
});
if (response.status === 200) {
let mensaje =
"Pronto nos pondremos en contacto contigo para confirmar tu cotización.";
if (response.data && response.data.mensaje) {
mensaje = response.data.mensaje;
} else if (response.data && response.data.message) {
mensaje = response.data.message;
}
await Swal.fire({
icon: "success",
title: "¡Cotización enviada!",
text: mensaje,
confirmButtonColor: "#2563eb",
confirmButtonText: "Aceptar",
timer: 5000,
timerProgressBar: true,
showClass: {
popup: "animate__animated animate__fadeInDown",
},
hideClass: {
popup: "animate__animated animate__fadeOutUp",
},
});
// Resetear formulario
form.reset();
if (itiPhoneInput) {
itiPhoneInput.setNumber("");
}
const fechaInput = document.getElementById("fecha");
if (fechaInput && fechaInput._flatpickr) {
fechaInput._flatpickr.clear();
}
// Limpiar errores
["nombre", "email", "fecha", "telefono"].forEach(clearFieldError);
// Limpiar validación visual
document.querySelectorAll(".form-control").forEach((input) => {
input.classList.remove("error");
});
} else {
throw new Error(
response.data?.message || "Error en la respuesta del servidor",
);
}
} catch (error) {
console.error("Error en el envío:", error);
let errorMessage =
"Ocurrió un error al enviar la cotización. Por favor intenta nuevamente.";
if (error.response) {
console.log("Respuesta de error:", error.response.data);
if (error.response.data && error.response.data.message) {
errorMessage = error.response.data.message;
} else if (error.response.data && error.response.data.error) {
errorMessage = error.response.data.error;
} else if (typeof error.response.data === "string") {
errorMessage = error.response.data;
} else if (error.response.status === 422) {
errorMessage = "Por favor verifica los datos ingresados";
} else if (error.response.status === 429) {
errorMessage =
"Demasiadas solicitudes. Por favor espera un momento.";
} else if (error.response.status >= 500) {
errorMessage =
"Error en el servidor. Por favor intenta más tarde.";
}
} else if (error.request) {
errorMessage =
"No se pudo conectar con el servidor. Verifica tu conexión a internet.";
} else if (error.message) {
errorMessage = error.message;
}
await Swal.fire({
icon: "error",
title: "Error",
text: errorMessage,
confirmButtonColor: "#2563eb",
confirmButtonText: "Intentar de nuevo",
});
} finally {
currentSubmitLock = false;
submitBtn.disabled = false;
submitBtn.classList.remove("btn-loading");
submitBtn.innerHTML = originalText;
}
}
/**
* Inicializa los botones de compartir
*/
function initShareButtons() {
const shareButtons = document.querySelectorAll(".share-btn");
const currentUrl = encodeURIComponent(window.location.href);
const currentTitle = encodeURIComponent(document.title);
shareButtons.forEach((btn) => {
btn.addEventListener("click", async () => {
const type = btn.dataset.share;
let shareUrl = "";
switch (type) {
case "facebook":
shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${currentUrl}`;
window.open(shareUrl, "_blank", "width=600,height=400");
break;
case "twitter":
shareUrl = `https://twitter.com/intent/tweet?text=${currentTitle}&url=${currentUrl}`;
window.open(shareUrl, "_blank", "width=600,height=400");
break;
case "whatsapp":
shareUrl = `https://wa.me/?text=${currentTitle}%20${currentUrl}`;
window.open(shareUrl, "_blank");
break;
case "copy":
try {
await navigator.clipboard.writeText(
window.location.href,
);
Swal.fire({
icon: "success",
title: "¡Enlace copiado!",
text: "El enlace ha sido copiado al portapapeles",
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
});
} catch (err) {
Swal.fire({
icon: "error",
title: "Error",
text: "No se pudo copiar el enlace",
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 3000,
});
}
break;
}
});
});
}
/**
* Inicializa animaciones al hacer scroll
*/
function initScrollAnimations() {
const elements = document.querySelectorAll(".section, .sidebar-card");
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.style.opacity = "1";
entry.target.style.transform = "translateY(0)";
observer.unobserve(entry.target);
}
});
},
{
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
},
);
elements.forEach((element) => {
element.style.opacity = "0";
element.style.transform = "translateY(20px)";
element.style.transition = "all 0.6s ease-out";
observer.observe(element);
});
}
// Exportar funciones para debugging (opcional)
if (typeof window !== "undefined") {
window.FlightDetails = {
validateForm,
submitForm,
getFullPhoneNumber,
};
}