--- import AppLayout from '@/layouts/AppLayout.astro'; import MaterialForm from '@/components/admin/inventario/MaterialForm.tsx'; import EliminarMaterial from '@/components/admin/inventario/EliminarMaterial.tsx'; import { todayMX } from '@/lib/date'; type Material = { id: number; nombre: string; descripcion: string | null; cantidad_total: number; cantidad_disponible: number; numero_inventario: string | null; estado: 'disponible' | 'mantenimiento' | 'baja'; categoria: { id: number; nombre: string } | null; }; // KPIs del día (fecha automática, timezone MX con DST) const dia = todayMX(); const supabase = Astro.locals.supabase; const countHead = { count: 'exact' as const, head: true }; const startISO = dia.startUTC.toISOString(); const endISO = dia.endUTC.toISOString(); const [ { count: nSolicitadas }, { count: nAprobadas }, { count: nRechazadas }, { count: nDevueltas }, { count: nEnPrestamo }, { count: nVencidos }, ] = await Promise.all([ supabase.from('prestamos').select('id', countHead).gte('fecha_solicitud', startISO).lt('fecha_solicitud', endISO), supabase.from('audit_log').select('id', countHead).eq('estado_nuevo', 'aprobado').gte('at', startISO).lt('at', endISO), supabase.from('audit_log').select('id', countHead).eq('estado_nuevo', 'rechazado').gte('at', startISO).lt('at', endISO), supabase.from('audit_log').select('id', countHead).eq('estado_nuevo', 'devuelto').gte('at', startISO).lt('at', endISO), supabase.from('prestamos').select('id', countHead).in('estado', ['aprobado', 'activo']), supabase.from('prestamos').select('id', countHead).in('estado', ['aprobado', 'activo']).lt('fecha_devolucion_estimada', dia.isoDate), ]); const kpis = [ { label: 'Solicitadas', value: nSolicitadas ?? 0, tone: 'primary' }, { label: 'Aprobadas', value: nAprobadas ?? 0, tone: 'primary' }, { label: 'Rechazadas', value: nRechazadas ?? 0, tone: 'neutral' }, { label: 'Devoluciones', value: nDevueltas ?? 0, tone: 'primary' }, { label: 'En préstamo', value: nEnPrestamo ?? 0, tone: 'secondary', total: true }, { label: 'Vencidos', value: nVencidos ?? 0, tone: (nVencidos ?? 0) > 0 ? 'danger' : 'neutral', total: true }, ]; const toneStyle = (tone: string) => { if (tone === 'primary') return 'color: var(--color-primary);'; if (tone === 'secondary') return 'color: var(--color-secondary-hover);'; if (tone === 'danger') return 'color: var(--color-danger);'; return 'color: color-mix(in oklab, var(--color-ink) 65%, transparent);'; }; const q = (Astro.url.searchParams.get('q') ?? '').trim(); const catRaw = Astro.url.searchParams.get('cat') ?? ''; const catId = catRaw && /^\d+$/.test(catRaw) ? Number(catRaw) : null; const estadoRaw = Astro.url.searchParams.get('estado') ?? ''; const estado = ['disponible', 'mantenimiento', 'baja'].includes(estadoRaw) ? estadoRaw : ''; let query = Astro.locals.supabase .from('materiales') .select( 'id, nombre, descripcion, cantidad_total, cantidad_disponible, numero_inventario, estado, categoria:categorias(id, nombre)', ); if (q) query = query.or(`nombre.ilike.%${q}%,numero_inventario.ilike.%${q}%`); if (catId != null) query = query.eq('categoria_id', catId); if (estado) query = query.eq('estado', estado); const { data: matData, error: matError } = await query.order('nombre'); const materiales = (matData ?? []) as unknown as Material[]; const { data: catData } = await Astro.locals.supabase .from('categorias') .select('id, nombre') .order('nombre'); const categorias = (catData ?? []) as { id: number; nombre: string }[]; const estadoBadge = (e: Material['estado']) => { if (e === 'disponible') return { label: 'Disponible', style: 'background: color-mix(in oklab, var(--color-primary) 12%, transparent); color: var(--color-primary);', }; if (e === 'mantenimiento') return { label: 'Mantenimiento', style: 'background: color-mix(in oklab, var(--color-secondary) 15%, transparent); color: var(--color-secondary-hover);', }; return { label: 'Baja', style: 'background: color-mix(in oklab, var(--color-ink) 8%, transparent); color: color-mix(in oklab, var(--color-ink) 60%, transparent);', }; }; ---

Estadísticas

Actividad del día ·

{kpis.map((k) => (
{k.label}{k.total ? ' (total)' : ' hoy'}
{k.value}
))}

Inventario

Materiales del laboratorio.

{matError && ( )} {!matError && materiales.length === 0 && (

Sin resultados

{q || catId != null || estado ? 'Ningún material coincide con los filtros. Ajusta la búsqueda.' : 'Aún no hay material registrado. Agrega uno para comenzar.'}

)} {!matError && materiales.length > 0 && ( <>
{materiales.map((m) => { const badge = estadoBadge(m.estado); return (

{m.nombre}

{m.categoria && (

{m.categoria.nombre}

)}
{badge.label}
{m.descripcion &&

{m.descripcion}

}
Inventario
{m.numero_inventario ?? '—'}
Stock
{m.cantidad_disponible} / {m.cantidad_total}
); })}
)}