🎉 Feature: Implemented Real-Time Dashboard Updates and Enhanced System Monitoring
This commit introduces a comprehensive update to the admin dashboard, adding real-time statistics for users, printers, and jobs. Key features include: - Live updates for user counts, printer statuses, and job statistics. - A new loading overlay for better user experience during data fetching. - Enhanced error handling and notifications for system health checks. - Introduction of a modal for displaying detailed system status and metrics. These improvements aim to provide administrators with immediate insights into system performance and operational status, enhancing overall usability and monitoring capabilities.
This commit is contained in:
@ -332,8 +332,12 @@ class AdminDashboard {
|
|||||||
try {
|
try {
|
||||||
const url = `${this.apiBaseUrl}/api/stats`;
|
const url = `${this.apiBaseUrl}/api/stats`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
const data = await this.validateApiResponse(response, 'Live-Statistiken');
|
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
this.updateStatsDisplay(data);
|
this.updateStatsDisplay(data);
|
||||||
this.retryCount = 0;
|
this.retryCount = 0;
|
||||||
|
|
||||||
@ -343,8 +347,6 @@ class AdminDashboard {
|
|||||||
|
|
||||||
if (this.retryCount <= this.maxRetries) {
|
if (this.retryCount <= this.maxRetries) {
|
||||||
setTimeout(() => this.loadLiveStats(), 5000);
|
setTimeout(() => this.loadLiveStats(), 5000);
|
||||||
} else {
|
|
||||||
this.showNotification('Live-Statistiken konnten nicht geladen werden', 'error');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -622,7 +624,12 @@ class AdminDashboard {
|
|||||||
async loadUserData(userId) {
|
async loadUserData(userId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`);
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`);
|
||||||
const data = await this.validateApiResponse(response, 'Benutzerdaten laden');
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const user = data.user;
|
const user = data.user;
|
||||||
@ -642,7 +649,7 @@ class AdminDashboard {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Benutzerdaten:', error);
|
console.error('Fehler beim Laden der Benutzerdaten:', error);
|
||||||
this.showNotification(`❌ Fehler beim Laden der Benutzerdaten: ${error.message}`, 'error');
|
this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,7 +680,7 @@ class AdminDashboard {
|
|||||||
body: JSON.stringify(userData)
|
body: JSON.stringify(userData)
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await this.validateApiResponse(response, 'Benutzer erstellen');
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.showNotification('✅ Benutzer erfolgreich erstellt!', 'success');
|
this.showNotification('✅ Benutzer erfolgreich erstellt!', 'success');
|
||||||
@ -888,101 +895,20 @@ class AdminDashboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error-Management
|
// Error-Management
|
||||||
/**
|
|
||||||
* Zentrale API-Response-Validierung mit umfassendem Error-Handling
|
|
||||||
* @param {Response} response - Fetch Response-Objekt
|
|
||||||
* @param {string} context - Kontext der API-Anfrage für bessere Fehlermeldungen
|
|
||||||
* @returns {Promise<Object>} - Validierte JSON-Daten
|
|
||||||
* @throws {Error} - Bei Validierungsfehlern
|
|
||||||
*/
|
|
||||||
async validateApiResponse(response, context = 'API-Anfrage') {
|
|
||||||
try {
|
|
||||||
// 1. HTTP Status Code prüfen
|
|
||||||
if (!response.ok) {
|
|
||||||
// Spezielle Behandlung für bekannte Fehler-Codes
|
|
||||||
switch (response.status) {
|
|
||||||
case 401:
|
|
||||||
throw new Error(`Authentifizierung fehlgeschlagen (${context})`);
|
|
||||||
case 403:
|
|
||||||
throw new Error(`Zugriff verweigert (${context})`);
|
|
||||||
case 404:
|
|
||||||
throw new Error(`Ressource nicht gefunden (${context})`);
|
|
||||||
case 429:
|
|
||||||
throw new Error(`Zu viele Anfragen (${context})`);
|
|
||||||
case 500:
|
|
||||||
throw new Error(`Serverfehler (${context})`);
|
|
||||||
case 503:
|
|
||||||
throw new Error(`Service nicht verfügbar (${context})`);
|
|
||||||
default:
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Content-Type prüfen (muss application/json enthalten)
|
|
||||||
const contentType = response.headers.get('content-type');
|
|
||||||
if (!contentType || !contentType.includes('application/json')) {
|
|
||||||
// Versuche Response-Text zu lesen für bessere Fehlermeldung
|
|
||||||
const responseText = await response.text();
|
|
||||||
|
|
||||||
// Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten)
|
|
||||||
if (responseText.includes('<!DOCTYPE html>') || responseText.includes('<html')) {
|
|
||||||
console.warn(`❌ HTML-Fehlerseite erhalten statt JSON (${context}):`, responseText.substring(0, 200));
|
|
||||||
throw new Error(`Server-Fehlerseite erhalten statt JSON-Response (${context})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.warn(`❌ Ungültiger Content-Type (${context}):`, contentType);
|
|
||||||
console.warn(`❌ Response-Text (${context}):`, responseText.substring(0, 500));
|
|
||||||
throw new Error(`Ungültiger Content-Type: ${contentType || 'fehlt'} (${context})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. JSON parsing mit detailliertem Error-Handling
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = await response.json();
|
|
||||||
} catch (jsonError) {
|
|
||||||
// Versuche rohen Text zu lesen für Debugging
|
|
||||||
const rawText = await response.text();
|
|
||||||
console.error(`❌ JSON-Parsing-Fehler (${context}):`, jsonError);
|
|
||||||
console.error(`❌ Raw Response (${context}):`, rawText.substring(0, 1000));
|
|
||||||
throw new Error(`Ungültige JSON-Response: ${jsonError.message} (${context})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Prüfe auf null/undefined Response
|
|
||||||
if (data === null || data === undefined) {
|
|
||||||
throw new Error(`Leere Response erhalten (${context})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Validiere Response-Struktur (wenn success-Feld erwartet wird)
|
|
||||||
if (typeof data === 'object' && data.hasOwnProperty('success')) {
|
|
||||||
if (!data.success && data.error) {
|
|
||||||
console.warn(`❌ API-Fehler (${context}):`, data.error);
|
|
||||||
throw new Error(`API-Fehler: ${data.error} (${context})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Erfolgreiche Validierung
|
|
||||||
console.log(`✅ API-Response validiert (${context}):`, data);
|
|
||||||
return data;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
// Error-Logging mit Kontext
|
|
||||||
console.error(`❌ validateApiResponse fehlgeschlagen (${context}):`, error);
|
|
||||||
console.error(`❌ Response-Details (${context}):`, {
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
url: response.url,
|
|
||||||
headers: Object.fromEntries(response.headers.entries())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Re-throw mit erweiterten Informationen
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkSystemHealth() {
|
async checkSystemHealth() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiBaseUrl}/api/admin/system/status`);
|
const response = await fetch(`${this.apiBaseUrl}/api/admin/system/status`);
|
||||||
const data = await this.validateApiResponse(response, 'System-Health-Check');
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe Content-Type vor JSON parsing
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
if (!contentType || !contentType.includes('application/json')) {
|
||||||
|
throw new Error('Server returned non-JSON response');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.updateHealthDisplay(data);
|
this.updateHealthDisplay(data);
|
||||||
@ -990,7 +916,6 @@ class AdminDashboard {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler bei System-Health-Check:', error);
|
console.error('Fehler bei System-Health-Check:', error);
|
||||||
this.showNotification(`System-Health-Check fehlgeschlagen: ${error.message}`, 'error');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user