2025-06-04 10:03:22 +02:00

354 lines
11 KiB
JavaScript

// 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 = '<li class="list-group-item text-danger">Fehler beim Laden</li>';
}
}
}
// Jobs-Liste aktualisieren
function updateRecentJobsList(jobs) {
if (!elements.recentJobsList) return;
if (!jobs || jobs.length === 0) {
elements.recentJobsList.innerHTML = '<li class="list-group-item text-muted">Keine aktuellen Jobs</li>';
return;
}
const jobsHtml = jobs.map(job => {
const statusClass = getStatusClass(job.status);
const timeAgo = formatTimeAgo(job.created_at);
return `
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>${escapeHtml(job.name)}</strong><br>
<small class="text-muted">${escapeHtml(job.printer_name)}${timeAgo}</small>
</div>
<span class="badge ${statusClass}">${getStatusText(job.status)}</span>
</li>
`;
}).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 = '<li class="list-group-item text-danger">Fehler beim Laden</li>';
}
}
}
// Aktivitäten-Liste aktualisieren
function updateRecentActivitiesList(activities) {
if (!elements.recentActivitiesList) return;
if (!activities || activities.length === 0) {
elements.recentActivitiesList.innerHTML = '<li class="list-group-item text-muted">Keine aktuellen Aktivitäten</li>';
return;
}
const activitiesHtml = activities.map(activity => {
const timeAgo = formatTimeAgo(activity.timestamp);
return `
<li class="list-group-item">
<div>${escapeHtml(activity.description)}</div>
<small class="text-muted">${timeAgo}</small>
</li>
`;
}).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 = '<span class="badge bg-secondary">Unbekannt</span>';
}
}
}
// 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 = `<span class="badge ${statusClass}">${statusText}</span>`;
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 = '<i class="fas fa-spinner fa-spin"></i> Aktualisiere...';
}
Promise.all([
loadDashboardData(),
loadRecentJobs(),
loadRecentActivities(),
loadSchedulerStatus()
]).finally(() => {
if (elements.refreshBtn) {
elements.refreshBtn.disabled = false;
elements.refreshBtn.innerHTML = '<i class="fas fa-sync-alt"></i> 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)}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
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);
}
});