diff --git a/db/data/006_notifications_seed.sql b/db/data/006_notifications_seed.sql index a770ae5..2f13bf9 100644 --- a/db/data/006_notifications_seed.sql +++ b/db/data/006_notifications_seed.sql @@ -1,26 +1,40 @@ -INSERT INTO notifications (user_id, type, level, channel, title, message, payload, is_read, created_at) VALUES --- Student (user_id=10) notifications -(10, 'course_created', 'info', 'in_app', 'New Course Available', 'A new course "Algebra Fundamentals" has been added. Check it out!', '{"course_title": "Algebra Fundamentals", "category": "Mathematics"}', false, now() - interval '30 days'), -(10, 'course_created', 'info', 'in_app', 'New Course Available', 'A new course "English Grammar 101" has been added. Check it out!', '{"course_title": "English Grammar 101", "category": "Language"}', false, now() - interval '25 days'), -(10, 'sub_course_created', 'info', 'in_app', 'New Content Available', 'A new sub-course "Linear Equations" has been added.', '{"sub_course_title": "Linear Equations", "course": "Algebra Fundamentals"}', false, now() - interval '24 days'), -(10, 'video_added', 'info', 'in_app', 'New Video Available', 'A new video "Introduction to Variables" has been added.', '{"video_title": "Introduction to Variables", "sub_course": "Linear Equations"}', false, now() - interval '23 days'), -(10, 'payment_verified', 'success', 'in_app', 'Payment Successful', 'Your payment has been verified successfully. Your subscription is now active.', '{"plan": "Premium Monthly", "amount": 500}', true, now() - interval '20 days'), -(10, 'subscription_activated', 'success', 'in_app', 'Subscription Activated', 'Your Premium Monthly subscription is now active until March 20, 2026.', '{"plan": "Premium Monthly", "expires": "2026-03-20"}', true, now() - interval '20 days'), -(10, 'knowledge_level_update', 'info', 'in_app', 'Knowledge Level Updated', 'Your knowledge level has been updated to: Intermediate', '{"previous_level": "Beginner", "new_level": "Intermediate"}', false, now() - interval '15 days'), -(10, 'issue_status_updated', 'info', 'in_app', 'Issue Status Updated', 'Your issue "Video not loading on mobile" has been updated to: in_progress', '{"issue_id": 1, "subject": "Video not loading on mobile", "status": "in_progress"}', true, now() - interval '12 days'), -(10, 'issue_status_updated', 'success', 'in_app', 'Issue Status Updated', 'Your issue "Cannot change profile picture" has been updated to: resolved', '{"issue_id": 3, "subject": "Cannot change profile picture", "status": "resolved"}', true, now() - interval '10 days'), -(10, 'course_enrolled', 'success', 'in_app', 'Course Enrolled', 'You have been enrolled in "Biology 101".', '{"course_title": "Biology 101"}', false, now() - interval '8 days'), -(10, 'assessment_assigned', 'info', 'in_app', 'New Assessment Available', 'A new assessment is available for "Algebra Fundamentals".', '{"course": "Algebra Fundamentals", "assessment_type": "quiz"}', false, now() - interval '5 days'), -(10, 'announcement', 'info', 'in_app', 'Platform Maintenance', 'Scheduled maintenance on Feb 15, 2026 from 2:00 AM - 4:00 AM EAT.', '{"scheduled_at": "2026-02-15T02:00:00+03:00", "duration_hours": 2}', false, now() - interval '2 days'), -(10, 'video_added', 'info', 'in_app', 'New Video Available', 'A new video "Solving Quadratic Equations" has been added.', '{"video_title": "Solving Quadratic Equations", "sub_course": "Quadratics"}', false, now() - interval '1 day'), +INSERT INTO notifications ( + id, user_id, receiver_type, type, level, channel, title, message, payload, is_read, created_at +) VALUES +-- Learner notifications (receiver_type=user, user_id=10) +(1001, 10, 'user', 'course_created', 'info', 'in_app', 'New Course Available', 'A new course "Algebra Fundamentals" has been added. Check it out!', '{"course_title": "Algebra Fundamentals", "category": "Mathematics"}', false, now() - interval '30 days'), +(1002, 10, 'user', 'course_created', 'info', 'in_app', 'New Course Available', 'A new course "English Grammar 101" has been added. Check it out!', '{"course_title": "English Grammar 101", "category": "Language"}', false, now() - interval '25 days'), +(1003, 10, 'user', 'sub_course_created', 'info', 'in_app', 'New Content Available', 'A new sub-course "Linear Equations" has been added.', '{"sub_course_title": "Linear Equations", "course": "Algebra Fundamentals"}', false, now() - interval '24 days'), +(1004, 10, 'user', 'video_added', 'info', 'in_app', 'New Video Available', 'A new video "Introduction to Variables" has been added.', '{"video_title": "Introduction to Variables", "sub_course": "Linear Equations"}', false, now() - interval '23 days'), +(1005, 10, 'user', 'payment_verified', 'success', 'in_app', 'Payment Successful', 'Your payment has been verified successfully. Your subscription is now active.', '{"plan": "Premium Monthly", "amount": 500}', true, now() - interval '20 days'), +(1006, 10, 'user', 'subscription_activated', 'success', 'in_app', 'Subscription Activated', 'Your Premium Monthly subscription is now active until March 20, 2026.', '{"plan": "Premium Monthly", "expires": "2026-03-20"}', true, now() - interval '20 days'), +(1007, 10, 'user', 'knowledge_level_update', 'info', 'in_app', 'Knowledge Level Updated', 'Your knowledge level has been updated to: Intermediate', '{"previous_level": "Beginner", "new_level": "Intermediate"}', false, now() - interval '15 days'), +(1008, 10, 'user', 'issue_status_updated', 'info', 'in_app', 'Issue Status Updated', 'Your issue "Video not loading on mobile" has been updated to: in_progress', '{"issue_id": 1, "subject": "Video not loading on mobile", "status": "in_progress"}', true, now() - interval '12 days'), +(1009, 10, 'user', 'issue_status_updated', 'success', 'in_app', 'Issue Status Updated', 'Your issue "Cannot change profile picture" has been updated to: resolved', '{"issue_id": 3, "subject": "Cannot change profile picture", "status": "resolved"}', true, now() - interval '10 days'), +(1010, 10, 'user', 'course_enrolled', 'success', 'in_app', 'Course Enrolled', 'You have been enrolled in "Biology 101".', '{"course_title": "Biology 101"}', false, now() - interval '8 days'), +(1011, 10, 'user', 'assessment_assigned', 'info', 'in_app', 'New Assessment Available', 'A new assessment is available for "Algebra Fundamentals".', '{"course": "Algebra Fundamentals", "assessment_type": "quiz"}', false, now() - interval '5 days'), +(1012, 10, 'user', 'announcement', 'info', 'in_app', 'Platform Maintenance', 'Scheduled maintenance on Feb 15, 2026 from 2:00 AM - 4:00 AM EAT.', '{"scheduled_at": "2026-02-15T02:00:00+03:00", "duration_hours": 2}', false, now() - interval '2 days'), +(1013, 10, 'user', 'video_added', 'info', 'in_app', 'New Video Available', 'A new video "Solving Quadratic Equations" has been added.', '{"video_title": "Solving Quadratic Equations", "sub_course": "Quadratics"}', false, now() - interval '1 day'), --- Admin (user_id=12) notifications -(12, 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Video not loading on mobile" has been reported.', '{"issue_id": 1, "subject": "Video not loading on mobile", "reporter_id": 10}', false, now() - interval '14 days'), -(12, 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Payment confirmation not received" has been reported.', '{"issue_id": 2, "subject": "Payment confirmation not received", "reporter_id": 10}', false, now() - interval '10 days'), -(12, 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Quiz results not saving" has been reported.', '{"issue_id": 5, "subject": "Quiz results not saving", "reporter_id": 10}', false, now() - interval '5 days'), -(12, 'user_deleted', 'warning', 'in_app', 'User Deleted', 'User ID 99 has been deleted.', '{"deleted_user_id": 99, "deleted_by": 12}', true, now() - interval '18 days'), -(12, 'admin_created', 'info', 'in_app', 'New Admin Created', 'A new admin account has been created for admin@yimaru.com.', '{"admin_email": "admin@yimaru.com"}', true, now() - interval '28 days'), -(12, 'team_member_created', 'info', 'in_app', 'New Team Member', 'A new team member has been added.', '{"member_email": "support@yimaru.com", "role": "support"}', true, now() - interval '26 days'), -(12, 'system_alert', 'warning', 'in_app', 'High Error Rate Detected', 'The notification delivery failure rate exceeded 5% in the last hour.', '{"failure_rate": 5.2, "window": "1h"}', false, now() - interval '3 days'), -(12, 'announcement', 'info', 'in_app', 'New Student Registrations', '15 new students registered this week.', '{"count": 15, "period": "weekly"}', false, now() - interval '1 day') -ON CONFLICT DO NOTHING; +-- Team member notifications (receiver_type=team_member, user_id references team_members.id) +(1014, 2, 'team_member', 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Video not loading on mobile" has been reported.', '{"issue_id": 1, "subject": "Video not loading on mobile", "reporter_id": 10}', false, now() - interval '14 days'), +(1015, 2, 'team_member', 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Payment confirmation not received" has been reported.', '{"issue_id": 2, "subject": "Payment confirmation not received", "reporter_id": 10}', false, now() - interval '10 days'), +(1016, 2, 'team_member', 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Quiz results not saving" has been reported.', '{"issue_id": 5, "subject": "Quiz results not saving", "reporter_id": 10}', false, now() - interval '5 days'), +(1017, 2, 'team_member', 'user_deleted', 'warning', 'in_app', 'User Deleted', 'User ID 99 has been deleted.', '{"deleted_user_id": 99, "deleted_by": 2}', true, now() - interval '18 days'), +(1018, 2, 'team_member', 'admin_created', 'info', 'in_app', 'New Admin Created', 'A new admin account has been created for admin@yimaru.com.', '{"admin_email": "admin@yimaru.com"}', true, now() - interval '28 days'), +(1019, 2, 'team_member', 'team_member_created','info', 'in_app', 'New Team Member', 'A new team member has been added.', '{"member_email": "support@yimaru.com", "role": "support"}', true, now() - interval '26 days'), +(1020, 2, 'team_member', 'system_alert', 'warning', 'in_app', 'High Error Rate Detected', 'The notification delivery failure rate exceeded 5% in the last hour.', '{"failure_rate": 5.2, "window": "1h"}', false, now() - interval '3 days'), +(1021, 3, 'team_member', 'announcement', 'info', 'in_app', 'Weekly Registration Report','15 new students registered this week.', '{"count": 15, "period": "weekly"}', false, now() - interval '1 day') +ON CONFLICT (id) DO NOTHING; + +-- Scheduled notifications seeds (created_by references users.id) +INSERT INTO scheduled_notifications ( + id, channel, title, message, html, scheduled_at, status, target_user_ids, target_role, target_raw, + attempt_count, last_error, processing_started_at, sent_at, cancelled_at, created_by, created_at, updated_at +) VALUES +(2001, 'push', 'Reminder: Continue Your Lesson', 'Pick up where you left off and continue learning today.', NULL, now() + interval '6 hours', 'pending', ARRAY[10,11], NULL, NULL, 0, NULL, NULL, NULL, NULL, 10, now() - interval '1 day', now() - interval '1 day'), +(2002, 'email', 'Weekly Progress Summary', 'Your weekly course progress summary is ready.', '
Your weekly course progress summary is ready.
', now() + interval '1 day', 'pending', NULL, 'STUDENT', NULL, 0, NULL, NULL, NULL, NULL, 10, now() - interval '1 day', now() - interval '1 day'), +(2003, 'sms', 'Platform Maintenance', 'Scheduled maintenance tonight from 02:00 to 04:00 EAT.', NULL, now() - interval '2 days', 'sent', ARRAY[10,12], NULL, NULL, 1, NULL, now() - interval '2 days' - interval '5 minutes', now() - interval '2 days', NULL, 10, now() - interval '3 days', now() - interval '2 days'), +(2004, 'email', 'Payment Service Alert', 'Some users may experience delayed payment confirmation.', 'Some users may experience delayed payment confirmation.
', now() - interval '1 day', 'failed', NULL, 'SUPPORT', NULL, 3, 'SMTP temporary outage', now() - interval '1 day' - interval '15 minutes', NULL, NULL, 10, now() - interval '2 days', now() - interval '1 day'), +(2005, 'push', 'Obsolete Campaign', 'This campaign was cancelled by admin.', NULL, now() + interval '2 days', 'cancelled', NULL, NULL, '{"segment":"inactive_users"}'::jsonb, 0, NULL, NULL, NULL, now() - interval '12 hours', 10, now() - interval '1 day', now() - interval '12 hours') +ON CONFLICT (id) DO NOTHING; diff --git a/docker-compose.yml b/docker-compose.yml index 48f375d..452423e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,7 +81,7 @@ services: dockerfile: Dockerfile target: runner ports: - - "${PORT}:8080" + - "${PORT}:${PORT}" environment: - DB_URL=postgresql://root:secret@postgres:5432/gh?sslmode=disable - MONGO_URI=mongodb://root:secret@mongo:27017 diff --git a/docs/LEARNER_PROGRESS_TRACKER_ADMIN_INTEGRATION.md b/docs/LEARNER_PROGRESS_TRACKER_ADMIN_INTEGRATION.md new file mode 100644 index 0000000..5580e8a --- /dev/null +++ b/docs/LEARNER_PROGRESS_TRACKER_ADMIN_INTEGRATION.md @@ -0,0 +1,167 @@ +# Learner Progress Tracker Admin Integration Guide + +This guide explains how to integrate learner sub-course progress tracking into the admin panel using the backend endpoint implemented for admin usage. + +## Scope + +- Track a specific learner's progress across all sub-courses inside a course. +- Show lock state, progress percentage, and completion timestamps. +- Integrate as a read-focused admin experience. + +## New Admin Endpoint + +- **Method:** `GET` +- **Path:** `/api/v1/admin/users/:userId/progress/courses/:courseId` +- **Auth:** Bearer token +- **Required permission:** `progress.get_any_user` + +### Path Parameters + +- `userId` (number): target learner user ID +- `courseId` (number): course ID + +### Success Response (`200`) + +```json +{ + "message": "Learner course progress retrieved successfully", + "data": [ + { + "sub_course_id": 11, + "title": "Beginner Conversation Basics", + "description": "Foundational speaking patterns", + "thumbnail": "https://cdn.example.com/sc-11.png", + "display_order": 1, + "level": "BEGINNER", + "progress_status": "IN_PROGRESS", + "progress_percentage": 45, + "started_at": "2026-03-07T09:10:11Z", + "completed_at": null, + "is_locked": false + }, + { + "sub_course_id": 12, + "title": "Beginner Listening Drills", + "description": "Daily listening practice", + "thumbnail": null, + "display_order": 2, + "level": "BEGINNER", + "progress_status": "NOT_STARTED", + "progress_percentage": 0, + "started_at": null, + "completed_at": null, + "is_locked": true + } + ] +} +``` + +### Error Responses + +- `400`: invalid `userId` or `courseId` +- `401`: missing/invalid token +- `403`: missing `progress.get_any_user` +- `500`: server/database issue + +## Data Semantics + +- `progress_status` values: + - `NOT_STARTED` + - `IN_PROGRESS` + - `COMPLETED` +- `progress_percentage` is `0..100` +- `is_locked` is computed from unmet sub-course prerequisites for that learner +- list is ordered by `display_order` +- only active sub-courses are included + +## Backend Rollout Steps + +After pulling this backend change: + +1. Restart backend service. +2. Sync permissions: + - `POST /api/v1/rbac/permissions/sync` +3. Ensure admin role includes `progress.get_any_user`. + - If your system uses explicit role-permission assignment, update role permissions after sync. + +## Admin Panel Integration Flow + +1. User opens learner progress screen. +2. Admin selects learner and course. +3. Frontend requests: + - `GET /api/v1/admin/users/{userId}/progress/courses/{courseId}` +4. Render returned `data` as ordered progress items. + +## Recommended UI Sections + +- Header: + - learner identity + - selected course +- Metrics row: + - total sub-courses + - completed count + - in-progress count + - locked count + - average progress percentage +- Ordered list/table: + - sub-course title + - level + - status badge + - progress bar + - locked icon + - started/completed timestamps + +## Frontend Mapping Example + +For each item: + +- `statusLabel = progress_status` +- `isCompleted = progress_status === "COMPLETED"` +- `isInProgress = progress_status === "IN_PROGRESS"` +- `isNotStarted = progress_status === "NOT_STARTED"` +- `canOpenDetails = !is_locked` + +## Suggested API Client Contract + +```ts +type LearnerCourseProgressItem = { + sub_course_id: number; + title: string; + description?: string | null; + thumbnail?: string | null; + display_order: number; + level: string; + progress_status: "NOT_STARTED" | "IN_PROGRESS" | "COMPLETED"; + progress_percentage: number; + started_at?: string | null; + completed_at?: string | null; + is_locked: boolean; +}; + +type LearnerCourseProgressResponse = { + message: string; + data: LearnerCourseProgressItem[]; +}; +``` + +## Example Request + +```bash +curl -X GET "http://localhost:8432/api/v1/admin/users/7/progress/courses/3" \ + -H "Authorization: Bearer