Documentación Técnica — inmersys-vrtraining-cms
Versión del proyecto: 1.58.0
Stack: React 18 + Redux Toolkit + Ant Design + Tailwind CSS
Backend API: https://xrlearning.inmersys.com/api/
Tipo: Create React App (CRA) — SPA
Índice
- Descripción General
- Estructura de Directorios
- Configuración del Proyecto
- Sistema de Autenticación
- Sistema de Rutas
- Estado Global — Redux
- Capa API
- Módulo VR Learning (VRL)
- Módulo VR Training (VRT)
- Módulo Admin Inmersys
- Lógica Especial por Proyecto/Cliente
- Funciones Helper — redux/helpers.js
- Funciones Helper — helpers/dateHelpers.js
- Componentes Globales
- Sistema de Validaciones
- Layouts
- Hook Personalizado — useHelpButtonReducer
- Exportación de Datos y PDF Viewer
1. Descripción General
inmersys-vrtraining-cms es un sistema de gestión de contenidos (CMS) y analítica para plataformas de entrenamiento en Realidad Virtual (VR). Está dirigido a administradores de empresas clientes que desean visualizar, gestionar y exportar los resultados de sus empleados en simulaciones VR.
Módulos principales
| Módulo | Ruta | Descripción |
|---|---|---|
| VR Training (VRT) | /vrt/client/* | Dashboard, tabla y analítica de respuestas de simulaciones de entrenamiento |
| VR Learning (VRL) | /vrl/client/* | Dashboard, tabla de usuarios, analítica y gestión de grupos/alumnos |
| Admin Inmersys | /vrl/inmersys/* | Panel interno de Inmersys para gestionar empresas y proyectos |
| Auth | /auth/* | Login, recuperación y cambio de contraseña |
Tipos de usuario
- VRT: Accede solo al módulo VR Training.
- VRL: Accede solo al módulo VR Learning.
- INMERSYS: Accede al panel de administración interno (gestión de empresas).
2. Estructura de Directorios
src/
├── api/ # Configuración de Axios, endpoints y peticiones
├── assets/ # Recursos estáticos (imágenes, íconos, fondos, PDFs)
├── components/ # Componentes UI reutilizables
│ └── common/
│ └── NewUi/ # Segunda versión de componentes UI
├── helpers/ # Utilidades puras (fechas, tablas)
├── hooks/
│ └── home/ # Hooks personalizados de la página home
├── layout/ # Plantillas de layout por sección
├── pages/
│ ├── AdminInmersys/ # Panel admin interno
│ ├── auth/ # Formularios de autenticación
│ ├── common/ # Componentes compartidos entre páginas
│ │ └── UIVersionTwo/ # Perfiles de usuario por cliente (Cemex, Paresa, etc.)
│ ├── Home/ # Página de inicio post-login
│ ├── VrLearning/ # Módulo VRL completo
│ │ └── templates/ # Sub-plantillas del módulo (menú, tablas, modales)
│ └── VrTraining/ # Módulo VRT completo
│ └── templates/
│ └── TemplatesVersionTwo/ # Segunda versión de vistas VRT
├── redux/ # Store, slices y thunks de Redux Toolkit
│ ├── auth/
│ ├── AdminInmersys/
│ ├── MenuSlice/
│ ├── tableSlice/
│ ├── VRL/
│ └── VRT/
├── routers/ # Configuración de React Router v6
├── validations/ # Esquemas Yup para formularios
├── App.js # Componente raíz
└── index.js # Punto de entrada
3. Configuración del Proyecto
Variables de entorno (.env)
GENERATE_SOURCEMAP=false
REACT_APP_OPENAI_API_KEY=<clave openai>
REACT_APP_OPENAI_MODEL=<id del asistente>
REACT_APP_OPENAI_MODELguarda el ID de un asistente de OpenAI, no el nombre del modelo base. Se utiliza para funcionalidades de asistente IA dentro del CMS.
Tailwind CSS — extensiones personalizadas (tailwind.config.js)
| Extensión | Valor | Uso |
|---|---|---|
colors.primary | #1B1464 | Azul oscuro principal de la marca |
colors.secondary | #3366FF | Azul brillante de acento |
spacing['860px'] | 860px | Ancho fijo de contenedores de tabla |
spacing['454px'] | 454px | Ancho fijo de tarjetas |
spacing['430px'] | 430px | Variante de tarjeta |
height[1/12..11/12] | fracciones | Heights fraccionales para layouts complejos |
Dependencias clave
| Paquete | Versión | Propósito |
|---|---|---|
react | 18.2.0 | Framework UI |
react-router-dom | 6.3.0 | Routing client-side |
@reduxjs/toolkit | 1.8.3 | Estado global |
antd | 4.24.7 | Componentes UI |
tailwindcss | 3.4.13 | Estilos utilitarios |
formik + yup | 2.2.9 / 0.32.11 | Formularios + validación |
axios | 1.2.3 | HTTP client |
@nivo/* | varios | Gráficos (bar, line, pie, radar, radial-bar) |
moment | 2.29.4 | Manipulación de fechas |
react-pdf | 6.2.2 | Visor de PDFs integrado |
react-csv | 2.2.2 | Exportación a CSV |
papaparse | 5.3.2 | Parseo de CSV para importación masiva |
4. Sistema de Autenticación
Archivos involucrados
src/redux/auth/authSlice.jssrc/redux/auth/thunk.jssrc/pages/auth/Form.jsxsrc/pages/auth/FormContainer.jsxsrc/pages/auth/FormRestorePassword.jsxsrc/pages/auth/RestorePassword.jsx
Estado Redux (authSlice)
{
email: string,
typeAdmin: string, // "VRL" | "VRT" | "INMERSYS"
enterpriseId: string, // ID de la empresa del usuario
imgUrl: string, // URL del logo de la empresa
nameEnterprise: string, // Nombre de la empresa
name: string // Nombre del usuario
}
Flujo de login (auth/thunk.js)
onLoginAdmin(email, password): RealizaPOST /auth/login. Si es exitoso, guarda elx-tokenenlocalStoragey despachasetLoginUser.onGetAdminInfo(): RealizaGET /admin/con el token almacenado. CargatypeAdmin,enterpriseId, imagen y nombre. Es llamado en elApp.jsal montar el componente para restaurar la sesión.- Redirección por tipo: En
Home.jsx, tras cargar la info, navega automáticamente al módulo correspondiente segúntypeAdmin:VRT→/vrt/client/dashboardVRL→/vrl/client/dashboardINMERSYS→/vrl/inmersys/{enterpriseId}/dashboard
Restauración de sesión
App.js llama onGetAdminInfo() en el useEffect inicial. Si el token en localStorage es válido, el estado de Redux se hidrata y el PrivateRouter permite el acceso sin reloguear.
Recuperación de contraseña
FormRestorePassword.jsx: Envía email →POST /auth/email→ llega link con token al correo.RestorePassword.jsx: Formulario de nueva contraseña →POST /auth/restore/{token}.
Persistencia de sesión
El token se almacena en localStorage con la clave x-token. El interceptor de Axios lo lee en cada petición y lo agrega como header x-token.
5. Sistema de Rutas
Archivos involucrados
src/routers/index.jsx—PrincipalRoutersrc/routers/PrivateRouter.jsxsrc/routers/PublicRouter.jsxsrc/routers/paths.js
Estructura del router
PrincipalRouter
├── PublicRouter (rutas sin autenticación)
│ └── LayoutAuth
│ ├── / → Form (Login)
│ ├── /restorepassword → FormRestorePassword
│ └── /newPassword → RestorePassword
└── PrivateRouter (requiere email en Redux)
├── / → Home
├── /vrt/client/dashboard → TrainingDashboard
├── /vrt/client/table → TrainingTable
├── /vrt/client/analytics → TrainingAnalytics
├── /vrt/client/help → TrainingHelp
├── /vrl/client/dashboard → LearningDashboard
├── /vrl/client/table → LearningTable
├── /vrl/client/analytics → LearningAnalytics
├── /vrl/client/users → LearningTableUsers
└── /vrl/inmersys/:idEnterprise/dashboard → HomeInmersys
/vrl/inmersys/:idEnterprise/table
/vrl/inmersys/:idEnterprise/analytics
Lógica de protección (PrivateRouter.jsx)
Verifica que state.auth.email no esté vacío. Si está vacío, redirige a /auth. Este es el único guard de autenticación: si el email existe en Redux, la sesión se considera válida.
Constantes de rutas (paths.js)
Centraliza todos los pathname como constantes para evitar strings hardcodeados en navegación.
6. Estado Global — Redux
Store (src/redux/store.js)
{
auth: // Datos del usuario autenticado
menuView: // Página activa del menú lateral
table: // Datos de tabla + headers + respuestas correctas
adminInmersys: // Lista de empresas (solo rol INMERSYS)
vrlSlice: // Estado del módulo VR Learning
vrtSlice: // Estado del módulo VR Training
}
Middleware personalizado: Se configura serializableCheck para ignorar rutas con fechas (table/onSetArrayData) y arrays de respuestas, ya que los datos procesados incluyen objetos moment y arrays de gran tamaño.
Slice: menuSlice
Archivo: src/redux/MenuSlice/menuSlice.js
Controla qué vista del módulo está activa (sin usar React Router para la navegación interna entre secciones).
state.actualPage:
1 → Dashboard
2 → Tabla principal
4 → Analytics
Acción: onChnageViewTo(number) — llamada desde los botones del menú lateral.
Slice: tableSlice
Archivo: src/redux/tableSlice/tableSlice.js
Almacena la data procesada que se muestra en la tabla principal.
{
arrayData: [], // Filas de la tabla (usuarios + respuestas formateadas)
headersTable: [], // Columnas generadas dinámicamente
correctAnswers: [] // Array de respuestas correctas para colorear celdas
}
Este slice recibe datos ya procesados por redux/helpers.js y los sirve como props a CustomTable.
Slice: vrlSlice (VR Learning)
Archivo: src/redux/VRL/vrlSlice.js
{
proyects: [], // Proyectos del usuario
responses: [], // Respuestas crudas del API
averageResponses: {}, // Promedios calculados
groups: [], // Grupos del proyecto seleccionado
proyectSelected: null, // Proyecto activo
arrayDataCombined: [] // Respuestas + datos de grupos combinados
}
arrayDataCombined es la estructura clave de VRL: combina el array de respuestas del quiz con la información de grupos a la que pertenece cada estudiante. Se genera en LearningHelpersTable.js::filteruserPerGrup().
Slice: vrtSlice (VR Training)
Archivo: src/redux/VRT/vrtSlice.js
Estructura similar a vrlSlice pero orientada a respuestas de entrenamiento. Incluye campos adicionales para manejar los datos formateados por cliente (Cemex, Fresnillo, estándar).
Thunks VRL (src/redux/VRL/thunks.js)
| Thunk | Endpoint | Descripción |
|---|---|---|
getAllProjectsByEnterprise() | GET /project/enterprise | Carga proyectos del usuario autenticado |
getAllResponsesFormProject(id) | GET /learning-answer/{id} | Respuestas de todos los estudiantes del proyecto |
getAllGroups(proyectId) | GET /learning-group/project/{id} | Grupos del proyecto |
createGroup(data) | POST /learning-group | Crea un nuevo grupo |
createManyStudens(data) | POST /student/many | Carga masiva de estudiantes desde CSV |
Thunks VRT (src/redux/VRT/thunks.js)
| Thunk | Endpoint | Descripción |
|---|---|---|
getAllResponsesTrainingFormProject(id) | GET /trainig-answer/{id} | Respuestas del módulo de entrenamiento |
formatinData(responses) | — (local) | Formatea datos para la mayoría de clientes |
formatinDataCemex(responses) | — (local) | Formato especial para Cemex |
formatinDataFresnilloAmacice(responses) | — (local) | Formato especial para Fresnillo-Amacice |
7. Capa API
Archivos involucrados
src/api/api.js— instancia de Axios con interceptorsrc/api/enpoints.js— objeto con todos los endpointssrc/api/index.js— re-exporta funciones de peticiónsrc/api/petitionsTraining.js— peticiones del módulo VRT
Interceptor de Axios (api.js)
// Agrega x-token en cada request desde localStorage
instance.interceptors.request.use(config => {
config.headers['x-token'] = localStorage.getItem('x-token');
return config;
});
La baseURL apunta a https://xrlearning.inmersys.com/api/. Todos los módulos usan esta misma instancia.
Mapa de endpoints (enpoints.js)
{
auth: {
login: "/auth/login",
requestNewPassword: "/auth/email",
changePassword: "/auth/restore/"
},
admin: {
getInfo: "/admin/",
createAdmin: "/admin/",
getProyectsById: "/project/inmersys/enterprise/"
},
enterprise: {
create: "/enterprise/",
getAll: "/enterprise/all",
disabed: "/enterprise/" // PATCH para deshabilitar
},
project: {
allProjectsForId: "/project/enterprise",
allresponsesbyproyectid: "/learning-answer/",
allresponsesTrainingByProyectid: "/trainig-answer/",
createProyect: "/project/",
detailsProyecstbyEnterpiseId: "/project/completeData/"
},
student: {
createone: "/student/",
createMany: "/student/many",
editStudent: "/student",
deletemany: "/student/deleteMany"
},
gruop: {
create: "/learning-group",
getAllByProyectId: "/learning-group/project/",
deletegroup: "/learning-group/"
}
}
Nota: Algunos nombres tienen typos originales del backend (
trainig-answer,gruop,disabed) que se mantienen intencionalmente para coincidir con la API.
8. Módulo VR Learning (VRL)
Archivos principales
src/pages/VrLearning/
├── LearningDashboard.jsx # Vista 1: resumen y KPIs
├── LearningTable.jsx # Vista 2: tabla de respuestas
├── LearningAnalytics.jsx # Vista 4: gráficos y análisis
├── LearningTableUsers.jsx # Vista especial: usuarios y grupos
├── ViewerPdf.jsx # Visor de manual PDF
└── templates/
├── MainContent.jsx # Contenedor principal del módulo
├── LearningMenu.jsx # Menú lateral del módulo
├── AddGroup.jsx # Modal para crear grupos (v1)
├── AddGroupV2.jsx # Modal para crear grupos (v2 con más campos)
├── AddStudents.jsx # Modal para añadir estudiantes uno a uno
├── AddUsersByFile.jsx # Modal para carga masiva de estudiantes CSV
├── AddResults.jsx # Modal para importar resultados
├── AddUser.jsx # Modal para crear usuario
├── CustomTableUsers.jsx # Tabla de usuarios dentro del módulo
├── HelpButtonsLearning.jsx # Botones de acción del menú (ayuda, CSV, etc.)
├── UpdateCampaing.jsx # Modal para actualizar campaña/proyecto
└── LearningTabl.jsx # Tabla principal del módulo
Flujo de datos
1. Al seleccionar proyecto → getAllProjectsByEnterprise()
2. Al seleccionar proyecto → getAllResponsesFormProject(proyectId)
3. Respuestas crudas → filteruserPerGrup() → arrayDataCombined
4. arrayDataCombined → formatingHeaders() → headersTable + arrayData
5. tableSlice recibe arrayData + headersTable → se renderiza en CustomTable
Lógica de combinación de grupos (LearningHelpersTable.js)
La función filteruserPerGrup(responses, groups) es la pieza central de VRL. Recibe:
responses: array de respuestas del quiz de cada estudiantegroups: array de grupos con sus alumnos asignados
Devuelve un array donde cada respuesta lleva incorporado el nombre del grupo al que pertenece el estudiante. Esto permite filtrar la tabla por grupo en el frontend.
9. Módulo VR Training (VRT)
Archivos principales
src/pages/VrTraining/
├── TrainingDashboard.jsx # Vista 1: KPIs generales
├── TrainingTable.jsx # Vista 2: tabla de respuestas de entrenamiento
├── TrainingAnalytics.jsx # Vista 4: gráficos
├── TrainingHelp.jsx # Vista de ayuda y documentación
└── templates/
├── TrainingMainContent.jsx # Contenedor principal del módulo
├── TrainingMenu.jsx # Menú lateral
├── TrainingTabl.jsx # Tabla principal
├── HelperButtonsTraining.jsx # Botones de acción
└── TemplatesVersionTwo/
├── TrainingContentVT.jsx # Segunda versión del contenedor (clientes nuevos)
└── TrainingTablevt.jsx # Segunda versión de tabla
Lógica de formateo por cliente
El VRT tiene tres rutas de formateo distintas según el ID del proyecto activo:
// En thunks VRT
if (proyectId === "66fafd1d92bbc20883798c43") {
formatinDataCemex(responses)
} else if (proyectId === "67367c71fa5b23562aee086c") {
formatinDataFresnilloAmacice(responses)
} else {
formatinData(responses) // formateo estándar
}
Cada función produce un array con estructura diferente de columnas para adaptarse a los módulos de entrenamiento específicos de cada cliente.
10. Módulo Admin Inmersys
Archivos principales
src/pages/AdminInmersys/
├── HomeInmersys.jsx # Dashboard del admin (listado de empresas)
├── HeaderAdmin.jsx # Cabecera con logo y nombre
├── MenuAdmin.jsx # Menú lateral del admin
└── ModalAddEnterprise.jsx # Modal para crear nueva empresa
Lógica especial
Solo los usuarios con typeAdmin === "INMERSYS" acceden a este módulo. El flujo completo de creación de empresa está en un solo thunk:
addNewEnterpriseAndAdmin() (en src/redux/AdminInmersys/thunks.js):
1. POST /enterprise/ → crea la empresa
2. POST /admin/ → crea el usuario admin de esa empresa
3. POST /project/ → crea el proyecto inicial asociado
4. Despacha addEnterprises al store
Otras acciones del admin:
getAllEnterpiseds():GET /enterprise/all— lista todas las empresas.ChangeStatusEnterprise(id):PATCH /enterprise/{id}— habilita/deshabilita empresa.
Validación del formulario de empresa (validationEnterpriseRegister.js)
Valida:
- Email format
- Password min 8 caracteres
- Nombre de empresa, nombre de proyecto, nombre de usuario: requeridos
- Tipo de usuario: requerido (
VRT,VRL,INMERSYS)
11. Lógica Especial por Proyecto/Cliente
Este es uno de los aspectos más únicos del proyecto. Varios archivos contienen condiciones if basadas en IDs de proyecto hardcodeados para adaptar el comportamiento del CMS a cada cliente.
Tabla de IDs especiales
| ID de Proyecto | Cliente | Comportamiento especial |
|---|---|---|
66fafd1d92bbc20883798c43 | Cemex | Usa formatinDataCemex() en VRT; perfil de usuario UserPerfilCemex.jsx; función getAverageUsersCemex() en analytics |
67367c71fa5b23562aee086c | Fresnillo-Amacice | Usa formatinDataFresnilloAmacice() en VRT; perfil UserPerfilAmaciceFresnillo.jsx |
672e4328031ac03e79f58508 | Módulos M1_L1 | Usa getinfobyModules() que parsea el campo de respuesta con formato M{n}_L{n} para identificar módulos y lecciones completadas |
679965563ed6fed031d3b488 | SimiAR | Perfil de usuario UserPerfilSimiAR.jsx |
66155e601e91fe7bde671941 | Paresa | Escala del score al 32% en el render; perfil UserPerfilParesa.jsx; lecciones especiales |
Componentes de perfil por cliente (pages/common/UIVersionTwo/)
Cada cliente tiene su propio componente de detalle de usuario que adapta los campos mostrados:
| Archivo | Cliente | Campos únicos |
|---|---|---|
UserPerfilCemex.jsx | Cemex VRT | Métricas de entrenamiento industrial |
UserPerfilCemexVrl.jsx | Cemex VRL | Progreso de módulos de aprendizaje |
UserPerfilParesa.jsx | Paresa | Score escalado al 32%, secciones de lecciones especiales |
UserPerfilSimiAR.jsx | SimiAR | Campos de AR (Realidad Aumentada) |
UserPerfilAmaciceFresnillo.jsx | Fresnillo-Amacice | Datos de entrenamiento minero |
UserPerfilMerz.jsx | Merz | Campos de entrenamiento médico |
UserPerfilEditing.jsx | Genérico | Perfil editable base |
Lógica de headers por proyecto (redux/helpersHeadersPerProyect.js)
Contiene funciones que devuelven arrays de columnas (headers) distintos según el ID de proyecto. En lugar de generar columnas genéricas, cada cliente puede tener columnas con nombres, anchos y ordenamientos personalizados.
Lógica de respuestas por proyecto (redux/helperResponsesPerProyect.js)
Similar a headers, pero para el procesamiento de las respuestas crudas del API. Permite que cada cliente tenga un modelo de datos diferente en el backend sin cambiar la interfaz base.
12. Funciones Helper — redux/helpers.js
Este archivo (534 líneas) es el núcleo de procesamiento de datos del proyecto. Todas las funciones son puras y trabajan sobre arrays de respuestas del API.
Funciones de renderizado de tabla
renderColor(value, correctAnswers)
Devuelve el color de fondo de una celda según si la respuesta es correcta, incorrecta o neutral. Se usa en formatingHeaders() para colorear la tabla de respuestas.
formatingHeaders(responses, correctAnswers)
Función central de la tabla. Recibe el array de respuestas y construye dinámicamente el array de columnas de Ant Design. Cada columna tiene:
title: nombre de la preguntadataIndex: clave del camporender: función que aplica color según si la respuesta fue correctasorter: función de ordenamiento adaptada al tipo de dato (string, número, tiempo, fecha)filters: filtros desplegables generados automáticamente a partir de los valores únicos
filters(data, key)
Genera el array de filtros únicos a partir de los valores existentes en la data para una columna específica.
Funciones de ordenamiento
| Función | Tipo de dato | Descripción |
|---|---|---|
sortPercentages(a, b, key) | "75%" → número | Extrae el número de un string con % y lo compara |
sortNumbers(a, b, key) | número | Comparación numérica estándar |
sortAlphabetically(a, b, key) | string | localeCompare con soporte de acentos |
sortTime(a, b, key) | "mm:ss" | Convierte tiempo a segundos totales para comparar |
sortDate(a, b, key) | fecha ISO | Compara timestamps |
Funciones de filtrado de datos
filterValues(data, filtersObj)
Aplica múltiples filtros simultáneos sobre el array de datos. filtersObj es el objeto que entrega onChange de la tabla de Ant Design.
filterDataForDates(data, startDate, endDate)
Filtra respuestas dentro de un rango de fechas. Las fechas se comparan con moment.js. Usado en DropDownFilterDate.
filterResponsesOnlyLastEntry(responses)
De todas las respuestas de un usuario, conserva solo la más reciente (la del mayor timestamp). Útil para mostrar el estado actual de cada estudiante sin duplicados.
filterResponsesLastEntryPerDate(responses, date)
Variante del anterior, pero además filtra por fecha. Devuelve la última entrada de cada usuario dentro de una fecha específica.
Funciones de análisis y KPIs
getAverageTimePerUser(responses)
Calcula el tiempo promedio que un usuario tarda en completar el módulo. Retorna el promedio en segundos de todas las respuestas del grupo.
getAverageCorrectAnswers(responses, correctAnswers)
Calcula el porcentaje promedio de respuestas correctas comparando cada respuesta con el array correctAnswers.
countUsersParticipate(responses)
Cuenta usuarios únicos que han enviado al menos una respuesta. Usa Set para deduplicar por ID de usuario.
correctResponsesByQuestions(responses, correctAnswers)
Devuelve un array con el porcentaje de aciertos por pregunta. Usado en los gráficos de barras de Analytics.
getAverageRepetitionsforUser(responses)
Calcula cuántas veces en promedio un usuario ha repetido el módulo (número de respuestas por usuario / total de usuarios).
getUsersPerTime(responses)
Agrupa a los usuarios según el tiempo que tardaron en completar, devolviendo un histograma. Usado en el gráfico de distribución de tiempos.
secondsToHms(seconds)
Convierte segundos a formato "Xh Xm Xs" o "Xm Xs" para presentación en UI.
Funciones de análisis de módulos
getinfobyModules(responses)
Lógica especial para proyectos con estructura modular. Parsea el campo de respuesta que sigue el formato M{número}_L{número} (ej. M1_L3, M2_L1) para determinar:
- Qué módulos ha completado el usuario
- Qué lecciones dentro de cada módulo ha completado
- El progreso total del curriculum
Solo se activa para el proyecto con ID 672e4328031ac03e79f58508.
getAverageUsersCemex(responses)
Función de análisis específica para Cemex. Calcula promedios considerando la estructura de datos particular de las respuestas del entrenamiento Cemex (campos de seguridad industrial, procedimientos, etc.).
13. Funciones Helper — helpers/dateHelpers.js
Funciones para generar rangos de fechas usados por DropDownFilterDate.
| Función | Descripción | Retorna |
|---|---|---|
setDateFormat(days) | Fecha actual menos N días | moment object |
geTdayToPassSunday() | Domingo de la semana actual | moment object |
getSundayWeekAgo() | Domingo de la semana pasada | moment object |
getTodayFilter() | Rango: hoy | [start, end] |
getWeekFilter() | Rango: lunes a hoy de la semana actual | [start, end] |
getMonthFilter() | Rango: día 1 del mes a hoy | [start, end] |
getYearFilter() | Rango: enero 1 del año a hoy | [start, end] |
Estos rangos se pasan como parámetros a filterDataForDates().
14. Componentes Globales
CustomTable.jsx
Wrapper de Table de Ant Design con configuración base (paginación, scroll, tamaño de fila). Recibe dataSource y columns del tableSlice.
CustomModal.jsx
Wrapper de Modal de Ant Design. Agrega estilos base y manejo de estado de apertura/cierre mediante prop isOpen.
CustomDragger.jsx
Componente de drag & drop para cargar archivos. Usado en AddUsersByFile.jsx para la importación masiva de estudiantes vía CSV. Internamente usa Upload.Dragger de Ant Design y PapaParse para parsear el CSV.
DropDownFilterDate.jsx
Dropdown con opciones de filtro temporal predefinidas:
- Hoy
- Esta semana
- Este mes
- Este año
- Rango personalizado (abre un
DatePicker.RangePicker)
Al seleccionar una opción, llama filterDataForDates() y despacha los resultados al tableSlice.
RangeDate.jsx
Selector de rango de fechas autónomo (sin dropdown). Usado en Analytics para filtrar el rango de los gráficos.
MenuHeader.jsx
Cabecera con el logo de la empresa (desde auth.imgUrl), nombre y botón de logout. Común a todos los módulos privados.
ItemDownloadCSV.jsx
Botón que usa react-csv para exportar el contenido actual de tableSlice.arrayData a un archivo .csv. Los headers del CSV se derivan de tableSlice.headersTable.
ItemHelpButton.jsx
Botón de interrogación que abre un modal de ayuda contextual. El contenido del modal varía según el módulo activo.
SplitSelector.jsx
Selector doble para elegir entre dos proyectos o grupos para comparación. Usado en vistas de Analytics comparativas.
WebVersion.jsx
Componente que detecta si el dispositivo es móvil y bloquea el acceso mostrando un mensaje de "solo disponible en tablet/desktop". Está presente en el App.js como wrapper global.
15. Sistema de Validaciones
Todos los formularios usan Formik + Yup.
ValidationLogin.js
validationLogin: {
user: string().required(),
password: string().min(6).required()
}
emailValidation: {
email: string().email().required()
}
Restorepassword: {
password: string().min(8).required(),
passwordRepeat: string().oneOf([ref('password')]).required()
}
ValidationRegister.js
Validación para registro de usuario final:
{
email: email format requerido,
password: min(8) + /[A-Z]/ + /[a-z]/ + /[0-9]/ + /[!@#$%]/,
name: requerido,
phone: exactamente 10 dígitos numéricos,
address: requerido,
age: número entero > 18,
accept: literal true (T&C)
}
validationEnterpriseRegister.js
Para el formulario de creación de empresa en el panel Admin Inmersys:
{
email: email format,
password: min(8),
namEnterprise: requerido,
namePyect: requerido,
nameUser: requerido,
typeUser: oneOf(["VRT", "VRL", "INMERSYS"]).required()
}
16. Layouts
Cada sección del CMS tiene su propio layout con estructura visual diferente.
| Archivo | Usado en | Descripción |
|---|---|---|
LayoutAuth.jsx | Rutas públicas | Fondo con imagen, formulario centrado |
LayoutHome.jsx | /home | Mínimo, solo redirige a módulo |
LayoutLearning.jsx | VRL | Sidebar + header + contenido |
LayoutTraining.jsx | VRT | Sidebar + header + contenido |
LayoutAdminInmersys.jsx | Admin | Layout completo admin |
LayoutAdminRoute.jsx | Subrutas admin | Wrapper para sub-páginas del admin |
17. Hook Personalizado — useHelpButtonReducer
Archivo: src/hooks/home/useHelpButtonReducer.js
Maneja el estado de visibilidad de múltiples modales de ayuda/acción que existen en la barra de herramientas de cada módulo.
Estado
{
updateFile: false, // Modal para actualizar archivo de resultados
addTraining: false, // Modal para agregar entrenamiento
exportCsv: false, // Modal de exportación CSV
addSubject: false, // Modal para agregar materia/grupo
addResults: false, // Modal de resultados
addStudents: false // Modal de estudiantes
}
Uso
const { state, setUpdateFile, setAddTraining, setExportCsv, ... } = useHelpButtonReducer();
// Para abrir/cerrar un modal:
setAddStudents(true); // o false
Cada setter acepta un booleano. El hook centraliza el estado que antes estaba disperso en múltiples useState dentro de los componentes de menú.
18. Exportación de Datos y PDF Viewer
Exportación CSV (ItemDownloadCSV.jsx)
Usa react-csv. Lee directamente de tableSlice.arrayData (los datos mostrados en tabla, ya filtrados y ordenados). Los headers se mapean desde tableSlice.headersTable.
El CSV exportado refleja exactamente lo que el usuario ve en pantalla, incluyendo filtros aplicados.
Importación masiva de estudiantes (AddUsersByFile.jsx)
Flujo:
- Usuario arrastra/sube un
.csvalCustomDragger. PapaParseparsea el CSV en el browser (sin enviar al servidor).- Se valida que las columnas requeridas existan (
nombre,email, etc.). - Se despacha
createManyStudens(parsedData)que llamaPOST /student/many.
PDF Viewer (ViewerPdf.jsx)
Usa react-pdf. Carga el PDF del manual desde src/assets/pdf/. Incluye controles de navegación por páginas y zoom. El PDF sirve como manual de uso de la plataforma VR para el usuario administrador.
Notas de desarrollo
- Los typos en nombres de endpoints (
trainig-answer,gruop,disabed) son del backend original y se mantienen para no romper las peticiones. - Algunos IDs de proyecto están hardcodeados en múltiples archivos. Si se agregan nuevos clientes con lógica especial, se deben buscar y actualizar en:
VRT/thunks.js,redux/helpers.js,redux/helpersHeadersPerProyect.js,redux/helperResponsesPerProyect.jsy el directoriopages/common/UIVersionTwo/. - La versión "Two" de algunos componentes (
TemplatesVersionTwo,NewUi,UIVersionTwo) coexiste con la versión original. No hay eliminación de la v1 por compatibilidad hacia atrás con clientes existentes. - El proyecto no tiene tests funcionales;
setupTests.jssolo configura@testing-library/jest-dom.