@campxdev/shared 0.3.15 → 0.3.17

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.
@@ -1,120 +1,119 @@
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 { 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
- 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
- }))
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
- textDecoration: 'none',
29
- }))
28
+ textDecoration: "none",
29
+ }));
30
30
 
31
31
  const imageMap = {
32
- ums: collegex,
33
- enroll: enrollx,
34
- exams: examx,
35
- payments: payx,
36
- peoplex: peoplex,
37
- campx: campxLogoPrimary,
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
- 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}[]
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
- clientLogo = imageMap.campx,
55
- username,
56
- profileIcon,
57
- permissions,
58
- userBoxActions = [],
59
- cogWheelMenu = [],
54
+ clientLogo = imageMap.campx,
55
+ username,
56
+ profileIcon,
57
+ permissions,
58
+ userBoxActions = [],
59
+ cogWheelMenu = [],
60
60
  }: 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
- )
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
- const originSubdomain = window.location.host.split('.')?.slice(-3)[0] ?? 'ums'
83
- const currentApp =
84
- applications.find((item) => item.key === originSubdomain)?.key ?? 'campx'
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
- return (
89
- <StyledLink to={'/'}>
90
- <StyledLogosWrapper>
91
- <StyledImageWrapper>
92
- {isDevelopment ? (
93
- <img src={imageMap.campx} />
94
- ) : (
95
- <img src={imageMap[currentApp]} />
96
- )}
97
- </StyledImageWrapper>
98
- <Box
99
- sx={{
100
- height: '26px',
101
- width: '2px',
102
- background: 'gray',
103
- }}
104
- ></Box>
105
- <StyledImageWrapper>
106
- {isDevelopment ? (
107
- <Typography variant='h1'>Developer</Typography>
108
- ) : (
109
- <img
110
- src={clientLogo}
111
- onError={(e: any) => {
112
- e.target.src = imageMap.campx
113
- }}
114
- />
115
- )}
116
- </StyledImageWrapper>
117
- </StyledLogosWrapper>
118
- </StyledLink>
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,69 @@
1
- import {Box, Typography} from '@mui/material'
2
- import {ReactNode} from 'react'
3
- import {avatarImage} from '../../../assets/images'
4
- import MenuButton from '../../MenuButton'
5
- import {StyledUser} from './styles'
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";
8
+ import { ExitToAppOutlined, HttpsOutlined } from "@mui/icons-material";
6
9
 
7
10
  export default function UserBox({
8
- username,
9
- profileIcon,
10
- actions,
11
+ username,
12
+ profileIcon,
13
+ actions,
11
14
  }: {
12
- username: string
13
- profileIcon: string
14
- actions: {label: ReactNode; icon?: ReactNode; onClick: any}[]
15
+ username: string;
16
+ profileIcon: string;
17
+ actions: { label: ReactNode; icon?: ReactNode; onClick: any }[];
15
18
  }) {
16
- return (
17
- <MenuButton
18
- anchor={
19
- <Box minWidth={'80px'} textAlign='center'>
20
- <StyledUser>
21
- <img
22
- src={profileIcon ?? avatarImage}
23
- style={{
24
- height: '32px',
25
- width: '32px',
26
- objectFit: 'contain',
27
- }}
28
- onError={(e: any) => {
29
- e.target.src = avatarImage
30
- }}
31
- />
32
- <Typography variant='h6'>{username}</Typography>
33
- </StyledUser>
34
- </Box>
35
- }
36
- menu={actions}
37
- menuProps={{
38
- PaperProps: {sx: {top: '64px !important'}},
39
- }}
40
- />
41
- )
19
+ const [open, setOpen] = useState(false);
20
+
21
+ const handleClickOpen = () => {
22
+ setOpen(true);
23
+ };
24
+ const handleClose = (value: string) => {
25
+ setOpen(false);
26
+ };
27
+
28
+ return (
29
+ <>
30
+ <MenuButton
31
+ anchor={
32
+ <Box minWidth={"80px"} textAlign="center">
33
+ <StyledUser>
34
+ <img
35
+ src={profileIcon ?? avatarImage}
36
+ style={{
37
+ height: "32px",
38
+ width: "32px",
39
+ objectFit: "contain",
40
+ }}
41
+ onError={(e: any) => {
42
+ e.target.src = avatarImage;
43
+ }}
44
+ />
45
+ <Typography variant="h6">{username}</Typography>
46
+ </StyledUser>
47
+ </Box>
48
+ }
49
+ menu={[
50
+ ...actions,
51
+ {
52
+ label: "Logout",
53
+ icon: <HttpsOutlined />,
54
+ onClick: logout,
55
+ },
56
+ {
57
+ label: "Change Password",
58
+ icon: <ExitToAppOutlined />,
59
+ onClick: handleClickOpen,
60
+ },
61
+ ]}
62
+ menuProps={{
63
+ PaperProps: { sx: { top: "64px !important" } },
64
+ }}
65
+ />
66
+ <ChangePassword open={open} onClose={handleClose} />
67
+ </>
68
+ );
42
69
  }
@@ -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
+ }));
@@ -13,6 +13,7 @@ function PageNotFound() {
13
13
  flexDirection: "column",
14
14
  alignItems: "center",
15
15
  justifyContent: "center",
16
+ marginTop: "50px",
16
17
  }}
17
18
  >
18
19
  <img src={pagenotfound} alt="page not found" width={470} />
@@ -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
+ };