Sistema de Eventos
Tabla de Contenidos
- Maquina de Estados
- Tipos de Evento
- Servicios por Evento
- Paquetes
- Gestion de Invitados
- RSVP
- Mesas y Asientos
- QR Passes y Check-in
- Acceso App Movil
- Mesa de Regalos y Hospedaje
- Colaboradores
1. Maquina de Estados
Cada evento transita por un ciclo de vida controlado por EventStatus. Las transiciones se validan en EventLifecycleService y mediante guards de NestJS.
Transiciones y validaciones
| Transicion | Trigger | Efecto colateral |
|---|---|---|
DRAFT -> ACTIVE | Invitacion aprobada y publicada | Se registra activatedAt y auditoria |
ACTIVE -> COMPLETED | Cron diario (medianoche, America/Mexico_City) | Invitaciones PUBLISHED/UNPUBLISHED -> CLOSED |
DRAFT -> CANCELLED | POST /events/:id/cancel | Se registra cancelledAt y cancellationReason |
ACTIVE -> CANCELLED | POST /events/:id/cancel | Invitaciones activas pasan a CLOSED |
Campos de auditoria
| Campo | Tipo | Descripcion |
|---|---|---|
activatedAt | DateTime | Fecha en que el evento paso a ACTIVE |
cancelledAt | DateTime | Fecha de cancelacion |
completedAt | DateTime | Fecha de auto-completado por cron |
statusChangedBy | UUID | Usuario que realizo el cambio de estado |
cancellationReason | String | Razon proporcionada al cancelar |
2. Tipos de Evento
El modelo EventType es un catalogo dinamico en base de datos. Cada tipo define un slug, icon y un arreglo JSON defaultSections con la estructura [{ "type": "Hero", "order": 0 }, ...] que precarga secciones al crear una invitacion.
| # | Slug | Nombre | Secciones por defecto |
|---|---|---|---|
| 1 | wedding | Boda | Hero, Couple, Itinerary, Location, RSVP |
| 2 | xv-anos | XV Anos | Hero, Quinceañera, Itinerary, Location, RSVP |
| 3 | baptism | Bautizo | Hero, Details, Location, RSVP, Gallery |
| 4 | birthday | Cumpleanos | Hero, Details, Location, RSVP, Gallery |
| 5 | first-communion | Primera Comunion | Hero, Details, Location, RSVP, Gallery |
| 6 | baby-shower | Baby Shower | Hero, Details, Location, RSVP, GiftRegistry |
| 7 | graduation | Graduacion | Hero, Details, Location, RSVP, Gallery |
| 8 | corporate | Evento Corporativo | Hero, Details, Itinerary, Location, RSVP |
| 9 | other | Otro | Hero, Details, Location, RSVP |
3. Servicios por Evento
Nvito utiliza un sistema modular de 18 servicios controlado por EventServiceType. Cada evento tiene un EventServiceConfig con el array enabledServices y un JSON serviceSettings para configuracion granular (ej: { "GALLERY": { "maxPhotos": 50 } }).
| Categoria | Servicio | Clave |
|---|---|---|
| Core | Invitacion digital | INVITATION |
| Core | Ubicaciones con GPS | LOCATIONS |
| Invitados | Lista de invitados | GUEST_LIST |
| Invitados | Confirmaciones (RSVP) | RSVP |
| Invitados | Grupos de invitados | GUEST_GROUPS |
| Dia del evento | Itinerario / timeline | ITINERARY |
| Dia del evento | Asignacion de mesas | TABLES |
| Dia del evento | Pases QR de entrada | QR_PASSES |
| Dia del evento | Check-in con camara QR | CHECK_IN |
| Media | Galeria de fotos | GALLERY |
| Media | Libro de audio (mensajes de voz) | AUDIO_GUESTBOOK |
| Media | Fotos subidas por invitados | GUEST_UPLOADS |
| Diseno | Dominio personalizado | CUSTOM_DOMAIN |
| Diseno | Musica de fondo | BACKGROUND_MUSIC |
| Extras | Mesa de regalos | GIFT_REGISTRY |
| Extras | Hospedaje y transporte | ACCOMMODATION |
| Contenido | Padrinos / Corte de Honor | SPONSORS |
| Contenido | Oracion / seccion religiosa | PRAYER |
| Contenido | Agradecimientos | THANK_YOU |
| Contenido | Codigo de vestimenta | DRESS_CODE |
Los servicios INVITATION y LOCATIONS son core y siempre estan activos. El resto se habilita segun el paquete contratado o configuracion manual.
4. Paquetes
Los paquetes son un catalogo dinamico (ServicePackage) administrado por el Super Admin. Al asignar un paquete, sus services se copian al enabledServices del evento, permitiendo personalizacion posterior.
| Paquete | Precio | Incluye |
|---|---|---|
| Essential | $1,499 MXN | Invitacion, ubicaciones, lista de invitados, RSVP, grupos |
| Plus | $2,999 MXN | Essential + itinerario, galeria, mesa de regalos, hospedaje, musica |
| VIP | $4,999 MXN | Plus + mesas, QR passes, check-in, subidas, libro de audio, dominio |
| Elite | $9,999 MXN | Los 18 servicios sin restriccion |
| Personalizado | Cotizacion | Seleccion manual de servicios individuales |
Modelo ServicePackage
| Campo | Tipo | Descripcion |
|---|---|---|
slug | VARCHAR(50) UNIQUE | Identificador unico del paquete |
name / displayName | VARCHAR(100) | Nombre interno y nombre visible en la UI |
price | DECIMAL(10,2) | Precio referencial del paquete |
currency | VARCHAR(3) | Moneda (default: MXN) |
services | EventServiceType[] | Servicios incluidos en el paquete |
color | VARCHAR(20) | Color de presentacion en la UI |
isRecommended | BOOLEAN | Marcado como recomendado |
isActive | BOOLEAN | Si el paquete esta disponible |
sortOrder | INT | Orden de visualizacion |
5. Gestion de Invitados
El controlador GuestsController expone CRUD completo con autorizacion RBAC y soporte de importacion masiva por CSV.
Endpoints
| Metodo | Ruta | Permiso |
|---|---|---|
POST /guests | Crear invitado | guests:create:assigned |
POST /guests/import | Importar CSV | guests:import:assigned |
GET /guests/event/:eventId | Listar (paginado) | guests:read:assigned |
GET /guests/event/:eventId/dietary | Resumen dietetico | guests:read:assigned |
GET /guests/:id | Detalle | guests:read:assigned |
PATCH /guests/:id | Actualizar | guests:update:assigned |
DELETE /guests/:id | Soft delete | guests:delete:assigned |
Campos principales del Guest
firstName, lastName, email (unico por evento), phone, groupId (FK a GuestGroup), groupSize, status (GuestStatus), dietaryRestrictions (array de DietaryRestriction), allergyInformation, mobilityNeeds, tags, metadata (JSON).
Estados del invitado (GuestStatus)
| Estado | Descripcion |
|---|---|
PENDING | Registrado, sin respuesta RSVP |
CONFIRMED | Confirmo asistencia |
DECLINED | Declino la invitacion |
ATTENDED | Asistio al evento (check-in registrado) |
Importacion CSV
El servicio CsvImportService procesa archivos CSV subidos via multipart/form-data. Retorna un ImportResult con total procesados, exitosos y errores por fila.
Grupos (GuestGroup)
Permiten organizar invitados en categorias (ej: "Familia del novio"). Campos: name, description, color (hex). Un invitado pertenece a un solo grupo opcional.
Restricciones alimentarias
Enum DietaryRestriction: VEGETARIAN, VEGAN, GLUTEN_FREE, HALAL, KOSHER, NUT_FREE, DAIRY_FREE, OTHER.
6. RSVP
El RSVP es un endpoint publico (sin autenticacion) para que los invitados confirmen asistencia. La consulta y estadisticas requieren autenticacion.
Diagrama de secuencia
Endpoints
| Metodo | Ruta | Auth |
|---|---|---|
POST /rsvp | Crear/actualizar RSVP | Publico |
GET /rsvp/event/:eventId | Listar RSVPs | Autenticado |
GET /rsvp/event/:eventId/stats | Estadisticas | Autenticado |
Estados (RSVPStatus)
PENDING (sin respuesta), CONFIRMED (asistira), DECLINED (no asistira), MAYBE (posible asistencia).
Modelo RSVP - campos clave
guestId, invitationId, status, attendeesCount, dietaryInfo (JSON con restricciones), message, respondedAt, ipAddress, userAgent, rsvpOpenedAt, responseTime (segundos), remindersSentCount.
El campo dietaryInfo sigue el formato: { "vegetarian": 1, "vegan": 0, "allergies": ["nuts"] }. El campo remindersSentCount permite rastrear recordatorios automaticos enviados a invitados con status PENDING.
7. Mesas y Asientos
El servicio TABLES permite planificar la distribucion fisica de invitados.
EventTable
Campos: name (ej: "Mesa Principal"), capacity (default 10), shape (TableShape), positionX/positionY (coordenadas en el plano), metadata (JSON).
Formas (TableShape): ROUND (defecto), SQUARE, RECTANGLE, OVAL.
TableAssignment
Asigna un invitado a una mesa con asiento opcional. La restriccion UNIQUE en guestId garantiza que cada invitado solo puede estar en una mesa. Campos: tableId, guestId, seatNumber (opcional).
Acceso Mobile (read-only)
El anfitrion (HOST) puede visualizar la distribucion y asignacion de mesas desde la app movil en modo lectura. La app ofrece dos vistas: Lista (cards con barra de ocupacion) y Plano visual (pinch-to-zoom, mismas coordenadas que el admin web). La gestion completa de mesas (crear, editar, asignar invitados, redistribuir) se realiza exclusivamente desde el admin web.
Endpoints mobile: GET /v1/mobile/events/:eventId/tables (lista con asignaciones) y GET /v1/mobile/events/:eventId/tables/stats (estadisticas de ocupacion).
8. QR Passes y Check-in
Modelo QRPass
Cada invitado puede tener pases QR vinculados al evento. Campos: code (VARCHAR UNIQUE), type (default "entry"), isUsed, usedAt, scannedBy, expiresAt, attendeesCheckedIn (para check-in parcial).
Flujo de check-in
- Generacion - Al habilitar
QR_PASSES, se genera unQRPasspor invitado con codigo unico. - Distribucion - El QR se incluye en la invitacion digital o se envia por separado.
- Escaneo - El servicio
CHECK_INproporciona una vista con camara QR. - Validacion - La API verifica existencia, expiracion y uso previo del codigo.
- Registro - Se marca
isUsed = true, se registrausedAt/scannedBy, el invitado cambia aATTENDED. - Check-in parcial -
attendeesCheckedInpermite registrar llegadas parciales del grupo.
9. Acceso App Movil
El modulo de Acceso App Movil permite a los organizadores generar codigos de acceso para que los anfitriones (HOST) inicien sesion en la app movil y gestionen el evento el dia del evento (check-in, estadisticas, invitados).
Modelo EventAccessCode
Cada codigo de acceso vincula un evento con credenciales de autenticacion movil. Campos principales:
| Campo | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
eventId | UUID FK | Evento al que pertenece |
code | VARCHAR(8) UNIQUE | Codigo alfanumerico de 8 caracteres (ej: KWZDR4VD) |
pin | VARCHAR(255) | PIN de 6 digitos hasheado con bcrypt (salt rounds: 10) |
role | MobileRole | Rol del acceso (default: HOST) |
isActive | BOOLEAN | Si el codigo esta activo |
expiresAt | DateTime? | Fecha de expiracion opcional |
createdAt | DateTime | Fecha de creacion |
updatedAt | DateTime | Ultima actualizacion |
Flujo de gestion de codigos
- Prerequisito — El evento debe estar en estado
ACTIVE(invitacion publicada). No se pueden generar codigos para eventos en DRAFT, CANCELLED o COMPLETED. - Generacion — Desde la pagina "Acceso App Movil" en nvito-admin, el organizador genera un codigo. Se crea un
EventAccessCodecon codigo alfanumerico aleatorio y PIN de 6 digitos. - Reveal unico del PIN — El PIN se muestra una sola vez en un dialog persistente al crear o regenerar. Se almacena hasheado con bcrypt y no puede recuperarse.
- Distribucion — El organizador comparte el codigo y PIN con el anfitrion (por cualquier medio externo).
- Login — El anfitrion ingresa codigo + PIN en la app movil. El API valida con
bcrypt.compare()y crea unaMobileSession. - Gestion — Desde el admin se puede: activar/desactivar, regenerar PIN (genera uno nuevo), o eliminar el codigo.
Limite de codigos activos
Maximo 3 codigos activos simultaneos por evento (MAX_ACTIVE_CODES = 3). Los codigos inactivos o eliminados no cuentan contra este limite.
Seguridad
- PIN hasheado: Se almacena con
bcrypt.hash(pin, 10), nunca en texto plano - One-time reveal: El PIN solo es visible al crear o regenerar — no se puede consultar despues
- Validacion de estado: El backend rechaza la creacion si el evento no esta en estado
ACTIVE(ValidationException, HTTP 400) - Separacion de auth: El sistema de autenticacion movil es independiente de Clerk (admin web)
Endpoints (admin)
| Metodo | Ruta | Descripcion |
|---|---|---|
GET | /events/:eventId/access-codes | Listar codigos del evento (sin PIN) |
POST | /events/:eventId/access-codes | Crear codigo con PIN auto-generado |
PATCH | /events/:eventId/access-codes/:codeId | Activar/desactivar codigo |
POST | /events/:eventId/access-codes/:codeId/regenerate-pin | Regenerar PIN |
DELETE | /events/:eventId/access-codes/:codeId | Eliminar codigo |
Panel de administracion (nvito-admin)
La pagina /events/[eventId]/mobile-access muestra:
- Badge de conteo: Codigos activos vs maximo permitido
- Tarjetas por codigo: Codigo visible con boton copiar, estado (activo/inactivo/expirado), fecha de creacion
- Menu de acciones: Regenerar PIN, activar/desactivar, eliminar
- Bloqueo por estado: Si el evento no esta activo, se muestra un
StatusAlertcon mensaje contextual y las acciones se deshabilitan
10. Mesa de Regalos y Hospedaje
Mesa de Regalos (GiftRegistry)
Configuracion por evento con: message, note, y un fondo en efectivo (cashFundEnabled, cashFundGoal en centavos, cashFundCurrent, cashFundPaymentMethods).
Items (GiftRegistryItem): Enlaces a tiendas con type (GiftRegistryType: AMAZON, LIVERPOOL, PALACIO_HIERRO, SANBORNS, COPPEL, SEARS, OTHER), storeName, registryUrl, logoUrl.
Tambien soporta cuentas bancarias (GiftBankAccount) para transferencias directas.
Hospedaje (Accommodation)
Configuracion por evento con: message, travelTips, travelParkingInfo, travelNearestAirport.
Hoteles (AccommodationHotel): name, address, phone, distance, websiteUrl, bookingUrl (reservacion directa).
Transporte (AccommodationTransportOption): Opciones con type (Uber/DiDi, Taxi, Autobus), description, icon.
11. Colaboradores
El sistema permite invitar usuarios para co-gestionar un evento mediante EventCollaboratorInvitation.
Modelo
Campos: email, role (RoleType), invitedBy, status (CollaboratorInvitationStatus), token (UNIQUE), expiresAt.
Estados de invitacion
| Estado | Descripcion |
|---|---|
PENDING | Invitacion enviada, esperando respuesta |
ACCEPTED | Colaborador tiene acceso al evento |
DECLINED | Colaborador rechazo |
EXPIRED | Token expiro |
CANCELLED | Organizador cancelo la invitacion |
Roles asignables
| Rol | Acceso |
|---|---|
ORGANIZATION_ADMIN | Administracion completa del evento |
EVENT_COORDINATOR | Coordinacion operativa sin acceso financiero |
EVENT_VIEWER | Solo lectura |
EVENT_STAFF | Check-in y operaciones del dia del evento |
Flujo
- El organizador invita a un colaborador proporcionando su email y el rol deseado.
- El sistema genera un token unico con fecha de expiracion y envia la invitacion por correo.
- El colaborador abre el enlace con el token.
- Si el colaborador ya tiene cuenta en Nvito, se le asigna el rol en el evento automaticamente.
- Si no tiene cuenta, se le invita a registrarse y al completar el registro se le asigna el rol.
- El organizador puede cancelar invitaciones pendientes en cualquier momento.
Referencias
Archivos fuente
| Archivo | Descripcion |
|---|---|
events.controller.ts | Controlador de eventos: CRUD y cancelacion |
event-lifecycle.service.ts | Maquina de estados y transiciones automaticas |
guests.controller.ts | CRUD de invitados con importacion CSV |
rsvp.controller.ts | Endpoints de RSVP (publico y autenticado) |
event-service.types.ts | Enum TypeScript de los 18 servicios |
schema.prisma | Modelos de datos de todo el sistema |
Guards de autorizacion
- ClerkAuthGuard - Autenticacion JWT via Clerk.
- PermissionsGuard - Verificacion de permisos RBAC con
@RequirePermission(). - EventAccessGuard - Acceso al evento especifico con
@RequireEventAccess(). - InvitationStateGuard - Bloquea edicion si la invitacion esta publicada o cerrada.