Tom Butcher b16cdd6e96
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Enhance styling and functionality of dashboard components
- Added new CSS variables for padding in ScrollBox to improve layout.
- Updated ObjectForm to include console logs for debugging during editing cancellation.
- Refactored ObjectInfo to use mergeWith for better array handling in state updates.
- Enhanced ObjectProperty and ObjectTable components to support new props for better integration with tags.
- Improved TagsDisplay and TagsInput components for better tag management and display, including loading states and dynamic options fetching.
- Introduced getModelPropertyValues function in ApiServerContext for fetching model-specific property values, enhancing data handling capabilities.
2026-07-24 23:20:00 +01:00

169 lines
4.8 KiB
JavaScript

import { Spin, Descriptions, Flex } from 'antd'
import { useState, useEffect } from 'react'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import ObjectProperty from './ObjectProperty'
import { getModelProperties } from '../../../database/ObjectModels'
import mergeWith from 'lodash/mergeWith'
const arrayReplaceCustomizer = (objValue, srcValue) => {
if (Array.isArray(srcValue)) {
return srcValue
}
}
const ObjectInfo = ({
loading = false,
isEditing = false,
type = 'unknown',
showHyperlink,
showLabels = true,
objectData = null,
parentData = null,
properties = [],
propertyDefinitions = null,
onPropertyChange = null,
required = undefined,
visibleProperties = {},
objectPropertyProps = {},
labelWidth = '150px',
column = {
xs: 1,
sm: 1,
md: 1,
lg: 2,
xl: 2,
xxl: 2
},
...rest
}) => {
const allItems = getModelProperties(type)
const [combinedObjectData, setCombinedObjectData] = useState(objectData)
useEffect(() => {
setCombinedObjectData((prev) =>
mergeWith({}, prev, objectData, arrayReplaceCustomizer)
)
}, [objectData])
// If properties array is empty, show all properties
// Otherwise, filter and order by the properties array
let items
if (Array.isArray(propertyDefinitions) && propertyDefinitions.length > 0) {
items = propertyDefinitions
} else if (properties.length === 0) {
items = allItems
} else {
items = properties
.map((propName) => allItems.find((item) => item.name === propName))
.filter(Boolean)
}
if (required != undefined) {
items = items.filter((item) => item.required === required)
}
if (showHyperlink) {
objectPropertyProps = { ...objectPropertyProps, showHyperlink }
}
// Filter items based on visibleProperties
// If all values in visibleProperties are true, use whitelist mode
// Otherwise, use blacklist mode (hide properties set to false)
const visibleValues = Object.values(visibleProperties)
const isWhitelistMode =
visibleValues.length > 0 && visibleValues.every((value) => value === true)
items = items.filter((item) => {
const propertyName = item.name
// Support property.visible as a function (objectData) => boolean
if (typeof item.visible === 'function') {
const visible = item.visible(objectData || combinedObjectData || {})
if (!visible) return false
}
if (isWhitelistMode) {
// Whitelist mode: only show properties that are explicitly set to true
return visibleProperties[propertyName] === true
} else {
// Blacklist mode: hide properties that are explicitly set to false
return !(
propertyName in visibleProperties &&
visibleProperties[propertyName] === false
)
}
})
const isControlled = typeof onPropertyChange === 'function'
// Map items to Descriptions 'items' prop format
const descriptionItems = items.map((item, idx) => {
const key = item.name || item.label || idx
return {
key,
label:
showLabels == true ? (
<Flex vertical style={{ height: '100%' }} justify='center'>
{item.label}
</Flex>
) : null,
children: (
<div style={{ maxWidth: '100%', minWidth: 0, width: '100%' }}>
<ObjectProperty
{...item}
{...objectPropertyProps}
isEditing={isEditing}
objectData={combinedObjectData}
parentData={parentData}
showSince={true}
useFormItem={isControlled ? false : objectPropertyProps.useFormItem}
value={isControlled ? combinedObjectData?.[item.name] : undefined}
modelType={type}
onChange={
isControlled
? (newVal) => onPropertyChange(item.name, newVal)
: objectPropertyProps.onChange
}
/>
</div>
),
span: item?.span || undefined
}
})
return (
<Spin spinning={loading} indicator={<LoadingOutlined />}>
<Descriptions
column={column}
bordered={true}
{...rest}
items={descriptionItems}
styles={{ label: { width: labelWidth } }}
className='object-info-descriptions'
/>
</Spin>
)
}
ObjectInfo.propTypes = {
loading: PropTypes.bool,
column: PropTypes.object,
showHyperlink: PropTypes.bool,
showLabels: PropTypes.bool,
indicator: PropTypes.node,
properties: PropTypes.array,
bordered: PropTypes.bool,
items: PropTypes.arrayOf(PropTypes.object),
isEditing: PropTypes.bool,
type: PropTypes.string,
objectData: PropTypes.object,
parentData: PropTypes.object,
propertyDefinitions: PropTypes.array,
onPropertyChange: PropTypes.func,
required: PropTypes.bool,
visibleProperties: PropTypes.object,
objectPropertyProps: PropTypes.object,
labelWidth: PropTypes.string
}
export default ObjectInfo