Merge branch 'staging_environment' into dev_environment

This commit is contained in:
csawatzky 2025-08-20 16:14:23 -06:00
commit 233150006c
15 changed files with 348 additions and 252 deletions

View file

@ -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}

View file

@ -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"

View 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>
);
}

View file

@ -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 => {

View file

@ -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>

View file

@ -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))
)} )}
/> />
} }

View 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>
)
}

View file

@ -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>
); );
} }

View file

@ -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
} }

View file

@ -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);

View file

@ -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"

View file

@ -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>

View file

@ -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 &&

View file

@ -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 => {

View file

@ -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>