Skip to content

feat: add notification bell in nav bar with unread badge and dropdown… - #740

Merged
Chucks1093 merged 2 commits into
accesslayerorg:devfrom
Mona-i:feat/notification-bell-720
Jul 30, 2026
Merged

feat: add notification bell in nav bar with unread badge and dropdown…#740
Chucks1093 merged 2 commits into
accesslayerorg:devfrom
Mona-i:feat/notification-bell-720

Conversation

@Mona-i

@Mona-i Mona-i commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Closes #720

  • Add NotificationBell component (src/components/common/NotificationBell.tsx)

    • Bell icon in the nav bar using lucide-react Bell icon
    • Unread count badge (red, capped at 99+) visible only when count > 0
    • Radix DropdownMenu shows up to 5 most recent notifications
    • Each item shows message, relative time (via formatRelativeTime), and an unread indicator dot
    • Clicking an item calls markAsRead() then navigates to notification.href
    • View all link at the bottom navigates to /notifications
    • Accessible aria-label includes unread count when present
    • Loading skeleton rows while query is in flight
    • Empty state when no notifications exist
  • Add notification.service.ts (src/services/notification.service.ts)

    • Extends BaseApiService following existing service pattern
    • getNotifications(userId) - GET /notifications
    • markAsRead(notificationId) - PATCH /notifications/:id/read
    • Notification, NotificationType, NotificationsResponse types exported
  • Add useNotifications hook (src/hooks/useNotifications.ts)

    • Wraps useQuery for data fetching (queryFn injected for testability)
    • Wraps useMutation for markAsRead with optimistic update: flips read flag and decrements unreadCount immediately, rolls back on error, invalidates on settled
    • Caps recent array at 5 items for dropdown display
    • Disabled when userId is empty
  • Add notifications query key family (src/lib/queryKeys.ts)

    • queryKeys.notifications.all()
    • queryKeys.notifications.list(userId)
  • Update Header (src/components/home/Header.tsx)

    • Import NotificationBell and useProfileStore
    • Render when profile is set, placed left of WalletStatusChip in a flex gap-2 wrapper
  • Add /notifications route (src/routes.tsx + src/pages/NotificationsPage.tsx)

    • Full-page notifications list matching View all destination
    • Uses the same useNotifications hook and formatRelativeTime
  • Add tests

    • src/hooks/tests/useNotifications.test.ts: loading state, empty, data resolved, 5-item cap, disabled when no userId, optimistic update
    • src/components/common/tests/NotificationBell.test.tsx: badge visibility, 99+ cap, accessible labels, dropdown open, empty state, skeleton, 5 items rendered, message text, markAsRead called on click, View all link, badge decrement after read

Summary

Testing

  • pnpm lint
  • pnpm build

Checklist

  • Linked issue or backlog item
  • Scope is limited to the stated change
  • Updated docs if behavior or setup changed
  • Added screenshots for UI changes when relevant

Mona-i added 2 commits July 29, 2026 14:46
…ccesslayerorg#720

- Add NotificationBell component (src/components/common/NotificationBell.tsx)
  - Bell icon in the nav bar using lucide-react Bell icon
  - Unread count badge (red, capped at 99+) visible only when count > 0
  - Radix DropdownMenu shows up to 5 most recent notifications
  - Each item shows message, relative time (via formatRelativeTime), and
    an unread indicator dot
  - Clicking an item calls markAsRead() then navigates to notification.href
  - View all link at the bottom navigates to /notifications
  - Accessible aria-label includes unread count when present
  - Loading skeleton rows while query is in flight
  - Empty state when no notifications exist

- Add notification.service.ts (src/services/notification.service.ts)
  - Extends BaseApiService following existing service pattern
  - getNotifications(userId) - GET /notifications
  - markAsRead(notificationId) - PATCH /notifications/:id/read
  - Notification, NotificationType, NotificationsResponse types exported

- Add useNotifications hook (src/hooks/useNotifications.ts)
  - Wraps useQuery for data fetching (queryFn injected for testability)
  - Wraps useMutation for markAsRead with optimistic update:
    flips read flag and decrements unreadCount immediately, rolls back
    on error, invalidates on settled
  - Caps recent array at 5 items for dropdown display
  - Disabled when userId is empty

- Add notifications query key family (src/lib/queryKeys.ts)
  - queryKeys.notifications.all()
  - queryKeys.notifications.list(userId)

- Update Header (src/components/home/Header.tsx)
  - Import NotificationBell and useProfileStore
  - Render <NotificationBell userId={profile.id} /> when profile is set,
    placed left of WalletStatusChip in a flex gap-2 wrapper

- Add /notifications route (src/routes.tsx + src/pages/NotificationsPage.tsx)
  - Full-page notifications list matching View all destination
  - Uses the same useNotifications hook and formatRelativeTime

- Add tests
  - src/hooks/__tests__/useNotifications.test.ts: loading state, empty,
    data resolved, 5-item cap, disabled when no userId, optimistic update
  - src/components/common/__tests__/NotificationBell.test.tsx: badge
    visibility, 99+ cap, accessible labels, dropdown open, empty state,
    skeleton, 5 items rendered, message text, markAsRead called on click,
    View all link, badge decrement after read
@Chucks1093
Chucks1093 merged commit e5a28b7 into accesslayerorg:dev Jul 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a notification bell in the nav bar showing unread count and a dropdown of recent notifications

2 participants