feat: Implement instructor course management pages and service for listing, creating, viewing, and editing courses.

This commit is contained in:
Missez 2026-01-21 09:52:10 +07:00
parent cad3f276f5
commit 1e06041769
5 changed files with 838 additions and 9 deletions

View file

@ -147,16 +147,119 @@ export const instructorService = {
}
const token = getAuthToken();
// Clean data - remove empty thumbnail_url
const cleanedData = { ...data };
if (!cleanedData.thumbnail_url) {
delete cleanedData.thumbnail_url;
}
console.log('=== CREATE COURSE DEBUG ===');
console.log('Body:', JSON.stringify({ data: cleanedData }, null, 2));
console.log('===========================');
const response = await $fetch<{ code: number; data: CourseResponse }>('/api/instructors/courses', {
method: 'POST',
baseURL: config.public.apiBaseUrl as string,
headers: {
Authorization: `Bearer ${token}`
},
body: { data: cleanedData }
});
return response.data;
},
async getCourseById(courseId: number): Promise<CourseDetailResponse> {
const config = useRuntimeConfig();
const useMockData = config.public.useMockData as boolean;
if (useMockData) {
await new Promise(resolve => setTimeout(resolve, 500));
return MOCK_COURSE_DETAIL;
}
const token = getAuthToken();
const response = await $fetch<{ code: number; data: CourseDetailResponse }>(`/api/instructors/courses/${courseId}`, {
baseURL: config.public.apiBaseUrl as string,
headers: {
Authorization: `Bearer ${token}`
}
});
return response.data;
},
async updateCourse(courseId: number, data: CreateCourseRequest): Promise<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;
}
const token = getAuthToken();
// Debug log
console.log('=== UPDATE COURSE DEBUG ===');
console.log('URL:', `${config.public.apiBaseUrl}/api/instructors/courses/${courseId}`);
console.log('Body:', JSON.stringify({ data }, null, 2));
console.log('===========================');
const response = await $fetch<{ code: number; data: CourseResponse }>(`/api/instructors/courses/${courseId}`, {
method: 'PUT',
baseURL: config.public.apiBaseUrl as string,
headers: {
Authorization: `Bearer ${token}`
},
body: { data }
});
return response.data;
},
async deleteCourse(courseId: number): Promise<void> {
const config = useRuntimeConfig();
const useMockData = config.public.useMockData as boolean;
if (useMockData) {
await new Promise(resolve => setTimeout(resolve, 500));
return;
}
const token = getAuthToken();
await $fetch(`/api/instructors/courses/${courseId}`, {
method: 'DELETE',
baseURL: config.public.apiBaseUrl as string,
headers: {
Authorization: `Bearer ${token}`
}
});
},
async sendForReview(courseId: number): Promise<void> {
const config = useRuntimeConfig();
const useMockData = config.public.useMockData as boolean;
if (useMockData) {
await new Promise(resolve => setTimeout(resolve, 500));
return;
}
const token = getAuthToken();
await $fetch(`/api/instructors/courses/send-review/${courseId}`, {
method: 'POST',
baseURL: config.public.apiBaseUrl as string,
headers: {
Authorization: `Bearer ${token}`
}
});
}
};
@ -177,3 +280,154 @@ export interface CreateCourseRequest {
is_free: boolean;
have_certificate: boolean;
}
// Course detail with chapters and lessons
export interface CourseDetailResponse extends CourseResponse {
chapters: ChapterResponse[];
}
export interface ChapterResponse {
id: number;
course_id: number;
title: { en: string; th: string };
description: { en: string; th: string };
sort_order: number;
is_published: boolean;
created_at: string;
updated_at: string;
lessons: LessonResponse[];
}
export interface LessonResponse {
id: number;
chapter_id: number;
title: { en: string; th: string };
content: { en: string; th: string } | null;
type: 'VIDEO' | 'QUIZ' | 'DOCUMENT';
duration_minutes: number;
sort_order: number;
is_sequential: boolean;
prerequisite_lesson_ids: number[] | null;
require_pass_quiz: boolean;
is_published: boolean;
created_at: string;
updated_at: string;
quiz: QuizResponse | null;
}
export interface QuizResponse {
id: number;
lesson_id: number;
title: { en: string; th: string };
description: { en: string; th: string };
passing_score: number;
time_limit: number;
shuffle_questions: boolean;
shuffle_choices: boolean;
show_answers_after_completion: boolean;
}
// Mock course detail
const MOCK_COURSE_DETAIL: CourseDetailResponse = {
...MOCK_COURSES[0],
chapters: [
{
id: 1,
course_id: 1,
title: { en: 'Chapter 1: Getting Started', th: 'บทที่ 1: เริ่มต้น' },
description: { en: 'Introduction to JavaScript', th: 'แนะนำ JavaScript' },
sort_order: 1,
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
lessons: [
{
id: 1,
chapter_id: 1,
title: { en: 'What is JavaScript', th: 'JavaScript คืออะไร' },
content: { en: 'Introduction', th: 'แนะนำ' },
type: 'VIDEO',
duration_minutes: 15,
sort_order: 1,
is_sequential: true,
prerequisite_lesson_ids: null,
require_pass_quiz: false,
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
quiz: null
},
{
id: 2,
chapter_id: 1,
title: { en: 'Variables', th: 'ตัวแปร' },
content: { en: 'Learn variables', th: 'เรียนรู้ตัวแปร' },
type: 'VIDEO',
duration_minutes: 20,
sort_order: 2,
is_sequential: true,
prerequisite_lesson_ids: null,
require_pass_quiz: false,
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
quiz: null
},
{
id: 3,
chapter_id: 1,
title: { en: 'Chapter 1 Quiz', th: 'แบบทดสอบบทที่ 1' },
content: null,
type: 'QUIZ',
duration_minutes: 10,
sort_order: 3,
is_sequential: true,
prerequisite_lesson_ids: null,
require_pass_quiz: true,
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
quiz: {
id: 1,
lesson_id: 3,
title: { en: 'Chapter 1 Quiz', th: 'แบบทดสอบบทที่ 1' },
description: { en: 'Test your knowledge', th: 'ทดสอบความรู้' },
passing_score: 70,
time_limit: 10,
shuffle_questions: true,
shuffle_choices: true,
show_answers_after_completion: true
}
}
]
},
{
id: 2,
course_id: 1,
title: { en: 'Chapter 2: Functions', th: 'บทที่ 2: ฟังก์ชัน' },
description: { en: 'Learn about functions', th: 'เรียนรู้เกี่ยวกับฟังก์ชัน' },
sort_order: 2,
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
lessons: [
{
id: 4,
chapter_id: 2,
title: { en: 'Creating Functions', th: 'การสร้างฟังก์ชัน' },
content: { en: 'How to create functions', th: 'วิธีสร้างฟังก์ชัน' },
type: 'VIDEO',
duration_minutes: 25,
sort_order: 1,
is_sequential: true,
prerequisite_lesson_ids: null,
require_pass_quiz: false,
is_published: true,
created_at: '2024-01-15T00:00:00Z',
updated_at: '2024-01-15T00:00:00Z',
quiz: null
}
]
}
]
};