-
Notifications
You must be signed in to change notification settings - Fork 20
[FRONTEND] Build notifications page and navbar unread badge #281
Copy link
Copy link
Open
Labels
Description
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.jsxpage 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, supportsis_readandnotification_typefilters)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.tspattern (axios,getSafeErrorMessage, timeouts)
2. Notification types (src/types/notifications.types.ts)
Notificationtype matching the backendNotificationSerializerresponseNotificationListParamsfor 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
.jsxto.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
/notificationsroute pointing to the new Notifications page - Should be a protected route (authenticated users only)
6. i18n
- Add all notification page strings to
en.jsonandar.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— useuseAuth()to check if user is logged in before polling - Routing:
App.jsxdefines routes — protected routes use the auth context - Existing patterns:
- List pages:
CourseListPage.tsx,SlideshowListPage.tsxfor pagination and empty state patterns - API services:
coursesApi.ts,slideshowApi.tsfor the service pattern - Types:
courses.types.tsfor type definition pattern - i18n:
en.json/ar.jsonfor translation structure
- List pages:
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_detailsfield 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.
Reactions are currently unavailable