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.
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:App.jsx(clicking the bell 404s)Notifications.jsxpage component is emptyThe 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>/coursesApi.tspattern (axios,getSafeErrorMessage, timeouts)2. Notification types (
src/types/notifications.types.ts)Notificationtype matching the backendNotificationSerializerresponseNotificationListParamsfor filtering3. Navbar unread badge (
src/components/Navbar.tsx)getUnreadCount()on an interval (every 30–60 seconds) when user is authenticated4. Notifications page (
src/pages/Notifications.tsx).jsxto.tsx5. Route registration (
App.jsxorApp.tsx)/notificationsroute pointing to the new Notifications page6. i18n
en.jsonandar.jsonArchitecture Context
src/components/Navbar.tsx— bell icon is at line ~73, currently a plain<Link>to/notificationssrc/pages/Notifications.jsx— blank filesrc/contexts/AuthContext.tsx— useuseAuth()to check if user is logged in before pollingApp.jsxdefines routes — protected routes use the auth contextCourseListPage.tsx,SlideshowListPage.tsxfor pagination and empty state patternscoursesApi.ts,slideshowApi.tsfor the service patterncourses.types.tsfor type definition patternen.json/ar.jsonfor translation structureAPI Dependencies
Depends on: Backend notification API endpoints issue (must be completed first)
Expected backend endpoints:
/api/notifications//api/notifications/unread-count/{ "count": N }/api/notifications/<id>//api/notifications/mark-all-read//api/notifications/<id>/The backend
NotificationSerializerreturns: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
src/types/notifications.types.tssrc/services/notificationsApi.tssrc/pages/Notifications.tsxsrc/components/Navbar.tsxApp.jsx(orApp.tsx)/notificationsroutesrc/i18n/locales/en.jsonsrc/i18n/locales/ar.jsonsrc/services/__tests__/notificationsApi.test.tssrc/pages/__tests__/Notifications.test.tsxTesting Requirements
Automated tests
Manual testing
Additional Context (Optional)
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.