import { SearchableSelect } from '../../components/SearchableSelect'; import { useEffect, useMemo, useState } from 'react'; import type { FormEvent } from 'react'; import { Alert, LoadingBlock, errorMessage } from '../../components/Feedback'; import { Icon } from '../../components/Icon'; import { changeAssetContext, listAssetContextHistory, listAssetParentOptions, listOperationalAreas, } from '../../lib/api'; import type { AssetContextHistoryItem, AssetDetail, AssetListItem, AssetType, OperationalAssetSummary, } from '../../lib/api'; import { formatDate } from '../../lib/format'; function localDateTimeNow(): string { const now = new Date(); const local = new Date(now.getTime() - now.getTimezoneOffset() * 60_000); return local.toISOString().slice(0, 16); } function contextLabel(item: AssetContextHistoryItem) { const parts = [ item.parent ? `Padre: ${item.parent.name}` : 'Sin padre', item.operationalArea ? `Área: ${item.operationalArea.name}` : null, ].filter(Boolean); return parts.join(' · '); } export function AssetContextHistoryPanel({ asset, type, canManage, onChanged, }: { asset: AssetDetail; type: AssetType; canManage: boolean; onChanged: (asset: AssetDetail) => void; }) { const [history, setHistory] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [editing, setEditing] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [parentId, setParentId] = useState(asset.parent?.id ?? ''); const [parentSearch, setParentSearch] = useState(''); const [parents, setParents] = useState([]); const [operationalAreaId, setOperationalAreaId] = useState(asset.operationalArea?.id ?? ''); const [areas, setAreas] = useState([]); const [effectiveAt, setEffectiveAt] = useState(localDateTimeNow()); const [reason, setReason] = useState(''); const genericContext = type.operationalRole === 'GENERIC'; const current = useMemo(() => history.find((item) => item.isCurrent) ?? history[0] ?? null, [history]); const loadHistory = () => { setLoading(true); listAssetContextHistory(asset.id) .then(setHistory) .catch((requestError) => setError(errorMessage(requestError))) .finally(() => setLoading(false)); }; useEffect(loadHistory, [asset.id]); useEffect(() => { if (!editing) return; const timer = window.setTimeout(() => { listAssetParentOptions(type.id, asset.id, parentSearch) .then(setParents) .catch((requestError) => setError(errorMessage(requestError))); }, 180); return () => window.clearTimeout(timer); }, [editing, type.id, asset.id, parentSearch]); useEffect(() => { if (!editing || !genericContext || !parentId) { setAreas([]); return; } listOperationalAreas(parentId) .then((items) => { setAreas(items); if (operationalAreaId && !items.some((item) => item.id === operationalAreaId)) { setOperationalAreaId(''); } }) .catch((requestError) => setError(errorMessage(requestError))); }, [editing, genericContext, parentId]); const beginEdit = () => { setParentId(asset.parent?.id ?? ''); setOperationalAreaId(asset.operationalArea?.id ?? ''); setEffectiveAt(localDateTimeNow()); setReason(''); setError(''); setSuccess(''); setEditing(true); }; const save = async (event: FormEvent) => { event.preventDefault(); setSaving(true); setError(''); setSuccess(''); try { const saved = await changeAssetContext(asset.id, { parentId: parentId || null, operationalAreaId: genericContext ? operationalAreaId || null : asset.operationalArea?.id ?? null, effectiveAt: effectiveAt ? new Date(effectiveAt).toISOString() : undefined, reason, }); onChanged(saved); setEditing(false); setSuccess('Contexto físico actualizado. La asignación anterior quedó preservada en el historial.'); loadHistory(); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; return
CONTEXTO TEMPORAL

Jerarquía y Área

{canManage && !editing && }

Los cambios físicos no reemplazan la historia. La Operadora se administra por separado en las relaciones temporales del Área.

{error && {error}} {success && {success}} {current &&

Contexto físico vigente. {contextLabel(current)}

} {editing &&

Cambiar contexto físico vigente

Indicá la nueva ubicación dentro del Inventario y el motivo. La relación física anterior se cierra automáticamente.

Registro padre setParentSearch(event.target.value)} placeholder="Buscar registro padre…" /> { setParentId(event.target.value); setParentSearch(''); }} required={!type.canBeRoot}>{parents.map((parent) => )}
{genericContext &&
}