trading-platform-frontend-v2/src/services/education.service.ts
rckrdmrd 5b53c2539a feat: Initial commit - Trading Platform Frontend
React frontend with:
- Authentication UI
- Trading dashboard
- ML signals display
- Portfolio management

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 04:30:39 -06:00

613 lines
17 KiB
TypeScript

/**
* 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<CourseCategory[]> {
const response = await api.get<ApiResponse<CourseCategory[]>>('/education/categories');
return response.data.data;
}
export async function createCategory(data: {
name: string;
slug: string;
description?: string;
icon?: string;
}): Promise<CourseCategory> {
const response = await api.post<ApiResponse<CourseCategory>>('/education/categories', data);
return response.data.data;
}
// ============================================================================
// Courses
// ============================================================================
export async function getCourses(
filters?: CourseFilters
): Promise<PaginatedResponse<CourseListItem>> {
const response = await api.get<ApiResponse<PaginatedResponse<CourseListItem>>>(
'/education/courses',
{ params: filters }
);
return response.data.data;
}
export async function getPopularCourses(limit = 6): Promise<CourseListItem[]> {
const response = await api.get<ApiResponse<CourseListItem[]>>('/education/courses/popular', {
params: { limit },
});
return response.data.data;
}
export async function getNewCourses(limit = 6): Promise<CourseListItem[]> {
const response = await api.get<ApiResponse<CourseListItem[]>>('/education/courses/new', {
params: { limit },
});
return response.data.data;
}
export async function getCourseById(courseId: string): Promise<CourseDetail> {
const response = await api.get<ApiResponse<CourseDetail>>(`/education/courses/${courseId}`);
return response.data.data;
}
export async function getCourseBySlug(slug: string): Promise<CourseDetail> {
const response = await api.get<ApiResponse<CourseDetail>>(`/education/courses/slug/${slug}`);
return response.data.data;
}
export async function getCourseModules(courseId: string): Promise<CourseModule[]> {
const response = await api.get<ApiResponse<CourseModule[]>>(
`/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<LessonDetail> {
const response = await api.get<ApiResponse<LessonDetail>>(`/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<ApiResponse<{ progressId: string; isCompleted: boolean }>>(
`/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<ApiResponse<{ progressId: string; xpAwarded: number }>>(
`/education/lessons/${lessonId}/complete`
);
return response.data.data;
}
// ============================================================================
// Enrollments
// ============================================================================
export async function getMyEnrollments(): Promise<EnrollmentWithCourse[]> {
const response = await api.get<ApiResponse<EnrollmentWithCourse[]>>('/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<UserEnrollment> {
const response = await api.post<ApiResponse<UserEnrollment>>(
`/education/courses/${courseId}/enroll`
);
return response.data.data;
}
export async function getEnrollmentStatus(courseId: string): Promise<UserEnrollment | null> {
try {
const response = await api.get<ApiResponse<UserEnrollment>>(
`/education/courses/${courseId}/enrollment`
);
return response.data.data;
} catch {
return null;
}
}
// ============================================================================
// Quizzes
// ============================================================================
export async function getQuizByLessonId(lessonId: string): Promise<Quiz | null> {
try {
const response = await api.get<ApiResponse<Quiz>>(`/education/lessons/${lessonId}/quiz`);
return response.data.data;
} catch {
return null;
}
}
export async function getQuizById(quizId: string): Promise<Quiz> {
const response = await api.get<ApiResponse<Quiz>>(`/education/quizzes/${quizId}`);
return response.data.data;
}
export async function getCourseQuizzes(courseId: string): Promise<Quiz[]> {
const response = await api.get<ApiResponse<Quiz[]>>(`/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<QuizResult> {
const response = await api.post<ApiResponse<QuizResult>>(
`/education/quizzes/attempts/${attemptId}/submit`,
{ answers }
);
return response.data.data;
}
export async function getQuizResults(attemptId: string): Promise<QuizResult> {
const response = await api.get<ApiResponse<QuizResult>>(
`/education/quizzes/attempts/${attemptId}/results`
);
return response.data.data;
}
export async function getUserQuizAttempts(quizId: string): Promise<QuizAttempt[]> {
const response = await api.get<ApiResponse<QuizAttempt[]>>(
`/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<GamificationProfile & { levelProgress: LevelProgress }> {
const response = await api.get<ApiResponse<GamificationProfile & { levelProgress: LevelProgress }>>(
'/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<LevelProgress> {
const response = await api.get<ApiResponse<LevelProgress>>(
'/education/gamification/level-progress'
);
return response.data.data;
}
export async function getStreakStats(): Promise<StreakStats> {
const response = await api.get<ApiResponse<StreakStats>>('/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<Achievement[]> {
const response = await api.get<ApiResponse<Achievement[]>>(
'/education/gamification/achievements'
);
return response.data.data;
}
export async function getUserAchievements(userId: string): Promise<Achievement[]> {
const response = await api.get<ApiResponse<Achievement[]>>(
`/education/gamification/achievements/${userId}`
);
return response.data.data;
}
export async function getLeaderboard(
period: 'all_time' | 'month' | 'week' = 'all_time',
limit = 100
): Promise<LeaderboardEntry[]> {
const response = await api.get<ApiResponse<LeaderboardEntry[]>>(
'/education/gamification/leaderboard',
{ params: { period, limit } }
);
return response.data.data;
}
export async function getMyLeaderboardPosition(
period: 'all_time' | 'month' | 'week' = 'all_time'
): Promise<UserLeaderboardPosition> {
const response = await api.get<ApiResponse<UserLeaderboardPosition>>(
'/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<LeaderboardEntry[]> {
const response = await api.get<ApiResponse<LeaderboardEntry[]>>(
'/education/gamification/leaderboard/nearby',
{ params: { period, radius } }
);
return response.data.data;
}
export async function getGamificationSummary(): Promise<GamificationSummary> {
const response = await api.get<ApiResponse<GamificationSummary>>(
'/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<CourseDetail> {
const response = await api.post<ApiResponse<CourseDetail>>('/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<CourseDetail> {
const response = await api.patch<ApiResponse<CourseDetail>>(
`/education/courses/${courseId}`,
data
);
return response.data.data;
}
export async function deleteCourse(courseId: string): Promise<void> {
await api.delete(`/education/courses/${courseId}`);
}
export async function publishCourse(courseId: string): Promise<CourseDetail> {
const response = await api.post<ApiResponse<CourseDetail>>(
`/education/courses/${courseId}/publish`
);
return response.data.data;
}
export async function createModule(
courseId: string,
data: {
title: string;
description?: string;
sortOrder?: number;
}
): Promise<CourseModule> {
const response = await api.post<ApiResponse<CourseModule>>(
`/education/courses/${courseId}/modules`,
data
);
return response.data.data;
}
export async function deleteModule(moduleId: string): Promise<void> {
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<LessonDetail> {
const response = await api.post<ApiResponse<LessonDetail>>(
`/education/modules/${moduleId}/lessons`,
data
);
return response.data.data;
}
export async function deleteLesson(lessonId: string): Promise<void> {
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<Quiz> {
const response = await api.post<ApiResponse<Quiz>>('/education/quizzes', data);
return response.data.data;
}
export async function deleteQuiz(quizId: string): Promise<void> {
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<ApiResponse<{ id: string }>>(
`/education/quizzes/${quizId}/questions`,
data
);
return response.data.data;
}
export async function deleteQuizQuestion(questionId: string): Promise<void> {
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;