feat: add new theme berry (#860)

* feat: add theme berry

* docs: add development notes

* fix: fix blank page

* chore: update implementation

* fix: fix package.json

* chore: update ui copy

---------

Co-authored-by: JustSong <songquanpeng@foxmail.com>
This commit is contained in:
Buer
2024-01-07 14:20:07 +08:00
committed by GitHub
parent 6227eee5bc
commit 48989d4a0b
157 changed files with 13979 additions and 5 deletions

View File

@@ -0,0 +1,173 @@
import { useState, useRef, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
// material-ui
import { useTheme } from '@mui/material/styles';
import {
Avatar,
Chip,
ClickAwayListener,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Paper,
Popper,
Typography
} from '@mui/material';
// project imports
import MainCard from 'ui-component/cards/MainCard';
import Transitions from 'ui-component/extended/Transitions';
import User1 from 'assets/images/users/user-round.svg';
import useLogin from 'hooks/useLogin';
// assets
import { IconLogout, IconSettings, IconUserScan } from '@tabler/icons-react';
// ==============================|| PROFILE MENU ||============================== //
const ProfileSection = () => {
const theme = useTheme();
const navigate = useNavigate();
const customization = useSelector((state) => state.customization);
const { logout } = useLogin();
const [open, setOpen] = useState(false);
/**
* anchorRef is used on different componets and specifying one type leads to other components throwing an error
* */
const anchorRef = useRef(null);
const handleLogout = async () => {
logout();
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const prevOpen = useRef(open);
useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current.focus();
}
prevOpen.current = open;
}, [open]);
return (
<>
<Chip
sx={{
height: '48px',
alignItems: 'center',
borderRadius: '27px',
transition: 'all .2s ease-in-out',
borderColor: theme.palette.primary.light,
backgroundColor: theme.palette.primary.light,
'&[aria-controls="menu-list-grow"], &:hover': {
borderColor: theme.palette.primary.main,
background: `${theme.palette.primary.main}!important`,
color: theme.palette.primary.light,
'& svg': {
stroke: theme.palette.primary.light
}
},
'& .MuiChip-label': {
lineHeight: 0
}
}}
icon={
<Avatar
src={User1}
sx={{
...theme.typography.mediumAvatar,
margin: '8px 0 8px 8px !important',
cursor: 'pointer'
}}
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
color="inherit"
/>
}
label={<IconSettings stroke={1.5} size="1.5rem" color={theme.palette.primary.main} />}
variant="outlined"
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
color="primary"
/>
<Popper
placement="bottom-end"
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
popperOptions={{
modifiers: [
{
name: 'offset',
options: {
offset: [0, 14]
}
}
]
}}
>
{({ TransitionProps }) => (
<Transitions in={open} {...TransitionProps}>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>
<List
component="nav"
sx={{
width: '100%',
maxWidth: 350,
minWidth: 150,
backgroundColor: theme.palette.background.paper,
borderRadius: '10px',
[theme.breakpoints.down('md')]: {
minWidth: '100%'
},
'& .MuiListItemButton-root': {
mt: 0.5
}
}}
>
<ListItemButton sx={{ borderRadius: `${customization.borderRadius}px` }} onClick={() => navigate('/panel/profile')}>
<ListItemIcon>
<IconUserScan stroke={1.5} size="1.3rem" />
</ListItemIcon>
<ListItemText primary={<Typography variant="body2">设置</Typography>} />
</ListItemButton>
<ListItemButton sx={{ borderRadius: `${customization.borderRadius}px` }} onClick={handleLogout}>
<ListItemIcon>
<IconLogout stroke={1.5} size="1.3rem" />
</ListItemIcon>
<ListItemText primary={<Typography variant="body2">登出</Typography>} />
</ListItemButton>
</List>
</MainCard>
</ClickAwayListener>
</Paper>
</Transitions>
)}
</Popper>
</>
);
};
export default ProfileSection;

View File

@@ -0,0 +1,68 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Avatar, Box, ButtonBase } from '@mui/material';
// project imports
import LogoSection from '../LogoSection';
import ProfileSection from './ProfileSection';
// assets
import { IconMenu2 } from '@tabler/icons-react';
// ==============================|| MAIN NAVBAR / HEADER ||============================== //
const Header = ({ handleLeftDrawerToggle }) => {
const theme = useTheme();
return (
<>
{/* logo & toggler button */}
<Box
sx={{
width: 228,
display: 'flex',
[theme.breakpoints.down('md')]: {
width: 'auto'
}
}}
>
<Box component="span" sx={{ display: { xs: 'none', md: 'block' }, flexGrow: 1 }}>
<LogoSection />
</Box>
<ButtonBase sx={{ borderRadius: '12px', overflow: 'hidden' }}>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.mediumAvatar,
transition: 'all .2s ease-in-out',
background: theme.palette.secondary.light,
color: theme.palette.secondary.dark,
'&:hover': {
background: theme.palette.secondary.dark,
color: theme.palette.secondary.light
}
}}
onClick={handleLeftDrawerToggle}
color="inherit"
>
<IconMenu2 stroke={1.5} size="1.3rem" />
</Avatar>
</ButtonBase>
</Box>
<Box sx={{ flexGrow: 1 }} />
<Box sx={{ flexGrow: 1 }} />
<ProfileSection />
</>
);
};
Header.propTypes = {
handleLeftDrawerToggle: PropTypes.func
};
export default Header;

View File

@@ -0,0 +1,23 @@
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
// material-ui
import { ButtonBase } from '@mui/material';
// project imports
import Logo from 'ui-component/Logo';
import { MENU_OPEN } from 'store/actions';
// ==============================|| MAIN LOGO ||============================== //
const LogoSection = () => {
const defaultId = useSelector((state) => state.customization.defaultId);
const dispatch = useDispatch();
return (
<ButtonBase disableRipple onClick={() => dispatch({ type: MENU_OPEN, id: defaultId })} component={Link} to="/">
<Logo />
</ButtonBase>
);
};
export default LogoSection;

View File

@@ -0,0 +1,130 @@
// import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
// material-ui
import { styled, useTheme } from '@mui/material/styles';
import {
Avatar,
Card,
CardContent,
// Grid,
// LinearProgress,
List,
ListItem,
ListItemAvatar,
ListItemText,
Typography
// linearProgressClasses
} from '@mui/material';
import User1 from 'assets/images/users/user-round.svg';
import { useNavigate } from 'react-router-dom';
// assets
// import TableChartOutlinedIcon from '@mui/icons-material/TableChartOutlined';
// styles
// const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
// height: 10,
// borderRadius: 30,
// [`&.${linearProgressClasses.colorPrimary}`]: {
// backgroundColor: '#fff'
// },
// [`& .${linearProgressClasses.bar}`]: {
// borderRadius: 5,
// backgroundColor: theme.palette.primary.main
// }
// }));
const CardStyle = styled(Card)(({ theme }) => ({
background: theme.palette.primary.light,
marginBottom: '22px',
overflow: 'hidden',
position: 'relative',
'&:after': {
content: '""',
position: 'absolute',
width: '157px',
height: '157px',
background: theme.palette.primary[200],
borderRadius: '50%',
top: '-105px',
right: '-96px'
}
}));
// ==============================|| PROGRESS BAR WITH LABEL ||============================== //
// function LinearProgressWithLabel({ value, ...others }) {
// const theme = useTheme();
// return (
// <Grid container direction="column" spacing={1} sx={{ mt: 1.5 }}>
// <Grid item>
// <Grid container justifyContent="space-between">
// <Grid item>
// <Typography variant="h6" sx={{ color: theme.palette.primary[800] }}>
// Progress
// </Typography>
// </Grid>
// <Grid item>
// <Typography variant="h6" color="inherit">{`${Math.round(value)}%`}</Typography>
// </Grid>
// </Grid>
// </Grid>
// <Grid item>
// <BorderLinearProgress variant="determinate" value={value} {...others} />
// </Grid>
// </Grid>
// );
// }
// LinearProgressWithLabel.propTypes = {
// value: PropTypes.number
// };
// ==============================|| SIDEBAR MENU Card ||============================== //
const MenuCard = () => {
const theme = useTheme();
const account = useSelector((state) => state.account);
const navigate = useNavigate();
return (
<CardStyle>
<CardContent sx={{ p: 2 }}>
<List sx={{ p: 0, m: 0 }}>
<ListItem alignItems="flex-start" disableGutters sx={{ p: 0 }}>
<ListItemAvatar sx={{ mt: 0 }}>
<Avatar
variant="rounded"
src={User1}
sx={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
color: theme.palette.primary.main,
border: 'none',
borderColor: theme.palette.primary.main,
background: '#fff',
marginRight: '12px'
}}
onClick={() => navigate('/panel/profile')}
></Avatar>
</ListItemAvatar>
<ListItemText
sx={{ mt: 0 }}
primary={
<Typography variant="subtitle1" sx={{ color: theme.palette.primary[800] }}>
{account.user?.username}
</Typography>
}
secondary={<Typography variant="caption"> 欢迎回来 </Typography>}
/>
</ListItem>
</List>
{/* <LinearProgressWithLabel value={80} /> */}
</CardContent>
</CardStyle>
);
};
export default MenuCard;

View File

@@ -0,0 +1,158 @@
import PropTypes from 'prop-types';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Collapse, List, ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material';
// project imports
import NavItem from '../NavItem';
// assets
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import { IconChevronDown, IconChevronUp } from '@tabler/icons-react';
// ==============================|| SIDEBAR MENU LIST COLLAPSE ITEMS ||============================== //
const NavCollapse = ({ menu, level }) => {
const theme = useTheme();
const customization = useSelector((state) => state.customization);
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const [selected, setSelected] = useState(null);
const handleClick = () => {
setOpen(!open);
setSelected(!selected ? menu.id : null);
if (menu?.id !== 'authentication') {
navigate(menu.children[0]?.url);
}
};
const { pathname } = useLocation();
const checkOpenForParent = (child, id) => {
child.forEach((item) => {
if (item.url === pathname) {
setOpen(true);
setSelected(id);
}
});
};
// menu collapse for sub-levels
useEffect(() => {
setOpen(false);
setSelected(null);
if (menu.children) {
menu.children.forEach((item) => {
if (item.children?.length) {
checkOpenForParent(item.children, menu.id);
}
if (item.url === pathname) {
setSelected(menu.id);
setOpen(true);
}
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname, menu.children]);
// menu collapse & item
const menus = menu.children?.map((item) => {
switch (item.type) {
case 'collapse':
return <NavCollapse key={item.id} menu={item} level={level + 1} />;
case 'item':
return <NavItem key={item.id} item={item} level={level + 1} />;
default:
return (
<Typography key={item.id} variant="h6" color="error" align="center">
Menu Items Error
</Typography>
);
}
});
const Icon = menu.icon;
const menuIcon = menu.icon ? (
<Icon strokeWidth={1.5} size="1.3rem" style={{ marginTop: 'auto', marginBottom: 'auto' }} />
) : (
<FiberManualRecordIcon
sx={{
width: selected === menu.id ? 8 : 6,
height: selected === menu.id ? 8 : 6
}}
fontSize={level > 0 ? 'inherit' : 'medium'}
/>
);
return (
<>
<ListItemButton
sx={{
borderRadius: `${customization.borderRadius}px`,
mb: 0.5,
alignItems: 'flex-start',
backgroundColor: level > 1 ? 'transparent !important' : 'inherit',
py: level > 1 ? 1 : 1.25,
pl: `${level * 24}px`
}}
selected={selected === menu.id}
onClick={handleClick}
>
<ListItemIcon sx={{ my: 'auto', minWidth: !menu.icon ? 18 : 36 }}>{menuIcon}</ListItemIcon>
<ListItemText
primary={
<Typography variant={selected === menu.id ? 'h5' : 'body1'} color="inherit" sx={{ my: 'auto' }}>
{menu.title}
</Typography>
}
secondary={
menu.caption && (
<Typography variant="caption" sx={{ ...theme.typography.subMenuCaption }} display="block" gutterBottom>
{menu.caption}
</Typography>
)
}
/>
{open ? (
<IconChevronUp stroke={1.5} size="1rem" style={{ marginTop: 'auto', marginBottom: 'auto' }} />
) : (
<IconChevronDown stroke={1.5} size="1rem" style={{ marginTop: 'auto', marginBottom: 'auto' }} />
)}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List
component="div"
disablePadding
sx={{
position: 'relative',
'&:after': {
content: "''",
position: 'absolute',
left: '32px',
top: 0,
height: '100%',
width: '1px',
opacity: 1,
background: theme.palette.primary.light
}
}}
>
{menus}
</List>
</Collapse>
</>
);
};
NavCollapse.propTypes = {
menu: PropTypes.object,
level: PropTypes.number
};
export default NavCollapse;

View File

@@ -0,0 +1,61 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Divider, List, Typography } from '@mui/material';
// project imports
import NavItem from '../NavItem';
import NavCollapse from '../NavCollapse';
// ==============================|| SIDEBAR MENU LIST GROUP ||============================== //
const NavGroup = ({ item }) => {
const theme = useTheme();
// menu list collapse & items
const items = item.children?.map((menu) => {
switch (menu.type) {
case 'collapse':
return <NavCollapse key={menu.id} menu={menu} level={1} />;
case 'item':
return <NavItem key={menu.id} item={menu} level={1} />;
default:
return (
<Typography key={menu.id} variant="h6" color="error" align="center">
Menu Items Error
</Typography>
);
}
});
return (
<>
<List
subheader={
item.title && (
<Typography variant="caption" sx={{ ...theme.typography.menuCaption }} display="block" gutterBottom>
{item.title}
{item.caption && (
<Typography variant="caption" sx={{ ...theme.typography.subMenuCaption }} display="block" gutterBottom>
{item.caption}
</Typography>
)}
</Typography>
)
}
>
{items}
</List>
{/* group divider */}
<Divider sx={{ mt: 0.25, mb: 1.25 }} />
</>
);
};
NavGroup.propTypes = {
item: PropTypes.object
};
export default NavGroup;

View File

@@ -0,0 +1,115 @@
import PropTypes from 'prop-types';
import { forwardRef, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography, useMediaQuery } from '@mui/material';
// project imports
import { MENU_OPEN, SET_MENU } from 'store/actions';
// assets
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
// ==============================|| SIDEBAR MENU LIST ITEMS ||============================== //
const NavItem = ({ item, level }) => {
const theme = useTheme();
const dispatch = useDispatch();
const { pathname } = useLocation();
const customization = useSelector((state) => state.customization);
const matchesSM = useMediaQuery(theme.breakpoints.down('lg'));
const Icon = item.icon;
const itemIcon = item?.icon ? (
<Icon stroke={1.5} size="1.3rem" />
) : (
<FiberManualRecordIcon
sx={{
width: customization.isOpen.findIndex((id) => id === item?.id) > -1 ? 8 : 6,
height: customization.isOpen.findIndex((id) => id === item?.id) > -1 ? 8 : 6
}}
fontSize={level > 0 ? 'inherit' : 'medium'}
/>
);
let itemTarget = '_self';
if (item.target) {
itemTarget = '_blank';
}
let listItemProps = {
component: forwardRef((props, ref) => <Link ref={ref} {...props} to={item.url} target={itemTarget} />)
};
if (item?.external) {
listItemProps = { component: 'a', href: item.url, target: itemTarget };
}
const itemHandler = (id) => {
dispatch({ type: MENU_OPEN, id });
if (matchesSM) dispatch({ type: SET_MENU, opened: false });
};
// active menu item on page load
useEffect(() => {
const currentIndex = document.location.pathname
.toString()
.split('/')
.findIndex((id) => id === item.id);
if (currentIndex > -1) {
dispatch({ type: MENU_OPEN, id: item.id });
}
// eslint-disable-next-line
}, [pathname]);
return (
<ListItemButton
{...listItemProps}
disabled={item.disabled}
sx={{
borderRadius: `${customization.borderRadius}px`,
mb: 0.5,
alignItems: 'flex-start',
backgroundColor: level > 1 ? 'transparent !important' : 'inherit',
py: level > 1 ? 1 : 1.25,
pl: `${level * 24}px`
}}
selected={customization.isOpen.findIndex((id) => id === item.id) > -1}
onClick={() => itemHandler(item.id)}
>
<ListItemIcon sx={{ my: 'auto', minWidth: !item?.icon ? 18 : 36 }}>{itemIcon}</ListItemIcon>
<ListItemText
primary={
<Typography variant={customization.isOpen.findIndex((id) => id === item.id) > -1 ? 'h5' : 'body1'} color="inherit">
{item.title}
</Typography>
}
secondary={
item.caption && (
<Typography variant="caption" sx={{ ...theme.typography.subMenuCaption }} display="block" gutterBottom>
{item.caption}
</Typography>
)
}
/>
{item.chip && (
<Chip
color={item.chip.color}
variant={item.chip.variant}
size={item.chip.size}
label={item.chip.label}
avatar={item.chip.avatar && <Avatar>{item.chip.avatar}</Avatar>}
/>
)}
</ListItemButton>
);
};
NavItem.propTypes = {
item: PropTypes.object,
level: PropTypes.number
};
export default NavItem;

View File

@@ -0,0 +1,36 @@
// material-ui
import { Typography } from '@mui/material';
// project imports
import NavGroup from './NavGroup';
import menuItem from 'menu-items';
import { isAdmin } from 'utils/common';
// ==============================|| SIDEBAR MENU LIST ||============================== //
const MenuList = () => {
const userIsAdmin = isAdmin();
return (
<>
{menuItem.items.map((item) => {
if (item.type !== 'group') {
return (
<Typography key={item.id} variant="h6" color="error" align="center">
Menu Items Error
</Typography>
);
}
const filteredChildren = item.children.filter((child) => !child.isAdmin || userIsAdmin);
if (filteredChildren.length === 0) {
return null;
}
return <NavGroup key={item.id} item={{ ...item, children: filteredChildren }} />;
})}
</>
);
};
export default MenuList;

View File

@@ -0,0 +1,94 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box, Chip, Drawer, Stack, useMediaQuery } from '@mui/material';
// third-party
import PerfectScrollbar from 'react-perfect-scrollbar';
import { BrowserView, MobileView } from 'react-device-detect';
// project imports
import MenuList from './MenuList';
import LogoSection from '../LogoSection';
import MenuCard from './MenuCard';
import { drawerWidth } from 'store/constant';
// ==============================|| SIDEBAR DRAWER ||============================== //
const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
const theme = useTheme();
const matchUpMd = useMediaQuery(theme.breakpoints.up('md'));
const drawer = (
<>
<Box sx={{ display: { xs: 'block', md: 'none' } }}>
<Box sx={{ display: 'flex', p: 2, mx: 'auto' }}>
<LogoSection />
</Box>
</Box>
<BrowserView>
<PerfectScrollbar
component="div"
style={{
height: !matchUpMd ? 'calc(100vh - 56px)' : 'calc(100vh - 88px)',
paddingLeft: '16px',
paddingRight: '16px'
}}
>
<MenuList />
<MenuCard />
<Stack direction="row" justifyContent="center" sx={{ mb: 2 }}>
<Chip label={process.env.REACT_APP_VERSION} disabled chipcolor="secondary" size="small" sx={{ cursor: 'pointer' }} />
</Stack>
</PerfectScrollbar>
</BrowserView>
<MobileView>
<Box sx={{ px: 2 }}>
<MenuList />
<MenuCard />
<Stack direction="row" justifyContent="center" sx={{ mb: 2 }}>
<Chip label={process.env.REACT_APP_VERSION} disabled chipcolor="secondary" size="small" sx={{ cursor: 'pointer' }} />
</Stack>
</Box>
</MobileView>
</>
);
const container = window !== undefined ? () => window.document.body : undefined;
return (
<Box component="nav" sx={{ flexShrink: { md: 0 }, width: matchUpMd ? drawerWidth : 'auto' }} aria-label="mailbox folders">
<Drawer
container={container}
variant={matchUpMd ? 'persistent' : 'temporary'}
anchor="left"
open={drawerOpen}
onClose={drawerToggle}
sx={{
'& .MuiDrawer-paper': {
width: drawerWidth,
background: theme.palette.background.default,
color: theme.palette.text.primary,
borderRight: 'none',
[theme.breakpoints.up('md')]: {
top: '88px'
}
}
}}
ModalProps={{ keepMounted: true }}
color="inherit"
>
{drawer}
</Drawer>
</Box>
);
};
Sidebar.propTypes = {
drawerOpen: PropTypes.bool,
drawerToggle: PropTypes.func,
window: PropTypes.object
};
export default Sidebar;

View File

@@ -0,0 +1,103 @@
import { useDispatch, useSelector } from 'react-redux';
import { Outlet } from 'react-router-dom';
import AuthGuard from 'utils/route-guard/AuthGuard';
// material-ui
import { styled, useTheme } from '@mui/material/styles';
import { AppBar, Box, CssBaseline, Toolbar, useMediaQuery } from '@mui/material';
import AdminContainer from 'ui-component/AdminContainer';
// project imports
import Breadcrumbs from 'ui-component/extended/Breadcrumbs';
import Header from './Header';
import Sidebar from './Sidebar';
import navigation from 'menu-items';
import { drawerWidth } from 'store/constant';
import { SET_MENU } from 'store/actions';
// assets
import { IconChevronRight } from '@tabler/icons-react';
// styles
const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
...theme.typography.mainContent,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
transition: theme.transitions.create(
'margin',
open
? {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}
: {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}
),
[theme.breakpoints.up('md')]: {
marginLeft: open ? 0 : -(drawerWidth - 20),
width: `calc(100% - ${drawerWidth}px)`
},
[theme.breakpoints.down('md')]: {
marginLeft: '20px',
width: `calc(100% - ${drawerWidth}px)`,
padding: '16px'
},
[theme.breakpoints.down('sm')]: {
marginLeft: '10px',
width: `calc(100% - ${drawerWidth}px)`,
padding: '16px',
marginRight: '10px'
}
}));
// ==============================|| MAIN LAYOUT ||============================== //
const MainLayout = () => {
const theme = useTheme();
const matchDownMd = useMediaQuery(theme.breakpoints.down('md'));
// Handle left drawer
const leftDrawerOpened = useSelector((state) => state.customization.opened);
const dispatch = useDispatch();
const handleLeftDrawerToggle = () => {
dispatch({ type: SET_MENU, opened: !leftDrawerOpened });
};
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
{/* header */}
<AppBar
enableColorOnDark
position="fixed"
color="inherit"
elevation={0}
sx={{
bgcolor: theme.palette.background.default,
transition: leftDrawerOpened ? theme.transitions.create('width') : 'none'
}}
>
<Toolbar>
<Header handleLeftDrawerToggle={handleLeftDrawerToggle} />
</Toolbar>
</AppBar>
{/* drawer */}
<Sidebar drawerOpen={!matchDownMd ? leftDrawerOpened : !leftDrawerOpened} drawerToggle={handleLeftDrawerToggle} />
{/* main content */}
<Main theme={theme} open={leftDrawerOpened}>
{/* breadcrumb */}
<Breadcrumbs separator={IconChevronRight} navigation={navigation} icon title rightAlign />
<AuthGuard>
<AdminContainer>
<Outlet />
</AdminContainer>
</AuthGuard>
</Main>
</Box>
);
};
export default MainLayout;