Saltar al contenido principal

📋 Plan de Ejecución - Módulo 3: Teams & Evaluation Module

Fecha de Creación: 31 de diciembre, 2025
Estado: 📝 Planificado
Dependencias: ✅ Core Layer, ✅ Users Module, ✅ Hackathons Module


🎯 Objetivo del Módulo

Implementar el sistema completo de equipos y evaluación con:

  • Sistema de equipos con códigos de invitación
  • Submissions de proyectos
  • Asignación de jueces (manual)
  • Sistema de scoring por criterio
  • Leaderboard con puntaje ponderado

📊 Análisis de Requerimientos

1. Schema Prisma (5 modelos nuevos)

Model Team

model Team {
id String @id @default(cuid())
hackathonId String
name String
code String @unique // Código de invitación único
description String?

hackathon Hackathon @relation(fields: [hackathonId], references: [id], onDelete: Cascade)
members TeamMember[]
submissions Submission[]

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([hackathonId])
@@index([code])
}

Model TeamMember

model TeamMember {
id String @id @default(cuid())
teamId String
profileId String

team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)

createdAt DateTime @default(now())

@@unique([teamId, profileId])
@@index([teamId])
@@index([profileId])
}

Model Submission

model Submission {
id String @id @default(cuid())
hackathonId String
teamId String
title String
description String @db.Text
repoUrl String?
demoUrl String?
extraLinks Json? // Para links adicionales (opcional)

hackathon Hackathon @relation(fields: [hackathonId], references: [id], onDelete: Cascade)
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
scores Score[]

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([teamId]) // Un equipo solo puede tener una submission
@@index([hackathonId])
@@index([teamId])
}

Model HackathonJudge

model HackathonJudge {
id String @id @default(cuid())
hackathonId String
profileId String

hackathon Hackathon @relation(fields: [hackathonId], references: [id], onDelete: Cascade)
profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)

createdAt DateTime @default(now())

@@unique([hackathonId, profileId])
@@index([hackathonId])
@@index([profileId])
}

Model Score

model Score {
id String @id @default(cuid())
submissionId String
judgeId String
criterionId String
value Int // 0 - maxScore del criterio
comment String? @db.Text

submission Submission @relation(fields: [submissionId], references: [id], onDelete: Cascade)
judge Profile @relation(fields: [judgeId], references: [id])
criterion Criterion @relation(fields: [criterionId], references: [id], onDelete: Cascade)

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([submissionId, judgeId, criterionId]) // Un juez solo puede puntuar un criterio una vez
@@index([submissionId])
@@index([judgeId])
@@index([criterionId])
}

Actualizaciones a Modelos Existentes

Profile:

model Profile {
// ... campos existentes
teamMemberships TeamMember[]
judgeAssignments HackathonJudge[]
scores Score[]
}

Hackathon:

model Hackathon {
// ... campos existentes
teams Team[]
submissions Submission[]
judges HackathonJudge[]
}

Criterion:

model Criterion {
// ... campos existentes
scores Score[]
}

2. Funcionalidades Core Requeridas

Teams Module

Queries (4 funciones):

  • getTeamById(id: string) - Obtener equipo por ID
  • getTeamByCode(code: string) - Obtener equipo por código de invitación
  • getUserTeams(profileId: string, hackathonId?: string) - Equipos del usuario
  • getTeamMembers(teamId: string) - Miembros de un equipo

Actions (3 Server Actions):

  1. createTeam(hackathonId: string, data: CreateTeamInput, currentUserId: string)

    • RBAC: PARTICIPANT
    • Validar: usuario registrado en hackathon, no tiene equipo en ese hackathon, now < submissionDeadline
    • Generar código único de invitación (6-8 caracteres alfanuméricos)
    • Crear Team + TeamMember (creator como miembro)
  2. joinTeam(inviteCode: string, currentUserId: string)

    • RBAC: PARTICIPANT
    • Validar: código válido, usuario registrado en hackathon, no tiene equipo en ese hackathon, now < submissionDeadline
    • Validar: equipo no está lleno (maxTeamSize del hackathon)
    • Agregar TeamMember
  3. leaveTeam(teamId: string, currentUserId: string)

    • RBAC: PARTICIPANT
    • Validar: usuario es miembro del equipo, now < submissionDeadline
    • Validar: si equipo tiene submission y es el último miembro → error
    • Si equipo queda vacío y NO tiene submission → eliminar equipo
    • Eliminar TeamMember

Validations:

  • createTeamSchema - name, description (opcional)
  • joinTeamSchema - inviteCode
  • leaveTeamSchema - teamId

Submissions Module

Queries (4 funciones):

  • getSubmissionById(id: string) - Obtener submission por ID
  • getSubmissionByTeamId(teamId: string) - Obtener submission del equipo
  • getHackathonSubmissions(hackathonId: string) - Todas las submissions del hackathon
  • getHackathonSubmissionsForJudge(hackathonId: string, judgeId: string) - Submissions para un juez (solo en JUDGING)

Actions (2 Server Actions):

  1. createSubmission(hackathonId: string, teamId: string, data: CreateSubmissionInput, currentUserId: string)

    • RBAC: PARTICIPANT
    • Validar: usuario es miembro del equipo, hackathon en estado RUNNING, now < submissionDeadline
    • Validar: equipo no tiene submission (unique constraint)
    • Crear Submission
  2. updateSubmission(submissionId: string, data: UpdateSubmissionInput, currentUserId: string)

    • RBAC: PARTICIPANT
    • Validar: usuario es miembro del equipo, now < submissionDeadline
    • Actualizar Submission

Validations:

  • createSubmissionSchema - title, description, repoUrl (opcional), demoUrl (opcional), extraLinks (opcional)
  • updateSubmissionSchema - campos opcionales

Evaluation Module

Queries (4 funciones):

  • getHackathonJudges(hackathonId: string) - Jueces asignados al hackathon
  • getJudgeAssignments(judgeId: string) - Hackathons asignados a un juez
  • getScoresForSubmission(submissionId: string) - Scores de una submission
  • calculateLeaderboard(hackathonId: string) - Calcular leaderboard con puntaje ponderado

Actions (3 Server Actions):

  1. assignJudge(hackathonId: string, judgeId: string, currentUserId: string)

    • RBAC: ORGANIZER/ADMIN
    • Validar: juez tiene rol JUDGE
    • Validar: juez NO participa en el hackathon (no tiene participation ni team)
    • Validar: juez no está ya asignado (unique constraint)
    • Crear HackathonJudge
  2. removeJudge(hackathonId: string, judgeId: string, currentUserId: string)

    • RBAC: ORGANIZER/ADMIN
    • Validar: juez está asignado
    • Eliminar HackathonJudge (scores permanecen pero no cuentan)
  3. submitScore(submissionId: string, criterionId: string, value: number, comment?: string, currentUserId: string)

    • RBAC: JUDGE
    • Validar: juez está asignado al hackathon, hackathon en estado JUDGING
    • Validar: submission pertenece al hackathon asignado
    • Validar: value está entre 0 y maxScore del criterio
    • Crear o actualizar Score (unique constraint)

Validations:

  • assignJudgeSchema - hackathonId, judgeId
  • submitScoreSchema - submissionId, criterionId, value (0-maxScore), comment (opcional)

3. UI Funcional Requerida

Teams UI (3 páginas/componentes)

  1. /hackathons/[slug]/teams/create - Crear equipo

    • Formulario: name, description (opcional)
    • Mostrar código de invitación después de crear
    • Botón copiar código
  2. /hackathons/[slug]/teams/join - Unirse a equipo

    • Input para código de invitación
    • Validación en servidor
  3. /hackathons/[slug]/teams/[teamId] - Detalle del equipo

    • Lista de miembros
    • Botón "Salir del equipo" (si es miembro)
    • Mostrar submission si existe

Submissions UI (3 páginas/componentes)

  1. /hackathons/[slug]/teams/[teamId]/submit - Crear/editar submission

    • Formulario: title, description, repoUrl, demoUrl, extraLinks
    • Validación: solo hasta submissionDeadline
    • Botón "Enviar" o "Actualizar"
  2. /hackathons/[slug]/submissions - Lista de submissions (público cuando FINISHED)

    • Mostrar todas las submissions del hackathon
    • Link a detalle
  3. /hackathons/[slug]/submissions/[submissionId] - Detalle de submission

    • Información completa
    • Leaderboard (si FINISHED)

Evaluation UI (3 páginas/componentes)

  1. /admin/hackathons/[slug]/judges - Asignar jueces (ORGANIZER/ADMIN)

    • Lista de jueces disponibles (rol JUDGE)
    • Botón asignar/remover
    • Validar conflictos (participación en hackathon)
  2. /judge/hackathons/[slug] - Panel de juez

    • Lista de submissions del hackathon asignado (solo en JUDGING)
    • Botón "Evaluar" por cada submission
    • Mostrar progreso: "Evaluadas: X/Y submissions"
  3. /judge/hackathons/[slug]/submissions/[submissionId]/evaluate - Formulario de evaluación

    • Mostrar criterios con pesos
    • Input de puntaje por criterio (0-maxScore)
    • Textarea de comentario (opcional) por criterio
    • Botón "Enviar evaluación"

Leaderboard UI (1 página)

  1. /hackathons/[slug]/leaderboard - Leaderboard público
    • Solo visible cuando hackathon está FINISHED
    • Mostrar ranking con puntaje ponderado
    • Fórmula: sum(score * criterion.weight) / sum(maxScore * criterion.weight) * 100

🗂️ Plan de Ejecución por Fases

FASE 1: Schema y Migración (Prioridad: CRÍTICA)

Objetivo: Definir y migrar el schema de base de datos

Tareas:

  1. Actualizar prisma/schema.prisma

    • Agregar model Team
    • Agregar model TeamMember
    • Agregar model Submission
    • Agregar model HackathonJudge
    • Agregar model Score
    • Actualizar relaciones en Profile, Hackathon, Criterion
    • Definir índices y constraints
  2. Ejecutar migración

    pnpm prisma migrate dev --name add_teams_and_evaluation_module
  3. Verificar schema en Prisma Studio

Criterio de Completitud:

  • Schema completo sin errores
  • Migración ejecutada exitosamente
  • Relaciones funcionando correctamente
  • Índices y constraints correctos

Tiempo Estimado: 45-60 minutos


FASE 2: Teams Module - Core (Prioridad: CRÍTICA)

Objetivo: Implementar la lógica core del módulo de equipos

2.1 Types (src/modules/teams/types.ts)

Tareas:

  1. Definir tipos TypeScript
    • TeamWithRelations (con miembros, submission)
    • CreateTeamInput
    • JoinTeamInput
    • LeaveTeamInput
    • TeamMemberWithProfile

Criterio de Completitud:

  • Todos los tipos exportados
  • Tipos usados en queries/actions

Tiempo Estimado: 15 minutos

2.2 Validations (src/modules/teams/validations.ts)

Tareas:

  1. Crear createTeamSchema (Zod)

    • name (min 3, max 100)
    • description (opcional, max 500)
  2. Crear joinTeamSchema (Zod)

    • inviteCode (string, min 6, max 8)
  3. Crear leaveTeamSchema (Zod)

    • teamId (CUID)

Criterio de Completitud:

  • Todos los schemas creados
  • Tests de validaciones pasando

Tiempo Estimado: 30 minutos

2.3 Queries (src/modules/teams/queries.ts)

Tareas:

  1. Implementar getTeamById(id: string)

    • Incluir miembros y submission
  2. Implementar getTeamByCode(code: string)

    • Incluir miembros y submission
  3. Implementar getUserTeams(profileId: string, hackathonId?: string)

    • Equipos del usuario (opcionalmente filtrado por hackathon)
  4. Implementar getTeamMembers(teamId: string)

    • Lista de miembros con perfil

Criterio de Completitud:

  • Todas las queries implementadas
  • Tests de queries pasando (100% coverage)

Tiempo Estimado: 1 hora

2.4 Actions (src/modules/teams/actions.ts)

Tareas:

  1. Implementar createTeam

    • Validar RBAC (PARTICIPANT)
    • Validar registro en hackathon
    • Validar no tiene equipo en hackathon
    • Validar now < submissionDeadline
    • Generar código único (función helper)
    • Crear Team + TeamMember en transaction
  2. Implementar joinTeam

    • Validar RBAC (PARTICIPANT)
    • Validar código válido
    • Validar registro en hackathon
    • Validar no tiene equipo en hackathon
    • Validar now < submissionDeadline
    • Validar equipo no lleno
    • Agregar TeamMember
  3. Implementar leaveTeam

    • Validar RBAC (PARTICIPANT)
    • Validar es miembro del equipo
    • Validar now < submissionDeadline
    • Validar: si tiene submission y es último miembro → error
    • Si equipo vacío sin submission → eliminar equipo
    • Eliminar TeamMember

Criterio de Completitud:

  • Todas las actions implementadas
  • RBAC funcionando
  • Validaciones funcionando
  • Tests pasando

Tiempo Estimado: 2 horas


FASE 3: Submissions Module - Core (Prioridad: CRÍTICA)

Objetivo: Implementar la lógica core del módulo de submissions

3.1 Types (src/modules/submissions/types.ts)

Tareas:

  1. Definir tipos TypeScript
    • SubmissionWithRelations (con team, scores)
    • CreateSubmissionInput
    • UpdateSubmissionInput

Tiempo Estimado: 15 minutos

3.2 Validations (src/modules/submissions/validations.ts)

Tareas:

  1. Crear createSubmissionSchema (Zod)

    • title (min 3, max 200)
    • description (min 10, max 5000)
    • repoUrl (URL opcional)
    • demoUrl (URL opcional)
    • extraLinks (JSON opcional)
  2. Crear updateSubmissionSchema (Zod)

    • Campos opcionales

Tiempo Estimado: 30 minutos

3.3 Queries (src/modules/submissions/queries.ts)

Tareas:

  1. Implementar getSubmissionById(id: string)
  2. Implementar getSubmissionByTeamId(teamId: string)
  3. Implementar getHackathonSubmissions(hackathonId: string)
  4. Implementar getHackathonSubmissionsForJudge(hackathonId: string, judgeId: string)
    • Solo si hackathon está en JUDGING
    • Filtrar por hackathon asignado al juez

Tiempo Estimado: 1 hora

3.4 Actions (src/modules/submissions/actions.ts)

Tareas:

  1. Implementar createSubmission

    • Validar RBAC (PARTICIPANT)
    • Validar es miembro del equipo
    • Validar hackathon en RUNNING
    • Validar now < submissionDeadline
    • Validar equipo no tiene submission
    • Crear Submission
  2. Implementar updateSubmission

    • Validar RBAC (PARTICIPANT)
    • Validar es miembro del equipo
    • Validar now < submissionDeadline
    • Actualizar Submission

Tiempo Estimado: 1.5 horas


FASE 4: Evaluation Module - Core (Prioridad: CRÍTICA)

Objetivo: Implementar la lógica core del módulo de evaluación

4.1 Types (src/modules/evaluation/types.ts)

Tareas:

  1. Definir tipos TypeScript
    • LeaderboardEntry (team, submission, weightedScore, position)
    • ScoreWithRelations (con submission, judge, criterion)
    • JudgeAssignment

Tiempo Estimado: 15 minutos

4.2 Validations (src/modules/evaluation/validations.ts)

Tareas:

  1. Crear assignJudgeSchema (Zod)

    • hackathonId (CUID)
    • judgeId (CUID)
  2. Crear submitScoreSchema (Zod)

    • submissionId (CUID)
    • criterionId (CUID)
    • value (number, min 0, max se obtiene del criterio)
    • comment (opcional, max 1000)

Tiempo Estimado: 30 minutos

4.3 Queries (src/modules/evaluation/queries.ts)

Tareas:

  1. Implementar getHackathonJudges(hackathonId: string)
  2. Implementar getJudgeAssignments(judgeId: string)
  3. Implementar getScoresForSubmission(submissionId: string)
    • Filtrar por judgeId si se proporciona (para ocultar scores de otros jueces durante JUDGING)
  4. Implementar calculateLeaderboard(hackathonId: string)
    • Obtener todas las submissions del hackathon
    • Para cada submission, calcular puntaje ponderado:
      • sum(score.value * criterion.weight) / sum(criterion.maxScore * criterion.weight) * 100
    • Ordenar por puntaje descendente
    • Retornar con posición

Tiempo Estimado: 2 horas

4.4 Actions (src/modules/evaluation/actions.ts)

Tareas:

  1. Implementar assignJudge

    • Validar RBAC (ORGANIZER/ADMIN)
    • Validar juez tiene rol JUDGE
    • Validar juez NO participa en hackathon (no participation, no team)
    • Validar juez no está ya asignado
    • Crear HackathonJudge
  2. Implementar removeJudge

    • Validar RBAC (ORGANIZER/ADMIN)
    • Validar juez está asignado
    • Eliminar HackathonJudge
  3. Implementar submitScore

    • Validar RBAC (JUDGE)
    • Validar juez está asignado al hackathon
    • Validar hackathon en JUDGING
    • Validar submission pertenece al hackathon
    • Validar value entre 0 y maxScore del criterio
    • Crear o actualizar Score (upsert)

Tiempo Estimado: 2 horas


FASE 5: Testing (Prioridad: CRÍTICA)

Objetivo: Crear tests completos para todos los módulos

Tareas:

  1. Tests de Teams Module

    • tests/modules/teams/validations.test.ts
    • tests/modules/teams/queries.test.ts
    • tests/modules/teams/actions.test.ts
  2. Tests de Submissions Module

    • tests/modules/submissions/validations.test.ts
    • tests/modules/submissions/queries.test.ts
    • tests/modules/submissions/actions.test.ts
  3. Tests de Evaluation Module

    • tests/modules/evaluation/validations.test.ts
    • tests/modules/evaluation/queries.test.ts
    • tests/modules/evaluation/actions.test.ts

Criterio de Completitud:

  • Coverage >80%
  • Todos los tests pasando
  • Tests de RBAC completos
  • Tests de validaciones de fechas

Tiempo Estimado: 4-5 horas


FASE 6: Teams UI (Prioridad: ALTA)

Objetivo: Implementar la UI para gestión de equipos

Tareas:

  1. Crear /hackathons/[slug]/teams/create/page.tsx

    • Formulario crear equipo
    • Mostrar código después de crear
    • Botón copiar código
  2. Crear /hackathons/[slug]/teams/join/page.tsx

    • Input código de invitación
    • Validación en servidor
  3. Crear /hackathons/[slug]/teams/[teamId]/page.tsx

    • Detalle del equipo
    • Lista de miembros
    • Botón salir (si es miembro)

Tiempo Estimado: 2 horas


FASE 7: Submissions UI (Prioridad: ALTA)

Objetivo: Implementar la UI para submissions

Tareas:

  1. Crear /hackathons/[slug]/teams/[teamId]/submit/page.tsx

    • Formulario crear/editar submission
    • Validación de fechas
  2. Crear /hackathons/[slug]/submissions/page.tsx

    • Lista de submissions (público cuando FINISHED)
  3. Crear /hackathons/[slug]/submissions/[submissionId]/page.tsx

    • Detalle de submission
    • Leaderboard (si FINISHED)

Tiempo Estimado: 2 horas


FASE 8: Evaluation UI (Prioridad: ALTA)

Objetivo: Implementar la UI para evaluación

Tareas:

  1. Crear /admin/hackathons/[slug]/judges/page.tsx

    • Lista de jueces disponibles
    • Asignar/remover jueces
    • Validar conflictos
  2. Crear /judge/hackathons/[slug]/page.tsx

    • Panel de juez
    • Lista de submissions (solo en JUDGING)
    • Progreso de evaluación
  3. Crear /judge/hackathons/[slug]/submissions/[submissionId]/evaluate/page.tsx

    • Formulario de evaluación
    • Input por criterio
    • Enviar evaluación

Tiempo Estimado: 2.5 horas


FASE 9: Leaderboard UI (Prioridad: MEDIA)

Objetivo: Implementar la UI del leaderboard

Tareas:

  1. Crear /hackathons/[slug]/leaderboard/page.tsx
    • Leaderboard público
    • Solo visible cuando FINISHED
    • Mostrar ranking con puntaje ponderado

Tiempo Estimado: 1 hora


📐 Orden de Ejecución Recomendado

Secuencia Lógica:

  1. FASE 1: Schema y Migración ⚡ (CRÍTICA)
  2. FASE 2: Teams Module Core ⚡ (CRÍTICA)
  3. FASE 3: Submissions Module Core ⚡ (CRÍTICA)
  4. FASE 4: Evaluation Module Core ⚡ (CRÍTICA)
  5. FASE 5: Testing ⚡ (CRÍTICA)
  6. FASE 6: Teams UI (ALTA)
  7. FASE 7: Submissions UI (ALTA)
  8. FASE 8: Evaluation UI (ALTA)
  9. FASE 9: Leaderboard UI (MEDIA)

Regla de Oro:

NO avanzar a la siguiente fase hasta que la anterior esté 100% completa y testeada.


✅ Criterios de Completitud por Fase

Fase 1 - Schema

  • Schema completo sin errores
  • Migración ejecutada
  • Relaciones funcionando

Fase 2-4 - Module Files Core

  • Types exportados
  • Validations con tests pasando
  • Queries con tests pasando (100% coverage)
  • Actions con tests pasando

Fase 5 - Testing

  • Coverage >80%
  • Todos los tests pasando
  • Tests de RBAC completos

Fase 6-9 - UI

  • Todas las páginas funcionando
  • Validaciones en servidor funcionando
  • Navegación funcionando

🎯 Principios de Implementación

1. Modularidad

  • Cada módulo con responsabilidad única
  • Separación clara: queries, actions, validations, types
  • Reutilización de helpers de core/

2. Profesionalismo

  • Código limpio y bien documentado
  • Nombres descriptivos
  • Manejo de errores consistente
  • Patrones consistentes con Módulos 1 y 2

3. Seguridad

  • RBAC en cada Server Action
  • Validación en servidor (nunca confiar en cliente)
  • Validación de ownership y membresía

4. Testing

  • Tests antes de UI (TDD cuando sea posible)
  • Coverage >80%
  • Tests de RBAC exhaustivos
  • Tests de validaciones de fechas

5. UI

  • Server Components por defecto
  • Client Components solo cuando necesario
  • Formularios HTML nativos + Server Actions

📝 Notas Importantes

Validación de Fechas

  • SIEMPRE validar now < submissionDeadline para crear/editar submissions
  • SIEMPRE validar now < submissionDeadline para crear/unirse/salir de equipos
  • Usar validateHackathonDates() y validateDateExtension() cuando corresponda

Código de Invitación

  • Generar código único de 6-8 caracteres alfanuméricos
  • Verificar unicidad antes de crear
  • Formato sugerido: ABC123 o similar

Cálculo de Leaderboard

  • Fórmula: sum(score.value * criterion.weight) / sum(criterion.maxScore * criterion.weight) * 100
  • Solo considerar scores de jueces con HackathonJudge vigente
  • Ordenar por puntaje descendente

Restricciones de Juez

  • Juez NO puede participar en el mismo hackathon que juzga
  • Juez NO puede ver scores de otros jueces durante JUDGING
  • Juez puede ver todos los scores cuando hackathon está FINISHED

⏱️ Estimación Total

Tiempo Total Estimado: 18-22 horas

Desglose:

  • Schema: 45-60 min
  • Teams Module Core: 3.5 horas
  • Submissions Module Core: 3 horas
  • Evaluation Module Core: 4.5 horas
  • Testing: 4-5 horas
  • UI: 5.5 horas

🚀 Siguiente Paso

Comenzar con FASE 1: Schema y Migración

¿Procedemos con la implementación siguiendo este plan?