// Interface สำหรับข้อมูลคอร์สเรียน (Public Course Data) export interface Course { id: number title: string | { th: string; en: string } // รองรับ 2 ภาษา slug: string description: string | { th: string; en: string } thumbnail_url: string price: string is_free: boolean have_certificate: boolean status: string // DRAFT, PUBLISHED category_id: number created_at?: string updated_at?: string created_by?: number updated_by?: number approved_at?: string approved_by?: number rejection_reason?: string rating?: string lessons?: number | string levelType?: 'neutral' | 'warning' | 'success' // ใช้สำหรับการแสดงผล Badge ระดับความยาก (Frontend Logic) // โครงสร้างบทเรียน (Chapters & Lessons) chapters?: { id: number title: string | { th: string; en: string } lessons: { id: number title: string | { th: string; en: string } duration_minutes: number video_url?: string }[] }[] } interface CourseResponse { code: number message: string data: Course[] total: number } // Interface สำหรับคอร์สที่ผู้ใช้ลงทะเบียนเรียนแล้ว (My Course) export interface EnrolledCourse { id: number course_id: number course: Course status: 'ENROLLED' | 'IN_PROGRESS' | 'COMPLETED' | 'DROPPED' progress_percentage: number enrolled_at: string started_at?: string completed_at?: string last_accessed_at?: string } interface EnrolledCourseResponse { code: number message: string data: EnrolledCourse[] total: number page: number limit: number } // ========================================== // Composable: useCourse // หน้าที่: จัดการ Logic ทุกอย่างเกี่ยวกับคอร์สเรียน // - ดึงข้อมูลคอร์ส (Public & Protected) // - ลงทะเบียนเรียน (Enroll) // - ติดตามความคืบหน้าการเรียน (Progress tracking) // ========================================== export const useCourse = () => { const config = useRuntimeConfig() const API_BASE_URL = config.public.apiBase as string const { token } = useAuth() // ฟังก์ชันดึงรายชื่อคอร์สทั้งหมด (Catalog) // ใช้สำหรับหน้า Discover/Browse // Endpoint: GET /courses const fetchCourses = async () => { try { const data = await $fetch(`${API_BASE_URL}/courses`, { method: 'GET', // ส่ง Token ไปด้วยถ้ามี (เผื่อ Logic ในอนาคตที่ต้องเช็คสิทธิ์) headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data || [], total: data.total || 0 } } catch (err: any) { console.error('Fetch courses failed:', err) return { success: false, error: err.data?.message || err.message || 'Error fetching courses' } } } // ฟังก์ชันดึงรายละเอียดคอร์สตาม ID // Endpoint: GET /courses/:id const fetchCourseById = async (id: number) => { try { const data = await $fetch(`${API_BASE_URL}/courses/${id}`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) // Logic จัดการข้อมูลที่ได้รับ (API อาจส่งกลับมาเป็น Array หรือ Object) let courseData: any = null if (Array.isArray(data.data)) { // ถ้าเป็น Array ให้หาอันที่ ID ตรงกัน courseData = data.data.find((c: any) => c.id == id) // Fallback: ถ้าหาไม่เจอ แต่มีข้อมูลตัวเดียว อาจจะเป็นตัวนั้น if (!courseData && data.data.length === 1) { courseData = data.data[0] } } else { courseData = data.data } if (!courseData) throw new Error('Course not found') return { success: true, data: courseData } } catch (err: any) { console.error('Fetch course details failed:', err) return { success: false, error: err.data?.message || err.message || 'Error fetching course details' } } } // ฟังก์ชันลงทะเบียนเรียน (Enroll) // Endpoint: POST /students/courses/:id/enroll const enrollCourse = async (courseId: number) => { try { const data = await $fetch<{ code: number; message: string; data: any }>(`${API_BASE_URL}/students/courses/${courseId}/enroll`, { method: 'POST', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data, message: data.message } } catch (err: any) { console.error('Enroll course failed:', err) // เช็ค Error 409 Conflict (กรณีลงทะเบียนไปแล้ว) const status = err.statusCode || err.status || err.response?.status if (status === 409) { return { success: false, error: 'ท่านได้ลงทะเบียนไปแล้ว', code: 409 } } return { success: false, error: err.data?.message || err.message || 'Error enrolling in course', code: err.data?.code } } } // ฟังก์ชันดึงคอร์สที่ฉันลงทะเบียนเรียน (My Courses) // รองรับ Pagination และการกรอง Status (ENROLLED, IN_PROGRESS, COMPLETED) const fetchEnrolledCourses = async (params: { page?: number; limit?: number; status?: string } = {}) => { try { const queryParams = new URLSearchParams() if (params.page) queryParams.append('page', params.page.toString()) if (params.limit) queryParams.append('limit', params.limit.toString()) if (params.status && params.status !== 'ALL') queryParams.append('status', params.status) const data = await $fetch(`${API_BASE_URL}/students/courses?${queryParams.toString()}`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data || [], total: data.total || 0, page: data.page, limit: data.limit } } catch (err: any) { console.error('Fetch enrolled courses failed:', err) return { success: false, error: err.data?.message || err.message || 'Error fetching enrolled courses' } } } // ฟังก์ชันดึงข้อมูลสำหรับการเรียน (Chapters, Lessons, Progress) // Endpoint: GET /students/courses/:id/learn const fetchCourseLearningInfo = async (courseId: number) => { try { const data = await $fetch<{ code: number; message: string; data: any }>(`${API_BASE_URL}/students/courses/${courseId}/learn`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data } } catch (err: any) { console.error('Fetch course learning info failed:', err) return { success: false, error: err.data?.message || err.message || 'Error fetching course learning info', code: err.data?.code } } } // ฟังก์ชันดึงเนื้อหาบทเรียน (Video, Content) // Endpoint: GET /students/courses/:cid/lessons/:lid const fetchLessonContent = async (courseId: number, lessonId: number) => { try { const data = await $fetch<{ code: number; message: string; data: any; progress?: any }>(`${API_BASE_URL}/students/courses/${courseId}/lessons/${lessonId}`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data, progress: data.progress } } catch (err: any) { console.error('Fetch lesson content failed:', err) return { success: false, error: err.data?.message || err.message || 'Error fetching lesson content', code: err.data?.code, status: err.status } } } // ฟังก์ชันเช็คสิทธิ์การเข้าถึงบทเรียน (Access Control) // ต้อง Enrolled ก่อนถึงจะเข้าได้ และต้องผ่านเงื่อนไข Prerequisites (ถ้ามี) // Endpoint: GET /students/courses/:cid/lessons/:lid/access-check const checkLessonAccess = async (courseId: number, lessonId: number) => { try { const data = await $fetch<{ code: number; message: string; data: any }>(`${API_BASE_URL}/students/courses/${courseId}/lessons/${lessonId}/access-check`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data } } catch (err: any) { console.error('Check lesson access failed:', err) return { success: false, error: err.data?.message || err.message || 'Error checking lesson access', code: err.data?.code, status: err.status } } } // ฟังก์ชันบันทึกเวลาที่ดูวิดีโอ (Video Progress) // Endpoint: POST /students/lessons/:id/progress const saveVideoProgress = async (lessonId: number, progressSeconds: number, durationSeconds: number) => { try { const data = await $fetch<{ code: number; message: string; data: any }>(`${API_BASE_URL}/students/lessons/${lessonId}/progress`, { method: 'POST', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {}, body: { video_progress_seconds: progressSeconds, video_duration_seconds: durationSeconds } }) return { success: true, data: data.data } } catch (err: any) { console.error('Save video progress failed:', err) return { success: false, error: err.data?.message || err.message || 'Error saving video progress', code: err.data?.code } } } // ฟังก์ชันดึง Video Progress ปัจจุบันของบทเรียน // Endpoint: GET /students/lessons/:id/progress const fetchVideoProgress = async (lessonId: number) => { try { const data = await $fetch<{ code: number; message: string; data: any }>(`${API_BASE_URL}/students/lessons/${lessonId}/progress`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data } } catch (err: any) { console.error('Fetch video progress failed:', err) return { success: false, error: err.data?.message || err.message || 'Error fetching video progress', code: err.data?.code, status: err.status } } } // ฟังก์ชันบันทึกว่าเรียนจบบทเรียนแล้ว (Mark Complete) // Endpoint: POST /students/courses/:cid/lessons/:lid/complete const markLessonComplete = async (courseId: number, lessonId: number) => { try { const data = await $fetch<{ code: number; message: string; data: any }>(`${API_BASE_URL}/students/courses/${courseId}/lessons/${lessonId}/complete`, { method: 'POST', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) return { success: true, data: data.data } } catch (err: any) { console.error('Mark lesson complete failed:', err) return { success: false, error: err.data?.message || err.message || 'Error marking lesson as complete', code: err.data?.code, status: err.status } } } return { fetchCourses, fetchCourseById, enrollCourse, fetchEnrolledCourses, fetchCourseLearningInfo, fetchLessonContent, checkLessonAccess, saveVideoProgress, fetchVideoProgress, markLessonComplete } }