// Dashboard JavaScript - Externe Datei für CSP-Konformität // Globale Variablen let dashboardData = {}; let updateInterval; // DOM-Elemente const elements = { activeJobs: null, scheduledJobs: null, availablePrinters: null, totalPrintTime: null, schedulerStatus: null, recentJobsList: null, recentActivitiesList: null, refreshBtn: null, schedulerToggleBtn: null }; // Initialisierung beim Laden der Seite document.addEventListener('DOMContentLoaded', function() { initializeDashboard(); }); function initializeDashboard() { // DOM-Elemente referenzieren elements.activeJobs = document.getElementById('active-jobs'); elements.scheduledJobs = document.getElementById('scheduled-jobs'); elements.availablePrinters = document.getElementById('available-printers'); elements.totalPrintTime = document.getElementById('total-print-time'); elements.schedulerStatus = document.getElementById('scheduler-status'); elements.recentJobsList = document.getElementById('recent-jobs-list'); elements.recentActivitiesList = document.getElementById('recent-activities-list'); elements.refreshBtn = document.getElementById('refresh-btn'); elements.schedulerToggleBtn = document.getElementById('scheduler-toggle-btn'); // Event-Listener hinzufügen if (elements.refreshBtn) { elements.refreshBtn.addEventListener('click', refreshDashboard); } if (elements.schedulerToggleBtn) { elements.schedulerToggleBtn.addEventListener('click', toggleScheduler); } // Initiales Laden der Daten loadDashboardData(); loadRecentJobs(); loadRecentActivities(); loadSchedulerStatus(); // Auto-Update alle 30 Sekunden updateInterval = setInterval(function() { loadDashboardData(); loadRecentJobs(); loadRecentActivities(); loadSchedulerStatus(); }, 30000); } // Dashboard-Hauptdaten laden async function loadDashboardData() { try { const response = await fetch('/api/dashboard'); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } dashboardData = await response.json(); updateDashboardUI(); } catch (error) { console.error('Fehler beim Laden der Dashboard-Daten:', error); showError('Fehler beim Laden der Dashboard-Daten'); } } // Dashboard-UI aktualisieren function updateDashboardUI() { if (elements.activeJobs) { elements.activeJobs.textContent = dashboardData.active_jobs || 0; } if (elements.scheduledJobs) { elements.scheduledJobs.textContent = dashboardData.scheduled_jobs || 0; } if (elements.availablePrinters) { elements.availablePrinters.textContent = dashboardData.available_printers || 0; } if (elements.totalPrintTime) { const hours = Math.floor((dashboardData.total_print_time || 0) / 3600); const minutes = Math.floor(((dashboardData.total_print_time || 0) % 3600) / 60); elements.totalPrintTime.textContent = `${hours}h ${minutes}m`; } } // Aktuelle Jobs laden async function loadRecentJobs() { try { const response = await fetch('/api/jobs/recent'); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); updateRecentJobsList(data.jobs); } catch (error) { console.error('Fehler beim Laden der aktuellen Jobs:', error); if (elements.recentJobsList) { elements.recentJobsList.innerHTML = '
  • Fehler beim Laden
  • '; } } } // Jobs-Liste aktualisieren function updateRecentJobsList(jobs) { if (!elements.recentJobsList) return; if (!jobs || jobs.length === 0) { elements.recentJobsList.innerHTML = '
  • Keine aktuellen Jobs
  • '; return; } const jobsHtml = jobs.map(job => { const statusClass = getStatusClass(job.status); const timeAgo = formatTimeAgo(job.created_at); return `
  • ${escapeHtml(job.name)}
    ${escapeHtml(job.printer_name)} • ${timeAgo}
    ${getStatusText(job.status)}
  • `; }).join(''); elements.recentJobsList.innerHTML = jobsHtml; } // Aktuelle Aktivitäten laden async function loadRecentActivities() { try { const response = await fetch('/api/activity/recent'); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); updateRecentActivitiesList(data.activities); } catch (error) { console.error('Fehler beim Laden der Aktivitäten:', error); if (elements.recentActivitiesList) { elements.recentActivitiesList.innerHTML = '
  • Fehler beim Laden
  • '; } } } // Aktivitäten-Liste aktualisieren function updateRecentActivitiesList(activities) { if (!elements.recentActivitiesList) return; if (!activities || activities.length === 0) { elements.recentActivitiesList.innerHTML = '
  • Keine aktuellen Aktivitäten
  • '; return; } const activitiesHtml = activities.map(activity => { const timeAgo = formatTimeAgo(activity.timestamp); return `
  • ${escapeHtml(activity.description)}
    ${timeAgo}
  • `; }).join(''); elements.recentActivitiesList.innerHTML = activitiesHtml; } // Scheduler-Status laden async function loadSchedulerStatus() { try { const response = await fetch('/api/scheduler/status'); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); updateSchedulerStatus(data.running); } catch (error) { console.error('Fehler beim Laden des Scheduler-Status:', error); if (elements.schedulerStatus) { elements.schedulerStatus.innerHTML = 'Unbekannt'; } } } // Scheduler-Status aktualisieren function updateSchedulerStatus(isRunning) { if (!elements.schedulerStatus) return; const statusClass = isRunning ? 'bg-success' : 'bg-danger'; const statusText = isRunning ? 'Aktiv' : 'Gestoppt'; elements.schedulerStatus.innerHTML = `${statusText}`; if (elements.schedulerToggleBtn) { elements.schedulerToggleBtn.textContent = isRunning ? 'Scheduler stoppen' : 'Scheduler starten'; elements.schedulerToggleBtn.className = isRunning ? 'btn btn-danger btn-sm' : 'btn btn-success btn-sm'; } } // Scheduler umschalten async function toggleScheduler() { try { const isRunning = dashboardData.scheduler_running; const endpoint = isRunning ? '/api/scheduler/stop' : '/api/scheduler/start'; const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); if (result.success) { showSuccess(result.message); // Status sofort neu laden setTimeout(loadSchedulerStatus, 1000); } else { showError(result.error || 'Unbekannter Fehler'); } } catch (error) { console.error('Fehler beim Umschalten des Schedulers:', error); showError('Fehler beim Umschalten des Schedulers'); } } // Dashboard manuell aktualisieren function refreshDashboard() { if (elements.refreshBtn) { elements.refreshBtn.disabled = true; elements.refreshBtn.innerHTML = ' Aktualisiere...'; } Promise.all([ loadDashboardData(), loadRecentJobs(), loadRecentActivities(), loadSchedulerStatus() ]).finally(() => { if (elements.refreshBtn) { elements.refreshBtn.disabled = false; elements.refreshBtn.innerHTML = ' Aktualisieren'; } }); } // Hilfsfunktionen function getStatusClass(status) { const statusClasses = { 'pending': 'bg-warning', 'printing': 'bg-primary', 'completed': 'bg-success', 'failed': 'bg-danger', 'cancelled': 'bg-secondary', 'scheduled': 'bg-info' }; return statusClasses[status] || 'bg-secondary'; } function getStatusText(status) { const statusTexts = { 'pending': 'Wartend', 'printing': 'Druckt', 'completed': 'Abgeschlossen', 'failed': 'Fehlgeschlagen', 'cancelled': 'Abgebrochen', 'scheduled': 'Geplant' }; return statusTexts[status] || status; } function formatTimeAgo(timestamp) { const now = new Date(); const time = new Date(timestamp); const diffMs = now - time; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMins / 60); const diffDays = Math.floor(diffHours / 24); if (diffMins < 1) return 'Gerade eben'; if (diffMins < 60) return `vor ${diffMins} Min`; if (diffHours < 24) return `vor ${diffHours} Std`; return `vor ${diffDays} Tag${diffDays > 1 ? 'en' : ''}`; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function showSuccess(message) { showNotification(message, 'success'); } function showError(message) { showNotification(message, 'danger'); } function showNotification(message, type) { // Einfache Notification - kann später durch Toast-System ersetzt werden const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`; alertDiv.style.top = '20px'; alertDiv.style.right = '20px'; alertDiv.style.zIndex = '9999'; alertDiv.innerHTML = ` ${escapeHtml(message)} `; document.body.appendChild(alertDiv); // Automatisch nach 5 Sekunden entfernen setTimeout(() => { if (alertDiv.parentNode) { alertDiv.parentNode.removeChild(alertDiv); } }, 5000); } // Cleanup beim Verlassen der Seite window.addEventListener('beforeunload', function() { if (updateInterval) { clearInterval(updateInterval); } });