@campxdev/shared 0.3.15 → 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/assets/images/404notfound.svg +1 -0
- package/src/assets/images/index.ts +1 -0
- package/src/assets/images/internalservererror.svg +1 -0
- package/src/assets/images/noInternet.svg +1 -0
- package/src/assets/images/permissiondenied.svg +1 -0
- package/src/assets/images/unauth.svg +92 -0
- package/src/components/ChangePassword.tsx +146 -0
- package/src/components/ErrorBoundary/ErrorFallback.tsx +217 -168
- package/src/components/Layout/Header/AppHeader.tsx +104 -105
- package/src/components/Layout/Header/UserBox.tsx +61 -37
- package/src/components/LayoutWrapper.tsx +25 -0
- package/src/components/ListItemButton.tsx +101 -0
- package/src/components/PageNotFound.tsx +1 -0
- package/src/components/SideMenuHeader.tsx +29 -0
- package/src/components/SideNav.tsx +99 -0
- package/src/components/index.ts +76 -64
- package/src/layouts/Components/DashBoardMenu.tsx +232 -0
- package/src/layouts/Components/icons/index.tsx +403 -0
- package/src/layouts/Components/styles.tsx +60 -0
|
@@ -1,120 +1,119 @@
|
|
|
1
|
-
import {Box, styled, Typography} from
|
|
2
|
-
import {ReactNode} from
|
|
3
|
-
import {Link} from
|
|
4
|
-
import {campxLogoPrimary} from
|
|
5
|
-
import {isDevelopment} from
|
|
6
|
-
import {applications} from
|
|
7
|
-
import AppsMenu from
|
|
8
|
-
import {collegex, enrollx, examx, payx, peoplex} from
|
|
9
|
-
import CogWheelMenu from
|
|
10
|
-
import FreshDeskHelpButton from
|
|
11
|
-
import Notification from
|
|
12
|
-
import {StyledHeader, StyledImageWrapper} from
|
|
13
|
-
import UserBox from
|
|
1
|
+
import { Box, styled, Typography } from "@mui/material";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
import { campxLogoPrimary } from "../../../assets/images";
|
|
5
|
+
import { isDevelopment } from "../../../constants/isDevelopment";
|
|
6
|
+
import { applications } from "./applications";
|
|
7
|
+
import AppsMenu from "./AppsMenu";
|
|
8
|
+
import { collegex, enrollx, examx, payx, peoplex } from "./assets";
|
|
9
|
+
import CogWheelMenu from "./CogWheelMenu";
|
|
10
|
+
import FreshDeskHelpButton from "./FreshDeskHelpButton";
|
|
11
|
+
import Notification from "./Notification";
|
|
12
|
+
import { StyledHeader, StyledImageWrapper } from "./styles";
|
|
13
|
+
import UserBox from "./UserBox";
|
|
14
14
|
|
|
15
15
|
const StyledLogosWrapper = styled(Box)(() => ({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}))
|
|
16
|
+
display: "flex",
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
gap: "10px",
|
|
19
|
+
padding: "10px",
|
|
20
|
+
transition: "background ease 0.3s",
|
|
21
|
+
borderRadius: "5px",
|
|
22
|
+
"&:hover": {
|
|
23
|
+
background: "rgba(0, 0, 0, 0.05)",
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
26
|
|
|
27
27
|
const StyledLink = styled(Link)(() => ({
|
|
28
|
-
|
|
29
|
-
}))
|
|
28
|
+
textDecoration: "none",
|
|
29
|
+
}));
|
|
30
30
|
|
|
31
31
|
const imageMap = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
32
|
+
ums: collegex,
|
|
33
|
+
enroll: enrollx,
|
|
34
|
+
exams: examx,
|
|
35
|
+
payments: payx,
|
|
36
|
+
peoplex: peoplex,
|
|
37
|
+
campx: campxLogoPrimary,
|
|
38
|
+
};
|
|
39
39
|
|
|
40
40
|
interface AppHeaderProps {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
clientLogo: string;
|
|
42
|
+
username: string;
|
|
43
|
+
profileIcon: string;
|
|
44
|
+
permissions?: any;
|
|
45
|
+
userBoxActions: {
|
|
46
|
+
label: ReactNode;
|
|
47
|
+
icon?: ReactNode;
|
|
48
|
+
onClick: any;
|
|
49
|
+
}[];
|
|
50
|
+
cogWheelMenu?: { label: string; path: string }[];
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export default function AppHeader({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
clientLogo = imageMap.campx,
|
|
55
|
+
username,
|
|
56
|
+
profileIcon,
|
|
57
|
+
permissions,
|
|
58
|
+
userBoxActions = [],
|
|
59
|
+
cogWheelMenu = [],
|
|
60
60
|
}: AppHeaderProps) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
return (
|
|
62
|
+
<StyledHeader>
|
|
63
|
+
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
|
64
|
+
<AppsMenu />
|
|
65
|
+
<AppLogo clientLogo={clientLogo} />
|
|
66
|
+
</Box>
|
|
67
|
+
<Box className="actions">
|
|
68
|
+
<FreshDeskHelpButton />
|
|
69
|
+
<Notification />
|
|
70
|
+
{cogWheelMenu?.length ? <CogWheelMenu menu={cogWheelMenu} /> : null}
|
|
71
|
+
<UserBox
|
|
72
|
+
username={username}
|
|
73
|
+
profileIcon={profileIcon}
|
|
74
|
+
actions={userBoxActions}
|
|
75
|
+
/>
|
|
76
|
+
</Box>
|
|
77
|
+
</StyledHeader>
|
|
78
|
+
);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const AppLogo = ({clientLogo}) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.log('App Name', currentApp)
|
|
81
|
+
const AppLogo = ({ clientLogo }) => {
|
|
82
|
+
const originSubdomain =
|
|
83
|
+
window.location.host.split(".")?.slice(-3)[0] ?? "ums";
|
|
84
|
+
const currentApp =
|
|
85
|
+
applications.find((item) => item.key === originSubdomain)?.key ?? "campx";
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
87
|
+
return (
|
|
88
|
+
<StyledLink to={"/"}>
|
|
89
|
+
<StyledLogosWrapper>
|
|
90
|
+
<StyledImageWrapper>
|
|
91
|
+
{isDevelopment ? (
|
|
92
|
+
<img src={imageMap.campx} />
|
|
93
|
+
) : (
|
|
94
|
+
<img src={imageMap[currentApp]} />
|
|
95
|
+
)}
|
|
96
|
+
</StyledImageWrapper>
|
|
97
|
+
<Box
|
|
98
|
+
sx={{
|
|
99
|
+
height: "26px",
|
|
100
|
+
width: "2px",
|
|
101
|
+
background: "gray",
|
|
102
|
+
}}
|
|
103
|
+
></Box>
|
|
104
|
+
<StyledImageWrapper>
|
|
105
|
+
{isDevelopment ? (
|
|
106
|
+
<Typography variant="h1">Developer</Typography>
|
|
107
|
+
) : (
|
|
108
|
+
<img
|
|
109
|
+
src={clientLogo}
|
|
110
|
+
onError={(e: any) => {
|
|
111
|
+
e.target.src = imageMap.campx;
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</StyledImageWrapper>
|
|
116
|
+
</StyledLogosWrapper>
|
|
117
|
+
</StyledLink>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
@@ -1,42 +1,66 @@
|
|
|
1
|
-
import {Box, Typography} from
|
|
2
|
-
import {ReactNode} from
|
|
3
|
-
import {avatarImage} from
|
|
4
|
-
import MenuButton from
|
|
5
|
-
import {StyledUser} from
|
|
1
|
+
import { Box, Typography } from "@mui/material";
|
|
2
|
+
import { ReactNode, useState } from "react";
|
|
3
|
+
import { avatarImage } from "../../../assets/images";
|
|
4
|
+
import MenuButton from "../../MenuButton";
|
|
5
|
+
import { StyledUser } from "./styles";
|
|
6
|
+
import logout from "../../../utils/logout";
|
|
7
|
+
import ChangePassword from "../../ChangePassword";
|
|
6
8
|
|
|
7
9
|
export default function UserBox({
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
username,
|
|
11
|
+
profileIcon,
|
|
12
|
+
actions,
|
|
11
13
|
}: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
username: string;
|
|
15
|
+
profileIcon: string;
|
|
16
|
+
actions: { label: ReactNode; icon?: ReactNode; onClick: any }[];
|
|
15
17
|
}) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
const [open, setOpen] = useState(true);
|
|
19
|
+
|
|
20
|
+
const handleClickOpen = () => {
|
|
21
|
+
setOpen(true);
|
|
22
|
+
};
|
|
23
|
+
const handleClose = (value: string) => {
|
|
24
|
+
setOpen(false);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<MenuButton
|
|
30
|
+
anchor={
|
|
31
|
+
<Box minWidth={"80px"} textAlign="center">
|
|
32
|
+
<StyledUser>
|
|
33
|
+
<img
|
|
34
|
+
src={profileIcon ?? avatarImage}
|
|
35
|
+
style={{
|
|
36
|
+
height: "32px",
|
|
37
|
+
width: "32px",
|
|
38
|
+
objectFit: "contain",
|
|
39
|
+
}}
|
|
40
|
+
onError={(e: any) => {
|
|
41
|
+
e.target.src = avatarImage;
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<Typography variant="h6">{username}</Typography>
|
|
45
|
+
</StyledUser>
|
|
46
|
+
</Box>
|
|
47
|
+
}
|
|
48
|
+
menu={[
|
|
49
|
+
...actions,
|
|
50
|
+
{
|
|
51
|
+
label: "Logout",
|
|
52
|
+
onClick: logout,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "Change Password",
|
|
56
|
+
onClick: handleClickOpen,
|
|
57
|
+
},
|
|
58
|
+
]}
|
|
59
|
+
menuProps={{
|
|
60
|
+
PaperProps: { sx: { top: "64px !important" } },
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
<ChangePassword open={open} onClose={handleClose} />
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
42
66
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StyledLeftNavContainer,
|
|
3
|
+
StyledMainContentContainer,
|
|
4
|
+
} from "../layouts/Components/styles";
|
|
5
|
+
import SideNav from "./SideNav";
|
|
6
|
+
interface Props {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
menu: any[];
|
|
9
|
+
sideMenuHeader?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function LayoutWrapper({
|
|
13
|
+
children,
|
|
14
|
+
menu,
|
|
15
|
+
sideMenuHeader,
|
|
16
|
+
}: Props) {
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<StyledLeftNavContainer>
|
|
20
|
+
<SideNav menuItems={menu as any[]} header={sideMenuHeader} />
|
|
21
|
+
</StyledLeftNavContainer>
|
|
22
|
+
<StyledMainContentContainer>{children}</StyledMainContentContainer>
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ChevronRight } from "@mui/icons-material";
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
ListItemButton as MuiListItemButton,
|
|
5
|
+
ListItemButtonProps,
|
|
6
|
+
ListItemIcon,
|
|
7
|
+
ListItemText,
|
|
8
|
+
styled,
|
|
9
|
+
} from "@mui/material";
|
|
10
|
+
import { ReactNode } from "react";
|
|
11
|
+
|
|
12
|
+
interface Props extends ListItemButtonProps {
|
|
13
|
+
hasChildren?: boolean;
|
|
14
|
+
isActive?: boolean;
|
|
15
|
+
label: String;
|
|
16
|
+
icon?: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const ListItemButton = ({
|
|
20
|
+
isActive,
|
|
21
|
+
hasChildren,
|
|
22
|
+
onClick,
|
|
23
|
+
label,
|
|
24
|
+
icon: Icon,
|
|
25
|
+
}: Props) => {
|
|
26
|
+
return (
|
|
27
|
+
<MyListItemButton
|
|
28
|
+
isactive={isActive && !hasChildren ? 1 : 0}
|
|
29
|
+
onClick={onClick}
|
|
30
|
+
>
|
|
31
|
+
{Icon ? (
|
|
32
|
+
<ListItemIcon>{<Icon />}</ListItemIcon>
|
|
33
|
+
) : (
|
|
34
|
+
<Box minWidth={16}></Box>
|
|
35
|
+
)}
|
|
36
|
+
<ListItemText primary={label} />
|
|
37
|
+
{hasChildren ? (
|
|
38
|
+
<>
|
|
39
|
+
<StyledChevronIcon open={isActive} />
|
|
40
|
+
</>
|
|
41
|
+
) : null}
|
|
42
|
+
</MyListItemButton>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const StyledChevronIcon = styled(ChevronRight)<{ open: boolean }>(
|
|
47
|
+
({ theme, open }) => ({
|
|
48
|
+
transform: open ? "rotate(90deg)" : "rotate(0deg)",
|
|
49
|
+
transition: "transform 0.2s ease-in-out",
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const MyListItemButton = styled(MuiListItemButton)<{
|
|
54
|
+
isactive: 1 | 0;
|
|
55
|
+
children: ReactNode;
|
|
56
|
+
}>(({ theme, isactive }) => ({
|
|
57
|
+
gap: "10px",
|
|
58
|
+
fontWeight: "bold",
|
|
59
|
+
backgroundColor: isactive && "#1d1d1d",
|
|
60
|
+
"&:hover": {
|
|
61
|
+
backgroundColor: isactive && "#1d1d1d",
|
|
62
|
+
},
|
|
63
|
+
"&:after": {
|
|
64
|
+
content: '""',
|
|
65
|
+
position: "absolute",
|
|
66
|
+
height: "100%",
|
|
67
|
+
left: 0,
|
|
68
|
+
top: 0,
|
|
69
|
+
width: isactive ? "5px" : 0,
|
|
70
|
+
opacity: isactive ? 1 : 0,
|
|
71
|
+
transition: theme.transitions.create(["opacity", "width"]),
|
|
72
|
+
background: theme.palette.common.yellow,
|
|
73
|
+
borderTopRightRadius: "3px",
|
|
74
|
+
borderBottomRightRadius: "3px",
|
|
75
|
+
},
|
|
76
|
+
"&.MuiButton-endIcon, &.MuiButton-startIcon": {
|
|
77
|
+
transition: theme.transitions.create(["color"]),
|
|
78
|
+
|
|
79
|
+
"&.MuiSvgIcon-root": {
|
|
80
|
+
fontSize: "inherit",
|
|
81
|
+
transition: "none",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
"& .MuiSvgIcon-root": {
|
|
85
|
+
fontSize: theme.typography.pxToRem(20),
|
|
86
|
+
marginRight: theme.spacing(1),
|
|
87
|
+
// color: isactive
|
|
88
|
+
// ? theme.sidebar.menuItemColorActive
|
|
89
|
+
// : theme.sidebar.menuItemIconColor,
|
|
90
|
+
},
|
|
91
|
+
"& .MuiTypography-root": {
|
|
92
|
+
color: theme.palette.common.white,
|
|
93
|
+
},
|
|
94
|
+
"& svg": {
|
|
95
|
+
color: theme.palette.common.white,
|
|
96
|
+
},
|
|
97
|
+
"& .MuiListItemIcon-root": {
|
|
98
|
+
minWidth: 0,
|
|
99
|
+
maxWidth: "1rem",
|
|
100
|
+
},
|
|
101
|
+
}));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { KeyboardBackspace } from "@mui/icons-material";
|
|
2
|
+
import { Box, styled as styledMui, Typography } from "@mui/material";
|
|
3
|
+
import { useNavigate } from "react-router-dom";
|
|
4
|
+
|
|
5
|
+
const StyledSideMenuHeader = styledMui(Box)(({ theme }) => ({
|
|
6
|
+
height: "60px",
|
|
7
|
+
display: "flex",
|
|
8
|
+
gap: "8px",
|
|
9
|
+
alignItems: "center",
|
|
10
|
+
cursor: "pointer",
|
|
11
|
+
background: "#1d1d1d",
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
export const SideMenuHeader = ({ title, path }) => {
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<StyledSideMenuHeader
|
|
19
|
+
onClick={() => {
|
|
20
|
+
navigate(path);
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
<KeyboardBackspace />
|
|
24
|
+
<Typography variant="h3" color={"white"}>
|
|
25
|
+
{title}
|
|
26
|
+
</Typography>
|
|
27
|
+
</StyledSideMenuHeader>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ReactNode, useState } from "react";
|
|
2
|
+
import { Link, useMatch, useResolvedPath, LinkProps } from "react-router-dom";
|
|
3
|
+
import { Box, Collapse, List, styled } from "@mui/material";
|
|
4
|
+
import { PermissionsStore } from "../permissions";
|
|
5
|
+
import { ListItemButton } from "./ListItemButton";
|
|
6
|
+
const accessIfNoKey = process.env.NODE_ENV === "development" ? true : true;
|
|
7
|
+
|
|
8
|
+
const StyledLink = styled(Link)({
|
|
9
|
+
textDecoration: "none",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default function SideNav({
|
|
13
|
+
menuItems,
|
|
14
|
+
header,
|
|
15
|
+
}: {
|
|
16
|
+
menuItems: any[];
|
|
17
|
+
header?: ReactNode;
|
|
18
|
+
}) {
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
{header ? header : null}
|
|
22
|
+
<Box>
|
|
23
|
+
{menuItems.map((item, index) => (
|
|
24
|
+
<RenderMenuItem key={index} menuItem={item} />
|
|
25
|
+
))}
|
|
26
|
+
</Box>
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const RenderMenuItem = ({ menuItem }) => {
|
|
32
|
+
const { path, title, children, icon, permissionKey } = menuItem;
|
|
33
|
+
|
|
34
|
+
let resolved = useResolvedPath(path);
|
|
35
|
+
let match = useMatch({ path: resolved.pathname, end: false });
|
|
36
|
+
|
|
37
|
+
const [open, setOpen] = useState(false);
|
|
38
|
+
const permissions = PermissionsStore.useState((s) => s).permissions;
|
|
39
|
+
|
|
40
|
+
const validateDropdownAccess = () => {
|
|
41
|
+
let arr = [];
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < children.length; i++) {
|
|
44
|
+
if (permissions[children[i].permissionKey]) {
|
|
45
|
+
arr.push(true);
|
|
46
|
+
} else {
|
|
47
|
+
arr.push(accessIfNoKey);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return arr.includes(true);
|
|
52
|
+
};
|
|
53
|
+
const hasAccess = permissionKey ? permissions[permissionKey] : accessIfNoKey;
|
|
54
|
+
const dropdownAccess = children?.length
|
|
55
|
+
? validateDropdownAccess()
|
|
56
|
+
: hasAccess;
|
|
57
|
+
|
|
58
|
+
if (!dropdownAccess) return null;
|
|
59
|
+
if (children?.length)
|
|
60
|
+
return (
|
|
61
|
+
<Box
|
|
62
|
+
sx={{
|
|
63
|
+
position: "relative",
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<ListItemButton
|
|
67
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
68
|
+
label={title}
|
|
69
|
+
isActive={open}
|
|
70
|
+
hasChildren={true}
|
|
71
|
+
icon={icon}
|
|
72
|
+
/>
|
|
73
|
+
<Collapse in={open}>
|
|
74
|
+
<List>
|
|
75
|
+
{children?.map((child) => (
|
|
76
|
+
<RenderMenuItem
|
|
77
|
+
key={child.path}
|
|
78
|
+
menuItem={{
|
|
79
|
+
...child,
|
|
80
|
+
path: path + child.path,
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
))}
|
|
84
|
+
</List>
|
|
85
|
+
</Collapse>
|
|
86
|
+
</Box>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<StyledLink to={path}>
|
|
91
|
+
<ListItemButton
|
|
92
|
+
hasChildren={false}
|
|
93
|
+
label={title}
|
|
94
|
+
isActive={!!match}
|
|
95
|
+
icon={icon}
|
|
96
|
+
/>
|
|
97
|
+
</StyledLink>
|
|
98
|
+
);
|
|
99
|
+
};
|