⚖️ 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:
- ORGANIZER va a
/admin/hackathons/[slug]/judges - Ve lista de jueces disponibles
- Selecciona juez
- Sistema valida:
- Juez no está ya asignado
- Juez no participa en el hackathon
- Juez no es miembro de equipo en el hackathon
- Sistema crea
HackathonJudge - 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 (
HackathonJudgeexiste) hackathon.status: JUDGING
Flujo Principal:
- Juez va a
/judge/hackathons/[slug] - Sistema valida:
- Juez está asignado
- Hackathon está en JUDGING
- Sistema muestra lista de submissions del hackathon
- Para cada submission muestra:
title,descriptionrepoUrl,demoUrlteamName,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:
- Juez selecciona submission
- Ve formulario de evaluación con:
- Información de la submission
- Lista de criterios con:
name,descriptionweight(1-10)maxScore(default 10)
- Por cada criterio:
- Ingresa
value(0 - maxScore) - Opcional:
comment
- Ingresa
- Clic en "Enviar evaluación"
- Sistema valida:
- Todos los scores están en rango válido
- Juez no ha evaluado previamente (unique constraint)
- Sistema crea registros
Score(uno por criterio) - Mensaje de éxito: "Evaluación enviada"
- 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:
- Juez va a
/judge/hackathons/[slug]/leaderboard - Sistema muestra:
- Leaderboard completo con scores ponderados
- Scores de todos los jueces (ya no filtrado)
- Comentarios de todos los jueces
- 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
- Organizer Flow - Flujo del organizador
- Evaluation System - Sistema de evaluación completo
- Leaderboard Calculation - Cálculo de leaderboard
Siguiente: Organizer Flow