React con estado real
TanStack Query para sincronizar servidor y cliente, Context API para la autenticación y React Router con rutas protegidas por rol.
PWA mobile-first para la gestión de turnos, cambios y reservas en entornos con rotación cíclica de personal: enfermería, seguridad, bomberos, policía o cualquier equipo que trabaje por turnos. Construida para uso real, con 54 usuarios, ciclos de 42 días y panel de administración completo.
En hospitales, servicios de emergencias, seguridad privada o cuerpos de policía, el personal se organiza en grupos con turnos de 12 horas y ciclos de rotación fijos. Los cambios de turno, las reservas y los eventos se suelen gestionar a mano: papeles, llamadas y mensajes de WhatsApp.
Esta app lo resuelve. Cada trabajador consulta su calendario desde el móvil y pide cambios desde la app; los responsables los aprueban o rechazan desde el panel de administración. Todo queda registrado y el sistema calcula los turnos futuros a partir del ciclo de cada grupo.
Lo que la separa de un ejercicio académico es que está hecha para usuarios reales, con restricciones operativas concretas: validaciones de negocio en servidor, protección de los turnos modificados a mano, documentos que no se pueden descargar por política interna y una PWA instalable en el iPhone sin pasar por el App Store.
Busca cambio 26 may (DÍA)
por cualquier turno de noche
Cambio 2 jun (NOCHE)
Esperando aprobación del admin
Cambio 15 jun (DÍA)
Motivo: festivo obligatorio
Maquetas ilustrativas con datos ficticios.
Calcula el turno de cualquier usuario en cualquier fecha futura. Un cron job semanal genera 12 semanas vista sin tocar los turnos modificados a mano.
Dos vías: tablón abierto (cualquiera puede responder) o acuerdo directo con un compañero. Flujo en dos fases con aprobación final del admin.
Aprobar o rechazar cambios, asignar reservas, cambiar el estado del personal (activo, baja, permiso), crear eventos y dar de alta usuarios.
Solo se puede asignar como reserva a quien tiene turno libre ese día. El frontend filtra, pero la regla se aplica en la API.
Se generan en cada evento relevante (cambios, reservas, documentos nuevos), con panel propio y marcado de leídas.
El admin sube PDFs que se sirven inline, sin opción de descarga: Content-Disposition: inline y Cache-Control: no-store.
Funciona en móvil, tablet y escritorio. Se instala en la pantalla de inicio del iPhone sin App Store, con experiencia de app nativa.
Login con email y contraseña, tokens de 7 días, rutas protegidas por rol (ADMIN/USER) y redirección automática al expirar la sesión.
La función calcularTurno() determina el turno de cualquier usuario en cualquier fecha sin tener todos los registros en memoria. Los turnos se guardan en base de datos solo para consultas rápidas y para admitir cambios manuales, no como fuente de verdad del ciclo.
El campo generadoAuto: false hace de protección. El cron semanal hace upsert con update: {} vacío: si el registro existe, no lo toca, y se conservan los cambios del admin.
El frontend filtra visualmente a los disponibles, pero POST /api/reservas comprueba en base de datos que el turno de ese día sea LIBRE antes de crear la asignación. La interfaz es experiencia de usuario; la validación real está en el servidor.
Decisión funcional: los trabajadores tienen derecho a saber por qué se rechaza su solicitud. Si aprueba: false llega sin motivoRechazo, la API responde 400. Es una regla de negocio, no una validación técnica.
TanStack Query para sincronizar servidor y cliente, Context API para la autenticación y React Router con rutas protegidas por rol.
Esquema con 10 modelos relacionados, migraciones, upserts seguros y cuándo guardar un estado calculado frente a calcularlo bajo demanda.
Interceptor de Axios que añade el token, gestión de la expiración con redirección al login y separación real de rutas por rol en servidor.
Configurar vite-plugin-pwa para instalar la app en iOS sin App Store y diseñar primero para móvil con CSS propio.
El repositorio será público cuando se complete el despliegue en producción.