Merge branch 'i2c_detect' into dev_environment
This commit is contained in:
commit
37b1a1fade
30 changed files with 332 additions and 141 deletions
|
|
@ -103,8 +103,10 @@ export default function BinCard(props: Props) {
|
||||||
let filteredNodes = c.filteredNodes(true)
|
let filteredNodes = c.filteredNodes(true)
|
||||||
|
|
||||||
allTemps.push(...filteredNodes.temps);
|
allTemps.push(...filteredNodes.temps);
|
||||||
|
if(avg(c.humidities) !== 0){ //if the average of the humidities is 0 that means that all the readings are 0 and it is a temp only cable
|
||||||
allHums.push(...filteredNodes.humids);
|
allHums.push(...filteredNodes.humids);
|
||||||
allMoistures.push(...filteredNodes.moistures);
|
allMoistures.push(...filteredNodes.moistures);
|
||||||
|
}
|
||||||
|
|
||||||
//set the hottest and coldest nodes
|
//set the hottest and coldest nodes
|
||||||
if (coldestNode === undefined || Math.min(...filteredNodes.temps) < coldestNode.temp) {
|
if (coldestNode === undefined || Math.min(...filteredNodes.temps) < coldestNode.temp) {
|
||||||
|
|
@ -490,7 +492,7 @@ export default function BinCard(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card style={{ cursor: "pointer" }}>
|
<Card style={{ cursor: "pointer" }}>
|
||||||
{user.hasFeature("admin") && (
|
{user.hasFeature("installer") && (
|
||||||
<IconButton
|
<IconButton
|
||||||
style={{
|
style={{
|
||||||
height: 35,
|
height: 35,
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -476,11 +476,11 @@ export default function BinSVGV2(props: Props) {
|
||||||
return cableGrainPath;
|
return cableGrainPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeClass = (nodeTemp: number) => {
|
const nodeClass = (nodeTemp: number, underTop: boolean) => {
|
||||||
if (hottestNodeTemp && hottestNodeTemp.toFixed(2) === nodeTemp.toFixed(2)) {
|
if (hottestNodeTemp && hottestNodeTemp.toFixed(2) === nodeTemp.toFixed(2) && underTop) {
|
||||||
return classes.hotNode;
|
return classes.hotNode;
|
||||||
}
|
}
|
||||||
if (coldestNodeTemp && coldestNodeTemp.toFixed(2) === nodeTemp.toFixed(2)) {
|
if (coldestNodeTemp && coldestNodeTemp.toFixed(2) === nodeTemp.toFixed(2) && underTop) {
|
||||||
return classes.coldNode;
|
return classes.coldNode;
|
||||||
}
|
}
|
||||||
return classes.cableNode;
|
return classes.cableNode;
|
||||||
|
|
@ -507,12 +507,12 @@ 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 (
|
if ((avg(a.humidities) === 0) && (avg(b.humidities) !== 0)) {
|
||||||
a.settings.subtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_TEMP &&
|
|
||||||
b.settings.subtype !== quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_TEMP
|
|
||||||
) {
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else if ((avg(b.humidities) === 0) && (avg(a.humidities) !== 0)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
return a.key() > b.key() ? 1 : -1;
|
return a.key() > b.key() ? 1 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -759,7 +759,7 @@ export default function BinSVGV2(props: Props) {
|
||||||
return (
|
return (
|
||||||
<g key={e.key}>
|
<g key={e.key}>
|
||||||
{e.nodeNumber === topNode && !e.excluded && topNodeHat(cablePos, e.y)}
|
{e.nodeNumber === topNode && !e.excluded && topNodeHat(cablePos, e.y)}
|
||||||
<circle cx={cablePos} cy={e.y} className={e.excluded ? classes.excludedNode : nodeClass(e.nodeTemp)} r="16"></circle>
|
<circle cx={cablePos} cy={e.y} className={e.excluded ? classes.excludedNode : nodeClass(e.nodeTemp, e.nodeNumber <= topNode)} r="16"></circle>
|
||||||
</g>
|
</g>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 => {
|
||||||
|
|
|
||||||
|
|
@ -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,11 +200,10 @@ export default function BinStorageConditions(props: Props) {
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(cable.subType() !== quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_TEMP){
|
||||||
let nodeEMCs: number[] = [];
|
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 (
|
||||||
|
|
@ -222,12 +221,12 @@ export default function BinStorageConditions(props: Props) {
|
||||||
} else {
|
} else {
|
||||||
emcCounts.onTarget++;
|
emcCounts.onTarget++;
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cableTempAvgs.push(avg(nodeTemps));
|
|
||||||
cableEMCAvgs.push(avg(nodeEMCs));
|
cableEMCAvgs.push(avg(nodeEMCs));
|
||||||
}
|
}
|
||||||
|
cableTempAvgs.push(avg(nodeTemps));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (cableTempAvgs.length > 0) {
|
if (cableTempAvgs.length > 0) {
|
||||||
setAverageTemp(avg(cableTempAvgs));
|
setAverageTemp(avg(cableTempAvgs));
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
const [cfmHighOpen, setCFMHighOpen] = useState(false);
|
const [cfmHighOpen, setCFMHighOpen] = useState(false);
|
||||||
const [{ user }] = useGlobalState();
|
const [{ user }] = useGlobalState();
|
||||||
const [newPreset, setNewPreset] = useState<pond.BinMode>(pond.BinMode.BIN_MODE_NONE);
|
const [newPreset, setNewPreset] = useState<pond.BinMode>(pond.BinMode.BIN_MODE_NONE);
|
||||||
|
const [newBinMode, setNewBinMode] = useState<pond.BinMode>(pond.BinMode.BIN_MODE_NONE);
|
||||||
const [selectedCable, setSelectedCable] = useState<GrainCable>();
|
const [selectedCable, setSelectedCable] = useState<GrainCable>();
|
||||||
const [openNodeDialog, setOpenNodeDialog] = useState(false);
|
const [openNodeDialog, setOpenNodeDialog] = useState(false);
|
||||||
const [cableDevice, setCableDevice] = useState<Device | undefined>();
|
const [cableDevice, setCableDevice] = useState<Device | undefined>();
|
||||||
|
|
@ -345,6 +346,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
setGrainOption(ToGrainOption(bin.settings.inventory.grainType));
|
setGrainOption(ToGrainOption(bin.settings.inventory.grainType));
|
||||||
setBushPerTonne(bin.settings.inventory.bushelsPerTonne.toFixed(2));
|
setBushPerTonne(bin.settings.inventory.bushelsPerTonne.toFixed(2));
|
||||||
setGrainSubtype(bin.subtype());
|
setGrainSubtype(bin.subtype());
|
||||||
|
setNewBinMode(bin.settings.mode);
|
||||||
}
|
}
|
||||||
if (bin.settings) {
|
if (bin.settings) {
|
||||||
let t = bin.settings.outdoorTemp;
|
let t = bin.settings.outdoorTemp;
|
||||||
|
|
@ -384,8 +386,11 @@ export default function BinVisualizer(props: Props) {
|
||||||
//also reverse it so that the node 1 (end of the cable) is in the first position
|
//also reverse it so that the node 1 (end of the cable) is in the first position
|
||||||
let filteredNodes = cable.filteredNodes(true)
|
let filteredNodes = cable.filteredNodes(true)
|
||||||
temps.push(...filteredNodes.temps)
|
temps.push(...filteredNodes.temps)
|
||||||
|
//only push to the humidity values if the cable reads humidities
|
||||||
|
if(cable.subType() !== quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_TEMP){
|
||||||
humids.push(...filteredNodes.humids)
|
humids.push(...filteredNodes.humids)
|
||||||
emcs.push(...filteredNodes.moistures)
|
emcs.push(...filteredNodes.moistures)
|
||||||
|
}
|
||||||
// let tempClone = cloneDeep(cable.temperatures).reverse();
|
// let tempClone = cloneDeep(cable.temperatures).reverse();
|
||||||
// let humClone = cloneDeep(cable.humidities).reverse();
|
// let humClone = cloneDeep(cable.humidities).reverse();
|
||||||
// let emcClone = cloneDeep(cable.grainMoistures).reverse();
|
// let emcClone = cloneDeep(cable.grainMoistures).reverse();
|
||||||
|
|
@ -409,14 +414,15 @@ export default function BinVisualizer(props: Props) {
|
||||||
|
|
||||||
//if the lowest temp for this cable is less than the temp in the low node conditions or the low node conditions are not set
|
//if the lowest temp for this cable is less than the temp in the low node conditions or the low node conditions are not set
|
||||||
//use this cables lowest node and trend data in the condition
|
//use this cables lowest node and trend data in the condition
|
||||||
if (!lowNodeConditions || Math.min(...temps) < lowNodeConditions.tempC) {
|
if (!lowNodeConditions || Math.min(...filteredNodes.temps) < lowNodeConditions.tempC) {
|
||||||
//determine which node is the coldest so that the data displayed is for the same node
|
//determine which node is the coldest so that the data displayed is for the same node
|
||||||
let lowTempIndex =
|
let lowTempIndex =
|
||||||
temps.indexOf(Math.min(...temps)) === -1 ? 0 : temps.indexOf(Math.min(...temps));
|
filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps)) === -1 ? 0 : filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps));
|
||||||
|
console.log(lowTempIndex)
|
||||||
lowNodeConditions = {
|
lowNodeConditions = {
|
||||||
tempC: temps[lowTempIndex],
|
tempC: filteredNodes.temps[lowTempIndex],
|
||||||
humidity: humids[lowTempIndex],
|
humidity: filteredNodes.humids[lowTempIndex],
|
||||||
emc: emcs[lowTempIndex],
|
emc: filteredNodes.moistures[lowTempIndex],
|
||||||
tempCTrend: cableTrendData.coldNodeTrend?.tempTrend ?? 0,
|
tempCTrend: cableTrendData.coldNodeTrend?.tempTrend ?? 0,
|
||||||
humidityTrend: cableTrendData.coldNodeTrend?.humidityTrend ?? 0,
|
humidityTrend: cableTrendData.coldNodeTrend?.humidityTrend ?? 0,
|
||||||
emcTrend: cableTrendData.coldNodeTrend?.emcTrend ?? 0
|
emcTrend: cableTrendData.coldNodeTrend?.emcTrend ?? 0
|
||||||
|
|
@ -424,13 +430,13 @@ export default function BinVisualizer(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//do the same for the high node
|
//do the same for the high node
|
||||||
if (!highNodeConditions || cable.maxTemp() > highNodeConditions.tempC) {
|
if (!highNodeConditions || Math.max(...filteredNodes.temps) > highNodeConditions.tempC) {
|
||||||
let highTempIndex =
|
let highTempIndex =
|
||||||
temps.indexOf(Math.max(...temps)) === -1 ? 0 : temps.indexOf(Math.max(...temps));
|
filteredNodes.temps.indexOf(Math.max(...filteredNodes.temps)) === -1 ? 0 : filteredNodes.temps.indexOf(Math.max(...filteredNodes.temps));
|
||||||
highNodeConditions = {
|
highNodeConditions = {
|
||||||
tempC: temps[highTempIndex],
|
tempC: filteredNodes.temps[highTempIndex],
|
||||||
humidity: humids[highTempIndex],
|
humidity: filteredNodes.humids[highTempIndex],
|
||||||
emc: emcs[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
|
||||||
|
|
@ -1967,6 +1973,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
|
|
||||||
const setModeStorage = () => {
|
const setModeStorage = () => {
|
||||||
setNewPreset(pond.BinMode.BIN_MODE_STORAGE);
|
setNewPreset(pond.BinMode.BIN_MODE_STORAGE);
|
||||||
|
setNewBinMode(pond.BinMode.BIN_MODE_STORAGE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setModeCooldown = () => {
|
const setModeCooldown = () => {
|
||||||
|
|
@ -1974,6 +1981,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setNewPreset(pond.BinMode.BIN_MODE_COOLDOWN);
|
setNewPreset(pond.BinMode.BIN_MODE_COOLDOWN);
|
||||||
|
setNewBinMode(pond.BinMode.BIN_MODE_COOLDOWN);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setModeDrying = () => {
|
const setModeDrying = () => {
|
||||||
|
|
@ -1982,13 +1990,16 @@ export default function BinVisualizer(props: Props) {
|
||||||
bin.settings.inventory?.initialMoisture < bin.settings.inventory?.targetMoisture
|
bin.settings.inventory?.initialMoisture < bin.settings.inventory?.targetMoisture
|
||||||
) {
|
) {
|
||||||
setNewPreset(pond.BinMode.BIN_MODE_HYDRATING);
|
setNewPreset(pond.BinMode.BIN_MODE_HYDRATING);
|
||||||
|
setNewBinMode(pond.BinMode.BIN_MODE_HYDRATING);
|
||||||
} else {
|
} else {
|
||||||
setNewPreset(pond.BinMode.BIN_MODE_DRYING);
|
setNewPreset(pond.BinMode.BIN_MODE_DRYING);
|
||||||
|
setNewBinMode(pond.BinMode.BIN_MODE_DRYING);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeMoistureDialog = () => {
|
const closeMoistureDialog = () => {
|
||||||
setNewPreset(0);
|
setNewPreset(0);
|
||||||
|
setNewBinMode(bin.settings.mode);
|
||||||
setShowInputMoisture(false);
|
setShowInputMoisture(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2049,6 +2060,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setNewPreset(0);
|
setNewPreset(0);
|
||||||
|
setNewBinMode(bin.settings.mode);
|
||||||
setShowInputMoisture(false);
|
setShowInputMoisture(false);
|
||||||
}}
|
}}
|
||||||
color="primary">
|
color="primary">
|
||||||
|
|
@ -2090,7 +2102,13 @@ export default function BinVisualizer(props: Props) {
|
||||||
}
|
}
|
||||||
if (b.settings.outdoorHumidity !== undefined)
|
if (b.settings.outdoorHumidity !== undefined)
|
||||||
b.settings.outdoorHumidity = Number(outdoorHumidityInput);
|
b.settings.outdoorHumidity = Number(outdoorHumidityInput);
|
||||||
b.settings.mode = newPreset;
|
|
||||||
|
if (b.settings.mode != newBinMode){
|
||||||
|
if(b.settings.inventory){
|
||||||
|
b.settings.inventory.initialTimestamp = moment().format();
|
||||||
|
}
|
||||||
|
b.settings.mode = newBinMode;
|
||||||
|
}
|
||||||
|
|
||||||
binAPI.updateBin(bin.key(), b.settings, as).then(resp => {
|
binAPI.updateBin(bin.key(), b.settings, as).then(resp => {
|
||||||
refresh();
|
refresh();
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -36,6 +36,7 @@ import {
|
||||||
} from "pbHelpers/AddressType";
|
} from "pbHelpers/AddressType";
|
||||||
import {
|
import {
|
||||||
extension,
|
extension,
|
||||||
|
FilterSubtypeI2C,
|
||||||
getAddressTypes,
|
getAddressTypes,
|
||||||
GetComponentTypeOptions,
|
GetComponentTypeOptions,
|
||||||
getFriendlyName,
|
getFriendlyName,
|
||||||
|
|
@ -240,7 +241,8 @@ export default function ComponentSettings(props: Props) {
|
||||||
|
|
||||||
const getAvailablePositions = (
|
const getAvailablePositions = (
|
||||||
addressType: quack.AddressType,
|
addressType: quack.AddressType,
|
||||||
componentType: quack.ComponentType
|
componentType: quack.ComponentType,
|
||||||
|
subtype: number
|
||||||
): number[] => {
|
): number[] => {
|
||||||
let positions = availablePositions.get(addressType);
|
let positions = availablePositions.get(addressType);
|
||||||
if (!positions) return [];
|
if (!positions) return [];
|
||||||
|
|
@ -249,7 +251,8 @@ export default function ComponentSettings(props: Props) {
|
||||||
case quack.AddressType.ADDRESS_TYPE_SPI:
|
case quack.AddressType.ADDRESS_TYPE_SPI:
|
||||||
let componentPositions;
|
let componentPositions;
|
||||||
if (positions) {
|
if (positions) {
|
||||||
componentPositions = (positions as ComponentAvailabilityMap).get(componentType);
|
componentPositions = (positions as ComponentAvailabilityMap).get(componentType); //gets the addresses that are not claimed yet
|
||||||
|
componentPositions = FilterSubtypeI2C(componentType, subtype, componentPositions as number[]) //filters the I2C addresses for valid ones for the subtype
|
||||||
}
|
}
|
||||||
return componentPositions ? (componentPositions as number[]) : [];
|
return componentPositions ? (componentPositions as number[]) : [];
|
||||||
case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
|
case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
|
||||||
|
|
@ -264,7 +267,8 @@ export default function ComponentSettings(props: Props) {
|
||||||
if (mode === "add") {
|
if (mode === "add") {
|
||||||
let positions = getAvailablePositions(
|
let positions = getAvailablePositions(
|
||||||
component.settings.addressType,
|
component.settings.addressType,
|
||||||
component.settings.type
|
component.settings.type,
|
||||||
|
component.settings.subtype
|
||||||
);
|
);
|
||||||
availablePosition = positions ? positions.length > 0 : false;
|
availablePosition = positions ? positions.length > 0 : false;
|
||||||
}
|
}
|
||||||
|
|
@ -285,7 +289,7 @@ export default function ComponentSettings(props: Props) {
|
||||||
component.settings.subtype
|
component.settings.subtype
|
||||||
);
|
);
|
||||||
for (let i = 0; i < addressTypes.length; i++) {
|
for (let i = 0; i < addressTypes.length; i++) {
|
||||||
if (getAvailablePositions(addressTypes[i], component.settings.type).length > 0) {
|
if (getAvailablePositions(addressTypes[i], component.settings.type, component.settings.subtype).length > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -383,7 +387,7 @@ export default function ComponentSettings(props: Props) {
|
||||||
formComponent.settings.subtype
|
formComponent.settings.subtype
|
||||||
)[0]);
|
)[0]);
|
||||||
formComponent.settings.address = or(
|
formComponent.settings.address = or(
|
||||||
getAvailablePositions(formComponent.settings.addressType, formComponent.settings.type)[0],
|
getAvailablePositions(formComponent.settings.addressType, formComponent.settings.type, formComponent.settings.subtype)[0],
|
||||||
Component.create().settings.address
|
Component.create().settings.address
|
||||||
);
|
);
|
||||||
formComponent.settings.name = option.label;
|
formComponent.settings.name = option.label;
|
||||||
|
|
@ -426,7 +430,7 @@ export default function ComponentSettings(props: Props) {
|
||||||
let availableMenuItemPositions = [];
|
let availableMenuItemPositions = [];
|
||||||
for (let i = 0; i < addressTypes.length; i++) {
|
for (let i = 0; i < addressTypes.length; i++) {
|
||||||
let addressType = addressTypes[i];
|
let addressType = addressTypes[i];
|
||||||
let availablePositions = getAvailablePositions(addressType, type);
|
let availablePositions = getAvailablePositions(addressType, type, subtype);
|
||||||
|
|
||||||
availableMenuItemPositions.push(
|
availableMenuItemPositions.push(
|
||||||
<MenuItem key={addressType} divider disabled>
|
<MenuItem key={addressType} divider disabled>
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export default function DeviceScannedComponents(props: Props){
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
console.log(scannedComponents)
|
//console.log(scannedComponents)
|
||||||
if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
|
if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
|
||||||
//makes the array empty for testing
|
//makes the array empty for testing
|
||||||
// if (scannedComponents.i2c) {
|
// if (scannedComponents.i2c) {
|
||||||
|
|
@ -226,7 +226,9 @@ export default function DeviceScannedComponents(props: Props){
|
||||||
<Button variant="contained" color="primary" onClick={()=>{removeScan(scannedI2C.key)}}>Clear Scan</Button>
|
<Button variant="contained" color="primary" onClick={()=>{removeScan(scannedI2C.key)}}>Clear Scan</Button>
|
||||||
</Box>
|
</Box>
|
||||||
{validCompAddresses.length > 0 ? validCompAddresses.map((addr, index) => {
|
{validCompAddresses.length > 0 ? validCompAddresses.map((addr, index) => {
|
||||||
return <ScannedI2C key={index} sensorNum={index+1} addressData={addr} deviceProduct={device.settings.product} componentSelectionCallback={componentSelection} availablePositions={availablePositions.get(quack.AddressType.ADDRESS_TYPE_I2C) ?? new Map<quack.ComponentType, number[]>()}/>
|
return (
|
||||||
|
<ScannedI2C key={index} sensorNum={index+1} addressData={addr} deviceProduct={device.settings.product} componentSelectionCallback={componentSelection} availablePositions={availablePositions.get(quack.AddressType.ADDRESS_TYPE_I2C) ?? new Map<quack.ComponentType, number[]>()}/>
|
||||||
|
)
|
||||||
}) :
|
}) :
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
margin: 1,
|
margin: 1,
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ export default function ScannedI2C(props: Props){
|
||||||
return (
|
return (
|
||||||
<Box sx={{marginY: 1}}>
|
<Box sx={{marginY: 1}}>
|
||||||
{sensorNum && <Typography>Sensor {sensorNum}</Typography>}
|
{sensorNum && <Typography>Sensor {sensorNum}</Typography>}
|
||||||
|
<Typography variant="caption">{addressData.expansionLine !== 0 && "Expansion Line: " + addressData.expansionLine}</Typography>
|
||||||
{types.length > 0 ?
|
{types.length > 0 ?
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Grid2 container direction="row" alignItems="center" justifyContent="space-between">
|
<Grid2 container direction="row" alignItems="center" justifyContent="space-between">
|
||||||
|
|
@ -145,7 +146,7 @@ export default function ScannedI2C(props: Props){
|
||||||
}}
|
}}
|
||||||
select>
|
select>
|
||||||
{subtypeOptions.map(s => (
|
{subtypeOptions.map(s => (
|
||||||
<MenuItem value={s.key}>{s.friendlyName}</MenuItem>
|
<MenuItem key={s.key} value={s.key}>{s.friendlyName}</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ export const GrainExtensions: Map<pond.Grain, GrainExtension> = new Map([
|
||||||
[
|
[
|
||||||
pond.Grain.GRAIN_OATS,
|
pond.Grain.GRAIN_OATS,
|
||||||
{
|
{
|
||||||
name: "Oats",
|
name: "Oats a",
|
||||||
group: "Oats",
|
group: "Oats",
|
||||||
equation: Equation.henderson,
|
equation: Equation.henderson,
|
||||||
a: 0.000085511,
|
a: 0.000085511,
|
||||||
|
|
@ -203,6 +203,40 @@ export const GrainExtensions: Map<pond.Grain, GrainExtension> = new Map([
|
||||||
weightConversionKg: 15.4222988297197,
|
weightConversionKg: 15.4222988297197,
|
||||||
bushelsPerTonne: 64.842
|
bushelsPerTonne: 64.842
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
pond.Grain.GRAIN_OATS_B,
|
||||||
|
{
|
||||||
|
name: "Oats b",
|
||||||
|
group: "Oats",
|
||||||
|
equation: Equation.chungPfost,
|
||||||
|
a: 442.85,
|
||||||
|
b: 0.21228,
|
||||||
|
c: 35.803,
|
||||||
|
setTempC: 32.0,
|
||||||
|
targetMC: 13.6,
|
||||||
|
img: OatImg,
|
||||||
|
colour: "#79955a",
|
||||||
|
weightConversionKg: 15.4222988297197,
|
||||||
|
bushelsPerTonne: 64.842
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
pond.Grain.GRAIN_OATS_C,
|
||||||
|
{
|
||||||
|
name: "Oats c",
|
||||||
|
group: "Oats",
|
||||||
|
equation: Equation.oswin,
|
||||||
|
a: 12.412,
|
||||||
|
b: -0.060707,
|
||||||
|
c: 2.9397,
|
||||||
|
setTempC: 32.0,
|
||||||
|
targetMC: 13.6,
|
||||||
|
img: OatImg,
|
||||||
|
colour: "#79955a",
|
||||||
|
weightConversionKg: 15.4222988297197,
|
||||||
|
bushelsPerTonne: 64.842
|
||||||
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
pond.Grain.GRAIN_PEANUTS,
|
pond.Grain.GRAIN_PEANUTS,
|
||||||
|
|
@ -506,6 +540,66 @@ export const GrainExtensions: Map<pond.Grain, GrainExtension> = new Map([
|
||||||
weightConversionKg: 27.2155,
|
weightConversionKg: 27.2155,
|
||||||
bushelsPerTonne: 36.744
|
bushelsPerTonne: 36.744
|
||||||
}
|
}
|
||||||
|
],[
|
||||||
|
pond.Grain.GRAIN_RYE_A,
|
||||||
|
{
|
||||||
|
name: "Rye a",
|
||||||
|
group: "Rye",
|
||||||
|
equation: Equation.henderson,
|
||||||
|
a: 0.00006343,
|
||||||
|
b: 2.2060,
|
||||||
|
c: 13.1810,
|
||||||
|
setTempC: defaultSetTemp,
|
||||||
|
targetMC: 15.0,
|
||||||
|
colour: "grey",
|
||||||
|
weightConversionKg: 25.4,
|
||||||
|
bushelsPerTonne: 39.368
|
||||||
|
}
|
||||||
|
],[
|
||||||
|
pond.Grain.GRAIN_RYE_B,
|
||||||
|
{
|
||||||
|
name: "Rye b",
|
||||||
|
group: "Rye",
|
||||||
|
equation: Equation.chungPfost,
|
||||||
|
a: 461.0230,
|
||||||
|
b: 0.1840,
|
||||||
|
c: 36.7410,
|
||||||
|
setTempC: defaultSetTemp,
|
||||||
|
targetMC: 15.0,
|
||||||
|
colour: "grey",
|
||||||
|
weightConversionKg: 25.4,
|
||||||
|
bushelsPerTonne: 39.368
|
||||||
|
},
|
||||||
|
],[
|
||||||
|
pond.Grain.GRAIN_RYE_C,
|
||||||
|
{
|
||||||
|
name: "Rye c",
|
||||||
|
group: "Rye",
|
||||||
|
equation: Equation.halsey,
|
||||||
|
a: 4.2970,
|
||||||
|
b: 0.380,
|
||||||
|
c: 2.2710,
|
||||||
|
setTempC: defaultSetTemp,
|
||||||
|
targetMC: 15.0,
|
||||||
|
colour: "grey",
|
||||||
|
weightConversionKg: 25.4,
|
||||||
|
bushelsPerTonne: 39.368
|
||||||
|
},
|
||||||
|
],[
|
||||||
|
pond.Grain.GRAIN_RYE_D,
|
||||||
|
{
|
||||||
|
name: "Rye d",
|
||||||
|
group: "Rye",
|
||||||
|
equation: Equation.oswin,
|
||||||
|
a: 11.8870,
|
||||||
|
b: 0.0210,
|
||||||
|
c: 3.2620,
|
||||||
|
setTempC: defaultSetTemp,
|
||||||
|
targetMC: 15.0,
|
||||||
|
colour: "grey",
|
||||||
|
weightConversionKg: 25.4,
|
||||||
|
bushelsPerTonne: 39.368
|
||||||
|
}
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@ export class GrainCable {
|
||||||
let filtered: number[] = []
|
let filtered: number[] = []
|
||||||
values.forEach((val, index) => {
|
values.forEach((val, index) => {
|
||||||
//if the node is not excluded AND is either under the top node OR top node is not set
|
//if the node is not excluded AND is either under the top node OR top node is not set
|
||||||
if(!this.excludedNodes.includes(index) && (index <= this.topNode || this.topNode === 0)){
|
if(!this.excludedNodes.includes(index) && (index < this.topNode || this.topNode === 0)){
|
||||||
filtered.push(val)
|
filtered.push(val)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ export default function Devices() {
|
||||||
]
|
]
|
||||||
if (hasPlenums) {
|
if (hasPlenums) {
|
||||||
columns.push({
|
columns.push({
|
||||||
title: "Plenum",
|
title: "Temp/Humidity",
|
||||||
// sortKey: "hi",
|
// sortKey: "hi",
|
||||||
// disableSort: true,
|
// disableSort: true,
|
||||||
render: (device: Device) => {
|
render: (device: Device) => {
|
||||||
|
|
|
||||||
|
|
@ -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[]>([])
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,10 @@ export interface ComponentTypeExtension {
|
||||||
filters?: GraphFilters
|
filters?: GraphFilters
|
||||||
) => LineChartData;
|
) => LineChartData;
|
||||||
secondaryComponentSettings?: () => pond.ComponentSettings[]
|
secondaryComponentSettings?: () => pond.ComponentSettings[]
|
||||||
|
//(only for I2C) possibly add an optional map here that has a number (subtype) as the key and the addresses available for that subtype
|
||||||
|
//if the map does not exist then just use the array from the device availability,
|
||||||
|
//if the map does exist filter the availability after claims to find matching address between them and use that
|
||||||
|
subtypeI2CMap?: Map<number, number[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Summary {
|
export interface Summary {
|
||||||
|
|
@ -1090,3 +1094,31 @@ export const GetNumNodesFromUnitMeasurement = (unitMeasurement: UnitMeasurement)
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FilterSubtypeI2C = (
|
||||||
|
componentType: quack.ComponentType,
|
||||||
|
subtype: number,
|
||||||
|
positions: number[]
|
||||||
|
) => {
|
||||||
|
console.log("Filter I2C")
|
||||||
|
console.log(positions)
|
||||||
|
let filtered: number[] = []
|
||||||
|
//create an extension using the type and subtype
|
||||||
|
let ext = extension(componentType, subtype)
|
||||||
|
//if the extension has a subtype map
|
||||||
|
if (ext.subtypeI2CMap !== undefined){
|
||||||
|
//check if any of the position passed in match to the map values according to the subtype
|
||||||
|
let subtypePositions = ext.subtypeI2CMap.get(subtype)
|
||||||
|
if(subtypePositions !== undefined){
|
||||||
|
positions.forEach(position => {
|
||||||
|
if(subtypePositions.includes(position)){
|
||||||
|
filtered.push(position)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
filtered = positions
|
||||||
|
}
|
||||||
|
//return any matches between the arrays
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,11 @@ export function Lidar(subtype: number = 0): ComponentTypeExtension {
|
||||||
minMeasurementPeriodMs: 1000,
|
minMeasurementPeriodMs: 1000,
|
||||||
icon: (theme?: "light" | "dark"): string | undefined => {
|
icon: (theme?: "light" | "dark"): string | undefined => {
|
||||||
return theme === "light" ? LidarSensorDarkIcon : LidarSensorLightIcon;
|
return theme === "light" ? LidarSensorDarkIcon : LidarSensorLightIcon;
|
||||||
}
|
},
|
||||||
|
subtypeI2CMap: new Map<number, number[]>([
|
||||||
|
[quack.LidarSubtype.LIDAR_SUBTYPE_NONE, [0x62]],
|
||||||
|
[quack.LidarSubtype.LIDAR_SUBTYPE_LONG_DISTANCE, [0x66]],
|
||||||
|
[quack.LidarSubtype.LIDAR_SUBTYPE_BENEWAKE_TF02W_MID_RANGE, [0x10]],
|
||||||
|
])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { GetProductAvailability } from "products/DeviceProduct";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { quack } from "protobuf-ts/quack";
|
import { quack } from "protobuf-ts/quack";
|
||||||
import { or } from "utils/types";
|
import { or } from "utils/types";
|
||||||
|
import { FilterSubtypeI2C } from "./ComponentType";
|
||||||
|
|
||||||
export type ComponentAvailabilityMap = Map<quack.ComponentType, number[]>;
|
export type ComponentAvailabilityMap = Map<quack.ComponentType, number[]>;
|
||||||
export type DevicePositions = number[] | ComponentAvailabilityMap | ConfigurablePin[];
|
export type DevicePositions = number[] | ComponentAvailabilityMap | ConfigurablePin[];
|
||||||
|
|
@ -47,16 +48,17 @@ const DefaultAvailability: DeviceAvailabilityMap = new Map<quack.AddressType, De
|
||||||
[quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, DefaultPinArray],
|
[quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, DefaultPinArray],
|
||||||
[
|
[
|
||||||
quack.AddressType.ADDRESS_TYPE_I2C,
|
quack.AddressType.ADDRESS_TYPE_I2C,
|
||||||
|
//this will contain all the possible addresses for a component type, if subtypes use specific addresses add a filter to the type describer, see lidar.ts for an example
|
||||||
new Map<quack.ComponentType, number[]>([
|
new Map<quack.ComponentType, number[]>([
|
||||||
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18, 0x19, 0x1a]],
|
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18, 0x19, 0x1a]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_LIGHT, [0x29]],
|
[quack.ComponentType.COMPONENT_TYPE_LIGHT, [0x29]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
|
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, [0x14, 0x15, 0x16, 0x17]],
|
[quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, [0x14, 0x15, 0x16, 0x17]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_PH, [0x40]],
|
[quack.ComponentType.COMPONENT_TYPE_PH, [0x40]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40, 0x61]], //TODO temp fix, needs to be fixed to use channels similar to PH
|
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40, 0x61]],
|
||||||
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
|
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
|
||||||
[quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY, [0x58]],
|
[quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY, [0x58]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
|
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62, 0x66, 0x10]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, [0x50]],
|
[quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, [0x50]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE, [0x50]],
|
[quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE, [0x50]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
|
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ export const BindaptV2MonitorAvailability: DeviceAvailabilityMap = new Map<
|
||||||
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
|
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
|
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
|
||||||
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
|
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
|
||||||
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
|
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62, 0x66, 0x10]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
|
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
|
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE, [0x71]]
|
[quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE, [0x71]]
|
||||||
|
|
@ -282,7 +282,7 @@ export const BindaptV2AutomateAvailability: DeviceAvailabilityMap = new Map<
|
||||||
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
|
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
|
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
|
||||||
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
|
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
|
||||||
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
|
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62, 0x66, 0x10]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
|
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
|
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
|
||||||
[quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE, [0x71]]
|
[quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE, [0x71]]
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -62,14 +62,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: FeatureImageTest,
|
||||||
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[] = []
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue