@campxdev/shared 0.4.0 → 0.5.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campxdev/shared",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "main": "./exports.ts",
5
5
  "scripts": {
6
6
  "start": "react-scripts start",
@@ -1,119 +1,113 @@
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";
1
+ import {Box, styled, Typography} from '@mui/material'
2
+ import {ReactNode} from 'react'
3
+ import {Link} from 'react-router-dom'
4
+ import {isDevelopment} from '../../../constants/isDevelopment'
5
+ import {applications} from './applications'
6
+ import AppsMenu from './AppsMenu'
7
+ import {collegex, enrollx, examx, payx, peoplex} from './assets'
8
+ import CogWheelMenu from './CogWheelMenu'
9
+ import FreshDeskHelpButton from './FreshDeskHelpButton'
10
+ import Notification from './Notification'
11
+ import {StyledHeader, StyledImageWrapper} from './styles'
12
+ import UserBox from './UserBox'
14
13
 
15
14
  const StyledLogosWrapper = styled(Box)(() => ({
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
- }));
15
+ display: 'flex',
16
+ alignItems: 'center',
17
+ gap: '10px',
18
+ padding: '10px',
19
+ transition: 'background ease 0.3s',
20
+ borderRadius: '5px',
21
+ '&:hover': {
22
+ background: 'rgba(0, 0, 0, 0.05)',
23
+ },
24
+ }))
26
25
 
27
26
  const StyledLink = styled(Link)(() => ({
28
- textDecoration: "none",
29
- }));
27
+ textDecoration: 'none',
28
+ }))
30
29
 
31
30
  const imageMap = {
32
- ums: collegex,
33
- enroll: enrollx,
34
- exams: examx,
35
- payments: payx,
36
- peoplex: peoplex,
37
- campx: collegex,
38
- };
31
+ ums: collegex,
32
+ enroll: enrollx,
33
+ exams: examx,
34
+ payments: payx,
35
+ peoplex: peoplex,
36
+ campx: collegex,
37
+ }
39
38
 
40
39
  interface AppHeaderProps {
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 }[];
40
+ clientLogo: string
41
+ username: string
42
+ profileIcon: string
43
+ permissions?: any
44
+ userBoxActions: {
45
+ label: ReactNode
46
+ icon?: ReactNode
47
+ onClick: any
48
+ }[]
49
+ cogWheelMenu?: {label: string; path: string}[]
51
50
  }
52
51
 
53
52
  export default function AppHeader({
54
- clientLogo = imageMap.campx,
55
- username,
56
- profileIcon,
57
- permissions,
58
- userBoxActions = [],
59
- cogWheelMenu = [],
53
+ clientLogo = imageMap.campx,
54
+ username,
55
+ profileIcon,
56
+ permissions,
57
+ userBoxActions = [],
58
+ cogWheelMenu = [],
60
59
  }: AppHeaderProps) {
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
- );
60
+ return (
61
+ <StyledHeader>
62
+ <Box sx={{display: 'flex', alignItems: 'center', gap: '10px'}}>
63
+ <AppsMenu />
64
+ <AppLogo clientLogo={clientLogo} />
65
+ </Box>
66
+ <Box className='actions'>
67
+ <FreshDeskHelpButton />
68
+ {/* <Notification /> */}
69
+ {cogWheelMenu?.length ? <CogWheelMenu menu={cogWheelMenu} /> : null}
70
+ <UserBox
71
+ username={username}
72
+ profileIcon={profileIcon}
73
+ actions={userBoxActions}
74
+ />
75
+ </Box>
76
+ </StyledHeader>
77
+ )
79
78
  }
80
79
 
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";
80
+ const AppLogo = ({clientLogo}) => {
81
+ const originSubdomain = window.location.host.split('.')?.slice(-3)[0] ?? 'ums'
82
+ const currentApp =
83
+ applications.find((item) => item.key === originSubdomain)?.key ?? 'campx'
86
84
 
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
- };
85
+ return (
86
+ <StyledLink to={'/'}>
87
+ <StyledLogosWrapper>
88
+ <StyledImageWrapper>
89
+ <img src={imageMap[currentApp]} />
90
+ </StyledImageWrapper>
91
+ <Box
92
+ sx={{
93
+ height: '26px',
94
+ width: '2px',
95
+ background: 'gray',
96
+ }}
97
+ ></Box>
98
+ <StyledImageWrapper>
99
+ {isDevelopment ? (
100
+ <Typography variant='h1'>Developer</Typography>
101
+ ) : (
102
+ <img
103
+ src={clientLogo}
104
+ onError={(e: any) => {
105
+ e.target.src = imageMap.campx
106
+ }}
107
+ />
108
+ )}
109
+ </StyledImageWrapper>
110
+ </StyledLogosWrapper>
111
+ </StyledLink>
112
+ )
113
+ }
@@ -1,87 +1,92 @@
1
- import { Box, Menu, Typography } from "@mui/material";
2
- import { useState } from "react";
3
- import { applications } from "./applications";
4
- import { AppsIcon } from "./icons";
1
+ import {Box, Menu, Typography} from '@mui/material'
2
+ import {useState} from 'react'
3
+ import {applications} from './applications'
4
+ import {AppsIcon} from './icons'
5
5
  import {
6
- StyledDescription,
7
- StyledIconButton,
8
- StyledMenuItem,
9
- StyledMenuItemContainer,
10
- } from "./styles";
6
+ StyledDescription,
7
+ StyledIconButton,
8
+ StyledMenuItem,
9
+ StyledMenuItemContainer,
10
+ StyledLink,
11
+ } from './styles'
11
12
 
12
13
  const AppsMenu = () => {
13
- const [anchorEl, setAnchorEl] = useState<any>(null);
14
- const open = Boolean(anchorEl);
14
+ const [anchorEl, setAnchorEl] = useState<any>(null)
15
+ const open = Boolean(anchorEl)
15
16
 
16
- const handleClick = (event) => {
17
- setAnchorEl(event.currentTarget);
18
- };
17
+ const handleClick = (event) => {
18
+ setAnchorEl(event.currentTarget)
19
+ }
19
20
 
20
- const handleClose = () => {
21
- setAnchorEl(null);
22
- };
21
+ const handleClose = () => {
22
+ setAnchorEl(null)
23
+ }
23
24
 
24
- return (
25
- <>
26
- <StyledIconButton onClick={handleClick}>
27
- <AppsIcon />
28
- </StyledIconButton>
25
+ return (
26
+ <>
27
+ <StyledIconButton onClick={handleClick}>
28
+ <AppsIcon />
29
+ </StyledIconButton>
29
30
 
30
- <Menu
31
- transitionDuration={150}
32
- elevation={3}
33
- id="basic-menu"
34
- anchorEl={anchorEl}
35
- open={open}
36
- onClose={handleClose}
37
- anchorOrigin={{
38
- vertical: "bottom",
39
- horizontal: "left",
40
- }}
41
- transformOrigin={{
42
- vertical: "top",
43
- horizontal: "left",
44
- }}
45
- sx={{
46
- "& .MuiPaper-root": {
47
- left: "0 !important",
48
- top: "64px !important",
49
- },
50
- }}
51
- >
52
- <Box sx={{ padding: "0.3rem 1rem" }}>
53
- <Typography variant="body2">Switch to</Typography>
54
- </Box>
55
- <Box>
56
- {applications.map((item, index) => (
57
- <StyledMenuItemContainer
58
- key={index}
59
- onClick={() => {
60
- window.location.href = item.path;
61
- handleClose();
62
- }}
63
- >
64
- <MenuItem data={item} />
65
- </StyledMenuItemContainer>
66
- ))}
67
- </Box>
68
- </Menu>
69
- </>
70
- );
71
- };
31
+ <Menu
32
+ transitionDuration={150}
33
+ elevation={3}
34
+ id='basic-menu'
35
+ anchorEl={anchorEl}
36
+ open={open}
37
+ onClose={handleClose}
38
+ anchorOrigin={{
39
+ vertical: 'bottom',
40
+ horizontal: 'left',
41
+ }}
42
+ transformOrigin={{
43
+ vertical: 'top',
44
+ horizontal: 'left',
45
+ }}
46
+ sx={{
47
+ '& .MuiPaper-root': {
48
+ left: '0 !important',
49
+ top: '64px !important',
50
+ },
51
+ }}
52
+ >
53
+ <Box sx={{padding: '0.3rem 1rem'}}>
54
+ <Typography variant='body2'>Switch to</Typography>
55
+ </Box>
56
+ <Box>
57
+ {applications.map((item, index) => (
58
+ <StyledLink href={item.path}>
59
+ <StyledMenuItemContainer
60
+ key={index}
61
+ onClick={() => {
62
+ window.location.href = item.path
63
+ handleClose()
64
+ }}
65
+ >
66
+ <MenuItem data={item} />
67
+ </StyledMenuItemContainer>
68
+ </StyledLink>
69
+ ))}
70
+ </Box>
71
+ </Menu>
72
+ </>
73
+ )
74
+ }
72
75
 
73
- export default AppsMenu;
76
+ export default AppsMenu
74
77
 
75
- const MenuItem = ({ data }) => {
76
- return (
77
- <StyledMenuItem>
78
- <Box>
79
- <img src={data.icon} />
80
- </Box>
81
- <Box>
82
- <Typography variant="h1">{data?.title}</Typography>
83
- <StyledDescription>{data?.description}</StyledDescription>
84
- </Box>
85
- </StyledMenuItem>
86
- );
87
- };
78
+ const MenuItem = ({data}) => {
79
+ return (
80
+ <StyledMenuItem>
81
+ <Box>
82
+ <img src={data.icon} style={{width: '28px', height: '28px'}} />
83
+ </Box>
84
+ <Box>
85
+ <Typography variant='h1' sx={{marginBottom: '7px'}}>
86
+ {data?.title}
87
+ </Typography>
88
+ <StyledDescription>{data?.description}</StyledDescription>
89
+ </Box>
90
+ </StyledMenuItem>
91
+ )
92
+ }
@@ -1,92 +1,98 @@
1
1
  import {
2
- alpha,
3
- AppBar,
4
- Box,
5
- IconButton,
6
- ListItemText,
7
- styled,
8
- Typography,
9
- } from "@mui/material";
2
+ alpha,
3
+ AppBar,
4
+ Box,
5
+ IconButton,
6
+ ListItemText,
7
+ styled,
8
+ Typography,
9
+ Link,
10
+ } from '@mui/material'
10
11
 
11
- export const StyledImageWrapper = styled("div")`
12
- width: auto;
13
- height: 20px;
14
- & img {
15
- width: 100%;
16
- height: 100%;
17
- object-fit: contain;
18
- }
19
- `;
12
+ export const StyledImageWrapper = styled('div')`
13
+ width: auto;
14
+ height: 24px;
15
+ & img {
16
+ width: 100%;
17
+ height: 100%;
18
+ object-fit: contain;
19
+ }
20
+ `
20
21
 
21
- export const StyledItemText = styled(ListItemText)(({ theme }) => ({
22
- color: theme.palette.text.primary,
23
- transition: "0.2s ease",
24
- }));
22
+ export const StyledItemText = styled(ListItemText)(({theme}) => ({
23
+ color: theme.palette.text.primary,
24
+ transition: '0.2s ease',
25
+ }))
25
26
 
26
- export const StyledAppBar = styled(AppBar)(({ theme }) => ({
27
- backgroundColor: "white",
28
- boxShadow: "0px 8px 28px rgb(136,136,136, 0.3)",
29
- }));
27
+ export const StyledAppBar = styled(AppBar)(({theme}) => ({
28
+ backgroundColor: 'white',
29
+ boxShadow: '0px 8px 28px rgb(136,136,136, 0.3)',
30
+ }))
30
31
 
31
- export const StyledHeader = styled(Box)(({ theme }) => ({
32
- boxShadow: "0px 2px 10px #0000001a",
33
- backgroundColor: "white",
34
- display: "flex",
35
- alignItems: "center",
36
- justifyContent: "space-between",
37
- "& .actions": {
38
- marginRight: "10px",
39
- display: "flex",
40
- alignItems: "center",
41
- gap: "14px",
42
- },
43
- }));
32
+ export const StyledHeader = styled(Box)(({theme}) => ({
33
+ boxShadow: '0px 2px 10px #0000001a',
34
+ backgroundColor: 'white',
35
+ display: 'flex',
36
+ alignItems: 'center',
37
+ justifyContent: 'space-between',
38
+ '& .actions': {
39
+ marginRight: '10px',
40
+ display: 'flex',
41
+ alignItems: 'center',
42
+ gap: '14px',
43
+ },
44
+ }))
44
45
 
45
- export const StyledUser = styled(Box)(({ theme }) => ({
46
- cursor: "pointer",
47
- borderRadius: "5px",
48
- transition: "background 0.2s ease",
49
- padding: "5px 16px",
50
- display: "flex",
51
- alignItems: "center",
52
- gap: "8px",
53
- "&:hover": {
54
- background: theme.palette.secondary.light,
55
- },
56
- }));
46
+ export const StyledUser = styled(Box)(({theme}) => ({
47
+ cursor: 'pointer',
48
+ borderRadius: '5px',
49
+ transition: 'background 0.2s ease',
50
+ padding: '5px 16px',
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ gap: '8px',
54
+ '&:hover': {
55
+ background: theme.palette.secondary.light,
56
+ },
57
+ }))
57
58
 
58
59
  export const StyledIconButton = styled(IconButton)({
59
- padding: "20px",
60
- backgroundColor: "black",
61
- display: "flex",
62
- alignItems: "center",
63
- justifyContent: "center",
64
- borderRadius: "0px",
65
- });
60
+ padding: '20px',
61
+ backgroundColor: 'black',
62
+ display: 'flex',
63
+ alignItems: 'center',
64
+ justifyContent: 'center',
65
+ borderRadius: '0px',
66
+ })
66
67
 
67
- export const StyledDescription = styled(Typography)(({ theme }) => ({
68
- fontSize: "15px",
69
- fontWeight: 600,
70
- color: alpha(theme?.palette?.secondary?.lighter, 0.5),
71
- }));
68
+ export const StyledLink = styled(Link)({
69
+ textDecoration: 'none',
70
+ })
71
+
72
+ export const StyledDescription = styled(Typography)(({theme}) => ({
73
+ fontSize: '12px',
74
+ fontWeight: 600,
75
+ color: alpha(theme?.palette?.secondary?.lighter, 0.5),
76
+ }))
72
77
 
73
78
  export const StyledMenuItem = styled(Box)({
74
- height: "64px",
75
- width: "400px",
76
- padding: "20px",
77
- transition: "background ease 0.3s",
78
- "&:hover": {
79
- background: "rgba(0, 0, 0, 0.03)",
80
- },
81
- display: "flex",
82
- alignItems: "center",
83
- gap: "20px",
84
- });
79
+ height: '64px',
80
+ width: '380px',
81
+ padding: '40px 20px',
82
+ transition: 'background ease 0.3s',
83
+ '&:hover': {
84
+ background: 'rgba(0, 0, 0, 0.03)',
85
+ },
86
+ display: 'flex',
87
+ alignItems: 'center',
88
+ gap: '20px',
89
+ })
85
90
 
86
- export const StyledMenuItemContainer = styled(Box)(({ theme }) => ({
87
- cursor: "pointer",
88
- borderBottom: `1px solid ${theme?.palette?.secondary?.lighter}`,
89
- "&:last-of-type": {
90
- border: "none",
91
- },
92
- }));
91
+ export const StyledMenuItemContainer = styled(Box)(({theme}) => ({
92
+ cursor: 'pointer',
93
+ borderBottom: `1px solid ${theme?.palette?.secondary?.lighter}`,
94
+ '&:last-of-type': {
95
+ border: 'none',
96
+ },
97
+ padding: '8px 0px',
98
+ }))
@@ -1,101 +1,94 @@
1
- import { ChevronRight } from "@mui/icons-material";
1
+ import {ChevronRight} from '@mui/icons-material'
2
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";
3
+ Box,
4
+ ListItemButton as MuiListItemButton,
5
+ ListItemButtonProps,
6
+ ListItemIcon,
7
+ ListItemText,
8
+ styled,
9
+ } from '@mui/material'
10
+ import {ReactNode} from 'react'
11
11
 
12
12
  interface Props extends ListItemButtonProps {
13
- hasChildren?: boolean;
14
- isActive?: boolean;
15
- label: String;
16
- icon?: any;
13
+ hasChildren?: boolean
14
+ isActive?: boolean
15
+ label: String
16
+ icon?: any
17
17
  }
18
18
 
19
19
  export const ListItemButton = ({
20
- isActive,
21
- hasChildren,
22
- onClick,
23
- label,
24
- icon: Icon,
20
+ isActive,
21
+ hasChildren,
22
+ onClick,
23
+ label,
24
+ icon: Icon,
25
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
- };
26
+ return (
27
+ <StyledListItemButton isActive={isActive} onClick={onClick}>
28
+ {Icon ? (
29
+ <ListItemIcon>{<Icon />}</ListItemIcon>
30
+ ) : (
31
+ <Box minWidth={16}></Box>
32
+ )}
33
+ <ListItemText primary={label} />
34
+ </StyledListItemButton>
35
+ )
36
+ }
45
37
 
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
- );
38
+ export const StyledChevronIcon = styled(ChevronRight)<{open: boolean}>(
39
+ ({theme, open}) => ({
40
+ transform: open ? 'rotate(90deg)' : 'rotate(0deg)',
41
+ transition: 'transform 0.2s ease-in-out',
42
+ })
43
+ )
52
44
 
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"]),
45
+ export const StyledListItemButton = styled(MuiListItemButton)<{
46
+ isActive: boolean
47
+ children: ReactNode
48
+ dropDown?: boolean
49
+ }>(({theme, isActive, dropDown = false}) => ({
50
+ gap: '10px',
51
+ fontWeight: 'bold',
52
+ backgroundColor: isActive && '#1d1d1d',
53
+ '&:hover': {
54
+ backgroundColor: isActive && '#1d1d1d',
55
+ },
56
+ '&:after': {
57
+ content: '""',
58
+ position: 'absolute',
59
+ height: '100%',
60
+ left: 0,
61
+ top: 0,
62
+ width: isActive ? '5px' : 0,
63
+ opacity: isActive ? (dropDown ? 0 : 1) : 0,
64
+ transition: theme.transitions.create(['opacity', 'width']),
65
+ background: theme.palette.common.yellow,
66
+ borderTopRightRadius: '3px',
67
+ borderBottomRightRadius: '3px',
68
+ },
69
+ '&.MuiButton-endIcon, &.MuiButton-startIcon': {
70
+ transition: theme.transitions.create(['color']),
78
71
 
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
- }));
72
+ '&.MuiSvgIcon-root': {
73
+ fontSize: 'inherit',
74
+ transition: 'none',
75
+ },
76
+ },
77
+ '& .MuiSvgIcon-root': {
78
+ fontSize: theme.typography.pxToRem(20),
79
+ marginRight: theme.spacing(1),
80
+ // color: isActive
81
+ // ? theme.sidebar.menuItemColorActive
82
+ // : theme.sidebar.menuItemIconColor,
83
+ },
84
+ '& .MuiTypography-root': {
85
+ color: theme.palette.common.white,
86
+ },
87
+ '& svg': {
88
+ color: theme.palette.common.white,
89
+ },
90
+ '& .MuiListItemIcon-root': {
91
+ minWidth: 0,
92
+ maxWidth: '1rem',
93
+ },
94
+ }))
@@ -1,99 +1,159 @@
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 : false;
1
+ import {ChevronLeft} from '@mui/icons-material'
2
+ import {
3
+ Box,
4
+ Collapse,
5
+ List,
6
+ ListItemIcon,
7
+ ListItemText,
8
+ styled,
9
+ } from '@mui/material'
10
+ import {ReactNode, useState} from 'react'
11
+ import {Link, useMatch, useResolvedPath} from 'react-router-dom'
12
+ import {PermissionsStore} from '../permissions'
13
+ import {
14
+ ListItemButton,
15
+ StyledChevronIcon,
16
+ StyledListItemButton,
17
+ } from './ListItemButton'
18
+ const accessIfNoKey = process.env.NODE_ENV === 'development' ? false : false
7
19
  const StyledLink = styled(Link)({
8
- textDecoration: "none",
9
- });
20
+ textDecoration: 'none',
21
+ })
10
22
 
11
23
  export default function SideNav({
12
- menuItems,
13
- header,
24
+ menuItems,
25
+ header,
14
26
  }: {
15
- menuItems: any[];
16
- header?: ReactNode;
27
+ menuItems: any[]
28
+ header?: ReactNode
17
29
  }) {
18
- return (
19
- <>
20
- {header ? header : null}
21
- <Box>
22
- {menuItems.map((item, index) => (
23
- <RenderMenuItem key={index} menuItem={item} />
24
- ))}
25
- </Box>
26
- </>
27
- );
30
+ return (
31
+ <Box
32
+ sx={{
33
+ background: (theme) => theme.palette.secondary.main,
34
+ }}
35
+ >
36
+ {header ? header : null}
37
+ <Box>
38
+ {menuItems.map((item, index) => (
39
+ <RenderMenuItem key={index} menuItem={item} />
40
+ ))}
41
+ </Box>
42
+ </Box>
43
+ )
28
44
  }
29
45
 
30
- const RenderMenuItem = ({ menuItem }) => {
31
- const { path, title, children, icon, permissionKey } = menuItem;
46
+ const RenderMenuItem = ({menuItem}) => {
47
+ const {path, title, children, icon, permissionKey} = menuItem
48
+ let resolved = useResolvedPath(path)
49
+ let match = useMatch({path: resolved.pathname, end: false})
32
50
 
33
- let resolved = useResolvedPath(path);
34
- let match = useMatch({ path: resolved.pathname, end: false });
51
+ const permissions = PermissionsStore.useState((s) => s).permissions
52
+ const hasAccess = permissionKey ? permissions[permissionKey] : accessIfNoKey
35
53
 
36
- const [open, setOpen] = useState(false);
37
- const permissions = PermissionsStore.useState((s) => s).permissions;
54
+ if (!hasAccess) return null
55
+ if (children?.length)
56
+ return (
57
+ <Box
58
+ sx={{
59
+ position: 'relative',
60
+ }}
61
+ >
62
+ <DropDownMenu
63
+ icon={icon}
64
+ menuItems={children}
65
+ path={path}
66
+ title={title}
67
+ permissionKey={permissionKey}
68
+ />
69
+ </Box>
70
+ )
38
71
 
39
- const validateDropdownAccess = () => {
40
- if (accessIfNoKey && !permissions) return true;
41
- let arr = [];
72
+ return (
73
+ <StyledLink to={path}>
74
+ <ListItemButton
75
+ hasChildren={false}
76
+ label={title}
77
+ isActive={!!match}
78
+ icon={icon}
79
+ />
80
+ </StyledLink>
81
+ )
82
+ }
83
+
84
+ const DropDownMenu = ({path, title, icon, menuItems, permissionKey}) => {
85
+ const [open, setOpen] = useState(false)
86
+ const permissions = PermissionsStore.useState((s) => s).permissions
87
+
88
+ const validateDropdownAccess = () => {
89
+ if (accessIfNoKey && !permissions) return true
90
+ let arr = []
91
+
92
+ for (let i = 0; i < menuItems.length; i++) {
93
+ if (permissions[menuItems[i].permissionKey]) {
94
+ arr.push(true)
95
+ } else {
96
+ arr.push(accessIfNoKey)
97
+ }
98
+ }
42
99
 
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
- }
100
+ return arr.includes(true)
101
+ }
50
102
 
51
- return arr.includes(true);
52
- };
53
- const hasAccess = permissionKey ? permissions[permissionKey] : accessIfNoKey;
54
- const dropdownAccess = children?.length
55
- ? validateDropdownAccess()
56
- : hasAccess;
103
+ const hasAccess = permissionKey ? permissions[permissionKey] : accessIfNoKey
57
104
 
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
- );
105
+ const dropdownAccess = menuItems?.length
106
+ ? validateDropdownAccess()
107
+ : hasAccess
88
108
 
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
- };
109
+ if (!dropdownAccess) return null
110
+ return (
111
+ <>
112
+ <Box
113
+ sx={{
114
+ position: 'relative',
115
+ }}
116
+ >
117
+ <MenuDropDownButton
118
+ isActive={open}
119
+ onClick={() => setOpen((prev) => !prev)}
120
+ label={title}
121
+ icon={icon}
122
+ />
123
+ <Collapse in={open}>
124
+ <List>
125
+ {menuItems?.map((child) => (
126
+ <RenderMenuItem
127
+ key={child.path}
128
+ menuItem={{
129
+ ...child,
130
+ path: path + child.path,
131
+ }}
132
+ />
133
+ ))}
134
+ </List>
135
+ </Collapse>
136
+ </Box>
137
+ </>
138
+ )
139
+ }
140
+
141
+ const MenuDropDownButton = ({icon: Icon, label, onClick, isActive}) => {
142
+ return (
143
+ <>
144
+ <StyledListItemButton
145
+ dropDown={true}
146
+ onClick={onClick}
147
+ isActive={isActive}
148
+ >
149
+ {Icon ? (
150
+ <ListItemIcon>{<Icon />}</ListItemIcon>
151
+ ) : (
152
+ <Box minWidth={16}></Box>
153
+ )}
154
+ <ListItemText primary={label} />
155
+ <StyledChevronIcon open={isActive} />
156
+ </StyledListItemButton>
157
+ </>
158
+ )
159
+ }
@@ -1,4 +1,3 @@
1
- import {Spinner} from '../components'
2
1
  import {Suspense} from 'react'
3
2
  import PageWithPermission from '../permissions/PageWithPermission'
4
3
  import {LinearProgress} from '../components/LinearProgress'
package/todo.md ADDED
@@ -0,0 +1,8 @@
1
+ [ ] add illustration component
2
+ [ ] fix tabs component to route match
3
+ [ ] add basename prop in Providers component
4
+ [ ] add permision denied component in withRouter component fallback
5
+
6
+ ## in progress
7
+
8
+ [x] fix sidenav dropdown