/**
* 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 = `
`;
// 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 = `
`;
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');
});