finished hooking up the inventory adjustment slider when using manual control on the bin page
This commit is contained in:
parent
b8a49197cb
commit
13c15ec195
6 changed files with 169 additions and 26 deletions
|
|
@ -207,16 +207,16 @@ 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>
|
<Typography sx={{fontWeight: 600, fontSize: 10}}>{user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "+20 °F" : "+10 °C"} above target</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</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) => (
|
{cables.map((cable, i) => (
|
||||||
<Box key={i} sx={{ flexShrink: 0 }}>
|
<Box key={i} sx={{ flexShrink: 0 }}>
|
||||||
<Typography className={classes.headerCellStyle} sx={{ textAlign: "center" }}>
|
<Typography className={classes.headerCellStyle} sx={{ textAlign: "center" }}>
|
||||||
{cable.name}
|
{cable.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
{cableTable(cable)}
|
{cableTable(cable)}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -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 { useMobile } from "hooks";
|
||||||
import { Bin, Component, Device } from "models";
|
import { Bin, Component, Device } from "models";
|
||||||
import Bin3dVisualizer from "./components/bin3dVisualizer";
|
import Bin3dVisualizer from "./components/bin3dVisualizer";
|
||||||
|
|
@ -13,9 +13,12 @@ import BinDetails from "./components/binDetails";
|
||||||
import { Plenum } from "models/Plenum";
|
import { Plenum } from "models/Plenum";
|
||||||
import BinSensorsDisplay from "../binSensorsDisplay";
|
import BinSensorsDisplay from "../binSensorsDisplay";
|
||||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import SearchSelect from "common/SearchSelect";
|
import SearchSelect from "common/SearchSelect";
|
||||||
import ChangeGrainDialog from "grain/ChangeGrainDialog";
|
import ChangeGrainDialog from "grain/ChangeGrainDialog";
|
||||||
|
import { useGlobalState } from "providers";
|
||||||
|
import GrainTransaction from "grain/GrainTransaction";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bin: Bin
|
bin: Bin
|
||||||
|
|
@ -32,13 +35,33 @@ interface Props {
|
||||||
React.SetStateAction<Map<string, pond.BinComponentPreferences> | undefined>
|
React.SetStateAction<Map<string, pond.BinComponentPreferences> | undefined>
|
||||||
>;
|
>;
|
||||||
updateBinCallback?: (bin: Bin) => void
|
updateBinCallback?: (bin: Bin) => void
|
||||||
|
setBin?: React.Dispatch<React.SetStateAction<Bin>>;
|
||||||
|
|
||||||
}
|
}
|
||||||
export default function BinSummary(props: Props){
|
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 [currentDevice, setCurrentDevice] = useState<Device | undefined>(undefined)
|
||||||
const isMobile = useMobile()
|
const isMobile = useMobile()
|
||||||
const [openGrainDialog, setOpenGrainDialog] = useState(false)
|
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(() => {
|
// useEffect(() => {
|
||||||
// if(devices.length > 0){
|
// 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 = () => {
|
const inventorySummary = () => {
|
||||||
return (
|
return (
|
||||||
<Card raised sx={{ marginBottom: 2 }}>
|
<Card raised sx={{ marginBottom: 2 }}>
|
||||||
|
|
@ -111,14 +146,58 @@ export default function BinSummary(props: Props){
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Bin Fill */}
|
{/* 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] }}>
|
<Typography variant="caption" sx={{ color: grey[500] }}>
|
||||||
Bin Fill
|
Bin Fill
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box display="flex" alignItems="center" gap={1} sx={{ mt: 0.5 }}>
|
<Box display="flex" alignItems="center" gap={1} sx={{ mt: 0.5 }}>
|
||||||
<Typography variant="body2" noWrap>
|
{/* hidden measuring span, renders off-screen */}
|
||||||
{bin.binFillCap()}
|
<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>
|
</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
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={bin.fillPercent()}
|
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>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
@ -141,15 +224,17 @@ export default function BinSummary(props: Props){
|
||||||
<Typography variant="caption" sx={{ color: grey[500] }}>
|
<Typography variant="caption" sx={{ color: grey[500] }}>
|
||||||
Last Updated
|
Last Updated
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box display="flex" alignItems="center" gap={2} sx={{ mt: 0.5 }}>
|
<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={1}>
|
<Box display="flex" alignItems="center" gap={2} sx={{ mt: 0.5 }}>
|
||||||
<AccessTime sx={{ fontSize: 16, color: grey[400] }} />
|
<Box display="flex" alignItems="center" gap={1}>
|
||||||
<Typography variant="body2" sx={{ color: grey[300] }}>
|
<AccessTime sx={{ fontSize: 16, color: grey[400] }} />
|
||||||
{moment(bin.status.timestamp).fromNow()}
|
<Typography variant="body2" sx={{ color: grey[300] }}>
|
||||||
</Typography>
|
{moment(bin.status.timestamp).fromNow()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
{activity(bin.status.timestamp)}
|
||||||
</Box>
|
</Box>
|
||||||
{activity(bin.status.timestamp)}
|
</Tooltip>
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -158,6 +243,27 @@ export default function BinSummary(props: Props){
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box padding={2}>
|
<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}/>
|
<ChangeGrainDialog bin={bin} open={openGrainDialog} closeDialog={()=>setOpenGrainDialog(false)} permissions={permissions} updateBin={updateBinCallback}/>
|
||||||
{inventorySummary()}
|
{inventorySummary()}
|
||||||
<Grid2 container spacing={2}>
|
<Grid2 container spacing={2}>
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,8 @@ export default function BinTableView(props: Props) {
|
||||||
}}>
|
}}>
|
||||||
Show All Cables
|
Show All Cables
|
||||||
</Button>
|
</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 />
|
<Info />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ interface Props {
|
||||||
asDestination?: boolean;
|
asDestination?: boolean;
|
||||||
restrictMatching?: boolean;
|
restrictMatching?: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
close: () => void;
|
close: (confirmed?: boolean) => void;
|
||||||
callback?: (newTransaction?: pond.Transaction) => void;
|
callback?: (newTransaction?: pond.Transaction) => void;
|
||||||
allowAttachmentUploads?: boolean;
|
allowAttachmentUploads?: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ export default function GrainTransaction(props: Props) {
|
||||||
const [filePermission, setFilePermission] = useState(false);
|
const [filePermission, setFilePermission] = useState(false);
|
||||||
|
|
||||||
const closeDialogs = (confirmed: boolean, newTransaction?: pond.Transaction) => {
|
const closeDialogs = (confirmed: boolean, newTransaction?: pond.Transaction) => {
|
||||||
close();
|
close(confirmed);
|
||||||
setGrainChangeVal("0");
|
setGrainChangeVal("0");
|
||||||
setGrainEntry("0");
|
setGrainEntry("0");
|
||||||
setIsObject("");
|
setIsObject("");
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,11 @@ export class Bin {
|
||||||
return colour;
|
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 {
|
public bushels(): number {
|
||||||
let control = this.settings.inventory?.inventoryControl;
|
let control = this.settings.inventory?.inventoryControl;
|
||||||
let bushels = this.settings.inventory?.grainBushels || 0
|
let bushels = this.settings.inventory?.grainBushels || 0
|
||||||
|
|
@ -172,6 +177,14 @@ export class Bin {
|
||||||
return bushels
|
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 {
|
public fillPercent(): number {
|
||||||
let fill = 0;
|
let fill = 0;
|
||||||
if (this.settings.inventory && this.settings.specs) {
|
if (this.settings.inventory && this.settings.specs) {
|
||||||
|
|
@ -281,6 +294,12 @@ export class Bin {
|
||||||
return bpt;
|
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 {
|
public grainInventory(user: User): number {
|
||||||
let grain = this.bushels()
|
let grain = this.bushels()
|
||||||
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.bushelsPerTonne() > 1){
|
if(user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && this.bushelsPerTonne() > 1){
|
||||||
|
|
@ -291,6 +310,22 @@ export class Bin {
|
||||||
return Math.round(grain*100)/100
|
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
|
* gets the enum value for the inventory control in the bins inventory
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -553,6 +553,7 @@ export default function BinV2(){
|
||||||
openSnack("Bin has Been Updated")
|
openSnack("Bin has Been Updated")
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
setBin={setBin}
|
||||||
/>
|
/>
|
||||||
{/* render drawers */}
|
{/* render drawers */}
|
||||||
{noteDrawer()}
|
{noteDrawer()}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue