Rediseño visual UABC + Mulish + auditoría de BD (P0/P1)

Rediseño visual:
- Nueva identidad UABC/editorial (DESIGN.md), tipografía Mulish, tokens
  verde/dorado/negro sobre bone-white, patrón de puntos verde en canvas,
  radios 8/12/30/9999, sin sombras, hairline 1px, tarjetas con leve
  transparencia + blur suave + borde verde muy sutil.
- 45 archivos .astro/.tsx alineados al nuevo sistema (Track A/B/C/D/E).
- Limpieza post-critique: eliminado triple CTA en home alumno, .card
  anidados en admin, aria-live decorativo, 26 aliases legacy en global.css,
  border-l verde en nav (patrón AI-slop), classList.remove obsoletos.

Auditoría de llamadas a BD (para 20-50 usuarios concurrentes):
- P0-1: middleware con cache in-memory de profile (30s) + is_banned (30s)
  por uid; exclusión de assets estáticos (/_astro/*, /audio/*, favicon).
  Invalidación en signout, PATCH /profile, baneos.
- P0-2: eliminado count pendientes duplicado en AppLayout; badge se
  hidrata client-side (BadgeSolicitudes con fetch inicial).
- P0-3: migración 0008 REPLICA IDENTITY FULL en solicitudes + delta
  preciso en BadgeSolicitudes (con fallback a refetch).
- P1: admin/index dedupe (4 queries → 2 con recientes.filter), catalogo
  reusa profile del middleware (3 queries → 1), inventario paralelo,
  estadisticas con cache 60s en RAM (stats-cache.ts), migración 0009
  con 3 índices (fecha, vencidos parcial, audit compuesto).

Fixes P0 de UI post-critique:
- estadisticas.astro: ReferenceError DIAS restaurado.
- solicitudes (3 vistas): error rendering sanitizado (no expone mensaje
  Postgres crudo al admin).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014xaxs8cGcQe9NwbMqA1gvn
This commit is contained in:
2026-09-21 09:42:12 -07:00
parent a41d50ca62
commit ac3e7061ac
53 changed files with 2132 additions and 1576 deletions
+10 -9
View File
@@ -31,22 +31,23 @@ const info = avatarInfo({
});
---
<AppLayout title="Mi perfil — LabPréstamos">
<div class="max-w-md mx-auto">
<header class="flex items-center gap-4 mb-6">
<Avatar client:load info={info} size={80} />
<div class="min-w-0">
<h1 class="text-xl md:text-2xl font-semibold uppercase tracking-wide leading-tight truncate">
<div class="max-w-xl mx-auto space-y-6">
<header class="card p-6 flex items-center gap-5">
<Avatar client:load info={info} size={80} className="w-20 h-20 rounded-full" />
<div class="min-w-0 flex-1">
<span class="eyebrow mb-1">Mi perfil</span>
<h1 class="text-2xl md:text-3xl leading-tight truncate">
{profile.nombre ?? profile.email.split('@')[0]}
</h1>
<p class="text-sm truncate" style="color: var(--color-pencil);">{profile.email}</p>
<span class="badge mt-2" style={`background: ${profile.rol === 'admin' ? 'var(--color-secondary)' : 'var(--color-chalk)'};`}>
<p class="text-sm text-graphite/70 truncate">{profile.email}</p>
<span class="badge mt-2">
{rolLabel}
</span>
</div>
</header>
<section class="card-raised">
<h2 class="text-sm uppercase tracking-wide font-semibold mb-4">Datos del perfil</h2>
<section class="card p-6">
<h2 class="text-xl md:text-2xl mb-4">Datos del perfil</h2>
<PerfilForm
client:load
userId={user.id}