1456 lines
48 KiB
TypeScript
1456 lines
48 KiB
TypeScript
import {
|
|
Accordion,
|
|
AccordionDetails,
|
|
AccordionSummary,
|
|
Box,
|
|
Button,
|
|
Checkbox,
|
|
Chip,
|
|
darken,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogTitle,
|
|
Divider,
|
|
FormControl,
|
|
FormControlLabel,
|
|
Grid2 as Grid,
|
|
IconButton,
|
|
ImageList,
|
|
ImageListItem,
|
|
InputLabel,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Menu,
|
|
MenuItem,
|
|
Select,
|
|
Skeleton,
|
|
Theme,
|
|
Typography,
|
|
useTheme,
|
|
} from "@mui/material";
|
|
import {
|
|
ArrowBackIos,
|
|
ArrowForwardIos,
|
|
ExpandMore,
|
|
LibraryAdd,
|
|
MoreVert,
|
|
ViewComfy,
|
|
ViewList
|
|
} from "@mui/icons-material";
|
|
import BinsFansStatusTable from "bin/BinFansStatusTable";
|
|
// 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 QrCodeGenerator, { QrCodeKey } from "common/QrCodeGenerator";
|
|
import GrainDescriber, { grainName } from "grain/GrainDescriber";
|
|
// import GrainBagList from "grainBag/grainBagList";
|
|
// import GrainBagSettings from "grainBag/grainBagSettings";
|
|
import { useMobile, useWidth } from "hooks";
|
|
import { Dictionary } from "lodash";
|
|
// import { Bin, Interaction, BinYard as BinYardModel } from "models";
|
|
import { Bin, Interaction, BinYard as BinYardModel } from "models";
|
|
// import { GrainBag } from "models/GrainBag";
|
|
import { pond, quack } from "protobuf-ts/pond";
|
|
import {
|
|
useBinAPI,
|
|
// useBinYardAPI,
|
|
useGlobalState,
|
|
useInteractionsAPI,
|
|
useSnackbar,
|
|
useTeamAPI
|
|
} from "providers";
|
|
import React, { SetStateAction, useCallback, useEffect, useState } from "react";
|
|
// import { getThemeType } from "theme";
|
|
import { getGrainUnit, stringToMaterialColour } from "utils";
|
|
//import InfiniteScroll from "react-infinite-scroller";
|
|
import PageContainer from "./PageContainer";
|
|
import ObjectTable from "objects/ObjectTable";
|
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
|
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
|
import { green, yellow } from "@mui/material/colors";
|
|
import { makeStyles } from "@mui/styles";
|
|
import { useNavigate } from "react-router-dom";
|
|
import BinSettings from "bin/BinSettings";
|
|
import AddBinFab from "bin/AddBinFab";
|
|
import { GrainBag } from "models/GrainBag";
|
|
import { getThemeType } from "theme/themeType";
|
|
import GrainBagSettings from "grainBag/grainBagSettings";
|
|
import GrainBagList from "grainBag/grainBagList";
|
|
import BinYards from "bin/BinYards";
|
|
import ButtonGroup from "common/ButtonGroup";
|
|
import TeamDialog from "teams/TeamDialog";
|
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
|
// import { useHistory } from "react-router";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
const themeType = getThemeType();
|
|
const accordionBg = themeType === "light" ? theme.palette.background.paper : darken(theme.palette.background.paper, 0.2)
|
|
return ({
|
|
"@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: accordionBg
|
|
}
|
|
});
|
|
});
|
|
|
|
interface PaginatedBins {
|
|
bins: Bin[];
|
|
binsOffset: number;
|
|
binsTotal: number;
|
|
}
|
|
|
|
interface Props {
|
|
yardFilter?: string;
|
|
yards?: pond.BinYardSettings[];
|
|
insert?: boolean;
|
|
}
|
|
|
|
export default function Bins(props: Props) {
|
|
const classes = useStyles();
|
|
const isMobile = useMobile();
|
|
const binAPI = useBinAPI();
|
|
const teamAPI = useTeamAPI();
|
|
// const binyardAPI = useBinYardAPI();
|
|
const interactionsAPI = useInteractionsAPI();
|
|
const [{ user, as }] = useGlobalState();
|
|
const [allLoading, setAllLoading] = useState(false);
|
|
const [binsLoading, setBinsLoading] = useState(false);
|
|
const [doneLoadingPage, setDoneLoadingPage] = useState(false)
|
|
const [addBinOpen, setAddBinOpen] = useState(false);
|
|
const [paginatedBins, setPaginatedBins] = useState<PaginatedBins>({
|
|
bins: [],
|
|
binsOffset: 0,
|
|
binsTotal: 0
|
|
});
|
|
const [binMetrics, setBinMetrics] = useState<pond.BinMetrics>();
|
|
const [binMenuAnchorEl, setBinMenuAnchorEl] = useState<Element | null>(null);
|
|
const [menuAnchorEl, setMenuAnchorEl] = useState<Element | null>(null);
|
|
const [contentFilter, setContentFilter] = useState<string>("");
|
|
const [yardFilter, setYardFilter] = useState<string>("");
|
|
const [order, setOrder] = useState<"asc" | "desc">("asc");
|
|
const [orderBy, setOrderBy] = useState<string>("name");
|
|
const binsLimit = 40;
|
|
const theme = useTheme();
|
|
const width = useWidth();
|
|
const [yards, setYards] = useState<pond.BinYardSettings[]>(props.yards ?? []);
|
|
const [yardPermissions, setYardPermissions] = useState<Dictionary<pond.Permission[]>>({});
|
|
const [loadingAs, setLoadingAs] = useState(as);
|
|
const [loadingFilter, setLoadingFilter] = useState("init");
|
|
const [displayGrain, setDisplayGrain] = useState(true);
|
|
const [displayFert, setDisplayFert] = useState(false);
|
|
const [displayEmpty, setDisplayEmpty] = useState(false);
|
|
const [grainBins, setGrainBins] = useState<Bin[]>([]);
|
|
const [fertilizerBins, setFertilizerBins] = useState<Bin[]>([]);
|
|
const [emptyBins, setEmptyBins] = useState<Bin[]>([]);
|
|
const [grainBags, setGrainBags] = useState<GrainBag[]>([]);
|
|
const [displayedInventory, setDisplayedInventory] = useState<GrainAmount[]>([]);
|
|
const [displayedFertilizer, setDisplayedFertilizer] = useState<GrainAmount[]>([]);
|
|
const [carouselIndex, setCarouselIndex] = useState(0);
|
|
const [addBagOpen, setAddBagOpen] = useState(false);
|
|
const [showing, setShowing] = useState<"all" | "bins" | "bags">("all");
|
|
//const [binGridView, setBinGridView] = useState(false);
|
|
const [binView, setBinView] = useState<"grid" | "scroll" | "list">("grid");
|
|
const [bagView, setBagView] = useState<"grid" | "scroll" | "list">("grid");
|
|
const [expandTotal, setExpandTotal] = useState(false);
|
|
const [expandUtilization, setExpandUtilization] = useState(false);
|
|
const [expandFanTable, setExpandFanTable] = useState(false);
|
|
const [openQrGenerator, setOpenQrGenerator] = useState(false);
|
|
const [qrKeyList, setQrKeyList] = useState<QrCodeKey[]>([]);
|
|
const [totalFanControllers, setTotalFanControllers] = useState(0);
|
|
const [totalFanControllersOn, setTotalFanControllersOn] = useState(0);
|
|
// const [yardsLoading, setYardsLoading] = useState(false);
|
|
const [openAddAlerts, setOpenAddAlerts] = useState(false);
|
|
const [alertBins, setAlertBins] = useState<Bin[]>([]);
|
|
const { openSnack } = useSnackbar();
|
|
// const history = useHistory();
|
|
const navigate = useNavigate()
|
|
const [cardValDisplay, setCardValDisplay] = useState<"high" | "low" | "average">("high");
|
|
|
|
const [teamsDialog, setTeamsDialog] = useState(false)
|
|
const snackbar = useSnackbar()
|
|
|
|
// const [scrollTranslations, setScrollTranslations] = useState({
|
|
// bins: 0,
|
|
// empty: 0,
|
|
// fertilizer: 0
|
|
// });
|
|
|
|
// const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
|
// '& .MuiToggleButtonGroup-grouped': {
|
|
// // border: 'none' replaces the old 'grouped' border: "none"
|
|
// border: 'none',
|
|
// padding: theme.spacing(1),
|
|
// // Middle buttons
|
|
// '&:not(:first-child):not(:last-child)': {
|
|
// borderRadius: 24,
|
|
// marginRight: theme.spacing(0.5),
|
|
// marginLeft: theme.spacing(0.5),
|
|
// },
|
|
// // First button
|
|
// '&:first-child': {
|
|
// borderRadius: 24,
|
|
// marginLeft: theme.spacing(0.25),
|
|
// },
|
|
// // Last button
|
|
// '&:last-child': {
|
|
// borderRadius: 24,
|
|
// marginRight: theme.spacing(0.25),
|
|
// },
|
|
// },
|
|
// // Root styles
|
|
// backgroundColor: darken(
|
|
// theme.palette.background.paper,
|
|
// getThemeType() === 'light' ? 0.05 : 0.25
|
|
// ),
|
|
// borderRadius: 24,
|
|
// boxSizing: 'content-box', // Fixed 'content: "border-box"' typo from original
|
|
// }));
|
|
|
|
// const StyledToggle = styled(ToggleButton)(({ theme }: { theme: Theme }) => ({
|
|
// root: {
|
|
// backgroundColor: "transparent",
|
|
// overflow: "visible",
|
|
// content: "content-box",
|
|
// "&$selected": {
|
|
// backgroundColor: "gold",
|
|
// color: "black",
|
|
// borderRadius: 24,
|
|
// fontWeight: "bold"
|
|
// },
|
|
// "&$selected:hover": {
|
|
// backgroundColor: "rgb(255, 255, 0)",
|
|
// color: "black",
|
|
// borderRadius: 24
|
|
// }
|
|
// },
|
|
// selected: {}
|
|
// }));
|
|
|
|
useEffect(() => {
|
|
if (doneLoadingPage && grainBins.length < 1 && grainBags.length < 1) {
|
|
teamAPI.listTeams(1, 0).then(resp => {
|
|
// console.log(resp.data.teams)
|
|
if (resp.data?.teams?.length > 0) {
|
|
// console.log("should?")
|
|
setTeamsDialog(true)
|
|
}
|
|
}).catch(err => {
|
|
snackbar.error("error listing teams: "+err)
|
|
})
|
|
}
|
|
}, [doneLoadingPage, grainBins, grainBags])
|
|
|
|
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 binsView = sessionStorage.getItem("binsView");
|
|
if (binsView === "scroll") {
|
|
setBinView("scroll");
|
|
} else {
|
|
setBinView("grid");
|
|
}
|
|
|
|
let bagsView = sessionStorage.getItem("bagsView");
|
|
if (bagsView === "scroll") {
|
|
setBagView("scroll");
|
|
} else {
|
|
setBagView("grid");
|
|
}
|
|
|
|
let fanTable = sessionStorage.getItem("expandFanTable");
|
|
if (fanTable === "true") {
|
|
setExpandFanTable(true);
|
|
} else {
|
|
setExpandFanTable(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (props.yardFilter) {
|
|
setYardFilter(props.yardFilter);
|
|
}
|
|
}, [props.yardFilter]);
|
|
|
|
const getKeys = () => {
|
|
if (as) return undefined
|
|
return getContextKeys()
|
|
}
|
|
|
|
const getTypes = () => {
|
|
if (as) return undefined
|
|
return getContextTypes()
|
|
}
|
|
|
|
const loadBins = useCallback(() => {
|
|
//let filter = grainFilter;
|
|
let filter = yardFilter;
|
|
if (as === loadingAs && loadingFilter === filter) {
|
|
return;
|
|
}
|
|
setLoadingAs(as);
|
|
setLoadingFilter(filter);
|
|
setAllLoading(true);
|
|
setBinsLoading(true);
|
|
setDisplayEmpty(false);
|
|
setDisplayFert(false);
|
|
|
|
binAPI
|
|
.listBinsAndData(binsLimit, 0, order, orderBy, filter, as, false, getKeys(), getTypes())
|
|
.then(resp => {
|
|
let grain: Bin[] = [];
|
|
let empty: Bin[] = [];
|
|
let fert: Bin[] = [];
|
|
let qr: QrCodeKey[] = [];
|
|
let b = resp.data.bins.map(b => Bin.any(b));
|
|
b.forEach(bin => {
|
|
if (bin.empty()) {
|
|
empty.push(bin);
|
|
} else if (bin.storage() === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
|
|
fert.push(bin);
|
|
} else {
|
|
grain.push(bin);
|
|
}
|
|
//build qr keys here
|
|
qr.push({
|
|
key: bin.key(),
|
|
name: bin.name()
|
|
});
|
|
});
|
|
setQrKeyList(qr);
|
|
setGrainBins(grain);
|
|
if (fert.length > 0) {
|
|
setDisplayFert(true);
|
|
}
|
|
if (empty.length > 0) {
|
|
setDisplayEmpty(true);
|
|
}
|
|
setFertilizerBins(fert);
|
|
setEmptyBins(empty);
|
|
setPaginatedBins({
|
|
bins: b,
|
|
binsOffset: resp.data.nextOffset,
|
|
binsTotal: resp.data.total
|
|
});
|
|
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);
|
|
if (yards.length === 0) {
|
|
let y: pond.BinYardSettings[] = [];
|
|
let p: Dictionary<pond.Permission[]> = {};
|
|
let sortedYards = resp.data.binYards.sort((a, b) => {
|
|
let yard1 = BinYardModel.create(a);
|
|
let yard2 = BinYardModel.create(b);
|
|
return (
|
|
yard1.settings.userSort[as ?? user.id()] - yard2.settings.userSort[as ?? user.id()]
|
|
);
|
|
});
|
|
sortedYards.forEach(yard => {
|
|
let newYard = pond.BinYard.fromObject(yard ?? {});
|
|
if (newYard.settings) {
|
|
y.push(newYard.settings);
|
|
p[newYard.settings.key] = newYard.yardPermissions;
|
|
}
|
|
});
|
|
setYardPermissions(p);
|
|
setYards(y);
|
|
}
|
|
|
|
//move load of grain bags into this api call
|
|
const bags = resp.data.bags.map(b => GrainBag.create(b));
|
|
setGrainBags(bags);
|
|
})
|
|
.catch(() => {
|
|
setPaginatedBins({ bins: [], binsOffset: 0, binsTotal: 0 });
|
|
setBinMetrics(pond.BinMetrics.create());
|
|
setYards([]);
|
|
})
|
|
.finally(() => {
|
|
setAllLoading(false);
|
|
setBinsLoading(false);
|
|
setDoneLoadingPage(true)
|
|
});
|
|
}, [binAPI, yardFilter, order, orderBy, as, loadingAs, loadingFilter]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
//made this a seperate function because we only need to load the bins themselves, we do not need to load all the metric data again
|
|
const loadMoreBins = (filter: string, offset?: number, limit?: number) => {
|
|
setBinsLoading(true);
|
|
let binFilter = filter;
|
|
binFilter = binFilter + " " + yardFilter;
|
|
binAPI
|
|
.listBins(limit ?? binsLimit, offset ?? 0, order, orderBy, binFilter, as)
|
|
.then(resp => {
|
|
const newBins = resp.data.bins.map(b => Bin.any(b));
|
|
setPaginatedBins({
|
|
//the offset being passed in means to load more rather than a new set so combine the loaded bins with the existing ones rather than replace them
|
|
bins: offset ? paginatedBins.bins.concat(newBins) : newBins,
|
|
binsOffset: resp.data.nextOffset,
|
|
binsTotal: resp.data.total
|
|
});
|
|
let empty: Bin[] = offset ? emptyBins : [];
|
|
let fert: Bin[] = offset ? fertilizerBins : [];
|
|
let grain: Bin[] = offset ? grainBins : [];
|
|
let newKeyList: QrCodeKey[] = qrKeyList;
|
|
newBins.forEach(bin => {
|
|
if (bin.empty()) {
|
|
empty.push(bin);
|
|
} else if (bin.storage() === pond.BinStorage.BIN_STORAGE_FERTILIZER) {
|
|
fert.push(bin);
|
|
} else {
|
|
grain.push(bin);
|
|
}
|
|
newKeyList.push({
|
|
key: bin.key(),
|
|
name: bin.name()
|
|
});
|
|
});
|
|
if (fert.length > 0) {
|
|
setDisplayFert(true);
|
|
}
|
|
if (empty.length > 0) {
|
|
setDisplayEmpty(true);
|
|
}
|
|
setGrainBins([...grain]);
|
|
setEmptyBins([...empty]);
|
|
setFertilizerBins([...fert]);
|
|
setQrKeyList([...newKeyList]);
|
|
})
|
|
.catch(() => {
|
|
setPaginatedBins({ bins: [], binsOffset: 0, binsTotal: 0 });
|
|
})
|
|
.finally(() => {
|
|
setBinsLoading(false);
|
|
});
|
|
};
|
|
|
|
// const searchYards = (
|
|
// search?: string,
|
|
// limit?: number,
|
|
// offset?: number,
|
|
// order?: "asc" | "desc"
|
|
// ) => {
|
|
// setYardsLoading(true);
|
|
// binyardAPI.listBinYards(limit ?? binsLimit, offset ?? 0, order, "name", search).then(resp => {
|
|
// let y: pond.BinYardSettings[] = [];
|
|
// let p: Dictionary<pond.Permission[]> = {};
|
|
// //sort the yards after loading
|
|
// let sortedYards = resp.data.yard.sort((a, b) => {
|
|
// let yard1 = BinYardModel.create(a);
|
|
// let yard2 = BinYardModel.create(b);
|
|
// return yard1.settings.userSort[as ?? user.id()] - yard2.settings.userSort[as ?? user.id()];
|
|
// });
|
|
// //loop through the sorted yards
|
|
// sortedYards.forEach(yard => {
|
|
// let newYard = pond.BinYard.fromObject(yard ?? {});
|
|
// if (newYard.settings) {
|
|
// y.push(newYard.settings);
|
|
// p[newYard.settings.key] = newYard.yardPermissions;
|
|
// }
|
|
// });
|
|
// if (y.length > 0) {
|
|
// //dont set the yards if it came back with nothing
|
|
// setYardPermissions(p);
|
|
// setYards(y);
|
|
// }
|
|
// setYardsLoading(false);
|
|
// });
|
|
// };
|
|
|
|
useEffect(() => {
|
|
loadBins();
|
|
//if it is a drawer only show the bins, bags are only shown on the all tab so should not show up on the yard drawers
|
|
if (props.insert) {
|
|
setShowing("bins");
|
|
}
|
|
}, [loadBins, props.insert, contentFilter]);
|
|
|
|
const determineNodeToggle = () => {
|
|
switch(cardValDisplay) {
|
|
case "low":
|
|
return [0]
|
|
case "high":
|
|
return [2]
|
|
default:
|
|
return [1]
|
|
}
|
|
}
|
|
|
|
const duplicateBin = (bin: Bin) => {
|
|
binAPI.addBin(bin.settings, as).then(() => {
|
|
loadMoreBins(contentFilter);
|
|
});
|
|
};
|
|
|
|
const addAlertsToCables = (bins: Bin[]) => {
|
|
let alertList: pond.AlertData[] = [];
|
|
bins.forEach(bin => {
|
|
//for each of the cable in the bin create alert data
|
|
bin.status.grainCables.forEach(cable => {
|
|
//split the location into its three parts
|
|
let locationParts = cable.address.split("-");
|
|
if (locationParts[0] && locationParts[1] && locationParts[2]) {
|
|
//build the alert based on the bin
|
|
alertList.push(
|
|
pond.AlertData.create({
|
|
device: cable.device,
|
|
nodeOne: cable.topNode,
|
|
interactionSubtype: Interaction.upToSubtype(cable.topNode),
|
|
condition: pond.InteractionCondition.create({
|
|
measurementType: quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
|
|
comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
|
|
value: describeMeasurement(
|
|
quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE
|
|
).toStored(bin.settings.highTemp)
|
|
}),
|
|
componentLocation: quack.ComponentID.create({
|
|
type: parseInt(locationParts[0]),
|
|
addressType: parseInt(locationParts[1]),
|
|
address: parseInt(locationParts[2])
|
|
})
|
|
})
|
|
);
|
|
}
|
|
});
|
|
});
|
|
interactionsAPI
|
|
.setAlertInteractions(alertList, as)
|
|
.then(resp => {
|
|
openSnack("Successfully added " + resp.data.numAlertsSet + " alerts");
|
|
})
|
|
.catch(() => {
|
|
console.log("errors found");
|
|
});
|
|
};
|
|
|
|
const addALertsDialog = () => {
|
|
return (
|
|
<ResponsiveDialog open={openAddAlerts} onClose={() => setOpenAddAlerts(false)}>
|
|
<DialogTitle>Add Alert to Bin Cables</DialogTitle>
|
|
<DialogContent>
|
|
This action will add an interaction to notify if the temp of any cable node in grain rises
|
|
above the high temp warning for all selected bins
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={() => setOpenAddAlerts(false)}>Close</Button>
|
|
<Button
|
|
onClick={() => {
|
|
addAlertsToCables(alertBins);
|
|
setOpenAddAlerts(false);
|
|
}}
|
|
variant="contained"
|
|
color="primary">
|
|
Confirm
|
|
</Button>
|
|
</DialogActions>
|
|
</ResponsiveDialog>
|
|
);
|
|
};
|
|
|
|
const binsContent = () => {
|
|
const { binsTotal } = paginatedBins;
|
|
|
|
if (!allLoading && binsTotal <= 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>
|
|
);
|
|
}
|
|
|
|
if (binView === "list") {
|
|
return (
|
|
<ObjectTable
|
|
startingObject={pond.ObjectType.OBJECT_TYPE_BIN}
|
|
preLoadedData={paginatedBins.bins}
|
|
rowClickFunction={data => {
|
|
let bin = data as Bin;
|
|
// let path = "bins/" + bin.key();
|
|
// navigate(path, { state: { bin: bin }});
|
|
navigate(bin.key(), { state: { bin: bin }, relative: "path"});
|
|
}}
|
|
customButtons={[
|
|
{
|
|
label: "Set High Temp Notifications",
|
|
function: objects => {
|
|
setAlertBins(objects as Bin[]);
|
|
setOpenAddAlerts(true);
|
|
}
|
|
}
|
|
]}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Grid container>
|
|
{displayGrain && (
|
|
<React.Fragment>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<BinsList
|
|
valDisplay={cardValDisplay}
|
|
gridView={binView === "grid"}
|
|
bins={grainBins}
|
|
duplicateBin={duplicateBin}
|
|
title={"Grain Bins"}
|
|
//startingTranslate={scrollTranslations.bins}
|
|
loadMore={() => {
|
|
if (paginatedBins.bins.length < paginatedBins.binsTotal) {
|
|
loadMoreBins(contentFilter, paginatedBins.binsOffset);
|
|
}
|
|
}}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
{displayFert && (
|
|
<React.Fragment>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<BinsList
|
|
valDisplay={cardValDisplay}
|
|
gridView={binView === "grid"}
|
|
bins={fertilizerBins}
|
|
duplicateBin={duplicateBin}
|
|
title={"Fertilizer Bins"}
|
|
//startingTranslate={scrollTranslations.fertilizer}
|
|
loadMore={() => {
|
|
if (paginatedBins.bins.length < paginatedBins.binsTotal) {
|
|
loadMoreBins(contentFilter, paginatedBins.binsOffset);
|
|
//setScrollTranslations({ ...scrollTranslations, fertilizer: newTranslation });
|
|
}
|
|
}}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
{displayEmpty && (
|
|
<React.Fragment>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<BinsList
|
|
valDisplay={cardValDisplay}
|
|
gridView={binView === "grid"}
|
|
bins={emptyBins}
|
|
duplicateBin={duplicateBin}
|
|
title={"Empty Bins"}
|
|
//startingTranslate={scrollTranslations.empty}
|
|
loadMore={() => {
|
|
if (paginatedBins.bins.length < paginatedBins.binsTotal) {
|
|
loadMoreBins(contentFilter, paginatedBins.binsOffset);
|
|
//setScrollTranslations({ ...scrollTranslations, empty: newTranslation });
|
|
}
|
|
}}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
)}
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const grainBagsContent = () => {
|
|
if (!allLoading && grainBags.length <= 0) {
|
|
return (
|
|
<Box textAlign="center" marginTop={1}>
|
|
<Typography variant="subtitle1">No bags found</Typography>
|
|
<Typography variant="body1" color="textSecondary">
|
|
Create a grain bag and it will appear here
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
// if (bagView === "list") {
|
|
// return (
|
|
// <ObjectTable
|
|
// startingObject={pond.ObjectType.OBJECT_TYPE_GRAIN_BAG}
|
|
// preLoadedData={grainBags}
|
|
// rowClickFunction={data => {
|
|
// let bag = data as GrainBag;
|
|
// let path = "/grainbags/" + bag.key();
|
|
// history.push(path);
|
|
// }}
|
|
// />
|
|
// );
|
|
// }
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Grid container>
|
|
<React.Fragment>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<GrainBagList
|
|
gridView={bagView === "grid"}
|
|
grainBags={grainBags}
|
|
title={"Grain Bags"}
|
|
/>
|
|
</Grid>
|
|
</React.Fragment>
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const binMenu = () => {
|
|
return (
|
|
<Menu
|
|
id="binMenu"
|
|
anchorEl={menuAnchorEl ? menuAnchorEl : null}
|
|
open={menuAnchorEl !== null}
|
|
onClose={() => {
|
|
setMenuAnchorEl(null);
|
|
setAddBinOpen(false);
|
|
}}
|
|
disableAutoFocusItem>
|
|
<MenuItem onClick={() => setAddBinOpen(true)} aria-label="Create Bin" dense>
|
|
<ListItemIcon>
|
|
<LibraryAdd className={classes.green} />
|
|
</ListItemIcon>
|
|
<ListItemText secondary="Create Bin" />
|
|
</MenuItem>
|
|
</Menu>
|
|
);
|
|
};
|
|
|
|
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={{ xs: 2 }}>
|
|
<Button
|
|
style={{ height: 160 }}
|
|
onClick={() => {
|
|
const newIndex = carouselIndex - 1;
|
|
setCarouselIndex(newIndex < 0 ? length - 1 : newIndex);
|
|
}}>
|
|
<ArrowBackIos />
|
|
</Button>
|
|
</Grid>
|
|
<Grid size={{ xs: 8 }}>
|
|
<Box height={"180px"} flexDirection="row" display="flex">
|
|
{charts[carouselIndex]}
|
|
</Box>
|
|
</Grid>
|
|
<Grid size={{ xs: 2 }}>
|
|
<Button
|
|
style={{ height: 160 }}
|
|
onClick={() => {
|
|
const newIndex = carouselIndex + 1;
|
|
setCarouselIndex(newIndex >= length ? 0 : newIndex);
|
|
}}>
|
|
<ArrowForwardIos />
|
|
</Button>
|
|
</Grid>
|
|
</Grid>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const desktopInventory = () => {
|
|
return (
|
|
<React.Fragment>
|
|
<Grid container direction="row" alignContent="center" alignItems="center">
|
|
<Grid style={{ height: "200px" }} size={{ xs: 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={{ xs: 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 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 ? mobileViewCarousel() : desktopInventory()}</AccordionDetails>
|
|
</Accordion>
|
|
);
|
|
};
|
|
|
|
const binUtilizationList = () => {
|
|
const hasInventory = binMetrics && binMetrics.grainInventory.length > 0;
|
|
const useWeight = getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT;
|
|
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>
|
|
<ImageList
|
|
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) => (
|
|
<ImageListItem key={key}>
|
|
<BinUtilizationChart
|
|
grain={inv.grainType}
|
|
customUnit={useWeight ? " mT" : " bu"}
|
|
bushelAmount={
|
|
useWeight
|
|
? inv.bushelAmount / GrainDescriber(inv.grainType).bushelsPerTonne
|
|
: inv.bushelAmount
|
|
}
|
|
bushelCapacity={
|
|
useWeight
|
|
? inv.bushelCapacity / GrainDescriber(inv.grainType).bushelsPerTonne
|
|
: inv.bushelCapacity
|
|
}
|
|
onClick={() => {
|
|
setContentFilter(pond.Grain[inv.grainType]);
|
|
loadMoreBins(pond.Grain[inv.grainType]);
|
|
}}
|
|
grainActive={contentFilter === pond.Grain[inv.grainType]}
|
|
/>
|
|
</ImageListItem>
|
|
))}
|
|
{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 (
|
|
<ImageListItem 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)}
|
|
/>
|
|
</ImageListItem>
|
|
);
|
|
})}
|
|
</ImageList>
|
|
</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={paginatedBins.bins}
|
|
isLoading={binsLoading}
|
|
getControllerInfo={(total, on) => {
|
|
setTotalFanControllers(total);
|
|
setTotalFanControllersOn(on);
|
|
}}
|
|
/>
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
);
|
|
};
|
|
|
|
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={(_, checked) => {
|
|
setDisplayGrain(checked);
|
|
}}
|
|
/>
|
|
}
|
|
label={<Typography>Grain</Typography>}
|
|
/>
|
|
</MenuItem>
|
|
<MenuItem>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={displayFert}
|
|
onChange={(_, checked) => {
|
|
setDisplayFert(checked);
|
|
}}
|
|
/>
|
|
}
|
|
label={<Typography>Fertilizer</Typography>}
|
|
/>
|
|
</MenuItem>
|
|
<MenuItem>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={displayEmpty}
|
|
onChange={(_, checked) => {
|
|
setDisplayEmpty(checked);
|
|
}}
|
|
/>
|
|
}
|
|
label={<Typography>Empty Bins</Typography>}
|
|
/>
|
|
</MenuItem>
|
|
<MenuItem
|
|
onClick={() => {
|
|
setOpenQrGenerator(true);
|
|
setBinMenuAnchorEl(null)
|
|
}}>
|
|
<Typography>Generate QR Codes</Typography>
|
|
</MenuItem>
|
|
</Menu>
|
|
);
|
|
};
|
|
|
|
const binsByGrainType = () => {
|
|
return (
|
|
<Box width={1} marginBottom={1}>
|
|
<Grid
|
|
container
|
|
direction="row"
|
|
alignContent="center"
|
|
alignItems="center"
|
|
justifyContent="space-between">
|
|
<Grid >
|
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
|
<Typography variant="h6" style={{ fontWeight: 650 }}>
|
|
Bins - ({paginatedBins.bins.length} of {paginatedBins.binsTotal})
|
|
</Typography>
|
|
{contentFilter && (
|
|
<Box marginLeft={1}>
|
|
<Chip
|
|
size="small"
|
|
onDelete={() => {
|
|
setContentFilter("");
|
|
loadMoreBins("");
|
|
}}
|
|
label={contentFilter}
|
|
/>
|
|
</Box>
|
|
)}
|
|
{(binView === "grid" || binView === "list") &&
|
|
paginatedBins.bins.length < paginatedBins.binsTotal && (
|
|
<Button
|
|
style={{ marginLeft: 10 }}
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={() => {
|
|
loadMoreBins(
|
|
contentFilter,
|
|
paginatedBins.binsOffset,
|
|
paginatedBins.binsTotal - paginatedBins.bins.length
|
|
);
|
|
}}>
|
|
Load All
|
|
</Button>
|
|
)}
|
|
<ButtonGroup
|
|
toggle
|
|
toggledButtons={determineNodeToggle()}
|
|
buttons={[
|
|
{
|
|
title: "Low",
|
|
function: () => {setCardValDisplay("low")}
|
|
},
|
|
{
|
|
title: "Average",
|
|
function: () => {setCardValDisplay("average")}
|
|
},
|
|
{
|
|
title: "High",
|
|
function: () => {setCardValDisplay("high")}
|
|
}
|
|
]}
|
|
/>
|
|
</Box>
|
|
</Grid>
|
|
<Grid >
|
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
|
<ButtonGroup
|
|
toggle
|
|
buttons={[
|
|
{
|
|
title: "Grid",
|
|
icon: <ViewComfy />,
|
|
function: () => {
|
|
setBinView("grid");
|
|
sessionStorage.setItem("binsView", "grid");
|
|
}
|
|
},
|
|
{
|
|
title: "List",
|
|
icon: <ViewList />,
|
|
function: () => {
|
|
setBinView("list");
|
|
sessionStorage.setItem("binsView", "list");
|
|
}
|
|
}
|
|
]}
|
|
/>
|
|
<MoreVert
|
|
className={classes.icon}
|
|
onClick={event => {
|
|
let target = event.currentTarget;
|
|
setBinMenuAnchorEl(target);
|
|
}}
|
|
/>
|
|
</Box>
|
|
</Grid>
|
|
</Grid>
|
|
{binsLoading ? <Skeleton variant="rectangular" height="200px" /> : binsContent()}
|
|
{binsHeader()}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const grainBagDisplay = () => {
|
|
return (
|
|
<Box width={1} marginBottom={1}>
|
|
<Grid
|
|
container
|
|
direction="row"
|
|
alignContent="center"
|
|
alignItems="center"
|
|
justifyContent="space-between">
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid >
|
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
|
<Typography variant="h6" style={{ fontWeight: 650 }}>
|
|
Bags
|
|
</Typography>
|
|
</Box>
|
|
</Grid>
|
|
<Grid >
|
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
|
<ButtonGroup
|
|
toggle
|
|
buttons={[
|
|
{
|
|
title: "Grid",
|
|
icon: <ViewComfy />,
|
|
function: () => {
|
|
setBagView("grid");
|
|
sessionStorage.setItem("bagsView", "grid");
|
|
}
|
|
},
|
|
{
|
|
title: "List",
|
|
icon: <ViewList />,
|
|
function: () => {
|
|
setBagView("list");
|
|
sessionStorage.setItem("bagsView", "list");
|
|
}
|
|
}
|
|
]}
|
|
/>
|
|
{/* <StyledToggleButtonGroup
|
|
id="tour-graph-tabs"
|
|
value={bagView}
|
|
exclusive
|
|
size="small"
|
|
aria-label="detail">
|
|
<StyledToggle
|
|
value={"grid"}
|
|
aria-label="grid view"
|
|
onClick={() => {
|
|
setBagView("grid");
|
|
sessionStorage.setItem("bagsView", "grid");
|
|
}}>
|
|
<ViewComfy />
|
|
</StyledToggle> */}
|
|
{/* hidden at dustins request so that grid view and list are the only two */}
|
|
{/* <StyledToggle
|
|
value={"scroll"}
|
|
aria-label="scroll view"
|
|
onClick={() => {
|
|
setBagView("scroll");
|
|
sessionStorage.setItem("bagsView", "scroll");
|
|
}}>
|
|
<ViewColumn />
|
|
</StyledToggle> */}
|
|
{/* <StyledToggle
|
|
value={"list"}
|
|
aria-label="list view"
|
|
onClick={() => {
|
|
setBagView("list");
|
|
sessionStorage.setItem("bagsView", "list");
|
|
}}>
|
|
<ViewList />
|
|
</StyledToggle>
|
|
</StyledToggleButtonGroup> */}
|
|
<IconButton
|
|
className={classes.icon}
|
|
onClick={() => {
|
|
setAddBagOpen(true);
|
|
}}>
|
|
+
|
|
</IconButton>
|
|
</Box>
|
|
</Grid>
|
|
</Grid>
|
|
{allLoading ? <Skeleton variant="rectangular" height="200px" /> : grainBagsContent()}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const { binsTotal } = paginatedBins;
|
|
|
|
const page = () => {
|
|
return (
|
|
<Box>
|
|
{addALertsDialog()}
|
|
<BinYards
|
|
yards={yards}
|
|
// yardsLoading={yardsLoading}
|
|
yardsLoading={false}
|
|
yardPerms={yardPermissions}
|
|
showTabs={!props.insert}
|
|
setYardFilter={setYardFilter}
|
|
bins={paginatedBins.bins}
|
|
loadBins={loadBins}
|
|
// loadYards={(search, limit, offset) => {
|
|
// searchYards(search, limit, offset);
|
|
// }}
|
|
loadYards={() => {}}
|
|
grainBags={grainBags}
|
|
setShowing={(val: "all" | "bins" | "bags") => {
|
|
setShowing(val);
|
|
}}
|
|
/>
|
|
<Box height={1}>
|
|
<Box
|
|
paddingY={isMobile || props.insert ? 0.5 : 1}
|
|
paddingX={isMobile || props.insert ? 1 : 2}>
|
|
<Grid container>
|
|
{(showing === "all" || showing === "bins") && (
|
|
<Grid size={{ xs: 12 }}>
|
|
{binsByGrainType()}
|
|
</Grid>
|
|
)}
|
|
<Grid size={{ xs: 12 }}>
|
|
{allLoading ? <Skeleton variant="rectangular" height="200px" /> : totalInventory()}
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
{allLoading ? <Skeleton variant="rectangular" height="200px" /> : binUtilizationList()}
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
{allLoading ? <Skeleton variant="rectangular" height="200px" /> : binFanTable()}
|
|
</Grid>
|
|
<Grid size={{ xs: 12 }}>
|
|
<Divider />
|
|
</Grid>
|
|
|
|
{(showing === "all" || showing === "bags") && (
|
|
<Grid size={{ xs: 12 }}>
|
|
{grainBagDisplay()}
|
|
</Grid>
|
|
)}
|
|
</Grid>
|
|
</Box>
|
|
</Box>
|
|
<BinSettings
|
|
mode="add"
|
|
canEdit={true}
|
|
open={addBinOpen}
|
|
userID={user.id()}
|
|
openedBinYard={yardFilter}
|
|
binYards={yards}
|
|
onClose={refresh => {
|
|
setAddBinOpen(false);
|
|
if (refresh) {
|
|
loadBins();
|
|
}
|
|
}}
|
|
/>
|
|
<GrainBagSettings
|
|
open={addBagOpen}
|
|
close={() => {
|
|
setAddBagOpen(false);
|
|
}}
|
|
/>
|
|
<QrCodeGenerator
|
|
open={openQrGenerator}
|
|
close={() => {
|
|
setOpenQrGenerator(false);
|
|
}}
|
|
keyList={qrKeyList}
|
|
requiredUrlAffix={"bins"}
|
|
/>
|
|
{binMenu()}
|
|
<AddBinFab onClick={() => setAddBinOpen(true)} pulse={binsTotal < 1 && !allLoading} />
|
|
<TeamDialog
|
|
open={teamsDialog}
|
|
setOpen={setTeamsDialog}
|
|
title={"You have teams!"}
|
|
info={"Looks like you don't have any bins, but are a part of at least one team. If you want to see your teams bins, please select your team and choose the option \"View as team\""}
|
|
hideKey={"bins-teams-notification-dialog"}
|
|
/>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
return !props.insert ? <PageContainer>{page()}</PageContainer> : page();
|
|
}
|