|  |  |  | 
|---|
|  |  |  | import React, { useState, useEffect, useMemo, Suspense } from "react"; | 
|---|
|  |  |  | import { | 
|---|
|  |  |  | useTranslate, | 
|---|
|  |  |  | DashboardMenuItem, | 
|---|
|  |  |  | MenuItemLink, | 
|---|
|  |  |  | Menu, | 
|---|
|  |  |  | useSidebarState, | 
|---|
|  |  |  | usePermissions, | 
|---|
|  |  |  | } from 'react-admin'; | 
|---|
|  |  |  | import { useLocation } from 'react-router-dom'; | 
|---|
|  |  |  | import { Box } from '@mui/material'; | 
|---|
|  |  |  | import SubMenu from './SubMenu'; | 
|---|
|  |  |  | import SettingsIcon from '@mui/icons-material/Settings'; | 
|---|
|  |  |  | import DashboardIcon from '@mui/icons-material/Dashboard'; | 
|---|
|  |  |  | import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule'; | 
|---|
|  |  |  | import PersonIcon from '@mui/icons-material/Person'; | 
|---|
|  |  |  | import * as Icons from '@mui/icons-material'; | 
|---|
|  |  |  | useTranslate, | 
|---|
|  |  |  | DashboardMenuItem, | 
|---|
|  |  |  | MenuItemLink, | 
|---|
|  |  |  | Menu, | 
|---|
|  |  |  | useSidebarState, | 
|---|
|  |  |  | usePermissions, | 
|---|
|  |  |  | } from "react-admin"; | 
|---|
|  |  |  | import { useLocation } from "react-router-dom"; | 
|---|
|  |  |  | import { Box } from "@mui/material"; | 
|---|
|  |  |  | import SubMenu from "./SubMenu"; | 
|---|
|  |  |  | import SettingsIcon from "@mui/icons-material/Settings"; | 
|---|
|  |  |  | import DashboardIcon from "@mui/icons-material/Dashboard"; | 
|---|
|  |  |  | import HorizontalRuleIcon from "@mui/icons-material/HorizontalRule"; | 
|---|
|  |  |  | import PersonIcon from "@mui/icons-material/Person"; | 
|---|
|  |  |  | import * as Icons from "@mui/icons-material"; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const getIconComponent = (iconStr) => { | 
|---|
|  |  |  | return Icons[iconStr] || HorizontalRuleIcon; | 
|---|
|  |  |  | return Icons[iconStr] || HorizontalRuleIcon; | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | export const MyMenu = ({ dense = false }) => { | 
|---|
|  |  |  | const [state, setState] = useState({}); | 
|---|
|  |  |  | const translate = useTranslate(); | 
|---|
|  |  |  | const location = useLocation(); | 
|---|
|  |  |  | const [sidebarIsOpen] = useSidebarState(); | 
|---|
|  |  |  | const { isPending, permissions } = usePermissions(); | 
|---|
|  |  |  | const [state, setState] = useState({}); | 
|---|
|  |  |  | const translate = useTranslate(); | 
|---|
|  |  |  | const location = useLocation(); | 
|---|
|  |  |  | const [sidebarIsOpen] = useSidebarState(); | 
|---|
|  |  |  | const { isPending, permissions } = usePermissions(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | useEffect(() => { | 
|---|
|  |  |  | // default open sub menu | 
|---|
|  |  |  | const defaultExpandMenu = ["menu.system", "menu.dispatcher", "menu.equipment"]; | 
|---|
|  |  |  | permissions?.forEach(item => { | 
|---|
|  |  |  | if (defaultExpandMenu.includes(item.name)) { | 
|---|
|  |  |  | setState(state => ({ ...state, [item.route]: true })); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | }, [permissions]); | 
|---|
|  |  |  | useEffect(() => { | 
|---|
|  |  |  | // default open sub menu | 
|---|
|  |  |  | const defaultExpandMenu = [ | 
|---|
|  |  |  | "menu.system", | 
|---|
|  |  |  | "menu.dispatcher", | 
|---|
|  |  |  | "menu.equipment", | 
|---|
|  |  |  | ]; | 
|---|
|  |  |  | permissions?.forEach((item) => { | 
|---|
|  |  |  | if (defaultExpandMenu.includes(item.name)) { | 
|---|
|  |  |  | setState((state) => ({ ...state, [item.route]: true })); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | }, [permissions]); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | useEffect(() => { | 
|---|
|  |  |  | // expand this parent menu | 
|---|
|  |  |  | const currentPath = location.pathname; | 
|---|
|  |  |  | const parentRoutes = findParentRoutes(currentPath, permissions) | 
|---|
|  |  |  | for (const parentRoute of parentRoutes) { | 
|---|
|  |  |  | setState(state => ({ ...state, [parentRoute]: true })); | 
|---|
|  |  |  | useEffect(() => { | 
|---|
|  |  |  | // expand this parent menu | 
|---|
|  |  |  | const currentPath = location.pathname; | 
|---|
|  |  |  | const parentRoutes = findParentRoutes(currentPath, permissions); | 
|---|
|  |  |  | for (const parentRoute of parentRoutes) { | 
|---|
|  |  |  | setState((state) => ({ ...state, [parentRoute]: true })); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }, [location.pathname]); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const handleToggle = (menu) => { | 
|---|
|  |  |  | setState((state) => ({ ...state, [menu]: !state[menu] })); | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const getIcon = (iconStr) => { | 
|---|
|  |  |  | const IconComponent = getIconComponent(iconStr); | 
|---|
|  |  |  | if (IconComponent) { | 
|---|
|  |  |  | return <IconComponent />; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | return <KeyboardArrowDownIcon /> | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const generateMenu = (permissions) => { | 
|---|
|  |  |  | return permissions.map((node) => { | 
|---|
|  |  |  | if (node.children) { | 
|---|
|  |  |  | return ( | 
|---|
|  |  |  | <SubMenu | 
|---|
|  |  |  | key={node.id} | 
|---|
|  |  |  | handleToggle={() => handleToggle(node.route)} | 
|---|
|  |  |  | isOpen={state[node.route]} | 
|---|
|  |  |  | name={node.name} | 
|---|
|  |  |  | dense={dense} | 
|---|
|  |  |  | icon={getIcon(node.icon)} | 
|---|
|  |  |  | > | 
|---|
|  |  |  | {generateMenu(node.children)} | 
|---|
|  |  |  | </SubMenu> | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | if (node.component) { | 
|---|
|  |  |  | return ( | 
|---|
|  |  |  | <MenuItemLink | 
|---|
|  |  |  | key={node.id} | 
|---|
|  |  |  | to={node.component} // correspond to Resource.name | 
|---|
|  |  |  | state={{ _scrollToTop: true }} | 
|---|
|  |  |  | // primaryText={translate(`resources.orders.name`, { | 
|---|
|  |  |  | //     smart_count: 2, | 
|---|
|  |  |  | // })} | 
|---|
|  |  |  | primaryText={node.name} | 
|---|
|  |  |  | leftIcon={getIcon(node.icon)} | 
|---|
|  |  |  | dense={dense} | 
|---|
|  |  |  | // sx={{ '& .RaMenuItemLink-icon': { visibility: 'hidden' } }} | 
|---|
|  |  |  | /> | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | }, [location.pathname]); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const handleToggle = (menu) => { | 
|---|
|  |  |  | setState(state => ({ ...state, [menu]: !state[menu] })); | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const getIcon = (iconStr) => { | 
|---|
|  |  |  | const IconComponent = getIconComponent(iconStr); | 
|---|
|  |  |  | if (IconComponent) { | 
|---|
|  |  |  | return <IconComponent />; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const generateMenu = (permissions) => { | 
|---|
|  |  |  | return permissions.map((node) => { | 
|---|
|  |  |  | if (node.children) { | 
|---|
|  |  |  | return ( | 
|---|
|  |  |  | <SubMenu | 
|---|
|  |  |  | key={node.id} | 
|---|
|  |  |  | handleToggle={() => handleToggle(node.route)} | 
|---|
|  |  |  | isOpen={state[node.route]} | 
|---|
|  |  |  | name={node.name} | 
|---|
|  |  |  | dense={dense} | 
|---|
|  |  |  | icon={getIcon(node.icon)} | 
|---|
|  |  |  | > | 
|---|
|  |  |  | {generateMenu(node.children)} | 
|---|
|  |  |  | </SubMenu> | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | if (node.component) { | 
|---|
|  |  |  | return ( | 
|---|
|  |  |  | <MenuItemLink | 
|---|
|  |  |  | key={node.id} | 
|---|
|  |  |  | to={node.component} // correspond to Resource.name | 
|---|
|  |  |  | state={{ _scrollToTop: true }} | 
|---|
|  |  |  | // primaryText={translate(`resources.orders.name`, { | 
|---|
|  |  |  | //     smart_count: 2, | 
|---|
|  |  |  | // })} | 
|---|
|  |  |  | primaryText={node.name} | 
|---|
|  |  |  | leftIcon={getIcon(node.icon)} | 
|---|
|  |  |  | dense={dense} | 
|---|
|  |  |  | /> | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return isPending | 
|---|
|  |  |  | ? (<div>Waiting for permissions...</div>) : | 
|---|
|  |  |  | ( | 
|---|
|  |  |  | <Box | 
|---|
|  |  |  | sx={{ | 
|---|
|  |  |  | width: sidebarIsOpen ? 200 : 50, | 
|---|
|  |  |  | marginTop: 1, | 
|---|
|  |  |  | marginBottom: 1, | 
|---|
|  |  |  | transition: theme => | 
|---|
|  |  |  | theme.transitions.create('width', { | 
|---|
|  |  |  | easing: theme.transitions.easing.sharp, | 
|---|
|  |  |  | duration: theme.transitions.duration.leavingScreen, | 
|---|
|  |  |  | }), | 
|---|
|  |  |  | }} | 
|---|
|  |  |  | > | 
|---|
|  |  |  | <Menu.Item | 
|---|
|  |  |  | to="/dashboard" | 
|---|
|  |  |  | primaryText="menu.dashboard" | 
|---|
|  |  |  | leftIcon={<DashboardIcon />} | 
|---|
|  |  |  | /> | 
|---|
|  |  |  | {permissions && (generateMenu(permissions))} | 
|---|
|  |  |  | {/* <Menu.ResourceItems /> */} | 
|---|
|  |  |  | <Menu.Item | 
|---|
|  |  |  | to="/settings" | 
|---|
|  |  |  | primaryText="menu.settings" | 
|---|
|  |  |  | leftIcon={<PersonIcon />} | 
|---|
|  |  |  | /> | 
|---|
|  |  |  | </Box> | 
|---|
|  |  |  | ) | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return isPending ? ( | 
|---|
|  |  |  | <div>Waiting for permissions...</div> | 
|---|
|  |  |  | ) : ( | 
|---|
|  |  |  | <Box | 
|---|
|  |  |  | sx={{ | 
|---|
|  |  |  | width: sidebarIsOpen ? 200 : 50, | 
|---|
|  |  |  | marginTop: 1, | 
|---|
|  |  |  | marginBottom: 1, | 
|---|
|  |  |  | transition: (theme) => | 
|---|
|  |  |  | theme.transitions.create("width", { | 
|---|
|  |  |  | easing: theme.transitions.easing.sharp, | 
|---|
|  |  |  | duration: theme.transitions.duration.leavingScreen, | 
|---|
|  |  |  | }), | 
|---|
|  |  |  | }} | 
|---|
|  |  |  | > | 
|---|
|  |  |  | <Menu.Item | 
|---|
|  |  |  | to="/dashboard" | 
|---|
|  |  |  | primaryText="menu.dashboard" | 
|---|
|  |  |  | leftIcon={<DashboardIcon />} | 
|---|
|  |  |  | /> | 
|---|
|  |  |  | {permissions && generateMenu(permissions)} | 
|---|
|  |  |  | {/* <Menu.ResourceItems /> */} | 
|---|
|  |  |  | <Menu.Item | 
|---|
|  |  |  | to="/settings" | 
|---|
|  |  |  | primaryText="menu.settings" | 
|---|
|  |  |  | leftIcon={<PersonIcon />} | 
|---|
|  |  |  | /> | 
|---|
|  |  |  | </Box> | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const findParentRoutes = (pathname, permissions) => { | 
|---|
|  |  |  | if (!pathname || !permissions) { | 
|---|
|  |  |  | return []; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const findMenu = (currentPermissions, path) => { | 
|---|
|  |  |  | for (const item of currentPermissions) { | 
|---|
|  |  |  | if (item.component === path) { | 
|---|
|  |  |  | return item; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (item.children) { | 
|---|
|  |  |  | const found = findMenu(item.children, path); | 
|---|
|  |  |  | if (found) { | 
|---|
|  |  |  | return found; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const findParentRoutesRecursive = (item, allPermissions) => { | 
|---|
|  |  |  | const parentRoutes = []; | 
|---|
|  |  |  | let current = item; | 
|---|
|  |  |  | while (current && current.parentId) { | 
|---|
|  |  |  | const parent = allPermissions.find(permission => permission.id === current.parentId); | 
|---|
|  |  |  | if (parent) { | 
|---|
|  |  |  | parentRoutes.push(parent.route); | 
|---|
|  |  |  | current = parent; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return parentRoutes; | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const currentMenu = findMenu(permissions, pathname.replace("/", "")); | 
|---|
|  |  |  | if (currentMenu) { | 
|---|
|  |  |  | return findParentRoutesRecursive(currentMenu, permissions); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (!pathname || !permissions) { | 
|---|
|  |  |  | return []; | 
|---|
|  |  |  | }; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const findMenu = (currentPermissions, path) => { | 
|---|
|  |  |  | for (const item of currentPermissions) { | 
|---|
|  |  |  | if (item.component === path) { | 
|---|
|  |  |  | return item; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (item.children) { | 
|---|
|  |  |  | const found = findMenu(item.children, path); | 
|---|
|  |  |  | if (found) { | 
|---|
|  |  |  | return found; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const findParentRoutesRecursive = (item, allPermissions) => { | 
|---|
|  |  |  | const parentRoutes = []; | 
|---|
|  |  |  | let current = item; | 
|---|
|  |  |  | while (current && current.parentId) { | 
|---|
|  |  |  | const parent = allPermissions.find( | 
|---|
|  |  |  | (permission) => permission.id === current.parentId, | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | if (parent) { | 
|---|
|  |  |  | parentRoutes.push(parent.route); | 
|---|
|  |  |  | current = parent; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return parentRoutes; | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | const currentMenu = findMenu(permissions, pathname.replace("/", "")); | 
|---|
|  |  |  | if (currentMenu) { | 
|---|
|  |  |  | return findParentRoutesRecursive(currentMenu, permissions); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return []; | 
|---|
|  |  |  | }; | 
|---|