Portafolio / Sistemas de negocio / snap

Seis sucursales, un flujo de reserva.
Cero reservas duplicadas.

SNAP es una cadena de estudios de autofoto en seis ciudades de Indonesia. El sitio anterior era un folleto estático — las reservas se gestionaban por DMs de WhatsApp, cada sucursal llevaba su propio cuaderno y aproximadamente una de cada doce sesiones terminaba en un conflicto a la puerta. Reconstruimos el flujo del cliente como una PWA y entregamos un panel de administración único que ve las seis sucursales a la vez.

ClienteSNAP Self-Photo Studio
AlcanceBooking PWA · QRIS · Administración · Reportes
Escala6 sucursales · 2 tipos de estudio · 30+ fondos
Plazo8 semanas · 2026 · en despliegue
Qué desarrollamos

Un sistema de reservas que sabe en qué sucursal está parado.

Una PWA mobile-first para el cliente, una SPA de escritorio para la administración, una sola API y una sola base de datos por debajo. Seis módulos sostienen toda la cadena — desde el splash de ciudad que el cliente ve en Instagram hasta el cierre de caja diario que el responsable de sucursal confirma al cerrar.

Módulo 01

Puertas de sucursal y ciudad

Seis ciudades, doce sucursales, dos arquetipos físicos de estudio. El flujo del cliente omite automáticamente el paso de sucursal en ciudades con un solo estudio y enruta el modelo de capacidad correcto según el local.

Geo-enrutadoMulti-tenant
Módulo 02

Dos modelos de reserva

Los estudios box operan con una matriz de fondo×horario — cada sala se reserva de forma independiente. Los estudios roll-down manejan un horario compartido con selección de fondo puramente estética. Misma interfaz, dos reglas de capacidad distintas por debajo.

MatrizTurno único
Módulo 03

Confirmación automática QRIS

El cliente escanea un código QRIS dinámico, el callback del comercio libera el turno y el cliente ve una confirmación. Sin personal mirando la app del banco. Sin el mensaje manual de "¿ya pagó?".

QRISWebhook
Módulo 04

Panel general de administración

Un panel para el fundador. Reservas del día en las seis sucursales, ventas vs. ayer, gastos por categoría, ocupación por sala. Desde cualquier tile se puede ir a los registros detallados.

KPIs en tiempo realDrill-down
Módulo 05

Consola de operaciones de sucursal

Los responsables de sucursal solo ven su sucursal — calendario del día, activación de turno para walk-ins, registro de gastos, cierre de caja al final del día. Basado en roles, bloqueado por defecto.

RBACCierre diario
Módulo 06

Confirmaciones por WhatsApp

Reserva confirmada → mensaje de WhatsApp con plantilla que incluye el turno, la dirección de la sucursal y un enlace al calendario. Recordatorio automático 2 horas antes. El hilo de DMs ya no es donde viven las reservas, pero sigue siendo donde los clientes esperan recibir respuesta.

PlantillasRecordatorios
Seis sucursales · escala de grises se convierte en color al pasar el cursor

Una línea de producto, una identidad, seis salas.

Snap / Kemang
Snap / Senopati
Snap / Bandung
Snap / Surabaya
Snap / Bali
Snap / Yogya
● El problema

Las reservas vivían en una bandeja de WhatsApp.

Cada sucursal tenía su propio número de administración. Los clientes enviaban mensajes del tipo "¿mañana 3pm Kemang?" y alguien respondía si estaba despierto. Dos empleados a veces confirmaban el mismo turno a dos clientes distintos porque la bandeja era la única fuente de verdad.

Los fines de semana con más actividad, aproximadamente una sesión de cada doce terminaba en un conflicto a la puerta — una parte quedaba sin lugar, recibía un reembolso y se enojaba en Instagram.

"Gestionábamos un calendario dentro de una app de mensajería. Funcionaba hasta que dos mensajes llegaban en el mismo minuto." — fundador, SNAP
S
Snap Kemang
visto hace 11 min
¡Hola! ¿Hay turno mañana a las 3pm?10:42
Sí, 50% de depósito por favor10:44
Hola, mañana 3pm vamos a ser 4 personas10:46
Hola, también quiero reservar mañana a las 3pm, ¿sigue disponible?10:47
Un momento, déjame verificar10:51
¿Hola? Ya transferí11:08
??? ¿hola?11:24
Reserva duplicada
● La solución

El turno es una fila en la base de datos, no un mensaje.

El cliente elige una ciudad, una sucursal, una fecha, un horario y un fondo — cada paso escribe un bloqueo en una fila real. Dos teléfonos tocando el mismo turno en el mismo segundo: uno gana, el otro ve "recién tomado" y recibe el siguiente bloque de 30 minutos disponible.

La confirmación se envía luego por WhatsApp, así los clientes siguen recibiendo respuesta donde lo esperan. El DM ahora es el comprobante — no la reserva.

9:41 ●●●● ◐ ▮▮▮
Snap / Kemang
Sáb 14 Feb · estudio único
Paso 4/6
Elegir hora · 30 min 8 libres
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
Elegir fondo · solo estética
Rp 120,00013:30 · Azul
Continuar →
● Segundo problema

"Fondos" significaba dos cosas distintas.

La mitad de las sucursales son estudios box — cada fondo tiene su propia sala, así que dos fondos implican dos sesiones en paralelo. La otra mitad son estudios roll-down — múltiples fondos colgados en una sola sala, pero solo puede realizarse una sesión a la vez.

El sitio anterior los trataba de forma idéntica. Los clientes reservaban cuatro fondos al mismo tiempo en una sucursal roll-down y llegaban para descubrir que no era posible realizarlos todos.

Kemang · Sáb
13:00 — Adi (4 pax)
13:30 — Sarah ✓
Senopati · Sáb
Box A — 1pm Tia
Box B — 1pm Rai
Box C — libre
Bandung
¿pago? — DP 50
efectivo 70 — ¿resto?
Surabaya
walk-in 2 pax
cliente esperando ←
Bali · Dom
reembolso 1 reserva
duplicada otra vez 😩
Yogya
gasto hielo 25k
electricidad 280k
limpieza 60k
● La solución

El modelo de capacidad vive en el turno, no en el fondo.

Cada sucursal está etiquetada como multi o single en la base de datos. Los estudios box tienen una cuadrícula de fondo×horario — elegir una celda es elegir una sala y un horario en un solo movimiento. Los estudios roll-down tienen un horario compartido y una selección de fondo separada que es puramente estética.

Mismo shell en React, mismos componentes, diferente forma de datos desde un único endpoint. El cliente nunca tiene que saber la diferencia.

9:41 ●●●● ◐ ▮▮▮
Snap / Senopati
Sáb 14 Feb · box · 4 salas
Multi
Elegir sala y hora 9 libres
·
12
12:30
13
13:30
14
A
×
×
B
×
C
×
×
D
×

○ libre    × ocupado    ● seleccionado

Sala B · 13:00Senopati · 30 min
Continuar →
● Tercer problema

Alguien tenía que estar mirando la app del banco.

El cliente transfería, capturaba el comprobante, lo enviaba por WhatsApp. El administrador de la sucursal abría la app del banco, buscaba el monto correspondiente, respondía "ok confirmado" y anotaba el turno en el cuaderno. Cinco minutos por reserva un día tranquilo, mucho más un sábado.

Si el administrador estaba en el almuerzo, el cliente esperaba. Si el cliente esperaba demasiado, cancelaba.

9:41 ●●●● ◐ ▮▮▮
Pagar con QRIS
Reserva #SNP-2614
Paso 6/6
Rp 120,000
Snap Kemang · 13:30
14:32 restantes

Confirma automáticamente.
No se necesita captura de pantalla.

Esperando pagodetección automática por callback QRIS
En vivo
Antes & después

Cómo era un sábado por la mañana.

Para un administrador de sucursal un sábado, desde la apertura hasta el mediodía — el tramo más ocupado. La misma persona, el mismo trabajo, antes y después de la reconstrucción.

Antes Sáb · 4h gestionando chats
  • 09:00aperturaAbrir WhatsApp Web. 17 chats no leídos de DMs de la noche.
  • 09:20+20 minCruzar el cuaderno con las reservas de anoche. Dos turnos en conflicto.
  • 10:05+1 hrCliente mensajea "ya transferí". Abrir la app del banco. Buscar el monto. Responder "ok".
  • 10:48+1h 48Dos teléfonos, dos clientes, ambos quieren las 11:30. Elegir uno. Reembolsar al otro.
  • 11:30+2h 30Llega un walk-in. El cuaderno dice que la sala está libre. El teléfono dice que está reservada. Discusión.
  • 12:55+3h 55Almuerzo postergado otra vez.
~4 hr
Después Sáb · 35 min de administración
  • 09:00aperturaAbrir el panel de control. El calendario del día ya está cargado desde la noche anterior.
  • 09:05+5 minRevisar el hilo de plantillas de WhatsApp por excepciones. Ninguna.
  • 10:00+1 hrLlega un walk-in. Tocar "abrir turno walk-in". El cliente paga por QRIS en el mostrador. Turno registrado.
  • 11:30+2h 30Echar un vistazo al widget de ocupación de salas. Las cuatro salas en marcha. Nada que hacer.
  • 12:00+3 hrAlmuerzo.
~35 min · ~14% de antes
Cómo se armó todo

Ocho semanas, tres despliegues.

01

Semanas 1–2 — mapear el caos

Pasamos un sábado completo con dos administradores de sucursal. Observamos cómo se llenaba el cuaderno, se gestionaban los chats, se actualizaba la app del banco. Anotamos cada paso. Eliminamos todo lo que no tenía que existir si hubiera una base de datos.

02

Semanas 3–6 — construir el flujo del cliente

PWA en React + Vite, Express + MySQL en el backend. Dos modelos de reserva detrás de un único shape de endpoint para que la interfaz no se bifurcara. Integración QRIS con fallback de webhook para que una red inestable nunca dejara a un cliente atrapado a mitad del pago.

03

Semanas 7–8 — administración + lanzamiento suave

SPA de administración: resumen general, calendario de reservas, gastos, ventas, reportes, configuración. Desplegamos primero en una sucursal (Kemang), la monitoreamos una semana, luego incorporamos el resto de a dos. Los cuadernos se retiraron sucursal por sucursal.

Resultado · cifras del despliegue inicial

Primera sucursal en el sistema por ~6 semanas.

Las cifras a continuación provienen de la base de datos en vivo de la sucursal piloto (Kemang) más las dos sucursales que se incorporaron después. Las cifras de toda la cadena se publicarán aquí solo cuando haya suficientes meses de datos reales detrás.

0
Conflictos a la puerta desde el lanzamiento
Sucursal piloto · 6 semanas en vivo · antes ~1 de cada 12 sesiones de fin de semana terminaba en conflicto.
~85%
Tiempo de administración recuperado
La franja del sábado antes del mediodía del administrador bajó de ~4 hr a ~35 min — solo sucursal piloto, por ahora.
< 6seg
Confirmación QRIS mediana
Desde el escaneo hasta el callback "pagado". Antes eran 3–5 min mientras alguien miraba la app del banco.
3 / 6
Sucursales incorporadas
Kemang, Senopati, Bandung. Las tres restantes se incorporan a razón de una cada quince días.
Bajo el capó

Tecnología sin sorpresas, a propósito.

Seis sucursales en seis ciudades, un fundador que tiene que poder leer el panel de control desde el teléfono mientras espera en la fila de un café. Cada decisión respondía a: ¿qué pasa si el wi-fi de una sucursal se cae en el pico del sábado?

Cliente del usuario

React PWA, mobile-first

Instalable desde la pantalla de inicio. Transiciones de página con Framer Motion. CSS Modules y una paleta estricta en blanco y negro — las fotos aportan el color, la interfaz no interfiere.

React 18 · Vite · Framer Motion · CSS Modules
Backend

Express + MySQL, un solo tenant

Una base de datos, sucursales como filas, modelo de capacidad como enum. Las escrituras de turnos pasan por un único endpoint que bloquea la fila para evitar que dos reservas simultáneas ganen la misma celda.

Node 20 · Express · MySQL 8 · bloqueos a nivel de fila
Pagos

QRIS dinámico + webhook

Código QRIS dinámico por reserva con TTL de 15 minutos. El webhook de liquidación libera el turno; si el webhook falla, un poller reconcilia en un minuto. De cualquier forma, el cliente ve la misma confirmación.

QRIS dinámico · webhook · fallback de polling
Administración

La misma app React, diferente shell

Sin codebase de administración separado. El login del fundador lleva al layout de barra lateral de escritorio; el login de sucursal lleva a la vista de una sola sucursal. RBAC en la API, no en la interfaz — navegar por la UI nunca puede otorgar permisos que el servidor no haya aprobado previamente.

RBAC · rutas verificadas por servidor · SPA de escritorio
Siguiente caso → GhostKitchen

¿Bandeja de reservas
fuera de control?

Llamada de diagnóstico sin costo. Le decimos qué es lo más sencillo que lo soluciona.

Builds como este funcionan sobre la Plataforma Core + módulos a medida — la mayoría de los sistemas de reservas queda entre $2.500 y $4.500. Precios completos →