Merge branch 'staging_environment' into field_dashboard
This commit is contained in:
commit
1028f0639b
38 changed files with 992 additions and 231 deletions
|
|
@ -24,7 +24,7 @@ import RemoveSelfFromObject from "user/RemoveSelfFromObject";
|
|||
import ShareObject from "user/ShareObject";
|
||||
import { isOffline } from "utils/environment";
|
||||
import ObjectTeams from "teams/ObjectTeams";
|
||||
// import BinDuplication from "./BinDuplication";
|
||||
import BinDuplication from "./BinDuplication";
|
||||
import BinsIcon from "products/Bindapt/BinsIcon";
|
||||
import HelpIcon from "@mui/icons-material/Help";
|
||||
import BinSensors from "./BinSensors";
|
||||
|
|
@ -269,14 +269,14 @@ export default function BinActions(props: Props) {
|
|||
closeDialogCallback={() => setOpenState({ ...openState, users: false })}
|
||||
refreshCallback={refreshCallback}
|
||||
/>
|
||||
{/* <BinDuplication
|
||||
<BinDuplication
|
||||
open={openState.duplication}
|
||||
closeDialog={() => {
|
||||
setOpenState({ ...openState, duplication: false });
|
||||
}}
|
||||
bin={bin}
|
||||
refreshCallback={refreshCallback}
|
||||
/> */}
|
||||
/>
|
||||
<RemoveSelfFromObject
|
||||
scope={binScope(key)}
|
||||
label={label}
|
||||
|
|
|
|||
|
|
@ -506,9 +506,6 @@ export default function BinCard(props: Props) {
|
|||
+
|
||||
</IconButton>
|
||||
)}
|
||||
{/* <Box position="absolute" top={4} right={4}>
|
||||
<BinModeDot mode={bin.settings.mode} />
|
||||
</Box> */}
|
||||
<Box padding={1}>
|
||||
<Typography
|
||||
align="left"
|
||||
|
|
|
|||
72
src/bin/BinDuplication.tsx
Normal file
72
src/bin/BinDuplication.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import {
|
||||
Button,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
import { Bin } from "models";
|
||||
import { useBinAPI, useSnackbar } from "providers";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
bin: Bin;
|
||||
closeDialog: () => void;
|
||||
refreshCallback: () => void;
|
||||
}
|
||||
|
||||
export default function BinDuplication(props: Props) {
|
||||
const { open, bin, closeDialog, refreshCallback } = props;
|
||||
const [binDupName, setBinDupName] = useState("(copy of) " + bin.name());
|
||||
const binAPI = useBinAPI();
|
||||
const { openSnack } = useSnackbar();
|
||||
|
||||
useEffect(() => {
|
||||
setBinDupName("(copy of) " + bin.name());
|
||||
}, [bin]);
|
||||
|
||||
const duplicate = () => {
|
||||
let settings = bin.settings;
|
||||
settings.name = binDupName;
|
||||
binAPI
|
||||
.addBin(settings)
|
||||
.then(resp => {
|
||||
openSnack("Successfully duplicated bin");
|
||||
})
|
||||
.catch(err => {
|
||||
openSnack("Failed to duplicate bin");
|
||||
});
|
||||
refreshCallback();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
return (
|
||||
<ResponsiveDialog open={open} onClose={closeDialog}>
|
||||
<DialogTitle>Create Duplicate Bin</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>
|
||||
This will create a new bin with the same settings as {bin.name()}.
|
||||
</Typography>
|
||||
<TextField
|
||||
id={"duplicateName"}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Name of Copy"
|
||||
value={binDupName}
|
||||
onChange={e => setBinDupName(e.target.value)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={closeDialog} style={{ color: "red" }}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={duplicate} color="primary">
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ import { Bin } from "models";
|
|||
import moment from "moment";
|
||||
import { GetBinShapeDescribers } from "pbHelpers/Bin";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useBinAPI, useBinYardAPI, useGlobalState } from "providers";
|
||||
import { useBinAPI, useBinYardAPI, useGlobalState, useLibraCartProxyAPI } from "providers";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
// import { useHistory } from "react-router";
|
||||
import { getDistanceUnit, or, getTemperatureUnit, getGrainUnit } from "utils";
|
||||
|
|
@ -176,7 +176,7 @@ export default function BinSettings(props: Props) {
|
|||
const grainOptions = GrainOptions();
|
||||
const grainUseOptions = GetGrainUseOptions();
|
||||
const [inputCapacity, setInputCapacity] = useState<string>("");
|
||||
const [{ as }] = useGlobalState();
|
||||
const [{ user, as }] = useGlobalState();
|
||||
const [grainDiff, setGrainDiff] = useState(0);
|
||||
const [isCustomInventory, setIsCustomInventory] = useState<boolean>(false);
|
||||
const [grainUpdate, setGrainUpdate] = useState(false);
|
||||
|
|
@ -197,10 +197,13 @@ export default function BinSettings(props: Props) {
|
|||
const [inventoryControl, setInventoryControl] = useState<pond.BinInventoryControl>(
|
||||
pond.BinInventoryControl.BIN_INVENTORY_CONTROL_UNKNOWN
|
||||
);
|
||||
|
||||
const [storageType, setStorageType] = useState<pond.BinStorage>(
|
||||
pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN
|
||||
);
|
||||
//libracart stuff
|
||||
const libracartAPI = useLibraCartProxyAPI()
|
||||
const [lcDestination, setlcDestination] = useState<Option | null>()
|
||||
const [lcDestinationOptions, setlcDestinationOptions] = useState<Option[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
|
|
@ -365,9 +368,33 @@ export default function BinSettings(props: Props) {
|
|||
}
|
||||
}, [binYardAPI, form.yardKey, userID, as, props.binYards]);
|
||||
|
||||
const loadLibraCartDestinations = useCallback(()=>{
|
||||
if(inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART){
|
||||
//load the destinations for the user/team they are viewing as
|
||||
let options: Option[] = []
|
||||
libracartAPI.listDestinations(0,0,undefined, as)
|
||||
.then(resp=>{
|
||||
//set the options for the search select
|
||||
resp.data.destinations.forEach(d => {
|
||||
let newOp: Option = {
|
||||
label: d.name,
|
||||
value: d.id
|
||||
}
|
||||
options.push(newOp)
|
||||
//set the current option based on the key in the form
|
||||
if (d.id === form.libracartDestinationKey){
|
||||
setlcDestination(newOp)
|
||||
}
|
||||
})
|
||||
setlcDestinationOptions(options)
|
||||
}).catch(()=>{})
|
||||
}
|
||||
},[libracartAPI, inventoryControl, as, form.libracartDestinationKey])
|
||||
|
||||
useEffect(() => {
|
||||
loadBinYards();
|
||||
}, [loadBinYards]);
|
||||
loadLibraCartDestinations();
|
||||
}, [loadBinYards, loadLibraCartDestinations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (bin?.settings.specs?.bushelCapacity) {
|
||||
|
|
@ -713,6 +740,8 @@ export default function BinSettings(props: Props) {
|
|||
return "Auto (cable)"
|
||||
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_LIDAR:
|
||||
return "Hybrid (lidar)"
|
||||
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART:
|
||||
return "Auto (LibraCart)"
|
||||
default:
|
||||
return "Manual"
|
||||
}
|
||||
|
|
@ -860,7 +889,7 @@ export default function BinSettings(props: Props) {
|
|||
<FormControlLabel
|
||||
value={pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC}
|
||||
control={<Radio />}
|
||||
label={"Auto"}
|
||||
label={"Auto (Cable)"}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value={pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR}
|
||||
|
|
@ -872,6 +901,13 @@ export default function BinSettings(props: Props) {
|
|||
control={<Radio />}
|
||||
label={"Hybrid (Lidar)"}
|
||||
/>
|
||||
{user.hasFeature("libra-cart") &&
|
||||
<FormControlLabel
|
||||
value={pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART}
|
||||
control={<Radio />}
|
||||
label={"Auto (LibraCart)"}
|
||||
/>
|
||||
}
|
||||
</RadioGroup>
|
||||
</AccordionDetails>
|
||||
{inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC &&
|
||||
|
|
@ -912,6 +948,25 @@ export default function BinSettings(props: Props) {
|
|||
/>
|
||||
</Box>
|
||||
}
|
||||
{inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART &&
|
||||
<Box width="100%" padding={2}>
|
||||
<SearchSelect
|
||||
label="LibraCart Destination"
|
||||
selected={lcDestination}
|
||||
changeSelection={option => {
|
||||
let newForm = form;
|
||||
newForm.libracartDestinationKey = option?.value;
|
||||
setForm(newForm);
|
||||
setlcDestination(option ? option : null);
|
||||
}}
|
||||
disabled={!canEdit}
|
||||
options={lcDestinationOptions}
|
||||
/>
|
||||
<Typography variant="caption">
|
||||
The linked bins inventory will be adjusted When the LibraCart Destination data is synced every 6 hours
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
</Accordion>
|
||||
{empty ? (
|
||||
<Box marginTop={3} display="flex" flexDirection="column" alignItems="center">
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ const useStyles = makeStyles((_theme) => {
|
|||
position: "relative",
|
||||
minHeight: "233px",
|
||||
height: "auto !important",
|
||||
width: "184px",
|
||||
padding: 2
|
||||
},
|
||||
hidden: {
|
||||
|
|
@ -113,7 +112,7 @@ export default function BinsList(props: Props) {
|
|||
return (
|
||||
<Grid container direction="row">
|
||||
{bins.map((b, i) =>
|
||||
isMobile ? (
|
||||
(
|
||||
<Grid
|
||||
size={{
|
||||
xs: 6,
|
||||
|
|
@ -133,21 +132,6 @@ export default function BinsList(props: Props) {
|
|||
valDisplay={valDisplay}
|
||||
/>
|
||||
</Grid>
|
||||
) : (
|
||||
<Box
|
||||
key={i}
|
||||
style={{ width: "184px" }}
|
||||
className={classes.gridListTile}
|
||||
onClick={() => {
|
||||
!duplicate && goToBin(i)
|
||||
}}>
|
||||
<BinCardV2
|
||||
bin={b}
|
||||
duplicateBin={duplicateBin}
|
||||
dupHovered={setDuplicate}
|
||||
valDisplay={valDisplay}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
)}
|
||||
</Grid>
|
||||
|
|
|
|||
67
src/bin/graphs/BinLevelAreaGraph.tsx
Normal file
67
src/bin/graphs/BinLevelAreaGraph.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { useTheme } from "@mui/material";
|
||||
import MaterialChartTooltip from "charts/MaterialChartTooltip";
|
||||
import moment from "moment";
|
||||
import { Area, AreaChart, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from "recharts";
|
||||
|
||||
export interface LevelAreaData {
|
||||
timestamp: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: LevelAreaData[]
|
||||
fill: string
|
||||
customHeight?: string | number
|
||||
}
|
||||
|
||||
export default function BinLevelAreaGraph(props: Props) {
|
||||
const { data, customHeight, fill } = props
|
||||
const theme = useTheme();
|
||||
const now = moment();
|
||||
|
||||
|
||||
return (
|
||||
<ResponsiveContainer width={"100%"} height={customHeight ?? 350}>
|
||||
<AreaChart data={data}>
|
||||
<XAxis
|
||||
allowDataOverflow
|
||||
dataKey="timestamp"
|
||||
domain={["dataMin", "dataMax"]}
|
||||
name="Time"
|
||||
tickFormatter={timestamp => {
|
||||
let t = moment(timestamp);
|
||||
return now.isSame(t, "day") ? t.format("LT") : t.format("MMM DD");
|
||||
}}
|
||||
scale="time"
|
||||
type="number"
|
||||
tick={{ fill: theme.palette.text.primary }}
|
||||
stroke={theme.palette.divider}
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
<YAxis />
|
||||
<Area
|
||||
dataKey={"value"}
|
||||
fill={fill}
|
||||
stroke={fill}
|
||||
strokeWidth={3}
|
||||
type="monotone"
|
||||
/>
|
||||
<Tooltip
|
||||
animationEasing="ease-out"
|
||||
cursor={{ fill: theme.palette.text.primary, opacity: "0.15" }}
|
||||
labelFormatter={timestamp => moment(timestamp).format("lll")}
|
||||
content={(props: TooltipProps<any, any>) => {
|
||||
return (
|
||||
<MaterialChartTooltip
|
||||
{...props}
|
||||
valueFormatter={value => {
|
||||
return value + " Bushels";
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,21 +1,18 @@
|
|||
import {
|
||||
Box,
|
||||
Card,
|
||||
CircularProgress,
|
||||
FormControlLabel,
|
||||
Grid,
|
||||
Switch,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
import { blue, grey, red, yellow, orange } from "@mui/material/colors";
|
||||
import BarGraph, { BarData, RefArea } from "charts/BarGraph";
|
||||
import BarGraph, { BarData } from "charts/BarGraph";
|
||||
import { Bin } from "models";
|
||||
import moment, { Moment } from "moment";
|
||||
import { pond, quack } from "protobuf-ts/pond";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { useBinAPI, useGlobalState } from "providers";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Legend } from "recharts";
|
||||
import { getGrainUnit } from "utils";
|
||||
import BinLevelAreaGraph from "./BinLevelAreaGraph";
|
||||
|
||||
interface Props {
|
||||
binLoading: boolean;
|
||||
|
|
@ -27,12 +24,16 @@ interface Props {
|
|||
customHeight?: number | string;
|
||||
}
|
||||
|
||||
interface InventoryAt {
|
||||
timestamp: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export default function BinLevelOverTime(props: Props) {
|
||||
const { bin, fertilizerBin, colour, startDate, endDate, customHeight } = props;
|
||||
const binAPI = useBinAPI();
|
||||
const [{as}] = useGlobalState();
|
||||
const [data, setData] = useState<BarData[]>([]);
|
||||
const [modeAreas, setModeAreas] = useState<RefArea[]>([]);
|
||||
const [inventoryData, setInventoryData] = useState<InventoryAt[]>([]);
|
||||
const [dataLoading, setDataLoading] = useState(false);
|
||||
const [capacity, setCapacity] = useState<number | undefined>();
|
||||
|
||||
|
|
@ -76,33 +77,15 @@ export default function BinLevelOverTime(props: Props) {
|
|||
.listHistoryBetween(bin.key(), 500, startDate.toISOString(), endDate.toISOString())
|
||||
.then(resp => {
|
||||
let data: BarData[] = [];
|
||||
let modeAreas: RefArea[] = [];
|
||||
let lastBushels = -1;
|
||||
|
||||
//values for ref areas
|
||||
let x1 = 0;
|
||||
let x2 = 0;
|
||||
let y1 = -50;
|
||||
let y2 = -200;
|
||||
|
||||
let currentMode: pond.BinMode;
|
||||
|
||||
let currentMode: pond.BinMode | undefined = undefined
|
||||
let modeData: BarData[] = []
|
||||
resp.data.history.forEach(hist => {
|
||||
//build the data for the inventory bar graph
|
||||
if (hist.settings?.inventory) {
|
||||
let settings = pond.BinSettings.fromObject(hist.settings);
|
||||
let bushels = hist.settings.inventory.grainBushels ?? 0;
|
||||
if (bushels !== lastBushels || currentMode !== settings.mode) {
|
||||
x1 = moment(hist.timestamp).valueOf();
|
||||
x2 = moment(hist.timestamp).valueOf();
|
||||
modeAreas.push({
|
||||
x1: x1,
|
||||
x2: x2,
|
||||
y1: cap ? cap * -0.1 : y1,
|
||||
y2: cap ? cap * -0.2 : y2,
|
||||
fill: getFill(settings.mode)
|
||||
});
|
||||
currentMode = settings.mode;
|
||||
let newData: BarData = {
|
||||
if (bushels !== lastBushels) {
|
||||
let newData: InventoryAt = {
|
||||
timestamp: moment(hist.timestamp).valueOf(),
|
||||
value: fertilizerBin
|
||||
? Math.round(bushels * 35.239)
|
||||
|
|
@ -114,10 +97,21 @@ export default function BinLevelOverTime(props: Props) {
|
|||
lastBushels = bushels;
|
||||
}
|
||||
}
|
||||
//build the data for the mode change bar graph
|
||||
let histBin = Bin.create()
|
||||
histBin.settings = hist.settings ?? pond.BinSettings.create()
|
||||
if(hist.settings && currentMode !== hist.settings.mode){
|
||||
currentMode = hist.settings.mode
|
||||
modeData.push({
|
||||
timestamp: moment(hist.timestamp).valueOf(),
|
||||
value: 1,
|
||||
fill: getFill(currentMode)
|
||||
})
|
||||
}
|
||||
});
|
||||
let currentTime = moment().valueOf();
|
||||
if (data.length === 0) {
|
||||
let bushels = bin.settings.inventory?.grainBushels ?? 0;
|
||||
let currentTime = moment().valueOf();
|
||||
let bushels = bin.bushels();
|
||||
data.push({
|
||||
value: fertilizerBin
|
||||
? Math.round(bushels * 35.239)
|
||||
|
|
@ -126,17 +120,16 @@ export default function BinLevelOverTime(props: Props) {
|
|||
: bushels,
|
||||
timestamp: currentTime
|
||||
});
|
||||
|
||||
modeAreas.push({
|
||||
x1: currentTime,
|
||||
x2: currentTime,
|
||||
y1: cap ? cap * -0.1 : y1,
|
||||
y2: cap ? cap * -0.2 : y2,
|
||||
fill: getFill(bin.settings.mode)
|
||||
});
|
||||
}
|
||||
setData(data);
|
||||
setModeAreas(modeAreas);
|
||||
if(modeData.length === 0){
|
||||
modeData.push({
|
||||
timestamp: currentTime,
|
||||
value: 1,
|
||||
fill: getFill(bin.settings.mode)
|
||||
})
|
||||
}
|
||||
setInventoryData(data);
|
||||
setModeData(modeData)
|
||||
})
|
||||
.finally(() => {
|
||||
setDataLoading(false);
|
||||
|
|
@ -180,7 +173,7 @@ export default function BinLevelOverTime(props: Props) {
|
|||
});
|
||||
});
|
||||
if (autoBarData.length === 0) {
|
||||
let bushels = bin.settings.inventory?.grainBushels ?? 0;
|
||||
let bushels = bin.bushels();
|
||||
let currentTime = moment().valueOf();
|
||||
autoBarData.push({
|
||||
value: fertilizerBin
|
||||
|
|
@ -191,7 +184,7 @@ export default function BinLevelOverTime(props: Props) {
|
|||
timestamp: currentTime
|
||||
});
|
||||
}
|
||||
setData(autoBarData);
|
||||
setInventoryData(autoBarData);
|
||||
})
|
||||
.catch(err => {})
|
||||
.finally(() => {
|
||||
|
|
@ -220,6 +213,13 @@ export default function BinLevelOverTime(props: Props) {
|
|||
})
|
||||
}
|
||||
})
|
||||
if(modeData.length === 0){
|
||||
modeData.push({
|
||||
timestamp: moment().valueOf(),
|
||||
value: 1,
|
||||
fill: getFill(bin.settings.mode)
|
||||
})
|
||||
}
|
||||
setModeData(modeData)
|
||||
})
|
||||
.catch(err => {
|
||||
|
|
@ -238,7 +238,8 @@ export default function BinLevelOverTime(props: Props) {
|
|||
let control = bin.inventoryControl()
|
||||
//for automatic lidar and cables get the data from measurements as an object measurement
|
||||
if(control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR ||
|
||||
control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC
|
||||
control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC ||
|
||||
control === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART
|
||||
){
|
||||
//load the measurement data for the bar graph
|
||||
loadMeasurementData()
|
||||
|
|
@ -270,23 +271,30 @@ export default function BinLevelOverTime(props: Props) {
|
|||
};
|
||||
|
||||
const inventoryChart = () => {
|
||||
return (
|
||||
<BarGraph
|
||||
if(inventoryData.length > 10){
|
||||
return (
|
||||
<BinLevelAreaGraph
|
||||
data={inventoryData}
|
||||
fill={colour}
|
||||
/>
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<BarGraph
|
||||
customHeight={customHeight}
|
||||
data={data}
|
||||
refAreas={modeAreas}
|
||||
data={inventoryData}
|
||||
barColour={colour}
|
||||
yMax={capacity}
|
||||
yMin={0}
|
||||
graphLegend={modeAreas.length > 0 ? legend() : undefined}
|
||||
labelColour="white"
|
||||
labels
|
||||
useGradient
|
||||
/>
|
||||
);
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const autoBinModeChart = () => {
|
||||
const binModeChart = () => {
|
||||
return (
|
||||
<BarGraph
|
||||
data={modeData}
|
||||
|
|
@ -305,9 +313,7 @@ export default function BinLevelOverTime(props: Props) {
|
|||
Grain Levels Over Time
|
||||
</Typography>
|
||||
{dataLoading ? <CircularProgress /> : inventoryChart()}
|
||||
{/* when using auto will need to make a new chart just for the bin modes since the auto inventory comes from another place */}
|
||||
{(bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR ||
|
||||
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC) && autoBinModeChart()}
|
||||
{binModeChart()}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue