| | |
| | | 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', minWidth: '24px' } }} |
| | | /> |
| | | ); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | }, [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 []; |
| | | }; |