Adds the production evidence for commit 4281567 (`test:pwa` passing against the HTTPS
domain, the install button present with the chunk delayed, the chart running as a
CSSAnimation, smoke 16/16) and what this deploy taught about the setup:
- Auto-deploy webhooks are active on BOTH remotes, so pushing to gitea and github in
sequence queues two concurrent deploys of the same commit.
- `force=true` is never needed after a push; it stops the container before building and
adds avoidable downtime.
- Measured 241s of unavailability for this deploy — explicitly not attributed to cold
start, since a second deploy was building concurrently.
- This Coolify instance only answers on collection endpoints; everything per-resource
404s, so container env vars and logs are not readable over the API.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
AgendaMax
Aplicación web multi-tenant (SaaS) para la gestión de negocios de servicios (estética, spa, barbería, clínicas, etc.): calendario de citas con arrastrar y soltar, gestión de empleados, servicios, clientes y tickets, y un tablero con ingresos, mejores empleados, servicios más rentables y clientes top.
Tres roles: Administrador de plataforma (crea y gestiona todos los negocios desde la consola admin), Dueño (ve su negocio y el tablero) y Empleado (agenda citas que se auto-asignan a él).
Stack
- Frontend: React 18 + TypeScript + Vite + Tailwind CSS
- Calendario: FullCalendar (drag & drop, resize, day/time grid)
- Gráficas: Recharts
- Backend: Node.js + Express
- Base de datos: SQLite (vía
node:sqlite, nativo de Node — sin compilación nativa)
Requisitos
- Node.js 22.5+ (recomendado 24+). Este proyecto usa el módulo nativo
node:sqlite.
Instalación
npm install
La base de datos se siembra automáticamente con datos demo la primera vez que arranca el servidor. Para regenerarla desde cero:
# Borra la DB y vuelve a sembrar
npm run seed
# o, forzando reset desde el servidor:
$env:RESET_DB="1"; npm run dev:server # PowerShell
RESET_DB=1 npm run dev:server # bash
Desarrollo
npm run dev
Abre http://localhost:5173 (el frontend proxya /api al backend en el puerto 3000).
| Ruta | Qué es |
|---|---|
/ |
Landing pública. Con sesión abierta redirige al panel según el rol. |
/login |
Acceso. En fase demo ofrece entrar de un clic con las cuentas de abajo. |
/b/:slug |
Reserva pública del negocio, sin sesión. |
Producción
npm run build # compila TS y empaqueta el frontend en dist/
npm start # sirve la API y el frontend estático en http://localhost:3000
El acceso de un clic y el selector «Ver como…» del panel viven detrás de la constante de
build DEMO, que en un build de producción solo se enciende con VITE_DEMO_UI=1. El
Dockerfile la declara con ese valor por defecto porque el despliegue es la
demostración; pasar --build-arg VITE_DEMO_UI=0 los elimina del bundle.
Instalar AgendaMax como app
- Android/Chrome: abre la URL HTTPS y selecciona la acción de instalación mostrada por AgendaMax o por la barra de direcciones del navegador.
- iPhone/iPad Safari: usa Compartir -> Añadir a pantalla de inicio; iOS no expone el aviso de instalación dentro de la página de Android.
- La app instalada puede abrirse sin la interfaz del navegador, pero los datos del negocio siguen requiriendo conexión a internet.
- Validación local: ejecuta
npm.cmd run build, inicia producción connpm.cmd starty después ejecutanpm.cmd run test:pwa. - La validación de producción requiere el dominio HTTPS de Coolify, no una dirección IP HTTP.
npm.cmd run test:pwa usa estas variables opcionales para los flujos autenticados;
si no se definen, usa las cuentas demo locales:
[email protected]
[email protected]
PWA_PASSWORD=demo1234
Tests y auditoría visual
npm run test:e2e # 22 pruebas end-to-end de la API (login, CRUD, dashboard, auto-asignación, roles, multi-tenant)
npm run test:admin # 17 pruebas del admin (crear negocio + plantilla, aislamiento, reset-demo, delete cascade)
npm run test:landing # WebKit: ruteo landing/login, acceso de un clic y prefers-reduced-motion
npm run audit:visual # Playwright: navega 10 páginas × 4 viewports (móvil/tablet/desktop/wide),
# mide overflow horizontal, captura errores de consola y guarda capturas en screenshots/
npm run audit:responsive # WebKit: iPhone/iPad reales — auto-zoom, safe areas, objetivos táctiles y canaletas
La auditoría visual verifica responsividad en 375 / 768 / 1280 / 1536 px, detecta overflow, errores de consola y confirma que el calendario, el modal de cita y el cambio de sesión funcionan en cada tamaño.
Cuentas demo
Todas usan la contraseña demo1234. En /login se puede entrar con un clic, sin
escribirlas.
| Rol | Contraseña | Nombre | |
|---|---|---|---|
| Admin | [email protected] |
demo1234 |
Administrador |
| Dueño | [email protected] |
demo1234 |
Daniela Reyes |
| Empleado | [email protected] |
demo1234 |
Valentina Cruz |
| Empleado | [email protected] |
demo1234 |
Mateo Herrera |
| Empleado | sofía.ramí[email protected] |
demo1234 |
Sofía Ramírez |
| Empleado | [email protected] |
demo1234 |
Diego Castillo |
| Empleado | [email protected] |
demo1234 |
Isabela Torres |
| Empleado | carolina.mé[email protected] |
demo1234 |
Carolina Méndez |
En la pantalla de login aparecen botones de acceso rápido a las cuentas demo, y desde la barra lateral puedes cambiar de cuenta con “Ver como…”.
Multi-tenant y consola de administrador
AgendaMax es multi-tenant: cada negocio es un tenant aislado (todos los datos llevan business_id
y las consultas se filtran por el negocio del usuario). El administrador de plataforma gestiona
todos los negocios desde /admin:
- Resumen: KPIs globales (negocios activos, usuarios, ingresos y citas de toda la plataforma).
- Negocios: lista buscable con stats por negocio (usuarios, citas, ingresos).
- Crear negocio (2 pasos): eliges una plantilla → se cargan empleados, servicios y clientes demo con historial de citas/tickets listos para usar; defines los datos del negocio y la cuenta del dueño.
- Detalle de negocio: editar nombre/industria, cambiar plan (Prueba/Pro/Free) y estado (Activo/Suspendido), recargar datos demo con otra plantilla (preserva las cuentas de usuario) o eliminar el negocio por completo (con cascade de todos sus datos).
Plantillas disponibles: estetica-spa (Lumière), barberia (Urban Cut), clinica (Dental Sonrisa)
y blank (vacío, para configurar desde cero). Defínelas en server/lib/templates.ts.
El aislamiento entre tenants se valida: un dueño solo ve los datos de su negocio, y los empleados no pueden acceder al tablero ni a la gestión (403).
Funcionalidades
Calendario (Dueño y Empleado)
- Vistas de mes / semana / día, con indicador de hora actual.
- En móvil se abre por defecto en vista Día y se ofrece una vista Lista (agenda) — la vista Semana/Mes queda reservada para tablet/desktop para evitar columnas aplastadas.
- Arrastra para reagendar y redimensiona para cambiar duración.
- Clic en un hueco o en Nueva cita para abrir el formulario.
- Clic en una cita para editarla: cambiar servicio, especialista, cliente, hora, precio, notas.
- Marcar como completada (genera un ticket con propina y método de pago).
- Cancelar o eliminar citas.
- Filtra por empleado y por estado (programada / completada / cancelada).
Formulario de cita
- Servicio desplegable (menú del negocio), con precio y duración.
- Especialista: el empleado lo tiene auto-asignado por defecto (filtrado a quienes ofrecen el servicio).
- Cliente: buscar existente o crear uno nuevo al vuelo.
- Duración, precio y notas editables.
Tablero (Dueño)
- KPIs: ingresos del período, citas programadas, ticket promedio, ocupación y % de cancelación.
- Gráfica de ingresos diarios y distribución por categoría.
- Mejores empleados por ingreso, con rating y utilización.
- Servicios más rentables.
- Tickets más altos recientes.
- Top clientes (por gasto) y clientes frecuentes (por visitas).
- Rango de tiempo ajustable (7 / 30 / 60 días).
Gestión (Dueño)
- Empleados: crear, editar, asignar servicios, ver ingresos/citas/rating.
- Servicios: catálogo agrupado por categoría, con especialistas asignados.
- Tickets: historial de ventas con filtros por método de pago.
Clientes (Dueño y Empleado)
- Buscador por nombre / teléfono / correo.
- Ficha del cliente con historial de citas, gasto total y visitas.
Estructura
.
├── server/ # API Express + SQLite
│ ├── db.ts # esquema multi-tenant + sistema de migraciones versionado
│ ├── index.ts # app Express (auto-siembla admin + negocio demo en primer arranque)
│ ├── lib/auth.ts # middlewares: authRequired, ownerOnly, adminOnly
│ ├── lib/templates.ts # plantillas: estetica-spa, barberia, clinica, blank
│ ├── routes/ # auth, business, employees, services, clients, appointments, dashboard, admin
│ └── scripts/seed.ts# seedBusiness(businessId, template) reutilizable + script standalone
├── shared/types.ts # tipos TS compartidos cliente/servidor
├── src/ # frontend React
│ ├── components/ # AppShell, AdminShell, Modal, AppointmentModal, ui, DemoSwitcher, admin/CreateBusinessModal
│ ├── lib/ # api, auth, format
│ └── pages/ # Login, Dashboard, Calendar, Employees, Services, Clients, Tickets + admin/*
└── data/ # SQLite (generado, .gitignored)
Notas de seguridad
Es una demo. La autenticación usa un token trivial (el id del usuario) sin hashing de contraseña ni sesiones reales — no usar en producción tal cual. Antes de un despliegue real habría que añadir hashing (bcrypt), JWT firmados, validación estricta con zod, rate-limiting y HTTPS.