From a0eabf50c6862328155e0e049d32be3d94b8d48b Mon Sep 17 00:00:00 2001 From: Carter Date: Mon, 10 Mar 2025 13:33:55 -0600 Subject: [PATCH] added inventory chart for bins --- src/charts/BinInventoryChart.tsx | 213 +++++++++++++++++++++++++++++ src/charts/BinUtilizationChart.tsx | 121 ++++++++++++++++ src/navigation/SideNavigator.tsx | 2 +- src/pages/Bins.tsx | 63 +++++---- 4 files changed, 368 insertions(+), 31 deletions(-) create mode 100644 src/charts/BinInventoryChart.tsx create mode 100644 src/charts/BinUtilizationChart.tsx diff --git a/src/charts/BinInventoryChart.tsx b/src/charts/BinInventoryChart.tsx new file mode 100644 index 0000000..4446072 --- /dev/null +++ b/src/charts/BinInventoryChart.tsx @@ -0,0 +1,213 @@ +import { Box, Chip, useTheme } from "@mui/material"; +import { GrainColour } from "grain"; +import { pond } from "protobuf-ts/pond"; +import { useEffect, useState } from "react"; +import { Cell, Legend, Pie, PieChart, ResponsiveContainer, Sector } from "recharts"; +import { stringToMaterialColour } from "utils"; + +export interface GrainAmount { + grain: pond.Grain; + grainName: string; + bushelAmount: number; + bushelsPerTonne?: number; +} + +interface Props { + inventory: GrainAmount[]; + onClick: (grain: pond.Grain, grainName: string) => void; + activeGrain?: pond.Grain | null; + customLabel?: string; + customUnit?: string; +} + +export default function BinInventoryChart(props: Props) { + const { inventory, onClick, activeGrain, customLabel, customUnit } = props; + const theme = useTheme(); + const [activeIndex, setActiveIndex] = useState(0); + + useEffect(() => { + let newIndex = inventory.findIndex(g => g.grain === activeGrain); + setActiveIndex(newIndex); + }, [inventory, activeGrain]); + + const renderNoGrain = (props: any) => { + const { cx, cy, innerRadius, outerRadius, startAngle, endAngle, fill, payload } = props; + + return ( + + + {payload.grainName} + + + Please make bin + + + + + ); + }; + + const renderActiveGrain = (props: any) => { + const { + cx, + cy, + innerRadius, + outerRadius, + startAngle, + endAngle, + fill, + payload, + percent + } = props; + return ( + + + {payload.grainName} + + + {`${(percent * 100).toFixed(2)}%`} + + + {payload.bushelAmount.toLocaleString() + (customUnit ? customUnit : " bu")} + + + + + ); + }; + + const pieChart = () => { + if (inventory.length > 0) { + return ( + + {customLabel && ( + ( + + + + )} + /> + )} + renderActiveGrain(props)} + onMouseEnter={(_data: any, index: number) => setActiveIndex(index)} + onClick={(data: any, _index: number) => { + onClick(data.grain as pond.Grain, data.grainName); + }}> + {inventory.map((g, index) => ( + + ))} + + + ); + } + let inv: any = [{ grain: 0, bushelAmount: 1 }]; + return ( + + {customLabel && ( + ( + + + + )} + /> + )} + renderNoGrain(props)}> + Please Create Bin + + + ); + }; + + return ( + + {pieChart()} + + ); +} diff --git a/src/charts/BinUtilizationChart.tsx b/src/charts/BinUtilizationChart.tsx new file mode 100644 index 0000000..c205493 --- /dev/null +++ b/src/charts/BinUtilizationChart.tsx @@ -0,0 +1,121 @@ +import { Box, CardActionArea, Chip, useTheme } from "@mui/material"; +import { GrainColour, grainName } from "grain"; +import { pond } from "protobuf-ts/pond"; +import { Label, Legend, Pie, PieChart, ResponsiveContainer, Text } from "recharts"; + +interface Props { + bushelAmount: number; + bushelCapacity: number; + grain: pond.Grain; + customLabel?: string; + customColour?: string; + onClick: () => void; + grainActive?: boolean; + customUnit?: string; +} + +export default function BinUtilizationChart(props: Props) { + const { + bushelAmount, + bushelCapacity, + grain, + onClick, + grainActive, + customLabel, + customColour, + customUnit + } = props; + const grainLabel = customLabel ? customLabel : grainName(grain); + const theme = useTheme(); + const grainColour = customColour ? customColour : GrainColour(grain); + return ( + + + + + ( + + + + )} + /> + + + + + + + ); +} diff --git a/src/navigation/SideNavigator.tsx b/src/navigation/SideNavigator.tsx index a81cb4d..553543d 100644 --- a/src/navigation/SideNavigator.tsx +++ b/src/navigation/SideNavigator.tsx @@ -115,7 +115,7 @@ export default function SideNavigator(props: Props) { - {open && } + {open && } diff --git a/src/pages/Bins.tsx b/src/pages/Bins.tsx index 5e52733..c7dea8e 100644 --- a/src/pages/Bins.tsx +++ b/src/pages/Bins.tsx @@ -17,6 +17,8 @@ import { // GridList, // GridListTile, IconButton, + ImageList, + ImageListItem, InputLabel, ListItemIcon, ListItemText, @@ -45,8 +47,8 @@ import { // import BinSettings from "bin/BinSettings"; // import BinsList from "bin/BinsList"; // import BinYard from "bin/BinYard"; -// import BinInventoryChart, { GrainAmount } from "charts/BinInventoryChart"; -// import BinUtilizationChart from "charts/BinUtilizationChart"; +import BinInventoryChart, { GrainAmount } from "charts/BinInventoryChart"; +import BinUtilizationChart from "charts/BinUtilizationChart"; // import QrCodeGenerator, { QrCodeKey } from "common/QrCodeGenerator"; import GrainDescriber, { grainName } from "grain/GrainDescriber"; // import GrainBagList from "grainBag/grainBagList"; @@ -167,7 +169,7 @@ export default function Bins(props: Props) { const [fertilizerBins, setFertilizerBins] = useState([]); const [emptyBins, setEmptyBins] = useState([]); // const [grainBags, setGrainBags] = useState([]); - // const [displayedInventory, setDisplayedInventory] = useState([]); + const [displayedInventory, setDisplayedInventory] = useState([]); // const [displayedFertilizer, setDisplayedFertilizer] = useState([]); const [carouselIndex, setCarouselIndex] = useState(0); const [addBagOpen, setAddBagOpen] = useState(false); @@ -338,23 +340,23 @@ export default function Bins(props: Props) { binsTotal: resp.data.total }); let metrics = pond.BinMetrics.fromObject(resp.data.metrics ?? {}); - // let inventory: GrainAmount[] = []; + let inventory: GrainAmount[] = []; // let fertInventory: GrainAmount[] = []; if (metrics) { metrics.grainInventory.forEach(grain => { - // inventory.push({ - // grain: grain.grainType, - // bushelAmount: grain.bushelAmount, - // grainName: grainName(grain.grainType) - // }); + inventory.push({ + grain: grain.grainType, + bushelAmount: grain.bushelAmount, + grainName: grainName(grain.grainType) + }); }); metrics.customInventory.forEach(invObject => { if (invObject.storageType === pond.BinStorage.BIN_STORAGE_UNSUPPORTED_GRAIN) { - // inventory.push({ - // grain: pond.Grain.GRAIN_CUSTOM, - // bushelAmount: invObject.amount, - // grainName: invObject.name - // }); + inventory.push({ + grain: pond.Grain.GRAIN_CUSTOM, + bushelAmount: invObject.amount, + grainName: invObject.name + }); } else if (invObject.storageType === pond.BinStorage.BIN_STORAGE_FERTILIZER) { // fertInventory.push({ // grain: pond.Grain.GRAIN_CUSTOM, @@ -365,7 +367,7 @@ export default function Bins(props: Props) { }); } setBinMetrics(metrics); - // setDisplayedInventory(inventory); + setDisplayedInventory(inventory); // setDisplayedFertilizer(fertInventory); if (yards.length === 0) { let y: pond.BinYardSettings[] = []; @@ -821,7 +823,7 @@ export default function Bins(props: Props) { - {/* { @@ -835,7 +837,7 @@ export default function Bins(props: Props) { loadMoreBins(filter); }} //activeGrain={grainFilter} - /> */} + /> {/* Click a chart to filter bins by grain - {/* + ? 7 + : 8 + } + > {binMetrics && binMetrics.grainInventory.map((inv, key) => ( - + - + ))} {binMetrics && binMetrics.customInventory.map((inv, key) => { @@ -959,7 +962,7 @@ export default function Bins(props: Props) { } return ( - + - + ); })} - */} + ) : (