Skip to content

[Bug] Notification bell shows hardcoded mock data while the real SSE notification system is broken and unused #78

Description

@zeemscript

What's Happening

The notification bell users actually see is fake, and the real notification system sitting next to it is broken and unused.

What's rendered: components/molecules/dashboard/nav-header.jsx imports Notybell from components/atoms/dashboard/Notybell.jsx. That component shows a hardcoded mockNotifications array ("New lesson available", "New message from Ali", …) with a comment // replace with real fetch later, and an always-on red dot badge regardless of whether anything is unread.

What's dead code: a complete real implementation exists — components/molecules/dashboard/Notybell.jsx (NotificationBell, with mark-as-read, mark-all, delete, reconnect UI) backed by hooks/useNotificationSSE.js — but it is imported nowhere, and it cannot work as written:

  1. useNotificationSSE destructures const { token } = useAuth(), but useAuth (hooks/useAuth.js) returns { user, isAuthenticated, loading, logout, refreshUser } — there is no token field. token is always undefined, so fetchNotifications and connectSSE bail immediately.
  2. connectSSE lists itself in its own useCallback dependency array ([token, reconnectAttempts, notifications, connectSSE]). Because connectSSE is a const still in its temporal dead zone when that array is evaluated, mounting the hook throws ReferenceError: Cannot access 'connectSSE' before initialization.
  3. It fetches relative paths (/api/notifications, /api/notifications/sse?token=...) — the Next app only has app/api/ai/* and app/api/books/* routes, so these 404; the backend lives at NEXT_PUBLIC_API_URL.
  4. The JWT is passed as a query-string parameter to the SSE endpoint, which leaks it into server/proxy logs.
  5. Listing notifications in connectSSE's deps means every incoming event tears down and recreates the EventSource.

Where to Find This

  • components/atoms/dashboard/Notybell.jsx — mock bell currently rendered
  • components/molecules/dashboard/Notybell.jsx — unused real bell UI
  • hooks/useNotificationSSE.js — broken hook
  • components/molecules/dashboard/nav-header.jsx — where the bell is mounted
  • hooks/useAuth.js — shape of what useAuth actually returns

What We Want

  1. Fix useNotificationSSE so it can run: get the token from the authToken cookie (js-cookie, as the rest of the app does) instead of the nonexistent useAuth().token; remove the self-referencing dependency; use refs for values the SSE callbacks need (e.g. current notifications) so the connection isn't rebuilt on every event.
  2. Point the fetches at the real backend (process.env.NEXT_PUBLIC_API_URL via axiosInstance for the REST calls; absolute URL for the EventSource). Coordinate with the backend on the SSE endpoint path and note in the issue/PR if it doesn't exist yet — the REST list/mark-read/delete endpoints should still be wired regardless, with the SSE connection degrading gracefully (polling fallback or silent disconnect state).
  3. Replace the mock Notybell in nav-header.jsx with the real NotificationBell, and delete the mock component (or keep it behind a storybook-style demo only).
  4. Badge count must reflect unreadCount (hide the dot at 0), and the existing exponential backoff/reconnect logic should be kept but capped as written.
  5. Use router.push instead of window.location.href for notification navigation in NotificationBell so client-side routing is preserved.

Technical Context

  • EventSource cannot send an Authorization header. If the backend can't read the cookie (CORS + withCredentials on the API origin), a short-lived SSE ticket or keeping the query token may be the pragmatic choice — if so, document the tradeoff in code comments. The current app already sends withCredentials: true on axios.
  • The reducer-ish state updates in the hook (notification_read, all_read, notification_deleted) are sound — reuse them.
  • Test the TDZ fix simply by rendering NotificationBell once; the current code throws on mount.

Acceptance Criteria

  • The header bell shows real notifications from the backend for the logged-in user; no mockNotifications remain in the tree.
  • Mounting the bell never throws (the TDZ crash is gone — covered by simply rendering it in dev).
  • Unread badge shows the actual unread count and disappears at zero.
  • Mark-as-read, mark-all-as-read, and delete hit the real API and update the UI optimistically with rollback on failure (logic already present — must work end-to-end).
  • SSE disconnects reconnect with backoff, max 5 attempts, and a manual "reconnect" affordance appears after exhaustion.
  • npm run lint and npm run build pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcomplexity:highMaps to Drips Wave High tier (200 pts)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions