Segunda iteración del rediseño (sesión separada con skill impeccable).
Toca prácticamente toda la UI: layout base, tokens (global.css),
todas las páginas de alumno y admin, componentes de solicitudes,
inventario, catálogo, filtros, toaster y modales.
Docs y sistema:
- DESIGN.md y PRODUCT.md actualizados con la nueva iteración.
- .impeccable/design.json refleja tokens vigentes.
- .impeccable/review/ contiene screenshots de la revisión visual.
Dependencias:
- Añade @fontsource-variable/jetbrains-mono (fuente monoespaciada).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rediseño hecho en sesión aparte con el skill impeccable. Se registran
las especificaciones y ajustes visuales al sistema ya en producción.
Añade:
- BrandMark.astro con el escudo/logo del laboratorio.
- Favicon actualizado (svg + ico).
- Páginas propias 403 y 404 consistentes con el sistema visual.
- DESIGN.md y PRODUCT.md documentando decisiones.
- .impeccable/design.json con el design system tokenizado.
Ajusta componentes existentes (modales, autocomplete, layout, login,
solicitudes, mis-préstamos, middleware) para alinearse a los tokens
del design system.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Elimina /api/whoami (endpoint diagnóstico temporal).
- Elimina bloque debug del 403 en /api/admin/prestamos/[id]/devolver.
- Bitácora con la causa raíz y el fix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Astro por default rechaza POST con "Cross-site POST form submissions
are forbidden" cuando el header Host y el Origin no matchean
literalmente. Detrás de Cloudflare Tunnel esto pasa siempre porque
Host llega como el hostname público pero Origin trae la URL edge.
Los endpoints ya están protegidos por cookies HttpOnly + SameSite=Lax
y OAuth PKCE, así que CSRF no aplica. Se desactiva checkOrigin.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Alumno:
- Modal de solicitud: campo "Notas" renombrado a "Motivo del préstamo",
ahora requerido, con hint sobre uso (para qué clase o proyecto).
Admin:
- Bandeja de solicitudes pendientes, activos y devoluciones ahora
muestran el motivo del alumno (en tabla desktop y tarjetas móvil).
- /admin/inventario se convirtió en "Estadísticas": grid de KPIs del
día (solicitadas, aprobadas, rechazadas, devoluciones, en préstamo,
vencidos) arriba del inventario existente. Los cambios de estado se
cuentan desde audit_log, el rango del día se calcula automáticamente
en timezone America/Tijuana con DST via Intl API (src/lib/date.ts).
Rediseño móvil:
- Sidebar desktop se puede colapsar con botón hamburguesa. Estado
persiste en localStorage sin flash (script is:inline bloqueante).
- Nav móvil reemplazado por dock píldora flotante con iconos SVG
inline (heroicons outline). Respeta safe-area-inset-bottom.
Notificaciones estilo Sileo:
- Componente Toaster global (React island client:idle) que escucha
CustomEvents. Toasts con backdrop-blur, animación de entrada
translateY+opacity, auto-dismiss 3.8s, click para cerrar. Helper
toastAfterReload deja pendiente en sessionStorage para sobrevivir
location.reload() post-acción.
Diagnóstico:
- /api/whoami devuelve user+profile actuales para depurar sesión.
- Endpoint devolver retorna un objeto `debug` en el 403 con
email+rol vistos por el server (temporal, para trazar bug del 403).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sin esto el middleware redirige a /login antes de entrar al handler,
haciendo que el botón "Continuar con Google" cicle contra el propio
login sin llegar nunca a Google.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
El flow OAuth de Supabase (@supabase/ssr) es PKCE por default: requiere
guardar un code_verifier en cookies del cliente ANTES de mandar al user
a Google. El login.astro anterior linkeaba directo a
supabase.buglabs.dev/auth/v1/authorize saltándose ese paso, así que el
exchangeCodeForSession del callback fallaba sin cookies válidas.
Ahora el botón "Continuar con Google" apunta a /api/auth/signin, que
usa supabase.auth.signInWithOAuth() — este método sí setea el
code_verifier en las cookies del dominio de la app, y después el
callback lo recupera para completar el intercambio.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Vite hornea import.meta.env.PUBLIC_* en build time, pero el Dockerfile
no tiene .env.production disponible durante `docker build` (correcto,
por seguridad). Cambia el server client y la página de login para leer
las vars desde process.env, que sí llega vía env_file en runtime.
El browserClient no lo usa ningún island — se mantiene con
import.meta.env para el día que se necesite.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sistema web para gestión de préstamos de material del Laboratorio de
Sistemas Computacionales de la UABC.
- Backend: Supabase self-hosted, schema aislado `prestamos` con RLS,
triggers de stock y audit log (supabase/migrations/0001_init.sql).
- Auth: Google OAuth restringido a @uabc.edu.mx, verificado en
middleware y como segunda línea en trigger de DB.
- Frontend: Astro 7 (SSR con adapter Node) + React islands + Tailwind
v4 con paleta UABC (primary #00723F, secondary #DD971A) bajo regla
60/30/10.
- Interfaz alumno mobile-first: catálogo con filtro por categorías,
solicitud de préstamos, historial personal.
- Interfaz admin desktop-first: panel con KPIs, bandeja de solicitudes
(aprobar/rechazar/devolver), CRUD de inventario y categorías,
reportes filtrables con export CSV nativo.
- Modales con `<dialog>` nativo, cero librerías de UI adicionales.
- Deploy: Dockerfile multi-stage node:22-alpine + docker-compose para
publicar bajo prestamos.buglabs.dev vía Cloudflare Tunnel.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>