first
This commit is contained in:
321
src/layout/CommonLayout/FooterBlock.js
Normal file
321
src/layout/CommonLayout/FooterBlock.js
Normal file
@@ -0,0 +1,321 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// material-ui
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import { Box, Button, Container, CardMedia, Divider, Stack, Grid, Link, Typography } from '@mui/material';
|
||||
|
||||
// third party
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// project import
|
||||
import { ThemeDirection, ThemeMode } from 'config';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
|
||||
// assets
|
||||
import { SendOutlined } from '@ant-design/icons';
|
||||
import imgfooterlogo from 'assets/images/landing/codedthemes-logo.svg';
|
||||
import imgfootersoc1 from 'assets/images/landing/img-soc1.svg';
|
||||
import imgfootersoc2 from 'assets/images/landing/img-soc2.svg';
|
||||
import imgfootersoc3 from 'assets/images/landing/img-soc3.svg';
|
||||
import AnimateButton from 'components/@extended/AnimateButton';
|
||||
|
||||
const dashImage = require.context('assets/images/landing', true);
|
||||
|
||||
// link - custom style
|
||||
const FooterLink = styled(Link)(({ theme }) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.main
|
||||
},
|
||||
'&:active': {
|
||||
color: theme.palette.primary.main
|
||||
}
|
||||
}));
|
||||
|
||||
// ==============================|| LANDING - FOOTER PAGE ||============================== //
|
||||
|
||||
const FooterBlock = ({ isFull }) => {
|
||||
const theme = useTheme();
|
||||
const { presetColor } = useConfig();
|
||||
const textColor = theme.palette.mode === ThemeMode.DARK ? 'text.primary' : 'background.paper';
|
||||
|
||||
const linkSX = {
|
||||
color: theme.palette.common.white,
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 400,
|
||||
opacity: '0.6',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
opacity: '1'
|
||||
}
|
||||
};
|
||||
|
||||
const frameworks = [
|
||||
{ title: 'CodeIgniter', link: 'https://links.codedthemes.com/dEGKs' },
|
||||
{
|
||||
title: 'React MUI',
|
||||
link: 'https://links.codedthemes.com/Aprwb'
|
||||
},
|
||||
{
|
||||
title: 'Angular',
|
||||
link: 'https://links.codedthemes.com/EIvof'
|
||||
},
|
||||
{
|
||||
title: 'Bootstrap 5',
|
||||
link: 'https://codedthemes.com/item/mantis-bootstrap-admin-dashboard/'
|
||||
},
|
||||
{
|
||||
title: '.Net',
|
||||
link: 'https://links.codedthemes.com/GPZhD'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
{isFull && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
bgcolor: theme.palette.grey.A700,
|
||||
zIndex: 1,
|
||||
mt: { xs: 0, md: 13.75 },
|
||||
pt: { xs: 8, sm: 7.5, md: 18.75 },
|
||||
pb: { xs: 2.5, md: 10 },
|
||||
'&:after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '80%',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
background:
|
||||
theme.direction === ThemeDirection.RTL
|
||||
? `linear-gradient(transparent 100%, rgb(31, 31, 31) 70%)`
|
||||
: `linear-gradient(180deg, transparent 0%, ${theme.palette.grey.A700} 70%)`
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CardMedia
|
||||
component="img"
|
||||
image={dashImage(`./img-footer-${presetColor}.png`)}
|
||||
sx={{
|
||||
display: { xs: 'none', md: 'block' },
|
||||
width: '55%',
|
||||
maxWidth: 700,
|
||||
position: 'absolute',
|
||||
top: '-28%',
|
||||
right: 0
|
||||
}}
|
||||
/>
|
||||
<Container>
|
||||
<Grid container alignItems="center" justifyContent="space-between" spacing={2}>
|
||||
<Grid item xs={12} md={6} sx={{ position: 'relative', zIndex: 1 }}>
|
||||
<Grid container spacing={2} sx={{ [theme.breakpoints.down('md')]: { pr: 0, textAlign: 'center' } }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" sx={{ color: theme.palette.common.white }}>
|
||||
Roadmap
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, translateY: 550 }}
|
||||
animate={{ opacity: 1, translateY: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 150,
|
||||
damping: 30
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
color: theme.palette.common.white,
|
||||
fontWeight: 700
|
||||
}}
|
||||
>
|
||||
Upcoming Release
|
||||
</Typography>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body1" sx={{ color: theme.palette.common.white }}>
|
||||
What is next? Checkout the Upcoming release of Mantis React.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{ my: 2 }}>
|
||||
<Box sx={{ display: 'inline-block' }}>
|
||||
<AnimateButton>
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
endIcon={<SendOutlined />}
|
||||
component={Link}
|
||||
href="https://links.codedthemes.com/RXnKQ"
|
||||
target="_blank"
|
||||
>
|
||||
Roadmap
|
||||
</Button>
|
||||
</AnimateButton>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box sx={{ pt: isFull ? 0 : 10, pb: 10, bgcolor: theme.palette.grey.A700 }}>
|
||||
<Container>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={4}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, translateY: 550 }}
|
||||
animate={{ opacity: 1, translateY: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 150,
|
||||
damping: 30
|
||||
}}
|
||||
>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<CardMedia component="img" image={imgfooterlogo} sx={{ width: 'auto' }} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 400, color: theme.palette.common.white }}>
|
||||
Since 2017, More than 50K+ Developers trust the CodedThemes Digital Product. Mantis React is Manage under their
|
||||
Experienced Team Players.
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={8}>
|
||||
<Grid container spacing={{ xs: 5, md: 2 }}>
|
||||
<Grid item xs={6} sm={3}>
|
||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
||||
Help
|
||||
</Typography>
|
||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
||||
<FooterLink href="https://links.codedthemes.com/nOuhw" target="_blank" underline="none">
|
||||
Blog
|
||||
</FooterLink>
|
||||
<FooterLink href="https://links.codedthemes.com/BQFrl" target="_blank" underline="none">
|
||||
Documentation
|
||||
</FooterLink>
|
||||
<FooterLink href="https://links.codedthemes.com/rhzvh" target="_blank" underline="none">
|
||||
Change Log
|
||||
</FooterLink>
|
||||
<FooterLink href="https://codedthemes.support-hub.io/" target="_blank" underline="none">
|
||||
Support
|
||||
</FooterLink>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={6} sm={3}>
|
||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
||||
Store Help
|
||||
</Typography>
|
||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
||||
<FooterLink href="https://mui.com/store/license/" target="_blank" underline="none">
|
||||
License
|
||||
</FooterLink>
|
||||
<FooterLink href="https://mui.com/store/customer-refund-policy/" target="_blank" underline="none">
|
||||
Refund Policy
|
||||
</FooterLink>
|
||||
<FooterLink
|
||||
href="https://support.mui.com/hc/en-us/sections/360002564979-For-customers"
|
||||
target="_blank"
|
||||
underline="none"
|
||||
>
|
||||
Submit a Request
|
||||
</FooterLink>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={6} sm={3}>
|
||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
||||
Mantis Eco-System
|
||||
</Typography>
|
||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
||||
{frameworks.map((item, index) => (
|
||||
<FooterLink href={item.link} target="_blank" underline="none" key={index}>
|
||||
{item.title}
|
||||
{/* {item.isUpcoming && <Chip variant="outlined" size="small" label="Upcoming" sx={{ ml: 0.5 }} />} */}
|
||||
</FooterLink>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={6} sm={3}>
|
||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
||||
More Products
|
||||
</Typography>
|
||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
||||
<FooterLink href="https://links.codedthemes.com/zyACc" target="_blank" underline="none">
|
||||
Berry React Material
|
||||
</FooterLink>
|
||||
<FooterLink href="https://links.codedthemes.com/WztNI" target="_blank" underline="none">
|
||||
Free Berry React
|
||||
</FooterLink>
|
||||
<FooterLink href="https://links.codedthemes.com/sTUAK" target="_blank" underline="none">
|
||||
Free Mantis React
|
||||
</FooterLink>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
<Divider sx={{ borderColor: 'grey.700' }} />
|
||||
<Box
|
||||
sx={{
|
||||
py: 1.5,
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? theme.palette.grey[50] : theme.palette.grey[800]
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={8}>
|
||||
<Typography variant="subtitle2" color="secondary">
|
||||
© Made with love by Team CodedThemes
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Grid container spacing={2} alignItems="center" sx={{ justifyContent: 'flex-end' }}>
|
||||
<Grid item>
|
||||
<Link underline="none" sx={linkSX}>
|
||||
<CardMedia component="img" image={imgfootersoc1} />
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Link underline="none" sx={linkSX}>
|
||||
<CardMedia component="img" image={imgfootersoc2} />
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Link underline="none" sx={linkSX}>
|
||||
<CardMedia component="img" image={imgfootersoc3} />
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
FooterBlock.propTypes = {
|
||||
isFull: PropTypes.bool
|
||||
};
|
||||
|
||||
export default FooterBlock;
|
||||
251
src/layout/CommonLayout/Header.js
Normal file
251
src/layout/CommonLayout/Header.js
Normal file
@@ -0,0 +1,251 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
useMediaQuery,
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
Container,
|
||||
Drawer,
|
||||
Link,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Stack,
|
||||
Toolbar,
|
||||
Typography,
|
||||
useScrollTrigger
|
||||
} from '@mui/material';
|
||||
|
||||
// project import
|
||||
import { APP_DEFAULT_PATH, ThemeMode } from 'config';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
import AnimateButton from 'components/@extended/AnimateButton';
|
||||
import Logo from 'components/logo';
|
||||
|
||||
// assets
|
||||
import { MenuOutlined, LineOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| COMPONENTS - APP BAR ||============================== //
|
||||
|
||||
// elevation scroll
|
||||
function ElevationScroll({ layout, children, window }) {
|
||||
const theme = useTheme();
|
||||
// const theme = useTheme();
|
||||
|
||||
const trigger = useScrollTrigger({
|
||||
disableHysteresis: true,
|
||||
threshold: 10,
|
||||
target: window ? window() : undefined
|
||||
});
|
||||
|
||||
const backColorScroll = theme.palette.mode === ThemeMode.DARK ? theme.palette.grey[50] : theme.palette.grey[800];
|
||||
const backColor = layout !== 'landing' ? backColorScroll : 'transparent';
|
||||
|
||||
return React.cloneElement(children, {
|
||||
style: {
|
||||
backgroundColor: trigger ? backColorScroll : backColor
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const Header = ({ handleDrawerOpen, layout = 'landing', ...others }) => {
|
||||
const theme = useTheme();
|
||||
const matchDownMd = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [drawerToggle, setDrawerToggle] = useState(false);
|
||||
|
||||
/** Method called on multiple components with different event types */
|
||||
const drawerToggler = (open) => (event) => {
|
||||
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
|
||||
return;
|
||||
}
|
||||
setDrawerToggle(open);
|
||||
};
|
||||
|
||||
return (
|
||||
<ElevationScroll layout={layout} {...others}>
|
||||
<AppBar sx={{ bgcolor: 'transparent', color: theme.palette.text.primary, boxShadow: 'none' }}>
|
||||
<Container disableGutters={matchDownMd}>
|
||||
<Toolbar sx={{ px: { xs: 1.5, md: 0, lg: 0 }, py: 2 }}>
|
||||
<Stack direction="row" sx={{ flexGrow: 1, display: { xs: 'none', md: 'block' } }} alignItems="center">
|
||||
<Typography component="div" sx={{ textAlign: 'left', display: 'inline-block' }}>
|
||||
<Logo reverse to="/" />
|
||||
</Typography>
|
||||
<Chip
|
||||
label={process.env.REACT_APP_VERSION}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
color="secondary"
|
||||
sx={{ mt: 0.5, ml: 1, fontSize: '0.725rem', height: 20, '& .MuiChip-label': { px: 0.5 } }}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
'& .header-link': { px: 1, '&:hover': { color: theme.palette.primary.main } },
|
||||
display: { xs: 'none', md: 'block' }
|
||||
}}
|
||||
spacing={2}
|
||||
>
|
||||
<Link className="header-link" color="white" component={RouterLink} to="/login" target="_blank" underline="none">
|
||||
Dashboard
|
||||
</Link>
|
||||
<Link
|
||||
className="header-link"
|
||||
color={handleDrawerOpen ? 'primary' : 'white'}
|
||||
component={RouterLink}
|
||||
to="/components-overview/buttons"
|
||||
underline="none"
|
||||
>
|
||||
Components
|
||||
</Link>
|
||||
<Link className="header-link" color="white" href="https://links.codedthemes.com/BQFrl" target="_blank" underline="none">
|
||||
Documentation
|
||||
</Link>
|
||||
<Box sx={{ display: 'inline-block' }}>
|
||||
<AnimateButton>
|
||||
<Button component={Link} href="https://links.codedthemes.com/Aprwb" disableElevation color="primary" variant="contained">
|
||||
Purchase Now
|
||||
</Button>
|
||||
</AnimateButton>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
display: { xs: 'flex', md: 'none' }
|
||||
}}
|
||||
>
|
||||
<Typography component="div" sx={{ textAlign: 'left', display: 'inline-block' }}>
|
||||
<Logo reverse to="/" />
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={2}>
|
||||
{layout === 'component' && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
color="warning"
|
||||
component={RouterLink}
|
||||
to={APP_DEFAULT_PATH}
|
||||
sx={{ mt: 0.5, height: 28 }}
|
||||
>
|
||||
Dashboard
|
||||
</Button>
|
||||
)}
|
||||
{layout !== 'component' && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
color="warning"
|
||||
component={RouterLink}
|
||||
to="/components-overview/buttons"
|
||||
sx={{ mt: 0.5, height: 28 }}
|
||||
>
|
||||
All Components
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<IconButton
|
||||
color="secondary"
|
||||
{...(layout === 'component' ? { onClick: handleDrawerOpen } : { onClick: drawerToggler(true) })}
|
||||
sx={{
|
||||
'&:hover': { bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.lighter' : 'secondary.dark' }
|
||||
}}
|
||||
>
|
||||
<MenuOutlined style={{ color: theme.palette.mode === ThemeMode.DARK ? 'inherit' : theme.palette.grey[100] }} />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
<Drawer
|
||||
anchor="top"
|
||||
open={drawerToggle}
|
||||
onClose={drawerToggler(false)}
|
||||
sx={{ '& .MuiDrawer-paper': { backgroundImage: 'none' } }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 'auto',
|
||||
'& .MuiListItemIcon-root': {
|
||||
fontSize: '1rem',
|
||||
minWidth: 28
|
||||
}
|
||||
}}
|
||||
role="presentation"
|
||||
onClick={drawerToggler(false)}
|
||||
onKeyDown={drawerToggler(false)}
|
||||
>
|
||||
<List>
|
||||
<Link style={{ textDecoration: 'none' }} href="/login" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Dashboard" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="/components-overview/buttons" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="All Components" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/sTUAK" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Free Version" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/BQFrl" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Documentation" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="https://codedthemes.support-hub.io/" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Support" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/Aprwb" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Purchase Now" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
<Chip color="primary" label="v1.0" size="small" />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
</List>
|
||||
</Box>
|
||||
</Drawer>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
</ElevationScroll>
|
||||
);
|
||||
};
|
||||
|
||||
Header.propTypes = {
|
||||
handleDrawerOpen: PropTypes.func,
|
||||
layout: PropTypes.string
|
||||
};
|
||||
|
||||
export default Header;
|
||||
74
src/layout/CommonLayout/index.js
Normal file
74
src/layout/CommonLayout/index.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { Container, Toolbar } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import { dispatch, useSelector } from 'store';
|
||||
import { openComponentDrawer } from 'store/reducers/menu';
|
||||
|
||||
// material-ui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
|
||||
const Header = lazy(() => import('./Header'));
|
||||
const FooterBlock = lazy(() => import('./FooterBlock'));
|
||||
|
||||
// ==============================|| Loader ||============================== //
|
||||
|
||||
const LoaderWrapper = styled('div')(({ theme }) => ({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 2001,
|
||||
width: '100%',
|
||||
'& > * + *': {
|
||||
marginTop: theme.spacing(2)
|
||||
}
|
||||
}));
|
||||
|
||||
const Loader = () => (
|
||||
<LoaderWrapper>
|
||||
<LinearProgress color="primary" />
|
||||
</LoaderWrapper>
|
||||
);
|
||||
|
||||
// ==============================|| MINIMAL LAYOUT ||============================== //
|
||||
|
||||
const CommonLayout = ({ layout = 'blank' }) => {
|
||||
const menu = useSelector((state) => state.menu);
|
||||
const { componentDrawerOpen } = menu;
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
dispatch(openComponentDrawer({ componentDrawerOpen: !componentDrawerOpen }));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{(layout === 'landing' || layout === 'simple') && (
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Header layout={layout} />
|
||||
<Outlet />
|
||||
<FooterBlock isFull={layout === 'landing'} />
|
||||
</Suspense>
|
||||
)}
|
||||
{layout === 'component' && (
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Container maxWidth="lg" sx={{ px: { xs: 0, sm: 2 } }}>
|
||||
<Header handleDrawerOpen={handleDrawerOpen} layout="component" />
|
||||
<Toolbar sx={{ my: 2 }} />
|
||||
</Container>
|
||||
</Suspense>
|
||||
)}
|
||||
{layout === 'blank' && <Outlet />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
CommonLayout.propTypes = {
|
||||
layout: PropTypes.string
|
||||
};
|
||||
|
||||
export default CommonLayout;
|
||||
32
src/layout/MainLayout/Drawer/DrawerContent/NavCard.js
Normal file
32
src/layout/MainLayout/Drawer/DrawerContent/NavCard.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// material-ui
|
||||
import { Button, Link, CardMedia, Stack, Typography } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import MainCard from 'components/MainCard';
|
||||
|
||||
// assets
|
||||
import avatar from 'assets/images/users/avatar-group.png';
|
||||
import AnimateButton from 'components/@extended/AnimateButton';
|
||||
|
||||
// ==============================|| DRAWER CONTENT - NAVIGATION CARD ||============================== //
|
||||
|
||||
const NavCard = () => (
|
||||
<MainCard sx={{ bgcolor: 'grey.50', m: 3 }}>
|
||||
<Stack alignItems="center" spacing={2.5}>
|
||||
<CardMedia component="img" image={avatar} />
|
||||
<Stack alignItems="center">
|
||||
<Typography variant="h5">Help?</Typography>
|
||||
<Typography variant="h6" color="secondary">
|
||||
Get to resolve query
|
||||
</Typography>
|
||||
</Stack>
|
||||
<AnimateButton>
|
||||
<Button variant="shadow" size="small" component={Link} href="https://codedthemes.support-hub.io/" target="_blank">
|
||||
Support
|
||||
</Button>
|
||||
</AnimateButton>
|
||||
</Stack>
|
||||
</MainCard>
|
||||
);
|
||||
|
||||
export default NavCard;
|
||||
@@ -0,0 +1,459 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
ClickAwayListener,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Paper,
|
||||
Popper,
|
||||
Typography,
|
||||
useMediaQuery
|
||||
} from '@mui/material';
|
||||
|
||||
// project import
|
||||
import NavItem from './NavItem';
|
||||
import Dot from 'components/@extended/Dot';
|
||||
import SimpleBar from 'components/third-party/SimpleBar';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
|
||||
import useConfig from 'hooks/useConfig';
|
||||
import { dispatch, useSelector } from 'store';
|
||||
import { activeItem } from 'store/reducers/menu';
|
||||
import { MenuOrientation, ThemeMode } from 'config';
|
||||
|
||||
// assets
|
||||
import { BorderOutlined, DownOutlined, UpOutlined, RightOutlined } from '@ant-design/icons';
|
||||
|
||||
// mini-menu - wrapper
|
||||
const PopperStyled = styled(Popper)(({ theme }) => ({
|
||||
overflow: 'visible',
|
||||
zIndex: 1202,
|
||||
minWidth: 180,
|
||||
'&:before': {
|
||||
content: '""',
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
top: 38,
|
||||
left: -5,
|
||||
width: 10,
|
||||
height: 10,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
transform: 'translateY(-50%) rotate(45deg)',
|
||||
zIndex: 120,
|
||||
borderLeft: `1px solid ${theme.palette.grey.A800}`,
|
||||
borderBottom: `1px solid ${theme.palette.grey.A800}`
|
||||
}
|
||||
}));
|
||||
|
||||
// ==============================|| NAVIGATION - LIST COLLAPSE ||============================== //
|
||||
|
||||
const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
const menuState = useSelector((state) => state.menu);
|
||||
|
||||
const { drawerOpen } = menuState;
|
||||
const { menuOrientation } = useConfig();
|
||||
const navigation = useNavigate();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selected, setSelected] = useState(null);
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(null);
|
||||
setSelectedLevel(level);
|
||||
if (drawerOpen) {
|
||||
setOpen(!open);
|
||||
setSelected(!selected ? menu.id : null);
|
||||
setSelectedItems(!selected ? menu.id : '');
|
||||
if (menu.url) navigation(`${menu.url}`);
|
||||
} else {
|
||||
setAnchorEl(event?.currentTarget);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerIconLink = () => {
|
||||
if (!drawerOpen) {
|
||||
if (menu.url) navigation(`${menu.url}`);
|
||||
setSelected(menu.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleHover = (event) => {
|
||||
setAnchorEl(event?.currentTarget);
|
||||
if (!drawerOpen) {
|
||||
setSelected(menu.id);
|
||||
}
|
||||
};
|
||||
|
||||
const miniMenuOpened = Boolean(anchorEl);
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
if (!miniMenuOpened) {
|
||||
if (!menu.url) {
|
||||
setSelected(null);
|
||||
}
|
||||
}
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
useMemo(() => {
|
||||
if (selected === selectedItems) {
|
||||
if (level === 1) {
|
||||
setOpen(true);
|
||||
}
|
||||
} else {
|
||||
if (level === selectedLevel) {
|
||||
setOpen(false);
|
||||
if (!miniMenuOpened && !drawerOpen && !selected) {
|
||||
setSelected(null);
|
||||
}
|
||||
if (drawerOpen) {
|
||||
setSelected(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [selectedItems, level, selected, miniMenuOpened, drawerOpen, selectedLevel]);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
if (pathname === menu.url) {
|
||||
setSelected(menu.id);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [pathname]);
|
||||
|
||||
const checkOpenForParent = (child, id) => {
|
||||
child.forEach((item) => {
|
||||
if (item.url === pathname) {
|
||||
setOpen(true);
|
||||
setSelected(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setOpen(false);
|
||||
if (!miniMenuOpened) {
|
||||
setSelected(null);
|
||||
}
|
||||
if (miniMenuOpened) setAnchorEl(null);
|
||||
if (menu.children) {
|
||||
menu.children.forEach((item) => {
|
||||
if (item.children?.length) {
|
||||
checkOpenForParent(item.children, menu.id);
|
||||
}
|
||||
if (pathname && pathname.includes('product-details')) {
|
||||
if (item.url && item.url.includes('product-details')) {
|
||||
setSelected(menu.id);
|
||||
setOpen(true);
|
||||
}
|
||||
}
|
||||
if (item.url === pathname) {
|
||||
setSelected(menu.id);
|
||||
setOpen(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pathname, menu.children]);
|
||||
|
||||
useEffect(() => {
|
||||
if (menu.url === pathname) {
|
||||
dispatch(activeItem({ openItem: [menu.id] }));
|
||||
setSelected(menu.id);
|
||||
setAnchorEl(null);
|
||||
setOpen(true);
|
||||
}
|
||||
}, [pathname, menu]);
|
||||
|
||||
const navCollapse = menu.children?.map((item) => {
|
||||
switch (item.type) {
|
||||
case 'collapse':
|
||||
return (
|
||||
<NavCollapse
|
||||
key={item.id}
|
||||
setSelectedItems={setSelectedItems}
|
||||
setSelectedLevel={setSelectedLevel}
|
||||
selectedLevel={selectedLevel}
|
||||
selectedItems={selectedItems}
|
||||
menu={item}
|
||||
level={level + 1}
|
||||
parentId={parentId}
|
||||
/>
|
||||
);
|
||||
case 'item':
|
||||
return <NavItem key={item.id} item={item} level={level + 1} />;
|
||||
default:
|
||||
return (
|
||||
<Typography key={item.id} variant="h6" color="error" align="center">
|
||||
Fix - Collapse or Item
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const isSelected = selected === menu.id;
|
||||
const borderIcon = level === 1 ? <BorderOutlined style={{ fontSize: '1rem' }} /> : false;
|
||||
const Icon = menu.icon;
|
||||
const menuIcon = menu.icon ? <Icon style={{ fontSize: drawerOpen ? '1rem' : '1.25rem' }} /> : borderIcon;
|
||||
const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : 'text.primary';
|
||||
const iconSelectedColor = theme.palette.mode === ThemeMode.DARK && drawerOpen ? theme.palette.text.primary : theme.palette.primary.main;
|
||||
const popperId = miniMenuOpened ? `collapse-pop-${menu.id}` : undefined;
|
||||
const FlexBox = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' };
|
||||
|
||||
return (
|
||||
<>
|
||||
{menuOrientation === MenuOrientation.VERTICAL || downLG ? (
|
||||
<>
|
||||
<ListItemButton
|
||||
disableRipple
|
||||
selected={selected === menu.id}
|
||||
{...(!drawerOpen && { onMouseEnter: handleClick, onMouseLeave: handleClose })}
|
||||
onClick={handleClick}
|
||||
sx={{
|
||||
pl: drawerOpen ? `${level * 28}px` : 1.5,
|
||||
py: !drawerOpen && level === 1 ? 1.25 : 1,
|
||||
...(drawerOpen && {
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter'
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'transparent',
|
||||
color: iconSelectedColor,
|
||||
'&:hover': { color: iconSelectedColor, bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'transparent' }
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen && {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
{menuIcon && (
|
||||
<ListItemIcon
|
||||
onClick={handlerIconLink}
|
||||
sx={{
|
||||
minWidth: 28,
|
||||
color: selected === menu.id ? 'primary.main' : textColor,
|
||||
...(!drawerOpen && {
|
||||
borderRadius: 1.5,
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.light' : 'secondary.lighter'
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen &&
|
||||
selected === menu.id && {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.900' : 'primary.lighter',
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.darker' : 'primary.lighter'
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
{menuIcon}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
{(drawerOpen || (!drawerOpen && level !== 1)) && (
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6" color={selected === menu.id ? 'primary' : textColor}>
|
||||
{menu.title}
|
||||
</Typography>
|
||||
}
|
||||
secondary={
|
||||
menu.caption && (
|
||||
<Typography variant="caption" color="secondary">
|
||||
{menu.caption}
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{(drawerOpen || (!drawerOpen && level !== 1)) &&
|
||||
(miniMenuOpened || open ? (
|
||||
<UpOutlined style={{ fontSize: '0.625rem', marginLeft: 1, color: theme.palette.primary.main }} />
|
||||
) : (
|
||||
<DownOutlined style={{ fontSize: '0.625rem', marginLeft: 1 }} />
|
||||
))}
|
||||
|
||||
{!drawerOpen && (
|
||||
<PopperStyled
|
||||
open={miniMenuOpened}
|
||||
anchorEl={anchorEl}
|
||||
placement="right-start"
|
||||
style={{
|
||||
zIndex: 2001
|
||||
}}
|
||||
popperOptions={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [-12, 1]
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions in={miniMenuOpened} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
mt: 1.5,
|
||||
boxShadow: theme.customShadows.z1,
|
||||
backgroundImage: 'none',
|
||||
border: `1px solid ${theme.palette.divider}`
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<SimpleBar
|
||||
sx={{
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
maxHeight: 'calc(100vh - 170px)'
|
||||
}}
|
||||
>
|
||||
{navCollapse}
|
||||
</SimpleBar>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</PopperStyled>
|
||||
)}
|
||||
</ListItemButton>
|
||||
{drawerOpen && (
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<List sx={{ p: 0 }}>{navCollapse}</List>
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ListItemButton
|
||||
id={`boundary-${popperId}`}
|
||||
disableRipple
|
||||
selected={isSelected}
|
||||
onMouseEnter={handleHover}
|
||||
onMouseLeave={handleClose}
|
||||
onClick={handleHover}
|
||||
aria-describedby={popperId}
|
||||
sx={{
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box onClick={handlerIconLink} sx={FlexBox}>
|
||||
{menuIcon && (
|
||||
<ListItemIcon sx={{ my: 'auto', minWidth: !menu.icon ? 18 : 36, color: theme.palette.secondary.dark }}>
|
||||
{menuIcon}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
{!menuIcon && level !== 1 && (
|
||||
<ListItemIcon
|
||||
sx={{ my: 'auto', minWidth: !menu.icon ? 18 : 36, bgcolor: 'transparent', '&:hover': { bgcolor: 'transparent' } }}
|
||||
>
|
||||
<Dot size={4} color={isSelected ? 'primary' : 'secondary'} />
|
||||
</ListItemIcon>
|
||||
)}
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="body1" color="inherit" sx={{ my: 'auto' }}>
|
||||
{menu.title}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{miniMenuOpened ? <RightOutlined /> : <DownOutlined />}
|
||||
</Box>
|
||||
|
||||
{anchorEl && (
|
||||
<PopperStyled
|
||||
id={popperId}
|
||||
open={miniMenuOpened}
|
||||
anchorEl={anchorEl}
|
||||
placement="right-start"
|
||||
style={{
|
||||
zIndex: 2001
|
||||
}}
|
||||
modifiers={[
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [-10, 0]
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions in={miniMenuOpened} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
mt: 1.5,
|
||||
py: 0.5,
|
||||
boxShadow: theme.shadows[8],
|
||||
backgroundImage: 'none'
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<SimpleBar
|
||||
sx={{
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
maxHeight: 'calc(100vh - 170px)'
|
||||
}}
|
||||
>
|
||||
{navCollapse}
|
||||
</SimpleBar>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</PopperStyled>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
NavCollapse.propTypes = {
|
||||
menu: PropTypes.object,
|
||||
level: PropTypes.number,
|
||||
parentId: PropTypes.string,
|
||||
setSelectedItems: PropTypes.func,
|
||||
selectedItems: PropTypes.string,
|
||||
setSelectedLevel: PropTypes.func,
|
||||
selectedLevel: PropTypes.number
|
||||
};
|
||||
|
||||
export default NavCollapse;
|
||||
@@ -0,0 +1,340 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
// material-ui
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Box,
|
||||
ClickAwayListener,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Paper,
|
||||
Popper,
|
||||
Typography,
|
||||
useMediaQuery
|
||||
} from '@mui/material';
|
||||
|
||||
// third-party
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
// project import
|
||||
import NavItem from './NavItem';
|
||||
import NavCollapse from './NavCollapse';
|
||||
import SimpleBar from 'components/third-party/SimpleBar';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
|
||||
import { MenuOrientation, ThemeMode } from 'config';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
import { dispatch, useSelector } from 'store';
|
||||
import { activeID } from 'store/reducers/menu';
|
||||
|
||||
// assets
|
||||
import { DownOutlined, RightOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| NAVIGATION - LIST GROUP ||============================== //
|
||||
|
||||
const PopperStyled = styled(Popper)(({ theme }) => ({
|
||||
overflow: 'visible',
|
||||
zIndex: 1202,
|
||||
minWidth: 180,
|
||||
'&:before': {
|
||||
content: '""',
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
top: 5,
|
||||
left: 32,
|
||||
width: 12,
|
||||
height: 12,
|
||||
transform: 'translateY(-50%) rotate(45deg)',
|
||||
zIndex: 120,
|
||||
borderWidth: '6px',
|
||||
borderStyle: 'solid',
|
||||
borderColor: `${theme.palette.background.paper} transparent transparent ${theme.palette.background.paper}`
|
||||
}
|
||||
}));
|
||||
|
||||
const NavGroup = ({ item, lastItem, remItems, lastItemId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) => {
|
||||
const theme = useTheme();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { menuOrientation } = useConfig();
|
||||
const menu = useSelector((state) => state.menu);
|
||||
const { drawerOpen, selectedID } = menu;
|
||||
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [currentItem, setCurrentItem] = useState(item);
|
||||
|
||||
const openMini = Boolean(anchorEl);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastItem) {
|
||||
if (item.id === lastItemId) {
|
||||
const localItem = { ...item };
|
||||
const elements = remItems.map((ele) => ele.elements);
|
||||
localItem.children = elements.flat(1);
|
||||
setCurrentItem(localItem);
|
||||
} else {
|
||||
setCurrentItem(item);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [item, lastItem, downLG]);
|
||||
|
||||
const checkOpenForParent = (child, id) => {
|
||||
child.forEach((ele) => {
|
||||
if (ele.children?.length) {
|
||||
checkOpenForParent(ele.children, currentItem.id);
|
||||
}
|
||||
if (ele.url === pathname) {
|
||||
dispatch(activeID(id));
|
||||
}
|
||||
});
|
||||
};
|
||||
const checkSelectedOnload = (data) => {
|
||||
const childrens = data.children ? data.children : [];
|
||||
childrens.forEach((itemCheck) => {
|
||||
if (itemCheck.children?.length) {
|
||||
checkOpenForParent(itemCheck.children, currentItem.id);
|
||||
}
|
||||
if (itemCheck.url === pathname) {
|
||||
dispatch(activeID(currentItem.id));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkSelectedOnload(currentItem);
|
||||
if (openMini) setAnchorEl(null);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pathname, currentItem]);
|
||||
|
||||
const handleClick = (event) => {
|
||||
if (!openMini) {
|
||||
setAnchorEl(event?.currentTarget);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const Icon = currentItem?.icon;
|
||||
const itemIcon = currentItem?.icon ? (
|
||||
<Icon
|
||||
style={{
|
||||
fontSize: 20,
|
||||
stroke: '1.5',
|
||||
color: selectedID === currentItem.id ? theme.palette.primary.main : theme.palette.secondary.dark
|
||||
}}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const navCollapse = item.children?.map((menuItem) => {
|
||||
switch (menuItem.type) {
|
||||
case 'collapse':
|
||||
return (
|
||||
<NavCollapse
|
||||
key={menuItem.id}
|
||||
menu={menuItem}
|
||||
setSelectedItems={setSelectedItems}
|
||||
setSelectedLevel={setSelectedLevel}
|
||||
selectedLevel={selectedLevel}
|
||||
selectedItems={selectedItems}
|
||||
level={1}
|
||||
parentId={currentItem.id}
|
||||
/>
|
||||
);
|
||||
case 'item':
|
||||
return <NavItem key={menuItem.id} item={menuItem} level={1} />;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
const moreItems = remItems.map((itemRem, i) => (
|
||||
<Fragment key={i}>
|
||||
{itemRem.title && (
|
||||
<Typography variant="caption" sx={{ pl: 2 }}>
|
||||
{itemRem.title}
|
||||
</Typography>
|
||||
)}
|
||||
{itemRem?.elements?.map((menu) => {
|
||||
switch (menu.type) {
|
||||
case 'collapse':
|
||||
return (
|
||||
<NavCollapse
|
||||
key={menu.id}
|
||||
menu={menu}
|
||||
level={1}
|
||||
parentId={currentItem.id}
|
||||
setSelectedItems={setSelectedItems}
|
||||
setSelectedLevel={setSelectedLevel}
|
||||
selectedLevel={selectedLevel}
|
||||
selectedItems={selectedItems}
|
||||
/>
|
||||
);
|
||||
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>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</Fragment>
|
||||
));
|
||||
|
||||
// menu list collapse & items
|
||||
const items = currentItem.children?.map((menu) => {
|
||||
switch (menu.type) {
|
||||
case 'collapse':
|
||||
return (
|
||||
<NavCollapse
|
||||
key={menu.id}
|
||||
menu={menu}
|
||||
level={1}
|
||||
parentId={currentItem.id}
|
||||
setSelectedItems={setSelectedItems}
|
||||
setSelectedLevel={setSelectedLevel}
|
||||
selectedLevel={selectedLevel}
|
||||
selectedItems={selectedItems}
|
||||
/>
|
||||
);
|
||||
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>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const popperId = openMini ? `group-pop-${item.id}` : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
{menuOrientation === MenuOrientation.VERTICAL || downLG ? (
|
||||
<List
|
||||
subheader={
|
||||
item.title &&
|
||||
drawerOpen && (
|
||||
<Box sx={{ pl: 3, mb: 1.5 }}>
|
||||
<Typography variant="subtitle2" color={theme.palette.mode === ThemeMode.DARK ? 'textSecondary' : 'text.secondary'}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
{item.caption && (
|
||||
<Typography variant="caption" color="secondary">
|
||||
{item.caption}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
sx={{ mt: drawerOpen && item.title ? 1.5 : 0, py: 0, zIndex: 0 }}
|
||||
>
|
||||
{navCollapse}
|
||||
</List>
|
||||
) : (
|
||||
<List>
|
||||
<ListItemButton
|
||||
selected={selectedID === currentItem.id}
|
||||
sx={{
|
||||
p: 1,
|
||||
my: 0.5,
|
||||
mr: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'inherit',
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}}
|
||||
onMouseEnter={handleClick}
|
||||
onClick={handleClick}
|
||||
onMouseLeave={handleClose}
|
||||
aria-describedby={popperId}
|
||||
>
|
||||
{itemIcon && (
|
||||
<ListItemIcon sx={{ minWidth: 28 }}>
|
||||
{currentItem.id === lastItemId ? <DownOutlined style={{ fontSize: 20, stroke: '1.5' }} /> : itemIcon}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
<ListItemText
|
||||
sx={{ mr: 1 }}
|
||||
primary={
|
||||
<Typography
|
||||
variant="body1"
|
||||
color={selectedID === currentItem.id ? theme.palette.primary.main : theme.palette.secondary.dark}
|
||||
>
|
||||
{currentItem.id === lastItemId ? <FormattedMessage id="More Items" /> : currentItem.title}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{openMini ? (
|
||||
<DownOutlined style={{ fontSize: 16, stroke: '1.5' }} />
|
||||
) : (
|
||||
<RightOutlined style={{ fontSize: 16, stroke: '1.5' }} />
|
||||
)}
|
||||
{anchorEl && (
|
||||
<PopperStyled
|
||||
id={popperId}
|
||||
open={openMini}
|
||||
anchorEl={anchorEl}
|
||||
placement="bottom-start"
|
||||
style={{
|
||||
zIndex: 2001
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions in={openMini} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
py: 1.25,
|
||||
boxShadow: theme.shadows[8],
|
||||
backgroundImage: 'none'
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<SimpleBar
|
||||
sx={{
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
maxHeight: 'calc(100vh - 170px)'
|
||||
}}
|
||||
>
|
||||
{currentItem.id !== lastItemId ? items : moreItems}
|
||||
</SimpleBar>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</PopperStyled>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</List>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
NavGroup.propTypes = {
|
||||
item: PropTypes.object,
|
||||
lastItem: PropTypes.number,
|
||||
remItems: PropTypes.array,
|
||||
lastItemId: PropTypes.string,
|
||||
setSelectedItems: PropTypes.func,
|
||||
selectedItems: PropTypes.string,
|
||||
setSelectedLevel: PropTypes.func,
|
||||
selectedLevel: PropTypes.number
|
||||
};
|
||||
|
||||
export default NavGroup;
|
||||
266
src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.js
Normal file
266
src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.js
Normal file
@@ -0,0 +1,266 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
import { Link } 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 import
|
||||
import Dot from 'components/@extended/Dot';
|
||||
import { MenuOrientation, ThemeMode } from 'config';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
import { activeItem, openDrawer } from 'store/reducers/menu';
|
||||
|
||||
// ==============================|| NAVIGATION - LIST ITEM ||============================== //
|
||||
|
||||
const NavItem = ({ item, level }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { menuOrientation } = useConfig();
|
||||
const { drawerOpen, openItem } = useSelector((state) => state.menu);
|
||||
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
let itemTarget = '_self';
|
||||
if (item.target) {
|
||||
itemTarget = '_blank';
|
||||
}
|
||||
|
||||
let listItemProps = { component: forwardRef((props, ref) => <Link {...props} to={item.url} target={itemTarget} ref={ref} />) };
|
||||
if (item?.external) {
|
||||
listItemProps = { component: 'a', href: item.url, target: itemTarget };
|
||||
}
|
||||
|
||||
const Icon = item.icon;
|
||||
const itemIcon = item.icon ? <Icon style={{ fontSize: drawerOpen ? '1rem' : '1.25rem' }} /> : false;
|
||||
|
||||
const isSelected = openItem.findIndex((id) => id === item.id) > -1;
|
||||
|
||||
// const { pathname } = useLocation();
|
||||
const pathname = document.location.pathname;
|
||||
|
||||
// active menu item on page load
|
||||
useEffect(() => {
|
||||
if (pathname && pathname.includes('product-details')) {
|
||||
if (item.url && item.url.includes('product-details')) {
|
||||
dispatch(activeItem({ openItem: [item.id] }));
|
||||
}
|
||||
}
|
||||
|
||||
if (pathname && pathname.includes('kanban')) {
|
||||
if (item.url && item.url.includes('kanban')) {
|
||||
dispatch(activeItem({ openItem: [item.id] }));
|
||||
}
|
||||
}
|
||||
|
||||
if (pathname.includes(item.url)) {
|
||||
dispatch(activeItem({ openItem: [item.id] }));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [pathname]);
|
||||
|
||||
const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : 'text.primary';
|
||||
const iconSelectedColor = theme.palette.mode === ThemeMode.DARK && drawerOpen ? 'text.primary' : 'primary.main';
|
||||
|
||||
return (
|
||||
<>
|
||||
{menuOrientation === MenuOrientation.VERTICAL || downLG ? (
|
||||
<ListItemButton
|
||||
{...listItemProps}
|
||||
disabled={item.disabled}
|
||||
selected={isSelected}
|
||||
sx={{
|
||||
zIndex: 1201,
|
||||
pl: drawerOpen ? `${level * 28}px` : 1.5,
|
||||
py: !drawerOpen && level === 1 ? 1.25 : 1,
|
||||
...(drawerOpen && {
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter'
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter',
|
||||
borderRight: `2px solid ${theme.palette.primary.main}`,
|
||||
color: iconSelectedColor,
|
||||
'&:hover': {
|
||||
color: iconSelectedColor,
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter'
|
||||
}
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen && {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
})
|
||||
}}
|
||||
{...(downLG && {
|
||||
onClick: () => dispatch(openDrawer(false))
|
||||
})}
|
||||
>
|
||||
{itemIcon && (
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 28,
|
||||
color: isSelected ? iconSelectedColor : textColor,
|
||||
...(!drawerOpen && {
|
||||
borderRadius: 1.5,
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.light' : 'secondary.lighter'
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen &&
|
||||
isSelected && {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.900' : 'primary.lighter',
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.darker' : 'primary.lighter'
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
{itemIcon}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
{(drawerOpen || (!drawerOpen && level !== 1)) && (
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6" sx={{ color: isSelected ? iconSelectedColor : textColor }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{(drawerOpen || (!drawerOpen && level !== 1)) && 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>
|
||||
) : (
|
||||
<ListItemButton
|
||||
{...listItemProps}
|
||||
disabled={item.disabled}
|
||||
selected={isSelected}
|
||||
sx={{
|
||||
zIndex: 1201,
|
||||
...(drawerOpen && {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'transparent',
|
||||
color: iconSelectedColor,
|
||||
'&:hover': {
|
||||
color: iconSelectedColor,
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen && {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
},
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
{itemIcon && (
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 36,
|
||||
...(!drawerOpen && {
|
||||
borderRadius: 1.5,
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen &&
|
||||
isSelected && {
|
||||
bgcolor: 'transparent',
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
{itemIcon}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
|
||||
{!itemIcon && (
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
color: isSelected ? 'primary.main' : 'secondary.main',
|
||||
...(!drawerOpen && {
|
||||
borderRadius: 1.5,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
}),
|
||||
...(!drawerOpen &&
|
||||
isSelected && {
|
||||
bgcolor: 'transparent',
|
||||
'&:hover': {
|
||||
bgcolor: 'transparent'
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Dot size={4} color={isSelected ? 'primary' : 'secondary'} />
|
||||
</ListItemIcon>
|
||||
)}
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6" color="inherit">
|
||||
{item.title}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{(drawerOpen || (!drawerOpen && level !== 1)) && 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;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useLayoutEffect, useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Box, Typography, useMediaQuery } from '@mui/material';
|
||||
|
||||
import { HORIZONTAL_MAX_ITEM, MenuOrientation } from 'config';
|
||||
import { useSelector } from 'store';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
|
||||
// project import
|
||||
import NavGroup from './NavGroup';
|
||||
import menuItem from 'menu-items';
|
||||
|
||||
// ==============================|| DRAWER CONTENT - NAVIGATION ||============================== //
|
||||
|
||||
const Navigation = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const { menuOrientation } = useConfig();
|
||||
const { drawerOpen } = useSelector((state) => state.menu);
|
||||
const [selectedItems, setSelectedItems] = useState('');
|
||||
const [selectedLevel, setSelectedLevel] = useState(0);
|
||||
const [menuItems, setMenuItems] = useState({ items: [] });
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setMenuItems(menuItem);
|
||||
// eslint-disable-next-line
|
||||
}, [menuItem]);
|
||||
|
||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
||||
|
||||
const lastItem = isHorizontal ? HORIZONTAL_MAX_ITEM : null;
|
||||
let lastItemIndex = menuItems.items.length - 1;
|
||||
let remItems = [];
|
||||
let lastItemId;
|
||||
|
||||
// first it checks menu item is more than giving HORIZONTAL_MAX_ITEM after that get lastItemid by giving horizontal max
|
||||
// item and it sets horizontal menu by giving horizontal max item lastly slice menuItem from array and set into remItems
|
||||
|
||||
if (lastItem && lastItem < menuItems.items.length) {
|
||||
lastItemId = menuItems.items[lastItem - 1].id;
|
||||
lastItemIndex = lastItem - 1;
|
||||
remItems = menuItems.items.slice(lastItem - 1, menuItems.items.length).map((item) => ({
|
||||
title: item.title,
|
||||
elements: item.children,
|
||||
icon: item.icon
|
||||
}));
|
||||
}
|
||||
|
||||
const navGroups = menuItems.items.slice(0, lastItemIndex + 1).map((item) => {
|
||||
switch (item.type) {
|
||||
case 'group':
|
||||
return (
|
||||
<NavGroup
|
||||
key={item.id}
|
||||
setSelectedItems={setSelectedItems}
|
||||
setSelectedLevel={setSelectedLevel}
|
||||
selectedLevel={selectedLevel}
|
||||
selectedItems={selectedItems}
|
||||
lastItem={lastItem}
|
||||
remItems={remItems}
|
||||
lastItemId={lastItemId}
|
||||
item={item}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Typography key={item.id} variant="h6" color="error" align="center">
|
||||
Fix - Navigation Group
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
pt: drawerOpen ? (isHorizontal ? 0 : 2) : 0,
|
||||
'& > ul:first-of-type': { mt: 0 },
|
||||
display: isHorizontal ? { xs: 'block', lg: 'flex' } : 'block'
|
||||
}}
|
||||
>
|
||||
{navGroups}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
34
src/layout/MainLayout/Drawer/DrawerContent/index.js
Normal file
34
src/layout/MainLayout/Drawer/DrawerContent/index.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// material-ui
|
||||
import { useMediaQuery, useTheme } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import NavCard from './NavCard';
|
||||
import Navigation from './Navigation';
|
||||
import SimpleBar from 'components/third-party/SimpleBar';
|
||||
import { useSelector } from 'store';
|
||||
|
||||
// ==============================|| DRAWER CONTENT ||============================== //
|
||||
|
||||
const DrawerContent = () => {
|
||||
const theme = useTheme();
|
||||
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const menu = useSelector((state) => state.menu);
|
||||
const { drawerOpen } = menu;
|
||||
|
||||
return (
|
||||
<SimpleBar
|
||||
sx={{
|
||||
'& .simplebar-content': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Navigation />
|
||||
{drawerOpen && !matchDownMD && <NavCard />}
|
||||
</SimpleBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrawerContent;
|
||||
@@ -0,0 +1,22 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// material-ui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
// ==============================|| DRAWER HEADER - STYLED ||============================== //
|
||||
|
||||
const DrawerHeaderStyled = styled(Box, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
|
||||
...theme.mixins.toolbar,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: open ? 'flex-start' : 'center',
|
||||
paddingLeft: theme.spacing(open ? 3 : 0)
|
||||
}));
|
||||
|
||||
DrawerHeaderStyled.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
open: PropTypes.bool
|
||||
};
|
||||
|
||||
export default DrawerHeaderStyled;
|
||||
44
src/layout/MainLayout/Drawer/DrawerHeader/index.js
Normal file
44
src/layout/MainLayout/Drawer/DrawerHeader/index.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import DrawerHeaderStyled from './DrawerHeaderStyled';
|
||||
import Logo from 'components/logo';
|
||||
|
||||
import { MenuOrientation } from 'config';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
|
||||
// ==============================|| DRAWER HEADER ||============================== //
|
||||
|
||||
const DrawerHeader = ({ open }) => {
|
||||
const theme = useTheme();
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const { menuOrientation } = useConfig();
|
||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
||||
|
||||
return (
|
||||
<DrawerHeaderStyled
|
||||
theme={theme}
|
||||
open={open}
|
||||
sx={{
|
||||
minHeight: isHorizontal ? 'unset' : '60px',
|
||||
width: isHorizontal ? { xs: '100%', lg: '424px' } : 'inherit',
|
||||
paddingTop: isHorizontal ? { xs: '10px', lg: '0' } : '8px',
|
||||
paddingBottom: isHorizontal ? { xs: '18px', lg: '0' } : '8px',
|
||||
paddingLeft: isHorizontal ? { xs: '24px', lg: '0' } : open ? '24px' : 0
|
||||
}}
|
||||
>
|
||||
<Logo isIcon={!open} sx={{ width: open ? 'auto' : 35, height: 35 }} />
|
||||
</DrawerHeaderStyled>
|
||||
);
|
||||
};
|
||||
|
||||
DrawerHeader.propTypes = {
|
||||
open: PropTypes.bool
|
||||
};
|
||||
|
||||
export default DrawerHeader;
|
||||
62
src/layout/MainLayout/Drawer/HorizontalBar.js
Normal file
62
src/layout/MainLayout/Drawer/HorizontalBar.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AppBar, Box, Container, useScrollTrigger } from '@mui/material';
|
||||
|
||||
// project imports
|
||||
import Navigation from './DrawerContent/Navigation';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
|
||||
// ==============================|| HORIZONTAL MENU LIST ||============================== //
|
||||
|
||||
function ElevationScroll({ children, window }) {
|
||||
const theme = useTheme();
|
||||
// Note that you normally won't need to set the window ref as useScrollTrigger
|
||||
// will default to window.
|
||||
// This is only being set here because the demo is in an iframe.
|
||||
const trigger = useScrollTrigger({
|
||||
disableHysteresis: true,
|
||||
threshold: 0,
|
||||
target: window
|
||||
});
|
||||
|
||||
theme.shadows[4] = theme.customShadows.z1;
|
||||
|
||||
return React.cloneElement(children, {
|
||||
elevation: trigger ? 4 : 0
|
||||
});
|
||||
}
|
||||
|
||||
// ==============================|| HORIZONTAL MENU LIST ||============================== //
|
||||
|
||||
const CustomAppBar = () => {
|
||||
const theme = useTheme();
|
||||
const { container } = useConfig();
|
||||
|
||||
return (
|
||||
<ElevationScroll>
|
||||
<AppBar
|
||||
sx={{
|
||||
top: 60,
|
||||
bgcolor: theme.palette.background.paper,
|
||||
width: '100%',
|
||||
height: 62,
|
||||
justifyContent: 'center',
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
zIndex: 1098,
|
||||
color: theme.palette.grey[500]
|
||||
}}
|
||||
>
|
||||
<Container maxWidth={container ? 'xl' : false}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Navigation />
|
||||
</Box>
|
||||
</Container>
|
||||
</AppBar>
|
||||
</ElevationScroll>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomAppBar;
|
||||
47
src/layout/MainLayout/Drawer/MiniDrawerStyled.js
Normal file
47
src/layout/MainLayout/Drawer/MiniDrawerStyled.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// material-ui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
|
||||
// project import
|
||||
import { DRAWER_WIDTH, ThemeMode } from 'config';
|
||||
|
||||
const openedMixin = (theme) => ({
|
||||
width: DRAWER_WIDTH,
|
||||
borderRight: `1px solid ${theme.palette.divider}`,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
boxShadow: theme.palette.mode === ThemeMode.DARK ? theme.customShadows.z1 : 'none'
|
||||
});
|
||||
|
||||
const closedMixin = (theme) => ({
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
width: theme.spacing(7.5),
|
||||
borderRight: 'none',
|
||||
boxShadow: theme.customShadows.z1
|
||||
});
|
||||
|
||||
// ==============================|| DRAWER - MINI STYLED ||============================== //
|
||||
|
||||
const MiniDrawerStyled = styled(Drawer, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
|
||||
width: DRAWER_WIDTH,
|
||||
flexShrink: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
boxSizing: 'border-box',
|
||||
...(open && {
|
||||
...openedMixin(theme),
|
||||
'& .MuiDrawer-paper': openedMixin(theme)
|
||||
}),
|
||||
...(!open && {
|
||||
...closedMixin(theme),
|
||||
'& .MuiDrawer-paper': closedMixin(theme)
|
||||
})
|
||||
}));
|
||||
|
||||
export default MiniDrawerStyled;
|
||||
70
src/layout/MainLayout/Drawer/index.js
Normal file
70
src/layout/MainLayout/Drawer/index.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Box, Drawer, useMediaQuery } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import DrawerHeader from './DrawerHeader';
|
||||
import DrawerContent from './DrawerContent';
|
||||
import MiniDrawerStyled from './MiniDrawerStyled';
|
||||
|
||||
import { DRAWER_WIDTH } from 'config';
|
||||
import { dispatch, useSelector } from 'store';
|
||||
import { openDrawer } from 'store/reducers/menu';
|
||||
|
||||
// ==============================|| MAIN LAYOUT - DRAWER ||============================== //
|
||||
|
||||
const MainDrawer = ({ window }) => {
|
||||
const theme = useTheme();
|
||||
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const menu = useSelector((state) => state.menu);
|
||||
const { drawerOpen } = menu;
|
||||
|
||||
// responsive drawer container
|
||||
const container = window !== undefined ? () => window().document.body : undefined;
|
||||
|
||||
// header content
|
||||
const drawerContent = useMemo(() => <DrawerContent />, []);
|
||||
const drawerHeader = useMemo(() => <DrawerHeader open={drawerOpen} />, [drawerOpen]);
|
||||
|
||||
return (
|
||||
<Box component="nav" sx={{ flexShrink: { md: 0 }, zIndex: 1200 }} aria-label="mailbox folders">
|
||||
{!matchDownMD ? (
|
||||
<MiniDrawerStyled variant="permanent" open={drawerOpen}>
|
||||
{drawerHeader}
|
||||
{drawerContent}
|
||||
</MiniDrawerStyled>
|
||||
) : (
|
||||
<Drawer
|
||||
container={container}
|
||||
variant="temporary"
|
||||
open={drawerOpen}
|
||||
onClose={() => dispatch(openDrawer(!drawerOpen))}
|
||||
ModalProps={{ keepMounted: true }}
|
||||
sx={{
|
||||
display: { xs: 'block', lg: 'none' },
|
||||
'& .MuiDrawer-paper': {
|
||||
boxSizing: 'border-box',
|
||||
width: DRAWER_WIDTH,
|
||||
borderRight: `1px solid ${theme.palette.divider}`,
|
||||
backgroundImage: 'none',
|
||||
boxShadow: 'inherit'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{drawerHeader}
|
||||
{drawerContent}
|
||||
</Drawer>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
MainDrawer.propTypes = {
|
||||
window: PropTypes.object
|
||||
};
|
||||
|
||||
export default MainDrawer;
|
||||
23
src/layout/MainLayout/Footer.js
Normal file
23
src/layout/MainLayout/Footer.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { Link, Stack, Typography } from '@mui/material';
|
||||
|
||||
const Footer = () => (
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ p: '24px 16px 0px', mt: 'auto' }}>
|
||||
<Typography variant="caption">© All rights reserved</Typography>
|
||||
<Stack spacing={1.5} direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Link component={RouterLink} to="#" target="_blank" variant="caption" color="textPrimary">
|
||||
About us
|
||||
</Link>
|
||||
<Link component={RouterLink} to="#" target="_blank" variant="caption" color="textPrimary">
|
||||
Privacy
|
||||
</Link>
|
||||
<Link component={RouterLink} to="#" target="_blank" variant="caption" color="textPrimary">
|
||||
Terms
|
||||
</Link>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
export default Footer;
|
||||
35
src/layout/MainLayout/Header/AppBarStyled.js
Normal file
35
src/layout/MainLayout/Header/AppBarStyled.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// material-ui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
|
||||
// project import
|
||||
import { DRAWER_WIDTH } from 'config';
|
||||
|
||||
// ==============================|| HEADER - APP BAR STYLED ||============================== //
|
||||
|
||||
const AppBarStyled = styled(AppBar, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
...(!open && {
|
||||
width: `calc(100% - ${theme.spacing(7.5)})`
|
||||
}),
|
||||
...(open && {
|
||||
marginLeft: DRAWER_WIDTH,
|
||||
width: `calc(100% - ${DRAWER_WIDTH}px)`,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
})
|
||||
})
|
||||
}));
|
||||
|
||||
AppBarStyled.propTypes = {
|
||||
open: PropTypes.bool
|
||||
};
|
||||
|
||||
export default AppBarStyled;
|
||||
301
src/layout/MainLayout/Header/HeaderContent/MegaMenuSection.js
Normal file
301
src/layout/MainLayout/Header/HeaderContent/MegaMenuSection.js
Normal file
@@ -0,0 +1,301 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Button,
|
||||
Box,
|
||||
CardMedia,
|
||||
ClickAwayListener,
|
||||
Grid,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
ListSubheader,
|
||||
Paper,
|
||||
Popper,
|
||||
Stack,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
|
||||
// project import
|
||||
import MainCard from 'components/MainCard';
|
||||
import Dot from 'components/@extended/Dot';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
import { DRAWER_WIDTH, ThemeMode } from 'config';
|
||||
|
||||
// assets
|
||||
import { ArrowRightOutlined, WindowsOutlined } from '@ant-design/icons';
|
||||
import backgroundVector from 'assets/images/mega-menu/back.svg';
|
||||
import imageChart from 'assets/images/mega-menu/chart.svg';
|
||||
import AnimateButton from 'components/@extended/AnimateButton';
|
||||
|
||||
// ==============================|| HEADER CONTENT - MEGA MENU SECTION ||============================== //
|
||||
|
||||
const MegaMenuSection = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const anchorRef = useRef(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
||||
|
||||
return (
|
||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
||||
<IconButton
|
||||
color="secondary"
|
||||
variant="light"
|
||||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
||||
aria-label="open profile"
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? 'profile-grow' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<WindowsOutlined />
|
||||
</IconButton>
|
||||
<Popper
|
||||
placement="bottom"
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
popperOptions={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [-180, 9]
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions type="grow" position="top" in={open} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
boxShadow: theme.customShadows.z1,
|
||||
minWidth: 750,
|
||||
width: {
|
||||
md: `calc(100vw - 100px)`,
|
||||
lg: `calc(100vw - ${DRAWER_WIDTH + 100}px)`,
|
||||
xl: `calc(100vw - ${DRAWER_WIDTH + 140}px)`
|
||||
},
|
||||
maxWidth: 1024
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MainCard elevation={0} border={false} content={false}>
|
||||
<Grid container>
|
||||
<Grid
|
||||
item
|
||||
md={4}
|
||||
sx={{
|
||||
background: `url(${backgroundVector}), linear-gradient(183.77deg, ${theme.palette.primary.main} 11.46%, ${theme.palette.primary[700]} 100.33%)`
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 4.5, pb: 3 }}>
|
||||
<Stack sx={{ color: 'background.paper' }}>
|
||||
<Typography variant="h2" sx={{ fontSize: '1.875rem', mb: 1 }}>
|
||||
Explore Components
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
Try our pre made component pages to check how it feels and suits as per your need.
|
||||
</Typography>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="flex-end" sx={{ mt: -1 }}>
|
||||
<AnimateButton>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
bgcolor: 'background.paper',
|
||||
color: 'text.primary',
|
||||
'&:hover': { bgcolor: 'background.paper', color: 'text.primary' }
|
||||
}}
|
||||
endIcon={<ArrowRightOutlined />}
|
||||
component={Link}
|
||||
to="/components-overview/buttons"
|
||||
target="_blank"
|
||||
>
|
||||
View All
|
||||
</Button>
|
||||
</AnimateButton>
|
||||
<CardMedia component="img" src={imageChart} alt="Chart" sx={{ mr: -2.5, mb: -2.5, width: 124 }} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item md={8}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 4,
|
||||
'& .MuiList-root': {
|
||||
pb: 0
|
||||
},
|
||||
'& .MuiListSubheader-root': {
|
||||
p: 0,
|
||||
pb: 1.5
|
||||
},
|
||||
'& .MuiListItemButton-root': {
|
||||
p: 0.5,
|
||||
'&:hover': {
|
||||
background: 'transparent',
|
||||
'& .MuiTypography-root': {
|
||||
color: 'primary.main'
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Grid container spacing={6}>
|
||||
<Grid item xs={4}>
|
||||
<List
|
||||
component="nav"
|
||||
aria-labelledby="nested-list-user"
|
||||
subheader={
|
||||
<ListSubheader id="nested-list-user">
|
||||
<Typography variant="subtitle1" color="textPrimary">
|
||||
Authentication
|
||||
</Typography>
|
||||
</ListSubheader>
|
||||
}
|
||||
>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/login">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Login" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/register">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Register" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/reset-password">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Reset Password" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/forgot-password">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Forgot Password" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/code-verification">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Verification Code" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<List
|
||||
component="nav"
|
||||
aria-labelledby="nested-list-user"
|
||||
subheader={
|
||||
<ListSubheader id="nested-list-user">
|
||||
<Typography variant="subtitle1" color="textPrimary">
|
||||
Other Pages
|
||||
</Typography>
|
||||
</ListSubheader>
|
||||
}
|
||||
>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="About us" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/contact-us">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Contact us" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} to="/pricing">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Pricing" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} to="/apps/profiles/user/payment">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Payment" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/maintenance/under-construction">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Construction" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/maintenance/coming-soon">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Coming Soon" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<List
|
||||
component="nav"
|
||||
aria-labelledby="nested-list-user"
|
||||
subheader={
|
||||
<ListSubheader id="nested-list-user">
|
||||
<Typography variant="subtitle1" color="textPrimary">
|
||||
SAAS Pages
|
||||
</Typography>
|
||||
</ListSubheader>
|
||||
}
|
||||
>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/maintenance/404">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="404 Error" />
|
||||
</ListItemButton>
|
||||
<ListItemButton disableRipple component={Link} target="_blank" to="/">
|
||||
<ListItemIcon>
|
||||
<Dot size={7} color="secondary" variant="outlined" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Landing" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MainCard>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default MegaMenuSection;
|
||||
252
src/layout/MainLayout/Header/HeaderContent/Message.js
Normal file
252
src/layout/MainLayout/Header/HeaderContent/Message.js
Normal file
@@ -0,0 +1,252 @@
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
ClickAwayListener,
|
||||
Divider,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Paper,
|
||||
Popper,
|
||||
Typography,
|
||||
useMediaQuery
|
||||
} from '@mui/material';
|
||||
|
||||
// project import
|
||||
import MainCard from 'components/MainCard';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
import { ThemeMode } from 'config';
|
||||
|
||||
// assets
|
||||
import avatar2 from 'assets/images/users/avatar-2.png';
|
||||
import avatar3 from 'assets/images/users/avatar-3.png';
|
||||
import avatar4 from 'assets/images/users/avatar-4.png';
|
||||
import avatar5 from 'assets/images/users/avatar-5.png';
|
||||
import { MailOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
|
||||
// sx styles
|
||||
const avatarSX = {
|
||||
width: 48,
|
||||
height: 48
|
||||
};
|
||||
|
||||
const actionSX = {
|
||||
mt: '6px',
|
||||
ml: 1,
|
||||
top: 'auto',
|
||||
right: 'auto',
|
||||
alignSelf: 'flex-start',
|
||||
transform: 'none'
|
||||
};
|
||||
|
||||
// ==============================|| HEADER CONTENT - MESSAGES ||============================== //
|
||||
|
||||
const Message = () => {
|
||||
const theme = useTheme();
|
||||
const matchesXs = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const anchorRef = useRef(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
||||
|
||||
return (
|
||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
||||
<IconButton
|
||||
color="secondary"
|
||||
variant="light"
|
||||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
||||
aria-label="open profile"
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? 'profile-grow' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<MailOutlined />
|
||||
</IconButton>
|
||||
<Popper
|
||||
placement={matchesXs ? 'bottom' : 'bottom-end'}
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
sx={{
|
||||
maxHeight: 'calc(100vh - 250px)',
|
||||
overflow: 'auto'
|
||||
}}
|
||||
popperOptions={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [matchesXs ? -60 : 0, 9]
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions type="grow" position={matchesXs ? 'top' : 'top-right'} in={open} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
boxShadow: theme.customShadows.z1,
|
||||
width: '100%',
|
||||
minWidth: 285,
|
||||
maxWidth: 420,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
maxWidth: 285
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MainCard
|
||||
title="Message"
|
||||
elevation={0}
|
||||
border={false}
|
||||
content={false}
|
||||
secondary={
|
||||
<IconButton size="small" onClick={handleToggle}>
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<List
|
||||
component="nav"
|
||||
sx={{
|
||||
p: 0,
|
||||
'& .MuiListItemButton-root': {
|
||||
py: 1.5,
|
||||
'& .MuiAvatar-root': avatarSX,
|
||||
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="profile user" src={avatar2} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
It's{' '}
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Cristina danny's
|
||||
</Typography>{' '}
|
||||
birthday today.
|
||||
</Typography>
|
||||
}
|
||||
secondary="2 min ago"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
3:00 AM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="profile user" src={avatar3} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Aida Burg
|
||||
</Typography>{' '}
|
||||
commented your post.
|
||||
</Typography>
|
||||
}
|
||||
secondary="5 August"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
6:00 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="profile user" src={avatar4} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography component="span" variant="subtitle1">
|
||||
There was a failure to your setup.
|
||||
</Typography>
|
||||
}
|
||||
secondary="7 hours ago"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
2:45 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="profile user" src={avatar5} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Cristina Danny
|
||||
</Typography>{' '}
|
||||
invited to join{' '}
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Meeting.
|
||||
</Typography>
|
||||
</Typography>
|
||||
}
|
||||
secondary="Daily scrum meeting time"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
9:10 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton sx={{ textAlign: 'center' }}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6" color="primary">
|
||||
View All
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</MainCard>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Message;
|
||||
103
src/layout/MainLayout/Header/HeaderContent/MobileSection.js
Normal file
103
src/layout/MainLayout/Header/HeaderContent/MobileSection.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AppBar, Box, ClickAwayListener, Paper, Popper, Toolbar } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import Search from './Search';
|
||||
import Profile from './Profile';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
import { ThemeMode } from 'config';
|
||||
|
||||
// assets
|
||||
import { MoreOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| HEADER CONTENT - MOBILE ||============================== //
|
||||
|
||||
const MobileSection = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const anchorRef = useRef(null);
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const prevOpen = useRef(open);
|
||||
useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current.focus();
|
||||
}
|
||||
|
||||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
||||
<IconButton
|
||||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
||||
aria-label="open more menu"
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? 'menu-list-grow' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
color="secondary"
|
||||
variant="light"
|
||||
>
|
||||
<MoreOutlined />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Popper
|
||||
placement="bottom-end"
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
style={{ width: '100%' }}
|
||||
popperOptions={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 9]
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions type="fade" in={open} {...TransitionProps}>
|
||||
<Paper sx={{ boxShadow: theme.customShadows.z1 }}>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<AppBar color="inherit">
|
||||
<Toolbar>
|
||||
<Search />
|
||||
<Profile />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileSection;
|
||||
289
src/layout/MainLayout/Header/HeaderContent/Notification.js
Normal file
289
src/layout/MainLayout/Header/HeaderContent/Notification.js
Normal file
@@ -0,0 +1,289 @@
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
ClickAwayListener,
|
||||
Divider,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Paper,
|
||||
Popper,
|
||||
Tooltip,
|
||||
Typography,
|
||||
useMediaQuery
|
||||
} from '@mui/material';
|
||||
|
||||
// project import
|
||||
import MainCard from 'components/MainCard';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
import { ThemeMode } from 'config';
|
||||
|
||||
// assets
|
||||
import { BellOutlined, CheckCircleOutlined, GiftOutlined, MessageOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
|
||||
// sx styles
|
||||
const avatarSX = {
|
||||
width: 36,
|
||||
height: 36,
|
||||
fontSize: '1rem'
|
||||
};
|
||||
|
||||
const actionSX = {
|
||||
mt: '6px',
|
||||
ml: 1,
|
||||
top: 'auto',
|
||||
right: 'auto',
|
||||
alignSelf: 'flex-start',
|
||||
|
||||
transform: 'none'
|
||||
};
|
||||
|
||||
// ==============================|| HEADER CONTENT - NOTIFICATION ||============================== //
|
||||
|
||||
const Notification = () => {
|
||||
const theme = useTheme();
|
||||
const matchesXs = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const anchorRef = useRef(null);
|
||||
const [read, setRead] = useState(2);
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
||||
|
||||
return (
|
||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
||||
<IconButton
|
||||
color="secondary"
|
||||
variant="light"
|
||||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
||||
aria-label="open profile"
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? 'profile-grow' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<Badge badgeContent={read} color="primary">
|
||||
<BellOutlined />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<Popper
|
||||
placement={matchesXs ? 'bottom' : 'bottom-end'}
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
popperOptions={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [matchesXs ? -5 : 0, 9]
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions type="grow" position={matchesXs ? 'top' : 'top-right'} sx={{ overflow: 'hidden' }} in={open} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
boxShadow: theme.customShadows.z1,
|
||||
width: '100%',
|
||||
minWidth: 285,
|
||||
maxWidth: 420,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
maxWidth: 285
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MainCard
|
||||
title="Notification"
|
||||
elevation={0}
|
||||
border={false}
|
||||
content={false}
|
||||
secondary={
|
||||
<>
|
||||
{read > 0 && (
|
||||
<Tooltip title="Mark as all read">
|
||||
<IconButton color="success" size="small" onClick={() => setRead(0)}>
|
||||
<CheckCircleOutlined style={{ fontSize: '1.15rem' }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<List
|
||||
component="nav"
|
||||
sx={{
|
||||
p: 0,
|
||||
'& .MuiListItemButton-root': {
|
||||
py: 0.5,
|
||||
'&.Mui-selected': { bgcolor: 'grey.50', color: 'text.primary' },
|
||||
'& .MuiAvatar-root': avatarSX,
|
||||
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemButton selected={read > 0}>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'success.main',
|
||||
bgcolor: 'success.lighter'
|
||||
}}
|
||||
>
|
||||
<GiftOutlined />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
It's{' '}
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Cristina danny's
|
||||
</Typography>{' '}
|
||||
birthday today.
|
||||
</Typography>
|
||||
}
|
||||
secondary="2 min ago"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
3:00 AM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton selected={read > 0}>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'primary.lighter'
|
||||
}}
|
||||
>
|
||||
<MessageOutlined />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Aida Burg
|
||||
</Typography>{' '}
|
||||
commented your post.
|
||||
</Typography>
|
||||
}
|
||||
secondary="5 August"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
6:00 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'error.main',
|
||||
bgcolor: 'error.lighter'
|
||||
}}
|
||||
>
|
||||
<SettingOutlined />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
Your Profile is Complete
|
||||
<Typography component="span" variant="subtitle1">
|
||||
60%
|
||||
</Typography>{' '}
|
||||
</Typography>
|
||||
}
|
||||
secondary="7 hours ago"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
2:45 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'primary.lighter'
|
||||
}}
|
||||
>
|
||||
C
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Cristina Danny
|
||||
</Typography>{' '}
|
||||
invited to join{' '}
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Meeting.
|
||||
</Typography>
|
||||
</Typography>
|
||||
}
|
||||
secondary="Daily scrum meeting time"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
9:10 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton sx={{ textAlign: 'center', py: `${12}px !important` }}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6" color="primary">
|
||||
View All
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</MainCard>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Notification;
|
||||
@@ -0,0 +1,59 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
||||
|
||||
// assets
|
||||
import { EditOutlined, ProfileOutlined, LogoutOutlined, UserOutlined, WalletOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| HEADER PROFILE - PROFILE TAB ||============================== //
|
||||
|
||||
const ProfileTab = ({ handleLogout }) => {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const handleListItemClick = (event, index) => {
|
||||
setSelectedIndex(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32 } }}>
|
||||
<ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 0)}>
|
||||
<ListItemIcon>
|
||||
<EditOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Edit Profile" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 1} onClick={(event) => handleListItemClick(event, 1)}>
|
||||
<ListItemIcon>
|
||||
<UserOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="View Profile" />
|
||||
</ListItemButton>
|
||||
|
||||
<ListItemButton selected={selectedIndex === 3} onClick={(event) => handleListItemClick(event, 3)}>
|
||||
<ListItemIcon>
|
||||
<ProfileOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Social Profile" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 4} onClick={(event) => handleListItemClick(event, 4)}>
|
||||
<ListItemIcon>
|
||||
<WalletOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Billing" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 2} onClick={handleLogout}>
|
||||
<ListItemIcon>
|
||||
<LogoutOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Logout" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
ProfileTab.propTypes = {
|
||||
handleLogout: PropTypes.func
|
||||
};
|
||||
|
||||
export default ProfileTab;
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
||||
|
||||
// assets
|
||||
import { CommentOutlined, LockOutlined, QuestionCircleOutlined, UserOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| HEADER PROFILE - SETTING TAB ||============================== //
|
||||
|
||||
const SettingTab = () => {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const handleListItemClick = (event, index) => {
|
||||
setSelectedIndex(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32 } }}>
|
||||
<ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 0)}>
|
||||
<ListItemIcon>
|
||||
<QuestionCircleOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Support" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 1} onClick={(event) => handleListItemClick(event, 1)}>
|
||||
<ListItemIcon>
|
||||
<UserOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Account Settings" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 2} onClick={(event) => handleListItemClick(event, 2)}>
|
||||
<ListItemIcon>
|
||||
<LockOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Privacy Center" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 3} onClick={(event) => handleListItemClick(event, 3)}>
|
||||
<ListItemIcon>
|
||||
<CommentOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Feedback" />
|
||||
</ListItemButton>
|
||||
<ListItemButton selected={selectedIndex === 4} onClick={(event) => handleListItemClick(event, 4)}>
|
||||
<ListItemIcon>
|
||||
<UnorderedListOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="History" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingTab;
|
||||
211
src/layout/MainLayout/Header/HeaderContent/Profile/index.js
Normal file
211
src/layout/MainLayout/Header/HeaderContent/Profile/index.js
Normal file
@@ -0,0 +1,211 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Box, ButtonBase, CardContent, ClickAwayListener, Grid, Paper, Popper, Stack, Tab, Tabs, Tooltip, Typography } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import ProfileTab from './ProfileTab';
|
||||
import SettingTab from './SettingTab';
|
||||
import Avatar from 'components/@extended/Avatar';
|
||||
import MainCard from 'components/MainCard';
|
||||
import Transitions from 'components/@extended/Transitions';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
|
||||
import { ThemeMode } from 'config';
|
||||
import useAuth from 'hooks/useAuth';
|
||||
|
||||
// assets
|
||||
import avatar1 from 'assets/images/users/avatar-1.png';
|
||||
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
|
||||
|
||||
// tab panel wrapper
|
||||
function TabPanel({ children, value, index, ...other }) {
|
||||
return (
|
||||
<div role="tabpanel" hidden={value !== index} id={`profile-tabpanel-${index}`} aria-labelledby={`profile-tab-${index}`} {...other}>
|
||||
{value === index && children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
TabPanel.propTypes = {
|
||||
children: PropTypes.node,
|
||||
index: PropTypes.any.isRequired,
|
||||
value: PropTypes.any.isRequired
|
||||
};
|
||||
|
||||
function a11yProps(index) {
|
||||
return {
|
||||
id: `profile-tab-${index}`,
|
||||
'aria-controls': `profile-tabpanel-${index}`
|
||||
};
|
||||
}
|
||||
|
||||
// ==============================|| HEADER CONTENT - PROFILE ||============================== //
|
||||
|
||||
const Profile = () => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { logout, user } = useAuth();
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await logout();
|
||||
navigate(`/login`, {
|
||||
state: {
|
||||
from: ''
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const anchorRef = useRef(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const [value, setValue] = useState(0);
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
||||
|
||||
return (
|
||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
p: 0.25,
|
||||
bgcolor: open ? iconBackColorOpen : 'transparent',
|
||||
borderRadius: 1,
|
||||
'&:hover': { bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.light' : 'secondary.lighter' },
|
||||
'&:focus-visible': {
|
||||
outline: `2px solid ${theme.palette.secondary.dark}`,
|
||||
outlineOffset: 2
|
||||
}
|
||||
}}
|
||||
aria-label="open profile"
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? 'profile-grow' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<Stack direction="row" spacing={2} alignItems="center" sx={{ p: 0.5 }}>
|
||||
<Avatar alt="profile user" src={avatar1} size="xs" />
|
||||
<Typography variant="subtitle1">{user?.name}</Typography>
|
||||
</Stack>
|
||||
</ButtonBase>
|
||||
<Popper
|
||||
placement="bottom-end"
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
popperOptions={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 9]
|
||||
}
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Transitions type="grow" position="top-right" in={open} {...TransitionProps}>
|
||||
<Paper
|
||||
sx={{
|
||||
boxShadow: theme.customShadows.z1,
|
||||
width: 290,
|
||||
minWidth: 240,
|
||||
maxWidth: 290,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
maxWidth: 250
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MainCard elevation={0} border={false} content={false}>
|
||||
<CardContent sx={{ px: 2.5, pt: 3 }}>
|
||||
<Grid container justifyContent="space-between" alignItems="center">
|
||||
<Grid item>
|
||||
<Stack direction="row" spacing={1.25} alignItems="center">
|
||||
<Avatar alt="profile user" src={avatar1} sx={{ width: 32, height: 32 }} />
|
||||
<Stack>
|
||||
<Typography variant="h6">{user?.name}</Typography>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
UI/UX Designer
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Tooltip title="Logout">
|
||||
<IconButton size="large" sx={{ color: 'text.primary' }} onClick={handleLogout}>
|
||||
<LogoutOutlined />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label="profile tabs">
|
||||
<Tab
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
textTransform: 'capitalize'
|
||||
}}
|
||||
icon={<UserOutlined style={{ marginBottom: 0, marginRight: '10px' }} />}
|
||||
label="Profile"
|
||||
{...a11yProps(0)}
|
||||
/>
|
||||
<Tab
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
textTransform: 'capitalize'
|
||||
}}
|
||||
icon={<SettingOutlined style={{ marginBottom: 0, marginRight: '10px' }} />}
|
||||
label="Setting"
|
||||
{...a11yProps(1)}
|
||||
/>
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={value} index={0} dir={theme.direction}>
|
||||
<ProfileTab handleLogout={handleLogout} />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={1} dir={theme.direction}>
|
||||
<SettingTab />
|
||||
</TabPanel>
|
||||
</MainCard>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
30
src/layout/MainLayout/Header/HeaderContent/Search.js
Normal file
30
src/layout/MainLayout/Header/HeaderContent/Search.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// material-ui
|
||||
import { Box, FormControl, InputAdornment, OutlinedInput } from '@mui/material';
|
||||
|
||||
// assets
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| HEADER CONTENT - SEARCH ||============================== //
|
||||
|
||||
const Search = () => (
|
||||
<Box sx={{ width: '100%', ml: { xs: 0, md: 1 } }}>
|
||||
<FormControl sx={{ width: { xs: '100%', md: 224 } }}>
|
||||
<OutlinedInput
|
||||
size="small"
|
||||
id="header-search"
|
||||
startAdornment={
|
||||
<InputAdornment position="start" sx={{ mr: -0.5 }}>
|
||||
<SearchOutlined />
|
||||
</InputAdornment>
|
||||
}
|
||||
aria-describedby="header-search-text"
|
||||
inputProps={{
|
||||
'aria-label': 'weight'
|
||||
}}
|
||||
placeholder="Ctrl + K"
|
||||
/>
|
||||
</FormControl>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default Search;
|
||||
36
src/layout/MainLayout/Header/HeaderContent/index.js
Normal file
36
src/layout/MainLayout/Header/HeaderContent/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { Box, useMediaQuery } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import Search from './Search';
|
||||
import Message from './Message';
|
||||
import Profile from './Profile';
|
||||
import Notification from './Notification';
|
||||
import MobileSection from './MobileSection';
|
||||
import MegaMenuSection from './MegaMenuSection';
|
||||
|
||||
// ==============================|| HEADER - CONTENT ||============================== //
|
||||
|
||||
const HeaderContent = () => {
|
||||
const matchesXs = useMediaQuery((theme) => theme.breakpoints.down('md'));
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const megaMenu = useMemo(() => <MegaMenuSection />, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!matchesXs && <Search />}
|
||||
{!matchesXs && megaMenu}
|
||||
{matchesXs && <Box sx={{ width: '100%', ml: 1 }} />}
|
||||
|
||||
<Notification />
|
||||
<Message />
|
||||
{!matchesXs && <Profile />}
|
||||
{matchesXs && <MobileSection />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderContent;
|
||||
83
src/layout/MainLayout/Header/index.js
Normal file
83
src/layout/MainLayout/Header/index.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AppBar, Toolbar, useMediaQuery } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import AppBarStyled from './AppBarStyled';
|
||||
import HeaderContent from './HeaderContent';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
|
||||
import { MenuOrientation, ThemeMode } from 'config';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
import { dispatch, useSelector } from 'store';
|
||||
import { openDrawer } from 'store/reducers/menu';
|
||||
|
||||
// assets
|
||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| MAIN LAYOUT - HEADER ||============================== //
|
||||
|
||||
const Header = () => {
|
||||
const theme = useTheme();
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
const { menuOrientation } = useConfig();
|
||||
|
||||
const menu = useSelector((state) => state.menu);
|
||||
const { drawerOpen } = menu;
|
||||
|
||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
||||
|
||||
// header content
|
||||
const headerContent = useMemo(() => <HeaderContent />, []);
|
||||
|
||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
||||
|
||||
// common header
|
||||
const mainHeader = (
|
||||
<Toolbar>
|
||||
{!isHorizontal ? (
|
||||
<IconButton
|
||||
aria-label="open drawer"
|
||||
onClick={() => dispatch(openDrawer(!drawerOpen))}
|
||||
edge="start"
|
||||
color="secondary"
|
||||
variant="light"
|
||||
sx={{ color: 'text.primary', bgcolor: drawerOpen ? iconBackColorOpen : iconBackColor, ml: { xs: 0, lg: -2 } }}
|
||||
>
|
||||
{!drawerOpen ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
</IconButton>
|
||||
) : null}
|
||||
{headerContent}
|
||||
</Toolbar>
|
||||
);
|
||||
|
||||
// app-bar params
|
||||
const appBar = {
|
||||
position: 'fixed',
|
||||
color: 'inherit',
|
||||
elevation: 0,
|
||||
sx: {
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
zIndex: 1200,
|
||||
width: isHorizontal ? '100%' : drawerOpen ? 'calc(100% - 260px)' : { xs: '100%', lg: 'calc(100% - 60px)' }
|
||||
// boxShadow: theme.customShadows.z1
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!downLG ? (
|
||||
<AppBarStyled open={drawerOpen} {...appBar}>
|
||||
{mainHeader}
|
||||
</AppBarStyled>
|
||||
) : (
|
||||
<AppBar {...appBar}>{mainHeader}</AppBar>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
65
src/layout/MainLayout/index.js
Normal file
65
src/layout/MainLayout/index.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useMediaQuery, Box, Container, Toolbar } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import Drawer from './Drawer';
|
||||
import Header from './Header';
|
||||
import Footer from './Footer';
|
||||
import HorizontalBar from './Drawer/HorizontalBar';
|
||||
import Breadcrumbs from 'components/@extended/Breadcrumbs';
|
||||
|
||||
import { MenuOrientation } from 'config';
|
||||
import navigation from 'menu-items';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
import { dispatch } from 'store';
|
||||
import { openDrawer } from 'store/reducers/menu';
|
||||
|
||||
// ==============================|| MAIN LAYOUT ||============================== //
|
||||
|
||||
const MainLayout = () => {
|
||||
const theme = useTheme();
|
||||
const matchDownXL = useMediaQuery(theme.breakpoints.down('xl'));
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const { container, miniDrawer, menuOrientation } = useConfig();
|
||||
|
||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
||||
|
||||
// set media wise responsive drawer
|
||||
useEffect(() => {
|
||||
if (!miniDrawer) {
|
||||
dispatch(openDrawer(!matchDownXL));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [matchDownXL]);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Header />
|
||||
{!isHorizontal ? <Drawer /> : <HorizontalBar />}
|
||||
<Box component="main" sx={{ width: 'calc(100% - 260px)', flexGrow: 1, p: { xs: 2, sm: 3 } }}>
|
||||
<Toolbar sx={{ mt: isHorizontal ? 8 : 'inherit' }} />
|
||||
<Container
|
||||
maxWidth={container ? 'xl' : false}
|
||||
sx={{
|
||||
...(container && { px: { xs: 0, sm: 2 } }),
|
||||
position: 'relative',
|
||||
minHeight: 'calc(100vh - 110px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}
|
||||
>
|
||||
<Breadcrumbs navigation={navigation} title titleBottom card={false} divider={false} />
|
||||
<Outlet />
|
||||
<Footer />
|
||||
</Container>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainLayout;
|
||||
Reference in New Issue
Block a user