F4: expose function changes in inventory history
This commit is contained in:
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
|||||||
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 {
|
import {
|
||||||
|
getAsset,
|
||||||
getAssetVersion,
|
getAssetVersion,
|
||||||
listAssetVersionTimeline,
|
listAssetVersionTimeline,
|
||||||
} from '../../lib/api';
|
} from '../../lib/api';
|
||||||
@@ -11,11 +12,26 @@ import type {
|
|||||||
} from '../../lib/api';
|
} from '../../lib/api';
|
||||||
import { formatDate } from '../../lib/format';
|
import { formatDate } from '../../lib/format';
|
||||||
import { AssetVersionDrawer } from './AssetVersionDrawer';
|
import { AssetVersionDrawer } from './AssetVersionDrawer';
|
||||||
|
import { InventoryFunctionPanel } from './InventoryFunctionPanel';
|
||||||
import {
|
import {
|
||||||
assetVersionChangeLabel,
|
assetVersionChangeLabel,
|
||||||
assetVersionFieldLabel,
|
assetVersionFieldLabel,
|
||||||
} from './assetVersionPresentation';
|
} from './assetVersionPresentation';
|
||||||
|
|
||||||
|
function normalized(value: string): string {
|
||||||
|
return value
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function supportsFunctionChange(code: string, name: string): boolean {
|
||||||
|
const value = `${normalized(code)} ${normalized(name)}`;
|
||||||
|
return value.split(' ').some((part) => part === 'estacion' || part === 'subestacion');
|
||||||
|
}
|
||||||
|
|
||||||
export function AssetHistoryPanel({
|
export function AssetHistoryPanel({
|
||||||
assetId,
|
assetId,
|
||||||
refreshKey,
|
refreshKey,
|
||||||
@@ -29,14 +45,19 @@ export function AssetHistoryPanel({
|
|||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [detail, setDetail] = useState<AssetVersionDetail | null>(null);
|
const [detail, setDetail] = useState<AssetVersionDetail | null>(null);
|
||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
|
const [functionEligible, setFunctionEligible] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError('');
|
setError('');
|
||||||
listAssetVersionTimeline(assetId, { pageSize: 10 })
|
Promise.all([
|
||||||
.then((response) => {
|
listAssetVersionTimeline(assetId, { pageSize: 10 }),
|
||||||
|
getAsset(assetId),
|
||||||
|
])
|
||||||
|
.then(([response, asset]) => {
|
||||||
setVersions(response.data);
|
setVersions(response.data);
|
||||||
setTotal(response.meta.total);
|
setTotal(response.meta.total);
|
||||||
|
setFunctionEligible(supportsFunctionChange(asset.type.code, asset.type.name));
|
||||||
})
|
})
|
||||||
.catch((requestError) => setError(errorMessage(requestError)))
|
.catch((requestError) => setError(errorMessage(requestError)))
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
@@ -55,12 +76,15 @@ export function AssetHistoryPanel({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return <article className="panel asset-history-panel">
|
return <div className="asset-tab-stack">
|
||||||
|
{functionEligible && <InventoryFunctionPanel assetId={assetId} />}
|
||||||
|
<article className="panel asset-history-panel">
|
||||||
<div className="panel-heading"><div><span className="eyebrow">TRAZABILIDAD TEMPORAL</span><h2>Historial del registro</h2></div><span className="count-pill">{total} versión{total === 1 ? '' : 'es'}</span></div>
|
<div className="panel-heading"><div><span className="eyebrow">TRAZABILIDAD TEMPORAL</span><h2>Historial del registro</h2></div><span className="count-pill">{total} versión{total === 1 ? '' : 'es'}</span></div>
|
||||||
<p className="section-copy">Cada cambio conserva una copia completa e inmutable del registro, sus atributos y su ubicación.</p>
|
<p className="section-copy">Cada cambio conserva una copia completa e inmutable del registro, sus atributos, su ubicación y, cuando corresponde, su función operativa.</p>
|
||||||
{error && <Alert>{error}</Alert>}
|
{error && <Alert>{error}</Alert>}
|
||||||
{loading ? <LoadingBlock label="Cargando historial…" /> : <div className="asset-timeline">{versions.map((version) => <button type="button" className="timeline-entry" key={version.id} onClick={() => open(version)}><span className={`timeline-dot ${version.isCurrent ? 'current' : ''}`} /><span><strong>v{version.versionNumber} · {assetVersionChangeLabel(version.changeType)}</strong><small>{formatDate(version.occurredAt)} · {version.actorUsername ?? 'Sistema'}</small><span className="tag-list">{version.changedFields.slice(0, 4).map((field) => <em className="tag" key={field}>{assetVersionFieldLabel(field)}</em>)}</span></span><Icon name="chevron" size={16} /></button>)}</div>}
|
{loading ? <LoadingBlock label="Cargando historial…" /> : <div className="asset-timeline">{versions.map((version) => <button type="button" className="timeline-entry" key={version.id} onClick={() => open(version)}><span className={`timeline-dot ${version.isCurrent ? 'current' : ''}`} /><span><strong>v{version.versionNumber} · {assetVersionChangeLabel(version.changeType)}</strong><small>{formatDate(version.occurredAt)} · {version.actorUsername ?? 'Sistema'}</small><span className="tag-list">{version.changedFields.slice(0, 4).map((field) => <em className="tag" key={field}>{assetVersionFieldLabel(field)}</em>)}</span></span><Icon name="chevron" size={16} /></button>)}</div>}
|
||||||
{!loading && total > versions.length && <p className="history-limit-note">Se muestran las 10 versiones más recientes. El historial completo está disponible en la sección Historial.</p>}
|
{!loading && total > versions.length && <p className="history-limit-note">Se muestran las 10 versiones más recientes. El historial completo está disponible en la sección Historial.</p>}
|
||||||
{(detailLoading || detail) && <AssetVersionDrawer detail={detail} loading={detailLoading} onClose={() => setDetail(null)} />}
|
{(detailLoading || detail) && <AssetVersionDrawer detail={detail} loading={detailLoading} onClose={() => setDetail(null)} />}
|
||||||
</article>;
|
</article>
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user