-
+
แก้ไข
@@ -486,6 +486,12 @@ const fetchChapters = async () => {
}
};
+// Navigate to lesson edit page based on type
+const navigateToLessonEdit = (chapter: ChapterResponse, lesson: LessonResponse) => {
+ const lessonType = lesson.type === 'QUIZ' ? 'quiz' : 'video';
+ navigateTo(`/instructor/courses/${courseId.value}/chapters/${chapter.id}/lessons/${lesson.id}/${lessonType}`);
+};
+
const getLessonIcon = (type: string) => {
const icons: Record = {
VIDEO: 'play_circle',
@@ -513,6 +519,10 @@ const getLessonTypeLabel = (type: string) => {
return labels[type] || type;
};
+const getSortedLessons = (lessons: LessonResponse[]) => {
+ return [...lessons].sort((a, b) => a.sort_order - b.sort_order);
+};
+
// Chapter CRUD
const openChapterDialog = (chapter?: ChapterResponse) => {
if (chapter) {
diff --git a/frontend_management/services/instructor.service.ts b/frontend_management/services/instructor.service.ts
index 838d6197..1405e19d 100644
--- a/frontend_management/services/instructor.service.ts
+++ b/frontend_management/services/instructor.service.ts
@@ -417,9 +417,179 @@ export const instructorService = {
}
await authRequest(
- `/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/reorder`,
+ `/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 {
+ 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
+ }))
+ };
+ }
+
+ const response = await authRequest<{ code: number; data: 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 {
+ 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
+ }))
+ };
+ }
+
+ const response = await authRequest<{ code: number; data: 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 {
+ const config = useRuntimeConfig();
+ const useMockData = config.public.useMockData as boolean;
+
+ if (useMockData) {
+ await new Promise(resolve => setTimeout(resolve, 500));
+ return;
+ }
+
+ await authRequest(
+ `/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/questions/${questionId}`,
+ { method: 'DELETE' }
+ );
+ },
+
+ // Video Upload
+ async uploadLessonVideo(courseId: number, chapterId: number, lessonId: number, video: File, attachments?: File[]): Promise {
+ 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;
+ }
+
+ const formData = new FormData();
+ formData.append('video', video);
+ if (attachments) {
+ attachments.forEach(file => {
+ formData.append('attachments', file);
+ });
+ }
+
+ const response = await authRequest<{ code: number; data: 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 {
+ 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;
+ }
+
+ const formData = new FormData();
+ if (video) {
+ formData.append('video', video);
+ }
+ if (attachments) {
+ attachments.forEach(file => {
+ formData.append('attachments', file);
+ });
+ }
+
+ const response = await authRequest<{ code: number; data: 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 {
+ 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;
+ }
+
+ const formData = new FormData();
+ files.forEach(file => {
+ formData.append('attachments', file);
+ });
+
+ const response = await authRequest<{ code: number; data: 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 {
+ const config = useRuntimeConfig();
+ const useMockData = config.public.useMockData as boolean;
+
+ if (useMockData) {
+ await new Promise(resolve => setTimeout(resolve, 500));
+ return;
+ }
+
+ await authRequest(
+ `/api/instructors/courses/${courseId}/chapters/${chapterId}/lessons/${lessonId}/attachments/${attachmentId}`,
+ { method: 'DELETE' }
+ );
}
};
@@ -472,9 +642,32 @@ export interface LessonResponse {
is_published: boolean;
created_at: string;
updated_at: string;
+ video_url: string | null;
+ attachments: AttachmentResponse[];
quiz: QuizResponse | null;
}
+export interface QuizChoiceResponse {
+ id: number;
+ question_id: number;
+ text: { en: string; th: string };
+ is_correct: boolean;
+ sort_order: number;
+}
+
+export interface QuizQuestionResponse {
+ id: number;
+ quiz_id: number;
+ question: { en: string; th: string };
+ explanation: { en: string; th: string } | null;
+ question_type: 'MULTIPLE_CHOICE' | 'TRUE_FALSE';
+ score: number;
+ sort_order: number;
+ created_at: string;
+ updated_at: string;
+ choices: QuizChoiceResponse[];
+}
+
export interface QuizResponse {
id: number;
lesson_id: number;
@@ -485,6 +678,9 @@ export interface QuizResponse {
shuffle_questions: boolean;
shuffle_choices: boolean;
show_answers_after_completion: boolean;
+ created_at?: string;
+ updated_at?: string;
+ questions?: QuizQuestionResponse[];
}
export interface CreateChapterRequest {
@@ -494,6 +690,20 @@ export interface CreateChapterRequest {
is_published?: boolean;
}
+export interface CreateChoiceRequest {
+ text: { th: string; en: string };
+ is_correct: boolean;
+ sort_order?: number;
+}
+
+export interface CreateQuestionRequest {
+ question: { th: string; en: string };
+ explanation?: { th: string; en: string } | null;
+ question_type: 'MULTIPLE_CHOICE' | 'TRUE_FALSE';
+ sort_order?: number;
+ choices: CreateChoiceRequest[];
+}
+
export interface CreateLessonRequest {
title: { th: string; en: string };
content?: { th: string; en: string } | null;
@@ -550,6 +760,8 @@ const MOCK_COURSE_DETAIL: CourseDetailResponse = {
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
+ video_url: null,
+ attachments: [],
quiz: null
},
{
@@ -566,6 +778,8 @@ const MOCK_COURSE_DETAIL: CourseDetailResponse = {
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
+ video_url: null,
+ attachments: [],
quiz: null
},
{
@@ -582,6 +796,8 @@ const MOCK_COURSE_DETAIL: CourseDetailResponse = {
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
+ video_url: null,
+ attachments: [],
quiz: {
id: 1,
lesson_id: 3,
@@ -620,6 +836,8 @@ const MOCK_COURSE_DETAIL: CourseDetailResponse = {
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
+ video_url: null,
+ attachments: [],
quiz: null
}
]