/** * Erweiterte Druckerkonflikt-Management-Engine - Frontend * MYP Platform * * Behandelt Konflikte zwischen Druckerreservierungen mit * intelligenten Lösungsvorschlägen und Benutzerführung. */ class ConflictManager { constructor() { this.lastConflictCheck = null; this.currentRecommendation = null; this.autoCheckEnabled = true; this.checkTimeout = null; this.debounceDelay = 500; // ms this.init(); } init() { this.createConflictModal(); this.createAvailabilityPanel(); this.createSmartRecommendationWidget(); this.attachEventListeners(); console.log('🔧 ConflictManager initialisiert'); } // =================== MODAL CREATION =================== createConflictModal() { const modalHTML = `
`; document.body.insertAdjacentHTML('beforeend', modalHTML); } createAvailabilityPanel() { const panelHTML = ` `; // Panel nach dem Kalender-Container einfügen const calendarContainer = document.querySelector('.container'); if (calendarContainer) { calendarContainer.insertAdjacentHTML('afterbegin', panelHTML); } } createSmartRecommendationWidget() { const widgetHTML = ` `; // Widget nach dem Kalender-Container einfügen const calendarContainer = document.querySelector('.container'); if (calendarContainer) { calendarContainer.insertAdjacentHTML('afterbegin', widgetHTML); } } // =================== EVENT LISTENERS =================== attachEventListeners() { // Konflikt-Modal Events document.getElementById('closeConflictModal')?.addEventListener('click', () => { this.hideConflictModal(); }); document.getElementById('applyAutoFix')?.addEventListener('click', () => { this.applyAutoFix(); }); document.getElementById('ignoreConflicts')?.addEventListener('click', () => { this.ignoreConflicts(); }); // Smart-Empfehlung Events document.getElementById('acceptRecommendation')?.addEventListener('click', () => { this.acceptRecommendation(); }); document.getElementById('dismissRecommendation')?.addEventListener('click', () => { this.dismissRecommendation(); }); // Formular-Validierung Events this.attachFormValidation(); // Verfügbarkeits-Button (falls vorhanden) document.getElementById('refreshAvailability')?.addEventListener('click', () => { this.refreshAvailability(); }); } attachFormValidation() { const formFields = ['eventStart', 'eventEnd', 'eventPrinter', 'eventPriority']; formFields.forEach(fieldId => { const field = document.getElementById(fieldId); if (field) { field.addEventListener('change', () => { this.scheduleValidation(); }); field.addEventListener('input', () => { this.scheduleValidation(); }); } }); } // =================== API CALLS =================== async checkConflicts(eventData) { try { const response = await fetch('/api/calendar/check-conflicts', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(eventData) }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const result = await response.json(); this.lastConflictCheck = result; return result; } catch (error) { console.error('❌ Fehler bei Konfliktprüfung:', error); this.showError('Konfliktprüfung fehlgeschlagen'); return null; } } async resolveConflictsAndCreate(eventData) { try { const response = await fetch('/api/calendar/resolve-conflicts', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({...eventData, auto_resolve: true}) }); if (!response.ok) { const errorData = await response.json(); if (response.status === 409) { this.showConflictModal(errorData); return null; } else { throw new Error(errorData.error || `HTTP ${response.status}`); } } return await response.json(); } catch (error) { console.error('❌ Fehler bei Konfliktlösung:', error); this.showError('Automatische Konfliktlösung fehlgeschlagen'); return null; } } async loadPrinterAvailability(startTime, endTime) { try { const params = new URLSearchParams({ start: startTime, end: endTime }); const response = await fetch(`/api/calendar/printer-availability?${params}`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); this.displayAvailability(data); return data; } catch (error) { console.error('❌ Fehler beim Laden der Verfügbarkeit:', error); return null; } } async getSmartRecommendation(eventData) { try { const response = await fetch('/api/calendar/smart-recommendation', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(eventData) }); if (!response.ok) return null; const recommendation = await response.json(); if (recommendation.success) { this.displayRecommendation(recommendation.recommendation); this.currentRecommendation = recommendation.recommendation; return recommendation.recommendation; } return null; } catch (error) { console.error('❌ Fehler bei Smart-Empfehlung:', error); return null; } } // =================== UI DISPLAY METHODS =================== showConflictModal(conflictData) { const modal = document.getElementById('conflictNotificationModal'); if (!modal) return; this.updateConflictSummary(conflictData); this.updateConflictDetails(conflictData); this.updateConflictRecommendations(conflictData); modal.classList.remove('hidden'); } hideConflictModal() { const modal = document.getElementById('conflictNotificationModal'); if (modal) { modal.classList.add('hidden'); } } updateConflictSummary(conflictData) { const title = document.getElementById('conflictTitle'); const description = document.getElementById('conflictDescription'); const icon = document.getElementById('conflictIcon'); const summary = document.getElementById('conflictSummary'); if (!title || !description || !icon || !summary) return; if (conflictData.severity_score >= 3) { icon.textContent = '🚨'; title.textContent = 'Kritische Konflikte'; summary.className = summary.className.replace(/amber/g, 'red'); } else { icon.textContent = '⚠️'; title.textContent = 'Konflikte erkannt'; } description.textContent = `${conflictData.conflict_count} Konflikt(e) gefunden. ${conflictData.can_proceed ? 'Automatische Lösung möglich.' : 'Manuelle Anpassung erforderlich.'}`; } updateConflictDetails(conflictData) { const details = document.getElementById('conflictDetails'); if (!details) return; details.innerHTML = ''; if (conflictData.conflicts) { conflictData.conflicts.forEach(conflict => { const conflictEl = this.createConflictElement(conflict); details.appendChild(conflictEl); }); } } updateConflictRecommendations(conflictData) { const recommendations = document.getElementById('smartRecommendations'); if (!recommendations) return; recommendations.innerHTML = ''; if (conflictData.recommendations) { conflictData.recommendations.forEach(rec => { const recEl = this.createRecommendationElement(rec); recommendations.appendChild(recEl); }); } // Auto-Fix Button aktivieren/deaktivieren const autoFixBtn = document.getElementById('applyAutoFix'); if (autoFixBtn) { autoFixBtn.disabled = !conflictData.can_proceed; } } createConflictElement(conflict) { const div = document.createElement('div'); div.className = 'p-3 border border-slate-200 dark:border-slate-600 rounded-lg'; const severityColors = { 'kritisch': 'red', 'hoch': 'orange', 'mittel': 'amber', 'niedrig': 'blue', 'information': 'slate' }; const color = severityColors[conflict.severity] || 'slate'; div.innerHTML = `${conflict.estimated_impact}
${conflict.conflicting_jobs?.length > 0 ? `${recommendation.message}
`; if (recommendation.suggestions) { content += '