import { useEffect, useRef, useState } from 'react'; import { toast, toastAfterReload } from '@/lib/toast'; type Unidad = { id: number; etiqueta: string }; type Item = { id: number; cantidad: number; descripcion: string | null; material: { id: number; nombre: string; numero_inventario: string | null; trackeado_por_unidad?: boolean } | null; material_unidad: Unidad | null; }; type Detalles = { prestamo: { id: number; estado: string; fecha_solicitud: string; fecha_aprobacion: string | null; fecha_devolucion_estimada: string | null; fecha_devolucion_real: string | null; notas: string | null; maestro_responsable: string; alumno: { nombre: string | null; email: string; matricula: string | null } | null; items: Item[]; }; audit_log: Array>; }; const fmt = new Intl.DateTimeFormat('es-MX', { dateStyle: 'medium', timeStyle: 'short' }); const ERROR_TXT: Record = { no_autorizado: 'No autorizado', item_no_existe: 'El renglón ya no existe', solicitud_no_activa: 'La solicitud no está activa', item_sin_unidad: 'Este renglón no tiene unidad asignada', unidad_no_existe: 'La unidad seleccionada no existe', unidad_de_otro_material: 'La unidad no corresponde a este material', unidad_no_disponible: 'La unidad ya no está disponible', }; function ReasignarUnidad({ solicitudId, item, }: { solicitudId: number; item: Item; }) { const [open, setOpen] = useState(false); const [unidades, setUnidades] = useState([]); const [loading, setLoading] = useState(false); const [selected, setSelected] = useState(''); const [saving, setSaving] = useState(false); const abrir = async () => { setOpen(true); if (unidades.length || !item.material) return; setLoading(true); try { const r = await fetch(`/api/admin/materiales/${item.material.id}/unidades?estado=disponible`); const j = await r.json(); if (!r.ok) throw new Error(j?.error ?? 'Error'); setUnidades(j.unidades ?? []); } catch (e) { toast({ kind: 'error', title: 'No se pudieron cargar unidades', description: e instanceof Error ? e.message : '' }); } finally { setLoading(false); } }; const confirmar = async () => { if (!selected || saving) return; setSaving(true); try { const r = await fetch(`/api/admin/solicitudes/${solicitudId}/reasignar-unidad`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ item_id: item.id, nueva_unidad_id: Number(selected) }), }); const j = await r.json().catch(() => ({})); if (!r.ok) { const key = String(j?.error ?? ''); toast({ kind: 'error', title: ERROR_TXT[key] ?? 'No se pudo reasignar' }); setSaving(false); return; } toastAfterReload({ kind: 'success', title: 'Unidad reasignada' }); location.reload(); } catch { toast({ kind: 'error', title: 'Error inesperado' }); setSaving(false); } }; if (!open) { return ( ); } return (
); } export default function VerDetalles({ prestamoId }: { prestamoId: number }) { const ref = useRef(null); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { const d = ref.current; if (!d) return; const onClick = (e: MouseEvent) => { if (e.target === d) d.close(); }; d.addEventListener('click', onClick); return () => d.removeEventListener('click', onClick); }, []); const open = async () => { ref.current?.showModal(); if (data) return; setLoading(true); setError(null); try { const res = await fetch(`/api/admin/solicitudes/${prestamoId}/detalles`); if (!res.ok) throw new Error(`Error ${res.status}`); setData(await res.json()); } catch (err: any) { setError(err.message ?? 'No se pudo cargar el detalle.'); } finally { setLoading(false); } }; const canReasignar = data && ['aprobado', 'activo'].includes(data.prestamo.estado); const getChipCls = (st: string) => { const map: Record = { pendiente: 'chip-pendiente', aprobado: 'chip-aprobado', activo: 'chip-activo', devuelto: 'chip-devuelto', rechazado: 'chip-rechazado', vencido: 'chip-vencido', }; return map[st] ?? 'chip-pendiente'; }; return ( <>

Detalle del préstamo

{loading &&

Cargando…

} {error && (
{error}
)} {data && ( <>
Alumno
{data.prestamo.alumno?.nombre ?? data.prestamo.alumno?.email}
Maestro
{data.prestamo.maestro_responsable}
Materiales
    {data.prestamo.items.map((i) => (
  • {i.cantidad}×{' '} {i.material?.nombre ?? '—'} Inv. {i.material?.numero_inventario ?? '—'}
    {canReasignar && i.material_unidad && ( )}
    {i.material_unidad?.etiqueta && (
    Unidad: {i.material_unidad.etiqueta}
    )} {i.descripcion &&
    {i.descripcion}
    }
  • ))}
Solicitado
{fmt.format(new Date(data.prestamo.fecha_solicitud))}
{data.prestamo.fecha_aprobacion && (<>
Aprobado
{fmt.format(new Date(data.prestamo.fecha_aprobacion))}
)} {data.prestamo.fecha_devolucion_estimada && (<>
Devolver antes
{fmt.format(new Date(data.prestamo.fecha_devolucion_estimada + 'T00:00:00'))}
)} {data.prestamo.notas && (<>
Notas
{data.prestamo.notas}
)}

Historial de cambios

{data.audit_log.length === 0 ? (

Sin cambios registrados.

) : (
{data.audit_log.map((e, i) => { const fecha = e.at ?? e.created_at ?? e.fecha ?? e.timestamp; const estadoNuevo = e.estado_nuevo; const estadoAnt = e.estado_anterior; return (
{e.accion ?? 'Cambio'} {fecha && {fmt.format(new Date(fecha))}}
{(estadoAnt || estadoNuevo) && (
{estadoAnt && ( <> {estadoAnt} → )} {estadoNuevo && ( {estadoNuevo} )}
)}
); })}
)} )}
); }