226 lines
No EOL
6.7 KiB
TypeScript
226 lines
No EOL
6.7 KiB
TypeScript
import {
|
|
AutoAwesomeMosaic,
|
|
Battery20,
|
|
Check,
|
|
PermScanWifi,
|
|
} from "@mui/icons-material";
|
|
import { Card, Grid2, Theme, Typography } from "@mui/material";
|
|
import { green } from "@mui/material/colors";
|
|
import { makeStyles } from "@mui/styles";
|
|
import classNames from "classnames";
|
|
import CircleGraphIcon from "common/CircleGraphIcon";
|
|
import { Group } from "models";
|
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { useDeviceAPI } from "providers";
|
|
import { useEffect, useState } from "react";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
card: {
|
|
padding: theme.spacing(1),
|
|
height: theme.spacing(10),
|
|
alignContent: "center",
|
|
"&:hover": {
|
|
cursor: "pointer",
|
|
backgroundColor: "rgba(150, 150, 150, 0.15)",
|
|
// border: "1px solid rgba(255, 255, 255, 0.25)"
|
|
}
|
|
},
|
|
cardSelected: {
|
|
border: "1px solid " + green[400]
|
|
}
|
|
});
|
|
});
|
|
|
|
interface Props {
|
|
setFieldContains: React.Dispatch<React.SetStateAction<Map<string, string>>>,
|
|
group?: Group;
|
|
}
|
|
|
|
export default function DevicesSummary(props: Props) {
|
|
const { setFieldContains, group } = props;
|
|
const deviceAPI = useDeviceAPI()
|
|
// const theme = useTheme()
|
|
const classes = useStyles()
|
|
|
|
const [stats, setStats] = useState<pond.DeviceStatistics>(pond.DeviceStatistics.create())
|
|
const [loadingStats, setLoadingStats] = useState(false)
|
|
const [selected, setSelected] = useState("");
|
|
|
|
const getKeys = () => {
|
|
let keys = getContextKeys()
|
|
if (group) keys.push(group.id().toString())
|
|
return keys
|
|
}
|
|
|
|
const getTypes = () => {
|
|
let types = getContextTypes()
|
|
if (group) types.push("group")
|
|
return types
|
|
}
|
|
|
|
const loadDeviceStatistics = () => {
|
|
setLoadingStats(true)
|
|
deviceAPI.statistics(
|
|
getKeys(),
|
|
getTypes()
|
|
).then(resp => {
|
|
let stats = pond.DeviceStatistics.fromObject(resp.data.stats)
|
|
setStats(stats)
|
|
}).finally(() => {
|
|
setLoadingStats(false)
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadDeviceStatistics()
|
|
}, [group])
|
|
|
|
useEffect(() => {
|
|
switch (selected) {
|
|
case "total":
|
|
setFieldContains(new Map<string, string>());
|
|
break;
|
|
case "active":
|
|
setFieldContains(new Map<string, string>([["state", "DEVICE_STATE_OK"]]));
|
|
break;
|
|
case "warning":
|
|
setFieldContains(new Map<string, string>([["state", "DEVICE_STATE_LOW_POWER"]]));
|
|
break;
|
|
case "offline":
|
|
setFieldContains(new Map<string, string>([["state", "DEVICE_STATE_MISSING"]]));
|
|
break;
|
|
}
|
|
}, [selected])
|
|
|
|
const getPercent = (devices: number) => {
|
|
const ratio = devices/stats.total
|
|
return ratio*100
|
|
}
|
|
|
|
return (
|
|
<Grid2 container spacing={1} marginBlock={1} >
|
|
<Grid2 size={{xs: 6, sm: 3}}>
|
|
<Card
|
|
className={
|
|
classNames(
|
|
classes.card,
|
|
selected==="total" ? classes.cardSelected : undefined
|
|
)
|
|
}
|
|
onClick={() => setSelected("total")}
|
|
>
|
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
|
<Grid2>
|
|
<CircleGraphIcon
|
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
|
progress={loadingStats ? undefined : 100}
|
|
color={"info"}
|
|
icon={<AutoAwesomeMosaic />}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 marginRight={1}>
|
|
<Typography variant="h6" >
|
|
{stats.total} Devices
|
|
</Typography>
|
|
<Typography variant="subtitle2" color="textSecondary">
|
|
Total
|
|
</Typography>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={{xs: 6, sm: 3}}>
|
|
<Card
|
|
className={
|
|
classNames(
|
|
classes.card,
|
|
selected==="active" ? classes.cardSelected : undefined
|
|
)
|
|
}
|
|
onClick={() => setSelected("active")}
|
|
>
|
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
|
<Grid2>
|
|
<CircleGraphIcon
|
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
|
progress={!loadingStats ? getPercent(stats.active) : undefined}
|
|
color={"success"}
|
|
icon={<Check />}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 marginRight={1}>
|
|
<Typography variant="h6" >
|
|
{stats.active} Devices
|
|
</Typography>
|
|
<Typography variant="subtitle2" color="textSecondary">
|
|
Active
|
|
</Typography>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={{xs: 6, sm: 3}}>
|
|
<Card
|
|
className={
|
|
classNames(
|
|
classes.card,
|
|
selected==="warning" ? classes.cardSelected : undefined
|
|
)
|
|
}
|
|
onClick={() => setSelected("warning")}
|
|
>
|
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
|
<Grid2>
|
|
<CircleGraphIcon
|
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
|
progress={!loadingStats ? getPercent(stats.warning) : undefined}
|
|
color="warning"
|
|
icon={<Battery20 />}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 marginRight={1}>
|
|
<Typography variant="h6" >
|
|
{stats.warning} Devices
|
|
</Typography>
|
|
<Typography variant="subtitle2" color="textSecondary">
|
|
Low Power
|
|
</Typography>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Card>
|
|
</Grid2>
|
|
<Grid2 size={{xs: 6, sm: 3}}>
|
|
<Card
|
|
className={
|
|
classNames(
|
|
classes.card,
|
|
selected==="offline" ? classes.cardSelected : undefined
|
|
)
|
|
}
|
|
onClick={() => setSelected("offline")}
|
|
>
|
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
|
<Grid2>
|
|
<CircleGraphIcon
|
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
|
progress={!loadingStats ? getPercent(stats.offline) : undefined}
|
|
color={green[50]}
|
|
icon={<PermScanWifi />}
|
|
/>
|
|
</Grid2>
|
|
<Grid2 marginRight={1}>
|
|
<Typography variant="h6" >
|
|
{stats.offline} Devices
|
|
</Typography>
|
|
<Typography variant="subtitle2" color="textSecondary">
|
|
Offline
|
|
</Typography>
|
|
</Grid2>
|
|
</Grid2>
|
|
</Card>
|
|
</Grid2>
|
|
</Grid2>
|
|
)
|
|
} |