"Refactor template files for better code organization (feat)"
This commit is contained in:
@@ -319,7 +319,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- User Menu Dropdown -->
|
<!-- User Menu Dropdown -->
|
||||||
<div id="user-menu-dropdown" class="absolute right-0 mt-2 w-64 bg-white dark:bg-slate-800 rounded-lg shadow-lg border border-slate-200 dark:border-slate-600 z-50 hidden origin-top-right">
|
<div id="user-dropdown" class="absolute right-0 mt-2 w-64 bg-white dark:bg-slate-800 rounded-lg shadow-lg border border-slate-200 dark:border-slate-600 z-50 hidden origin-top-right">
|
||||||
<!-- User Info Header -->
|
<!-- User Info Header -->
|
||||||
<div class="px-4 py-3 border-b border-slate-200 dark:border-slate-600">
|
<div class="px-4 py-3 border-b border-slate-200 dark:border-slate-600">
|
||||||
<div class="flex items-center space-x-3">
|
<div class="flex items-center space-x-3">
|
||||||
@@ -581,6 +581,62 @@
|
|||||||
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Additional JavaScript Functions -->
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* Logout-Handler für sicheres Abmelden
|
||||||
|
*/
|
||||||
|
function handleLogout() {
|
||||||
|
// Bestätigung abfragen
|
||||||
|
if (confirm('Möchten Sie sich wirklich abmelden?')) {
|
||||||
|
// Loading-Animation anzeigen
|
||||||
|
document.body.style.opacity = '0.7';
|
||||||
|
document.body.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
// CSRF-Token aus Meta-Tag holen
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]');
|
||||||
|
|
||||||
|
// Logout-Formular erstellen und absenden
|
||||||
|
const form = document.createElement('form');
|
||||||
|
form.method = 'POST';
|
||||||
|
form.action = '{{ url_for("logout") }}';
|
||||||
|
form.style.display = 'none';
|
||||||
|
|
||||||
|
// CSRF-Token hinzufügen falls verfügbar
|
||||||
|
if (csrfToken) {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'hidden';
|
||||||
|
input.name = 'csrf_token';
|
||||||
|
input.value = csrfToken.getAttribute('content');
|
||||||
|
form.appendChild(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formular absenden
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisierung aller UI-Komponenten nach DOM-Load
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Dark Mode Manager initialisieren
|
||||||
|
if (typeof MYP !== 'undefined' && MYP.UI) {
|
||||||
|
window.darkModeManager = new MYP.UI.DarkModeManager();
|
||||||
|
window.mobileMenuManager = new MYP.UI.MobileMenuManager();
|
||||||
|
window.userDropdownManager = new MYP.UI.UserDropdownManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MYP App für Offline-Funktionalität initialisieren
|
||||||
|
if (typeof MYPApp !== 'undefined') {
|
||||||
|
window.mypApp = new MYPApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🚀 MYP Platform UI erfolgreich initialisiert');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -476,19 +476,453 @@
|
|||||||
|
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
class DashboardManager {
|
||||||
// Refresh Button
|
constructor() {
|
||||||
const refreshBtn = document.getElementById('refreshDashboard');
|
this.updateInterval = 30000; // 30 Sekunden
|
||||||
if (refreshBtn) {
|
this.autoUpdateTimer = null;
|
||||||
refreshBtn.addEventListener('click', function() {
|
this.isUpdating = false;
|
||||||
window.location.reload();
|
this.wsConnection = null;
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.setupAutoUpdate();
|
||||||
|
this.setupWebSocket();
|
||||||
|
this.animateCounters();
|
||||||
|
console.log('🚀 Dashboard Manager initialisiert');
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
// Refresh Button
|
||||||
|
const refreshBtn = document.getElementById('refreshDashboard');
|
||||||
|
if (refreshBtn) {
|
||||||
|
refreshBtn.addEventListener('click', () => {
|
||||||
|
this.refreshDashboard();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job-Zeilen klickbar machen für Details
|
||||||
|
this.setupJobRowClicks();
|
||||||
|
|
||||||
|
// Drucker-Karten klickbar machen
|
||||||
|
this.setupPrinterClicks();
|
||||||
|
|
||||||
|
// Dark Mode Updates
|
||||||
|
window.addEventListener('darkModeChanged', (e) => {
|
||||||
|
this.updateThemeElements(e.detail.isDark);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Visibility Change Detection für intelligente Updates
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
this.pauseAutoUpdate();
|
||||||
|
} else {
|
||||||
|
this.resumeAutoUpdate();
|
||||||
|
this.refreshDashboard(); // Einmalige Aktualisierung bei Rückkehr
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dashboard Theme Switch Updates
|
setupJobRowClicks() {
|
||||||
window.addEventListener('darkModeChanged', function(e) {
|
const jobRows = document.querySelectorAll('tbody tr[data-job-id]');
|
||||||
// Update any theme-specific elements if needed
|
jobRows.forEach(row => {
|
||||||
console.log('Theme changed to:', e.detail.isDark ? 'dark' : 'light');
|
row.style.cursor = 'pointer';
|
||||||
|
row.addEventListener('click', () => {
|
||||||
|
const jobId = row.dataset.jobId;
|
||||||
|
if (jobId) {
|
||||||
|
window.location.href = `/jobs/${jobId}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hover-Effekt verstärken
|
||||||
|
row.addEventListener('mouseenter', () => {
|
||||||
|
row.style.transform = 'scale(1.01)';
|
||||||
|
row.style.transition = 'transform 0.2s ease';
|
||||||
|
});
|
||||||
|
|
||||||
|
row.addEventListener('mouseleave', () => {
|
||||||
|
row.style.transform = 'scale(1)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupPrinterClicks() {
|
||||||
|
const printerCards = document.querySelectorAll('[data-printer-id]');
|
||||||
|
printerCards.forEach(card => {
|
||||||
|
card.style.cursor = 'pointer';
|
||||||
|
card.addEventListener('click', () => {
|
||||||
|
const printerId = card.dataset.printerId;
|
||||||
|
if (printerId) {
|
||||||
|
window.location.href = `/printers/${printerId}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupAutoUpdate() {
|
||||||
|
this.autoUpdateTimer = setInterval(() => {
|
||||||
|
if (!document.hidden && !this.isUpdating) {
|
||||||
|
this.updateDashboardData();
|
||||||
|
}
|
||||||
|
}, this.updateInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseAutoUpdate() {
|
||||||
|
if (this.autoUpdateTimer) {
|
||||||
|
clearInterval(this.autoUpdateTimer);
|
||||||
|
this.autoUpdateTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resumeAutoUpdate() {
|
||||||
|
if (!this.autoUpdateTimer) {
|
||||||
|
this.setupAutoUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupWebSocket() {
|
||||||
|
// WebSocket für Real-time Updates
|
||||||
|
try {
|
||||||
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
const wsUrl = `${protocol}//${window.location.host}/ws/dashboard`;
|
||||||
|
|
||||||
|
this.wsConnection = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
this.wsConnection.onopen = () => {
|
||||||
|
console.log('📡 WebSocket Verbindung zu Dashboard hergestellt');
|
||||||
|
};
|
||||||
|
|
||||||
|
this.wsConnection.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
this.handleWebSocketUpdate(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Verarbeiten der WebSocket-Nachricht:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.wsConnection.onclose = () => {
|
||||||
|
console.log('📡 WebSocket Verbindung geschlossen - versuche Wiederverbindung in 5s');
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setupWebSocket();
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.wsConnection.onerror = (error) => {
|
||||||
|
console.error('WebSocket Fehler:', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('WebSocket nicht verfügbar, verwende Polling-Updates');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWebSocketUpdate(data) {
|
||||||
|
console.log('📡 Erhaltenes WebSocket-Update:', data);
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case 'job_status_update':
|
||||||
|
this.updateJobStatus(data.job_id, data.status, data.progress);
|
||||||
|
break;
|
||||||
|
case 'printer_status_update':
|
||||||
|
this.updatePrinterStatus(data.printer_id, data.status);
|
||||||
|
break;
|
||||||
|
case 'new_activity':
|
||||||
|
this.addNewActivity(data.activity);
|
||||||
|
break;
|
||||||
|
case 'stats_update':
|
||||||
|
this.updateStats(data.stats);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log('Unbekannter WebSocket-Update-Typ:', data.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshDashboard() {
|
||||||
|
if (this.isUpdating) return;
|
||||||
|
|
||||||
|
this.isUpdating = true;
|
||||||
|
const refreshBtn = document.getElementById('refreshDashboard');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Button-Status aktualisieren
|
||||||
|
if (refreshBtn) {
|
||||||
|
refreshBtn.disabled = true;
|
||||||
|
refreshBtn.innerHTML = `
|
||||||
|
<svg class="w-5 h-5 animate-spin" 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>
|
||||||
|
<span>Aktualisiert...</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vollständige Seitenaktualisierung
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Aktualisieren:', error);
|
||||||
|
this.showToast('Fehler beim Aktualisieren des Dashboards', 'error');
|
||||||
|
} finally {
|
||||||
|
this.isUpdating = false;
|
||||||
|
|
||||||
|
// Button zurücksetzen
|
||||||
|
if (refreshBtn) {
|
||||||
|
refreshBtn.disabled = false;
|
||||||
|
refreshBtn.innerHTML = `
|
||||||
|
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
|
<span>Aktualisieren</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDashboardData() {
|
||||||
|
if (this.isUpdating) return;
|
||||||
|
|
||||||
|
this.isUpdating = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Stille Hintergrund-Updates ohne vollständige Neuladen
|
||||||
|
const responses = await Promise.all([
|
||||||
|
fetch('/api/dashboard/stats'),
|
||||||
|
fetch('/api/dashboard/active-jobs'),
|
||||||
|
fetch('/api/dashboard/printers'),
|
||||||
|
fetch('/api/dashboard/activities')
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [statsData, jobsData, printersData, activitiesData] = await Promise.all(
|
||||||
|
responses.map(r => r.json())
|
||||||
|
);
|
||||||
|
|
||||||
|
// UI-Updates
|
||||||
|
this.updateStats(statsData);
|
||||||
|
this.updateActiveJobs(jobsData);
|
||||||
|
this.updatePrinters(printersData);
|
||||||
|
this.updateActivities(activitiesData);
|
||||||
|
|
||||||
|
console.log('🔄 Dashboard-Daten erfolgreich aktualisiert');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Dashboard-Daten:', error);
|
||||||
|
// Fehlschlag stumm - verwende WebSocket oder warte auf nächste Aktualisierung
|
||||||
|
} finally {
|
||||||
|
this.isUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateJobStatus(jobId, status, progress) {
|
||||||
|
const jobRow = document.querySelector(`tr[data-job-id="${jobId}"]`);
|
||||||
|
if (jobRow) {
|
||||||
|
// Status-Indikator aktualisieren
|
||||||
|
const statusIndicator = jobRow.querySelector('.mb-status-indicator');
|
||||||
|
const statusText = jobRow.querySelector('.text-sm.font-medium');
|
||||||
|
const progressBar = jobRow.querySelector('.mb-progress-bar');
|
||||||
|
const progressText = jobRow.querySelector('.text-xs.text-right');
|
||||||
|
|
||||||
|
if (statusIndicator) {
|
||||||
|
statusIndicator.className = `mb-status-indicator ${this.getStatusClass(status)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusText) {
|
||||||
|
statusText.textContent = this.getStatusText(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressBar && progress !== undefined) {
|
||||||
|
progressBar.style.width = `${progress}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressText && progress !== undefined) {
|
||||||
|
progressText.textContent = `${progress}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation für Updates
|
||||||
|
jobRow.style.backgroundColor = 'rgba(59, 130, 246, 0.1)';
|
||||||
|
setTimeout(() => {
|
||||||
|
jobRow.style.backgroundColor = '';
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePrinterStatus(printerId, status) {
|
||||||
|
const printerCard = document.querySelector(`[data-printer-id="${printerId}"]`);
|
||||||
|
if (printerCard) {
|
||||||
|
const statusIndicator = printerCard.querySelector('.mb-status-indicator');
|
||||||
|
const statusText = printerCard.querySelector('.text-sm.font-medium:last-child');
|
||||||
|
|
||||||
|
if (statusIndicator) {
|
||||||
|
statusIndicator.className = `mb-status-indicator ${this.getStatusClass(status)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusText) {
|
||||||
|
statusText.textContent = this.getStatusText(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation für Updates
|
||||||
|
printerCard.style.transform = 'scale(1.02)';
|
||||||
|
setTimeout(() => {
|
||||||
|
printerCard.style.transform = 'scale(1)';
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewActivity(activity) {
|
||||||
|
const activitiesContainer = document.querySelector('.space-y-3');
|
||||||
|
if (activitiesContainer) {
|
||||||
|
const newActivity = document.createElement('div');
|
||||||
|
newActivity.className = 'mb-activity-item pl-4 py-3 opacity-0';
|
||||||
|
newActivity.innerHTML = `
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex-1">
|
||||||
|
<p class="text-sm text-slate-700 dark:text-slate-300">${activity.description}</p>
|
||||||
|
<p class="text-xs text-slate-500 dark:text-slate-400">${activity.time}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Am Anfang einfügen
|
||||||
|
activitiesContainer.insertBefore(newActivity, activitiesContainer.firstChild);
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
setTimeout(() => {
|
||||||
|
newActivity.style.opacity = '1';
|
||||||
|
newActivity.style.transition = 'opacity 0.5s ease';
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Alte Aktivitäten entfernen (max 10)
|
||||||
|
const activities = activitiesContainer.querySelectorAll('.mb-activity-item');
|
||||||
|
if (activities.length > 10) {
|
||||||
|
activities[activities.length - 1].remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStats(stats) {
|
||||||
|
// Statistik-Karten aktualisieren mit Animation
|
||||||
|
const statValues = document.querySelectorAll('.stat-value');
|
||||||
|
|
||||||
|
const statsMapping = [
|
||||||
|
{ element: statValues[0], value: stats.active_jobs_count },
|
||||||
|
{ element: statValues[1], value: stats.available_printers_count },
|
||||||
|
{ element: statValues[2], value: stats.total_jobs_count },
|
||||||
|
{ element: statValues[3], value: `${stats.success_rate}%` }
|
||||||
|
];
|
||||||
|
|
||||||
|
statsMapping.forEach(({ element, value }) => {
|
||||||
|
if (element && element.textContent !== value.toString()) {
|
||||||
|
this.animateValueChange(element, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
animateValueChange(element, newValue) {
|
||||||
|
element.style.transform = 'scale(1.1)';
|
||||||
|
element.style.transition = 'transform 0.3s ease';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
element.textContent = newValue;
|
||||||
|
element.style.transform = 'scale(1)';
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
animateCounters() {
|
||||||
|
// Initialanimation für Counter
|
||||||
|
const counters = document.querySelectorAll('.stat-value');
|
||||||
|
counters.forEach((counter, index) => {
|
||||||
|
const finalValue = parseInt(counter.textContent) || 0;
|
||||||
|
if (finalValue > 0) {
|
||||||
|
let currentValue = 0;
|
||||||
|
const increment = finalValue / 30; // 30 Schritte
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
currentValue += increment;
|
||||||
|
if (currentValue >= finalValue) {
|
||||||
|
counter.textContent = finalValue + (counter.textContent.includes('%') ? '%' : '');
|
||||||
|
clearInterval(timer);
|
||||||
|
} else {
|
||||||
|
counter.textContent = Math.floor(currentValue) + (counter.textContent.includes('%') ? '%' : '');
|
||||||
|
}
|
||||||
|
}, 50 + (index * 100)); // Verzögerung zwischen Countern
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusClass(status) {
|
||||||
|
const statusClasses = {
|
||||||
|
'running': 'mb-status-busy',
|
||||||
|
'completed': 'mb-status-online',
|
||||||
|
'failed': 'mb-status-offline',
|
||||||
|
'paused': 'mb-status-idle',
|
||||||
|
'queued': 'mb-status-idle',
|
||||||
|
'online': 'mb-status-online',
|
||||||
|
'offline': 'mb-status-offline',
|
||||||
|
'busy': 'mb-status-busy',
|
||||||
|
'idle': 'mb-status-idle'
|
||||||
|
};
|
||||||
|
return statusClasses[status] || 'mb-status-idle';
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusText(status) {
|
||||||
|
const statusTexts = {
|
||||||
|
'running': 'Druckt',
|
||||||
|
'completed': 'Abgeschlossen',
|
||||||
|
'failed': 'Fehlgeschlagen',
|
||||||
|
'paused': 'Pausiert',
|
||||||
|
'queued': 'Warteschlange',
|
||||||
|
'online': 'Online',
|
||||||
|
'offline': 'Offline',
|
||||||
|
'busy': 'Beschäftigt',
|
||||||
|
'idle': 'Bereit'
|
||||||
|
};
|
||||||
|
return statusTexts[status] || 'Unbekannt';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateThemeElements(isDark) {
|
||||||
|
// Theme-spezifische Updates
|
||||||
|
console.log(`🎨 Dashboard Theme aktualisiert: ${isDark ? 'Dark' : 'Light'} Mode`);
|
||||||
|
|
||||||
|
// Spezielle Animationen für Theme-Wechsel
|
||||||
|
const cards = document.querySelectorAll('.dashboard-card');
|
||||||
|
cards.forEach(card => {
|
||||||
|
card.style.transition = 'all 0.3s ease';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast(message, type = 'info') {
|
||||||
|
// Toast-Benachrichtigung anzeigen
|
||||||
|
if (window.MYP && window.MYP.UI && window.MYP.UI.ToastManager) {
|
||||||
|
const toast = new window.MYP.UI.ToastManager();
|
||||||
|
toast.show(message, type, 5000);
|
||||||
|
} else {
|
||||||
|
console.log(`Toast: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup beim Verlassen der Seite
|
||||||
|
cleanup() {
|
||||||
|
if (this.autoUpdateTimer) {
|
||||||
|
clearInterval(this.autoUpdateTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.wsConnection) {
|
||||||
|
this.wsConnection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dashboard Manager initialisieren
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
window.dashboardManager = new DashboardManager();
|
||||||
|
|
||||||
|
// Cleanup beim Verlassen
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
if (window.dashboardManager) {
|
||||||
|
window.dashboardManager.cleanup();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user