feat: add course catalog, enrollment, progress tracking, quizzes, and reviews

Backend changes:
- Add Enrollment and LessonProgress models to track user progress
- Add UserRole enum (USER, MODERATOR, ADMIN)
- Add course verification and moderation fields
- New CatalogModule: public course browsing, publishing, verification
- New EnrollmentModule: enroll, progress tracking, quiz submission, reviews
- Add quiz generation endpoint to LessonsController

Frontend changes:
- Redesign course viewer: proper course UI with lesson navigation, progress bar
- Add beautiful typography styles for course content (prose-course)
- Fix first-login bug with token exchange retry logic
- New pages: /catalog (public courses), /catalog/[id] (course details), /learning (enrollments)
- Add LessonQuiz component with scoring and results
- Update sidebar navigation: add Catalog and My Learning links
- Add publish/verify buttons in course editor
- Integrate enrollment progress tracking with backend

All courses now support: sequential progression, quiz tests, reviews, ratings,
author verification badges, and full marketplace publishing workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-02-06 10:44:05 +00:00
parent dab726e8d1
commit 2ed65f5678
23 changed files with 1796 additions and 78 deletions

View File

@ -30,10 +30,15 @@ model User {
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Role
role UserRole @default(USER)
// Relations
settings UserSettings?
subscription Subscription?
courses Course[] @relation("AuthoredCourses")
enrollments Enrollment[]
lessonProgress LessonProgress[]
purchases Purchase[]
reviews Review[]
generations CourseGeneration[]
@ -65,6 +70,12 @@ model UserSettings {
@@map("user_settings")
}
enum UserRole {
USER
MODERATOR
ADMIN
}
// ============================================
// Subscription & Payments
// ============================================
@ -113,7 +124,9 @@ model Subscription {
enum CourseStatus {
DRAFT
GENERATING
PENDING_REVIEW
PUBLISHED
REJECTED
ARCHIVED
}
@ -130,11 +143,14 @@ model Course {
// Status
status CourseStatus @default(DRAFT)
// Marketplace (future)
// Marketplace
isPublished Boolean @default(false) @map("is_published")
price Decimal? @db.Decimal(10, 2) // null = private course
price Decimal? @db.Decimal(10, 2) // null = free course
currency String @default("USD")
// Author verification — author checked the content and vouches for quality
isVerified Boolean @default(false) @map("is_verified")
// Categorization
categoryId String? @map("category_id")
tags String[] @default([])
@ -155,10 +171,15 @@ model Course {
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Moderation
moderationNote String? @db.Text @map("moderation_note")
moderatedAt DateTime? @map("moderated_at")
// Relations
author User @relation("AuthoredCourses", fields: [authorId], references: [id], onDelete: Cascade)
category Category? @relation(fields: [categoryId], references: [id])
chapters Chapter[]
enrollments Enrollment[]
purchases Purchase[]
reviews Review[]
generation CourseGeneration?
@ -360,3 +381,53 @@ model Review {
@@index([courseId])
@@map("reviews")
}
// ============================================
// Enrollment & Progress
// ============================================
model Enrollment {
id String @id @default(uuid())
userId String @map("user_id")
courseId String @map("course_id")
// Progress
progress Int @default(0) // 0-100
completedAt DateTime? @map("completed_at")
// Certificate
certificateUrl String? @map("certificate_url")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
lessons LessonProgress[]
@@unique([userId, courseId])
@@index([userId])
@@index([courseId])
@@map("enrollments")
}
model LessonProgress {
id String @id @default(uuid())
userId String @map("user_id")
enrollmentId String @map("enrollment_id")
lessonId String @map("lesson_id")
completedAt DateTime? @map("completed_at")
quizScore Int? @map("quiz_score") // 0-100
createdAt DateTime @default(now()) @map("created_at")
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
enrollment Enrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade)
@@unique([userId, lessonId])
@@index([enrollmentId])
@@map("lesson_progress")
}