Saltar a contenido

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

GET /api/v2/blocks/pending

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

POST /api/v2/blocks/student-block/:studentBlockId/review

Autenticación

Requiere token JWT. Enviar Authorization: Bearer <JWT>

Parámetros

Parámetro Ubicación Requerido Descripción
studentBlockId path ID del registro en student_blocks
action body "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 nextBlockUnlocked se devuelve subjectCompleted: 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 pending pueden ser revisados.
  • Al aprobar, el bloque pasa a closed (estado final).
  • Al rechazar, el bloque vuelve a open para 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).