feat: add enrolled student detail endpoint and reorder student endpoints

Add getEnrolledStudentDetail endpoint to retrieve individual student's lesson progress across all course chapters. Move searchStudents endpoint before getEnrolledStudentDetail to prevent route conflicts. Remove correct_choice_id and correct_choice_text from quiz attempt detail answers review. Fix selected_choice_id mapping to use choice_id from student answers.
This commit is contained in:
JakkrapartXD 2026-02-03 14:42:45 +07:00
parent 12e71c48b4
commit 7749a39be7
3 changed files with 252 additions and 34 deletions

View file

@ -20,6 +20,7 @@ import {
GetQuizScoresResponse,
GetQuizAttemptDetailResponse,
SearchStudentsResponse,
GetEnrolledStudentDetailResponse,
} from '../types/CoursesInstructor.types';
import { CreateCourseValidator } from "../validators/CoursesInstructor.validator";
@ -300,6 +301,60 @@ export class CoursesInstructorController {
});
}
/**
*
* Search students in course by firstname, lastname, email, or username
* @param courseId - / Course ID
* @param query - / Search query
* @param limit - / Max results
*/
@Get('{courseId}/students/search')
@Security('jwt', ['instructor'])
@SuccessResponse('200', 'Students found successfully')
@Response('401', 'Invalid or expired token')
@Response('403', 'Not an instructor of this course')
public async searchStudents(
@Request() request: any,
@Path() courseId: number,
@Query() query: string,
@Query() limit?: number
): Promise<SearchStudentsResponse> {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) throw new ValidationError('No token provided');
return await CoursesInstructorService.searchStudentsInCourse({
token,
course_id: courseId,
query,
limit,
});
}
/**
* (progress lesson)
* Get enrolled student detail with lesson progress
* @param courseId - / Course ID
* @param studentId - / Student ID
*/
@Get('{courseId}/students/{studentId}')
@Security('jwt', ['instructor'])
@SuccessResponse('200', 'Enrolled student detail retrieved successfully')
@Response('401', 'Invalid or expired token')
@Response('403', 'Not an instructor of this course')
@Response('404', 'Student not found or not enrolled')
public async getEnrolledStudentDetail(
@Request() request: any,
@Path() courseId: number,
@Path() studentId: number
): Promise<GetEnrolledStudentDetailResponse> {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) throw new ValidationError('No token provided');
return await CoursesInstructorService.getEnrolledStudentDetail({
token,
course_id: courseId,
student_id: studentId,
});
}
/**
* lesson quiz
* Get quiz scores of all students for a specific lesson
@ -360,32 +415,4 @@ export class CoursesInstructorController {
student_id: studentId,
});
}
/**
*
* Search students in course by firstname, lastname, email, or username
* @param courseId - / Course ID
* @param query - / Search query
* @param limit - / Max results
*/
@Get('{courseId}/students/search')
@Security('jwt', ['instructor'])
@SuccessResponse('200', 'Students found successfully')
@Response('401', 'Invalid or expired token')
@Response('403', 'Not an instructor of this course')
public async searchStudents(
@Request() request: any,
@Path() courseId: number,
@Query() query: string,
@Query() limit?: number
): Promise<SearchStudentsResponse> {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) throw new ValidationError('No token provided');
return await CoursesInstructorService.searchStudentsInCourse({
token,
course_id: courseId,
query,
limit,
});
}
}