Merge branch 'staging_environment' into dev_environment
This commit is contained in:
commit
233150006c
15 changed files with 348 additions and 252 deletions
|
|
@ -24,7 +24,7 @@ import RemoveSelfFromObject from "user/RemoveSelfFromObject";
|
||||||
import ShareObject from "user/ShareObject";
|
import ShareObject from "user/ShareObject";
|
||||||
import { isOffline } from "utils/environment";
|
import { isOffline } from "utils/environment";
|
||||||
import ObjectTeams from "teams/ObjectTeams";
|
import ObjectTeams from "teams/ObjectTeams";
|
||||||
// import BinDuplication from "./BinDuplication";
|
import BinDuplication from "./BinDuplication";
|
||||||
import BinsIcon from "products/Bindapt/BinsIcon";
|
import BinsIcon from "products/Bindapt/BinsIcon";
|
||||||
import HelpIcon from "@mui/icons-material/Help";
|
import HelpIcon from "@mui/icons-material/Help";
|
||||||
import BinSensors from "./BinSensors";
|
import BinSensors from "./BinSensors";
|
||||||
|
|
@ -269,14 +269,14 @@ export default function BinActions(props: Props) {
|
||||||
closeDialogCallback={() => setOpenState({ ...openState, users: false })}
|
closeDialogCallback={() => setOpenState({ ...openState, users: false })}
|
||||||
refreshCallback={refreshCallback}
|
refreshCallback={refreshCallback}
|
||||||
/>
|
/>
|
||||||
{/* <BinDuplication
|
<BinDuplication
|
||||||
open={openState.duplication}
|
open={openState.duplication}
|
||||||
closeDialog={() => {
|
closeDialog={() => {
|
||||||
setOpenState({ ...openState, duplication: false });
|
setOpenState({ ...openState, duplication: false });
|
||||||
}}
|
}}
|
||||||
bin={bin}
|
bin={bin}
|
||||||
refreshCallback={refreshCallback}
|
refreshCallback={refreshCallback}
|
||||||
/> */}
|
/>
|
||||||
<RemoveSelfFromObject
|
<RemoveSelfFromObject
|
||||||
scope={binScope(key)}
|
scope={binScope(key)}
|
||||||
label={label}
|
label={label}
|
||||||
|
|
|
||||||
|
|
@ -506,9 +506,6 @@ export default function BinCard(props: Props) {
|
||||||
+
|
+
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
{/* <Box position="absolute" top={4} right={4}>
|
|
||||||
<BinModeDot mode={bin.settings.mode} />
|
|
||||||
</Box> */}
|
|
||||||
<Box padding={1}>
|
<Box padding={1}>
|
||||||
<Typography
|
<Typography
|
||||||
align="left"
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -374,7 +374,6 @@ export default function BinSettings(props: Props) {
|
||||||
let options: Option[] = []
|
let options: Option[] = []
|
||||||
libracartAPI.listDestinations(0,0,undefined, as)
|
libracartAPI.listDestinations(0,0,undefined, as)
|
||||||
.then(resp=>{
|
.then(resp=>{
|
||||||
console.log(resp)
|
|
||||||
//set the options for the search select
|
//set the options for the search select
|
||||||
resp.data.destinations.forEach(d => {
|
resp.data.destinations.forEach(d => {
|
||||||
let newOp: Option = {
|
let newOp: Option = {
|
||||||
|
|
@ -486,7 +485,6 @@ export default function BinSettings(props: Props) {
|
||||||
form.inventory.inventoryControl = inventoryControl
|
form.inventory.inventoryControl = inventoryControl
|
||||||
form.inventory.autoThreshold = autoFillThreshold
|
form.inventory.autoThreshold = autoFillThreshold
|
||||||
}
|
}
|
||||||
console.log(form)
|
|
||||||
binAPI
|
binAPI
|
||||||
.updateBin(bin.key(), form, as)
|
.updateBin(bin.key(), form, as)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ const useStyles = makeStyles((_theme) => {
|
||||||
position: "relative",
|
position: "relative",
|
||||||
minHeight: "233px",
|
minHeight: "233px",
|
||||||
height: "auto !important",
|
height: "auto !important",
|
||||||
width: "184px",
|
|
||||||
padding: 2
|
padding: 2
|
||||||
},
|
},
|
||||||
hidden: {
|
hidden: {
|
||||||
|
|
@ -113,7 +112,7 @@ export default function BinsList(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Grid container direction="row">
|
<Grid container direction="row">
|
||||||
{bins.map((b, i) =>
|
{bins.map((b, i) =>
|
||||||
isMobile ? (
|
(
|
||||||
<Grid
|
<Grid
|
||||||
size={{
|
size={{
|
||||||
xs: 6,
|
xs: 6,
|
||||||
|
|
@ -133,21 +132,6 @@ export default function BinsList(props: Props) {
|
||||||
valDisplay={valDisplay}
|
valDisplay={valDisplay}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</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>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ export default function BinComponentGraph(props: Props) {
|
||||||
<UnitMeasurementSummary
|
<UnitMeasurementSummary
|
||||||
component={component}
|
component={component}
|
||||||
reading={UnitMeasurement.convertLastMeasurement(
|
reading={UnitMeasurement.convertLastMeasurement(
|
||||||
component.status.lastGoodMeasurement.map(m => UnitMeasurement.create(m, user))
|
lastMeasurement.map(m => UnitMeasurement.create(m, user))
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 {
|
import {
|
||||||
Box,
|
|
||||||
Card,
|
Card,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
FormControlLabel,
|
|
||||||
Grid,
|
|
||||||
Switch,
|
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { blue, grey, red, yellow, orange } from "@mui/material/colors";
|
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 { Bin } from "models";
|
||||||
import moment, { Moment } from "moment";
|
import moment, { Moment } from "moment";
|
||||||
import { pond, quack } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { useBinAPI, useGlobalState } from "providers";
|
import { useBinAPI, useGlobalState } from "providers";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { Legend } from "recharts";
|
import { Legend } from "recharts";
|
||||||
import { getGrainUnit } from "utils";
|
import { getGrainUnit } from "utils";
|
||||||
|
import BinLevelAreaGraph from "./BinLevelAreaGraph";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
binLoading: boolean;
|
binLoading: boolean;
|
||||||
|
|
@ -27,12 +24,16 @@ interface Props {
|
||||||
customHeight?: number | string;
|
customHeight?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface InventoryAt {
|
||||||
|
timestamp: number;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
export default function BinLevelOverTime(props: Props) {
|
export default function BinLevelOverTime(props: Props) {
|
||||||
const { bin, fertilizerBin, colour, startDate, endDate, customHeight } = props;
|
const { bin, fertilizerBin, colour, startDate, endDate, customHeight } = props;
|
||||||
const binAPI = useBinAPI();
|
const binAPI = useBinAPI();
|
||||||
const [{as}] = useGlobalState();
|
const [{as}] = useGlobalState();
|
||||||
const [data, setData] = useState<BarData[]>([]);
|
const [inventoryData, setInventoryData] = useState<InventoryAt[]>([]);
|
||||||
const [modeAreas, setModeAreas] = useState<RefArea[]>([]);
|
|
||||||
const [dataLoading, setDataLoading] = useState(false);
|
const [dataLoading, setDataLoading] = useState(false);
|
||||||
const [capacity, setCapacity] = useState<number | undefined>();
|
const [capacity, setCapacity] = useState<number | undefined>();
|
||||||
|
|
||||||
|
|
@ -76,33 +77,15 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
.listHistoryBetween(bin.key(), 500, startDate.toISOString(), endDate.toISOString())
|
.listHistoryBetween(bin.key(), 500, startDate.toISOString(), endDate.toISOString())
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
let data: BarData[] = [];
|
let data: BarData[] = [];
|
||||||
let modeAreas: RefArea[] = [];
|
|
||||||
let lastBushels = -1;
|
let lastBushels = -1;
|
||||||
|
let currentMode: pond.BinMode | undefined = undefined
|
||||||
//values for ref areas
|
let modeData: BarData[] = []
|
||||||
let x1 = 0;
|
|
||||||
let x2 = 0;
|
|
||||||
let y1 = -50;
|
|
||||||
let y2 = -200;
|
|
||||||
|
|
||||||
let currentMode: pond.BinMode;
|
|
||||||
|
|
||||||
resp.data.history.forEach(hist => {
|
resp.data.history.forEach(hist => {
|
||||||
|
//build the data for the inventory bar graph
|
||||||
if (hist.settings?.inventory) {
|
if (hist.settings?.inventory) {
|
||||||
let settings = pond.BinSettings.fromObject(hist.settings);
|
|
||||||
let bushels = hist.settings.inventory.grainBushels ?? 0;
|
let bushels = hist.settings.inventory.grainBushels ?? 0;
|
||||||
if (bushels !== lastBushels || currentMode !== settings.mode) {
|
if (bushels !== lastBushels) {
|
||||||
x1 = moment(hist.timestamp).valueOf();
|
let newData: InventoryAt = {
|
||||||
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 = {
|
|
||||||
timestamp: moment(hist.timestamp).valueOf(),
|
timestamp: moment(hist.timestamp).valueOf(),
|
||||||
value: fertilizerBin
|
value: fertilizerBin
|
||||||
? Math.round(bushels * 35.239)
|
? Math.round(bushels * 35.239)
|
||||||
|
|
@ -114,10 +97,21 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
lastBushels = bushels;
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (data.length === 0) {
|
|
||||||
let bushels = bin.settings.inventory?.grainBushels ?? 0;
|
|
||||||
let currentTime = moment().valueOf();
|
let currentTime = moment().valueOf();
|
||||||
|
if (data.length === 0) {
|
||||||
|
let bushels = bin.bushels();
|
||||||
data.push({
|
data.push({
|
||||||
value: fertilizerBin
|
value: fertilizerBin
|
||||||
? Math.round(bushels * 35.239)
|
? Math.round(bushels * 35.239)
|
||||||
|
|
@ -126,17 +120,16 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
: bushels,
|
: bushels,
|
||||||
timestamp: currentTime
|
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);
|
if(modeData.length === 0){
|
||||||
setModeAreas(modeAreas);
|
modeData.push({
|
||||||
|
timestamp: currentTime,
|
||||||
|
value: 1,
|
||||||
|
fill: getFill(bin.settings.mode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setInventoryData(data);
|
||||||
|
setModeData(modeData)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setDataLoading(false);
|
setDataLoading(false);
|
||||||
|
|
@ -180,7 +173,7 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (autoBarData.length === 0) {
|
if (autoBarData.length === 0) {
|
||||||
let bushels = bin.settings.inventory?.grainBushels ?? 0;
|
let bushels = bin.bushels();
|
||||||
let currentTime = moment().valueOf();
|
let currentTime = moment().valueOf();
|
||||||
autoBarData.push({
|
autoBarData.push({
|
||||||
value: fertilizerBin
|
value: fertilizerBin
|
||||||
|
|
@ -191,7 +184,7 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
timestamp: currentTime
|
timestamp: currentTime
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setData(autoBarData);
|
setInventoryData(autoBarData);
|
||||||
})
|
})
|
||||||
.catch(err => {})
|
.catch(err => {})
|
||||||
.finally(() => {
|
.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)
|
setModeData(modeData)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
@ -271,23 +271,30 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const inventoryChart = () => {
|
const inventoryChart = () => {
|
||||||
|
if(inventoryData.length > 10){
|
||||||
|
return (
|
||||||
|
<BinLevelAreaGraph
|
||||||
|
data={inventoryData}
|
||||||
|
fill={colour}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}else{
|
||||||
return (
|
return (
|
||||||
<BarGraph
|
<BarGraph
|
||||||
customHeight={customHeight}
|
customHeight={customHeight}
|
||||||
data={data}
|
data={inventoryData}
|
||||||
refAreas={modeAreas}
|
|
||||||
barColour={colour}
|
barColour={colour}
|
||||||
yMax={capacity}
|
yMax={capacity}
|
||||||
yMin={0}
|
yMin={0}
|
||||||
graphLegend={modeAreas.length > 0 ? legend() : undefined}
|
|
||||||
labelColour="white"
|
labelColour="white"
|
||||||
labels
|
labels
|
||||||
useGradient
|
useGradient
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const autoBinModeChart = () => {
|
const binModeChart = () => {
|
||||||
return (
|
return (
|
||||||
<BarGraph
|
<BarGraph
|
||||||
data={modeData}
|
data={modeData}
|
||||||
|
|
@ -306,10 +313,7 @@ export default function BinLevelOverTime(props: Props) {
|
||||||
Grain Levels Over Time
|
Grain Levels Over Time
|
||||||
</Typography>
|
</Typography>
|
||||||
{dataLoading ? <CircularProgress /> : inventoryChart()}
|
{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 */}
|
{binModeChart()}
|
||||||
{(bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR ||
|
|
||||||
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC ||
|
|
||||||
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_LIBRACART) && autoBinModeChart()}
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
},
|
},
|
||||||
stickyHeader: {
|
stickyHeader: {
|
||||||
position: "sticky",
|
position: "sticky",
|
||||||
// backgroundColor: getThemeType() === "light" ? "rgb(245, 245, 245)" : "rgb(40, 40, 40)",
|
backgroundColor: getThemeType() === "light" ? "rgb(245, 245, 245)" : "rgb(40, 40, 40)",
|
||||||
top: 0,
|
top: 0,
|
||||||
zIndex: 300 //giving a really high z-index to make sure nothing is in front of it
|
zIndex: 300 //giving a really high z-index to make sure nothing is in front of it
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,13 @@ export default function PeriodSelect(props: Props) {
|
||||||
}
|
}
|
||||||
}, [unit, prevUnit, onChange, value]);
|
}, [unit, prevUnit, onChange, value]);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
let initialUnit: TimeUnit = bestUnit(initialMs);
|
||||||
|
let initialValue = milliToX(initialMs, initialUnit).toString();
|
||||||
|
setValue(initialValue)
|
||||||
|
setUnit(initialUnit)
|
||||||
|
},[initialMs])
|
||||||
|
|
||||||
const changeValue = (event: any) => {
|
const changeValue = (event: any) => {
|
||||||
let value = event.target.value;
|
let value = event.target.value;
|
||||||
setValue(value);
|
setValue(value);
|
||||||
|
|
|
||||||
|
|
@ -699,6 +699,86 @@ export default function ComponentForm(props: Props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const componentMode = () => {
|
||||||
|
const { component, coefficient, offset } = form;
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid size={{ xs: 4, sm: 3 }}>
|
||||||
|
<Tooltip
|
||||||
|
title="Switch to be able to customize the mode of the component"
|
||||||
|
placement="bottom">
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={component.settings.calibrate}
|
||||||
|
onChange={toggleCalibrate}
|
||||||
|
name="mode"
|
||||||
|
aria-label="mode"
|
||||||
|
color="secondary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Mode"
|
||||||
|
labelPlacement="top"
|
||||||
|
className={classes.switchControl}
|
||||||
|
disabled={!canEdit}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 10, sm: 4 }}>
|
||||||
|
{compMode.mode.entryA && (
|
||||||
|
<TextField
|
||||||
|
id={compMode.mode.labelA}
|
||||||
|
label={compMode.mode.labelA}
|
||||||
|
disabled={!component.settings.calibrate}
|
||||||
|
error={!isCoefficientValid()}
|
||||||
|
value={coefficient}
|
||||||
|
onChange={changeCoefficient}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
select={compMode.mode.entryA === "select"}
|
||||||
|
type={compMode.mode.entryA}
|
||||||
|
variant="outlined">
|
||||||
|
{compMode.mode.entryA === "select" &&
|
||||||
|
compMode.mode.dictionaryA.map((elem: any) => {
|
||||||
|
if (!elem.restricted || (elem.restricted && user.hasAdmin())) {
|
||||||
|
return (
|
||||||
|
<MenuItem key={elem.key} value={elem.value}>
|
||||||
|
{elem.key}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined; //TODO-CS: Time permitting re-think the structure of this function so that I don't have to return undefined
|
||||||
|
})}
|
||||||
|
</TextField>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 10, sm: 5 }}>
|
||||||
|
{compMode.mode.entryB && (
|
||||||
|
<TextField
|
||||||
|
id={compMode.mode.labelB}
|
||||||
|
label={compMode.mode.labelB}
|
||||||
|
disabled={!component.settings.calibrate}
|
||||||
|
error={!isOffsetValid(compMode.mode.minB, compMode.mode.maxB)}
|
||||||
|
value={offset}
|
||||||
|
onChange={changeOffset}
|
||||||
|
onFocus={selectText}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
type={compMode.mode.entryB}
|
||||||
|
variant="outlined">
|
||||||
|
{compMode.mode.entryB === "select" &&
|
||||||
|
compMode.mode.dictionaryB.map((elem: any) => (
|
||||||
|
<MenuItem key={elem.key} value={elem.value}>
|
||||||
|
{elem.key}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const describeSource = (measurementType: quack.MeasurementType): MeasurementDescriber => {
|
const describeSource = (measurementType: quack.MeasurementType): MeasurementDescriber => {
|
||||||
const { component } = form;
|
const { component } = form;
|
||||||
return describeMeasurement(measurementType, component.type(), component.subType());
|
return describeMeasurement(measurementType, component.type(), component.subType());
|
||||||
|
|
@ -1046,87 +1126,17 @@ export default function ComponentForm(props: Props) {
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
}
|
}
|
||||||
{!compMode ? (
|
{device.featureSupported("individualCalibrations") ? (
|
||||||
device.featureSupported("individualCalibrations") ? (
|
|
||||||
individualCalibration()
|
|
||||||
) : (
|
|
||||||
blanketCalibration()
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Grid size={{ xs: 4, sm: 3 }}>
|
{compMode && componentMode()}
|
||||||
<Tooltip
|
{individualCalibration()}
|
||||||
title="Switch to be able to customize the mode of the component"
|
|
||||||
placement="bottom">
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
checked={component.settings.calibrate}
|
|
||||||
onChange={toggleCalibrate}
|
|
||||||
name="mode"
|
|
||||||
aria-label="mode"
|
|
||||||
color="secondary"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Mode"
|
|
||||||
labelPlacement="top"
|
|
||||||
className={classes.switchControl}
|
|
||||||
disabled={!canEdit}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Grid>
|
|
||||||
<Grid size={{ xs: 10, sm: 4 }}>
|
|
||||||
{compMode.mode.entryA && (
|
|
||||||
<TextField
|
|
||||||
id={compMode.mode.labelA}
|
|
||||||
label={compMode.mode.labelA}
|
|
||||||
disabled={!component.settings.calibrate}
|
|
||||||
error={!isCoefficientValid()}
|
|
||||||
value={coefficient}
|
|
||||||
onChange={changeCoefficient}
|
|
||||||
margin="normal"
|
|
||||||
fullWidth
|
|
||||||
select={compMode.mode.entryA === "select"}
|
|
||||||
type={compMode.mode.entryA}
|
|
||||||
variant="outlined">
|
|
||||||
{compMode.mode.entryA === "select" &&
|
|
||||||
compMode.mode.dictionaryA.map((elem: any) => {
|
|
||||||
if (!elem.restricted || (elem.restricted && user.hasAdmin())) {
|
|
||||||
return (
|
|
||||||
<MenuItem key={elem.key} value={elem.value}>
|
|
||||||
{elem.key}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return undefined; //TODO-CS: Time permitting re-think the structure of this function so that I don't have to return undefined
|
|
||||||
})}
|
|
||||||
</TextField>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
<Grid size={{ xs: 10, sm: 5 }}>
|
|
||||||
{compMode.mode.entryB && (
|
|
||||||
<TextField
|
|
||||||
id={compMode.mode.labelB}
|
|
||||||
label={compMode.mode.labelB}
|
|
||||||
disabled={!component.settings.calibrate}
|
|
||||||
error={!isOffsetValid(compMode.mode.minB, compMode.mode.maxB)}
|
|
||||||
value={offset}
|
|
||||||
onChange={changeOffset}
|
|
||||||
onFocus={selectText}
|
|
||||||
margin="normal"
|
|
||||||
fullWidth
|
|
||||||
type={compMode.mode.entryB}
|
|
||||||
variant="outlined">
|
|
||||||
{compMode.mode.entryB === "select" &&
|
|
||||||
compMode.mode.dictionaryB.map((elem: any) => (
|
|
||||||
<MenuItem key={elem.key} value={elem.value}>
|
|
||||||
{elem.key}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</TextField>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
!compMode ? (
|
||||||
|
blanketCalibration()
|
||||||
|
):(
|
||||||
|
componentMode()
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
<TextField
|
<TextField
|
||||||
id="dataAveraging"
|
id="dataAveraging"
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
import { teamScope, User } from "models";
|
import { teamScope, User } from "models";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { useSnackbar, useUserAPI } from "providers";
|
import { useGlobalState, useSnackbar, useUserAPI } from "providers";
|
||||||
import { useLibraCartProxyAPI } from "providers/pond/libracartProxyAPI";
|
import { useLibraCartProxyAPI } from "providers/pond/libracartProxyAPI";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import TeamSearch from "teams/TeamSearch";
|
import TeamSearch from "teams/TeamSearch";
|
||||||
|
|
@ -33,9 +33,7 @@ export default function LibraCartAccess() {
|
||||||
const libracartAPI = useLibraCartProxyAPI();
|
const libracartAPI = useLibraCartProxyAPI();
|
||||||
//const [dataOps, setDataOps] = useState<pond.DataOption[]>([]);
|
//const [dataOps, setDataOps] = useState<pond.DataOption[]>([]);
|
||||||
const { openSnack } = useSnackbar();
|
const { openSnack } = useSnackbar();
|
||||||
//const [{ as }] = useGlobalState();
|
const [{ as }] = useGlobalState();
|
||||||
// const integration_url = process.env.REACT_APP_LIBRACART_INTEGRATION_URL;
|
|
||||||
const integration_url = import.meta.env.VITE_LIBRACART_INTEGRATION_URL;
|
|
||||||
|
|
||||||
|
|
||||||
const submitNewOrganization = () => {
|
const submitNewOrganization = () => {
|
||||||
|
|
@ -176,6 +174,7 @@ export default function LibraCartAccess() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box style={{ padding: 10 }}>
|
<Box style={{ padding: 10 }}>
|
||||||
|
<Grid container direction="row" alignContent="center" alignItems="center" wrap="nowrap" width="100%" justifyContent="space-between">
|
||||||
<Grid container direction="row" alignContent="center" alignItems="center" wrap="nowrap">
|
<Grid container direction="row" alignContent="center" alignItems="center" wrap="nowrap">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -183,21 +182,37 @@ export default function LibraCartAccess() {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
window.open(`${integration_url}`, "_blank");
|
window.open(`https://staging.cloud.agrimatics.com/grain/integrations`, "_blank");
|
||||||
}}>
|
}}>
|
||||||
Link Libra Cart Account
|
Link Libra Cart Account
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title='The integration can be found by selecting my account in the corner, selecting the "Manage Account" option and then selecting integrations in the left side menu'
|
title='The integration can be found by selecting my account in the corner,
|
||||||
>
|
selecting the "Manage Account" option and then selecting integrations in the left side menu'>
|
||||||
<Help />
|
<Help />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Tooltip title={"This will Sync the data for all LibraCart accounts linked to " + (as ? "this team" : "your account")}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={e => {
|
||||||
|
libracartAPI.syncData().then(resp => {}).catch(err => {})
|
||||||
|
}}>
|
||||||
|
Sync
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Box paddingTop={2}>
|
||||||
|
<Typography>
|
||||||
|
LibraCart data will sync every 6 hours, if the data is needed immediately you can select the Sync button. It may take up to 15 minutes for the data to appear in our platform.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{newOrgDialog()}
|
{newOrgDialog()}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ export class Component {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addressDescription = (deviceProduct?: pond.DeviceProduct) => {
|
public addressDescription(deviceProduct?: pond.DeviceProduct): string {
|
||||||
if (
|
if (
|
||||||
this.settings.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY ||
|
this.settings.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY ||
|
||||||
(this.settings.addressType >= quack.AddressType.ADDRESS_TYPE_PIN_OFFSET1 &&
|
(this.settings.addressType >= quack.AddressType.ADDRESS_TYPE_PIN_OFFSET1 &&
|
||||||
|
|
|
||||||
|
|
@ -1214,37 +1214,6 @@ export default function Bins(props: Props) {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
{/* <StyledToggleButtonGroup
|
|
||||||
id="cardValueDisplay"
|
|
||||||
value={cardValDisplay}
|
|
||||||
exclusive
|
|
||||||
size="small"
|
|
||||||
aria-label="card detail">
|
|
||||||
<StyledToggle
|
|
||||||
value={"low"}
|
|
||||||
aria-label="low"
|
|
||||||
onClick={() => {
|
|
||||||
setCardValDisplay("low");
|
|
||||||
}}>
|
|
||||||
Low
|
|
||||||
</StyledToggle>
|
|
||||||
<StyledToggle
|
|
||||||
value={"average"}
|
|
||||||
aria-label="average"
|
|
||||||
onClick={() => {
|
|
||||||
setCardValDisplay("average");
|
|
||||||
}}>
|
|
||||||
Average
|
|
||||||
</StyledToggle>
|
|
||||||
<StyledToggle
|
|
||||||
value={"high"}
|
|
||||||
aria-label="high"
|
|
||||||
onClick={() => {
|
|
||||||
setCardValDisplay("high");
|
|
||||||
}}>
|
|
||||||
High
|
|
||||||
</StyledToggle>
|
|
||||||
</StyledToggleButtonGroup> */}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid >
|
<Grid >
|
||||||
|
|
@ -1270,41 +1239,6 @@ export default function Bins(props: Props) {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
{/* <StyledToggleButtonGroup
|
|
||||||
id="tour-graph-tabs"
|
|
||||||
value={binView}
|
|
||||||
exclusive
|
|
||||||
size="small"
|
|
||||||
aria-label="detail">
|
|
||||||
<StyledToggle
|
|
||||||
value={"grid"}
|
|
||||||
aria-label="grid view"
|
|
||||||
onClick={() => {
|
|
||||||
setBinView("grid");
|
|
||||||
sessionStorage.setItem("binsView", "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={() => {
|
|
||||||
setBinView("scroll");
|
|
||||||
sessionStorage.setItem("binsView", "scroll");
|
|
||||||
}}>
|
|
||||||
<ViewColumn />
|
|
||||||
</StyledToggle> */}
|
|
||||||
{/* <StyledToggle
|
|
||||||
value={"list"}
|
|
||||||
aria-label="list view"
|
|
||||||
onClick={() => {
|
|
||||||
setBinView("list");
|
|
||||||
sessionStorage.setItem("binsView", "list");
|
|
||||||
}}>
|
|
||||||
<ViewList />
|
|
||||||
</StyledToggle>
|
|
||||||
</StyledToggleButtonGroup> */}
|
|
||||||
<MoreVert
|
<MoreVert
|
||||||
className={classes.icon}
|
className={classes.icon}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { pond } from "protobuf-ts/pond";
|
||||||
import React, { createContext, PropsWithChildren, useContext } from "react";
|
import React, { createContext, PropsWithChildren, useContext } from "react";
|
||||||
//import { or } from "utils";
|
//import { or } from "utils";
|
||||||
import { pondURL } from "./pond";
|
import { pondURL } from "./pond";
|
||||||
|
import { useGlobalState } from "providers/StateContainer";
|
||||||
|
|
||||||
export interface ILibraCartProxyAPIContext {
|
export interface ILibraCartProxyAPIContext {
|
||||||
//add new organization
|
//add new organization
|
||||||
|
|
@ -33,6 +34,7 @@ export interface ILibraCartProxyAPIContext {
|
||||||
libracartKey?: string,
|
libracartKey?: string,
|
||||||
as?: string
|
as?: string
|
||||||
) => Promise<AxiosResponse<pond.ListLibraCartDestinationsResponse>>;
|
) => Promise<AxiosResponse<pond.ListLibraCartDestinationsResponse>>;
|
||||||
|
syncData: () => Promise<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LibraCartProxyAPIContext = createContext<ILibraCartProxyAPIContext>(
|
export const LibraCartProxyAPIContext = createContext<ILibraCartProxyAPIContext>(
|
||||||
|
|
@ -43,6 +45,7 @@ interface Props {}
|
||||||
|
|
||||||
export default function LibraCartProvider(props: PropsWithChildren<Props>) {
|
export default function LibraCartProvider(props: PropsWithChildren<Props>) {
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
const [{as}] = useGlobalState();
|
||||||
const { post, get, put } = useHTTP();
|
const { post, get, put } = useHTTP();
|
||||||
|
|
||||||
const addAccount = (
|
const addAccount = (
|
||||||
|
|
@ -104,13 +107,18 @@ export default function LibraCartProvider(props: PropsWithChildren<Props>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncData = () => {
|
||||||
|
return get(pondURL("/libracartImport" + (as ? "?as=" + as : "")))
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LibraCartProxyAPIContext.Provider
|
<LibraCartProxyAPIContext.Provider
|
||||||
value={{
|
value={{
|
||||||
addAccount,
|
addAccount,
|
||||||
listAccounts,
|
listAccounts,
|
||||||
updateAccount,
|
updateAccount,
|
||||||
listDestinations
|
listDestinations,
|
||||||
|
syncData
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</LibraCartProxyAPIContext.Provider>
|
</LibraCartProxyAPIContext.Provider>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue