5.7 KiB
5.7 KiB
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
Course-level Summary Endpoint
- Method:
GET - Path:
/api/v1/admin/users/:userId/progress/courses/:courseId/summary - Auth: Bearer token
- Required permission:
progress.get_any_user
Success Response (200)
{
"message": "Learner course progress summary retrieved successfully",
"data": {
"course_id": 1,
"learner_user_id": 10,
"overall_progress_percentage": 40,
"total_sub_courses": 5,
"completed_sub_courses": 2,
"in_progress_sub_courses": 1,
"not_started_sub_courses": 2,
"locked_sub_courses": 2
}
}
Path Parameters
userId(number): target learner user IDcourseId(number): course ID
Success Response (200)
{
"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: invaliduserIdorcourseId401: missing/invalid token403: missingprogress.get_any_user500: server/database issue
Data Semantics
progress_statusvalues:NOT_STARTEDIN_PROGRESSCOMPLETED
progress_percentageis0..100is_lockedis computed from unmet sub-course prerequisites for that learner- list is ordered by
display_order - only active sub-courses are included
Progress Calculation Model
Sub-course progress is automatically aggregated from completion records:
- completed published videos in the sub-course (
user_sub_course_video_progress) - completed published practices in the sub-course (
user_practice_progress)
Formula:
total_items = published_videos + published_practicescompleted_items = completed_videos + completed_practicesprogress_percentage = round((completed_items / total_items) * 100)- if
total_items = 0,progress_percentage = 0
Status transitions:
NOT_STARTEDwhencompleted_items = 0IN_PROGRESSwhen0 < completed_items < total_itemsCOMPLETEDwhencompleted_items >= total_itemsandtotal_items > 0
Auto-recalculation triggers:
POST /api/v1/progress/videos/:id/completePOST /api/v1/progress/practices/:id/complete
Backend Rollout Steps
After pulling this backend change:
- Restart backend service.
- Sync permissions:
POST /api/v1/rbac/permissions/sync
- 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
- User opens learner progress screen.
- Admin selects learner and course.
- Frontend requests:
GET /api/v1/admin/users/{userId}/progress/courses/{courseId}GET /api/v1/admin/users/{userId}/progress/courses/{courseId}/summary
- Render returned
dataas 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_statusisCompleted = progress_status === "COMPLETED"isInProgress = progress_status === "IN_PROGRESS"isNotStarted = progress_status === "NOT_STARTED"canOpenDetails = !is_locked
Suggested API Client Contract
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
curl -X GET "http://localhost:8432/api/v1/admin/users/7/progress/courses/3" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
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_userto learner-facing roles.
QA Checklist
- valid admin token + permission returns
200 - token without permission returns
403 - invalid
userIdorcourseIdreturns400 - locked sub-courses correctly show
is_locked: true - ordering in UI follows
display_order