listing devices and linking them to their respoective page

This commit is contained in:
Carter 2024-12-10 08:35:50 -06:00
parent 03f8ed20ca
commit 84d06d9783
7 changed files with 119 additions and 27 deletions

View file

@ -13,11 +13,11 @@ const useStyles = makeStyles((theme: Theme) => {
border: "none" border: "none"
}, },
tableContainer: { tableContainer: {
margin: theme.spacing(1), // margin: theme.spacing(1),
[theme.breakpoints.up("sm")]: { // [theme.breakpoints.up("sm")]: {
margin: theme.spacing(2) // margin: theme.spacing(2)
}, // },
width: "auto" // width: "auto"
}, },
title: { title: {
padding: theme.spacing(1) padding: theme.spacing(1)
@ -41,6 +41,12 @@ const useStyles = makeStyles((theme: Theme) => {
// padding: theme.spacing(1), // padding: theme.spacing(1),
margin: theme.spacing(1), margin: theme.spacing(1),
},
rowHover: {
cursor: "pointer",
"&:hover": {
backgroundColor: "rgba(150, 150, 150, 0.05)"
}
} }
})}, })},
); );
@ -69,6 +75,7 @@ interface Props<T> {
noDataMessage?: string; noDataMessage?: string;
renderMobile?: (row: T) => JSX.Element; renderMobile?: (row: T) => JSX.Element;
hideKeys?: boolean; hideKeys?: boolean;
onRowClick?: (row: T) => void;
} }
export default function ResponsiveTable<T extends Object>(props: Props<T>) { export default function ResponsiveTable<T extends Object>(props: Props<T>) {
@ -90,6 +97,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
noDataMessage, noDataMessage,
renderMobile, renderMobile,
hideKeys, hideKeys,
onRowClick
} = props } = props
const classes = useStyles(); const classes = useStyles();
@ -191,7 +199,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
) )
if (subtitle) return ( if (subtitle) return (
<Box className={classes.subtitle}> <Box className={classes.subtitle}>
{subtitle} haha lol {subtitle}
</Box> </Box>
) )
return null return null
@ -358,7 +366,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
rows.map((row, index) => { rows.map((row, index) => {
return ( return (
<React.Fragment key={"row-"+index}> <React.Fragment key={"row-"+index}>
<TableRow > <TableRow onClick={() => onRowClick&&onRowClick(row)} className={onRowClick ? classes.rowHover : undefined}>
{renderGutter && {renderGutter &&
<TableCell width={1}> <TableCell width={1}>
<IconButton sx={{ <IconButton sx={{

View file

@ -71,6 +71,7 @@ export default function Router(props: Props) {
<Route path="/teams" element={<Teams/>} /> <Route path="/teams" element={<Teams/>} />
<Route path="/teams/:teamID" element={<TeamPage/>} /> <Route path="/teams/:teamID" element={<TeamPage/>} />
<Route path="/users" element={<Users/>} /> <Route path="/users" element={<Users/>} />
<Route path="*/devices" element={<Devices/>} />
<Route path="/devices" element={<Devices/>} /> <Route path="/devices" element={<Devices/>} />
{/* <Route {/* <Route
path="/teams/:teamID" path="/teams/:teamID"

View file

@ -1,20 +1,41 @@
import { DeveloperBoard as ProvisionIcon } from "@mui/icons-material"; import { DeveloperBoard as ProvisionIcon } from "@mui/icons-material";
import { IconButton, Theme, Tooltip } from "@mui/material"; import { IconButton, Theme, Tooltip, Typography } from "@mui/material";
import { blue } from "@mui/material/colors"; import { blue } from "@mui/material/colors";
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
import ResponsiveTable, { Column } from "common/ResponsiveTable";
import ProvisionDevice from "device/ProvisionDevice"; import ProvisionDevice from "device/ProvisionDevice";
import { useState } from "react"; import { pond } from "protobuf-ts/pond";
import { useDeviceAPI } 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";
const useStyles = makeStyles((theme: Theme) => { const useStyles = makeStyles((theme: Theme) => {
return ({ return ({
provisionIcon: { provisionIcon: {
color: blue["700"] color: blue["700"]
},
cellContainer: {
padding: theme.spacing(1)
} }
}); });
}); });
export default function Devices() { export default function Devices() {
const isMobile = useMobile();
const classes = useStyles(); const classes = useStyles();
const navigate = useNavigate();
const location = useLocation();
const deviceAPI = useDeviceAPI();
const [limit, setLimit] = useState(10);
const [offset, setOffset] = 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 [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
const openProvisionDialog = () => { const openProvisionDialog = () => {
@ -25,18 +46,73 @@ export default function Devices() {
setIsProvisionDialogOpen(false); setIsProvisionDialogOpen(false);
}; };
return( const loadDevices = () => {
<> deviceAPI.list(limit, offset*limit, order, orderBy, search).then(resp => {
setDevices(resp.data.devices)
setTotal(resp.data.total)
})
}
useEffect(() => {
loadDevices()
}, [limit, offset, order, orderBy, search])
const handleChange = (event: any) => {
setLimit(event.target.value);
};
const appendToUrl = (appendage: number | string) => {
const basePath = location.pathname.replace(/\/$/, ""); // Ensure no trailing slash
return(`${basePath}/${appendage}`);
};
const toDevice = (device: pond.Device) => {
navigate(appendToUrl(or(device.settings?.deviceId, "")))
};
const columns = (): Column<pond.Device>[] => {
return [{
title: "Device",
render: (device) => {
return (
<Typography className={classes.cellContainer}>
{device.settings?.name ? device.settings.name : "Device " + device.settings?.deviceId}
</Typography>
)
}
}]
}
const provisionButton= () => {
return (
<Tooltip title="Provision Device"> <Tooltip title="Provision Device">
<IconButton onClick={openProvisionDialog} aria-label="Provision Device"> <IconButton onClick={openProvisionDialog} aria-label="Provision Device">
<ProvisionIcon className={classes.provisionIcon} /> <ProvisionIcon className={classes.provisionIcon} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
)
}
return(
<PageContainer padding={isMobile ? 0 : 2}>
<ResponsiveTable<pond.Device>
title="Devices"
subtitle={provisionButton()}
rows={devices}
columns={columns()}
total={total}
pageSize={limit}
page={offset}
setPage={setOffset}
handleRowsPerPageChange={handleChange}
onRowClick={toDevice}
setSearchText={setSearch}
/>
<ProvisionDevice <ProvisionDevice
isOpen={isProvisionDialogOpen} isOpen={isProvisionDialogOpen}
refreshCallback={/*load*/()=>{}} refreshCallback={loadDevices}
closeDialogCallback={closeProvisionDialog} closeDialogCallback={closeProvisionDialog}
/> />
</> </PageContainer>
) )
} }

View file

@ -38,11 +38,12 @@ interface Props extends PropsWithChildren{
fullViewport?: boolean; fullViewport?: boolean;
isCenterCenter?: boolean; isCenterCenter?: boolean;
sx?: SxProps<Theme>; sx?: SxProps<Theme>;
padding?: number;
} }
export const PageContainer: React.FunctionComponent<Props> = props => { export const PageContainer: React.FunctionComponent<Props> = props => {
const classes = useStyles(); const classes = useStyles();
const { children, fullViewport, isCenterCenter, sx } = props; const { children, fullViewport, isCenterCenter, sx, padding } = props;
// let fullViewport = false // let fullViewport = false
// let isCenterCenter = false // let isCenterCenter = false
return ( return (
@ -51,7 +52,10 @@ export const PageContainer: React.FunctionComponent<Props> = props => {
fullViewport ? classes.fullViewportContainer : classes.pageContainer, fullViewport ? classes.fullViewportContainer : classes.pageContainer,
isCenterCenter && classes.centerCenter isCenterCenter && classes.centerCenter
)} )}
sx={sx} sx={{
...sx, // Spread existing sx
...(padding !== undefined && { padding }), // Conditionally add padding
}}
disableGutters disableGutters
maxWidth={false} maxWidth={false}
children={<React.Fragment>{children}</React.Fragment>} children={<React.Fragment>{children}</React.Fragment>}

View file

@ -199,7 +199,7 @@ export default function TeamPage() {
} }
return ( return (
<PageContainer sx={{ padding: isMobile ? 1 : 2 }}> <PageContainer padding={isMobile ? 0 : 2}>
<Box className={classes.container}> <Box className={classes.container}>
<Box className={classes.leftBox}> <Box className={classes.leftBox}>
{title()} {title()}

View file

@ -1,9 +1,11 @@
import { useMobile } from "hooks";
import PageContainer from "pages/PageContainer"; import PageContainer from "pages/PageContainer";
import TeamList from "teams/TeamList"; import TeamList from "teams/TeamList";
export default function Teams() { export default function Teams() {
const isMobile = useMobile()
return ( return (
<PageContainer> <PageContainer padding={isMobile ? 0 : 2}>
<TeamList /> <TeamList />
</PageContainer> </PageContainer>
); );

View file

@ -22,7 +22,7 @@ import {
} from "@mui/material"; } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog"; import ResponsiveDialog from "common/ResponsiveDialog";
import SearchSelect, { Option } from "common/SearchSelect"; import SearchSelect, { Option } from "common/SearchSelect";
import { useSnackbar, useUserAPI } from "hooks"; import { useMobile, useSnackbar, useUserAPI } from "hooks";
import { User } from "models"; import { User } from "models";
import PageContainer from "pages/PageContainer"; import PageContainer from "pages/PageContainer";
// import { ListUsersResponse } from "providers/pond/userAPI"; // import { ListUsersResponse } from "providers/pond/userAPI";
@ -88,6 +88,7 @@ const useStyles = makeStyles((theme: Theme) => {
export default function Users() { export default function Users() {
const classes = useStyles(); const classes = useStyles();
const isMobile = useMobile();
const userAPI = useUserAPI(); const userAPI = useUserAPI();
// let tableRef: any = React.createRef(); // let tableRef: any = React.createRef();
const { error, success } = useSnackbar(); const { error, success } = useSnackbar();
@ -347,7 +348,7 @@ export default function Users() {
} }
return ( return (
<PageContainer> <PageContainer padding={isMobile ? 0 : 2}>
<ResponsiveTable<User> <ResponsiveTable<User>
title="Users" title="Users"
rows={rows} rows={rows}