feat: Implement frontend production deployment and enhance admin dashboard functionality

This commit is contained in:
2025-05-26 21:54:13 +02:00
parent c2ea6c34ea
commit 7aa70cf976
59 changed files with 9161 additions and 10894 deletions

View File

@@ -0,0 +1,391 @@
/**
* 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() {
// Versuche verschiedene Ports zu erkennen
const currentHost = window.location.hostname;
const currentPort = window.location.port;
const currentProtocol = window.location.protocol;
console.log('🔍 Aktuelle URL-Informationen:', {
host: currentHost,
port: currentPort,
protocol: currentProtocol,
fullUrl: window.location.href
});
// Wenn wir bereits auf dem richtigen Port sind, verwende relative URLs
if (currentPort === '5000' || !currentPort) {
console.log('✅ Verwende relative URLs (gleicher Port oder Standard)');
return '';
}
// Wenn wir auf 8443 sind, versuche 5000 (häufiger Fall)
if (currentPort === '8443') {
const fallbackUrl = `http://${currentHost}:5000`;
console.log('🔄 Fallback von HTTPS:8443 zu HTTP:5000:', fallbackUrl);
return fallbackUrl;
}
// Für andere Ports, verwende Standard-Backend-Port
const defaultPort = currentProtocol === 'https:' ? '8443' : '5000';
const fallbackUrl = `${currentProtocol}//${currentHost}:${defaultPort}`;
console.log('🔄 Standard-Fallback:', 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;

File diff suppressed because it is too large Load Diff