# Plan PoC Reservas de Espacios

Documento vivo con el plan original del PoC y el estado actual de implementacion en `hu-rooms`.

## Plan original

### Alcance confirmado

- Login PoC: ingreso escribiendo email `@humand.co` (sin password, sin validacion de inbox).
- MVP hackathon: busqueda de disponibilidad, reserva/cancelacion, calendario, panel admin, check-in y deteccion de no-show.

### Flujos funcionales del MVP

- **Login PoC**: usuario ingresa email; backend valida dominio `@humand.co`; sesion por cookie.
- **Reserva**: filtro por ubicacion/tipo/fecha/capacidad; validacion contra DB; estado `confirmed`.
- **Cancelacion**: dentro de politica.
- **Check-in**: manual en app (y opcion QR).
- **No-show**: job marca no-show si no hubo check-in en ventana (ej. 10 min).

### Diferencial del PoC: Sammy (chatbot accionable)

- Objetivo: chatbot que tambien ejecuta acciones (primera accion: `reservar_sala` simulada).
- Flujo: intencion -> recoleccion de datos -> sugerencia de opciones -> confirmacion obligatoria -> reserva mock y comprobante.
- Guardrails:
  - confirmacion explicita;
  - no inventar disponibilidad;
  - etiquetado visible `simulado/mock`.

### Pitch para jurado (6 bullets)

- De chatbot informativo a transaccional: Sammy ejecuta acciones.
- Reserva conversacional en lenguaje natural; menos friccion que formularios.
- Flujo completo: intencion, validacion, confirmacion y comprobante.
- Diseno extensible: mismo patron para salas, parking, escritorios, lockers.
- Impacto operativo: reserva + check-in + no-show para mejor uso real de espacios.
- Mock funcional hoy; base lista para integracion productiva despues.

### Roadmap de expansion a "mas que salas"

- Tipos inmediatos: estacionamientos, escritorios flex, lockers, cabinas, equipos compartidos.
- Futuro: reglas por tipo, aprobaciones, prioridades, cupos por persona, sanciones por no-show.

### Criterios de exito demo

- Usuario `@humand.co` inicia sesion y reserva un recurso disponible.
- Sin solapes para el mismo recurso/hora.
- Admin crea/edita recursos sin tocar codigo.
- Check-in y no-show visibles en la reserva.
- Demo con al menos dos tipos: `meeting_room` + `parking_spot`.
- Sammy completa flujo de reserva de sala punta a punta (resultado simulado/mock).

### Features adicionales planteadas

- Carga masiva por Excel (descargar template, completar y subir).
- Deeplinks de busqueda: filtros en query params para compartir una reserva prefiltrada.

### UX admin (objetivo funcional)

Login -> creacion y gestion de recursos (`sala`, `estacionamiento`, etc.) con:

- nombre;
- descripcion (opcional);
- foto (opcional);
- ubicacion jerarquica (ej: pais -> ciudad -> oficina -> piso -> salon -> asiento);
- modelo de reserva:
  - bloquea unidad o maneja cupos;
  - permitir incluir acompanantes en reserva ("voy yo con ...");
- disponibilidad:
  - slot fijo;
  - rango (ej. 8-12);
  - repeticion (ej. semanal);
- aprobaciones por admin (similar Time Off);
- cancelacion:
  - tiempo limite antes de penalidad;
  - lista de espera ante cancelacion;
- tiempo de antelacion maxima para reservar;
- tags multiples para filtros (ej. "Tiene proyector");
- deshabilitar recurso (mantenimiento);
- visibilidad (oculto/visible);
- preguntas de satisfaccion (opcional).

Adicionales de administracion:

- heatmap de uso de recursos;
- visualizacion de usuarios (uso, no-shows, cancelaciones).

### UX usuario (objetivo funcional)

Login -> vista de reservas disponibles/ocupadas. Si es admin, puede cancelar reservas.

- UI inspirada en reservas de avion/cine:
  - filtros arriba y resultados abajo;
  - actualizacion en tiempo real al cambiar filtros;
  - filtros persistidos en URL (query params) para compartir vistas prefiltradas;
- sugerencias cuando no hay resultados (tags/alternativas similares);
- sugerencias por reservas previas (repetir);
- reportar problemas en salas + encuesta de satisfaccion (guardado parcial);
- historial de reservas previas ("pedir de nuevo").
- seccion "Mis reservas" separada en:
  - **Proximas**: reservas por ocurrir ordenadas por proximidad de fecha;
  - **Historial**: reservas pasadas con accion de "pedir de nuevo".
- acciones por reserva proxima:
  - editar/cancelar sujetas a politica de antelacion y limite de cancelacion;
  - recordatorio previo cuando el tipo de recurso tenga notificaciones habilitadas;
  - reconfirmacion explicita de uso (confirmar/cancelar/editar) antes del limite maximo de cancelacion.

### Decisiones de arquitectura (Next.js + Supabase)

- **Stack**: `Next.js` (App Router + Route Handlers) como UI + BFF en un deploy a Vercel.
- **DB**: `Postgres` en Supabase.
- **Auth demo actual**: endpoint server `app/api/auth/poc-login/route.ts` prepara usuario en Supabase Auth (sin email) y el cliente inicia sesion con credencial deterministica para evitar SMTP/rate limits.
- **Principio de producto**: recurso generico reservable (no solo salas).

```mermaid
flowchart LR
  user[Usuario Humand] --> web[Next.js Web UI]
  web --> api[Next.js API Routes]
  api --> db[(Supabase Postgres)]
  admin[Admin Operaciones] --> web
```

### Repos propuestos

- `humand-space-booking-app` (app completa, frontend + backend, deploy Vercel).
- `humand-space-booking-infra` (IaC/documentacion operativa; opcional en hackathon).

### Estructura objetivo del repo app

- `app/(public)/login/page.tsx`
- `app/(app)/calendar/page.tsx`
- `app/(app)/resources/page.tsx`
- `app/(app)/admin/resources/page.tsx`
- `app/api/auth/poc-login/route.ts`
- `app/api/reservations/route.ts`
- `app/api/reservations/[id]/cancel/route.ts`
- `app/api/checkins/route.ts`
- `lib/auth/poc-auth.ts`
- `lib/auth/session.ts`
- `lib/db/client.ts`
- `lib/reservations/availability.ts`
- `supabase/migrations/*.sql`
- `supabase/seed.sql`

### Modelo de datos base (escalable)

- `users`: perfil minimo (`email`, `display_name`, `company_id`).
- `offices`: tabla legacy de sedes fisicas (en desuso funcional).
- `locations`: jerarquia de ubicaciones multinivel (padre/hijo) para reemplazar el uso plano de `offices` como unica ubicacion.
- `resource_types`: catalogo (`meeting_room`, `parking_spot`, `desk`, `locker`, `equipment`) + configuracion por tipo (`booking_unit` siempre `hour` en producto, `booking_window_start` / `booking_window_end`, `slot_size` 15/30/60 min, `time_granularity`, `reminders_enabled`).
- `resources`: entidad reservable (`resource_type_id`, `location_id`, `office_id` legacy opcional, `name`, `capacity`, `attributes_jsonb`).
- `reservations`: `resource_id`, `user_id`, `start_at`, `end_at`, `status`, `checked_in_at`, `cancelled_at`.
- `resource_policies`: reglas por tipo/sede (lead time de reserva, duracion maxima, ventana de cancelacion/edicion, aprobacion) + ventanas operativas (`booking_advance_minutes`, `cancellation_deadline_minutes`, `editing_deadline_minutes`).
- `no_show_events`: registro explicito para analitica y reglas futuras.

Snippet clave anti-solapamiento (Postgres):

```sql
ALTER TABLE reservations
ADD CONSTRAINT no_overlap_reservations
EXCLUDE USING gist (
  resource_id WITH =,
  tstzrange(start_at, end_at, '[)') WITH &&
)
WHERE (status IN ('confirmed','checked_in'));
```

### Plan de ejecucion por fases (hackathon)

1. Fundacion tecnica: bootstrap Next.js + Supabase + CI minima.
2. Dominio y migraciones: esquema base + seeds.
3. Auth PoC + sesion: login por dominio + proteccion de rutas.
4. Reservas core: disponibilidad, alta, cancelacion, calendario/lista.
5. Admin + check-in/no-show: CRUD de recursos/reglas + job de no-show.
6. Sammy actions (mock): flujo conversacional + confirmacion explicita.
7. Hardening demo: datos realistas, tracking de errores, smoke tests E2E.

### Riesgos y mitigaciones (PoC)

- Suplantacion por login sin verificacion -> allowlist `@humand.co`, disclaimer, plan de migracion a SSO/magic-link.
- Condiciones de carrera en concurrencia -> constraint de exclusion + transacciones.
- Complejidad prematura de reglas -> politicas minimas en MVP, diferir complejidad.

## Estado actual del proyecto HU Rooms

### Completado

1. **Database Schema (Supabase)**
   - Tablas: `users`, `offices`, `resource_types`, `resources`, `reservations`, `resource_policies`, `no_show_events`.
   - RLS policies configuradas.
   - Triggers para auto-crear usuarios y actualizar timestamps.
   - Datos semilla insertados (2 oficinas, 5 tipos de recursos, 7 recursos de ejemplo).

2. **Infraestructura Supabase**
   - `lib/supabase/client.ts` (cliente browser).
   - `lib/supabase/server.ts` (cliente server).
   - `lib/supabase/proxy.ts` (session handling).
   - `lib/supabase/database.types.ts` (tipos TypeScript generados).
   - `proxy.ts` (entrypoint de proxy/middleware en Next 16).

3. **Paginas creadas**
   - `app/auth/login/page.tsx` (login demo con validacion `@humand.co`, sin magic link).
   - `app/(protected)/dashboard/page.tsx` (dashboard principal).
   - `app/(protected)/admin/page.tsx` (panel admin).
   - `app/(protected)/admin/offices/page.tsx` (redirect a ubicaciones, legado).
   - `app/(protected)/admin/resources/page.tsx` (CRUD recursos).
   - `app/(protected)/reservations/page.tsx` (nueva reserva).
   - `app/(protected)/reservations/my-reservations/page.tsx` (mis reservas).
   - `app/(protected)/sammy/page.tsx` (chatbot mock).
   - `app/api/auth/poc-login/route.ts` (provision/login demo sin SMTP).
   - Navegacion de dashboard/sidebar alineada a rutas reales (`/reservations`, `/reservations/my-reservations`, `/sammy`) con deeplink opcional `?type=`.
   - Sidebar sin seccion "Reservar": la reserva se inicia desde Menu Principal > Dashboard, donde se muestran todos los tipos de recurso y se selecciona uno para ir a `/reservations?type=...`.

4. **Componentes**
   - `components/app-sidebar.tsx` (navegacion lateral).
   - `components/admin/office-dialog.tsx` (legacy, sin acceso desde navegacion).
   - `components/admin/resource-dialog.tsx` (crear/editar recurso).
   - `components/reservations/reservation-dialog.tsx` (nueva reserva).

5. **Validaciones recientes comprobadas**
   - Build local: `pnpm build` compila correctamente.
   - Lint local: `pnpm lint` corre sin errores bloqueantes (hay warnings).

6. **Operativa check-in/no-show**
   - `app/api/checkins/route.ts`: check-in server-side con validacion de estado, ownership/admin y ventana de check-in por policy.
   - `app/api/jobs/no-show/route.ts`: job de no-show que marca reservas y registra `no_show_events`.
   - `vercel.json`: cron diario (`0 6 * * *`) para ejecutar `/api/jobs/no-show` y evitar fallo de deploy en planes Hobby.

7. **Design system foundations + refresh visual de login**
   - Skill de proyecto agregada en `.cursor/skills/design-system-foundations/` (con referencias de colores, tipografia, spacing, border-radius, grid y elevation).
   - `app/layout.tsx` actualizado para usar Roboto (400/600) acorde al foundation.
   - `app/globals.css` extendido con tokens `--ds-*` del skill y utilidades tipograficas.
   - `app/auth/login/page.tsx` redisenada aplicando tokens de color/espaciado/radius/elevation sin tocar el flujo funcional de autenticacion PoC.

8. **Refresh visual DS - paso 1 (shell + dashboard)**
   - `app/(protected)/layout.tsx`: header y contenedor principal alineados a grilla, spacing y jerarquia tipografica del design system.
   - `components/app-sidebar.tsx`: sidebar y footer de usuario redisenados con tokens DS, estados activos consistentes y acentos de marca.
   - `app/(protected)/dashboard/page.tsx`: hero, quick actions y bloque de proximas reservas actualizados con tokens DS (color, spacing, radius y elevation).
   - `app/globals.css`: sidebar semantic tokens conectados al set `--ds-*` y clase utilitaria `ds-grid-shell` para margenes responsive.

9. **Refresh visual DS - paso 2 (reservas)**
   - `app/(protected)/reservations/page.tsx`: cabecera, filtros, tarjetas de recursos y slots horarios redisenados con tokens DS.
   - Filtros de reservas: ocultos por defecto; boton "Filtrar" (icono funnel) abre popover estilo Notion: primer nivel = tipo de filtro (tipo de recurso, ubicacion, dia, etiqueta), segundo nivel = opciones del filtro seleccionado.
   - `app/(protected)/reservations/my-reservations/page.tsx`: tabs, tarjetas de reserva, estados y acciones (check-in/cancelar) alineados al mismo lenguaje visual.
   - `components/reservations/reservation-dialog.tsx`: modal de nueva reserva actualizado con tokens DS para superficies, campos, estados y CTAs.

10. **Refresh visual DS - paso 3 (admin + sammy)**
    - `app/(protected)/admin/page.tsx` y `app/(protected)/admin/resources/page.tsx` actualizados con la misma jerarquia visual (hero, cards, tablas, badges y acciones).
    - `app/(protected)/sammy/page.tsx` adaptado a tokens DS en todo el layout de chat (cabecera, burbujas, sugerencias, input y estados).
    - Ajustes menores de higiene en `sammy` (ids y fallback de sugerencias) para eliminar warnings de funciones impuras durante render.

11. **Refresh visual DS - paso 4 (flujo auth restante)**
    - `app/auth/error/page.tsx` alineada al mismo language visual de login y del resto de pantallas (tipografia, colores semanticos, radios y elevation del DS).

12. **Compatibilidad de policies en check-in/no-show**
    - `app/api/checkins/route.ts` y `app/api/jobs/no-show/route.ts`: se elimina el filtro SQL `.eq("is_active", true)` sobre `resource_policies` para evitar error `column resource_policies.is_active does not exist` en esquemas que no incluyen esa columna.
    - `lib/reservations/policies.ts`: el filtrado de politicas activas pasa a ser tolerante a ambos esquemas (`is_active` opcional); si existe y es `false`, se excluye, y si no existe, la policy se considera activa.

13. **Ajuste de regla operativa para CD**
    - `AGENTS.md`: se agrega la directiva de hacer push a `origin/main` al terminar cualquier feature/fix para testeo rapido en produccion.

14. **Polling live en reservas (smooth, 10s)**
    - `app/(protected)/reservations/page.tsx`: se agrega polling cada 10 segundos para refrescar disponibilidad de `reservations` en la fecha seleccionada sin recargar toda la vista.
    - El polling se pausa cuando la pestana no esta visible (`document.visibilityState`) y hace refresh inmediato al volver al foco.
    - Se evita el flash visual: los refresh periodicos no activan el estado `loading` global ni reemplazan la grilla por spinner.

15. **Regla operativa: sync antes de editar**
    - `AGENTS.md`: se agrega la directiva de sincronizar `main` con `origin/main` via `git pull --ff-only` antes de comenzar cualquier feature/fix.

16. **Regla operativa: uso obligatorio de skill de design system**
    - `AGENTS.md` y `CLAUDE.md`: se agrega la directiva de consultar y aplicar siempre la skill `design-system-foundations` antes de implementar cambios de UI/UX.

17. **Reservas v2 (usuario): historial + proximas + rebook + edicion/cancelacion por ventana**
    - `app/(protected)/reservations/my-reservations/page.tsx`: tabs `Proximas` (ordenadas por fecha cercana) e `Historial` (incluye estados historicos y accion `Pedir de nuevo`).
    - Se agregan acciones de `Editar` y `Cancelar` en reservas proximas con validacion server-side de ventanas de edicion/cancelacion.
    - `components/reservations/reservation-editor-dialog.tsx`: modal para editar todos los campos de la reserva o crear una nueva "prellenada" desde historial.

18. **Recordatorios + reconfirmacion de reserva**
    - `app/api/reservations/[id]/send-reminder/route.ts`: envio de email de recordatorio (24h antes) y boton de trigger de demo.
    - `app/api/reservations/[id]/reconfirm/route.ts`: endpoint de reconfirmacion explicita.
    - `app/(protected)/reservations/my-reservations/page.tsx`: banner de recordatorio en proximas con acciones `Confirmar uso`, `Editar`, `Cancelar` y `Enviar email demo`.

19. **Policies por tipo de recurso (admin)**
    - `components/admin/resource-type-dialog.tsx`: configuracion por tipo de recurso para:
      - activar/desactivar recordatorios por email;
      - antelacion maxima para reservar;
      - limite para cancelar;
      - limite para editar.
    - `app/(protected)/admin/resource-types/page.tsx`: visualizacion de estado de recordatorios y ventanas por tipo.

20. **Ubicaciones jerarquicas (arbol libre)**
    - `app/(protected)/admin/locations/page.tsx` + `app/api/locations/*`: CRUD admin para nodos de ubicacion (hasta 20 niveles, valores libres).
    - `components/admin/resource-dialog.tsx`: asignacion de ubicacion jerarquica por niveles al recurso (guardada en `resources.metadata`).
    - `app/(protected)/reservations/page.tsx`: filtro top-down de ubicacion para seleccion de recursos.
    - `components/app-sidebar.tsx` y `app/(protected)/admin/page.tsx`: acceso de navegacion a la nueva seccion de ubicaciones.

21. **APIs de reservas centralizadas**
    - `app/api/reservations/route.ts`: creacion de reserva con validaciones de politica (antelacion maxima y solapes).
    - `app/api/reservations/[id]/route.ts`: edicion de reserva con politicas y validacion de conflictos.
    - `app/api/reservations/[id]/cancel/route.ts`: cancelacion con validacion de ventana permitida.
    - `components/reservations/reservation-dialog.tsx`: alta de reserva migrada a API server-side.

22. **Script SQL incremental para aplicar en Supabase**
    - `scripts/004_reservations_v2_locations_notifications.sql`: columnas/politicas para reservas v2, notificaciones por tipo y tabla `locations`.

23. **Configuracion de reserva por tipo (integrado desde remoto)**
    - `scripts/005_resource_type_booking_config.sql`: agrega `booking_unit`, `slot_size` y `time_granularity` en `resource_types`.
    - `components/admin/resource-type-dialog.tsx` y `app/(protected)/admin/resource-types/page.tsx`: administracion y visualizacion de esa configuracion junto con recordatorios/ventanas.

23b. **Ventana horaria por tipo y reserva siempre por hora (2025-03)**
    - `scripts/009_booking_window_hour_only.sql`: columnas `booking_window_start` / `booking_window_end` (TIME), fuerza `booking_unit = hour`, normaliza `slot_size` legacy (`half_day`/`full_day` → `1h`), CHECK de `slot_size` solo valores por hora.
    - Admin: sin selector Horas/Días; inputs de ventana; listado/detalle muestran rango horario.
    - `lib/booking.ts`: generación de slots, solapes por duración = granularidad, opción día completo vía rango de ventana.
    - `app/(protected)/reservations/page.tsx`, `app/(protected)/dashboard/book/[typeSlug]/page.tsx`, `components/reservations/reservation-dialog.tsx`, `components/reservations/reservation-editor-dialog.tsx`: consumen config del tipo.
    - `app/api/reservations/route.ts` y `[id]/route.ts`: validan ventana (tz `BOOKING_LOCAL_TIMEZONE`) y duración mínima.

23c. **Granularidad 60min (1 hora)**
    - `scripts/010_time_granularity_60min.sql`: extiende CHECK de `time_granularity` con `60min`.
    - Admin y `lib/booking.ts`: opción "1 hora" en selector de granularidad; `timeGranularityToMinutes` → 60.

23d. **Seed de reservas de demo**
    - `scripts/011_seed_reservations_demo.sql`: INSERT idempotente (`ON CONFLICT (id) DO NOTHING`) con UUID fijos; resuelve `user_id` y `resource_id` por email y nombre; horarios en `America/Santiago`; marca `notes = 'Seed demo hu-rooms'`.
    - `scripts/seed-reservations.default.json` + `scripts/seed-reservations.mjs` + npm script `seed:reservations`: misma data vía `SUPABASE_SERVICE_ROLE_KEY`, con chequeo de solapes antes de insertar (además del conflicto por PK).
    - `SEED_AUTO=1` / `pnpm seed:reservations:auto`: consulta `users` y `resources` en Supabase (y ventana del tipo) y genera reservas sin dataset fijo; UUID idempotentes prefijo `a1000000-…`.

24. **Fix visual global de toggles (Switch)**
    - `components/ui/switch.tsx`: se corrigen dimensiones y traslacion del thumb a valores enteros (`h-5`, `w-9`, `translate-x-4`) para evitar desborde visual.
    - Se agrega `overflow-hidden` y `p-px` al track para garantizar que el thumb no sobresalga en ninguno de los dialogs que reutilizan el componente.

25. **Reemplazo funcional de oficinas por ubicaciones**
    - Se elimina `Oficinas` de la navegacion admin y del flujo funcional; `app/(protected)/admin/offices/page.tsx` ahora redirige a `app/(protected)/admin/locations/page.tsx`.
    - `app/(protected)/admin/resources/page.tsx`, `app/(protected)/reservations/page.tsx`, `app/(protected)/dashboard/book/[typeSlug]/page.tsx`, `app/(protected)/reservations/my-reservations/page.tsx`, `components/reservations/*` y `app/(protected)/sammy/page.tsx` dejan de usar/mostrar oficina y priorizan ubicacion jerarquica.
    - `app/api/reservations/[id]/send-reminder/route.ts` deja de armar labels con oficina y usa ubicacion jerarquica + detalle.
    - El reemplazo en UI/API quedo aplicado; para entornos legacy queda pendiente formalizar un script incremental de backfill para `resources.location_id` (ver pendientes).

26. **Estandar colaborativo para queries/mutations remotas**
    - Se centraliza la lectura de env Supabase en `lib/supabase/env.ts` y `lib/supabase/env.server.ts`.
    - Compatibilidad de llaves publishable para equipos mixtos: se acepta `NEXT_PUBLIC_SUPABASE_ANON_KEY` y tambien `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY`.
    - `lib/supabase/client.ts`, `lib/supabase/server.ts`, `lib/supabase/proxy.ts`, `app/api/auth/poc-login/route.ts` y `app/api/jobs/no-show/route.ts` usan ahora la misma resolucion de entorno para evitar drift entre maquinas.
    - Se agrega `.env.example` y se documenta en `README.md` el workflow de colaboracion para aplicar scripts SQL sin pisarse.

27. **Hotfix prod env key (separacion local vs produccion)**
    - `lib/supabase/env.ts`: en produccion vuelve a requerirse `NEXT_PUBLIC_SUPABASE_ANON_KEY` como canon (comportamiento seguro para deploy).
    - Se mantiene compatibilidad con `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY` solo en local/dev para no bloquear setups de colaboradores.
    - `README.md` y `AGENTS.md` actualizados con esta separacion explicita.

28. **Catalogo publico de iconos Lucide para consumo en deploy**
    - Se agrega `public/lucide-icons.json` con el listado completo de nombres de iconos publicados en `https://lucide.dev/icons/`.
    - El archivo queda disponible para frontend/backend en runtime via ruta publica `/lucide-icons.json` (ej. formularios de tipos de recurso).
    - Se guarda metadata de origen (`source`, `extracted_from`), fecha de generacion (`generated_at`) y total (`total_icons`) para trazabilidad.

29. **Autocomplete de iconos Lucide en formularios admin**
    - `components/admin/resource-type-form.tsx` y `components/admin/resource-type-dialog.tsx` ahora usan un selector buscable de iconos consumiendo `/lucide-icons.json`.
    - El selector abre automaticamente al tipear y tambien permite apertura manual mostrando la lista completa o filtrada por texto.
    - Se mejora accesibilidad del selector con navegacion por teclado (`ArrowUp`/`ArrowDown` para navegar, `Enter` para seleccionar, `Escape` para cerrar).
    - Ajuste UX solicitado: al escribir/borrar en el input se reinicia el estado del listado, reabriendo solo cuando hay resultados para evitar paneles vacios, y la flecha de apertura/cierre evita el toggle incorrecto que reabria inmediatamente.
    - Ajuste adicional UX: la vista previa del icono ahora sigue la opcion activa mientras se navega por la lista (teclado o hover), sin requerir seleccion previa.
    - Se agrega precarga de modulos de iconos "visibles/probables" al abrir o filtrar el selector para reducir el parpadeo a fallback durante navegacion rapida entre opciones.
    - Ajuste de renderizado en preview: cuando cambia la opcion activa se mantiene el ultimo icono resuelto en pantalla hasta que el siguiente modulo termina de cargar, evitando el salto visual intermedio a icono por defecto.
    - Se agrega `hooks/use-lucide-icon-names.ts` con cache en cliente para reutilizar el listado sin refetch innecesario entre pantallas/componentes.
    - Ambos formularios muestran feedback si el icono tipeado no existe en el catalogo local, reduciendo errores de carga en runtime.

30. **Fix check-in en home (Próximas reservas)**
    - Se agrega `components/dashboard/dashboard-check-in-button.tsx` (client component) para ejecutar `POST /api/checkins`, mostrar feedback de resultado y refrescar el dashboard.
    - `app/(protected)/dashboard/page.tsx` reemplaza el botón estático de check-in por la nueva acción funcional.
    - El botón ahora se muestra solo cuando aplica check-in (reserva confirmada del día y no finalizada), alineado al criterio usado en `mis reservas`.
    - Se unifica estado de carga del CTA de check-in usando `Spinner` en lugar de texto temporal para mantener consistencia visual.

31. **Alineación UI check-in con ventana real de backend**
    - `app/(protected)/dashboard/page.tsx` y `app/(protected)/reservations/my-reservations/page.tsx` pasan a usar `resolveCheckInWindowMinutes` + `isCheckInWindowOpen` para decidir si se muestra el botón `Check-in`.
    - Se elimina el criterio simplificado (solo "hoy y no finalizada"), evitando que la UI ofrezca check-in cuando el endpoint devolvería `409 Check-in window is closed`.
    - En dashboard se amplía el select de `resource_types` para incluir `id`, necesario para resolver correctamente policies por tipo.

32. **Tooltips en acciones/slots deshabilitados por policies**
    - Se agrega `components/reservations/policy-disabled-tooltip.tsx` para mostrar una razon contextual cuando un control queda deshabilitado por reglas de policy.
    - `app/(protected)/reservations/page.tsx`: los slots deshabilitados por antelacion muestran tooltip explicando la restriccion (bloqueo total del dia o franja aun no habilitada).
    - `app/(protected)/reservations/my-reservations/page.tsx`: los botones `Editar` y `Cancelar` (incluyendo modal de confirmacion y banner de recordatorio) muestran tooltip cuando la ventana de edicion/cancelacion ya cerro.
    - `lib/reservations/advance-restriction.ts`: `getAdvanceRestriction` unifica la antelación mínima de reserva (`booking_advance_minutes`); `minSlotStart` aplica en cualquier día civil donde caiga el primer instante permitido (no solo cuando el día seleccionado es “hoy”). La vista global y el flujo por tipo usan la misma regla para slots y para el auto-avance al primer día con al menos un hueco libre que cumpla política + ventana + conflictos. En `/reservations`, al cambiar el filtro de tipo se vuelve a la fecha de hoy salvo query `date` en la URL, invalidando el ref de auto-avance para recalcular. En `/dashboard/book/[typeSlug]` se cargan `resource_policies`, los botones de horario respetan antelación y el día inicial se auto-ajusta igual que en la lista global; al cambiar de `typeSlug` se reinicia la fecha y el ref de auto-avance.

33. **Allowlist de admins por defecto actualizado**
    - `lib/auth/is-admin.ts`: se cambia el fallback de `DEMO_ADMIN_EMAILS` para usar por defecto `sebastian.favaron@humand.co`, `lucia.strozza@humand.co`, `gaston.rios@humand.co` y `marcos.palacio@humand.co`.
    - `app/(protected)/admin/layout.tsx`: el gate de admin pasa a reutilizar `isAdminUser` para evitar drift con otras validaciones server-side.
    - `app/api/auth/poc-login/route.ts`: la asignacion de `role` en `user_metadata` y en `public.users` pasa a depender del mismo allowlist (`DEMO_ADMIN_EMAILS`), dejando de hardcodear `admin@humand.co`.
    - `.env.example`: se actualiza `DEMO_ADMIN_EMAILS` con los nuevos mails por defecto.

34. **Migración de login a Google SSO (sin restricción de dominio)**
    - `app/auth/login/page.tsx`: se reemplaza el formulario PoC por botón `Continuar con Google` usando `supabase.auth.signInWithOAuth` y callback a `/auth/callback`.
    - `app/auth/callback/route.ts`: nuevo handler que hace `exchangeCodeForSession`, valida sesión y redirige a `/dashboard` (o `next` relativo) tras autenticar.
    - `lib/auth/ensure-user-profile.ts`: nuevo helper idempotente para garantizar perfil en `public.users` (`id`, `email`, `full_name`, `role`, `is_active`) y evitar roturas por FK en reservas.
    - `app/(protected)/layout.tsx`: se asegura el perfil de usuario en cada ingreso autenticado y se resuelve `role` con `isAdminUser` para mantener navegación/admin consistente con allowlist.

35. **Sammy AI real (Claude) con acciones**
    - `app/api/sammy/route.ts`: nuevo endpoint server-side que usa Claude con fallback de modelos solo `claude-*`, interpreta intencion y decide accion estructurada.
    - Politica operativa implementada:
      - consultas (`check_availability`) se ejecutan automaticamente;
      - mutaciones (`create/edit/cancel`) quedan en estado pendiente hasta confirmacion explicita del usuario.
    - Ejecucion de mutaciones reusa APIs existentes de reservas (`/api/reservations`, `/api/reservations/[id]`, `/api/reservations/[id]/cancel`) para mantener validaciones de negocio unificadas.
    - `app/(protected)/sammy/page.tsx`: reemplazo del mock por chat transaccional real contra `/api/sammy`, con UI de confirmacion/descartar accion pendiente.
    - `.env.example` y `README.md`: nuevas variables `ANTHROPIC_API_KEY` y `SAMMY_CLAUDE_MODELS`.

36. **Fix de sesion/interaccion en Sammy (401 + confirmacion por texto)**
    - `proxy.ts` vuelve a conectar el entrypoint de Next 16 con `updateSession` (`lib/supabase/proxy.ts`) para refrescar cookies/sesion en requests de app y API.
    - `lib/supabase/proxy.ts` incorpora `/sammy` en rutas protegidas para mantener comportamiento consistente con otras pantallas autenticadas.
    - `app/(protected)/sammy/page.tsx` ahora interpreta confirmaciones explicitas por texto (`confirmo`, `si`, `ok`, etc.) para ejecutar la accion pendiente aunque el usuario no use el boton.
    - Si `/api/sammy` responde `401`, el chat informa que la sesion expiro y pide relogin en vez de mostrar un error generico.

37. **Fix anti-loop en disponibilidad Sammy (horario/recurso inferidos)**
    - `app/api/sammy/route.ts` agrega enriquecimiento server-side de `check_availability` para completar `startAt`, `endAt` y `resourceId` cuando Claude no los devuelve aunque ya esten implícitos en la conversacion.
    - El endpoint ahora infiere ventana horaria desde mensajes del usuario (ej. "manana 11-12") y referencia de recurso por nombre o deicticos ("ese/esa"), usando el contexto real de recursos activos.
    - `executeAvailabilityAction` acepta filtro por `resourceId` para validar disponibilidad del recurso exacto y cortar el loop de "Necesito horario exacto..." en casos con datos ya provistos.
    - Prompt de sistema ajustado para que Sammy no pida nombre del usuario autenticado y priorice params estructurados (`ISO 8601` + `resourceId`) en acciones.

38. **UX Sammy: confirmacion persistente en timeline + CTA visible**
    - `app/(protected)/sammy/page.tsx` migra el bloque de accion pendiente a una tarjeta persistente dentro del historial del chat (timeline), manteniendo estados `pendiente`, `confirmada` o `descartada`.
    - La accion pendiente activa conserva botones de `Confirmar`/`Descartar` in-line en la tarjeta del chat; una vez resuelta, el registro queda visible para trazabilidad de la conversacion.
    - El CTA `Confirmar` deja de usar un token inexistente (`--ds-success-600`) y pasa a `--ds-success-700` para visibilidad consistente sin depender de hover.

39. **Sammy: confirmacion unificada + mensaje claro de ventana de cancelacion**
    - `app/api/sammy/route.ts` mantiene mutaciones siempre en modo `texto de confirmacion + accion pendiente`, evitando ejecucion directa en la misma respuesta del modelo.
    - Flujo objetivo unificado: primero pregunta de confirmacion en el mensaje, luego tarjeta/modal clickeable para ejecutar o descartar la accion.
    - El error de cancelacion por ventana vencida se traduce a mensaje entendible para usuario final: se informa que la ventana cerro y se muestra el limite (ej. `5 horas antes del inicio`) en lugar de propagar solo el texto tecnico.

40. **Error de cancelacion en castellano y con contexto de policy**
    - `app/api/reservations/[id]/cancel/route.ts` devuelve error `409` en castellano cuando vence la ventana de cancelacion, explicando que es una restriccion de policy de esa reserva/recurso (no un fallo de plataforma).
    - El payload agrega `errorCode: CANCELLATION_POLICY_WINDOW_CLOSED` y `policyScope` (`resource`, `resource_type`, `default`) para consumo consistente desde UI/chat.
    - `app/api/sammy/route.ts` prioriza ese `errorCode` y muestra el texto de negocio tal cual llega del endpoint de cancelacion.

41. **Sammy: resultados de disponibilidad en cards + CTA reservar**
    - `app/api/sammy/route.ts` agrega respuesta estructurada `availabilityResults` (id, tipo, ubicacion, capacidad, amenities, imagen y ventana horaria) para consultas `check_availability`.
    - `app/(protected)/sammy/page.tsx` renderiza esos resultados como grilla de cards en el timeline del chat, reutilizando el lenguaje visual de `reservations`.
    - Cada card incluye boton `Reservar`:
      - si ya hay horario completo, dispara mensaje con datos exactos (`resourceId`, `startAt`, `endAt`) para continuar en flujo transaccional;
      - si faltan datos, dispara mensaje de continuidad para que Sammy pida lo minimo necesario.

42. **Demo 100% castellano: normalizacion de errores app + backend**
    - Se traducen a castellano los mensajes de error controlados en endpoints de reservas (`create/edit/cancel/reconfirm/send-reminder`), check-in, ubicaciones, jobs y Sammy.
    - Se evita exponer mensajes crudos en ingles de proveedores/DB en errores de negocio frecuentes, devolviendo copy consistente en español para toasts y chat.
    - `lib/supabase/env.ts` y `lib/supabase/env.server.ts` tambien pasan a mensajes de configuracion en castellano para mantener consistencia en troubleshooting de entorno.

43. **Sammy: reserva directa desde card cuando ya hay datos completos**
    - `app/(protected)/sammy/page.tsx` ejecuta reserva directa al clickear `Reservar` en una card de disponibilidad que ya incluye `resourceId + startAt + endAt`.
    - Se evita la confirmacion extra en ese caso porque el click en CTA funciona como confirmacion explicita de accion.
    - Si la card no tiene horario completo, el mismo boton mantiene el flujo conversacional para pedir datos faltantes.

44. **Fix timezone en disponibilidad Sammy (hora pedida = hora mostrada)**
    - `app/(protected)/sammy/page.tsx` envia `clientNowIso` y `clientUtcOffsetMinutes` en cada request a `/api/sammy`.
    - `app/api/sammy/route.ts` usa ese contexto temporal para inferir `hoy/manana` y construir `startAt/endAt` en ISO respetando la zona horaria del cliente, no la del servidor.
    - Se evita desfase UTC en cards (ej. pedir `12-13` y ver `09-10`).

45. **Login: hero con valor de producto**
    - `app/auth/login/page.tsx`: el panel azul deja de centrarse en el mecanismo de Google; muestra titular/subtítulo de la solución y 3 bullets con iconos (Layers, BarChart3, CalendarDays) sobre gestión de recursos limitados, visualización/métricas de uso y reservas con reglas. Los detalles de SSO siguen en la tarjeta de ingreso.

46. **Carga masiva demo (6 tipos × 5 recursos)**
    - `recursos_carga_masiva_demo.xlsx`: Excel generado con las mismas columnas que `plantilla_recursos.xlsx`, rotando ubicaciones hoja (Córdoba B/C, Neuquén Oficina A, Montevideo).
    - `scripts/generate-recursos-carga-masiva.cjs` + script npm `generate:bulk-xlsx` para regenerar el archivo.
    - `scripts/007_bulk_demo_resource_types.sql`: upsert de tipos `Celulares`, `Escritorios`, `Espacios Deportivos`, `Parkings`, `Sala de conferencias` (sin duplicar `meeting_room` / Sala de reuniones).
    - `scripts/README-bulk-recursos.md`: notas sobre columna Ubicación y template dinámico.

47. **Limpieza acotada: recursos y reservas**
    - `scripts/008_clean_resources_and_reservations.sql`: `TRUNCATE reservations CASCADE`, borrado de `resource_policies` con `resource_id` seteado, `DELETE FROM resources` (evita error de FK al truncar el padre con constraint activa); no borra users, locations, resource_types ni políticas solo por tipo. Diferente de `000_reset_data.sql` (reset completo).

48. **Home: cards de "Reserva rapido" simplificadas (descripcion del tipo)**
    - `app/(protected)/dashboard/page.tsx`: se vuelve al enfoque simple por card, mostrando solo la descripcion del tipo de recurso.
    - Se compacta el espaciado vertical del card y la descripcion queda visualmente pegada al CTA `Reservar`, manteniendo un `min-height` mas chico para evitar saltos entre cards.

49. **Fix lint en icono dinámico (React static-components)**
    - `components/ui/dynamic-icon.tsx`: se elimina la creación de componentes `lazy(...)` durante render.
    - Se preconstruye un mapa de iconos lazy a nivel de módulo (`lazyIcons`) y el componente sólo selecciona por `name`, evitando el error `react-hooks/static-components`.

50. **Ajuste de naming global a "Recursos"**
    - `app/(protected)/layout.tsx`: breadcrumb principal pasa de `Reservas y Espacios` a `Reserva y Recursos`.
    - `components/app-sidebar.tsx`: título/subtítulo del brand ajustados a `Reserva y Recursos` / `Reserva de recursos`.
    - `app/layout.tsx`: metadata global actualizada para eliminar typo de `Recursoss` y alinear naming a `Recursos`.

51. **Reservas: filtros persistidos en URL + limpieza rápida + fallback de fecha**
    - `app/(protected)/reservations/page.tsx`: los filtros de tipo, ubicación, día, etiqueta y búsqueda ahora se hidratan desde query params (`type`, `location`, `date`, `tag`, `q`) y se sincronizan de vuelta a la URL para compartir vistas filtradas.
    - Se mantiene compatibilidad con deeplinks existentes por `type` (slug) y también por `id`, resolviendo ambos formatos al cargar.
    - En el popover de filtros se agrega acción de limpieza individual (icono de tacho) para tipo, ubicación, día y etiqueta sin abrir sub-vistas adicionales.
    - Si la fecha default (hoy) no tiene ningún slot reservable, la vista salta automáticamente al próximo día con disponibilidad (ventana de búsqueda: 30 días), excepto cuando el usuario llega con una fecha explícita por URL.

52. **Fix defensivo en modal de reserva ante horarios inválidos**
    - `components/reservations/reservation-dialog.tsx`: se agrega validación estricta para `startTime` (`HH:mm`), `duration` (> 0) y fechas calculadas antes de formatear o enviar la reserva.
    - El resumen de horario (`inicio - fin`) ahora usa fallback `--:--` cuando el dato no es válido, evitando crash por `Invalid time value`.
    - `handleSubmit` corta con mensaje claro cuando la configuración horaria es inválida (rango día completo inválido o combinación hora/duración inválida) en vez de romper en runtime.
    - El botón `Confirmar reserva` queda deshabilitado cuando el horario calculado no es válido, para prevenir requests inconsistentes.

53. **Fix filtro de tipo: limpiar no re-aplica query param anterior**
    - `app/(protected)/reservations/page.tsx`: la hidratación de `type` desde URL se limita a la carga inicial (una sola vez) para evitar que al limpiar el filtro vuelva automáticamente al valor previo del query param.
    - Al limpiar tipo (`tacho`), se marca explícitamente como resuelta la hidratación inicial de `type`, priorizando la acción del usuario sobre cualquier estado stale de `searchParams`.

54. **Filtro de reservas: popover forzado hacia abajo**
    - `app/(protected)/reservations/page.tsx`: el popover de filtros se configura con `side="bottom"` + `avoidCollisions={false}` para renderizar siempre hacia abajo y evitar que aparezca recortado por reposicionamientos automáticos.
    - Se agrega `sideOffset={8}` para mantener separación visual consistente respecto al botón `Filtrar`.

55. **Métricas admin: filtro por tipo de recurso**
    - `app/(protected)/admin/metrics/page.tsx`: el selector del dashboard y el de la pestaña "Todas las reservas" filtran por tipo de recurso (`resource_types` activos), no por sala/recurso individual.
    - La consulta de reservas incluye `resource_type_id` en el join de `resources`; los KPIs de ocupación usan el conteo de recursos activos del tipo seleccionado (o el total si el filtro es "todos").

56. **RLS admin: `is_admin_user()` con fallback por email JWT**
    - `scripts/012_is_admin_user_email_fallback.sql`: reemplaza `public.is_admin_user()` para que, además de `public.users.id = auth.uid()` con `role = 'admin'`, considere admin si el email del JWT (`auth.jwt() ->> 'email'`) coincide con una fila `public.users` con `role = 'admin'`.
    - Corrige el caso en que el usuario entra al panel admin (allowlist/email) pero los joins a `users` desde el cliente devolvían vacío para terceros por desalineación entre `auth.uid()` y el `id` del perfil admin en `public.users`.

57. **Sammy: sin auto-reserva por default + prioridad visual en cards**
    - `app/(protected)/sammy/page.tsx`: el CTA de cada card de disponibilidad deja de ejecutar reservas en un solo click; ahora siempre prepara la accion y espera confirmacion explicita posterior del usuario.
    - `app/api/sammy/route.ts`: se bloquean acciones `create/edit` si no hay horario explicitamente indicado en el mensaje del usuario (evita asumir ventanas por defecto como 24h).
    - `app/api/sammy/route.ts`: para consultas de disponibilidad se prioriza respuesta breve cuando hay `availabilityResults`, dejando que la grilla de cards comunique el detalle en lugar de duplicarlo en texto largo.
    - Hardening adicional:
      - el mensaje generado por click en card nunca envia `startAt/endAt`; siempre pide elegir horario antes de preparar la accion.
      - en `check_availability`, si el ultimo mensaje del usuario no incluye hora explicita, se ignoran ventanas horarias de contexto para evitar heredar rangos implicitos.
      - si llega un rango invalido en UI (`end <= start`), la card muestra "Horario no confirmado" y no un intervalo engañoso.
      - ajuste visual de cards en `app/(protected)/sammy/page.tsx`: layout con alturas consistentes, area de ubicacion reservada a 3 lineas y copy simplificado de fallback (`Sin ubicación`).
      - cards de disponibilidad usan `gap-0` en `Card`, `CardContent` con `flex-1` y `CardFooter` dedicado para el botón; esto fuerza que `Reservar` quede pegado al fondo incluso cuando la grilla estira tarjetas de una misma fila.
      - ajuste de copy en sugerencias: se elimina placeholder `<id>` y se reemplaza por un prompt realista sin identificadores mock.
      - loader de typing ajustado a una variante simple y estable (`flex items-center` + `py` tokenizado).

58. **Reservas: buscador y filtro alineados en toolbar superior**
    - `app/(protected)/reservations/page.tsx`: el botón `Filtrar` se mueve al extremo derecho de la misma fila del buscador para mantener patrón "controles arriba, resultados abajo".
    - Buscador y botón de filtro quedan con la misma altura visual (`h-10`) y estilos consistentes con tokens del design system (radius, spacing, colores y tipografía).
    - Ajuste adicional DS en el input de búsqueda: paddings/offset del ícono pasan a tokens (`--ds-space-*`) en lugar de valores hardcodeados.

59. **Reservas: tokenización visual extendida (incluye popover de filtros)**
    - `app/(protected)/reservations/page.tsx`: se reemplazan tamaños/espaciados hardcodeados por tokens `--ds-*` en toolbar de filtros, popover y acciones de limpieza (íconos, botones y contenedores).
    - Se normalizan superficies de cards e input/filter trigger a variables de color/tamaño del sistema (`--background`, `--ds-space-*`, `--ds-radius-*`, `--ds-shadow-*`), evitando utilidades visuales sueltas.
    - Ajustes de consistencia DS en estados/metadata de la lista de recursos (íconos, separadores y hover de slots), manteniendo intacta la lógica funcional de reservas y filtros.

60. **Reservas: reforzar lenguaje visual azul (Humand) en filtros**
    - `app/(protected)/reservations/page.tsx`: la toolbar superior (`buscar + filtrar`) pasa a fondo/borde azul semántico (`--ds-blueprimary-100`, `--ds-brand-400`) con texto/acento `--ds-blueprimary-800`.
    - Popover de filtros: borde y headers en paleta azul de marca; filas de filtros y acciones de limpieza agregan estados hover azules para reforzar jerarquía visual sin cambiar funcionalidad.
    - Selectores y CTAs `Listo` dentro del flujo de filtros migran a variantes azules tokenizadas para coherencia con el resto de la pantalla de reservas.

61. **Ajuste visual puntual en reservas: input neutro + header de popover corregido**
    - `app/(protected)/reservations/page.tsx`: se revierte el input de búsqueda a estilo neutro (sin fondo/borde azul) para mantener contraste y balance visual.
    - Popover de filtros: se corrige el “pintado” superior del header agregando clip del contenedor (`overflow-hidden`) y ajuste de top-radius del bloque de cabecera.
    - Se elimina padding vertical extra en la vista principal del popover para evitar el artefacto visual en el borde superior.

62. **Calendario UI en Humand Blue**
    - `components/ui/calendar.tsx`: el componente base de calendario pasa a usar acentos de marca (`--ds-brand-400`, `--ds-blueprimary-100`, `--ds-blueprimary-800`) en navegación, caption, estados seleccionados y rangos.
    - Se ajustan estados `today`, `outside` y `disabled` para mantener contraste y coherencia con tokens semánticos del design system.
    - El cambio es global: impacta todos los calendarios que reutilizan `Calendar` (reservas, filtros y dialogs) sin modificar lógica funcional.

63. **Calendario: alineación visual entre día actual y seleccionado**
    - `components/ui/calendar.tsx`: se corrige la diferencia visual de padding/tamaño entre `today` y `selected`.
    - `today` deja de usar `border` y pasa a `inset shadow` para mantener el mismo box model interno.
    - Se agrega `border transparent` base en el botón de día para que todos los estados compartan la misma caja visual.

64. **Calendario: unificación de estilos en el mismo nodo DOM (`button`)**
    - Se detecta diferencia residual porque `today` estaba estilado en `td.rdp-day` y `selected` en `button.rdp-day_button`.
    - `components/ui/calendar.tsx`: se mueve el estilo de `today` al `button` usando `group-data-[today=true]/day:*` y se neutraliza el estilo custom de `classNames.today`.
    - Resultado esperado: `today` y `selected` comparten exactamente el mismo box model y desaparece la diferencia aparente de padding.

65. **Sammy: no repregunta horario si ya está explícito + IDs solo en acciones**
    - `app/(protected)/sammy/page.tsx`: el CTA `Reservar` en cards con franja horaria confirmada ahora envía al backend `resourceId + startAt + endAt` en el mensaje API, sin exponer IDs en el texto visible del chat.
    - `app/api/sammy/route.ts`: `create/edit` enriquecen datos desde la conversación (recurso y ventana) y solo piden horario cuando realmente falta, evitando repreguntar si ya estaba explícito.
    - `app/api/sammy/route.ts`: en respuestas informativas (`check_availability` y `none`) se redactan IDs para no mostrarlos en búsquedas; los IDs quedan reservados para resultados de acciones ejecutadas (crear/editar/cancelar).

66. **Tokenización DS (alto impacto): flujo de reserva por tipo**
    - `app/(protected)/dashboard/book/[typeSlug]/page.tsx`: se reemplaza el estilo legacy (muted/primary y tamaños default de shadcn) por tokens `--ds-*` en hero, barra de filtros, toggles de flujo, cards de recursos y estados de slots.
    - Se normalizan superficies, tipografía y spacing a la misma gramática visual de `reservations` (`bg-[var(--background)]`, `--ds-radius-*`, `--ds-shadow-*`, `ds-type-global-*`).
    - La lógica funcional (filtros, disponibilidad, selección de slot y apertura de diálogo) se mantiene sin cambios.

67. **Tokenización DS (alto impacto): dashboard de métricas admin**
    - `app/(protected)/admin/metrics/page.tsx`: se eliminan colores hardcodeados y estilos legacy en charts/KPIs (`#hex`, `red-*`, `white`) migrándolos a tokens DS (`--ds-brand-400`, `--ds-error-700`, `--ds-neutral-200`, `--ds-text-neutral-*`).
    - Se unifican estados visuales de filtros, tablas, botones e indicadores con sizing/spacing tokenizado y colores semánticos del sistema.
    - Recharts queda alineado a DS mediante constantes de estilo reutilizables para grilla, ticks y tooltip.

68. **Seed local: usuarios fake + actividad para métricas/admin**
    - `scripts/seed-users-activity.mjs`: nuevo seed idempotente con IDs determinísticos para crear usuarios demo (`demo.user.XX@humand.co`) y reservas con estados mixtos (`confirmed`, `checked_in`, `completed`, `cancelled`, `no_show`).
    - El script valida solapes para estados activos (`confirmed`/`checked_in`) antes de insertar, para no romper constraints de no-overlap en recursos.
    - También crea `no_show_events` para las reservas fake con estado `no_show`.
    - `package.json`: nuevo comando `seed:users-activity`.
    - `README.md`: documentación de uso y variables opcionales (`SEED_FAKE_USERS_COUNT`, `SEED_FAKE_RESERVATIONS_PER_USER`, `SEED_FAKE_FUTURE_PER_USER`, `SEED_DRY_RUN`).

69. **Allowlist admin actualizado con Nico y Gero**
    - `lib/auth/is-admin.ts`: se agregan `nico@humand.co` y `gero@humand.co` al fallback `DEFAULT_ADMIN_EMAILS`.
    - `.env.example`: `DEMO_ADMIN_EMAILS` también incluye ambos correos para alinear configuración local/deploy.
    - Con SSO, `ensureUserProfile` toma `isAdminUser(user)` y persiste `role='admin'` en `public.users` para esos emails cuando inician sesión.

### Pendiente / por mejorar

1. Aplicar en Supabase el script `scripts/003_fix_users_rls.sql` para corregir policies recursivas de `public.users` (error `42P17` en prod).
2. Validar en deploy que este seteada `SUPABASE_SERVICE_ROLE_KEY` para mutaciones server-side (jobs y endpoints que usan cliente de servicio).
3. Setear `NO_SHOW_CRON_SECRET` (o `CRON_SECRET` para Vercel Cron) y verificar ejecucion real del cron.
4. Validar en deploy el flujo de check-in (ventana y permisos) y no-show (cambio de estado + evento).
5. Sammy AI (siguiente paso de hardening):
   - validar en deploy comportamiento de fallback de modelos Claude y errores transitorios del provider;
   - evaluar persistencia de conversaciones y auditoria de acciones ejecutadas por Sammy.
6. Reservas v2 en deploy:
   - validar en produccion los flujos de editar/cancelar/rebook/reconfirmar con datos reales.
7. Recordatorios y reconfirmacion:
   - aplicar en Supabase los scripts `scripts/004_reservations_v2_locations_notifications.sql`, `scripts/005_resource_type_booking_config.sql`, `scripts/006_reminder_advance_minutes.sql` y `scripts/009_booking_window_hour_only.sql`;
   - configurar `RESEND_API_KEY` y `RESEND_FROM_EMAIL` en deploy para envio real de emails;
   - agregar job/cron dedicado para reminder automatico independiente de navegacion de usuario.
8. Ubicaciones jerarquicas:
   - crear y aplicar un script incremental de backfill para `resources.location_id` en entornos legacy (el archivo `scripts/006_replace_offices_with_locations.sql` no existe en el repo actual);
   - terminar migracion de datos historicos y limpiar datos legacy de `offices` cuando deje de ser necesario.
9. Reservas multiples (un usuario reserva N recursos en una sola operacion):
   - definir alcance funcional: mismo rango horario para todos los recursos vs. horarios independientes por recurso, y politica de rollback (`todo o nada` vs. `parcial`).
   - backend:
     - crear endpoint bulk (`POST /api/reservations/bulk`) con `items[]` y validaciones por item (policy, solapes, ownership, disponibilidad);
     - ejecutar en transaccion para soportar `all_or_nothing=true` y devolver errores por item para UI/Sammy.
   - frontend web:
     - agregar modo multi-seleccion en flujo de reservas con resumen final y confirmacion unica;
     - mostrar claramente resultados por recurso (creada / rechazada + motivo) cuando se permita modo parcial.
   - Sammy:
     - extender actions con `create_reservations_batch` para que pueda proponer y confirmar varias reservas juntas en un solo paso.
