Gestión de Instructores
Sección titulada «Gestión de Instructores»TalentBricksAI incluye un sistema completo de perfiles de instructores que permite asociar cursos a personas específicas y mostrar sus perfiles públicos.
Modelo de Datos
Sección titulada «Modelo de Datos»El modelo Instructor en schema.prisma:
model Instructor { id String @id @default(uuid()) name String slug String @unique // URL-friendly: "juan-perez" title String? // "Senior Data Engineer" bio String? @db.Text // Biografía larga avatarUrl String? // URL de foto de perfil linkedinUrl String? githubUrl String? twitterUrl String? isPublished Boolean @default(true) // false = oculto del sitio público courses Course[] // cursos vinculados createdAt DateTime @default(now()) updatedAt DateTime @updatedAt}Relación con Course: un curso puede tener cero o un instructor (instructorId opcional en
Course).
Operaciones Wasp
Sección titulada «Operaciones Wasp»Las operaciones están declaradas en main.wasp e implementadas en app/src/admin/operations.ts.
Queries
Sección titulada «Queries»| Query | Descripción | Requiere Admin |
|---|---|---|
getAllInstructors | Lista todos los instructores con conteo de cursos y estudiantes | No (público) |
getAdminInstructorDetail | Detalle completo con cursos vinculados | Sí |
Actions
Sección titulada «Actions»| Action | Descripción | Requiere Admin |
|---|---|---|
createInstructor | Crea un nuevo perfil | Sí |
updateInstructor | Actualiza datos del perfil | Sí |
deleteInstructor | Elimina el perfil (desvincula cursos primero) | Sí |
Administración desde el Panel Admin
Sección titulada «Administración desde el Panel Admin»Acceder a Instructores
Sección titulada «Acceder a Instructores»Ruta: /admin/instructors — visible en el sidebar del admin.
Crear un Instructor
Sección titulada «Crear un Instructor»- Clic en “Nuevo Instructor”
- Completar campos requeridos (nombre, slug)
- El slug se genera automáticamente desde el nombre — puede editarse manualmente
- Añadir título profesional, biografía y foto de perfil (URL externa)
- Añadir links sociales (LinkedIn, GitHub, Twitter)
- Activar/desactivar “Publicado”
- Guardar
Reglas del slug:
- Solo minúsculas, números y guiones
- Único en todo el sistema
- Generado automáticamente: “Juan Pérez” →
juan-perez - Se puede editar antes de guardar
Editar un Instructor
Sección titulada «Editar un Instructor»Desde la lista → ícono de edición (lápiz). Abre el mismo formulario con datos pre-poblados.
El slug no se puede editar una vez el instructor está publicado (rompería URLs públicas). Para cambiarlo, desactivar primero.
Vincular Cursos a un Instructor
Sección titulada «Vincular Cursos a un Instructor»- Desde la lista → ícono de ojo para abrir la página de detalle del instructor
- En la sección “Cursos” → “Vincular Curso”
- Seleccionar un curso del dropdown (solo muestra cursos no vinculados)
- Confirmar
También puedes crear un nuevo curso directamente desde aquí con “Nuevo Curso” — pasa el
instructorId como parámetro.
Desvincular un Curso
Sección titulada «Desvincular un Curso»Desde la página de detalle del instructor → ícono de desvinculación (cadena rota) junto al curso.
Desvincular no borra el curso — solo elimina la relación. El campo
instructorIden el curso queda ennull.
Eliminar un Instructor
Sección titulada «Eliminar un Instructor»Solo disponible desde la lista de instructores (ícono papelera). No es posible si tiene cursos vinculados — desvincular primero.
Integración en Páginas de Cursos
Sección titulada «Integración en Páginas de Cursos»Cuando un curso tiene instructor asignado, la página del curso muestra:
- Foto de perfil del instructor
- Nombre y título profesional
- Enlace al perfil completo del instructor
Si no hay instructor asignado, se muestra el campo instructorName del curso (texto plano, default:
“TalentBricksAI”).
Ejemplo de Uso en Componentes
Sección titulada «Ejemplo de Uso en Componentes»Mostrar instructor en un card de curso
Sección titulada «Mostrar instructor en un card de curso»import { useQuery, getAllInstructors } from "wasp/client/operations";
function InstructorBadge({ instructorId }: { instructorId: string | null }) { const { data: instructors } = useQuery(getAllInstructors);
if (!instructorId || !instructors) return null;
const instructor = instructors.find(i => i.id === instructorId); if (!instructor) return null;
return ( <div className="flex items-center gap-2"> {instructor.avatarUrl && ( <img src={instructor.avatarUrl} alt={instructor.name} className="w-8 h-8 rounded-full" /> )} <div> <p className="text-sm font-medium">{instructor.name}</p> {instructor.title && <p className="text-xs text-muted-foreground">{instructor.title}</p>} </div> </div> );}Estadísticas de Instructor
Sección titulada «Estadísticas de Instructor»La query getAllInstructors retorna campos calculados:
{ id: string; name: string; slug: string; title: string | null; avatarUrl: string | null; isPublished: boolean; _count: { courses: number; // número de cursos vinculados } totalStudents: number; // suma de enrollments de todos sus cursos}Archivos Clave
Sección titulada «Archivos Clave»| Archivo | Descripción |
|---|---|
app/schema.prisma | Modelo Instructor (línea ~166) |
app/src/admin/dashboards/instructors/AdminInstructorsPage.tsx | Lista y CRUD |
app/src/admin/dashboards/instructors/AdminInstructorDetailPage.tsx | Detalle + vinculación de cursos |
app/src/admin/operations.ts | Implementación de queries y actions |
app/main.wasp | Declaraciones de operaciones + rutas admin |
Consideraciones de Producción
Sección titulada «Consideraciones de Producción»- Las fotos de perfil usan URLs externas — hostear en un CDN propio (ej. AWS S3) en producción
- Los slugs son públicos en URLs — elegir slugs permanentes antes de publicar
- Los instructores con
isPublished: falseno aparecen en el sitio público pero sí en el admin