Sistema web de reserva de entradas de cine construido como monorepo full-stack. Simula la experiencia completa de un cine real: cartelera, selección de asientos, snacks, pago y un panel administrativo funcional.
Proyecto de portafolio — pensado para demostrar arquitectura full-stack (NestJS + React), modelado de datos relacional y un flujo de UX de comercio electrónico de principio a fin.
- Vista previa
- Tecnologías
- Funcionalidades
- Requisitos
- Instalación y arranque
- Usuarios de prueba
- Scripts disponibles
- API Endpoints
- Estructura del proyecto
- Consideraciones importantes
- Licencia
| Destacadas | Cartelera con funciones |
|---|---|
![]() |
![]() |
Sección de "Próximas funciones" filtrable por día de la semana, con horario, sala, formato (2D / IMAX / 4DX) y precio.
Ficha con sinopsis, reparto, trailer embebido de YouTube y funciones disponibles agrupadas por fecha.
| 1. Selección de asientos | 2. Bocadillos | 3. Confirmación y pago |
|---|---|---|
![]() |
![]() |
![]() |
Mapa de asientos interactivo en tiempo real (disponible / ocupado / seleccionado / VIP), carrito de snacks por categoría y resumen de compra con formulario de pago (simulado).
| Login | Mis reservas |
|---|---|
![]() |
![]() |
| Dashboard | Gestión de películas |
|---|---|
![]() |
![]() |
El dashboard muestra métricas en vivo (películas activas, funciones activas, reservas del día, ingresos) y las últimas reservas registradas. El CRUD de películas, funciones y bocadillos es completo (crear, editar, activar/desactivar, eliminar).
| Capa | Tecnología |
|---|---|
| Frontend | React 19 + TypeScript + Vite 8 (Rolldown) |
| Backend | NestJS 10 + TypeORM |
| Base de datos | PostgreSQL 16 (Docker) |
| Autenticación | JWT en cookie httpOnly |
| UI | Tailwind CSS 4 + Framer Motion + Radix UI |
| Estado / datos | Zustand + TanStack Query |
- Catálogo de películas con pósters desde TMDB y películas destacadas
- Cartelera semanal filtrable por día y por película
- Selección de asientos interactiva por sala (estándar, VIP, IMAX, 4DX)
- Carrito de snacks con categorías (bebidas, combos, dulces, etc.)
- Flujo de reserva de 3 pasos con resumen de precio en vivo
- Autenticación de usuarios (registro / inicio de sesión) con JWT en cookie httpOnly
- Historial de reservas por usuario ("Mis reservas")
- Panel administrativo con dashboard de métricas y CRUD de películas, funciones, salas, bocadillos y usuarios
- Roles de usuario (
useryadmin) - Tema claro / oscuro
- Prevención de doble reserva del mismo asiento (trigger en base de datos)
- Node.js 20.19+ o 22.12+ (Vite 8 + TypeScript 6 lo exigen; "Node 18+" ya no es suficiente)
- Docker Desktop (con Docker Compose v2 integrado — el comando es
docker compose, sin guion, en instalaciones recientes) - npm
# 1. Clonar el repositorio
git clone <url-del-repositorio>
cd CinemaApp/Cinema # el proyecto vive dentro de la carpeta Cinema
# 2. Instalar dependencias (cliente + servidor)
npm run setup
# 3. Levantar PostgreSQL con Docker
npm run db:start
# 4. Iniciar frontend y backend en modo desarrollo
npm run devNo hace falta crear server/.env manualmente: el repositorio ya incluye uno completo con la configuración de base de datos, JWT_SECRET, PORT=3001 y FRONTEND_URL. Solo edítalo si necesitas cambiar algún valor (por ejemplo, el puerto de PostgreSQL si el 5432 local ya está en uso por otro contenedor).
La aplicación estará disponible en:
- Frontend:
http://localhost:5173(Vite usa el siguiente puerto libre, p. ej.5174, si el5173está ocupado) - Backend API:
http://localhost:3001
Nota sobre el seed:
server/db/init.sqlsolo se ejecuta la primera vez que se crea el volumen de Docker (pgdata). Las funciones de cine (screenings) se generan para los 7 días siguientes al momento del seed, así que si el proyecto lleva un tiempo sin levantarse, la cartelera puede aparecer vacía. En ese caso, ejecutanpm run db:resetpara regenerar el volumen y las funciones con fechas vigentes (esto borra los datos actuales de la base).
npm run db:stop # Detiene el contenedor de PostgreSQL
npm run db:reset # Reinicia la base de datos desde cero (docker-compose down -v && up -d)
npm run dev:server # Solo backend
npm run dev:client # Solo frontend
npm run build # Build de producción del frontend| Rol | Contraseña | |
|---|---|---|
| Admin | admin@cinemax.pe |
Admin123! |
| Usuario | juan.perez@gmail.com |
User123! |
| Usuario | maria.garcia@gmail.com |
User123! |
| Script | Descripción |
|---|---|
npm run dev |
Inicia frontend y backend simultáneamente |
npm run dev:server |
Inicia solo el backend |
npm run dev:client |
Inicia solo el frontend |
npm run build |
Compila el frontend para producción |
npm run db:start |
Levanta PostgreSQL con Docker (docker-compose up -d) |
npm run db:stop |
Detiene PostgreSQL |
npm run db:reset |
Reinicia la base de datos desde cero |
npm run setup |
Instala dependencias de cliente y servidor |
POST /api/auth/register— Registro de usuarioPOST /api/auth/login— Inicio de sesiónPOST /api/auth/logout— Cerrar sesiónGET /api/auth/me— Perfil del usuario autenticadoPUT /api/auth/me— Actualizar perfil
GET /api/movies— Listar películas (?all=trueincluye inactivas)GET /api/movies/featured— Películas destacadasGET /api/movies/:id— Detalle de películaPOST /api/movies— Crear película (admin)PUT /api/movies/:id— Actualizar película (admin)DELETE /api/movies/:id— Eliminar película (admin)PATCH /api/movies/:id/featured— Alternar destacado (admin)
GET /api/screenings— Listar funciones (?all=trueincluye inactivas)GET /api/screenings/by-movie/:movieId— Funciones por películaGET /api/screenings/:id— Detalle de funciónGET /api/screenings/:id/seats— Asientos de una funciónPOST /api/screenings— Crear función (admin)PUT /api/screenings/:id— Actualizar función (admin)DELETE /api/screenings/:id— Desactivar función (admin)
POST /api/reservations— Crear reservaGET /api/reservations/my— Mis reservasGET /api/reservations/:id— Detalle de reservaPOST /api/reservations/:id/simulate-payment— Simular pagoPOST /api/reservations/:id/cancel— Cancelar reservaGET /api/reservations— Todas las reservas (admin)
GET /api/snacks— Listar bocadillos (?all=trueincluye no disponibles)GET /api/snacks/:id— Detalle de bocadilloPOST /api/snacks— Crear bocadillo (admin)PUT /api/snacks/:id— Actualizar bocadillo (admin)DELETE /api/snacks/:id— Eliminar bocadillo (admin)PATCH /api/snacks/:id/availability— Alternar disponibilidad (admin)
GET /api/users— Listar usuarios (paginado)PUT /api/users/:id/role— Cambiar rol de usuario
GET /api/rooms— Listar salasGET /api/rooms/:id— Detalle de sala
CinemaApp/
├── README.md
├── screenshots/ # Capturas usadas en este README
└── Cinema/ # Proyecto real (cd aquí para trabajar)
├── client/ # Frontend React + Vite
│ ├── src/
│ │ ├── api/ # Cliente HTTP y endpoints
│ │ ├── components/ # Componentes UI
│ │ ├── pages/ # Páginas (admin, booking, etc.)
│ │ ├── store/ # Zustand stores
│ │ ├── hooks/ # Custom hooks
│ │ ├── types/ # Tipos TypeScript
│ │ └── styles/ # CSS tokens y estilos
│ └── .env
├── server/ # Backend NestJS
│ ├── src/
│ │ ├── auth/ # Módulo de autenticación
│ │ ├── movies/ # Módulo de películas
│ │ ├── screenings/ # Módulo de funciones
│ │ ├── reservations/# Módulo de reservas
│ │ ├── snacks/ # Módulo de bocadillos
│ │ ├── rooms/ # Módulo de salas
│ │ ├── users/ # Módulo de usuarios
│ │ ├── entities/ # Entidades TypeORM
│ │ └── common/ # Decoradores y filtros compartidos
│ ├── db/
│ │ └── init.sql # Schema y seed data
│ └── .env
├── docker-compose.yml
└── package.json
Los pagos no están implementados. Es un proyecto de muestra. Existe un endpoint POST /api/reservations/:id/simulate-payment que marca la reserva como paid sin procesar ningún pago real; el formulario de tarjeta del checkout es puramente visual.
- Backend: NestJS con TypeORM y PostgreSQL (
synchronize: false— el schema lo creaserver/db/init.sql, no TypeORM). Cada controlador define su propio prefijoapi/...(no haysetGlobalPrefixenmain.ts). Las imágenes de las entidades se guardan como URLs (no archivos locales). - Frontend: React 19 con Vite.
axiosconwithCredentials: truepara cookies. El cliente llama a/apiconbaseURLvacío y Vite hace proxy alocalhost:3001, por esoclient/.envno necesitaVITE_API_URL. El build actual usa Rolldown (bundler de Vite 8). - Autenticación: JWT almacenado en cookie httpOnly, no en localStorage. La sesión se puede invalidar desde el backend (token version).
- Seed data: La base de datos se inicializa automáticamente mediante
server/db/init.sqlal crear el volumen de Docker por primera vez (ver nota sobredb:resetmás arriba).
- Películas: Pósters obtenidos de TMDB (
image.tmdb.org). Si una URL falla, se muestra un placeholder deplacehold.co. - Bocadillos: Imágenes placeholder de
placehold.cocon el nombre del producto, ya que no existe un recurso equivalente a TMDB para imágenes de comida. Se incluye fallbackonErrorcon la inicial del producto. - Usuarios: Sin avatar por defecto.
- Las contraseñas se almacenan hasheadas con
bcrypt. - El JWT expira en 1 hora y se envía como cookie httpOnly.
helmetactivado para cabeceras de seguridad.- Prevención de doble reserva del mismo asiento mediante trigger en base de datos.
- El archivo
server/.envcontiene las credenciales de la base de datos y la clave JWT. Para un despliegue real, estos valores deben cambiarse.
Proyecto educativo y de portafolio. Sin licencia de uso comercial.











