From 589acab5925d145779737f6696303b3cd386be07 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Tue, 1 Sep 2026 13:04:05 +0100 Subject: [PATCH] Enhance NewHost and ObjectForm Components with Additional Device Info Fields - Updated NewHost component to include new fields for 'deviceInfo.user.shell' and 'deviceInfo.process.execPath', improving the detail of host information captured. - Enhanced ObjectForm component to handle nested property paths correctly, ensuring computed values do not overwrite parent objects when sibling properties exist. - Modified ObjectInfo component to correctly display values for object properties, improving data representation. - Refactored Host model to include new device information fields, enhancing the overall data structure for hosts. --- .../Dashboard/Management/Hosts/NewHost.jsx | 8 ++- .../Dashboard/common/ObjectForm.jsx | 50 ++++++++++++++++--- .../Dashboard/common/ObjectInfo.jsx | 2 +- src/database/models/Host.js | 30 ++++++++++- 4 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/components/Dashboard/Management/Hosts/NewHost.jsx b/src/components/Dashboard/Management/Hosts/NewHost.jsx index 61c928e4..b6691571 100644 --- a/src/components/Dashboard/Management/Hosts/NewHost.jsx +++ b/src/components/Dashboard/Management/Hosts/NewHost.jsx @@ -40,14 +40,16 @@ const NewHost = ({ onOk }) => { _id: false, createdAt: false, updatedAt: false, - operatingSystem: false, + 'deviceInfo.os.platform': false, 'deviceInfo.os': false, 'deviceInfo.os.hostname': false, 'deviceInfo.cpu': false, 'deviceInfo.cpu.model': false, 'deviceInfo.user.username': false, 'deviceInfo.user.homedir': false, + 'deviceInfo.user.shell': false, 'deviceInfo.process.nodeVersion': false, + 'deviceInfo.process.execPath': false, files: false }} /> @@ -66,14 +68,16 @@ const NewHost = ({ onOk }) => { createdAt: false, _reference: false, updatedAt: false, - operatingSystem: false, + 'deviceInfo.os.platform': false, 'deviceInfo.os': false, 'deviceInfo.os.hostname': false, 'deviceInfo.cpu': false, 'deviceInfo.cpu.model': false, 'deviceInfo.user.username': false, 'deviceInfo.user.homedir': false, + 'deviceInfo.user.shell': false, 'deviceInfo.process.nodeVersion': false, + 'deviceInfo.process.execPath': false, files: false, connectedAt: false, online: false diff --git a/src/components/Dashboard/common/ObjectForm.jsx b/src/components/Dashboard/common/ObjectForm.jsx index 3e1e67cb..7227d4ef 100644 --- a/src/components/Dashboard/common/ObjectForm.jsx +++ b/src/components/Dashboard/common/ObjectForm.jsx @@ -81,6 +81,37 @@ const applyComputedEntries = (base, entries = []) => { return result } +const collectModelPropertyPaths = (modelDefinition) => { + const paths = [] + + const visit = (properties) => { + ;(properties || []).forEach((property) => { + if (property?.name) { + paths.push(property.name) + } + if (Array.isArray(property?.properties) && property.properties.length > 0) { + visit(property.properties) + } + }) + } + + visit(modelDefinition?.properties) + return paths +} + +const pathToString = (path) => + Array.isArray(path) ? path.join('.') : String(path) + +// Computed display fields (e.g. deviceInfo.cpu) must not replace parent objects +// when sibling nested properties exist (e.g. deviceInfo.cpu.model). +const hasNestedPropertyPaths = (propertyPath, allPropertyPaths) => { + const pathStr = pathToString(propertyPath) + const prefix = `${pathStr}.` + return allPropertyPaths.some( + (name) => name !== pathStr && name.startsWith(prefix) + ) +} + const getObjectChildrenNames = (modelDefinition) => (modelDefinition?.properties || []) .filter((property) => property?.type === 'objectChildren' && property?.name) @@ -301,6 +332,7 @@ const ObjectForm = forwardRef( } const computedEntries = [] + const allPropertyPaths = collectModelPropertyPaths(modelDefinition) const processProperty = (property, parentPath = []) => { if (!property?.name) return @@ -320,12 +352,18 @@ const ObjectForm = forwardRef( try { const computedValue = property.value(scopeData || {}) if (computedValue !== undefined) { - computedEntries.push({ - namePath: propertyPath, - value: computedValue - }) - // Update workingData so subsequent properties can use this value - set(workingData, propertyPath, computedValue) + const preserveNestedObject = hasNestedPropertyPaths( + propertyPath, + allPropertyPaths + ) + if (!preserveNestedObject) { + computedEntries.push({ + namePath: propertyPath, + value: computedValue + }) + // Update workingData so subsequent properties can use this value + set(workingData, propertyPath, computedValue) + } } } catch (error) { console.warn( diff --git a/src/components/Dashboard/common/ObjectInfo.jsx b/src/components/Dashboard/common/ObjectInfo.jsx index 99558e2e..abb18c3c 100644 --- a/src/components/Dashboard/common/ObjectInfo.jsx +++ b/src/components/Dashboard/common/ObjectInfo.jsx @@ -100,7 +100,7 @@ const ObjectInfo = ({ parentData={parentData} showSince={true} useFormItem={isControlled ? false : objectPropertyProps.useFormItem} - value={isControlled ? objectData?.[item.name] : undefined} + value={isControlled ? objectData?.[item.name] : item.value} modelType={type} onChange={ isControlled diff --git a/src/database/models/Host.js b/src/database/models/Host.js index 908315d4..70d11813 100644 --- a/src/database/models/Host.js +++ b/src/database/models/Host.js @@ -38,7 +38,11 @@ export const Host = { label: 'New Host', icon: PlusIcon, content: (objectData, { onOk } = {}) => { - return createElement(NewHost, { defaultValues: objectData, onOk, reset: true }) + return createElement(NewHost, { + defaultValues: objectData, + onOk, + reset: true + }) } }, { @@ -213,6 +217,14 @@ export const Host = { columnWidth: 125 }, + { + name: 'deviceInfo.os.platform', + label: 'Platform', + type: 'text', + required: false, + readOnly: true, + columnWidth: 120 + }, { name: 'deviceInfo.os', label: 'Operating System', @@ -278,6 +290,14 @@ export const Host = { readOnly: true, columnWidth: 200 }, + { + name: 'deviceInfo.user.shell', + label: 'User Shell', + type: 'text', + required: false, + readOnly: true, + columnWidth: 180 + }, { name: 'deviceInfo.process.nodeVersion', label: 'NodeJS Version', @@ -286,6 +306,14 @@ export const Host = { readOnly: true, columnWidth: 150 }, + { + name: 'deviceInfo.process.execPath', + label: 'NodeJS Exec Path', + type: 'text', + required: false, + readOnly: true, + columnWidth: 300 + }, { name: 'tags', label: 'Tags',