feat: Implement instructor course management, including student progress tracking and course content views, alongside admin user management.
This commit is contained in:
parent
c9381b9385
commit
be5b9756be
11 changed files with 2116 additions and 1386 deletions
|
|
@ -12,6 +12,7 @@ export interface AdminUserResponse {
|
|||
th: string;
|
||||
};
|
||||
};
|
||||
avatar_url?: string;
|
||||
profile: {
|
||||
prefix: {
|
||||
en: string;
|
||||
|
|
|
|||
|
|
@ -251,6 +251,13 @@ export const instructorService = {
|
|||
);
|
||||
},
|
||||
|
||||
async submitCourseForApproval(courseId: number): Promise<ApiResponse<void>> {
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/submit`,
|
||||
{ method: 'POST' }
|
||||
);
|
||||
},
|
||||
|
||||
async getCourseInstructors(courseId: number): Promise<CourseInstructorResponse[]> {
|
||||
const response = await authRequest<InstructorsListResponse>(
|
||||
`/api/instructors/courses/listinstructor/${courseId}`
|
||||
|
|
@ -434,6 +441,35 @@ export const instructorService = {
|
|||
);
|
||||
},
|
||||
|
||||
// Quiz Scores
|
||||
async getLessonQuizScores(
|
||||
courseId: number,
|
||||
lessonId: number,
|
||||
page: number = 1,
|
||||
limit: number = 10,
|
||||
search?: string,
|
||||
isPassed?: boolean
|
||||
): Promise<QuizScoresResponse> {
|
||||
let url = `/api/instructors/courses/${courseId}/lessons/${lessonId}/quiz/scores?page=${page}&limit=${limit}`;
|
||||
if (search) {
|
||||
url += `&search=${encodeURIComponent(search)}`;
|
||||
}
|
||||
if (isPassed !== undefined) {
|
||||
url += `&isPassed=${isPassed}`;
|
||||
}
|
||||
return await authRequest<QuizScoresResponse>(url);
|
||||
},
|
||||
|
||||
async getStudentQuizAttemptDetail(
|
||||
courseId: number,
|
||||
lessonId: number,
|
||||
studentId: number
|
||||
): Promise<QuizAttemptDetailResponse> {
|
||||
return await authRequest<QuizAttemptDetailResponse>(
|
||||
`/api/instructors/courses/${courseId}/lessons/${lessonId}/quiz/students/${studentId}`
|
||||
);
|
||||
},
|
||||
|
||||
// Video Upload
|
||||
async uploadLessonVideo(courseId: number, chapterId: number, lessonId: number, video: File, attachments?: File[]): Promise<ApiResponse<LessonResponse>> {
|
||||
const formData = new FormData();
|
||||
|
|
@ -467,6 +503,19 @@ export const instructorService = {
|
|||
);
|
||||
},
|
||||
|
||||
async setLessonYoutubeVideo(courseId: number, chapterId: number, lessonId: number, youtubeVideoId: string, videoTitle: string): Promise<ApiResponse<LessonResponse>> {
|
||||
return await authRequest<ApiResponse<LessonResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/youtube-video`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: {
|
||||
youtube_video_id: youtubeVideoId,
|
||||
video_title: videoTitle
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Attachments
|
||||
async addAttachments(courseId: number, chapterId: number, lessonId: number, files: File[]): Promise<ApiResponse<LessonResponse>> {
|
||||
const formData = new FormData();
|
||||
|
|
@ -719,6 +768,7 @@ export interface AnnouncementResponse {
|
|||
is_pinned: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
published_at?: string;
|
||||
attachments: AnnouncementAttachment[];
|
||||
}
|
||||
|
||||
|
|
@ -740,6 +790,97 @@ export interface CreateAnnouncementRequest {
|
|||
th: string;
|
||||
en: string;
|
||||
};
|
||||
status?: 'DRAFT' | 'PUBLISHED';
|
||||
is_pinned?: boolean;
|
||||
status?: 'DRAFT' | 'PUBLISHED';
|
||||
published_at?: string;
|
||||
}
|
||||
|
||||
export interface QuizScoreStudentResponse {
|
||||
user_id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
avatar_url: string | null;
|
||||
latest_attempt: {
|
||||
completed_at: string;
|
||||
attempt_number: number;
|
||||
is_passed: boolean;
|
||||
total_score: number;
|
||||
score: number;
|
||||
id: number;
|
||||
} | null;
|
||||
best_score: number;
|
||||
total_attempts: number;
|
||||
is_passed: boolean;
|
||||
}
|
||||
|
||||
export interface QuizScoresData {
|
||||
students: QuizScoreStudentResponse[];
|
||||
total_score: number;
|
||||
passing_score: number;
|
||||
quiz_title: {
|
||||
th: string;
|
||||
en: string;
|
||||
};
|
||||
quiz_id: number;
|
||||
lesson_title: {
|
||||
th: string;
|
||||
en: string;
|
||||
};
|
||||
lesson_id: number;
|
||||
}
|
||||
|
||||
export interface QuizScoresResponse {
|
||||
code: number;
|
||||
message: string;
|
||||
data: QuizScoresData;
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface QuizAnswerReview {
|
||||
question_id: number;
|
||||
question_text: {
|
||||
th: string;
|
||||
en: string;
|
||||
};
|
||||
sort_order: number;
|
||||
selected_choice_id: number;
|
||||
selected_choice_text: {
|
||||
th: string;
|
||||
en: string;
|
||||
};
|
||||
is_correct: boolean;
|
||||
score: number;
|
||||
question_score: number;
|
||||
}
|
||||
|
||||
export interface QuizAttemptDetailData {
|
||||
attempt_id: number;
|
||||
quiz_id: number;
|
||||
student: {
|
||||
user_id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
};
|
||||
score: number;
|
||||
total_score: number;
|
||||
total_questions: number;
|
||||
correct_answers: number;
|
||||
is_passed: boolean;
|
||||
passing_score: number;
|
||||
attempt_number: number;
|
||||
started_at: string;
|
||||
completed_at: string;
|
||||
answers_review: QuizAnswerReview[];
|
||||
}
|
||||
|
||||
export interface QuizAttemptDetailResponse {
|
||||
code: number;
|
||||
message: string;
|
||||
data: QuizAttemptDetailData;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue