From fce370b287d16f1c05030611b61502041c6b7f19 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Fri, 11 Sep 2026 21:13:00 -0300 Subject: [PATCH] ux(inventory): simplify manual create flow --- web-v2/src/pages/InventoryCreatePage.tsx | 273 +++++++++++++++++------ 1 file changed, 207 insertions(+), 66 deletions(-) diff --git a/web-v2/src/pages/InventoryCreatePage.tsx b/web-v2/src/pages/InventoryCreatePage.tsx index ec822ee..32fcd91 100644 --- a/web-v2/src/pages/InventoryCreatePage.tsx +++ b/web-v2/src/pages/InventoryCreatePage.tsx @@ -6,13 +6,13 @@ import { Icon } from '../components/Icon'; import { getAsset } from '../lib/api'; import { createInventoryStructure, - getInventoryFamilyFindings, getInventoryStructureOptions, + listCompaniesForInventoryArea, listInventoryStructureParents, } from '../lib/inventoryStructureApi'; import type { + InventoryAreaCompanyOption, InventoryFamily, - InventoryFamilyFindings, InventoryStructureKind, InventoryStructureOptions, InventoryStructureParent, @@ -26,13 +26,24 @@ const KINDS: Array<{ kind: InventoryStructureKind; label: string }> = [ { kind: 'SUBINSTALACION', label: 'Subinstalación' }, { kind: 'EMPRESA', label: 'Empresa' }, ]; + const childKindByParentType: Record = { - departamento: 'AREA', area: 'YACIMIENTO', yacimiento: 'INSTALACION', instalacion: 'SUBINSTALACION', + departamento: 'AREA', + area: 'YACIMIENTO', + yacimiento: 'INSTALACION', + instalacion: 'SUBINSTALACION', }; -const parentLabelByKind: Partial> = { - AREA:'Departamento', YACIMIENTO:'Área', INSTALACION:'Yacimiento', SUBINSTALACION:'Instalación', + +const parentLabelByKind: Partial> = { + AREA: 'Departamento', + YACIMIENTO: 'Área', + INSTALACION: 'Yacimiento', + SUBINSTALACION: 'Instalación', }; -function kindLabel(kind: InventoryStructureKind) { return KINDS.find((item) => item.kind===kind)?.label ?? kind; } + +function kindLabel(kind: InventoryStructureKind) { + return KINDS.find((item) => item.kind === kind)?.label ?? kind; +} export function InventoryCreatePage() { const navigate = useNavigate(); @@ -44,102 +55,232 @@ export function InventoryCreatePage() { const [parentSearch, setParentSearch] = useState(''); const [parentId, setParentId] = useState(''); const [familyId, setFamilyId] = useState(''); + const [areaCompanies, setAreaCompanies] = useState([]); const [operatorCompanyId, setOperatorCompanyId] = useState(''); - const [concessionTypeId, setConcessionTypeId] = useState(''); - const [familyFindings, setFamilyFindings] = useState(null); const [name, setName] = useState(''); const [code, setCode] = useState(''); - const [commonName, setCommonName] = useState(''); - const [description, setDescription] = useState(''); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); useEffect(() => { - getInventoryStructureOptions().then(async (loaded) => { - setOptions(loaded); - if (contextParentId) { - const parent = await getAsset(contextParentId); - const inferred = childKindByParentType[parent.type.code.toLowerCase()]; - if (inferred) { setKind(inferred); setParentId(parent.id); } - } - }).catch((requestError) => setError(errorMessage(requestError))).finally(() => setLoading(false)); + getInventoryStructureOptions() + .then(async (loaded) => { + setOptions(loaded); + if (contextParentId) { + const parent = await getAsset(contextParentId); + const inferred = childKindByParentType[parent.type.code.toLowerCase()]; + if (inferred) { + setKind(inferred); + setParentId(parent.id); + } + } + }) + .catch((requestError) => setError(errorMessage(requestError))) + .finally(() => setLoading(false)); }, [contextParentId]); const requiresParent = Boolean(parentLabelByKind[kind]); const requiresFamily = kind === 'INSTALACION' || kind === 'SUBINSTALACION'; - const requiresYacimientoContext = kind === 'YACIMIENTO'; + const requiresCompany = kind === 'YACIMIENTO'; const parentLabel = parentLabelByKind[kind] ?? ''; useEffect(() => { - if (!requiresParent) { setParents([]); setParentId(''); return; } + if (!requiresParent) { + setParents([]); + setParentId(''); + return; + } const timer = window.setTimeout(() => { - listInventoryStructureParents(kind,parentSearch).then((loaded) => { - setParents(loaded); - if (contextParentId && loaded.some((item) => item.id===contextParentId)) setParentId(contextParentId); - }).catch((requestError) => setError(errorMessage(requestError))); - },150); + listInventoryStructureParents(kind, parentSearch) + .then((loaded) => { + setParents(loaded); + if (contextParentId && loaded.some((item) => item.id === contextParentId)) setParentId(contextParentId); + }) + .catch((requestError) => setError(errorMessage(requestError))); + }, 150); return () => window.clearTimeout(timer); - }, [kind,parentSearch,contextParentId,requiresParent]); + }, [kind, parentSearch, contextParentId, requiresParent]); - const selectedParent = parents.find((item) => item.id===parentId) ?? null; + useEffect(() => { + if (kind !== 'YACIMIENTO' || !parentId) { + setAreaCompanies([]); + setOperatorCompanyId(''); + return; + } + setOperatorCompanyId(''); + listCompaniesForInventoryArea(parentId) + .then(setAreaCompanies) + .catch((requestError) => setError(errorMessage(requestError))); + }, [kind, parentId]); + + const selectedParent = parents.find((item) => item.id === parentId) ?? null; const families = useMemo(() => { if (!options) return [] as InventoryFamily[]; - if (kind==='INSTALACION') return options.installationFamilies; - if (kind==='SUBINSTALACION') { - const parentFamilyId=selectedParent?.inventoryFamily?.id; - return parentFamilyId ? options.subinstallationFamilies.filter((item)=>item.parentFamilyIds.includes(parentFamilyId)) : []; + if (kind === 'INSTALACION') return options.installationFamilies; + if (kind === 'SUBINSTALACION') { + const parentFamilyId = selectedParent?.inventoryFamily?.id; + return parentFamilyId + ? options.subinstallationFamilies.filter((item) => item.parentFamilyIds.includes(parentFamilyId)) + : []; } return []; - },[options,kind,selectedParent]); - const selectedFamily=families.find((item)=>item.id===familyId) ?? null; + }, [options, kind, selectedParent]); - useEffect(() => { - if (!familyId) { setFamilyFindings(null); return; } - getInventoryFamilyFindings(familyId).then(setFamilyFindings).catch((requestError)=>setError(errorMessage(requestError))); - },[familyId]); useEffect(() => { if (!requiresFamily) setFamilyId(''); - if (kind==='SUBINSTALACION' && familyId && !families.some((item)=>item.id===familyId)) setFamilyId(''); - },[kind,requiresFamily,families,familyId]); - useEffect(() => { - if (!requiresYacimientoContext) { setOperatorCompanyId(''); setConcessionTypeId(''); } - },[requiresYacimientoContext]); + if (kind === 'SUBINSTALACION' && familyId && !families.some((item) => item.id === familyId)) setFamilyId(''); + }, [kind, requiresFamily, families, familyId]); - const changeKind=(next:InventoryStructureKind) => { - setKind(next); setParentId(''); setParentSearch(''); setFamilyId(''); setFamilyFindings(null); - setOperatorCompanyId(''); setConcessionTypeId(''); setError(''); + const changeKind = (next: InventoryStructureKind) => { + setKind(next); + setParentId(''); + setParentSearch(''); + setFamilyId(''); + setAreaCompanies([]); + setOperatorCompanyId(''); + setError(''); }; - const save=async(event:FormEvent) => { + + const save = async (event: FormEvent) => { event.preventDefault(); - if (requiresParent && !parentId) { setError(`Seleccioná ${parentLabel}.`); return; } - if (requiresYacimientoContext && !operatorCompanyId) { setError('Seleccioná la Empresa relacionada del Yacimiento.'); return; } - if (requiresYacimientoContext && !concessionTypeId) { setError('Seleccioná el Tipo de concesión del Yacimiento.'); return; } - if (requiresFamily && !familyId) { setError(`Seleccioná el tipo de ${kindLabel(kind).toLowerCase()}.`); return; } - setSaving(true); setError(''); + if (requiresParent && !parentId) { + setError(`Seleccioná ${parentLabel}.`); + return; + } + if (requiresCompany && !operatorCompanyId) { + setError('Seleccioná la Empresa que opera este Yacimiento.'); + return; + } + if (requiresFamily && !familyId) { + setError(`Seleccioná el tipo de ${kindLabel(kind).toLowerCase()}.`); + return; + } + + setSaving(true); + setError(''); try { - const created=await createInventoryStructure({ - kind,name:name.trim(),parentId:parentId || null,familyId:familyId || null, - operatorCompanyId:operatorCompanyId || null,concessionTypeId:concessionTypeId || null, - code:code.trim() || null,commonName:commonName.trim() || null,description:description.trim() || null, + const created = await createInventoryStructure({ + kind, + name: name.trim(), + parentId: parentId || null, + familyId: familyId || null, + operatorCompanyId: operatorCompanyId || null, + code: code.trim() || null, }); - navigate(`/inventarios/${created.id}`,{replace:true}); - } catch(requestError) { setError(errorMessage(requestError)); } - finally { setSaving(false); } + navigate(`/inventarios/${created.id}`, { replace: true }); + } catch (requestError) { + setError(errorMessage(requestError)); + } finally { + setSaving(false); + } }; if (loading) return ; + + const disableSave = saving + || !name.trim() + || (requiresParent && !parentId) + || (requiresCompany && !operatorCompanyId) + || (requiresFamily && !familyId); + return
- -
CARGA MANUAL

Nuevo registro

Modelo fijo: Departamento → Área → Yacimiento → Instalación → Subinstalación. La Empresa y el Tipo de concesión pertenecen al Yacimiento.

+ + +
+
+ ALTA RÁPIDA +

Agregar al Inventario

+

Elegí qué querés crear, dónde está y completá sólo los datos necesarios.

+
+
+ {error && {error}} +
-

1. ¿Qué querés crear?

Área sólo depende de Departamento. Yacimiento define Área, Empresa relacionada y Tipo de concesión.

- {requiresParent &&

2. Ubicación

Elegí el {parentLabel.toLowerCase()} concreto.

} - {requiresYacimientoContext &&

3. Contexto del Yacimiento

Estas relaciones pertenecen directamente al Yacimiento y no al Área.

} - {requiresFamily &&

3. Clasificación técnica

Define los datos técnicos y Hallazgos aplicables. En Subinstalaciones sólo aparecen clasificaciones compatibles con la Instalación elegida.

{kind==='SUBINSTALACION' && !selectedParent?.inventoryFamily ? La Instalación elegida todavía no tiene clasificación técnica. : }{kind==='SUBINSTALACION' && selectedParent?.inventoryFamily && families.length===0 && No hay tipos de Subinstalación compatibles con {selectedParent.inventoryFamily.name}.}{selectedFamily &&
{selectedFamily.name}{familyFindings ? `${familyFindings.count} Hallazgo${familyFindings.count===1?'':'s'} asociado${familyFindings.count===1?'':'s'}` : 'Cargando Hallazgos asociados…'}{familyFindings && familyFindings.items.length>0 &&
    {familyFindings.items.slice(0,6).map((item)=>
  • {item.title}
  • )}{familyFindings.items.length>6 &&
  • + {familyFindings.items.length-6} más
  • }
}
}
} -

{requiresYacimientoContext ? '4' : requiresFamily ? '4' : requiresParent ? '3' : '2'}. Nombre

El nombre es el dato propio principal de este nivel. Los datos técnicos se completan después, cuando corresponda.

Datos opcionales