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

1085 lines
42 KiB
JavaScript

/**
* Admin Panel JavaScript
* Handles the functionality of the admin panel interface
*/
document.addEventListener('DOMContentLoaded', function() {
// Initialize admin panel
initializeAdminPanel();
// Load initial data
loadAdminStats();
// Initialisiere den aktiven Tab basierend auf der URL oder default
initializeActiveTab();
// Initialize modals and button event handlers
initializeEventHandlers();
// Set up auto-refresh timer (every 30 seconds)
setInterval(function() {
if (document.visibilityState === 'visible') {
refreshActiveTabData();
}
}, 30000);
});
// Initialisiere den aktiven Tab
function initializeActiveTab() {
// Prüfe URL Hash
const hash = window.location.hash.substring(1);
if (hash) {
activateTab(hash);
} else {
// Default Tab laden
const defaultTab = document.querySelector('.nav-tab');
if (defaultTab) {
const tabName = defaultTab.getAttribute('data-tab');
activateTab(tabName);
}
}
}
// Aktiviere einen Tab und lade seine Daten
function activateTab(tabName) {
// UI aktualisieren
const tabs = document.querySelectorAll('.nav-tab');
tabs.forEach(tab => {
if (tab.getAttribute('data-tab') === tabName) {
tab.classList.add('active');
} else {
tab.classList.remove('active');
}
});
// Inhalte anzeigen/verstecken
const tabPanes = document.querySelectorAll('.tab-pane');
tabPanes.forEach(pane => {
if (pane.id === `${tabName}-tab`) {
pane.classList.add('active');
pane.classList.remove('hidden');
} else {
pane.classList.remove('active');
pane.classList.add('hidden');
}
});
// Daten für den Tab laden
switch(tabName) {
case 'users':
loadUsers();
break;
case 'printers':
loadPrinters();
break;
case 'scheduler':
loadSchedulerStatus();
break;
case 'system':
loadSystemStats();
break;
case 'logs':
loadLogs();
break;
}
// URL Hash aktualisieren
window.location.hash = tabName;
}
// Refresh only the data for the active tab
function refreshActiveTabData() {
const activeTab = document.querySelector('.nav-tab.active');
if (!activeTab) return;
const tabName = activeTab.getAttribute('data-tab');
switch(tabName) {
case 'users':
loadUsers();
break;
case 'printers':
loadPrinters();
break;
case 'scheduler':
loadSchedulerStatus();
break;
case 'system':
loadSystemStats();
break;
case 'logs':
loadLogs();
break;
}
// Always refresh the stats
loadAdminStats();
}
// Initialize event handlers for buttons that previously used onclick
function initializeEventHandlers() {
// Add user button
const addUserBtn = document.getElementById('add-user-btn');
if (addUserBtn) {
addUserBtn.addEventListener('click', showAddUserModal);
}
// Add printer button
const addPrinterBtn = document.getElementById('add-printer-btn');
if (addPrinterBtn) {
addPrinterBtn.addEventListener('click', showAddPrinterModal);
}
// Refresh logs button
const refreshLogsBtn = document.getElementById('refresh-logs-btn');
if (refreshLogsBtn) {
refreshLogsBtn.addEventListener('click', refreshLogs);
}
// Delete modal close buttons
const closeDeleteModalBtn = document.getElementById('close-delete-modal');
if (closeDeleteModalBtn) {
closeDeleteModalBtn.addEventListener('click', closeDeleteModal);
}
const cancelDeleteBtn = document.getElementById('cancel-delete');
if (cancelDeleteBtn) {
cancelDeleteBtn.addEventListener('click', closeDeleteModal);
}
// Add user modal handlers
const closeAddUserBtn = document.getElementById('close-add-user-modal');
if (closeAddUserBtn) {
closeAddUserBtn.addEventListener('click', closeAddUserModal);
}
const cancelAddUserBtn = document.getElementById('cancel-add-user');
if (cancelAddUserBtn) {
cancelAddUserBtn.addEventListener('click', closeAddUserModal);
}
const confirmAddUserBtn = document.getElementById('confirm-add-user');
if (confirmAddUserBtn) {
confirmAddUserBtn.addEventListener('click', addUser);
}
// Add printer modal handlers
const closeAddPrinterBtn = document.getElementById('close-add-printer-modal');
if (closeAddPrinterBtn) {
closeAddPrinterBtn.addEventListener('click', closeAddPrinterModal);
}
const cancelAddPrinterBtn = document.getElementById('cancel-add-printer');
if (cancelAddPrinterBtn) {
cancelAddPrinterBtn.addEventListener('click', closeAddPrinterModal);
}
const confirmAddPrinterBtn = document.getElementById('confirm-add-printer');
if (confirmAddPrinterBtn) {
confirmAddPrinterBtn.addEventListener('click', addPrinter);
}
// Scheduler buttons
const startSchedulerBtn = document.getElementById('start-scheduler');
if (startSchedulerBtn) {
startSchedulerBtn.addEventListener('click', startScheduler);
}
const stopSchedulerBtn = document.getElementById('stop-scheduler');
if (stopSchedulerBtn) {
stopSchedulerBtn.addEventListener('click', stopScheduler);
}
// Global refresh button
const refreshButton = document.createElement('button');
refreshButton.id = 'refresh-admin-btn';
refreshButton.className = 'fixed bottom-8 right-8 p-3 bg-primary text-white rounded-full shadow-lg z-40';
refreshButton.innerHTML = `
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
`;
refreshButton.title = 'Alle Daten aktualisieren';
refreshButton.addEventListener('click', refreshAllData);
// Füge den Button zum Body hinzu, falls er noch nicht existiert
if (!document.getElementById('refresh-admin-btn')) {
document.body.appendChild(refreshButton);
}
}
// Modal functions
function showAddUserModal() {
const modal = document.getElementById('add-user-modal');
if (modal) {
// Reset form
const form = document.getElementById('add-user-form');
if (form) {
form.reset();
}
// Show modal
modal.classList.remove('hidden');
modal.classList.add('show');
modal.style.display = 'flex';
}
}
function closeAddUserModal() {
const modal = document.getElementById('add-user-modal');
if (modal) {
modal.classList.remove('show');
modal.classList.add('hidden');
modal.style.display = 'none';
}
}
async function addUser() {
const form = document.getElementById('add-user-form');
if (!form) return;
const userData = {
email: document.getElementById('user-email').value,
name: document.getElementById('user-name').value,
password: document.getElementById('user-password').value,
role: document.getElementById('user-role').value,
status: document.getElementById('user-status').value
};
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCSRFToken()
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error('Fehler beim Hinzufügen des Benutzers');
}
// Erfolgsmeldung anzeigen
showNotification('Benutzer erfolgreich hinzugefügt', 'success');
// Modal schließen
closeAddUserModal();
// Benutzerliste aktualisieren
loadUsers();
} catch (error) {
console.error('Error adding user:', error);
showNotification(error.message, 'error');
}
}
function showAddPrinterModal() {
const modal = document.getElementById('add-printer-modal');
if (modal) {
// Reset form
const form = document.getElementById('add-printer-form');
if (form) {
form.reset();
}
// Show modal
modal.classList.remove('hidden');
modal.classList.add('show');
modal.style.display = 'flex';
}
}
function closeAddPrinterModal() {
const modal = document.getElementById('add-printer-modal');
if (modal) {
modal.classList.remove('show');
modal.classList.add('hidden');
modal.style.display = 'none';
}
}
async function addPrinter() {
const form = document.getElementById('add-printer-form');
if (!form) return;
const printerData = {
name: document.getElementById('printer-name').value,
model: document.getElementById('printer-model').value,
ip_address: document.getElementById('printer-ip').value,
location: document.getElementById('printer-location').value,
status: document.getElementById('printer-status').value
};
try {
const response = await fetch('/api/printers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCSRFToken()
},
body: JSON.stringify(printerData)
});
if (!response.ok) {
throw new Error('Fehler beim Hinzufügen des Druckers');
}
// Erfolgsmeldung anzeigen
showNotification('Drucker erfolgreich hinzugefügt', 'success');
// Modal schließen
closeAddPrinterModal();
// Druckerliste aktualisieren
loadPrinters();
} catch (error) {
console.error('Error adding printer:', error);
showNotification(error.message, 'error');
}
}
function refreshLogs() {
loadLogs();
showNotification('Logs aktualisiert', 'info');
}
function closeDeleteModal() {
const modal = document.getElementById('delete-confirm-modal');
if (modal) {
modal.classList.remove('show');
modal.style.display = 'none';
}
}
function showDeleteConfirmation(id, type, name, callback) {
const modal = document.getElementById('delete-confirm-modal');
const messageEl = document.getElementById('delete-message');
const idField = document.getElementById('delete-id');
const typeField = document.getElementById('delete-type');
const confirmBtn = document.getElementById('confirm-delete');
if (modal && messageEl && idField && typeField && confirmBtn) {
messageEl.textContent = `Möchten Sie ${type === 'user' ? 'den Benutzer' : 'den Drucker'} "${name}" wirklich löschen?`;
idField.value = id;
typeField.value = type;
confirmBtn.onclick = function() {
if (typeof callback === 'function') {
callback(id, name);
}
closeDeleteModal();
};
modal.classList.add('show');
modal.style.display = 'flex';
}
}
function initializeAdminPanel() {
// Tab Navigation
const tabs = document.querySelectorAll('.nav-tab');
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const tabName = this.getAttribute('data-tab');
if (!tabName) return;
activateTab(tabName);
});
});
// Log Level Filter
const logLevelFilter = document.getElementById('log-level-filter');
if (logLevelFilter) {
logLevelFilter.addEventListener('change', function() {
if (window.logsData) {
const level = this.value;
if (level === 'all') {
window.filteredLogs = [...window.logsData];
} else {
window.filteredLogs = window.logsData.filter(log => log.level.toLowerCase() === level);
}
renderLogs();
}
});
}
// Confirm Delete
const confirmDeleteBtn = document.getElementById('confirm-delete');
if (confirmDeleteBtn) {
confirmDeleteBtn.addEventListener('click', function() {
const id = document.getElementById('delete-id').value;
const type = document.getElementById('delete-type').value;
if (type === 'user') {
deleteUser(id);
} else if (type === 'printer') {
deletePrinter(id);
}
closeDeleteModal();
});
}
}
async function loadAdminStats() {
try {
const response = await fetch('/api/admin/stats');
if (!response.ok) {
throw new Error('Fehler beim Laden der Admin-Statistiken');
}
const data = await response.json();
// Aktualisiere die Statistik-Karten
const statsContainer = document.getElementById('admin-stats');
if (!statsContainer) {
console.warn('Stats-Container nicht gefunden');
return;
}
statsContainer.innerHTML = `
<div class="stat-card">
<div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</div>
<div class="stat-title">Benutzer</div>
<div class="stat-value" id="admin-total-users-count">${data.total_users || 0}</div>
<div class="stat-desc">Registrierte Benutzer</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
</svg>
</div>
<div class="stat-title">Drucker</div>
<div class="stat-value" id="admin-total-printers-count">${data.total_printers || 0}</div>
<div class="stat-desc">Verbundene Drucker</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
</div>
<div class="stat-title">Aktive Jobs</div>
<div class="stat-value" id="admin-active-jobs-count">${data.active_jobs || 0}</div>
<div class="stat-desc">Laufende Druckaufträge</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div class="stat-title">Erfolgsrate</div>
<div class="stat-value" id="admin-success-rate">${data.success_rate || '0%'}</div>
<div class="stat-desc">Erfolgreiche Druckaufträge</div>
</div>
`;
console.log('✅ Admin-Statistiken erfolgreich aktualisiert:', data);
} catch (error) {
console.error('Error loading admin stats:', error);
showNotification('Fehler beim Laden der Admin-Statistiken', 'error');
}
}
// Lade Benutzer für die Benutzerverwaltung
async function loadUsers() {
try {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Fehler beim Laden der Benutzer');
}
const data = await response.json();
// Benutzer in der Tabelle anzeigen
const userTableBody = document.querySelector('#users-tab table tbody');
if (!userTableBody) return;
let html = '';
if (data.users && data.users.length > 0) {
data.users.forEach(user => {
html += `
<tr class="border-b border-gray-200 dark:border-gray-700">
<td class="px-4 py-3">${user.id}</td>
<td class="px-4 py-3">${user.name || '-'}</td>
<td class="px-4 py-3">${user.email}</td>
<td class="px-4 py-3">
<span class="px-2 py-1 text-xs rounded-full ${user.active ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'}">
${user.active ? 'Aktiv' : 'Inaktiv'}
</span>
</td>
<td class="px-4 py-3">${user.role || 'Benutzer'}</td>
<td class="px-4 py-3">
<div class="flex justify-end space-x-2">
<button onclick="editUser(${user.id})" class="p-1 text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-200">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button onclick="showDeleteConfirmation(${user.id}, 'user', '${user.name || user.email}', deleteUser)" class="p-1 text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</td>
</tr>
`;
});
} else {
html = `
<tr>
<td colspan="6" class="px-4 py-6 text-center text-gray-500 dark:text-gray-400">
Keine Benutzer gefunden
</td>
</tr>
`;
}
userTableBody.innerHTML = html;
} catch (error) {
console.error('Error loading users:', error);
showNotification('Fehler beim Laden der Benutzer', 'error');
}
}
// Lade Drucker für die Druckerverwaltung
async function loadPrinters() {
try {
const response = await fetch('/api/printers');
if (!response.ok) {
throw new Error('Fehler beim Laden der Drucker');
}
const data = await response.json();
// Drucker in der Tabelle anzeigen
const printerTableBody = document.querySelector('#printers-tab table tbody');
if (!printerTableBody) return;
let html = '';
if (data.printers && data.printers.length > 0) {
data.printers.forEach(printer => {
html += `
<tr class="border-b border-gray-200 dark:border-gray-700">
<td class="px-4 py-3">${printer.id}</td>
<td class="px-4 py-3">${printer.name}</td>
<td class="px-4 py-3">${printer.model || '-'}</td>
<td class="px-4 py-3">${printer.ip_address || '-'}</td>
<td class="px-4 py-3">${printer.location || '-'}</td>
<td class="px-4 py-3">
<span class="px-2 py-1 text-xs rounded-full ${printer.status === 'online' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'}">
${printer.status === 'online' ? 'Online' : 'Offline'}
</span>
</td>
<td class="px-4 py-3">
<div class="flex justify-end space-x-2">
<button onclick="editPrinter(${printer.id})" class="p-1 text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-200">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button onclick="showDeleteConfirmation(${printer.id}, 'printer', '${printer.name}', deletePrinter)" class="p-1 text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</td>
</tr>
`;
});
} else {
html = `
<tr>
<td colspan="7" class="px-4 py-6 text-center text-gray-500 dark:text-gray-400">
Keine Drucker gefunden
</td>
</tr>
`;
}
printerTableBody.innerHTML = html;
} catch (error) {
console.error('Error loading printers:', error);
showNotification('Fehler beim Laden der Drucker', 'error');
}
}
// Lade Scheduler-Status
async function loadSchedulerStatus() {
try {
const response = await fetch('/api/scheduler/status');
if (!response.ok) {
throw new Error('Fehler beim Laden des Scheduler-Status');
}
const data = await response.json();
// Status-Anzeige aktualisieren
const statusIndicator = document.getElementById('scheduler-status-indicator');
const statusText = document.getElementById('scheduler-status-text');
const startSchedulerBtn = document.getElementById('start-scheduler');
const stopSchedulerBtn = document.getElementById('stop-scheduler');
if (statusIndicator && statusText) {
if (data.active) {
statusIndicator.classList.remove('bg-red-500');
statusIndicator.classList.add('bg-green-500');
statusText.textContent = 'Aktiv';
if (startSchedulerBtn && stopSchedulerBtn) {
startSchedulerBtn.disabled = true;
stopSchedulerBtn.disabled = false;
}
} else {
statusIndicator.classList.remove('bg-green-500');
statusIndicator.classList.add('bg-red-500');
statusText.textContent = 'Inaktiv';
if (startSchedulerBtn && stopSchedulerBtn) {
startSchedulerBtn.disabled = false;
stopSchedulerBtn.disabled = true;
}
}
}
// Scheduler-Details aktualisieren
const schedulerDetails = document.getElementById('scheduler-details');
if (schedulerDetails) {
schedulerDetails.innerHTML = `
<div class="mb-4">
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Letzte Ausführung</h4>
<p class="text-gray-800 dark:text-gray-100">${data.last_run ? new Date(data.last_run).toLocaleString('de-DE') : 'Nie'}</p>
</div>
<div class="mb-4">
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Nächste Ausführung</h4>
<p class="text-gray-800 dark:text-gray-100">${data.next_run ? new Date(data.next_run).toLocaleString('de-DE') : 'Nicht geplant'}</p>
</div>
<div class="mb-4">
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Ausführungsintervall</h4>
<p class="text-gray-800 dark:text-gray-100">${data.interval || '60'} Sekunden</p>
</div>
<div>
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Verarbeitete Jobs</h4>
<p class="text-gray-800 dark:text-gray-100">${data.processed_jobs || 0} Jobs seit dem letzten Neustart</p>
</div>
`;
}
// Job-Warteschlange aktualisieren
const jobQueueContainer = document.getElementById('job-queue');
if (jobQueueContainer && data.pending_jobs) {
let queueHtml = '';
if (data.pending_jobs.length > 0) {
queueHtml = `
<div class="bg-white dark:bg-gray-800 shadow-md rounded-lg overflow-hidden">
<table class="min-w-full">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Job ID</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Geplant für</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
`;
data.pending_jobs.forEach(job => {
queueHtml += `
<tr>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">${job.id}</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">${job.name}</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">${new Date(job.start_time).toLocaleString('de-DE')}</td>
<td class="px-4 py-3 text-sm">
<span class="px-2 py-1 text-xs rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
Warten
</span>
</td>
</tr>
`;
});
queueHtml += `
</tbody>
</table>
</div>
`;
} else {
queueHtml = `
<div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6 text-center">
<p class="text-gray-500 dark:text-gray-400">Keine ausstehenden Jobs in der Warteschlange</p>
</div>
`;
}
jobQueueContainer.innerHTML = queueHtml;
}
} catch (error) {
console.error('Error loading scheduler status:', error);
showNotification('Fehler beim Laden des Scheduler-Status', 'error');
}
}
// Lade Systemstatistiken
async function loadSystemStats() {
try {
const response = await fetch('/api/system/stats');
if (!response.ok) {
throw new Error('Fehler beim Laden der Systemstatistiken');
}
const data = await response.json();
// CPU-Auslastung
const cpuUsageElement = document.getElementById('cpu-usage');
if (cpuUsageElement) {
cpuUsageElement.style.width = `${data.cpu_usage || 0}%`;
cpuUsageElement.textContent = `${data.cpu_usage || 0}%`;
}
// RAM-Auslastung
const ramUsageElement = document.getElementById('ram-usage');
if (ramUsageElement) {
ramUsageElement.style.width = `${data.ram_usage_percent || 0}%`;
ramUsageElement.textContent = `${data.ram_usage_percent || 0}%`;
}
// Speichernutzung
const diskUsageElement = document.getElementById('disk-usage');
if (diskUsageElement) {
diskUsageElement.style.width = `${data.disk_usage_percent || 0}%`;
diskUsageElement.textContent = `${data.disk_usage_percent || 0}%`;
}
// Weitere Systemdetails
const systemDetailsElement = document.getElementById('system-details');
if (systemDetailsElement) {
systemDetailsElement.innerHTML = `
<div class="mb-4">
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">System</h4>
<p class="text-gray-800 dark:text-gray-100">${data.os_name || 'Unbekannt'} ${data.os_version || ''}</p>
</div>
<div class="mb-4">
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Laufzeit</h4>
<p class="text-gray-800 dark:text-gray-100">${data.uptime || 'Unbekannt'}</p>
</div>
<div class="mb-4">
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Python-Version</h4>
<p class="text-gray-800 dark:text-gray-100">${data.python_version || 'Unbekannt'}</p>
</div>
<div>
<h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Server-Zeit</h4>
<p class="text-gray-800 dark:text-gray-100">${data.server_time ? new Date(data.server_time).toLocaleString('de-DE') : 'Unbekannt'}</p>
</div>
`;
}
// Systemereignisse anzeigen
const systemEventsElement = document.getElementById('system-events');
if (systemEventsElement && data.recent_events) {
let eventsHtml = '';
if (data.recent_events.length > 0) {
eventsHtml = '<ul class="divide-y divide-gray-200 dark:divide-gray-700">';
data.recent_events.forEach(event => {
let eventTypeClass = 'text-blue-600 dark:text-blue-400';
switch (event.type.toLowerCase()) {
case 'error':
eventTypeClass = 'text-red-600 dark:text-red-400';
break;
case 'warning':
eventTypeClass = 'text-yellow-600 dark:text-yellow-400';
break;
case 'success':
eventTypeClass = 'text-green-600 dark:text-green-400';
break;
}
eventsHtml += `
<li class="py-3">
<div class="flex items-start">
<span class="${eventTypeClass} mr-2">
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</span>
<div>
<p class="text-sm text-gray-800 dark:text-gray-200">${event.message}</p>
<p class="text-xs text-gray-500 dark:text-gray-400">${new Date(event.timestamp).toLocaleString('de-DE')}</p>
</div>
</div>
</li>
`;
});
eventsHtml += '</ul>';
} else {
eventsHtml = '<p class="text-center text-gray-500 dark:text-gray-400 py-4">Keine Ereignisse vorhanden</p>';
}
systemEventsElement.innerHTML = eventsHtml;
}
} catch (error) {
console.error('Error loading system stats:', error);
showNotification('Fehler beim Laden der Systemstatistiken', 'error');
}
}
// Lade Logs
async function loadLogs() {
try {
const response = await fetch('/api/logs');
if (!response.ok) {
throw new Error('Fehler beim Laden der Logs');
}
const data = await response.json();
// Logs im globalen Objekt speichern
window.logsData = data.logs || [];
window.filteredLogs = [...window.logsData];
// Logs rendern
renderLogs();
} catch (error) {
console.error('Error loading logs:', error);
showNotification('Fehler beim Laden der Logs', 'error');
}
}
// Hilfsfunktion zum Rendern der Logs
function renderLogs() {
const logsContainer = document.getElementById('logs-container');
if (!logsContainer) return;
if (!window.filteredLogs || window.filteredLogs.length === 0) {
logsContainer.innerHTML = '<div class="p-8 text-center text-gray-500 dark:text-gray-400">Keine Logs gefunden</div>';
return;
}
let html = '';
window.filteredLogs.forEach(log => {
let levelClass = '';
switch (log.level.toLowerCase()) {
case 'error':
levelClass = 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200';
break;
case 'warning':
levelClass = 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200';
break;
case 'info':
levelClass = 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200';
break;
case 'debug':
levelClass = 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200';
break;
default:
levelClass = 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200';
}
html += `
<div class="log-entry border-l-4 border-gray-300 dark:border-gray-600 pl-4 py-3 mb-3">
<div class="flex items-center justify-between mb-1">
<div>
<span class="px-2 py-1 text-xs rounded-full ${levelClass} mr-2">
${log.level}
</span>
<span class="text-sm text-gray-500 dark:text-gray-400">
${log.timestamp ? new Date(log.timestamp).toLocaleString('de-DE') : ''}
</span>
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
${log.source || 'System'}
</div>
</div>
<div class="text-sm text-gray-800 dark:text-gray-200">
${log.message}
</div>
${log.details ? `
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800 p-2 rounded overflow-auto">
<pre>${log.details}</pre>
</div>
` : ''}
</div>
`;
});
logsContainer.innerHTML = html;
}
// Funktionen zum Löschen von Benutzern und Druckern
async function deleteUser(userId) {
try {
const response = await fetch(`/api/users/${userId}`, {
method: 'DELETE',
headers: {
'X-CSRF-Token': getCSRFToken()
}
});
if (!response.ok) {
throw new Error('Fehler beim Löschen des Benutzers');
}
showNotification('Benutzer erfolgreich gelöscht', 'success');
loadUsers();
} catch (error) {
console.error('Error deleting user:', error);
showNotification(error.message, 'error');
}
}
async function deletePrinter(printerId) {
try {
const response = await fetch(`/api/printers/${printerId}`, {
method: 'DELETE',
headers: {
'X-CSRF-Token': getCSRFToken()
}
});
if (!response.ok) {
throw new Error('Fehler beim Löschen des Druckers');
}
showNotification('Drucker erfolgreich gelöscht', 'success');
loadPrinters();
} catch (error) {
console.error('Error deleting printer:', error);
showNotification(error.message, 'error');
}
}
// Scheduler-Steuerungsfunktionen
async function startScheduler() {
try {
const response = await fetch('/api/scheduler/start', {
method: 'POST',
headers: {
'X-CSRF-Token': getCSRFToken()
}
});
if (!response.ok) {
throw new Error('Fehler beim Starten des Schedulers');
}
showNotification('Scheduler erfolgreich gestartet', 'success');
loadSchedulerStatus();
} catch (error) {
console.error('Error starting scheduler:', error);
showNotification(error.message, 'error');
}
}
async function stopScheduler() {
try {
const response = await fetch('/api/scheduler/stop', {
method: 'POST',
headers: {
'X-CSRF-Token': getCSRFToken()
}
});
if (!response.ok) {
throw new Error('Fehler beim Stoppen des Schedulers');
}
showNotification('Scheduler erfolgreich gestoppt', 'success');
loadSchedulerStatus();
} catch (error) {
console.error('Error stopping scheduler:', error);
showNotification(error.message, 'error');
}
}
// Hilfs-Funktion, um den CSRF-Token aus den Meta-Tags zu bekommen
function getCSRFToken() {
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
}
// Refresh all data
function refreshAllData() {
loadAdminStats();
const activeTab = document.querySelector('.nav-tab.active');
if (activeTab) {
const tabName = activeTab.getAttribute('data-tab');
activateTab(tabName);
}
showNotification('Daten wurden aktualisiert', 'success');
}
/**
* Show a notification message
* @param {string} message - The message to show
* @param {string} type - The type of notification (success, error, warning, info)
*/
function showNotification(message, type = 'info') {
const notification = document.getElementById('notification');
const messageEl = document.getElementById('notification-message');
const iconEl = document.getElementById('notification-icon');
if (!notification || !messageEl || !iconEl) return;
// Set message
messageEl.textContent = message;
// Remove all previous classes
notification.classList.remove('notification-success', 'notification-error', 'notification-warning', 'notification-info');
// Add appropriate class based on type
notification.classList.add(`notification-${type}`);
// Set appropriate icon
let icon = '';
switch(type) {
case 'success':
icon = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />';
break;
case 'error':
icon = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />';
break;
case 'warning':
icon = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />';
break;
case 'info':
default:
icon = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />';
break;
}
iconEl.innerHTML = icon;
// Show notification
notification.classList.remove('hidden');
notification.classList.add('show');
// Hide after 5 seconds
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
notification.classList.add('hidden');
}, 300);
}, 5000);
}
// Weitere Funktionen aus dem ursprünglichen Code...