export interface Course { id: number title: string | { th: string; en: string } slug: string description: string | { th: string; en: string } thumbnail_url: string price: string is_free: boolean have_certificate: boolean status: string 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' 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 } 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 } export const useCourse = () => { const config = useRuntimeConfig() const API_BASE_URL = config.public.apiBase as string const { token } = useAuth() const fetchCourses = async () => { try { const data = await $fetch(`${API_BASE_URL}/courses`, { method: 'GET', 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' } } } const fetchCourseById = async (id: number) => { try { const data = await $fetch(`${API_BASE_URL}/courses/${id}`, { method: 'GET', headers: token.value ? { Authorization: `Bearer ${token.value}` } : {} }) // API might return an array (list) or single object let courseData: any = null if (Array.isArray(data.data)) { // Try to find the matching course ID in the array courseData = data.data.find((c: any) => c.id == id) // Fallback: If not found, and array has length 1, it might be the one (if ID mismatch isn't the issue) // But generally, we should expect a match. If not match, maybe the API returned a generic list. 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' } } } 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) // Check for 409 Conflict (Already Enrolled) // ofetch/h3 error properties might vary, check common ones 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 } } } 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' } } } 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 } } } 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 } } } 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 } } } 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 } } } 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 } } } 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 } }