Enhance Inventory Overview and Model History Display Components
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Updated InventoryOverview to include additional statistics for part stock, filament stock, product stock, and stock events, improving data visibility. - Refactored InfoCollapse components to support new statistics and ensure proper state management for collapsible sections. - Enhanced ModelHistoryDisplay to support both line and bar chart types, providing more flexibility in data visualization. - Added new stats definitions for FilamentStock, PartStock, ProductStock, and StockEvent models to support the expanded inventory features.
This commit is contained in:
parent
e0a5ff3cf8
commit
1edd48c1d7
@ -1,15 +1,29 @@
|
||||
import { Flex } from 'antd'
|
||||
import useCollapseState from '../hooks/useCollapseState'
|
||||
import useCollapseState from '../hooks/useCollapseState.jsx'
|
||||
import StatsDisplay from '../common/StatsDisplay'
|
||||
import ModelHistoryDisplay from '../common/ModelHistoryDisplay'
|
||||
import InfoCollapse from '../common/InfoCollapse'
|
||||
import ScrollBox from '../common/ScrollBox'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
|
||||
const InventoryOverview = () => {
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'InventoryOverview',
|
||||
{
|
||||
inventoryStats: true,
|
||||
purchaseOrderStats: true
|
||||
partStockStats: true,
|
||||
filamentStockStats: true,
|
||||
productStockStats: true,
|
||||
purchaseOrderStats: true,
|
||||
stockEventStats: true,
|
||||
partStockHistory: true,
|
||||
newPartStocks: true,
|
||||
filamentStockHistory: true,
|
||||
unconsumedFilamentStocks: true,
|
||||
productStockHistory: true,
|
||||
draftProductStocks: true,
|
||||
purchaseOrderHistory: true,
|
||||
draftPurchaseOrders: true,
|
||||
stockEventHistory: true
|
||||
}
|
||||
)
|
||||
|
||||
@ -25,15 +39,15 @@ const InventoryOverview = () => {
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Inventory Statistics'
|
||||
title='Part Stock Statistics'
|
||||
icon={null}
|
||||
active={collapseState.inventoryStats}
|
||||
canCollapse={false}
|
||||
active={collapseState.partStockStats}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('inventoryStats', isActive)
|
||||
updateCollapseState('partStockStats', isActive)
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='inventoryStats'
|
||||
canCollapse={false}
|
||||
collapseKey='partStockStats'
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
@ -41,10 +55,49 @@ const InventoryOverview = () => {
|
||||
wrap='wrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<Flex gap='middle' wrap='wrap'>
|
||||
<StatsDisplay objectType='partStock' />
|
||||
<StatsDisplay objectType='filamentStock' />
|
||||
</Flex>
|
||||
<StatsDisplay objectType='partStock' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
|
||||
<InfoCollapse
|
||||
title='Filament Stock Statistics'
|
||||
icon={null}
|
||||
canCollapse={false}
|
||||
active={collapseState.filamentStockStats}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('filamentStockStats', isActive)
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='filamentStockStats'
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
gap='middle'
|
||||
wrap='wrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<StatsDisplay objectType='filamentStock' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
|
||||
<InfoCollapse
|
||||
title='Product Stock Statistics'
|
||||
icon={null}
|
||||
canCollapse={false}
|
||||
active={collapseState.productStockStats}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('productStockStats', isActive)
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='productStockStats'
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
gap='middle'
|
||||
wrap='wrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<StatsDisplay objectType='productStock' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
|
||||
@ -69,8 +122,29 @@ const InventoryOverview = () => {
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
|
||||
<InfoCollapse
|
||||
title='Stock Event Statistics'
|
||||
icon={null}
|
||||
canCollapse={false}
|
||||
active={collapseState.stockEventStats}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('stockEventStats', isActive)
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='stockEventStats'
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
gap='middle'
|
||||
wrap='wrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<StatsDisplay objectType='stockEvent' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
|
||||
<Flex gap='large' wrap='wrap'>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }}>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }} gap='large'>
|
||||
<InfoCollapse
|
||||
title='Part Stock History'
|
||||
icon={null}
|
||||
@ -80,9 +154,23 @@ const InventoryOverview = () => {
|
||||
}
|
||||
collapseKey='partStockHistory'
|
||||
canCollapse={false}
|
||||
></InfoCollapse>
|
||||
>
|
||||
<ModelHistoryDisplay objectType='partStock' chartType='line' />
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='New Part Stocks'
|
||||
icon={null}
|
||||
active={collapseState.newPartStocks}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('newPartStocks', isActive)
|
||||
}
|
||||
collapseKey='newPartStocks'
|
||||
canCollapse={false}
|
||||
>
|
||||
<ObjectTable type='partStock' masterFilter={{ state: 'new' }} />
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }}>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }} gap='large'>
|
||||
<InfoCollapse
|
||||
title='Filament Stock History'
|
||||
icon={null}
|
||||
@ -92,8 +180,95 @@ const InventoryOverview = () => {
|
||||
}
|
||||
canCollapse={false}
|
||||
collapseKey='filamentStockHistory'
|
||||
></InfoCollapse>
|
||||
>
|
||||
<ModelHistoryDisplay
|
||||
objectType='filamentStock'
|
||||
chartType='line'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Unconsumed Filament Stocks'
|
||||
icon={null}
|
||||
active={collapseState.unconsumedFilamentStocks}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('unconsumedFilamentStocks', isActive)
|
||||
}
|
||||
canCollapse={false}
|
||||
collapseKey='unconsumedFilamentStocks'
|
||||
>
|
||||
<ObjectTable
|
||||
type='filamentStock'
|
||||
masterFilter={{ state: 'unconsumed' }}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex gap='large' wrap='wrap'>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }} gap='large'>
|
||||
<InfoCollapse
|
||||
title='Product Stock History'
|
||||
icon={null}
|
||||
active={collapseState.productStockHistory}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('productStockHistory', isActive)
|
||||
}
|
||||
collapseKey='productStockHistory'
|
||||
canCollapse={false}
|
||||
>
|
||||
<ModelHistoryDisplay
|
||||
objectType='productStock'
|
||||
chartType='line'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Draft Product Stocks'
|
||||
icon={null}
|
||||
active={collapseState.draftProductStocks}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('draftProductStocks', isActive)
|
||||
}
|
||||
collapseKey='draftProductStocks'
|
||||
canCollapse={false}
|
||||
>
|
||||
<ObjectTable
|
||||
type='productStock'
|
||||
masterFilter={{ state: 'draft' }}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }} gap='large'>
|
||||
<InfoCollapse
|
||||
title='Purchase Order History'
|
||||
icon={null}
|
||||
active={collapseState.purchaseOrderHistory}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('purchaseOrderHistory', isActive)
|
||||
}
|
||||
canCollapse={false}
|
||||
collapseKey='purchaseOrderHistory'
|
||||
>
|
||||
<ModelHistoryDisplay objectType='purchaseOrder' />
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Draft Purchase Orders'
|
||||
icon={null}
|
||||
active={collapseState.draftPurchaseOrders}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('draftPurchaseOrders', isActive)
|
||||
}
|
||||
canCollapse={false}
|
||||
collapseKey='draftPurchaseOrders'
|
||||
>
|
||||
<ObjectTable
|
||||
type='purchaseOrder'
|
||||
masterFilter={{ state: 'draft' }}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex gap='large' wrap='wrap'>
|
||||
<Flex flex={1} vertical style={{ minWidth: '300px' }}>
|
||||
<InfoCollapse
|
||||
title='Stock Event History'
|
||||
@ -104,7 +279,9 @@ const InventoryOverview = () => {
|
||||
}
|
||||
canCollapse={false}
|
||||
collapseKey='stockEventHistory'
|
||||
></InfoCollapse>
|
||||
>
|
||||
<ModelHistoryDisplay objectType='stockEvent' chartType='line' />
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@ -4,6 +4,8 @@ import {
|
||||
ResponsiveContainer,
|
||||
BarChart,
|
||||
Bar,
|
||||
LineChart,
|
||||
Line,
|
||||
CartesianGrid,
|
||||
XAxis,
|
||||
YAxis,
|
||||
@ -26,7 +28,8 @@ const ModelHistoryDisplay = ({
|
||||
startDate,
|
||||
endDate,
|
||||
styles,
|
||||
height = 400
|
||||
height = 400,
|
||||
chartType = 'bar'
|
||||
}) => {
|
||||
const { getModelHistory, connected } = useContext(ApiServerContext)
|
||||
const { token } = useContext(AuthContext)
|
||||
@ -120,7 +123,9 @@ const ModelHistoryDisplay = ({
|
||||
return null
|
||||
}
|
||||
|
||||
const modelStats = model.stats || []
|
||||
const modelStats = (model.stats || []).filter(
|
||||
(statDef) => statDef.history !== false
|
||||
)
|
||||
|
||||
if (modelStats.length === 0) {
|
||||
return null
|
||||
@ -323,6 +328,70 @@ const ModelHistoryDisplay = ({
|
||||
</Space.Compact>
|
||||
)
|
||||
|
||||
const axisTickStyle = {
|
||||
fill: isDarkMode ? '#d9d9d9' : '#595959',
|
||||
fontSize: 12
|
||||
}
|
||||
|
||||
const tooltipStyle = {
|
||||
backgroundColor: isDarkMode ? '#1f1f1f' : '#ffffff',
|
||||
border: `1px solid ${isDarkMode ? '#303030' : '#f0f0f0'}`,
|
||||
borderRadius: 8
|
||||
}
|
||||
|
||||
const renderChart = () => {
|
||||
const commonElements = (
|
||||
<>
|
||||
<CartesianGrid
|
||||
strokeDasharray='3 3'
|
||||
stroke={isDarkMode ? '#303030' : '#f0f0f0'}
|
||||
/>
|
||||
<XAxis dataKey='dateFormatted' tick={axisTickStyle} />
|
||||
<YAxis tick={axisTickStyle} />
|
||||
<Tooltip contentStyle={tooltipStyle} />
|
||||
<Legend />
|
||||
</>
|
||||
)
|
||||
|
||||
if (chartType === 'line') {
|
||||
return (
|
||||
<LineChart data={chartRows}>
|
||||
{commonElements}
|
||||
{modelStats.map((statDef, index) => {
|
||||
const category = statDef.label || statDef.name
|
||||
return (
|
||||
<Line
|
||||
key={category}
|
||||
type='monotone'
|
||||
dataKey={category}
|
||||
stroke={colorRange[index] || colors.default}
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</LineChart>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<BarChart data={chartRows}>
|
||||
{commonElements}
|
||||
{modelStats.map((statDef, index) => {
|
||||
const category = statDef.label || statDef.name
|
||||
return (
|
||||
<Bar
|
||||
key={category}
|
||||
dataKey={category}
|
||||
stackId='history'
|
||||
fill={colorRange[index] || colors.default}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</BarChart>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{ width: '100%' }}
|
||||
@ -370,44 +439,7 @@ const ModelHistoryDisplay = ({
|
||||
{chartRows.length > 0 && (
|
||||
<div style={{ width: '100%', height: `${height}px` }}>
|
||||
<ResponsiveContainer width='100%' height='100%'>
|
||||
<BarChart data={chartRows}>
|
||||
<CartesianGrid
|
||||
strokeDasharray='3 3'
|
||||
stroke={isDarkMode ? '#303030' : '#f0f0f0'}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey='dateFormatted'
|
||||
tick={{
|
||||
fill: isDarkMode ? '#d9d9d9' : '#595959',
|
||||
fontSize: 12
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{
|
||||
fill: isDarkMode ? '#d9d9d9' : '#595959',
|
||||
fontSize: 12
|
||||
}}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: isDarkMode ? '#1f1f1f' : '#ffffff',
|
||||
border: `1px solid ${isDarkMode ? '#303030' : '#f0f0f0'}`,
|
||||
borderRadius: 8
|
||||
}}
|
||||
/>
|
||||
<Legend />
|
||||
{modelStats.map((statDef, index) => {
|
||||
const category = statDef.label || statDef.name
|
||||
return (
|
||||
<Bar
|
||||
key={category}
|
||||
dataKey={category}
|
||||
stackId='history'
|
||||
fill={colorRange[index] || colors.default}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</BarChart>
|
||||
{renderChart()}
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)}
|
||||
@ -433,7 +465,8 @@ ModelHistoryDisplay.propTypes = {
|
||||
]),
|
||||
styles: PropTypes.object,
|
||||
endDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
|
||||
height: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
||||
height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
chartType: PropTypes.oneOf(['line', 'bar'])
|
||||
}
|
||||
|
||||
export default ModelHistoryDisplay
|
||||
|
||||
@ -188,6 +188,27 @@ export const FilamentStock = {
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'unconsumed.count',
|
||||
label: 'Unconsumed',
|
||||
type: 'number',
|
||||
color: 'success',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'used.count',
|
||||
label: 'Used',
|
||||
type: 'number',
|
||||
color: 'warning',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'consumed.count',
|
||||
label: 'Consumed',
|
||||
type: 'number',
|
||||
color: 'default',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'totalCurrentWeight.sum',
|
||||
label: 'Total Current Weight',
|
||||
|
||||
@ -199,6 +199,27 @@ export const PartStock = {
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'new.count',
|
||||
label: 'New',
|
||||
type: 'number',
|
||||
color: 'success',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'used.count',
|
||||
label: 'Used',
|
||||
type: 'number',
|
||||
color: 'warning',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'consumed.count',
|
||||
label: 'Consumed',
|
||||
type: 'number',
|
||||
color: 'default',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'totalCurrentQuantity.sum',
|
||||
label: 'Total Current Quantity',
|
||||
|
||||
@ -419,13 +419,29 @@ export const ProductStock = {
|
||||
name: 'draft.count',
|
||||
label: 'Draft',
|
||||
type: 'number',
|
||||
color: 'default'
|
||||
color: 'default',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'new.count',
|
||||
label: 'New',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
color: 'success',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'used.count',
|
||||
label: 'Used',
|
||||
type: 'number',
|
||||
color: 'warning',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'consumed.count',
|
||||
label: 'Consumed',
|
||||
type: 'number',
|
||||
color: 'default',
|
||||
history: false
|
||||
},
|
||||
{
|
||||
name: 'totalCurrentQuantity.sum',
|
||||
|
||||
@ -437,6 +437,18 @@ export const PurchaseOrder = {
|
||||
label: 'Received',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
},
|
||||
{
|
||||
name: 'cancelled.count',
|
||||
label: 'Cancelled',
|
||||
type: 'number',
|
||||
color: 'error'
|
||||
},
|
||||
{
|
||||
name: 'completed.count',
|
||||
label: 'Completed',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -93,5 +93,28 @@ export const StockEvent = {
|
||||
return objectData.unit
|
||||
}
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'partStock.count',
|
||||
label: 'Part Stock Events',
|
||||
type: 'number',
|
||||
color: 'processing',
|
||||
cardWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'filamentStock.count',
|
||||
label: 'Filament Stock Events',
|
||||
type: 'number',
|
||||
color: 'warning',
|
||||
cardWidth: 220
|
||||
},
|
||||
{
|
||||
name: 'productStock.count',
|
||||
label: 'Product Stock Events',
|
||||
type: 'number',
|
||||
color: 'success',
|
||||
cardWidth: 220
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user