API Frontend – Revisión Administrativa de Bloques¶
Esta guía explica cómo los administradores pueden revisar, aprobar o rechazar bloques que han sido cerrados por los profesores.
Arquitectura¶
La tabla student_blocks almacena el estado de cada bloque por estudiante:
Block (id=28, name="block1") - Plantilla de contenidos
└── StudentBlock (id=1, blockId=28, studentId=123, status="pending", grade=7.5)
└── StudentBlock (id=2, blockId=28, studentId=456, status="open", grade=null)
└── StudentBlock (id=3, blockId=28, studentId=789, status="closed", grade=8.0)
Esto permite que cada estudiante tenga su propio estado del bloque, independiente de los demás.
Endpoints¶
| Método | Endpoint | Descripción |
|---|---|---|
GET |
/api/v2/blocks/pending |
Obtener bloques pendientes de revisión |
POST |
/api/v2/blocks/student-block/:studentBlockId/review |
Aprobar o rechazar un bloque de estudiante |
1. Obtener Bloques Pendientes¶
Endpoint¶
Autenticación¶
Requiere token JWT. Enviar Authorization: Bearer <JWT>
Parámetros (Query)¶
| Parámetro | Requerido | Descripción |
|---|---|---|
studentId |
No | Filtrar por ID de estudiante |
subjectId |
No | Filtrar por ID de asignatura |
Ejemplo (curl)¶
# Todos los bloques pendientes
curl -X GET \
"https://<host>/api/v2/blocks/pending" \
-H "Authorization: Bearer <token>"
# Filtrar por estudiante
curl -X GET \
"https://<host>/api/v2/blocks/pending?studentId=123" \
-H "Authorization: Bearer <token>"
# Filtrar por asignatura
curl -X GET \
"https://<host>/api/v2/blocks/pending?subjectId=5" \
-H "Authorization: Bearer <token>"
Respuesta Exitosa¶
{
"ok": true,
"total": 3,
"byStudent": [
{
"studentId": 123,
"studentName": "Juan Pérez García",
"blocks": [
{
"blockId": 45,
"blockName": "Bloque 1",
"subjectId": 5,
"subjectName": "Piano",
"grade": 8.50,
"status": "pending",
"employeeId": 10,
"employeeName": "María López Ruiz",
"closedAt": "2025-11-27T15:30:00.000Z"
},
{
"blockId": 46,
"blockName": "Bloque 2",
"subjectId": 5,
"subjectName": "Piano",
"grade": 7.25,
"status": "pending",
"employeeId": 10,
"employeeName": "María López Ruiz",
"closedAt": "2025-11-27T16:00:00.000Z"
}
]
},
{
"studentId": 456,
"studentName": "Ana Martínez Sánchez",
"blocks": [
{
"blockId": 78,
"blockName": "Bloque 1",
"subjectId": 3,
"subjectName": "Guitarra",
"grade": 9.00,
"status": "pending",
"employeeId": 15,
"employeeName": "Carlos Fernández",
"closedAt": "2025-11-27T14:00:00.000Z"
}
]
}
]
}
Ejemplo (TypeScript/fetch)¶
interface PendingBlock {
blockId: number;
blockName: string;
subjectId: number;
subjectName: string;
grade: number | null;
status: string;
employeeId: number | null;
employeeName: string;
closedAt: string | null;
}
interface PendingBlocksByStudent {
studentId: number;
studentName: string;
blocks: PendingBlock[];
}
interface GetPendingResponse {
ok: boolean;
total: number;
byStudent: PendingBlocksByStudent[];
}
const response = await fetch('/api/v2/blocks/pending', {
headers: { Authorization: `Bearer ${token}` },
});
const data: GetPendingResponse = await response.json();
// Mostrar total de bloques pendientes
console.log(`Total pendientes: ${data.total}`);
// Iterar por estudiante
data.byStudent.forEach(student => {
console.log(`${student.studentName}: ${student.blocks.length} bloques`);
});
2. Aprobar o Rechazar Bloque¶
Endpoint¶
Autenticación¶
Requiere token JWT. Enviar Authorization: Bearer <JWT>
Parámetros¶
| Parámetro | Ubicación | Requerido | Descripción |
|---|---|---|---|
studentBlockId |
path | Sí | ID del registro en student_blocks |
action |
body | Sí | "approve" o "reject" |
reason |
body | No | Razón del rechazo (opcional) |
Comportamiento¶
| Acción | Estado Anterior | Estado Nuevo | Descripción |
|---|---|---|---|
approve |
pending |
closed |
Bloque aprobado, desbloquea siguiente bloque, notifica a profesor y estudiante/tutor |
reject |
pending |
open |
Bloque rechazado, vuelve al profesor para revisión |
Ejemplo: Aprobar (curl)¶
curl -X POST \
"https://<host>/api/v2/blocks/student-block/1/review" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"action": "approve"}'
Respuesta:
{
"ok": true,
"studentBlockId": 1,
"blockId": 45,
"studentId": 123,
"previousStatus": "pending",
"newStatus": "closed",
"action": "approve",
"nextBlockUnlocked": true,
"message": "Bloque aprobado correctamente. Estado cambiado a 'closed'."
}
Nota: Si es el último bloque de la asignatura, en lugar de
nextBlockUnlockedse devuelvesubjectCompleted: true.
Ejemplo: Rechazar (curl)¶
curl -X POST \
"https://<host>/api/v2/blocks/student-block/2/review" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"action": "reject", "reason": "Falta completar evaluaciones de contenido"}'
Respuesta:
{
"ok": true,
"studentBlockId": 2,
"blockId": 46,
"studentId": 456,
"previousStatus": "pending",
"newStatus": "open",
"action": "reject",
"message": "Bloque rechazado. Estado cambiado a 'open' para revisión del profesor."
}
Ejemplo (TypeScript/fetch)¶
// Aprobar bloque de estudiante
const approveStudentBlock = async (studentBlockId: number) => {
const response = await fetch(`/api/v2/blocks/student-block/${studentBlockId}/review`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'approve' }),
});
return response.json();
};
// Rechazar bloque de estudiante con razón
const rejectStudentBlock = async (studentBlockId: number, reason: string) => {
const response = await fetch(`/api/v2/blocks/student-block/${studentBlockId}/review`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'reject', reason }),
});
return response.json();
};
Notificaciones al Aprobar¶
Cuando se aprueba un bloque, el sistema envía notificaciones automáticas:
Al Profesor¶
- Email con plantilla
blockApprovedEmployeeTemplate - Informa que el bloque fue aprobado
- Si hay siguiente bloque: indica que está disponible
- Si es el último: felicita por completar la asignatura
Al Estudiante/Tutor¶
- Email con plantilla
blockApprovedStudentTemplate - Lógica de envío:
- Si tiene tutor (
guardianId): notifica al tutor - Si el estudiante tiene email válido (no
@constructor.com): notifica al estudiante - Si tiene ambos: notifica a los dos
- Mensaje personalizado según destinatario
Errores Comunes¶
| Código | Error | Descripción |
|---|---|---|
| 400 | STUDENT_BLOCK_ID_REQUIRED |
studentBlockId inválido o faltante |
| 400 | INVALID_ACTION |
action debe ser "approve" o "reject" |
| 400 | INVALID_STATUS_FOR_REVIEW |
El bloque no está en estado pending |
| 404 | STUDENT_BLOCK_NOT_FOUND |
studentBlockId no existe |
| 401 | UNAUTHORIZED |
Falta/expiró token o no es administrador |
Flujo Completo de UI¶
┌─────────────────────────────────────────────────────────────┐
│ PANEL ADMINISTRATIVO │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📋 Bloques Pendientes de Revisión (3) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 👤 Juan Pérez García │ │
│ │ │ │
│ │ 📚 Bloque 1 - Piano │ │
│ │ Nota: 8.50 | Profesor: María López │ │
│ │ Cerrado: 27/11/2025 15:30 │ │
│ │ [✅ Aprobar] [❌ Rechazar] │ │
│ │ │ │
│ │ 📚 Bloque 2 - Piano │ │
│ │ Nota: 7.25 | Profesor: María López │ │
│ │ Cerrado: 27/11/2025 16:00 │ │
│ │ [✅ Aprobar] [❌ Rechazar] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 👤 Ana Martínez Sánchez │ │
│ │ │ │
│ │ 📚 Bloque 1 - Guitarra │ │
│ │ Nota: 9.00 | Profesor: Carlos Fernández │ │
│ │ Cerrado: 27/11/2025 14:00 │ │
│ │ [✅ Aprobar] [❌ Rechazar] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Notas Técnicas¶
- Solo bloques en estado
pendingpueden ser revisados. - Al aprobar, el bloque pasa a
closed(estado final). - Al rechazar, el bloque vuelve a
openpara que el profesor lo revise y cierre nuevamente. - La razón del rechazo (
reason) se registra en logs pero actualmente no se persiste en BD. - Se recomienda implementar notificación al profesor cuando su bloque es rechazado (futuro).