finished hooking up the inventory adjustment slider when using manual control on the bin page

This commit is contained in:
csawatzky 2026-06-03 12:36:08 -06:00
parent b8a49197cb
commit 13c15ec195
6 changed files with 169 additions and 26 deletions

View file

@ -207,7 +207,7 @@ export default function BinTableExpanded(props: Props) {
<Typography sx={{fontWeight: 600, fontSize: 10}}>{user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "+20 °F" : "+10 °C"} above target</Typography>
</Box>
</Box>
<Box sx={{ display: "flex", flexDirection: "row", overflowX: "auto", gap: 2}}>
<Box sx={{ display: "flex", flexDirection: "row", overflowX: "auto", gap: 2, paddingBottom: 2}}>
{cables.map((cable, i) => (
<Box key={i} sx={{ flexShrink: 0 }}>
<Typography className={classes.headerCellStyle} sx={{ textAlign: "center" }}>

View file

@ -1,4 +1,4 @@
import { Box, Card, DialogContent, DialogTitle, Grid2, IconButton, LinearProgress, Switch, Typography } from "@mui/material";
import { Box, Card, DialogContent, DialogTitle, Grid2, IconButton, LinearProgress, Slider, Switch, Tooltip, Typography } from "@mui/material";
import { useMobile } from "hooks";
import { Bin, Component, Device } from "models";
import Bin3dVisualizer from "./components/bin3dVisualizer";
@ -13,9 +13,12 @@ import BinDetails from "./components/binDetails";
import { Plenum } from "models/Plenum";
import BinSensorsDisplay from "../binSensorsDisplay";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import SearchSelect from "common/SearchSelect";
import ChangeGrainDialog from "grain/ChangeGrainDialog";
import { useGlobalState } from "providers";
import GrainTransaction from "grain/GrainTransaction";
import { cloneDeep } from "lodash";
interface Props {
bin: Bin
@ -32,13 +35,33 @@ interface Props {
React.SetStateAction<Map<string, pond.BinComponentPreferences> | undefined>
>;
updateBinCallback?: (bin: Bin) => void
setBin?: React.Dispatch<React.SetStateAction<Bin>>;
}
export default function BinSummary(props: Props){
const {bin, devices, fans, heaters, permissions, componentDevices, componentMap, binPrefs, updateBinCallback, cables = [], plenums = [], setPreferences} = props
const {bin, devices, fans, heaters, permissions, componentDevices, componentMap, binPrefs, updateBinCallback, cables = [], plenums = [], setPreferences, setBin} = props
//const [currentDevice, setCurrentDevice] = useState<Device | undefined>(undefined)
const isMobile = useMobile()
const [openGrainDialog, setOpenGrainDialog] = useState(false)
const [manualFillPercent, setManualFillPercent] = useState(0)
const [manualBushels, setManualBushels] = useState(0)
const [{user}] = useGlobalState()
const fillCapRef = useRef<HTMLSpanElement>(null);
const [fillCapWidth, setFillCapWidth] = useState<number | undefined>(undefined);
const [openNewTransaction, setOpenNewTransaction] = useState(false)
useEffect(() => {
if (fillCapRef.current) {
// measure the max possible string width once
fillCapRef.current.textContent = `${bin.grainCapacity(user)}/${bin.grainCapacity(user)}`;
setFillCapWidth(fillCapRef.current.offsetWidth);
}
}, [bin, user]);
useEffect(()=>{
setManualFillPercent(bin.fillPercent())
setManualBushels(bin.bushels())
},[bin])
// useEffect(() => {
// if(devices.length > 0){
@ -76,6 +99,18 @@ export default function BinSummary(props: Props){
)
}
const currentFill = () => {
if(bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL){
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && bin.bushelsPerTonne() > 1){
return Math.round((manualBushels / bin.bushelsPerTonne())*10)/10
}else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && bin.bushelsPerTonne() > 1){
return Math.round((manualBushels / (bin.bushelsPerTonne() * 0.907))*10)/10
}
return Math.round(manualBushels)
}
return bin.grainInventory(user)
}
const inventorySummary = () => {
return (
<Card raised sx={{ marginBottom: 2 }}>
@ -111,14 +146,58 @@ export default function BinSummary(props: Props){
</Box>
{/* Bin Fill */}
<Box sx={{ flexGrow: 1, maxWidth: 400, mx: 4 }}>
<Box sx={{ flexGrow: 1, maxWidth: 450, mx: 4 }}>
<Typography variant="caption" sx={{ color: grey[500] }}>
Bin Fill
</Typography>
<Box display="flex" alignItems="center" gap={1} sx={{ mt: 0.5 }}>
<Typography variant="body2" noWrap>
{bin.binFillCap()}
{/* hidden measuring span, renders off-screen */}
<span
ref={fillCapRef}
style={{
position: "absolute",
visibility: "hidden",
whiteSpace: "nowrap",
fontSize: "0.875rem", // match body2
}}
/>
<Typography variant="body2" noWrap sx={{ width: fillCapWidth, flexShrink: 0 }}>
{currentFill()}/{bin.grainCapacity(user)}
</Typography>
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL ?
<Slider
value={manualFillPercent}
onChangeCommitted={() => {
setOpenNewTransaction(true)
}}
onChange={(_, val) => {
let fillPercent = val as number
setManualFillPercent(fillPercent)
let newBushels = (fillPercent/100) * bin.bushelCapacity()
setManualBushels(newBushels)
//this does casue the 3d bin to fill as you drag it but it throws off the transaction and the reset if you cancel
// let clone = cloneDeep(bin)
// let inventory = clone.settings.inventory ?? pond.BinInventory.create()
// inventory.grainBushels = newBushels
// if(setBin){
// setBin(clone)
// }
}}
min={0}
max={100}
sx={{
flexGrow: 1,
color: "#4caf50",
"& .MuiSlider-thumb": {
width: 16,
height: 16,
},
"& .MuiSlider-rail": {
backgroundColor: "rgba(255,255,255,0.1)",
},
}}
/>
:
<LinearProgress
variant="determinate"
value={bin.fillPercent()}
@ -133,6 +212,10 @@ export default function BinSummary(props: Props){
},
}}
/>
}
<Typography variant="body2" noWrap sx={{ width: 100 }}>
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL ? manualFillPercent : bin.fillPercent()}%
</Typography>
</Box>
</Box>
@ -141,6 +224,7 @@ export default function BinSummary(props: Props){
<Typography variant="caption" sx={{ color: grey[500] }}>
Last Updated
</Typography>
<Tooltip title="Bin Status will update whenever its settings change or a device with a connected sensor checks in">
<Box display="flex" alignItems="center" gap={2} sx={{ mt: 0.5 }}>
<Box display="flex" alignItems="center" gap={1}>
<AccessTime sx={{ fontSize: 16, color: grey[400] }} />
@ -150,6 +234,7 @@ export default function BinSummary(props: Props){
</Box>
{activity(bin.status.timestamp)}
</Box>
</Tooltip>
</Box>
</Box>
</Card>
@ -158,6 +243,27 @@ export default function BinSummary(props: Props){
return (
<Box padding={2}>
<GrainTransaction
open={openNewTransaction}
mainObject={bin}
grainAdjustment={manualBushels - bin.bushels()}
close={(confirmed) => {
//this resets all of the state variables used
setOpenNewTransaction(false);
if(!confirmed){
setManualBushels(bin.bushels())
setManualFillPercent(bin.fillPercent())
}else{
let clone = cloneDeep(bin)
let inventory = clone.settings.inventory ?? pond.BinInventory.create()
inventory.grainBushels = manualBushels
if(setBin){
setBin(clone)
}
}
}}
/>
<ChangeGrainDialog bin={bin} open={openGrainDialog} closeDialog={()=>setOpenGrainDialog(false)} permissions={permissions} updateBin={updateBinCallback}/>
{inventorySummary()}
<Grid2 container spacing={2}>

View file

@ -300,7 +300,8 @@ export default function BinTableView(props: Props) {
}}>
Show All Cables
</Button>
<Tooltip title="testing">
<Tooltip title="This table shows the bins levels using nodes that are in the grain averaged together. Data comes from the bins status.
Use the button to see all of the cables individually.">
<Info />
</Tooltip>
</Box>

View file

@ -37,7 +37,7 @@ interface Props {
asDestination?: boolean;
restrictMatching?: boolean;
open: boolean;
close: () => void;
close: (confirmed?: boolean) => void;
callback?: (newTransaction?: pond.Transaction) => void;
allowAttachmentUploads?: boolean;
}
@ -82,7 +82,7 @@ export default function GrainTransaction(props: Props) {
const [filePermission, setFilePermission] = useState(false);
const closeDialogs = (confirmed: boolean, newTransaction?: pond.Transaction) => {
close();
close(confirmed);
setGrainChangeVal("0");
setGrainEntry("0");
setIsObject("");

View file

@ -162,6 +162,11 @@ export class Bin {
return colour;
}
/**
* this function returns the number of bushels in the bin, if using the automatic lidar or libracart to control inventory it will use the bushels in status
* otherwise will use the bushels in the inventory found in settings
* @returns (number) the current bushels in the bin
*/
public bushels(): number {
let control = this.settings.inventory?.inventoryControl;
let bushels = this.settings.inventory?.grainBushels || 0
@ -172,6 +177,14 @@ export class Bin {
return bushels
}
/**
* this function returns the bushelCapacity in the bins specs as it is stored or 0 if the specs are undefined
* @returns (number) the capacity of the bin in bushels
*/
public bushelCapacity(): number {
return this.settings.specs?.bushelCapacity ?? 0
}
public fillPercent(): number {
let fill = 0;
if (this.settings.inventory && this.settings.specs) {
@ -281,6 +294,12 @@ export class Bin {
return bpt;
}
/**
* this function returns a bins stored inventory in the unit of the passed in user,
* it can return it in bushels, US tons, or metric Tonnes
* @param user the user object
* @returns (number) bins current stored inventory
*/
public grainInventory(user: User): number {
let grain = this.bushels()
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.bushelsPerTonne() > 1){
@ -291,6 +310,22 @@ export class Bin {
return Math.round(grain*100)/100
}
/**
* this function returns a bins capacity in the unit of the passed in user,
* it can return it in bushels, US tons, or metric Tonnes
* @param user the user object
* @returns (number) bins max capacity
*/
public grainCapacity(user: User): number {
let capacity = this.bushelCapacity()
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.bushelsPerTonne() > 1){
capacity = capacity / this.bushelsPerTonne()
}else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && this.bushelsPerTonne() > 1){
capacity = capacity / (this.bushelsPerTonne() * 0.907)
}
return Math.round(capacity*100)/100
}
/**
* gets the enum value for the inventory control in the bins inventory
*/

View file

@ -553,6 +553,7 @@ export default function BinV2(){
openSnack("Bin has Been Updated")
})
}}
setBin={setBin}
/>
{/* render drawers */}
{noteDrawer()}