Commit Graph

5 Commits

Author SHA1 Message Date
LakG dea268d975 v1.7: overlay carga, baneos, carrito persistente, fix upload
Lista negra:
- Migración 0007: tabla baneos con historial (soporta expires_at futuro)
- Helper is_banned() usado por middleware
- Middleware guard: user baneado → rewrite /banned (admin exento)
- Página /banned con razón/fecha/expires + botón signout
- Panel /admin/usuarios con banear/desbanear (razón obligatoria, self-ban bloqueado)
- Nav admin nueva entrada Usuarios + icono users

Fix upload de fotos (bug definitivo):
- Root cause: browserClient no autentica al Storage (cookies httpOnly no legibles desde JS)
- Nuevos endpoints proxy /api/upload/material-foto/[id] y /avatar
- Validan sesión con cookie httpOnly, suben con service_role (bypass RLS)
- Refactor MaterialForm y PerfilForm para usar fetch multipart
- Cookies httpOnly intactas (cero riesgo XSS)

Overlay de carga:
- Layout.astro con #page-loader (backdrop-filter blur + spinner mono)
- Script inline captura clicks en <a href> y submits de <form> mismo origen
- Filtros: modifier keys, target=_blank, anchor#, cross-origin, defaultPrevented
- Respeta prefers-reduced-motion; pageshow limpia por bfcache

Carrito persistente + vaciar:
- SolicitudCart: useEffect hidrata/persiste labre:cart:v1 en localStorage
- Guard cartHydrated evita pisar en montaje inicial
- Botón "Vaciar" en checkout con window.confirm (solo con perfilCompleto)

Filtro inventario sin auto-submit:
- Removido script debounced que hacía form.submit() en cada tecla
- Botón "Filtrar" existente sigue funcionando
2026-08-27 10:18:04 -07:00
LakG 0f2ea59754 v1.6: rol docente, combobox maestros, sonido notif, fecha 1 día, wrap admin
Rol docente:
- profiles.rol acepta 'alumno'|'docente'|'admin' (migración 0006)
- Docente en checkout: no elige maestro; RPC usa su propio nombre como maestro_responsable
- Perfil/onboarding condicionales por rol: docente sin semestre ni tutor, label matrícula → "Número de empleado"
- catalogo.astro: perfilCompleto para docente solo requiere matrícula

Combobox maestros + separar tutores:
- maestros.es_tutor bool: subset marcado que aparece en el select de tutor del perfil
- MaestroForm: nuevo checkbox "Es tutor"; endpoints POST/PATCH aceptan es_tutor
- admin/maestros.astro: columna Tutor con SVG check
- perfil/onboarding queries filtran es_tutor=true, activo=true
- Checkout SolicitudCart: <select> combobox de maestros activos (reemplaza textarea);
  option vacía usa el tutor por defecto; endpoint recibe maestro_id numeric

Fecha default 1 día:
- AccionesSolicitud: enDias(7) → enDias(1) por default al aprobar

Sonido notificación:
- Movido src/audio/sonido_notificacion.mp3 → public/audio/
- BadgeSolicitudes: new Audio('/audio/sonido_notificacion.mp3').play() en INSERT

Issues:
- Límite motivo 250 chars (era 500) en checkout + backend
- break-words en Maestro/Motivo de las 3 vistas admin y VerDetalles
2026-08-25 11:37:26 -07:00
LakG e159e8d297 Perfil + onboarding + home reformulado + fixes
Perfil de usuario:
- /perfil con avatar, matrícula, semestre, tutor, foto de perfil
- /onboarding opcional (skippeable) para primer login
- Avatar con prioridad: foto propia > Google OAuth > iniciales sobre color HSL
- Endpoint PATCH /api/profile con validación y guard self-only
- Middleware carga semestre, tutor_id, foto_path
- Sidebar footer con Avatar + link a perfil

CRUD admin de maestros:
- /admin/maestros con tabla desktop / cards mobile
- MaestroForm + EliminarMaestro (mismo patrón que categorias)
- Endpoints POST/PATCH/DELETE con guard admin + 23503 traducido
- Subnav de inventario ahora incluye tab Maestros

Home reformulado:
- Alumno: saludo por hora + CTA "¿Qué vas a pedir hoy?" + card de último préstamo activo
- Admin: saludo + 2 KPIs de alerta (Pendientes, Vencidos) + últimas 2 pendientes + actividad reciente (tabla desktop, cards mobile — fix del bug 6)
- Helper saludoHora() y fmtFechaRelativa() en date.ts con offset MX

Checkout con tutor auto:
- SolicitudCart: input maestro → textarea con placeholder = nombre del tutor
- Guard perfil incompleto: CTA "Completa tu perfil" si !matricula || !tutor_id
- RPC crear_solicitud: fallback automático al nombre del tutor si textarea vacío

Fixes:
- Bug upload imagen: Dockerfile ARG + docker-compose build.args pasan PUBLIC_* al bundle client de Vite
- Grid inventario mobile: acciones en grid 2-col con prop compact en botones
- UnidadesManager mobile: tabla reemplazada por cards, select estado con min-width 130px
- Chart donut: Legend horizontal debajo (sin labels internos cortadas)
- Chart barras: YAxis width fijo 100 + tickFormatter que trunca >14 chars
- Panel admin mobile: tabla ahora tiene versión cards mobile (parte del rediseño home)
2026-08-24 08:46:39 -07:00
LakG 544bbf38a9 5 features: buscador catálogo, grid+fotos+unidades, estadísticas separada, badge realtime
Buscador catálogo alumno:
- BuscadorCatalogo.tsx con debounce 120ms, normaliza tildes, ?q= en URL
- FiltroCategorias usa data-* attributes, coordinación via window.__labreFiltrar
- Cards con data-nombre y data-numero-inventario para filtro combinable AND

Grid + imágenes + unidades individuales (admin):
- Inventario ahora es grid 2/3/4/5 col con fotos aspect-square
- MaterialForm: input file con preview, checkbox trackeado_por_unidad
- UnidadesManager: agregar/editar/eliminar unidades por material
- Reasignar unidad desde detalles de solicitud (aprobada/activa)
- Alumno ve etiqueta de unidad asignada en mis-préstamos
- Helper materialImg.imgUrl(path) para URL pública del bucket
- Auto-submit debounced del filtro de inventario (input + selects)

Estadísticas separada:
- Nueva ruta /admin/estadisticas con 6 KPIs (movidos de /admin/inventario)
- 3 gráficas recharts: top 10 materiales, solicitudes/día, distribución estados
- Chart.tsx wrapper con paleta UABC + respeta prefers-reduced-motion
- Nav admin: Panel · Solicitudes · Inventario (box) · Estadísticas (stats) · Reportes
- admin/index.astro usa todayMX() (fix inconsistencia)

Notificaciones pendientes (admin):
- Badge SSR con conteo de pendientes sobre ícono Solicitudes (sidebar + dock)
- BadgeSolicitudes.tsx: canal Realtime en prestamos.solicitudes, +1 en INSERT
  con toast, refetch en UPDATE (replica identity default no trae old.estado)
- Recharts añadido a package.json
2026-08-22 18:03:38 -07:00
LakG 81a5ea016e Rediseño: vale de préstamo multi-ítem, fiel al formulario de papel
El vale físico del LSC permite pedir varios materiales en un solo
trámite; el sistema modelaba 1 solicitud = 1 material. Migra
prestamos.prestamos -> solicitudes (cabecera) + solicitud_items
(renglones), vía RENAME + backfill (preserva ids/historial real).

- RPC prestamos.crear_solicitud: transaccional, lockea materiales
  por fila, arregla la race condition del insert directo anterior.
  El alumno ya no inserta directo (RLS lo bloquea).
- sync_stock/log_estado reescritos para iterar renglones por vale.
- maestro_responsable (por vale) y profiles.semestre (perfil,
  nullable, sin UI todavía — onboarding queda para después).
- Alumno: carrito (SolicitudCart/AgregarMaterial) reemplaza el
  modal de solicitud único por material.
- Admin: 3 vistas de solicitudes, reportes y CSV export listan
  renglones por vale; api/admin/prestamos -> api/admin/solicitudes.
  De paso corrige un bug preexistente en detalles.ts (audit_log
  nunca se ordenaba por la columna correcta).

Verificado: build limpio, ciclo RPC+triggers probado en una
transacción revertida en prod (sin residuo), 9 vistas SSR
smoke-testeadas contra el schema real.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 08:37:44 -07:00