fix: Corregir bloqueo del navegador en el dashboard

Se soluciona un error crítico que causaba el bloqueo del navegador al visitar la pestaña del dashboard.

El problema era causado por la inicialización de la librería Chart.js en un elemento del DOM que no estaba visible, lo que provocaba un bucle de renderizado infinito.

La solución implementada consiste en:
- Inicializar el gráfico solo la primera vez que la pestaña del dashboard se hace visible.
- En visitas posteriores a la pestaña, solo se actualizan los datos del gráfico ya existente.
This commit is contained in:
Marco Gallegos
2025-08-13 07:21:41 -06:00
parent e36bf76425
commit 727527e30d

View File

@@ -35,33 +35,40 @@ const formCredentials = document.getElementById('formCredentials');
const formAddUser = document.getElementById('formAddUser');
const tblUsersBody = document.getElementById('tblUsers')?.querySelector('tbody');
let isDashboardLoading = false;
// --- LÓGICA DE NEGOCIO ---
async function loadDashboardData() {
// Solo admins pueden cargar esto
if (currentUser.role !== 'admin' || !incomeChart) return;
// Guardia para prevenir ejecuciones múltiples
if (currentUser.role !== 'admin' || !incomeChart || isDashboardLoading) return;
isDashboardLoading = true;
try {
const response = await fetch('/api/dashboard');
if (!response.ok) {
if (response.status === 403) {
console.warn('Access to dashboard denied.');
console.warn('Acceso al dashboard denegado.');
return;
}
throw new Error('Failed to fetch dashboard data');
throw new Error('Falló la carga de datos del dashboard');
}
const data = await response.json();
// Update stat cards
// Actualizar tarjetas de estadísticas
document.getElementById('stat-total-income').textContent = `${Number(data.totalIncome || 0).toFixed(2)}`;
document.getElementById('stat-total-movements').textContent = data.totalMovements || 0;
// Update chart data instead of recreating it
// Actualizar datos del gráfico
incomeChart.data.labels = data.incomeByService.map(item => item.tipo);
incomeChart.data.datasets[0].data = data.incomeByService.map(item => item.total);
incomeChart.update();
} catch (error) {
console.error('Error loading dashboard:', error);
console.error('Error al cargar el dashboard:', error);
} finally {
isDashboardLoading = false;
}
}
@@ -437,6 +444,27 @@ function activateTab(tabId) {
// Cargar datos dinámicos si es la pestaña del dashboard
if (tabId === 'tab-dashboard' && currentUser.role === 'admin') {
// Si es la primera vez que se visita la pestaña, inicializar el gráfico
if (!incomeChart) {
const ctx = document.getElementById('incomeChart').getContext('2d');
incomeChart = new Chart(ctx, {
type: 'pie',
data: {
labels: [],
datasets: [{
label: 'Ingresos por Servicio',
data: [],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40'],
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: false
}
});
}
// Cargar (o recargar) los datos del dashboard
loadDashboardData();
}
}
@@ -538,25 +566,6 @@ async function initializeApp() {
if (currentUser.role === 'admin') {
formAddUser?.addEventListener('submit', handleAddUser);
tblUsersBody?.addEventListener('click', handleTableClick);
// Inicializar el gráfico vacío para el admin
const ctx = document.getElementById('incomeChart').getContext('2d');
incomeChart = new Chart(ctx, {
type: 'pie',
data: {
labels: [],
datasets: [{
label: 'Ingresos por Servicio',
data: [],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40'],
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: false
}
});
}
btnLogout?.addEventListener('click', async () => {