Saltar al contenido principal

⚖️ Flujo de Juez

Visión General

El flujo del JUDGE se enfoca en la evaluación de proyectos asignados, con restricciones importantes para evitar conflictos de interés.

Diagrama de Flujo Completo

Restricciones Críticas

1. No Participar en Mismo Hackathon

Validación:

export async function assignJudge(hackathonId: string, judgeId: string) {
// Verificar que no participa
const participates = await judgeParticipatesInHackathon(hackathonId, judgeId);
if (participates) {
throw new Error('Judge cannot participate in the same hackathon they judge');
}

// Crear asignación
await db.hackathonJudge.create({
data: { hackathonId, profileId: judgeId },
});
}

2. Solo Ver Submissions en JUDGING

3. No Ver Scores de Otros Durante JUDGING

export async function getScoresForSubmission(
submissionId: string,
judgeId?: string
) {
const where: {
submissionId: string;
judgeId?: string;
} = {
submissionId,
};

// Si es juez, solo ver sus propios scores
if (judgeId) {
where.judgeId = judgeId;
}

return db.score.findMany({ where });
}

Casos de Uso Detallados

CU-1: Asignación como Juez

Actor: ORGANIZER o ADMIN
Precondiciones:

  • Usuario tiene role: JUDGE
  • Hackathon existe
  • Usuario NO participa en ese hackathon

Flujo Principal:

  1. ORGANIZER va a /admin/hackathons/[slug]/judges
  2. Ve lista de jueces disponibles
  3. Selecciona juez
  4. Sistema valida:
    • Juez no está ya asignado
    • Juez no participa en el hackathon
    • Juez no es miembro de equipo en el hackathon
  5. Sistema crea HackathonJudge
  6. Juez recibe notificación (email vía Clerk)

Flujos Alternativos:

  • FA-1.1: Juez ya asignado → Error: "Este juez ya está asignado"
  • FA-1.2: Juez participa → Error: "El juez no puede participar en el mismo hackathon que juzga"

CU-2: Ver Submissions para Evaluar

Actor: JUDGE
Precondiciones:

  • Juez está asignado al hackathon (HackathonJudge existe)
  • hackathon.status: JUDGING

Flujo Principal:

  1. Juez va a /judge/hackathons/[slug]
  2. Sistema valida:
    • Juez está asignado
    • Hackathon está en JUDGING
  3. Sistema muestra lista de submissions del hackathon
  4. Para cada submission muestra:
    • title, description
    • repoUrl, demoUrl
    • teamName, teamMembers
    • Criterios con pesos
    • Estado de evaluación (evaluada/pendiente)

Flujos Alternativos:

  • FA-2.1: Hackathon no está en JUDGING → Mensaje: "La evaluación empieza en [judgingStartsAt]"
  • FA-2.2: Juez no asignado → Error: "No estás asignado a este hackathon"

CU-3: Evaluar Submission

Actor: JUDGE
Precondiciones:

  • Juez está asignado
  • hackathon.status: JUDGING
  • Submission pertenece al hackathon asignado

Flujo Principal:

  1. Juez selecciona submission
  2. Ve formulario de evaluación con:
    • Información de la submission
    • Lista de criterios con:
      • name, description
      • weight (1-10)
      • maxScore (default 10)
  3. Por cada criterio:
    • Ingresa value (0 - maxScore)
    • Opcional: comment
  4. Clic en "Enviar evaluación"
  5. Sistema valida:
    • Todos los scores están en rango válido
    • Juez no ha evaluado previamente (unique constraint)
  6. Sistema crea registros Score (uno por criterio)
  7. Mensaje de éxito: "Evaluación enviada"
  8. Actualiza progreso: "Evaluadas: X/Y submissions"

Flujos Alternativos:

  • FA-3.1: Score fuera de rango → Error: "El score debe estar entre 0 y [maxScore]"
  • FA-3.2: Ya evaluó → Error: "Ya evaluaste esta submission"

CU-4: Ver Scores de Otros (Solo en FINISHED)

Actor: JUDGE
Precondiciones:

  • hackathon.status: FINISHED

Flujo Principal:

  1. Juez va a /judge/hackathons/[slug]/leaderboard
  2. Sistema muestra:
    • Leaderboard completo con scores ponderados
    • Scores de todos los jueces (ya no filtrado)
    • Comentarios de todos los jueces
  3. Puede comparar sus scores con los de otros jueces

Nota: Durante JUDGING, solo ve sus propios scores. En FINISHED, ve todos.

Diagrama de Secuencia: Proceso de Evaluación

Validaciones en Server Actions

submitScores

export async function submitScores(
submissionId: string,
scores: Array<{ criterionId: string; value: number; comment?: string }>
) {
const user = await getCurrentUser();
requireRole(user, ['JUDGE']);

// Obtener submission
const submission = await db.submission.findUnique({
where: { id: submissionId },
include: { hackathon: true },
});

if (!submission) {
throw new Error('Submission no encontrada');
}

// Verificar que juez está asignado
const isAssigned = await isJudgeAssigned(
submission.hackathonId,
user.profile.id
);

if (!isAssigned) {
throw new Error('No estás asignado a este hackathon');
}

// Verificar que hackathon está en JUDGING
if (submission.hackathon.status !== 'JUDGING') {
throw new Error('El hackathon no está en fase de evaluación');
}

// Validar cada score
for (const scoreData of scores) {
const criterion = await db.criterion.findUnique({
where: { id: scoreData.criterionId },
});

if (!criterion) {
throw new Error(`Criterio ${scoreData.criterionId} no encontrado`);
}

if (scoreData.value < 0 || scoreData.value > criterion.maxScore) {
throw new Error(
`Score debe estar entre 0 y ${criterion.maxScore} para ${criterion.name}`
);
}
}

// Crear scores en transacción
await db.$transaction(
scores.map((scoreData) =>
db.score.upsert({
where: {
submissionId_judgeId_criterionId: {
submissionId,
judgeId: user.profile.id,
criterionId: scoreData.criterionId,
},
},
create: {
submissionId,
judgeId: user.profile.id,
criterionId: scoreData.criterionId,
value: scoreData.value,
comment: scoreData.comment,
},
update: {
value: scoreData.value,
comment: scoreData.comment,
},
})
)
);

return { success: true };
}

Progreso de Evaluación

El sistema muestra el progreso del juez:

export async function getEvaluationProgress(
hackathonId: string,
judgeId: string
) {
const [totalSubmissions, evaluatedSubmissions] = await Promise.all([
db.submission.count({
where: { hackathonId },
}),
db.score.findMany({
where: {
submission: { hackathonId },
judgeId,
},
distinct: ['submissionId'],
}),
]);

return {
total: totalSubmissions,
evaluated: evaluatedSubmissions.length,
pending: totalSubmissions - evaluatedSubmissions.length,
percentage: totalSubmissions > 0
? Math.round((evaluatedSubmissions.length / totalSubmissions) * 100)
: 0,
};
}

Próximos Pasos


Siguiente: Organizer Flow