Skip to content

[FRONTEND] Build notifications page and navbar unread badge #281

@smattymatty

Description

@smattymatty

Claiming This Task

Before you start working, check the Assignees section. If no one is assigned, leave a comment claiming the issue and assign it to yourself. This prevents duplicate work.

See the Community Wiki for contributing guidelines and git workflow.

Problem

The navbar has a notification bell icon that links to /notifications, but:

  • The route doesn't exist in App.jsx (clicking the bell 404s)
  • Notifications.jsx page component is empty
  • There's no unread count badge on the bell icon
  • No frontend service exists to call the notification API

The backend notification API endpoints must be built first (see dependency below).

Requirements

1. Notification API service (src/services/notificationsApi.ts)

  • listNotifications(params?) — GET /api/notifications/ (paginated, supports is_read and notification_type filters)
  • getUnreadCount() — GET /api/notifications/unread-count/ returns { count: number }
  • markAsRead(id) — PATCH /api/notifications/<id>/ with { is_read: true }
  • markAllAsRead() — POST /api/notifications/mark-all-read/
  • deleteNotification(id) — DELETE /api/notifications/<id>/
  • Follow existing coursesApi.ts pattern (axios, getSafeErrorMessage, timeouts)

2. Notification types (src/types/notifications.types.ts)

  • Notification type matching the backend NotificationSerializer response
  • NotificationListParams for filtering
  • Paginated response type (standard DRF pagination)

3. Navbar unread badge (src/components/Navbar.tsx)

  • Poll getUnreadCount() on an interval (every 30–60 seconds) when user is authenticated
  • Display a red badge with the count on the bell icon (e.g. red circle with "3")
  • Hide badge when count is 0
  • Refresh count after marking notifications as read

4. Notifications page (src/pages/Notifications.tsx)

  • Convert from .jsx to .tsx
  • List notifications with pagination (paginated list, newest first)
  • Each notification item shows:
    • Actor avatar/name (if available)
    • Verb text (e.g. "invited you to the course 'Physics 101'")
    • Timestamp (relative, e.g. "2 hours ago")
    • Read/unread visual distinction (e.g. unread has a blue dot or bolder text)
    • Click target link to navigate to the relevant item (course page, friend request, chat room, etc.)
  • "Mark all as read" button in the header
  • Individual mark-as-read on click or via a button
  • Delete individual notifications
  • Empty state when no notifications
  • Mobile responsive, dark mode support, RTL support

5. Route registration (App.jsx or App.tsx)

  • Add /notifications route pointing to the new Notifications page
  • Should be a protected route (authenticated users only)

6. i18n

  • Add all notification page strings to en.json and ar.json
  • Strings needed: page title, mark all read, delete, empty state, time-relative labels, notification type labels, etc.

Architecture Context

  • Navbar: src/components/Navbar.tsx — bell icon is at line ~73, currently a plain <Link> to /notifications
  • Empty page: src/pages/Notifications.jsx — blank file
  • Auth context: src/contexts/AuthContext.tsx — use useAuth() to check if user is logged in before polling
  • Routing: App.jsx defines routes — protected routes use the auth context
  • Existing patterns:
    • List pages: CourseListPage.tsx, SlideshowListPage.tsx for pagination and empty state patterns
    • API services: coursesApi.ts, slideshowApi.ts for the service pattern
    • Types: courses.types.ts for type definition pattern
    • i18n: en.json/ar.json for translation structure

API Dependencies

Depends on: Backend notification API endpoints issue (must be completed first)

Expected backend endpoints:

Method URL Response
GET /api/notifications/ Paginated notification list
GET /api/notifications/unread-count/ { "count": N }
PATCH /api/notifications/<id>/ Updated notification
POST /api/notifications/mark-all-read/ Success response
DELETE /api/notifications/<id>/ 204 No Content

The backend NotificationSerializer returns: id, actor_details (id, username, first_name, last_name), verb, notification_type, is_read, created_at, target_details (type, id, display).

Files to be Altered

File Change
src/types/notifications.types.ts NEW — notification type definitions
src/services/notificationsApi.ts NEW — API service
src/pages/Notifications.tsx REWRITE — full notifications page (rename from .jsx)
src/components/Navbar.tsx MODIFY — add unread count badge + polling
App.jsx (or App.tsx) MODIFY — add /notifications route
src/i18n/locales/en.json MODIFY — add notification i18n keys
src/i18n/locales/ar.json MODIFY — add Arabic translations
src/services/__tests__/notificationsApi.test.ts NEW — API service tests
src/pages/__tests__/Notifications.test.tsx NEW — page tests

Testing Requirements

Automated tests

  • API service tests: List, unread count, mark read, mark all read, delete — success and error cases
  • Notifications page tests: Renders list, pagination, mark as read, mark all read, delete, empty state, loading state
  • Navbar badge: Verify badge shows correct count, hides at 0, updates after marking read

Manual testing

  • Verify notifications appear after receiving a friend request, chat invite, or course invitation
  • Click a notification — should navigate to the relevant page
  • Mark as read — visual change + count updates
  • Mark all as read — all notifications change, badge disappears
  • Delete a notification — removed from list
  • Test on mobile (375px, 390px), tablet (768px), desktop (1024px+)
  • Test in dark mode
  • Test in Arabic (RTL)

Additional Context (Optional)

  • The unread count polling approach is chosen over WebSockets for simplicity — EduLite targets areas with weak internet where persistent connections are unreliable. A 30-60s poll interval is a reasonable tradeoff.
  • If the backend adds WebSocket notification support later (Django Channels infrastructure already exists for chat), the polling can be replaced with a subscription.
  • The target_details field from the serializer can be used to build navigation links (e.g. type: "courses.coursemembership" + id: 5 → link to /courses/<course_id>). The exact link mapping will need to handle each notification type.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions