Added edit mode to tables.
This commit is contained in:
parent
38cafdb4a4
commit
50bc816e97
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user