From 5ebbd01c3a013ac0a763288f5abb3796c67338e1 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Wed, 28 Jan 2026 15:29:55 -0600 Subject: [PATCH 01/14] hotfix for how the device was being loaded when you open the device drawer of the construction map --- package-lock.json | 4 +-- src/device/DeviceViewer.tsx | 65 ++++++++++++++----------------------- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index c150062..bc69395 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", @@ -10953,7 +10953,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#c9ef7906fd97cda8ef4bd149ec4a796159a7c067", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#bcf085706791036841f7047a5b37f00c18e54574", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/src/device/DeviceViewer.tsx b/src/device/DeviceViewer.tsx index e03dae3..5836f28 100644 --- a/src/device/DeviceViewer.tsx +++ b/src/device/DeviceViewer.tsx @@ -31,6 +31,7 @@ import React, { useCallback, useEffect, useState } from "react"; //import { Redirect } from "react-router"; import { or } from "utils/types"; import DeviceActions from "./DeviceActions"; +import { getContextKeys, getContextTypes } from "pbHelpers/Context"; const useStyles = makeStyles((theme: Theme) => ({ title: { @@ -97,45 +98,28 @@ export default function Device(props: Props) { const load = useCallback(() => { if (user.id() === "") return; setIsLoading(true); - //let devicePromise = deviceAPI.get(deviceID); - let userPromise = userAPI.getUser(user.id(), deviceScope(deviceID.toString())); - let componentsPromise = componentAPI.list( - deviceID, - undefined, - [deviceID.toString()], - ["device"], - true, - as - ); - let interactionsPromise = interactionsAPI.listInteractionsByDevice(deviceID, undefined, as); - let groupPromise: Promise = Promise.resolve(undefined); - Promise.all([userPromise, componentsPromise, interactionsPromise, groupPromise]) - .then(([userRes, componentsRes, interactionsRes]) => { - //let rDevice = DeviceModel.any(deviceRes.data); - let rawComponents: Array = or(componentsRes.data.components, []); - let rComponents: Map = new Map(); - rawComponents.forEach((rawComponent: any) => { - let component = Component.any(rawComponent); - rComponents.set(component.key(), component); - }); + deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes(), as).then(resp => { + console.log(resp) + let rawComponents: Array = or(resp.data.components, []); + let rComponents: Map = new Map(); + rawComponents.forEach((rawComponent: any) => { + let component = Component.any(rawComponent); + rComponents.set(component.key(), component); + }); + setComponents(rComponents); - setComponents(rComponents); - - setInteractions(interactionsRes); - setPermissions(userRes.permissions); - setPreferences(userRes.preferences); - - setDevice(props.device); - setLongitude(props.device.status.longitude ? props.device.status.longitude : NaN); - setLatitude(props.device.status.latitude ? props.device.status.latitude : NaN); - let available = FindAvailablePositions( - Array.from(rComponents.values()), - props.device.settings.product - ); - setAvailablePositions(available.GetAvailability()); - setAvailableOffsets(available.offsetAvailability); - }) - .catch((err: any) => { + setInteractions(resp.data.interactions.map(i => Interaction.create(i))) + setPermissions(resp.data.permissions) + setDevice(props.device); + setLongitude(props.device.status.longitude ? props.device.status.longitude : NaN); + setLatitude(props.device.status.latitude ? props.device.status.latitude : NaN); + let available = FindAvailablePositions( + Array.from(rComponents.values()), + props.device.settings.product + ); + setAvailablePositions(available.GetAvailability()); + setAvailableOffsets(available.offsetAvailability); + }).catch(err => { setDevice(DeviceModel.create()); setComponents(new Map()); setInteractions([]); @@ -145,8 +129,9 @@ export default function Device(props: Props) { setPreferences(pond.UserPreferences.create()); setInvalidDevice(true); error(err); - }) - .finally(() => setIsLoading(false)); + }).finally(() => { + setIsLoading(false) + }) usageAPI .getUsage(deviceID, moment().subtract(1, "days")) .then((res: any) => { From edf865752dfe290dc319803375d47fa54c9fea84 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Thu, 12 Feb 2026 15:00:57 -0600 Subject: [PATCH 02/14] fixing airflow measurment types using the wrong enum --- src/pbHelpers/ComponentTypes/Airflow.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pbHelpers/ComponentTypes/Airflow.ts b/src/pbHelpers/ComponentTypes/Airflow.ts index b441256..ddfbc48 100644 --- a/src/pbHelpers/ComponentTypes/Airflow.ts +++ b/src/pbHelpers/ComponentTypes/Airflow.ts @@ -33,13 +33,13 @@ export function Airflow(subtype: number = 0): ComponentTypeExtension { let measurementTypes = [ { - measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PPM, + measurementType: quack.MeasurementType.MEASUREMENT_TYPE_CFM, label: cfm.label(), colour: cfm.colour(), graphType: cfm.graph() } as ComponentMeasurement, { - measurementType: quack.MeasurementType.MEASUREMENT_TYPE_VOLTAGE, + measurementType: quack.MeasurementType.MEASUREMENT_TYPE_SPEED, label: velocity.label(), colour: velocity.colour(), graphType: velocity.graph() From 24c157747c93e944678ef7c399d24a96fcc917f9 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Tue, 17 Feb 2026 16:40:23 -0600 Subject: [PATCH 03/14] removed a slash before the ? for the url params that may have been the cause of iOS devices using safari to trigger an error when calling updatedComponentPreferences in the binAPI --- src/providers/pond/binAPI.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/pond/binAPI.tsx b/src/providers/pond/binAPI.tsx index f4ac73f..def6699 100644 --- a/src/providers/pond/binAPI.tsx +++ b/src/providers/pond/binAPI.tsx @@ -290,7 +290,7 @@ export default function BinProvider(props: PropsWithChildren) { ) => { const view = otherTeam ? otherTeam : as let url = "/bins/" + bin + "/updateComponent/" + component + "/preferences"; - if (view) url = url + "/?as=" + view; + if (view) url = url + "?as=" + view; interface request { preferences: Object; } From b5adba8f93a3c4715cd825a73ca5dc68ce1733fc Mon Sep 17 00:00:00 2001 From: csawatzky Date: Wed, 25 Feb 2026 16:10:12 -0600 Subject: [PATCH 04/14] updating the gate and terminal stuff for the device changes since we no longer have MiPCA device use chains for pressure and duct temp and are now only measuring the outlet and substituting the temp inlet with the ambient temp and the pressure inlet with 0 --- src/gate/GateDeltaTempGraph.tsx | 82 +++++++++++++++++++++++++++++++-- src/gate/GateDevice.tsx | 65 ++++++++++++-------------- src/gate/GateGraphs.tsx | 7 +-- src/gate/GateList.tsx | 50 ++++++++++++++------ src/gate/GateSVG.tsx | 24 +++++++--- src/providers/pond/gateAPI.tsx | 1 - 6 files changed, 164 insertions(+), 65 deletions(-) diff --git a/src/gate/GateDeltaTempGraph.tsx b/src/gate/GateDeltaTempGraph.tsx index 6a883bd..fc5b253 100644 --- a/src/gate/GateDeltaTempGraph.tsx +++ b/src/gate/GateDeltaTempGraph.tsx @@ -1,4 +1,4 @@ -import { Box, Card, CardHeader, Grid2, Typography } from "@mui/material" +import { Card, CardHeader, Grid2, Typography } from "@mui/material" import { blue, orange } from "@mui/material/colors" import SingleSetAreaChart, { SSAreaDataPoint } from "charts/SingleSetAreaChart" import { useComponentAPI } from "hooks" @@ -38,11 +38,68 @@ export default function GateDeltaTempGraph(props: Props){ const warmingColour = orange["500"] const coolingColour = blue["500"] + const getUnitByType = (unitMeasurements: UnitMeasurement[], type: quack.MeasurementType) => { + return unitMeasurements.find(u => u.type === type); + } + + const buildDeltas = (ambientMeasurements: UnitMeasurement[], tempCompMeasurements: UnitMeasurement[], measurementType: quack.MeasurementType) => { + const ambient = getUnitByType(ambientMeasurements, measurementType); + const outlet = getUnitByType(tempCompMeasurements, measurementType); + + if (!ambient || !outlet) return []; + + let i = 0; + let j = 0; + + let lastAmbient: number | null = null; + let lastOutlet: number | null = null; + + const deltas: SSAreaDataPoint[] = []; + + const ambientTimes = ambient.timestamps.map(t => moment(t).valueOf()).reverse(); + const outletTimes = outlet.timestamps.map(t => moment(t).valueOf()).reverse(); + const ambientValues = [...ambient.values].reverse(); + const outletValues = [...outlet.values].reverse(); + + while (i < ambientTimes.length || j < outletTimes.length) { + + const aTime = i < ambientTimes.length ? ambientTimes[i] : Infinity; + const bTime = j < outletTimes.length ? outletTimes[j] : Infinity; + + if (aTime <= bTime) { + const valueArray = ambientValues[i++]; + if (!valueArray.error && valueArray.values.length > 0) { + lastAmbient = valueArray.values[valueArray.values.length -1]; + } + + if (lastAmbient !== null && lastOutlet !== null) { + deltas.push({ + timestamp: aTime, + value: Math.round((lastOutlet - lastAmbient)*100)/100 + }); + } + + } else { + const valueArray = outletValues[j++]; + if (!valueArray.error && valueArray.values.length > 0) { + lastOutlet = valueArray.values[valueArray.values.length -1]; + } + + if (lastAmbient !== null && lastOutlet !== null) { + deltas.push({ + timestamp: bTime, + value: Math.round((lastOutlet - lastAmbient)*100)/100 + }); + } + } + } + return deltas + } + /** * the use effect will use the temp component passed in get the measurements, then use the first node and the last node to determine the difference, * if there is only one node, or only one node is reading due to errors, then it will use that as both the inlet and outlet so the delta will be 0, * - * //NOT IMPLEMENTED YET// * if an ambient is passed in it will use the readings from the ambient as the inlet and the temp component as the outlet */ useEffect(()=>{ @@ -52,7 +109,7 @@ export default function GateDeltaTempGraph(props: Props){ tempComponent.key(), start.toISOString(), end.toISOString(), - 500, + 500, 0, "timestamp", ).then(resp => { @@ -60,7 +117,24 @@ export default function GateDeltaTempGraph(props: Props){ let tempCompMeasurements = resp.data.measurements.map(um => UnitMeasurement.any(um, user)); //if there is an ambient component, then treat the temp component like a single sensor and not a chain, and use the ambient measurements as the inlet if(ambient){ - //TODO: this is just an idea of how to handle a possible path for the overhaul of gates, if that is the route we take then this needs to be coded + //need to then load the measurements for the ambient component + componentAPI.listUnitMeasurements( + deviceID, + ambient.key(), + start.toISOString(), + end.toISOString(), + 500, + 0, + "timestamp", + ).then(resp => { + let ambientMeasurements = resp.data.measurements.map(um => UnitMeasurement.any(um, user)); + //now loop through each of them to 'combine' them into a single array + const deltas = buildDeltas(ambientMeasurements, tempCompMeasurements, quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE) + if(deltas.length > 0){ + setData(deltas) + setRecent(deltas[deltas.length-1]) + } + }) }else{//else no ambient is passed in treat the temp component like a chain let deltas: SSAreaDataPoint[] = [] diff --git a/src/gate/GateDevice.tsx b/src/gate/GateDevice.tsx index bfc1641..f62dd25 100644 --- a/src/gate/GateDevice.tsx +++ b/src/gate/GateDevice.tsx @@ -23,6 +23,7 @@ import GateGraphs from "./GateGraphs"; //import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab"; import { getThemeType } from "theme"; import ButtonGroup from "common/ButtonGroup"; +import { cloneDeep } from "lodash"; interface Props { gate: Gate; @@ -55,8 +56,11 @@ export default function GateDevice(props: Props) { // const [pcaFanOn, setPCAFanOn] = useState(false); const [detail, setDetail] = useState<"sensors" | "analytics">("analytics"); const [lastAmbient, setLastAmbient] = useState(0); - const [lastTemps, setLastTemps] = useState({ t1: 0, t2: 0 }); - const [lastPressures, setLastPressures] = useState({ p1: 0, p2: 0 }); + //const [lastTemps, setLastTemps] = useState({ t1: 0, t2: 0 }); + const [ductTemp, setDuctTemp] = useState() + //const [lastPressures, setLastPressures] = useState({ p1: 0, p2: 0 }); + const [ductPressure, setDuctPressure] = useState() + useEffect(() => { //addresses of controller LEDs on a V1 device @@ -142,24 +146,20 @@ export default function GateDevice(props: Props) { useEffect(() => { let tempComponent = componentOptions.get(tempKey); - let t1 = 0; - let t2 = 0; - if (tempComponent) { + if(tempComponent){ tempComponent.status.measurement.forEach(um => { - let measurement = UnitMeasurement.any(um, user); - if ( - measurement.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE && - measurement.values.length > 0 - ) { - let nodeVals = measurement.values[0].values; - if (nodeVals.length > 1) { - t1 = nodeVals[0]; //uses the first node - t2 = nodeVals[1]; //uses second node node + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + setDuctTemp(readings.values[readings.values.length -1]) + } } } - }); + }) } - setLastTemps({ t1, t2 }); }, [componentOptions, tempKey, user]); useEffect(() => { @@ -180,24 +180,20 @@ export default function GateDevice(props: Props) { useEffect(() => { let pressureComponent = componentOptions.get(pressureKey); - let p1 = 0; - let p2 = 0; - if (pressureComponent) { + if(pressureComponent){ pressureComponent.status.measurement.forEach(um => { - let measurement = UnitMeasurement.any(um, user); - if ( - measurement.type === quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE && - measurement.values.length > 0 - ) { - let nodeVals = measurement.values[0].values; - if (nodeVals.length > 1) { - p1 = nodeVals[0]; - p2 = nodeVals[1]; + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + setDuctPressure(readings.values[readings.values.length -1]) + } } } - }); + }) } - setLastPressures({ p1, p2 }); }, [componentOptions, pressureKey, user]); const ambientSelector = () => { @@ -340,8 +336,10 @@ export default function GateDevice(props: Props) { : 0 } ambientTemp={lastAmbient} - innerTemps={lastTemps} - innerPressures={lastPressures} + //innerTemps={lastTemps} + //innerPressures={lastPressures} + ductTemp={ductTemp} + ductPressure={ductPressure} ambientSelector={ambientSelector} tempChainSelector={tempSelector} pressureChainSelector={pressureSelector} @@ -374,9 +372,6 @@ export default function GateDevice(props: Props) { gate={gate} display={detail} compMap={componentOptions} - // setPCAState={(state: boolean) => { - // setPCAState(state); - // }} ambient={ambientKey} pressure={pressureKey} tempChain={tempKey} diff --git a/src/gate/GateGraphs.tsx b/src/gate/GateGraphs.tsx index 3ce5932..b2ec455 100644 --- a/src/gate/GateGraphs.tsx +++ b/src/gate/GateGraphs.tsx @@ -303,6 +303,7 @@ export default function GateGraphs(props: Props) { */ const sensorGraphs = () => { const tempComp = compMap.get(tempChain) + const ambientComp = compMap.get(ambient) let graphs: JSX.Element[] = [] //pressure @@ -314,7 +315,7 @@ export default function GateGraphs(props: Props) { if(tempComp){ graphs.push( - + ) } //T/H chain @@ -323,7 +324,6 @@ export default function GateGraphs(props: Props) { graphs.push(graphCard(tempComp, tempReadings)) } //ambient - let ambientComp = compMap.get(ambient) let ambientReadings = compMeasurements.get(ambient) if (ambientComp && ambientReadings){ graphs.push(graphCard(ambientComp, ambientReadings)) @@ -345,6 +345,7 @@ export default function GateGraphs(props: Props) { const analyticGraphs = () => { let tempComp = compMap.get(tempChain) + let ambientComp = compMap.get(ambient) return ( {/* add a new chart here for the delta temp over time */} {tempComp && - + } ); diff --git a/src/gate/GateList.tsx b/src/gate/GateList.tsx index bbc5ecd..2cdec52 100644 --- a/src/gate/GateList.tsx +++ b/src/gate/GateList.tsx @@ -164,29 +164,49 @@ export default function GateList(props: Props) { // } const conditionDisplay = (gate: Gate) => { - console.log(gate) let display = "" - let deltaTemp = 0 if(gate.status.pcaState === pond.PCAState.PCA_STATE_OFF){ display = "Inactive" } else if (gate.status.pcaState === pond.PCAState.PCA_STATE_UNKNOWN){ display = "--" - } else { //the pca is currently active calulate the delta temp (T2 - T1) provided there are two temps - //loop to find the temp readings - let clone = cloneDeep(gate.status.lastTempReading) - clone.forEach(unitMeasurement => { - let um = UnitMeasurement.create(unitMeasurement, user) - if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ - if(um.values.length > 0){ //as long as there is at least on thing in the measurements - let lastReading = um.values[um.values.length-1] //there should only be one measurement in here but just make sure to get the end of the array - if(lastReading.values.length > 1){ //make sure there are at least two values in the array - console.log(lastReading.values) - deltaTemp = lastReading.values[lastReading.values.length -1] - lastReading.values[0] //subtract the first value from the last value to get the delta + } else { //the pca is currently active calulate the delta temp (outlet - ambient) + let ambient = cloneDeep(gate.status.lastAmbientReading) + let temp = cloneDeep(gate.status.lastTempReading) + + if(ambient.length > 0 && temp.length > 0){ + let ambientTemp: number | undefined + let outletTemp: number | undefined + ambient.forEach(um => { + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + ambientTemp = readings.values[readings.values.length -1] + } } } + }) + temp.forEach(um => { + if(um.type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE){ + let clone = cloneDeep(um) + let measurement = UnitMeasurement.any(clone, user) + if(measurement.values.length > 0){ + let readings = measurement.values[measurement.values.length -1] + if(readings.values.length > 0){ + outletTemp = readings.values[readings.values.length -1] + } + } + } + }) + if(ambientTemp && outletTemp){ + let deltaTemp = outletTemp - ambientTemp + display = deltaTemp.toFixed(2) + (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C") + }else{ + display = "Sensor Missing" } - }) - display = deltaTemp.toFixed(2) + (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C") + } } return ( diff --git a/src/gate/GateSVG.tsx b/src/gate/GateSVG.tsx index d71f857..33e767d 100644 --- a/src/gate/GateSVG.tsx +++ b/src/gate/GateSVG.tsx @@ -43,8 +43,10 @@ const useStyles = makeStyles(() => ({ interface Props { finalTemp: number; ambientTemp: number; - innerTemps: { t1: number; t2: number }; - innerPressures: { p1: number; p2: number }; + //innerTemps: { t1: number; t2: number }; + //innerPressures: { p1: number; p2: number }; + ductTemp?: number; + ductPressure?: number; ambientSelector: () => JSX.Element; tempChainSelector: () => JSX.Element; pressureChainSelector: () => JSX.Element; @@ -57,8 +59,10 @@ export default function GateSVG(props: Props) { const { finalTemp, ambientTemp, - innerTemps, - innerPressures, + //innerTemps, + //innerPressures, + ductTemp, + ductPressure, ambientSelector, tempChainSelector, pressureChainSelector, @@ -277,12 +281,15 @@ export default function GateSVG(props: Props) { values.push( - T1: {innerTemps.t1} + {/* T1: {innerTemps.t1} {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C"} - , T2: {innerTemps.t2} + , T1: {innerTemps.t2} {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT + ? "°F" + : "°C"} */} + Temperature: {ductTemp ?? "N/A"} {user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "°F" : "°C"} @@ -292,12 +299,15 @@ export default function GateSVG(props: Props) { values.push( - P1: {innerPressures.p1}{" "} + {/* P1: {innerPressures.p1}{" "} {user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? "iwg" : "kPa"} , P2: {innerPressures.p2}{" "} {user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER + ? "iwg" + : "kPa"} */} + Pressure: {ductPressure ?? "N/A"} {user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? "iwg" : "kPa"} diff --git a/src/providers/pond/gateAPI.tsx b/src/providers/pond/gateAPI.tsx index ac63bb0..5b07606 100644 --- a/src/providers/pond/gateAPI.tsx +++ b/src/providers/pond/gateAPI.tsx @@ -138,7 +138,6 @@ export default function GateProvider(props: PropsWithChildren) { return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.ListGatesResponse.fromObject(resp.data) - console.log(resp) return resolve(resp) }).catch(err => { return reject(err) From 74e737179281641e728e67aa15aa5dc5cd74c9d7 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Thu, 26 Feb 2026 10:24:57 -0600 Subject: [PATCH 05/14] changed the localhost whitelabel to use the staging whitelabel --- src/services/whiteLabel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/whiteLabel.ts b/src/services/whiteLabel.ts index ca8bbf1..c9346d4 100644 --- a/src/services/whiteLabel.ts +++ b/src/services/whiteLabel.ts @@ -337,7 +337,7 @@ export function getWhitelabel(): WhiteLabel { return BXT_WHITE_LABEL; } if (window.location.origin.includes("localhost")) { - return MIPCA_WHITE_LABEL; + return STAGING_WHITELABEL; } if (window.location.origin.includes("staging") || import.meta.env.VITE_LOCAL_STAGING=='true') { return STAGING_WHITELABEL; From abc77cc27adc8e1d4ec6898fb5379e3cb6df5995 Mon Sep 17 00:00:00 2001 From: Carter Date: Thu, 26 Feb 2026 15:00:32 -0600 Subject: [PATCH 06/14] re-aligned add bin button and closing crisp chat when chat drawer is opened --- src/bin/AddBinFab.tsx | 2 +- src/chat/ChatDrawer.tsx | 10 ++++++++-- src/pages/Bin.tsx | 5 +---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/bin/AddBinFab.tsx b/src/bin/AddBinFab.tsx index 45c08c6..774785e 100644 --- a/src/bin/AddBinFab.tsx +++ b/src/bin/AddBinFab.tsx @@ -27,7 +27,7 @@ const useStyles = makeStyles((theme: Theme) => { }, position: "fixed", bottom: theme.spacing(8), //for mobile navigator - right: theme.spacing(10.25), + right: theme.spacing(2), [theme.breakpoints.up("sm")]: { bottom: theme.spacing(1.75) } diff --git a/src/chat/ChatDrawer.tsx b/src/chat/ChatDrawer.tsx index ecf83b7..8394d69 100644 --- a/src/chat/ChatDrawer.tsx +++ b/src/chat/ChatDrawer.tsx @@ -1,5 +1,5 @@ import { ChevronRight } from "@mui/icons-material"; -import { Avatar, Box, Divider, Drawer, IconButton, Theme, Tooltip, Typography } from "@mui/material"; +import { Avatar, Box, Drawer, IconButton, Theme, Tooltip, Typography } from "@mui/material"; import { makeStyles } from "@mui/styles"; import { useMobile } from "hooks"; import { Team } from "models"; @@ -7,7 +7,7 @@ import Chat from "./Chat"; import { pond } from "protobuf-ts/pond"; import { useEffect, useState } from "react"; import { useTeamAPI } from "providers"; -import { openCrispChat } from './CrispChat'; +import { closeCrispChat, openCrispChat } from './CrispChat'; import RobotIcon from "products/CommonIcons/robotIcon"; const useStyles = makeStyles((theme: Theme) => ({ @@ -67,6 +67,12 @@ export function ChatDrawer(props: Props) { onClose() } + useEffect(() => { + if (open) { + closeCrispChat() + } + }, [open]) + return ( Date: Fri, 27 Feb 2026 12:56:46 -0600 Subject: [PATCH 07/14] added entries to the grain form for imperial fields, filling the imperial will calculate its metric counterpart --- package-lock.json | 4 +- package.json | 2 +- src/grain/CustomGrainForm.tsx | 83 ++++++++++++++++++++++++++++++----- src/services/whiteLabel.ts | 2 +- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc86b31..2a91371 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#imperial_bushels", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", @@ -11967,7 +11967,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#ab9e8d4fd38f9af7802398403d38fddda5eb01da", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#7649feaa2222b555fe15fe9e4a19244c23f0e442", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/package.json b/package.json index 90ac71b..b535c88 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "mui-tel-input": "^7.0.0", "notistack": "^3.0.1", "openweathermap-ts": "^1.2.10", - "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#staging", + "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#imperial_bushels", "query-string": "^9.2.1", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", diff --git a/src/grain/CustomGrainForm.tsx b/src/grain/CustomGrainForm.tsx index 905931c..23e4f71 100644 --- a/src/grain/CustomGrainForm.tsx +++ b/src/grain/CustomGrainForm.tsx @@ -18,7 +18,9 @@ export default function CustomGrainForm(props: Props) { const [constantB, setConstantB] = useState("0") const [constantC, setConstantC] = useState("0") const [kgPerBushel, setKgPerBushel] = useState("0") - const [bushelsPerTonne, setBushelsPerTonne] = useState("0") + const [bushelsPerTonne, setBushelsPerTonne] = useState("0")//this is metric + const [lbPerBushel, setLbPerBushel] = useState("0") + const [bushelsPerTon, setBushelsPerTon] = useState("0")//this is imperial const valid = useRef(false) useEffect(()=>{ @@ -35,14 +37,25 @@ export default function CustomGrainForm(props: Props) { } },[grainSettings]) - const validate = (name: string, group: string, constA: string, constB: string, constC: string, kgPerBushel: string, bushelsPerTonne: string) => { + const validate = ( + name: string, + group: string, + constA: string, + constB: string, + constC: string, + kgPerBushel: string, + bushelsPerTonne: string, + lbPerBushel: string, + bushelsPerTon: string) => { if (name === "") return false if (group === "") return false if (isNaN(parseFloat(constA))) return false if (isNaN(parseFloat(constB))) return false if (isNaN(parseFloat(constC))) return false if (isNaN(parseFloat(kgPerBushel))) return false + if (isNaN(parseFloat(lbPerBushel))) return false if (isNaN(parseFloat(bushelsPerTonne))) return false + if (isNaN(parseFloat(bushelsPerTon))) return false return true } @@ -64,7 +77,7 @@ export default function CustomGrainForm(props: Props) { setName(name) let settings = cloneDeep(newGrainSettings) settings.name = name - valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, bushelsPerTonne) + valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, bushelsPerTonne, lbPerBushel, bushelsPerTon) settingsChanged(settings) }}/> @@ -121,7 +134,7 @@ export default function CustomGrainForm(props: Props) { onChange={(e) => { let val = e.target.value setConstantA(val) - valid.current = validate(name, group, val, constantB, constantC, kgPerBushel, bushelsPerTonne) + valid.current = validate(name, group, val, constantB, constantC, kgPerBushel, bushelsPerTonne, lbPerBushel, bushelsPerTon) let settings = cloneDeep(newGrainSettings) if(!isNaN(parseFloat(val))){ settings.a = parseFloat(val) @@ -139,7 +152,7 @@ export default function CustomGrainForm(props: Props) { onChange={(e) => { let val = e.target.value setConstantB(val) - valid.current = validate(name, group, constantA, val, constantC, kgPerBushel, bushelsPerTonne) + valid.current = validate(name, group, constantA, val, constantC, kgPerBushel, bushelsPerTonne, lbPerBushel, bushelsPerTon) let settings = cloneDeep(newGrainSettings) if(!isNaN(parseFloat(val))){ settings.b = parseFloat(val) @@ -157,7 +170,7 @@ export default function CustomGrainForm(props: Props) { onChange={(e) => { let val = e.target.value setConstantC(val) - valid.current = validate(name, group, constantA, constantB, val, kgPerBushel, bushelsPerTonne) + valid.current = validate(name, group, constantA, constantB, val, kgPerBushel, bushelsPerTonne, lbPerBushel, bushelsPerTon) let settings = cloneDeep(newGrainSettings) if(!isNaN(parseFloat(val))){ settings.c = parseFloat(val) @@ -175,14 +188,40 @@ export default function CustomGrainForm(props: Props) { onChange={(e) => { let val = e.target.value setKgPerBushel(val) - valid.current = validate(name, group, constantA, constantB, constantC, val, bushelsPerTonne) + valid.current = validate(name, group, constantA, constantB, constantC, val, bushelsPerTonne, lbPerBushel, bushelsPerTon) let settings = cloneDeep(newGrainSettings) if(!isNaN(parseFloat(val))){ settings.kgPerBushel = parseFloat(val) + //then calculate the kilograms to pounds and update it as well + let newLb = Math.round((parseFloat(val)*2.205)*100)/100 + settings.poundsPerBushel = newLb + setLbPerBushel(newLb.toString()) } settingsChanged(settings) }} label="kg per Bushel"/> + { + let val = e.target.value + setLbPerBushel(val) + valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, bushelsPerTonne, val, bushelsPerTon) + let settings = cloneDeep(newGrainSettings) + if(!isNaN(parseFloat(val))){ + settings.poundsPerBushel = parseFloat(val) + //then calculate the pounds to kilograms and update it as well + let newKg = Math.round((parseFloat(val)/2.205)*100)/100 + settings.kgPerBushel = newKg + setKgPerBushel(newKg.toString()) + } + settingsChanged(settings) + }} + label="lb per Bushel"/> { let val = e.target.value setBushelsPerTonne(val) - valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, val) + valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, val, lbPerBushel, bushelsPerTon) let settings = cloneDeep(newGrainSettings) if(!isNaN(parseFloat(val))){ settings.bushelsPerTonne = parseFloat(val) + let newTon = Math.round((parseFloat(val)*1.102)*100)/100 + settings.bushelsPerTon = newTon + setBushelsPerTon(newTon.toString()) } settingsChanged(settings) }} label="Bushels per Tonne"/> + { + let val = e.target.value + setBushelsPerTon(val) + valid.current = validate(name, group, constantA, constantB, constantC, kgPerBushel, bushelsPerTonne, lbPerBushel, val) + let settings = cloneDeep(newGrainSettings) + if(!isNaN(parseFloat(val))){ + settings.bushelsPerTon = parseFloat(val) + let newTonne = Math.round((parseFloat(val)/1.102)*100)/100 + settings.bushelsPerTonne = newTonne + setBushelsPerTonne(newTonne.toString()) + } + settingsChanged(settings) + }} + label="Bushels per Ton"/> ) } diff --git a/src/services/whiteLabel.ts b/src/services/whiteLabel.ts index c9346d4..29c4427 100644 --- a/src/services/whiteLabel.ts +++ b/src/services/whiteLabel.ts @@ -337,7 +337,7 @@ export function getWhitelabel(): WhiteLabel { return BXT_WHITE_LABEL; } if (window.location.origin.includes("localhost")) { - return STAGING_WHITELABEL; + return STREAMLINE_WHITE_LABEL; } if (window.location.origin.includes("staging") || import.meta.env.VITE_LOCAL_STAGING=='true') { return STAGING_WHITELABEL; From 29d9e7377ca6b37e5e55f2a425252567bd9d8ec3 Mon Sep 17 00:00:00 2001 From: csawatzky Date: Wed, 4 Mar 2026 12:51:10 -0600 Subject: [PATCH 08/14] working on US ton stuff --- package-lock.json | 2 +- src/bin/BinCardV2.tsx | 12 +- src/bin/BinSettings.tsx | 210 +++++++++++++++++--- src/bin/BinTransactions.tsx | 25 ++- src/bin/BinVisualizerV2.tsx | 46 +++-- src/bin/BinyardDisplay.tsx | 30 ++- src/bin/graphs/BinLevelOverTime.tsx | 25 ++- src/field/FieldSettings.tsx | 3 + src/grain/CustomGrainForm.tsx | 8 +- src/grain/GrainDescriber.ts | 109 ++++++---- src/grain/GrainTransaction.tsx | 47 ++++- src/grainBag/grainBagSettings.tsx | 1 + src/models/Bin.ts | 31 ++- src/pages/grainBag.tsx | 1 - src/providers/pond/grainBagAPI.tsx | 2 +- src/transactions/transactionDataDisplay.tsx | 1 + src/user/UserSettings.tsx | 10 +- src/utils/units.ts | 28 ++- 18 files changed, 474 insertions(+), 117 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a91371..3c23e65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11967,7 +11967,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#7649feaa2222b555fe15fe9e4a19244c23f0e442", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#8281f64aa2c8db09a73f5a2eb8a1b97a63bcc653", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/src/bin/BinCardV2.tsx b/src/bin/BinCardV2.tsx index f39c553..f8fa5fb 100644 --- a/src/bin/BinCardV2.tsx +++ b/src/bin/BinCardV2.tsx @@ -443,14 +443,24 @@ export default function BinCard(props: Props) { " L" ); } - if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && bin.bushelsPerTonne() > 1) { + + if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && bin.bushelsPerTonne() > 1) { return ( bin.grainTonnes().toLocaleString() + " mT " + bin.fillPercent() + "%" ); + } else if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && bin.bushelsPerTon() > 1) { + return ( + bin.grainTons().toLocaleString() + + " t " + + bin.fillPercent() + + "%" + ); } + + return ( current.toLocaleString() + "/" + diff --git a/src/bin/BinSettings.tsx b/src/bin/BinSettings.tsx index 4192974..df9a6fe 100644 --- a/src/bin/BinSettings.tsx +++ b/src/bin/BinSettings.tsx @@ -126,14 +126,13 @@ interface BinFormExtension { hopperHeight: string; diameter: string; grainBushels: string; - grainTonnes: string; + grainWeight: string; grainType: Option | null; grainUse: Option | null; grainSubtype: string; customTypeName: string; highTemp: number; lowTemp: number; - bushelsPerTonne: string; tempTarget: number; } @@ -160,14 +159,13 @@ export default function BinSettings(props: Props) { hopperHeight: "", diameter: "", grainBushels: "", - grainTonnes: "", + grainWeight: "", grainType: null, grainUse: null, grainSubtype: "", customTypeName: "", highTemp: 20, lowTemp: 10, - bushelsPerTonne: "", tempTarget: 15 }); const [initialized, setInitialized] = useState(false); @@ -194,6 +192,7 @@ export default function BinSettings(props: Props) { const [isCustomBin, setIsCustomBin] = useState(false); const [binModelOps, setBinModelOptions] = useState([]); const [selectedBinModel, setSelectedBinModel] = useState