diff --git a/web-v2/src/pages/TerritorialInventoryPage.tsx b/web-v2/src/pages/TerritorialInventoryPage.tsx index 7700a70..22c353e 100644 --- a/web-v2/src/pages/TerritorialInventoryPage.tsx +++ b/web-v2/src/pages/TerritorialInventoryPage.tsx @@ -2,17 +2,19 @@ import { useEffect, useMemo, useState } from 'react'; import type { FormEvent } from 'react'; import { Link, useSearchParams } from 'react-router'; import { useAuth } from '../auth/AuthContext'; +import { hasAdministratorRole } from '../auth/adminAccess'; import { Alert, LoadingBlock, errorMessage } from '../components/Feedback'; import { Icon } from '../components/Icon'; import { AssetHistoryPanel } from '../features/assets/AssetHistoryPanel'; import { assetStatusClass, assetStatusLabel } from '../features/assets/assetPresentation'; import { + getAssetLineage, listAreaCompanyRelations, listAssetTreeChildren, listInspectionVisits, updateAsset, } from '../lib/api'; -import type { AreaCompanyRelation, AssetDetail, AssetListItem } from '../lib/api'; +import type { AreaCompanyRelation, AssetDetail, AssetLineageItem, AssetListItem } from '../lib/api'; import { listInspectionActsGlobalF4, type InspectionActStatusF4, @@ -31,8 +33,12 @@ function normalized(value: string) { .trim(); } +function tokensForType(type: { code: string; name: string }) { + return new Set(`${normalized(type.code)} ${normalized(type.name)}`.split(' ').filter(Boolean)); +} + function typeTokens(asset: AssetDetail) { - return new Set(`${normalized(asset.type.code)} ${normalized(asset.type.name)}`.split(' ').filter(Boolean)); + return tokensForType(asset.type); } export function isTerritorialInventoryAsset(asset: AssetDetail) { @@ -44,6 +50,14 @@ function isOperationalArea(asset: AssetDetail) { return typeTokens(asset).has('area'); } +function isYacimiento(asset: AssetDetail) { + return typeTokens(asset).has('yacimiento'); +} + +function lineageArea(lineage: AssetLineageItem[]) { + return [...lineage].reverse().find((item) => tokensForType(item.type).has('area')) ?? null; +} + function relationRoleLabel(role: AreaCompanyRelation['relationRole']) { if (role === 'OPERATOR') return 'Operadora'; if (role === 'TECHNICAL_OPERATOR') return 'Operadora técnica'; @@ -57,7 +71,7 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset const [params, setParams] = useSearchParams(); const requestedTab = params.get('tab') as TerritoryTab | null; const tab: TerritoryTab = requestedTab === 'history' ? 'history' : 'summary'; - const { hasPermission } = useAuth(); + const { user, hasPermission } = useAuth(); const canEdit = hasPermission('assets.update'); const canCreate = hasPermission('assets.create'); const canReadHistory = hasPermission('assets.read_history'); @@ -65,10 +79,12 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset const canReadActs = hasPermission('inspection_acts.read'); const canReadReports = hasPermission('inspection_reports.read'); const canReadInspections = hasPermission('inspections.read'); + const canAdvanced = hasAdministratorRole(user); const [asset, setAsset] = useState(initialAsset); const [children, setChildren] = useState([]); const [childrenHasMore, setChildrenHasMore] = useState(false); + const [lineage, setLineage] = useState([]); const [relations, setRelations] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); @@ -80,18 +96,31 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset const [metrics, setMetrics] = useState({ activeInspections: 0, acts: 0, reports: 0 }); const area = isOperationalArea(asset); + const yacimiento = isYacimiento(asset); const activeRelations = useMemo(() => relations.filter((item) => item.active), [relations]); + const relationAreaContext = useMemo(() => { + if (area) return { id: asset.id, code: asset.code, name: asset.name }; + if (asset.operationalArea) return asset.operationalArea; + const ancestor = lineageArea(lineage); + return ancestor ? { id: ancestor.id, code: ancestor.code, name: ancestor.name } : null; + }, [area, asset.id, asset.code, asset.name, asset.operationalArea, lineage]); const loadSummary = async () => { setLoading(true); setError(''); try { + const loadedLineage = await getAssetLineage(asset.id); + const ancestorArea = lineageArea(loadedLineage); + const relationAreaId = area + ? asset.id + : asset.operationalArea?.id ?? ancestorArea?.id ?? null; const [childPage, loadedRelations] = await Promise.all([ listAssetTreeChildren({ parentId: asset.id, limit: 100 }), - area && canReadRelations - ? listAreaCompanyRelations({ areaId: asset.id, includeHistory: true }) + relationAreaId && canReadRelations + ? listAreaCompanyRelations({ areaId: relationAreaId, includeHistory: true }) : Promise.resolve([]), ]); + setLineage(loadedLineage); setChildren(childPage.data); setChildrenHasMore(childPage.meta.hasMore); setRelations(loadedRelations); @@ -223,10 +252,10 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset {childrenHasMore &&
Ver contenido completo
} - {area && canReadRelations &&
-
OPERACIÓN

Empresas vinculadas

Quién opera o participa actualmente en esta área.

{activeRelations.length}
- {loading ? : activeRelations.length === 0 ?
No hay empresas con vínculo vigente.
:
- {activeRelations.map((relation) => {relation.company.name}{relationRoleLabel(relation.relationRole)})} + {relationAreaContext && canReadRelations &&
+
OPERACIÓN

Empresas vinculadas

{area ? 'Quién opera o participa actualmente en esta área.' : yacimiento ? `Empresas que operan o participan en el Área ${relationAreaContext.name}, contexto operativo de este yacimiento.` : `Empresas vinculadas al Área ${relationAreaContext.name}.`}

{activeRelations.length}
+ {loading ? : activeRelations.length === 0 ?
No hay empresas con vínculo vigente en el Área {relationAreaContext.name}.
:
+ {activeRelations.map((relation) => {relation.company.name}{relationRoleLabel(relation.relationRole)}{!area ? ` · ${relation.area.name}` : ''})}
}
} @@ -239,7 +268,7 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset
} -
Los datos estructurales, procedencia, ubicación y opciones técnicas quedan fuera de la vista cotidiana.Administración avanzada
+ {canAdvanced &&
Los datos estructurales, procedencia, ubicación y opciones técnicas quedan fuera de la vista cotidiana.Administración avanzada
} } {tab === 'history' && canReadHistory && }