manage-your-printer/static/js/admin-unified.min.js
2025-06-04 10:03:22 +02:00

101 lines
34 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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:}
init(){if(this.isInitialized){console.log('🔄 Admin Dashboard bereits initialisiert, überspringe...');return;}
console.log('🚀 Initialisiere Mercedes-Benz MYP Admin Dashboard');this.csrfToken=this.extractCSRFToken();console.log('🔒 CSRF Token:',this.csrfToken?'verfügbar':'FEHLT!');this.attachEventListeners();this.startLiveUpdates();this.loadInitialData();this.isInitialized=true;console.log('✅ Admin Dashboard erfolgreich initialisiert');}
extractCSRFToken(){const metaToken=document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');if(metaToken){console.log('🔒 CSRF Token aus meta Tag geladen');return metaToken;}
const hiddenInput=document.querySelector('input[name="csrf_token"]')?.value;if(hiddenInput){console.log('🔒 CSRF Token aus hidden input geladen');return hiddenInput;}
const cookieToken=document.cookie.split('; ').find(row=>row.startsWith('csrf_token='))?.split('=')[1];if(cookieToken){console.log('🔒 CSRF Token aus Cookie geladen');return cookieToken;}
const flaskToken=document.querySelector('meta[name="csrf-token"]')?.content;if(flaskToken){console.log('🔒 CSRF Token aus Flask-WTF Meta geladen');return flaskToken;}
console.error('❌ CSRF Token konnte nicht gefunden werden!');return null;}
attachEventListeners(){if(this.eventListenersAttached){console.log('⚠️ Event-Listener bereits registriert, überspringe...');return;}
this.attachSystemButtons();this.attachUserManagement();this.attachPrinterManagement();this.attachJobManagement();this.attachModalEvents();this.eventListenersAttached=true;console.log('📌 Event-Listener erfolgreich registriert');}
attachSystemButtons(){this.addEventListenerSafe('#system-status-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showSystemStatus();});this.addEventListenerSafe('#analytics-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showAnalytics();});this.addEventListenerSafe('#clear-cache-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.clearSystemCache();});this.addEventListenerSafe('#optimize-db-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.optimizeDatabase();});this.addEventListenerSafe('#create-backup-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.createSystemBackup();});this.addEventListenerSafe('#force-init-printers-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.forceInitializePrinters();});}
attachUserManagement(){this.addEventListenerSafe('#add-user-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showUserModal();});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(){this.addEventListenerSafe('#add-printer-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showPrinterModal();});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);}
if(e.target.closest('.toggle-printer-power-btn')){e.preventDefault();e.stopPropagation();const button=e.target.closest('button');const printerId=button.dataset.printerId;const printerName=button.dataset.printerName;this.togglePrinterPower(printerId,printerName,button);}});}
attachJobManagement(){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(){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';});this.addEventListenerSafe('#refresh-logs-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.loadLogs();});this.addEventListenerSafe('#export-logs-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.exportLogs();});this.addEventListenerSafe('#log-level-filter','change',(e)=>{this.loadLogs();});}
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);}
this.updateInterval=setInterval(()=>{this.loadLiveStats();},30000);setInterval(()=>{this.updateLiveTime();},1000);setInterval(()=>{this.checkSystemHealth();},30000);console.log('🔄 Live-Updates gestartet');}
async loadInitialData(){await this.loadLiveStats();await this.checkSystemHealth();setTimeout(()=>{this.testButtons();},1000);if(window.location.search.includes('tab=logs')||document.querySelector('.tabs [href*="logs"]')?.classList.contains('active')){await this.loadLogs();}
const urlParams=new URLSearchParams(window.location.search);const activeTab=urlParams.get('tab');if(activeTab==='logs'){await this.loadLogs();}
const logsContainer=document.getElementById('logs-container');if(logsContainer&&logsContainer.offsetParent!==null){await this.loadLogs();}}
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){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}%`);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');}}
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');}}
showUserModal(userId=null){const isEdit=userId!==null;const title=isEdit?'Benutzer bearbeiten':'Neuer Benutzer';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>`;document.body.insertAdjacentHTML('beforeend',modalHtml);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));}});if(isEdit){this.loadUserData(userId);}
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;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{submitBtn.innerHTML='<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mx-auto"></div>';submitBtn.disabled=true;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();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{submitBtn.innerHTML=originalText;submitBtn.disabled=false;}}
async updateUser(userId,formData){const submitBtn=document.getElementById('user-submit-btn');const originalText=submitBtn.innerHTML;try{submitBtn.innerHTML='<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mx-auto"></div>';submitBtn.disabled=true;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'};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();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{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');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');}}
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');}
async togglePrinterPower(printerId,printerName,button){console.log(`🔌 Smart-Plug Toggle für Drucker ${printerId}(${printerName})`);const confirmMessage=`Möchten Sie die Steckdose für"${printerName}"umschalten?\n\nDies schaltet den Drucker ein/aus.`;if(!confirm(confirmMessage))return;const originalContent=button.innerHTML;button.disabled=true;button.innerHTML=`<div class="animate-spin rounded-full h-4 w-4 border-2 border-white border-t-transparent"></div><span class="hidden lg:inline">Schaltet...</span>`;try{const response=await fetch(`${this.apiBaseUrl}/api/admin/printers/${printerId}/toggle`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken},body:JSON.stringify({reason:'Admin-Panel Smart-Plug Toggle'})});const data=await response.json();if(response.ok&&data.success){const action=data.action||'umgeschaltet';this.showNotification(`✅ Steckdose für"${printerName}"erfolgreich ${action}`,'success');button.classList.remove('from-orange-500','to-red-500');button.classList.add('from-green-500','to-green-600');setTimeout(()=>{button.classList.remove('from-green-500','to-green-600');button.classList.add('from-orange-500','to-red-500');},2000);setTimeout(()=>{this.loadLiveStats();},1000);}else{const errorMsg=data.error||'Unbekannter Fehler beim Schalten der Steckdose';this.showNotification(`❌ Fehler:${errorMsg}`,'error');console.error('Smart-Plug Toggle Fehler:',data);}}catch(error){console.error('Netzwerkfehler beim Smart-Plug Toggle:',error);this.showNotification(`❌ Netzwerkfehler beim Schalten der Steckdose für"${printerName}"`,'error');}finally{button.disabled=false;button.innerHTML=originalContent;}}
handleJobAction(action,jobId){console.log(`📋 Job-Aktion"${action}"für Job ${jobId}`);this.showNotification(`Job-Aktion"${action}"wird ausgeführt...`,'info');}
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');if(!this.csrfToken){console.error('❌ CSRF Token fehlt! Versuche Token neu zu laden...');this.csrfToken=this.extractCSRFToken();if(!this.csrfToken){this.showNotification('❌ Sicherheitsfehler: CSRF Token nicht verfügbar','error');return;}}
console.log('🔧 Starte automatische Fehlerkorrektur...');console.log('🔒 CSRF Token für Request:',this.csrfToken.substring(0,10)+'...');try{const requestOptions={method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken}};console.log('📡 Sende Request an:','/api/admin/fix-errors');console.log('📝 Request Headers:',requestOptions.headers);const response=await fetch('/api/admin/fix-errors',requestOptions);console.log('📡 Response Status:',response.status);console.log('📡 Response Headers:',Object.fromEntries(response.headers.entries()));if(!response.ok){const errorText=await response.text();console.error('❌ Response Error:',errorText);throw new Error(`HTTP ${response.status}:${response.statusText}-${errorText}`);}
const data=await response.json();console.log('✅ Response Data:',data);if(data.success){this.showNotification('✅ Automatische Reparatur erfolgreich!','success');setTimeout(()=>this.checkSystemHealth(),2000);}else{this.showNotification(`❌ Automatische Reparatur fehlgeschlagen:${data.message||'Unbekannter Fehler'}`,'error');}}catch(error){console.error('❌ Fehler bei automatischer Reparatur:',error);this.showNotification(`❌ Fehler bei der automatischen Reparatur:${error.message}`,'error');}}
dismissErrors(){const alertContainer=document.getElementById('critical-errors-alert');if(alertContainer){alertContainer.classList.add('hidden');}}
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)';setTimeout(()=>{if(notification){notification.style.transform='translateX(100%)';setTimeout(()=>{if(notification&&notification.parentNode){notification.parentNode.removeChild(notification);}},300);}},3000);}
async loadLogs(level=null){const logsContainer=document.getElementById('logs-container');if(!logsContainer)return;logsContainer.innerHTML=`<div class="flex justify-center items-center py-12"><div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 dark:border-blue-400"></div><span class="ml-3 text-slate-600 dark:text-slate-400">Logs werden geladen...</span></div>`;try{const filter=level||document.getElementById('log-level-filter')?.value||'all';const url=`${this.apiBaseUrl}/api/admin/logs?level=${filter}&limit=100`;const response=await fetch(url,{headers:{'X-CSRFToken':this.csrfToken}});if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
const data=await response.json();this.displayLogs(data.logs||[]);console.log('📋 Logs erfolgreich geladen');}catch(error){console.error('Fehler beim Laden der Logs:',error);logsContainer.innerHTML=`<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl p-6 text-center"><svg class="w-12 h-12 text-red-500 mx-auto mb-4"fill="none"stroke="currentColor"viewBox="0 0 24 24"><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-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.35 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg><h3 class="text-lg font-semibold text-red-800 dark:text-red-200 mb-2">Fehler beim Laden der Logs</h3><p class="text-red-600 dark:text-red-400 mb-4">${error.message}</p><button onclick="adminDashboard.loadLogs()"class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">🔄 Erneut versuchen</button></div>`;}}
displayLogs(logs){const logsContainer=document.getElementById('logs-container');if(!logsContainer)return;if(!logs||logs.length===0){logsContainer.innerHTML=`<div class="text-center py-12"><svg class="w-16 h-16 mx-auto text-slate-400 dark:text-slate-500 mb-4"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg><h3 class="text-lg font-medium text-slate-900 dark:text-white mb-2">Keine Logs gefunden</h3><p class="text-slate-500 dark:text-slate-400">Es sind keine Logs für die ausgewählten Kriterien vorhanden.</p></div>`;return;}
const logsHtml=logs.map(log=>{const levelColors={'error':'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-800 dark:text-red-200','warning':'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800 text-yellow-800 dark:text-yellow-200','info':'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-800 dark:text-blue-200','debug':'bg-gray-50 dark:bg-gray-900/20 border-gray-200 dark:border-gray-800 text-gray-800 dark:text-gray-200','critical':'bg-red-100 dark:bg-red-900/40 border-red-300 dark:border-red-700 text-red-900 dark:text-red-100'};const levelIcons={'error':'❌','warning':'⚠️','info':'','debug':'🔍','critical':'🚨'};const levelClass=levelColors[log.level]||levelColors['info'];const levelIcon=levelIcons[log.level]||'';return`<div class="border rounded-xl p-4 transition-all duration-200 hover:shadow-md ${levelClass}"><div class="flex items-start justify-between mb-2"><div class="flex items-center space-x-2"><span class="text-lg">${levelIcon}</span><span class="font-semibold text-sm uppercase tracking-wide">${log.level}</span><span class="text-xs opacity-75">${log.component||'System'}</span></div><div class="text-xs opacity-75">${this.formatLogTimestamp(log.timestamp)}</div></div><div class="mb-2"><p class="font-medium">${this.escapeHtml(log.message)}</p>${log.details?`<p class="text-sm opacity-75 mt-1">${this.escapeHtml(log.details)}</p>`:''}</div>${log.user?`<div class="text-xs opacity-75"><span class="font-medium">Benutzer:</span>${this.escapeHtml(log.user)}</div>`:''}
${log.ip_address?`<div class="text-xs opacity-75"><span class="font-medium">IP:</span>${this.escapeHtml(log.ip_address)}</div>`:''}
${log.request_id?`<div class="text-xs opacity-75 mt-2 font-mono"><span class="font-medium">Request-ID:</span>${this.escapeHtml(log.request_id)}</div>`:''}</div>`;}).join('');logsContainer.innerHTML=logsHtml;}
formatLogTimestamp(timestamp){if(!timestamp)return'Unbekannt';try{const date=new Date(timestamp);return date.toLocaleString('de-DE',{year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit'});}catch(error){return timestamp;}}
escapeHtml(text){if(!text)return'';const div=document.createElement('div');div.textContent=text;return div.innerHTML;}
async exportLogs(){try{this.showNotification('📥 Logs werden exportiert...','info');const filter=document.getElementById('log-level-filter')?.value||'all';const url=`${this.apiBaseUrl}/api/admin/logs/export?level=${filter}`;const response=await fetch(url,{headers:{'X-CSRFToken':this.csrfToken}});if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
const blob=await response.blob();const downloadUrl=window.URL.createObjectURL(blob);const a=document.createElement('a');a.href=downloadUrl;a.download=`system-logs-${new Date().toISOString().split('T')[0]}.csv`;document.body.appendChild(a);a.click();document.body.removeChild(a);window.URL.revokeObjectURL(downloadUrl);this.showNotification('✅ Logs erfolgreich exportiert!','success');}catch(error){console.error('Fehler beim Exportieren der Logs:',error);this.showNotification('❌ Fehler beim Exportieren der Logs: '+error.message,'error');}}
testButtons(){console.log('🧪 Teste Button-Funktionalität...');const fixBtn=document.querySelector('#fix-errors-btn');if(fixBtn){console.log('✅ Fix-Errors Button gefunden:',fixBtn);console.log('🔗 Event-Listener-Status:',fixBtn.dataset.listenerAttached);fixBtn.addEventListener('click',(e)=>{console.log('🖱️ Fix-Errors Button wurde geklickt (manueller Listener)');e.preventDefault();e.stopPropagation();this.testFixErrors();});}else{console.error('❌ Fix-Errors Button NICHT gefunden!');}
const viewBtn=document.querySelector('#view-error-details-btn');if(viewBtn){console.log('✅ View-Details Button gefunden:',viewBtn);console.log('🔗 Event-Listener-Status:',viewBtn.dataset.listenerAttached);viewBtn.addEventListener('click',(e)=>{console.log('🖱️ View-Details Button wurde geklickt (manueller Listener)');e.preventDefault();e.stopPropagation();console.log('🔄 Weiterleitung zu Logs-Tab...');window.location.href='/admin-dashboard?tab=logs';});}else{console.error('❌ View-Details Button NICHT gefunden!');}}
async testFixErrors(){console.log('🧪 TEST: Fix-Errors wird ausgeführt...');console.log('🔒 Aktueller CSRF Token:',this.csrfToken);if(!this.csrfToken){console.error('❌ CSRF Token fehlt - versuche neu zu laden...');this.csrfToken=this.extractCSRFToken();console.log('🔒 Neu geladener Token:',this.csrfToken);}
this.showNotification('🧪 TEST: Starte automatische Fehlerkorrektur...','info');try{const requestOptions={method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken,'X-Requested-With':'XMLHttpRequest'}};console.log('📡 TEST Request an:','/api/admin/fix-errors');console.log('📝 TEST Headers:',requestOptions.headers);const response=await fetch('/api/admin/fix-errors',requestOptions);console.log('📡 TEST Response Status:',response.status);console.log('📡 TEST Response Headers:',Object.fromEntries(response.headers.entries()));if(!response.ok){const errorText=await response.text();console.error('❌ TEST Response Error:',errorText);this.showNotification(`❌ TEST Fehler:${response.status}-${errorText}`,'error');return;}
const data=await response.json();console.log('✅ TEST Response Data:',data);if(data.success){this.showNotification('✅ TEST: Automatische Reparatur erfolgreich!','success');}else{this.showNotification(`❌ TEST:Reparatur fehlgeschlagen-${data.message}`,'error');}}catch(error){console.error('❌ TEST Fehler:',error);this.showNotification(`❌ TEST Netzwerk-Fehler:${error.message}`,'error');}}}
let adminDashboardInstance=null;document.addEventListener('DOMContentLoaded',function(){if(!adminDashboardInstance){adminDashboardInstance=new AdminDashboard();window.AdminDashboard=adminDashboardInstance;console.log('🎯 Admin Dashboard erfolgreich initialisiert (unified)');}});window.AdminDashboard=AdminDashboard;