Skip to content

TalentBricksAI usa PostgreSQL con Prisma ORM. Los modelos se definen en app/schema.prisma.

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ User │────▶│ Enrollment │◀────│ Course │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Certificate │ │LessonProgress│◀───│ Lesson │
└─────────────┘ └─────────────┘ └─────────────┘
model Course {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
slug String @unique
description String
thumbnail String?
price Int // en centavos (USD)
currency String @default("USD")
difficulty Difficulty @default(BEGINNER)
category String
isPublished Boolean @default(false)
instructorName String @default("TalentBricksAI")
lessons Lesson[]
enrollments Enrollment[]
certificates Certificate[]
}
enum Difficulty {
BEGINNER
INTERMEDIATE
ADVANCED
}
CampoTipoDescripcion
idIntID autoincremental
slugStringURL-friendly identifier, unico
priceIntPrecio en centavos (2900 = $29.00)
difficultyEnumBEGINNER, INTERMEDIATE, ADVANCED
isPublishedBooleanSolo cursos publicados son visibles
instructorNameStringNombre del instructor del curso
model Lesson {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
courseId Int
course Course @relation(fields: [courseId], references: [id])
title String
description String?
order Int
duration Int // en segundos
videoUrl String
content String? // markdown content
isPreview Boolean @default(false)
progress LessonProgress[]
}
CampoTipoDescripcion
orderIntOrden de la leccion en el curso
durationIntDuracion en segundos
videoUrlStringURL del video (S3/CloudFront)
isPreviewBooleanLecciones preview son accesibles sin enrollment
model Enrollment {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
userId Int
user User @relation(fields: [userId], references: [id])
courseId Int
course Course @relation(fields: [courseId], references: [id])
completedAt DateTime?
progress LessonProgress[]
@@unique([userId, courseId])
}

El constraint @@unique([userId, courseId]) asegura que un usuario solo puede tener una inscripcion por curso.

model LessonProgress {
id Int @id @default(autoincrement())
updatedAt DateTime @updatedAt
userId Int
lessonId Int
lesson Lesson @relation(fields: [lessonId], references: [id])
enrollmentId Int
enrollment Enrollment @relation(fields: [enrollmentId], references: [id])
watchedSeconds Int @default(0)
isCompleted Boolean @default(false)
@@unique([userId, lessonId])
}
CampoTipoDescripcion
watchedSecondsIntSegundos vistos del video
isCompletedBooleanLeccion marcada como completada
model Certificate {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
userId String
user User @relation(fields: [userId], references: [id])
courseId Int
course Course @relation(fields: [courseId], references: [id])
verificationCode String @unique @default(uuid())
// Snapshot data (capturado al momento de generacion)
studentName String
courseTitle String
instructorName String
totalDurationMinutes Int
@@unique([userId, courseId])
@@index([verificationCode])
}
CampoTipoDescripcion
verificationCodeStringUUID unico para verificacion publica
studentNameStringNombre del estudiante (snapshot)
courseTitleStringTitulo del curso (snapshot)
instructorNameStringNombre del instructor (snapshot)
totalDurationMinutesIntDuracion total en minutos (snapshot)
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
// Campos existentes
isAdmin Boolean @default(false)
subscriptionStatus String?
// ...otros campos existentes
// Nuevas relaciones para cursos
enrollments Enrollment[]
certificates Certificate[]
}
Terminal window
# Iniciar PostgreSQL (dejar corriendo)
wasp db start
# Crear migracion despues de cambiar schema.prisma
wasp db migrate-dev
# Abrir Prisma Studio (GUI para ver datos)
wasp db studio
# Ejecutar seed script
wasp db seed
const courses = await context.entities.Course.findMany({
where: { isPublished: true },
include: {
lessons: {
orderBy: { order: 'asc' }
}
}
});

Obtener enrollments de un usuario con progreso

Section titled “Obtener enrollments de un usuario con progreso”
const enrollments = await context.entities.Enrollment.findMany({
where: { userId: context.user.id },
include: {
course: {
include: {
lessons: true
}
},
progress: true
}
});
const enrollment = await context.entities.Enrollment.findUnique({
where: { userId_courseId: { userId, courseId } },
include: {
course: { include: { lessons: true } },
progress: { where: { isCompleted: true } }
}
});
const totalLessons = enrollment.course.lessons.length;
const completedLessons = enrollment.progress.length;
const progressPercent = Math.round((completedLessons / totalLessons) * 100);

Para optimizar consultas frecuentes:

model Course {
// ...
@@index([isPublished])
@@index([category])
}
model Lesson {
// ...
@@index([courseId, order])
}
model Enrollment {
// ...
@@index([userId])
@@index([courseId])
}