F5.1: listar todos los niveles de Inventario
This commit is contained in:
@@ -22,7 +22,7 @@ import type {
|
||||
AssetType,
|
||||
PageMeta,
|
||||
} from '../lib/api';
|
||||
import { listRealInventory } from '../lib/inventoryBrowserApi';
|
||||
import { listInventory } from '../lib/inventoryBrowserApi';
|
||||
import type { InventoryListItem, InventoryQuery } from '../lib/inventoryBrowserApi';
|
||||
import { formatDate } from '../lib/format';
|
||||
|
||||
@@ -60,24 +60,19 @@ export function AssetsPage() {
|
||||
const effectiveOperationalStatus = quick === 'out' ? 'OUT_OF_SERVICE' as AssetOperationalStatus : operationalStatus;
|
||||
|
||||
useEffect(() => {
|
||||
listAssetTypes().then((data) => setTypes(data.filter((type) => ['instalacion','subinstalacion'].includes(type.code.toLowerCase())))).catch(() => undefined);
|
||||
listAssetTypes().then((data) => setTypes(data.filter((type) =>
|
||||
type.operationalRole === 'COMPANY'
|
||||
|| ['departamento','area','yacimiento','instalacion','subinstalacion'].includes(type.code.toLowerCase()),
|
||||
))).catch(() => undefined);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (view !== 'list') return;
|
||||
setLoading(true);
|
||||
setError('');
|
||||
listRealInventory({
|
||||
page,
|
||||
pageSize: 25,
|
||||
search,
|
||||
typeId,
|
||||
status,
|
||||
setLoading(true); setError('');
|
||||
listInventory({
|
||||
page, pageSize: 25, search, typeId, status,
|
||||
operationalStatus: effectiveOperationalStatus,
|
||||
operationalAreaId,
|
||||
operatorCompanyId,
|
||||
needsValidation,
|
||||
hasGeometry,
|
||||
operationalAreaId, operatorCompanyId, needsValidation, hasGeometry,
|
||||
})
|
||||
.then((response) => { setAssets(response.data); setMeta(response.meta); })
|
||||
.catch((requestError) => setError(errorMessage(requestError)))
|
||||
@@ -85,36 +80,26 @@ export function AssetsPage() {
|
||||
}, [view, page, search, typeId, status, effectiveOperationalStatus, operationalAreaId, operatorCompanyId, needsValidation, hasGeometry]);
|
||||
|
||||
const filters: InventoryQuery = useMemo(() => ({
|
||||
search,
|
||||
typeId,
|
||||
status,
|
||||
operationalStatus: effectiveOperationalStatus,
|
||||
operationalAreaId,
|
||||
operatorCompanyId,
|
||||
needsValidation,
|
||||
hasGeometry,
|
||||
search, typeId, status, operationalStatus: effectiveOperationalStatus,
|
||||
operationalAreaId, operatorCompanyId, needsValidation, hasGeometry,
|
||||
}), [search, typeId, status, effectiveOperationalStatus, operationalAreaId, operatorCompanyId, needsValidation, hasGeometry]);
|
||||
|
||||
const update = (changes: Record<string, string | null>) => {
|
||||
const next = new URLSearchParams(urlParams);
|
||||
Object.entries(changes).forEach(([key, value]) => value ? next.set(key, value) : next.delete(key));
|
||||
next.delete('page');
|
||||
setUrlParams(next);
|
||||
next.delete('page'); setUrlParams(next);
|
||||
};
|
||||
const applySearch = (event: FormEvent) => { event.preventDefault(); update({ search: draftSearch.trim() || null }); };
|
||||
const setQuick = (key: string) => update({ quick: key === 'all' ? null : key, operationalStatus: key === 'out' ? null : rawOperationalStatus || null });
|
||||
const clearFilters = () => {
|
||||
const next = new URLSearchParams();
|
||||
if (view === 'list') next.set('view', 'list');
|
||||
const parentId=urlParams.get('parentId');
|
||||
if (parentId) next.set('parentId',parentId);
|
||||
setDraftSearch('');
|
||||
setUrlParams(next);
|
||||
const parentId=urlParams.get('parentId'); if (parentId) next.set('parentId',parentId);
|
||||
setDraftSearch(''); setUrlParams(next);
|
||||
};
|
||||
const setPage = (value: number) => {
|
||||
const next = new URLSearchParams(urlParams);
|
||||
value > 1 ? next.set('page', String(value)) : next.delete('page');
|
||||
setUrlParams(next);
|
||||
value > 1 ? next.set('page', String(value)) : next.delete('page'); setUrlParams(next);
|
||||
};
|
||||
const advancedActive = Boolean(typeId || status || rawOperationalStatus);
|
||||
|
||||
@@ -123,7 +108,7 @@ export function AssetsPage() {
|
||||
<div>
|
||||
<span className="eyebrow">INVENTARIOS</span>
|
||||
<h1>Inventarios</h1>
|
||||
<p>Instancias reales registradas en campo, organizadas por Área → Yacimiento → Instalación → Subinstalación.</p>
|
||||
<p>Todos los registros: Departamento → Área → Yacimiento → Instalación → Subinstalación, más el maestro independiente de Empresas.</p>
|
||||
</div>
|
||||
<PermissionGate permission="assets.create"><Link className="button primary" to="/inventarios/nuevo"><Icon name="plus" />Nuevo registro</Link></PermissionGate>
|
||||
</div>
|
||||
@@ -133,7 +118,7 @@ export function AssetsPage() {
|
||||
<div className="asset-center-controls">
|
||||
<form className="asset-center-search" onSubmit={applySearch}>
|
||||
<Icon name="search" />
|
||||
<input value={draftSearch} onChange={(event) => setDraftSearch(event.target.value)} placeholder="Buscar Inventario, código o identificación…" />
|
||||
<input value={draftSearch} onChange={(event) => setDraftSearch(event.target.value)} placeholder="Buscar por nombre, código o identificación…" />
|
||||
<button className="button primary" type="submit">Buscar</button>
|
||||
</form>
|
||||
<div className="quick-view-row" aria-label="Vistas rápidas">
|
||||
@@ -141,7 +126,7 @@ export function AssetsPage() {
|
||||
{view === 'list' && <button type="button" className={advancedOpen || advancedActive ? 'advanced active' : 'advanced'} onClick={() => setAdvancedOpen((current) => !current)}>Más filtros</button>}
|
||||
</div>
|
||||
{view === 'list' && (advancedOpen || advancedActive) && <div className="advanced-filter-panel">
|
||||
<label className="field compact-field"><span>Nivel</span><SearchableSelect value={typeId} onChange={(event) => update({ typeId: event.target.value || null })}><option value="">Instalaciones y Subinstalaciones</option>{types.map((type) => <option key={type.id} value={type.id}>{type.name}</option>)}</SearchableSelect></label>
|
||||
<label className="field compact-field"><span>Nivel</span><SearchableSelect value={typeId} onChange={(event) => update({ typeId: event.target.value || null })}><option value="">Todos los niveles</option>{types.map((type) => <option key={type.id} value={type.id}>{type.name}</option>)}</SearchableSelect></label>
|
||||
<label className="field compact-field"><span>Estado del dato</span><SearchableSelect value={status} onChange={(event) => update({ status: event.target.value || null })}><option value="">Todos</option>{ASSET_STATUSES.map((item) => <option key={item.value} value={item.value}>{item.label}</option>)}</SearchableSelect></label>
|
||||
<label className="field compact-field"><span>Estado operativo</span><SearchableSelect value={rawOperationalStatus} onChange={(event) => update({ operationalStatus: event.target.value || null, quick: quick === 'out' ? null : quick === 'all' ? null : quick })}><option value="">Todos</option>{ASSET_OPERATIONAL_STATUSES.map((item) => <option key={item.value} value={item.value}>{item.label}</option>)}</SearchableSelect></label>
|
||||
<button type="button" className="button text filter-clear" onClick={clearFilters}>Limpiar filtros</button>
|
||||
@@ -152,16 +137,16 @@ export function AssetsPage() {
|
||||
{view === 'hierarchy'
|
||||
? <AssetHierarchyView filters={filters} />
|
||||
: loading
|
||||
? <LoadingBlock label="Cargando Inventario real…" />
|
||||
? <LoadingBlock label="Cargando Inventarios…" />
|
||||
: assets.length === 0
|
||||
? <EmptyState title="Todavía no hay Inventario real" text="Las Áreas y Yacimientos precargados son contexto. Las Instalaciones/Subinstalaciones aparecerán aquí cuando sean registradas realmente." />
|
||||
? <EmptyState title="Todavía no hay registros" text="La base está lista para comenzar la carga manual desde Departamento." />
|
||||
: <div className="table-panel compact-assets-table">
|
||||
<div className="table-summary"><strong>{meta.total} instancia{meta.total === 1 ? '' : 's'} real{meta.total === 1 ? '' : 'es'}</strong><span>Página {meta.page} de {Math.max(meta.totalPages, 1)}</span></div>
|
||||
<div className="table-scroll"><table><thead><tr><th>Inventario</th><th>Nivel</th><th>Área / Operadora vigente</th><th>Estado</th><th>Actualizado</th><th aria-label="Acciones" /></tr></thead><tbody>
|
||||
<div className="table-summary"><strong>{meta.total} registro{meta.total === 1 ? '' : 's'}</strong><span>Página {meta.page} de {Math.max(meta.totalPages, 1)}</span></div>
|
||||
<div className="table-scroll"><table><thead><tr><th>Registro</th><th>Nivel</th><th>Ubicación / Operadora</th><th>Estado</th><th>Actualizado</th><th aria-label="Acciones" /></tr></thead><tbody>
|
||||
{assets.map((asset) => <tr key={asset.id}>
|
||||
<td><div className="asset-cell"><span className="asset-symbol"><Icon name="layers" size={16} /></span><div><Link to={`/inventarios/${asset.id}`} className="table-primary">{asset.name}</Link><small>{asset.code}{asset.parent ? ` · en ${asset.parent.name}` : ''}</small></div></div></td>
|
||||
<td><span className="tag">{asset.type.name}</span></td>
|
||||
<td>{asset.operationalArea ? <span><strong className="table-primary">{asset.operationalArea.name}</strong><small className="cell-subtext">{asset.operatorCompany?.name ?? 'Sin operadora vigente'}</small></span> : <span className="muted">Sin Área</span>}</td>
|
||||
<td>{asset.operationalArea ? <span><strong className="table-primary">{asset.operationalArea.name}</strong><small className="cell-subtext">{asset.operatorCompany?.name ?? 'Sin operadora vigente'}</small></span> : asset.operatorCompany ? <span>{asset.operatorCompany.name}</span> : <span className="muted">{asset.parent?.name ?? 'Raíz / maestro independiente'}</span>}</td>
|
||||
<td><div className="dual-status"><span className={`status-badge ${assetStatusClass(asset.informationStatus)}`}>{assetStatusLabel(asset.informationStatus)}</span><small>{assetOperationalStatusLabel(asset.operationalStatus)}</small></div></td>
|
||||
<td>{formatDate(asset.updatedAt)}</td>
|
||||
<td className="action-cell"><Link className="icon-button" to={`/inventarios/${asset.id}`} aria-label={`Abrir ${asset.name}`}><Icon name="chevron" /></Link></td>
|
||||
|
||||
Reference in New Issue
Block a user