# 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 " ``` ## Operational Notes - This endpoint is intended for admin/super-admin workflows. - Existing learner self endpoint remains available: - `GET /api/v1/progress/courses/:courseId` - Do not expose `progress.get_any_user` to learner-facing roles. ## QA Checklist - valid admin token + permission returns `200` - token without permission returns `403` - invalid `userId` or `courseId` returns `400` - locked sub-courses correctly show `is_locked: true` - ordering in UI follows `display_order`