vueloenglobo.mx/public/dashboard/js/dashboard.js

279 lines
10 KiB
JavaScript

(function () {
"use strict";
// ========== PRELOADER ==========
const preloader = document.getElementById("preloader");
const fallbackSpinner = document.getElementById("preloaderFallback");
// Mostrar fallback si la imagen no carga en 1 segundo
const fallbackTimer = setTimeout(() => {
const logoImg = document.querySelector(".preloader-logo");
if (logoImg && logoImg.complete && logoImg.naturalHeight === 0) {
logoImg.style.display = "none";
if (fallbackSpinner) fallbackSpinner.style.display = "block";
}
}, 1000);
window.addEventListener("load", function () {
clearTimeout(fallbackTimer);
// Ocultar preloader con animación
if (preloader) {
preloader.classList.add("fade-out");
// Eliminar del DOM después de la transición
setTimeout(() => {
preloader.style.display = "none";
}, 500);
}
});
// ========== SIDEBAR ==========
const sidebar = document.getElementById("sidebar");
const backdrop = document.getElementById("sidebarBackdrop");
const menuToggle = document.getElementById("menuToggleBtn");
const closeBtn = document.getElementById("closeSidebarBtn");
function openSidebar() {
if (sidebar) {
sidebar.classList.add("open");
if (backdrop) backdrop.classList.add("show");
document.body.style.overflow = "hidden";
menuToggle?.setAttribute("aria-expanded", "true");
}
}
function closeSidebar() {
if (sidebar) {
sidebar.classList.remove("open");
if (backdrop) backdrop.classList.remove("show");
document.body.style.overflow = "";
menuToggle?.setAttribute("aria-expanded", "false");
}
}
if (menuToggle) menuToggle.addEventListener("click", openSidebar);
if (closeBtn) closeBtn.addEventListener("click", closeSidebar);
if (backdrop) backdrop.addEventListener("click", closeSidebar);
// Close sidebar on nav link click (mobile only)
const navLinks = document.querySelectorAll("#sidebar .nav-item-custom");
navLinks.forEach((link) => {
link.addEventListener("click", () => {
if (window.innerWidth < 992) closeSidebar();
});
});
// ========== NOTIFICACIONES ==========
const notifBtn = document.getElementById("notifBtn");
const notifBadge = document.getElementById("notifBadge");
if (notifBtn && notifBadge) {
notifBtn.addEventListener("click", () => {
notifBadge.textContent = "0";
notifBadge.style.opacity = "0.5";
notifBadge.style.animation = "none";
});
}
// ========== RELOJ EN VIVO ==========
function updateDateTime() {
const now = new Date();
const timeEl = document.getElementById("liveTime");
const dateEl = document.getElementById("dateChip");
if (timeEl) {
timeEl.textContent = now.toLocaleTimeString("es-MX", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
}
if (dateEl) {
dateEl.textContent = now.toLocaleDateString("es-MX", {
day: "numeric",
month: "short",
year: "numeric",
});
}
}
updateDateTime();
setInterval(updateDateTime, 1000);
// ========== GRÁFICOS ==========
function initCharts() {
// Sales Chart
const salesCanvas = document.getElementById("salesChart");
if (salesCanvas) {
const ctx = salesCanvas.getContext("2d");
const gradient = ctx.createLinearGradient(0, 0, 0, 250);
gradient.addColorStop(0, "rgba(59,130,246,0.2)");
gradient.addColorStop(1, "rgba(59,130,246,0)");
new Chart(ctx, {
type: "line",
data: {
labels: [
"Ene",
"Feb",
"Mar",
"Abr",
"May",
"Jun",
"Jul",
"Ago",
"Sep",
"Oct",
"Nov",
"Dic",
],
datasets: [
{
label: "2026 (proy.)",
data: [
5400, 6200, 7100, 8500, 10200, 12800, 14900,
16800, 19200, 21500, 24700, 28900,
],
borderColor: "#3b82f6",
backgroundColor: gradient,
borderWidth: 2.5,
pointBackgroundColor: "#3b82f6",
pointBorderColor: "#fff",
pointRadius: window.innerWidth < 768 ? 2 : 4,
pointHoverRadius: 7,
tension: 0.4,
fill: true,
},
{
label: "2025",
data: [
4100, 4800, 5400, 6200, 7100, 8100, 9100, 10200,
11300, 12400, 13600, 15000,
],
borderColor: "#cbd5e1",
backgroundColor: "transparent",
borderWidth: 1.5,
borderDash: [5, 5],
pointRadius: window.innerWidth < 768 ? 1 : 2,
tension: 0.4,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
backgroundColor: "#0f172a",
titleColor: "#fff",
bodyColor: "#94a3b8",
padding: 12,
callbacks: {
label: (ctx) =>
`${ctx.dataset.label}: $${ctx.parsed.y.toLocaleString()}`,
},
},
legend: {
position: "top",
labels: {
usePointStyle: true,
boxWidth: 10,
font: {
size: window.innerWidth < 768 ? 10 : 12,
},
},
},
},
scales: {
x: {
grid: {
display: false,
},
ticks: {
font: {
size: window.innerWidth < 768 ? 9 : 11,
},
},
},
y: {
grid: {
color: "#f1f5f9",
},
ticks: {
callback: (v) =>
window.innerWidth < 768
? `$${v / 1000}K`
: `$${v.toLocaleString()}`,
font: {
size: window.innerWidth < 768 ? 9 : 11,
},
},
},
},
},
});
}
// Traffic Chart
const trafficCanvas = document.getElementById("trafficChart");
if (trafficCanvas) {
new Chart(trafficCanvas.getContext("2d"), {
type: "doughnut",
data: {
labels: ["Orgánico", "Social Ads", "Directo", "Referidos"],
datasets: [
{
data: [52, 24, 16, 8],
backgroundColor: [
"#3b82f6",
"#10b981",
"#f59e0b",
"#8b5cf6",
],
borderWidth: 0,
hoverOffset: 8,
borderRadius: 8,
spacing: 2,
},
],
},
options: {
cutout: "70%",
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
tooltip: {
backgroundColor: "#0f172a",
padding: 10,
cornerRadius: 8,
},
},
},
});
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initCharts);
} else {
initCharts();
}
// Handle window resize
let resizeTimeout;
window.addEventListener("resize", () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (window.innerWidth >= 992) closeSidebar();
}, 250);
});
// Fallback para imágenes con error
document.querySelectorAll('img[src="logo.png"]').forEach((img) => {
img.addEventListener("error", function () {
this.style.display = "none";
// Mostrar un placeholder alternativo si es necesario
});
});
})();