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.
Ciclo de Vida del Evento
ACTIVE→COMPLETEDfecha_pasaDRAFT→CANCELLEDcancelacionTransiciones y validaciones
| Transición | Trigger | Efecto colateral |
|---|---|---|
DRAFT -> ACTIVE | Invitación 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 |
Estado visual derivado:
EXPIRED(Expirado) no es una transición de BD. Se calcula en el frontend cuandoCOMPLETED+activatedAt === null(nunca se público invitación). Tiene el mismo bloqueo que COMPLETED/CANCELLED.
Campos de auditoria
| Campo | Tipo | Descripción |
|---|---|---|
activatedAt | DateTime | Fecha en que el evento paso a ACTIVE |
cancelledAt | DateTime | Fecha de cancelación |
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 catálogo dinámico 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 invitación.
| # | Slug | Nombre | Secciones por defecto |
|---|---|---|---|
| 1 | wedding | Boda | Hero, Couple, Itinerary, Location, RSVP |
| 2 | xv-anos | XV Años | Hero, Quinceañera, Itinerary, Location, RSVP |
| 3 | baptism | Bautizo | Hero, Details, Location, RSVP, Gallery |
| 4 | birthday | Cumpleaños | Hero, Details, Location, RSVP, Gallery |
| 5 | first-communion | Primera Comunión | Hero, Details, Location, RSVP, Gallery |
| 6 | baby-shower | Baby Shower | Hero, Details, Location, RSVP, GiftRegistry |
| 7 | graduation | Graduación | 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 configuración granular (ej: { "GALLERY": { "maxPhotos": 50 } }).
| Categoría | Servicio | Clave |
|---|---|---|
| Core | Invitación 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 | Asignación de mesas | TABLES |
| Dia del evento | Pases QR de entrada | QR_PASSES |
| Dia del evento | Check-in con camara QR | CHECK_IN |
| Media | Galería de fotos | GALLERY |
| Media | Libro de audio (mensajes de voz) | AUDIO_GUESTBOOK |
| Media | Fotos subidas por invitados | GUEST_UPLOADS |
| Diseño | Dominio personalizado | CUSTOM_DOMAIN |
| Diseño | Música de fondo | BACKGROUND_MUSIC |
| Extras | Mesa de regalos | GIFT_REGISTRY |
| Extras | Hospedaje y transporte | ACCOMMODATION |
| Contenido | Padrinos / Corte de Honor | SPONSORS |
| Contenido | Oración / sección religiosa | PRAYER |
| Contenido | Agradecimientos | THANK_YOU |
| Contenido | Código de vestimenta | DRESS_CODE |
Los servicios INVITATION y LOCATIONS son core y siempre estan activos. El resto se habilita según el paquete contratado o configuración manual.
4. Paquetes
Los paquetes son un catálogo dinámico (ServicePackage) administrado por el Super Admin. Al asignar un paquete, sus services se copian al enabledServices del evento, permitiendo personalización posterior.
| Paquete | Precio | Incluye |
|---|---|---|
| Essential | $1,499 MXN | Invitación, ubicaciones, lista de invitados, RSVP, grupos |
| Plus | $2,999 MXN | Essential + itinerario, galería, mesa de regalos, hospedaje, música |
| 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 | Descripción |
|---|---|---|
slug | VARCHAR(50) UNIQUE | Identificador único 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 presentación en la UI |
isRecommended | BOOLEAN | Marcado como recomendado |
isActive | BOOLEAN | Si el paquete está disponible |
sortOrder | INT | Orden de visualización |
5. Gestión de Invitados
El controlador GuestsController expone CRUD completo con autorización RBAC y soporte de importación masiva via Excel.
Endpoints
| Metodo | Ruta | Permiso |
|---|---|---|
POST /guests | Crear invitado | guests:create:assigned |
POST /guests/import/generate-template | Generar template Excel | guests:import:assigned |
POST /guests/import/excel | Importar Excel | guests:import:assigned |
GET /guests/event/:eventId | Listar (páginado) | 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 (único por evento), phone, title (título formal, max 30 chars), groupId (FK a GuestGroup), groupSize, status (GuestStatus), dietaryRestrictions (array de DietaryRestriction), dietaryNotes, allergyInformation, mobilityNeeds, relationship.
Estados del invitado (GuestStatus)
| Estado | Descripción |
|---|---|
PENDING | Registrado, sin respuesta RSVP |
CONFIRMED | Confirmo asistencia |
DECLINED | Declino la invitación |
ATTENDED | Asistio al evento (check-in registrado) |
Importacion masiva via Excel
El sistema de importación masiva genera un archivo Excel (.xlsx) dinámico personalizado según los servicios contratados del evento (EventServiceConfig.enabledServices[]).
Flujo:
- El admin genera el template Excel desde el dialog de importación
- El
ExcelTemplateServicecrea un archivo con hoja "Instrucciones" (leyenda de colores y reglas) y hoja "Invitados" (headers coloreados por sección con data validations/dropdowns) - El archivo se sube a S3 con URL firmada de 7 dias
- El admin descarga el template y se lo envia al cliente
- El cliente llena el Excel con datos de invitados
- El admin sube el Excel completado
- El
ExcelImportServiceprocesa el archivo en una transacción atomica: crea grupos, mesas, invitados y asignaciones automáticamente - Deduplicacion por email (hash), validación fila por fila, limite de 1000 filas
Columnas dinámicas según servicio:
| Servicio | Columnas | Color |
|---|---|---|
| GUEST_LIST (siempre) | Nombre, Apellido, Titulo, Email, Telefono, Pases | #0B3D5A |
| GUEST_GROUPS | Grupo | #F17F3A |
| TABLES | Mesa, Capacidad Mesa, Asiento | #005587 |
| Siempre (dietarios) | Restricciones Dietarias, Alergias, Movilidad, Notas Dieta | #9C27B0 |
| Siempre | Relación | #FF9800 |
Grupos (GuestGroup)
Permiten organizar invitados en categorías (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 público (sin autenticación) para que los invitados confirmen asistencia. La consulta y estadisticas requieren autenticación.
Diagrama de secuencia
Flujo de Confirmacion RSVP
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 automáticos enviados a invitados con status PENDING.
7. Mesas y Asientos
El servicio TABLES permite planificar la distribución 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 distribución y asignación 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 gestión 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
- Generación - Al habilitar
QR_PASSES, se genera unQRPasspor invitado con código unico. - Distribucion - El QR se incluye en la invitación digital o se envia por separado.
- Escaneo - El servicio
CHECK_INproporciona una vista con camara QR. - Validación - La API verifica existencia, expiración y uso previo del código.
- 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 módulo de Acceso App Movil permite a los organizadores generar códigos de acceso para que los anfitriones (HOST) inicien sesion en la app movil y gestiónen el evento el día del evento (check-in, estadisticas, invitados).
Modelo EventAccessCode
Cada código de acceso vincula un evento con credenciales de autenticación movil. Campos principales:
| Campo | Tipo | Descripción |
|---|---|---|
id | UUID PK | Identificador único |
eventId | UUID FK | Evento al que pertenece |
code | VARCHAR(8) UNIQUE | Código 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 código está activo |
expiresAt | DateTime? | Fecha de expiración opcional |
createdAt | DateTime | Fecha de creación |
updatedAt | DateTime | Ultima actualización |
Flujo de gestión de códigos
- Prerequisito — El evento debe estar en estado
ACTIVE(invitación publicada). No se pueden generar códigos para eventos en DRAFT, CANCELLED o COMPLETED. - Generación — Desde la página "Acceso App Movil" en nvito-admin, el organizador genera un código. Se crea un
EventAccessCodecon código alfanumerico aleatorio y PIN de 6 digitos. - Reveal único 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 código y PIN con el anfitrion (por cualquier medio externo).
- Login — El anfitrion ingresa código + PIN en la app movil. El API valida con
bcrypt.compare()y crea unaMobileSession. - Gestión — Desde el admin se puede: activar/desactivar, regenerar PIN (genera uno nuevo), o eliminar el código.
Limite de códigos activos
Máximo 3 códigos activos simultaneos por evento (MAX_ACTIVE_CODES = 3). Los códigos 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 después
- Validación de estado: El backend rechaza la creación si el evento no está en estado
ACTIVE(ValidationException, HTTP 400) - Separacion de auth: El sistema de autenticación movil es independiente de Clerk (admin web)
Endpoints (admin)
| Metodo | Ruta | Descripción |
|---|---|---|
GET | /events/:eventId/access-codes | Listar códigos del evento (sin PIN) |
POST | /events/:eventId/access-codes | Crear código con PIN auto-generado |
PATCH | /events/:eventId/access-codes/:codeId | Activar/desactivar código |
POST | /events/:eventId/access-codes/:codeId/regenerate-pin | Regenerar PIN |
DELETE | /events/:eventId/access-codes/:codeId | Eliminar código |
Panel de administración (nvito-admin)
La página /events/[eventId]/mobile-access muestra:
- Badge de conteo: Códigos activos vs máximo permitido
- Tarjetas por código: Código visible con botón copiar, estado (activo/inactivo/expirado), fecha de creación
- Menu de acciones: Regenerar PIN, activar/desactivar, eliminar
- Bloqueo por estado: Si el evento no está activo, se muestra un
StatusAlertcon mensaje contextual y las acciones se deshabilitan
10. Mesa de Regalos y Hospedaje
Mesa de Regalos (GiftRegistry)
Configuración 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.
También soporta cuentas bancarias (GiftBankAccount) para transferencias directas.
Hospedaje (Accommodation)
Configuración 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 invitación
| Estado | Descripción |
|---|---|
PENDING | Invitación enviada, esperando respuesta |
ACCEPTED | Colaborador tiene acceso al evento |
DECLINED | Colaborador rechazo |
EXPIRED | Token expiro |
CANCELLED | Organizador cancelo la invitación |
Roles asignables
| Rol | Acceso |
|---|---|
EVENT_MANAGER | Control completo sobre el evento asignado |
EVENT_EDITOR | Edicion de eventos e invitados, sin eliminar |
EVENT_COLLABORATOR | Acceso limitado: agregar/editar invitados y ver info |
EVENT_VIEWER | Solo lectura del evento asignado |
El permiso collaborators:manage:assigned está asignado a SUPER_ADMIN (wildcard), PLATFORM_ADMIN, ORG_OWNER, ORG_ADMIN y EVENT_MANAGER. Los roles EVENT_EDITOR, EVENT_COLLABORATOR y EVENT_VIEWER no pueden gestionar colaboradores.
Flujo
- El organizador invita a un colaborador proporcionando su email y el rol deseado.
- El sistema genera un token único con fecha de expiración y envia la invitación 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 automáticamente.
- 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 | Descripción |
|---|---|
events.controller.ts | Controlador de eventos: CRUD y cancelación |
event-lifecycle.service.ts | Maquina de estados y transiciones automáticas |
guests.controller.ts | CRUD de invitados con importación Excel |
rsvp.controller.ts | Endpoints de RSVP (público y autenticado) |
event-service.types.ts | Enum TypeScript de los 18 servicios |
schema.prisma | Modelos de datos de todo el sistema |
Guards de autorización
- ClerkAuthGuard - Autenticación JWT via Clerk.
- PermissionsGuard - Verificacion de permisos RBAC con
@RequirePermission(). - EventAccessGuard - Acceso al evento específico con
@RequireEventAccess(). - InvitationStateGuard - Bloquea edicion si la invitación está publicada o cerrada.