Saltar al contenido principal

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

  1. Descripción General
  2. Estructura de Directorios
  3. Configuración del Proyecto
  4. Sistema de Autenticación
  5. Sistema de Rutas
  6. Estado Global — Redux
  7. Capa API
  8. Módulo VR Learning (VRL)
  9. Módulo VR Training (VRT)
  10. Módulo Admin Inmersys
  11. Lógica Especial por Proyecto/Cliente
  12. Funciones Helper — redux/helpers.js
  13. Funciones Helper — helpers/dateHelpers.js
  14. Componentes Globales
  15. Sistema de Validaciones
  16. Layouts
  17. Hook Personalizado — useHelpButtonReducer
  18. 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óduloRutaDescripció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_MODEL guarda 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ónValorUso
colors.primary#1B1464Azul oscuro principal de la marca
colors.secondary#3366FFAzul brillante de acento
spacing['860px']860pxAncho fijo de contenedores de tabla
spacing['454px']454pxAncho fijo de tarjetas
spacing['430px']430pxVariante de tarjeta
height[1/12..11/12]fraccionesHeights fraccionales para layouts complejos

Dependencias clave

PaqueteVersiónPropósito
react18.2.0Framework UI
react-router-dom6.3.0Routing client-side
@reduxjs/toolkit1.8.3Estado global
antd4.24.7Componentes UI
tailwindcss3.4.13Estilos utilitarios
formik + yup2.2.9 / 0.32.11Formularios + validación
axios1.2.3HTTP client
@nivo/*variosGráficos (bar, line, pie, radar, radial-bar)
moment2.29.4Manipulación de fechas
react-pdf6.2.2Visor de PDFs integrado
react-csv2.2.2Exportación a CSV
papaparse5.3.2Parseo de CSV para importación masiva

4. Sistema de Autenticación

Archivos involucrados

  • src/redux/auth/authSlice.js
  • src/redux/auth/thunk.js
  • src/pages/auth/Form.jsx
  • src/pages/auth/FormContainer.jsx
  • src/pages/auth/FormRestorePassword.jsx
  • src/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)

  1. onLoginAdmin(email, password): Realiza POST /auth/login. Si es exitoso, guarda el x-token en localStorage y despacha setLoginUser.
  2. onGetAdminInfo(): Realiza GET /admin/ con el token almacenado. Carga typeAdmin, enterpriseId, imagen y nombre. Es llamado en el App.js al montar el componente para restaurar la sesión.
  3. Redirección por tipo: En Home.jsx, tras cargar la info, navega automáticamente al módulo correspondiente según typeAdmin:
    • VRT/vrt/client/dashboard
    • VRL/vrl/client/dashboard
    • INMERSYS/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

  1. FormRestorePassword.jsx: Envía email → POST /auth/email → llega link con token al correo.
  2. 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.jsxPrincipalRouter
  • src/routers/PrivateRouter.jsx
  • src/routers/PublicRouter.jsx
  • src/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:
1Dashboard
2Tabla principal
4Analytics

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)

ThunkEndpointDescripción
getAllProjectsByEnterprise()GET /project/enterpriseCarga 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-groupCrea un nuevo grupo
createManyStudens(data)POST /student/manyCarga masiva de estudiantes desde CSV

Thunks VRT (src/redux/VRT/thunks.js)

ThunkEndpointDescripció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 interceptor
  • src/api/enpoints.js — objeto con todos los endpoints
  • src/api/index.js — re-exporta funciones de petición
  • src/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 estudiante
  • groups: 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 ProyectoClienteComportamiento especial
66fafd1d92bbc20883798c43CemexUsa formatinDataCemex() en VRT; perfil de usuario UserPerfilCemex.jsx; función getAverageUsersCemex() en analytics
67367c71fa5b23562aee086cFresnillo-AmaciceUsa formatinDataFresnilloAmacice() en VRT; perfil UserPerfilAmaciceFresnillo.jsx
672e4328031ac03e79f58508Módulos M1_L1Usa getinfobyModules() que parsea el campo de respuesta con formato M{n}_L{n} para identificar módulos y lecciones completadas
679965563ed6fed031d3b488SimiARPerfil de usuario UserPerfilSimiAR.jsx
66155e601e91fe7bde671941ParesaEscala 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:

ArchivoClienteCampos únicos
UserPerfilCemex.jsxCemex VRTMétricas de entrenamiento industrial
UserPerfilCemexVrl.jsxCemex VRLProgreso de módulos de aprendizaje
UserPerfilParesa.jsxParesaScore escalado al 32%, secciones de lecciones especiales
UserPerfilSimiAR.jsxSimiARCampos de AR (Realidad Aumentada)
UserPerfilAmaciceFresnillo.jsxFresnillo-AmaciceDatos de entrenamiento minero
UserPerfilMerz.jsxMerzCampos de entrenamiento médico
UserPerfilEditing.jsxGenéricoPerfil 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 pregunta
  • dataIndex: clave del campo
  • render: función que aplica color según si la respuesta fue correcta
  • sorter: 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ónTipo de datoDescripción
sortPercentages(a, b, key)"75%" → númeroExtrae el número de un string con % y lo compara
sortNumbers(a, b, key)númeroComparación numérica estándar
sortAlphabetically(a, b, key)stringlocaleCompare con soporte de acentos
sortTime(a, b, key)"mm:ss"Convierte tiempo a segundos totales para comparar
sortDate(a, b, key)fecha ISOCompara 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ónDescripciónRetorna
setDateFormat(days)Fecha actual menos N díasmoment object
geTdayToPassSunday()Domingo de la semana actualmoment object
getSundayWeekAgo()Domingo de la semana pasadamoment 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.

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.

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.

ArchivoUsado enDescripción
LayoutAuth.jsxRutas públicasFondo con imagen, formulario centrado
LayoutHome.jsx/homeMínimo, solo redirige a módulo
LayoutLearning.jsxVRLSidebar + header + contenido
LayoutTraining.jsxVRTSidebar + header + contenido
LayoutAdminInmersys.jsxAdminLayout completo admin
LayoutAdminRoute.jsxSubrutas adminWrapper 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:

  1. Usuario arrastra/sube un .csv al CustomDragger.
  2. PapaParse parsea el CSV en el browser (sin enviar al servidor).
  3. Se valida que las columnas requeridas existan (nombre, email, etc.).
  4. Se despacha createManyStudens(parsedData) que llama POST /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.js y el directorio pages/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.js solo configura @testing-library/jest-dom.