Sistema de préstamos LabRe UABC — implementación inicial

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>
This commit is contained in:
2026-08-14 19:37:49 -07:00
parent 6bbd6d3a6b
commit 1121ab7199
58 changed files with 6281 additions and 252 deletions
+56
View File
@@ -0,0 +1,56 @@
---
import Layout from '@/layouts/Layout.astro';
const url = Astro.url;
const errorParam = url.searchParams.get('error');
const errorMsg = errorParam === 'dominio'
? 'Debes iniciar sesión con tu correo institucional @uabc.edu.mx'
: errorParam === 'oauth'
? 'No se pudo completar la autenticación con Google. Inténtalo de nuevo.'
: null;
const appUrl = import.meta.env.PUBLIC_APP_URL ?? url.origin;
const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL;
const redirectTo = `${appUrl}/api/auth/callback`;
const googleAuthUrl = `${supabaseUrl}/auth/v1/authorize?provider=google&redirect_to=${encodeURIComponent(redirectTo)}`;
---
<Layout title="Iniciar sesión — LabPréstamos UABC">
<main id="main" class="min-h-screen grid place-items-center p-4">
<div class="w-full max-w-md">
<div class="text-center mb-8">
<div class="inline-grid place-items-center w-16 h-16 rounded-2xl bg-[color:var(--color-primary)] text-white font-bold text-3xl mb-4 motion-safe:animate-[fadein_.4s_ease-out]">L</div>
<h1 class="text-2xl md:text-3xl font-semibold">Sistema de Préstamos</h1>
<p class="text-sm mt-2 opacity-70">Laboratorio de Sistemas Computacionales · UABC</p>
</div>
<div class="card motion-safe:animate-[fadein_.5s_ease-out]">
<h2 class="text-lg font-semibold mb-1">Inicia sesión</h2>
<p class="text-sm opacity-70 mb-6">Accede con tu cuenta institucional.</p>
<a href={googleAuthUrl} class="btn btn-primary w-full">
<svg width="18" height="18" viewBox="0 0 48 48" aria-hidden="true" focusable="false">
<path fill="#fff" d="M44.5 20H24v8.5h11.7C34.4 33.4 29.7 36 24 36c-6.6 0-12-5.4-12-12s5.4-12 12-12c2.9 0 5.5 1 7.6 2.7l6.2-6.2C33.8 5.1 29.2 3 24 3 12.4 3 3 12.4 3 24s9.4 21 21 21c11 0 20-8 20-21 0-1.4-.1-2.7-.5-4z"/>
</svg>
Continuar con Google
</a>
{errorMsg && (
<div role="alert" class="mt-4 rounded-lg p-3 text-sm" style="background: color-mix(in oklab, var(--color-danger) 12%, transparent); color: var(--color-danger);">
{errorMsg}
</div>
)}
</div>
<p class="text-center text-xs opacity-60 mt-6">
Acceso exclusivo con correo institucional <strong>@uabc.edu.mx</strong>
</p>
</div>
</main>
</Layout>
<style is:global>
@keyframes fadein {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
</style>