/**
* MYP Platform - Globale Refresh-Funktionen
* Sammelt alle Refresh-Funktionen für verschiedene Seiten und Komponenten
*/
/**
* Utility-Funktionen für robustes DOM-Element-Handling
*/
/**
* Sicheres Aktualisieren von Element-Inhalten
* @param {string} elementId - Die ID des Elements
* @param {string|number} value - Der zu setzende Wert
* @param {Object} options - Optionale Parameter
* @returns {boolean} - true wenn erfolgreich, false wenn Element nicht gefunden
*/
function safeUpdateElement(elementId, value, options = {}) {
const {
fallbackValue = '-',
logWarning = true,
attribute = 'textContent',
transform = null
} = options;
const element = document.getElementById(elementId);
if (!element) {
if (logWarning) {
console.warn(`🔍 Element mit ID '${elementId}' nicht gefunden`);
}
return false;
}
try {
const finalValue = value !== undefined && value !== null ? value : fallbackValue;
const displayValue = transform ? transform(finalValue) : finalValue;
element[attribute] = displayValue;
return true;
} catch (error) {
console.error(`❌ Fehler beim Aktualisieren von Element '${elementId}':`, error);
return false;
}
}
/**
* Sicheres Aktualisieren mehrerer Elemente
* @param {Object} updates - Objekt mit elementId: value Paaren
* @param {Object} options - Optionale Parameter
*/
function safeBatchUpdate(updates, options = {}) {
const results = {};
Object.entries(updates).forEach(([elementId, value]) => {
results[elementId] = safeUpdateElement(elementId, value, options);
});
const successful = Object.values(results).filter(Boolean).length;
const total = Object.keys(updates).length;
console.log(`📊 Batch-Update: ${successful}/${total} Elemente erfolgreich aktualisiert`);
return results;
}
/**
* Prüfen ob Element existiert
* @param {string} elementId - Die ID des Elements
* @returns {boolean} - true wenn Element existiert
*/
function elementExists(elementId) {
return document.getElementById(elementId) !== null;
}
/**
* Dashboard-Refresh-Funktion
*/
window.refreshDashboard = async function() {
const refreshButton = document.getElementById('refreshDashboard');
if (refreshButton) {
// Button-State ändern
refreshButton.disabled = true;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.add('animate-spin');
}
}
try {
// Dashboard-Statistiken aktualisieren
const response = await fetch('/api/dashboard/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
}
});
const data = await response.json();
if (data.success) {
// Statistiken im DOM aktualisieren
updateDashboardStats(data.stats);
// Benachrichtigung anzeigen
showToast('✅ Dashboard erfolgreich aktualisiert', 'success');
// Seite neu laden für vollständige Aktualisierung
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
showToast('❌ Fehler beim Aktualisieren des Dashboards', 'error');
}
} catch (error) {
console.error('Dashboard-Refresh Fehler:', error);
showToast('❌ Netzwerkfehler beim Dashboard-Refresh', 'error');
} finally {
// Button-State zurücksetzen
if (refreshButton) {
refreshButton.disabled = false;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.remove('animate-spin');
}
}
}
};
/**
* Statistiken-Refresh-Funktion
*/
window.refreshStats = async function() {
const refreshButton = document.querySelector('button[onclick="refreshStats()"]');
if (refreshButton) {
refreshButton.disabled = true;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.add('animate-spin');
}
}
try {
// Basis-Statistiken laden
if (typeof loadBasicStats === 'function') {
await loadBasicStats();
} else {
// Fallback: API-Aufruf für Statistiken
const response = await fetch('/api/stats');
const data = await response.json();
if (response.ok) {
// Statistiken im DOM aktualisieren
updateStatsCounter('total-jobs-count', data.total_jobs);
updateStatsCounter('completed-jobs-count', data.completed_jobs);
updateStatsCounter('online-printers-count', data.online_printers);
updateStatsCounter('success-rate-percent', data.success_rate + '%');
updateStatsCounter('active-jobs-count', data.active_jobs);
updateStatsCounter('failed-jobs-count', data.failed_jobs);
updateStatsCounter('total-users-count', data.total_users);
} else {
throw new Error(data.error || 'Fehler beim Laden der Statistiken');
}
}
// Charts aktualisieren
if (window.refreshAllCharts) {
window.refreshAllCharts();
}
showToast('✅ Statistiken erfolgreich aktualisiert', 'success');
} catch (error) {
console.error('Stats-Refresh Fehler:', error);
showToast('❌ Fehler beim Aktualisieren der Statistiken', 'error');
} finally {
if (refreshButton) {
refreshButton.disabled = false;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.remove('animate-spin');
}
}
}
};
/**
* Jobs-Refresh-Funktion - VERBESSERT mit umfassenden Null-Checks
*/
window.refreshJobs = async function() {
const refreshButton = document.getElementById('refresh-button');
if (refreshButton) {
refreshButton.disabled = true;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.add('animate-spin');
}
}
try {
console.log('🔄 Starte Jobs-Refresh...');
// Mehrstufige Manager-Prüfung mit erweiterten Sicherheitschecks
let refreshSuccess = false;
if (typeof window.jobManager !== 'undefined' &&
window.jobManager &&
typeof window.jobManager.loadJobs === 'function') {
console.log('📝 Verwende window.jobManager.loadJobs()');
await window.jobManager.loadJobs();
refreshSuccess = true;
} else if (typeof jobManager !== 'undefined' &&
jobManager &&
typeof jobManager.loadJobs === 'function') {
console.log('📝 Verwende lokalen jobManager.loadJobs()');
await jobManager.loadJobs();
refreshSuccess = true;
} else {
// Fallback: Direkter API-Aufruf mit verbesserter Fehlerbehandlung
console.log('📝 JobManager nicht verfügbar - verwende direkten API-Aufruf');
const response = await fetch('/api/jobs', {
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken()
}
});
if (!response.ok) {
throw new Error(`API-Fehler: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log('📝 API-Response erhalten:', data);
// VERBESSERTE Datenvalidierung
if (!data || typeof data !== 'object') {
throw new Error('Ungültige API-Response: Keine Daten erhalten');
}
// Sichere Jobs-Extraktion
let jobs = [];
if (Array.isArray(data.jobs)) {
jobs = data.jobs;
} else if (Array.isArray(data)) {
jobs = data;
} else if (data.success && Array.isArray(data.data)) {
jobs = data.data;
} else {
console.warn('⚠️ Keine Jobs-Array in API-Response gefunden:', data);
jobs = [];
}
console.log(`📝 ${jobs.length} Jobs aus API extrahiert:`, jobs);
// Container-Updates mit Fallback-Rendering
const jobsContainers = [
'.jobs-container',
'#jobs-container',
'.job-grid',
'#jobs-list',
'#jobs-grid'
];
let containerFound = false;
for (const selector of jobsContainers) {
const container = document.querySelector(selector);
if (container) {
containerFound = true;
console.log(`📝 Container gefunden: ${selector}`);
if (jobs.length === 0) {
container.innerHTML = `
📭
Keine Jobs vorhanden
Es wurden noch keine Druckaufträge erstellt.
`;
} else {
// Sichere Job-Darstellung mit Null-Checks
const jobCards = jobs.map(job => {
if (!job || typeof job !== 'object') {
console.warn('⚠️ Ungültiges Job-Objekt übersprungen:', job);
return '';
}
return `
${job.filename || job.title || job.name || 'Unbekannter Job'}
ID: ${job.id || 'N/A'}
Status: ${job.status || 'Unbekannt'}
${job.printer_name ? `
Drucker: ${job.printer_name}
` : ''}
${job.created_at ? `
Erstellt: ${new Date(job.created_at).toLocaleDateString('de-DE')}
` : ''}
`;
}).filter(card => card !== '').join('');
container.innerHTML = jobCards || `
Keine gültigen Jobs zum Anzeigen
`;
}
break;
}
}
if (!containerFound) {
console.warn('⚠️ Kein Jobs-Container gefunden. Verfügbare Container:', jobsContainers);
}
refreshSuccess = true;
}
if (refreshSuccess) {
showToast('✅ Druckaufträge erfolgreich aktualisiert', 'success');
}
} catch (error) {
console.error('❌ Jobs-Refresh Fehler:', error);
// Intelligente Fehlermeldung basierend auf dem Fehlertyp
let errorMessage;
if (error.message.includes('undefined')) {
errorMessage = 'Jobs-Daten nicht verfügbar';
} else if (error.message.includes('fetch')) {
errorMessage = 'Netzwerkfehler beim Laden der Jobs';
} else if (error.message.includes('API')) {
errorMessage = 'Server-Fehler beim Laden der Jobs';
} else {
errorMessage = error.message || 'Unbekannter Fehler beim Laden der Jobs';
}
showToast(`❌ Fehler: ${errorMessage}`, 'error');
// Fallback-Container mit Fehleranzeige
const container = document.querySelector('.jobs-container, #jobs-container, .job-grid, #jobs-list');
if (container) {
container.innerHTML = `
⚠️
Fehler beim Laden
${errorMessage}
`;
}
} finally {
if (refreshButton) {
refreshButton.disabled = false;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.remove('animate-spin');
}
}
}
};
/**
* Calendar-Refresh-Funktion
*/
window.refreshCalendar = async function() {
const refreshButton = document.getElementById('refresh-button');
if (refreshButton) {
refreshButton.disabled = true;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.add('animate-spin');
}
}
try {
// FullCalendar neu laden falls verfügbar
if (typeof calendar !== 'undefined' && calendar.refetchEvents) {
calendar.refetchEvents();
showToast('✅ Kalender erfolgreich aktualisiert', 'success');
} else {
// Fallback: Seite neu laden
window.location.reload();
}
} catch (error) {
console.error('Calendar-Refresh Fehler:', error);
showToast('❌ Fehler beim Aktualisieren des Kalenders', 'error');
} finally {
if (refreshButton) {
refreshButton.disabled = false;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.remove('animate-spin');
}
}
}
};
/**
* Drucker-Refresh-Funktion
*/
window.refreshPrinters = async function() {
const refreshButton = document.getElementById('refresh-button');
if (refreshButton) {
refreshButton.disabled = true;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.add('animate-spin');
}
}
try {
// Drucker-Manager verwenden falls verfügbar
if (typeof printerManager !== 'undefined' && printerManager.loadPrinters) {
await printerManager.loadPrinters();
} else {
// Fallback: API-Aufruf für Drucker-Update
const response = await fetch('/api/printers/status/live', {
headers: {
'X-CSRFToken': getCSRFToken()
}
});
if (response.ok) {
// Seite neu laden für vollständige Aktualisierung
window.location.reload();
} else {
throw new Error('Drucker-Status konnte nicht abgerufen werden');
}
}
showToast('✅ Drucker erfolgreich aktualisiert', 'success');
} catch (error) {
console.error('Printer-Refresh Fehler:', error);
showToast('❌ Fehler beim Aktualisieren der Drucker', 'error');
} finally {
if (refreshButton) {
refreshButton.disabled = false;
const icon = refreshButton.querySelector('svg');
if (icon) {
icon.classList.remove('animate-spin');
}
}
}
};
/**
* Dashboard-Statistiken im DOM aktualisieren
*/
function updateDashboardStats(stats) {
// Aktive Jobs
const activeJobsEl = document.querySelector('[data-stat="active-jobs"]');
if (activeJobsEl) {
activeJobsEl.textContent = stats.active_jobs || 0;
}
// Verfügbare Drucker
const availablePrintersEl = document.querySelector('[data-stat="available-printers"]');
if (availablePrintersEl) {
availablePrintersEl.textContent = stats.available_printers || 0;
}
// Gesamte Jobs
const totalJobsEl = document.querySelector('[data-stat="total-jobs"]');
if (totalJobsEl) {
totalJobsEl.textContent = stats.total_jobs || 0;
}
// Erfolgsrate
const successRateEl = document.querySelector('[data-stat="success-rate"]');
if (successRateEl) {
successRateEl.textContent = (stats.success_rate || 0) + '%';
}
console.log('📊 Dashboard-Statistiken aktualisiert:', stats);
}
/**
* Statistiken-Counter im DOM aktualisieren
*/
function updateStatsCounter(elementId, value, animate = true) {
const element = document.getElementById(elementId);
if (!element) {
console.warn(`Element mit ID '${elementId}' nicht gefunden`);
return;
}
if (animate) {
// Animierte Zählung
const currentValue = parseInt(element.textContent.replace(/[^\d]/g, '')) || 0;
const targetValue = parseInt(value.toString().replace(/[^\d]/g, '')) || 0;
if (currentValue !== targetValue) {
animateCounter(element, currentValue, targetValue, value.toString());
}
} else {
element.textContent = value;
}
}
/**
* Animierte Counter-Funktion
*/
function animateCounter(element, start, end, finalText) {
const duration = 1000; // 1 Sekunde
const startTime = performance.now();
function updateCounter(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing-Funktion (ease-out)
const easeOut = 1 - Math.pow(1 - progress, 3);
const currentValue = Math.round(start + (end - start) * easeOut);
if (finalText.includes('%')) {
element.textContent = currentValue + '%';
} else {
element.textContent = currentValue;
}
if (progress < 1) {
requestAnimationFrame(updateCounter);
} else {
element.textContent = finalText;
}
}
requestAnimationFrame(updateCounter);
}
/**
* CSRF-Token abrufen
*/
function getCSRFToken() {
const token = document.querySelector('meta[name="csrf-token"]');
return token ? token.getAttribute('content') : '';
}
/**
* Toast-Benachrichtigung anzeigen
*/
function showToast(message, type = 'info') {
// Prüfen ob der OptimizationManager verfügbar ist und dessen Toast-Funktion verwenden
if (typeof optimizationManager !== 'undefined' && optimizationManager.showToast) {
optimizationManager.showToast(message, type);
return;
}
// Fallback: Einfache Console-Ausgabe
const emoji = {
success: '✅',
error: '❌',
warning: '⚠️',
info: 'ℹ️'
};
console.log(`${emoji[type] || 'ℹ️'} ${message}`);
// Versuche eine Alert-Benachrichtigung zu erstellen
try {
const toast = document.createElement('div');
toast.className = `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg transition-all duration-300 transform translate-x-full`;
const colors = {
success: 'bg-green-500 text-white',
error: 'bg-red-500 text-white',
warning: 'bg-yellow-500 text-black',
info: 'bg-blue-500 text-white'
};
toast.className += ` ${colors[type]}`;
toast.textContent = message;
document.body.appendChild(toast);
// Animation einblenden
setTimeout(() => {
toast.classList.remove('translate-x-full');
}, 100);
// Nach 3 Sekunden automatisch entfernen
setTimeout(() => {
toast.classList.add('translate-x-full');
setTimeout(() => {
toast.remove();
}, 300);
}, 3000);
} catch (error) {
console.warn('Toast-Erstellung fehlgeschlagen:', error);
}
}
/**
* Universelle Refresh-Funktion basierend auf aktueller Seite
*/
window.universalRefresh = function() {
const currentPath = window.location.pathname;
if (currentPath.includes('/dashboard')) {
refreshDashboard();
} else if (currentPath.includes('/jobs')) {
refreshJobs();
} else if (currentPath.includes('/calendar') || currentPath.includes('/schichtplan')) {
refreshCalendar();
} else if (currentPath.includes('/printers') || currentPath.includes('/drucker')) {
refreshPrinters();
} else {
// Fallback: Seite neu laden
window.location.reload();
}
};
/**
* Auto-Refresh-Funktionalität
*/
class AutoRefreshManager {
constructor() {
this.isEnabled = false;
this.interval = null;
this.intervalTime = 30000; // 30 Sekunden
}
start() {
if (this.isEnabled) return;
this.isEnabled = true;
this.interval = setInterval(() => {
// Nur refresh wenn Seite sichtbar ist
if (!document.hidden) {
universalRefresh();
}
}, this.intervalTime);
console.log('🔄 Auto-Refresh aktiviert (alle 30 Sekunden)');
}
stop() {
if (!this.isEnabled) return;
this.isEnabled = false;
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
console.log('⏸️ Auto-Refresh deaktiviert');
}
toggle() {
if (this.isEnabled) {
this.stop();
} else {
this.start();
}
}
}
// Globaler Auto-Refresh-Manager
window.autoRefreshManager = new AutoRefreshManager();
/**
* Keyboard-Shortcuts
*/
document.addEventListener('keydown', function(e) {
// F5 oder Ctrl+R abfangen und eigene Refresh-Funktion verwenden
if (e.key === 'F5' || (e.ctrlKey && e.key === 'r')) {
e.preventDefault();
universalRefresh();
}
// Ctrl+Shift+R für Auto-Refresh-Toggle
if (e.ctrlKey && e.shiftKey && e.key === 'R') {
e.preventDefault();
autoRefreshManager.toggle();
showToast(
autoRefreshManager.isEnabled ?
'🔄 Auto-Refresh aktiviert' :
'⏸️ Auto-Refresh deaktiviert',
'info'
);
}
});
/**
* Visibility API für Auto-Refresh bei Tab-Wechsel
*/
document.addEventListener('visibilitychange', function() {
if (!document.hidden && autoRefreshManager.isEnabled) {
// Verzögertes Refresh wenn Tab wieder sichtbar wird
setTimeout(universalRefresh, 1000);
}
});
console.log('🔄 Globale Refresh-Funktionen geladen');