240 lines
6.1 KiB
JavaScript
240 lines
6.1 KiB
JavaScript
import { useEffect, useContext, useState } from 'react'
|
|
import { Table, Typography } from 'antd'
|
|
import PropTypes from 'prop-types'
|
|
import IdDisplay from './IdDisplay'
|
|
import { AuditOutlined } from '@ant-design/icons'
|
|
import { PrintServerContext } from '../context/PrintServerContext'
|
|
import moment from 'moment'
|
|
import TimeDisplay from '../common/TimeDisplay'
|
|
import PlusMinusIcon from '../../Icons/PlusMinusIcon'
|
|
import SubJobIcon from '../../Icons/SubJobIcon'
|
|
import PlayCircleIcon from '../../Icons/PlayCircleIcon'
|
|
|
|
const { Text } = Typography
|
|
|
|
const StockEventTable = ({ stockEvents }) => {
|
|
const { printServer } = useContext(PrintServerContext)
|
|
const [initialized, setInitialized] = useState(false)
|
|
const [stockEventsData, setStockEventsData] = useState(stockEvents)
|
|
|
|
useEffect(() => {
|
|
// Add WebSocket event listener for real-time updates
|
|
if (printServer && !initialized) {
|
|
setInitialized(true)
|
|
printServer.on('notify_stockevent_update', (updateData) => {
|
|
setStockEventsData((prevData) => {
|
|
return prevData.map((stockEvent) => {
|
|
if (stockEvent?._id) {
|
|
if (stockEvent._id === updateData._id) {
|
|
return {
|
|
...stockEvent,
|
|
...updateData
|
|
}
|
|
} else {
|
|
return stockEvent
|
|
}
|
|
}
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
return () => {
|
|
if (printServer && initialized) {
|
|
printServer.off('notify_stockevent_update')
|
|
}
|
|
}
|
|
}, [printServer, initialized])
|
|
|
|
useEffect(() => {
|
|
setStockEventsData(stockEvents)
|
|
}, [stockEvents])
|
|
|
|
const getTypeFilterProps = () => {
|
|
// Get unique types from the data
|
|
const uniqueTypes = [
|
|
...new Set(
|
|
stockEventsData.map((record) => {
|
|
const type = record.type.toLowerCase()
|
|
if (type === 'subjob') return 'Sub Job'
|
|
if (type === 'audit') return 'Audit Adjustment'
|
|
return type.charAt(0).toUpperCase() + type.slice(1)
|
|
})
|
|
)
|
|
]
|
|
|
|
return {
|
|
filters: uniqueTypes.map((type) => ({ text: type, value: type })),
|
|
onFilter: (value, record) => {
|
|
const recordType = record.type.toLowerCase()
|
|
if (recordType === 'subjob') {
|
|
return value === 'Sub Job'
|
|
} else if (recordType === 'audit') {
|
|
return value === 'Audit Adjustment'
|
|
}
|
|
return (
|
|
value === recordType.charAt(0).toUpperCase() + recordType.slice(1)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
const columns = [
|
|
{
|
|
title: '',
|
|
key: 'icon',
|
|
width: 50,
|
|
render: (record) => {
|
|
switch (record.type.toLowerCase()) {
|
|
case 'subjob':
|
|
return <SubJobIcon />
|
|
case 'audit':
|
|
return <AuditOutlined />
|
|
case 'initial':
|
|
return <PlayCircleIcon />
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
title: 'Type',
|
|
dataIndex: 'type',
|
|
key: 'type',
|
|
width: 200,
|
|
sorter: (a, b) => a.type.localeCompare(b.type),
|
|
...getTypeFilterProps(),
|
|
render: (type) => {
|
|
switch (type.toLowerCase()) {
|
|
case 'subjob':
|
|
return 'Sub Job'
|
|
case 'audit':
|
|
return 'Audit Adjustment'
|
|
default:
|
|
return type.charAt(0).toUpperCase() + type.slice(1)
|
|
}
|
|
}
|
|
},
|
|
{
|
|
title: <PlusMinusIcon />,
|
|
dataIndex: 'value',
|
|
key: 'value',
|
|
width: 100,
|
|
sorter: (a, b) => a.value - b.value,
|
|
render: (value, record) => {
|
|
const formattedValue = value.toFixed(2) + record.unit
|
|
return (
|
|
<Text type={value < 0 ? 'danger' : 'success'}>
|
|
{value > 0 ? '+' + formattedValue : formattedValue}
|
|
</Text>
|
|
)
|
|
}
|
|
},
|
|
{
|
|
title: 'Linked ID',
|
|
width: 100,
|
|
render: (record) => {
|
|
if (record.subJob) {
|
|
return (
|
|
<IdDisplay
|
|
id={record.subJob.number.toString().padStart(6, '0')}
|
|
longId={false}
|
|
type={'subjob'}
|
|
/>
|
|
)
|
|
}
|
|
if (record.stockAudit) {
|
|
return (
|
|
<IdDisplay
|
|
id={record.stockAudit._id}
|
|
longId={false}
|
|
type={'stockaudit'}
|
|
showHyperlink={true}
|
|
/>
|
|
)
|
|
}
|
|
return 'n/a'
|
|
}
|
|
},
|
|
{
|
|
title: 'Job ID',
|
|
width: 100,
|
|
render: (record) => {
|
|
if (record.subJob) {
|
|
return (
|
|
<IdDisplay
|
|
id={record.job._id}
|
|
longId={false}
|
|
type={'job'}
|
|
showHyperlink={true}
|
|
/>
|
|
)
|
|
}
|
|
return 'n/a'
|
|
}
|
|
},
|
|
{
|
|
title: 'Created At',
|
|
dataIndex: 'createdAt',
|
|
key: 'createdAt',
|
|
width: 180,
|
|
defaultSortOrder: 'descend',
|
|
sorter: (a, b) => moment(a.createdAt).unix() - moment(b.createdAt).unix(),
|
|
render: (createdAt) => {
|
|
if (createdAt) {
|
|
return <TimeDisplay dateTime={createdAt} />
|
|
} else {
|
|
return 'n/a'
|
|
}
|
|
}
|
|
},
|
|
{
|
|
title: 'Updated At',
|
|
dataIndex: 'updatedAt',
|
|
key: 'updatedAt',
|
|
width: 180,
|
|
sorter: (a, b) => moment(a.updatedAt).unix() - moment(b.updatedAt).unix(),
|
|
render: (updatedAt) => {
|
|
if (updatedAt) {
|
|
return <TimeDisplay dateTime={updatedAt} />
|
|
} else {
|
|
return 'n/a'
|
|
}
|
|
}
|
|
}
|
|
]
|
|
|
|
return (
|
|
<Table
|
|
dataSource={stockEventsData}
|
|
columns={columns}
|
|
rowKey={(record) => record._id}
|
|
pagination={false}
|
|
scroll={{ x: 'max-content' }}
|
|
/>
|
|
)
|
|
}
|
|
|
|
StockEventTable.propTypes = {
|
|
stockEvents: PropTypes.arrayOf(
|
|
PropTypes.shape({
|
|
type: PropTypes.string.isRequired,
|
|
value: PropTypes.number.isRequired,
|
|
subJobId: PropTypes.shape({
|
|
$oid: PropTypes.string.isRequired
|
|
}),
|
|
jobId: PropTypes.shape({
|
|
$oid: PropTypes.string.isRequired
|
|
}),
|
|
timestamp: PropTypes.shape({
|
|
$date: PropTypes.string.isRequired
|
|
}),
|
|
_id: PropTypes.shape({
|
|
$oid: PropTypes.string.isRequired
|
|
}).isRequired
|
|
})
|
|
).isRequired
|
|
}
|
|
|
|
export default StockEventTable
|