Merge branch 'staging_environment' into field_dashboard

This commit is contained in:
csawatzky 2025-10-14 13:42:53 -06:00
commit 7a8a010a39
28 changed files with 212 additions and 128 deletions

2
package-lock.json generated
View file

@ -42,7 +42,7 @@
"mui-tel-input": "^7.0.0", "mui-tel-input": "^7.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10", "openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#field_dashboard", "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master",
"query-string": "^9.2.1", "query-string": "^9.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",

View file

@ -54,7 +54,7 @@
"mui-tel-input": "^7.0.0", "mui-tel-input": "^7.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10", "openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#field_dashboard", "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master",
"query-string": "^9.2.1", "query-string": "^9.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -249,9 +249,9 @@ export default function BinCard(props: Props) {
noWrap noWrap
style={{ fontSize: "0.85rem", fontWeight: 650, color: tempColour }}> style={{ fontSize: "0.85rem", fontWeight: 650, color: tempColour }}>
{display + {display +
(user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
? C" ? F"
: F")} : C")}
</Typography> </Typography>
); );
}; };

View file

@ -33,7 +33,7 @@ const useStyles = makeStyles((theme: Theme) => {
interface Props { interface Props {
interactions: Interaction[]; interactions: Interaction[];
interactionDevices: Map<string, number>; interactionDevices: Map<string, number>;
mode: pond.BinMode.BIN_MODE_DRYING | pond.BinMode.BIN_MODE_HYDRATING; mode: pond.BinMode;
plenums?: Plenum[]; plenums?: Plenum[];
ambients?: Ambient[]; ambients?: Ambient[];
heaters?: Controller[]; heaters?: Controller[];
@ -94,10 +94,21 @@ export default function BinConditioningCard(props: Props) {
return conditioningInteractions; return conditioningInteractions;
}; };
const modeDisplay = () => {
switch(mode){
case pond.BinMode.BIN_MODE_DRYING:
return "Drying"
case pond.BinMode.BIN_MODE_HYDRATING:
return "Hydrating"
case pond.BinMode.BIN_MODE_COOLDOWN:
return "Cooldown"
}
}
return ( return (
<Card raised className={classes.card}> <Card raised className={classes.card}>
<Typography style={{ fontWeight: 650 }}> <Typography style={{ fontWeight: 650 }}>
{mode === pond.BinMode.BIN_MODE_DRYING ? "Drying" : "Hydrating"} Conditions {modeDisplay()} Conditions
</Typography> </Typography>
{conditionsDisplay()} {conditionsDisplay()}
</Card> </Card>

View file

@ -47,8 +47,6 @@ const useStyles = makeStyles((theme: Theme) => {
sliderThumb: { sliderThumb: {
height: 15, height: 15,
width: 15, width: 15,
marginTop: -6,
marginLeft: -7.5,
backgroundColor: "yellow" backgroundColor: "yellow"
}, },
sliderTrack: { sliderTrack: {
@ -60,10 +58,10 @@ const useStyles = makeStyles((theme: Theme) => {
backgroundColor: "white" backgroundColor: "white"
}, },
sliderValLabel: { sliderValLabel: {
left: "calc(-50%)", left: -20,
top: 22, top: 40,
background: "transparent",
"& *": { "& *": {
background: "transparent",
color: "#fff" color: "#fff"
} }
}, },

View file

@ -507,9 +507,9 @@ export default function BinSVGV2(props: Props) {
//if the cables have the same number of nodes //if the cables have the same number of nodes
if (b.temperatures.length === a.temperatures.length) { if (b.temperatures.length === a.temperatures.length) {
//sort by temp only last and any type that has humidity first //sort by temp only last and any type that has humidity first
if ((avg(a.humidities) === 0 || a.humidities.length === 0) && (avg(b.humidities) !== 0 || b.humidities.length > 0)) { if ((avg(a.humidities) === 0) && (avg(b.humidities) !== 0)) {
return 1; return 1;
} else if ((avg(b.humidities) === 0 || b.humidities.length === 0) && (avg(a.humidities) !== 0 || a.humidities.length > 0)) { } else if ((avg(b.humidities) === 0) && (avg(a.humidities) !== 0)) {
return -1; return -1;
} }
else { else {

View file

@ -485,6 +485,7 @@ 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

@ -21,7 +21,7 @@ import { Bin, Component } from "models";
import { GrainCable } from "models/GrainCable"; import { GrainCable } from "models/GrainCable";
import { UnitMeasurement } from "models/UnitMeasurement"; import { UnitMeasurement } from "models/UnitMeasurement";
import Co2Icon from "products/CommonIcons/co2Icon"; import Co2Icon from "products/CommonIcons/co2Icon";
import { pond } from "protobuf-ts/pond"; import { pond, quack } from "protobuf-ts/pond";
import { useBinAPI, useGlobalState, useSnackbar } from "providers"; import { useBinAPI, useGlobalState, useSnackbar } from "providers";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { avg, getTemperatureUnit } from "utils"; import { avg, getTemperatureUnit } from "utils";
@ -200,33 +200,32 @@ export default function BinStorageConditions(props: Props) {
// } // }
}); });
let nodeEMCs: number[] = []; if(cable.subType() !== quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_TEMP){
let nodeEMCs: number[] = [];
filteredNodes.moistures.forEach((emc, i) => { filteredNodes.moistures.forEach((emc, i) => {
if (bin.settings.inventory?.targetMoisture) { if (bin.settings.inventory?.targetMoisture) {
// if (i < cable.topNode) { nodeEMCs.push(emc);
nodeEMCs.push(emc);
emcCounts.total++; emcCounts.total++;
if ( if (
emc > emc >
bin.settings.inventory.targetMoisture + bin.settings.inventory.targetMoisture +
bin.settings.inventory.moistureTargetDeviation bin.settings.inventory.moistureTargetDeviation
) { ) {
emcCounts.above++; emcCounts.above++;
} else if ( } else if (
emc < emc <
bin.settings.inventory.targetMoisture - bin.settings.inventory.targetMoisture -
bin.settings.inventory.moistureTargetDeviation bin.settings.inventory.moistureTargetDeviation
) { ) {
emcCounts.below++; emcCounts.below++;
} else { } else {
emcCounts.onTarget++; emcCounts.onTarget++;
} }
// } }
} });
}); cableEMCAvgs.push(avg(nodeEMCs));
}
cableTempAvgs.push(avg(nodeTemps)); cableTempAvgs.push(avg(nodeTemps));
cableEMCAvgs.push(avg(nodeEMCs));
} }
}); });
if (cableTempAvgs.length > 0) { if (cableTempAvgs.length > 0) {

View file

@ -436,7 +436,7 @@ export default function BinVisualizer(props: Props) {
highNodeConditions = { highNodeConditions = {
tempC: filteredNodes.temps[highTempIndex], tempC: filteredNodes.temps[highTempIndex],
humidity: filteredNodes.humids[highTempIndex], humidity: filteredNodes.humids[highTempIndex],
emc: filteredNodes.humids[highTempIndex], emc: filteredNodes.moistures[highTempIndex],
tempCTrend: cableTrendData.hotNodeTrend?.tempTrend ?? 0, tempCTrend: cableTrendData.hotNodeTrend?.tempTrend ?? 0,
humidityTrend: cableTrendData.hotNodeTrend?.humidityTrend ?? 0, humidityTrend: cableTrendData.hotNodeTrend?.humidityTrend ?? 0,
emcTrend: cableTrendData.hotNodeTrend?.emcTrend ?? 0 emcTrend: cableTrendData.hotNodeTrend?.emcTrend ?? 0

View file

@ -22,6 +22,7 @@ interface Props {
loadMore?: () => void; loadMore?: () => void;
//startingTranslate: number; //startingTranslate: number;
valDisplay?: "high" | "low" | "average"; valDisplay?: "high" | "low" | "average";
insert?: boolean
} }
const useStyles = makeStyles((_theme) => { const useStyles = makeStyles((_theme) => {
@ -39,7 +40,7 @@ const useStyles = makeStyles((_theme) => {
}); });
export default function BinsList(props: Props) { export default function BinsList(props: Props) {
const { bins, duplicateBin, title, gridView, loadMore, valDisplay } = props; const { bins, duplicateBin, title, gridView, loadMore, valDisplay, insert } = props;
const classes = useStyles(); const classes = useStyles();
// const history = useHistory(); // const history = useHistory();
const navigate = useNavigate() const navigate = useNavigate()
@ -114,7 +115,12 @@ export default function BinsList(props: Props) {
{bins.map((b, i) => {bins.map((b, i) =>
( (
<Grid <Grid
size={{ size={insert ? {
xs: 6,
sm: 6,
md: 4,
lg: 3,
} : {
xs: 6, xs: 6,
sm: 4, sm: 4,
md: 3, md: 3,

View file

@ -316,6 +316,7 @@ export default function BinyardDisplay(props: Props) {
<Grid size={12}> <Grid size={12}>
<BinsList <BinsList
gridView gridView
insert
valDisplay="average" valDisplay="average"
bins={grainBins} bins={grainBins}
duplicateBin={duplicateBin} duplicateBin={duplicateBin}
@ -332,6 +333,7 @@ export default function BinyardDisplay(props: Props) {
<Grid size={12}> <Grid size={12}>
<BinsList <BinsList
gridView gridView
insert
valDisplay="average" valDisplay="average"
bins={fertBins} bins={fertBins}
duplicateBin={duplicateBin} duplicateBin={duplicateBin}
@ -348,6 +350,7 @@ export default function BinyardDisplay(props: Props) {
<Grid size={12}> <Grid size={12}>
<BinsList <BinsList
gridView gridView
insert
valDisplay="average" valDisplay="average"
bins={emptyBins} bins={emptyBins}
duplicateBin={duplicateBin} duplicateBin={duplicateBin}

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,7 @@ import {
MenuItem, MenuItem,
OutlinedInput, OutlinedInput,
Select, Select,
TextField,
Theme Theme
} from "@mui/material"; } from "@mui/material";
import moment, { Moment } from "moment"; import moment, { Moment } from "moment";
@ -16,6 +17,7 @@ import ReactDOM from "react-dom";
import { DateRangePreset, SetDefaultPreset } from "./DateRange"; import { DateRangePreset, SetDefaultPreset } from "./DateRange";
import ResponsiveDialog from "common/ResponsiveDialog"; import ResponsiveDialog from "common/ResponsiveDialog";
import { withStyles, WithStyles } from "@mui/styles"; import { withStyles, WithStyles } from "@mui/styles";
import { cloneDeep } from "lodash";
const styles = (_theme: Theme) => createStyles({}); const styles = (_theme: Theme) => createStyles({});
@ -249,18 +251,30 @@ class DateSelect extends React.Component<Props, State> {
onClose={this.closeDateRangeDialog} onClose={this.closeDateRangeDialog}
aria-labelledby="date-range-dialog"> aria-labelledby="date-range-dialog">
<DialogContent> <DialogContent>
{/* <StaticDateRangePicker <TextField
disableFuture fullWidth
value={dateRange} margin="normal"
onChange={date => this.updateDateRange(date)} type="date"
renderInput={(startProps, endProps) => ( label="Start Time"
<React.Fragment> value={dateRange[0]?.format("YYYY-MM-DD")}
<TextField {...startProps} /> onChange={e => {
<DateRangeDelimiter> to </DateRangeDelimiter> let range = cloneDeep(dateRange)
<TextField {...endProps} /> range[0] = moment(e.target.value)
</React.Fragment> this.updateDateRange(range)
)} }}
/> */} />
<TextField
fullWidth
margin="normal"
type="date"
label="End Time"
value={dateRange[1]?.format("YYYY-MM-DD")}
onChange={e => {
let range = cloneDeep(dateRange)
range[1] = moment(e.target.value)
this.updateDateRange(range)
}}
/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={this.closeDateRangeDialog} color="primary"> <Button onClick={this.closeDateRangeDialog} color="primary">

View file

@ -270,7 +270,7 @@ export default function UpgradeDevice(props: Props) {
<Divider /> <Divider />
<DialogContent className={classes.dialogContent}> <DialogContent className={classes.dialogContent}>
<DialogContentText id="alert-dialog-slide-description"> <DialogContentText id="alert-dialog-slide-description">
Upgrading from {currentFirmware()} to {latestFirmware()} Upgrading {device.platformName()} from {currentFirmware()} to {latestFirmware()}
<br /> <br />
<br /> <br />
It is recommended that the device be plugged in during the upgrade as it will take It is recommended that the device be plugged in during the upgrade as it will take

View file

@ -338,14 +338,12 @@ export default function GateDevice(props: Props) {
return ( return (
<Grid <Grid
container container
direction="row" direction={(isMobile) ? "column" : "row"}
alignContent="center"
width="100%" width="100%"
//alignItems="center" alignItems="center"
> >
<Grid size={{ sm: 12, lg: isMobile || drawerView ? 12 : 4}} style={{ padding: 10 }}> <Grid size={{ sm: 12, lg: isMobile || drawerView ? 12 : 4}} style={{ padding: 10 }}>
<Box <Box
paddingLeft={1}
display="flex" display="flex"
justifyContent="space-between" justifyContent="space-between"
alignItems="center" alignItems="center"
@ -364,20 +362,6 @@ export default function GateDevice(props: Props) {
} }
]} ]}
/> />
{/* <ToggleButtonGroup value={detail} exclusive size="small" aria-label="detail">
<ToggleButton
onClick={() => setDetail("analytics")}
value={"analytics"}
aria-label="Analysis Graphs">
Analysis
</ToggleButton>
<ToggleButton
onClick={() => setDetail("sensors")}
value={"sensors"}
aria-label="Sensor Graphs">
Sensors
</ToggleButton>
</ToggleButtonGroup> */}
</Box> </Box>
<Card raised> <Card raised>
<GateSVG <GateSVG

View file

@ -258,9 +258,9 @@ export default function GateFlowGraph(props: Props) {
<React.Fragment> <React.Fragment>
{flowEvents.length > 0 ? ( {flowEvents.length > 0 ? (
<Grid container direction="row" spacing={2} className={classes.eventGrid}> <Grid container direction="row" spacing={2} className={classes.eventGrid}>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.eventCard}> <Card raised className={classes.eventCard}>
<Grid container alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid size={9}> <Grid size={9}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Total Events:</Typography> <Typography>Total Events:</Typography>
@ -271,7 +271,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
<Grid size={9}> <Grid size={9}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Total Time:</Typography> <Typography>Time:</Typography>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
{moment.duration(totalTimeS, "s").humanize()} {moment.duration(totalTimeS, "s").humanize()}
</Typography> </Typography>
@ -280,9 +280,9 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
</Card> </Card>
</Grid> </Grid>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.eventCard}> <Card raised className={classes.eventCard}>
<Grid container alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid size={9}> <Grid size={9}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Events Inside:</Typography> <Typography>Events Inside:</Typography>
@ -293,7 +293,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
<Grid size={9}> <Grid size={9}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Event Time:</Typography> <Typography>Time:</Typography>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
{moment.duration(timeSInside, "s").humanize()} {moment.duration(timeSInside, "s").humanize()}
</Typography> </Typography>
@ -302,9 +302,9 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
</Card> </Card>
</Grid> </Grid>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.eventCard}> <Card raised className={classes.eventCard}>
<Grid container alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid size={9}> <Grid size={9}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Events Outside:</Typography> <Typography>Events Outside:</Typography>
@ -315,7 +315,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
<Grid size={9}> <Grid size={9}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Event Time:</Typography> <Typography>Time:</Typography>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
{moment.duration(timeSOutside, "s").humanize()} {moment.duration(timeSOutside, "s").humanize()}
</Typography> </Typography>
@ -324,15 +324,15 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
</Card> </Card>
</Grid> </Grid>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.eventCard}> <Card raised className={classes.eventCard}>
<Grid container direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid> <Grid>
<Typography>Performance:</Typography> <Typography>Performance:</Typography>
</Grid> </Grid>
<Grid> <Grid>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
{(eventsInside / totalEvents) * 100}% {((eventsInside / totalEvents) * 100).toFixed(2)}%
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>
@ -367,7 +367,7 @@ export default function GateFlowGraph(props: Props) {
<React.Fragment> <React.Fragment>
{runtime && ( {runtime && (
<Grid container direction="row" spacing={2} className={classes.runtimeGrid}> <Grid container direction="row" spacing={2} className={classes.runtimeGrid}>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container width="100%" direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Approximate Runtime:</Grid> <Grid>Approximate Runtime:</Grid>
@ -379,7 +379,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
</Card> </Card>
</Grid> </Grid>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container width="100%" direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Cost to run PCA:</Grid> <Grid>Cost to run PCA:</Grid>
@ -394,7 +394,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
</Card> </Card>
</Grid> </Grid>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container width="100%" direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Cost to run APU:</Grid> <Grid>Cost to run APU:</Grid>
@ -409,7 +409,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
</Card> </Card>
</Grid> </Grid>
<Grid size={3}> <Grid size={{xs: 6, md: 3}}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container width="100%" direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Total Cost:</Grid> <Grid>Total Cost:</Grid>

View file

@ -249,9 +249,9 @@ export default function GateSVG(props: Props) {
<g key={"finalTemp"} id={"finalTemp"} data-name={"finalTemp"}> <g key={"finalTemp"} id={"finalTemp"} data-name={"finalTemp"}>
<text fontSize={7} className={classes.fontBase} x={100} y={85}> <text fontSize={7} className={classes.fontBase} x={100} y={85}>
{convertFinalTemp(finalTemp).toFixed(2)}° {convertFinalTemp(finalTemp).toFixed(2)}°
{user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
? "C" ? "°F"
: "F"} : "°C"}
</text> </text>
</g> </g>
); );
@ -260,9 +260,9 @@ export default function GateSVG(props: Props) {
<g key={"ambientTemp"} id={"ambientTemp"} data-name={"ambientTemp"}> <g key={"ambientTemp"} id={"ambientTemp"} data-name={"ambientTemp"}>
<text fontSize={7} className={classes.fontBase} x={30} y={114}> <text fontSize={7} className={classes.fontBase} x={30} y={114}>
Ambient: {ambientTemp}° Ambient: {ambientTemp}°
{user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
? "C" ? "°F"
: "F"} : "°C"}
</text> </text>
</g> </g>
); );
@ -271,13 +271,13 @@ export default function GateSVG(props: Props) {
<g key={"tempChain"} id={"tempChain"} data-name={"tempChain"}> <g key={"tempChain"} id={"tempChain"} data-name={"tempChain"}>
<text fontSize={7} className={classes.fontBase} x={30} y={149}> <text fontSize={7} className={classes.fontBase} x={30} y={149}>
T1: {innerTemps.t1}° T1: {innerTemps.t1}°
{user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
? "C" ? "°F"
: "F"} : "°C"}
, T2: {innerTemps.t2}° , T2: {innerTemps.t2}°
{user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
? "C" ? "°F"
: "F"} : "°C"}
</text> </text>
</g> </g>
); );

View file

@ -182,7 +182,7 @@ export default function LibraCartAccess() {
color="primary" color="primary"
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
window.open(`https://staging.cloud.agrimatics.com/grain/integrations`, "_blank"); window.open(`https://cloud.agrimatics.com/grain/integrations`, "_blank");
}}> }}>
Link Libra Cart Account Link Libra Cart Account
</Button> </Button>
@ -196,7 +196,7 @@ export default function LibraCartAccess() {
</Grid> </Grid>
</Grid> </Grid>
<Grid> <Grid>
<Tooltip title={"This will Sync the data for all LibraCart accounts linked to " + (as ? "this team" : "your account")}> <Tooltip title={"This will Sync the data for all Libra Cart accounts linked to " + (as ? "this team" : "your account")}>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
@ -210,7 +210,7 @@ export default function LibraCartAccess() {
</Grid> </Grid>
<Box paddingTop={2}> <Box paddingTop={2}>
<Typography> <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. Libra Cart data will sync every 6 hours, if the data is needed immediately you can select the Sync button.
</Typography> </Typography>
</Box> </Box>

View file

@ -96,7 +96,7 @@ export class Bin {
} }
public empty(): boolean { public empty(): boolean {
return this.settings.inventory?.empty || (this.settings.inventory !== undefined && this.settings.inventory !== null && this.settings.inventory.grainBushels < 5); return this.settings.inventory?.empty || (this.settings.inventory !== undefined && this.settings.inventory !== null && this.bushels() < 5);
} }
public objectType(): pond.ObjectType { public objectType(): pond.ObjectType {

View file

@ -109,6 +109,35 @@ export class Device {
return m; return m;
} }
public platformName(): string {
switch (this.settings.platform) {
case pond.DevicePlatform.DEVICE_PLATFORM_PHOTON:
return "Photon"
case pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON:
return "Electron"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR:
return "V2 Cellular"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI:
return "V2 Wifi"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_S3:
return "V2 Wifi S3"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_S3:
return "V2 Cellular S3"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK:
return "V2 Cellular Black"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN:
return "V2 Callular Green"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_BLUE:
return "V2 Wifi Blue"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE:
return "V2 Cellular Blue"
case pond.DevicePlatform.DEVICE_PLATFORM_V2_ETHERNET_BLUE:
return "V2 Ethernet Blue"
default:
return "";
}
}
public featureSupported(feature: string): boolean { public featureSupported(feature: string): boolean {
let versions = featureVersions.get(feature); let versions = featureVersions.get(feature);
if (!versions) { if (!versions) {

View file

@ -70,19 +70,26 @@ const useStyles = makeStyles((theme: Theme) => ({
}) })
}, },
sideMenuOnClosed: { sideMenuOnClosed: {
transition: theme.transitions.create(["width", "z-index", "opacity"], { transition: theme.transitions.create(["width"], {
easing: theme.transitions.easing.sharp, easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen duration: theme.transitions.duration.leavingScreen
}), }),
overflowX: "hidden", // overflowX: "hidden",
width: theme.spacing(7), width: theme.spacing(0),
zIndex: theme.zIndex.drawer, // zIndex: theme.zIndex.drawer,
opacity: 0, // opacity: 0,
[theme.breakpoints.up("md")]: { [theme.breakpoints.up("md")]: {
width: theme.spacing(9), width: theme.spacing(9.25),
opacity: 1 opacity: 1
} }
}, },
sideMenuOnClosedMobile: {
transition: theme.transitions.create(["width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
width: theme.spacing(0),
},
list: { list: {
paddingTop: 0 paddingTop: 0
}, },
@ -480,7 +487,7 @@ export default function SideNavigator(props: Props) {
</Tooltip> </Tooltip>
} }
{user.hasFeature("libra-cart") && {user.hasFeature("libra-cart") &&
<Tooltip title="LibraCart" placement="right"> <Tooltip title="Libra Cart" placement="right">
<ListItemButton <ListItemButton
id="tour-libraCart" id="tour-libraCart"
onClick={() => goTo("/libracart")} onClick={() => goTo("/libracart")}
@ -489,7 +496,7 @@ export default function SideNavigator(props: Props) {
<ListItemIcon> <ListItemIcon>
<LibraCartIcon /> <LibraCartIcon />
</ListItemIcon> </ListItemIcon>
{open && <ListItemText primary="LibraCart" />} {open && <ListItemText primary="Libra Cart" />}
</ListItemButton> </ListItemButton>
</Tooltip> </Tooltip>
} }
@ -504,7 +511,7 @@ export default function SideNavigator(props: Props) {
classes={{ classes={{
paper: classNames( paper: classNames(
classes.sideMenu, classes.sideMenu,
open ? classes.sideMenuOpened : classes.sideMenuOnClosed open ? classes.sideMenuOpened : (isMobile ? classes.sideMenuOnClosedMobile : classes.sideMenuOnClosed)
) )
}} }}
anchor="left" anchor="left"

View file

@ -810,7 +810,8 @@ export default function Bin(props: Props) {
</Grid> </Grid>
<Grid size={{ xs: 12, sm: 12, md: 12, lg: 2.6 }}> <Grid size={{ xs: 12, sm: 12, md: 12, lg: 2.6 }}>
{(bin.settings.mode === pond.BinMode.BIN_MODE_DRYING || {(bin.settings.mode === pond.BinMode.BIN_MODE_DRYING ||
bin.settings.mode === pond.BinMode.BIN_MODE_HYDRATING) && ( bin.settings.mode === pond.BinMode.BIN_MODE_HYDRATING ||
bin.settings.mode === pond.BinMode.BIN_MODE_COOLDOWN) && (
<BinConditioningCard <BinConditioningCard
mode={bin.settings.mode} mode={bin.settings.mode}
interactions={interactions} interactions={interactions}

View file

@ -103,7 +103,7 @@ export default function LibraCart() {
<Grid> <Grid>
<Select <Select
//style={{ maxWidth: 110 }} //style={{ maxWidth: 110 }}
title="LibraCart Account" title="Libra Cart Account"
displayEmpty displayEmpty
disableUnderline={true} disableUnderline={true}
value={currentOrg} value={currentOrg}

View file

@ -129,7 +129,7 @@ export default function Users() {
"marketplace", "marketplace",
"installer", "installer",
"cnhi", "cnhi",
"libracart" "libra-cart"
].sort(); ].sort();
const [rows, setRows] = useState<User[]>([]) const [rows, setRows] = useState<User[]>([])

View file

@ -71,16 +71,19 @@ export function describePower(power?: pond.DevicePower | null): PowerDescriber {
} }
} else { } else {
let thresholds = notChargingThresholds; let thresholds = notChargingThresholds;
let suffix = "not charging"; // we would not actually know if it is charging here since it is just looking at the voltage now and not comparing to previous voltage
if (power.inputVoltage >= 4) { // let suffix = "not charging";
thresholds = chargingThresholds; // if (power.inputVoltage >= 4) {
if (power.chargePercent === 100) { // thresholds = chargingThresholds;
suffix = "charged"; // if (power.chargePercent === 100) {
} else { // suffix = "charged";
suffix = "charging"; // } else {
} // suffix = "charging";
} // }
result.description = power.chargePercent.toString() + "%, " + suffix; // }
// result.description = power.chargePercent.toString() + "%, " + suffix;
// TODO: there is a plan in place to add a boolean value to power in the backend to have it check and set the boolean, we can then use that to know if it is charging or not
result.description = power.chargePercent.toString() + "%"
for (let i = 0; i < thresholds.length; i++) { for (let i = 0; i < thresholds.length; i++) {
if (power.chargePercent >= thresholds[i].threshold) { if (power.chargePercent >= thresholds[i].threshold) {
result.icon = thresholds[i].icon; result.icon = thresholds[i].icon;

View file

@ -10,7 +10,7 @@ import {
Textsms as TextIcon, Textsms as TextIcon,
Contrast, Contrast,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { AppBar, Box, Button, Checkbox, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, MenuItem, Radio, RadioGroup, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material"; import { AppBar, Box, Button, Checkbox, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, MenuItem, Radio, RadioGroup, Slider, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog"; import ResponsiveDialog from "common/ResponsiveDialog";
import UserAvatar from "./UserAvatar"; import UserAvatar from "./UserAvatar";
import { useGlobalState, useSnackbar, useUserAPI } from "providers"; import { useGlobalState, useSnackbar, useUserAPI } from "providers";
@ -31,6 +31,7 @@ import UnitsIcon from "@mui/icons-material/Category";
import { setThemeType } from "theme"; import { setThemeType } from "theme";
import { IsAdaptiveAgriculture } from "services/whiteLabel"; import { IsAdaptiveAgriculture } from "services/whiteLabel";
import { setDistanceUnit, setGrainUnit, setPressureUnit, setTemperatureUnit } from "utils"; import { setDistanceUnit, setGrainUnit, setPressureUnit, setTemperatureUnit } from "utils";
import FieldsIcon from "products/AgIcons/FieldsIcon";
const useStyles = makeStyles((theme: Theme) => { const useStyles = makeStyles((theme: Theme) => {
return ({ return ({
@ -82,7 +83,9 @@ export default function UserSettings(props: Props) {
const [avatarExpanded, setAvatarExpanded] = useState<boolean>(false); const [avatarExpanded, setAvatarExpanded] = useState<boolean>(false);
const [notificationsExpanded, setNotificationsExpanded] = useState<boolean>(false); const [notificationsExpanded, setNotificationsExpanded] = useState<boolean>(false);
const [unitsExpanded, setUnitsExpanded] = useState<boolean>(false); const [unitsExpanded, setUnitsExpanded] = useState<boolean>(false);
const [mapsExpanded, setMapsExpanded] = useState<boolean>(false);
const [avatarUrl, setAvatarUrl] = useState<string>(""); const [avatarUrl, setAvatarUrl] = useState<string>("");
const [sliderVal, setSliderVal] = useState(globalState.user.settings.mapZoom);
// const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode) // const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode)
useEffect(() => { useEffect(() => {
@ -151,6 +154,30 @@ export default function UserSettings(props: Props) {
setUser(updatedUser); setUser(updatedUser);
}; };
const changeDefaultZoom = (value: number) => {
setSliderVal(value);
let updatedUser = User.clone(user);
updatedUser.settings.mapZoom = value;
setUser(updatedUser)
};
const mapSettings = () => {
return (
<Box display="flex">
<Typography>Map Zoom Level</Typography>
<Slider
valueLabelDisplay="auto"
value={sliderVal}
min={1}
max={16}
onChange={(_, val) => {
changeDefaultZoom(val as number);
}}
/>
</Box>
);
};
const generalSettings = () => { const generalSettings = () => {
const { name, email, phoneNumber, timezone } = user.settings; const { name, email, phoneNumber, timezone } = user.settings;
@ -631,15 +658,15 @@ export default function UserSettings(props: Props) {
</List> </List>
</Collapse> </Collapse>
{/* <Divider /> <Divider />
<ListItem button onClick={() => setMapsExpanded(!mapsExpanded)}> <ListItemButton onClick={() => setMapsExpanded(!mapsExpanded)}>
<ListItemIcon> <ListItemIcon>
<FieldsIcon /> <FieldsIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary={"Map Settings"} /> <ListItemText primary={"Map Settings"} />
{mapsExpanded ? <ExpandLess /> : <ExpandMore />} {mapsExpanded ? <ExpandLess /> : <ExpandMore />}
</ListItem> </ListItemButton>
<Collapse in={mapsExpanded} timeout="auto" unmountOnExit> <Collapse in={mapsExpanded} timeout="auto" unmountOnExit>
<List component="div"> <List component="div">
<ListItem> <ListItem>
@ -648,7 +675,7 @@ export default function UserSettings(props: Props) {
</ListItemText> </ListItemText>
</ListItem> </ListItem>
</List> </List>
</Collapse> */} </Collapse>
</List> </List>
); );
}; };

View file

@ -17,6 +17,7 @@ import FeatureCard from "./FeatureCard";
//import { ImgIcon } from "common/ImgIcon"; //import { ImgIcon } from "common/ImgIcon";
//images to import for the image in the card for the feature make sure to have a 2:1 aspect ratio to keep the cards symmetrical //images to import for the image in the card for the feature make sure to have a 2:1 aspect ratio to keep the cards symmetrical
import FeatureImageTest from "assets/marketplaceImages/VisualFarmMPImage.png"; import FeatureImageTest from "assets/marketplaceImages/VisualFarmMPImage.png";
import LibraCartImage from "assets/marketplaceImages/Libra_Cart_temp.jpg";
// export interface FAQ { // export interface FAQ {
// question: string; // question: string;
@ -62,14 +63,14 @@ const agFeatureList: ProductDetails[] = [
// bulletPoints: ["bullet one", "bullet two"], // bulletPoints: ["bullet one", "bullet two"],
// questions: [], // questions: [],
}, },
// { {
// featureName: "libra-cart", featureName: "libra-cart",
// featureLogo: <LibraCartIcon />, featureLogo: <LibraCartIcon />,
// featureCost: 0, featureCost: 0,
// cardImage: FeatureImageTest, cardImage: LibraCartImage,
// featureTitle: "Libra Cart", featureTitle: "Libra Cart",
// longDescription: "Integrate with Libra Cart to bring your data into our platform" longDescription: "Integrate with Libra Cart to bring your data into our platform"
// } }
]; ];
// const constructionFeatureList: ProductDetails[] = [] // const constructionFeatureList: ProductDetails[] = []