/** * Education Service * API client for courses, lessons, quizzes, and gamification */ import axios from 'axios'; import type { CourseListItem, CourseDetail, CourseCategory, CourseModule, LessonDetail, UserEnrollment, EnrollmentWithCourse, CourseFilters, PaginatedResponse, ApiResponse, Quiz, QuizAttempt, QuizResult, GamificationProfile, LevelProgress, StreakStats, Achievement, LeaderboardEntry, UserLeaderboardPosition, GamificationSummary, } from '../types/education.types'; const API_BASE_URL = import.meta.env?.VITE_API_URL || '/api/v1'; const api = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json', }, }); // Add auth token to requests api.interceptors.request.use((config) => { const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // ============================================================================ // Categories // ============================================================================ export async function getCategories(): Promise { const response = await api.get>('/education/categories'); return response.data.data; } export async function createCategory(data: { name: string; slug: string; description?: string; icon?: string; }): Promise { const response = await api.post>('/education/categories', data); return response.data.data; } // ============================================================================ // Courses // ============================================================================ export async function getCourses( filters?: CourseFilters ): Promise> { const response = await api.get>>( '/education/courses', { params: filters } ); return response.data.data; } export async function getPopularCourses(limit = 6): Promise { const response = await api.get>('/education/courses/popular', { params: { limit }, }); return response.data.data; } export async function getNewCourses(limit = 6): Promise { const response = await api.get>('/education/courses/new', { params: { limit }, }); return response.data.data; } export async function getCourseById(courseId: string): Promise { const response = await api.get>(`/education/courses/${courseId}`); return response.data.data; } export async function getCourseBySlug(slug: string): Promise { const response = await api.get>(`/education/courses/slug/${slug}`); return response.data.data; } export async function getCourseModules(courseId: string): Promise { const response = await api.get>( `/education/courses/${courseId}/modules` ); return response.data.data; } export async function getCourseStats(courseId: string): Promise<{ totalEnrollments: number; completionRate: number; avgRating: number; }> { const response = await api.get< ApiResponse<{ totalEnrollments: number; completionRate: number; avgRating: number }> >(`/education/courses/${courseId}/stats`); return response.data.data; } // ============================================================================ // Lessons // ============================================================================ export async function getModuleLessons( moduleId: string ): Promise<{ id: string; title: string; contentType: string; durationMinutes: number }[]> { const response = await api.get< ApiResponse<{ id: string; title: string; contentType: string; durationMinutes: number }[]> >(`/education/modules/${moduleId}/lessons`); return response.data.data; } export async function getLessonById(lessonId: string): Promise { const response = await api.get>(`/education/lessons/${lessonId}`); return response.data.data; } export async function updateLessonProgress( lessonId: string, data: { videoWatchedSeconds?: number; videoCompleted?: boolean; userNotes?: string; } ): Promise<{ progressId: string; isCompleted: boolean }> { const response = await api.post>( `/education/lessons/${lessonId}/progress`, data ); return response.data.data; } export async function markLessonComplete( lessonId: string ): Promise<{ progressId: string; xpAwarded: number }> { const response = await api.post>( `/education/lessons/${lessonId}/complete` ); return response.data.data; } // ============================================================================ // Enrollments // ============================================================================ export async function getMyEnrollments(): Promise { const response = await api.get>('/education/my/enrollments'); return response.data.data; } export async function getMyLearningStats(): Promise<{ totalEnrollments: number; completedCourses: number; inProgressCourses: number; totalLessonsCompleted: number; totalTimeSpent: number; }> { const response = await api.get< ApiResponse<{ totalEnrollments: number; completedCourses: number; inProgressCourses: number; totalLessonsCompleted: number; totalTimeSpent: number; }> >('/education/my/stats'); return response.data.data; } export async function enrollInCourse(courseId: string): Promise { const response = await api.post>( `/education/courses/${courseId}/enroll` ); return response.data.data; } export async function getEnrollmentStatus(courseId: string): Promise { try { const response = await api.get>( `/education/courses/${courseId}/enrollment` ); return response.data.data; } catch { return null; } } // ============================================================================ // Quizzes // ============================================================================ export async function getQuizByLessonId(lessonId: string): Promise { try { const response = await api.get>(`/education/lessons/${lessonId}/quiz`); return response.data.data; } catch { return null; } } export async function getQuizById(quizId: string): Promise { const response = await api.get>(`/education/quizzes/${quizId}`); return response.data.data; } export async function getCourseQuizzes(courseId: string): Promise { const response = await api.get>(`/education/courses/${courseId}/quizzes`); return response.data.data; } export async function startQuizAttempt( quizId: string, enrollmentId?: string ): Promise<{ attemptId: string; quiz: Quiz; startedAt: string; expiresAt?: string }> { const response = await api.post< ApiResponse<{ attemptId: string; quiz: Quiz; startedAt: string; expiresAt?: string }> >(`/education/quizzes/${quizId}/start`, { enrollmentId }); return response.data.data; } export async function submitQuizAttempt( attemptId: string, answers: { questionId: string; answer: string | string[] }[] ): Promise { const response = await api.post>( `/education/quizzes/attempts/${attemptId}/submit`, { answers } ); return response.data.data; } export async function getQuizResults(attemptId: string): Promise { const response = await api.get>( `/education/quizzes/attempts/${attemptId}/results` ); return response.data.data; } export async function getUserQuizAttempts(quizId: string): Promise { const response = await api.get>( `/education/quizzes/${quizId}/my-attempts` ); return response.data.data; } export async function getQuizStatistics(quizId: string): Promise<{ totalAttempts: number; passRate: number; averageScore: number; }> { const response = await api.get< ApiResponse<{ totalAttempts: number; passRate: number; averageScore: number }> >(`/education/quizzes/${quizId}/stats`); return response.data.data; } export async function getUserQuizStats(): Promise<{ totalAttempts: number; quizzesPassed: number; averageScore: number; bestScore: number; }> { const response = await api.get< ApiResponse<{ totalAttempts: number; quizzesPassed: number; averageScore: number; bestScore: number; }> >('/education/my/quiz-stats'); return response.data.data; } // ============================================================================ // Gamification // ============================================================================ export async function getGamificationProfile(): Promise { const response = await api.get>( '/education/gamification/profile' ); return response.data.data; } export async function getPublicProfile(userId: string): Promise<{ userId: string; totalXp: number; currentLevel: number; currentStreakDays: number; totalCoursesCompleted: number; levelProgress: { currentLevel: number; progressPercentage: number }; }> { const response = await api.get< ApiResponse<{ userId: string; totalXp: number; currentLevel: number; currentStreakDays: number; totalCoursesCompleted: number; levelProgress: { currentLevel: number; progressPercentage: number }; }> >(`/education/gamification/profile/${userId}`); return response.data.data; } export async function getLevelProgress(): Promise { const response = await api.get>( '/education/gamification/level-progress' ); return response.data.data; } export async function getStreakStats(): Promise { const response = await api.get>('/education/gamification/streak'); return response.data.data; } export async function recordDailyActivity(): Promise<{ currentStreak: number; longestStreak: number; streakMaintained: boolean; xpBonusApplied: boolean; }> { const response = await api.post< ApiResponse<{ currentStreak: number; longestStreak: number; streakMaintained: boolean; xpBonusApplied: boolean; }> >('/education/gamification/daily-activity'); return response.data.data; } export async function getMyAchievements(): Promise { const response = await api.get>( '/education/gamification/achievements' ); return response.data.data; } export async function getUserAchievements(userId: string): Promise { const response = await api.get>( `/education/gamification/achievements/${userId}` ); return response.data.data; } export async function getLeaderboard( period: 'all_time' | 'month' | 'week' = 'all_time', limit = 100 ): Promise { const response = await api.get>( '/education/gamification/leaderboard', { params: { period, limit } } ); return response.data.data; } export async function getMyLeaderboardPosition( period: 'all_time' | 'month' | 'week' = 'all_time' ): Promise { const response = await api.get>( '/education/gamification/leaderboard/my-position', { params: { period } } ); return response.data.data; } export async function getNearbyUsers( period: 'all_time' | 'month' | 'week' = 'all_time', radius = 5 ): Promise { const response = await api.get>( '/education/gamification/leaderboard/nearby', { params: { period, radius } } ); return response.data.data; } export async function getGamificationSummary(): Promise { const response = await api.get>( '/education/gamification/summary' ); return response.data.data; } // ============================================================================ // Admin/Instructor Operations // ============================================================================ export async function createCourse(data: { title: string; slug: string; description?: string; shortDescription?: string; categoryId?: string; difficultyLevel: 'beginner' | 'intermediate' | 'advanced'; isFree: boolean; priceUsd?: number; thumbnailUrl?: string; learningObjectives?: string[]; requirements?: string[]; tags?: string[]; }): Promise { const response = await api.post>('/education/courses', data); return response.data.data; } export async function updateCourse( courseId: string, data: Partial<{ title: string; description: string; shortDescription: string; categoryId: string; difficultyLevel: 'beginner' | 'intermediate' | 'advanced'; isFree: boolean; priceUsd: number; thumbnailUrl: string; learningObjectives: string[]; requirements: string[]; tags: string[]; }> ): Promise { const response = await api.patch>( `/education/courses/${courseId}`, data ); return response.data.data; } export async function deleteCourse(courseId: string): Promise { await api.delete(`/education/courses/${courseId}`); } export async function publishCourse(courseId: string): Promise { const response = await api.post>( `/education/courses/${courseId}/publish` ); return response.data.data; } export async function createModule( courseId: string, data: { title: string; description?: string; sortOrder?: number; } ): Promise { const response = await api.post>( `/education/courses/${courseId}/modules`, data ); return response.data.data; } export async function deleteModule(moduleId: string): Promise { await api.delete(`/education/modules/${moduleId}`); } export async function createLesson( moduleId: string, data: { title: string; description?: string; contentType: 'video' | 'text' | 'quiz' | 'exercise'; durationMinutes?: number; sortOrder?: number; isFree?: boolean; videoUrl?: string; contentHtml?: string; contentMarkdown?: string; } ): Promise { const response = await api.post>( `/education/modules/${moduleId}/lessons`, data ); return response.data.data; } export async function deleteLesson(lessonId: string): Promise { await api.delete(`/education/lessons/${lessonId}`); } export async function createQuiz(data: { courseId: string; lessonId?: string; title: string; description?: string; passingScore?: number; maxAttempts?: number; timeLimitMinutes?: number; shuffleQuestions?: boolean; showCorrectAnswers?: boolean; }): Promise { const response = await api.post>('/education/quizzes', data); return response.data.data; } export async function deleteQuiz(quizId: string): Promise { await api.delete(`/education/quizzes/${quizId}`); } export async function createQuizQuestion( quizId: string, data: { questionType: 'multiple_choice' | 'multiple_answer' | 'true_false' | 'short_answer'; questionText: string; explanation?: string; options?: { text: string; isCorrect: boolean }[]; points?: number; sortOrder?: number; } ): Promise<{ id: string }> { const response = await api.post>( `/education/quizzes/${quizId}/questions`, data ); return response.data.data; } export async function deleteQuizQuestion(questionId: string): Promise { await api.delete(`/education/questions/${questionId}`); } // Export service object for convenience export const educationService = { // Categories getCategories, createCategory, // Courses getCourses, getPopularCourses, getNewCourses, getCourseById, getCourseBySlug, getCourseModules, getCourseStats, createCourse, updateCourse, deleteCourse, publishCourse, // Modules createModule, deleteModule, // Lessons getModuleLessons, getLessonById, updateLessonProgress, markLessonComplete, createLesson, deleteLesson, // Enrollments getMyEnrollments, getMyLearningStats, enrollInCourse, getEnrollmentStatus, // Quizzes getQuizByLessonId, getQuizById, getCourseQuizzes, startQuizAttempt, submitQuizAttempt, getQuizResults, getUserQuizAttempts, getQuizStatistics, getUserQuizStats, createQuiz, deleteQuiz, createQuizQuestion, deleteQuizQuestion, // Gamification getGamificationProfile, getPublicProfile, getLevelProgress, getStreakStats, recordDailyActivity, getMyAchievements, getUserAchievements, getLeaderboard, getMyLeaderboardPosition, getNearbyUsers, getGamificationSummary, }; export default educationService;