/** * Charts.js - Diagramm-Management mit Chart.js für MYP Platform * * Verwaltet alle Diagramme auf der Statistiken-Seite. * Unterstützt Dark Mode und Live-Updates. */ // Chart.js Instanzen Global verfügbar machen window.statsCharts = {}; // Chart.js Konfiguration für Dark/Light Theme function getChartTheme() { const isDark = document.documentElement.classList.contains('dark'); return { isDark: isDark, backgroundColor: isDark ? 'rgba(30, 41, 59, 0.8)' : 'rgba(255, 255, 255, 0.8)', textColor: isDark ? '#e2e8f0' : '#374151', gridColor: isDark ? 'rgba(148, 163, 184, 0.1)' : 'rgba(156, 163, 175, 0.2)', borderColor: isDark ? 'rgba(148, 163, 184, 0.3)' : 'rgba(156, 163, 175, 0.5)' }; } // Standard Chart.js Optionen function getDefaultChartOptions() { const theme = getChartTheme(); return { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: theme.textColor, font: { family: 'Inter, sans-serif' } } }, tooltip: { backgroundColor: theme.backgroundColor, titleColor: theme.textColor, bodyColor: theme.textColor, borderColor: theme.borderColor, borderWidth: 1 } }, scales: { x: { ticks: { color: theme.textColor, font: { family: 'Inter, sans-serif' } }, grid: { color: theme.gridColor } }, y: { ticks: { color: theme.textColor, font: { family: 'Inter, sans-serif' } }, grid: { color: theme.gridColor } } } }; } // Job Status Doughnut Chart async function createJobStatusChart() { try { const response = await fetch('/api/stats/charts/job-status'); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Fehler beim Laden der Job-Status-Daten'); } const ctx = document.getElementById('job-status-chart'); if (!ctx) return; // Vorhandenes Chart zerstören falls vorhanden if (window.statsCharts.jobStatus) { window.statsCharts.jobStatus.destroy(); } const theme = getChartTheme(); window.statsCharts.jobStatus = new Chart(ctx, { type: 'doughnut', data: data, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: theme.textColor, font: { family: 'Inter, sans-serif', size: 12 }, padding: 15 } }, tooltip: { backgroundColor: theme.backgroundColor, titleColor: theme.textColor, bodyColor: theme.textColor, borderColor: theme.borderColor, borderWidth: 1, callbacks: { label: function(context) { const label = context.label || ''; const value = context.parsed; const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = total > 0 ? Math.round((value / total) * 100) : 0; return `${label}: ${value} (${percentage}%)`; } } } }, cutout: '60%' } }); } catch (error) { console.error('Fehler beim Erstellen des Job-Status-Charts:', error); showChartError('job-status-chart', 'Fehler beim Laden der Job-Status-Daten'); } } // Drucker-Nutzung Bar Chart async function createPrinterUsageChart() { try { const response = await fetch('/api/stats/charts/printer-usage'); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Fehler beim Laden der Drucker-Nutzung-Daten'); } const ctx = document.getElementById('printer-usage-chart'); if (!ctx) return; // Vorhandenes Chart zerstören falls vorhanden if (window.statsCharts.printerUsage) { window.statsCharts.printerUsage.destroy(); } const options = getDefaultChartOptions(); options.scales.y.title = { display: true, text: 'Anzahl Jobs', color: getChartTheme().textColor, font: { family: 'Inter, sans-serif' } }; window.statsCharts.printerUsage = new Chart(ctx, { type: 'bar', data: data, options: options }); } catch (error) { console.error('Fehler beim Erstellen des Drucker-Nutzung-Charts:', error); showChartError('printer-usage-chart', 'Fehler beim Laden der Drucker-Nutzung-Daten'); } } // Jobs Timeline Line Chart async function createJobsTimelineChart() { try { const response = await fetch('/api/stats/charts/jobs-timeline'); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Fehler beim Laden der Jobs-Timeline-Daten'); } const ctx = document.getElementById('jobs-timeline-chart'); if (!ctx) return; // Vorhandenes Chart zerstören falls vorhanden if (window.statsCharts.jobsTimeline) { window.statsCharts.jobsTimeline.destroy(); } const options = getDefaultChartOptions(); options.scales.y.title = { display: true, text: 'Jobs pro Tag', color: getChartTheme().textColor, font: { family: 'Inter, sans-serif' } }; options.scales.x.title = { display: true, text: 'Datum (letzte 30 Tage)', color: getChartTheme().textColor, font: { family: 'Inter, sans-serif' } }; window.statsCharts.jobsTimeline = new Chart(ctx, { type: 'line', data: data, options: options }); } catch (error) { console.error('Fehler beim Erstellen des Jobs-Timeline-Charts:', error); showChartError('jobs-timeline-chart', 'Fehler beim Laden der Jobs-Timeline-Daten'); } } // Benutzer-Aktivität Bar Chart async function createUserActivityChart() { try { const response = await fetch('/api/stats/charts/user-activity'); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Fehler beim Laden der Benutzer-Aktivität-Daten'); } const ctx = document.getElementById('user-activity-chart'); if (!ctx) return; // Vorhandenes Chart zerstören falls vorhanden if (window.statsCharts.userActivity) { window.statsCharts.userActivity.destroy(); } const options = getDefaultChartOptions(); options.indexAxis = 'y'; // Horizontales Balkendiagramm options.scales.x.title = { display: true, text: 'Anzahl Jobs', color: getChartTheme().textColor, font: { family: 'Inter, sans-serif' } }; options.scales.y.title = { display: true, text: 'Benutzer', color: getChartTheme().textColor, font: { family: 'Inter, sans-serif' } }; window.statsCharts.userActivity = new Chart(ctx, { type: 'bar', data: data, options: options }); } catch (error) { console.error('Fehler beim Erstellen des Benutzer-Aktivität-Charts:', error); showChartError('user-activity-chart', 'Fehler beim Laden der Benutzer-Aktivität-Daten'); } } // Fehleranzeige in Chart-Container function showChartError(chartId, message) { const container = document.getElementById(chartId); if (container) { container.innerHTML = `

${message}

`; } } // Alle Charts erstellen async function initializeAllCharts() { // Loading-Indikatoren anzeigen showChartLoading(); // Charts parallel erstellen await Promise.allSettled([ createJobStatusChart(), createPrinterUsageChart(), createJobsTimelineChart(), createUserActivityChart() ]); } // Loading-Indikatoren anzeigen function showChartLoading() { const chartIds = ['job-status-chart', 'printer-usage-chart', 'jobs-timeline-chart', 'user-activity-chart']; chartIds.forEach(chartId => { const container = document.getElementById(chartId); if (container) { container.innerHTML = `

Diagramm wird geladen...

`; } }); } // Alle Charts aktualisieren async function refreshAllCharts() { console.log('Aktualisiere alle Diagramme...'); // Bestehende Charts zerstören Object.values(window.statsCharts).forEach(chart => { if (chart && typeof chart.destroy === 'function') { chart.destroy(); } }); // Charts neu erstellen await initializeAllCharts(); console.log('Alle Diagramme aktualisiert'); } // Theme-Wechsel handhaben function updateChartsTheme() { // Alle Charts mit neuem Theme aktualisieren refreshAllCharts(); } // Auto-refresh (alle 5 Minuten) let chartRefreshInterval; function startChartAutoRefresh() { // Bestehenden Interval stoppen if (chartRefreshInterval) { clearInterval(chartRefreshInterval); } // Neuen Interval starten (5 Minuten) chartRefreshInterval = setInterval(() => { refreshAllCharts(); }, 5 * 60 * 1000); } function stopChartAutoRefresh() { if (chartRefreshInterval) { clearInterval(chartRefreshInterval); chartRefreshInterval = null; } } // Cleanup beim Verlassen der Seite function cleanup() { stopChartAutoRefresh(); // Alle Charts zerstören Object.values(window.statsCharts).forEach(chart => { if (chart && typeof chart.destroy === 'function') { chart.destroy(); } }); window.statsCharts = {}; } // Globale Funktionen verfügbar machen window.refreshAllCharts = refreshAllCharts; window.updateChartsTheme = updateChartsTheme; window.startChartAutoRefresh = startChartAutoRefresh; window.stopChartAutoRefresh = stopChartAutoRefresh; window.cleanup = cleanup; // Event Listeners document.addEventListener('DOMContentLoaded', function() { // Charts initialisieren wenn auf Stats-Seite if (document.getElementById('job-status-chart')) { initializeAllCharts(); startChartAutoRefresh(); } }); // Dark Mode Event Listener if (typeof window.addEventListener !== 'undefined') { window.addEventListener('darkModeChanged', function(e) { updateChartsTheme(); }); } // Page unload cleanup window.addEventListener('beforeunload', cleanup);