Docs

Panel de Administración

Aplicación Next.js 16 con App Router que sirve como panel de administración para organizadores de eventos en Nvito.

PublicadoMarzo 2026Equipo de desarrollo, arquitectos, stakeholders

Estructura del Proyecto

nvito-admin es una aplicación Next.js 16 con App Router que sirve como panel de administración para organizadores de eventos. Es el frontend principal donde los usuarios gestiónan eventos, invitados, invitaciones, mesas, RSVP, comunicaciones y más.

Stack Tecnologico

TecnologiaVersiónProposito
Next.js16.1.6Framework React con App Router
React19.2.3Libreria de UI
TypeScript^5Tipado estático
Tailwind CSSv4Estilos utilitarios
@clerk/nextjs^6.37.1Autenticación
@tanstack/react-query^5.90.20Cache y fetching de datos
SWR^2.4.0Data fetching reactivo
React Hook Form^7.71.1Gestión de formularios
Zod^4.3.6Validación de esquemas
shadcn/ui + Radix UIMultiplesComponentes de UI
Recharts^3.7.0Graficas y visualizaciones
Framer Motion^12.33.0Animaciones
next-themes^0.4.6Modo oscuro

Estructura de Directorios

Estructura de Directorios

nvito-admin/
├── src/
├── app/
├── (auth)/Rutas de autenticacion
├── sign-in/[[...sign-in]]/
└── sign-up/[[...sign-up]]/
└── (dashboard)/Rutas del dashboard
├── layout.tsxLayout con sidebar
├── page.tsxDashboard principal
├── error.tsxError boundary
├── events/Gestion de eventos
├── admin/Panel super admin
└── organization/Config organizacion
├── components/
├── ui/41+ componentes shadcn/ui
├── admin/Componentes de admin
├── visual-editor/Editor visual Artisan
└── organizations/Componentes de organizacion
├── hooks/Hooks globales
├── lib/
├── api/
├── client.tsAPI Client base (con getValidated)
├── response-schemas.tsSchemas Zod para response validation
├── services/33 archivos de servicio
└── hooks/queries/React Query hooks (39 archivos)
├── actions/17 server actions con Zod validation
└── types.tsActionResult discriminated union
├── hooks/Hooks de dominio
├── validations/16 esquemas Zod
└── themes/Temas del admin
└── providers/Context providers
├── config/environments/Archivos .env por ambiente
└── scripts/Scripts de utilidad

Mapa de Rutas

El siguiente diagrama muestra todas las rutas de la aplicación organizadas por grupo de layout:

Descripción de Rutas

RutaDescripción
/sign-inInicio de sesion via Clerk (catch-all)
/sign-upRegistro de nuevos usuarios via Clerk
/ (dashboard)Panel principal con estadisticas generales
/eventsLista de todos los eventos de la organización
/events/newWizard de creación de evento (paso a paso)
/events/[eventId]Dashboard del evento con metricas
/organizationConfiguración de la organización actual
/admin/clientsGestión de clientes que contratan eventos. CRUD completo con filtros por estado, tipo y responsable
/admin/*Panel de administración de plataforma (Super Admin + Platform Admin). Las acciones destructivas (eliminar usuarios/orgs/invitaciones) son exclusivas de Super Admin

Sub-rutas de Evento

Cada evento (/events/[eventId]/) expone 21 sub-rutas que cubren la gestión completa:

Sub-rutaModuloDescripción
/invitationsInvitacionesGestión de invitaciones: Editor Visual Artisan (internas) o gestión HTML (externas)
/guestsInvitadosLista de invitados, importación Excel, grupos
/rsvpsConfirmacionesRespuestas de invitados, estadisticas RSVP
/tablesMesasAsignación de mesas con drag-and-drop (dnd-kit)
/check-inCheck-inRegistro de asistencia día del evento
/qr-passesPases QRGeneración y gestión de códigos QR por invitado
/mobile-accessAcceso App MovilCódigos de acceso HOST para la app movil (max 3 activos)
/gift-registryMesa de regalosConfiguración de listas de regalos y cuentas bancarias
/locationsUbicacionesSedes del evento con coordenadas
/accommodationHospedajeOpciones de alojamiento para invitados
/itineraryItinerarioAgenda del evento con horarios
/background-musicMúsicaConfiguración de música de fondo para la invitación
/collaboratorsColaboradoresGestión de acceso por evento
/servicesServiciosServicios contratados para el evento
/communicationsComunicacionesEnvio multicanal (5 tipos de campana), templates, workflows, historial con filtros, estadisticas y chatbot WhatsApp
/analyticsAnaliticaMetricas de vistas, RSVP, engagement
/mediaMedia CenterGalería de fotos, subidas de invitados, audio guestbook (con moderación) y mensajes de felicitación — con tabs, paginación server-side y grids compactos
/custom-domainDominioConfiguración de dominio personalizado
/editEditarEdicion de datos generales del evento

Bloqueo en Estados Terminales

Los eventos en estado CANCELLED, COMPLETED, EXPIRED (visual) o con fecha pasada bloquean todas las acciones de modificación en el admin dashboard. El EventContext expone un flag centralizado isTerminalState que las 14 páginas de evento consultan para deshabilitar interacciones.

Expirado es un estado visual (no de BD) que se calcula cuando COMPLETED + activatedAt === null — el evento nunca público su invitación y la fecha ya paso.

Comportamiento en estado terminal:

  • ServiceCards: Muestran "Bloqueado" en vez de link navegable
  • DisabledServiceCards: No permiten activar servicios nuevos
  • Acciones rápidas: Solo muestran "Ver Estadisticas"
  • Alertas inteligentes: No se generan en estado terminal
  • Botón "Nueva Invitación": Completamente deshabilitado (sin link navegable)
  • Formularios de edicion: Deshabilitados con mensaje informativo

Cancelación de eventos:

La cancelación ahora incluye un campo obligatorio cancellationReason que se muestra en la tarjeta de estado del dashboard del evento. La respuesta GET /events/:id incluye los campos cancellationReason, cancelledAt, activatedAt y completedAt. Tras cancelar, la UI se actualiza inmediatamente via invalidación del cache de React Query.

Componentes Clave

Event Wizard

El wizard de creación de eventos (/events/new) guia al usuario a traves de un flujo multi-paso para configurar su evento. Incluye seleccion de tipo de evento, datos básicos, ubicaciones y plantilla de invitación.

Editor Visual Artisan (iframe-based)

El editor visual Artisan permite a los usuarios personalizar sus invitaciones de forma interactiva. Se integra mediante un iframe que carga el editor con comunicación bidireccional entre el admin y el editor. El hook use-artisan-invitation-editor.ts gestióna el estado y la comunicación con el iframe.

Invitaciones Externas

El módulo de invitaciones soporta dos origenes (InvitationSource): INTERNAL (editor visual + IA) y EXTERNAL (HTML subido por el usuario). La página de creación (/invitations/new) muestra 2 opciones al mismo nivel: "Elegir Template" y "Cargar HTML Existente".

Componentes principales:

  • ExternalUploadForm — Formulario drag & drop para subir archivo .html/.htm (max 5 MB). Campos opcionales: SEO title y description. Valida extension y tamano en cliente antes de enviar.
  • ExternalInvitationDetail — Vista de detalle para invitaciones externas (early return, sin editor visual). Muestra preview en iframe, estado con badge naranja "Externa", y acciones contextuales según el estado.
  • ReuploadExternalDialog — Dialog modal para reemplazar el HTML (solo en estado IN_CONSTRUCTION). Advierte que la operación es irreversible.
  • DownloadExternalButton — Descarga el HTML original desde la BD como archivo .html.
  • InvitationsTable — Muestra badge naranja "Externa" cuando source === EXTERNAL.

Acciones disponibles por estado:

EstadoAcciones
IN_CONSTRUCTIONDescargar HTML, Re-subir HTML, Publicar
PUBLISHEDDescargar HTML, Ver publicada, Despublicar, Cerrar
CLOSEDDescargar HTML, Reabrir

Flujo de datos:

  • Service: external-invitations.ts (3 métodos: upload, reupload, download)
  • Actions: external-invitations.ts (3 server actions con validación Zod)
  • Schemas: uploadExternalInvitationSchema, reuploadExternalInvitationSchema en invitation.ts

Personalización por Invitado (Editor)

El editor de invitaciones incluye controles para configurar la personalización por invitado:

  • Loader Editor (loader-editor): Permite configurar el texto de bienvenida personalizado que se muestra en la pantalla de carga. Incluye un campo para el template del saludo (con placeholder {guestName}) y un campo para el texto de fallback generico que se muestra cuando no hay shortCode.
  • RSVP Fields (rsvp-fields): Permite configurar el saludo personalizado de la sección RSVP. Incluye campos para el template de saludo con nombre (ej: ", confirma tu asistencia") y el texto de fallback.

Los textos configurados se almacenan en el schema de la invitación y se inyectan en el HTML generado como parte del JavaScript de personalización. Los tipos TypeScript correspondientes estan en lib/api/types/invitation.ts.

RSVP Analytics (Recharts)

La sección de analitica utiliza Recharts para mostrar graficas de:

  • Tendencias de confirmaciones en el tiempo
  • Distribucion de respuestas (confirmados / pendientes / rechazados)
  • Vistas de la invitación por dispositivo y navegador
  • Timeline de actividad

Gestión de Clientes (Admin)

La página /admin/clients permite gestionar la relación comercial con los clientes que contratan eventos. Es accesible para usuarios con permisos de Admin Panel (Super Admin y Platform Admin).

Componentes principales:

  • ClientsTable — Tabla con páginación server-side, filtros por estado (ClientStatus), tipo (ClientType), búsqueda por nombre/email/teléfono, y ordenamiento en columnas. Incluye columna de organización visible para Super Admin.
  • ClientFormDialog — Dialog con React Hook Form + Zod para crear y editar clientes. Secciones: contacto, clasificación, datos fiscales (colapsable), adquisicion, asignación de responsable y notas.
  • MemberSelector — Selector de miembros activos de una organización para asignar un responsable (assignedToId). Carga usuarios via GET /admin/users?organizationId=x.
  • OrganizationSelector — Selector de organización para Super Admin (elige tenant al crear un cliente).
  • ClientStatusBadge / ClientTypeBadge — Badges visuales para estado y tipo de cliente.

Flujo de datos:

  • Hooks: useClients(params), useClient(id), useCreateClient(), useUpdateClient(), useDeleteClient()
  • Server Actions: createClientAction, updateClientAction, deleteClientAction
  • Validación: schema Zod createClientSchema con validación de RFC mexicano (/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/i)

Gestión de Mesas (dnd-kit)

El módulo de mesas usa @dnd-kit (core + sortable) para permitir arrastrar y soltar invitados entre mesas, con vista visual del layout.

Comunicaciones (/communications)

La página de comunicaciones es una de las más completas del admin, organizada en 5 tabs:

1. Enviar Mensaje — 5 cards de acción que abren wizards multi-paso:

CardTipo de campanaDescripción
Enviar Invitacióninvitation_sendEnvia el link de la invitación digital
Recordatorio RSVPrsvp_reminderRecordatorio para invitados pendientes
Save the Datesave_the_dateAnuncio visual con imagen del evento
RSVP por WhatsAppwhatsapp_rsvp_chatbotConfirmación automatizada via chatbot
Mensaje Personalizadocustom_messageComunicado libre del organizador

Cada wizard incluye: seleccion de canal, filtrado avanzado de destinatarios, estimacion en tiempo real, preview del mensaje, detección de duplicados y confirmación final. El componente CampaignWizardShell proporciona una shell reutilizable con prop disabledHint para indicar al usuario por que no puede avanzar al siguiente paso.

2. Historial — Lista de campanas con filtros client-side:

  • Filtros: tipo de campana, estado, canal, búsqueda por nombre
  • Columnas ordenables: fecha, nombre, tipo, estado, destinatarios, enviados
  • Vista detalle con 4 stat cards (destinatarios, enviados, entregados, fallidos) y porcentajes
  • Tabla de mensajes individuales con status badges y botón de retry para fallidos
  • Reutiliza componentes estandar: useTableFilters, useSortableTable, FilterBar, SortableHeader, EmptyState

3. Estadisticas — Dashboard con metricas agregadas:

  • Cards principales: Enviados, Entregados, Abiertos, Fallidos (con porcentajes)
  • Desglose por canal (Email, WhatsApp)
  • Desglose por tipo de campana

4. Plantillas — Gestión de templates de comunicación:

  • CRUD con editor WYSIWYG basado en Tiptap (toolbar, variables, image placeholder)
  • Filtros por tipo de campana y estado
  • Acciones: archivar, marcar como default
  • 12 variables disponibles (nombre, evento, fecha, hora, ubicación, etc.)

5. Automatizacion — Workflows automáticos:

  • 6 triggers: GUEST_ADDED, RSVP_CONFIRMED, RSVP_DECLINED, DAYS_BEFORE_EVENT, EVENT_DAY, POST_EVENT
  • Toggle activo/pausado con Switch
  • Conteo de ejecuciones y última ejecución
  • Crear, editar, eliminar workflows

Componentes (20+ archivos en src/components/communications/):

  • 5 dialog wizards: send-invitation-dialog, send-reminder-dialog, send-message-dialog, send-save-the-date-dialog, send-rsvp-chatbot-dialog
  • Listas y detalle: campaign-list, campaign-detail, communication-stats
  • Templates: template-list, template-editor-dialog, template-selector
  • Workflows: workflow-list, workflow-create-dialog
  • Componentes compartidos: recipient-selector-advanced, guest-search-selector, channel-selector, message-preview, send-confirmation-step, image-upload-field, campaign-wizard-shell
  • Editor rico: template-rich-editor/ (subdirectorio con 5 archivos: editor, toolbar, hooks, extensions)

Flujo de datos:

  • Services: communications.service.ts (19 métodos), communication-templates.service.ts (7 métodos)
  • Hooks: use-communications.ts (5 queries + 3 mutations), use-communication-templates.ts (2 queries + 4 mutations)
  • Actions: communications.ts (13 server actions con validación Zod)
  • Validación: communication.ts (16 schemas Zod), communication-template.ts
  • Types: communication.ts (352 lineas — campanas, mensajes, workflows, DTOs), communication-template.ts (78 lineas)

Arquitectura del API Client

El sistema de comunicación con el backend sigue una arquitectura en capas claramente separada:

ApiClient (client.ts)

Clase base que encapsula todas las operaciones HTTP:

  • Metodos: get, post, put, patch, delete, upload
  • Autenticación: Token Bearer opcional en cada request
  • Base URL: Configurable via NEXT_PUBLIC_API_URL (default: http://localhost:3000/v1)
  • Error handling: Clase ApiError personalizada con statusCode y message
  • Cache: Deshabilitado para API calls (cache: 'no-store')
  • Upload: Soporte para multipart/form-data sin Content-Type header (lo genera el browser)

Services (33 archivos)

Cada dominio tiene un archivo de servicio dedicado que encapsula los endpoints:

ServicioArchivoEndpoints
Eventosevents.tsCRUD de eventos
Invitadosguests.service.tsCRUD invitados, importación Excel
Invitacionesinvitations.tsCRUD invitaciones, publicación
RSVPsrsvps.service.tsLista y stats de confirmaciones
Mesastables.service.tsCRUD mesas, asignaciones
Ubicacioneslocations.service.tsCRUD ubicaciones
Itinerarioitinerary.service.tsCRUD actividades
Mesa de regalosgift-registry.service.tsConfig, items, cuentas
Músicabackground-music.service.tsConfig de música
Hospedajeaccommodation.service.tsConfig de alojamiento
QR Passesqr-passes.service.tsGeneración y gestión QR
Acceso Movilmobile-access.service.tsCódigos de acceso HOST
Comunicacionescommunications.service.tsCampanas, mensajes, preview, estimacion
Templates de comunicacióncommunication-templates.service.tsTemplates CRUD, defaults
Analiticaanalytics.service.tsMetricas y stats
Mediamedia.service.tsGalería, uploads
Organizaciónorganization.service.tsConfig org actual
Organizaciones (admin)organizations.service.tsCRUD orgs (super admin)
Adminadmin.service.tsOperaciones de super admin
Rolesroles.service.tsGestión de roles
Paquetespackages.service.tsPlanes y paquetes
Ventassales.service.tsReportes de ventas
Contratoscontracts.service.tsGestión de contratos
Clientesclients.service.tsCRUD de clientes
Templates (admin)templates-admin.service.tsTemplates del sistema
Saludhealth.service.tsHealth checks
Tipos de eventoevent-types.tsCatálogo de tipos
Servicios de eventoevent-services.service.tsServicios contratados
Generación IAai-generation.service.tsGeneración con IA
Invitaciones externasexternal-invitations.tsUpload, re-upload, download HTML
Dominio personalizado(dentro de invitations)Config de dominio

React Query Hooks (39 hooks)

Ubicados en src/lib/hooks/queries/, cada hook encapsula:

  • Queries (useQuery): Lectura de datos con cache automático
  • Mutations (useMutation): Operaciones de escritura con invalidación de cache
  • Query Keys: Sistema centralizado en keys.ts para invalidación predecible

Server Actions

Arquitectura de Server Actions

El proyecto utiliza Next.js Server Actions como capa de mutacion entre el frontend y la API. Todas las acciones del servidor siguen un patrón consistente:

  1. Autenticación: Obtienen el token de Clerk via auth().getToken()
  2. Validación: Validan inputs con schemas Zod antes de llamar a la API
  3. Ejecución: Llaman al API Client con el token de autenticación
  4. Resultado: Retornan ActionResult<T> tipado (discriminated union)
  5. Revalidación: Ejecutan revalidatePath() para actualizar cache de Next.js

ActionResult (Error Handling)

Todas las server actions retornan un tipo ActionResult<T> que es una discriminated union:

type ActionResult<T> =
  | { success: true; data: T }
  | { success: false; error: string };

Esto garantiza que el consumer siempre maneje ambos casos (exito y error) de forma type-safe, sin excepciones no controladas.

Server Actions Disponibles (17 archivos)

ArchivoDominioAcciones
events.tsEventosCRUD, cancelación, reapertura
guests.tsInvitadosCRUD, importación Excel
invitations.tsInvitacionesCRUD, publicación, estados
users.tsUsuariosGestión, roles
organizations.tsOrganizacionesConfiguración
communications.tsComunicacionesCampanas, envio masivo
media.tsMediaUpload (presigned URL), confirmación, eliminación
tables.tsMesasCRUD, asignaciones
locations.tsUbicacionesCRUD
itinerary.tsItinerarioCRUD, reordenamiento
gift-registry.tsMesa de regalosConfiguración, items
accommodation.tsHospedajeConfiguración
qr-passes.tsPases QRGeneración
background-music.tsMúsicaConfiguración
collaborators.tsColaboradoresInvitaciones, gestión
clients.tsClientesCRUD, crear, actualizar, eliminar
external-invitations.tsInv. externasUpload HTML, re-upload, descarga HTML original

Todas las acciones tienen validación Zod en la frontera y tests unitarios co-localizados.

Gestión de Estado

React Query 5 + SWR

La aplicación usa un enfoque hibrido para gestión de estado del servidor:

  • React Query 5 (@tanstack/react-query): Se usa como sistema principal para la mayoria de queries y mutations
  • SWR: Se usa en casos puntuales de revalidación reactiva (tablas, invitaciones)

Query Keys Factory

El archivo keys.ts centraliza todas las claves de cache siguiendo el patrón de factory:

// Ejemplo de estructura de keys
queryKeys.guests.all(eventId)         // ['guests', eventId]
queryKeys.guests.list(eventId, params) // ['guests', eventId, 'list', params]
queryKeys.guests.detail(eventId, id)   // ['guests', eventId, id]

Dominios cubiertos por el Query Key Factory (19 dominios):

  • admin (users, roles, invitations, health)
  • contracts, clients, packages, organizations, sales
  • guests, guestGroups, tables, qrPasses
  • communications, analytics, rsvps, media
  • invitations, organization, permissions
  • events, dashboard, giftRegistry, backgroundMusic
  • accommodation, locations, itinerary
  • collaborators, team, customDomain
  • eventTypes, templates, templatesAdmin
  • mobileAccess

Invalidación de Cache

Al completar una mutation, se invalidan selectivamente las queries relacionadas usando queryClient.invalidateQueries() con las keys del factory. Esto asegura que los datos mostrados siempre estan actualizados.

Validación de Formularios

React Hook Form + Zod

Todos los formularios usan React Hook Form con Zod como resolver de validación a traves de @hookform/resolvers.

Esquemas de Validación (16 archivos)

EsquemaArchivoCampos principales
Eventoevent.tsnombre, tipo, fecha, descripción
Invitadoguest.tsnombre, email, teléfono, grupo
Invitacióninvitation.tsslug, template, configuración
Ubicaciónlocation.tsnombre, direccion, coordenadas
Itinerarioitinerary.tstítulo, hora inicio/fin, descripción
Mediamedia.tsarchivo, tipo, tamano, eventId
QR Passqr-pass.tsinvitado, tipo de pase
Acceso Movilmobile-access.tsexpiresAt (crear), isActive (actualizar), MAX_ACTIVE_CODES
Usuariouser.tsnombre, email, rol
Organizaciónorganization.tsnombre, slug, configuración
Contratocontract.tsorganización, paquete, fechas
Mesatable.tsnombre, capacidad, zona
Servicios de eventoevent-services.tsproveedor, costo, notas
Comunicacionescommunication.tseventId, campaignId, parametros
Clienteclient.tsnombre, email, teléfono, tipo, estado, RFC, datos fiscales, fuente de adquisicion

Todos los esquemas tienen tests unitarios co-localizados en __tests__/.

Sistema de UI

shadcn/ui (41+ componentes)

La aplicación utiliza shadcn/ui como sistema de componentes base, construido sobre Radix UI para accesibilidad y Tailwind CSS v4 para estilos.

Componentes UI Disponibles

CategoríaComponentes
LayoutCard, Separator, Scroll Area, Tabs, Collapsible
FormulariosInput, Textarea, Select, Checkbox, Switch, Radio Group, Slider, Calendar, Money Input
FeedbackAlert, Alert Dialog, Dialog, Tooltip, Popover, Sonner (toasts), Progress, Status Alert
NavegaciónDropdown Menu, Command (cmdk), Badge
DatosTable, Skeleton, Table Skeleton, Empty State, Filter Bar, Search Input, Sortable Header
AccionesButton, Label, Avatar, Highlight Text, Time Ago, Package Badge
OverlayAlert Dialog, Dialog, Popover, Dropdown Menu

Componentes Custom

Además de shadcn/ui, el proyecto incluye componentes personalizados:

  • filter-bar.tsx: Barra de filtros reutilizable para tablas
  • search-input.tsx: Input de búsqueda con debounce
  • service-page-header.tsx: Header estandar para páginas de servicio
  • table-skeleton.tsx: Skeleton loader para tablas
  • empty-state.tsx: Estado vacio con icono y acción
  • sortable-header.tsx: Header de columna con ordenamiento
  • money-input.tsx: Input especializado para montos monetarios
  • package-badge.tsx: Badge visual para planes/paquetes
  • time-ago.tsx: Componente de tiempo relativo
  • highlight-text.tsx: Resaltado de texto de búsqueda

Modo Oscuro

El modo oscuro se implementa con next-themes y el hook personalizado use-admin-theme.ts. Todos los componentes de shadcn/ui soportan dark mode de forma nativa a traves de las variables CSS de Tailwind.

Iconos

Se usa Lucide React (lucide-react) como libreria de iconos. Los componentes del itinerario usan un mapa explicito de iconos (itinerary-icons.ts) en vez de barrel imports para optimizar bundle size.

Animaciones

Framer Motion se utiliza para animaciones de transición en:

  • Apertura y cierre de modales
  • Transiciones entre pasos del wizard
  • Animaciones de drag-and-drop
  • Microinteracciones de UI

RBAC Dinamico (Permisos por Evento)

Principio rector

El backend es la fuente única de verdad de permisos. El frontend NUNCA hardcodea que puede hacer un rol. Todos los permisos se obtienen dinámicamente desde el endpoint GET /events/:id/access que retorna (hasAccess, role, isSuperAdmin, permissions[]).

Componente ShowIfCan

El componente ShowIfCan verifica permisos del usuario antes de renderizar contenido. Recibe un eventId y una action semantica, y consulta la lista de permisos retornada por el backend para decidir si mostrar u ocultar el contenido.

12 vistas de evento protegidas con ShowIfCan:

VistaAcciones protegidas
Dashboard del eventoEditar evento, eliminar evento
InvitadosImportar, enviar comunicaciones, crear invitado
InvitacionesCrear invitación
MesasGestiónar mesas
QR PassesGestiónar pases QR
UbicacionesGestiónar ubicaciones
ItinerarioGestiónar itinerario
Música de fondoGestiónar música
HospedajeGestiónar hospedaje
Mesa de regalosGestiónar regalos
Acceso movilGestiónar códigos de acceso
ColaboradoresInvitar, cancelar, eliminar colaboradores
RSVPsGestiónar confirmaciones

Hook usePermissionsQuery

El hook usePermissionsQuery(eventId) consume GET /events/:id/access y cachea los permisos del usuario para el evento actual. Retorna la lista de permisos como strings ("guests:create:assigned", "events:update:assigned", etc.) que ShowIfCan evalua internamente.

Flujo de verificacion

  1. El usuario navega a una página de evento
  2. usePermissionsQuery consulta GET /events/:id/access (cacheado por React Query)
  3. El backend resuelve el rol del usuario via user_roles y retorna getRolePermissions(role)
  4. ShowIfCan evalua si la acción semantica está incluida en los permisos retornados
  5. Si el permiso existe, renderiza el contenido. Si no, renderiza el fallback (null por defecto)

Seguridad

Autenticación con Clerk

La autenticación se gestióna via Clerk 6 (@clerk/nextjs):

  • Clerk Provider: Contexto de autenticación a nivel de layout raiz
  • auth() en Server Actions: Cada action obtiene el token JWT de Clerk para llamadas a la API
  • Protección de rutas: El layout del dashboard verifica autenticación; las rutas de auth (/sign-in, /sign-up) son públicas

Content Security Policy (CSP)

Headers CSP configurados en next.config.ts con directivas adaptadas a los servicios usados:

DirectivaOrigenes permitidos
default-src'self'
script-src'self', Clerk, Mapbox
style-src'self', 'unsafe-inline'
img-src'self', data:, blob:, Clerk, Mapbox, CDN
connect-src'self', API, Clerk, Mapbox, Sentry
frame-src'self', Clerk
worker-src'self', blob:

Response Validation en Runtime

Los servicios criticos utilizan apiClient.getValidated() que valida las respuestas del API con schemas Zod en runtime, proporcionando defense-in-depth contra responses inesperadas:

  • events.ts — Valida estructura de eventos
  • guests.service.ts — Valida arrays de invitados
  • organizations.service.ts — Valida contexto de organización

Performance

React Compiler

El proyecto tiene React Compiler habilitado (reactCompiler: true en next.config.ts), lo que proporciona memoizacion automática de componentes y hooks sin necesidad de React.memo, useMemo o useCallback manuales.

Build Optimization

OptimizacionImplementación
Standalone outputoutput: 'standalone' para builds optimizados en Docker
React CompilerMemoizacion automática de componentes
Import optimizationMapa explicito de iconos de itinerario (evita barrel import de ~560 iconos)
Hook useDebounceHook reutilizable para debounce de inputs (evita re-renders innecesarios)
React QueryCache inteligente con invalidación selectiva

Accesibilidad

Tablas

Las 6 tablas principales implementan atributos ARIA para accesibilidad:

  • aria-label: Describe el proposito de la tabla (ej: "Lista de invitados")
  • aria-sort: Indica la direccion de ordenamiento en headers con SortableHeader (ascending/descending/none)

Los componentes interactivos tipo card soportan navegación por teclado:

  • role="button" en cards clickeables
  • onKeyDown para Enter y Space
  • Focus visible en todos los elementos interactivos

ARIA Labels

Los botones de icono (sin texto visible) incluyen aria-label descriptivo para lectores de pantalla.

Testing

Metricas

MetricaCantidad
Test suites141
Tests individuales1,434
Pass rate100%
FrameworkVitest 4.x + Testing Library

Cobertura por Area

AreaArchivos con testsTests
API Services33/33Tests unitarios
React Query Hooks39Tests unitarios
Server Actions17/17Tests unitarios con Zod validation
Validaciones Zod16/16Tests de schema
Componentes25+Rendering + interacciones
Hooks custom5+Tests con renderHook

Organización de Tests

Los tests estan co-localizados en directorios __tests__/ junto al código fuente:

src/lib/api/services/__tests__/      # Tests de servicios
src/lib/actions/__tests__/           # Tests de server actions
src/lib/validations/__tests__/       # Tests de schemas Zod
src/lib/hooks/queries/__tests__/     # Tests de React Query hooks
src/components/*/__tests__/          # Tests de componentes

Patron de Tests de Server Actions

// Ejemplo: test de una server action
it('retorna error sin token', async () => {
  mockAuth.mockResolvedValueOnce({
    getToken: vi.fn().mockResolvedValue(null),
  } as any);

  const result = await someAction({ eventId: 'evt-1' });
  expect(result.success).toBe(false);
});

Arquitectura SOLID

Descomposición de Componentes

Los componentes complejos siguen el principio de responsabilidad única (SRP) mediante extracción de hooks y sub-componentes:

Contract Form Dialog (~400 lineas orquestador):

  • contracts/hooks/use-contract-calculations.ts — Calculos de precios (useMemo)
  • contracts/hooks/use-contract-form-state.ts — Estado del formulario (useState + useEffect)
  • contracts/contract-event-search.tsx — Búsqueda de evento
  • contracts/contract-pricing-section.tsx — Sección de precios
  • contracts/contract-payment-section.tsx — Sección de pago

Groups Manager (~250 lineas orquestador):

  • groups/hooks/use-groups-manager.ts — Estado y handlers
  • groups/group-card.tsx — Card individual de grupo
  • groups/group-dialogs.tsx — Dialogs compound (crear, editar, eliminar, asignar)

Scripts de Desarrollo

# Desarrollo local (puerto 5050)
npm run dev

# Cambiar ambiente
npm run env:dev           # Desarrollo local
npm run env:dev-remote    # API remota desarrollo
npm run env:test-remote   # API remota testing

# Build de producción
npm run build

# Desarrollo con Bitwarden Secrets Manager
npm run dev:bws

Referencias

  • Código fuente: nvito-admin/
  • API Client: src/lib/api/client.ts
  • Response Schemas: src/lib/api/response-schemas.ts
  • Services: src/lib/api/services/
  • Server Actions: src/lib/actions/
  • ActionResult: src/lib/actions/types.ts
  • Query Hooks: src/lib/hooks/queries/
  • Query Keys: src/lib/hooks/queries/keys.ts
  • Validaciones: src/lib/validations/
  • Componentes UI: src/components/ui/
  • CSP Config: next.config.ts
Esta pagina fue util?