213 lines
5.5 KiB
TypeScript
213 lines
5.5 KiB
TypeScript
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 (
|
|
<g>
|
|
<text x={cx} y={cy} dy={-2} textAnchor="middle" fill={fill}>
|
|
{payload.grainName}
|
|
</text>
|
|
<text
|
|
x={cx}
|
|
y={cy}
|
|
dy={12}
|
|
textAnchor="middle"
|
|
fill={theme.palette.text.secondary}
|
|
fontSize="0.7rem">
|
|
Please make bin
|
|
</text>
|
|
<Sector
|
|
cx={cx}
|
|
cy={cy}
|
|
innerRadius={innerRadius}
|
|
outerRadius={outerRadius}
|
|
startAngle={startAngle}
|
|
endAngle={endAngle}
|
|
fill={fill}
|
|
/>
|
|
<Sector
|
|
cx={cx}
|
|
cy={cy}
|
|
startAngle={startAngle}
|
|
endAngle={endAngle}
|
|
innerRadius={innerRadius - 8}
|
|
outerRadius={innerRadius - 5}
|
|
fill={fill}
|
|
/>
|
|
</g>
|
|
);
|
|
};
|
|
|
|
const renderActiveGrain = (props: any) => {
|
|
const {
|
|
cx,
|
|
cy,
|
|
innerRadius,
|
|
outerRadius,
|
|
startAngle,
|
|
endAngle,
|
|
fill,
|
|
payload,
|
|
percent
|
|
} = props;
|
|
return (
|
|
<g>
|
|
<text x={cx} y={cy} dy={-8} textAnchor="middle" fill={fill}>
|
|
{payload.grainName}
|
|
</text>
|
|
<text
|
|
x={cx}
|
|
y={cy}
|
|
dy={6}
|
|
textAnchor="middle"
|
|
fill={theme.palette.text.secondary}
|
|
fontSize="0.7rem">
|
|
{`${(percent * 100).toFixed(2)}%`}
|
|
</text>
|
|
<text
|
|
x={cx}
|
|
y={cy}
|
|
dy={20}
|
|
textAnchor="middle"
|
|
fill={theme.palette.text.secondary}
|
|
fontSize="0.7rem">
|
|
{payload.bushelAmount.toLocaleString() + (customUnit ? customUnit : " bu")}
|
|
</text>
|
|
<Sector
|
|
cx={cx}
|
|
cy={cy}
|
|
innerRadius={innerRadius}
|
|
outerRadius={outerRadius}
|
|
startAngle={startAngle}
|
|
endAngle={endAngle}
|
|
fill={fill}
|
|
/>
|
|
<Sector
|
|
cx={cx}
|
|
cy={cy}
|
|
startAngle={startAngle}
|
|
endAngle={endAngle}
|
|
innerRadius={innerRadius - 8}
|
|
outerRadius={innerRadius - 5}
|
|
fill={fill}
|
|
/>
|
|
</g>
|
|
);
|
|
};
|
|
|
|
const pieChart = () => {
|
|
if (inventory.length > 0) {
|
|
return (
|
|
<PieChart>
|
|
{customLabel && (
|
|
<Legend
|
|
verticalAlign="top"
|
|
content={() => (
|
|
<Box textAlign="center" marginY={0.5}>
|
|
<Chip label={customLabel} />
|
|
</Box>
|
|
)}
|
|
/>
|
|
)}
|
|
<Pie
|
|
cursor="pointer"
|
|
isAnimationActive={false}
|
|
dataKey="bushelAmount"
|
|
data={inventory}
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius="70%"
|
|
outerRadius="100%"
|
|
stroke="transparent"
|
|
paddingAngle={3}
|
|
labelLine={false}
|
|
activeIndex={activeIndex === -1 ? 0 : activeIndex}
|
|
activeShape={(props: any) => 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) => (
|
|
<Cell
|
|
key={`cell-${index}`}
|
|
fill={
|
|
g.grain === pond.Grain.GRAIN_CUSTOM
|
|
? stringToMaterialColour(g.grainName)
|
|
: GrainColour(g.grain)
|
|
}
|
|
/>
|
|
))}
|
|
</Pie>
|
|
</PieChart>
|
|
);
|
|
}
|
|
let inv: any = [{ grain: 0, bushelAmount: 1 }];
|
|
return (
|
|
<PieChart>
|
|
{customLabel && (
|
|
<Legend
|
|
verticalAlign="top"
|
|
content={() => (
|
|
<Box textAlign="center" marginY={0.5}>
|
|
<Chip label={customLabel} />
|
|
</Box>
|
|
)}
|
|
/>
|
|
)}
|
|
<Pie
|
|
cursor="pointer"
|
|
isAnimationActive={false}
|
|
dataKey="bushelAmount"
|
|
data={inv}
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius="70%"
|
|
outerRadius="100%"
|
|
stroke="transparent"
|
|
paddingAngle={3}
|
|
labelLine={false}
|
|
activeIndex={0}
|
|
activeShape={(props: any) => renderNoGrain(props)}>
|
|
Please Create Bin
|
|
</Pie>
|
|
</PieChart>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box height={1} width={1}>
|
|
<ResponsiveContainer>{pieChart()}</ResponsiveContainer>
|
|
</Box>
|
|
);
|
|
}
|