Files
dh-inspeccion-v2/web-v2/src/pages/InventoryFunctionsPage.tsx
T

167 lines
7.3 KiB
TypeScript

import { useEffect, useState } from 'react';
import type { FormEvent } from 'react';
import { Alert, LoadingBlock, errorMessage } from '../components/Feedback';
import {
createInventoryFunction,
listInventoryFunctions,
updateInventoryFunction,
} from '../lib/inventoryFunctionApi';
import type { InventoryFunction } from '../lib/inventoryFunctionApi';
export function InventoryFunctionsPage() {
const [items, setItems] = useState<InventoryFunction[]>([]);
const [loading, setLoading] = useState(true);
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
const [success, setSuccess] = useState('');
const [code, setCode] = useState('');
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [sortOrder, setSortOrder] = useState('0');
const [editingId, setEditingId] = useState<string | null>(null);
const [editName, setEditName] = useState('');
const [editDescription, setEditDescription] = useState('');
const [editSortOrder, setEditSortOrder] = useState('0');
const load = () => {
setLoading(true);
setError('');
listInventoryFunctions(true)
.then(setItems)
.catch((requestError) => setError(errorMessage(requestError)))
.finally(() => setLoading(false));
};
useEffect(() => { load(); }, []);
const create = async (event: FormEvent) => {
event.preventDefault();
if (!code.trim() || !name.trim()) return;
setBusy(true);
setError('');
setSuccess('');
try {
await createInventoryFunction({
code,
name,
description: description.trim() || null,
sortOrder: Number(sortOrder || 0),
});
setCode('');
setName('');
setDescription('');
setSortOrder('0');
setSuccess('Función incorporada al catálogo. Ya puede asignarse a Estaciones y Subestaciones.');
load();
} catch (requestError) {
setError(errorMessage(requestError));
} finally {
setBusy(false);
}
};
const beginEdit = (item: InventoryFunction) => {
setEditingId(item.id);
setEditName(item.name);
setEditDescription(item.description ?? '');
setEditSortOrder(String(item.sortOrder));
setError('');
setSuccess('');
};
const saveEdit = async (event: FormEvent) => {
event.preventDefault();
if (!editingId || !editName.trim()) return;
setBusy(true);
setError('');
setSuccess('');
try {
await updateInventoryFunction(editingId, {
name: editName,
description: editDescription.trim() || null,
sortOrder: Number(editSortOrder || 0),
});
setEditingId(null);
setSuccess('Función actualizada. Los registros históricos conservan la referencia temporal correspondiente.');
load();
} catch (requestError) {
setError(errorMessage(requestError));
} finally {
setBusy(false);
}
};
const toggleActive = async (item: InventoryFunction) => {
setBusy(true);
setError('');
setSuccess('');
try {
await updateInventoryFunction(item.id, { isActive: !item.isActive });
setSuccess(item.isActive
? 'Función desactivada. No podrá elegirse en nuevos cambios, pero permanece en el historial.'
: 'Función reactivada y disponible nuevamente para asignación.');
load();
} catch (requestError) {
setError(errorMessage(requestError));
} finally {
setBusy(false);
}
};
if (loading) return <LoadingBlock label="Cargando catálogo de funciones…" />;
return <section className="narrow-section">
<div className="page-heading">
<div><span className="eyebrow">ADMINISTRACIÓN</span><h1>Catálogo de funciones</h1><p>Funciones operativas que pueden asumir las Estaciones y Subestaciones. Desactivar una opción nunca borra su uso histórico.</p></div>
</div>
{error && <Alert>{error}</Alert>}
{success && <Alert type="success">{success}</Alert>}
<form className="panel form-panel" onSubmit={create}>
<div className="form-section">
<div><h2>Nueva función</h2><p className="section-copy">Ejemplos: Bombeo Mecánico AIB, Bombeo Mecánico Rotaflex, PCP.</p></div>
<div className="form-grid">
<label className="field"><span>Código</span><input value={code} onChange={(event) => setCode(event.target.value.toUpperCase())} required maxLength={120} placeholder="BOMBEO_AIB" /></label>
<label className="field"><span>Nombre</span><input value={name} onChange={(event) => setName(event.target.value)} required maxLength={240} placeholder="Bombeo Mecánico AIB" /></label>
<label className="field"><span>Orden</span><input type="number" min={0} max={100000} value={sortOrder} onChange={(event) => setSortOrder(event.target.value)} /></label>
</div>
<label className="field"><span>Descripción <em>opcional</em></span><textarea rows={2} maxLength={4000} value={description} onChange={(event) => setDescription(event.target.value)} /></label>
<div className="form-actions"><button className="button primary" disabled={busy}>{busy ? 'Guardando…' : 'Agregar función'}</button></div>
</div>
</form>
<article className="panel">
<div className="panel-heading"><div><span className="eyebrow">CATÁLOGO</span><h2>Funciones disponibles</h2></div><span className="count-pill">{items.length}</span></div>
{items.length === 0 ? <div className="inline-empty">Todavía no hay funciones configuradas.</div> : <div className="dossier-link-list">
{items.map((item) => <div key={item.id}>
<div>
<strong>{item.name}</strong>
<small>{item.code}{item.description ? ` · ${item.description}` : ''}</small>
</div>
<span>{item.isActive ? 'Activa' : 'Inactiva'} · orden {item.sortOrder}</span>
<div className="form-actions">
<button type="button" className="button secondary" disabled={busy} onClick={() => beginEdit(item)}>Editar</button>
<button type="button" className="button secondary" disabled={busy} onClick={() => toggleActive(item)}>{item.isActive ? 'Desactivar' : 'Reactivar'}</button>
</div>
</div>)}
</div>}
</article>
{editingId && <form className="panel form-panel" onSubmit={saveEdit}>
<div className="form-section">
<div><h2>Editar función</h2><p className="section-copy">El código técnico permanece estable; podés corregir nombre, descripción y orden.</p></div>
<div className="form-grid">
<label className="field"><span>Nombre</span><input value={editName} onChange={(event) => setEditName(event.target.value)} required maxLength={240} /></label>
<label className="field"><span>Orden</span><input type="number" min={0} max={100000} value={editSortOrder} onChange={(event) => setEditSortOrder(event.target.value)} /></label>
</div>
<label className="field"><span>Descripción</span><textarea rows={2} maxLength={4000} value={editDescription} onChange={(event) => setEditDescription(event.target.value)} /></label>
<div className="form-actions">
<button type="button" className="button secondary" onClick={() => setEditingId(null)}>Cancelar</button>
<button className="button primary" disabled={busy}>{busy ? 'Guardando…' : 'Guardar cambios'}</button>
</div>
</div>
</form>}
</section>;
}