258 lines
No EOL
7.2 KiB
TypeScript
258 lines
No EOL
7.2 KiB
TypeScript
import { DeveloperBoard as ProvisionIcon, LibraryAdd as CreateGroupIcon } from "@mui/icons-material";
|
|
import { Box, Chip, IconButton, Theme, Tooltip, Typography } from "@mui/material";
|
|
import { blue, green } from "@mui/material/colors";
|
|
import { makeStyles } from "@mui/styles";
|
|
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
|
import ProvisionDevice from "device/ProvisionDevice";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { useDeviceAPI, useGroupAPI } from "providers";
|
|
import { useEffect, useState } from "react";
|
|
import PageContainer from "./PageContainer";
|
|
import { useMobile } from "hooks";
|
|
import { useLocation, useNavigate } from "react-router-dom";
|
|
import { or } from "utils/types";
|
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
|
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
|
import { Group } from "models";
|
|
import GroupSettings from "group/GroupSettings";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
provisionIcon: {
|
|
color: blue["700"]
|
|
},
|
|
cellContainer: {
|
|
margin: theme.spacing(1),
|
|
marginLeft: theme.spacing(2),
|
|
display: "flex",
|
|
// justifyContent: "center",
|
|
},
|
|
green: {
|
|
color: green["600"]
|
|
},
|
|
});
|
|
});
|
|
|
|
export default function Devices() {
|
|
const isMobile = useMobile();
|
|
const classes = useStyles();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const deviceAPI = useDeviceAPI();
|
|
const groupAPI = useGroupAPI();
|
|
const [limit, setLimit] = useState(10);
|
|
const [page, setPage] = useState(0);
|
|
const [order, ] = useState<"asc" | "desc">("asc");
|
|
const [orderBy, ] = useState("name");
|
|
const [search, setSearch] = useState("");
|
|
const [total, setTotal] = useState(0);
|
|
const [devices, setDevices] = useState<pond.Device[]>([])
|
|
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
|
|
|
const [groupLimit, setGroupLimit] = useState(10);
|
|
const [groupPage, setGroupPage] = useState(0);
|
|
const [orderGroup, ] = useState<"asc" | "desc">("asc");
|
|
const [orderGroupBy, ] = useState("name");
|
|
const [searchGroup, setGroupSearch] = useState("");
|
|
const [totalGroups, setTotalGroups] = useState(0);
|
|
|
|
const [selectedGroup, setSelectedGroup] = useState<Group | undefined>(undefined);
|
|
const [groupSettingsMode, setGroupSettingsMode] = useState<
|
|
"add" | "update" | "remove" | undefined
|
|
>(undefined);
|
|
const [groupSettingsIsOpen, setGroupSettingsIsOpen] = useState<boolean>(false);
|
|
|
|
const openProvisionDialog = () => {
|
|
setIsProvisionDialogOpen(true);
|
|
};
|
|
|
|
const closeProvisionDialog = () => {
|
|
setIsProvisionDialogOpen(false);
|
|
};
|
|
|
|
const openGroupSettings = (
|
|
group: Group | undefined,
|
|
mode: "add" | "update" | "remove" | undefined
|
|
) => {
|
|
setGroupSettingsIsOpen(true);
|
|
setGroupSettingsMode(mode);
|
|
setSelectedGroup(group);
|
|
};
|
|
|
|
const closeGroupSettings = (event: any) => {
|
|
setGroupSettingsIsOpen(false);
|
|
setGroupSettingsMode(undefined);
|
|
setSelectedGroup(undefined);
|
|
};
|
|
|
|
const loadGroups = () => {
|
|
groupAPI.listGroups(
|
|
groupLimit,
|
|
groupPage*groupLimit,
|
|
orderGroup,
|
|
orderGroupBy,
|
|
searchGroup,
|
|
).then(resp => {
|
|
console.log(resp.data)
|
|
})
|
|
}
|
|
|
|
const loadDevices = () => {
|
|
deviceAPI.list(
|
|
limit,
|
|
page*limit,
|
|
order,
|
|
orderBy,
|
|
search,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
getContextKeys(),
|
|
getContextTypes()
|
|
).then(resp => {
|
|
setDevices(resp.data.devices)
|
|
setTotal(resp.data.total)
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadDevices()
|
|
}, [limit, page, order, orderBy, search])
|
|
|
|
useEffect(() => {
|
|
loadGroups()
|
|
}, [groupLimit, groupPage, orderGroup, orderGroupBy, searchGroup])
|
|
|
|
|
|
const handleChange = (event: any) => {
|
|
setLimit(event.target.value);
|
|
};
|
|
|
|
const appendToUrl = (appendage: number | string) => {
|
|
const basePath = location.pathname.replace(/\/$/, "");
|
|
return(`${basePath}/${appendage}`);
|
|
};
|
|
|
|
const toDevice = (device: pond.Device) => {
|
|
navigate(appendToUrl(or(device.settings?.deviceId, "")), { state: {device: device} })
|
|
};
|
|
|
|
const columns = (): Column<pond.Device>[] => {
|
|
return [
|
|
{
|
|
title: "State",
|
|
render: (device) => {
|
|
const status = device.status ?? pond.DeviceStatus.create()
|
|
const deviceStateHelper = getDeviceStateHelper(status.state);
|
|
return (
|
|
<Box className={classes.cellContainer}>
|
|
<Tooltip title={deviceStateHelper.description}>{deviceStateHelper.icon}</Tooltip>
|
|
</Box>
|
|
)
|
|
}
|
|
},
|
|
{
|
|
title: "ID",
|
|
render: device => <span className={classes.cellContainer}>
|
|
{device.settings?.deviceId}
|
|
</span>
|
|
},
|
|
{
|
|
title: "Device",
|
|
render: (device) => {
|
|
return (
|
|
<Box className={classes.cellContainer} >
|
|
<Chip
|
|
variant="outlined"
|
|
label={device.settings?.name ? device.settings.name : "Device " + device.settings?.deviceId}
|
|
/>
|
|
</Box>
|
|
|
|
)
|
|
}
|
|
},
|
|
{
|
|
title: "Description",
|
|
render: device => {
|
|
const description = device.settings?.description ?? ""
|
|
return (
|
|
<Box className={classes.cellContainer}>
|
|
<Tooltip title={device.settings?.description}>
|
|
<span>
|
|
{description.length > 10
|
|
? description.substring(0, 10) + "..."
|
|
: description}
|
|
</span>
|
|
</Tooltip>
|
|
</Box>
|
|
)
|
|
}
|
|
},
|
|
]
|
|
}
|
|
|
|
const provisionButton= () => {
|
|
return (
|
|
<Tooltip title="Provision Device">
|
|
<IconButton onClick={openProvisionDialog} aria-label="Provision Device">
|
|
<ProvisionIcon className={classes.provisionIcon} />
|
|
</IconButton>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
|
|
const groupButton = () => {
|
|
return (
|
|
<Tooltip title="Create Group">
|
|
<IconButton
|
|
onClick={() => openGroupSettings(undefined, "add")}
|
|
aria-label="Create Group">
|
|
<CreateGroupIcon className={classes.green} />
|
|
</IconButton>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
|
|
const subtitle = () => {
|
|
return (
|
|
<>
|
|
{provisionButton()}
|
|
{groupButton()}
|
|
</>
|
|
)
|
|
}
|
|
|
|
return(
|
|
<PageContainer padding={isMobile ? 0 : 2}>
|
|
<ResponsiveTable<pond.Device>
|
|
title="Devices"
|
|
subtitle={subtitle()}
|
|
rows={devices}
|
|
columns={columns()}
|
|
total={total}
|
|
pageSize={limit}
|
|
page={page}
|
|
setPage={setPage}
|
|
handleRowsPerPageChange={handleChange}
|
|
onRowClick={toDevice}
|
|
setSearchText={setSearch}
|
|
/>
|
|
<ProvisionDevice
|
|
isOpen={isProvisionDialogOpen}
|
|
refreshCallback={loadDevices}
|
|
closeDialogCallback={closeProvisionDialog}
|
|
/>
|
|
<GroupSettings
|
|
initialGroup={selectedGroup}
|
|
mode={groupSettingsMode}
|
|
isDialogOpen={groupSettingsIsOpen}
|
|
closeDialogCallback={closeGroupSettings}
|
|
refreshCallback={loadGroups}
|
|
canEdit={true}
|
|
/>
|
|
</PageContainer>
|
|
)
|
|
} |