71 lines
19 KiB
JavaScript
71 lines
19 KiB
JavaScript
class OptimizationManager{constructor(){this.isAutoOptimizationEnabled=false;this.isBatchModeEnabled=false;this.selectedJobs=new Set();this.optimizationSettings={algorithm:'round_robin',considerDistance:true,minimizeChangeover:true,maxBatchSize:10,timeWindow:24};this.init();}
|
|
init(){this.setupEventListeners();this.loadSavedSettings();this.updateUI();}
|
|
setupEventListeners(){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();}});}
|
|
toggleAutoOptimization(){this.isAutoOptimizationEnabled=!this.isAutoOptimizationEnabled;const button=document.getElementById('auto-opt-toggle');if(button){this.updateAutoOptimizationButton(button);}
|
|
localStorage.setItem('myp-auto-optimization',this.isAutoOptimizationEnabled);this.showOptimizationNotification(this.isAutoOptimizationEnabled?'aktiviert':'deaktiviert','auto-optimization');if(this.isAutoOptimizationEnabled){this.performAutoOptimization();}
|
|
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.style.transform='scale(1.05)';setTimeout(()=>{button.style.transform='';},200);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';}}
|
|
toggleBatchMode(){this.isBatchModeEnabled=!this.isBatchModeEnabled;const button=document.getElementById('batch-toggle');if(button){this.updateBatchModeButton(button);}
|
|
this.toggleBatchSelection();localStorage.setItem('myp-batch-mode',this.isBatchModeEnabled);this.showOptimizationNotification(this.isBatchModeEnabled?'aktiviert':'deaktiviert','batch-mode');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.style.transform='scale(1.05)';setTimeout(()=>{button.style.transform='';},200);}else{button.classList.remove('btn-warning');button.classList.add('btn-secondary');span.textContent='Mehrfachauswahl';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){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';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();});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})`;}}
|
|
async performAutoOptimization(){try{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();this.hideOptimizationLoading();if(data.success){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');}}
|
|
showRewardModal(data){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';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>`;document.body.appendChild(modal);this.playSuccessSound();setTimeout(()=>{if(modal&&modal.parentNode){modal.style.opacity='0';modal.style.transform='scale(0.95)';setTimeout(()=>modal.remove(),300);}},20000);}
|
|
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+=`<div class="confetti-piece"style="
|
|
background-color: ${color};
|
|
left: ${left}%;
|
|
animation-delay: ${delay}s;
|
|
animation-duration: ${duration}s;
|
|
"></div>`;}
|
|
return confetti;}
|
|
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);}
|
|
hideOptimizationLoading(){const loader=document.getElementById('optimization-loader');if(loader){loader.style.opacity='0';setTimeout(()=>loader.remove(),200);}}
|
|
playSuccessSound(){try{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);oscillator.frequency.setValueAtTime(659.25,audioContext.currentTime+0.1);oscillator.frequency.setValueAtTime(783.99,audioContext.currentTime+0.2);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){console.log('Audio-API nicht verfügbar');}}
|
|
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');}}
|
|
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=`<div class="dashboard-card max-w-2xl w-full p-8 transform transition-all duration-300"><div class="flex justify-between items-center mb-6"><div><h3 class="text-xl font-bold text-slate-900 dark:text-white mb-2">Optimierungs-Einstellungen</h3><p class="text-slate-500 dark:text-slate-400">Konfigurieren Sie die automatische Optimierung für maximale Effizienz</p></div><button onclick="this.closest('#optimization-settings-modal').remove()"
|
|
class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors"><svg class="w-6 h-6 text-slate-500 dark:text-slate-400"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"></path></svg></button></div><div class="space-y-6"><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Optimierungs-Algorithmus</label><select id="optimization-algorithm"class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white"><option value="round_robin">Round Robin-Gleichmäßige Verteilung</option><option value="load_balance">Load Balancing-Auslastungsoptimierung</option><option value="priority_based">Prioritätsbasiert-Wichtige Jobs zuerst</option></select></div><div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label class="flex items-center"><input type="checkbox"id="consider-distance"class="mr-2"><span class="text-sm text-slate-700 dark:text-slate-300">Druckerentfernung berücksichtigen</span></label></div><div><label class="flex items-center"><input type="checkbox"id="minimize-changeover"class="mr-2"><span class="text-sm text-slate-700 dark:text-slate-300">Rüstzeiten minimieren</span></label></div></div><div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Max.Batch-Größe</label><input type="number"id="max-batch-size"min="1"max="50"
|
|
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white"></div><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Planungshorizont(Stunden)</label><input type="number"id="time-window"min="1"max="168"
|
|
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white"></div></div></div><div class="flex items-center justify-end gap-3 pt-6 border-t border-gray-200 dark:border-slate-600 mt-6"><button onclick="this.closest('#optimization-settings-modal').remove()"
|
|
class="btn-secondary">Abbrechen</button><button onclick="optimizationManager.saveOptimizationSettings()"
|
|
class="btn-primary">Einstellungen speichern</button></div></div>`;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');if(this.isAutoOptimizationEnabled){this.performAutoOptimization();}}
|
|
loadSavedSettings(){const savedAutoOpt=localStorage.getItem('myp-auto-optimization');if(savedAutoOpt!==null){this.isAutoOptimizationEnabled=savedAutoOpt==='true';}
|
|
const savedBatchMode=localStorage.getItem('myp-batch-mode');if(savedBatchMode!==null){this.isBatchModeEnabled=savedBatchMode==='true';}
|
|
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(){const autoOptButton=document.getElementById('auto-opt-toggle');if(autoOptButton){this.updateAutoOptimizationButton(autoOptButton);}
|
|
const batchButton=document.getElementById('batch-toggle');if(batchButton){this.updateBatchModeButton(batchButton);}
|
|
if(this.isBatchModeEnabled){this.toggleBatchSelection();}}
|
|
getCSRFToken(){const token=document.querySelector('meta[name="csrf-token"]');return token?token.getAttribute('content'):'';}
|
|
refreshCurrentView(){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){if(typeof showFlashMessage==='function'){showFlashMessage(message,'success');}else{console.log('Success:',message);}}
|
|
showErrorMessage(message){if(typeof showFlashMessage==='function'){showFlashMessage(message,'error');}else{console.error('Error:',message);}}
|
|
showWarningMessage(message){if(typeof showFlashMessage==='function'){showFlashMessage(message,'warning');}else{console.warn('Warning:',message);}}
|
|
showToast(message,type='info'){if(typeof showFlashMessage==='function'){showFlashMessage(message,type);}else{console.log(`${type.toUpperCase()}:${message}`);}}}
|
|
let optimizationManager;window.toggleAutoOptimization=function(){if(!optimizationManager){optimizationManager=new OptimizationManager();}
|
|
optimizationManager.toggleAutoOptimization();};window.toggleBatchMode=function(){if(!optimizationManager){optimizationManager=new OptimizationManager();}
|
|
optimizationManager.toggleBatchMode();};window.openBatchPlanningModal=function(){if(!optimizationManager){optimizationManager=new OptimizationManager();}
|
|
optimizationManager.showOptimizationSettings();};window.showOptimizationSettings=function(){if(!optimizationManager){optimizationManager=new OptimizationManager();}
|
|
optimizationManager.showOptimizationSettings();};document.addEventListener('DOMContentLoaded',function(){optimizationManager=new OptimizationManager();console.log('🎯 Optimierungs-Manager initialisiert');}); |