¡Bienvenido al equipo! Este documento te guiará a través del proyecto VTG‑WebForm, una aplicación desarrollada por Grupo NOVA para gestionar las inscripciones de estudiantes a los diferentes grupos estudiantiles durante el evento VTG (Vive Tu Grupo). El objetivo es que cualquier persona nueva en la organización pueda entender la estructura, ejecutar el proyecto localmente y empezar a contribuir sin problemas.
- Introducción
- Requisitos previos
- Estructura del proyecto
- Módulos principales
- 4.1 Páginas y enrutamiento (App Router)
- 4.2 Formularios de grupos
- 4.3 Formulario de correo y datos personales/académicos
- 4.4 Panel de análisis (analytics)
- 4.5 Paneles de listas (lists)
- 4.6 Autenticación (JWT + cookies)
- 4.7 API routes
- 4.8 Utilidades de encriptación
- 4.9 Capa de base de datos
- 4.10 Componentes de UI reutilizables
- 4.11 Configuración y assets
- Guía de ejecución local
VTG‑WebForm es una aplicación web construida con Next.js (versión 15, App Router) y TypeScript. Su propósito es centralizar el proceso de inscripción de los estudiantes de la universidad a los distintos grupos estudiantiles durante la semana VTG. Cada grupo tiene su propio formulario con preguntas específicas, y toda la información se almacena en una base de datos PostgreSQL (usamos Supabase como proveedor en la nube).
La aplicación cuenta con:
- Un flujo de registro por pasos: correo → datos personales → datos académicos → selección de grupo → formulario específico del grupo.
- Autenticación mediante JWT almacenado en cookies.
- reCAPTCHA en el primer formulario para evitar spam.
- Encriptación híbrida (RSA + AES) de los datos sensibles enviados desde el frontend.
- Paneles de análisis (estadísticas generales) y paneles de listas (para que cada grupo pueda ver sus inscritos) protegidos por contraseñas fijas.
- Un diseño colorido y temático (estilo FIFA) con animaciones (confeti) para mejorar la experiencia de usuario.
Este documento está pensado para los nuevos miembros de Grupo NOVA, por lo que encontrarás explicaciones claras y ejemplos que te ayudarán a familiarizarte rápidamente con el código.
Antes de empezar, asegúrate de tener instalado en tu máquina:
- Node.js versión 22 o superior (recomendada la LTS).
- npm como gestor de paquetes (Puedes usar pnpm para que sea mas rapido).
- Git para clonar el repositorio.
- Docker y Docker Compose (opcional, solo si quieres levantar una base de datos local en lugar de usar Supabase).
- Conocimientos básicos de:
- React (componentes, hooks)
- Next.js (enrutamiento, API routes)
- TypeScript (tipado básico)
- SQL (consultas simples)
Si aún no dominas alguno de estos temas, no te preocupes: con la práctica irás aprendiendo. Lo importante es tener la actitud para explorar y preguntar.
vtg-webform/ │ ├── public/ # Assets estáticos (imágenes, logos, fuentes) ├── scripts/ # Scripts auxiliares y automatizaciones ├── src/ │ ├── app/ # Frontend (Next.js App Router) │ ├── pages/api/ # Backend (API Routes) │ ├── lib/ # Utilidades (ej: crypto, decrypt) │ └── fonts/ # Tipografías personalizadas │ ├── docker-compose.yaml # Configuración de servicios (MySQL + phpMyAdmin) ├── init.sql # Script de inicialización de base de datos ├── package.json # Dependencias y scripts del proyecto └── README.md # Documentación principal
Todas las rutas están en src/app/. Cada subcarpeta representa una ruta:
/→ página de inicio con animación de confeti y redirección automática a/email./email→ formulario de correo institucional con reCAPTCHA./home→ formulario de datos personales (nombre y apellidos)./academic→ formulario de datos académicos (pregrado, segundo pregrado, semestre)./groupslist→ selector de grupo estudiantil./groups/[grupo]→ formulario específico de cada grupo (ej:/groups/aiesec)./90+1→ página final después de inscribirse a un grupo ./analytics→ dashboard de estadísticas (requiere admin)./lists/[grupo]→ listado de inscritos por grupo/assessment,/assessmentassistance,/talk,/talk_animation→ páginas para el proceso de assessment de NOVA (otro flujo interno).
Cada página es un componente de React (page.tsx) que puede incluir lógica de autenticación, formularios y animaciones.
Los formularios específicos de cada grupo se encuentran en src/app/globalcomponents/Forms/ y tienen nombres como Form-Aiesec.tsx, Form-NOVA.tsx, etc. Todos siguen una estructura similar:
- Importan
FormContainer,Select,Input,Buttonde@/app/globalcomponents/UI. - Usan el hook
useStatepara manejar el estado de envío. - Al enviar, llaman a
encryptedFetch(ver sección 4.8) para enviar los datos a la API correspondiente (ej:/api/forms/aiesec). - Muestran notificaciones con
react-toastify. - Redirigen a
/90+1en caso de éxito (excepto algunos grupos que redirigen a otra pagina como el flujo de nova).
Ejemplo mínimo de un formulario de grupo:
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
const formData = new FormData(e.currentTarget);
const response = await encryptedFetch('/api/forms/aiesec', Object.fromEntries(formData));
// manejo de respuesta...
};- Form-Email.tsx: pide correo institucional y verifica reCAPTCHA. Se comunica con
/api/Data-Email. Si el usuario ya existe, redirige según su progreso; si no, lo crea y redirige a/home. Genera un JWT y lo guarda en cookie HttpOnly. - Form-Personal.tsx: envía nombre y apellidos a
/api/Data-Personal. - Form-Academic.tsx: envía pregrado, segundo pregrado y semestre a
/api/Data-Academic.
Estos formularios comparten la misma estructura básica que los de grupo.
Ruta: /analytics. Protegido por la variable NEXT_PUBLIC_ANALY_TSS. Muestra el componente <Dashboard /> con varios subcomponentes:
career.tsx– Inscritos por carrera.days.tsx– Inscritos por día.groups.tsx– Inscritos por grupo estudiantil.hours.tsx– Inscritos por hora del día.semester.tsx– Inscritos por semestre.top-group.tsx– Grupo con más inscritos.totalpersons.tsx– Total de personas registradas.
Todos obtienen datos de los endpoints /api/analytics/* y se actualizan cada 5 segundos con setInterval.
Ruta: /lists/[grupo] (ej. /lists/aiesec). Cada grupo tiene su propia página, protegida por contraseñas individuales. Consultan su endpoint /api/lists/[grupo] y muestran los datos en tablas, con opción de exportar a CSV y cambiar entre vista agrupada y lista plana.
Se usa JWT almacenado en cookie HttpOnly llamada jwtToken.
- Generación: en
/api/Data-Email(y otros endpoints) se firma el JWT conJWT_SECRET_KEYy se envía medianteSet-Cookie. - Verificación en cliente: el hook
useAuthCheck(enhooks/useAuthCheck.ts) llama a/api/cookieCheck. Si no es válido, redirige a/. - Verificación en servidor: muchos endpoints usan
verifyJwtFromCookies(encookieManagement.ts) para extraer y verificar el token, devolviendo el email.
La cookie tiene una duración corta (12 minutos) pero se renueva en cada paso.
Todas las rutas están en src/pages/api/. Principales endpoints:
Data-Email.ts,Data-Personal.ts,Data-Academic.ts– manejan los pasos iniciales.forms/[grupo].ts– reciben datos de formularios de grupos e insertan en la tabla correspondiente.analytics/*.ts– devuelven datos agregados para gráficos.lists/[grupo].ts– devuelven registros de cada grupo con joins apersona.redirecting.ts– recibe el grupo seleccionado, verifica si ya está inscrito y redirige al formulario correspondiente.cookieCheck.ts– verifica validez del JWT.
src/lib/crypto.ts y src/lib/decrypt.ts implementan encriptación híbrida RSA + AES.
- Cliente (
crypto.ts): genera una clave AES aleatoria, encripta los datos con AES‑CBC, luego encripta la clave AES con la clave pública RSA (NEXT_PUBLIC_RSA_PUBLIC_KEY). Envía{ encryptedKey, encryptedData, iv }. - Servidor (
decrypt.ts): desencripta la clave AES con la clave privada RSA (RSA_PRIVATE_KEY), luego desencripta los datos con AES. El helpergetRequestBodydesencripta automáticamente si el headerX-Encryptedestá presente.
src/pages/api/db.ts maneja la conexión a PostgreSQL (Supabase). Exporta:
getPool()– devuelve un pool de conexiones (singleton).dbQuery()– ejecuta una consulta, con reconexión automática en caso de fallo.withTransaction()– ejecuta múltiples consultas dentro de una transacción.
Tablas principales:
persona: datos básicos (correo, nombre, pregrado, semestre, etc.)- Tablas por grupo:
aiesec,nova,club_in, etc. (clave foránea apersona(correo))
Esquema simplificado:
persona (
correo VARCHAR PRIMARY KEY,
nombre VARCHAR,
pregrado VARCHAR,
pregrado_2 VARCHAR,
semestre INTEGER,
fecha_creacion TIMESTAMP DEFAULT NOW()
);
aiesec (
id_grupo INTEGER,
correo VARCHAR REFERENCES persona(correo),
departamento VARCHAR,
PRIMARY KEY (id_grupo, correo)
);
-- similar para otros gruposEn src/app/globalcomponents/UI/:
- Button.tsx: botones con temas (default, fifa, china) y variantes de color.
- Input.tsx y Select.tsx: inputs y selects estilizados, con soporte para errores.
- FormContainer.tsx: contenedor con diseño de tarjeta para formularios.
- ConfettiAnimation.tsx y Pixels_animation.tsx: animaciones en canvas.
- ExportCSV.tsx: botón para exportar a CSV.
- Footer_NOVA_blanco/negro.tsx: pies de página con logo NOVA.
- Tailwind CSS: configurado en
tailwind.config.tsypostcss.config.mjs. Se definen fuentes personalizadas (EA Font,FIFA26,FWC26) y utilidades para viewport dinámico (dvh). - Next.js config: en
next.config.tsse ignoran errores de ESLint en builds y se configuran patrones remotos para imágenes (Azure Blob Storage). - Fuentes e imágenes: en
public/ysrc/fonts/. El scriptscripts/optimize-images.mjsconvierte PNG a WebP para optimizar carga. - Variables de entorno: se definen en
.env.local(ver sección 5.2).
SUPABASE_DB_URL=
JWT_SECRET_KEY=una_clave_secreta_muy_larga
NEXT_PUBLIC_CLIENT_KEY_CAPTCHA=clave_publica_del_sitio SERVER_KEY_CAPTCHA=clave_secreta_del_servidor
NEXT_PUBLIC_RSA_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" RSA_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
NEXT_PUBLIC_ANALY_TSS=clave_analytics NEXT_PUBLIC_AIESEC_TSS=clave_aiesec NEXT_PUBLIC_NOVA_TSS=clave_nova
5.3 Instalar dependencias
Usamos npm:
npm install