feat: add notification bell polling to dashboard shell

- Poll /api/me/notifications/unread-count every 30 seconds
- Show orange dot badge when unread count > 0
- Matches PRD requirement for real-time notifications
This commit is contained in:
Ashwin Kumar 2026-04-06 18:23:12 +02:00
parent bbf11b91e1
commit eee2c367ca

View file

@ -3,7 +3,7 @@
* Used for pages that need actual backend connectivity
* (My Profile, My Portfolio, Verification) instead of the preview mock.
*/
import { For, JSX, Show, createMemo } from 'solid-js';
import { For, JSX, Show, createMemo, createSignal, onMount } from 'solid-js';
import {
User, Briefcase, LayoutDashboard, FolderOpen, MapPin, Star,
CreditCard, Globe, ShieldCheck, HelpCircle, Settings,
@ -65,6 +65,33 @@ export default function DashboardShell(props: Props) {
return k.charAt(0).toUpperCase() + k.slice(1).toLowerCase();
});
const [unreadCount, setUnreadCount] = createSignal(0);
// Fetch unread notification count
const fetchUnreadCount = async () => {
try {
const token = typeof window !== 'undefined' ? window.sessionStorage.getItem('nxtgauge_access_token') || '' : '';
if (!token) return;
const res = await fetch('/api/me/notifications/unread-count', {
headers: { Authorization: `Bearer ${token}` },
credentials: 'include',
});
if (res.ok) {
const data = await res.json();
setUnreadCount(data.unread_count || 0);
}
} catch (e) {
console.error('Failed to fetch unread count:', e);
}
};
// Start polling on mount
onMount(() => {
fetchUnreadCount();
const interval = setInterval(fetchUnreadCount, 30000);
return () => clearInterval(interval);
});
return (
<div style={{
display: 'flex',
@ -160,17 +187,31 @@ export default function DashboardShell(props: Props) {
<p style={{ margin: '0', 'font-size': '15px', 'font-weight': '700', color: NAVY }}>
{props.activeSidebar}
</p>
<div style={{ display: 'flex', 'align-items': 'center', gap: '12px' }}>
<Bell size={18} style={{ color: '#9CA3AF' }} />
<div style={{
width: '32px', height: '32px', 'border-radius': '999px',
background: ORANGE, color: '#fff', display: 'flex',
'align-items': 'center', 'justify-content': 'center',
'font-size': '13px', 'font-weight': '700',
}}>
{(props.userName || 'U').charAt(0).toUpperCase()}
</div>
</div>
<div style={{ display: 'flex', 'align-items': 'center', gap: '12px' }}>
<button type="button" style={{ position: 'relative', border: 'none', background: 'transparent', cursor: 'pointer', display: 'flex', 'align-items': 'center', 'justify-content': 'center', padding: 0 }}>
<Bell size={18} style={{ color: '#9CA3AF' }} />
<Show when={unreadCount() > 0}>
<span style={{
position: 'absolute',
top: '-2px',
right: '-2px',
width: '8px',
height: '8px',
background: '#FF5E13',
'border-radius': '50%',
border: '1px solid white'
}}></span>
</Show>
</button>
<div style={{
width: '32px', height: '32px', 'border-radius': '999px',
background: ORANGE, color: '#fff', display: 'flex',
'align-items': 'center', 'justify-content': 'center',
'font-size': '13px', 'font-weight': '700',
}}>
{(props.userName || 'U').charAt(0).toUpperCase()}
</div>
</div>
</header>
{/* Page content */}