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 { getGrainUnit, stringToMaterialColour } from "utils"; //import BinsFansStatusTable from "./BinFansStatusTable"; import BinsList from "./BinsList"; interface Props { yardKey: string; insert?: boolean; } 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 } = 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(null); const [{ as }] = useGlobalState(); const maxBins = 40; const [binsLoading, setBinsLoading] = useState(false); const [expandTotal, setExpandTotal] = useState(false); const [displayedInventory, setDisplayedInventory] = useState([]); const [displayedFertilizer, setDisplayedFertilizer] = useState([]); 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([]); const [grainBins, setGrainBins] = useState([]); const [fertBins, setFertBins] = useState([]); const [emptyBins, setEmptyBins] = useState([]); const [binTotal, setBinTotal] = useState(0); const [binMetrics, setBinMetrics] = useState(); const [contentFilter, setContentFilter] = useState(""); 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("name"); //const [qrKeyList, setQrKeyList] = useState([]); 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 ( No bins found Create a bin and it will appear here ); } return ( {displayGrain && ( )} {displayFert && ( )} {displayEmpty && ( { // //only triggered in the scroll view so this will never trigger in grid view // if (yardBins.length < binTotal) { // loadMoreBins(contentFilter); // setScrollTranslations({ ...scrollTranslations, empty: newTranslation }); // } // }} /> )} ); }; const binsHeader = () => { return ( { setBinMenuAnchorEl(null); }} disableAutoFocusItem> Order Order By { setDisplayGrain(checked); }} /> } label={Grain} /> { setDisplayFert(checked); }} /> } label={Fertilizer} /> { setDisplayEmpty(checked); }} /> } label={Empty Bins} /> {/* { setOpenQrGenerator(true); }}> Generate QR Codes */} ); }; const binsByGrainType = () => { return ( Bins - ({yardBins.length} of {binTotal}) {contentFilter && ( { setContentFilter(""); loadMoreBins(""); }} label={contentFilter} /> )} {yardBins.length < binTotal && ( )} { let target = event.currentTarget; setBinMenuAnchorEl(target); }} /> {binsLoading ? : binsContent()} {binsHeader()} ); }; const desktopInventory = () => { return ( { 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} /> { 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} /> ); }; const mobileViewCarousel = () => { let length = 2; let charts: JSX.Element[] = [ { 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} />, { 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 ( {charts[carouselIndex]} ); }; const totalInventory = () => { return ( { setExpandTotal(expanded); sessionStorage.setItem("expandBinTotal", expanded.toString()); }}> }> Total Inventory {isMobile || insert ? mobileViewCarousel() : desktopInventory()} ); }; const binUtilizationList = () => { const hasInventory = binMetrics && binMetrics.grainInventory.length > 0; const useWeight = getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT; return ( { setExpandUtilization(expanded); sessionStorage.setItem("expandBinUtilization", expanded.toString()); }}> }> Bin Utilization {hasInventory ? ( Click a chart to filter bins by grain {binMetrics && binMetrics.grainInventory.map((inv, key) => ( { setContentFilter(pond.Grain[inv.grainType]); loadMoreBins(pond.Grain[inv.grainType]); }} grainActive={contentFilter === pond.Grain[inv.grainType]} /> ))} {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"; } if (useWeight && inv.bushelsPerTonne > 1) { amount = amount / inv.bushelsPerTonne; cap = cap / inv.bushelsPerTonne; unit = " mT"; } return ( { setContentFilter(inv.name); loadMoreBins(inv.name); }} grainActive={contentFilter === inv.name} customLabel={inv.name} customColour={stringToMaterialColour(inv.name)} /> ); })} ) : ( No active bins )} ); }; const binFanTable = () => { return ( { setExpandFanTable(expanded); sessionStorage.setItem("expandFanTable", expanded.toString()); }}> }> Fan Status{" "} {totalFanControllers > 0 && !isMobile ? " - " + totalFanControllersOn + " of " + totalFanControllers + " controllers on" : ""} {/* { setTotalFanControllers(total); setTotalFanControllersOn(on); }} /> */} ); }; return ( {binsByGrainType()} {binsLoading ? : totalInventory()} {binsLoading ? : binUtilizationList()} {binsLoading ? : binFanTable()} {/* { setOpenQrGenerator(false); }} keyList={qrKeyList} requiredUrlAffix={"bins"} /> */} ); }