Enhance ObjectProperty component by adding support for min/max value handling and array input/display functionality. Introduce new MinMaxDisplay, MinMaxInput, ArrayDisplay, and ArrayInput components, improving data entry and visualization capabilities. Update validation logic for min/max and list inputs to ensure data integrity.

This commit is contained in:
Tom Butcher 2026-07-20 02:06:40 +01:00
parent a38d9c91f6
commit 46fd3b0c8e

View File

@ -27,6 +27,8 @@ import { getPropertyValue } from '../../../database/ObjectModels'
import PropertyChanges from './PropertyChanges'
import NetGrossDisplay from './NetGrossDisplay'
import NetGrossInput from './NetGrossInput'
import MinMaxDisplay from './MinMaxDisplay'
import MinMaxInput from './MinMaxInput'
import InputNumberCal from './InputNumberCal'
import ObjectList from './ObjectList'
import VarianceDisplay from './VarianceDisplay'
@ -47,10 +49,35 @@ import FileList from './FileList'
import ObjectChildTable from './ObjectChildTable'
import MiscId from './MiscId'
import AddressDisplay from './AddressDisplay'
import ArrayDisplay from './ArrayDisplay'
import ArrayInput from './ArrayInput'
import { round } from '../utils/Utils'
const { Text } = Typography
const getTextValue = (value) => {
if (value == null || typeof value === 'string' || typeof value === 'number') {
return value
}
if (typeof value === 'object') {
if (typeof value._id === 'string') {
return value._id
}
if (typeof value._id?._id === 'string') {
return value._id._id
}
try {
return JSON.stringify(value)
} catch {
return String(value)
}
}
return String(value)
}
const timeEditFormItemProps = {
getValueProps: (v) => ({ value: v ? dayjs(v) : null })
}
@ -97,7 +124,10 @@ const ObjectProperty = ({
loading = false,
rollups = [],
showCard = true,
showDownload = true,
columns = [],
style = {},
hiddenPropertyWidth = '100px',
...rest
}) => {
if (value && typeof value == 'function' && objectData) {
@ -152,7 +182,11 @@ const ObjectProperty = ({
type = type(objectData, parentData)
}
if (!value) {
if (columns && typeof columns == 'function' && objectData) {
columns = columns(objectData, parentData)
}
if (value == null) {
value = getPropertyValue(objectData, name)
}
@ -183,6 +217,15 @@ const ObjectProperty = ({
return (
<NetGrossDisplay value={value} prefix={prefix} suffix={suffix} />
)
case 'minMax':
return (
<MinMaxDisplay
value={value}
prefix={prefix}
suffix={suffix}
step={step}
/>
)
case 'secret':
if (value != null) {
return <SecretDisplay value={value} {...rest} />
@ -313,6 +356,9 @@ const ObjectProperty = ({
)
}
}
case 'numberList':
case 'stringList':
return <ArrayDisplay value={value} prefix={prefix} suffix={suffix} />
case 'variance': {
if (value != null) {
return (
@ -331,7 +377,7 @@ const ObjectProperty = ({
return (
<Text ellipsis style={style}>
{prefix}
{value}
{getTextValue(value)}
{suffix}
</Text>
)
@ -432,8 +478,10 @@ const ObjectProperty = ({
loading={loading}
rollups={rollups}
size={size}
hiddenPropertyWidth={hiddenPropertyWidth}
minimal={minimal}
label={label}
columns={columns}
canAddRemove={canAddRemove}
/>
)
@ -571,6 +619,7 @@ const ObjectProperty = ({
defaultPreviewOpen={previewOpen}
showPreview={showPreview}
showInfo={showHyperlink}
showDownload={showDownload}
/>
)
}
@ -588,8 +637,8 @@ const ObjectProperty = ({
)
}
default: {
if (value) {
return <Text {...textParams}>{value}</Text>
if (value != null && value !== '') {
return <Text {...textParams}>{getTextValue(value)}</Text>
} else {
return (
<Text type='secondary' {...textParams}>
@ -620,6 +669,59 @@ const ObjectProperty = ({
}
mergedFormItemProps.rules = rules
}
if (type === 'minMax' && disabled == false) {
const rules = [...(mergedFormItemProps.rules || [])]
rules.push({
validator: (_, range) => {
if (range == null) return Promise.resolve()
if (typeof range !== 'object' || Array.isArray(range)) {
return Promise.reject(new Error('Value must be a min/max range'))
}
const values = [range.min, range.max].filter((item) => item != null)
const valid = values.every(
(item) =>
typeof item === 'number' &&
Number.isFinite(item) &&
(min == null || item >= min) &&
(max == null || item <= max)
)
return valid
? Promise.resolve()
: Promise.reject(new Error('Enter valid min and max values'))
}
})
mergedFormItemProps.rules = rules
}
if ((type === 'numberList' || type === 'stringList') && disabled == false) {
const rules = [...(mergedFormItemProps.rules || [])]
rules.push({
validator: (_, items) => {
if (items == null) return Promise.resolve()
if (!Array.isArray(items)) {
return Promise.reject(new Error('Value must be a list'))
}
const valid = items.every((item) => {
if (type === 'numberList') {
return (
typeof item === 'number' &&
Number.isFinite(item) &&
(min == null || item >= min) &&
(max == null || item <= max)
)
}
return typeof item === 'string' && item.trim().length > 0
})
return valid
? Promise.resolve()
: Promise.reject(new Error('Complete or remove empty list items'))
}
})
mergedFormItemProps.rules = rules
}
// Remove name from mergedFormItemProps if present
if (mergedFormItemProps.name) {
delete mergedFormItemProps.name
@ -657,6 +759,17 @@ const ObjectProperty = ({
{...inputProps}
/>
)
case 'minMax':
return (
<MinMaxInput
prefix={prefix}
suffix={suffix}
min={min}
max={max}
step={step}
{...inputProps}
/>
)
case 'secret':
return (
<Input.Password
@ -751,6 +864,26 @@ const ObjectProperty = ({
{...inputProps}
/>
)
case 'numberList':
return (
<ArrayInput
numeric
min={min}
max={max}
step={step}
placeholder={label}
disabled={disabled}
{...inputProps}
/>
)
case 'stringList':
return (
<ArrayInput
placeholder={label}
disabled={disabled}
{...inputProps}
/>
)
case 'text':
return <Input placeholder={label} {...inputProps} />
case 'codeBlock':
@ -831,9 +964,11 @@ const ObjectProperty = ({
<ObjectChildTable
name={name}
properties={properties}
columns={columns}
objectData={objectData}
isEditing={true}
rollups={rollups}
hiddenPropertyWidth={hiddenPropertyWidth}
size={size}
canAddRemove={canAddRemove}
{...inputProps}
@ -901,7 +1036,9 @@ ObjectProperty.propTypes = {
rollups: PropTypes.arrayOf(PropTypes.object),
canAddRemove: PropTypes.bool,
showCard: PropTypes.bool,
style: PropTypes.object
showDownload: PropTypes.bool,
style: PropTypes.object,
hiddenPropertyWidth: PropTypes.string
}
export default ObjectProperty