added ability to prepend paths to smart breadcrumb
This commit is contained in:
parent
2d98917322
commit
77fb5a6bcf
5 changed files with 49 additions and 25 deletions
|
|
@ -85,6 +85,7 @@ interface Props<T> {
|
||||||
renderMobile?: (row: T) => JSX.Element;
|
renderMobile?: (row: T) => JSX.Element;
|
||||||
hideKeys?: boolean;
|
hideKeys?: boolean;
|
||||||
onRowClick?: (row: T) => void;
|
onRowClick?: (row: T) => void;
|
||||||
|
actions?: string | JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
|
|
@ -106,7 +107,8 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
noDataMessage,
|
noDataMessage,
|
||||||
renderMobile,
|
renderMobile,
|
||||||
hideKeys,
|
hideKeys,
|
||||||
onRowClick
|
onRowClick,
|
||||||
|
actions
|
||||||
} = props
|
} = props
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
|
@ -334,9 +336,16 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
</Box>
|
</Box>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<Grid2>
|
<Grid2>
|
||||||
{setSearchText &&<Box className={classes.searchContainer}>
|
<Grid2 container alignItems={"center"} spacing={1}>
|
||||||
{searchBar()}
|
<Grid2>
|
||||||
</Box>}
|
{actions && actions}
|
||||||
|
</Grid2>
|
||||||
|
<Grid2>
|
||||||
|
{setSearchText &&<Box className={classes.searchContainer}>
|
||||||
|
{searchBar()}
|
||||||
|
</Box>}
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ interface Props {
|
||||||
paddingRight?: number;
|
paddingRight?: number;
|
||||||
paddingTop?: number;
|
paddingTop?: number;
|
||||||
paddingBottom?: number;
|
paddingBottom?: number;
|
||||||
|
prependPaths?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LinkRouterProps extends LinkProps {
|
interface LinkRouterProps extends LinkProps {
|
||||||
|
|
@ -68,6 +69,7 @@ interface LinkRouterProps extends LinkProps {
|
||||||
const LinkRouter = (props: LinkRouterProps) => <Link {...props} component={RouterLink as any} />;
|
const LinkRouter = (props: LinkRouterProps) => <Link {...props} component={RouterLink as any} />;
|
||||||
|
|
||||||
export default function SmartBreadcrumb(props: Props) {
|
export default function SmartBreadcrumb(props: Props) {
|
||||||
|
const prependPaths = props.prependPaths;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
|
|
@ -75,6 +77,12 @@ export default function SmartBreadcrumb(props: Props) {
|
||||||
const { loading, reload } = props;
|
const { loading, reload } = props;
|
||||||
|
|
||||||
const groupID = (): string => {
|
const groupID = (): string => {
|
||||||
|
let index = prependPaths?.indexOf("groups")
|
||||||
|
if (index !== undefined && index > -1) {
|
||||||
|
if (prependPaths && prependPaths[index+1]) {
|
||||||
|
return prependPaths[index+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
const regexMatch = or(location.pathname.match(/\/groups\/([^/]+)/), [])
|
const regexMatch = or(location.pathname.match(/\/groups\/([^/]+)/), [])
|
||||||
// console.log(or(regexMatch[1], "0").toString())
|
// console.log(or(regexMatch[1], "0").toString())
|
||||||
return or(regexMatch[1], "0").toString();
|
return or(regexMatch[1], "0").toString();
|
||||||
|
|
@ -254,12 +262,14 @@ export default function SmartBreadcrumb(props: Props) {
|
||||||
// console.log(breadcrumbMap)
|
// console.log(breadcrumbMap)
|
||||||
|
|
||||||
let links: ReactNode[] = [];
|
let links: ReactNode[] = [];
|
||||||
const pathnames = location.pathname.split("/").filter((x: any) => x);
|
let pathnames = location.pathname.split("/").filter((x: any) => x);
|
||||||
const blacklist = breadcrumbBlacklist();
|
const blacklist = breadcrumbBlacklist();
|
||||||
|
|
||||||
if (pathnames.length < 1) {
|
if (pathnames.length < 1) {
|
||||||
return [linkComponent("/", "Dashboard", true)];
|
return [linkComponent("/", "Dashboard", true)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prependPaths) pathnames.unshift(...prependPaths)
|
||||||
|
|
||||||
pathnames.forEach((_value: any, index: any) => {
|
pathnames.forEach((_value: any, index: any) => {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||||
import { or } from "utils/types";
|
import { or } from "utils/types";
|
||||||
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
||||||
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
||||||
import { Group } from "models";
|
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";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -59,7 +60,6 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function Devices() {
|
export default function Devices() {
|
||||||
const [groups, setGroups] = useState<Group[]>([]);
|
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
@ -73,11 +73,11 @@ export default function Devices() {
|
||||||
const [orderBy, ] = useState("name");
|
const [orderBy, ] = useState("name");
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [devices, setDevices] = useState<pond.Device[]>([])
|
const [devices, setDevices] = useState<Device[]>([])
|
||||||
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
const [isProvisionDialogOpen, setIsProvisionDialogOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const [groupsLoading, setGroupsLoading] = useState(false)
|
const [groupsLoading, setGroupsLoading] = useState(false)
|
||||||
// const [groups, setGroups] = useState<Group[]>([]);
|
const [groups, setGroups] = useState<Group[]>([]);
|
||||||
const [groupLimit, ] = useState(10);
|
const [groupLimit, ] = useState(10);
|
||||||
const [groupPage, ] = useState(0);
|
const [groupPage, ] = useState(0);
|
||||||
const [orderGroup, ] = useState<"asc" | "desc">("asc");
|
const [orderGroup, ] = useState<"asc" | "desc">("asc");
|
||||||
|
|
@ -153,8 +153,7 @@ export default function Devices() {
|
||||||
resp.data.groups?.forEach((group: pond.Group) => {
|
resp.data.groups?.forEach((group: pond.Group) => {
|
||||||
newGroups.push(Group.create(group))
|
newGroups.push(Group.create(group))
|
||||||
})
|
})
|
||||||
updateGroups(newGroups)
|
updateGroups(newGroups)
|
||||||
// setTotalGroups(resp.data.total)
|
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setGroupsLoading(false)
|
setGroupsLoading(false)
|
||||||
})
|
})
|
||||||
|
|
@ -177,18 +176,19 @@ export default function Devices() {
|
||||||
getKeys(),
|
getKeys(),
|
||||||
getTypes()
|
getTypes()
|
||||||
).then(resp => {
|
).then(resp => {
|
||||||
setDevices(resp.data.devices)
|
let newDevices: Device[] = []
|
||||||
|
resp.data.devices.forEach(device => {
|
||||||
|
newDevices.push(Device.create(device))
|
||||||
|
})
|
||||||
|
setDevices(newDevices)
|
||||||
setTotal(resp.data.total)
|
setTotal(resp.data.total)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setDevicesLoading(false)
|
setDevicesLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadDevices()
|
loadDevices()
|
||||||
|
|
||||||
}, [limit, page, order, orderBy, search, tab])
|
}, [limit, page, order, orderBy, search, tab])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -205,7 +205,7 @@ export default function Devices() {
|
||||||
return(`${basePath}/${appendage}`);
|
return(`${basePath}/${appendage}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toDevice = (device: pond.Device) => {
|
const toDevice = (device: Device) => {
|
||||||
navigate(appendToUrl(or(device.settings?.deviceId, "")), { state: {device: device} })
|
navigate(appendToUrl(or(device.settings?.deviceId, "")), { state: {device: device} })
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -214,7 +214,7 @@ export default function Devices() {
|
||||||
if (newValue === "never") openGroupSettings(undefined, "add");
|
if (newValue === "never") openGroupSettings(undefined, "add");
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = (): Column<pond.Device>[] => {
|
const columns = (): Column<Device>[] => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: "State",
|
title: "State",
|
||||||
|
|
@ -298,6 +298,11 @@ export default function Devices() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getGroup = () => {
|
||||||
|
let group = groups[parseInt(tab)-1]
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
// interface MyTabButtonProps {
|
// interface MyTabButtonProps {
|
||||||
// onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
// onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||||
// disabled?: boolean;
|
// disabled?: boolean;
|
||||||
|
|
@ -395,8 +400,8 @@ 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>
|
||||||
<ResponsiveTable<pond.Device>
|
<ResponsiveTable<Device>
|
||||||
title={<SmartBreadcrumb paddingBottom={1}/>}
|
title={<SmartBreadcrumb prependPaths={getGroup() && ["groups", getGroup().id().toString()]} groupName={getGroup() && getGroup().name()} paddingBottom={1}/>}
|
||||||
subtitle={subtitle()}
|
subtitle={subtitle()}
|
||||||
rows={devices}
|
rows={devices}
|
||||||
columns={columns()}
|
columns={columns()}
|
||||||
|
|
@ -408,6 +413,7 @@ export default function Devices() {
|
||||||
onRowClick={toDevice}
|
onRowClick={toDevice}
|
||||||
setSearchText={setSearch}
|
setSearchText={setSearch}
|
||||||
isLoading={devicesLoading}
|
isLoading={devicesLoading}
|
||||||
|
actions={getGroup() && <GroupActions group={getGroup()} permissions={[]} devices={devices} refreshCallback={loadGroups}/>}
|
||||||
/>
|
/>
|
||||||
<ProvisionDevice
|
<ProvisionDevice
|
||||||
isOpen={isProvisionDialogOpen}
|
isOpen={isProvisionDialogOpen}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Chip, Grid2, Theme, Tooltip, Typography } from "@mui/material";
|
import { Box, Chip, Grid2, Theme, Tooltip } from "@mui/material";
|
||||||
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
||||||
import { Device, Group } from "models"
|
import { Device, Group } from "models"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
@ -14,7 +14,6 @@ import { appendToUrl } from "navigation/Router";
|
||||||
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
import { getDeviceStateHelper } from "pbHelpers/DeviceState";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { blue, green } from "@mui/material/colors";
|
import { blue, green } from "@mui/material/colors";
|
||||||
import { permissionToString } from "pbHelpers/Permission";
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -75,8 +74,8 @@ export default function GroupPage() {
|
||||||
const [limit, setLimit] = useState(0)
|
const [limit, setLimit] = useState(0)
|
||||||
const [total, setTotal] = useState(0)
|
const [total, setTotal] = useState(0)
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [order, setOrder] = useState<"asc" | "desc">("asc")
|
const [order, ] = useState<"asc" | "desc">("asc")
|
||||||
const [orderBy, setOrderBy] = useState("")
|
const [orderBy, ] = useState("")
|
||||||
const [search, setSearch] = useState("")
|
const [search, setSearch] = useState("")
|
||||||
|
|
||||||
const loadGroup = () => {
|
const loadGroup = () => {
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ export default function GroupsPage() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [limit, setLimit] = useState(10)
|
const [limit, setLimit] = useState(10)
|
||||||
const [page, setPage] = useState(0)
|
const [page, setPage] = useState(0)
|
||||||
const [order, setOrder] = useState<"asc" | "desc">("asc")
|
const [order, ] = useState<"asc" | "desc">("asc")
|
||||||
const [orderBy, setOrderBy] = useState("")
|
const [orderBy, ] = useState("")
|
||||||
const [search, setSearch] = useState("")
|
const [search, setSearch] = useState("")
|
||||||
const [total, setTotal] = useState(0)
|
const [total, setTotal] = useState(0)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue