215 lines
6.2 KiB
Plaintext
Executable File
215 lines
6.2 KiB
Plaintext
Executable File
// 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
|
|
}
|
|
|
|
// --- 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
|
|
}
|
|
|
|
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[]
|
|
|
|
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[]
|
|
}
|
|
|
|
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?
|
|
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)
|
|
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])
|
|
} |