// prisma/schema.prisma generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" } // --- ENUMS --- enum UserRole { SUPER_ADMIN // You (Full Access) ORG_ADMIN // Law Firm Manager (View Team Progress, Manage Seats) TEACHER // Content Creator (Create/Edit their own courses) LEARNER // Student (View content, take quizzes) } enum ContentStatus { DRAFT PUBLISHED ARCHIVED } enum ProficiencyLevel { BEGINNER // A1-A2 INTERMEDIATE // B1-B2 ADVANCED // C1-C2 EXPERT // Legal Specific } enum ExerciseType { MULTIPLE_CHOICE FILL_IN_BLANK TRANSLATION MATCHING } enum MiniGameDifficulty { BEGINNER INTERMEDIATE ADVANCED } // --- MODELS --- model Profile { id String @id @default(uuid()) // Links to Supabase Auth.uid() email String @unique fullName String? avatarUrl String? role UserRole @default(LEARNER) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relationships memberships Membership[] // B2B Access enrollments Enrollment[] // B2C Purchases (Mercado Pago) progress UserProgress[] certificates Certificate[] authoredCourses Course[] @relation("CourseAuthor") // Teachers own courses miniGameAttempts MiniGameAttempt[] recommendations StudyRecommendation[] } model Company { id String @id @default(uuid()) name String logoUrl String? billingEmail String? maxSeats Int @default(5) // Limit for B2B plans createdAt DateTime @default(now()) updatedAt DateTime @updatedAt memberships Membership[] certificates Certificate[] } model Membership { id String @id @default(uuid()) userId String companyId String isActive Boolean @default(true) role String @default("MEMBER") // Internal org role (Member/Admin) joinedAt DateTime @default(now()) updatedAt DateTime @updatedAt user Profile @relation(fields: [userId], references: [id], onDelete: Cascade) company Company @relation(fields: [companyId], references: [id], onDelete: Cascade) @@unique([userId, companyId]) // Prevent duplicate memberships } model Course { id String @id @default(uuid()) title Json // { "en": "...", "es": "..." } slug String @unique // Required for SEO URLs (e.g. /course/contract-law) description Json // { "en": "...", "es": "..." } level ProficiencyLevel @default(INTERMEDIATE) tags String[] learningOutcomes Json? // "What you will learn"; array of strings now, i18n object later status ContentStatus @default(DRAFT) price Decimal @default(0.00) // Price in MXN authorId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Prerequisites prerequisiteId String? prerequisite Course? @relation("CoursePrerequisites", fields: [prerequisiteId], references: [id]) dependents Course[] @relation("CoursePrerequisites") author Profile @relation("CourseAuthor", fields: [authorId], references: [id]) modules Module[] certificates Certificate[] enrollments Enrollment[] recommendations StudyRecommendation[] } model Enrollment { id String @id @default(uuid()) userId String courseId String // Payment Proof (Mercado Pago) amountPaid Decimal // e.g. 500.00 currency String @default("MXN") paymentMethod String @default("MERCADO_PAGO") externalId String? // The Mercado Pago "payment_id" or "preference_id" purchasedAt DateTime @default(now()) user Profile @relation(fields: [userId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@unique([userId, courseId]) // Prevent buying the same course twice } model Module { id String @id @default(uuid()) courseId String title Json orderIndex Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) lessons Lesson[] } model Lesson { id String @id @default(uuid()) moduleId String title Json description Json? slug String? // Optional for direct linking orderIndex Int videoUrl String? youtubeUrl String? estimatedDuration Int // Seconds version Int @default(1) isFreePreview Boolean @default(false) // Marketing hook createdAt DateTime @default(now()) updatedAt DateTime @updatedAt module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade) exercises Exercise[] resources Resource[] userProgress UserProgress[] } model Exercise { id String @id @default(uuid()) lessonId String type ExerciseType content Json // { question: "...", options: [...], answer: "..." } orderIndex Int @default(0) lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) } model Resource { id String @id @default(uuid()) lessonId String fileUrl String displayName Json fileType String lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) } model UserProgress { id String @id @default(uuid()) userId String lessonId String isCompleted Boolean @default(false) score Int? // For quiz results startedAt DateTime @default(now()) finishedAt DateTime? lastPlayedAt DateTime @default(now()) // For "Resume" feature user Profile @relation(fields: [userId], references: [id], onDelete: Cascade) lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade) @@unique([userId, lessonId]) // One progress record per lesson } model Certificate { id String @id @default(uuid()) userId String courseId String companyId String? // Captured for co-branding (Nullable for B2C) certificateNumber String @unique pdfVersion Int @default(1) issuedAt DateTime @default(now()) metadataSnapshot Json // Burn the course name/version here user Profile @relation(fields: [userId], references: [id]) course Course @relation(fields: [courseId], references: [id]) company Company? @relation(fields: [companyId], references: [id]) @@unique([userId, courseId]) } model MiniGame { id String @id @default(uuid()) slug String @unique title String description String isActive Boolean @default(true) difficulty MiniGameDifficulty @default(INTERMEDIATE) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt questions MiniGameQuestion[] attempts MiniGameAttempt[] } model MiniGameQuestion { id String @id @default(uuid()) miniGameId String prompt String choices String[] answerIndex Int orderIndex Int @default(0) miniGame MiniGame @relation(fields: [miniGameId], references: [id], onDelete: Cascade) @@index([miniGameId, orderIndex]) } model MiniGameAttempt { id String @id @default(uuid()) userId String miniGameId String scorePercent Int correctCount Int totalQuestions Int startedAt DateTime @default(now()) completedAt DateTime @default(now()) user Profile @relation(fields: [userId], references: [id], onDelete: Cascade) miniGame MiniGame @relation(fields: [miniGameId], references: [id], onDelete: Cascade) @@index([userId, miniGameId, completedAt]) } model StudyRecommendation { id String @id @default(uuid()) userId String courseId String reason String priority Int @default(0) isActive Boolean @default(true) createdAt DateTime @default(now()) user Profile @relation(fields: [userId], references: [id], onDelete: Cascade) course Course @relation(fields: [courseId], references: [id], onDelete: Cascade) @@index([userId, isActive, priority]) }