/**
* MYP Platform UI Components
* JavaScript-Utilities für erweiterte UI-Funktionalität
* Version: 3.0.0
*/
(function() {
'use strict';
// Namespace für MYP UI Components
window.MYP = window.MYP || {};
window.MYP.UI = window.MYP.UI || {};
/**
* Dark Mode Handler
*/
class DarkModeManager {
constructor() {
// DOM-Elemente
this.darkModeToggle = document.getElementById('darkModeToggle');
this.sunIcon = this.darkModeToggle ? this.darkModeToggle.querySelector('.sun-icon') : null;
this.moonIcon = this.darkModeToggle ? this.darkModeToggle.querySelector('.moon-icon') : null;
this.html = document.documentElement;
// Local Storage Key
this.STORAGE_KEY = 'myp-dark-mode';
this.init();
}
/**
* Aktuellen Dark Mode Status aus Local Storage oder Systemeinstellung abrufen
* @returns {boolean} True wenn Dark Mode aktiviert ist
*/
isDarkMode() {
const savedMode = localStorage.getItem(this.STORAGE_KEY);
if (savedMode !== null) {
return savedMode === 'true';
}
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
/**
* Dark Mode aktivieren/deaktivieren
* @param {boolean} enable - Dark Mode aktivieren (true) oder deaktivieren (false)
*/
setDarkMode(enable) {
if (enable) {
this.html.classList.add('dark');
this.html.setAttribute('data-theme', 'dark');
this.html.style.colorScheme = 'dark';
this.updateDarkModeIcon(true);
this.darkModeToggle.setAttribute('aria-pressed', 'true');
this.darkModeToggle.setAttribute('title', 'Light Mode aktivieren');
if (this.sunIcon && this.moonIcon) {
this.sunIcon.classList.add('hidden');
this.moonIcon.classList.remove('hidden');
}
} else {
this.html.classList.remove('dark');
this.html.setAttribute('data-theme', 'light');
this.html.style.colorScheme = 'light';
this.updateDarkModeIcon(false);
this.darkModeToggle.setAttribute('aria-pressed', 'false');
this.darkModeToggle.setAttribute('title', 'Dark Mode aktivieren');
if (this.sunIcon && this.moonIcon) {
this.sunIcon.classList.remove('hidden');
this.moonIcon.classList.add('hidden');
}
}
// Einstellung im Local Storage speichern
localStorage.setItem(this.STORAGE_KEY, enable);
// ThemeColor Meta-Tag aktualisieren
const metaThemeColor = document.getElementById('metaThemeColor');
if (metaThemeColor) {
metaThemeColor.setAttribute('content', enable ? '#000000' : '#ffffff');
}
// Event für andere Komponenten auslösen
window.dispatchEvent(new CustomEvent('darkModeChanged', {
detail: { isDark: enable }
}));
console.log(`${enable ? '🌙' : '☀️'} Design umgeschaltet auf: ${enable ? 'Dark Mode' : 'Light Mode'}`);
}
/**
* Icon für Dark Mode Toggle aktualisieren
* @param {boolean} isDark - Ob Dark Mode aktiv ist
*/
updateDarkModeIcon(isDark) {
if (!this.darkModeToggle) return;
// Stellen sicher, dass die Icons korrekt angezeigt werden
if (this.sunIcon && this.moonIcon) {
if (isDark) {
this.sunIcon.classList.add('hidden');
this.moonIcon.classList.remove('hidden');
} else {
this.sunIcon.classList.remove('hidden');
this.moonIcon.classList.add('hidden');
}
}
}
/**
* Event Listener einrichten und Darkmode initialisieren
*/
init() {
if (!this.darkModeToggle) {
console.error('⚠️ Dark Mode Toggle Button nicht gefunden! UI-Komponente nicht verfügbar.');
return;
}
console.log('🚀 Dark Mode Manager erfolgreich initialisiert');
// Event Listener für den Dark Mode Toggle Button
this.darkModeToggle.addEventListener('click', () => {
console.log('👆 Dark Mode Toggle Button angeklickt');
const newDarkModeState = !this.isDarkMode();
this.setDarkMode(newDarkModeState);
});
// Alternative Event-Listener für Buttons mit data-action
document.addEventListener('click', (e) => {
if (e.target.closest('[data-action="toggle-dark-mode"]')) {
console.log('👆 Dark Mode Toggle über data-action aktiviert');
const newDarkModeState = !this.isDarkMode();
this.setDarkMode(newDarkModeState);
}
});
// Tastaturkürzel: Strg+Shift+D für Dark Mode Toggle
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
const newDarkModeState = !this.isDarkMode();
this.setDarkMode(newDarkModeState);
e.preventDefault();
}
});
// System-Preference für Dark Mode überwachen
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMediaQuery.addEventListener('change', (e) => {
// Nur ändern, wenn der Benutzer noch keine eigene Einstellung hat
if (localStorage.getItem(this.STORAGE_KEY) === null) {
this.setDarkMode(e.matches);
}
});
// Initialisierung: Aktuellen Status setzen
const isDark = this.isDarkMode();
console.log(`🔍 Initialer Dark Mode Status: ${isDark ? '🌙 aktiviert' : '☀️ deaktiviert'}`);
this.setDarkMode(isDark);
}
}
/**
* Mobile Menu Manager
*/
class MobileMenuManager {
constructor() {
this.mobileMenuToggle = document.getElementById('mobileMenuToggle');
this.mobileMenu = document.getElementById('mobileMenu');
this.isOpen = false;
this.init();
}
init() {
if (!this.mobileMenuToggle || !this.mobileMenu) {
console.log('ℹ️ Mobile Menu Komponenten nicht gefunden - Feature deaktiviert');
return;
}
console.log('📱 Mobile Menu Manager erfolgreich initialisiert');
// Event Listener für den Mobile Menu Toggle Button
this.mobileMenuToggle.addEventListener('click', () => {
this.toggleMenu();
});
// Event Listener für Klicks außerhalb des Menüs
document.addEventListener('click', (e) => {
if (this.isOpen && !this.mobileMenuToggle.contains(e.target) && !this.mobileMenu.contains(e.target)) {
this.closeMenu();
}
});
// Event Listener für das Schließen bei Klick auf einen Menüpunkt
this.mobileMenu.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
this.closeMenu();
});
});
// Beim Scrollen das Menü schließen
window.addEventListener('scroll', () => {
if (this.isOpen && window.scrollY > 50) {
this.closeMenu();
}
});
}
toggleMenu() {
if (this.isOpen) {
this.closeMenu();
} else {
this.openMenu();
}
}
openMenu() {
if (!this.mobileMenu || this.isOpen) return;
this.mobileMenu.classList.remove('hidden');
this.isOpen = true;
this.mobileMenuToggle.setAttribute('aria-expanded', 'true');
// Icon ändern
const menuIcon = this.mobileMenuToggle.querySelector('svg');
if (menuIcon) {
menuIcon.innerHTML = '';
}
// Animation - kleine Verzögerung für sanfte Animation
setTimeout(() => {
this.mobileMenu.classList.add('open');
}, 10);
// Body-Scroll verhindern (optional)
// document.body.style.overflow = 'hidden';
}
closeMenu() {
if (!this.mobileMenu || !this.isOpen) return;
this.mobileMenu.classList.remove('open');
this.isOpen = false;
this.mobileMenuToggle.setAttribute('aria-expanded', 'false');
// Icon zurücksetzen
const menuIcon = this.mobileMenuToggle.querySelector('svg');
if (menuIcon) {
menuIcon.innerHTML = '';
}
// Nach der Animation ausblenden
setTimeout(() => {
this.mobileMenu.classList.add('hidden');
}, 300);
// Body-Scroll wiederherstellen
// document.body.style.overflow = '';
}
}
/**
* User Dropdown Manager
*/
class UserDropdownManager {
constructor() {
this.toggleButton = document.getElementById('user-menu-button');
this.container = document.getElementById('user-menu-container');
this.dropdown = document.getElementById('user-dropdown');
this.isOpen = false;
// Dropdown erstellen, falls nicht vorhanden
if (!this.dropdown && this.container) {
this.createDropdown();
}
this.init();
}
/**
* Dropdown-Menü dynamisch erstellen, falls es nicht existiert
*/
createDropdown() {
if (!this.container) return;
// Neue Dropdown-Box erstellen
this.dropdown = document.createElement('div');
this.dropdown.id = 'user-dropdown';
this.dropdown.className = 'user-dropdown hidden';
this.dropdown.setAttribute('role', 'menu');
this.dropdown.setAttribute('aria-orientation', 'vertical');
this.dropdown.setAttribute('aria-labelledby', 'user-menu-button');
// HTML für das Dropdown-Menü
this.dropdown.innerHTML = `
Mein Profil
Einstellungen
`;
// Dropdown zum Container hinzufügen
this.container.appendChild(this.dropdown);
}
/**
* Benutzername aus E-Mail extrahieren oder Standardwert verwenden
*/
getCurrentUsername() {
const metaUsername = document.querySelector('meta[name="user-name"]');
if (metaUsername) {
return metaUsername.getAttribute('content');
}
// Aus dem vorhandenen DOM extrahieren
const usernameElement = this.container.querySelector('.text-sm.font-medium');
if (usernameElement) {
return usernameElement.textContent.trim();
}
return 'Benutzer';
}
/**
* E-Mail-Adresse aus dem DOM extrahieren
*/
getCurrentUserEmail() {
const metaEmail = document.querySelector('meta[name="user-email"]');
if (metaEmail) {
return metaEmail.getAttribute('content');
}
// Aus dem vorhandenen DOM extrahieren
const emailElement = this.container.querySelector('.text-xs');
if (emailElement) {
return emailElement.textContent.trim();
}
return 'Mercedes-Benz Mitarbeiter';
}
/**
* Initial des Benutzers extrahieren
*/
getCurrentUserInitial() {
const avatar = this.container.querySelector('.user-avatar');
if (avatar) {
return avatar.textContent.trim();
}
return 'U';
}
open() {
if (!this.dropdown || this.isOpen) return;
this.dropdown.classList.remove('hidden');
this.isOpen = true;
this.toggleButton.setAttribute('aria-expanded', 'true');
// Rotate dropdown arrow
const arrow = this.toggleButton.querySelector('svg');
if (arrow) {
arrow.style.transform = 'rotate(180deg)';
}
// Close when clicking outside
setTimeout(() => {
document.addEventListener('click', this.outsideClickHandler);
}, 10);
// Close when pressing escape
document.addEventListener('keydown', this.escapeKeyHandler);
}
close() {
if (!this.dropdown || !this.isOpen) return;
this.dropdown.classList.add('hidden');
this.isOpen = false;
this.toggleButton.setAttribute('aria-expanded', 'false');
// Reset dropdown arrow
const arrow = this.toggleButton.querySelector('svg');
if (arrow) {
arrow.style.transform = 'rotate(0deg)';
}
// Remove event listeners
document.removeEventListener('click', this.outsideClickHandler);
document.removeEventListener('keydown', this.escapeKeyHandler);
}
toggle() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
init() {
if (!this.toggleButton || !this.dropdown) return;
// Bind methods to this instance
this.outsideClickHandler = this.handleOutsideClick.bind(this);
this.escapeKeyHandler = this.handleEscapeKey.bind(this);
// Set up click listener for toggle button
this.toggleButton.addEventListener('click', (e) => {
e.stopPropagation();
this.toggle();
});
// Set up logout button
const logoutButton = this.dropdown.querySelector('[data-action="logout"]');
if (logoutButton) {
logoutButton.addEventListener('click', this.handleLogout.bind(this));
}
// Set up settings button
const settingsLink = this.dropdown.querySelector('#settings-link');
if (settingsLink) {
settingsLink.addEventListener('click', this.handleSettings.bind(this));
}
}
handleOutsideClick(e) {
if (this.container && !this.container.contains(e.target)) {
this.close();
}
}
handleEscapeKey(e) {
if (e.key === 'Escape') {
this.close();
}
}
handleLogout(e) {
e.preventDefault();
// Add smooth logout animation
document.body.style.opacity = '0.7';
// Create and submit logout form
const form = document.createElement('form');
form.method = 'POST';
form.action = '/logout';
// Add CSRF token if available
const csrfToken = document.querySelector('meta[name="csrf-token"]');
if (csrfToken) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'csrf_token';
input.value = csrfToken.getAttribute('content');
form.appendChild(input);
}
// Submit the form
document.body.appendChild(form);
form.submit();
}
handleSettings(e) {
// Optionales preventDefault für spezielle Handhabung
// e.preventDefault();
}
}
/**
* Toast-Benachrichtigungen
*/
class ToastManager {
constructor() {
this.container = this.createContainer();
this.toasts = new Map();
}
createContainer() {
let container = document.getElementById('toast-container');
if (!container) {
container = document.createElement('div');
container.id = 'toast-container';
container.className = 'fixed top-4 right-4 z-50 space-y-2';
document.body.appendChild(container);
}
return container;
}
show(message, type = 'info', duration = 5000) {
const id = 'toast-' + Date.now();
const toast = this.createToast(id, message, type);
this.container.appendChild(toast);
this.toasts.set(id, toast);
// Animation einblenden
requestAnimationFrame(() => {
toast.classList.remove('translate-x-full', 'opacity-0');
});
// Auto-Hide nach duration
if (duration > 0) {
setTimeout(() => this.hide(id), duration);
}
return id;
}
createToast(id, message, type) {
const toast = document.createElement('div');
toast.id = id;
toast.className = `transform translate-x-full opacity-0 transition-all duration-300 ease-in-out max-w-sm w-full bg-white dark:bg-slate-800 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden`;
const typeClasses = {
'success': 'border-l-4 border-green-400',
'error': 'border-l-4 border-red-400',
'warning': 'border-l-4 border-yellow-400',
'info': 'border-l-4 border-blue-400'
};
const iconHTML = {
'success': '',
'error': '',
'warning': '',
'info': ''
};
toast.className += ' ' + (typeClasses[type] || typeClasses.info);
toast.innerHTML = `
${iconHTML[type] || iconHTML.info}
`;
return toast;
}
hide(id) {
const toast = this.toasts.get(id);
if (toast) {
toast.classList.add('translate-x-full', 'opacity-0');
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
this.toasts.delete(id);
}, 300);
}
}
success(message, duration) {
return this.show(message, 'success', duration);
}
error(message, duration) {
return this.show(message, 'error', duration);
}
warning(message, duration) {
return this.show(message, 'warning', duration);
}
info(message, duration) {
return this.show(message, 'info', duration);
}
}
/**
* Modal-Dialog-Manager
*/
class ModalManager {
constructor() {
this.activeModals = new Set();
this.setupEventListeners();
}
setupEventListeners() {
// ESC-Taste zum Schließen
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.activeModals.size > 0) {
this.closeTopModal();
}
});
}
open(modalId, options = {}) {
const modal = document.getElementById(modalId);
if (!modal) {
console.error(`Modal mit ID '${modalId}' nicht gefunden`);
return;
}
// Backdrop erstellen
const backdrop = this.createBackdrop(modalId);
document.body.appendChild(backdrop);
// Modal anzeigen
modal.style.display = 'block';
this.activeModals.add(modalId);
// Animation
requestAnimationFrame(() => {
backdrop.classList.remove('opacity-0');
modal.querySelector('.modal-content')?.classList.remove('scale-95', 'opacity-0');
});
// Body-Scroll verhindern
document.body.style.overflow = 'hidden';
// Auto-Focus auf erstes Input-Element
const firstInput = modal.querySelector('input, select, textarea, button');
if (firstInput) {
firstInput.focus();
}
}
close(modalId) {
const modal = document.getElementById(modalId);
const backdrop = document.getElementById(`backdrop-${modalId}`);
if (modal && backdrop) {
// Animation ausblenden
backdrop.classList.add('opacity-0');
modal.querySelector('.modal-content')?.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.style.display = 'none';
if (backdrop.parentNode) {
backdrop.parentNode.removeChild(backdrop);
}
this.activeModals.delete(modalId);
// Body-Scroll wiederherstellen, falls kein Modal mehr offen
if (this.activeModals.size === 0) {
document.body.style.overflow = '';
}
}, 150);
}
}
closeTopModal() {
if (this.activeModals.size > 0) {
const lastModal = Array.from(this.activeModals).pop();
this.close(lastModal);
}
}
createBackdrop(modalId) {
const backdrop = document.createElement('div');
backdrop.id = `backdrop-${modalId}`;
backdrop.className = 'fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity opacity-0 z-40';
backdrop.onclick = () => this.close(modalId);
return backdrop;
}
}
/**
* Dropdown-Manager
*/
class DropdownManager {
constructor() {
this.activeDropdowns = new Set();
this.setupEventListeners();
}
setupEventListeners() {
document.addEventListener('click', (e) => {
// Schließe alle Dropdowns, wenn außerhalb geklickt wird
if (!e.target.closest('[data-dropdown]')) {
this.closeAll();
}
});
}
toggle(dropdownId) {
const dropdown = document.querySelector(`[data-dropdown="${dropdownId}"]`);
if (!dropdown) return;
const menu = dropdown.querySelector('.dropdown-menu');
if (!menu) return;
if (this.activeDropdowns.has(dropdownId)) {
this.close(dropdownId);
} else {
this.closeAll(); // Andere schließen
this.open(dropdownId);
}
}
open(dropdownId) {
const dropdown = document.querySelector(`[data-dropdown="${dropdownId}"]`);
const menu = dropdown?.querySelector('.dropdown-menu');
if (dropdown && menu) {
menu.classList.remove('hidden');
this.activeDropdowns.add(dropdownId);
// Position berechnen
this.positionDropdown(dropdown, menu);
}
}
close(dropdownId) {
const dropdown = document.querySelector(`[data-dropdown="${dropdownId}"]`);
const menu = dropdown?.querySelector('.dropdown-menu');
if (menu) {
menu.classList.add('hidden');
this.activeDropdowns.delete(dropdownId);
}
}
closeAll() {
this.activeDropdowns.forEach(id => this.close(id));
}
positionDropdown(dropdown, menu) {
const rect = dropdown.getBoundingClientRect();
const menuRect = menu.getBoundingClientRect();
const viewportHeight = window.innerHeight;
// Prüfen ob Platz nach unten ist
if (rect.bottom + menuRect.height > viewportHeight) {
// Nach oben öffnen
menu.style.bottom = '100%';
menu.style.top = 'auto';
} else {
// Nach unten öffnen
menu.style.top = '100%';
menu.style.bottom = 'auto';
}
}
}
/**
* Loading-Spinner-Manager
*/
class LoadingManager {
show(target = document.body, message = 'Laden...') {
const loadingId = 'loading-' + Date.now();
const overlay = document.createElement('div');
overlay.id = loadingId;
overlay.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
overlay.innerHTML = `
`;
target.appendChild(overlay);
return loadingId;
}
hide(loadingId) {
const overlay = document.getElementById(loadingId);
if (overlay) {
overlay.remove();
}
}
}
/**
* Status-Badge-Helper
*/
class StatusHelper {
static getPrinterStatusClass(status) {
const statusMap = {
'ready': 'printer-ready',
'busy': 'printer-busy',
'error': 'printer-error',
'offline': 'printer-offline',
'maintenance': 'printer-maintenance'
};
return `printer-status ${statusMap[status] || 'printer-offline'}`;
}
static getJobStatusClass(status) {
const statusMap = {
'queued': 'job-queued',
'printing': 'job-printing',
'completed': 'job-completed',
'failed': 'job-failed',
'cancelled': 'job-cancelled',
'paused': 'job-paused'
};
return `job-status ${statusMap[status] || 'job-queued'}`;
}
static formatStatus(status, type = 'job') {
const translations = {
job: {
'queued': 'In Warteschlange',
'printing': 'Wird gedruckt',
'completed': 'Abgeschlossen',
'failed': 'Fehlgeschlagen',
'cancelled': 'Abgebrochen',
'paused': 'Pausiert'
},
printer: {
'ready': 'Bereit',
'busy': 'Beschäftigt',
'error': 'Fehler',
'offline': 'Offline',
'maintenance': 'Wartung'
}
};
return translations[type]?.[status] || status;
}
}
/**
* Connection Status Manager
*/
class ConnectionStatusManager {
constructor() {
this.statusElement = document.getElementById('connection-status');
if (this.statusElement) {
this.init();
}
}
updateStatus() {
if (!this.statusElement) return;
if (navigator.onLine) {
this.statusElement.innerHTML = 'Online';
} else {
this.statusElement.innerHTML = 'Offline';
}
}
init() {
// Initial Update
this.updateStatus();
// Event Listener für Netzwerkänderungen
window.addEventListener('online', () => this.updateStatus());
window.addEventListener('offline', () => this.updateStatus());
}
}
/**
* API-Aufruf Hilfsfunktion für Fetch-Requests
* @param {string} url - API-Endpunkt
* @param {object} options - Fetch-Optionen
* @returns {Promise