Skip to content

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

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

    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