# ESPECIFICACION TECNICA - FASE 5B ## Migracion de Funcionalidades Students a Monitoring (Medio Riesgo) **Fecha:** 2025-12-15 **Prioridad:** MEDIA **Riesgo:** MEDIO **Prerequisito:** FASE 5A completada **Tiempo estimado de implementacion:** 4-6 horas --- ## 1. OBJETIVO Migrar las funcionalidades UNICAS de `TeacherStudents` a `StudentMonitoringPanel` para que el usuario no pierda capacidades al eliminar la pagina de Estudiantes del sidebar. --- ## 2. FUNCIONALIDADES A MIGRAR ### 2.1 Funcionalidades de TeacherStudents que NO existen en Monitoring: | Funcionalidad | En Students | En Monitoring | Accion | |---------------|-------------|---------------|--------| | Filtro por rendimiento (Alto/Medio/Bajo) | SI | NO | MIGRAR | | Vista en tabla sorteable | SI | NO | AGREGAR COMO OPCION | | Ordenamiento multi-campo | SI | NO | MIGRAR | | Estadisticas por rendimiento | SI | NO | MIGRAR | | Vista consolidada todas las aulas | SI | NO | EVALUAR | ### 2.2 Funcionalidades que ya existen en Monitoring: | Funcionalidad | Estado | |---------------|--------| | Busqueda por nombre | OK | | Filtro por estado (Active/Inactive/Offline) | OK | | Vista en tarjetas | OK | | Detalle de estudiante (modal) | OK | | Auto-refresh | OK | | Notificaciones toast | OK | --- ## 3. ARCHIVOS A MODIFICAR ### 3.1 StudentMonitoringPanel.tsx **Ruta:** `/home/isem/workspace/projects/gamilit/apps/frontend/src/apps/teacher/components/monitoring/StudentMonitoringPanel.tsx` #### 3.1.1 CAMBIOS EN INTERFACE StudentFilter **CODIGO ACTUAL (types/index.ts o inline):** ```typescript interface StudentFilter { search?: string; status?: ('active' | 'inactive' | 'offline')[]; } ``` **CODIGO PROPUESTO:** ```typescript interface StudentFilter { search?: string; status?: ('active' | 'inactive' | 'offline' | 'in_exercise')[]; performanceLevel?: ('high' | 'medium' | 'low')[]; } ``` #### 3.1.2 AGREGAR ESTADO PARA VISTA **AGREGAR despues de useState existentes (linea ~20):** ```typescript const [viewMode, setViewMode] = useState<'cards' | 'table'>('cards'); const [sortField, setSortField] = useState<'name' | 'score' | 'completion' | 'activity'>('name'); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); ``` #### 3.1.3 AGREGAR FUNCION calculatePerformanceLevel **AGREGAR antes de getStudentStatus (linea ~95):** ```typescript // Calculate performance level based on score const calculatePerformanceLevel = (student: StudentMonitoring): 'high' | 'medium' | 'low' => { const score = student.score_average || 0; if (score >= 80) return 'high'; if (score >= 60) return 'medium'; return 'low'; }; ``` #### 3.1.4 AGREGAR ESTADISTICAS DE RENDIMIENTO **AGREGAR despues de offlineCount (linea ~110):** ```typescript // Performance stats const highPerformanceCount = students.filter((s) => calculatePerformanceLevel(s) === 'high').length; const mediumPerformanceCount = students.filter((s) => calculatePerformanceLevel(s) === 'medium').length; const lowPerformanceCount = students.filter((s) => calculatePerformanceLevel(s) === 'low').length; ``` #### 3.1.5 AGREGAR FILTRO DE RENDIMIENTO **AGREGAR despues del handleStatusFilter (linea ~93):** ```typescript const handlePerformanceFilter = (level: 'high' | 'medium' | 'low') => { setFilters((prev) => { const currentLevels = prev.performanceLevel || []; const newLevels = currentLevels.includes(level) ? currentLevels.filter((l) => l !== level) : [...currentLevels, level]; return { ...prev, performanceLevel: newLevels.length > 0 ? newLevels : undefined, }; }); }; ``` #### 3.1.6 AGREGAR FUNCION DE ORDENAMIENTO **AGREGAR despues de handlePerformanceFilter:** ```typescript const handleSort = (field: typeof sortField) => { if (sortField === field) { setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')); } else { setSortField(field); setSortDirection('asc'); } }; // Apply sorting to students const sortedStudents = useMemo(() => { return [...students].sort((a, b) => { let aValue: string | number; let bValue: string | number; switch (sortField) { case 'name': aValue = a.full_name.toLowerCase(); bValue = b.full_name.toLowerCase(); break; case 'score': aValue = a.score_average || 0; bValue = b.score_average || 0; break; case 'completion': aValue = a.progress_percentage || 0; bValue = b.progress_percentage || 0; break; case 'activity': aValue = a.last_activity ? new Date(a.last_activity).getTime() : 0; bValue = b.last_activity ? new Date(b.last_activity).getTime() : 0; break; default: return 0; } if (aValue < bValue) return sortDirection === 'asc' ? -1 : 1; if (aValue > bValue) return sortDirection === 'asc' ? 1 : -1; return 0; }); }, [students, sortField, sortDirection]); ``` #### 3.1.7 AGREGAR TARJETAS DE RENDIMIENTO AL GRID DE STATS **MODIFICAR el grid de stats (linea ~146-204) para agregar:** ```typescript {/* Performance Stats - Nueva fila */}

{highPerformanceCount}

Alto Rendimiento

{mediumPerformanceCount}

Rendimiento Medio

{lowPerformanceCount}

Bajo Rendimiento

``` #### 3.1.8 AGREGAR BOTONES DE FILTRO POR RENDIMIENTO **AGREGAR despues de los botones de filtro por estado (linea ~235):** ```typescript {/* Separador */}
{/* Filtros de rendimiento */} handlePerformanceFilter('high')} size="sm" > Alto handlePerformanceFilter('medium')} size="sm" > Medio handlePerformanceFilter('low')} size="sm" > Bajo ``` #### 3.1.9 AGREGAR TOGGLE DE VISTA (TARJETAS/TABLA) **AGREGAR despues del RefreshControl (linea ~142):** ```typescript {/* View Toggle */}
setViewMode('cards')} size="sm" > setViewMode('table')} size="sm" >
``` #### 3.1.10 AGREGAR VISTA DE TABLA OPCIONAL **AGREGAR despues del grid de cards (linea ~260), condicional:** ```typescript {/* Vista Tabla (alternativa a cards) */} {viewMode === 'table' ? (
{sortedStudents.map((student) => ( setSelectedStudent(student)} > ))}
handleSort('name')} > Nombre {sortField === 'name' && (sortDirection === 'asc' ? '↑' : '↓')} Estado handleSort('score')} > Puntuacion {sortField === 'score' && (sortDirection === 'asc' ? '↑' : '↓')} handleSort('completion')} > Completitud {sortField === 'completion' && (sortDirection === 'asc' ? '↑' : '↓')} Rendimiento handleSort('activity')} > Ultima Actividad {sortField === 'activity' && (sortDirection === 'asc' ? '↑' : '↓')}

{student.full_name}

{student.email}

{getStudentStatus(student) === 'active' && 'Activo'} {getStudentStatus(student) === 'in_exercise' && 'En ejercicio'} {getStudentStatus(student) === 'inactive' && 'Inactivo'} {getStudentStatus(student) === 'offline' && 'Offline'} = 80 ? 'text-green-500' : (student.score_average || 0) >= 60 ? 'text-yellow-500' : 'text-red-500' }`}> {(student.score_average || 0).toFixed(1)}%
= 70 ? 'bg-green-500' : (student.progress_percentage || 0) >= 50 ? 'bg-yellow-500' : 'bg-red-500' }`} style={{ width: `${student.progress_percentage || 0}%` }} />
{(student.progress_percentage || 0).toFixed(0)}%
{calculatePerformanceLevel(student) === 'high' && 'Alto'} {calculatePerformanceLevel(student) === 'medium' && 'Medio'} {calculatePerformanceLevel(student) === 'low' && 'Bajo'} {student.last_activity ? new Date(student.last_activity).toLocaleDateString('es-ES') : 'Sin actividad'}
) : ( /* Vista Cards existente */
{sortedStudents.map((student) => ( setSelectedStudent(student)} /> ))}
)} ``` --- ## 4. IMPORTS ADICIONALES REQUERIDOS **Agregar al inicio del archivo:** ```typescript import { // ... imports existentes TrendingUp, TrendingDown, Minus, LayoutGrid, List, } from 'lucide-react'; import { useMemo } from 'react'; ``` --- ## 5. MODIFICAR useStudentMonitoring HOOK **Ruta:** `/home/isem/workspace/projects/gamilit/apps/frontend/src/apps/teacher/hooks/useStudentMonitoring.ts` **Agregar soporte para filtro de rendimiento en el hook si el filtrado se hace del lado cliente:** ```typescript // Filtrar por rendimiento si se especifica if (filters?.performanceLevel && filters.performanceLevel.length > 0) { filtered = filtered.filter((student) => { const level = calculatePerformanceLevel(student.score_average); return filters.performanceLevel!.includes(level); }); } ``` --- ## 6. PLAN DE PRUEBAS ### 6.1 Pruebas Funcionales ```yaml Prueba_1_Toggle_Vista: precondicion: "Login como teacher, navegar a /teacher/monitoring" pasos: - Click en icono de tabla - Verificar que se muestra la vista tabla - Click en icono de tarjetas - Verificar que se muestra la vista tarjetas resultado_esperado: "Cambio de vista sin perder datos" Prueba_2_Filtro_Rendimiento: precondicion: "Login como teacher, navegar a /teacher/monitoring" pasos: - Click en boton "Alto" - Verificar que solo se muestran estudiantes con score >= 80 - Click en boton "Bajo" - Verificar que solo se muestran estudiantes con score < 60 - Click en ambos - Verificar que se muestran Alto y Bajo resultado_esperado: "Filtrado correcto por rendimiento" Prueba_3_Ordenamiento_Tabla: precondicion: "Login como teacher, vista tabla activa" pasos: - Click en header "Puntuacion" - Verificar orden ascendente - Click de nuevo - Verificar orden descendente - Repetir para otros campos resultado_esperado: "Ordenamiento correcto multi-campo" Prueba_4_Stats_Rendimiento: precondicion: "Login como teacher, clase con estudiantes" pasos: - Verificar que aparecen tarjetas de Alto/Medio/Bajo rendimiento - Verificar que los conteos son correctos resultado_esperado: "Estadisticas de rendimiento visibles y precisas" Prueba_5_Funcionalidad_Existente: precondicion: "Login como teacher, navegar a /teacher/monitoring" pasos: - Verificar busqueda funciona - Verificar filtros de estado funcionan - Verificar auto-refresh funciona - Verificar toast notifications funcionan - Verificar modal de detalle funciona resultado_esperado: "Funcionalidad existente sin regresiones" ``` ### 6.2 Criterios de Aceptacion - [ ] Toggle de vista funciona correctamente - [ ] Filtro por rendimiento funciona - [ ] Ordenamiento en vista tabla funciona - [ ] Estadisticas de rendimiento son precisas - [ ] Funcionalidad existente sin regresiones - [ ] Responsive en mobile - [ ] No hay errores en consola --- ## 7. CONSIDERACIONES ### 7.1 Vista Consolidada de Todas las Aulas La funcionalidad de ver TODOS los estudiantes de TODAS las aulas (que existe en TeacherStudents) NO se migrara en esta fase porque: 1. StudentMonitoringPanel requiere un `classroomId` especifico 2. El Dashboard permite seleccionar una clase 3. Para ver todos los estudiantes, el usuario puede seleccionar "Todas las clases" en TeacherProgressPage **Alternativa futura:** Crear un componente AllStudentsView que agregue datos de todas las aulas. ### 7.2 Rendimiento El ordenamiento y filtrado se hace del lado cliente, lo cual es aceptable para: - Clases con < 100 estudiantes - Si hay clases mas grandes, considerar paginacion server-side --- ## 8. ROLLBACK En caso de necesitar revertir: 1. Restaurar StudentMonitoringPanel.tsx al estado anterior 2. Restaurar useStudentMonitoring.ts si fue modificado 3. Re-agregar "Estudiantes" al sidebar (revertir FASE 5A parcialmente) --- **Estado:** LISTO PARA IMPLEMENTAR **Prerequisitos:** FASE 5A completada **Siguiente fase:** FASE 5C (Fusion Analytics)