From ca0128d9866c1f51f6bd919efa7b479207233e11 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Mon, 20 Jul 2026 02:06:04 +0100 Subject: [PATCH] Enhance ObjectChildTable component by adding detail modal functionality for property editing and viewing. Introduce new icons for edit and view actions, improve property resolution logic, and streamline item removal handling. Update table column rendering to accommodate hidden properties, enhancing user interaction and data management. --- .../Dashboard/common/ObjectChildTable.jsx | 269 +++++++++++++----- 1 file changed, 193 insertions(+), 76 deletions(-) diff --git a/src/components/Dashboard/common/ObjectChildTable.jsx b/src/components/Dashboard/common/ObjectChildTable.jsx index 9aa43c4..c58b9ba 100644 --- a/src/components/Dashboard/common/ObjectChildTable.jsx +++ b/src/components/Dashboard/common/ObjectChildTable.jsx @@ -3,8 +3,11 @@ import PropTypes from 'prop-types' import { Table, Skeleton, Card, Button, Flex, Typography, Modal } from 'antd' import PlusIcon from '../../Icons/PlusIcon' import ObjectProperty from './ObjectProperty' +import ObjectInfo from './ObjectInfo' import { LoadingOutlined } from '@ant-design/icons' import BinIcon from '../../Icons/BinIcon' +import EyeIcon from '../../Icons/EyeIcon' +import EditIcon from '../../Icons/EditIcon' const { Text, Link, Title } = Typography const DEFAULT_COLUMN_WIDTHS = { @@ -64,6 +67,7 @@ const ObjectChildTable = ({ value = [], rollups = [], onChange, + hiddenPropertyWidth = '300px', minimal = false, label = '', ...tableProps @@ -73,6 +77,9 @@ const ObjectChildTable = ({ const generatedRowKeysRef = useRef(new WeakMap()) const generatedRowKeyCountRef = useRef(0) const [minimalModelOpen, setMinimalModelOpen] = useState(false) + const [detailModalOpen, setDetailModalOpen] = useState(false) + const [detailModalRecord, setDetailModalRecord] = useState(null) + const [detailModalIndex, setDetailModalIndex] = useState(null) const getFallbackRowKey = (record) => { if (!record || typeof record !== 'object') { return `object-child-table-row-${String(record)}` @@ -124,15 +131,14 @@ const ObjectChildTable = ({ }, [columns, properties]) const resolvedProperties = useMemo(() => { - const explicit = orderedPropertyNames - .map((name) => propertyMap.get(name)) - .filter(Boolean) + const baseProperties = + columns && columns.length > 0 + ? orderedPropertyNames + .map((name) => propertyMap.get(name)) + .filter(Boolean) + : properties - const remaining = properties.filter( - (property) => !orderedPropertyNames.includes(property.name) - ) - - return [...explicit, ...remaining].filter((property) => { + return baseProperties.filter((property) => { if (!property?.name) return false if ( visibleColumns && @@ -142,13 +148,87 @@ const ObjectChildTable = ({ } return true }) - }, [orderedPropertyNames, propertyMap, properties, visibleColumns]) + }, [columns, orderedPropertyNames, propertyMap, properties, visibleColumns]) + + const hasHiddenProperties = useMemo(() => { + return ( + Array.isArray(columns) && + columns.length > 0 && + Array.isArray(properties) && + columns.length < properties.length + ) + }, [columns, properties]) // When used inside antd Form.Item without Form.List, `value` will be the controlled array. const itemsSource = useMemo(() => { return value ?? [] }, [value]) + const handleRemoveItem = useCallback( + (record, index) => { + const currentItems = Array.isArray(itemsSource) ? itemsSource : [] + + let newItems + if (typeof rowKey === 'string' && record[rowKey] != null) { + newItems = currentItems.filter( + (item) => item[rowKey] !== record[rowKey] + ) + } else if (typeof rowKey === 'function') { + const recordKey = getResolvedRecordKey(record) + newItems = currentItems.filter((item) => { + const itemKey = getResolvedRecordKey(item) + return itemKey !== recordKey + }) + } else { + newItems = currentItems.filter((_, i) => i !== index) + } + + if (typeof onChange === 'function') { + onChange(newItems) + } + }, + [itemsSource, onChange, rowKey, getResolvedRecordKey] + ) + + const handleOpenDetailModal = useCallback((record, index) => { + setDetailModalRecord({ ...record }) + setDetailModalIndex(index) + setDetailModalOpen(true) + }, []) + + const handleDetailPropertyChange = useCallback( + (propName, newVal) => { + const property = propertyMap.get(propName) + const resolved = resolveChangeValue(newVal, property?.type) + + setDetailModalRecord((prev) => + prev ? { ...prev, [propName]: resolved } : prev + ) + + if (detailModalIndex == null) return + + const currentItems = Array.isArray(itemsSource) ? [...itemsSource] : [] + const existingRowKey = + currentItems[detailModalIndex] != null + ? getResolvedRecordKey(currentItems[detailModalIndex]) + : null + const updatedItem = { + ...currentItems[detailModalIndex], + [propName]: resolved + } + + if (existingRowKey) { + generatedRowKeysRef.current.set(updatedItem, existingRowKey) + } + + currentItems[detailModalIndex] = updatedItem + if (typeof onChange === 'function') { + onChange(currentItems) + } + }, + [propertyMap, detailModalIndex, itemsSource, onChange, getResolvedRecordKey] + ) + const tableColumns = useMemo(() => { const propertyColumns = resolvedProperties.map((property) => ({ title: property.label || property.name, @@ -197,54 +277,44 @@ const ObjectChildTable = ({ } })) - const deleteColumn = - isEditing && canAddRemove + const actionsColumn = + hasHiddenProperties || (isEditing && canAddRemove) ? { title: '', - key: 'delete', - width: 10, + key: 'actions', + width: hasHiddenProperties && isEditing && canAddRemove ? 70 : 40, fixed: 'right', render: (_text, record, index) => { if (record?.isSkeleton) { return null } + return ( - + <> + + + + - {canAddRemove && ( -