feat: Implement instructor course and lesson management with dedicated views for quizzes, videos, and admin categories.
This commit is contained in:
parent
344e1e4341
commit
878a17f922
9 changed files with 903 additions and 399 deletions
|
|
@ -25,6 +25,12 @@ export interface CourseResponse {
|
|||
updated_by: number | null;
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface CoursesListResponse {
|
||||
code: number;
|
||||
message: string;
|
||||
|
|
@ -32,6 +38,31 @@ export interface CoursesListResponse {
|
|||
total: number;
|
||||
}
|
||||
|
||||
export interface CourseInstructorResponse {
|
||||
id: number;
|
||||
course_id: number;
|
||||
user_id: number;
|
||||
is_primary: boolean;
|
||||
joined_at: string;
|
||||
user: {
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
role_id: number;
|
||||
email_verified_at: string | null;
|
||||
is_deactivated: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
avatar_url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InstructorsListResponse {
|
||||
code: number;
|
||||
message: string;
|
||||
data: CourseInstructorResponse[];
|
||||
}
|
||||
|
||||
// Helper function to get auth token from cookie
|
||||
const getAuthToken = (): string => {
|
||||
const tokenCookie = useCookie('token');
|
||||
|
|
@ -161,21 +192,25 @@ export const instructorService = {
|
|||
return response.data;
|
||||
},
|
||||
|
||||
async createCourse(data: CreateCourseRequest): Promise<CourseResponse> {
|
||||
async createCourse(data: CreateCourseRequest): Promise<ApiResponse<CourseResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
...MOCK_COURSES[0],
|
||||
id: Date.now(),
|
||||
...data,
|
||||
price: String(data.price), // Convert number to string to match CourseResponse type
|
||||
status: 'DRAFT',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
} as CourseResponse;
|
||||
code: 201,
|
||||
message: 'Course created successfully (Mock)',
|
||||
data: {
|
||||
...MOCK_COURSES[0],
|
||||
id: Date.now(),
|
||||
...data,
|
||||
price: String(data.price), // Convert number to string to match CourseResponse type
|
||||
status: 'DRAFT',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
} as CourseResponse
|
||||
};
|
||||
}
|
||||
|
||||
// Clean data - remove empty thumbnail_url
|
||||
|
|
@ -184,11 +219,10 @@ export const instructorService = {
|
|||
delete cleanedData.thumbnail_url;
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: CourseResponse }>('/api/instructors/courses', {
|
||||
return await authRequest<ApiResponse<CourseResponse>>('/api/instructors/courses', {
|
||||
method: 'POST',
|
||||
body: { data: cleanedData }
|
||||
body: cleanedData
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async getCourseById(courseId: number): Promise<CourseDetailResponse> {
|
||||
|
|
@ -204,68 +238,172 @@ export const instructorService = {
|
|||
return response.data;
|
||||
},
|
||||
|
||||
async updateCourse(courseId: number, data: CreateCourseRequest): Promise<CourseResponse> {
|
||||
async updateCourse(courseId: number, data: CreateCourseRequest): Promise<ApiResponse<CourseResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
...MOCK_COURSES[0],
|
||||
id: courseId,
|
||||
...data,
|
||||
price: String(data.price)
|
||||
} as CourseResponse;
|
||||
code: 200,
|
||||
message: 'Course updated successfully (Mock)',
|
||||
data: {
|
||||
...MOCK_COURSES[0],
|
||||
id: courseId,
|
||||
...data,
|
||||
price: String(data.price)
|
||||
} as CourseResponse
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: CourseResponse }>(`/api/instructors/courses/${courseId}`, {
|
||||
return await authRequest<ApiResponse<CourseResponse>>(`/api/instructors/courses/${courseId}`, {
|
||||
method: 'PUT',
|
||||
body: { data }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async uploadCourseThumbnail(courseId: number, file: File): Promise<{ thumbnail_url: string }> {
|
||||
async uploadCourseThumbnail(courseId: number, file: File): Promise<ApiResponse<{ thumbnail_url: string }>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return { thumbnail_url: URL.createObjectURL(file) };
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Thumbnail uploaded successfully (Mock)',
|
||||
data: { thumbnail_url: URL.createObjectURL(file) }
|
||||
};
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await authRequest<{ code: number; data: { thumbnail_url: string } }>(
|
||||
return await authRequest<ApiResponse<{ thumbnail_url: string }>>(
|
||||
`/api/instructors/courses/${courseId}/thumbnail`,
|
||||
{ method: 'POST', body: formData }
|
||||
);
|
||||
},
|
||||
|
||||
async getCourseInstructors(courseId: number): Promise<CourseInstructorResponse[]> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
course_id: courseId,
|
||||
user_id: 1,
|
||||
is_primary: true,
|
||||
joined_at: new Date().toISOString(),
|
||||
user: {
|
||||
id: 1,
|
||||
username: 'instructor',
|
||||
email: 'instructor@elearning.local',
|
||||
role_id: 2,
|
||||
email_verified_at: new Date().toISOString(),
|
||||
is_deactivated: false,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
const response = await authRequest<InstructorsListResponse>(
|
||||
`/api/instructors/courses/listinstructor/${courseId}`
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteCourse(courseId: number): Promise<void> {
|
||||
async addInstructor(courseId: number, userId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Instructor added successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(`/api/instructors/courses/${courseId}`, { method: 'DELETE' });
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/add-instructor/${courseId}/${userId}`,
|
||||
{ method: 'POST' }
|
||||
);
|
||||
},
|
||||
|
||||
async sendForReview(courseId: number): Promise<void> {
|
||||
async removeInstructor(courseId: number, userId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Instructor removed successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(`/api/instructors/courses/send-review/${courseId}`, { method: 'POST' });
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/remove-instructor/${courseId}/${userId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
async setPrimaryInstructor(courseId: number, userId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Primary instructor updated successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/set-primary-instructor/${courseId}/${userId}`,
|
||||
{ method: 'PUT' }
|
||||
);
|
||||
},
|
||||
|
||||
async deleteCourse(courseId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Course deleted successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
return await authRequest<ApiResponse<void>>(`/api/instructors/courses/${courseId}`, { method: 'DELETE' });
|
||||
},
|
||||
|
||||
async sendForReview(courseId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Sent for review successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
return await authRequest<ApiResponse<void>>(`/api/instructors/courses/send-review/${courseId}`, { method: 'POST' });
|
||||
},
|
||||
|
||||
async getChapters(courseId: number): Promise<ChapterResponse[]> {
|
||||
|
|
@ -284,72 +422,86 @@ export const instructorService = {
|
|||
return response.data.chapters || [];
|
||||
},
|
||||
|
||||
async createChapter(courseId: number, data: CreateChapterRequest): Promise<ChapterResponse> {
|
||||
async createChapter(courseId: number, data: CreateChapterRequest): Promise<ApiResponse<ChapterResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
...MOCK_COURSE_DETAIL.chapters[0],
|
||||
id: Date.now(),
|
||||
...data,
|
||||
lessons: []
|
||||
code: 201,
|
||||
message: 'Chapter created successfully (Mock)',
|
||||
data: {
|
||||
...MOCK_COURSE_DETAIL.chapters[0],
|
||||
id: Date.now(),
|
||||
...data,
|
||||
lessons: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: ChapterResponse }>(
|
||||
return await authRequest<ApiResponse<ChapterResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters`,
|
||||
{ method: 'POST', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateChapter(courseId: number, chapterId: number, data: CreateChapterRequest): Promise<ChapterResponse> {
|
||||
async updateChapter(courseId: number, chapterId: number, data: CreateChapterRequest): Promise<ApiResponse<ChapterResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
...MOCK_COURSE_DETAIL.chapters[0],
|
||||
id: chapterId,
|
||||
...data
|
||||
code: 200,
|
||||
message: 'Chapter updated successfully (Mock)',
|
||||
data: {
|
||||
...MOCK_COURSE_DETAIL.chapters[0],
|
||||
id: chapterId,
|
||||
...data
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: ChapterResponse }>(
|
||||
return await authRequest<ApiResponse<ChapterResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}`,
|
||||
{ method: 'PUT', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteChapter(courseId: number, chapterId: number): Promise<void> {
|
||||
async deleteChapter(courseId: number, chapterId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Chapter deleted successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
async reorderChapter(courseId: number, chapterId: number, sortOrder: number): Promise<void> {
|
||||
async reorderChapter(courseId: number, chapterId: number, sortOrder: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Chapter reordered successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/reorder`,
|
||||
{ method: 'PUT', body: { sort_order: sortOrder } }
|
||||
);
|
||||
|
|
@ -371,208 +523,243 @@ export const instructorService = {
|
|||
return response.data;
|
||||
},
|
||||
|
||||
async createLesson(courseId: number, chapterId: number, data: CreateLessonRequest): Promise<LessonResponse> {
|
||||
async createLesson(courseId: number, chapterId: number, data: CreateLessonRequest): Promise<ApiResponse<LessonResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
...MOCK_COURSE_DETAIL.chapters[0].lessons[0],
|
||||
id: Date.now(),
|
||||
...data
|
||||
code: 201,
|
||||
message: 'Lesson created successfully (Mock)',
|
||||
data: {
|
||||
...MOCK_COURSE_DETAIL.chapters[0].lessons[0],
|
||||
id: Date.now(),
|
||||
...data
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: LessonResponse }>(
|
||||
return await authRequest<ApiResponse<LessonResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons`,
|
||||
{ method: 'POST', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateLesson(courseId: number, chapterId: number, lessonId: number, data: UpdateLessonRequest): Promise<LessonResponse> {
|
||||
async updateLesson(courseId: number, chapterId: number, lessonId: number, data: UpdateLessonRequest): Promise<ApiResponse<LessonResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
...MOCK_COURSE_DETAIL.chapters[0].lessons[0],
|
||||
id: lessonId,
|
||||
...data
|
||||
code: 200,
|
||||
message: 'Lesson updated successfully (Mock)',
|
||||
data: {
|
||||
...MOCK_COURSE_DETAIL.chapters[0].lessons[0],
|
||||
id: lessonId,
|
||||
...data
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: LessonResponse }>(
|
||||
return await authRequest<ApiResponse<LessonResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}`,
|
||||
{ method: 'PUT', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteLesson(courseId: number, chapterId: number, lessonId: number): Promise<void> {
|
||||
async deleteLesson(courseId: number, chapterId: number, lessonId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Lesson deleted successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
async reorderLesson(courseId: number, chapterId: number, lessonId: number, sortOrder: number): Promise<void> {
|
||||
async reorderLesson(courseId: number, chapterId: number, lessonId: number, sortOrder: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Lesson reordered successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/reorder-lessons`,
|
||||
{ method: 'PUT', body: { lesson_id: lessonId, sort_order: sortOrder } }
|
||||
);
|
||||
},
|
||||
|
||||
// Question CRUD
|
||||
async createQuestion(courseId: number, chapterId: number, lessonId: number, data: CreateQuestionRequest): Promise<QuizQuestionResponse> {
|
||||
async createQuestion(courseId: number, chapterId: number, lessonId: number, data: CreateQuestionRequest): Promise<ApiResponse<QuizQuestionResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
id: Date.now(),
|
||||
quiz_id: 1,
|
||||
question: data.question,
|
||||
explanation: data.explanation || null,
|
||||
question_type: data.question_type,
|
||||
score: data.score,
|
||||
sort_order: data.sort_order || 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
choices: data.choices.map((c, i) => ({
|
||||
id: Date.now() + i,
|
||||
question_id: Date.now(),
|
||||
text: c.text,
|
||||
is_correct: c.is_correct,
|
||||
sort_order: c.sort_order || i + 1
|
||||
}))
|
||||
code: 201,
|
||||
message: 'Question created successfully (Mock)',
|
||||
data: {
|
||||
id: Date.now(),
|
||||
quiz_id: 1,
|
||||
question: data.question,
|
||||
explanation: data.explanation || null,
|
||||
question_type: data.question_type,
|
||||
score: data.score || 1,
|
||||
sort_order: data.sort_order || 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
choices: data.choices.map((c, i) => ({
|
||||
id: Date.now() + i,
|
||||
question_id: Date.now(),
|
||||
text: c.text,
|
||||
is_correct: c.is_correct,
|
||||
sort_order: c.sort_order || i + 1
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: QuizQuestionResponse }>(
|
||||
return await authRequest<ApiResponse<QuizQuestionResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/questions`,
|
||||
{ method: 'POST', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateQuestion(courseId: number, chapterId: number, lessonId: number, questionId: number, data: CreateQuestionRequest): Promise<QuizQuestionResponse> {
|
||||
async updateQuestion(courseId: number, chapterId: number, lessonId: number, questionId: number, data: CreateQuestionRequest): Promise<ApiResponse<QuizQuestionResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
id: questionId,
|
||||
quiz_id: 1,
|
||||
question: data.question,
|
||||
explanation: data.explanation || null,
|
||||
question_type: data.question_type,
|
||||
score: data.score,
|
||||
sort_order: data.sort_order || 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
choices: data.choices.map((c, i) => ({
|
||||
id: Date.now() + i,
|
||||
question_id: questionId,
|
||||
text: c.text,
|
||||
is_correct: c.is_correct,
|
||||
sort_order: c.sort_order || i + 1
|
||||
}))
|
||||
code: 200,
|
||||
message: 'Question updated successfully (Mock)',
|
||||
data: {
|
||||
id: questionId,
|
||||
quiz_id: 1,
|
||||
question: data.question,
|
||||
explanation: data.explanation || null,
|
||||
question_type: data.question_type,
|
||||
score: data.score || 1,
|
||||
sort_order: data.sort_order || 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
choices: data.choices.map((c, i) => ({
|
||||
id: Date.now() + i,
|
||||
question_id: questionId,
|
||||
text: c.text,
|
||||
is_correct: c.is_correct,
|
||||
sort_order: c.sort_order || i + 1
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: QuizQuestionResponse }>(
|
||||
return await authRequest<ApiResponse<QuizQuestionResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/questions/${questionId}`,
|
||||
{ method: 'PUT', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteQuestion(courseId: number, chapterId: number, lessonId: number, questionId: number): Promise<void> {
|
||||
async deleteQuestion(courseId: number, chapterId: number, lessonId: number, questionId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Question deleted successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/questions/${questionId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
async reorderQuestion(courseId: number, chapterId: number, lessonId: number, questionId: number, sortOrder: number): Promise<void> {
|
||||
async reorderQuestion(courseId: number, chapterId: number, lessonId: number, questionId: number, sortOrder: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Question reordered successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/questions/${questionId}/reorder`,
|
||||
{ method: 'PUT', body: { sort_order: sortOrder } }
|
||||
);
|
||||
},
|
||||
|
||||
// Quiz Settings
|
||||
async updateQuizSettings(courseId: number, chapterId: number, lessonId: number, data: UpdateQuizSettingsRequest): Promise<QuizResponse> {
|
||||
async updateQuizSettings(courseId: number, chapterId: number, lessonId: number, data: UpdateQuizSettingsRequest): Promise<ApiResponse<QuizResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
id: 1,
|
||||
lesson_id: lessonId,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
passing_score: data.passing_score,
|
||||
time_limit: data.time_limit,
|
||||
shuffle_questions: data.shuffle_questions,
|
||||
shuffle_choices: data.shuffle_choices,
|
||||
show_answers_after_completion: data.show_answers_after_completion
|
||||
code: 200,
|
||||
message: 'Quiz settings updated successfully (Mock)',
|
||||
data: {
|
||||
id: 1,
|
||||
lesson_id: lessonId,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
passing_score: data.passing_score,
|
||||
time_limit: data.time_limit,
|
||||
shuffle_questions: data.shuffle_questions,
|
||||
shuffle_choices: data.shuffle_choices,
|
||||
show_answers_after_completion: data.show_answers_after_completion
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: QuizResponse }>(
|
||||
return await authRequest<ApiResponse<QuizResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/quiz`,
|
||||
{ method: 'PUT', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Video Upload
|
||||
async uploadLessonVideo(courseId: number, chapterId: number, lessonId: number, video: File, attachments?: File[]): Promise<LessonResponse> {
|
||||
async uploadLessonVideo(courseId: number, chapterId: number, lessonId: number, video: File, attachments?: File[]): Promise<ApiResponse<LessonResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
return MOCK_COURSE_DETAIL.chapters[0].lessons[0] as LessonResponse;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Video uploaded successfully (Mock)',
|
||||
data: MOCK_COURSE_DETAIL.chapters[0].lessons[0] as LessonResponse
|
||||
};
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
|
@ -583,20 +770,23 @@ export const instructorService = {
|
|||
});
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: LessonResponse }>(
|
||||
return await authRequest<ApiResponse<LessonResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/video`,
|
||||
{ method: 'POST', body: formData }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateLessonVideo(courseId: number, chapterId: number, lessonId: number, video?: File, attachments?: File[]): Promise<LessonResponse> {
|
||||
async updateLessonVideo(courseId: number, chapterId: number, lessonId: number, video?: File, attachments?: File[]): Promise<ApiResponse<LessonResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
return MOCK_COURSE_DETAIL.chapters[0].lessons[0] as LessonResponse;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Video updated successfully (Mock)',
|
||||
data: MOCK_COURSE_DETAIL.chapters[0].lessons[0] as LessonResponse
|
||||
};
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
|
@ -609,21 +799,24 @@ export const instructorService = {
|
|||
});
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: LessonResponse }>(
|
||||
return await authRequest<ApiResponse<LessonResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/video`,
|
||||
{ method: 'PUT', body: formData }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Attachments
|
||||
async addAttachments(courseId: number, chapterId: number, lessonId: number, files: File[]): Promise<LessonResponse> {
|
||||
async addAttachments(courseId: number, chapterId: number, lessonId: number, files: File[]): Promise<ApiResponse<LessonResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return MOCK_COURSE_DETAIL.chapters[0].lessons[0] as LessonResponse;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Attachments added successfully (Mock)',
|
||||
data: MOCK_COURSE_DETAIL.chapters[0].lessons[0] as LessonResponse
|
||||
};
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
|
@ -631,23 +824,26 @@ export const instructorService = {
|
|||
formData.append('attachment', file);
|
||||
});
|
||||
|
||||
const response = await authRequest<{ code: number; data: LessonResponse }>(
|
||||
return await authRequest<ApiResponse<LessonResponse>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/attachments`,
|
||||
{ method: 'POST', body: formData }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteAttachment(courseId: number, chapterId: number, lessonId: number, attachmentId: number): Promise<void> {
|
||||
async deleteAttachment(courseId: number, chapterId: number, lessonId: number, attachmentId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Attachment deleted successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/attachments/${attachmentId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
|
|
@ -669,21 +865,25 @@ export const instructorService = {
|
|||
return response.data;
|
||||
},
|
||||
|
||||
async createAnnouncement(courseId: number, data: CreateAnnouncementRequest, files?: File[]): Promise<AnnouncementResponse> {
|
||||
async createAnnouncement(courseId: number, data: CreateAnnouncementRequest, files?: File[]): Promise<ApiResponse<AnnouncementResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
id: Date.now(),
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
status: data.status || 'DRAFT',
|
||||
is_pinned: data.is_pinned || false,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
attachments: []
|
||||
code: 201,
|
||||
message: 'Announcement created successfully (Mock)',
|
||||
data: {
|
||||
id: Date.now(),
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
status: data.status || 'DRAFT',
|
||||
is_pinned: data.is_pinned || false,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
attachments: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -696,82 +896,95 @@ export const instructorService = {
|
|||
});
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: AnnouncementResponse }>(
|
||||
return await authRequest<ApiResponse<AnnouncementResponse>>(
|
||||
`/api/instructors/courses/${courseId}/announcements`,
|
||||
{ method: 'POST', body: formData }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateAnnouncement(courseId: number, announcementId: number, data: CreateAnnouncementRequest): Promise<AnnouncementResponse> {
|
||||
async updateAnnouncement(courseId: number, announcementId: number, data: CreateAnnouncementRequest): Promise<ApiResponse<AnnouncementResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return {
|
||||
id: announcementId,
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
status: data.status || 'DRAFT',
|
||||
is_pinned: data.is_pinned || false,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
attachments: []
|
||||
code: 200,
|
||||
message: 'Announcement updated successfully (Mock)',
|
||||
data: {
|
||||
id: announcementId,
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
status: data.status || 'DRAFT',
|
||||
is_pinned: data.is_pinned || false,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
attachments: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const response = await authRequest<{ code: number; data: AnnouncementResponse }>(
|
||||
return await authRequest<ApiResponse<AnnouncementResponse>>(
|
||||
`/api/instructors/courses/${courseId}/announcements/${announcementId}`,
|
||||
{ method: 'PUT', body: data }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteAnnouncement(courseId: number, announcementId: number): Promise<void> {
|
||||
async deleteAnnouncement(courseId: number, announcementId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Announcement deleted successfully (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/announcements/${announcementId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
|
||||
async uploadAnnouncementAttachment(courseId: number, announcementId: number, file: File): Promise<AnnouncementResponse> {
|
||||
async uploadAnnouncementAttachment(courseId: number, announcementId: number, file: File): Promise<ApiResponse<AnnouncementResponse>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return MOCK_ANNOUNCEMENTS[0];
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Upload attachment success (Mock)',
|
||||
data: MOCK_ANNOUNCEMENTS[0]
|
||||
};
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await authRequest<{ code: number; data: AnnouncementResponse }>(
|
||||
return await authRequest<ApiResponse<AnnouncementResponse>>(
|
||||
`/api/instructors/courses/${courseId}/announcements/${announcementId}/attachments`,
|
||||
{ method: 'POST', body: formData }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteAnnouncementAttachment(courseId: number, announcementId: number, attachmentId: number): Promise<void> {
|
||||
async deleteAnnouncementAttachment(courseId: number, announcementId: number, attachmentId: number): Promise<ApiResponse<void>> {
|
||||
const config = useRuntimeConfig();
|
||||
const useMockData = config.public.useMockData as boolean;
|
||||
|
||||
if (useMockData) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return;
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Delete attachment success (Mock)',
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
await authRequest(
|
||||
return await authRequest<ApiResponse<void>>(
|
||||
`/api/instructors/courses/${courseId}/announcements/${announcementId}/attachments/${attachmentId}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
|
|
@ -895,6 +1108,7 @@ export interface CreateQuestionRequest {
|
|||
question: { th: string; en: string };
|
||||
explanation?: { th: string; en: string } | null;
|
||||
question_type: 'MULTIPLE_CHOICE' | 'TRUE_FALSE';
|
||||
score?: number;
|
||||
sort_order?: number;
|
||||
choices: CreateChoiceRequest[];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue