created a change grain dialog component and made the grain display a 'button' to be able to change the grain without going into the settings
This commit is contained in:
parent
dc52ef77a7
commit
b8a49197cb
3 changed files with 263 additions and 107 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Card, Grid2, LinearProgress, Typography } from "@mui/material";
|
import { Box, Card, DialogContent, DialogTitle, Grid2, IconButton, LinearProgress, Switch, 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";
|
||||||
|
|
@ -12,6 +12,10 @@ import { pond } from "protobuf-ts/pond";
|
||||||
import BinDetails from "./components/binDetails";
|
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 { useState } from "react";
|
||||||
|
import SearchSelect from "common/SearchSelect";
|
||||||
|
import ChangeGrainDialog from "grain/ChangeGrainDialog";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bin: Bin
|
bin: Bin
|
||||||
|
|
@ -34,6 +38,7 @@ 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} = 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)
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if(devices.length > 0){
|
// if(devices.length > 0){
|
||||||
|
|
@ -71,79 +76,90 @@ export default function BinSummary(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inventorySummary = () => {
|
||||||
|
return (
|
||||||
|
<Card raised sx={{ marginBottom: 2 }}>
|
||||||
|
<Box display="flex" justifyContent="space-between" alignItems="flex-start" sx={{ padding: 2 }}>
|
||||||
|
|
||||||
|
{/* Grain Type */}
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" sx={{ color: grey[500] }}>
|
||||||
|
Grain Type
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
gap={0.5}
|
||||||
|
onClick={() => setOpenGrainDialog(true)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
mt: 0.5,
|
||||||
|
px: 1,
|
||||||
|
py: 0.5,
|
||||||
|
borderRadius: 1,
|
||||||
|
border: "1px solid",
|
||||||
|
borderColor: grey[700],
|
||||||
|
"&:hover": {
|
||||||
|
borderColor: grey[500],
|
||||||
|
backgroundColor: "rgba(255,255,255,0.05)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spa sx={{ color: "#4caf50", fontSize: 18 }} />
|
||||||
|
<Typography variant="body1">{bin.grainName()}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Bin Fill */}
|
||||||
|
<Box sx={{ flexGrow: 1, maxWidth: 400, 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()}
|
||||||
|
</Typography>
|
||||||
|
<LinearProgress
|
||||||
|
variant="determinate"
|
||||||
|
value={bin.fillPercent()}
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: "rgba(255,255,255,0.1)",
|
||||||
|
"& .MuiLinearProgress-bar": {
|
||||||
|
backgroundColor: "#4caf50",
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Last Updated */}
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" sx={{ color: grey[500] }}>
|
||||||
|
Last Updated
|
||||||
|
</Typography>
|
||||||
|
<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] }} />
|
||||||
|
<Typography variant="body2" sx={{ color: grey[300] }}>
|
||||||
|
{moment(bin.status.timestamp).fromNow()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
{activity(bin.status.timestamp)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box padding={2}>
|
<Box padding={2}>
|
||||||
<Card raised sx={{marginBottom: 2}}>
|
<ChangeGrainDialog bin={bin} open={openGrainDialog} closeDialog={()=>setOpenGrainDialog(false)} permissions={permissions} updateBin={updateBinCallback}/>
|
||||||
<Grid2 container justifyContent={"space-between"} sx={{padding:2}}>
|
{inventorySummary()}
|
||||||
<Grid2>
|
|
||||||
<Typography variant="caption" sx={{ color: grey[500] }}>
|
|
||||||
Grain Type
|
|
||||||
</Typography>
|
|
||||||
<Box display="flex" alignItems="center">
|
|
||||||
<Spa sx={{ color: "#4caf50", fontSize: 18 }} />
|
|
||||||
{/* <GrassIcon sx={{ color: "#4caf50", fontSize: 18 }} /> */}
|
|
||||||
<Typography variant="body1">{bin.grainName()}</Typography>
|
|
||||||
</Box>
|
|
||||||
</Grid2>
|
|
||||||
<Grid2 sx={{ flexGrow: 1, maxWidth: 400 }}>
|
|
||||||
<Typography variant="caption" sx={{ color: grey[500] }}>
|
|
||||||
Bin Fill
|
|
||||||
</Typography>
|
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
|
||||||
<Typography variant="body2" noWrap>
|
|
||||||
{bin.binFillCap()}
|
|
||||||
</Typography>
|
|
||||||
<LinearProgress
|
|
||||||
variant="determinate"
|
|
||||||
value={bin.fillPercent()} // adjust to your data
|
|
||||||
sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
backgroundColor: "rgba(255,255,255,0.1)",
|
|
||||||
"& .MuiLinearProgress-bar": {
|
|
||||||
backgroundColor: "#4caf50",
|
|
||||||
borderRadius: 4,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Grid2>
|
|
||||||
<Grid2>
|
|
||||||
<Typography variant="caption" sx={{ color: grey[500] }}>
|
|
||||||
Last Updated
|
|
||||||
</Typography>
|
|
||||||
{/* this would be to show the last time the device checked in */}
|
|
||||||
{/* {currentDevice ?
|
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
|
||||||
<AccessTime sx={{ fontSize: 16, color: grey[400] }} />
|
|
||||||
<Typography variant="body2" sx={{ color: grey[300] }}>
|
|
||||||
{moment(currentDevice.status.lastActive).fromNow()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
{activity(currentDevice.status.lastActive)}
|
|
||||||
</Box>
|
|
||||||
:
|
|
||||||
<Box display="flex" alignItems="center">
|
|
||||||
<Typography variant="body2" sx={{ color: "grey.300" }}>
|
|
||||||
No Connected Devices Found
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
} */}
|
|
||||||
{/* this shows the last time the bins status updated */}
|
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
|
||||||
<AccessTime sx={{ fontSize: 16, color: grey[400] }} />
|
|
||||||
<Typography variant="body2" sx={{ color: grey[300] }}>
|
|
||||||
{moment(bin.status.timestamp).fromNow()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
{activity(bin.status.timestamp)}
|
|
||||||
</Box>
|
|
||||||
</Grid2>
|
|
||||||
</Grid2>
|
|
||||||
</Card>
|
|
||||||
<Grid2 container spacing={2}>
|
<Grid2 container spacing={2}>
|
||||||
<Grid2 size={isMobile ? 12 : 7}>
|
<Grid2 size={isMobile ? 12 : 7}>
|
||||||
<Card raised sx={{height: 600}}>
|
<Card raised sx={{height: 600}}>
|
||||||
|
|
|
||||||
|
|
@ -394,41 +394,6 @@ export default function BinTableView(props: Props) {
|
||||||
{levelDisplay(level)}
|
{levelDisplay(level)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TESTING</TableCell>
|
|
||||||
<TableCell>SCROLLING</TableCell>
|
|
||||||
<TableCell>ROWS</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
175
src/grain/ChangeGrainDialog.tsx
Normal file
175
src/grain/ChangeGrainDialog.tsx
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
import { Box, Button, DialogActions, DialogContent, DialogTitle, Grid2, Switch, TextField } from "@mui/material";
|
||||||
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
|
import SearchSelect, { Option } from "common/SearchSelect";
|
||||||
|
import { Bin } from "models";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import GrainDescriber, { GrainOptions, ToGrainOption } from "grain/GrainDescriber";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import CustomGrainSelector from "./CustomGrainSelector";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { useBinAPI, useSnackbar } from "providers";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
bin: Bin
|
||||||
|
open: boolean
|
||||||
|
closeDialog: () => void
|
||||||
|
permissions: pond.Permission[]
|
||||||
|
/**
|
||||||
|
* this function can be passed in if you want to just return a new bin with the changes and handle the actual update in the parent,
|
||||||
|
* it will stop the component from doing the update itself
|
||||||
|
* @param bin the bin with the new changes
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
updateBin?: (bin: Bin) => void
|
||||||
|
/**
|
||||||
|
* this function can be passed in as a callback function if the component handles the update
|
||||||
|
* @param updated whether the bin api was used to update the bin
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
updateCallback?: (bin: Bin) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ChangeGrainDialog(props: Props){
|
||||||
|
const { bin, open, permissions, closeDialog, updateBin, updateCallback } = props
|
||||||
|
const canEdit = useMemo(()=>{
|
||||||
|
return permissions ? permissions.includes(pond.Permission.PERMISSION_WRITE) : false;
|
||||||
|
},[permissions])
|
||||||
|
|
||||||
|
const [isCustom, setIsCustom] = useState(false)
|
||||||
|
const grainOptions = GrainOptions();
|
||||||
|
const [grainOption, setGrainOption] = useState<Option>();
|
||||||
|
const [newGrainType, setNewGrainType] = useState<pond.Grain>(0);
|
||||||
|
const [newGrainSettings, setNewGrainSettings] = useState<pond.GrainSettings | undefined>()
|
||||||
|
const binAPI = useBinAPI()
|
||||||
|
const {openSnack} = useSnackbar()
|
||||||
|
const [grainSubtype, setGrainSubtype] = useState("")
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
setGrainOption(ToGrainOption(bin.grain()))
|
||||||
|
setIsCustom(bin.grain() === pond.Grain.GRAIN_CUSTOM)
|
||||||
|
setGrainSubtype(bin.subtype())
|
||||||
|
setNewGrainType(bin.grain())
|
||||||
|
},[bin])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this function is where the split happens,
|
||||||
|
* if update bin is passed in it will just clone the bin, change the settings and pass the updated clone to the parent so that it can handle the api call
|
||||||
|
* otherwise the component will update the bin itself through the bin api and upon completion pass that updated bin to the parent if successfull
|
||||||
|
* in the event both functions are passed in the parent update will be given priority and the internal update will not occur
|
||||||
|
*/
|
||||||
|
const updateBinSettings = () => {
|
||||||
|
let clone = cloneDeep(bin)
|
||||||
|
let inventory = clone.settings.inventory ?? pond.BinInventory.create()
|
||||||
|
inventory.grainType = newGrainType
|
||||||
|
inventory.customGrain = newGrainSettings
|
||||||
|
inventory.grainSubtype = grainSubtype
|
||||||
|
if(updateBin){
|
||||||
|
//simply pass the clone up to the parent and it can deal with it
|
||||||
|
updateBin(clone)
|
||||||
|
}else if (updateCallback) {
|
||||||
|
//use the actual bin api to update the bin inside the component and upon completion return the updated bin
|
||||||
|
binAPI.updateBin(bin.key(), clone.settings).then(resp => {
|
||||||
|
openSnack("Updated the grain type")
|
||||||
|
updateCallback(clone)
|
||||||
|
}).catch(err => {
|
||||||
|
openSnack("There was a problem changing the grain type")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//closes the dialog
|
||||||
|
const close = () => {
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResponsiveDialog
|
||||||
|
open={open}
|
||||||
|
onClose={close}>
|
||||||
|
<DialogTitle>Change Grain Type</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Grid2 container justifyContent="space-between" alignItems="center">
|
||||||
|
<Grid2>
|
||||||
|
<Grid2 container alignItems="center">
|
||||||
|
<Grid2 >Grain</Grid2>
|
||||||
|
<Grid2 >
|
||||||
|
<Switch
|
||||||
|
color="default"
|
||||||
|
value={isCustom}
|
||||||
|
checked={isCustom}
|
||||||
|
onChange={(_, checked) => {
|
||||||
|
setIsCustom(checked)
|
||||||
|
if(checked){
|
||||||
|
setNewGrainType(pond.Grain.GRAIN_CUSTOM)
|
||||||
|
}else{
|
||||||
|
setNewGrainType(bin.grain())
|
||||||
|
setNewGrainSettings(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
name="storage"
|
||||||
|
/>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 >Custom</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
{!isCustom && (
|
||||||
|
<Box>
|
||||||
|
<SearchSelect
|
||||||
|
label="Type"
|
||||||
|
selected={grainOption}
|
||||||
|
changeSelection={option => {
|
||||||
|
let newGrainType = option
|
||||||
|
? pond.Grain[option.value as keyof typeof pond.Grain]
|
||||||
|
: pond.Grain.GRAIN_INVALID;
|
||||||
|
setGrainOption(ToGrainOption(newGrainType));
|
||||||
|
setNewGrainType(newGrainType);
|
||||||
|
//setBushPerTonne(GrainDescriber(newGrainType).bushelsPerTonne.toFixed(2));
|
||||||
|
}}
|
||||||
|
group
|
||||||
|
disabled={!canEdit}
|
||||||
|
options={grainOptions}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{isCustom ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<CustomGrainSelector initialGrain={bin.customGrain()} onGrainSettingsChange={settings => {setNewGrainSettings(settings)}}/>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<TextField
|
||||||
|
sx={{marginTop: 2}}
|
||||||
|
label="Grain Variant"
|
||||||
|
value={grainSubtype}
|
||||||
|
type="text"
|
||||||
|
onChange={event => {
|
||||||
|
setGrainSubtype(event.target.value);
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
disabled={!grainOption}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
close();
|
||||||
|
}}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
// disabled={grainErrorCheck()}
|
||||||
|
onClick={() => {
|
||||||
|
updateBinSettings();
|
||||||
|
close()
|
||||||
|
}}>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</ResponsiveDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue