diff --git a/src/components/Dashboard/common/ObjectTable.jsx b/src/components/Dashboard/common/ObjectTable.jsx
index fe142f3..e1257ed 100644
--- a/src/components/Dashboard/common/ObjectTable.jsx
+++ b/src/components/Dashboard/common/ObjectTable.jsx
@@ -5,6 +5,7 @@ import {
useEffect,
useState,
useCallback,
+ useMemo,
createElement
} from 'react'
import {
@@ -19,7 +20,8 @@ import {
Button,
Input,
Space,
- Tooltip
+ Tooltip,
+ Form
} from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
@@ -43,6 +45,46 @@ import { ElectronContext } from '../context/ElectronContext'
const logger = loglevel.getLogger('DasboardTable')
logger.setLevel(config.logLevel)
+const RowForm = ({ record, isEditing, onRegister, children }) => {
+ const [form] = Form.useForm()
+ useEffect(() => {
+ if (isEditing && record && !record.isSkeleton) {
+ form.setFieldsValue(record)
+ onRegister(record._id, form)
+ }
+ return () => {
+ if (record?._id) onRegister(record._id, null)
+ }
+ }, [isEditing, record, form, onRegister])
+
+ return (
+
+ )
+}
+
+RowForm.propTypes = {
+ record: PropTypes.object,
+ isEditing: PropTypes.bool,
+ onRegister: PropTypes.func,
+ children: PropTypes.node
+}
+
+const EditableRow = ({ record, isEditing, onRegister, ...props }) => {
+ return (
+
+
+
+ )
+}
+
+EditableRow.propTypes = {
+ record: PropTypes.object,
+ isEditing: PropTypes.bool,
+ onRegister: PropTypes.func
+}
+
const ObjectTable = forwardRef(
(
{
@@ -54,17 +96,26 @@ const ObjectTable = forwardRef(
cards = false,
visibleColumns = {},
masterFilter = {},
- size = 'middle'
+ size = 'middle',
+ onStateChange
},
ref
) => {
const { token } = useContext(AuthContext)
const { isElectron } = useContext(ElectronContext)
+ const onStateChangeRef = useRef(onStateChange)
+
+ useEffect(() => {
+ onStateChangeRef.current = onStateChange
+ }, [onStateChange])
const {
fetchObjects,
connected,
subscribeToObjectUpdates,
- subscribeToObjectTypeUpdates
+ subscribeToObjectTypeUpdates,
+ updateMultipleObjects,
+ lockObject,
+ unlockObject
} = useContext(ApiServerContext)
const isMobile = useMediaQuery({ maxWidth: 768 })
const navigate = useNavigate()
@@ -98,6 +149,21 @@ const ObjectTable = forwardRef(
const [lazyLoading, setLazyLoading] = useState(false)
const [tableData, setTableData] = useState([])
+ const [isEditing, setIsEditing] = useState(false)
+ const [editLoading, setEditLoading] = useState(false)
+ const rowFormsRef = useRef({})
+ const registerForm = useCallback((id, form) => {
+ if (form) {
+ rowFormsRef.current[id] = form
+ } else {
+ delete rowFormsRef.current[id]
+ }
+ }, [])
+
+ useEffect(() => {
+ onStateChangeRef.current?.({ isEditing, editLoading })
+ }, [isEditing, editLoading])
+
const subscribedIdsRef = useRef([])
// const [typeSubscribed, setTypeSubscribed] = useState(false)
const unsubscribesRef = useRef([])
@@ -300,6 +366,49 @@ const ObjectTable = forwardRef(
}
}, [fetchData])
+ const startEditing = useCallback(() => {
+ setIsEditing(true)
+ tableData.forEach((item) => {
+ if (!item.isSkeleton) {
+ console.log('Locking object:', item)
+ lockObject(item._id, type)
+ }
+ })
+ }, [tableData, lockObject, type])
+
+ const cancelEditing = useCallback(() => {
+ setIsEditing(false)
+ tableData.forEach((item) => {
+ if (!item.isSkeleton) {
+ unlockObject(item._id, type)
+ }
+ })
+ }, [tableData, unlockObject, type])
+
+ const handleUpdate = useCallback(async () => {
+ setEditLoading(true)
+ try {
+ const updates = await Promise.all(
+ Object.entries(rowFormsRef.current).map(async ([id, form]) => {
+ const values = await form.validateFields()
+ return { _id: id, ...values }
+ })
+ )
+ await updateMultipleObjects(type, updates)
+ setIsEditing(false)
+ reload()
+ tableData.forEach((item) => {
+ if (!item.isSkeleton) {
+ unlockObject(item._id, type)
+ }
+ })
+ } catch (err) {
+ console.error(err)
+ } finally {
+ setEditLoading(false)
+ }
+ }, [type, updateMultipleObjects, reload, tableData, unlockObject])
+
// Update event handler for real-time updates
const updateEventHandler = useCallback((id, updatedData) => {
setPages((prevPages) =>
@@ -316,6 +425,12 @@ const ObjectTable = forwardRef(
}
})
)
+
+ console.log('updatedData', updatedData)
+
+ if (rowFormsRef.current[id]) {
+ rowFormsRef.current[id].setFieldsValue(updatedData)
+ }
}, [])
// Store the latest updateEventHandler in a ref
@@ -349,6 +464,9 @@ const ObjectTable = forwardRef(
updateEventHandlerRef.current(itemId, updateData)
}
)
+ console.log('unsubscribe', unsubscribe)
+ console.log('subscribedIdsRef', subscribedIdsRef.current)
+ console.log('unsubscribesRef', unsubscribesRef.current)
subscribedIdsRef.current.push(itemId)
if (unsubscribe) {
unsubscribesRef.current.push(unsubscribe)
@@ -408,8 +526,8 @@ const ObjectTable = forwardRef(
}, [type, subscribeToObjectTypeUpdates, connected, newEventHandler])
const updateData = useCallback(
- (_id, updatedData) => {
- updateEventHandler({ _id, ...updatedData })
+ (id, updatedData) => {
+ updateEventHandler(id, updatedData)
},
[updateEventHandler]
)
@@ -445,7 +563,12 @@ const ObjectTable = forwardRef(
setData: (newData) => {
setPages([{ pageNum: 1, items: newData }])
},
- updateData
+ updateData,
+ startEditing,
+ cancelEditing,
+ handleUpdate,
+ isEditing,
+ editLoading
}))
useEffect(() => {
@@ -614,7 +737,7 @@ const ObjectTable = forwardRef(
{...prop}
longId={false}
objectData={record}
- isEditing={false}
+ isEditing={isEditing}
/>
)
}
@@ -715,61 +838,71 @@ const ObjectTable = forwardRef(
loading={record.isSkeleton}
variant={'borderless'}
>
-
-
- {(() => {
- const descriptionItems = []
+
+
+
+ {(() => {
+ const descriptionItems = []
- // Add columns in the order specified by model.columns (same logic as table)
- model.columns.forEach((colName) => {
- const prop = modelProperties.find(
- (p) => p.name === colName
- )
- if (prop) {
- // Check if column should be visible based on visibleColumns prop
- if (
- Object.keys(visibleColumns).length > 0 &&
- visibleColumns[prop.name] === false
- ) {
- return // Skip this column if it's not visible
+ // Add columns in the order specified by model.columns (same logic as table)
+ model.columns.forEach((colName) => {
+ const prop = modelProperties.find(
+ (p) => p.name === colName
+ )
+ if (prop) {
+ // Check if column should be visible based on visibleColumns prop
+ if (
+ Object.keys(visibleColumns).length > 0 &&
+ visibleColumns[prop.name] === false
+ ) {
+ return // Skip this column if it's not visible
+ }
+
+ descriptionItems.push(
+
+
+
+ )
}
+ })
+ // Add actions if they exist (same as table)
+ if (rowActions.length > 0) {
descriptionItems.push(
-
+ {renderActions(record)}
)
}
- })
- // Add actions if they exist (same as table)
- if (rowActions.length > 0) {
- descriptionItems.push(
-
- {renderActions(record)}
-
- )
- }
-
- return descriptionItems
- })()}
-
-
+ return descriptionItems
+ })()}
+
+
+
))}
@@ -777,32 +910,52 @@ const ObjectTable = forwardRef(
)
}
- return (
- <>
-
- }}
- onScroll={handleScroll}
- onChange={handleTableChange}
- showSorterTooltip={false}
- style={{ height: '100%' }}
- size={size}
- />
- {cards ? (
- } spinning={loading}>
- {renderCards()}
-
- ) : null}
-
- >
+ const components = useMemo(
+ () => ({
+ body: {
+ row: EditableRow
+ }
+ }),
+ []
)
+
+ const onRow = useCallback(
+ (record) => ({
+ record,
+ isEditing,
+ onRegister: registerForm
+ }),
+ [isEditing, registerForm]
+ )
+
+ const tableContent = (
+
+ }}
+ onScroll={handleScroll}
+ onChange={handleTableChange}
+ showSorterTooltip={false}
+ style={{ height: '100%' }}
+ size={size}
+ components={components}
+ onRow={onRow}
+ />
+ {cards ? (
+ } spinning={loading}>
+ {renderCards()}
+
+ ) : null}
+
+ )
+
+ return tableContent
}
)
@@ -818,7 +971,8 @@ ObjectTable.propTypes = {
cardRenderer: PropTypes.func,
visibleColumns: PropTypes.object,
masterFilter: PropTypes.object,
- size: PropTypes.string
+ size: PropTypes.string,
+ onStateChange: PropTypes.func
}
export default ObjectTable