Files
dh-inspeccion-v2/web-v2/src/features/assets/AssetFindingCatalogPanel.tsx
T
admin d938db7913 F6 · Modelo definitivo de Inventarios + APK Android (#29)
Jerarquía física definitiva, compatibilidades N↔N de clasificaciones, Hallazgos y campos técnicos por familia, navegación WEB/Android y APK F6 0.14.0-debug.
2026-09-09 11:19:12 -03:00

98 lines
4.8 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router';
import { useAuth } from '../../auth/AuthContext';
import { Alert, LoadingBlock, errorMessage } from '../../components/Feedback';
import { Icon } from '../../components/Icon';
import {
getInventoryFamilyFindings,
getInventoryTechnicalValues,
} from '../../lib/inventoryStructureApi';
import type {
InventoryFamilyFinding,
InventoryTechnicalValues,
} from '../../lib/inventoryStructureApi';
import { AssetTechnicalDataPanel } from './AssetTechnicalDataPanel';
export function AssetFindingCatalogPanel({ assetId, canManage }: { assetId: string; canManage: boolean }) {
const { hasPermission } = useAuth();
const canEditTechnical = hasPermission('assets.update');
const [technical, setTechnical] = useState<InventoryTechnicalValues | null>(null);
const [items, setItems] = useState<InventoryFamilyFinding[]>([]);
const [search, setSearch] = useState('');
const [loading, setLoading] = useState(true);
const [unsupported, setUnsupported] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
let active = true;
setLoading(true);
setUnsupported(false);
setError('');
getInventoryTechnicalValues(assetId)
.then(async (loaded) => {
const findings = await getInventoryFamilyFindings(loaded.family.id);
if (!active) return;
setTechnical(loaded);
setItems(findings.items);
})
.catch((requestError) => {
if (!active) return;
const message = errorMessage(requestError);
if (message.includes('no tiene clasificación técnica')) setUnsupported(true);
else setError(message);
})
.finally(() => active && setLoading(false));
return () => { active = false; };
}, [assetId]);
const visible = useMemo(() => {
const needle = search.trim().toLocaleLowerCase();
if (!needle) return items;
return items.filter((item) => [item.title, item.code, item.categoryName, item.legalBasis ?? '']
.some((value) => value.toLocaleLowerCase().includes(needle)));
}, [items, search]);
if (loading) return <div className="panel"><LoadingBlock label="Cargando clasificación y Hallazgos aplicables…" /></div>;
if (error) return <Alert>{error}</Alert>;
if (unsupported || !technical) return <section className="panel asset-finding-catalog-panel">
<div className="panel-heading"><div><span className="eyebrow">HALLAZGOS APLICABLES</span><h2>Sin clasificación técnica</h2><p className="section-copy">Departamento, Área y Yacimiento son niveles estructurales. Los Hallazgos se registran sobre Instalaciones y Subinstalaciones clasificadas.</p></div></div>
</section>;
return <div className="asset-tab-stack">
<AssetTechnicalDataPanel assetId={assetId} canEdit={canEditTechnical} />
<section className="panel asset-finding-catalog-panel">
<div className="panel-heading">
<div>
<span className="eyebrow">HALLAZGOS APLICABLES</span>
<h2>{technical.family.name}</h2>
<p className="section-copy">Se muestran únicamente los Hallazgos asociados a esta clasificación técnica. Éste es el mismo catálogo que consume la APK para este objeto.</p>
</div>
<span className="count-pill">{items.length} asociados</span>
</div>
{canManage && <div className="temporal-notice">
<Icon name="layers" />
<p><strong>¿Falta un Hallazgo?</strong> Administrá la clasificación en Configuración de Inventarios y el cambio se aplicará a todos los objetos de este mismo tipo técnico.</p>
<Link className="button secondary" to="/admin/asset-types">Configurar Inventarios</Link>
</div>}
{items.length > 0 && <label className="search-field">
<Icon name="search" />
<input value={search} onChange={(event) => setSearch(event.target.value)} placeholder={`Buscar entre los ${items.length} Hallazgos asociados…`} />
</label>}
{items.length === 0
? <div className="inline-empty">Esta clasificación todavía no tiene Hallazgos asociados. Desde Configuración de Inventarios podés vincular los que correspondan.</div>
: visible.length === 0
? <div className="inline-empty">No hay Hallazgos asociados que coincidan con la búsqueda.</div>
: <div className="finding-selection-list">{visible.map((item) => <div className="finding-selection-row" key={item.id}>
<span className="asset-symbol"><Icon name="alert" size={15} /></span>
<span>
<strong>{item.title}</strong>
<small>{item.categoryName} · {item.code}{item.suggestedSeverity ? ` · gravedad sugerida ${item.suggestedSeverity}/10` : ''}</small>
{item.legalBasis && <small>{item.legalBasis}</small>}
</span>
</div>)}</div>}
</section>
</div>;
}