Improved card design for object table.
This commit is contained in:
parent
4f0fe89398
commit
b4512a1948
@ -11,7 +11,7 @@ const EmailDisplay = ({ email, showCopy = true, showLink = false }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex>
|
<Flex style={{ minWidth: 0 }}>
|
||||||
{showLink ? (
|
{showLink ? (
|
||||||
<Link href={`mailto:${email}`} style={{ marginRight: 8 }}>
|
<Link href={`mailto:${email}`} style={{ marginRight: 8 }}>
|
||||||
{email}
|
{email}
|
||||||
|
|||||||
111
src/components/Dashboard/common/ObjectCard.jsx
Normal file
111
src/components/Dashboard/common/ObjectCard.jsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { Descriptions, Card, Flex, Divider } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectProperty from './ObjectProperty'
|
||||||
|
import { createElement } from 'react'
|
||||||
|
|
||||||
|
const ObjectCard = ({
|
||||||
|
model,
|
||||||
|
modelProperties,
|
||||||
|
visibleColumns = {},
|
||||||
|
record,
|
||||||
|
isEditing = false,
|
||||||
|
rowActions = [],
|
||||||
|
renderActions,
|
||||||
|
cardStyle = 'borderless'
|
||||||
|
}) => {
|
||||||
|
const descriptionItems = []
|
||||||
|
const modelIcon = createElement(model.icon, { style: { fontSize: 24 } })
|
||||||
|
|
||||||
|
model.columns.forEach((colName) => {
|
||||||
|
const prop = modelProperties.find((p) => p.name === colName)
|
||||||
|
if (prop) {
|
||||||
|
if (
|
||||||
|
(Object.keys(visibleColumns).length > 0 &&
|
||||||
|
visibleColumns[prop.name] === false) ||
|
||||||
|
prop.name == 'name'
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionItems.push(
|
||||||
|
<Descriptions.Item label={prop.label} key={prop.name} colspan={2}>
|
||||||
|
<ObjectProperty
|
||||||
|
{...prop}
|
||||||
|
longId={false}
|
||||||
|
objectData={record}
|
||||||
|
isEditing={isEditing}
|
||||||
|
name={prop.name}
|
||||||
|
/>
|
||||||
|
</Descriptions.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var actions = undefined
|
||||||
|
|
||||||
|
if (rowActions.length > 0) {
|
||||||
|
actions = renderActions(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
styles={{ body: { padding: 18 } }}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
variant={cardStyle}
|
||||||
|
>
|
||||||
|
<Flex vertical gap={8}>
|
||||||
|
{visibleColumns?.name == true && (
|
||||||
|
<Flex align='center' gap={12}>
|
||||||
|
{modelIcon}
|
||||||
|
<ObjectProperty
|
||||||
|
{...model.properties.find((p) => p.name === 'name')}
|
||||||
|
objectData={record}
|
||||||
|
isEditing={isEditing}
|
||||||
|
style={{
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: '600',
|
||||||
|
lineHeight: 1,
|
||||||
|
overflow: 'visible'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{visibleColumns?.state == true && (
|
||||||
|
<ObjectProperty
|
||||||
|
{...model.properties.find((p) => p.name === 'state')}
|
||||||
|
objectData={record}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
<Descriptions
|
||||||
|
column={1}
|
||||||
|
size='small'
|
||||||
|
style={{ width: '100%', tableLayout: 'fixed' }}
|
||||||
|
className='objectTableDescritions'
|
||||||
|
>
|
||||||
|
{descriptionItems}
|
||||||
|
</Descriptions>
|
||||||
|
{actions && (
|
||||||
|
<>
|
||||||
|
<Divider style={{ margin: '2px 0 0 0' }} />
|
||||||
|
<Flex align='flex-end' gap={10}>
|
||||||
|
{actions}
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectCard.propTypes = {
|
||||||
|
model: PropTypes.object.isRequired,
|
||||||
|
modelProperties: PropTypes.array.isRequired,
|
||||||
|
visibleColumns: PropTypes.object,
|
||||||
|
record: PropTypes.object.isRequired,
|
||||||
|
isEditing: PropTypes.bool,
|
||||||
|
rowActions: PropTypes.array,
|
||||||
|
renderActions: PropTypes.func.isRequired,
|
||||||
|
cardStyle: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectCard
|
||||||
@ -92,6 +92,7 @@ const ObjectProperty = ({
|
|||||||
loading = false,
|
loading = false,
|
||||||
rollups = [],
|
rollups = [],
|
||||||
showCard = true,
|
showCard = true,
|
||||||
|
style = {},
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
if (value && typeof value == 'function' && objectData) {
|
if (value && typeof value == 'function' && objectData) {
|
||||||
@ -310,7 +311,7 @@ const ObjectProperty = ({
|
|||||||
case 'text':
|
case 'text':
|
||||||
if (value != null && value != '') {
|
if (value != null && value != '') {
|
||||||
return (
|
return (
|
||||||
<Text ellipsis>
|
<Text ellipsis style={style}>
|
||||||
{prefix}
|
{prefix}
|
||||||
{value}
|
{value}
|
||||||
{suffix}
|
{suffix}
|
||||||
@ -888,7 +889,8 @@ ObjectProperty.propTypes = {
|
|||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
rollups: PropTypes.arrayOf(PropTypes.object),
|
rollups: PropTypes.arrayOf(PropTypes.object),
|
||||||
canAddRemove: PropTypes.bool,
|
canAddRemove: PropTypes.bool,
|
||||||
showCard: PropTypes.bool
|
showCard: PropTypes.bool,
|
||||||
|
style: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectProperty
|
export default ObjectProperty
|
||||||
|
|||||||
@ -11,10 +11,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Card,
|
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Descriptions,
|
|
||||||
Flex,
|
Flex,
|
||||||
Spin,
|
Spin,
|
||||||
Button,
|
Button,
|
||||||
@ -35,6 +33,7 @@ import {
|
|||||||
getModelByName
|
getModelByName
|
||||||
} from '../../../database/ObjectModels'
|
} from '../../../database/ObjectModels'
|
||||||
import ObjectProperty from './ObjectProperty'
|
import ObjectProperty from './ObjectProperty'
|
||||||
|
import ObjectCard from './ObjectCard'
|
||||||
import FilterSidebar from './FilterSidebar'
|
import FilterSidebar from './FilterSidebar'
|
||||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||||
import CheckIcon from '../../Icons/CheckIcon'
|
import CheckIcon from '../../Icons/CheckIcon'
|
||||||
@ -128,7 +127,7 @@ const ObjectTable = forwardRef(
|
|||||||
adjustedScrollHeight = 'calc(var(--unit-100vh) - 316px)'
|
adjustedScrollHeight = 'calc(var(--unit-100vh) - 316px)'
|
||||||
}
|
}
|
||||||
if (cards) {
|
if (cards) {
|
||||||
adjustedScrollHeight = 'calc(var(--unit-100vh) - 280px)'
|
adjustedScrollHeight = 'calc(var(--unit-100vh) - 210px)'
|
||||||
}
|
}
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
adjustedScrollHeight = 'calc(var(--unit-100vh) - 244px)'
|
adjustedScrollHeight = 'calc(var(--unit-100vh) - 244px)'
|
||||||
@ -857,78 +856,25 @@ const ObjectTable = forwardRef(
|
|||||||
>
|
>
|
||||||
{tableData.map((record) => (
|
{tableData.map((record) => (
|
||||||
<Col xs={24} sm={12} md={12} lg={8} xl={6} xxl={6} key={record._id}>
|
<Col xs={24} sm={12} md={12} lg={8} xl={6} xxl={6} key={record._id}>
|
||||||
<Card
|
<div style={{ width: '100%', overflow: 'hidden' }}>
|
||||||
style={{ width: '100%', overflow: 'hidden' }}
|
|
||||||
styles={{ body: { padding: 0 } }}
|
|
||||||
loading={record.isSkeleton}
|
|
||||||
variant={'borderless'}
|
|
||||||
>
|
|
||||||
<RowForm
|
<RowForm
|
||||||
record={record}
|
record={record}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
onRegister={registerForm}
|
onRegister={registerForm}
|
||||||
>
|
>
|
||||||
<Flex align={'center'} vertical gap={'middle'}>
|
<Flex align={'center'} vertical gap={'middle'}>
|
||||||
<Descriptions
|
<ObjectCard
|
||||||
column={1}
|
model={model}
|
||||||
size='small'
|
modelProperties={modelProperties}
|
||||||
bordered={true}
|
visibleColumns={visibleColumns}
|
||||||
style={{ width: '100%', tableLayout: 'fixed' }}
|
record={record}
|
||||||
className='objectTableDescritions'
|
isEditing={isEditing}
|
||||||
>
|
rowActions={rowActions}
|
||||||
{(() => {
|
renderActions={renderActions}
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptionItems.push(
|
|
||||||
<Descriptions.Item
|
|
||||||
label={prop.label}
|
|
||||||
key={prop.name}
|
|
||||||
colspan={2}
|
|
||||||
>
|
|
||||||
<ObjectProperty
|
|
||||||
{...prop}
|
|
||||||
longId={false}
|
|
||||||
objectData={record}
|
|
||||||
isEditing={isEditing}
|
|
||||||
name={prop.name}
|
|
||||||
/>
|
|
||||||
</Descriptions.Item>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add actions if they exist (same as table)
|
|
||||||
if (rowActions.length > 0) {
|
|
||||||
descriptionItems.push(
|
|
||||||
<Descriptions.Item
|
|
||||||
label={'Actions'}
|
|
||||||
key={'actions'}
|
|
||||||
>
|
|
||||||
{renderActions(record)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptionItems
|
|
||||||
})()}
|
|
||||||
</Descriptions>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</RowForm>
|
</RowForm>
|
||||||
</Card>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
@ -955,28 +901,29 @@ const ObjectTable = forwardRef(
|
|||||||
|
|
||||||
const tableContent = (
|
const tableContent = (
|
||||||
<Flex gap={'middle'} vertical style={{ flex: 1, minWidth: 0 }}>
|
<Flex gap={'middle'} vertical style={{ flex: 1, minWidth: 0 }}>
|
||||||
<Table
|
|
||||||
ref={tableRef}
|
|
||||||
dataSource={tableData}
|
|
||||||
columns={columnsWithSkeleton}
|
|
||||||
className={cards ? 'dashboard-cards-header' : 'dashboard-table'}
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: adjustedScrollHeight }}
|
|
||||||
rowKey='_id'
|
|
||||||
loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }}
|
|
||||||
onScroll={handleScroll}
|
|
||||||
onChange={handleTableChange}
|
|
||||||
showSorterTooltip={false}
|
|
||||||
style={{ height: '100%' }}
|
|
||||||
size={size}
|
|
||||||
components={components}
|
|
||||||
onRow={onRow}
|
|
||||||
/>
|
|
||||||
{cards ? (
|
{cards ? (
|
||||||
<Spin indicator={<LoadingOutlined />} spinning={loading}>
|
<Spin indicator={<LoadingOutlined />} spinning={loading}>
|
||||||
{renderCards()}
|
{renderCards()}
|
||||||
</Spin>
|
</Spin>
|
||||||
) : null}
|
) : (
|
||||||
|
<Table
|
||||||
|
ref={tableRef}
|
||||||
|
dataSource={tableData}
|
||||||
|
columns={columnsWithSkeleton}
|
||||||
|
className='dashboard-table'
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ y: adjustedScrollHeight }}
|
||||||
|
rowKey='_id'
|
||||||
|
loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
showSorterTooltip={false}
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
size={size}
|
||||||
|
components={components}
|
||||||
|
onRow={onRow}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user