Nexo Barber
SaaS multi-tenant para barberías independientes en Costa Rica. Reservas públicas, cola en vivo, dashboard del dueño y PWA, diseñado y shippeado de punta a punta.
- Next.js 16
- React 19
- TypeScript
- Supabase (Postgres, Auth, Storage, Realtime)
- TailwindCSS v4
- Framer Motion
- Vercel
- Sentry
Resumen
Nexo Barber es un SaaS mobile-first que ayuda a las barberías independientes a reemplazar el flujo de “cuaderno + WhatsApp” con una sola plataforma: los clientes reservan en línea, los walk-ins entran a una cola en vivo, los barberos ven su día y los dueños operan el negocio desde un único dashboard. Cada barbería tiene su propio subdominio, branding, horario y programa de lealtad, todo sobre un backend multi-tenant en Supabase.
Diseñé el producto, escribí el código, monté la infraestructura, defino el roadmap y atiendo el soporte. El trabajo cubre todo el stack: UI, design system, API, base de datos, políticas de RLS, push notifications, observabilidad, billing y DevOps.
El problema
Las barberías independientes operan con piezas sueltas: un cuaderno para las citas, un grupo de WhatsApp para el equipo, Google Calendar para el dueño y nada del lado del cliente. Las reservas se duplican. Los walk-ins se pierden. Los dueños no pueden ver los ingresos del día sin hacer cuentas a mano. Las herramientas off-the-shelf (Calendly, Square) son demasiado caras, demasiado genéricas o están construidas para otro mercado.
La apuesta de Nexo: un producto enfocado, diseñado para la realidad operativa de un país, que cuesta menos de lo que la barbería pierde con una sola cita no asistida por semana.
El enfoque
Tres audiencias, un solo producto:
- Clientes reservan un servicio desde un sitio público en
<barberia>.nexocr.pro, siguen una cola en vivo con seis estados (countdown → “sos el siguiente” → “es tu turno” → completada) y tienen un perfil con su historial y progreso de lealtad. - Barberos ven su día en una vista mobile-first “Mi día”: citas, walk-ins y huecos, todo en una sola columna.
- Dueños tienen un dashboard por día, semana y mes que cubre ingresos, tracking de no-shows, servicios, equipo, clientes, programas de lealtad, anuncios y manejo de suscripción.
Todo es mobile-first. La app web es una PWA: instalable en la pantalla de inicio, funciona offline para la data ya cacheada y envía notificaciones push cuando se abre un espacio o llega el turno.
Arquitectura
Un tour corto por las partes que más pensamiento llevaron:
- Data multi-tenant con Row-Level Security. Un único schema de Postgres
aísla la data de cada barbería a nivel de fila vía RLS de Supabase. Las
políticas se escriben evitando patrones auto-referenciales (que disparan
recursión infinita la primera vez que haces
JOINsobre tu propia tabla de verificación). Cada request corre bajo el JWT del usuario; no existe camino de superusuario desde el cliente. - Routing por subdominio. Un middleware de Next.js lee el host del
request, lo resuelve a una barbería y reescribe la URL para que el mismo
App Router sirva a todas. Las rutas reservadas (
/api,/auth,/_next,/track) se saltan el rewrite del subdominio para no romper SSE, OAuth ni internals de Next. - Cola en tiempo real. Tanto el tracking del cliente como la vista “hoy”
del dueño se suscriben a canales de Supabase Realtime filtrados por
business_id. Las transiciones de estado (reservada → llegó → en servicio → completada) se propagan a cada cliente activo en menos de un segundo. - PWA con push. Un service worker maneja el prompt de instalación, los fallbacks offline para la página de reservas y las notificaciones Web Push para actualizaciones de cola y recordatorios.
- Observabilidad sin ruido. Los errores se categorizan en el momento de captura: fallas de validación, fallas de RPC, resultados esperados de negocio (rate-limited, suscripción expirada) y errores realmente inesperados. Solo la última categoría dispara alerta; el resto se queda en logs estructurados. Esa fue la diferencia entre “200 pings de Sentry al día” y “una alerta que realmente importa”.
- Disciplina de zona horaria. Vercel corre en UTC. Costa Rica no. Toda ruta de API que filtra por día o mes calcula su rango usando intervalos UTC semi-abiertos derivados de la zona local de la barbería, nunca la del servidor. El CI bloquea los patrones peligrosos. Esto eliminó toda una clase de bugs tipo “la cita de las 7 pm desapareció”.
Funciones destacadas
El producto está construido alrededor de dos audiencias con necesidades muy distintas: el cliente reservando desde el sitio público de la barbería y el dueño operando su negocio desde un dashboard. Las dos vistas comparten un design system mobile-first, el dark mode por default y el mismo backend en Supabase.
Flujo público de reservas
Cuatro pasos, sin necesidad de crear cuenta para llegar al final. Diseñado para sentirse tan fluido como pedir comida.
Espacio del dueño
Citas de día, semana y mes, ingresos, tracking de no-shows, manejo de equipo, servicios, clientes, lealtad, anuncios y suscripción: todo en un layout mobile-first que corre desde cualquier teléfono.
Decisiones que vale la pena compartir
Algunas elecciones que no se ven en los screenshots.
01
Baseline mobile Apple-HIG
Touch targets de 44 px, bottom sheets para cualquier contexto tipo overlay, filas swipeables para acciones destructivas, header sticky de dos filas en las vistas de calendario y KPIs en texto inline (no en cards). El producto se siente nativo porque eligió las convenciones de una sola plataforma y se comprometió con ellas.
02
Restaurar el estado de la reserva tras OAuth
Bug sutil pero de alto tráfico: cuando un usuario se loguea a mitad de una reserva, restaurar el flujo tiene que degradar el paso si el horario ya no es válido. La página hoy lleva un invariante en runtime que auto-corrige el estado en vez de renderizar una pantalla en blanco.
03
Reglas operativas enforced del lado del servidor
Los guards de UI no alcanzan. Reservar, cerrar, ofertas vencidas: cada transición de estado pasa por un check server-side, escrito asumiendo un cliente hostil.
04
Gates de CI para los patrones que muerden
lint:tz bloquea todo código nuevo que trate los bordes
día/mes en tiempo de servidor. lint:adapter agarra
lecturas de columnas JSONB sin el adapter canónico. Ambas reglas se
sumaron el día después del bug que previenen.
Qué optimizo
El producto opera con presupuesto ajustado (tanto en plata como en entropía de diseño). Las constraints que defiendo, en orden:
- Velocidad mobile. Core Web Vitals primero. La pantalla de inicio y el flujo de reservas son las únicas rutas donde no acepto regresiones bajo ningún concepto.
- Seguridad multi-tenant. Cada feature nueva pasa revisión de impersonación y aislamiento por tenant. Prefiero shippear una feature con una semana de atraso que shippear una que filtre data.
- Claridad para el operador. Los dueños no son ingenieros. Cada pantalla tiene que cerrar en tres segundos, o está mal.
- Rigor de diseño. Apple-HIG es el piso, no el techo. El producto compite con apps que la gente ya quiere.
Stack
- Next.js 16 (App Router)
- React 19
- TypeScript (strict)
- Supabase Postgres
- Supabase Auth
- Supabase Storage
- Supabase Realtime
- Row-Level Security
- TailwindCSS v4
- Framer Motion
- Resend (correo transaccional)
- Web Push (VAPID)
- Service Worker / PWA
- Vercel
- Sentry
- pino (logging estructurado)
- Vitest + Playwright
- Cloudflare DNS
Dónde mirar
Producto en vivo: barberapp.nexocr.pro. ¿Querés una demo guiada o un write-up técnico más profundo? Escribime a bryn.acuna7@gmail.com.