377 lines
13 KiB
JavaScript
377 lines
13 KiB
JavaScript
/**
|
||
* Mercedes-Benz MYP Admin Live Dashboard
|
||
* Echtzeit-Updates für das Admin Panel mit echten Daten
|
||
*/
|
||
|
||
class AdminLiveDashboard {
|
||
constructor() {
|
||
this.isLive = false;
|
||
this.updateInterval = null;
|
||
this.retryCount = 0;
|
||
this.maxRetries = 3;
|
||
|
||
// Dynamische API-Base-URL-Erkennung
|
||
this.apiBaseUrl = this.detectApiBaseUrl();
|
||
console.log('🔗 API Base URL erkannt:', this.apiBaseUrl);
|
||
|
||
this.init();
|
||
}
|
||
|
||
detectApiBaseUrl() {
|
||
const currentHost = window.location.hostname;
|
||
const currentProtocol = window.location.protocol;
|
||
const currentPort = window.location.port;
|
||
|
||
console.log('🔍 Live Dashboard API URL Detection:', { currentHost, currentProtocol, currentPort });
|
||
|
||
// Wenn wir bereits auf dem richtigen Port sind, verwende relative URLs
|
||
if (currentPort === '443' || !currentPort) {
|
||
console.log('✅ Verwende relative URLs (HTTPS Port 443)');
|
||
return '';
|
||
}
|
||
|
||
// Für alle anderen Fälle, verwende HTTPS auf Port 443
|
||
const fallbackUrl = `https://${currentHost}`;
|
||
console.log('🔄 Fallback zu HTTPS:443:', fallbackUrl);
|
||
|
||
return fallbackUrl;
|
||
}
|
||
|
||
init() {
|
||
console.log('🚀 Mercedes-Benz MYP Admin Live Dashboard gestartet');
|
||
|
||
// Live-Status anzeigen
|
||
this.updateLiveTime();
|
||
this.startLiveUpdates();
|
||
|
||
// Event Listeners
|
||
this.bindEvents();
|
||
|
||
// Initial Load
|
||
this.loadLiveStats();
|
||
}
|
||
|
||
bindEvents() {
|
||
// Quick Action Buttons
|
||
const systemStatusBtn = document.getElementById('system-status-btn');
|
||
const analyticsBtn = document.getElementById('analytics-btn');
|
||
const maintenanceBtn = document.getElementById('maintenance-btn');
|
||
|
||
if (systemStatusBtn) {
|
||
systemStatusBtn.addEventListener('click', () => this.showSystemStatus());
|
||
}
|
||
|
||
if (analyticsBtn) {
|
||
analyticsBtn.addEventListener('click', () => this.showAnalytics());
|
||
}
|
||
|
||
if (maintenanceBtn) {
|
||
maintenanceBtn.addEventListener('click', () => this.showMaintenance());
|
||
}
|
||
|
||
// Page Visibility API für optimierte Updates
|
||
document.addEventListener('visibilitychange', () => {
|
||
if (document.hidden) {
|
||
this.pauseLiveUpdates();
|
||
} else {
|
||
this.resumeLiveUpdates();
|
||
}
|
||
});
|
||
}
|
||
|
||
startLiveUpdates() {
|
||
this.isLive = true;
|
||
this.updateLiveIndicator(true);
|
||
|
||
// Live Stats alle 30 Sekunden aktualisieren
|
||
this.updateInterval = setInterval(() => {
|
||
this.loadLiveStats();
|
||
}, 30000);
|
||
|
||
// Zeit jede Sekunde aktualisieren
|
||
setInterval(() => {
|
||
this.updateLiveTime();
|
||
}, 1000);
|
||
}
|
||
|
||
pauseLiveUpdates() {
|
||
this.isLive = false;
|
||
this.updateLiveIndicator(false);
|
||
if (this.updateInterval) {
|
||
clearInterval(this.updateInterval);
|
||
}
|
||
}
|
||
|
||
resumeLiveUpdates() {
|
||
if (!this.isLive) {
|
||
this.startLiveUpdates();
|
||
this.loadLiveStats(); // Sofortiges Update beim Fortsetzen
|
||
}
|
||
}
|
||
|
||
updateLiveIndicator(isLive) {
|
||
const indicator = document.getElementById('live-indicator');
|
||
if (indicator) {
|
||
if (isLive) {
|
||
indicator.className = 'w-2 h-2 bg-green-400 rounded-full animate-pulse';
|
||
} else {
|
||
indicator.className = 'w-2 h-2 bg-gray-400 rounded-full';
|
||
}
|
||
}
|
||
}
|
||
|
||
updateLiveTime() {
|
||
const timeElement = document.getElementById('live-time');
|
||
if (timeElement) {
|
||
const now = new Date();
|
||
timeElement.textContent = now.toLocaleTimeString('de-DE');
|
||
}
|
||
}
|
||
|
||
async loadLiveStats() {
|
||
try {
|
||
const url = `${this.apiBaseUrl}/api/admin/stats/live`;
|
||
console.log('🔄 Lade Live-Statistiken von:', url);
|
||
|
||
const response = await fetch(url, {
|
||
method: 'GET',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRFToken': this.getCSRFToken()
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
this.updateStatsDisplay(data);
|
||
this.retryCount = 0; // Reset retry count on success
|
||
|
||
// Success notification (optional)
|
||
this.showQuietNotification('Live-Daten aktualisiert', 'success');
|
||
} else {
|
||
throw new Error(data.error || 'Unbekannter Fehler beim Laden der Live-Statistiken');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Fehler beim Laden der Live-Statistiken:', error);
|
||
|
||
this.retryCount++;
|
||
if (this.retryCount <= this.maxRetries) {
|
||
console.log(`Versuche erneut... (${this.retryCount}/${this.maxRetries})`);
|
||
setTimeout(() => this.loadLiveStats(), 5000); // Retry nach 5 Sekunden
|
||
} else {
|
||
this.handleConnectionError();
|
||
}
|
||
}
|
||
}
|
||
|
||
updateStatsDisplay(data) {
|
||
// Benutzer Stats
|
||
this.updateCounter('live-users-count', data.total_users);
|
||
this.updateProgress('users-progress', Math.min((data.total_users / 20) * 100, 100)); // Max 20 users = 100%
|
||
|
||
// Drucker Stats
|
||
this.updateCounter('live-printers-count', data.total_printers);
|
||
this.updateElement('live-printers-online', `${data.online_printers} online`);
|
||
if (data.total_printers > 0) {
|
||
this.updateProgress('printers-progress', (data.online_printers / data.total_printers) * 100);
|
||
}
|
||
|
||
// Jobs Stats
|
||
this.updateCounter('live-jobs-active', data.active_jobs);
|
||
this.updateElement('live-jobs-queued', `${data.queued_jobs} in Warteschlange`);
|
||
this.updateProgress('jobs-progress', Math.min(data.active_jobs * 20, 100)); // Max 5 jobs = 100%
|
||
|
||
// Erfolgsrate Stats
|
||
this.updateCounter('live-success-rate', `${data.success_rate}%`);
|
||
this.updateProgress('success-progress', data.success_rate);
|
||
|
||
// Trend Analysis
|
||
this.updateSuccessTrend(data.success_rate);
|
||
|
||
console.log('📊 Live-Statistiken aktualisiert:', data);
|
||
}
|
||
|
||
updateCounter(elementId, newValue) {
|
||
const element = document.getElementById(elementId);
|
||
if (element) {
|
||
const currentValue = parseInt(element.textContent) || 0;
|
||
if (currentValue !== newValue) {
|
||
this.animateCounter(element, currentValue, newValue);
|
||
}
|
||
}
|
||
}
|
||
|
||
animateCounter(element, from, to) {
|
||
const duration = 1000; // 1 Sekunde
|
||
const increment = (to - from) / (duration / 16); // 60 FPS
|
||
let current = from;
|
||
|
||
const timer = setInterval(() => {
|
||
current += increment;
|
||
if ((increment > 0 && current >= to) || (increment < 0 && current <= to)) {
|
||
current = to;
|
||
clearInterval(timer);
|
||
}
|
||
element.textContent = Math.round(current);
|
||
}, 16);
|
||
}
|
||
|
||
updateElement(elementId, newValue) {
|
||
const element = document.getElementById(elementId);
|
||
if (element && element.textContent !== newValue) {
|
||
element.textContent = newValue;
|
||
}
|
||
}
|
||
|
||
updateProgress(elementId, percentage) {
|
||
const element = document.getElementById(elementId);
|
||
if (element) {
|
||
element.style.width = `${Math.max(0, Math.min(100, percentage))}%`;
|
||
}
|
||
}
|
||
|
||
updateSuccessTrend(successRate) {
|
||
const trendElement = document.getElementById('success-trend');
|
||
if (trendElement) {
|
||
let trendText = 'Stabil';
|
||
let trendClass = 'text-green-500';
|
||
let trendIcon = 'M5 10l7-7m0 0l7 7m-7-7v18'; // Up arrow
|
||
|
||
if (successRate >= 95) {
|
||
trendText = 'Excellent';
|
||
trendClass = 'text-green-600';
|
||
} else if (successRate >= 80) {
|
||
trendText = 'Gut';
|
||
trendClass = 'text-green-500';
|
||
} else if (successRate >= 60) {
|
||
trendText = 'Mittel';
|
||
trendClass = 'text-yellow-500';
|
||
trendIcon = 'M5 12h14'; // Horizontal line
|
||
} else {
|
||
trendText = 'Niedrig';
|
||
trendClass = 'text-red-500';
|
||
trendIcon = 'M19 14l-7 7m0 0l-7-7m7 7V3'; // Down arrow
|
||
}
|
||
|
||
trendElement.className = `text-sm ${trendClass}`;
|
||
trendElement.innerHTML = `
|
||
<span class="inline-flex items-center">
|
||
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${trendIcon}"/>
|
||
</svg>
|
||
${trendText}
|
||
</span>
|
||
`;
|
||
}
|
||
}
|
||
|
||
showSystemStatus() {
|
||
// System Status Modal oder Navigation
|
||
console.log('🔧 System Status angezeigt');
|
||
this.showNotification('System Status wird geladen...', 'info');
|
||
|
||
// Hier könnten weitere System-Details geladen werden
|
||
const url = `${this.apiBaseUrl}/api/admin/system/status`;
|
||
fetch(url)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
// System Status anzeigen
|
||
console.log('System Status:', data);
|
||
})
|
||
.catch(error => {
|
||
console.error('Fehler beim Laden des System Status:', error);
|
||
});
|
||
}
|
||
|
||
showAnalytics() {
|
||
console.log('📈 Live Analytics angezeigt');
|
||
this.showNotification('Analytics werden geladen...', 'info');
|
||
|
||
// Analytics Tab aktivieren oder Modal öffnen
|
||
const analyticsTab = document.querySelector('a[href*="tab=system"]');
|
||
if (analyticsTab) {
|
||
analyticsTab.click();
|
||
}
|
||
}
|
||
|
||
showMaintenance() {
|
||
console.log('🛠️ Wartung angezeigt');
|
||
this.showNotification('Wartungsoptionen werden geladen...', 'info');
|
||
|
||
// Wartungs-Tab aktivieren oder Modal öffnen
|
||
const systemTab = document.querySelector('a[href*="tab=system"]');
|
||
if (systemTab) {
|
||
systemTab.click();
|
||
}
|
||
}
|
||
|
||
handleConnectionError() {
|
||
console.error('🔴 Verbindung zu Live-Updates verloren');
|
||
this.updateLiveIndicator(false);
|
||
this.showNotification('Verbindung zu Live-Updates verloren. Versuche erneut...', 'error');
|
||
|
||
// Auto-Recovery nach 30 Sekunden
|
||
setTimeout(() => {
|
||
this.retryCount = 0;
|
||
this.loadLiveStats();
|
||
}, 30000);
|
||
}
|
||
|
||
showNotification(message, type = 'info') {
|
||
// Erstelle oder aktualisiere Notification
|
||
let notification = document.getElementById('live-notification');
|
||
if (!notification) {
|
||
notification = document.createElement('div');
|
||
notification.id = 'live-notification';
|
||
notification.className = 'fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm';
|
||
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 ${colors[type]} transform transition-all duration-300 translate-x-0`;
|
||
notification.textContent = message;
|
||
|
||
// Auto-Hide nach 3 Sekunden
|
||
setTimeout(() => {
|
||
if (notification) {
|
||
notification.style.transform = 'translateX(100%)';
|
||
setTimeout(() => {
|
||
if (notification && notification.parentNode) {
|
||
notification.parentNode.removeChild(notification);
|
||
}
|
||
}, 300);
|
||
}
|
||
}, 3000);
|
||
}
|
||
|
||
showQuietNotification(message, type) {
|
||
// Nur in der Konsole loggen für nicht-störende Updates
|
||
const emoji = type === 'success' ? '✅' : type === 'error' ? '❌' : 'ℹ️';
|
||
console.log(`${emoji} ${message}`);
|
||
}
|
||
|
||
getCSRFToken() {
|
||
const meta = document.querySelector('meta[name="csrf-token"]');
|
||
return meta ? meta.getAttribute('content') : '';
|
||
}
|
||
}
|
||
|
||
// Initialize when DOM is ready
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
new AdminLiveDashboard();
|
||
});
|
||
|
||
// Export for global access
|
||
window.AdminLiveDashboard = AdminLiveDashboard;
|