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
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
import AppLayout from '@/layouts/AppLayout.astro';
|
||||
import CartProvider from '@/components/alumno/SolicitudCart.tsx';
|
||||
import FiltroCategorias from '@/components/alumno/FiltroCategorias.tsx';
|
||||
import BuscadorCatalogo from '@/components/alumno/BuscadorCatalogo.tsx';
|
||||
|
||||
const qInitial = Astro.url.searchParams.get('q') ?? '';
|
||||
|
||||
type Material = {
|
||||
id: number;
|
||||
@@ -10,12 +13,13 @@ type Material = {
|
||||
cantidad_disponible: number;
|
||||
cantidad_total: number;
|
||||
numero_inventario: string | null;
|
||||
imagen_path: string | null;
|
||||
categoria: { id: number; nombre: string } | null;
|
||||
};
|
||||
|
||||
const { data, error } = await Astro.locals.supabase
|
||||
.from('materiales')
|
||||
.select('id, nombre, descripcion, cantidad_disponible, cantidad_total, numero_inventario, categoria:categorias(id, nombre)')
|
||||
.select('id, nombre, descripcion, cantidad_disponible, cantidad_total, numero_inventario, imagen_path, categoria:categorias(id, nombre)')
|
||||
.eq('estado', 'disponible')
|
||||
.order('nombre');
|
||||
|
||||
@@ -57,11 +61,12 @@ const categoriasFiltro = grupoList
|
||||
|
||||
{materiales.length > 0 && (
|
||||
<>
|
||||
{categoriasFiltro.length > 0 && (
|
||||
<div class="mb-6">
|
||||
<div class="mb-6 flex flex-col gap-3">
|
||||
<BuscadorCatalogo q={qInitial} client:load />
|
||||
{categoriasFiltro.length > 0 && (
|
||||
<FiltroCategorias categorias={categoriasFiltro} client:idle />
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p id="catalogo-empty-filter" hidden class="text-sm opacity-70 mb-4">
|
||||
Ningún material en esta categoría. Prueba con otra.
|
||||
|
||||
Reference in New Issue
Block a user