import { useEffect, useRef, useState } from 'react'; type Categoria = { id: number; nombre: string }; type Material = { id: number; nombre: string; categoria_id: number | null; descripcion: string | null; cantidad_total: number; cantidad_disponible: number; numero_inventario: string | null; estado: 'disponible' | 'mantenimiento' | 'baja'; }; type Props = { mode: 'create' | 'edit'; material?: Material; categorias: Categoria[]; }; export default function MaterialForm({ mode, material, categorias }: Props) { const dialogRef = useRef(null); const firstFieldRef = useRef(null); const errorRef = useRef(null); const [nombre, setNombre] = useState(material?.nombre ?? ''); const [categoriaId, setCategoriaId] = useState( material?.categoria_id != null ? String(material.categoria_id) : '', ); const [descripcion, setDescripcion] = useState(material?.descripcion ?? ''); const [cantidadTotal, setCantidadTotal] = useState( material ? String(material.cantidad_total) : '0', ); const [numeroInventario, setNumeroInventario] = useState(material?.numero_inventario ?? ''); const [estado, setEstado] = useState(material?.estado ?? 'disponible'); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const titleId = `mat-form-${mode}-${material?.id ?? 'new'}`; const open = () => { setError(null); if (mode === 'create') { setNombre(''); setCategoriaId(''); setDescripcion(''); setCantidadTotal('0'); setNumeroInventario(''); setEstado('disponible'); } dialogRef.current?.showModal(); queueMicrotask(() => firstFieldRef.current?.focus()); }; const close = () => { if (loading) return; dialogRef.current?.close(); }; useEffect(() => { const dlg = dialogRef.current; if (!dlg) return; const onClick = (e: MouseEvent) => { if (e.target === dlg) close(); }; dlg.addEventListener('click', onClick); return () => dlg.removeEventListener('click', onClick); }); useEffect(() => { if (error) { errorRef.current?.focus(); } }, [error]); const submit = async (e: React.FormEvent) => { e.preventDefault(); if (loading) return; setLoading(true); setError(null); const payload = { nombre: nombre.trim(), categoria_id: categoriaId === '' ? null : Number(categoriaId), descripcion: descripcion.trim() || null, cantidad_total: Number(cantidadTotal), numero_inventario: numeroInventario.trim() || null, estado, }; if (!payload.nombre) { setError('El nombre es obligatorio'); setLoading(false); return; } if (!Number.isInteger(payload.cantidad_total) || payload.cantidad_total < 0) { setError('La cantidad total debe ser un entero mayor o igual a 0'); setLoading(false); return; } const url = mode === 'create' ? '/api/admin/materiales' : `/api/admin/materiales/${material!.id}`; const method = mode === 'create' ? 'POST' : 'PATCH'; try { const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const json = await res.json().catch(() => ({})); if (!res.ok) throw new Error(json?.error ?? 'No se pudo guardar'); dialogRef.current?.close(); location.reload(); } catch (err) { setError(err instanceof Error ? err.message : 'Error inesperado'); } finally { setLoading(false); } }; const triggerLabel = mode === 'create' ? 'Nuevo material' : 'Editar'; const triggerClass = mode === 'create' ? 'btn btn-primary' : 'btn btn-ghost'; return ( <>

{mode === 'create' ? 'Nuevo material' : `Editar: ${material?.nombre}`}

setNombre(e.target.value)} required autoComplete="off" aria-invalid={error ? true : undefined} aria-describedby={error ? `${titleId}-err` : undefined} />
setNumeroInventario(e.target.value)} autoComplete="off" placeholder="Opcional…" />