Refactor Object Form and Child Table for Improved Data Handling and Computed Values Management
- Introduced applyComputedEntries function to streamline the merging of computed entries with existing object data, enhancing clarity and maintainability. - Updated NewObjectForm and ObjectForm components to utilize applyComputedEntries for setting initial form data and computed values, improving data handling consistency. - Refactored ObjectChildTable to ensure proper handling of value props and fallback mechanisms, enhancing user experience during editing. - Removed unused DeleteObjectModal from ObjectForm, simplifying the component structure.
This commit is contained in:
parent
978f444538
commit
c99227816e
@ -3,6 +3,7 @@ import { Form } from 'antd'
|
|||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import { useMessageContext } from '../context/MessageContext'
|
import { useMessageContext } from '../context/MessageContext'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import merge from 'lodash/merge'
|
||||||
import mergeWith from 'lodash/mergeWith'
|
import mergeWith from 'lodash/mergeWith'
|
||||||
import set from 'lodash/set'
|
import set from 'lodash/set'
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
@ -18,6 +19,16 @@ const buildObjectFromEntries = (entries = []) => {
|
|||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyComputedEntries = (base, entries = []) => {
|
||||||
|
const result = merge({}, base || {})
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const { namePath, value } = entry || {}
|
||||||
|
if (!Array.isArray(namePath) || value === undefined) return
|
||||||
|
set(result, namePath, value)
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
const arrayReplaceCustomizer = (objValue, srcValue, key) => {
|
const arrayReplaceCustomizer = (objValue, srcValue, key) => {
|
||||||
if (Array.isArray(srcValue)) {
|
if (Array.isArray(srcValue)) {
|
||||||
return srcValue
|
return srcValue
|
||||||
@ -172,12 +183,9 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
|
|||||||
if (Object.keys(defaultValues).length > 0) {
|
if (Object.keys(defaultValues).length > 0) {
|
||||||
// Calculate computed values for initial data
|
// Calculate computed values for initial data
|
||||||
const computedEntries = calculateComputedValues(defaultValues, model)
|
const computedEntries = calculateComputedValues(defaultValues, model)
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
const initialFormData = applyComputedEntries(
|
||||||
const initialFormData = mergeWith(
|
|
||||||
{},
|
|
||||||
defaultValues,
|
defaultValues,
|
||||||
computedValuesObject,
|
computedEntries
|
||||||
arrayReplaceCustomizer
|
|
||||||
)
|
)
|
||||||
form.setFieldsValue(initialFormData)
|
form.setFieldsValue(initialFormData)
|
||||||
setObjectData((prev) =>
|
setObjectData((prev) =>
|
||||||
@ -196,12 +204,9 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
|
|||||||
try {
|
try {
|
||||||
setSubmitLoading(true)
|
setSubmitLoading(true)
|
||||||
const computedEntries = calculateComputedValues(objectData, model)
|
const computedEntries = calculateComputedValues(objectData, model)
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
const computedObjectData = applyComputedEntries(
|
||||||
const computedObjectData = mergeWith(
|
|
||||||
{},
|
|
||||||
objectData,
|
objectData,
|
||||||
computedValuesObject,
|
computedEntries
|
||||||
arrayReplaceCustomizer
|
|
||||||
)
|
)
|
||||||
const newObject = await createObject(type, computedObjectData)
|
const newObject = await createObject(type, computedObjectData)
|
||||||
showSuccess('Object created successfully')
|
showSuccess('Object created successfully')
|
||||||
@ -253,14 +258,7 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge all values (user input + computed values)
|
const allValues = applyComputedEntries(allFormValues, computedEntries)
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
|
||||||
const allValues = mergeWith(
|
|
||||||
{},
|
|
||||||
allFormValues,
|
|
||||||
computedValuesObject,
|
|
||||||
arrayReplaceCustomizer
|
|
||||||
)
|
|
||||||
setObjectData((prev) => {
|
setObjectData((prev) => {
|
||||||
return mergeWith({}, prev, allValues, arrayReplaceCustomizer)
|
return mergeWith({}, prev, allValues, arrayReplaceCustomizer)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -64,7 +64,7 @@ const ObjectChildTable = ({
|
|||||||
additionalColumns = [],
|
additionalColumns = [],
|
||||||
emptyText = 'No items',
|
emptyText = 'No items',
|
||||||
isEditing = false,
|
isEditing = false,
|
||||||
value = [],
|
value,
|
||||||
rollups = [],
|
rollups = [],
|
||||||
onChange,
|
onChange,
|
||||||
hiddenPropertyWidth = '300px',
|
hiddenPropertyWidth = '300px',
|
||||||
@ -159,10 +159,13 @@ const ObjectChildTable = ({
|
|||||||
)
|
)
|
||||||
}, [columns, properties])
|
}, [columns, properties])
|
||||||
|
|
||||||
// When used inside antd Form.Item without Form.List, `value` will be the controlled array.
|
// Form.Item may not have hydrated yet; fall back to objectData the same way
|
||||||
|
// rollups already do.
|
||||||
const itemsSource = useMemo(() => {
|
const itemsSource = useMemo(() => {
|
||||||
return value ?? []
|
if (Array.isArray(value)) return value
|
||||||
}, [value])
|
if (name && Array.isArray(objectData?.[name])) return objectData[name]
|
||||||
|
return []
|
||||||
|
}, [value, name, objectData])
|
||||||
|
|
||||||
const handleRemoveItem = useCallback(
|
const handleRemoveItem = useCallback(
|
||||||
(record, index) => {
|
(record, index) => {
|
||||||
@ -265,12 +268,11 @@ const ObjectChildTable = ({
|
|||||||
<ObjectProperty
|
<ObjectProperty
|
||||||
{...property}
|
{...property}
|
||||||
longId={false}
|
longId={false}
|
||||||
|
inTable={true}
|
||||||
objectData={record}
|
objectData={record}
|
||||||
parentData={objectData}
|
parentData={objectData}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
useFormItem={false}
|
useFormItem={false}
|
||||||
name={undefined}
|
|
||||||
value={record[property.name]}
|
|
||||||
onChange={handleCellChange}
|
onChange={handleCellChange}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -384,10 +386,7 @@ const ObjectChildTable = ({
|
|||||||
const rollupDataSource = useMemo(() => {
|
const rollupDataSource = useMemo(() => {
|
||||||
if (!rollups || rollups.length === 0) return []
|
if (!rollups || rollups.length === 0) return []
|
||||||
|
|
||||||
// Use value from form/props, or fall back to objectData when entering edit mode
|
const itemsForRollup = itemsSource
|
||||||
// (form may not have populated the field yet)
|
|
||||||
const itemsForRollup =
|
|
||||||
value ?? (name && objectData ? objectData[name] : null) ?? []
|
|
||||||
|
|
||||||
// Build parent object with children array for rollup functions (e.g. objectData.parts)
|
// Build parent object with children array for rollup functions (e.g. objectData.parts)
|
||||||
const updatedObjectData = { ...objectData }
|
const updatedObjectData = { ...objectData }
|
||||||
@ -420,7 +419,7 @@ const ObjectChildTable = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return [summaryRow]
|
return [summaryRow]
|
||||||
}, [properties, rollups, objectData, value, name])
|
}, [properties, rollups, objectData, itemsSource, name])
|
||||||
|
|
||||||
const rollupColumns = useMemo(() => {
|
const rollupColumns = useMemo(() => {
|
||||||
const propertyColumns = resolvedProperties.map((property, index) => {
|
const propertyColumns = resolvedProperties.map((property, index) => {
|
||||||
@ -634,7 +633,8 @@ const ObjectChildTable = ({
|
|||||||
setMinimalModelOpen(true)
|
setMinimalModelOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value?.length || 0} {value?.length == 1 ? 'item' : 'items'}
|
{itemsSource?.length || 0}{' '}
|
||||||
|
{itemsSource?.length == 1 ? 'item' : 'items'}
|
||||||
</Link>
|
</Link>
|
||||||
<Modal
|
<Modal
|
||||||
open={minimalModelOpen}
|
open={minimalModelOpen}
|
||||||
|
|||||||
@ -79,6 +79,19 @@ const buildObjectFromEntries = (entries = []) => {
|
|||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Patch computed fields onto a full object. Building them as a sparse tree and
|
||||||
|
// merge-replacing arrays would wipe stored child-row fields (shipment, amount,
|
||||||
|
// etc.) while leaving calculated columns intact.
|
||||||
|
const applyComputedEntries = (base, entries = []) => {
|
||||||
|
const result = merge({}, base || {})
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const { namePath, value } = entry || {}
|
||||||
|
if (!Array.isArray(namePath) || value === undefined) return
|
||||||
|
set(result, namePath, value)
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
const getEditDisabled = (model, objectData, userProfile) => {
|
const getEditDisabled = (model, objectData, userProfile) => {
|
||||||
if (model?.readOnly === true) {
|
if (model?.readOnly === true) {
|
||||||
return true
|
return true
|
||||||
@ -356,12 +369,9 @@ const ObjectForm = forwardRef(
|
|||||||
serverObjectData.current,
|
serverObjectData.current,
|
||||||
model
|
model
|
||||||
)
|
)
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
const resetFormData = applyComputedEntries(
|
||||||
const resetFormData = mergeWith(
|
|
||||||
{},
|
|
||||||
serverObjectData.current,
|
serverObjectData.current,
|
||||||
computedValuesObject,
|
computedEntries
|
||||||
arrayReplaceCustomizer
|
|
||||||
)
|
)
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
isEditingRef.current = false
|
isEditingRef.current = false
|
||||||
@ -498,13 +508,7 @@ const ObjectForm = forwardRef(
|
|||||||
|
|
||||||
// Calculate and set computed values on initial load
|
// Calculate and set computed values on initial load
|
||||||
const computedEntries = calculateComputedValues(data, model)
|
const computedEntries = calculateComputedValues(data, model)
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
const initialFormData = applyComputedEntries(data, computedEntries)
|
||||||
const initialFormData = mergeWith(
|
|
||||||
{},
|
|
||||||
data,
|
|
||||||
computedValuesObject,
|
|
||||||
arrayReplaceCustomizer
|
|
||||||
)
|
|
||||||
setObjectData({ ...initialFormData, _isEditing: isEditingRef.current })
|
setObjectData({ ...initialFormData, _isEditing: isEditingRef.current })
|
||||||
form.setFieldsValue(initialFormData)
|
form.setFieldsValue(initialFormData)
|
||||||
setFetchLoading(false)
|
setFetchLoading(false)
|
||||||
@ -703,17 +707,11 @@ const ObjectForm = forwardRef(
|
|||||||
setIsEditing(true)
|
setIsEditing(true)
|
||||||
|
|
||||||
const computedEntries = calculateComputedValues(objectData, model)
|
const computedEntries = calculateComputedValues(objectData, model)
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
|
||||||
setObjectData((prev) => ({
|
|
||||||
...prev,
|
|
||||||
...computedValuesObject,
|
|
||||||
_isEditing: true
|
|
||||||
}))
|
|
||||||
const nextObjectData = {
|
const nextObjectData = {
|
||||||
...objectData,
|
...applyComputedEntries(objectData, computedEntries),
|
||||||
...computedValuesObject,
|
|
||||||
_isEditing: true
|
_isEditing: true
|
||||||
}
|
}
|
||||||
|
setObjectData(nextObjectData)
|
||||||
onStateChangeRef.current({
|
onStateChangeRef.current({
|
||||||
isEditing: true,
|
isEditing: true,
|
||||||
objectData: nextObjectData,
|
objectData: nextObjectData,
|
||||||
@ -868,12 +866,9 @@ const ObjectForm = forwardRef(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
const mergedFormValues = applyComputedEntries(
|
||||||
const mergedFormValues = mergeWith(
|
|
||||||
{},
|
|
||||||
allFormValues,
|
allFormValues,
|
||||||
computedValuesObject,
|
computedEntries
|
||||||
arrayReplaceCustomizer
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mergedFormValues._isEditing = isEditingRef.current
|
mergedFormValues._isEditing = isEditingRef.current
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user