diff --git a/src/dashboard.html b/src/dashboard.html index 36ed338..6f1c673 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -640,6 +640,10 @@ h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;
+
+

Notifications

+ +
Loading…
@@ -2076,31 +2080,86 @@ async function toggleEvent(eventId, activate, btn) { // ── Notifications ───────────────────────────────────────────────────────────── +const NOTIF_ICONS = { + level_up: '⬆', + objective_complete: '✓', + loan_expiring: '⏳', + loan_expired: '✕', + season_end: '🏆', + season_ending: '⌛', +}; + +function notifKindLabel(type) { + return { + level_up: 'Level Up', + objective_complete: 'Objective', + loan_expiring: 'Loan Warning', + loan_expired: 'Loan Expired', + season_end: 'Season', + season_ending: 'Season', + }[type] || type; +} + async function loadNotifications() { try { const data = await api('GET', '/notifications'); const notifs = data.notifications || []; - el('notif-badge').textContent = notifs.length > 0 ? ` (${notifs.length})` : ''; + const unread = data.unread_count ?? notifs.filter(n => !n.is_read).length; + el('notif-badge').textContent = unread > 0 ? ` (${unread})` : ''; + if (el('notif-unread-label')) { + el('notif-unread-label').textContent = unread > 0 ? `${unread} unread` : 'all read'; + } if (!notifs.length) { - el('notif-list').innerHTML = 'No notifications right now.'; + el('notif-list').innerHTML = 'No notifications yet. Play matches, complete objectives, and level up to generate notifications.'; return; } el('notif-list').innerHTML = notifs.map(n => ` -
-
${n.title}
-
${n.message}
+
+ ${NOTIF_ICONS[n.type] || '•'} +
+
+
${n.title}
+ ${notifKindLabel(n.type)} +
+
${n.body || n.message || ''}
+ ${n.created_at ? `
${new Date(n.created_at).toLocaleString()}
` : ''} +
+ ${n.id && !n.is_read ? `` : ''}
`).join(''); } catch (e) { el('notif-list').innerHTML = `
${e.message}
`; } } +function notifBorderColor(type) { + return { level_up: '#f5a623', objective_complete: '#3fb950', loan_expiring: '#d29922', loan_expired: '#f85149', season_end: '#58a6ff', season_ending: '#d29922' }[type] || '#30363d'; +} + +async function markNotificationRead(id, btn) { + btn.disabled = true; + try { + await api('PATCH', `/notifications/${id}/read`); + loadNotifications(); + } catch (e) { showToast(e.message, true); btn.disabled = false; } +} + +async function markAllNotificationsRead() { + try { + await api('POST', '/notifications/read-all'); + showToast('All notifications marked as read'); + loadNotifications(); + } catch (e) { showToast(e.message, true); } +} + // ── Init ───────────────────────────────────────────────────────────────────── window.addEventListener('DOMContentLoaded', () => { loadClub(); // Load notification badge count silently api('GET', '/notifications').then(d => { - const n = (d.notifications || []).length; - el('notif-badge').textContent = n > 0 ? ` (${n})` : ''; + const unread = d.unread_count ?? (d.notifications || []).filter(n => !n.is_read).length; + el('notif-badge').textContent = unread > 0 ? ` (${unread})` : ''; }).catch(() => {}); });