Sistema de turnos para barberías
Reservas, tienda, academia y panel de gestiónDemo
Sistema completo para una barbería: reserva de turnos online, tienda, academia y panel de administración. Demo con marca genérica que desarrollé en Qontera, con React y una API en Node con SQLite.
Es una demo con datos ficticios; los pagos y los avisos por WhatsApp están simulados. Tiene acceso privado; si querés verla, escribime.
Las reglas de negocio (disponibilidad de horarios, permisos y reportes) son funciones puras en una carpeta compartida: el navegador las usa para armar la interfaz y el servidor las vuelve a aplicar antes de guardar. La API está hecha con Hono y guarda todo en SQLite, y el despliegue se empaqueta en Docker para publicarse bajo un subpath.
Herramientas desarrolladas
Hechas para Sistema de turnos para barberías, ordenadas de la más compleja a la más simple.
Motor de reservas sin turnos superpuestos
- TypeScript
- Hono
- SQLite
Qué hace
Una función pura calcula los horarios libres de un barbero en un día a partir de su jornada, la duración del servicio, el intervalo configurado y los turnos que ya ocupan lugar (pendientes, confirmados, completados y ausentes). Además de ofrecer los horarios de un barbero, permite unir los de todos, y al elegir «cualquiera» el servidor asigna al barbero libre con menos turnos ese día.
Al reservar, el servidor no confía en lo que mostró el navegador: calcula de nuevo el precio y la seña, normaliza y valida el celular, y si el cliente ya existe conserva el nombre guardado. Reprogramar un turno usa la misma regla e ignora el propio turno.
Qué complejidad tiene
Dos personas pueden elegir el mismo horario a la vez. Por eso la reserva completa se ejecuta dentro de una transacción de SQLite abierta con BEGIN IMMEDIATE, que toma el bloqueo de escritura antes de leer: la disponibilidad se revalida con las mismas reglas del navegador y, si otro turno ganó el lugar, la segunda reserva se rechaza con un conflicto en lugar de duplicarse.
Como la regla vive en un solo módulo, la interfaz y el servidor no pueden discrepar sobre qué horario está libre. Los turnos cargados desde el panel pueden ir en el pasado, para registrar historial, pero siguen sin poder pisar a otro.
Permisos por rol y por barbero
- TypeScript
- Hono
- zod
Qué hace
El dueño tiene acceso total y puede administrar equipo, servicios, productos, cursos y ajustes. A cada barbero se le asignan cuatro permisos: ver todos los turnos, editarlos, ver clientes y ver reportes. Una sola función traduce al usuario en sus permisos efectivos, y la usan tanto las pantallas como la API.
Un barbero sin permiso para ver todo queda limitado a su propia agenda: la API filtra los turnos y los reportes por su barbero y le impide crear turnos en la agenda de otro. A quien no puede ver clientes se le ocultan los datos de contacto.
Qué complejidad tiene
Ocultar un botón no protege nada, así que cada ruta del panel pasa por un chequeo de sesión y otro de permiso antes de ejecutarse, y los filtros por barbero se aplican en la consulta, no después en el navegador. Los datos de entrada se validan con esquemas de zod.
Tener una única función de permisos compartida evita que la pantalla muestre algo que el servidor después rechaza, o al revés.
Inicio de sesión y sesiones
- TypeScript
- Node.js
- scrypt
Qué hace
El acceso al panel usa contraseñas cifradas con scrypt y una sal aleatoria por usuario, que se comparan en tiempo constante. La sesión se guarda en una cookie que el navegador no deja leer desde JavaScript, con vencimiento, y se cierra desde el servidor. Los accesos y la consulta pública de clientes por celular tienen un límite de intentos.
Qué complejidad tiene
El servidor no guarda el token de sesión: guarda su huella calculada con una clave secreta, de modo que una copia de la base de datos no alcanza para reutilizar una sesión. Las sesiones vencidas se purgan al iniciar otras.
Dashboard de reportes
- React
- Recharts
- TypeScript
Qué hace
El panel muestra turnos de hoy, ingresos del mes, ticket promedio, clientes nuevos y ausentismo, junto con un gráfico de ingresos de los últimos 30 días, los turnos de cada barbero en el mes y los servicios más pedidos. Los cálculos son funciones puras con pruebas, y los gráficos se dibujan con Recharts.
Qué complejidad tiene
Cada indicador tiene una definición precisa: los ingresos del mes suman solo turnos completados, el ticket promedio divide por esos mismos turnos y el ausentismo se calcula sobre los turnos ya pasados que no están pendientes, confirmados ni cancelados.
La API solo entrega los reportes a quien tiene ese permiso, y fuera del dueño cada barbero ve únicamente las cifras de su propia agenda.
Tienda y academia con stock y cupos
- TypeScript
- Hono
- SQLite
- zustand
Qué hace
La tienda tiene filtros por categoría y un carrito del lado del navegador. Al confirmar, el servidor arma el pedido con los precios del catálogo y no con los que envía el cliente, y descuenta el stock. La academia permite inscribirse a cursos con cupos limitados.
Qué complejidad tiene
Tanto el pedido como la inscripción corren en una transacción: se verifica el stock o los cupos y se guardan los cambios juntos, así que dos compras simultáneas del último producto no lo venden dos veces y un curso completo rechaza nuevas inscripciones. Los productos repetidos en el carrito se suman antes de compararlos con el stock.
Demo que se regenera sola
- TypeScript
- SQLite
- Node.js
Qué hace
Un generador crea turnos, clientes, pedidos, cursos e inscripciones con fechas calculadas a partir de hoy, de modo que la agenda y los reportes siempre se ven con actividad. Si la base está vacía al arrancar, se carga sola; en modo demo se recarga todos los días a una hora configurada y el dueño puede reiniciarla desde el panel.
Qué complejidad tiene
Reiniciar borra y vuelve a cargar todas las tablas dentro de una transacción, así que nadie ve la base a medias. El reinicio, tanto el automático como el manual, solo está disponible en modo demo.



