feat: Einführung neuer API-Endpunkte zur Verwaltung von Benutzereinstellungen und Druckerstatus. Implementierung von Funktionen zur Überprüfung wartender Jobs und zur Aktualisierung aller Drucker. Verbesserung der Benutzeroberfläche durch optimierte Ladeanzeigen und Warnungen für Offline-Drucker. Anpassungen in den Templates zur Unterstützung neuer Funktionen und zur Verbesserung der Benutzererfahrung.

This commit is contained in:
2025-05-27 12:19:03 +02:00
parent cbe1864678
commit e9071c7b57
11 changed files with 1101 additions and 17 deletions

View File

@@ -997,6 +997,379 @@ async function createPrinter(formData) {
}
}
/**
* Fehlende Admin-Funktionen
*/
// System-Einstellungen anzeigen
function showSystemSettings() {
const modal = createModal('⚙️ System-Einstellungen', `
<form id="settings-form" class="space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Server-Konfiguration</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Host</label>
<input type="text" name="host" value="0.0.0.0"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent
dark:bg-gray-700 dark:text-white">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Port</label>
<input type="number" name="port" value="443" min="1" max="65535"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent
dark:bg-gray-700 dark:text-white">
</div>
<div class="flex items-center">
<input type="checkbox" name="ssl_enabled" id="ssl-enabled" checked
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="ssl-enabled" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">
SSL aktiviert
</label>
</div>
</div>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Scheduler-Einstellungen</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Intervall (Sekunden)</label>
<input type="number" name="scheduler_interval" value="60" min="10" max="3600"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent
dark:bg-gray-700 dark:text-white">
</div>
<div class="flex items-center">
<input type="checkbox" name="scheduler_enabled" id="scheduler-enabled" checked
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="scheduler-enabled" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">
Scheduler aktiviert
</label>
</div>
</div>
</div>
</div>
<div class="flex justify-end space-x-3 mt-6">
<button type="button" onclick="closeModal()"
class="px-4 py-2 bg-gray-300 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition-colors">
Abbrechen
</button>
<button type="submit"
class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
Einstellungen speichern
</button>
</div>
</form>
`);
// Form-Handler
const form = document.getElementById('settings-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
await saveSystemSettings(new FormData(e.target));
});
}
// System-Einstellungen speichern
async function saveSystemSettings(formData) {
try {
showLoadingOverlay();
const settings = {
server: {
host: formData.get('host'),
port: parseInt(formData.get('port')),
ssl_enabled: formData.get('ssl_enabled') === 'on'
},
scheduler: {
interval_seconds: parseInt(formData.get('scheduler_interval')),
enabled: formData.get('scheduler_enabled') === 'on'
}
};
const url = `${API_BASE_URL}/api/admin/settings`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
},
body: JSON.stringify(settings)
});
const result = await response.json();
if (result.success) {
showNotification('✅ Einstellungen erfolgreich gespeichert!', 'success');
closeModal();
} else {
showNotification('❌ Fehler beim Speichern: ' + result.message, 'error');
}
} catch (error) {
console.error('Settings save error:', error);
showNotification('❌ Fehler beim Speichern der Einstellungen', 'error');
} finally {
hideLoadingOverlay();
}
}
// Alle Drucker aktualisieren
async function updateAllPrinters() {
if (!confirm('🔄 Möchten Sie den Status aller Drucker aktualisieren?')) return;
showLoadingOverlay();
try {
const url = `${API_BASE_URL}/api/admin/printers/update-all`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
}
});
const result = await response.json();
if (result.success) {
showNotification(`${result.message}`, 'success');
// Seite nach 2 Sekunden neu laden
setTimeout(() => location.reload(), 2000);
} else {
showNotification('❌ Fehler beim Aktualisieren: ' + result.message, 'error');
}
} catch (error) {
console.error('Printer update error:', error);
showNotification('❌ Fehler beim Aktualisieren der Drucker', 'error');
} finally {
hideLoadingOverlay();
}
}
// System neustarten
async function restartSystem() {
if (!confirm('🔄 Möchten Sie das System wirklich neu starten? Dies kann einige Minuten dauern.')) return;
showLoadingOverlay();
try {
const url = `${API_BASE_URL}/api/admin/system/restart`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
}
});
const result = await response.json();
if (result.success) {
showNotification('🔄 System wird neu gestartet...', 'info');
// Nach 5 Sekunden versuchen, die Seite neu zu laden
setTimeout(() => {
location.reload();
}, 5000);
} else {
showNotification('❌ Fehler beim Neustart: ' + result.message, 'error');
}
} catch (error) {
console.error('System restart error:', error);
showNotification('❌ Fehler beim System-Neustart', 'error');
} finally {
hideLoadingOverlay();
}
}
// Drucker verwalten
function managePrinter(printerId) {
window.location.href = `/admin/printers/${printerId}/manage`;
}
// Drucker-Einstellungen anzeigen
function showPrinterSettings(printerId) {
window.location.href = `/admin/printers/${printerId}/settings`;
}
// Benutzer bearbeiten
function editUser(userId) {
window.location.href = `/admin/users/${userId}/edit`;
}
// Benutzer löschen
async function deleteUser(userId, userName) {
if (!confirm(`⚠️ Möchten Sie den Benutzer "${userName}" wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.`)) return;
showLoadingOverlay();
try {
const url = `${API_BASE_URL}/api/admin/users/${userId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
}
});
const result = await response.json();
if (response.ok) {
showNotification(`✅ Benutzer "${userName}" erfolgreich gelöscht`, 'success');
// Seite nach 2 Sekunden neu laden
setTimeout(() => location.reload(), 2000);
} else {
showNotification('❌ Fehler beim Löschen: ' + result.error, 'error');
}
} catch (error) {
console.error('User delete error:', error);
showNotification('❌ Fehler beim Löschen des Benutzers', 'error');
} finally {
hideLoadingOverlay();
}
}
// Job-Aktionen verarbeiten
async function handleJobAction(action, jobId) {
let confirmMessage = '';
let url = '';
let method = 'POST';
switch (action) {
case 'cancel':
confirmMessage = 'Möchten Sie diesen Job wirklich abbrechen?';
url = `${API_BASE_URL}/api/jobs/${jobId}/cancel`;
break;
case 'delete':
confirmMessage = 'Möchten Sie diesen Job wirklich löschen?';
url = `${API_BASE_URL}/api/jobs/${jobId}`;
method = 'DELETE';
break;
case 'finish':
confirmMessage = 'Möchten Sie diesen Job als beendet markieren?';
url = `${API_BASE_URL}/api/jobs/${jobId}/finish`;
break;
default:
showNotification('❌ Unbekannte Aktion', 'error');
return;
}
if (!confirm(confirmMessage)) return;
showLoadingOverlay();
try {
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
}
});
const result = await response.json();
if (response.ok) {
showNotification('✅ Aktion erfolgreich ausgeführt', 'success');
// Seite nach 2 Sekunden neu laden
setTimeout(() => location.reload(), 2000);
} else {
showNotification('❌ Fehler: ' + result.error, 'error');
}
} catch (error) {
console.error('Job action error:', error);
showNotification('❌ Fehler beim Ausführen der Aktion', 'error');
} finally {
hideLoadingOverlay();
}
}
// Such- und Filter-Funktionen
function filterUsers(searchTerm) {
const userRows = document.querySelectorAll('tbody tr');
userRows.forEach(row => {
const text = row.textContent.toLowerCase();
const matches = text.includes(searchTerm.toLowerCase());
row.style.display = matches ? '' : 'none';
});
}
function filterJobs(status) {
const jobRows = document.querySelectorAll('.job-row, [data-job-status]');
jobRows.forEach(row => {
if (status === 'all') {
row.style.display = '';
} else {
const jobStatus = row.dataset.jobStatus || row.querySelector('.status')?.textContent?.toLowerCase();
const matches = jobStatus === status.toLowerCase();
row.style.display = matches ? '' : 'none';
}
});
}
// Daten-Export
function exportData(type) {
const url = `${API_BASE_URL}/api/admin/export/${type}`;
window.open(url, '_blank');
}
// Analytics-Daten laden
async function loadAnalyticsData() {
try {
const response = await fetch(`${API_BASE_URL}/api/admin/stats/live`);
const data = await response.json();
// Charts mit Chart.js erstellen (falls verfügbar)
if (typeof Chart !== 'undefined') {
createPrinterUsageChart(data);
createSuccessRateChart(data);
}
} catch (error) {
console.error('Analytics data error:', error);
}
}
// Live-Analytics starten
function startLiveAnalytics() {
setInterval(async () => {
try {
const response = await fetch(`${API_BASE_URL}/api/admin/stats/live`);
const data = await response.json();
// Live-Werte aktualisieren
document.getElementById('live-jobs').textContent = data.jobs?.active || 0;
document.getElementById('live-printers').textContent = data.printers?.online || 0;
document.getElementById('live-queue').textContent = data.jobs?.queued || 0;
document.getElementById('live-success').textContent = (data.jobs?.success_rate || 0) + '%';
} catch (error) {
console.error('Live analytics error:', error);
}
}, 5000); // Alle 5 Sekunden
}
// System-Status aktualisieren
async function updateSystemStatus() {
try {
const response = await fetch(`${API_BASE_URL}/api/admin/system/status`);
const data = await response.json();
// Status-Indikatoren aktualisieren
const indicators = document.querySelectorAll('.status-indicator');
indicators.forEach(indicator => {
// Aktualisiere basierend auf data
});
} catch (error) {
console.error('System status update error:', error);
}
}
/**
* Drucker aktualisieren
*/