fix(web): mostrar empresas heredadas en yacimientos y proteger avanzado
This commit is contained in:
@@ -2,17 +2,19 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
import type { FormEvent } from 'react';
|
import type { FormEvent } from 'react';
|
||||||
import { Link, useSearchParams } from 'react-router';
|
import { Link, useSearchParams } from 'react-router';
|
||||||
import { useAuth } from '../auth/AuthContext';
|
import { useAuth } from '../auth/AuthContext';
|
||||||
|
import { hasAdministratorRole } from '../auth/adminAccess';
|
||||||
import { Alert, LoadingBlock, errorMessage } from '../components/Feedback';
|
import { Alert, LoadingBlock, errorMessage } from '../components/Feedback';
|
||||||
import { Icon } from '../components/Icon';
|
import { Icon } from '../components/Icon';
|
||||||
import { AssetHistoryPanel } from '../features/assets/AssetHistoryPanel';
|
import { AssetHistoryPanel } from '../features/assets/AssetHistoryPanel';
|
||||||
import { assetStatusClass, assetStatusLabel } from '../features/assets/assetPresentation';
|
import { assetStatusClass, assetStatusLabel } from '../features/assets/assetPresentation';
|
||||||
import {
|
import {
|
||||||
|
getAssetLineage,
|
||||||
listAreaCompanyRelations,
|
listAreaCompanyRelations,
|
||||||
listAssetTreeChildren,
|
listAssetTreeChildren,
|
||||||
listInspectionVisits,
|
listInspectionVisits,
|
||||||
updateAsset,
|
updateAsset,
|
||||||
} from '../lib/api';
|
} from '../lib/api';
|
||||||
import type { AreaCompanyRelation, AssetDetail, AssetListItem } from '../lib/api';
|
import type { AreaCompanyRelation, AssetDetail, AssetLineageItem, AssetListItem } from '../lib/api';
|
||||||
import {
|
import {
|
||||||
listInspectionActsGlobalF4,
|
listInspectionActsGlobalF4,
|
||||||
type InspectionActStatusF4,
|
type InspectionActStatusF4,
|
||||||
@@ -31,8 +33,12 @@ function normalized(value: string) {
|
|||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tokensForType(type: { code: string; name: string }) {
|
||||||
|
return new Set(`${normalized(type.code)} ${normalized(type.name)}`.split(' ').filter(Boolean));
|
||||||
|
}
|
||||||
|
|
||||||
function typeTokens(asset: AssetDetail) {
|
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) {
|
export function isTerritorialInventoryAsset(asset: AssetDetail) {
|
||||||
@@ -44,6 +50,14 @@ function isOperationalArea(asset: AssetDetail) {
|
|||||||
return typeTokens(asset).has('area');
|
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']) {
|
function relationRoleLabel(role: AreaCompanyRelation['relationRole']) {
|
||||||
if (role === 'OPERATOR') return 'Operadora';
|
if (role === 'OPERATOR') return 'Operadora';
|
||||||
if (role === 'TECHNICAL_OPERATOR') return 'Operadora técnica';
|
if (role === 'TECHNICAL_OPERATOR') return 'Operadora técnica';
|
||||||
@@ -57,7 +71,7 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset
|
|||||||
const [params, setParams] = useSearchParams();
|
const [params, setParams] = useSearchParams();
|
||||||
const requestedTab = params.get('tab') as TerritoryTab | null;
|
const requestedTab = params.get('tab') as TerritoryTab | null;
|
||||||
const tab: TerritoryTab = requestedTab === 'history' ? 'history' : 'summary';
|
const tab: TerritoryTab = requestedTab === 'history' ? 'history' : 'summary';
|
||||||
const { hasPermission } = useAuth();
|
const { user, hasPermission } = useAuth();
|
||||||
const canEdit = hasPermission('assets.update');
|
const canEdit = hasPermission('assets.update');
|
||||||
const canCreate = hasPermission('assets.create');
|
const canCreate = hasPermission('assets.create');
|
||||||
const canReadHistory = hasPermission('assets.read_history');
|
const canReadHistory = hasPermission('assets.read_history');
|
||||||
@@ -65,10 +79,12 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset
|
|||||||
const canReadActs = hasPermission('inspection_acts.read');
|
const canReadActs = hasPermission('inspection_acts.read');
|
||||||
const canReadReports = hasPermission('inspection_reports.read');
|
const canReadReports = hasPermission('inspection_reports.read');
|
||||||
const canReadInspections = hasPermission('inspections.read');
|
const canReadInspections = hasPermission('inspections.read');
|
||||||
|
const canAdvanced = hasAdministratorRole(user);
|
||||||
|
|
||||||
const [asset, setAsset] = useState(initialAsset);
|
const [asset, setAsset] = useState(initialAsset);
|
||||||
const [children, setChildren] = useState<AssetListItem[]>([]);
|
const [children, setChildren] = useState<AssetListItem[]>([]);
|
||||||
const [childrenHasMore, setChildrenHasMore] = useState(false);
|
const [childrenHasMore, setChildrenHasMore] = useState(false);
|
||||||
|
const [lineage, setLineage] = useState<AssetLineageItem[]>([]);
|
||||||
const [relations, setRelations] = useState<AreaCompanyRelation[]>([]);
|
const [relations, setRelations] = useState<AreaCompanyRelation[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState('');
|
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 [metrics, setMetrics] = useState({ activeInspections: 0, acts: 0, reports: 0 });
|
||||||
|
|
||||||
const area = isOperationalArea(asset);
|
const area = isOperationalArea(asset);
|
||||||
|
const yacimiento = isYacimiento(asset);
|
||||||
const activeRelations = useMemo(() => relations.filter((item) => item.active), [relations]);
|
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 () => {
|
const loadSummary = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
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([
|
const [childPage, loadedRelations] = await Promise.all([
|
||||||
listAssetTreeChildren({ parentId: asset.id, limit: 100 }),
|
listAssetTreeChildren({ parentId: asset.id, limit: 100 }),
|
||||||
area && canReadRelations
|
relationAreaId && canReadRelations
|
||||||
? listAreaCompanyRelations({ areaId: asset.id, includeHistory: true })
|
? listAreaCompanyRelations({ areaId: relationAreaId, includeHistory: true })
|
||||||
: Promise.resolve([]),
|
: Promise.resolve([]),
|
||||||
]);
|
]);
|
||||||
|
setLineage(loadedLineage);
|
||||||
setChildren(childPage.data);
|
setChildren(childPage.data);
|
||||||
setChildrenHasMore(childPage.meta.hasMore);
|
setChildrenHasMore(childPage.meta.hasMore);
|
||||||
setRelations(loadedRelations);
|
setRelations(loadedRelations);
|
||||||
@@ -223,10 +252,10 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset
|
|||||||
{childrenHasMore && <div className="territory-card-actions"><Link className="button secondary" to={`/inventarios?section=territory&parentId=${asset.id}`}>Ver contenido completo</Link></div>}
|
{childrenHasMore && <div className="territory-card-actions"><Link className="button secondary" to={`/inventarios?section=territory&parentId=${asset.id}`}>Ver contenido completo</Link></div>}
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{area && canReadRelations && <article className="panel territory-card">
|
{relationAreaContext && canReadRelations && <article className="panel territory-card">
|
||||||
<div className="territory-card-heading"><div><span className="eyebrow">OPERACIÓN</span><h2>Empresas vinculadas</h2><p>Quién opera o participa actualmente en esta área.</p></div><span className="count-pill">{activeRelations.length}</span></div>
|
<div className="territory-card-heading"><div><span className="eyebrow">OPERACIÓN</span><h2>Empresas vinculadas</h2><p>{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}.`}</p></div><span className="count-pill">{activeRelations.length}</span></div>
|
||||||
{loading ? <LoadingBlock label="Cargando empresas…" /> : activeRelations.length === 0 ? <div className="inline-empty">No hay empresas con vínculo vigente.</div> : <div className="territory-company-list">
|
{loading ? <LoadingBlock label="Cargando empresas…" /> : activeRelations.length === 0 ? <div className="inline-empty">No hay empresas con vínculo vigente en el Área {relationAreaContext.name}.</div> : <div className="territory-company-list">
|
||||||
{activeRelations.map((relation) => <Link to={`/inventarios/${relation.company.id}`} key={relation.id} className="territory-company-row"><span><strong>{relation.company.name}</strong><small>{relationRoleLabel(relation.relationRole)}</small></span><Icon name="chevron" size={16} /></Link>)}
|
{activeRelations.map((relation) => <Link to={`/inventarios/${relation.company.id}`} key={relation.id} className="territory-company-row"><span><strong>{relation.company.name}</strong><small>{relationRoleLabel(relation.relationRole)}{!area ? ` · ${relation.area.name}` : ''}</small></span><Icon name="chevron" size={16} /></Link>)}
|
||||||
</div>}
|
</div>}
|
||||||
</article>}
|
</article>}
|
||||||
|
|
||||||
@@ -239,7 +268,7 @@ export function TerritorialInventoryPage({ initialAsset }: { initialAsset: Asset
|
|||||||
</div>
|
</div>
|
||||||
</article>}
|
</article>}
|
||||||
|
|
||||||
<div className="territory-advanced-link"><span>Los datos estructurales, procedencia, ubicación y opciones técnicas quedan fuera de la vista cotidiana.</span><Link to={`/inventarios/${asset.id}?advanced=1`}>Administración avanzada</Link></div>
|
{canAdvanced && <div className="territory-advanced-link"><span>Los datos estructurales, procedencia, ubicación y opciones técnicas quedan fuera de la vista cotidiana.</span><Link to={`/inventarios/${asset.id}?advanced=1`}>Administración avanzada</Link></div>}
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{tab === 'history' && canReadHistory && <AssetHistoryPanel assetId={asset.id} />}
|
{tab === 'history' && canReadHistory && <AssetHistoryPanel assetId={asset.id} />}
|
||||||
|
|||||||
Reference in New Issue
Block a user