manage-your-printer/static/js/notifications.min.js
2025-06-04 10:03:22 +02:00

40 lines
16 KiB
JavaScript

class ModernNotificationManager{constructor(){this.notificationToggle=document.getElementById('notificationToggle');this.notificationDropdown=document.getElementById('notificationDropdown');this.notificationBadge=document.getElementById('notificationBadge');this.notificationList=document.getElementById('notificationList');this.markAllReadBtn=document.getElementById('markAllRead');this.isOpen=false;this.notifications=[];this.activeToasts=new Map();this.toastCounter=0;this.csrfToken=this.getCSRFToken();this.init();this.setupGlobalNotificationSystem();}
getCSRFToken(){const metaTag=document.querySelector('meta[name="csrf-token"]');return metaTag?metaTag.getAttribute('content'):'';}
getAPIHeaders(){const headers={'Content-Type':'application/json',};if(this.csrfToken){headers['X-CSRFToken']=this.csrfToken;}
return headers;}
init(){if(!this.notificationToggle)return;this.notificationToggle.addEventListener('click',(e)=>{e.stopPropagation();this.toggleDropdown();});if(this.markAllReadBtn){this.markAllReadBtn.addEventListener('click',()=>{this.markAllAsRead();});}
document.addEventListener('click',(e)=>{if(!this.notificationDropdown.contains(e.target)&&!this.notificationToggle.contains(e.target)){this.closeDropdown();}});this.loadNotifications();setInterval(()=>{this.loadNotifications();},30000);}
toggleDropdown(){if(this.isOpen){this.closeDropdown();}else{this.openDropdown();}}
openDropdown(){this.notificationDropdown.classList.remove('hidden');this.notificationToggle.setAttribute('aria-expanded','true');this.isOpen=true;this.notificationDropdown.style.opacity='0';this.notificationDropdown.style.transform='translateY(-10px)';requestAnimationFrame(()=>{this.notificationDropdown.style.transition='opacity 0.2s ease, transform 0.2s ease';this.notificationDropdown.style.opacity='1';this.notificationDropdown.style.transform='translateY(0)';});}
closeDropdown(){this.notificationDropdown.style.transition='opacity 0.2s ease, transform 0.2s ease';this.notificationDropdown.style.opacity='0';this.notificationDropdown.style.transform='translateY(-10px)';setTimeout(()=>{this.notificationDropdown.classList.add('hidden');this.notificationToggle.setAttribute('aria-expanded','false');this.isOpen=false;},200);}
async loadNotifications(){try{const response=await fetch('/api/notifications');if(response.ok){const data=await response.json();this.notifications=data.notifications||[];this.updateUI();}}catch(error){console.error('Fehler beim Laden der Benachrichtigungen:',error);}}
updateUI(){this.updateBadge();this.updateNotificationList();}
updateBadge(){const unreadCount=this.notifications.filter(n=>!n.read).length;if(unreadCount>0){this.notificationBadge.textContent=unreadCount>99?'99+':unreadCount.toString();this.notificationBadge.classList.remove('hidden');}else{this.notificationBadge.classList.add('hidden');}}
updateNotificationList(){if(this.notifications.length===0){this.notificationList.innerHTML=`<div class="p-4 text-center text-slate-500 dark:text-slate-400">Keine neuen Benachrichtigungen</div>`;return;}
const notificationHTML=this.notifications.map(notification=>{const isUnread=!notification.read;const timeAgo=this.formatTimeAgo(new Date(notification.created_at));return`<div class="notification-item p-4 border-b border-slate-200 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors ${isUnread ? 'bg-blue-50 dark:bg-blue-900/20' : ''}"
data-notification-id="${notification.id}"><div class="flex items-start space-x-3"><div class="flex-shrink-0">${this.getNotificationIcon(notification.type)}</div><div class="flex-1 min-w-0"><div class="flex items-center justify-between"><p class="text-sm font-medium text-slate-900 dark:text-white">${this.getNotificationTitle(notification.type)}</p>${isUnread?'<div class="w-2 h-2 bg-blue-500 rounded-full"></div>':''}</div><p class="text-sm text-slate-600 dark:text-slate-400 mt-1">${this.getNotificationMessage(notification)}</p><p class="text-xs text-slate-500 dark:text-slate-500 mt-2">${timeAgo}</p></div></div></div>`;}).join('');this.notificationList.innerHTML=notificationHTML;this.notificationList.querySelectorAll('.notification-item').forEach(item=>{item.addEventListener('click',(e)=>{const notificationId=item.dataset.notificationId;this.markAsRead(notificationId);});});}
getNotificationIcon(type){const icons={'guest_request':`<div class="w-8 h-8 bg-blue-100 dark:bg-blue-900 rounded-full flex items-center justify-center"><svg class="w-4 h-4 text-blue-600 dark:text-blue-400"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg></div>`,'job_completed':`<div class="w-8 h-8 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center"><svg class="w-4 h-4 text-green-600 dark:text-green-400"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M5 13l4 4L19 7"/></svg></div>`,'system':`<div class="w-8 h-8 bg-gray-100 dark:bg-gray-900 rounded-full flex items-center justify-center"><svg class="w-4 h-4 text-gray-600 dark:text-gray-400"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></div>`};return icons[type]||icons['system'];}
getNotificationTitle(type){const titles={'guest_request':'Neue Gastanfrage','job_completed':'Druckauftrag abgeschlossen','system':'System-Benachrichtigung'};return titles[type]||'Benachrichtigung';}
getNotificationMessage(notification){try{const payload=JSON.parse(notification.payload||'{}');switch(notification.type){case'guest_request':return`${payload.guest_name||'Ein Gast'}hat eine neue Druckanfrage gestellt.`;case'job_completed':return`Der Druckauftrag"${payload.job_name || 'Unbekannt'}"wurde erfolgreich abgeschlossen.`;default:return payload.message||'Neue Benachrichtigung erhalten.';}}catch(error){return'Neue Benachrichtigung erhalten.';}}
formatTimeAgo(date){const now=new Date();const diffInSeconds=Math.floor((now-date)/1000);if(diffInSeconds<60){return'Gerade eben';}else if(diffInSeconds<3600){const minutes=Math.floor(diffInSeconds/60);return`vor ${minutes}Minute${minutes!==1?'n':''}`;}else if(diffInSeconds<86400){const hours=Math.floor(diffInSeconds/3600);return`vor ${hours}Stunde${hours!==1?'n':''}`;}else{const days=Math.floor(diffInSeconds/86400);return`vor ${days}Tag${days!==1?'en':''}`;}}
async markAsRead(notificationId){try{const response=await fetch(`/api/notifications/${notificationId}/read`,{method:'POST',headers:this.getAPIHeaders()});if(response.ok){const notification=this.notifications.find(n=>n.id==notificationId);if(notification){notification.read=true;this.updateUI();}}else{console.error('Fehler beim Markieren als gelesen:',response.status,response.statusText);}}catch(error){console.error('Fehler beim Markieren als gelesen:',error);}}
async markAllAsRead(){try{const response=await fetch('/api/notifications/mark-all-read',{method:'POST',headers:this.getAPIHeaders()});if(response.ok){this.notifications.forEach(notification=>{notification.read=true;});this.updateUI();}else{console.error('Fehler beim Markieren aller als gelesen:',response.status,response.statusText);}}catch(error){console.error('Fehler beim Markieren aller als gelesen:',error);}}
setupGlobalNotificationSystem(){window.showFlashMessage=this.showGlassToast.bind(this);window.showToast=this.showGlassToast.bind(this);window.showNotification=this.showGlassToast.bind(this);window.showSuccessMessage=(message)=>this.showGlassToast(message,'success');window.showErrorMessage=(message)=>this.showGlassToast(message,'error');window.showWarningMessage=(message)=>this.showGlassToast(message,'warning');window.showInfoMessage=(message)=>this.showGlassToast(message,'info');}
showGlassToast(message,type='info',duration=5000,options={}){if(window.dndManager&&window.dndManager.isEnabled){window.dndManager.suppressNotification(message,type);return;}
const toastId=`toast-${++this.toastCounter}`;const toast=document.createElement('div');toast.id=toastId;toast.className=`glass-toast notification notification-${type}show`;const iconMap={success:`<svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M5 13l4 4L19 7"/></svg>`,error:`<svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg>`,warning:`<svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>`,info:`<svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>`};toast.innerHTML=`<div class="flex items-center"><div class="notification-icon">${iconMap[type]||iconMap.info}</div><div class="notification-content">${options.title?`<div class="notification-title">${options.title}</div>`:''}<div class="notification-message">${message}</div></div><button class="notification-close"onclick="modernNotificationManager.closeToast('${toastId}')"><svg class="w-4 h-4"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg></button></div>`;let container=document.getElementById('toast-container');if(!container){container=document.createElement('div');container.id='toast-container';container.className='notifications-container';document.body.appendChild(container);}
const existingToasts=container.children.length;toast.style.top=`${1+existingToasts*5}rem`;container.appendChild(toast);this.activeToasts.set(toastId,toast);let isPaused=false;let timeoutId;const startTimer=()=>{if(!isPaused&&duration>0){timeoutId=setTimeout(()=>{this.closeToast(toastId);},duration);}};toast.addEventListener('mouseenter',()=>{isPaused=true;clearTimeout(timeoutId);toast.style.transform='translateY(-2px) scale(1.02)';});toast.addEventListener('mouseleave',()=>{isPaused=false;toast.style.transform='translateY(0) scale(1)';startTimer();});setTimeout(startTimer,100);if(options.playSound!==false){this.playNotificationSound(type);}
return toastId;}
closeToast(toastId){const toast=this.activeToasts.get(toastId);if(!toast)return;toast.classList.add('hiding');setTimeout(()=>{if(toast.parentNode){toast.parentNode.removeChild(toast);}
this.activeToasts.delete(toastId);this.repositionToasts();},400);}
repositionToasts(){let index=0;this.activeToasts.forEach((toast)=>{if(toast.parentNode){toast.style.top=`${1+index*5}rem`;index++;}});}
playNotificationSound(type){try{if(typeof AudioContext!=='undefined'||typeof webkitAudioContext!=='undefined'){const audioContext=new(window.AudioContext||window.webkitAudioContext)();const oscillator=audioContext.createOscillator();const gainNode=audioContext.createGain();oscillator.connect(gainNode);gainNode.connect(audioContext.destination);const frequencies={success:[523.25,659.25,783.99],error:[440,415.3],warning:[493.88,523.25],info:[523.25]};const freq=frequencies[type]||frequencies.info;freq.forEach((f,i)=>{setTimeout(()=>{const osc=audioContext.createOscillator();const gain=audioContext.createGain();osc.connect(gain);gain.connect(audioContext.destination);osc.frequency.setValueAtTime(f,audioContext.currentTime);gain.gain.setValueAtTime(0.1,audioContext.currentTime);gain.gain.exponentialRampToValueAtTime(0.01,audioContext.currentTime+0.1);osc.start(audioContext.currentTime);osc.stop(audioContext.currentTime+0.1);},i*100);});}}catch(error){}}
showBrowserNotification(title,message,options={}){if('Notification'in window){if(Notification.permission==='granted'){const notification=new Notification(title,{body:message,icon:options.icon||'/static/icons/static/icons/notification-icon.png',badge:options.badge||'/static/icons/static/icons/badge-icon.png',tag:options.tag||'myp-notification',requireInteraction:options.requireInteraction||false,...options});notification.onclick=options.onClick||(()=>{window.focus();notification.close();});return notification;}else if(Notification.permission==='default'){Notification.requestPermission().then(permission=>{if(permission==='granted'){this.showBrowserNotification(title,message,options);}});}}
return null;}
showAlert(message,type='info',options={}){const alertId=`alert-${Date.now()}`;const alert=document.createElement('div');alert.id=alertId;alert.className=`alert alert-${type}`;alert.innerHTML=`<div class="flex items-start"><div class="notification-icon mr-3">${this.getIconForType(type)}</div><div class="flex-1">${options.title?`<h4 class="font-semibold mb-2">${options.title}</h4>`:''}<p>${message}</p></div>${options.dismissible!==false?`<button class="ml-3 p-1 rounded-lg opacity-70 hover:opacity-100 transition-opacity"onclick="document.getElementById('${alertId}').remove()"><svg class="w-4 h-4"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg></button>`:''}</div>`;const container=options.container||document.querySelector('.flash-messages')||document.body;container.appendChild(alert);if(options.autoDismiss!==false){setTimeout(()=>{if(alert.parentNode){alert.style.opacity='0';alert.style.transform='translateY(-20px)';setTimeout(()=>alert.remove(),300);}},options.duration||7000);}
return alertId;}
getIconForType(type){const icons={success:`<svg class="w-5 h-5 text-green-600"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M5 13l4 4L19 7"/></svg>`,error:`<svg class="w-5 h-5 text-red-600"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg>`,warning:`<svg class="w-5 h-5 text-yellow-600"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>`,info:`<svg class="w-5 h-5 text-blue-600"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>`};return icons[type]||icons.info;}}
class NotificationManager extends ModernNotificationManager{constructor(){super();console.warn('NotificationManager ist deprecated. Verwenden Sie ModernNotificationManager.');}}
const modernNotificationManager=new ModernNotificationManager();if(typeof window!=='undefined'){window.notificationManager=modernNotificationManager;window.modernNotificationManager=modernNotificationManager;}
const toastStyles=`<style>.glass-toast{position:relative;transform:translateX(0)translateY(0)scale(1);animation:toast-slide-in 0.6s cubic-bezier(0.4,0,0.2,1);}.glass-toast:hover{transform:translateY(-2px)scale(1.02)!important;transition:transform 0.2s ease;}@keyframes toast-slide-in{0%{opacity:0;transform:translateX(100%)translateY(-20px)scale(0.9);}
50%{opacity:0.8;transform:translateX(20px)translateY(-10px)scale(1.05);}
100%{opacity:1;transform:translateX(0)translateY(0)scale(1);}}.notification-item.unread{background:linear-gradient(135deg,rgba(59,130,246,0.08)0%,rgba(147,197,253,0.05)100%);border-left:3px solid rgb(59,130,246);}.dark.notification-item.unread{background:linear-gradient(135deg,rgba(59,130,246,0.15)0%,rgba(147,197,253,0.08)100%);}</style>`;if(typeof document!=='undefined'&&!document.getElementById('toast-styles')){const styleElement=document.createElement('div');styleElement.id='toast-styles';styleElement.innerHTML=toastStyles;document.head.appendChild(styleElement);}