/** * MYP Platform - Erweiterte Optimierungs- und Batch-Funktionen * Implementiert Auto-Optimierung und Batch-Planung für 3D-Druckaufträge */ class OptimizationManager { constructor() { this.isAutoOptimizationEnabled = false; this.isBatchModeEnabled = false; this.selectedJobs = new Set(); this.optimizationSettings = { algorithm: 'round_robin', // 'round_robin', 'load_balance', 'priority_based' considerDistance: true, minimizeChangeover: true, maxBatchSize: 10, timeWindow: 24 // Stunden }; this.init(); } init() { this.setupEventListeners(); this.loadSavedSettings(); this.updateUI(); } setupEventListeners() { // Keyboard shortcuts document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.altKey && e.key === 'O') { this.toggleAutoOptimization(); e.preventDefault(); } if (e.ctrlKey && e.altKey && e.key === 'B') { this.toggleBatchMode(); e.preventDefault(); } }); } /** * Auto-Optimierung ein-/ausschalten * Diese Funktion optimiert automatisch die Druckreihenfolge basierend auf verschiedenen Faktoren */ toggleAutoOptimization() { this.isAutoOptimizationEnabled = !this.isAutoOptimizationEnabled; const button = document.getElementById('auto-opt-toggle'); if (button) { this.updateAutoOptimizationButton(button); } // Settings speichern localStorage.setItem('myp-auto-optimization', this.isAutoOptimizationEnabled); // Notification anzeigen this.showOptimizationNotification( this.isAutoOptimizationEnabled ? 'aktiviert' : 'deaktiviert', 'auto-optimization' ); // Wenn aktiviert, sofortige Optimierung durchführen if (this.isAutoOptimizationEnabled) { this.performAutoOptimization(); } // UI aktualisieren this.updateUI(); } updateAutoOptimizationButton(button) { const span = button.querySelector('span'); const icon = button.querySelector('svg'); if (this.isAutoOptimizationEnabled) { button.classList.remove('btn-secondary'); button.classList.add('btn-primary'); span.textContent = 'Auto-Optimierung AN'; // Button-Animation button.style.transform = 'scale(1.05)'; setTimeout(() => { button.style.transform = ''; }, 200); // Icon-Animation icon.style.animation = 'spin 1s ease-in-out'; setTimeout(() => { icon.style.animation = ''; }, 1000); } else { button.classList.remove('btn-primary'); button.classList.add('btn-secondary'); span.textContent = 'Auto-Optimierung'; } } /** * Batch-Modus ein-/ausschalten * Ermöglicht die Auswahl mehrerer Jobs für Batch-Operationen */ toggleBatchMode() { this.isBatchModeEnabled = !this.isBatchModeEnabled; const button = document.getElementById('batch-toggle'); if (button) { this.updateBatchModeButton(button); } // Batch-Funktionalität aktivieren/deaktivieren this.toggleBatchSelection(); // Settings speichern localStorage.setItem('myp-batch-mode', this.isBatchModeEnabled); // Notification anzeigen this.showOptimizationNotification( this.isBatchModeEnabled ? 'aktiviert' : 'deaktiviert', 'batch-mode' ); // UI aktualisieren this.updateUI(); } updateBatchModeButton(button) { const span = button.querySelector('span'); if (this.isBatchModeEnabled) { button.classList.remove('btn-secondary'); button.classList.add('btn-warning'); span.textContent = `Batch-Modus (${this.selectedJobs.size})`; // Button-Animation button.style.transform = 'scale(1.05)'; setTimeout(() => { button.style.transform = ''; }, 200); } else { button.classList.remove('btn-warning'); button.classList.add('btn-secondary'); span.textContent = 'Mehrfachauswahl'; // Auswahl zurücksetzen this.selectedJobs.clear(); } } toggleBatchSelection() { const jobCards = document.querySelectorAll('.job-card, [data-job-id]'); jobCards.forEach(card => { if (this.isBatchModeEnabled) { this.enableBatchSelection(card); } else { this.disableBatchSelection(card); } }); } enableBatchSelection(card) { // Checkbox hinzufügen let checkbox = card.querySelector('.batch-checkbox'); if (!checkbox) { checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'batch-checkbox absolute top-3 left-3 w-5 h-5 rounded border-2 border-gray-300 text-blue-600 focus:ring-blue-500'; checkbox.style.zIndex = '10'; // Event Listener für Checkbox checkbox.addEventListener('change', (e) => { const jobId = card.dataset.jobId; if (e.target.checked) { this.selectedJobs.add(jobId); card.classList.add('selected-for-batch'); } else { this.selectedJobs.delete(jobId); card.classList.remove('selected-for-batch'); } this.updateBatchCounter(); }); // Checkbox in die Karte einfügen card.style.position = 'relative'; card.appendChild(checkbox); } checkbox.style.display = 'block'; card.classList.add('batch-selectable'); } disableBatchSelection(card) { const checkbox = card.querySelector('.batch-checkbox'); if (checkbox) { checkbox.style.display = 'none'; } card.classList.remove('batch-selectable', 'selected-for-batch'); } updateBatchCounter() { const button = document.getElementById('batch-toggle'); if (button && this.isBatchModeEnabled) { const span = button.querySelector('span'); span.textContent = `Batch-Modus (${this.selectedJobs.size})`; } } /** * Automatische Optimierung durchführen */ async performAutoOptimization() { try { // Ladeanimation anzeigen this.showOptimizationLoading(); const response = await fetch('/api/optimization/auto-optimize', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() }, body: JSON.stringify({ settings: this.optimizationSettings, enabled: this.isAutoOptimizationEnabled }) }); const data = await response.json(); // Ladeanimation ausblenden this.hideOptimizationLoading(); if (data.success) { // 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 = `
${this.generateConfetti()}
${celebration}

${message}

${optimizedCount}
Jobs optimiert
${data.algorithm?.replace('_', ' ') || 'Standard'}
Algorithmus
Effizienz-Boost erreicht!
`; // Modal zum DOM hinzufügen document.body.appendChild(modal); // Sound-Effekt (optional) this.playSuccessSound(); // Auto-Close nach 20 Sekunden (verlängert für bessere Animation-Wirkung) setTimeout(() => { if (modal && modal.parentNode) { modal.style.opacity = '0'; modal.style.transform = 'scale(0.95)'; setTimeout(() => modal.remove(), 300); } }, 20000); } /** * Konfetti-Animation generieren */ generateConfetti() { const colors = ['#FFD700', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']; let confetti = ''; for (let i = 0; i < 50; i++) { const color = colors[Math.floor(Math.random() * colors.length)]; const delay = Math.random() * 5; const duration = 4 + Math.random() * 3; const left = Math.random() * 100; confetti += `
`; } 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 = `

Optimierung läuft...

Jobs werden intelligent verteilt

`; 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 */ async performBatchOperation(operation) { if (this.selectedJobs.size === 0) { this.showWarningMessage('Keine Jobs für Batch-Operation ausgewählt'); return; } const jobIds = Array.from(this.selectedJobs); try { const response = await fetch('/api/jobs/batch-operation', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCSRFToken() }, body: JSON.stringify({ job_ids: jobIds, operation: operation }) }); const data = await response.json(); if (data.success) { this.showSuccessMessage(`Batch-Operation "${operation}" erfolgreich auf ${jobIds.length} Jobs angewendet`); this.selectedJobs.clear(); this.updateBatchCounter(); this.refreshCurrentView(); } else { this.showErrorMessage(`Batch-Operation fehlgeschlagen: ${data.error}`); } } catch (error) { console.error('Batch-Operation Fehler:', error); this.showErrorMessage('Netzwerkfehler bei der Batch-Operation'); } } /** * Optimierungs-Einstellungen konfigurieren */ showOptimizationSettings() { this.createOptimizationModal(); } createOptimizationModal() { const modal = document.createElement('div'); modal.id = 'optimization-settings-modal'; modal.className = 'fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4'; modal.innerHTML = `

Optimierungs-Einstellungen

Konfigurieren Sie die automatische Optimierung für maximale Effizienz

`; document.body.appendChild(modal); this.loadOptimizationSettingsInModal(); } loadOptimizationSettingsInModal() { document.getElementById('optimization-algorithm').value = this.optimizationSettings.algorithm; document.getElementById('consider-distance').checked = this.optimizationSettings.considerDistance; document.getElementById('minimize-changeover').checked = this.optimizationSettings.minimizeChangeover; document.getElementById('max-batch-size').value = this.optimizationSettings.maxBatchSize; document.getElementById('time-window').value = this.optimizationSettings.timeWindow; } saveOptimizationSettings() { this.optimizationSettings.algorithm = document.getElementById('optimization-algorithm').value; this.optimizationSettings.considerDistance = document.getElementById('consider-distance').checked; this.optimizationSettings.minimizeChangeover = document.getElementById('minimize-changeover').checked; this.optimizationSettings.maxBatchSize = parseInt(document.getElementById('max-batch-size').value); this.optimizationSettings.timeWindow = parseInt(document.getElementById('time-window').value); localStorage.setItem('myp-optimization-settings', JSON.stringify(this.optimizationSettings)); document.getElementById('optimization-settings-modal').remove(); this.showSuccessMessage('Optimierungs-Einstellungen gespeichert'); // Wenn Auto-Optimierung aktiv ist, neue Optimierung durchführen if (this.isAutoOptimizationEnabled) { this.performAutoOptimization(); } } loadSavedSettings() { // Auto-Optimierung Status laden const savedAutoOpt = localStorage.getItem('myp-auto-optimization'); if (savedAutoOpt !== null) { this.isAutoOptimizationEnabled = savedAutoOpt === 'true'; } // Batch-Modus Status laden const savedBatchMode = localStorage.getItem('myp-batch-mode'); if (savedBatchMode !== null) { this.isBatchModeEnabled = savedBatchMode === 'true'; } // Optimierungs-Einstellungen laden const savedSettings = localStorage.getItem('myp-optimization-settings'); if (savedSettings) { try { this.optimizationSettings = { ...this.optimizationSettings, ...JSON.parse(savedSettings) }; } catch (error) { console.error('Fehler beim Laden der Optimierungs-Einstellungen:', error); } } } updateUI() { // Buttons aktualisieren const autoOptButton = document.getElementById('auto-opt-toggle'); if (autoOptButton) { this.updateAutoOptimizationButton(autoOptButton); } const batchButton = document.getElementById('batch-toggle'); if (batchButton) { this.updateBatchModeButton(batchButton); } // Batch-Auswahl aktualisieren if (this.isBatchModeEnabled) { this.toggleBatchSelection(); } } // Utility-Funktionen getCSRFToken() { const token = document.querySelector('meta[name="csrf-token"]'); return token ? token.getAttribute('content') : ''; } refreshCurrentView() { // Je nach aktueller Seite entsprechende Refresh-Funktion aufrufen if (typeof refreshJobs === 'function') { refreshJobs(); } else if (typeof refreshCalendar === 'function') { refreshCalendar(); } else if (typeof refreshDashboard === 'function') { refreshDashboard(); } } showOptimizationNotification(status, type) { const messages = { 'auto-optimization': { 'aktiviert': '🚀 Auto-Optimierung aktiviert - Jobs werden automatisch optimiert', 'deaktiviert': '⏸️ Auto-Optimierung deaktiviert' }, 'batch-mode': { 'aktiviert': '📦 Batch-Modus aktiviert - Wählen Sie Jobs für Batch-Operationen aus', 'deaktiviert': '✅ Batch-Modus deaktiviert' } }; const message = messages[type]?.[status] || `${type} ${status}`; this.showSuccessMessage(message); } showSuccessMessage(message) { // Verwende das globale Glassmorphism-System anstelle eines einfachen Toasts if (typeof showFlashMessage === 'function') { showFlashMessage(message, 'success'); } else { console.log('Success:', message); } } showErrorMessage(message) { // Verwende das globale Glassmorphism-System anstelle eines einfachen Toasts if (typeof showFlashMessage === 'function') { showFlashMessage(message, 'error'); } else { console.error('Error:', message); } } showWarningMessage(message) { // Verwende das globale Glassmorphism-System anstelle eines einfachen Toasts if (typeof showFlashMessage === 'function') { showFlashMessage(message, 'warning'); } else { console.warn('Warning:', message); } } showToast(message, type = 'info') { // Verwende das globale Glassmorphism-System für alle Toast-Nachrichten if (typeof showFlashMessage === 'function') { showFlashMessage(message, type); } else { // Fallback falls Glassmorphism-System nicht verfügbar console.log(`${type.toUpperCase()}: ${message}`); } } } // Globale Funktionen für Template-Integration let optimizationManager; // Auto-Optimierung umschalten window.toggleAutoOptimization = function() { if (!optimizationManager) { optimizationManager = new OptimizationManager(); } optimizationManager.toggleAutoOptimization(); }; // Batch-Modus umschalten window.toggleBatchMode = function() { if (!optimizationManager) { optimizationManager = new OptimizationManager(); } optimizationManager.toggleBatchMode(); }; // Batch-Planung Modal öffnen window.openBatchPlanningModal = function() { if (!optimizationManager) { optimizationManager = new OptimizationManager(); } optimizationManager.showOptimizationSettings(); }; // Optimierungs-Einstellungen anzeigen window.showOptimizationSettings = function() { if (!optimizationManager) { optimizationManager = new OptimizationManager(); } optimizationManager.showOptimizationSettings(); }; // Initialisierung beim Laden der Seite document.addEventListener('DOMContentLoaded', function() { optimizationManager = new OptimizationManager(); console.log('🎯 Optimierungs-Manager initialisiert'); });