# US-ANA-003: Vista de Estudiante Individual
**Épica:** EAI-004 (Analytics Básico)
**Sprint:** Mes 1, Semana 3
**Story Points:** 8 SP
**Presupuesto:** $4,000 MXN
**Prioridad:** Alta (Alcance Inicial)
**Estado:** ✅ Completada (Mes 1)
---
## Descripción
Como profesor, quiero ver el detalle completo del progreso de un estudiante individual para entender qué ha completado, en qué está trabajando, y dónde necesita ayuda.
**Contexto del Alcance Inicial:**
Esta vista proporciona un perfil detallado del estudiante con su progreso por módulo, actividades completadas, y tiempo invertido. Es una vista de solo lectura con información básica. NO incluye análisis de desempeño avanzado, gráficas de tendencias, ni comparativas con otros estudiantes (eso va a EXT-005 Reportes Avanzados).
---
## Criterios de Aceptación
### CA-01: Información del Estudiante
- [ ] Muestra avatar/foto del estudiante
- [ ] Muestra nombre completo del estudiante
- [ ] Muestra nivel actual con icono de insignia
- [ ] Muestra XP total acumulado
- [ ] Muestra progreso general (% completitud)
- [ ] Muestra fecha de última actividad
### CA-02: Progreso por Módulo
- [ ] Lista de todos los módulos asignados a la clase
- [ ] Para cada módulo muestra:
- Nombre del módulo
- Porcentaje de completitud
- Número de actividades completadas / total
- Estado visual (completado, en progreso, no iniciado)
- [ ] Barra de progreso visual para cada módulo
### CA-03: Actividades Completadas
- [ ] Lista de las últimas 20 actividades completadas
- [ ] Para cada actividad muestra:
- Nombre de la actividad
- Módulo al que pertenece
- Fecha y hora de completado
- Puntaje/resultado (si aplica)
- [ ] Ordenadas de más reciente a más antigua
### CA-04: Métricas de Tiempo
- [ ] Tiempo total invertido en la plataforma
- [ ] Promedio de tiempo por sesión
- [ ] Número total de sesiones
- [ ] Última sesión (fecha y duración)
### CA-05: Navegación
- [ ] Breadcrumb: Dashboard > Estudiantes > [Nombre del estudiante]
- [ ] Botón "Volver a Estudiantes" (regresa a US-ANA-002)
- [ ] Navegación a siguiente/anterior estudiante (flechas)
### CA-06: Estados de Datos
- [ ] Muestra mensaje si el estudiante no ha iniciado ninguna actividad
- [ ] Muestra placeholder si no hay datos de tiempo
- [ ] Skeleton loaders mientras carga
---
## Especificaciones Técnicas
### Backend
**Endpoint Principal:**
```
GET /api/teacher/student/{studentId}/profile
Query params: ?classroomId=uuid
```
**Response:**
```json
{
"studentId": "uuid",
"classroomId": "uuid",
"profile": {
"name": "Juan Pérez García",
"avatarUrl": "/avatars/student-uuid.png",
"level": 3,
"xp": 1250,
"overallProgress": 65.5,
"lastActivity": "2025-11-01T15:30:00Z"
},
"moduleProgress": [
{
"moduleId": "module-uuid",
"moduleName": "Fracciones",
"progress": 85,
"completedActivities": 17,
"totalActivities": 20,
"status": "in_progress"
},
{
"moduleId": "module-uuid-2",
"moduleName": "Geometría",
"progress": 100,
"completedActivities": 15,
"totalActivities": 15,
"status": "completed"
},
{
"moduleId": "module-uuid-3",
"moduleName": "Álgebra",
"progress": 0,
"completedActivities": 0,
"totalActivities": 18,
"status": "not_started"
}
],
"recentActivities": [
{
"activityId": "activity-uuid",
"activityName": "Suma de fracciones con igual denominador",
"moduleName": "Fracciones",
"completedAt": "2025-11-01T15:30:00Z",
"score": 95,
"xpEarned": 50
}
],
"timeMetrics": {
"totalTime": 12600, // segundos
"averageSessionTime": 1800, // segundos
"totalSessions": 15,
"lastSession": {
"startedAt": "2025-11-01T15:00:00Z",
"endedAt": "2025-11-01T15:45:00Z",
"duration": 2700 // segundos
}
}
}
```
**Controller:**
```typescript
// TeacherAnalyticsController.ts
@Get('student/:studentId/profile')
async getStudentProfile(
@Param('studentId') studentId: string,
@Query('classroomId') classroomId: string,
@CurrentUser() teacher: User
) {
return this.analyticsService.getStudentProfile(
studentId,
classroomId,
teacher.id
);
}
```
**Service:**
```typescript
// TeacherAnalyticsService.ts
async getStudentProfile(
studentId: string,
classroomId: string,
teacherId: string
) {
// Validar que el profesor tiene acceso al estudiante
await this.validateTeacherAccessToStudent(classroomId, teacherId, studentId);
// Obtener perfil del estudiante
const profile = await this.getStudentBasicProfile(studentId);
// Obtener progreso por módulo
const moduleProgress = await this.getStudentModuleProgress(
studentId,
classroomId
);
// Obtener actividades recientes (últimas 20)
const recentActivities = await this.getStudentRecentActivities(
studentId,
20
);
// Obtener métricas de tiempo
const timeMetrics = await this.getStudentTimeMetrics(studentId);
return {
studentId,
classroomId,
profile,
moduleProgress,
recentActivities,
timeMetrics
};
}
private async getStudentModuleProgress(
studentId: string,
classroomId: string
) {
// Obtener módulos asignados a la clase
const assignedModules = await this.classroomService.getAssignedModules(
classroomId
);
// Para cada módulo, calcular progreso del estudiante
return Promise.all(
assignedModules.map(async (module) => {
const progress = await this.calculateModuleProgress(
studentId,
module.id
);
return {
moduleId: module.id,
moduleName: module.name,
progress: progress.percentage,
completedActivities: progress.completed,
totalActivities: progress.total,
status: this.getModuleStatus(progress.percentage)
};
})
);
}
private getModuleStatus(progress: number): string {
if (progress === 0) return 'not_started';
if (progress === 100) return 'completed';
return 'in_progress';
}
```
### Frontend
**Ruta:**
```
/teacher/student/:studentId?classroomId=:classroomId
```
**Componente Principal:**
```typescript
// StudentProfileView.tsx
export const StudentProfileView = () => {
const { studentId } = useParams();
const [searchParams] = useSearchParams();
const classroomId = searchParams.get('classroomId');
const { profileData, isLoading } = useStudentProfile(studentId, classroomId);
if (isLoading) return
{activity.moduleName}