feat: add search and filter capabilities to student and quiz endpoints, implement YouTube video support for lessons
Add search and status filter parameters to getEnrolledStudents endpoint to filter students by name/email/username and enrollment status. Add search and isPassed filter parameters to getQuizScores endpoint to filter quiz results by student details and pass status. Remove separate searchStudents endpoint as its functionality is now integrated into getEnrolledStudents. Add setYouTubeVideo endpoint to
This commit is contained in:
parent
e8a10e5024
commit
ff841c7638
6 changed files with 246 additions and 125 deletions
|
|
@ -19,7 +19,6 @@ import {
|
|||
GetEnrolledStudentsResponse,
|
||||
GetQuizScoresResponse,
|
||||
GetQuizAttemptDetailResponse,
|
||||
SearchStudentsResponse,
|
||||
GetEnrolledStudentDetailResponse,
|
||||
} from '../types/CoursesInstructor.types';
|
||||
import { CreateCourseValidator } from "../validators/CoursesInstructor.validator";
|
||||
|
|
@ -279,6 +278,8 @@ export class CoursesInstructorController {
|
|||
* @param courseId - รหัสคอร์ส / Course ID
|
||||
* @param page - หน้าที่ต้องการ / Page number
|
||||
* @param limit - จำนวนต่อหน้า / Items per page
|
||||
* @param search - ค้นหาด้วย firstname, lastname, email, username
|
||||
* @param status - กรองตามสถานะ (ENROLLED, COMPLETED)
|
||||
*/
|
||||
@Get('{courseId}/students')
|
||||
@Security('jwt', ['instructor'])
|
||||
|
|
@ -289,7 +290,9 @@ export class CoursesInstructorController {
|
|||
@Request() request: any,
|
||||
@Path() courseId: number,
|
||||
@Query() page?: number,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() search?: string,
|
||||
@Query() status?: 'ENROLLED' | 'COMPLETED'
|
||||
): Promise<GetEnrolledStudentsResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
|
@ -298,34 +301,8 @@ export class CoursesInstructorController {
|
|||
course_id: courseId,
|
||||
page,
|
||||
limit,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ค้นหานักเรียนในคอร์ส
|
||||
* 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,
|
||||
search,
|
||||
status,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +339,8 @@ export class CoursesInstructorController {
|
|||
* @param lessonId - รหัสบทเรียน / Lesson ID
|
||||
* @param page - หน้าที่ต้องการ / Page number
|
||||
* @param limit - จำนวนต่อหน้า / Items per page
|
||||
* @param search - ค้นหาด้วย firstname, lastname, email, username
|
||||
* @param isPassed - กรองตามผลสอบ (true = ผ่าน, false = ไม่ผ่าน)
|
||||
*/
|
||||
@Get('{courseId}/lessons/{lessonId}/quiz/scores')
|
||||
@Security('jwt', ['instructor'])
|
||||
|
|
@ -374,7 +353,9 @@ export class CoursesInstructorController {
|
|||
@Path() courseId: number,
|
||||
@Path() lessonId: number,
|
||||
@Query() page?: number,
|
||||
@Query() limit?: number
|
||||
@Query() limit?: number,
|
||||
@Query() search?: string,
|
||||
@Query() isPassed?: boolean
|
||||
): Promise<GetQuizScoresResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
|
@ -384,6 +365,8 @@ export class CoursesInstructorController {
|
|||
lesson_id: lessonId,
|
||||
page,
|
||||
limit,
|
||||
search,
|
||||
is_passed: isPassed,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Delete, Path, Post, Put, Request, Response, Route, Security, SuccessResponse, Tags, UploadedFile } from 'tsoa';
|
||||
import { Body, Delete, Path, Post, Put, Request, Response, Route, Security, SuccessResponse, Tags, UploadedFile } from 'tsoa';
|
||||
import { ValidationError } from '../middleware/errorHandler';
|
||||
import { ChaptersLessonService } from '../services/ChaptersLesson.service';
|
||||
import {
|
||||
|
|
@ -7,7 +7,9 @@ import {
|
|||
UpdateLessonResponse,
|
||||
VideoOperationResponse,
|
||||
AttachmentOperationResponse,
|
||||
DeleteAttachmentResponse
|
||||
DeleteAttachmentResponse,
|
||||
YouTubeVideoResponse,
|
||||
SetYouTubeVideoBody,
|
||||
} from '../types/ChaptersLesson.typs';
|
||||
|
||||
const chaptersLessonService = new ChaptersLessonService();
|
||||
|
|
@ -184,4 +186,46 @@ export class LessonsController {
|
|||
attachment_id: attachmentId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ตั้งค่าวิดีโอ YouTube ให้บทเรียน (แทนที่วิดีโอเดิมถ้ามี)
|
||||
* Set YouTube video for a lesson (replaces existing video if any)
|
||||
*
|
||||
* @param courseId Course ID
|
||||
* @param chapterId Chapter ID
|
||||
* @param lessonId Lesson ID
|
||||
* @param body YouTube video info
|
||||
*/
|
||||
@Post('{lessonId}/youtube-video')
|
||||
@Security('jwt', ['instructor'])
|
||||
@SuccessResponse('200', 'YouTube video set successfully')
|
||||
@Response('400', 'Validation error')
|
||||
@Response('401', 'Unauthorized')
|
||||
@Response('403', 'Forbidden')
|
||||
@Response('404', 'Lesson not found')
|
||||
public async setYouTubeVideo(
|
||||
@Request() request: any,
|
||||
@Path() courseId: number,
|
||||
@Path() chapterId: number,
|
||||
@Path() lessonId: number,
|
||||
@Body() body: SetYouTubeVideoBody
|
||||
): Promise<YouTubeVideoResponse> {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) throw new ValidationError('No token provided');
|
||||
|
||||
if (!body.youtube_video_id) {
|
||||
throw new ValidationError('YouTube video ID is required');
|
||||
}
|
||||
if (!body.video_title) {
|
||||
throw new ValidationError('Video title is required');
|
||||
}
|
||||
|
||||
return await chaptersLessonService.setYouTubeVideo({
|
||||
token,
|
||||
course_id: courseId,
|
||||
lesson_id: lessonId,
|
||||
youtube_video_id: body.youtube_video_id,
|
||||
video_title: body.video_title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue