implemented device statistics
This commit is contained in:
parent
0076f2d208
commit
3a000b0094
8 changed files with 276 additions and 111 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -4247,7 +4247,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/protobuf-ts": {
|
"node_modules/protobuf-ts": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#91931b1936a92bb0bc24700664adf72eb8a912dd",
|
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#3317c70ecda93740f8552e656429a42f1b15f350",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"protobufjs": "^6.8.8"
|
"protobufjs": "^6.8.8"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
83
src/common/CircleGraphIcon.tsx
Normal file
83
src/common/CircleGraphIcon.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
CircularProgress,
|
||||||
|
SxProps,
|
||||||
|
Theme,
|
||||||
|
useTheme
|
||||||
|
} from "@mui/material";
|
||||||
|
import React, { ReactElement } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
size?: number | string;
|
||||||
|
progress?: number;
|
||||||
|
// color?: OverridableStringUnion<
|
||||||
|
// 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | 'inherit',
|
||||||
|
// CircularProgressPropsColorOverrides
|
||||||
|
// >;
|
||||||
|
color?: string;
|
||||||
|
sx?: SxProps<Theme>,
|
||||||
|
icon: ReactElement,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CircleGraphIcon(props: Props) {
|
||||||
|
|
||||||
|
const { progress, icon } = props;
|
||||||
|
const theme = useTheme()
|
||||||
|
const size = props.size ? props.size : theme.spacing(5)
|
||||||
|
const exists = props.color ? props.color in theme.palette : false
|
||||||
|
|
||||||
|
const color = exists && props.color ? (theme.palette as any)[props.color].main : props.color
|
||||||
|
console.log(props.color)
|
||||||
|
console.log(color)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'relative',
|
||||||
|
display: 'inline-flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
marginBottom: theme.spacing(-1),
|
||||||
|
...props.sx,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Circular Graph */}
|
||||||
|
<CircularProgress
|
||||||
|
variant="determinate"
|
||||||
|
value={progress}
|
||||||
|
// color={color}
|
||||||
|
thickness={4}
|
||||||
|
style={{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
position: 'absolute',
|
||||||
|
}}
|
||||||
|
sx={{ color: {color} }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Icon at the center */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
color: {color}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* CLone element to apply color and size */}
|
||||||
|
{icon &&
|
||||||
|
React.cloneElement(icon, {
|
||||||
|
// color: {color},
|
||||||
|
sx: {
|
||||||
|
width: `calc(${size} - 14px)`,
|
||||||
|
height: `calc(${size} - 14px)`,
|
||||||
|
color: {color},
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
CircularProgress,
|
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
|
@ -20,9 +19,8 @@ import TagSettings from "common/TagSettings";
|
||||||
import { Device, Tag } from "models";
|
import { Device, Tag } from "models";
|
||||||
import { filterByTag } from "pbHelpers/Tag";
|
import { filterByTag } from "pbHelpers/Tag";
|
||||||
import { useDeviceAPI, useSnackbar, useTagAPI } from "providers";
|
import { useDeviceAPI, useSnackbar, useTagAPI } from "providers";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
|
||||||
|
|
||||||
const useStyles = makeStyles((_theme: Theme) => {
|
const useStyles = makeStyles((_theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -137,14 +135,14 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
// const [{ tags }] = useGlobalState();
|
// const [{ tags }] = useGlobalState();
|
||||||
const tagAPI = useTagAPI()
|
const tagAPI = useTagAPI()
|
||||||
const [tags, setTags] = useState<Tag[]>([])
|
const [tags, setTags] = useState<Tag[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
// const [loading, setLoading] = useState(false)
|
||||||
const deviceAPI = useDeviceAPI();
|
const deviceAPI = useDeviceAPI();
|
||||||
const { error } = useSnackbar();
|
const { error } = useSnackbar();
|
||||||
const [deviceTags, setDeviceTags] = useState<string[]>(device.status.tagKeys);
|
const [deviceTags, setDeviceTags] = useState<string[]>(device.status.tagKeys);
|
||||||
const previousDeviceRef = useRef(device);
|
// const previousDeviceRef = useRef(device);
|
||||||
|
|
||||||
const loadTags = () => {
|
const loadTags = () => {
|
||||||
setLoading(true)
|
// setLoading(true)
|
||||||
tagAPI.listTags().then(resp => {
|
tagAPI.listTags().then(resp => {
|
||||||
let newTags: Tag[] = [];
|
let newTags: Tag[] = [];
|
||||||
resp.data.tags.forEach((tag: pond.Tag) => {
|
resp.data.tags.forEach((tag: pond.Tag) => {
|
||||||
|
|
@ -152,12 +150,11 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
})
|
})
|
||||||
setTags(newTags)
|
setTags(newTags)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setLoading(false)
|
// setLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(props.tags)
|
|
||||||
let newTags: string[] = []
|
let newTags: string[] = []
|
||||||
props.tags.forEach(tag => {
|
props.tags.forEach(tag => {
|
||||||
if (tag.settings) newTags.push(tag.settings?.key)
|
if (tag.settings) newTags.push(tag.settings?.key)
|
||||||
|
|
@ -169,34 +166,6 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
loadTags()
|
loadTags()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// // if (previousDeviceRef.current !== device) {
|
|
||||||
// let keys = getContextKeys()
|
|
||||||
// keys.push(device.id().toString())
|
|
||||||
// let types = getContextTypes()
|
|
||||||
// types.push("device")
|
|
||||||
// setDeviceTags(device.status.tagKeys);
|
|
||||||
// previousDeviceRef.current = device;
|
|
||||||
// tagAPI.listTags(
|
|
||||||
// undefined,
|
|
||||||
// undefined,
|
|
||||||
// undefined,
|
|
||||||
// undefined,
|
|
||||||
// undefined,
|
|
||||||
// undefined,
|
|
||||||
// keys,
|
|
||||||
// types,
|
|
||||||
// ).then(resp => {
|
|
||||||
// // console.log(resp.data)
|
|
||||||
// let newTagKeys: string[] = []
|
|
||||||
// resp.data.tags.forEach((tag: any) => {
|
|
||||||
// newTagKeys.push(tag.settings.key)
|
|
||||||
// })
|
|
||||||
// setDeviceTags(newTagKeys)
|
|
||||||
// })
|
|
||||||
// // }
|
|
||||||
// }, [device]);
|
|
||||||
|
|
||||||
const addTag = (tag: Tag) => {
|
const addTag = (tag: Tag) => {
|
||||||
if (!deviceTags.some(dt => dt === tag.key())) {
|
if (!deviceTags.some(dt => dt === tag.key())) {
|
||||||
deviceAPI
|
deviceAPI
|
||||||
|
|
|
||||||
140
src/device/DevicesSummary.tsx
Normal file
140
src/device/DevicesSummary.tsx
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
import {
|
||||||
|
AutoAwesomeMosaic,
|
||||||
|
Battery20,
|
||||||
|
Check,
|
||||||
|
CheckCircle,
|
||||||
|
PermScanWifi,
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
import { Card, Grid2, Typography, useTheme } from "@mui/material";
|
||||||
|
import { blue, green, purple, red } from "@mui/material/colors";
|
||||||
|
import CircleGraphIcon from "common/CircleGraphIcon";
|
||||||
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import { useDeviceAPI } from "providers";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface Props {}
|
||||||
|
|
||||||
|
export default function DevicesSummary(props: Props) {
|
||||||
|
|
||||||
|
const deviceAPI = useDeviceAPI()
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
const [stats, setStats] = useState<pond.DeviceStatistics>(pond.DeviceStatistics.create())
|
||||||
|
const [loadingStats, setLoadingStats] = useState(false)
|
||||||
|
|
||||||
|
const loadDeviceStatistics = () => {
|
||||||
|
setLoadingStats(true)
|
||||||
|
deviceAPI.statistics(
|
||||||
|
getContextKeys(),
|
||||||
|
getContextTypes()
|
||||||
|
).then(resp => {
|
||||||
|
let stats = pond.DeviceStatistics.fromObject(resp.data.stats)
|
||||||
|
setStats(stats)
|
||||||
|
}).finally(() => {
|
||||||
|
setLoadingStats(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadDeviceStatistics()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
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 sx={{ padding: 1 }}>
|
||||||
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
||||||
|
<Grid2>
|
||||||
|
<CircleGraphIcon
|
||||||
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
||||||
|
progress={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 sx={{ padding: 1 }}>
|
||||||
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
||||||
|
<Grid2>
|
||||||
|
<CircleGraphIcon
|
||||||
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
||||||
|
progress={getPercent(stats.active)}
|
||||||
|
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 sx={{ padding: 1 }}>
|
||||||
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
||||||
|
<Grid2>
|
||||||
|
<CircleGraphIcon
|
||||||
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
||||||
|
progress={getPercent(stats.warning)}
|
||||||
|
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 sx={{ padding: 1 }}>
|
||||||
|
<Grid2 container spacing={1} justifyContent={"center"} alignItems={"center"} >
|
||||||
|
<Grid2>
|
||||||
|
<CircleGraphIcon
|
||||||
|
sx={{ marginRight: 1, marginLeft: 1 }}
|
||||||
|
progress={100}
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ChevronRight, People, PeopleOutline, PeopleOutlined, PeopleSharp, Person } from "@mui/icons-material";
|
import { ChevronRight, People, Person } from "@mui/icons-material";
|
||||||
import ChevronLeft from "@mui/icons-material/ChevronLeft";
|
import ChevronLeft from "@mui/icons-material/ChevronLeft";
|
||||||
import { darken, Divider, Grid2 as Grid, IconButton, lighten, List, ListItemButton, ListItemIcon, ListItemText, SwipeableDrawer, Theme, Toolbar, Tooltip, useTheme } from "@mui/material";
|
import { darken, Divider, Grid2 as Grid, IconButton, lighten, List, ListItemButton, ListItemIcon, ListItemText, SwipeableDrawer, Theme, Toolbar, Tooltip, useTheme } from "@mui/material";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
@ -6,7 +6,6 @@ import { useWidth } from "../hooks/useWidth";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import BindaptIcon from "../products/Bindapt/BindaptIcon";
|
import BindaptIcon from "../products/Bindapt/BindaptIcon";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { getSignatureAccentColour } from "services/whiteLabel";
|
|
||||||
|
|
||||||
const drawerWidth = 230;
|
const drawerWidth = 230;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { DeveloperBoard as ProvisionIcon, LibraryAdd as CreateGroupIcon } from "@mui/icons-material";
|
import { DeveloperBoard as ProvisionIcon, LibraryAdd as CreateGroupIcon, CheckCircle } from "@mui/icons-material";
|
||||||
import { Box, Chip, CircularProgress, IconButton, Tab, Tabs, Theme, Tooltip, Typography } from "@mui/material";
|
import { Box, Card, Chip, CircularProgress, Grid2, IconButton, Tab, Tabs, Theme, Tooltip, Typography } from "@mui/material";
|
||||||
import { blue, green } from "@mui/material/colors";
|
import { blue, green } from "@mui/material/colors";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||||
|
|
@ -16,6 +16,8 @@ import { Device, Group } from "models";
|
||||||
import GroupSettings from "group/GroupSettings";
|
import GroupSettings from "group/GroupSettings";
|
||||||
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
||||||
import GroupActions from "group/GroupActions";
|
import GroupActions from "group/GroupActions";
|
||||||
|
import CircleGraphIcon from "common/CircleGraphIcon";
|
||||||
|
import DevicesSummary from "device/DevicesSummary";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -74,6 +76,8 @@ export default function Devices() {
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [devices, setDevices] = useState<Device[]>([])
|
const [devices, setDevices] = useState<Device[]>([])
|
||||||
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
||||||
|
const [stats, setStats] = useState<pond.DeviceStatistics>(pond.DeviceStatistics.create())
|
||||||
|
const [loadingStats, setLoadingStats] = useState(false)
|
||||||
|
|
||||||
const [groupsLoading, setGroupsLoading] = useState(false)
|
const [groupsLoading, setGroupsLoading] = useState(false)
|
||||||
const [groups, setGroups] = useState<Group[]>([]);
|
const [groups, setGroups] = useState<Group[]>([]);
|
||||||
|
|
@ -190,10 +194,28 @@ export default function Devices() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadDeviceStatistics = () => {
|
||||||
|
setLoadingStats(true)
|
||||||
|
deviceAPI.statistics(
|
||||||
|
getContextKeys(),
|
||||||
|
getContextTypes()
|
||||||
|
).then(resp => {
|
||||||
|
let stats = pond.DeviceStatistics.fromObject(resp.data.stats)
|
||||||
|
// console.log(stats)
|
||||||
|
setStats(stats)
|
||||||
|
}).finally(() => {
|
||||||
|
setLoadingStats(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadDevices()
|
loadDevices()
|
||||||
}, [limit, page, order, orderBy, search, tab])
|
}, [limit, page, order, orderBy, search, tab])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadDeviceStatistics()
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log("groups loaded")
|
// console.log("groups loaded")
|
||||||
loadGroups()
|
loadGroups()
|
||||||
|
|
@ -313,78 +335,9 @@ export default function Devices() {
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface MyTabButtonProps {
|
|
||||||
// onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
||||||
// disabled?: boolean;
|
|
||||||
// }
|
|
||||||
// const [leftButtonProps, setLeftButtonProps] = useState<MyTabButtonProps>({disabled: true})
|
|
||||||
// const [rightButtonProps, setRightButtonProps] = useState<MyTabButtonProps>({disabled: true})
|
|
||||||
// Use refs to store the props received during render
|
|
||||||
// const leftButtonRef = useRef<MyTabButtonProps>({});
|
|
||||||
// const rightButtonRef = useRef<MyTabButtonProps>({});
|
|
||||||
// const [tabsHovered, setTabsHovered] = useState(false)
|
|
||||||
// const location = useLocation()
|
|
||||||
|
|
||||||
// Update state using useEffect based on ref changes (the props received)
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (leftButtonRef.current.disabled !== leftButtonProps.disabled) {
|
|
||||||
// setLeftButtonProps({ disabled: leftButtonRef.current.disabled, onClick: leftButtonRef.current.onClick });
|
|
||||||
// }
|
|
||||||
// if (rightButtonRef.current.disabled !== rightButtonProps.disabled) {
|
|
||||||
// setRightButtonProps({ disabled: rightButtonRef.current.disabled, onClick: rightButtonRef.current.onClick });
|
|
||||||
// }
|
|
||||||
// }, [leftButtonRef.current, rightButtonRef.current]);
|
|
||||||
|
|
||||||
// const handleScrollButtonProps = (props: TabScrollButtonProps) => {
|
|
||||||
// if ((props.direction === "left" &&
|
|
||||||
// leftButtonProps.disabled !== props.disabled)
|
|
||||||
// ) {
|
|
||||||
// let newProps: any = {}
|
|
||||||
// newProps.disabled = props.disabled
|
|
||||||
// newProps.onClick = props.onClick
|
|
||||||
// setLeftButtonProps(newProps)
|
|
||||||
// // leftButtonRef.current = newProps
|
|
||||||
// }
|
|
||||||
// if (props.direction === "right" &&
|
|
||||||
// rightButtonProps.disabled !== props.disabled
|
|
||||||
// ) {
|
|
||||||
// let newProps: any = {}
|
|
||||||
// newProps.disabled = props.disabled
|
|
||||||
// newProps.onClick = props.onClick
|
|
||||||
// setRightButtonProps(newProps)
|
|
||||||
// // rightButtonRef.current = newProps
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (tab !== "all") {
|
|
||||||
// const group = groups[typeof(tab) === "number" ? tab-1 : parseInt(tab)-1]
|
|
||||||
// const url = location.pathname;
|
|
||||||
// if (group) {
|
|
||||||
// const updatedUrl = url.replace(
|
|
||||||
// /(\/groups\/[^/]+)?\/devices$/, // Matches "/groups/some-id/devices" or just "/devices"
|
|
||||||
// `/groups/${group.id()}/devices`
|
|
||||||
// );
|
|
||||||
// navigate(updatedUrl, { replace: true });
|
|
||||||
// loadDevices()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// const url = location.pathname;
|
|
||||||
// const updatedUrl = url.replace(
|
|
||||||
// /(\/groups\/[^/]+)?\/devices$/, // Matches "/groups/some-id/devices" or just "/devices"
|
|
||||||
// `/devices`
|
|
||||||
// );
|
|
||||||
// navigate(updatedUrl, { replace: true });
|
|
||||||
// loadDevices()
|
|
||||||
// }
|
|
||||||
// }, [tab])
|
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<PageContainer padding={isMobile ? 0 : 2}>
|
<PageContainer padding={isMobile ? 0 : 2}>
|
||||||
{/* <SmartBreadcrumb /> */}
|
|
||||||
<Box
|
<Box
|
||||||
// onMouseEnter={() => setTabsHovered(true)}
|
|
||||||
// onMouseLeave={() => setTabsHovered(false)}
|
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
border: "1px solid rgba(150, 150, 150, 0.2)",
|
border: "1px solid rgba(150, 150, 150, 0.2)",
|
||||||
|
|
@ -410,6 +363,7 @@ export default function Devices() {
|
||||||
<Tab wrapped value={"never"} label={<CreateGroupIcon className={classes.green} />} onClick={() => openGroupSettings(undefined, "add")} />
|
<Tab wrapped value={"never"} label={<CreateGroupIcon className={classes.green} />} onClick={() => openGroupSettings(undefined, "add")} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
<DevicesSummary />
|
||||||
<ResponsiveTable<Device>
|
<ResponsiveTable<Device>
|
||||||
title={<SmartBreadcrumb prependPaths={getGroup() && ["groups", getGroup().id().toString()]} groupName={getGroup() && getGroup().name()} paddingBottom={1}/>}
|
title={<SmartBreadcrumb prependPaths={getGroup() && ["groups", getGroup().id().toString()]} groupName={getGroup() && getGroup().name()} paddingBottom={1}/>}
|
||||||
subtitle={subtitle()}
|
subtitle={subtitle()}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import BindaptDarkIcon from "../../assets/products/bindapt/bindaptPlusDark.png";
|
import BindaptDarkIcon from "../../assets/products/bindapt/bindaptPlusDark.png";
|
||||||
import BindaptLightIcon from "../../assets/products/bindapt/bindaptPlusLight.png";
|
import BindaptLightIcon from "../../assets/products/bindapt/bindaptPlusLight.png";
|
||||||
import { ImgIcon } from "../../common/ImgIcon";
|
import { ImgIcon } from "../../common/ImgIcon";
|
||||||
import React from "react";
|
|
||||||
import { useThemeType } from "../../hooks/useThemeType";
|
import { useThemeType } from "../../hooks/useThemeType";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ export interface IDeviceAPIContext {
|
||||||
keys?: string[],
|
keys?: string[],
|
||||||
types?: string[]
|
types?: string[]
|
||||||
) => Promise<any>;
|
) => Promise<any>;
|
||||||
|
statistics: (
|
||||||
|
keys?: string[],
|
||||||
|
types?: string[]
|
||||||
|
) => Promise<any>;
|
||||||
tag: (id: number, tag: string) => Promise<any>;
|
tag: (id: number, tag: string) => Promise<any>;
|
||||||
untag: (id: number, tag: string) => Promise<any>;
|
untag: (id: number, tag: string) => Promise<any>;
|
||||||
getDatacap: (id: number) => Promise<any>;
|
getDatacap: (id: number) => Promise<any>;
|
||||||
|
|
@ -177,6 +181,22 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
||||||
return get(url);
|
return get(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const statistics = (
|
||||||
|
keys?: string[],
|
||||||
|
types?: string[]
|
||||||
|
) => {
|
||||||
|
if (types && types.length > 0 && types[0] === "team")
|
||||||
|
return get(
|
||||||
|
pondURL(
|
||||||
|
"/deviceStatistics/" +
|
||||||
|
(keys ? "?keys=" + keys.toString() : "") +
|
||||||
|
(types ? "&types=" + types.toString() : "")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const url = pondURL("/deviceStatistics");
|
||||||
|
return get<pond.GetDeviceStatisticsResponse>(url);
|
||||||
|
};
|
||||||
|
|
||||||
const getDevicePageData = (
|
const getDevicePageData = (
|
||||||
id: number | string,
|
id: number | string,
|
||||||
keys?: string[],
|
keys?: string[],
|
||||||
|
|
@ -615,6 +635,7 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
||||||
update,
|
update,
|
||||||
remove,
|
remove,
|
||||||
get: getDevice,
|
get: getDevice,
|
||||||
|
statistics,
|
||||||
getPageData: getDevicePageData,
|
getPageData: getDevicePageData,
|
||||||
getMulti,
|
getMulti,
|
||||||
getGeoJson: getDeviceGeoJSON,
|
getGeoJson: getDeviceGeoJSON,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue