📝 "Refactor backend

This commit is contained in:
2025-05-31 23:44:20 +02:00
parent f5a39a450a
commit 831caec87a
11 changed files with 1828 additions and 22 deletions

View File

@ -210,6 +210,9 @@ class OptimizationManager {
*/
async performAutoOptimization() {
try {
// Ladeanimation anzeigen
this.showOptimizationLoading();
const response = await fetch('/api/optimization/auto-optimize', {
method: 'POST',
headers: {
@ -224,18 +227,249 @@ class OptimizationManager {
const data = await response.json();
// Ladeanimation ausblenden
this.hideOptimizationLoading();
if (data.success) {
this.showSuccessMessage(`Optimierung erfolgreich: ${data.optimized_jobs} Jobs optimiert`);
// Belohnendes animiertes Modal anzeigen
this.showRewardModal(data);
this.refreshCurrentView();
} else {
this.showErrorMessage(`Optimierung fehlgeschlagen: ${data.error}`);
}
} catch (error) {
this.hideOptimizationLoading();
console.error('Auto-Optimierung Fehler:', error);
this.showErrorMessage('Netzwerkfehler bei der Auto-Optimierung');
}
}
/**
* Belohnendes animiertes Modal für erfolgreiche Optimierung
*/
showRewardModal(data) {
// Existing Modal entfernen falls vorhanden
const existingModal = document.getElementById('optimization-reward-modal');
if (existingModal) {
existingModal.remove();
}
const modal = document.createElement('div');
modal.id = 'optimization-reward-modal';
modal.className = 'fixed inset-0 bg-black/70 backdrop-blur-md z-50 flex items-center justify-center p-4 animate-fade-in';
// Erfolgs-Emojis basierend auf Anzahl der optimierten Jobs
const optimizedCount = data.optimized_jobs || 0;
let celebration = '🎉';
let message = 'Optimierung erfolgreich!';
if (optimizedCount === 0) {
celebration = '✅';
message = 'System bereits optimal!';
} else if (optimizedCount <= 3) {
celebration = '🚀';
message = 'Kleine Verbesserungen durchgeführt!';
} else if (optimizedCount <= 10) {
celebration = '⚡';
message = 'Deutliche Optimierung erreicht!';
} else {
celebration = '💎';
message = 'Exzellente Optimierung!';
}
modal.innerHTML = `
<div class="relative bg-white dark:bg-slate-800 rounded-2xl shadow-2xl max-w-md w-full p-8 transform animate-bounce-in">
<!-- Konfetti Animation -->
<div class="absolute inset-0 pointer-events-none overflow-hidden rounded-2xl">
<div class="confetti-container">
${this.generateConfetti()}
</div>
</div>
<!-- Schließen Button -->
<button onclick="this.closest('#optimization-reward-modal').remove()"
class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors z-10">
<svg class="w-6 h-6" 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>
<!-- Haupt-Content -->
<div class="text-center relative z-10">
<!-- Animiertes Erfolgs-Icon -->
<div class="relative mb-6">
<div class="text-8xl animate-pulse-scale mx-auto">${celebration}</div>
<div class="absolute -top-2 -right-2 text-4xl animate-float">✨</div>
<div class="absolute -bottom-2 -left-2 text-3xl animate-float-delay">⭐</div>
</div>
<!-- Erfolgs-Nachricht -->
<h2 class="text-2xl font-bold text-slate-800 dark:text-white mb-2 animate-slide-up">
${message}
</h2>
<!-- Statistiken -->
<div class="bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20
rounded-xl p-6 mb-6 animate-slide-up-delay">
<div class="grid grid-cols-2 gap-4">
<div class="text-center">
<div class="text-3xl font-bold text-green-600 dark:text-green-400 animate-count-up">
${optimizedCount}
</div>
<div class="text-sm text-green-700 dark:text-green-300 font-medium">
Jobs optimiert
</div>
</div>
<div class="text-center">
<div class="text-lg font-semibold text-slate-700 dark:text-slate-300 capitalize">
${data.algorithm?.replace('_', ' ') || 'Standard'}
</div>
<div class="text-sm text-slate-600 dark:text-slate-400">
Algorithmus
</div>
</div>
</div>
</div>
<!-- Belohnungs-Badge -->
<div class="inline-flex items-center gap-2 bg-gradient-to-r from-blue-500 to-purple-600
text-white px-6 py-3 rounded-full text-sm font-semibold mb-6 animate-glow">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
</svg>
Effizienz-Boost erreicht!
</div>
<!-- Action Buttons -->
<div class="flex gap-3">
<button onclick="this.closest('#optimization-reward-modal').remove()"
class="flex-1 bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300
py-3 px-6 rounded-xl font-semibold transition-all hover:bg-slate-200
dark:hover:bg-slate-600 animate-slide-up-delay-2">
Schließen
</button>
<button onclick="optimizationManager.showOptimizationSettings();
this.closest('#optimization-reward-modal').remove();"
class="flex-1 bg-gradient-to-r from-blue-500 to-blue-600 text-white
py-3 px-6 rounded-xl font-semibold transition-all hover:from-blue-600
hover:to-blue-700 animate-slide-up-delay-3">
Einstellungen
</button>
</div>
</div>
</div>
`;
// Modal zum DOM hinzufügen
document.body.appendChild(modal);
// Sound-Effekt (optional)
this.playSuccessSound();
// Auto-Close nach 10 Sekunden
setTimeout(() => {
if (modal && modal.parentNode) {
modal.style.opacity = '0';
modal.style.transform = 'scale(0.95)';
setTimeout(() => modal.remove(), 300);
}
}, 10000);
}
/**
* Konfetti-Animation generieren
*/
generateConfetti() {
const colors = ['#FFD700', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'];
let confetti = '';
for (let i = 0; i < 30; i++) {
const color = colors[Math.floor(Math.random() * colors.length)];
const delay = Math.random() * 3;
const duration = 3 + Math.random() * 2;
const left = Math.random() * 100;
confetti += `
<div class="confetti-piece" style="
background-color: ${color};
left: ${left}%;
animation-delay: ${delay}s;
animation-duration: ${duration}s;
"></div>
`;
}
return confetti;
}
/**
* Ladeanimation für Optimierung anzeigen
*/
showOptimizationLoading() {
const loader = document.createElement('div');
loader.id = 'optimization-loader';
loader.className = 'fixed inset-0 bg-black/50 backdrop-blur-sm z-40 flex items-center justify-center';
loader.innerHTML = `
<div class="bg-white dark:bg-slate-800 rounded-2xl p-8 text-center max-w-sm mx-4 shadow-2xl">
<div class="relative mb-6">
<div class="w-16 h-16 mx-auto">
<svg class="animate-spin text-blue-500" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
<div class="absolute -top-1 -right-1 text-2xl animate-bounce">⚡</div>
</div>
<h3 class="text-xl font-semibold text-slate-800 dark:text-white mb-2">
Optimierung läuft...
</h3>
<p class="text-slate-600 dark:text-slate-400">
Jobs werden intelligent verteilt
</p>
</div>
`;
document.body.appendChild(loader);
}
/**
* Ladeanimation ausblenden
*/
hideOptimizationLoading() {
const loader = document.getElementById('optimization-loader');
if (loader) {
loader.style.opacity = '0';
setTimeout(() => loader.remove(), 200);
}
}
/**
* Erfolgs-Sound abspielen (optional)
*/
playSuccessSound() {
try {
// Erstelle einen kurzen, angenehmen Ton
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(523.25, audioContext.currentTime); // C5
oscillator.frequency.setValueAtTime(659.25, audioContext.currentTime + 0.1); // E5
oscillator.frequency.setValueAtTime(783.99, audioContext.currentTime + 0.2); // G5
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.5);
} catch (e) {
// Sound nicht verfügbar, ignorieren
console.log('Audio-API nicht verfügbar');
}
}
/**
* Batch-Operationen durchführen
*/