feat: phase1 platform upgrade with moderation, dev payments, admin panel and landing updates

This commit is contained in:
root
2026-02-06 17:26:53 +00:00
parent 4ca66ea896
commit 979adb9d3d
54 changed files with 2687 additions and 318 deletions

View File

@ -183,6 +183,13 @@ class ApiClient {
return this.request<any>(`/courses/${courseId}/lessons/${lessonId}/quiz`);
}
async generateLessonHomework(courseId: string, lessonId: string, type?: 'TEXT' | 'FILE' | 'PROJECT' | 'QUIZ' | 'GITHUB') {
return this.request<any>(`/courses/${courseId}/lessons/${lessonId}/homework/generate`, {
method: 'POST',
body: JSON.stringify({ type }),
});
}
// Generation
async startGeneration(prompt: string) {
return this.request<{ id: string; status: string; progress: number }>('/generation/start', {
@ -290,10 +297,15 @@ class ApiClient {
return this.request<any>(`/enrollment/${courseId}/lessons/${lessonId}/homework`);
}
async submitLessonHomework(courseId: string, lessonId: string, content: string) {
async submitLessonHomework(
courseId: string,
lessonId: string,
data: { content?: string; type?: string; attachmentUrl?: string; githubUrl?: string } | string
) {
const payload = typeof data === 'string' ? { content: data } : data;
return this.request<any>(`/enrollment/${courseId}/lessons/${lessonId}/homework`, {
method: 'POST',
body: JSON.stringify({ content }),
body: JSON.stringify(payload),
});
}
@ -340,18 +352,19 @@ class ApiClient {
return this.request<any>(`/groups/course/${courseId}/default`);
}
async getGroupMessages(groupId: string) {
return this.request<any[]>(`/groups/${groupId}/messages`);
async getGroupMessages(groupId: string, lessonId?: string) {
const query = lessonId ? `?lessonId=${encodeURIComponent(lessonId)}` : '';
return this.request<any[]>(`/groups/${groupId}/messages${query}`);
}
async getGroupMembers(groupId: string) {
return this.request<any[]>(`/groups/${groupId}/members`);
}
async sendGroupMessage(groupId: string, content: string) {
async sendGroupMessage(groupId: string, content: string, lessonId?: string) {
return this.request<any>(`/groups/${groupId}/messages`, {
method: 'POST',
body: JSON.stringify({ content }),
body: JSON.stringify({ content, lessonId }),
});
}
@ -431,6 +444,17 @@ class ApiClient {
return this.request<any[]>('/moderation/pending');
}
async getModerationCoursePreview(courseId: string) {
return this.request<any>(`/moderation/${courseId}/preview`);
}
async previewModerationQuiz(courseId: string, lessonId: string, answers?: number[]) {
return this.request<any>(`/moderation/${courseId}/quiz-preview`, {
method: 'POST',
body: JSON.stringify({ lessonId, answers }),
});
}
async approveModerationCourse(courseId: string, note?: string) {
return this.request<any>(`/moderation/${courseId}/approve`, {
method: 'POST',
@ -451,6 +475,80 @@ class ApiClient {
});
}
async getAdminUsers(params?: { search?: string; role?: string; limit?: number }) {
const searchParams = new URLSearchParams();
if (params?.search) searchParams.set('search', params.search);
if (params?.role) searchParams.set('role', params.role);
if (params?.limit) searchParams.set('limit', String(params.limit));
const query = searchParams.toString();
return this.request<any[]>(`/admin/users${query ? `?${query}` : ''}`);
}
async updateAdminUserRole(userId: string, role: 'USER' | 'MODERATOR' | 'ADMIN') {
return this.request<any>(`/admin/users/${userId}/role`, {
method: 'PATCH',
body: JSON.stringify({ role }),
});
}
async getAdminPayments(params?: {
mode?: 'DEV' | 'PROD';
provider?: 'STRIPE' | 'YOOMONEY';
status?: string;
search?: string;
limit?: number;
}) {
const searchParams = new URLSearchParams();
if (params?.mode) searchParams.set('mode', params.mode);
if (params?.provider) searchParams.set('provider', params.provider);
if (params?.status) searchParams.set('status', params.status);
if (params?.search) searchParams.set('search', params.search);
if (params?.limit) searchParams.set('limit', String(params.limit));
const query = searchParams.toString();
return this.request<any[]>(`/admin/payments${query ? `?${query}` : ''}`);
}
async submitCooperationRequest(data: {
organization: string;
contactName: string;
email: string;
phone?: string;
role?: string;
organizationType?: string;
message: string;
}) {
return this.request<any>('/cooperation/requests', {
method: 'POST',
body: JSON.stringify(data),
});
}
async uploadCourseSource(courseId: string, file: File) {
const token = getApiToken();
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_URL}/courses/${courseId}/sources/upload`, {
method: 'POST',
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
body: formData,
});
if (!response.ok) {
const error = await response.json().catch(() => ({ message: 'Upload failed' }));
throw new Error(error.message || `HTTP ${response.status}`);
}
return response.json();
}
async getCourseSources(courseId: string) {
return this.request<any[]>(`/courses/${courseId}/sources`);
}
async getCourseSourceOutlineHints(courseId: string) {
return this.request<any>(`/courses/${courseId}/sources/outline-hints`);
}
// Search
async searchCourses(query: string, filters?: { category?: string; difficulty?: string }) {
const searchParams = new URLSearchParams({ q: query });