## Development When starting the dev server, use background mode: ``` astro dev --background ``` Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`. ## Documentation Full documentation: https://docs.astro.build Consult these guides before working on related tasks: - [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/) - [Working with Astro components](https://docs.astro.build/en/basics/astro-components/) - [Using React, Vue, Svelte, or other framework components](https://docs.astro.build/en/guides/framework-components/) - [Adding or managing content](https://docs.astro.build/en/guides/content-collections/) - [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/) - [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/) ## Proceso de trabajo de este proyecto Al final de cada prompt del usuario, actualizar la sección **Bitácora** de este archivo con lo que se hizo, decisiones tomadas y qué sigue. No crear archivos de plan aparte: este CLAUDE.md es la única fuente de verdad del progreso. Antes de ejecutar cambios de infraestructura (SSH a buglabs, docker, Cloudflare tunnel, DB) pedir confirmación al usuario si el cambio es destructivo o afecta a otros proyectos que viven en el mismo servidor (genqbar, gitea, etc). Cuando el trabajo se pueda dividir en partes que no toquen los mismos archivos, lanzar agentes en paralelo (Agent tool) en vez de hacerlo secuencial. --- # Proyecto: Sistema de Préstamos — Laboratorio de Sistemas Computacionales UABC Sistema web para gestionar préstamos de material del laboratorio. Dos roles: **alumno** (solicita material) y **admin** (gestiona solicitudes, inventario y reportes). Login exclusivo con Google restringido a correo institucional `@uabc.edu.mx`. ## Decisiones de arquitectura | Decisión | Elegido | Motivo | |---|---|---| | Backend | Supabase **self-hosted existente** en buglabs (`supabase.buglabs.dev`) | Ya está corriendo (Postgres 15, Auth, Kong, Studio) — no se despliega uno nuevo. Compartido con otro proyecto (genqbar). | | Aislamiento de datos | Schema Postgres propio: **`prestamos`** | `public` ya lo usa genqbar (tablas `profiles`, `edificios`, `eventos`, etc). Evita colisión de nombres. | | Auth | Supabase Auth + proveedor Google OAuth (a configurar, hoy no está activo en la instancia) | Auth es compartido entre apps del mismo Supabase; el proveedor Google se habilita una vez a nivel instancia. | | Restricción de dominio `@uabc.edu.mx` | **A nivel aplicación** (middleware de Astro tras login), no a nivel GoTrue | GoTrue es compartido con otras apps del servidor; no se puede bloquear el signup global sin afectar a genqbar. | | Alta de admin inicial | Manual vía Supabase Studio, después del primer login | Decisión del usuario — no se hardcodea ningún correo admin. | | Frontend interactivo | Astro + **React** (islands) | Ya es un proyecto Astro; React da el ecosistema más grande para tablas/formularios/export. | | Hosting de la app | Servidor propio (buglabs), adapter **`@astrojs/node`** | Mismo homelab que Supabase; se agrega contenedor + ruta en el túnel de Cloudflare existente. | | Subdominio | **prestamos.buglabs.dev** | Se añade al mismo túnel Cloudflare (`3de17b3c-...`) junto a supabase/git/status/genqbar. | | Exportar reportes | CSV nativo (sin librería) para v1 | Cero dependencias nuevas; se sube a PDF/Excel solo si se pide explícitamente. | ## Modelo de datos (schema `prestamos`) Rediseñado 2026-08-17 para reflejar el vale de préstamo físico del LSC (un trámite agrupa varios materiales, no uno solo — ver Bitácora del 2026-08-17 para el detalle completo). - `profiles` — id (=auth.users.id), email, nombre, matricula, semestre (nullable, sin UI aún — ver backlog), rol (`alumno`|`admin`), created_at - `categorias` — id, nombre - `materiales` — id, nombre, categoria_id, descripcion, cantidad_total, cantidad_disponible, numero_inventario, estado (`disponible`|`mantenimiento`|`baja`) - `solicitudes` (cabecera del vale, antes se llamaba `prestamos`) — id, alumno_id, maestro_responsable, estado (`pendiente`|`aprobado`|`rechazado`|`activo`|`devuelto`|`vencido`), fecha_solicitud, fecha_aprobacion, fecha_devolucion_estimada, fecha_devolucion_real, aprobado_por, notas (motivo del préstamo) - `solicitud_items` (renglones del vale) — id, solicitud_id, material_id, cantidad, descripcion (nota libre por renglón) Alta de solicitudes: solo vía RPC `prestamos.crear_solicitud(maestro_responsable, notas, items jsonb)` (transaccional, valida stock con lock por fila) — el alumno ya no puede insertar directo en `solicitudes` (RLS lo bloquea). RLS: alumno solo ve/edita sus propias solicitudes/renglones; admin ve y gestiona todo. Enforced con `auth.uid()` contra `profiles.id` y policies por rol. ## Fases de implementación 1. **Backend Supabase** — schema `prestamos`, tablas, RLS, trigger `handle_new_user`, seed de categorías. (bloqueante, va primero) 2. **Scaffolding Astro** — integración React, cliente Supabase, middleware de auth + verificación de dominio/rol, layout base. 3. **Interfaz alumno** — catálogo de materiales, solicitar préstamo, ver mis préstamos y su estado. 4. **Interfaz admin — solicitudes** — bandeja de solicitudes (aprobar/rechazar), marcar devoluciones. 5. **Interfaz admin — inventario** — alta/baja/edición de materiales y categorías. 6. **Reportes** — filtros (fecha, material, alumno, estado), export CSV, vista de vencidos. 7. **Deploy** — Dockerfile + compose para el adapter Node, ruta `prestamos.buglabs.dev` en el túnel de Cloudflare de buglabs. 8. **QA end-to-end** — flujo real de login + alumno + admin en producción. Fases 3, 4/5 y 6 tocan carpetas de rutas distintas (`src/pages/alumno/*`, `src/pages/admin/*`) y pueden correr como agentes en paralelo una vez completa la fase 2. Fase 7 (infra) también es independiente del código de UI y puede prepararse en paralelo. ## Bitácora - **2026-08-13** — Plan inicial definido. Se hizo reconocimiento por SSH del homelab buglabs: Supabase self-hosted ya corre ahí (compartido con proyecto "genqbar" en schema `public`), túnel Cloudflare ya gestiona varios subdominios de buglabs.dev, Docker/Compose disponibles. Decisiones de arquitectura tomadas junto con el usuario (ver tabla arriba). Pendiente: confirmación del usuario para arrancar Fase 1 (backend Supabase). - **2026-08-14** — Plan refinado y aprobado. Ajustes: (1) schema `public.profiles` NO se reutiliza — el sistema de préstamos usa `prestamos.profiles` propio y aislado; (2) sí se agrega `prestamos.audit_log` con trigger para trazabilidad de cambios de estado; (3) v1 sin reglas duras de límite/duración (admin decide caso por caso). Se corrigió la identificación del vecino que usa el schema `public`: no es genqbar (genqbar es un estático sin auth), es otra app de check-in por QR corriendo en el 8080. Google OAuth se confirmó ya habilitado en la instancia de Supabase (`GOOGLE_ENABLED=true`, con CLIENT_ID/SECRET presentes en `~/supabase/docker/.env`), no requiere alta nueva. Se añadieron normas de diseño al plan: paleta UABC (primario `#00723F` verde, secundario `#DD971A` dorado, terciario `#F4F7F5`, neutral `#2D3748`) bajo regla 60/30/10, admin=desktop-first y alumno=mobile-first pero ambos cross-device, minimalista con animaciones ligeras, Inter Variable como tipografía, WCAG AA no negociable. Plan guardado en `~/.claude/plans/inicia-con-la-faze-modular-reef.md`. - **2026-08-14 — Fase 1 completada (Backend Supabase)**. - Escrita migración `supabase/migrations/0001_init.sql` con: schema `prestamos`; tablas `profiles, categorias, materiales, prestamos, audit_log`; función helper `prestamos.is_admin()`; trigger `prestamos_on_auth_user_created` en `auth.users` que rechaza emails no `@uabc.edu.mx` (segunda línea de defensa) y crea el perfil; triggers `prestamos_sync_stock` (ajusta `cantidad_disponible` según transiciones de estado) y `prestamos_log_estado` (registra cambios en `audit_log`); 11 policies RLS distribuidas en las 5 tablas usando `is_admin()`; seed de 6 categorías. - Aplicada por `psql` sobre la instancia de buglabs. Verificado: 5 tablas creadas, 11 policies activas, 3 triggers propios registrados, 6 categorías seed presentes. - Editado `~/supabase/docker/.env` en buglabs (backup previo con timestamp): `PGRST_DB_SCHEMAS` incluye ahora `prestamos`, `ADDITIONAL_REDIRECT_URLS` incluye `https://prestamos.buglabs.dev/**`. - Recreados contenedores `supabase-rest` y `supabase-auth` con `docker compose up -d` (un simple `restart` no relee `.env`; ese fue un aprendizaje del proceso). Downtime real: ~5s. - Corrección aplicada tras primer curl de verificación: faltaba `grant` a `service_role`. Se añadió `grant ... to service_role` + `alter default privileges` (tanto en la DB como en el archivo de migración, para que sea idempotente si se re-aplica). Verificado con `curl` que `GET /rest/v1/categorias` con `Accept-Profile: prestamos` retorna las 6 categorías. - Estado: backend listo para consumir desde la app. Próximo paso: Fase 2 (scaffolding Astro + React + Tailwind + adapter Node + cliente Supabase SSR + middleware de auth con verificación de dominio). - **2026-08-14 — Fase 2 completada (Scaffolding Astro)**. - Instaladas integraciones vía `npx astro add react node tailwind --yes`. Añadido a `package.json`: `@astrojs/react`, `@astrojs/node`, `tailwindcss` (v4, sin config JS — configura en CSS con `@theme`), `react`, `react-dom`. Adaptador Node en modo `standalone`. - Añadidas deps runtime: `@supabase/supabase-js`, `@supabase/ssr`, `@fontsource-variable/inter`. - `astro.config.mjs`: se explicitó `output: 'server'` (sin esto, Astro intentaba prerenderizar rutas SSR y el cliente Supabase reventaba). Integraciones: React + Tailwind v4 (vía `@tailwindcss/vite`). - `tsconfig.json`: añadido `baseUrl: '.'` + `paths: { "@/*": ["src/*"] }` para import alias. - `src/styles/global.css`: paleta UABC como tokens Tailwind v4 (`@theme` con `--color-primary #00723F, --color-secondary #DD971A, --color-surface #F4F7F5, --color-ink #2D3748, --color-danger #C53030`), Inter Variable como `--font-sans`, componentes `.btn`, `.btn-primary`, `.btn-secondary`, `.btn-ghost`, `.card`, `.input`, `.label`. Tap targets ≥44px. `touch-action: manipulation` y `-webkit-tap-highlight-color: transparent` en botones. `text-wrap: balance/pretty` en tipografía. - `src/lib/supabase.ts`: helpers `serverClient(cookies)` y `browserClient()`. Ambos apuntan a `db: { schema: 'prestamos' }` para que las queries no necesiten prefijo. Cookies con `httpOnly`, `sameSite: 'lax'`, `secure` solo en prod. Usa el patrón `get/set/remove` (el `getAll/setAll` moderno de `@supabase/ssr` no funcionó con `AstroCookies` de Astro 7 — el método `.getAll()` no existe ahí). - `src/env.d.ts`: tipos para `App.Locals` (`supabase`, `user`, `profile`) y `ImportMetaEnv` (`PUBLIC_SUPABASE_URL`, `PUBLIC_SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `PUBLIC_APP_URL`). - `src/middleware.ts`: instancia `serverClient`, obtiene user, verifica dominio `@uabc.edu.mx` (rechaza + redirect `/login?error=dominio`), carga `profile` desde `prestamos.profiles`, protege rutas privadas (redirect a `/login` si no hay user; 403 en `/admin/*` si no es admin), redirige a `/` si un user logueado visita `/login`. - `src/pages/login.astro`: pantalla completa aparte, tarjeta centrada con logo, `

Sistema de Préstamos

`, `` Google (verde primario) que arma `authorize` URL de Supabase, alerta accesible si `?error=dominio|oauth`, nota de dominio institucional al pie. Animación entrada solo detrás de `motion-safe:`. - `src/pages/api/auth/callback.ts`: intercambia `code` por sesión, verifica dominio por segunda vez y redirige a `/` (o a `/login?error=…` según el caso). - `src/pages/api/auth/signout.ts`: POST → `signOut` → redirect `/login`. - `src/layouts/Layout.astro`: HTML base (`lang="es"`, `meta viewport`, `meta theme-color=#00723F`, `color-scheme: light`), incluye skip-to-content link con `focus:not-sr-only`. `title` prop. - `src/layouts/AppLayout.astro`: sidebar `bg-primary` con logo enlazado a `/`, nav responsive (sidebar en desktop, topbar en mobile), items admin vs alumno según `rol`, botón signout desktop full + icon-only en mobile con `aria-label`. - `src/pages/index.astro`: dashboard con tarjetas según rol (alumno: catálogo + mis préstamos; admin: solicitudes + inventario + reportes). Placeholders navegables — sus destinos se implementarán en Fases 3–6. - `.env` local creado (obtenidas ANON y SERVICE_ROLE keys por SSH de buglabs). `.env.example` versionado con placeholders. `.env*` ya estaba en `.gitignore`. - Borrado `src/components/Welcome.astro` y `src/assets/*` del starter, `Layout.astro` reescrito para nuestros propósitos. - Auditoría de UI con skill `web-design-guidelines` (checklist Vercel Labs): resueltos 6 hallazgos accionables — `transition:all` reemplazado por transiciones específicas por propiedad; `role="button"` sobrante en `` de Google removido; `theme-color` y `color-scheme` metas añadidos; `touch-action: manipulation` en interactivos; logo mobile con `aria-label` sobre link y letra `aria-hidden`; `text-wrap: balance/pretty` en tipografía. - Verificación: `npm run build` sin errores. `npm run dev` (background) → `GET /` sin sesión redirige `/login` (200), `/login` renderiza copy correcto, `?error=dominio` muestra la alerta esperada. - Aprendizajes registrados: (a) Tailwind v4 requiere `@reference "tailwindcss"` dentro de `