From 8708c9c93f13447a93a64dee35fede0ba2c83838 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Wed, 9 Sep 2026 09:34:55 -0300 Subject: [PATCH] =?UTF-8?q?feat(f6):=20agregar=20datos=20t=C3=A9cnicos=20p?= =?UTF-8?q?or=20clasificaci=C3=B3n=20a=20la=20ficha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/AssetTechnicalDataPanel.tsx | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 web-v2/src/features/assets/AssetTechnicalDataPanel.tsx diff --git a/web-v2/src/features/assets/AssetTechnicalDataPanel.tsx b/web-v2/src/features/assets/AssetTechnicalDataPanel.tsx new file mode 100644 index 0000000..d3d3274 --- /dev/null +++ b/web-v2/src/features/assets/AssetTechnicalDataPanel.tsx @@ -0,0 +1,62 @@ +import { useEffect, useState } from 'react'; +import { SearchableSelect } from '../../components/SearchableSelect'; +import { Alert, LoadingBlock, errorMessage } from '../../components/Feedback'; +import { Icon } from '../../components/Icon'; +import { getInventoryTechnicalValues, replaceInventoryTechnicalValues } from '../../lib/inventoryStructureApi'; +import type { InventoryFamilyAttribute, InventoryTechnicalValues } from '../../lib/inventoryStructureApi'; + +function localDateTime(value:unknown){ + if(typeof value!=='string'||!value)return ''; + const date=new Date(value); if(Number.isNaN(date.getTime()))return value.slice(0,16); + const local=new Date(date.getTime()-date.getTimezoneOffset()*60000); return local.toISOString().slice(0,16); +} +function displayValue(definition:InventoryFamilyAttribute,value:unknown){ + if(value===undefined||value===null||value==='')return 'Sin completar'; + if(definition.dataType==='BOOLEAN')return value?'Sí':'No'; + if(definition.dataType==='DATETIME'&&typeof value==='string')return new Date(value).toLocaleString('es-AR'); + return `${String(value)}${definition.unit?` ${definition.unit}`:''}`; +} + +export function AssetTechnicalDataPanel({assetId,canEdit}:{assetId:string;canEdit:boolean}){ + const [data,setData]=useState(null); + const [values,setValues]=useState>({}); + const [loading,setLoading]=useState(true); const [saving,setSaving]=useState(false); + const [unsupported,setUnsupported]=useState(false); const [error,setError]=useState(''); const [success,setSuccess]=useState(''); + useEffect(()=>{ + let active=true; setLoading(true); setUnsupported(false); setError(''); + getInventoryTechnicalValues(assetId).then((loaded)=>{if(!active)return;setData(loaded);setValues(Object.fromEntries(loaded.definitions.map((definition)=>{ + const raw=loaded.values[definition.id]; return [definition.id,definition.dataType==='DATETIME'?localDateTime(raw):raw??'']; + })))}).catch((requestError)=>{if(!active)return;const message=errorMessage(requestError);if(message.includes('no tiene clasificación técnica'))setUnsupported(true);else setError(message);}).finally(()=>active&&setLoading(false)); + return()=>{active=false}; + },[assetId]); + if(loading)return
; + if(unsupported)return null; + if(error&&!data)return
{error}
; + if(!data)return null; + const activeDefinitions=data.definitions.filter((item)=>item.isActive); + const save=async()=>{ + setSaving(true);setError('');setSuccess(''); + try{ + const normalized:Record={}; + for(const definition of activeDefinitions){const raw=values[definition.id];if(raw===''||raw===null||raw===undefined)continue; + if(definition.dataType==='NUMBER')normalized[definition.id]=Number(raw); + else if(definition.dataType==='BOOLEAN')normalized[definition.id]=Boolean(raw); + else if(definition.dataType==='DATETIME')normalized[definition.id]=new Date(String(raw)).toISOString(); + else normalized[definition.id]=raw; + } + const saved=await replaceInventoryTechnicalValues(assetId,normalized);setData(saved);setSuccess('Datos técnicos guardados.'); + }catch(requestError){setError(errorMessage(requestError));}finally{setSaving(false)} + }; + return
+

Información técnica · {data.family.name}

Estos campos pertenecen a la clasificación concreta del elemento, no al nivel genérico.

+ {error&&{error}}{success&&{success}} + {activeDefinitions.length===0?
Esta clasificación todavía no tiene campos técnicos configurados.
:canEdit?
{activeDefinitions.map((definition)=>{ + const value=values[definition.id];const label={definition.name}{definition.unit?` (${definition.unit})`:''}{definition.isRequired?obligatorio:opcional}; + if(definition.dataType==='BOOLEAN')return ; + if(definition.dataType==='SELECT')return ; + const inputType=definition.dataType==='NUMBER'?'number':definition.dataType==='DATE'?'date':definition.dataType==='DATETIME'?'datetime-local':'text'; + return ; + })}
:
{activeDefinitions.map((definition)=>
{definition.name}{definition.code}{displayValue(definition,data.values[definition.id])}
)}
} + {canEdit&&activeDefinitions.length>0&&
} +
; +}