219 lines
5.7 KiB
Markdown
219 lines
5.7 KiB
Markdown
# 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`)
|
|
|
|
```json
|
|
{
|
|
"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 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
|
|
|
|
## 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_practices`
|
|
- `completed_items = completed_videos + completed_practices`
|
|
- `progress_percentage = round((completed_items / total_items) * 100)`
|
|
- if `total_items = 0`, `progress_percentage = 0`
|
|
|
|
Status transitions:
|
|
|
|
- `NOT_STARTED` when `completed_items = 0`
|
|
- `IN_PROGRESS` when `0 < completed_items < total_items`
|
|
- `COMPLETED` when `completed_items >= total_items` and `total_items > 0`
|
|
|
|
Auto-recalculation triggers:
|
|
|
|
- `POST /api/v1/progress/videos/:id/complete`
|
|
- `POST /api/v1/progress/practices/:id/complete`
|
|
|
|
## 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}`
|
|
- `GET /api/v1/admin/users/{userId}/progress/courses/{courseId}/summary`
|
|
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 <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_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`
|