From 1edd48c1d727202c0089acb166e4ce924839c61f Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Mon, 24 Aug 2026 22:54:06 +0100 Subject: [PATCH] Enhance Inventory Overview and Model History Display Components - 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. --- .../Dashboard/Inventory/InventoryOverview.jsx | 211 ++++++++++++++++-- .../Dashboard/common/ModelHistoryDisplay.jsx | 115 ++++++---- src/database/models/FilamentStock.js | 21 ++ src/database/models/PartStock.js | 21 ++ src/database/models/ProductStock.js | 20 +- src/database/models/PurchaseOrder.js | 12 + src/database/models/StockEvent.js | 23 ++ 7 files changed, 363 insertions(+), 60 deletions(-) diff --git a/src/components/Dashboard/Inventory/InventoryOverview.jsx b/src/components/Dashboard/Inventory/InventoryOverview.jsx index 2edbe6a1..6584847c 100644 --- a/src/components/Dashboard/Inventory/InventoryOverview.jsx +++ b/src/components/Dashboard/Inventory/InventoryOverview.jsx @@ -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 = () => { - updateCollapseState('inventoryStats', isActive) + updateCollapseState('partStockStats', isActive) } className='no-t-padding-collapse' - collapseKey='inventoryStats' - canCollapse={false} + collapseKey='partStockStats' > { wrap='wrap' align='flex-start' > - - - - + + + + + + updateCollapseState('filamentStockStats', isActive) + } + className='no-t-padding-collapse' + collapseKey='filamentStockStats' + > + + + + + + + updateCollapseState('productStockStats', isActive) + } + className='no-t-padding-collapse' + collapseKey='productStockStats' + > + + @@ -69,8 +122,29 @@ const InventoryOverview = () => { + + updateCollapseState('stockEventStats', isActive) + } + className='no-t-padding-collapse' + collapseKey='stockEventStats' + > + + + + + - + { } collapseKey='partStockHistory' canCollapse={false} - > + > + + + + updateCollapseState('newPartStocks', isActive) + } + collapseKey='newPartStocks' + canCollapse={false} + > + + - + { } canCollapse={false} collapseKey='filamentStockHistory' - > + > + + + + updateCollapseState('unconsumedFilamentStocks', isActive) + } + canCollapse={false} + collapseKey='unconsumedFilamentStocks' + > + + + + + + + + updateCollapseState('productStockHistory', isActive) + } + collapseKey='productStockHistory' + canCollapse={false} + > + + + + updateCollapseState('draftProductStocks', isActive) + } + collapseKey='draftProductStocks' + canCollapse={false} + > + + + + + + updateCollapseState('purchaseOrderHistory', isActive) + } + canCollapse={false} + collapseKey='purchaseOrderHistory' + > + + + + updateCollapseState('draftPurchaseOrders', isActive) + } + canCollapse={false} + collapseKey='draftPurchaseOrders' + > + + + + + + { } canCollapse={false} collapseKey='stockEventHistory' - > + > + + diff --git a/src/components/Dashboard/common/ModelHistoryDisplay.jsx b/src/components/Dashboard/common/ModelHistoryDisplay.jsx index 3b957161..682cf311 100644 --- a/src/components/Dashboard/common/ModelHistoryDisplay.jsx +++ b/src/components/Dashboard/common/ModelHistoryDisplay.jsx @@ -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 = ({ ) + 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 = ( + <> + + + + + + + ) + + if (chartType === 'line') { + return ( + + {commonElements} + {modelStats.map((statDef, index) => { + const category = statDef.label || statDef.name + return ( + + ) + })} + + ) + } + + return ( + + {commonElements} + {modelStats.map((statDef, index) => { + const category = statDef.label || statDef.name + return ( + + ) + })} + + ) + } + return ( 0 && (
- - - - - - - {modelStats.map((statDef, index) => { - const category = statDef.label || statDef.name - return ( - - ) - })} - + {renderChart()}
)} @@ -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 diff --git a/src/database/models/FilamentStock.js b/src/database/models/FilamentStock.js index f5606ab2..6580d0e0 100644 --- a/src/database/models/FilamentStock.js +++ b/src/database/models/FilamentStock.js @@ -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', diff --git a/src/database/models/PartStock.js b/src/database/models/PartStock.js index 0f7d8b2e..dd3a22f8 100644 --- a/src/database/models/PartStock.js +++ b/src/database/models/PartStock.js @@ -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', diff --git a/src/database/models/ProductStock.js b/src/database/models/ProductStock.js index 19dfa9dc..3e2fabf8 100644 --- a/src/database/models/ProductStock.js +++ b/src/database/models/ProductStock.js @@ -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', diff --git a/src/database/models/PurchaseOrder.js b/src/database/models/PurchaseOrder.js index b64e77bf..832b4f9a 100644 --- a/src/database/models/PurchaseOrder.js +++ b/src/database/models/PurchaseOrder.js @@ -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' } ] } diff --git a/src/database/models/StockEvent.js b/src/database/models/StockEvent.js index 8debad49..c93c5c47 100644 --- a/src/database/models/StockEvent.js +++ b/src/database/models/StockEvent.js @@ -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 + } ] }