/** * GlobalNotify JavaScript * Floating messenger-style notification widget */ var GlobalNotify = { isOpen: false, isDragging: false, dragOffset: { x: 0, y: 0 }, /** * Toggle panel visibility */ toggle: function() { var panel = document.getElementById('globalnotify-panel'); if (!panel) return; if (this.isOpen) { this.close(); } else { this.open(); } }, /** * Open panel */ open: function() { var panel = document.getElementById('globalnotify-panel'); if (!panel) return; panel.style.display = 'flex'; this.isOpen = true; // Close when clicking outside setTimeout(function() { document.addEventListener('click', GlobalNotify.handleOutsideClick); }, 10); }, /** * Close panel */ close: function() { var panel = document.getElementById('globalnotify-panel'); if (!panel) return; panel.style.display = 'none'; this.isOpen = false; document.removeEventListener('click', GlobalNotify.handleOutsideClick); }, /** * Handle click outside */ handleOutsideClick: function(event) { var widget = document.getElementById('globalnotify-widget'); if (widget && !widget.contains(event.target)) { GlobalNotify.close(); } }, /** * Toggle history section */ toggleHistory: function() { var toggle = document.querySelector('.globalnotify-history-toggle'); var history = document.getElementById('globalnotify-history'); if (!toggle || !history) return; if (history.classList.contains('open')) { history.classList.remove('open'); toggle.classList.remove('open'); } else { history.classList.add('open'); toggle.classList.add('open'); } }, /** * Toggle item (mark as read/unread) */ toggleItem: function(notificationId, event) { if (event) { event.stopPropagation(); } var item = document.querySelector('.globalnotify-item[data-id="' + notificationId + '"]'); if (!item) return; // Visual feedback item.style.opacity = '0.5'; // AJAX call to toggle read status this.ajaxCall('dismiss', { id: notificationId }, function(data) { if (data.success) { // Move item to history or remove item.style.transition = 'opacity 0.3s, transform 0.3s'; item.style.opacity = '0'; item.style.transform = 'translateX(-100%)'; setTimeout(function() { item.remove(); GlobalNotify.updateBadge(); GlobalNotify.updateCounts(); }, 300); } else { item.style.opacity = '1'; } }); }, /** * Go to action URL and mark as read */ goToAction: function(notificationId, url) { // Mark as read first this.ajaxCall('dismiss', { id: notificationId }, function() { // Navigate to URL window.location.href = url; }); }, /** * Mark all as read */ markAllRead: function() { var section = document.querySelector('.globalnotify-section'); var items = section ? section.querySelectorAll('.globalnotify-item') : []; // Visual feedback items.forEach(function(item) { item.style.opacity = '0.5'; }); this.ajaxCall('markallread', {}, function(data) { if (data.success) { // Clear the section if (section) { section.innerHTML = '

Keine Benachrichtigungen
'; } GlobalNotify.updateBadge(); GlobalNotify.updateCounts(); } }); }, /** * Update badge on FAB */ updateBadge: function() { var badge = document.querySelector('.globalnotify-fab-badge'); var items = document.querySelectorAll('.globalnotify-section .globalnotify-item'); var count = items.length; if (count > 0) { if (badge) { badge.textContent = count; } else { // Create badge var fab = document.getElementById('globalnotify-fab'); if (fab) { badge = document.createElement('span'); badge.className = 'globalnotify-fab-badge'; badge.textContent = count; fab.appendChild(badge); } } } else if (badge) { badge.remove(); } // Update urgent state var fab = document.getElementById('globalnotify-fab'); if (fab) { var hasUrgent = document.querySelector('.globalnotify-item-error, .globalnotify-item-action'); if (hasUrgent && count > 0) { fab.classList.add('globalnotify-fab-urgent'); } else { fab.classList.remove('globalnotify-fab-urgent'); } } }, /** * Update header counts */ updateCounts: function() { var title = document.querySelector('.globalnotify-panel-title'); if (!title) return; var unreadItems = document.querySelectorAll('.globalnotify-section .globalnotify-item'); var historyItems = document.querySelectorAll('.globalnotify-history .globalnotify-item'); var unreadCount = unreadItems.length; var totalCount = unreadCount + historyItems.length; title.textContent = 'Benachrichtigungen (' + unreadCount + '/' + totalCount + ')'; // Hide/show mark all link var markAll = document.querySelector('.globalnotify-panel-actions .globalnotify-action-link'); if (markAll) { markAll.style.display = unreadCount > 0 ? '' : 'none'; } }, /** * AJAX helper */ ajaxCall: function(action, params, callback) { var url = (typeof DOL_URL_ROOT !== 'undefined' ? DOL_URL_ROOT : '') + '/custom/globalnotify/ajax/action.php'; var body = 'action=' + encodeURIComponent(action); for (var key in params) { body += '&' + key + '=' + encodeURIComponent(params[key]); } if (typeof TOKEN !== 'undefined') { body += '&token=' + TOKEN; } fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: body }) .then(function(response) { return response.json(); }) .then(function(data) { if (callback) callback(data); }) .catch(function(error) { console.error('GlobalNotify error:', error); if (callback) callback({ success: false, error: error }); }); }, /** * Initialize dragging */ initDrag: function() { var widget = document.getElementById('globalnotify-widget'); var handle = document.getElementById('globalnotify-drag-handle'); if (!widget || !handle) return; handle.addEventListener('mousedown', function(e) { if (e.target.closest('.globalnotify-action-link')) return; GlobalNotify.isDragging = true; var rect = widget.getBoundingClientRect(); GlobalNotify.dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top }; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', function(e) { if (!GlobalNotify.isDragging) return; var x = e.clientX - GlobalNotify.dragOffset.x; var y = e.clientY - GlobalNotify.dragOffset.y; // Keep within viewport x = Math.max(0, Math.min(x, window.innerWidth - 60)); y = Math.max(0, Math.min(y, window.innerHeight - 60)); widget.style.left = x + 'px'; widget.style.top = y + 'px'; widget.style.bottom = 'auto'; widget.style.right = 'auto'; }); document.addEventListener('mouseup', function() { GlobalNotify.isDragging = false; document.body.style.userSelect = ''; }); }, /** * Refresh notifications via AJAX */ refresh: function() { this.ajaxCall('getcount', {}, function(data) { if (data.success) { GlobalNotify.updateBadgeFromServer(data.count); } }); }, /** * Update badge from server count */ updateBadgeFromServer: function(count) { var badge = document.querySelector('.globalnotify-fab-badge'); var fab = document.getElementById('globalnotify-fab'); if (count > 0) { if (badge) { badge.textContent = count; } else if (fab) { badge = document.createElement('span'); badge.className = 'globalnotify-fab-badge'; badge.textContent = count; fab.appendChild(badge); } } else if (badge) { badge.remove(); } }, /** * Initialize */ init: function() { this.initDrag(); // Periodic refresh every 2 minutes setInterval(function() { GlobalNotify.refresh(); }, 120000); } }; // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', function() { GlobalNotify.init(); });