872 lines
34 KiB
JavaScript
872 lines
34 KiB
JavaScript
/**
|
|
* Mercedes-Benz MYP Admin Dashboard - Unified JavaScript
|
|
* Konsolidierte Admin-Funktionalitäten ohne Event-Handler-Konflikte
|
|
*/
|
|
|
|
// Globale Variablen und State-Management
|
|
class AdminDashboard {
|
|
constructor() {
|
|
this.csrfToken = null;
|
|
this.updateInterval = null;
|
|
this.eventListenersAttached = false;
|
|
this.apiBaseUrl = this.detectApiBaseUrl();
|
|
this.retryCount = 0;
|
|
this.maxRetries = 3;
|
|
this.isInitialized = false;
|
|
|
|
this.init();
|
|
}
|
|
|
|
detectApiBaseUrl() {
|
|
const currentHost = window.location.hostname;
|
|
const currentPort = window.location.port;
|
|
|
|
if (currentPort === '5000') {
|
|
return '';
|
|
}
|
|
|
|
return `http://${currentHost}:5000`;
|
|
}
|
|
|
|
init() {
|
|
if (this.isInitialized) {
|
|
console.log('🔄 Admin Dashboard bereits initialisiert, überspringe...');
|
|
return;
|
|
}
|
|
|
|
console.log('🚀 Initialisiere Mercedes-Benz MYP Admin Dashboard');
|
|
|
|
// CSRF Token setzen
|
|
this.csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
|
|
|
// Event-Listener nur einmal registrieren
|
|
this.attachEventListeners();
|
|
|
|
// Live-Updates starten
|
|
this.startLiveUpdates();
|
|
|
|
// Initiale Daten laden
|
|
this.loadInitialData();
|
|
|
|
this.isInitialized = true;
|
|
console.log('✅ Admin Dashboard erfolgreich initialisiert');
|
|
}
|
|
|
|
attachEventListeners() {
|
|
if (this.eventListenersAttached) {
|
|
console.log('⚠️ Event-Listener bereits registriert, überspringe...');
|
|
return;
|
|
}
|
|
|
|
// System-Action-Buttons mit Event-Delegation
|
|
this.attachSystemButtons();
|
|
this.attachUserManagement();
|
|
this.attachPrinterManagement();
|
|
this.attachJobManagement();
|
|
this.attachModalEvents();
|
|
|
|
this.eventListenersAttached = true;
|
|
console.log('📌 Event-Listener erfolgreich registriert');
|
|
}
|
|
|
|
attachSystemButtons() {
|
|
// System Status Button
|
|
this.addEventListenerSafe('#system-status-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.showSystemStatus();
|
|
});
|
|
|
|
// Analytics Button
|
|
this.addEventListenerSafe('#analytics-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.showAnalytics();
|
|
});
|
|
|
|
// Maintenance Button
|
|
this.addEventListenerSafe('#maintenance-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.showMaintenance();
|
|
});
|
|
|
|
// Cache leeren
|
|
this.addEventListenerSafe('#clear-cache-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.clearSystemCache();
|
|
});
|
|
|
|
// Datenbank optimieren
|
|
this.addEventListenerSafe('#optimize-db-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.optimizeDatabase();
|
|
});
|
|
|
|
// Backup erstellen
|
|
this.addEventListenerSafe('#create-backup-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.createSystemBackup();
|
|
});
|
|
|
|
// Drucker-Initialisierung erzwingen
|
|
this.addEventListenerSafe('#force-init-printers-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.forceInitializePrinters();
|
|
});
|
|
}
|
|
|
|
attachUserManagement() {
|
|
// Neuer Benutzer Button
|
|
this.addEventListenerSafe('#add-user-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.showUserModal();
|
|
});
|
|
|
|
// Event-Delegation für Benutzer-Aktionen
|
|
document.addEventListener('click', (e) => {
|
|
if (e.target.closest('.edit-user-btn')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const userId = e.target.closest('button').dataset.userId;
|
|
this.editUser(userId);
|
|
}
|
|
|
|
if (e.target.closest('.delete-user-btn')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const userId = e.target.closest('button').dataset.userId;
|
|
const userName = e.target.closest('button').dataset.userName;
|
|
this.deleteUser(userId, userName);
|
|
}
|
|
});
|
|
}
|
|
|
|
attachPrinterManagement() {
|
|
// Drucker hinzufügen Button
|
|
this.addEventListenerSafe('#add-printer-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.showPrinterModal();
|
|
});
|
|
|
|
// Event-Delegation für Drucker-Aktionen
|
|
document.addEventListener('click', (e) => {
|
|
if (e.target.closest('.manage-printer-btn')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const printerId = e.target.closest('button').dataset.printerId;
|
|
this.managePrinter(printerId);
|
|
}
|
|
|
|
if (e.target.closest('.settings-printer-btn')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const printerId = e.target.closest('button').dataset.printerId;
|
|
this.showPrinterSettings(printerId);
|
|
}
|
|
});
|
|
}
|
|
|
|
attachJobManagement() {
|
|
// Event-Delegation für Job-Aktionen
|
|
document.addEventListener('click', (e) => {
|
|
if (e.target.closest('.job-action-btn')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const action = e.target.closest('button').dataset.action;
|
|
const jobId = e.target.closest('button').dataset.jobId;
|
|
this.handleJobAction(action, jobId);
|
|
}
|
|
});
|
|
}
|
|
|
|
attachModalEvents() {
|
|
// Error-Alert Buttons
|
|
this.addEventListenerSafe('#fix-errors-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.fixErrors();
|
|
});
|
|
|
|
this.addEventListenerSafe('#dismiss-errors-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.dismissErrors();
|
|
});
|
|
|
|
this.addEventListenerSafe('#view-error-details-btn', 'click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
window.location.href = '/admin-dashboard?tab=logs';
|
|
});
|
|
}
|
|
|
|
addEventListenerSafe(selector, event, handler) {
|
|
const element = document.querySelector(selector);
|
|
if (element && !element.dataset.listenerAttached) {
|
|
element.addEventListener(event, handler);
|
|
element.dataset.listenerAttached = 'true';
|
|
}
|
|
}
|
|
|
|
startLiveUpdates() {
|
|
if (this.updateInterval) {
|
|
clearInterval(this.updateInterval);
|
|
}
|
|
|
|
// Statistiken alle 30 Sekunden aktualisieren
|
|
this.updateInterval = setInterval(() => {
|
|
this.loadLiveStats();
|
|
}, 30000);
|
|
|
|
// Live-Zeit jede Sekunde aktualisieren
|
|
setInterval(() => {
|
|
this.updateLiveTime();
|
|
}, 1000);
|
|
|
|
// System-Health alle 30 Sekunden prüfen
|
|
setInterval(() => {
|
|
this.checkSystemHealth();
|
|
}, 30000);
|
|
|
|
console.log('🔄 Live-Updates gestartet');
|
|
}
|
|
|
|
async loadInitialData() {
|
|
await this.loadLiveStats();
|
|
await this.checkSystemHealth();
|
|
}
|
|
|
|
async loadLiveStats() {
|
|
try {
|
|
const url = `${this.apiBaseUrl}/api/stats`;
|
|
const response = await fetch(url);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
this.updateStatsDisplay(data);
|
|
this.retryCount = 0;
|
|
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Live-Statistiken:', error);
|
|
this.retryCount++;
|
|
|
|
if (this.retryCount <= this.maxRetries) {
|
|
setTimeout(() => this.loadLiveStats(), 5000);
|
|
}
|
|
}
|
|
}
|
|
|
|
updateStatsDisplay(data) {
|
|
// Sichere Updates mit null-Checks
|
|
this.updateElement('live-users-count', data.total_users || 0);
|
|
this.updateElement('live-printers-count', data.total_printers || 0);
|
|
this.updateElement('live-printers-online', `${data.online_printers || 0} online`);
|
|
this.updateElement('live-jobs-active', data.active_jobs || 0);
|
|
this.updateElement('live-jobs-queued', `${data.queued_jobs || 0} in Warteschlange`);
|
|
this.updateElement('live-success-rate', `${data.success_rate || 0}%`);
|
|
|
|
// Progress Bars aktualisieren
|
|
this.updateProgressBar('users-progress', data.total_users, 20);
|
|
this.updateProgressBar('printers-progress', data.online_printers, data.total_printers);
|
|
this.updateProgressBar('jobs-progress', data.active_jobs, 10);
|
|
this.updateProgressBar('success-progress', data.success_rate, 100);
|
|
|
|
console.log('📊 Live-Statistiken aktualisiert');
|
|
}
|
|
|
|
updateElement(elementId, value) {
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
element.textContent = value;
|
|
}
|
|
}
|
|
|
|
updateProgressBar(progressId, currentValue, maxValue) {
|
|
const progressEl = document.getElementById(progressId);
|
|
if (progressEl && currentValue !== undefined && maxValue > 0) {
|
|
const percentage = Math.min(100, Math.max(0, (currentValue / maxValue) * 100));
|
|
progressEl.style.width = `${percentage}%`;
|
|
}
|
|
}
|
|
|
|
updateLiveTime() {
|
|
const timeElement = document.getElementById('live-time');
|
|
if (timeElement) {
|
|
const now = new Date();
|
|
timeElement.textContent = now.toLocaleTimeString('de-DE');
|
|
}
|
|
}
|
|
|
|
// System-Funktionen
|
|
async showSystemStatus() {
|
|
console.log('🔧 System Status wird angezeigt');
|
|
this.showNotification('System Status wird geladen...', 'info');
|
|
}
|
|
|
|
async showAnalytics() {
|
|
console.log('📈 Analytics wird angezeigt');
|
|
this.showNotification('Analytics werden geladen...', 'info');
|
|
}
|
|
|
|
async showMaintenance() {
|
|
console.log('🛠️ Wartung wird angezeigt');
|
|
const systemTab = document.querySelector('a[href*="tab=system"]');
|
|
if (systemTab) {
|
|
systemTab.click();
|
|
}
|
|
}
|
|
|
|
async clearSystemCache() {
|
|
if (!confirm('🗑️ Möchten Sie wirklich den System-Cache leeren?')) return;
|
|
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/cache/clear`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': this.csrfToken
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Cache erfolgreich geleert!', 'success');
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else {
|
|
this.showNotification('❌ Fehler beim Leeren des Cache', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('❌ Fehler beim Leeren des Cache', 'error');
|
|
}
|
|
}
|
|
|
|
async optimizeDatabase() {
|
|
if (!confirm('🔧 Möchten Sie die Datenbank optimieren?')) return;
|
|
|
|
this.showNotification('🔄 Datenbank wird optimiert...', 'info');
|
|
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/database/optimize`, {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': this.csrfToken }
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Datenbank erfolgreich optimiert!', 'success');
|
|
} else {
|
|
this.showNotification('❌ Fehler bei der Datenbank-Optimierung', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('❌ Fehler bei der Datenbank-Optimierung', 'error');
|
|
}
|
|
}
|
|
|
|
async createSystemBackup() {
|
|
if (!confirm('💾 Möchten Sie ein System-Backup erstellen?')) return;
|
|
|
|
this.showNotification('🔄 Backup wird erstellt...', 'info');
|
|
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/backup/create`, {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': this.csrfToken }
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Backup erfolgreich erstellt!', 'success');
|
|
} else {
|
|
this.showNotification('❌ Fehler beim Erstellen des Backups', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('❌ Fehler beim Erstellen des Backups', 'error');
|
|
}
|
|
}
|
|
|
|
async forceInitializePrinters() {
|
|
if (!confirm('🔄 Möchten Sie die Drucker-Initialisierung erzwingen?')) return;
|
|
|
|
this.showNotification('🔄 Drucker werden initialisiert...', 'info');
|
|
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/printers/force-init`, {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': this.csrfToken }
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Drucker erfolgreich initialisiert!', 'success');
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else {
|
|
this.showNotification('❌ Fehler bei der Drucker-Initialisierung', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('❌ Fehler bei der Drucker-Initialisierung', 'error');
|
|
}
|
|
}
|
|
|
|
// User-Management
|
|
showUserModal(userId = null) {
|
|
const isEdit = userId !== null;
|
|
const title = isEdit ? 'Benutzer bearbeiten' : 'Neuer Benutzer';
|
|
|
|
// Modal HTML erstellen
|
|
const modalHtml = `
|
|
<div id="user-modal" class="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
|
<div class="bg-white dark:bg-slate-800 rounded-2xl p-8 max-w-md w-full shadow-2xl transform scale-100 transition-all duration-300">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h3 class="text-2xl font-bold text-slate-900 dark:text-white">${title}</h3>
|
|
<button onclick="this.closest('#user-modal').remove()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors">
|
|
<svg class="w-6 h-6 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<form id="user-form" class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">E-Mail-Adresse *</label>
|
|
<input type="email" name="email" id="user-email" required
|
|
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
|
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
|
dark:bg-slate-700 dark:text-white bg-slate-50">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Benutzername</label>
|
|
<input type="text" name="username" id="user-username"
|
|
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
|
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
|
dark:bg-slate-700 dark:text-white bg-slate-50">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Name</label>
|
|
<input type="text" name="name" id="user-name"
|
|
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
|
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
|
dark:bg-slate-700 dark:text-white bg-slate-50">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Passwort ${isEdit ? '(leer lassen für keine Änderung)' : '*'}</label>
|
|
<input type="password" name="password" id="user-password" ${!isEdit ? 'required' : ''}
|
|
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
|
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
|
dark:bg-slate-700 dark:text-white bg-slate-50">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Rolle</label>
|
|
<select name="role" id="user-role"
|
|
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
|
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
|
dark:bg-slate-700 dark:text-white bg-slate-50">
|
|
<option value="user">Benutzer</option>
|
|
<option value="admin">Administrator</option>
|
|
</select>
|
|
</div>
|
|
|
|
${isEdit ? `
|
|
<div class="flex items-center space-x-2">
|
|
<input type="checkbox" name="is_active" id="user-active"
|
|
class="w-4 h-4 text-blue-600 bg-slate-100 border-slate-300 rounded focus:ring-blue-500">
|
|
<label for="user-active" class="text-sm font-medium text-slate-700 dark:text-slate-300">Aktiv</label>
|
|
</div>
|
|
` : ''}
|
|
|
|
<div class="flex justify-end space-x-3 mt-8 pt-6 border-t border-slate-200 dark:border-slate-600">
|
|
<button type="button" onclick="this.closest('#user-modal').remove()"
|
|
class="px-6 py-3 bg-slate-200 dark:bg-slate-600 text-slate-700 dark:text-slate-300
|
|
rounded-xl hover:bg-slate-300 dark:hover:bg-slate-500 transition-colors font-medium">
|
|
Abbrechen
|
|
</button>
|
|
<button type="submit" id="user-submit-btn"
|
|
class="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white
|
|
rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all duration-300
|
|
shadow-lg hover:shadow-xl font-medium">
|
|
${isEdit ? 'Aktualisieren' : 'Erstellen'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Modal zum DOM hinzufügen
|
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|
|
|
// Event-Listener für das Formular
|
|
const form = document.getElementById('user-form');
|
|
form.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (isEdit) {
|
|
this.updateUser(userId, new FormData(form));
|
|
} else {
|
|
this.createUser(new FormData(form));
|
|
}
|
|
});
|
|
|
|
// Bei Bearbeitung: Benutzer-Daten laden
|
|
if (isEdit) {
|
|
this.loadUserData(userId);
|
|
}
|
|
|
|
// Fokus auf erstes Eingabefeld
|
|
setTimeout(() => {
|
|
document.getElementById('user-email').focus();
|
|
}, 100);
|
|
}
|
|
|
|
async loadUserData(userId) {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const user = data.user;
|
|
|
|
// Formular mit Benutzerdaten füllen
|
|
document.getElementById('user-email').value = user.email || '';
|
|
document.getElementById('user-username').value = user.username || '';
|
|
document.getElementById('user-name').value = user.name || '';
|
|
document.getElementById('user-role').value = user.is_admin ? 'admin' : 'user';
|
|
|
|
const activeCheckbox = document.getElementById('user-active');
|
|
if (activeCheckbox) {
|
|
activeCheckbox.checked = user.is_active !== false;
|
|
}
|
|
} else {
|
|
this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Benutzerdaten:', error);
|
|
this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error');
|
|
}
|
|
}
|
|
|
|
async createUser(formData) {
|
|
const submitBtn = document.getElementById('user-submit-btn');
|
|
const originalText = submitBtn.innerHTML;
|
|
|
|
try {
|
|
// Loading-Zustand
|
|
submitBtn.innerHTML = '<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mx-auto"></div>';
|
|
submitBtn.disabled = true;
|
|
|
|
// FormData zu JSON konvertieren
|
|
const userData = {
|
|
email: formData.get('email'),
|
|
username: formData.get('username') || formData.get('email').split('@')[0],
|
|
name: formData.get('name'),
|
|
password: formData.get('password'),
|
|
is_admin: formData.get('role') === 'admin'
|
|
};
|
|
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/users`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': this.csrfToken
|
|
},
|
|
body: JSON.stringify(userData)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Benutzer erfolgreich erstellt!', 'success');
|
|
document.getElementById('user-modal').remove();
|
|
|
|
// Seite nach 1 Sekunde neu laden um neue Benutzerliste zu zeigen
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 1000);
|
|
} else {
|
|
this.showNotification(`❌ Fehler: ${data.error}`, 'error');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Fehler beim Erstellen des Benutzers:', error);
|
|
this.showNotification('❌ Fehler beim Erstellen des Benutzers', 'error');
|
|
} finally {
|
|
// Button zurücksetzen
|
|
submitBtn.innerHTML = originalText;
|
|
submitBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
async updateUser(userId, formData) {
|
|
const submitBtn = document.getElementById('user-submit-btn');
|
|
const originalText = submitBtn.innerHTML;
|
|
|
|
try {
|
|
// Loading-Zustand
|
|
submitBtn.innerHTML = '<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mx-auto"></div>';
|
|
submitBtn.disabled = true;
|
|
|
|
// FormData zu JSON konvertieren
|
|
const userData = {
|
|
email: formData.get('email'),
|
|
username: formData.get('username'),
|
|
name: formData.get('name'),
|
|
is_admin: formData.get('role') === 'admin',
|
|
is_active: formData.get('is_active') === 'on'
|
|
};
|
|
|
|
// Passwort nur hinzufügen wenn es gesetzt wurde
|
|
const password = formData.get('password');
|
|
if (password && password.trim()) {
|
|
userData.password = password;
|
|
}
|
|
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': this.csrfToken
|
|
},
|
|
body: JSON.stringify(userData)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Benutzer erfolgreich aktualisiert!', 'success');
|
|
document.getElementById('user-modal').remove();
|
|
|
|
// Seite nach 1 Sekunde neu laden um aktualisierte Benutzerliste zu zeigen
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 1000);
|
|
} else {
|
|
this.showNotification(`❌ Fehler: ${data.error}`, 'error');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Fehler beim Aktualisieren des Benutzers:', error);
|
|
this.showNotification('❌ Fehler beim Aktualisieren des Benutzers', 'error');
|
|
} finally {
|
|
// Button zurücksetzen
|
|
submitBtn.innerHTML = originalText;
|
|
submitBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
editUser(userId) {
|
|
console.log(`✏️ Benutzer ${userId} wird bearbeitet`);
|
|
this.showUserModal(userId);
|
|
}
|
|
|
|
async deleteUser(userId, userName) {
|
|
if (!confirm(`🗑️ Möchten Sie den Benutzer "${userName}" wirklich löschen?\n\nDiese Aktion kann nicht rückgängig gemacht werden!`)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
this.showNotification(`🔄 Benutzer "${userName}" wird gelöscht...`, 'info');
|
|
|
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': this.csrfToken
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification(`✅ Benutzer "${userName}" erfolgreich gelöscht!`, 'success');
|
|
|
|
// Seite nach 1 Sekunde neu laden um aktualisierte Benutzerliste zu zeigen
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 1000);
|
|
} else {
|
|
this.showNotification(`❌ Fehler beim Löschen: ${data.error}`, 'error');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Fehler beim Löschen des Benutzers:', error);
|
|
this.showNotification('❌ Fehler beim Löschen des Benutzers', 'error');
|
|
}
|
|
}
|
|
|
|
// Printer-Management
|
|
showPrinterModal() {
|
|
console.log('🖨️ Drucker-Modal wird angezeigt');
|
|
this.showNotification('Drucker-Funktionen werden geladen...', 'info');
|
|
}
|
|
|
|
managePrinter(printerId) {
|
|
console.log(`🔧 Drucker ${printerId} wird verwaltet`);
|
|
this.showNotification(`Drucker ${printerId} wird verwaltet...`, 'info');
|
|
}
|
|
|
|
showPrinterSettings(printerId) {
|
|
console.log(`⚙️ Drucker-Einstellungen ${printerId} werden angezeigt`);
|
|
this.showNotification(`Drucker-Einstellungen werden geladen...`, 'info');
|
|
}
|
|
|
|
// Job-Management
|
|
handleJobAction(action, jobId) {
|
|
console.log(`📋 Job-Aktion "${action}" für Job ${jobId}`);
|
|
this.showNotification(`Job-Aktion "${action}" wird ausgeführt...`, 'info');
|
|
}
|
|
|
|
// Error-Management
|
|
async checkSystemHealth() {
|
|
try {
|
|
const response = await fetch('/api/admin/system-health');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.updateHealthDisplay(data);
|
|
this.updateErrorAlerts(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler bei System-Health-Check:', error);
|
|
}
|
|
}
|
|
|
|
updateHealthDisplay(data) {
|
|
const statusIndicator = document.getElementById('db-status-indicator');
|
|
const statusText = document.getElementById('db-status-text');
|
|
|
|
if (statusIndicator && statusText) {
|
|
if (data.health_status === 'critical') {
|
|
statusIndicator.className = 'w-3 h-3 bg-red-500 rounded-full animate-pulse';
|
|
statusText.textContent = 'Kritisch';
|
|
statusText.className = 'text-sm font-medium text-red-600 dark:text-red-400';
|
|
} else if (data.health_status === 'warning') {
|
|
statusIndicator.className = 'w-3 h-3 bg-yellow-500 rounded-full animate-pulse';
|
|
statusText.textContent = 'Warnung';
|
|
statusText.className = 'text-sm font-medium text-yellow-600 dark:text-yellow-400';
|
|
} else {
|
|
statusIndicator.className = 'w-3 h-3 bg-green-400 rounded-full animate-pulse';
|
|
statusText.textContent = 'Gesund';
|
|
statusText.className = 'text-sm font-medium text-green-600 dark:text-green-400';
|
|
}
|
|
}
|
|
|
|
this.updateElement('last-migration', data.last_migration || 'Unbekannt');
|
|
this.updateElement('schema-integrity', data.schema_integrity || 'Prüfung');
|
|
this.updateElement('recent-errors-count', data.recent_errors_count || 0);
|
|
}
|
|
|
|
updateErrorAlerts(data) {
|
|
const alertContainer = document.getElementById('critical-errors-alert');
|
|
if (!alertContainer) return;
|
|
|
|
const allErrors = [...(data.critical_errors || []), ...(data.warnings || [])];
|
|
|
|
if (allErrors.length > 0) {
|
|
alertContainer.classList.remove('hidden');
|
|
} else {
|
|
alertContainer.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
async fixErrors() {
|
|
if (!confirm('🔧 Möchten Sie die automatische Fehlerkorrektur durchführen?')) return;
|
|
|
|
this.showNotification('🔄 Fehler werden automatisch behoben...', 'info');
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/fix-errors', {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': this.csrfToken }
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('✅ Automatische Reparatur erfolgreich!', 'success');
|
|
setTimeout(() => this.checkSystemHealth(), 2000);
|
|
} else {
|
|
this.showNotification('❌ Automatische Reparatur fehlgeschlagen', 'error');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('❌ Fehler bei der automatischen Reparatur', 'error');
|
|
}
|
|
}
|
|
|
|
dismissErrors() {
|
|
const alertContainer = document.getElementById('critical-errors-alert');
|
|
if (alertContainer) {
|
|
alertContainer.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
// Notification System
|
|
showNotification(message, type = 'info') {
|
|
let notification = document.getElementById('admin-notification');
|
|
if (!notification) {
|
|
notification = document.createElement('div');
|
|
notification.id = 'admin-notification';
|
|
notification.className = 'fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transition-all duration-300';
|
|
document.body.appendChild(notification);
|
|
}
|
|
|
|
const colors = {
|
|
success: 'bg-green-500 text-white',
|
|
error: 'bg-red-500 text-white',
|
|
info: 'bg-blue-500 text-white',
|
|
warning: 'bg-yellow-500 text-white'
|
|
};
|
|
|
|
notification.className = `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transition-all duration-300 ${colors[type]}`;
|
|
notification.textContent = message;
|
|
notification.style.transform = 'translateX(0)';
|
|
|
|
// Auto-Hide nach 3 Sekunden
|
|
setTimeout(() => {
|
|
if (notification) {
|
|
notification.style.transform = 'translateX(100%)';
|
|
setTimeout(() => {
|
|
if (notification && notification.parentNode) {
|
|
notification.parentNode.removeChild(notification);
|
|
}
|
|
}, 300);
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
// Sichere Initialisierung - nur einmal ausführen
|
|
let adminDashboardInstance = null;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
if (!adminDashboardInstance) {
|
|
adminDashboardInstance = new AdminDashboard();
|
|
window.AdminDashboard = adminDashboardInstance;
|
|
console.log('🎯 Admin Dashboard erfolgreich initialisiert (unified)');
|
|
}
|
|
});
|
|
|
|
// Export für globalen Zugriff
|
|
window.AdminDashboard = AdminDashboard;
|