diff --git a/src/common/SmartBreadcrumb.tsx b/src/common/SmartBreadcrumb.tsx new file mode 100644 index 0000000..7661f5f --- /dev/null +++ b/src/common/SmartBreadcrumb.tsx @@ -0,0 +1,336 @@ +import { + Breadcrumbs, + Chip, + IconButton, + Link, + LinkProps, + Skeleton, + Theme, + useTheme +} from "@mui/material"; +import { Replay } from "@mui/icons-material"; +// import Link, { LinkProps } from "@material-ui/core/Link"; +// import { Skeleton } from "@mui/l"; +import { useMobile } from "hooks"; +// import { MatchParams } from "navigation/Routes"; +import React, { ReactNode } from "react"; +import { useLocation } from "react-router"; +import { Link as RouterLink } from "react-router-dom"; +import { or } from "utils/types"; +import { makeStyles } from "@mui/styles"; +import { getThemeType } from "theme/themeType"; + +const useStyles = makeStyles((theme: Theme) => { + // const isMobile = useMobile() + return ({ + chip: { + maxWidth: "150px", + [theme.breakpoints.up("md")]: { + maxWidth: "200px" + }, + color: "inherit", + textDecoration: "none", + '&:hover': { + color: "inherit" + } + }, + textOverflow: { + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + textTransform: "capitalize", + textDecoration: "none" + } + }); +}); + +interface Props { + deviceName?: string; + componentName?: string; + groupName?: string; + binName?: string; + reportTarget?: string; + loading?: boolean; + reload?: () => void; +} + +interface LinkRouterProps extends LinkProps { + to: string; + replace?: boolean; +} + +const LinkRouter = (props: LinkRouterProps) => ; + +export default function SmartBreadcrumb(props: Props) { + const theme = useTheme(); + const location = useLocation(); + const isMobile = useMobile(); + const classes = useStyles(); + const { loading, reload } = props; + + const groupID = (): string => { + const regexMatch = or(location.pathname.match(/\/groups\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const deviceID = (): string => { + const regexMatch = or(location.pathname.match(/\/devices\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const mineKey = (): string => { + const regexMatch = or(location.pathname.match(/\/mines\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const jobsiteKey = (): string => { + const regexMatch = or(location.pathname.match(/\/jobs\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const componentID = (): string => { + const regexMatch = or(location.pathname.match(/\/components\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const reportID = (): string => { + const regexMatch = or(location.pathname.match(/\/reports\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const binID = (): string => { + const regexMatch = or(location.pathname.match(/\/bins\/([^/]+)/), []) + return or(regexMatch[1], "0").toString(); + }; + + const reportIDShort = (): string => { + return reportID().split("-")[0]; + }; + + // Paths + + const dashboardPath = (): string => { + return "/"; + }; + + const groupsPath = (): string => { + return "/groups"; + }; + + const groupPath = (): string => { + return groupsPath() + "/" + groupID(); + }; + + const devicesPath = (): string => { + return "/devices"; + }; + + const devicePath = (): string => { + return devicesPath() + "/" + deviceID(); + }; + + const minesPath = (): string => { + return "/mines"; + }; + + const minePath = (): string => { + return minesPath() + "/" + mineKey(); + }; + + const jobsitesPath = (): string => { + return "/jobsites"; + }; + + const jobsitePath = (): string => { + return jobsitesPath() + "/" + jobsiteKey(); + }; + + const componentsPath = (): string => { + return "/components"; + }; + + const componentPath = (): string => { + return componentsPath() + "/" + componentID(); + }; + + const securityPath = (): string => { + return "/security"; + }; + + const reportPath = (): string => { + return securityPath() + "/" + reportID(); + }; + + const groupDevicesPath = (): string => { + return groupPath() + devicesPath(); + }; + + const groupDevicePath = (): string => { + return groupPath() + devicePath(); + }; + + const groupComponentsPath = (): string => { + return groupPath() + componentsPath(); + }; + + const groupComponentPath = (): string => { + return groupPath() + componentPath(); + }; + + const historyPath = (): string => { + return "/history"; + }; + + const deviceHistoryPath = (): string => { + return devicePath() + historyPath(); + }; + + const groupDeviceHistoryPath = (): string => { + return groupDevicePath() + historyPath(); + }; + + const firmwarePath = (): string => { + return "/firmware"; + }; + + const usersPath = (): string => { + return "/users"; + }; + + const binsPath = (): string => { + return "/bins"; + }; + + const binPath = (): string => { + return binsPath() + "/" + binID(); + }; + + const getBreadcrumbMap = (): { [key: string]: string } => { + const { groupName, deviceName, componentName, reportTarget, binName } = props; + const deviceLabel: string = deviceName ? deviceName : "Device " + deviceID(); + const componentLabel: string = componentName ? componentName : "Component " + componentID(); + const binLabel: string = binName ?? "Bin " + binID(); + const mineLabel: string = "Mine Site"; + const jobsiteLabel: string = "Job Site"; + + return { + [dashboardPath()]: "Dashboard", + [groupsPath()]: "Groups", + [groupPath()]: groupName ? groupName : "Group " + groupID(), + [devicesPath()]: "Devices", + [devicePath()]: deviceLabel, + [minesPath()]: "Mines", + [minePath()]: mineLabel, + [jobsitesPath()]: "Job Sites", + [jobsitePath()]: jobsiteLabel, + [componentsPath()]: "Components", + [componentPath()]: componentLabel, + [securityPath()]: "Security", + [reportPath()]: "Report " + reportIDShort() + (reportTarget ? " (" + reportTarget + ")" : ""), + [groupDevicePath()]: deviceLabel, + [groupComponentPath()]: componentLabel, + [deviceHistoryPath()]: "History", + [groupDeviceHistoryPath()]: "History", + [firmwarePath()]: "Firmware", + [usersPath()]: "Users", + [binsPath()]: "Bins", + [binPath()]: binLabel + }; + }; + + const breadcrumbBlacklist = (): string[] => { + return [dashboardPath(), componentsPath(), groupDevicesPath(), groupComponentsPath()]; + }; + + const breadcrumbLinks = (): ReactNode[] => { + const breadcrumbMap = getBreadcrumbMap(); + + let links: ReactNode[] = []; + const pathnames = location.pathname.split("/").filter((x: any) => x); + const blacklist = breadcrumbBlacklist(); + + // console.log(pathnames) + + if (pathnames.length < 1) { + return [linkComponent("/", "Dashboard", true)]; + } + + pathnames.forEach((_value: any, index: any) => { + const lastPath = index === pathnames.length - 1; + const to = `/${pathnames.slice(0, index + 1).join("/")}`; + + let label = breadcrumbMap[to]; + let result = to.split("/").filter(str => str !== ""); + // console.log(result) + if (!label) { + if (result.length > 1) { + if (result[result.length - 1] !== "components") { + label = breadcrumbMap["/" + result[result.length - 1]]; + console.log(label) + } + } + if (!label && result[result.length - 1] !== "components") { + label = breadcrumbMap["/" + result[result.length - 2] + "/" + result[result.length - 1]]; + console.log(label) + } + } + + if (!blacklist.includes(to) && label) { + links.push(linkComponent(to, label, lastPath)); + } + }); + + // console.log(links) + + return links; + }; + + const linkComponent = (to: string, label: string, lastPath: boolean) => { + return ( + + {label} + + } + className={classes.chip} + /> + ); + }; + + const localReload = () => { + if (reload) { + reload(); + } else { + } + }; + + if (loading === true) return ; + + return ( + + + {breadcrumbLinks()} + + {reload && ( + + + + )} + + ); +} diff --git a/src/pages/Device.tsx b/src/pages/Device.tsx index 0b4985b..6f46dda 100644 --- a/src/pages/Device.tsx +++ b/src/pages/Device.tsx @@ -8,6 +8,7 @@ import { useLocation, useParams } from "react-router-dom"; import PageContainer from "./PageContainer"; import { useMobile } from "hooks"; import LoadingScreen from "app/LoadingScreen"; +import SmartBreadcrumb from "common/SmartBreadcrumb"; const useStyles = makeStyles((theme: Theme) => { // const isMobile = useMobile() @@ -52,9 +53,10 @@ export default function DevicePage() { return ( - + {/* {device.name()} - + */} + ); } \ No newline at end of file diff --git a/src/theme/theme.ts b/src/theme/theme.ts index 44625f9..e05b0f9 100644 --- a/src/theme/theme.ts +++ b/src/theme/theme.ts @@ -234,6 +234,17 @@ function options(themeType: "light" | "dark"): ThemeOptions { }, }, }, + MuiLink: { + styleOverrides: { + root: { + color: 'inherit', // Default link color + textDecoration: 'none', // Remove underline if desired + '&:hover': { + // color: 'red !important', // Your hover color + }, + }, + }, + }, // MuiDataGrid: { // styleOverrides: { // root: {