more visual tweaks and design, fixed a coule bugs and removed some console logs
This commit is contained in:
parent
f04213af1e
commit
ea13f97c57
8 changed files with 257 additions and 293 deletions
|
|
@ -138,8 +138,8 @@ export default function Bin3dView(props: Props) {
|
||||||
<BinShell
|
<BinShell
|
||||||
binBodyColour="#fff"
|
binBodyColour="#fff"
|
||||||
radialSegments={20}
|
radialSegments={20}
|
||||||
binMetalness={0.5}
|
binMetalness={0.7}
|
||||||
binRoughness={0.7}
|
binRoughness={0.3}
|
||||||
binOpacity={0.2}
|
binOpacity={0.2}
|
||||||
diameter={dims.diameter}
|
diameter={dims.diameter}
|
||||||
sidewallHeight={dims.sidewallHeight}
|
sidewallHeight={dims.sidewallHeight}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { DateRange, PlayArrow, Start, Stop } from "@mui/icons-material";
|
import { DateRange, PlayArrow, Start, Stop } from "@mui/icons-material";
|
||||||
import { Box, Button, DialogActions, DialogContent, FormControl, IconButton, LinearProgress, MenuItem, Select, TextField, Typography } from "@mui/material";
|
import { Box, Button, darken, DialogActions, DialogContent, FormControl, IconButton, LinearProgress, MenuItem, Select, TextField, Typography } from "@mui/material";
|
||||||
|
import { grey } from "@mui/material/colors";
|
||||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
import { GetDefaultDateRange } from "common/time/DateRange";
|
import { GetDefaultDateRange } from "common/time/DateRange";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
|
|
@ -312,7 +313,7 @@ export default function BinPlayer(props: Props) {
|
||||||
const checkpointStartTime = useRef(0);
|
const checkpointStartTime = useRef(0);
|
||||||
const currentCheckpointIndex = useRef(0);
|
const currentCheckpointIndex = useRef(0);
|
||||||
const isPlaying = useRef(false);
|
const isPlaying = useRef(false);
|
||||||
const PLAYBACK_INTERVAL = 2000 //playback time between checkpoints
|
const PLAYBACK_INTERVAL = 1000 //playback time between checkpoints in ms
|
||||||
const [isPlayingState, setIsPlayingState] = useState(false);
|
const [isPlayingState, setIsPlayingState] = useState(false);
|
||||||
const stopPlayer = () => { isPlaying.current = false; };
|
const stopPlayer = () => { isPlaying.current = false; };
|
||||||
const originalBin = useRef<Bin | null>(null);
|
const originalBin = useRef<Bin | null>(null);
|
||||||
|
|
@ -350,8 +351,6 @@ export default function BinPlayer(props: Props) {
|
||||||
* then builds `statusCheckpoints` for playback over [startDate, endDate].
|
* then builds `statusCheckpoints` for playback over [startDate, endDate].
|
||||||
*/
|
*/
|
||||||
const startPlayer = async () => {
|
const startPlayer = async () => {
|
||||||
console.log(startDate.toISOString())
|
|
||||||
console.log(endDate.toISOString())
|
|
||||||
originalBin.current = cloneDeep(bin); // capture before async work begins
|
originalBin.current = cloneDeep(bin); // capture before async work begins
|
||||||
isPlaying.current = true;
|
isPlaying.current = true;
|
||||||
setIsPlayingState(true)
|
setIsPlayingState(true)
|
||||||
|
|
@ -369,8 +368,6 @@ const startPlayer = async () => {
|
||||||
endDate,
|
endDate,
|
||||||
checkpointCountFromRange(startDate, endDate)
|
checkpointCountFromRange(startDate, endDate)
|
||||||
);
|
);
|
||||||
console.log(responses)
|
|
||||||
console.log(checkpoints)
|
|
||||||
|
|
||||||
for (let i = 0; i < checkpoints.length; i++) {
|
for (let i = 0; i < checkpoints.length; i++) {
|
||||||
if (!isPlaying.current) {
|
if (!isPlaying.current) {
|
||||||
|
|
@ -387,7 +384,6 @@ const startPlayer = async () => {
|
||||||
setBin(newBin);
|
setBin(newBin);
|
||||||
setCurrentCheckpointTime(checkpoints[i].time)
|
setCurrentCheckpointTime(checkpoints[i].time)
|
||||||
await new Promise(res => setTimeout(res, PLAYBACK_INTERVAL));
|
await new Promise(res => setTimeout(res, PLAYBACK_INTERVAL));
|
||||||
console.log("next step")
|
|
||||||
}
|
}
|
||||||
isPlaying.current = false;
|
isPlaying.current = false;
|
||||||
setIsPlayingState(false)
|
setIsPlayingState(false)
|
||||||
|
|
@ -455,8 +451,10 @@ const dateSelector = () => {
|
||||||
<Select
|
<Select
|
||||||
value={dateSelect}
|
value={dateSelect}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 1,
|
fontSize: 12,
|
||||||
bgcolor: 'rgba(255,255,255,0.08)',
|
borderRadius: 2,
|
||||||
|
border: "1px solid",
|
||||||
|
borderColor: grey[800],
|
||||||
'& .MuiSelect-select': {
|
'& .MuiSelect-select': {
|
||||||
py: 1.5,
|
py: 1.5,
|
||||||
},
|
},
|
||||||
|
|
@ -502,16 +500,15 @@ const dateSelector = () => {
|
||||||
{/* start/stop button */}
|
{/* start/stop button */}
|
||||||
<Box>
|
<Box>
|
||||||
{isPlayingState ? (
|
{isPlayingState ? (
|
||||||
<IconButton color="primary" sx={{ border: "1px solid", borderRadius: "50%", padding: 1 }} onClick={stopPlayer}><Stop /></IconButton>
|
<IconButton sx={{ border: "1px solid", borderRadius: "50%", padding: 1, height: 60, width: 60}} onClick={stopPlayer}><Stop fontSize="large" /></IconButton>
|
||||||
) : (
|
) : (
|
||||||
<IconButton color="primary" sx={{ border: "1px solid", borderRadius: "50%", padding: 1 }} onClick={startPlayer}><PlayArrow /></IconButton>
|
<IconButton sx={{ border: "1px solid", borderRadius: "50%", padding: 1, height: 60, width: 60 }} onClick={startPlayer}><PlayArrow fontSize="large" /></IconButton>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
{/* progress bar and date display */}
|
{/* progress bar and date display */}
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<Typography textAlign={"center"}>{currentCheckpointTime ? currentCheckpointTime.format("MMM D, YYYY h:mm A") : moment().format("MMM D, YYYY h:mm A")}</Typography>
|
<Typography textAlign={"center"}>{currentCheckpointTime ? currentCheckpointTime.format("MMM D, YYYY h:mm A") : moment().format("MMM D, YYYY h:mm A")}</Typography>
|
||||||
<LinearProgress value={displayProgress} variant="determinate" />
|
<LinearProgress value={displayProgress} variant="determinate" color="inherit" />
|
||||||
<Box display="flex" flexDirection="row" justifyContent="space-between">
|
<Box display="flex" flexDirection="row" justifyContent="space-between">
|
||||||
<Typography variant="caption">{startDate.format("MMM D, YYYY")}</Typography>
|
<Typography variant="caption">{startDate.format("MMM D, YYYY")}</Typography>
|
||||||
<Typography variant="caption">{endDate.format("MMM D, YYYY")}</Typography>
|
<Typography variant="caption">{endDate.format("MMM D, YYYY")}</Typography>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Table, TableBody, TableCell, TableHead, TableRow, Theme, Typography } from "@mui/material";
|
import { Box, Table, TableBody, TableCell, TableHead, TableRow, Theme, Typography, useTheme } from "@mui/material";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
@ -8,20 +8,34 @@ import { darken } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
tableStyle: { borderCollapse: "collapse" },
|
tableStyle: {
|
||||||
|
borderCollapse: "separate",
|
||||||
|
borderSpacing: 0,
|
||||||
|
},
|
||||||
headerCellStyle: {
|
headerCellStyle: {
|
||||||
fontWeight: 650,
|
fontWeight: 650,
|
||||||
backgroundColor: darken(theme.palette.background.paper, 0.2),
|
backgroundColor: darken(theme.palette.background.paper, 0.3),
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
border: "1px solid black"
|
borderBottom: "none",
|
||||||
|
padding: 10,
|
||||||
},
|
},
|
||||||
cellStyle: {
|
defaultCellStyle: {
|
||||||
padding: 2,
|
padding: 2,
|
||||||
fontWeight: 650,
|
fontWeight: 650,
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
border: "1px solid black",
|
backgroundColor: darken(theme.palette.background.paper, 0.1),
|
||||||
|
borderTop: "1px solid " + darken(theme.palette.background.paper, 0.4),
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
colouredCellStyle: {
|
||||||
|
padding: 2,
|
||||||
|
fontWeight: 650,
|
||||||
|
fontSize: 17,
|
||||||
|
textAlign: "center",
|
||||||
|
borderTop: "1px solid " + darken(theme.palette.background.paper, 0.4),
|
||||||
|
borderBottom: "none",
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -45,22 +59,10 @@ export default function BinTableExpanded(props: Props) {
|
||||||
const { cables, targetTemp, targetMoisture, inBounds = "#43a047", caution = "#f9a825", warning = "#c62828" } = props
|
const { cables, targetTemp, targetMoisture, inBounds = "#43a047", caution = "#f9a825", warning = "#c62828" } = props
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [{ user }] = useGlobalState()
|
const [{ user }] = useGlobalState()
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
const useFahrenheit = user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
|
const useFahrenheit = user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
|
||||||
|
|
||||||
// Build column definitions by iterating cables once
|
|
||||||
// const columns = useMemo<Column[]>(() => {
|
|
||||||
// const cols: Column[] = []
|
|
||||||
// cables.forEach((cable, cableIndex) => {
|
|
||||||
// cols.push({ label: `Cable ${cableIndex + 1} Temp`, cableIndex, type: "temp" })
|
|
||||||
// // Same moisture-validity check as BinTableView
|
|
||||||
// if (cable.moisture.length > 0 && avg(cable.moisture) > 0) {
|
|
||||||
// cols.push({ label: `Cable ${cableIndex + 1} Moisture`, cableIndex, type: "moisture" })
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// return cols
|
|
||||||
// }, [cables])
|
|
||||||
|
|
||||||
// Row count driven by the longest cable
|
// Row count driven by the longest cable
|
||||||
const rowCount = useMemo(
|
const rowCount = useMemo(
|
||||||
() => Math.max(0, ...cables.map(c => c.celcius.length)),
|
() => Math.max(0, ...cables.map(c => c.celcius.length)),
|
||||||
|
|
@ -110,87 +112,57 @@ export default function BinTableExpanded(props: Props) {
|
||||||
|
|
||||||
const cableTable = (cable: pond.GrainCable) => {
|
const cableTable = (cable: pond.GrainCable) => {
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table className={classes.tableStyle}>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell className={classes.headerCellStyle}>
|
<TableCell className={classes.headerCellStyle} sx={{ borderTopLeftRadius: 8 }}>
|
||||||
Level
|
Level
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.headerCellStyle}>
|
<TableCell className={classes.headerCellStyle}>
|
||||||
Temperature
|
Temperature
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.headerCellStyle}>
|
<TableCell className={classes.headerCellStyle} sx={{ borderTopRightRadius: 8 }}>
|
||||||
Moisture
|
Moisture
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{/* Rows are reversed so highest node is at top, matching BinTableView */}
|
{Array.from({ length: rowCount }, (_, i) => rowCount - 1 - i).map((nodeIndex, rowI) => {
|
||||||
{Array.from({ length: rowCount }, (_, i) => rowCount - 1 - i).map(nodeIndex => {
|
const isLast = rowI === rowCount - 1
|
||||||
|
|
||||||
const tempData = renderCell(cable, nodeIndex, "temp")
|
const tempData = renderCell(cable, nodeIndex, "temp")
|
||||||
const moistureData = renderCell(cable, nodeIndex, "moisture")
|
const moistureData = renderCell(cable, nodeIndex, "moisture")
|
||||||
return (
|
return (
|
||||||
<TableRow key={nodeIndex}>
|
<TableRow key={nodeIndex}>
|
||||||
<TableCell padding="none" className={classes.cellStyle}>
|
<TableCell
|
||||||
|
padding="none"
|
||||||
|
className={classes.defaultCellStyle}
|
||||||
|
sx={{ borderBottomLeftRadius: isLast ? 8 : 0 }}>
|
||||||
{nodeIndex + 1}
|
{nodeIndex + 1}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
padding="none"
|
padding="none"
|
||||||
className={classes.cellStyle}
|
className={classes.colouredCellStyle}
|
||||||
sx={{ backgroundColor: tempData.color || undefined }}>
|
sx={{ backgroundColor: tempData.color || darken(theme.palette.background.paper, 0.1) }}>
|
||||||
{tempData.display}
|
{tempData.display}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
padding="none"
|
padding="none"
|
||||||
className={classes.cellStyle}
|
className={classes.colouredCellStyle}
|
||||||
sx={{ backgroundColor: moistureData.color || undefined }}>
|
sx={{
|
||||||
|
backgroundColor: moistureData.color || darken(theme.palette.background.paper, 0.1),
|
||||||
|
borderBottomRightRadius: isLast ? 8 : 0,
|
||||||
|
}}>
|
||||||
{moistureData.display}
|
{moistureData.display}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)})}
|
)
|
||||||
|
})}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// <Table className={classes.tableStyle}>
|
|
||||||
// <TableHead>
|
|
||||||
// <TableRow>
|
|
||||||
// <TableCell className={classes.headerCellStyle}>Level</TableCell>
|
|
||||||
// {columns.map((col, i) => (
|
|
||||||
// <TableCell key={i} className={classes.headerCellStyle}>
|
|
||||||
// {col.label}
|
|
||||||
// </TableCell>
|
|
||||||
// ))}
|
|
||||||
// </TableRow>
|
|
||||||
// </TableHead>
|
|
||||||
// <TableBody>
|
|
||||||
// {/* Rows are reversed so highest node is at top, matching BinTableView */}
|
|
||||||
// {Array.from({ length: rowCount }, (_, i) => rowCount - 1 - i).map(nodeIndex => (
|
|
||||||
// <TableRow key={nodeIndex}>
|
|
||||||
// <TableCell padding="none" className={classes.cellStyle}>
|
|
||||||
// {nodeIndex + 1}
|
|
||||||
// </TableCell>
|
|
||||||
// {columns.map((col, colI) => {
|
|
||||||
// const cable = cables[col.cableIndex]
|
|
||||||
// const { display, color } = renderCell(cable, nodeIndex, col.type)
|
|
||||||
// return (
|
|
||||||
// <TableCell
|
|
||||||
// key={colI}
|
|
||||||
// padding="none"
|
|
||||||
// className={classes.cellStyle}
|
|
||||||
// sx={{ backgroundColor: color || undefined }}
|
|
||||||
// >
|
|
||||||
// {display}
|
|
||||||
// </TableCell>
|
|
||||||
// )
|
|
||||||
// })}
|
|
||||||
// </TableRow>
|
|
||||||
// ))}
|
|
||||||
// </TableBody>
|
|
||||||
// </Table>
|
|
||||||
<Box padding={1}>
|
<Box padding={1}>
|
||||||
{/* Table Legend */}
|
{/* Table Legend */}
|
||||||
<Box display="flex" alignItems="center" justifyContent={"space-between"} marginY={2}>
|
<Box display="flex" alignItems="center" justifyContent={"space-between"} marginY={2}>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Checkbox, FormControl, MenuItem, List, ListItem, InputLabel, Select, Typography, Stack, FormControlLabel } from "@mui/material";
|
import { Box, Checkbox, FormControl, MenuItem, List, ListItem, InputLabel, Select, Typography, Stack, FormControlLabel, useTheme } from "@mui/material";
|
||||||
import Bin3dView from "bin/3dView/Scene/Bin3dView";
|
import Bin3dView from "bin/3dView/Scene/Bin3dView";
|
||||||
import { Bin, Component, Device } from "models";
|
import { Bin, Component, Device } from "models";
|
||||||
import { Controller } from "models/Controller";
|
import { Controller } from "models/Controller";
|
||||||
|
|
@ -26,6 +26,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
export default function bin3dVisualizer(props: Props){
|
export default function bin3dVisualizer(props: Props){
|
||||||
const {bin, fans, heaters, permissions, devices, componentDevices, componentMap, binPrefs, updateBinCallback} = props
|
const {bin, fans, heaters, permissions, devices, componentDevices, componentMap, binPrefs, updateBinCallback} = props
|
||||||
|
const theme = useTheme()
|
||||||
const [showHeatmap, setShowHeatmap] = useState(true)
|
const [showHeatmap, setShowHeatmap] = useState(true)
|
||||||
// const [showHotspots, setShowHotspots] = useState(false)
|
// const [showHotspots, setShowHotspots] = useState(false)
|
||||||
// const [showFill, setShowFill] = useState(false)
|
// const [showFill, setShowFill] = useState(false)
|
||||||
|
|
@ -68,16 +69,17 @@ export default function bin3dVisualizer(props: Props){
|
||||||
const binModeControl = () => {
|
const binModeControl = () => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography sx={{fontWeight: 650, fontSize: isMobile ? 13 : 20}}>{bin.name()}</Typography>
|
<Typography sx={{fontWeight: 650, fontSize: isMobile ? 13 : 17}}>{bin.name()}</Typography>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<Select
|
<Select
|
||||||
value={binMode}
|
value={binMode}
|
||||||
disabled={modeChangeInProgress}
|
disabled={modeChangeInProgress}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
bgcolor: 'rgba(255,255,255,0.08)',
|
bgcolor: '#151E27',
|
||||||
'& .MuiSelect-select': {
|
'& .MuiSelect-select': {
|
||||||
py: 1.5,
|
py: isMobile ? 0.5 : 1.5,
|
||||||
|
fontSize: 13
|
||||||
},
|
},
|
||||||
'& .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
'& .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
||||||
'&:hover .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
'&:hover .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
||||||
|
|
@ -103,7 +105,7 @@ export default function bin3dVisualizer(props: Props){
|
||||||
const heatmapDisplay = () => {
|
const heatmapDisplay = () => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography noWrap sx={{fontWeight: 650, fontSize: isMobile ? 13 : 20}}>
|
<Typography noWrap sx={{fontWeight: 650, fontSize: isMobile ? 13 : 17}}>
|
||||||
Heatmap
|
Heatmap
|
||||||
</Typography>
|
</Typography>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
|
@ -111,8 +113,11 @@ export default function bin3dVisualizer(props: Props){
|
||||||
value={binDisplay}
|
value={binDisplay}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
bgcolor: 'rgba(255,255,255,0.08)',
|
bgcolor: '#151E27',
|
||||||
'& .MuiSelect-select': { py: 1.5 },
|
'& .MuiSelect-select': {
|
||||||
|
py: isMobile ? 0.5 : 1.5,
|
||||||
|
fontSize: 13
|
||||||
|
},
|
||||||
'& .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
'& .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
||||||
'&:hover .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
'&:hover .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
||||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': { border: 'none' },
|
||||||
|
|
@ -173,9 +178,12 @@ export default function bin3dVisualizer(props: Props){
|
||||||
}
|
}
|
||||||
|
|
||||||
const controllerDisplay = () => {
|
const controllerDisplay = () => {
|
||||||
|
if (fans && fans.length > 1 || heaters && heaters.length > 1){
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box width="100%">
|
||||||
<Typography sx={{fontWeight: 650, fontSize: isMobile ? 13 : 20}}>
|
<Typography sx={{fontWeight: 650, fontSize: isMobile ? 13 : 17}}>
|
||||||
Controllers
|
Controllers
|
||||||
</Typography>
|
</Typography>
|
||||||
{fans && fans.map(fan => {
|
{fans && fans.map(fan => {
|
||||||
|
|
@ -187,15 +195,16 @@ export default function bin3dVisualizer(props: Props){
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
bgcolor: 'rgba(255,255,255,0.05)',
|
// bgcolor: 'rgba(255,255,255,0.05)',
|
||||||
|
bgcolor: '#151E27',
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
px: 2,
|
px: 2,
|
||||||
py: 1,
|
py: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{fontSize: isMobile ? 13 : 15}}>{fan.name()}</Typography>
|
<Typography sx={{fontSize: 13}}>{fan.name()}</Typography>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<Typography sx={{fontWeight: 650, fontSize: isMobile ? 13 : 15}}>
|
<Typography sx={{fontWeight: 650, fontSize: 13}}>
|
||||||
{isOn ? "ON" : "OFF"}
|
{isOn ? "ON" : "OFF"}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
|
|
@ -216,15 +225,16 @@ export default function bin3dVisualizer(props: Props){
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
bgcolor: 'rgba(255,255,255,0.05)',
|
// bgcolor: 'rgba(255,255,255,0.05)',
|
||||||
|
bgcolor: '#151E27',
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
px: 2,
|
px: 2,
|
||||||
py: 1,
|
py: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{fontSize: isMobile ? 13 : 15}}>{heater.name()}</Typography>
|
<Typography sx={{fontSize: 13}}>{heater.name()}</Typography>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<Typography sx={{fontWeight: 650, fontSize: isMobile ? 13 : 15}}>
|
<Typography sx={{fontWeight: 650, fontSize: 13}}>
|
||||||
{isOn ? "ON" : "OFF"}
|
{isOn ? "ON" : "OFF"}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
|
|
@ -240,11 +250,14 @@ export default function bin3dVisualizer(props: Props){
|
||||||
// loop through controllers displaying each one
|
// loop through controllers displaying each one
|
||||||
|
|
||||||
)
|
)
|
||||||
|
}else{
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const desktopHUD = () => {
|
const desktopHUD = () => {
|
||||||
return (
|
return (
|
||||||
<Box position={"absolute"} top={20} left={30} height={"80%"} width={"30%"} padding={2}>
|
<Box position={"absolute"} top={20} left={25} height={"75%"} width={"25%"}>
|
||||||
<Stack direction={"column"} justifyContent={"space-between"} sx={{height: "100%", width: "100%"}}>
|
<Stack direction={"column"} justifyContent={"space-between"} sx={{height: "100%", width: "100%"}}>
|
||||||
{binModeControl()}
|
{binModeControl()}
|
||||||
{heatmapDisplay()}
|
{heatmapDisplay()}
|
||||||
|
|
@ -397,7 +410,7 @@ export default function bin3dVisualizer(props: Props){
|
||||||
showMoisture={showMoisture && showLabels}
|
showMoisture={showMoisture && showLabels}
|
||||||
// showGrain={showFill}
|
// showGrain={showFill}
|
||||||
// showHotspots={showHotspots}
|
// showHotspots={showHotspots}
|
||||||
xOffset={isMobile ? undefined : -150}
|
xOffset={isMobile ? undefined : -120}
|
||||||
/>
|
/>
|
||||||
{isMobile ? mobileHUD() : desktopHUD()}
|
{isMobile ? mobileHUD() : desktopHUD()}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -734,7 +734,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(plenum.type(), plenum.subType(), themeType)
|
let icon = GetComponentIcon(plenum.type(), plenum.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={plenum.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={plenum.name()}
|
title={plenum.name()}
|
||||||
|
|
@ -754,7 +754,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={plenum.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{tempHumidCard(plenum)}
|
{tempHumidCard(plenum)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -765,7 +765,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(pressure.type(), pressure.subType(), themeType)
|
let icon = GetComponentIcon(pressure.type(), pressure.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={pressure.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={pressure.name()}
|
title={pressure.name()}
|
||||||
|
|
@ -785,7 +785,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={pressure.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{pressureCard(pressure)}
|
{pressureCard(pressure)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -795,7 +795,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(ambient.type(), ambient.subType(), themeType)
|
let icon = GetComponentIcon(ambient.type(), ambient.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={ambient.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={ambient.name()}
|
title={ambient.name()}
|
||||||
|
|
@ -815,7 +815,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={ambient.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{tempHumidCard(ambient)}
|
{tempHumidCard(ambient)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -826,7 +826,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(h.type(), h.subType(), themeType)
|
let icon = GetComponentIcon(h.type(), h.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={h.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={h.name()}
|
title={h.name()}
|
||||||
|
|
@ -846,7 +846,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={h.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{tempHumidCard(h)}
|
{tempHumidCard(h)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -857,7 +857,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(co2.type(), co2.subType(), themeType)
|
let icon = GetComponentIcon(co2.type(), co2.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={co2.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={co2.name()}
|
title={co2.name()}
|
||||||
|
|
@ -877,7 +877,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={co2.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{co2Card(co2)}
|
{co2Card(co2)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -887,7 +887,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(lidar.type(), lidar.subType(), themeType)
|
let icon = GetComponentIcon(lidar.type(), lidar.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={lidar.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={lidar.name()}
|
title={lidar.name()}
|
||||||
|
|
@ -907,7 +907,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={lidar.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{lidarCard(lidar)}
|
{lidarCard(lidar)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -917,7 +917,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(cable.type(), cable.subType(), themeType)
|
let icon = GetComponentIcon(cable.type(), cable.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={cable.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={cable.name()}
|
title={cable.name()}
|
||||||
|
|
@ -939,7 +939,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={cable.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{cableCard(cable)}
|
{cableCard(cable)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -957,7 +957,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(heater.type(), heater.subType(), themeType)
|
let icon = GetComponentIcon(heater.type(), heater.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={heater.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
device={device}
|
device={device}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
|
|
@ -977,7 +977,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={heater.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{controllerCard(heater)}
|
{controllerCard(heater)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -987,7 +987,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
if(showGraphs && device){
|
if(showGraphs && device){
|
||||||
let icon = GetComponentIcon(fan.type(), fan.subType(), themeType)
|
let icon = GetComponentIcon(fan.type(), fan.subType(), themeType)
|
||||||
return(
|
return(
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={fan.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
<BinSensorGraph
|
<BinSensorGraph
|
||||||
device={device}
|
device={device}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
|
|
@ -1007,7 +1007,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Grid2 size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
<Grid2 key={fan.key()} size={{xs: 12, sm: 6, md: 4, lg: 3}}>
|
||||||
{controllerCard(fan)}
|
{controllerCard(fan)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
)
|
)
|
||||||
|
|
@ -1020,7 +1020,7 @@ export default function BinSensorsDisplay(props: Props){
|
||||||
<Typography sx={{ fontWeight: 650, fontSize: 25 }}>Unassigned Components</Typography>
|
<Typography sx={{ fontWeight: 650, fontSize: 25 }}>Unassigned Components</Typography>
|
||||||
<Grid2 container spacing={2}>
|
<Grid2 container spacing={2}>
|
||||||
{unassignedComponents.map(comp => (
|
{unassignedComponents.map(comp => (
|
||||||
<Grid2 size={{ xs: 6, sm: 6, md: 4, lg: 3 }}>
|
<Grid2 key={comp.key()} size={{ xs: 6, sm: 6, md: 4, lg: 3 }}>
|
||||||
{unassignedComponentCard(comp)}
|
{unassignedComponentCard(comp)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -113,125 +113,69 @@ export default function BinSummary(props: Props){
|
||||||
return bin.grainInventory(user)
|
return bin.grainInventory(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
const inventorySummaryMobile = () => {
|
const inventorySummaryMobile = () => (
|
||||||
return (
|
<Card raised sx={{ mb: 2, overflow: "hidden", borderRadius: 4 }}>
|
||||||
<Card raised sx={{ marginBottom: 2 }}>
|
{/* Header row */}
|
||||||
<Box display="flex" justifyContent="space-between" alignItems="flex-start" sx={{ padding: 1, gap: 2 }}>
|
<Box display="flex" alignItems="center" justifyContent="space-between" sx={{ px: 2, pt: 1.5, pb: 1.25 }}>
|
||||||
|
<Box display="flex" alignItems="center" gap={1}>
|
||||||
{/* Grain Type */}
|
<Box sx={{ width: 30, height: 30, borderRadius: 2, backgroundColor: "rgba(76,175,80,0.15)", display: "flex", alignItems: "center", justifyContent: "center" }}
|
||||||
<Box>
|
onClick={() => setOpenGrainDialog(true)}>
|
||||||
<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 }} />
|
<Spa sx={{ color: "#4caf50", fontSize: 18 }} />
|
||||||
<Typography variant="body1" sx={{fontSize: 12}}>{bin.grainName()}</Typography>
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" sx={{ color: grey[500], display: "block" }}>Grain type</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 500, fontSize: 14 }}>{bin.grainName()}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box display="flex" alignItems="center" gap={0.75} sx={{ textAlign: "right" }}>
|
||||||
|
<Box sx={{ width: 7, height: 7, borderRadius: "50%", backgroundColor: "#4caf50", flexShrink: 0 }} />
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" sx={{ color: grey[500], display: "block" }}>Last updated</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontSize: 13 }}>{moment(bin.status.timestamp).fromNow()}</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Bin Fill */}
|
{/* Fill row */}
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
<Box sx={{ borderTop: `1px solid ${grey[800]}`, px: 2, pt: 1.5, pb: 1.75 }}>
|
||||||
|
<Box display="flex" alignItems="baseline" justifyContent="space-between" sx={{ mb: 1 }}>
|
||||||
|
<Typography variant="caption" sx={{ color: grey[500] }}>Bin fill</Typography>
|
||||||
|
<Box display="flex" alignItems="baseline" gap={0.75}>
|
||||||
|
<Typography sx={{ fontSize: 20, fontWeight: 500 }}>
|
||||||
|
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL
|
||||||
|
? manualFillPercent : bin.fillPercent()}%
|
||||||
|
</Typography>
|
||||||
<Typography variant="caption" sx={{ color: grey[500] }}>
|
<Typography variant="caption" sx={{ color: grey[500] }}>
|
||||||
Bin Fill
|
{currentFill().toLocaleString()} / {bin.grainCapacity(user).toLocaleString()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ mt: 0.5 }}>
|
</Box>
|
||||||
{/* Top line: current / capacity */}
|
</Box>
|
||||||
<Typography sx={{fontSize: 12}} noWrap>
|
|
||||||
{currentFill()} / {bin.grainCapacity(user)}
|
|
||||||
</Typography>
|
|
||||||
{/* Bottom line: bar/slider + percent */}
|
|
||||||
<Box display="flex" alignItems="center" gap={1} sx={{ mt: 0.5 }}>
|
|
||||||
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL ? (
|
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL ? (
|
||||||
<Slider
|
<Box sx={{ display: "flex", alignItems: "center", marginY: -2.5 }}>
|
||||||
value={manualFillPercent}
|
<Slider value={manualFillPercent} onChangeCommitted={() => setOpenNewTransaction(true)}
|
||||||
onChangeCommitted={() => setOpenNewTransaction(true)}
|
onChange={(_, val) => { setManualFillPercent(val as number); setManualBushels((val as number / 100) * bin.bushelCapacity()); }}
|
||||||
onChange={(_, val) => {
|
min={0} max={100} sx={{
|
||||||
let fillPercent = val as number
|
|
||||||
setManualFillPercent(fillPercent)
|
|
||||||
setManualBushels((fillPercent / 100) * bin.bushelCapacity())
|
|
||||||
}}
|
|
||||||
min={0}
|
|
||||||
max={100}
|
|
||||||
sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
color: "#4caf50",
|
color: "#4caf50",
|
||||||
|
py: 0,
|
||||||
|
mt: 0,
|
||||||
|
mb: 0,
|
||||||
|
height: 4,
|
||||||
|
"& .MuiSlider-root": { py: 0 },
|
||||||
"& .MuiSlider-thumb": { width: 16, height: 16 },
|
"& .MuiSlider-thumb": { width: 16, height: 16 },
|
||||||
"& .MuiSlider-rail": { backgroundColor: "rgba(255,255,255,0.1)" },
|
"& .MuiSlider-rail": { backgroundColor: "rgba(255,255,255,0.1)" },
|
||||||
}}
|
}} />
|
||||||
/>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<LinearProgress
|
<LinearProgress variant="determinate" value={bin.fillPercent()}
|
||||||
variant="determinate"
|
sx={{ height: 8, borderRadius: 4, backgroundColor: "rgba(255,255,255,0.1)", "& .MuiLinearProgress-bar": { backgroundColor: "#4caf50", borderRadius: 4 } }} />
|
||||||
value={bin.fillPercent()}
|
|
||||||
sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
backgroundColor: "rgba(255,255,255,0.1)",
|
|
||||||
"& .MuiLinearProgress-bar": {
|
|
||||||
backgroundColor: "#4caf50",
|
|
||||||
borderRadius: 4,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<Typography variant="body2" noWrap sx={{ flexShrink: 0, fontSize: 12 }}>
|
|
||||||
{bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_MANUAL
|
|
||||||
? manualFillPercent
|
|
||||||
: bin.fillPercent()}%
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Last Updated */}
|
|
||||||
<Box>
|
|
||||||
<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 sx={{ mt: 0.5 }}>
|
|
||||||
{/* Top line: time */}
|
|
||||||
<Box display="flex" alignItems="center" gap={0.5}>
|
|
||||||
<AccessTime sx={{ fontSize: 16, color: grey[400] }} />
|
|
||||||
<Typography variant="body2" sx={{ color: grey[300], fontSize: 12 }}>
|
|
||||||
{moment(bin.status.timestamp).fromNow()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
{/* Bottom line: live/inactive status */}
|
|
||||||
<Box sx={{ mt: 0.5 }}>
|
|
||||||
{activity(bin.status.timestamp)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const inventorySummaryDesktop = () => {
|
const inventorySummaryDesktop = () => {
|
||||||
return (
|
return (
|
||||||
<Card raised sx={{ marginBottom: 2 }}>
|
<Card raised sx={{ marginBottom: 2, borderRadius: 4 }}>
|
||||||
<Box display="flex" justifyContent="space-between" alignItems="flex-start" sx={{ padding: 2 }}>
|
<Box display="flex" justifyContent="space-between" alignItems="flex-start" sx={{ padding: 2 }}>
|
||||||
|
|
||||||
{/* Grain Type */}
|
{/* Grain Type */}
|
||||||
|
|
@ -395,7 +339,7 @@ export default function BinSummary(props: Props){
|
||||||
{loading ?
|
{loading ?
|
||||||
<Skeleton variant="rectangular" height={600}/>
|
<Skeleton variant="rectangular" height={600}/>
|
||||||
:
|
:
|
||||||
<Card raised sx={{height: 600, position: "relative"}}>
|
<Card raised sx={{height: 600, position: "relative", borderRadius: 4}}>
|
||||||
<Bin3dVisualizer
|
<Bin3dVisualizer
|
||||||
bin={bin}
|
bin={bin}
|
||||||
fans={fans}
|
fans={fans}
|
||||||
|
|
@ -408,7 +352,7 @@ export default function BinSummary(props: Props){
|
||||||
updateBinCallback={updateBinCallback}
|
updateBinCallback={updateBinCallback}
|
||||||
/>
|
/>
|
||||||
{setBin &&
|
{setBin &&
|
||||||
<Box paddingX={2} position="absolute" bottom={10} width="100%">
|
<Box padding={3} position="absolute" bottom={3} width="100%">
|
||||||
<BinPlayer bin={bin} componentDevices={componentDevices} setBin={setBin}/>
|
<BinPlayer bin={bin} componentDevices={componentDevices} setBin={setBin}/>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
|
@ -420,7 +364,7 @@ export default function BinSummary(props: Props){
|
||||||
{loading ?
|
{loading ?
|
||||||
<Skeleton variant="rectangular" height={600}/>
|
<Skeleton variant="rectangular" height={600}/>
|
||||||
:
|
:
|
||||||
<Card raised sx={{ height: 600, display: "flex", flexDirection: "column", overflow: "hidden" }}>
|
<Card raised sx={{ height: 600, display: "flex", flexDirection: "column", overflow: "hidden", borderRadius: 4 }}>
|
||||||
<BinDetails
|
<BinDetails
|
||||||
bin={bin}
|
bin={bin}
|
||||||
updateBinCallback={updateBinCallback}
|
updateBinCallback={updateBinCallback}
|
||||||
|
|
@ -442,7 +386,7 @@ export default function BinSummary(props: Props){
|
||||||
{loading ?
|
{loading ?
|
||||||
<Skeleton variant="rectangular" height={250}/>
|
<Skeleton variant="rectangular" height={250}/>
|
||||||
:
|
:
|
||||||
<Card>
|
<Card sx={{borderRadius: 4}}>
|
||||||
<BinSensorsDisplay components={componentMap} bin={bin} preferences={binPrefs} setPreferences={setPreferences} componentDevices={componentDevices}/>
|
<BinSensorsDisplay components={componentMap} bin={bin} preferences={binPrefs} setPreferences={setPreferences} componentDevices={componentDevices}/>
|
||||||
</Card>
|
</Card>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Edit, GppGood, Info, Save } from "@mui/icons-material";
|
import { Edit, GppGood, Info, Save } from "@mui/icons-material";
|
||||||
import { Box, Button, darken, DialogActions, IconButton, Table, TableBody, TableCell, TableHead, TableRow, TextField, Theme, Tooltip, Typography } from "@mui/material";
|
import { Box, Button, darken, DialogActions, IconButton, Table, TableBody, TableCell, TableHead, TableRow, TextField, Theme, Tooltip, Typography, useTheme } from "@mui/material";
|
||||||
import { green, grey, orange, red, yellow } from "@mui/material/colors";
|
import { green, grey, orange, red, yellow } from "@mui/material/colors";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
|
|
@ -22,34 +22,40 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
},
|
},
|
||||||
headerCellStyle: {
|
headerCellStyle: {
|
||||||
fontWeight: 650,
|
fontWeight: 650,
|
||||||
backgroundColor: darken(theme.palette.background.paper, 0.2),
|
backgroundColor: darken(theme.palette.background.paper, 0.3),
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
borderTop: "1px solid black",
|
padding: 10
|
||||||
borderLeft: "1px solid black",
|
|
||||||
borderBottom: "1px solid black",
|
|
||||||
"&:last-child": {
|
|
||||||
borderRight: "1px solid black",
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mobileHeaderCellStyle: {
|
mobileHeaderCellStyle: {
|
||||||
fontWeight: 650,
|
fontWeight: 650,
|
||||||
backgroundColor: darken(theme.palette.background.paper, 0.2),
|
backgroundColor: darken(theme.palette.background.paper, 0.3),
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
borderTop: "1px solid black",
|
|
||||||
borderLeft: "1px solid black",
|
|
||||||
borderBottom: "1px solid black",
|
|
||||||
"&:last-child": {
|
|
||||||
borderRight: "1px solid black",
|
|
||||||
},
|
|
||||||
padding: 2
|
padding: 2
|
||||||
},
|
},
|
||||||
cellStyle: {
|
targetCellStyle: {
|
||||||
padding: 2,
|
|
||||||
fontWeight: 650,
|
|
||||||
fontSize: 17,
|
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
border: "1px solid black",
|
backgroundColor: theme.palette.background.paper
|
||||||
|
},
|
||||||
|
mobileTargetCellStyle: {
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
padding: 2
|
||||||
|
},
|
||||||
|
defaultCellStyle: {
|
||||||
|
padding: 2,
|
||||||
|
fontSize: 15,
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: darken(theme.palette.background.paper, 0.1),
|
||||||
|
borderTop: "1px solid " + darken(theme.palette.background.paper, 0.4),
|
||||||
|
borderBottom: "none"
|
||||||
|
},
|
||||||
|
colouredCellStyle: {
|
||||||
|
padding: 2,
|
||||||
|
borderTop: "1px solid " + darken(theme.palette.background.paper, 0.4),
|
||||||
|
// fontWeight: 650,
|
||||||
|
fontSize: 15,
|
||||||
|
textAlign: "center",
|
||||||
|
borderBottom: "none"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -71,6 +77,7 @@ export default function BinTableView(props: Props) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const {bin, updateBinCallback} = props
|
const {bin, updateBinCallback} = props
|
||||||
const [{user}] = useGlobalState()
|
const [{user}] = useGlobalState()
|
||||||
|
const theme = useTheme()
|
||||||
const [enterTemp, setEnterTemp] = useState(false)
|
const [enterTemp, setEnterTemp] = useState(false)
|
||||||
const [enterMoisture, setEnterMoisture] = useState(false)
|
const [enterMoisture, setEnterMoisture] = useState(false)
|
||||||
const [tempTarget, setTempTarget] = useState(0)
|
const [tempTarget, setTempTarget] = useState(0)
|
||||||
|
|
@ -163,13 +170,13 @@ export default function BinTableView(props: Props) {
|
||||||
return levels
|
return levels
|
||||||
},[bin])
|
},[bin])
|
||||||
|
|
||||||
const levelDisplay = (level: BinLevel) => {
|
const levelDisplay = (level: BinLevel, lastRow?: boolean) => {
|
||||||
let tempDisplay = "--"
|
let tempDisplay = "--"
|
||||||
let moistureDisplay = "--"
|
let moistureDisplay = "--"
|
||||||
let lvlTemp
|
let lvlTemp
|
||||||
let lvlMoisture
|
let lvlMoisture
|
||||||
let tempColour: string = ""
|
let tempColour: string = darken(theme.palette.background.paper, 0.1)
|
||||||
let moistureColour: string = ""
|
let moistureColour: string = darken(theme.palette.background.paper, 0.1)
|
||||||
|
|
||||||
//determine the temp
|
//determine the temp
|
||||||
if(level.grainTemps.length > 0){
|
if(level.grainTemps.length > 0){
|
||||||
|
|
@ -207,12 +214,12 @@ export default function BinTableView(props: Props) {
|
||||||
lvlMoisture = avg(level.airMoistures)
|
lvlMoisture = avg(level.airMoistures)
|
||||||
}
|
}
|
||||||
if(lvlMoisture){
|
if(lvlMoisture){
|
||||||
moistureDisplay = lvlMoisture.toFixed(2) + "%"
|
moistureDisplay = lvlMoisture.toFixed(2)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<TableCell padding="none" className={classes.cellStyle} sx={{backgroundColor: tempColour}}>{tempDisplay}</TableCell>
|
<TableCell className={classes.colouredCellStyle} padding="none" sx={{backgroundColor: tempColour}}>{tempDisplay}</TableCell>
|
||||||
<TableCell padding="none" className={classes.cellStyle} sx={{backgroundColor: moistureColour}}>{moistureDisplay}</TableCell>
|
<TableCell className={classes.colouredCellStyle} padding="none" sx={{backgroundColor: moistureColour, borderBottomRightRadius: lastRow ? 8 : 0}}>{moistureDisplay}</TableCell>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -301,10 +308,11 @@ export default function BinTableView(props: Props) {
|
||||||
{expandedTableDialog()}
|
{expandedTableDialog()}
|
||||||
{/* Level Table */}
|
{/* Level Table */}
|
||||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||||
<Typography>Grain Table</Typography>
|
<Typography fontWeight={650}>Grain Table</Typography>
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
<Box display="flex" alignItems="center" gap={1}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
sx={{ background: theme.palette.background.default}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenExpandedCables(true)
|
setOpenExpandedCables(true)
|
||||||
}}>
|
}}>
|
||||||
|
|
@ -324,7 +332,7 @@ export default function BinTableView(props: Props) {
|
||||||
<TableRow ref={headerRow1Ref}>
|
<TableRow ref={headerRow1Ref}>
|
||||||
{/* "Targets" cell — hide on mobile when either field is being edited */}
|
{/* "Targets" cell — hide on mobile when either field is being edited */}
|
||||||
{!(isMobile && (enterTemp || enterMoisture)) && (
|
{!(isMobile && (enterTemp || enterMoisture)) && (
|
||||||
<TableCell className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} sx={{top: 0, position: "sticky", zIndex: 3 }}>
|
<TableCell className={isMobile ? classes.mobileTargetCellStyle : classes.targetCellStyle} sx={{top: 0, position: "sticky", zIndex: 4, borderBottom: "none" }}>
|
||||||
<Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
<Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||||
<GppGood />
|
<GppGood />
|
||||||
{!isMobile &&
|
{!isMobile &&
|
||||||
|
|
@ -339,14 +347,14 @@ export default function BinTableView(props: Props) {
|
||||||
{/* Temperature cell */}
|
{/* Temperature cell */}
|
||||||
{!(isMobile && enterMoisture) && (
|
{!(isMobile && enterMoisture) && (
|
||||||
<TableCell
|
<TableCell
|
||||||
className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle}
|
className={isMobile ? classes.mobileTargetCellStyle : classes.targetCellStyle}
|
||||||
colSpan={isMobile && enterTemp ? 3 : 1}
|
colSpan={isMobile && enterTemp ? 3 : 1}
|
||||||
sx={{ top: 0, position: "sticky", zIndex: 3 }}
|
sx={{ top: 0, position: "sticky", zIndex: 4, borderBottom: "none" }}
|
||||||
>
|
>
|
||||||
<Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
<Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||||
<TemperatureIcon heightWidth={isMobile ? 25 : 30} />
|
<TemperatureIcon heightWidth={isMobile ? 25 : 30} />
|
||||||
{enterTemp ? updateTempEntry() : (
|
{enterTemp ? updateTempEntry() : (
|
||||||
<Typography fontSize={isMobile ? 12 : 16} fontWeight={650}>
|
<Typography fontSize={isMobile ? 12 : 16}>
|
||||||
{tempThreshDisplay(isMobile)}
|
{tempThreshDisplay(isMobile)}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
@ -365,15 +373,15 @@ export default function BinTableView(props: Props) {
|
||||||
{/* Moisture cell — hide on mobile when temperature is being edited */}
|
{/* Moisture cell — hide on mobile when temperature is being edited */}
|
||||||
{!(isMobile && enterTemp) && (
|
{!(isMobile && enterTemp) && (
|
||||||
<TableCell
|
<TableCell
|
||||||
className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle}
|
className={isMobile ? classes.mobileTargetCellStyle : classes.targetCellStyle}
|
||||||
colSpan={isMobile && enterMoisture ? 3 : 1}
|
colSpan={isMobile && enterMoisture ? 3 : 1}
|
||||||
sx={{ top: 0, position: "sticky", zIndex: 3 }}
|
sx={{ top: 0, position: "sticky", zIndex: 4, borderBottom: "none" }}
|
||||||
>
|
>
|
||||||
<Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
<Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||||
<HumidityIcon height={isMobile ? 25 : 30} width={20} />
|
<HumidityIcon height={isMobile ? 25 : 30} width={20} />
|
||||||
{enterMoisture ? updateMoistureEntry() : (
|
{enterMoisture ? updateMoistureEntry() : (
|
||||||
<Typography fontSize={isMobile ? 12 : 16} fontWeight={650} sx={{ marginLeft: "10px" }}>
|
<Typography fontSize={isMobile ? 12 : 16} sx={{ marginLeft: "10px" }}>
|
||||||
{bin.targetMoisture()}%
|
{bin.targetMoisture()}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
{enterMoisture ? (
|
{enterMoisture ? (
|
||||||
|
|
@ -390,19 +398,49 @@ export default function BinTableView(props: Props) {
|
||||||
)}
|
)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} sx={{ top: firstRowHeight, position: "sticky", zIndex: 3 }}>Level</TableCell>
|
<TableCell
|
||||||
<TableCell className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} sx={{ top: firstRowHeight, position: "sticky", zIndex: 3 }}>
|
className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle}
|
||||||
|
sx={{
|
||||||
|
top: firstRowHeight,
|
||||||
|
position: "sticky",
|
||||||
|
zIndex: 3,
|
||||||
|
borderBottom: "none",
|
||||||
|
borderTopLeftRadius: 8,
|
||||||
|
boxShadow: `-12px -12px 0px 4px ${theme.palette.background.paper}`
|
||||||
|
}}>
|
||||||
|
Level
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle}
|
||||||
|
sx={{
|
||||||
|
top: firstRowHeight,
|
||||||
|
position: "sticky",
|
||||||
|
zIndex: 3,
|
||||||
|
borderBottom: "none"
|
||||||
|
}}>
|
||||||
{isMobile ? "T" : "Temperature"}{(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " (°F)" : " (°C)")}
|
{isMobile ? "T" : "Temperature"}{(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " (°F)" : " (°C)")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} sx={{ top: firstRowHeight, position: "sticky", zIndex: 3 }}>EMC (%)</TableCell>
|
<TableCell
|
||||||
|
className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle}
|
||||||
|
sx={{
|
||||||
|
top: firstRowHeight,
|
||||||
|
position: "sticky",
|
||||||
|
zIndex: 3,
|
||||||
|
borderBottom: "none",
|
||||||
|
borderTopRightRadius: 8,
|
||||||
|
// fills the transparent corner area with the target row's background colour
|
||||||
|
boxShadow: `16px -16px 8px 12px ${theme.palette.background.paper}`
|
||||||
|
}}>
|
||||||
|
EMC (%)
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{binLevels.map((level, i) => (
|
{binLevels.map((level, i) => (
|
||||||
<TableRow key={i}>
|
<TableRow key={i}>
|
||||||
<TableCell padding="none" className={classes.cellStyle}>{binLevels.length - i}</TableCell>
|
<TableCell sx={{borderBottomLeftRadius: (i === binLevels.length-1 ? 8 : 0)}} padding="none" className={classes.defaultCellStyle}>{binLevels.length - i}</TableCell>
|
||||||
{levelDisplay(level)}
|
{levelDisplay(level, i === binLevels.length-1)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ export const getTheme = (mode: 'light' | 'dark') =>
|
||||||
...(mode === 'dark'
|
...(mode === 'dark'
|
||||||
? {
|
? {
|
||||||
background: {
|
background: {
|
||||||
default: '#0A1118', // was #0F1923 — push it darker
|
default: '#070F17', // was #0F1923 — push it darker
|
||||||
paper: '#141F2A', // was #1A2530 — slightly darker, less grey/blue
|
paper: '#0D1820', // was #1A2530 — slightly darker, less grey/blue
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue