862 lines
28 KiB
TypeScript
862 lines
28 KiB
TypeScript
import {
|
|
Accordion,
|
|
AccordionDetails,
|
|
AccordionSummary,
|
|
Box,
|
|
Button,
|
|
Checkbox,
|
|
Chip,
|
|
darken,
|
|
Divider,
|
|
FormControl,
|
|
FormControlLabel,
|
|
Grid2 as Grid,
|
|
ImageList as GridList,
|
|
ImageListItem as GridListTile,
|
|
InputLabel,
|
|
Menu,
|
|
MenuItem,
|
|
Select,
|
|
Skeleton,
|
|
Theme,
|
|
Typography,
|
|
useTheme
|
|
} from "@mui/material";
|
|
import { green, yellow } from "@mui/material/colors";
|
|
import { makeStyles } from "@mui/styles";
|
|
import { ArrowBackIos, ArrowForwardIos, ExpandMore, MoreVert } from "@mui/icons-material";
|
|
//import { Skeleton } from "@material-ui/lab";
|
|
import BinInventoryChart, { GrainAmount } from "charts/BinInventoryChart";
|
|
import BinUtilizationChart from "charts/BinUtilizationChart";
|
|
//import QrCodeGenerator, { QrCodeKey } from "common/QrCodeGenerator";
|
|
import GrainDescriber, { grainName } from "grain/GrainDescriber";
|
|
import { useMobile, useWidth } from "hooks";
|
|
import { Bin } from "models";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { useBinAPI, useGlobalState } from "providers";
|
|
import React, { useEffect, useState, useCallback, SetStateAction } from "react";
|
|
import { stringToMaterialColour } from "utils";
|
|
//import BinsFansStatusTable from "./BinFansStatusTable";
|
|
import BinsList from "./BinsList";
|
|
|
|
interface Props {
|
|
yardKey: string;
|
|
insert?: boolean;
|
|
/**
|
|
* function to run when a bin card is clicked, when not passed in will default to attempting to navigate to the bin page by adding the bin key to the url
|
|
* @param bin the selected bin model
|
|
*/
|
|
binClicked?: (bin: Bin) => void
|
|
}
|
|
|
|
const useStyles = makeStyles((theme: Theme) => ({
|
|
"@keyframes pulsate": {
|
|
to: {
|
|
boxShadow: "0 0 0 16px" + yellow["500"] + "00"
|
|
}
|
|
},
|
|
green: {
|
|
color: green["500"],
|
|
"&:hover": {
|
|
color: green["600"]
|
|
}
|
|
},
|
|
gridList: {
|
|
width: "100%",
|
|
flexWrap: "nowrap",
|
|
transform: "translateZ(0)"
|
|
},
|
|
pulse: {
|
|
boxShadow: "0 0 0 0 " + yellow["500"] + "75",
|
|
animation: "$pulsate 1.75s infinite cubic-bezier(0.66, 0.33, 0, 1)"
|
|
},
|
|
icon: {
|
|
padding: 6,
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: "18px",
|
|
background: "rgba(0,0,0,0)",
|
|
"&:hover": {
|
|
background:
|
|
"radial-gradient(closest-side, rgba(150, 150, 150, 0.5) 50%, rgba(150, 150, 150, 0.5))"
|
|
}
|
|
},
|
|
accordion: {
|
|
background: darken(theme.palette.background.paper, 0.4)
|
|
}
|
|
}));
|
|
|
|
export default function BinyardDisplay(props: Props) {
|
|
const { yardKey, insert, binClicked } = props;
|
|
const classes = useStyles();
|
|
const binAPI = useBinAPI();
|
|
const theme = useTheme();
|
|
const width = useWidth();
|
|
const isMobile = useMobile();
|
|
const [carouselIndex, setCarouselIndex] = useState(0);
|
|
const [binMenuAnchorEl, setBinMenuAnchorEl] = useState<Element | null>(null);
|
|
const [{ as, user }] = useGlobalState();
|
|
const maxBins = 40;
|
|
const [binsLoading, setBinsLoading] = useState(false);
|
|
const [expandTotal, setExpandTotal] = useState(false);
|
|
const [displayedInventory, setDisplayedInventory] = useState<GrainAmount[]>([]);
|
|
const [displayedFertilizer, setDisplayedFertilizer] = useState<GrainAmount[]>([]);
|
|
const [expandUtilization, setExpandUtilization] = useState(false);
|
|
const [expandFanTable, setExpandFanTable] = useState(false);
|
|
const [displayGrain, setDisplayGrain] = useState(true);
|
|
const [displayFert, setDisplayFert] = useState(false);
|
|
const [displayEmpty, setDisplayEmpty] = useState(false);
|
|
const [yardBins, setYardBins] = useState<Bin[]>([]);
|
|
const [grainBins, setGrainBins] = useState<Bin[]>([]);
|
|
const [fertBins, setFertBins] = useState<Bin[]>([]);
|
|
const [emptyBins, setEmptyBins] = useState<Bin[]>([]);
|
|
const [binTotal, setBinTotal] = useState(0);
|
|
const [binMetrics, setBinMetrics] = useState<pond.BinMetrics>();
|
|
const [contentFilter, setContentFilter] = useState<string>("");
|
|
const [totalFanControllers, setTotalFanControllers] = useState(0);
|
|
const [totalFanControllersOn, setTotalFanControllersOn] = useState(0);
|
|
const [openQrGenerator, setOpenQrGenerator] = useState(false);
|
|
const [order, setOrder] = useState<"asc" | "desc">("asc");
|
|
const [orderBy, setOrderBy] = useState<string>("name");
|
|
|
|
//const [qrKeyList, setQrKeyList] = useState<QrCodeKey[]>([]);
|
|
|
|
const [scrollTranslations, setScrollTranslations] = useState({
|
|
bins: 0,
|
|
empty: 0,
|
|
fertilizer: 0
|
|
});
|
|
|
|
const loadBins = useCallback(() => {
|
|
if (yardKey === "") return
|
|
//load the bins based on the yard key
|
|
setBinsLoading(true);
|
|
binAPI
|
|
.listBinsAndData(maxBins, 0, "asc", "name", undefined, undefined, undefined, [yardKey], ["binyard"])
|
|
.then(resp => {
|
|
let yardBins: Bin[] = [];
|
|
let grainBins: Bin[] = [];
|
|
let fertBins: Bin[] = [];
|
|
let emptyBins: Bin[] = [];
|
|
//let qr: QrCodeKey[] = [];
|
|
//set the list of bins into the yard bins
|
|
resp.data.bins.forEach(b => {
|
|
let bin = Bin.any(b);
|
|
yardBins.push(bin);
|
|
if (bin.empty()) {
|
|
emptyBins.push(bin);
|
|
} else if (bin.storage() === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
|
|
fertBins.push(bin);
|
|
} else {
|
|
grainBins.push(bin);
|
|
}
|
|
//build qr keys here
|
|
// qr.push({
|
|
// key: bin.key(),
|
|
// name: bin.name()
|
|
// });
|
|
});
|
|
setYardBins(yardBins);
|
|
setBinTotal(resp.data.total);
|
|
//setQrKeyList(qr);
|
|
setGrainBins(grainBins);
|
|
if (fertBins.length > 0) {
|
|
setDisplayFert(true);
|
|
}
|
|
if (emptyBins.length > 0) {
|
|
setDisplayEmpty(true);
|
|
}
|
|
setFertBins(fertBins);
|
|
setEmptyBins(emptyBins);
|
|
setBinsLoading(false);
|
|
|
|
let metrics = pond.BinMetrics.fromObject(resp.data.metrics ?? {});
|
|
let inventory: GrainAmount[] = [];
|
|
let fertInventory: GrainAmount[] = [];
|
|
if (metrics) {
|
|
metrics.grainInventory.forEach(grain => {
|
|
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
|
|
});
|
|
} else if (invObject.storageType === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
|
|
fertInventory.push({
|
|
grain: pond.Grain.GRAIN_CUSTOM,
|
|
bushelAmount: invObject.amount * 35.239,
|
|
grainName: invObject.name
|
|
});
|
|
}
|
|
});
|
|
}
|
|
setBinMetrics(metrics);
|
|
setDisplayedInventory(inventory);
|
|
setDisplayedFertilizer(fertInventory);
|
|
});
|
|
}, [as, binAPI, yardKey]);
|
|
|
|
const loadMoreBins = (filter: string, total?: number, offset?: number) => {
|
|
setBinsLoading(true);
|
|
binAPI
|
|
.listBins(
|
|
total ?? maxBins,
|
|
offset ?? 0,
|
|
order,
|
|
orderBy,
|
|
filter,
|
|
as,
|
|
undefined,
|
|
undefined,
|
|
[yardKey],
|
|
["binyard"]
|
|
)
|
|
.then(resp => {
|
|
let bins = yardBins;
|
|
//if the offset was 0 or undefined we started from the beginning so replace the yard bins with the new ones
|
|
if (!offset || offset === 0) {
|
|
bins = resp.data.bins.map(b => Bin.any(b));
|
|
//else append the new ones on to the end of the existing yard bins
|
|
} else {
|
|
bins = bins.concat(resp.data.bins.map(b => Bin.any(b)));
|
|
}
|
|
//loop through the new list of bins setting the bins into the correct sections
|
|
let grainBins: Bin[] = [];
|
|
let fertBins: Bin[] = [];
|
|
let emptyBins: Bin[] = [];
|
|
//let qr: QrCodeKey[] = [];
|
|
//set the list of bins into the yard bins
|
|
resp.data.bins.forEach(b => {
|
|
let bin = Bin.any(b);
|
|
yardBins.push(bin);
|
|
if (bin.empty()) {
|
|
emptyBins.push(bin);
|
|
} else if (bin.storage() === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
|
|
fertBins.push(bin);
|
|
} else {
|
|
grainBins.push(bin);
|
|
}
|
|
//build qr keys here
|
|
// qr.push({
|
|
// key: bin.key(),
|
|
// name: bin.name()
|
|
// });
|
|
});
|
|
setYardBins(bins);
|
|
setBinTotal(resp.data.total);
|
|
//setQrKeyList(qr);
|
|
setGrainBins(grainBins);
|
|
if (fertBins.length > 0) {
|
|
setDisplayFert(true);
|
|
}
|
|
if (emptyBins.length > 0) {
|
|
setDisplayEmpty(true);
|
|
}
|
|
setFertBins(fertBins);
|
|
setEmptyBins(emptyBins);
|
|
setBinsLoading(false);
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
loadBins();
|
|
}, [yardKey, loadBins]);
|
|
|
|
useEffect(() => {
|
|
let ebt = sessionStorage.getItem("expandBinTotal");
|
|
if (ebt === "true") {
|
|
setExpandTotal(true);
|
|
} else {
|
|
setExpandTotal(false);
|
|
}
|
|
|
|
let ebu = sessionStorage.getItem("expandBinUtilization");
|
|
if (ebu === "true") {
|
|
setExpandUtilization(true);
|
|
} else {
|
|
setExpandUtilization(false);
|
|
}
|
|
|
|
let fanTable = sessionStorage.getItem("expandFanTable");
|
|
if (fanTable === "true") {
|
|
setExpandFanTable(true);
|
|
} else {
|
|
setExpandFanTable(false);
|
|
}
|
|
}, []);
|
|
|
|
const duplicateBin = (bin: Bin) => {
|
|
binAPI.addBin(bin.settings, as).then(resp => {
|
|
loadBins();
|
|
});
|
|
};
|
|
|
|
const binsContent = () => {
|
|
if (!binsLoading && binTotal === 0) {
|
|
return (
|
|
<Box textAlign="center" marginTop={1}>
|
|
<Typography variant="subtitle1">No bins found</Typography>
|
|
<Typography variant="body1" color="textSecondary">
|
|
Create a bin and it will appear here
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Grid container>
|
|
{displayGrain && (
|
|
<React.Fragment>
|
|
<Grid size={12}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={12}>
|
|
<BinsList
|
|
gridView
|
|
insert
|
|
valDisplay="average"
|
|
bins={grainBins}
|
|
duplicateBin={duplicateBin}
|
|
title={"Grain Bins"}
|
|
cardClickFunction={binClicked}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
{displayFert && (
|
|
<React.Fragment>
|
|
<Grid size={12}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={12}>
|
|
<BinsList
|
|
gridView
|
|
insert
|
|
valDisplay="average"
|
|
bins={fertBins}
|
|
duplicateBin={duplicateBin}
|
|
title={"Fertilizer Bins"}
|
|
cardClickFunction={binClicked}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
{displayEmpty && (
|
|
<React.Fragment>
|
|
<Grid size={12}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={12}>
|
|
<BinsList
|
|
gridView
|
|
insert
|
|
valDisplay="average"
|
|
bins={emptyBins}
|
|
duplicateBin={duplicateBin}
|
|
title={"Empty Bins"}
|
|
cardClickFunction={binClicked}
|
|
// loadMore={newTranslation => {
|
|
// //only triggered in the scroll view so this will never trigger in grid view
|
|
// if (yardBins.length < binTotal) {
|
|
// loadMoreBins(contentFilter);
|
|
// setScrollTranslations({ ...scrollTranslations, empty: newTranslation });
|
|
// }
|
|
// }}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const binsHeader = () => {
|
|
return (
|
|
<Menu
|
|
id="yardMenu"
|
|
anchorEl={binMenuAnchorEl ? binMenuAnchorEl : null}
|
|
open={binMenuAnchorEl !== null}
|
|
onClose={() => {
|
|
setBinMenuAnchorEl(null);
|
|
}}
|
|
disableAutoFocusItem>
|
|
<MenuItem>
|
|
<FormControl style={{ marginLeft: theme.spacing(2) }}>
|
|
<InputLabel id="demo-simple-select-label">Order</InputLabel>
|
|
<Select
|
|
labelId="demo-simple-select-label"
|
|
id="demo-simple-select"
|
|
value={order}
|
|
onChange={event => setOrder(event.target.value as SetStateAction<"asc" | "desc">)}>
|
|
<MenuItem value={"asc"}>Ascending</MenuItem>
|
|
<MenuItem value={"desc"}>Descending</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
</MenuItem>
|
|
<MenuItem>
|
|
<FormControl style={{ marginLeft: theme.spacing(2) }}>
|
|
<InputLabel id="demo-simple-select-label">Order By</InputLabel>
|
|
<Select
|
|
labelId="demo-simple-select-label"
|
|
id="demo-simple-select"
|
|
value={orderBy}
|
|
onChange={event => setOrderBy(event.target.value as SetStateAction<string>)}>
|
|
<MenuItem value={"name"}>Name</MenuItem>
|
|
<MenuItem value={"timestamp"}>Timestamp</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
</MenuItem>
|
|
<Divider />
|
|
<MenuItem>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={displayGrain}
|
|
onChange={(e, checked) => {
|
|
setDisplayGrain(checked);
|
|
}}
|
|
/>
|
|
}
|
|
label={<Typography>Grain</Typography>}
|
|
/>
|
|
</MenuItem>
|
|
<MenuItem>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={displayFert}
|
|
onChange={(e, checked) => {
|
|
setDisplayFert(checked);
|
|
}}
|
|
/>
|
|
}
|
|
label={<Typography>Fertilizer</Typography>}
|
|
/>
|
|
</MenuItem>
|
|
<MenuItem>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={displayEmpty}
|
|
onChange={(e, checked) => {
|
|
setDisplayEmpty(checked);
|
|
}}
|
|
/>
|
|
}
|
|
label={<Typography>Empty Bins</Typography>}
|
|
/>
|
|
</MenuItem>
|
|
{/* <MenuItem
|
|
onClick={() => {
|
|
setOpenQrGenerator(true);
|
|
}}>
|
|
<Typography>Generate QR Codes</Typography>
|
|
</MenuItem> */}
|
|
</Menu>
|
|
);
|
|
};
|
|
|
|
const binsByGrainType = () => {
|
|
return (
|
|
<Box width={1} marginBottom={1}>
|
|
<Grid
|
|
container
|
|
direction="row"
|
|
alignContent="center"
|
|
alignItems="center">
|
|
<Grid>
|
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
|
<Typography variant="h6" style={{ fontWeight: 650 }}>
|
|
Bins - ({yardBins.length} of {binTotal})
|
|
</Typography>
|
|
{contentFilter && (
|
|
<Box marginLeft={1}>
|
|
<Chip
|
|
size="small"
|
|
onDelete={() => {
|
|
setContentFilter("");
|
|
loadMoreBins("");
|
|
}}
|
|
label={contentFilter}
|
|
/>
|
|
</Box>
|
|
)}
|
|
{yardBins.length < binTotal && (
|
|
<Button
|
|
style={{ marginLeft: 10 }}
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={() => {
|
|
loadMoreBins(contentFilter, 0, yardBins.length);
|
|
}}>
|
|
Load All
|
|
</Button>
|
|
)}
|
|
</Box>
|
|
</Grid>
|
|
<Grid>
|
|
<MoreVert
|
|
className={classes.icon}
|
|
onClick={event => {
|
|
let target = event.currentTarget;
|
|
setBinMenuAnchorEl(target);
|
|
}}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
{binsLoading ? <Skeleton variant="rectangular" height="200px" /> : binsContent()}
|
|
{binsHeader()}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const desktopInventory = () => {
|
|
return (
|
|
<React.Fragment>
|
|
<Grid container direction="row" alignContent="center" alignItems="center">
|
|
<Grid style={{ height: "200px" }} size={6}>
|
|
<BinInventoryChart
|
|
customLabel="Grain"
|
|
inventory={displayedInventory}
|
|
onClick={(grain: pond.Grain, grainName: string) => {
|
|
let filter = "";
|
|
if (grain !== pond.Grain.GRAIN_NONE && grain !== pond.Grain.GRAIN_CUSTOM) {
|
|
filter = pond.Grain[grain];
|
|
} else {
|
|
filter = grainName;
|
|
}
|
|
setContentFilter(filter);
|
|
loadMoreBins(filter);
|
|
}}
|
|
//activeGrain={grainFilter}
|
|
/>
|
|
</Grid>
|
|
<Grid style={{ height: "200px" }} size={6}>
|
|
<BinInventoryChart
|
|
customLabel="Fertilizer"
|
|
inventory={displayedFertilizer}
|
|
onClick={(grain: pond.Grain, grainName: string) => {
|
|
let filter = "";
|
|
if (grain !== pond.Grain.GRAIN_NONE && grain !== pond.Grain.GRAIN_CUSTOM) {
|
|
filter = pond.Grain[grain];
|
|
} else {
|
|
filter = grainName;
|
|
}
|
|
setContentFilter(filter);
|
|
loadMoreBins(filter);
|
|
}}
|
|
customUnit={"L"}
|
|
//activeGrain={grainFilter}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const mobileViewCarousel = () => {
|
|
let length = 2;
|
|
let charts: JSX.Element[] = [
|
|
<BinInventoryChart
|
|
customLabel="Grain"
|
|
inventory={displayedInventory}
|
|
onClick={(grain: pond.Grain, grainName: string) => {
|
|
let filter = "";
|
|
if (grain !== pond.Grain.GRAIN_NONE && grain !== pond.Grain.GRAIN_CUSTOM) {
|
|
filter = pond.Grain[grain];
|
|
} else {
|
|
filter = grainName;
|
|
}
|
|
setContentFilter(filter);
|
|
loadMoreBins(filter);
|
|
}}
|
|
//activeGrain={grainFilter}
|
|
/>,
|
|
<BinInventoryChart
|
|
customLabel="Fertilizer"
|
|
inventory={displayedFertilizer}
|
|
onClick={(grain: pond.Grain, grainName: string) => {
|
|
let filter = "";
|
|
if (grain !== pond.Grain.GRAIN_NONE && grain !== pond.Grain.GRAIN_CUSTOM) {
|
|
filter = pond.Grain[grain];
|
|
} else {
|
|
filter = grainName;
|
|
}
|
|
setContentFilter(filter);
|
|
loadMoreBins(filter);
|
|
}}
|
|
customUnit={"L"}
|
|
//activeGrain={grainFilter}
|
|
/>
|
|
];
|
|
return (
|
|
<React.Fragment>
|
|
<Grid container direction="row" alignContent="center" alignItems="center">
|
|
<Grid size={2}>
|
|
<Button
|
|
style={{ height: 160 }}
|
|
onClick={() => {
|
|
const newIndex = carouselIndex - 1;
|
|
setCarouselIndex(newIndex < 0 ? length - 1 : newIndex);
|
|
}}>
|
|
<ArrowBackIos />
|
|
</Button>
|
|
</Grid>
|
|
<Grid size={8}>
|
|
<Box height={"180px"} flexDirection="row" display="flex">
|
|
{charts[carouselIndex]}
|
|
</Box>
|
|
</Grid>
|
|
<Grid size={2}>
|
|
<Button
|
|
style={{ height: 160 }}
|
|
onClick={() => {
|
|
const newIndex = carouselIndex + 1;
|
|
setCarouselIndex(newIndex >= length ? 0 : newIndex);
|
|
}}>
|
|
<ArrowForwardIos />
|
|
</Button>
|
|
</Grid>
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const totalInventory = () => {
|
|
return (
|
|
<Accordion
|
|
className={classes.accordion}
|
|
expanded={expandTotal}
|
|
onChange={(_, expanded) => {
|
|
setExpandTotal(expanded);
|
|
sessionStorage.setItem("expandBinTotal", expanded.toString());
|
|
}}>
|
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
<Typography variant="h6" style={{ fontWeight: 650 }}>
|
|
Total Inventory
|
|
</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
{isMobile || insert ? mobileViewCarousel() : desktopInventory()}
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
);
|
|
};
|
|
|
|
const grainUnitDisplay = (custom?: pond.CustomInventory) => {
|
|
// If custom and not convertible, always bushels
|
|
if (custom && custom.bushelsPerTonne <= 1) {
|
|
return " bu";
|
|
}
|
|
|
|
switch (user.grainUnit()) {
|
|
case pond.GrainUnit.GRAIN_UNIT_TONNE:
|
|
return " mT";
|
|
case pond.GrainUnit.GRAIN_UNIT_TON:
|
|
return " t";
|
|
default:
|
|
return " bu";
|
|
}
|
|
};
|
|
|
|
const customQuantityDisplay = (customInventory: pond.CustomInventory, bushels: number) => {
|
|
let amount = bushels
|
|
if(customInventory.bushelsPerTonne > 1){
|
|
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE){
|
|
amount = bushels/customInventory.bushelsPerTonne
|
|
}else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON){
|
|
amount = bushels/(customInventory.bushelsPerTonne*0.907)
|
|
}
|
|
}
|
|
return Math.round(amount*100)/100
|
|
}
|
|
|
|
const supportedGrainDisplay = (grain: pond.Grain, bushels: number) => {
|
|
const describer = GrainDescriber(grain)
|
|
let amount = bushels
|
|
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE){
|
|
amount = bushels/describer.bushelsPerTonne
|
|
}else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON){
|
|
amount = bushels/describer.bushelsPerTon
|
|
}
|
|
return Math.round(amount*100)/100
|
|
}
|
|
|
|
const binUtilizationList = () => {
|
|
const hasInventory = binMetrics && binMetrics.grainInventory.length > 0;
|
|
return (
|
|
<Accordion
|
|
className={classes.accordion}
|
|
style={{ marginTop: 5 }}
|
|
expanded={expandUtilization}
|
|
onChange={(_, expanded) => {
|
|
setExpandUtilization(expanded);
|
|
sessionStorage.setItem("expandBinUtilization", expanded.toString());
|
|
}}>
|
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
<Typography variant="h6" style={{ fontWeight: 650 }}>
|
|
Bin Utilization
|
|
</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
{hasInventory ? (
|
|
<Box width={"100%"}>
|
|
<Typography variant="caption" color="textSecondary">
|
|
Click a chart to filter bins by grain
|
|
</Typography>
|
|
<GridList
|
|
className={classes.gridList}
|
|
cols={
|
|
props.insert && !isMobile
|
|
? 3
|
|
: width === "xs"
|
|
? 3
|
|
: width === "sm"
|
|
? 5
|
|
: width === "md"
|
|
? 6
|
|
: width === "lg"
|
|
? 7
|
|
: 8
|
|
}>
|
|
{binMetrics &&
|
|
binMetrics.grainInventory.map((inv, key) => (
|
|
<GridListTile key={key}>
|
|
<BinUtilizationChart
|
|
grain={inv.grainType}
|
|
customUnit={grainUnitDisplay()}
|
|
bushelAmount={supportedGrainDisplay(inv.grainType, inv.bushelAmount)}
|
|
bushelCapacity={supportedGrainDisplay(inv.grainType, inv.bushelCapacity)}
|
|
onClick={() => {
|
|
setContentFilter(pond.Grain[inv.grainType]);
|
|
loadMoreBins(pond.Grain[inv.grainType]);
|
|
}}
|
|
grainActive={contentFilter === pond.Grain[inv.grainType]}
|
|
/>
|
|
</GridListTile>
|
|
))}
|
|
{binMetrics &&
|
|
binMetrics.customInventory.map((inv, key) => {
|
|
//default to bushel values
|
|
let amount = inv.amount;
|
|
let cap = inv.capacity;
|
|
let unit = " bu";
|
|
if (inv.storageType === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
|
|
amount = amount * 35.239;
|
|
cap = cap * 35.239;
|
|
unit = " L";
|
|
}else{
|
|
amount = customQuantityDisplay(inv, amount)
|
|
cap = customQuantityDisplay(inv, cap)
|
|
unit = grainUnitDisplay(inv)
|
|
}
|
|
|
|
return (
|
|
<GridListTile key={key}>
|
|
<BinUtilizationChart
|
|
grain={pond.Grain.GRAIN_CUSTOM}
|
|
bushelAmount={amount}
|
|
bushelCapacity={cap}
|
|
customUnit={unit}
|
|
onClick={() => {
|
|
setContentFilter(inv.name);
|
|
loadMoreBins(inv.name);
|
|
}}
|
|
grainActive={contentFilter === inv.name}
|
|
customLabel={inv.name}
|
|
customColour={stringToMaterialColour(inv.name)}
|
|
/>
|
|
</GridListTile>
|
|
);
|
|
})}
|
|
</GridList>
|
|
</Box>
|
|
) : (
|
|
<Typography color="textSecondary" style={{ margin: "2rem", marginTop: "0rem" }}>
|
|
No active bins
|
|
</Typography>
|
|
)}
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
);
|
|
};
|
|
|
|
const binFanTable = () => {
|
|
return (
|
|
<Accordion
|
|
className={classes.accordion}
|
|
expanded={expandFanTable}
|
|
style={{ marginTop: 5 }}
|
|
onChange={(_, expanded) => {
|
|
setExpandFanTable(expanded);
|
|
sessionStorage.setItem("expandFanTable", expanded.toString());
|
|
}}>
|
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
<Typography variant="h6" style={{ fontWeight: 650 }}>
|
|
Fan Status{" "}
|
|
{totalFanControllers > 0 && !isMobile
|
|
? " - " + totalFanControllersOn + " of " + totalFanControllers + " controllers on"
|
|
: ""}
|
|
</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
{/* <BinsFansStatusTable
|
|
bins={yardBins}
|
|
isLoading={binsLoading}
|
|
getControllerInfo={(total, on) => {
|
|
setTotalFanControllers(total);
|
|
setTotalFanControllersOn(on);
|
|
}}
|
|
/> */}
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box height={1}>
|
|
<Box paddingY={0.5} paddingX={1}>
|
|
<Grid container>
|
|
<Grid size={12}>
|
|
{binsByGrainType()}
|
|
</Grid>
|
|
<Grid size={12}>
|
|
{binsLoading ? <Skeleton variant="rectangular" height="200px" /> : totalInventory()}
|
|
</Grid>
|
|
<Grid size={12}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={12}>
|
|
{binsLoading ? <Skeleton variant="rectangular" height="200px" /> : binUtilizationList()}
|
|
</Grid>
|
|
<Grid size={12}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={12}>
|
|
{binsLoading ? <Skeleton variant="rectangular" height="200px" /> : binFanTable()}
|
|
</Grid>
|
|
<Grid size={12}>
|
|
<Divider />
|
|
</Grid>
|
|
</Grid>
|
|
</Box>
|
|
{/* <QrCodeGenerator
|
|
open={openQrGenerator}
|
|
close={() => {
|
|
setOpenQrGenerator(false);
|
|
}}
|
|
keyList={qrKeyList}
|
|
requiredUrlAffix={"bins"}
|
|
/> */}
|
|
</Box>
|
|
);
|
|
}
|