Added edit mode to tables.

This commit is contained in:
Tom Butcher 2025-12-27 13:50:30 +00:00
parent 38cafdb4a4
commit 50bc816e97

View File

@ -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 (
<Form form={form} component={false}>
{children}
</Form>
)
}
RowForm.propTypes = {
record: PropTypes.object,
isEditing: PropTypes.bool,
onRegister: PropTypes.func,
children: PropTypes.node
}
const EditableRow = ({ record, isEditing, onRegister, ...props }) => {
return (
<RowForm record={record} isEditing={isEditing} onRegister={onRegister}>
<tr {...props} />
</RowForm>
)
}
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}
/>
)
}
@ -714,6 +837,11 @@ const ObjectTable = forwardRef(
styles={{ body: { padding: 0 } }}
loading={record.isSkeleton}
variant={'borderless'}
>
<RowForm
record={record}
isEditing={isEditing}
onRegister={registerForm}
>
<Flex align={'center'} vertical gap={'middle'}>
<Descriptions
@ -750,7 +878,8 @@ const ObjectTable = forwardRef(
{...prop}
longId={false}
objectData={record}
isEditing={false}
isEditing={isEditing}
name={prop.name}
/>
</Descriptions.Item>
)
@ -760,7 +889,10 @@ const ObjectTable = forwardRef(
// Add actions if they exist (same as table)
if (rowActions.length > 0) {
descriptionItems.push(
<Descriptions.Item label={'Actions'} key={'actions'}>
<Descriptions.Item
label={'Actions'}
key={'actions'}
>
{renderActions(record)}
</Descriptions.Item>
)
@ -770,6 +902,7 @@ const ObjectTable = forwardRef(
})()}
</Descriptions>
</Flex>
</RowForm>
</Card>
</Col>
))}
@ -777,8 +910,25 @@ const ObjectTable = forwardRef(
)
}
return (
<>
const components = useMemo(
() => ({
body: {
row: EditableRow
}
}),
[]
)
const onRow = useCallback(
(record) => ({
record,
isEditing,
onRegister: registerForm
}),
[isEditing, registerForm]
)
const tableContent = (
<Flex gap={'middle'} vertical>
<Table
ref={tableRef}
@ -794,6 +944,8 @@ const ObjectTable = forwardRef(
showSorterTooltip={false}
style={{ height: '100%' }}
size={size}
components={components}
onRow={onRow}
/>
{cards ? (
<Spin indicator={<LoadingOutlined />} spinning={loading}>
@ -801,8 +953,9 @@ const ObjectTable = forwardRef(
</Spin>
) : null}
</Flex>
</>
)
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