/** * @fileoverview A popover component to display user notifications. * Grouped by date (Today, Yesterday, Earlier) with mark as read support. */ import notificationService from '../services/notification-service.js'; class DsNotificationCenter extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this._isConnected = false; } connectedCallback() { this._isConnected = true; this.render(); this._updateNotifications = this._updateNotifications.bind(this); notificationService.addEventListener('notifications-updated', this._updateNotifications); // Initialize the service and get initial notifications // Only update if component is still connected when promise resolves notificationService.init().then(() => { if (this._isConnected) { this._updateNotifications({ detail: { notifications: notificationService.getAll() } }); } }).catch((error) => { console.error('[DsNotificationCenter] Failed to initialize notifications:', error); }); this.shadowRoot.getElementById('mark-all-read').addEventListener('click', () => { notificationService.markAllAsRead(); }); this.shadowRoot.getElementById('clear-all').addEventListener('click', () => { notificationService.clearAll(); }); this.shadowRoot.getElementById('notification-list').addEventListener('click', this._handleNotificationClick.bind(this)); } disconnectedCallback() { this._isConnected = false; notificationService.removeEventListener('notifications-updated', this._updateNotifications); } _handleNotificationClick(e) { const notificationEl = e.target.closest('.notification'); if (!notificationEl) return; const id = notificationEl.dataset.id; if (!id) return; // Mark as read if it was unread if (notificationEl.classList.contains('unread')) { notificationService.markAsRead(id); } // Handle action button clicks const actionButton = e.target.closest('[data-event]'); if (actionButton) { let payload = {}; try { payload = JSON.parse(actionButton.dataset.payload || '{}'); } catch (e) { console.error('Invalid action payload:', e); } this.dispatchEvent(new CustomEvent('notification-action', { bubbles: true, composed: true, detail: { event: actionButton.dataset.event, payload } })); // Close the notification center this.removeAttribute('open'); } // Handle delete button const deleteButton = e.target.closest('.delete-btn'); if (deleteButton) { e.stopPropagation(); notificationService.delete(id); } } _updateNotifications({ detail }) { const { notifications } = detail; const listEl = this.shadowRoot?.getElementById('notification-list'); // Null safety check - component may be disconnecting if (!listEl) { console.warn('[DsNotificationCenter] Notification list element not found'); return; } if (!notifications || notifications.length === 0) { listEl.innerHTML = `
No notifications yet
You're all caught up!${this._escapeHtml(n.title)}
${n.message ? `` : ''} ${actionsHtml ? `