Contexto.
Empezó como un proyecto de clase del ciclo DAW. Después lo reescribí entero por mi cuenta para aprender lo que quería de verdad: Spring Boot en el backend, React en el frontend, autenticación real, restricciones en la base de datos y tests contra un PostgreSQL de verdad.
Es lo que más me enseñó sobre roles y permisos: qué roles hay y qué puede hacer cada uno.
Después le di un diseño propio con movimiento ligado al scroll, ordené el frontend y lo hice accesible. El objetivo es WCAG 2.2 AA y lo comprueba axe en la CI.
El problema.
Administración, gerencia, recepción y clientes usan la misma plataforma, pero cada uno solo debe ver y tocar lo suyo.
Lo que decidí, y por qué.
Cuatro perfiles, tres de personal
- qué
- Administración, gerencia y recepción en el ERP, y cliente en su área. Las reglas viven en el backend y el frontend solo enseña lo que cada rol puede usar.
- por qué
- Si la regla solo estuviera en la interfaz, bastaría con llamar a la API para saltársela.
Refresh tokens que rotan
- qué
- El token de acceso vive en memoria y el de refresco cambia en cada uso. Si uno se usa dos veces, se revoca toda la familia.
- por qué
- Mi primera versión emitía un token largo y ya está. Al leer OWASP con calma vi que la rotación con detección de reutilización era barata y mucho más segura.
Que la base de datos impida las reservas solapadas
- qué
- El servicio rechaza reservas que se pisan y, además, PostgreSQL tiene una restricción EXCLUDE USING gist como red de seguridad.
- por qué
- Mis dos primeras comprobaciones a mano fallaban: una con el día de salida y otra con las zonas horarias. La tercera la hace la base de datos y el test es una línea.
Accesibilidad comprobada en cada PR
- qué
- Un trabajo de la CI recorre 18 pantallas a 390 y 1440 px con axe y mide el desplazamiento horizontal. Falla si hay violaciones serias o críticas.
- por qué
- Una revisión a mano al final se olvida. Con la prueba en cada PR, una regresión se ve el mismo día.
Datos de demo que no caducan
- qué
- Un proceso opcional renueva las reservas, facturas y tareas de demo para que el panel no muestre 0 € ni 0 reservas. Los clientes de demo no tienen cuenta y sus reservas no envían correos. Está apagado por defecto y se activa con una variable de entorno.
- por qué
- Un panel vacío no enseña lo que hace el ERP, y las fechas escritas a mano caducan.


Resultado.
- 31 pruebas en el backend, incluidas pruebas con Testcontainers contra PostgreSQL.
- axe en la CI: 18 pantallas a 390 y 1440 px, sin violaciones serias ni críticas.
- Despliegue gratuito con demo pública.
Qué aprendí.
- La rotación de refresh tokens no es opcional.
- Una restricción en la base de datos vale más que tres comprobaciones a mano.
- TanStack Query cambió mi forma de pensar el estado del frontend.
Qué haría distinto
- Empezaría con componentes más grandes y los dividiría solo cuando algo se repita de verdad.
