247 lines
No EOL
7 KiB
TypeScript
247 lines
No EOL
7 KiB
TypeScript
import { Box, Chip, Grid2, Theme, Tooltip } from "@mui/material";
|
|
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
|
import { Device, Group } from "models"
|
|
import { useEffect, useState } from "react"
|
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
|
import PageContainer from "./PageContainer";
|
|
import { useDeviceAPI, useGlobalState, useGroupAPI } from "providers";
|
|
import GroupActions from "group/GroupActions";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
|
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
|
import { or } from "utils/types";
|
|
import { appendToUrl } from "navigation/Router";
|
|
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
|
import { makeStyles } from "@mui/styles";
|
|
import { blue, green } from "@mui/material/colors";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
buttonBox: {
|
|
position: "absolute",
|
|
zIndex: 1,
|
|
right: theme.spacing(4),
|
|
transform: "translateY(-50%)", // Move it up by half its height
|
|
transition: "opacity 0.2s ease-in-out",
|
|
opacity: 1,
|
|
// top: 0,
|
|
},
|
|
fadeBox: {
|
|
opacity: 0, // Fully transparent by default
|
|
transition: "opacity 0.2s ease-in-out", // Smooth transition
|
|
"&:hover": {
|
|
opacity: 1, // Fully opaque on hover
|
|
},
|
|
},
|
|
provisionIcon: {
|
|
color: blue["700"]
|
|
},
|
|
cellContainer: {
|
|
margin: theme.spacing(1),
|
|
marginLeft: theme.spacing(2),
|
|
display: "flex",
|
|
// justifyContent: "center",
|
|
},
|
|
green: {
|
|
color: green["600"]
|
|
},
|
|
tab: {
|
|
display: "block",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
maxWidth: theme.spacing(26),
|
|
whiteSpace: "nowrap",
|
|
padding: theme.spacing(1.5)
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
export default function GroupPage() {
|
|
|
|
const groupAPI = useGroupAPI()
|
|
const deviceAPI = useDeviceAPI()
|
|
const navigate = useNavigate()
|
|
const classes = useStyles()
|
|
|
|
const groupID = parseInt(useParams<{ groupID: string }>()?.groupID ?? "0");
|
|
const { state } = useLocation();
|
|
const [{as}] = useGlobalState();
|
|
const [group, setGroup] = useState<Group>(state?.group ? Group.create(state.group) : Group.create())
|
|
const [loadingGroup, setLoadingGroup] = useState(false)
|
|
const [loadingDevices, setLoadingDevices] = useState(false)
|
|
const [devices, setDevices] = useState<Device[]>([])
|
|
const [permissions, setPermissions] = useState<pond.Permission[]>([])
|
|
const [limit, setLimit] = useState(10)
|
|
const [total, setTotal] = useState(0)
|
|
const [page, setPage] = useState(0)
|
|
const [order, ] = useState<"asc" | "desc">("asc")
|
|
const [orderBy, ] = useState("")
|
|
const [search, setSearch] = useState("")
|
|
|
|
const loadGroup = () => {
|
|
setLoadingGroup(true)
|
|
groupAPI.getGroupAndPermissions(groupID).then(resp => {
|
|
if (resp.data.group) setGroup(Group.create(resp.data.group))
|
|
let newPerms: pond.Permission[] = []
|
|
resp.data.permissions.forEach(perm => {
|
|
if (typeof(perm) === "string") {
|
|
const permNumber = pond.Permission[perm as keyof typeof pond.Permission]
|
|
newPerms.push(permNumber)
|
|
} else {
|
|
newPerms.push(perm)
|
|
}
|
|
})
|
|
setPermissions(newPerms)
|
|
}).finally(() => {
|
|
setLoadingGroup(false)
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
// if (group.id() === groupID) return;
|
|
loadGroup()
|
|
// }, [group, groupID])
|
|
}, [])
|
|
|
|
const getKeys = () => {
|
|
let keys = getContextKeys()
|
|
keys.push(groupID.toString())
|
|
return keys
|
|
}
|
|
|
|
const getTypes = () => {
|
|
let keys = getContextTypes()
|
|
keys.push("group")
|
|
return keys
|
|
}
|
|
|
|
|
|
const loadDevices = () => {
|
|
setLoadingDevices(true)
|
|
deviceAPI.list(
|
|
limit,
|
|
page*limit,
|
|
order,
|
|
orderBy,
|
|
search,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
getKeys(),
|
|
getTypes(),
|
|
undefined,
|
|
undefined,
|
|
as
|
|
).then(resp => {
|
|
let newDevices: Device[] = [];
|
|
resp.data.devices.forEach(device => {
|
|
newDevices.push(Device.create(device))
|
|
})
|
|
setDevices(newDevices)
|
|
setTotal(resp.data.total)
|
|
}).finally(() => {
|
|
setLoadingDevices(false)
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadDevices()
|
|
}, [group, limit, page, order, orderBy, search])
|
|
|
|
const toDevice = (device: Device) => {
|
|
navigate(appendToUrl(or("devices/"+device.settings?.deviceId, "")), { state: {device: device} })
|
|
};
|
|
|
|
const columns = (): Column<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 > 20
|
|
? description.substring(0, 20) + "..."
|
|
: description}
|
|
</span>
|
|
</Tooltip>
|
|
</Box>
|
|
)
|
|
}
|
|
},
|
|
]
|
|
}
|
|
|
|
const handleChange = (event: any) => {
|
|
setLimit(event.target.value);
|
|
};
|
|
|
|
return (
|
|
<PageContainer>
|
|
<Grid2 container justifyContent={"space-between"} alignItems={"center"} paddingBottom={2}>
|
|
<Grid2 >
|
|
<SmartBreadcrumb groupName={group.name()} loading={loadingGroup} />
|
|
</Grid2>
|
|
<Grid2 marginTop={"auto"} marginBottom="auto">
|
|
<GroupActions
|
|
group={group}
|
|
permissions={permissions ? permissions : []}
|
|
devices={devices}
|
|
refreshCallback={() => {}}
|
|
/>
|
|
</Grid2>
|
|
</Grid2>
|
|
<ResponsiveTable<Device>
|
|
title={group.name()}
|
|
subtitle={group.settings.description}
|
|
rows={devices}
|
|
columns={columns()}
|
|
total={total}
|
|
pageSize={limit}
|
|
page={page}
|
|
setPage={setPage}
|
|
handleRowsPerPageChange={handleChange}
|
|
onRowClick={toDevice}
|
|
setSearchText={setSearch}
|
|
isLoading={loadingDevices}
|
|
/>
|
|
</PageContainer>
|
|
)
|
|
} |