Add UI and login screen

This commit is contained in:
Yann Stepienik 2023-03-12 18:17:28 +00:00
parent f85910f59b
commit 70f860e7b1
131 changed files with 5888 additions and 24 deletions

View file

@ -23,6 +23,18 @@ jobs:
name: set Go path
command: echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV
- run:
name: install Node 1/3
command: curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
- run:
name: install Node 2/3
command: sudo bash ./nodesource_setup.sh
- run:
name: install Node 3/3
command: sudo apt-get install -y nodejs
- run:
name: Install GuPM
command: curl -fsSL https://azukaar.github.io/GuPM/install.sh | bash
@ -36,6 +48,11 @@ jobs:
- run:
name: Install dependencies
command: ~/.gupm/gupm/g make
- run:
name: Build UI
command: g vite build
- run:
name: Build Linux (ARM)
command: ~/.gupm/gupm/g ci/build linux arm64

2
.gitignore vendored
View file

@ -8,8 +8,10 @@ localcert.crt
localcert.key
.vite
dev.json
static
.bin
client/dist
client/.vite
config_dev.json
tests
todo.txt

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
16

25
client/TEMPLATE LICENSE Normal file
View file

@ -0,0 +1,25 @@
Built from original template from CodedThemes.
The template was distributed with the licence.
This licence does not cover the changes made to the template:
MIT License
Copyright (c) 2022 CodedThemes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
client/index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cosmos</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.jsx"></script>
</body>
</html>

18
client/src/App.jsx Normal file
View file

@ -0,0 +1,18 @@
// project import
import Routes from './routes';
import ThemeCustomization from './themes';
import ScrollTop from './components/ScrollTop';
// ==============================|| APP - THEME, ROUTER, LOCAL ||============================== //
const App = () => {
return (
<ThemeCustomization>
<ScrollTop>
<Routes />
</ScrollTop>
</ThemeCustomization>
)
}
export default App;

9
client/src/App.test.jsx Normal file
View file

@ -0,0 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View file

@ -0,0 +1,26 @@
function login(values) {
return fetch('/cosmos/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(values)
})
.then((res) => res.json())
}
function me() {
return fetch('/cosmos/api/me/', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then((res) => res.json())
}
export {
login,
me
};

4
client/src/api/index.jsx Normal file
View file

@ -0,0 +1,4 @@
import * as auth from './authentication.jsx';
export {
auth
};

View file

@ -0,0 +1,18 @@
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box } from '@mui/material';
import logo from '../../../../../logo.png';
// ==============================|| AUTH BLUR BACK SVG ||============================== //
const AuthBackground = () => {
const theme = useTheme();
return (
<Box sx={{ position: 'fixed', float: 'left', height: 'calc(100vh - 50px)', overflow: 'hidden', filter: 'blur(25px)', zIndex: -1, top: 100, left: -500 }}>
<img src={logo} style={{ display:'inline'}} alt="Cosmos" width="1100" />
</Box>
);
};
export default AuthBackground;

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><g id="图层_2" data-name="图层 2"><g id="Discord_Logos" data-name="Discord Logos"><g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White"><path d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 925 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><g id="图层_2" data-name="图层 2"><g id="Discord_Logos" data-name="Discord Logos"><g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White"><path d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 925 B

View file

@ -0,0 +1,3 @@
<svg width="9" height="16" viewBox="0 0 9 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.04102 0.125034C3.96989 0.125034 2.29102 1.80391 2.29102 3.87503V5.75003H0.0410156V8.75003H2.29102V15.875H5.29102V8.75003H7.91602L8.29102 5.75003H5.29102V4.25003C5.29102 3.42166 5.96264 2.75003 6.79102 2.75003H8.29102V0.245784C7.57514 0.171909 6.76139 0.123534 6.04102 0.125034Z" fill="#4267B2"/>
</svg>

After

Width:  |  Height:  |  Size: 409 B

View file

@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.6871 6.53113H15.083V6.5H8.33301V9.5H12.5716C11.9533 11.2464 10.2916 12.5 8.33301 12.5C5.84788 12.5 3.83301 10.4851 3.83301 8C3.83301 5.51487 5.84788 3.5 8.33301 3.5C9.48013 3.5 10.5238 3.93275 11.3184 4.63962L13.4398 2.51825C12.1003 1.26987 10.3085 0.5 8.33301 0.5C4.19113 0.5 0.833008 3.85812 0.833008 8C0.833008 12.1419 4.19113 15.5 8.33301 15.5C12.4749 15.5 15.833 12.1419 15.833 8C15.833 7.49713 15.7813 7.00625 15.6871 6.53113Z" fill="#FFC107"/>
<path d="M1.69824 4.50913L4.16237 6.31625C4.82912 4.6655 6.44387 3.5 8.33349 3.5C9.48062 3.5 10.5242 3.93275 11.3189 4.63963L13.4402 2.51825C12.1007 1.26988 10.309 0.5 8.33349 0.5C5.45274 0.5 2.95449 2.12638 1.69824 4.50913Z" fill="#FF3D00"/>
<path d="M8.33312 15.5C10.2704 15.5 12.0306 14.7586 13.3615 13.553L11.0402 11.5888C10.2872 12.1591 9.35125 12.5 8.33312 12.5C6.38237 12.5 4.726 11.2561 4.102 9.52026L1.65625 11.4046C2.8975 13.8335 5.41825 15.5 8.33312 15.5Z" fill="#4CAF50"/>
<path d="M15.6871 6.53113H15.083V6.5H8.33301V9.5H12.5716C12.2746 10.3389 11.735 11.0622 11.039 11.5891C11.0394 11.5887 11.0398 11.5887 11.0401 11.5884L13.3614 13.5526C13.1971 13.7019 15.833 11.75 15.833 8C15.833 7.49713 15.7813 7.00625 15.6871 6.53113Z" fill="#1976D2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,3 @@
<svg width="16" height="13" viewBox="0 0 16 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.5 1.42863C14.9488 1.67278 14.3559 1.83568 13.7306 1.91276C14.3663 1.53529 14.8555 0.933256 15.085 0.222065C14.4901 0.570786 13.831 0.827014 13.1298 0.962003C12.5698 0.368303 11.7711 0 10.8862 0C9.18636 0 7.80856 1.36572 7.80856 3.04975C7.80856 3.28806 7.83647 3.52012 7.88897 3.74552C5.33168 3.6172 3.06354 2.40147 1.54616 0.55662C1.27952 1.00742 1.12953 1.53529 1.12953 2.09233C1.12953 3.15099 1.67157 4.08299 2.49817 4.63211C1.99363 4.6167 1.51866 4.47629 1.10287 4.25131C1.10287 4.26048 1.10287 4.27423 1.10287 4.28714C1.10287 5.7666 2.16403 6.99858 3.57058 7.27898C3.31352 7.34939 3.04187 7.38855 2.76189 7.38855C2.56316 7.38855 2.36943 7.36605 2.18194 7.33231C2.57358 8.54137 3.70973 9.42505 5.05587 9.4513C4.00262 10.2679 2.67607 10.757 1.23369 10.757C0.984543 10.757 0.740813 10.7429 0.5 10.7137C1.8628 11.5765 3.481 12.0823 5.21794 12.0823C10.8779 12.0823 13.9743 7.43438 13.9743 3.40222C13.9743 3.27014 13.9701 3.13849 13.9639 3.0085C14.568 2.58187 15.0888 2.04358 15.5 1.42863Z" fill="#03A9F4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,4 @@
.apexcharts-legend-series .apexcharts-legend-marker {
left: -4px !important;
top: 2px !important;
}

View file

@ -0,0 +1,29 @@
import PropTypes from 'prop-types';
// third-party
import { motion } from 'framer-motion';
// ==============================|| ANIMATION BUTTON ||============================== //
export default function AnimateButton({ children, type }) {
switch (type) {
case 'rotate': // only available in paid version
case 'slide': // only available in paid version
case 'scale': // only available in paid version
default:
return (
<motion.div whileHover={{ scale: 1 }} whileTap={{ scale: 0.9 }}>
{children}
</motion.div>
);
}
}
AnimateButton.propTypes = {
children: PropTypes.node,
type: PropTypes.oneOf(['slide', 'scale', 'rotate'])
};
AnimateButton.defaultProps = {
type: 'scale'
};

View file

@ -0,0 +1,106 @@
import PropTypes from 'prop-types';
import { useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
// material-ui
import MuiBreadcrumbs from '@mui/material/Breadcrumbs';
import { Grid, Typography } from '@mui/material';
// project imports
import MainCard from '../MainCard';
// ==============================|| BREADCRUMBS ||============================== //
const Breadcrumbs = ({ navigation, title, ...others }) => {
const location = useLocation();
const [main, setMain] = useState();
const [item, setItem] = useState();
// set active item state
const getCollapse = (menu) => {
if (menu.children) {
menu.children.filter((collapse) => {
if (collapse.type && collapse.type === 'collapse') {
getCollapse(collapse);
} else if (collapse.type && collapse.type === 'item') {
if (location.pathname === collapse.url) {
setMain(menu);
setItem(collapse);
}
}
return false;
});
}
};
useEffect(() => {
navigation?.items?.map((menu) => {
if (menu.type && menu.type === 'group') {
getCollapse(menu);
}
return false;
});
});
// only used for component demo breadcrumbs
if (location.pathname === '/breadcrumbs') {
location.pathname = '/dashboard/analytics';
}
let mainContent;
let itemContent;
let breadcrumbContent = <Typography />;
let itemTitle = '';
// collapse item
if (main && main.type === 'collapse') {
mainContent = (
<Typography component={Link} to={document.location.pathname} variant="h6" sx={{ textDecoration: 'none' }} color="textSecondary">
{main.title}
</Typography>
);
}
// items
if (item && item.type === 'item') {
itemTitle = item.title;
itemContent = (
<Typography variant="subtitle1" color="textPrimary">
{itemTitle}
</Typography>
);
// main
if (item.breadcrumbs !== false) {
breadcrumbContent = (
<MainCard border={false} sx={{ mb: 3, bgcolor: 'transparent' }} {...others} content={false}>
<Grid container direction="column" justifyContent="flex-start" alignItems="flex-start" spacing={1}>
<Grid item>
<MuiBreadcrumbs aria-label="breadcrumb">
<Typography component={Link} to="/" color="textSecondary" variant="h6" sx={{ textDecoration: 'none' }}>
Home
</Typography>
{mainContent}
{itemContent}
</MuiBreadcrumbs>
</Grid>
{title && (
<Grid item sx={{ mt: 2 }}>
<Typography variant="h5">{item.title}</Typography>
</Grid>
)}
</Grid>
</MainCard>
);
}
}
return breadcrumbContent;
};
Breadcrumbs.propTypes = {
navigation: PropTypes.object,
title: PropTypes.bool
};
export default Breadcrumbs;

View file

@ -0,0 +1,48 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box } from '@mui/material';
const Dot = ({ color, size }) => {
const theme = useTheme();
let main;
switch (color) {
case 'secondary':
main = theme.palette.secondary.main;
break;
case 'error':
main = theme.palette.error.main;
break;
case 'warning':
main = theme.palette.warning.main;
break;
case 'info':
main = theme.palette.info.main;
break;
case 'success':
main = theme.palette.success.main;
break;
case 'primary':
default:
main = theme.palette.primary.main;
}
return (
<Box
sx={{
width: size || 8,
height: size || 8,
borderRadius: '50%',
bgcolor: main
}}
/>
);
};
Dot.propTypes = {
color: PropTypes.string,
size: PropTypes.number
};
export default Dot;

View file

@ -0,0 +1,62 @@
import PropTypes from 'prop-types';
import { forwardRef } from 'react';
// material-ui
import { Fade, Box, Grow } from '@mui/material';
// ==============================|| TRANSITIONS ||============================== //
const Transitions = forwardRef(({ children, position, type, ...others }, ref) => {
let positionSX = {
transformOrigin: '0 0 0'
};
switch (position) {
case 'top-right':
case 'top':
case 'bottom-left':
case 'bottom-right':
case 'bottom':
case 'top-left':
default:
positionSX = {
transformOrigin: '0 0 0'
};
break;
}
return (
<Box ref={ref}>
{type === 'grow' && (
<Grow {...others}>
<Box sx={positionSX}>{children}</Box>
</Grow>
)}
{type === 'fade' && (
<Fade
{...others}
timeout={{
appear: 0,
enter: 300,
exit: 150
}}
>
<Box sx={positionSX}>{children}</Box>
</Fade>
)}
</Box>
);
});
Transitions.propTypes = {
children: PropTypes.node,
type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']),
position: PropTypes.oneOf(['top-left', 'top-right', 'top', 'bottom-left', 'bottom-right', 'bottom'])
};
Transitions.defaultProps = {
type: 'grow',
position: 'top-left'
};
export default Transitions;

View file

@ -0,0 +1,15 @@
import { Suspense } from 'react';
// project import
import Loader from './Loader';
// ==============================|| LOADABLE - LAZY LOADING ||============================== //
const Loadable = (Component) => (props) =>
(
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
export default Loadable;

View file

@ -0,0 +1,25 @@
// material-ui
import { styled } from '@mui/material/styles';
import LinearProgress from '@mui/material/LinearProgress';
// loader style
const LoaderWrapper = styled('div')(({ theme }) => ({
position: 'fixed',
top: 0,
left: 0,
zIndex: 2001,
width: '100%',
'& > * + *': {
marginTop: theme.spacing(2)
}
}));
// ==============================|| Loader ||============================== //
const Loader = () => (
<LoaderWrapper>
<LinearProgress color="primary" />
</LoaderWrapper>
);
export default Loader;

View file

@ -0,0 +1,26 @@
// material-ui
import { useTheme } from '@mui/material/styles';
import { fontWeight } from '@mui/system';
import logo from '../../../../logo.png';
// ==============================|| LOGO SVG ||============================== //
const Logo = () => {
const theme = useTheme();
return (
/**
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
*
* <img src={logo} alt="Mantis" width="100" />
*
*/
<>
<img src={logo} alt="Cosmos" width="50" />
<span style={{fontWeight: 'bold', fontSize: '170%', paddingLeft:'10px'}}> Cosmos</span>
</>
);
};
export default Logo;

View file

@ -0,0 +1,24 @@
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
// material-ui
import { ButtonBase } from '@mui/material';
// project import
import Logo from './Logo';
import config from '../../config';
// ==============================|| MAIN LOGO ||============================== //
const LogoSection = ({ sx, to }) => (
<ButtonBase disableRipple component={Link} to={!to ? config.defaultPath : to} sx={sx}>
<Logo />
</ButtonBase>
);
LogoSection.propTypes = {
sx: PropTypes.object,
to: PropTypes.string
};
export default LogoSection;

View file

@ -0,0 +1,109 @@
import PropTypes from 'prop-types';
import { forwardRef } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material';
// project import
import Highlighter from './third-party/Highlighter';
// header style
const headerSX = {
p: 2.5,
'& .MuiCardHeader-action': { m: '0px auto', alignSelf: 'center' }
};
// ==============================|| CUSTOM - MAIN CARD ||============================== //
const MainCard = forwardRef(
(
{
border = true,
boxShadow,
children,
content = true,
contentSX = {},
darkTitle,
divider = true,
elevation,
secondary,
shadow,
sx = {},
title,
codeHighlight,
...others
},
ref
) => {
const theme = useTheme();
boxShadow = theme.palette.mode === 'dark' ? boxShadow || true : boxShadow;
return (
<Card
elevation={elevation || 0}
ref={ref}
{...others}
sx={{
...sx,
border: border ? '1px solid' : 'none',
borderRadius: 2,
borderColor: theme.palette.mode === 'dark' ? theme.palette.divider : theme.palette.grey.A800,
boxShadow: boxShadow && (!border || theme.palette.mode === 'dark') ? shadow || theme.customShadows.z1 : 'inherit',
':hover': {
boxShadow: boxShadow ? shadow || theme.customShadows.z1 : 'inherit'
},
'& pre': {
m: 0,
p: '16px !important',
fontFamily: theme.typography.fontFamily,
fontSize: '0.75rem'
}
}}
>
{/* card header and action */}
{!darkTitle && title && (
<CardHeader sx={headerSX} titleTypographyProps={{ variant: 'subtitle1' }} title={title} action={secondary} />
)}
{darkTitle && title && (
<CardHeader sx={headerSX} title={<Typography variant="h3">{title}</Typography>} action={secondary} />
)}
{/* content & header divider */}
{title && divider && <Divider />}
{/* card content */}
{content && <CardContent sx={contentSX}>{children}</CardContent>}
{!content && children}
{/* card footer - clipboard & highlighter */}
{codeHighlight && (
<>
<Divider sx={{ borderStyle: 'dashed' }} />
<Highlighter codeHighlight={codeHighlight} main>
{children}
</Highlighter>
</>
)}
</Card>
);
}
);
MainCard.propTypes = {
border: PropTypes.bool,
boxShadow: PropTypes.bool,
contentSX: PropTypes.object,
darkTitle: PropTypes.bool,
divider: PropTypes.bool,
elevation: PropTypes.number,
secondary: PropTypes.node,
shadow: PropTypes.string,
sx: PropTypes.object,
title: PropTypes.string,
codeHighlight: PropTypes.bool,
content: PropTypes.bool,
children: PropTypes.node
};
export default MainCard;

View file

@ -0,0 +1,26 @@
import PropTypes from 'prop-types';
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
// ==============================|| NAVIGATION - SCROLL TO TOP ||============================== //
const ScrollTop = ({ children }) => {
const location = useLocation();
const { pathname } = location;
useEffect(() => {
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
}, [pathname]);
return children || null;
};
ScrollTop.propTypes = {
children: PropTypes.node
};
export default ScrollTop;

View file

@ -0,0 +1,22 @@
// material-ui
import { useMediaQuery, Container, Link, Typography, Stack } from '@mui/material';
// ==============================|| FOOTER - AUTHENTICATION ||============================== //
const AuthFooter = () => {
const matchDownSM = useMediaQuery((theme) => theme.breakpoints.down('sm'));
return (
<Container maxWidth="xl">
<Stack
direction={matchDownSM ? 'column' : 'row'}
justifyContent={matchDownSM ? 'center' : 'space-between'}
spacing={2}
textAlign={matchDownSM ? 'center' : 'inherit'}
>
</Stack>
</Container>
);
};
export default AuthFooter;

View file

@ -0,0 +1,70 @@
import PropTypes from 'prop-types';
// material-ui
import { Box, Chip, Grid, Stack, Typography } from '@mui/material';
// project import
import MainCard from '../../../components/MainCard';
// assets
import { RiseOutlined, FallOutlined } from '@ant-design/icons';
// ==============================|| STATISTICS - ECOMMERCE CARD ||============================== //
const AnalyticEcommerce = ({ color, title, count, percentage, isLoss, extra }) => (
<MainCard contentSX={{ p: 2.25 }}>
<Stack spacing={0.5}>
<Typography variant="h6" color="textSecondary">
{title}
</Typography>
<Grid container alignItems="center">
<Grid item>
<Typography variant="h4" color="inherit">
{count}
</Typography>
</Grid>
{percentage && (
<Grid item>
<Chip
variant="combined"
color={color}
icon={
<>
{!isLoss && <RiseOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
{isLoss && <FallOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
</>
}
label={`${percentage}%`}
sx={{ ml: 1.25, pl: 1 }}
size="small"
/>
</Grid>
)}
</Grid>
</Stack>
<Box sx={{ pt: 2.25 }}>
<Typography variant="caption" color="textSecondary">
You made an extra{' '}
<Typography component="span" variant="caption" sx={{ color: `${color || 'primary'}.main` }}>
{extra}
</Typography>{' '}
this year
</Typography>
</Box>
</MainCard>
);
AnalyticEcommerce.propTypes = {
color: PropTypes.string,
title: PropTypes.string,
count: PropTypes.string,
percentage: PropTypes.number,
isLoss: PropTypes.bool,
extra: PropTypes.oneOfType([PropTypes.node, PropTypes.string])
};
AnalyticEcommerce.defaultProps = {
color: 'primary'
};
export default AnalyticEcommerce;

View file

@ -0,0 +1,65 @@
import PropTypes from 'prop-types';
import { useState } from 'react';
// material-ui
import { Box, CardActions, Collapse, Divider, IconButton, Tooltip } from '@mui/material';
// third-party
import { CopyToClipboard } from 'react-copy-to-clipboard';
import reactElementToJSXString from 'react-element-to-jsx-string';
// project import
import SyntaxHighlight from '../../utils/SyntaxHighlight';
// assets
import { CodeOutlined, CopyOutlined } from '@ant-design/icons';
// ==============================|| CLIPBOARD & HIGHLIGHTER ||============================== //
const Highlighter = ({ children }) => {
const [highlight, setHighlight] = useState(false);
return (
<Box sx={{ position: 'relative' }}>
<CardActions sx={{ justifyContent: 'flex-end', p: 1, mb: highlight ? 1 : 0 }}>
<Box sx={{ display: 'flex', position: 'inherit', right: 0, top: 6 }}>
<CopyToClipboard text={reactElementToJSXString(children, { showFunctions: true, maxInlineAttributesLineLength: 100 })}>
<Tooltip title="Copy the source" placement="top-end">
<IconButton color="secondary" size="small" sx={{ fontSize: '0.875rem' }}>
<CopyOutlined />
</IconButton>
</Tooltip>
</CopyToClipboard>
<Divider orientation="vertical" variant="middle" flexItem sx={{ mx: 1 }} />
<Tooltip title="Show the source" placement="top-end">
<IconButton
sx={{ fontSize: '0.875rem' }}
size="small"
color={highlight ? 'primary' : 'secondary'}
onClick={() => setHighlight(!highlight)}
>
<CodeOutlined />
</IconButton>
</Tooltip>
</Box>
</CardActions>
<Collapse in={highlight}>
{highlight && (
<SyntaxHighlight>
{reactElementToJSXString(children, {
showFunctions: true,
showDefaultProps: false,
maxInlineAttributesLineLength: 100
})}
</SyntaxHighlight>
)}
</Collapse>
</Box>
);
};
Highlighter.propTypes = {
children: PropTypes.node
};
export default Highlighter;

View file

@ -0,0 +1,62 @@
import PropTypes from 'prop-types';
// material-ui
import { alpha, styled } from '@mui/material/styles';
import { Box } from '@mui/material';
// third-party
import SimpleBar from 'simplebar-react';
import { BrowserView, MobileView } from 'react-device-detect';
// root style
const RootStyle = styled(BrowserView)({
flexGrow: 1,
height: '100%',
overflow: 'hidden'
});
// scroll bar wrapper
const SimpleBarStyle = styled(SimpleBar)(({ theme }) => ({
maxHeight: '100%',
'& .simplebar-scrollbar': {
'&:before': {
backgroundColor: alpha(theme.palette.grey[500], 0.48)
},
'&.simplebar-visible:before': {
opacity: 1
}
},
'& .simplebar-track.simplebar-vertical': {
width: 10
},
'& .simplebar-track.simplebar-horizontal .simplebar-scrollbar': {
height: 6
},
'& .simplebar-mask': {
zIndex: 'inherit'
}
}));
// ==============================|| SIMPLE SCROLL BAR ||============================== //
export default function SimpleBarScroll({ children, sx, ...other }) {
return (
<>
<RootStyle>
<SimpleBarStyle timeout={500} clickOnTrack={false} sx={sx} {...other}>
{children}
</SimpleBarStyle>
</RootStyle>
<MobileView>
<Box sx={{ overflowX: 'auto', ...sx }} {...other}>
{children}
</Box>
</MobileView>
</>
);
}
SimpleBarScroll.propTypes = {
children: PropTypes.node,
sx: PropTypes.object
};

19
client/src/config.jsx Normal file
View file

@ -0,0 +1,19 @@
// ==============================|| THEME CONFIG ||============================== //
const config = {
defaultPath: '/dashboard/default',
fontFamily: `'Public Sans', sans-serif`,
i18n: 'en',
miniDrawer: false,
container: true,
mode: 'light',
presetColor: 'default',
themeDirection: 'ltr'
};
export default config;
export const drawerWidth = 260;
export const twitterColor = '#1DA1F2';
export const facebookColor = '#3b5998';
export const linkedInColor = '#0e76a8';

36
client/src/index.jsx Normal file
View file

@ -0,0 +1,36 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
// scroll bar
import 'simplebar/src/simplebar.css';
// third-party
import { Provider as ReduxProvider } from 'react-redux';
// apex-chart
import './assets/third-party/apex-chart.css';
// project import
import App from './App';
import { store } from './store';
import reportWebVitals from './reportWebVitals';
// ==============================|| MAIN - REACT DOM RENDER ||============================== //
const container = document.getElementById('root');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(
<StrictMode>
<ReduxProvider store={store}>
<BrowserRouter basename="/">
<App />
</BrowserRouter>
</ReduxProvider>
</StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

13
client/src/isLoggedIn.jsx Normal file
View file

@ -0,0 +1,13 @@
import * as API from './api';
import { useEffect } from 'react';
const isLoggedIn = () => useEffect(() => {
API.auth.me().then((data) => {
if(data.status != 'OK') {
window.location.href = '/login';
}
});
});
export default isLoggedIn;

View file

@ -0,0 +1,31 @@
// material-ui
import { Button, CardMedia, Link, 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}>
<Stack alignItems="center">
<Typography variant="h5">Cosmos Pro</Typography>
<Typography variant="h6" color="secondary">
Checkout pro features
</Typography>
</Stack>
<AnimateButton>
<Button component={Link} target="_blank" href="https://mantisdashboard.io" variant="contained" color="success" size="small">
Pro
</Button>
</AnimateButton>
</Stack>
</MainCard>
);
export default NavCard;

View file

@ -0,0 +1,59 @@
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
// material-ui
import { Box, List, Typography } from '@mui/material';
// project import
import NavItem from './NavItem';
// ==============================|| NAVIGATION - LIST GROUP ||============================== //
const NavGroup = ({ item }) => {
const menu = useSelector((state) => state.menu);
const { drawerOpen } = menu;
const navCollapse = item.children?.map((menuItem) => {
switch (menuItem.type) {
case 'collapse':
return (
<Typography key={menuItem.id} variant="caption" color="error" sx={{ p: 2.5 }}>
collapse - only available in paid version
</Typography>
);
case 'item':
return <NavItem key={menuItem.id} item={menuItem} level={1} />;
default:
return (
<Typography key={menuItem.id} variant="h6" color="error" align="center">
Fix - Group Collapse or Items
</Typography>
);
}
});
return (
<List
subheader={
item.title &&
drawerOpen && (
<Box sx={{ pl: 3, mb: 1.5 }}>
<Typography variant="subtitle2" color="textSecondary">
{item.title}
</Typography>
{/* only available in paid version */}
</Box>
)
}
sx={{ mb: drawerOpen ? 1.5 : 0, py: 0, zIndex: 0 }}
>
{navCollapse}
</List>
);
};
NavGroup.propTypes = {
item: PropTypes.object
};
export default NavGroup;

View file

@ -0,0 +1,146 @@
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 } from '@mui/material';
// project import
import { activeItem } from '../../../../../store/reducers/menu';
// ==============================|| NAVIGATION - LIST ITEM ||============================== //
const NavItem = ({ item, level }) => {
const theme = useTheme();
const dispatch = useDispatch();
const menu = useSelector((state) => state.menu);
const { drawerOpen, openItem } = menu;
let itemTarget = '_self';
if (item.target) {
itemTarget = '_blank';
}
let listItemProps = { component: forwardRef((props, ref) => <Link ref={ref} {...props} to={item.url} target={itemTarget} />) };
if (item?.external) {
listItemProps = { component: 'a', href: item.url, target: itemTarget };
}
const itemHandler = (id) => {
dispatch(activeItem({ openItem: [id] }));
};
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;
// active menu item on page load
useEffect(() => {
const currentIndex = document.location.pathname
.toString()
.split('/')
.findIndex((id) => id === item.id);
if (currentIndex > -1) {
dispatch(activeItem({ openItem: [item.id] }));
}
// eslint-disable-next-line
}, []);
const textColor = 'text.primary';
const iconSelectedColor = 'primary.main';
return (
<ListItemButton
{...listItemProps}
disabled={item.disabled}
onClick={() => itemHandler(item.id)}
selected={isSelected}
sx={{
zIndex: 1201,
pl: drawerOpen ? `${level * 28}px` : 1.5,
py: !drawerOpen && level === 1 ? 1.25 : 1,
...(drawerOpen && {
'&:hover': {
bgcolor: 'primary.lighter'
},
'&.Mui-selected': {
bgcolor: 'primary.lighter',
borderRight: `2px solid ${theme.palette.primary.main}`,
color: iconSelectedColor,
'&:hover': {
color: iconSelectedColor,
bgcolor: 'primary.lighter'
}
}
}),
...(!drawerOpen && {
'&:hover': {
bgcolor: 'transparent'
},
'&.Mui-selected': {
'&:hover': {
bgcolor: 'transparent'
},
bgcolor: 'transparent'
}
})
}}
>
{itemIcon && (
<ListItemIcon
sx={{
minWidth: 28,
color: isSelected ? iconSelectedColor : textColor,
...(!drawerOpen && {
borderRadius: 1.5,
width: 36,
height: 36,
alignItems: 'center',
justifyContent: 'center',
'&:hover': {
bgcolor: 'secondary.lighter'
}
}),
...(!drawerOpen &&
isSelected && {
bgcolor: 'primary.lighter',
'&:hover': {
bgcolor: '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>
);
};
NavItem.propTypes = {
item: PropTypes.object,
level: PropTypes.number
};
export default NavItem;

View file

@ -0,0 +1,27 @@
// material-ui
import { Box, Typography } from '@mui/material';
// project import
import NavGroup from './NavGroup';
import menuItem from '../../../../../menu-items';
// ==============================|| DRAWER CONTENT - NAVIGATION ||============================== //
const Navigation = () => {
const navGroups = menuItem.items.map((item) => {
switch (item.type) {
case 'group':
return <NavGroup key={item.id} item={item} />;
default:
return (
<Typography key={item.id} variant="h6" color="error" align="center">
Fix - Navigation Group
</Typography>
);
}
});
return <Box sx={{ pt: 2 }}>{navGroups}</Box>;
};
export default Navigation;

View file

@ -0,0 +1,22 @@
// project import
import NavCard from './NavCard';
import Navigation from './Navigation';
import SimpleBar from '../../../../components/third-party/SimpleBar';
// ==============================|| DRAWER CONTENT ||============================== //
const DrawerContent = () => (
<SimpleBar
sx={{
'& .simplebar-content': {
display: 'flex',
flexDirection: 'column'
}
}}
>
<Navigation />
{/* <NavCard /> */}
</SimpleBar>
);
export default DrawerContent;

View file

@ -0,0 +1,15 @@
// 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)
}));
export default DrawerHeaderStyled;

View file

@ -0,0 +1,38 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Stack, Chip } from '@mui/material';
// project import
import DrawerHeaderStyled from './DrawerHeaderStyled';
import Logo from '../../../../components/Logo';
import {version} from '../../../../../../gupm.json';
// ==============================|| DRAWER HEADER ||============================== //
const DrawerHeader = ({ open }) => {
const theme = useTheme();
return (
<DrawerHeaderStyled theme={theme} open={open}>
<Stack direction="row" spacing={1} alignItems="center">
<Logo />
<Chip
label={version}
size="small"
sx={{ height: 16, '& .MuiChip-label': { fontSize: '0.625rem', py: 0.25 } }}
component="a"
href="/"
clickable
/>
</Stack>
</DrawerHeaderStyled>
);
};
DrawerHeader.propTypes = {
open: PropTypes.bool
};
export default DrawerHeader;

View file

@ -0,0 +1,47 @@
// material-ui
import { styled } from '@mui/material/styles';
import Drawer from '@mui/material/Drawer';
// project import
import { drawerWidth } from '../../../config';
const openedMixin = (theme) => ({
width: drawerWidth,
borderRight: `1px solid ${theme.palette.divider}`,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
}),
overflowX: 'hidden',
boxShadow: 'none'
});
const closedMixin = (theme) => ({
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
overflowX: 'hidden',
width: 0,
borderRight: 'none',
boxShadow: theme.customShadows.z1
});
// ==============================|| DRAWER - MINI STYLED ||============================== //
const MiniDrawerStyled = styled(Drawer, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme)
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme)
})
}));
export default MiniDrawerStyled;

View file

@ -0,0 +1,66 @@
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 { drawerWidth } from '../../../config';
// ==============================|| MAIN LAYOUT - DRAWER ||============================== //
const MainDrawer = ({ open, handleDrawerToggle, window }) => {
const theme = useTheme();
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
// responsive drawer container
const container = window !== undefined ? () => window().document.body : undefined;
// header content
const drawerContent = useMemo(() => <DrawerContent />, []);
const drawerHeader = useMemo(() => <DrawerHeader open={open} />, [open]);
return (
<Box component="nav" sx={{ flexShrink: { md: 0 }, zIndex: 1300 }} aria-label="mailbox folders">
{!matchDownMD ? (
<MiniDrawerStyled variant="permanent" open={open}>
{drawerHeader}
{drawerContent}
</MiniDrawerStyled>
) : (
<Drawer
container={container}
variant="temporary"
open={open}
onClose={handleDrawerToggle}
ModalProps={{ keepMounted: true }}
sx={{
display: { xs: 'block', lg: 'none' },
'& .MuiDrawer-paper': {
boxSizing: 'border-box',
width: drawerWidth,
borderRight: `1px solid ${theme.palette.divider}`,
backgroundImage: 'none',
boxShadow: 'inherit'
}
}}
>
{open && drawerHeader}
{open && drawerContent}
</Drawer>
)}
</Box>
);
};
MainDrawer.propTypes = {
open: PropTypes.bool,
handleDrawerToggle: PropTypes.func,
window: PropTypes.object
};
export default MainDrawer;

View file

@ -0,0 +1,26 @@
// material-ui
import { styled } from '@mui/material/styles';
import AppBar from '@mui/material/AppBar';
// project import
import { drawerWidth } 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 && {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
})
}));
export default AppBarStyled;

View file

@ -0,0 +1,102 @@
import { useEffect, useRef, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { AppBar, Box, ClickAwayListener, IconButton, Paper, Popper, Toolbar } from '@mui/material';
// project import
import Search from './Search';
import Profile from './Profile';
import Transitions from '../../../../components/@extended/Transitions';
// 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]);
return (
<>
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
<IconButton
component="span"
disableRipple
sx={{
bgcolor: open ? 'grey.300' : 'grey.100'
}}
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
color="inherit"
>
<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;

View file

@ -0,0 +1,278 @@
import { useRef, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import {
Avatar,
Badge,
Box,
ClickAwayListener,
Divider,
IconButton,
List,
ListItemButton,
ListItemAvatar,
ListItemText,
ListItemSecondaryAction,
Paper,
Popper,
Typography,
useMediaQuery
} from '@mui/material';
// project import
import MainCard from '../../../../components/MainCard';
import Transitions from '../../../../components/@extended/Transitions';
// assets
import { BellOutlined, CloseOutlined, 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 [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 = 'grey.300';
const iconBackColor = 'grey.100';
return (
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
<IconButton
disableRipple
color="secondary"
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={4} 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="fade" 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={
<IconButton size="small" onClick={handleToggle}>
<CloseOutlined />
</IconButton>
}
>
<List
component="nav"
sx={{
p: 0,
'& .MuiListItemButton-root': {
py: 0.5,
'& .MuiAvatar-root': avatarSX,
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
}
}}
>
<ListItemButton>
<ListItemAvatar>
<Avatar
sx={{
color: 'success.main',
bgcolor: 'success.lighter'
}}
>
<GiftOutlined />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={
<Typography variant="h6">
It&apos;s{' '}
<Typography component="span" variant="subtitle1">
Cristina danny&apos;s
</Typography>{' '}
birthday today.
</Typography>
}
secondary="2 min ago"
/>
<ListItemSecondaryAction>
<Typography variant="caption" noWrap>
3:00 AM
</Typography>
</ListItemSecondaryAction>
</ListItemButton>
<Divider />
<ListItemButton>
<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 &nbsp;
<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;

View file

@ -0,0 +1,62 @@
import PropTypes from 'prop-types';
import { useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
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 theme = useTheme();
const [selectedIndex, setSelectedIndex] = useState(0);
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};
return (
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32, color: theme.palette.grey[500] } }}>
<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;

View file

@ -0,0 +1,56 @@
import { useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
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 theme = useTheme();
const [selectedIndex, setSelectedIndex] = useState(0);
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};
return (
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32, color: theme.palette.grey[500] } }}>
<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;

View file

@ -0,0 +1,212 @@
import PropTypes from 'prop-types';
import { useRef, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import {
Avatar,
Box,
ButtonBase,
CardContent,
ClickAwayListener,
Grid,
IconButton,
Paper,
Popper,
Stack,
Tab,
Tabs,
Typography
} from '@mui/material';
// project import
import MainCard from '../../../../../components/MainCard';
import Transitions from '../../../../../components/@extended/Transitions';
import ProfileTab from './ProfileTab';
import SettingTab from './SettingTab';
// 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 handleLogout = async () => {
// logout
};
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 = 'grey.300';
return (
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
<ButtonBase
sx={{
p: 0.25,
bgcolor: open ? iconBackColorOpen : 'transparent',
borderRadius: 1,
'&:hover': { bgcolor: 'secondary.lighter' }
}}
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} sx={{ width: 32, height: 32 }} />
<Typography variant="subtitle1">John Doe</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="fade" in={open} {...TransitionProps}>
{open && (
<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">John Doe</Typography>
<Typography variant="body2" color="textSecondary">
UI/UX Designer
</Typography>
</Stack>
</Stack>
</Grid>
<Grid item>
<IconButton size="large" color="secondary" onClick={handleLogout}>
<LogoutOutlined />
</IconButton>
</Grid>
</Grid>
</CardContent>
{open && (
<>
<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;

View 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;

View file

@ -0,0 +1,28 @@
// material-ui
import { Box, IconButton, Link, useMediaQuery } from '@mui/material';
import { GithubOutlined } from '@ant-design/icons';
// project import
import Search from './Search';
import Profile from './Profile';
import Notification from './Notification';
import MobileSection from './MobileSection';
// ==============================|| HEADER - CONTENT ||============================== //
const HeaderContent = () => {
const matchesXs = useMediaQuery((theme) => theme.breakpoints.down('md'));
return (
<>
{!matchesXs && <Search />}
{matchesXs && <Box sx={{ width: '100%', ml: 1 }} />}
<Notification />
{!matchesXs && <Profile />}
{matchesXs && <MobileSection />}
</>
);
};
export default HeaderContent;

View file

@ -0,0 +1,69 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { AppBar, IconButton, Toolbar, useMediaQuery } from '@mui/material';
// project import
import AppBarStyled from './AppBarStyled';
import HeaderContent from './HeaderContent';
// assets
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
// ==============================|| MAIN LAYOUT - HEADER ||============================== //
const Header = ({ open, handleDrawerToggle }) => {
const theme = useTheme();
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
const iconBackColor = 'grey.100';
const iconBackColorOpen = 'grey.200';
// common header
const mainHeader = (
<Toolbar>
<IconButton
disableRipple
aria-label="open drawer"
onClick={handleDrawerToggle}
edge="start"
color="secondary"
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor, ml: { xs: 0, lg: -2 } }}
>
{!open ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</IconButton>
<HeaderContent />
</Toolbar>
);
// app-bar params
const appBar = {
position: 'fixed',
color: 'inherit',
elevation: 0,
sx: {
borderBottom: `1px solid ${theme.palette.divider}`
// boxShadow: theme.customShadows.z1
}
};
return (
<>
{!matchDownMD ? (
<AppBarStyled open={open} {...appBar}>
{mainHeader}
</AppBarStyled>
) : (
<AppBar {...appBar}>{mainHeader}</AppBar>
)}
</>
);
};
Header.propTypes = {
open: PropTypes.bool,
handleDrawerToggle: PropTypes.func
};
export default Header;

View file

@ -0,0 +1,60 @@
import { useEffect, useState } from 'react';
import { Outlet } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box, Toolbar, useMediaQuery } from '@mui/material';
// project import
import Drawer from './Drawer';
import Header from './Header';
import navigation from '../../menu-items';
import Breadcrumbs from '../../components/@extended/Breadcrumbs';
// types
import { openDrawer } from '../../store/reducers/menu';
// ==============================|| MAIN LAYOUT ||============================== //
const MainLayout = () => {
const theme = useTheme();
const matchDownLG = useMediaQuery(theme.breakpoints.down('xl'));
const dispatch = useDispatch();
const { drawerOpen } = useSelector((state) => state.menu);
// drawer toggler
const [open, setOpen] = useState(drawerOpen);
const handleDrawerToggle = () => {
setOpen(!open);
dispatch(openDrawer({ drawerOpen: !open }));
};
// set media wise responsive drawer
useEffect(() => {
setOpen(!matchDownLG);
dispatch(openDrawer({ drawerOpen: !matchDownLG }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [matchDownLG]);
useEffect(() => {
if (open !== drawerOpen) setOpen(drawerOpen);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [drawerOpen]);
return (
<Box sx={{ display: 'flex', width: '100%' }}>
<Header open={open} handleDrawerToggle={handleDrawerToggle} />
<Drawer open={open} handleDrawerToggle={handleDrawerToggle} />
<Box component="main" sx={{ width: '100%', flexGrow: 1, p: { xs: 2, sm: 3 } }}>
<Toolbar />
<Breadcrumbs navigation={navigation} title titleBottom card={false} divider={false} />
<Outlet />
</Box>
</Box>
);
};
export default MainLayout;

View file

@ -0,0 +1,11 @@
import { Outlet } from 'react-router-dom';
// ==============================|| MINIMAL LAYOUT ||============================== //
const MinimalLayout = () => (
<>
<Outlet />
</>
);
export default MinimalLayout;

0
client/src/main.css Normal file
View file

9
client/src/main.tsx Normal file
View file

@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import './main.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<div>Hello</div>
</React.StrictMode>
)

View file

@ -0,0 +1,27 @@
// assets
import { HomeOutlined } from '@ant-design/icons';
// icons
const icons = {
HomeOutlined
};
// ==============================|| MENU ITEMS - DASHBOARD ||============================== //
const dashboard = {
id: 'group-dashboard',
title: 'Navigation',
type: 'group',
children: [
{
id: 'home',
title: 'Home',
type: 'item',
url: '/',
icon: icons.HomeOutlined,
breadcrumbs: false
}
]
};
export default dashboard;

View file

@ -0,0 +1,12 @@
// project import
import pages from './pages';
import dashboard from './dashboard';
import support from './support';
// ==============================|| MENU ITEMS ||============================== //
const menuItems = {
items: [dashboard, pages, support]
};
export default menuItems;

View file

@ -0,0 +1,42 @@
// assets
import { ProfileOutlined, SettingOutlined, NodeExpandOutlined} from '@ant-design/icons';
// icons
const icons = {
NodeExpandOutlined,
ProfileOutlined,
SettingOutlined
};
// ==============================|| MENU ITEMS - EXTRA PAGES ||============================== //
const pages = {
id: 'management',
title: 'Management',
type: 'group',
children: [
{
id: 'proxy',
title: 'Proxy Routes',
type: 'item',
url: '/config/proxy',
icon: icons.NodeExpandOutlined,
},
{
id: 'users',
title: 'Manage Users',
type: 'item',
url: '/config/users',
icon: icons.ProfileOutlined,
},
{
id: 'config',
title: 'Configuration',
type: 'item',
url: '/config/general',
icon: icons.SettingOutlined,
}
]
};
export default pages;

View file

@ -0,0 +1,48 @@
// assets
import { GithubOutlined, QuestionOutlined } from '@ant-design/icons';
import DiscordOutlined from '../assets/images/icons/discord.svg'
// ==============================|| MENU ITEMS - SAMPLE PAGE & DOCUMENTATION ||============================== //
const DiscordOutlinedIcon = (props) => {
return (
<img src={DiscordOutlined} width="16px" alt="Discord" {...props} />
);
};
const support = {
id: 'support',
title: 'Support',
type: 'group',
children: [
{
id: 'discord',
title: 'Discord',
type: 'item',
url: 'https://discord.com/invite/PwMWwsrwHA',
icon: DiscordOutlinedIcon,
external: true,
target: true
},
{
id: 'github',
title: 'Github',
type: 'item',
url: 'https://github.com/azukaar/Cosmos-Server',
icon: GithubOutlined,
external: true,
target: true
},
{
id: 'documentation',
title: 'Documentation',
type: 'item',
url: 'https://github.com/azukaar/Cosmos-Server/wiki',
icon: QuestionOutlined,
external: true,
target: true
}
]
};
export default support;

View file

@ -0,0 +1,35 @@
import PropTypes from 'prop-types';
// material-ui
import { Box } from '@mui/material';
// project import
import MainCard from '../../components/MainCard';
// ==============================|| AUTHENTICATION - CARD WRAPPER ||============================== //
const AuthCard = ({ children, ...other }) => (
<MainCard
sx={{
maxWidth: { xs: 400, lg: 475 },
margin: { xs: 2.5, md: 3 },
'& > *': {
flexGrow: 1,
flexBasis: '50%'
}
}}
content={false}
{...other}
border={false}
boxShadow
shadow={(theme) => theme.customShadows.z1}
>
<Box sx={{ p: { xs: 2, sm: 3, md: 4, xl: 5 } }}>{children}</Box>
</MainCard>
);
AuthCard.propTypes = {
children: PropTypes.node
};
export default AuthCard;

View file

@ -0,0 +1,55 @@
import PropTypes from 'prop-types';
// material-ui
import { Box, Grid } from '@mui/material';
// project import
import AuthCard from './AuthCard';
import Logo from '../../components/Logo';
import AuthFooter from '../../components/cards/AuthFooter';
// assets
import AuthBackground from '../../assets/images/auth/AuthBackground';
// ==============================|| AUTHENTICATION - WRAPPER ||============================== //
const AuthWrapper = ({ children }) => (
<Box sx={{ minHeight: '100vh' }}>
<AuthBackground />
<Grid
container
direction="column"
justifyContent="flex-end"
sx={{
minHeight: '100vh'
}}
>
<Grid item xs={12} sx={{ ml: 3, mt: 3 }}>
<Logo />
</Grid>
<Grid item xs={12}>
<Grid
item
xs={12}
container
justifyContent="center"
alignItems="center"
sx={{ minHeight: { xs: 'calc(100vh - 134px)', md: 'calc(100vh - 112px)' } }}
>
<Grid item>
<AuthCard>{children}</AuthCard>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sx={{ m: 3, mt: 1 }}>
<AuthFooter />
</Grid>
</Grid>
</Box>
);
AuthWrapper.propTypes = {
children: PropTypes.node
};
export default AuthWrapper;

View file

@ -0,0 +1,30 @@
import { Link } from 'react-router-dom';
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import AuthLogin from './auth-forms/AuthLogin';
import AuthWrapper from './AuthWrapper';
// ================================|| LOGIN ||================================ //
const Login = () => (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography variant="h3">Login</Typography>
{/* <Typography component={Link} to="/register" variant="body1" sx={{ textDecoration: 'none' }} color="primary">
Don&apos;t have an account?
</Typography> */}
</Stack>
</Grid>
<Grid item xs={12}>
<AuthLogin />
</Grid>
</Grid>
</AuthWrapper>
);
export default Login;

View file

@ -0,0 +1,30 @@
import { Link } from 'react-router-dom';
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project import
import FirebaseRegister from './auth-forms/AuthRegister';
import AuthWrapper from './AuthWrapper';
// ================================|| REGISTER ||================================ //
const Register = () => (
<AuthWrapper>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography variant="h3">Sign up</Typography>
<Typography component={Link} to="/login" variant="body1" sx={{ textDecoration: 'none' }} color="primary">
Already have an account?
</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<FirebaseRegister />
</Grid>
</Grid>
</AuthWrapper>
);
export default Register;

View file

@ -0,0 +1,226 @@
import React, { useEffect } from 'react';
import { Link as RouterLink } from 'react-router-dom';
// material-ui
import {
Button,
Checkbox,
Divider,
FormControlLabel,
FormHelperText,
Grid,
Link,
IconButton,
InputAdornment,
InputLabel,
OutlinedInput,
Stack,
Typography,
Alert
} from '@mui/material';
import * as API from "../../../api";
// third party
import * as Yup from 'yup';
import { Formik } from 'formik';
// project import
import FirebaseSocial from './FirebaseSocial';
import AnimateButton from '../../../components/@extended/AnimateButton';
// assets
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
// ============================|| FIREBASE - LOGIN ||============================ //
const AuthLogin = () => {
const [checked, setChecked] = React.useState(false);
const [showPassword, setShowPassword] = React.useState(false);
const handleClickShowPassword = () => {
setShowPassword(!showPassword);
};
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
// TODO: Add ?notlogged=1 and ?invalid=1 to check for errors
// TODO: Extract ?redirect=<URL> to redirect to a specific page after login
const urlSearchParams = new URLSearchParams(window.location.search);
const notLogged = urlSearchParams.get('notlogged') == 1;
const invalid = urlSearchParams.get('invalid') == 1;
const redirectTo = urlSearchParams.get('redirect') ? urlSearchParams.get('redirect') : '/';
useEffect(() => {
API.auth.me().then((data) => {
if(data.status == 'OK') {
window.location.href = redirectTo;
}
});
});
return (
<>
{ notLogged &&<Grid container spacing={2} justifyContent="center">
<Alert severity="error">You need to be logged in to access this</Alert>
<br />
</Grid>}
{ invalid &&<Grid container spacing={2} justifyContent="center">
<Alert severity="error">You have been disconnected. Please login to continue</Alert>
<br />
</Grid>}
<Formik
initialValues={{
nickname: '',
password: '',
submit: null
}}
validationSchema={Yup.object().shape({
nickname: Yup.string().max(255).required('Nickname is required'),
password: Yup.string().max(255).required('Password is required')
})}
onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {
try {
API.auth.login(values).then((data) => {
if(data.status == 'error') {
setStatus({ success: false });
if(data.code == 'UL001') {
setErrors({ submit: 'Wrong nickname or password. Try again or try resetting your password' });
} else if (data.code == 'UL002') {
setErrors({ submit: 'You have not yet registered your account. You should have an invite link in your emails. If you need a new one, contact your administrator.' });
} else if(data.status == 'error') {
setErrors({ submit: 'Unexpected error. Try again later.' });
}
setSubmitting(false);
return;
} else {
setStatus({ success: true });
setSubmitting(false);
window.location.href = redirectTo;
}
})
} catch (err) {
setStatus({ success: false });
setErrors({ submit: err.message });
setSubmitting(false);
}
}}
>
{({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => (
<form noValidate onSubmit={handleSubmit}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack spacing={1}>
<InputLabel htmlFor="nickname-login">Nickname</InputLabel>
<OutlinedInput
id="nickname-login"
type="nickname"
value={values.nickname}
name="nickname"
onBlur={handleBlur}
onChange={handleChange}
placeholder="Enter your nickname"
fullWidth
error={Boolean(touched.nickname && errors.nickname)}
/>
{touched.nickname && errors.nickname && (
<FormHelperText error id="standard-weight-helper-text-nickname-login">
{errors.nickname}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1}>
<InputLabel htmlFor="password-login">Password</InputLabel>
<OutlinedInput
fullWidth
error={Boolean(touched.password && errors.password)}
id="-password-login"
type={showPassword ? 'text' : 'password'}
value={values.password}
name="password"
onBlur={handleBlur}
onChange={handleChange}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
size="large"
>
{showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
</IconButton>
</InputAdornment>
}
placeholder="Enter your password"
/>
{touched.password && errors.password && (
<FormHelperText error id="standard-weight-helper-text-password-login">
{errors.password}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={12} sx={{ mt: -1 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
<FormControlLabel
control={
<Checkbox
checked={checked}
onChange={(event) => setChecked(event.target.checked)}
name="checked"
color="primary"
size="small"
/>
}
label={<Typography variant="h6">Keep me sign in</Typography>}
/>
<Link variant="h6" component={RouterLink} to="" color="text.primary">
Forgot Password?
</Link>
</Stack>
</Grid>
{errors.submit && (
<Grid item xs={12}>
<FormHelperText error>{errors.submit}</FormHelperText>
</Grid>
)}
<Grid item xs={12}>
<AnimateButton>
<Button
disableElevation
disabled={isSubmitting}
fullWidth
size="large"
type="submit"
variant="contained"
color="primary"
>
Login
</Button>
</AnimateButton>
</Grid>
{/* <Grid item xs={12}>
<Divider>
<Typography variant="caption"> Login with</Typography>
</Divider>
</Grid>
<Grid item xs={12}>
<FirebaseSocial />
</Grid> */}
</Grid>
</form>
)}
</Formik>
</>
);
};
export default AuthLogin;

View file

@ -0,0 +1,271 @@
import { useEffect, useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
// material-ui
import {
Box,
Button,
Divider,
FormControl,
FormHelperText,
Grid,
Link,
IconButton,
InputAdornment,
InputLabel,
OutlinedInput,
Stack,
Typography
} from '@mui/material';
// third party
import * as Yup from 'yup';
import { Formik } from 'formik';
// project import
import FirebaseSocial from './FirebaseSocial';
import AnimateButton from '../../../components/@extended/AnimateButton';
import { strengthColor, strengthIndicator } from '../../../utils/password-strength';
// assets
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
// ============================|| FIREBASE - REGISTER ||============================ //
const AuthRegister = () => {
const [level, setLevel] = useState();
const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => {
setShowPassword(!showPassword);
};
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
const changePassword = (value) => {
const temp = strengthIndicator(value);
setLevel(strengthColor(temp));
};
useEffect(() => {
changePassword('');
}, []);
return (
<>
<Formik
initialValues={{
firstname: '',
lastname: '',
email: '',
company: '',
password: '',
submit: null
}}
validationSchema={Yup.object().shape({
firstname: Yup.string().max(255).required('First Name is required'),
lastname: Yup.string().max(255).required('Last Name is required'),
email: Yup.string().email('Must be a valid email').max(255).required('Email is required'),
password: Yup.string().max(255).required('Password is required')
})}
onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {
try {
setStatus({ success: false });
setSubmitting(false);
} catch (err) {
console.error(err);
setStatus({ success: false });
setErrors({ submit: err.message });
setSubmitting(false);
}
}}
>
{({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => (
<form noValidate onSubmit={handleSubmit}>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Stack spacing={1}>
<InputLabel htmlFor="firstname-signup">First Name*</InputLabel>
<OutlinedInput
id="firstname-login"
type="firstname"
value={values.firstname}
name="firstname"
onBlur={handleBlur}
onChange={handleChange}
placeholder="John"
fullWidth
error={Boolean(touched.firstname && errors.firstname)}
/>
{touched.firstname && errors.firstname && (
<FormHelperText error id="helper-text-firstname-signup">
{errors.firstname}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={12} md={6}>
<Stack spacing={1}>
<InputLabel htmlFor="lastname-signup">Last Name*</InputLabel>
<OutlinedInput
fullWidth
error={Boolean(touched.lastname && errors.lastname)}
id="lastname-signup"
type="lastname"
value={values.lastname}
name="lastname"
onBlur={handleBlur}
onChange={handleChange}
placeholder="Doe"
inputProps={{}}
/>
{touched.lastname && errors.lastname && (
<FormHelperText error id="helper-text-lastname-signup">
{errors.lastname}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1}>
<InputLabel htmlFor="company-signup">Company</InputLabel>
<OutlinedInput
fullWidth
error={Boolean(touched.company && errors.company)}
id="company-signup"
value={values.company}
name="company"
onBlur={handleBlur}
onChange={handleChange}
placeholder="Demo Inc."
inputProps={{}}
/>
{touched.company && errors.company && (
<FormHelperText error id="helper-text-company-signup">
{errors.company}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1}>
<InputLabel htmlFor="email-signup">Email Address*</InputLabel>
<OutlinedInput
fullWidth
error={Boolean(touched.email && errors.email)}
id="email-login"
type="email"
value={values.email}
name="email"
onBlur={handleBlur}
onChange={handleChange}
placeholder="demo@company.com"
inputProps={{}}
/>
{touched.email && errors.email && (
<FormHelperText error id="helper-text-email-signup">
{errors.email}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1}>
<InputLabel htmlFor="password-signup">Password</InputLabel>
<OutlinedInput
fullWidth
error={Boolean(touched.password && errors.password)}
id="password-signup"
type={showPassword ? 'text' : 'password'}
value={values.password}
name="password"
onBlur={handleBlur}
onChange={(e) => {
handleChange(e);
changePassword(e.target.value);
}}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
size="large"
>
{showPassword ? <EyeOutlined /> : <EyeInvisibleOutlined />}
</IconButton>
</InputAdornment>
}
placeholder="******"
inputProps={{}}
/>
{touched.password && errors.password && (
<FormHelperText error id="helper-text-password-signup">
{errors.password}
</FormHelperText>
)}
</Stack>
<FormControl fullWidth sx={{ mt: 2 }}>
<Grid container spacing={2} alignItems="center">
<Grid item>
<Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} />
</Grid>
<Grid item>
<Typography variant="subtitle1" fontSize="0.75rem">
{level?.label}
</Typography>
</Grid>
</Grid>
</FormControl>
</Grid>
<Grid item xs={12}>
<Typography variant="body2">
By Signing up, you agree to our &nbsp;
<Link variant="subtitle2" component={RouterLink} to="#">
Terms of Service
</Link>
&nbsp; and &nbsp;
<Link variant="subtitle2" component={RouterLink} to="#">
Privacy Policy
</Link>
</Typography>
</Grid>
{errors.submit && (
<Grid item xs={12}>
<FormHelperText error>{errors.submit}</FormHelperText>
</Grid>
)}
<Grid item xs={12}>
<AnimateButton>
<Button
disableElevation
disabled={isSubmitting}
fullWidth
size="large"
type="submit"
variant="contained"
color="primary"
>
Create Account
</Button>
</AnimateButton>
</Grid>
<Grid item xs={12}>
<Divider>
<Typography variant="caption">Sign up with</Typography>
</Divider>
</Grid>
<Grid item xs={12}>
<FirebaseSocial />
</Grid>
</Grid>
</form>
)}
</Formik>
</>
);
};
export default AuthRegister;

View file

@ -0,0 +1,66 @@
// material-ui
import { useTheme } from '@mui/material/styles';
import { useMediaQuery, Button, Stack } from '@mui/material';
// assets
import Google from '../../../assets/images/icons/google.svg';
import Twitter from '../../../assets/images/icons/twitter.svg';
import Facebook from '../../../assets/images/icons/facebook.svg';
// ==============================|| FIREBASE - SOCIAL BUTTON ||============================== //
const FirebaseSocial = () => {
const theme = useTheme();
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
const googleHandler = async () => {
// login || singup
};
const twitterHandler = async () => {
// login || singup
};
const facebookHandler = async () => {
// login || singup
};
return (
<Stack
direction="row"
spacing={matchDownSM ? 1 : 2}
justifyContent={matchDownSM ? 'space-around' : 'space-between'}
sx={{ '& .MuiButton-startIcon': { mr: matchDownSM ? 0 : 1, ml: matchDownSM ? 0 : -0.5 } }}
>
<Button
variant="outlined"
color="secondary"
fullWidth={!matchDownSM}
startIcon={<img src={Google} alt="Google" />}
onClick={googleHandler}
>
{!matchDownSM && 'Google'}
</Button>
<Button
variant="outlined"
color="secondary"
fullWidth={!matchDownSM}
startIcon={<img src={Twitter} alt="Twitter" />}
onClick={twitterHandler}
>
{!matchDownSM && 'Twitter'}
</Button>
<Button
variant="outlined"
color="secondary"
fullWidth={!matchDownSM}
startIcon={<img src={Facebook} alt="Facebook" />}
onClick={facebookHandler}
>
{!matchDownSM && 'Facebook'}
</Button>
</Stack>
);
};
export default FirebaseSocial;

View file

@ -0,0 +1,24 @@
// material-ui
import { styled } from '@mui/material/styles';
// project import
import ComponentSkeleton from './ComponentSkeleton';
import MainCard from '../../components/MainCard';
// styles
const IFrameWrapper = styled('iframe')(() => ({
height: 'calc(100vh - 210px)',
border: 'none'
}));
// ============================|| ANT ICONS ||============================ //
const AntIcons = () => (
<ComponentSkeleton>
<MainCard title="Ant Icons">
<IFrameWrapper title="Ant Icon" width="100%" src="https://ant.design/components/icon/" />
</MainCard>
</ComponentSkeleton>
);
export default AntIcons;

View file

@ -0,0 +1,141 @@
import PropTypes from 'prop-types';
// material-ui
import { Box, Card, Grid, Stack, Typography } from '@mui/material';
// project import
import MainCard from '../../components/MainCard';
import ComponentSkeleton from './ComponentSkeleton';
// ===============================|| COLOR BOX ||=============================== //
function ColorBox({ bgcolor, title, data, dark, main }) {
return (
<>
<Card sx={{ '&.MuiPaper-root': { borderRadius: '0px' } }}>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
py: 2.5,
bgcolor,
color: dark ? 'grey.800' : '#ffffff',
border: main ? '1px dashed' : '1px solid transparent'
}}
>
{title && (
<Grid container justifyContent="space-around" alignItems="center">
<Grid item>
{data && (
<Stack spacing={0.75} alignItems="center">
<Typography variant="subtitle2">{data.label}</Typography>
<Typography variant="subtitle1">{data.color}</Typography>
</Stack>
)}
</Grid>
<Grid item>
<Typography variant="subtitle1" color="inherit">
{title}
</Typography>
</Grid>
</Grid>
)}
</Box>
</Card>
</>
);
}
ColorBox.propTypes = {
bgcolor: PropTypes.string,
title: PropTypes.string,
data: PropTypes.object.isRequired,
dark: PropTypes.bool,
main: PropTypes.bool
};
// ===============================|| COMPONENT - COLOR ||=============================== //
const ComponentColor = () => (
<ComponentSkeleton>
<Grid container spacing={3}>
<Grid item xs={12} sm={6} md={4}>
<MainCard title="Primary Color" codeHighlight>
<Stack>
<ColorBox bgcolor="primary.lighter" data={{ label: 'Blue-1', color: '#e6f7ff' }} title="primary.lighter" dark />
<ColorBox bgcolor="primary.100" data={{ label: 'Blue-2', color: '#bae7ff' }} title="primary[100]" dark />
<ColorBox bgcolor="primary.200" data={{ label: 'Blue-3', color: '#91d5ff' }} title="primary[200]" dark />
<ColorBox bgcolor="primary.light" data={{ label: 'Blue-4', color: '#69c0ff' }} title="primary.light" dark />
<ColorBox bgcolor="primary.400" data={{ label: 'Blue-5', color: '#40a9ff' }} title="primary[400]" />
<ColorBox bgcolor="primary.main" data={{ label: 'Blue-6', color: '#1890ff' }} title="primary.main" main />
<ColorBox bgcolor="primary.dark" data={{ label: 'Blue-7', color: '#096dd9' }} title="primary.dark" />
<ColorBox bgcolor="primary.700" data={{ label: 'Blue-8', color: '#0050b3' }} title="primary[700]" />
<ColorBox bgcolor="primary.darker" data={{ label: 'Blue-9', color: '#003a8c' }} title="primary.darker" />
<ColorBox bgcolor="primary.900" data={{ label: 'Blue-10', color: '#002766' }} title="primary.900" />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<MainCard title="Secondary Color" codeHighlight>
<Stack>
<ColorBox bgcolor="secondary.lighter" data={{ label: 'Grey-1', color: '#fafafa' }} title="secondary.lighter" dark />
<ColorBox bgcolor="secondary.100" data={{ label: 'Grey-2', color: '#f5f5f5' }} title="secondary[100]" dark />
<ColorBox bgcolor="secondary.200" data={{ label: 'Grey-3', color: '#f0f0f0' }} title="secondary[200]" dark />
<ColorBox bgcolor="secondary.light" data={{ label: 'Grey-4', color: '#d9d9d9' }} title="secondary.light" dark />
<ColorBox bgcolor="secondary.400" data={{ label: 'Grey-5', color: '#bfbfbf' }} title="secondary[400]" dark />
<ColorBox bgcolor="secondary.main" data={{ label: 'Grey-6', color: '#8c8c8c' }} title="secondary.main" main />
<ColorBox bgcolor="secondary.600" data={{ label: 'Grey-7', color: '#595959' }} title="secondary.600" />
<ColorBox bgcolor="secondary.dark" data={{ label: 'Grey-8', color: '#262626' }} title="secondary.dark" />
<ColorBox bgcolor="secondary.800" data={{ label: 'Grey-9', color: '#141414' }} title="secondary[800]" />
<ColorBox bgcolor="secondary.darker" data={{ label: 'Grey-10', color: '#000000' }} title="secondary.darker" />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<MainCard title="Other Color" codeHighlight>
<Stack>
<ColorBox bgcolor="secondary.A100" data={{ label: 'Grey-A1', color: '#ffffff' }} title="secondary.A100" dark />
<ColorBox bgcolor="secondary.A200" data={{ label: 'Grey-A2', color: '#434343' }} title="secondary.A200" />
<ColorBox bgcolor="secondary.A300" data={{ label: 'Grey-A3', color: '#1f1f1f' }} title="secondary.A300" />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<MainCard title="Success Color" codeHighlight>
<Stack>
<ColorBox bgcolor="success.lighter" data={{ label: 'Green-1', color: '#f6ffed' }} title="success.lighter" dark />
<ColorBox bgcolor="success.light" data={{ label: 'Green-4', color: '#95de64' }} title="success.light" dark />
<ColorBox bgcolor="success.main" data={{ label: 'Green-6', color: '#52c41a' }} title="success.main" main />
<ColorBox bgcolor="success.dark" data={{ label: 'Green-8', color: '#237804' }} title="success.dark" />
<ColorBox bgcolor="success.darker" data={{ label: 'Green-10', color: '#092b00' }} title="success.darker" />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<MainCard title="Error Color" codeHighlight>
<Stack>
<ColorBox bgcolor="error.lighter" data={{ label: 'Red-1', color: '#fff1f0' }} title="error.lighter" dark />
<ColorBox bgcolor="error.light" data={{ label: 'Red-4', color: '#ff7875' }} title="error.light" dark />
<ColorBox bgcolor="error.main" data={{ label: 'Red-6', color: '#f5222d' }} title="error.main" main />
<ColorBox bgcolor="error.dark" data={{ label: 'Red-8', color: '#a8071a' }} title="error.dark" />
<ColorBox bgcolor="error.darker" data={{ label: 'Red-10', color: '#5c0011' }} title="error.darker" />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<MainCard title="Warning Color" codeHighlight>
<Stack>
<ColorBox bgcolor="warning.lighter" data={{ label: 'Gold-1', color: '#fffbe6' }} title="warning.lighter" dark />
<ColorBox bgcolor="warning.light" data={{ label: 'Gold-4', color: '#ffd666' }} title="warning.light" dark />
<ColorBox bgcolor="warning.main" data={{ label: 'Gold-6', color: '#faad14' }} title="warning.main" main />
<ColorBox bgcolor="warning.dark" data={{ label: 'Gold-8', color: '#ad6800' }} title="warning.dark" />
<ColorBox bgcolor="warning.darker" data={{ label: 'Gold-10', color: '#613400' }} title="warning.darker" />
</Stack>
</MainCard>
</Grid>
</Grid>
</ComponentSkeleton>
);
export default ComponentColor;

View file

@ -0,0 +1,59 @@
import PropTypes from 'prop-types';
import { useEffect, useState } from 'react';
// material-ui
import { Grid, Skeleton, Stack } from '@mui/material';
// project import
import MainCard from '../../components/MainCard';
// ===============================|| COMPONENT - SKELETON ||=============================== //
const ComponentSkeleton = ({ children }) => {
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
const skeletonCard = (
<MainCard
title={<Skeleton sx={{ width: { xs: 120, md: 180 } }} />}
secondary={<Skeleton animation="wave" variant="circular" width={24} height={24} />}
>
<Stack spacing={1}>
<Skeleton />
<Skeleton sx={{ height: 64 }} animation="wave" variant="rectangular" />
<Skeleton />
<Skeleton />
</Stack>
</MainCard>
);
return (
<>
{isLoading && (
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
{skeletonCard}
</Grid>
<Grid item xs={12} md={6}>
{skeletonCard}
</Grid>
<Grid item xs={12} md={6}>
{skeletonCard}
</Grid>
<Grid item xs={12} md={6}>
{skeletonCard}
</Grid>
</Grid>
)}
{!isLoading && children}
</>
);
};
ComponentSkeleton.propTypes = {
children: PropTypes.node
};
export default ComponentSkeleton;

View file

@ -0,0 +1,152 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Grid, Stack, Typography } from '@mui/material';
// project import
import MainCard from '../../components/MainCard';
import ComponentSkeleton from './ComponentSkeleton';
// ===============================|| SHADOW BOX ||=============================== //
function ShadowBox({ shadow }) {
return (
<MainCard border={false} sx={{ boxShadow: shadow }}>
<Stack spacing={1} justifyContent="center" alignItems="center">
<Typography variant="h6">boxShadow</Typography>
<Typography variant="subtitle1">{shadow}</Typography>
</Stack>
</MainCard>
);
}
ShadowBox.propTypes = {
shadow: PropTypes.string.isRequired
};
// ===============================|| CUSTOM - SHADOW BOX ||=============================== //
function CustomShadowBox({ shadow, label, color, bgcolor }) {
return (
<MainCard border={false} sx={{ bgcolor: bgcolor || 'inherit', boxShadow: shadow }}>
<Stack spacing={1} justifyContent="center" alignItems="center">
<Typography variant="subtitle1" color={color}>
{label}
</Typography>
</Stack>
</MainCard>
);
}
CustomShadowBox.propTypes = {
shadow: PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
bgcolor: PropTypes.string.isRequired
};
// ============================|| COMPONENT - SHADOW ||============================ //
const ComponentShadow = () => {
const theme = useTheme();
return (
<ComponentSkeleton>
<Grid container spacing={3}>
<Grid item xs={12}>
<MainCard title="Basic Shadow" codeHighlight>
<Grid container spacing={3}>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="0" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="1" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="2" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="3" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="4" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="5" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="6" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="7" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="8" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="9" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="10" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="11" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="12" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="13" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="14" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="15" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="16" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="17" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="18" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="19" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="20" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="21" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="22" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="23" />
</Grid>
<Grid item xs={6} sm={4} md={3} lg={2}>
<ShadowBox shadow="24" />
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12}>
<MainCard title="Custom Shadow" codeHighlight>
<Grid container spacing={3}>
<Grid item xs={6} sm={4} md={3} lg={2}>
<CustomShadowBox shadow={theme.customShadows.z1} label="z1" color="inherit" />
</Grid>
</Grid>
</MainCard>
</Grid>
</Grid>
</ComponentSkeleton>
);
};
export default ComponentShadow;

View file

@ -0,0 +1,263 @@
// material-ui
import { Breadcrumbs, Divider, Grid, Link, Stack, Typography } from '@mui/material';
// project import
import ComponentSkeleton from './ComponentSkeleton';
import MainCard from '../../components/MainCard';
// ==============================|| COMPONENTS - TYPOGRAPHY ||============================== //
const ComponentTypography = () => (
<ComponentSkeleton>
<Grid container spacing={3}>
<Grid item xs={12} lg={6}>
<Stack spacing={3}>
<MainCard title="Basic" codeHighlight>
<Stack spacing={0.75} sx={{ mt: -1.5 }}>
<Typography variant="h1">Inter</Typography>
<Typography variant="h5">Font Family</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Regular</Typography>
<Typography variant="h6">Medium</Typography>
<Typography variant="h6">Bold</Typography>
</Breadcrumbs>
</Stack>
</MainCard>
<MainCard title="Heading" codeHighlight>
<Stack spacing={2}>
<Typography variant="h1">H1 Heading</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 38px</Typography>
<Typography variant="h6">Weight: Bold</Typography>
<Typography variant="h6">Line Height: 46px</Typography>
</Breadcrumbs>
<Divider />
<Typography variant="h2">H2 Heading</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 30px</Typography>
<Typography variant="h6">Weight: Bold</Typography>
<Typography variant="h6">Line Height: 38px</Typography>
</Breadcrumbs>
<Divider />
<Typography variant="h3">H3 Heading</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 24px</Typography>
<Typography variant="h6">Weight: Regular & Bold</Typography>
<Typography variant="h6">Line Height: 32px</Typography>
</Breadcrumbs>
<Divider />
<Typography variant="h4">H4 Heading</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 20px</Typography>
<Typography variant="h6">Weight: Bold</Typography>
<Typography variant="h6">Line Height: 28px</Typography>
</Breadcrumbs>
<Divider />
<Typography variant="h5">H5 Heading</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 16px</Typography>
<Typography variant="h6">Weight: Regular & Medium & Bold</Typography>
<Typography variant="h6">Line Height: 24px</Typography>
</Breadcrumbs>
<Divider />
<Typography variant="h6">H6 Heading / Subheading</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 14px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 22px</Typography>
</Breadcrumbs>
</Stack>
</MainCard>
<MainCard title="Body 1" codeHighlight>
<>
<Typography variant="body1" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 14px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 22px</Typography>
</Breadcrumbs>
</>
</MainCard>
<MainCard title="Body 2" codeHighlight>
<>
<Typography variant="body2" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 12px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 20px</Typography>
</Breadcrumbs>
</>
</MainCard>
<MainCard title="Subtitle 1" codeHighlight>
<>
<Typography variant="subtitle1" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 14px</Typography>
<Typography variant="h6">Weight: Medium</Typography>
<Typography variant="h6">Line Height: 22px</Typography>
</Breadcrumbs>
</>
</MainCard>
<MainCard title="Subtitle 2" codeHighlight>
<>
<Typography variant="subtitle2" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 12px</Typography>
<Typography variant="h6">Weight: Medium</Typography>
<Typography variant="h6">Line Height: 20px</Typography>
</Breadcrumbs>
</>
</MainCard>
<MainCard title="Caption" codeHighlight>
<Stack spacing={1}>
<Typography variant="caption">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 12px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 20px</Typography>
</Breadcrumbs>
</Stack>
</MainCard>
</Stack>
</Grid>
<Grid item xs={12} lg={6}>
<Stack spacing={3}>
<MainCard title="Alignment" codeHighlight>
<>
<Typography variant="body2" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</Typography>
<Typography variant="body2" textAlign="center" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</Typography>
<Typography variant="body2" textAlign="right">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</Typography>
</>
</MainCard>
<MainCard title="Gutter Bottom" codeHighlight>
<>
<Typography variant="body1" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Typography variant="body2" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 12px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 20px</Typography>
</Breadcrumbs>
</>
</MainCard>
<MainCard title="Overline" codeHighlight>
<Stack spacing={1.5}>
<Typography variant="overline">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 12px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 20px</Typography>
</Breadcrumbs>
</Stack>
</MainCard>
<MainCard title="Link" codeHighlight>
<Stack spacing={1.5}>
<Link href="#">www.mantis.com</Link>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 12px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 20px</Typography>
</Breadcrumbs>
</Stack>
</MainCard>
<MainCard title="Colors" codeHighlight>
<>
<Typography variant="h6" color="textPrimary" gutterBottom>
This is textPrimary text color.
</Typography>
<Typography variant="h6" color="textSecondary" gutterBottom>
This is textSecondary text color.
</Typography>
<Typography variant="h6" color="primary" gutterBottom>
This is primary text color.
</Typography>
<Typography variant="h6" color="secondary" gutterBottom>
This is secondary text color.
</Typography>
<Typography variant="h6" color="success" gutterBottom>
This is success text color.
</Typography>
<Typography variant="h6" sx={{ color: 'warning.main' }} gutterBottom>
This is warning text color.
</Typography>
<Typography variant="h6" color="error" gutterBottom>
This is error text color.
</Typography>
</>
</MainCard>
<MainCard title="Paragraph" codeHighlight>
<>
<Typography variant="body1" gutterBottom>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 14px</Typography>
<Typography variant="h6">Weight: Regular</Typography>
<Typography variant="h6">Line Height: 22px</Typography>
</Breadcrumbs>
</>
</MainCard>
<MainCard title="Font Style" codeHighlight>
<>
<Typography variant="body1" gutterBottom sx={{ fontStyle: 'italic' }}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Typography variant="subtitle1" gutterBottom sx={{ fontStyle: 'italic' }}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</Typography>
<Breadcrumbs aria-label="breadcrumb">
<Typography variant="h6">Size: 14px</Typography>
<Typography variant="h6">Weight: Italic Regular & Italic Bold</Typography>
<Typography variant="h6">Line Height: 22px</Typography>
</Breadcrumbs>
</>
</MainCard>
</Stack>
</Grid>
</Grid>
</ComponentSkeleton>
);
export default ComponentTypography;

View file

@ -0,0 +1,121 @@
import PropTypes from 'prop-types';
import { useState, useEffect } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
// chart options
const areaChartOptions = {
chart: {
height: 450,
type: 'area',
toolbar: {
show: false
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 2
},
grid: {
strokeDashArray: 0
}
};
// ==============================|| INCOME AREA CHART ||============================== //
const IncomeAreaChart = ({ slot }) => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const line = theme.palette.divider;
const [options, setOptions] = useState(areaChartOptions);
useEffect(() => {
setOptions((prevState) => ({
...prevState,
colors: [theme.palette.primary.main, theme.palette.primary[700]],
xaxis: {
categories:
slot === 'month'
? ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
labels: {
style: {
colors: [
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary
]
}
},
axisBorder: {
show: true,
color: line
},
tickAmount: slot === 'month' ? 11 : 7
},
yaxis: {
labels: {
style: {
colors: [secondary]
}
}
},
grid: {
borderColor: line
},
tooltip: {
theme: 'light'
}
}));
}, [primary, secondary, line, theme, slot]);
const [series, setSeries] = useState([
{
name: 'Page Views',
data: [0, 86, 28, 115, 48, 210, 136]
},
{
name: 'Sessions',
data: [0, 43, 14, 56, 24, 105, 68]
}
]);
useEffect(() => {
setSeries([
{
name: 'Page Views',
data: slot === 'month' ? [76, 85, 101, 98, 87, 105, 91, 114, 94, 86, 115, 35] : [31, 40, 28, 51, 42, 109, 100]
},
{
name: 'Sessions',
data: slot === 'month' ? [110, 60, 150, 35, 60, 36, 26, 45, 65, 52, 53, 41] : [11, 32, 45, 32, 34, 52, 41]
}
]);
}, [slot]);
return <ReactApexChart options={options} series={series} type="area" height={450} />;
};
IncomeAreaChart.propTypes = {
slot: PropTypes.string
};
export default IncomeAreaChart;

View file

@ -0,0 +1,85 @@
import { useEffect, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
// chart options
const barChartOptions = {
chart: {
type: 'bar',
height: 365,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
columnWidth: '45%',
borderRadius: 4
}
},
dataLabels: {
enabled: false
},
xaxis: {
categories: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
show: false
},
grid: {
show: false
}
};
// ==============================|| MONTHLY BAR CHART ||============================== //
const MonthlyBarChart = () => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const info = theme.palette.info.light;
const [series] = useState([
{
data: [80, 95, 70, 42, 65, 55, 78]
}
]);
const [options, setOptions] = useState(barChartOptions);
useEffect(() => {
setOptions((prevState) => ({
...prevState,
colors: [info],
xaxis: {
labels: {
style: {
colors: [secondary, secondary, secondary, secondary, secondary, secondary, secondary]
}
}
},
tooltip: {
theme: 'light'
}
}));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [primary, info, secondary]);
return (
<div id="chart">
<ReactApexChart options={options} series={series} type="bar" height={365} />
</div>
);
};
export default MonthlyBarChart;

View file

@ -0,0 +1,224 @@
import PropTypes from 'prop-types';
import { useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
// material-ui
import { Box, Link, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material';
// third-party
import NumberFormat from 'react-number-format';
// project import
import Dot from '../../components/@extended/Dot';
function createData(trackingNo, name, fat, carbs, protein) {
return { trackingNo, name, fat, carbs, protein };
}
const rows = [
createData(84564564, 'Camera Lens', 40, 2, 40570),
createData(98764564, 'Laptop', 300, 0, 180139),
createData(98756325, 'Mobile', 355, 1, 90989),
createData(98652366, 'Handset', 50, 1, 10239),
createData(13286564, 'Computer Accessories', 100, 1, 83348),
createData(86739658, 'TV', 99, 0, 410780),
createData(13256498, 'Keyboard', 125, 2, 70999),
createData(98753263, 'Mouse', 89, 2, 10570),
createData(98753275, 'Desktop', 185, 1, 98063),
createData(98753291, 'Chair', 100, 0, 14001)
];
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === 'desc' ? (a, b) => descendingComparator(a, b, orderBy) : (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) {
return order;
}
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
// ==============================|| ORDER TABLE - HEADER CELL ||============================== //
const headCells = [
{
id: 'trackingNo',
align: 'left',
disablePadding: false,
label: 'Tracking No.'
},
{
id: 'name',
align: 'left',
disablePadding: true,
label: 'Product Name'
},
{
id: 'fat',
align: 'right',
disablePadding: false,
label: 'Total Order'
},
{
id: 'carbs',
align: 'left',
disablePadding: false,
label: 'Status'
},
{
id: 'protein',
align: 'right',
disablePadding: false,
label: 'Total Amount'
}
];
// ==============================|| ORDER TABLE - HEADER ||============================== //
function OrderTableHead({ order, orderBy }) {
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.align}
padding={headCell.disablePadding ? 'none' : 'normal'}
sortDirection={orderBy === headCell.id ? order : false}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
OrderTableHead.propTypes = {
order: PropTypes.string,
orderBy: PropTypes.string
};
// ==============================|| ORDER TABLE - STATUS ||============================== //
const OrderStatus = ({ status }) => {
let color;
let title;
switch (status) {
case 0:
color = 'warning';
title = 'Pending';
break;
case 1:
color = 'success';
title = 'Approved';
break;
case 2:
color = 'error';
title = 'Rejected';
break;
default:
color = 'primary';
title = 'None';
}
return (
<Stack direction="row" spacing={1} alignItems="center">
<Dot color={color} />
<Typography>{title}</Typography>
</Stack>
);
};
OrderStatus.propTypes = {
status: PropTypes.number
};
// ==============================|| ORDER TABLE ||============================== //
export default function OrderTable() {
const [order] = useState('asc');
const [orderBy] = useState('trackingNo');
const [selected] = useState([]);
const isSelected = (trackingNo) => selected.indexOf(trackingNo) !== -1;
return (
<Box>
<TableContainer
sx={{
width: '100%',
overflowX: 'auto',
position: 'relative',
display: 'block',
maxWidth: '100%',
'& td, & th': { whiteSpace: 'nowrap' }
}}
>
<Table
aria-labelledby="tableTitle"
sx={{
'& .MuiTableCell-root:first-child': {
pl: 2
},
'& .MuiTableCell-root:last-child': {
pr: 3
}
}}
>
<OrderTableHead order={order} orderBy={orderBy} />
<TableBody>
{stableSort(rows, getComparator(order, orderBy)).map((row, index) => {
const isItemSelected = isSelected(row.trackingNo);
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
role="checkbox"
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
aria-checked={isItemSelected}
tabIndex={-1}
key={row.trackingNo}
selected={isItemSelected}
>
<TableCell component="th" id={labelId} scope="row" align="left">
<Link color="secondary" component={RouterLink} to="">
{row.trackingNo}
</Link>
</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="left">
<OrderStatus status={row.carbs} />
</TableCell>
<TableCell align="right">
<NumberFormat value={row.protein} displayType="text" thousandSeparator prefix="$" />
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Box>
);
}

View file

@ -0,0 +1,105 @@
import { useEffect, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
// chart options
const areaChartOptions = {
chart: {
height: 340,
type: 'line',
toolbar: {
show: false
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 1.5
},
grid: {
strokeDashArray: 4
},
xaxis: {
type: 'datetime',
categories: [
'2018-05-19T00:00:00.000Z',
'2018-06-19T00:00:00.000Z',
'2018-07-19T01:30:00.000Z',
'2018-08-19T02:30:00.000Z',
'2018-09-19T03:30:00.000Z',
'2018-10-19T04:30:00.000Z',
'2018-11-19T05:30:00.000Z',
'2018-12-19T06:30:00.000Z'
],
labels: {
format: 'MMM'
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
show: false
},
tooltip: {
x: {
format: 'MM'
}
}
};
// ==============================|| REPORT AREA CHART ||============================== //
const ReportAreaChart = () => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const line = theme.palette.divider;
const [options, setOptions] = useState(areaChartOptions);
useEffect(() => {
setOptions((prevState) => ({
...prevState,
colors: [theme.palette.warning.main],
xaxis: {
labels: {
style: {
colors: [secondary, secondary, secondary, secondary, secondary, secondary, secondary, secondary]
}
}
},
grid: {
borderColor: line
},
tooltip: {
theme: 'light'
},
legend: {
labels: {
colors: 'grey.500'
}
}
}));
}, [primary, secondary, line, theme]);
const [series] = useState([
{
name: 'Series 1',
data: [58, 115, 28, 83, 63, 75, 35, 55]
}
]);
return <ReactApexChart options={options} series={series} type="line" height={345} />;
};
export default ReportAreaChart;

View file

@ -0,0 +1,148 @@
import { useEffect, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
// chart options
const columnChartOptions = {
chart: {
type: 'bar',
height: 430,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
columnWidth: '30%',
borderRadius: 4
}
},
dataLabels: {
enabled: false
},
stroke: {
show: true,
width: 8,
colors: ['transparent']
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
},
yaxis: {
title: {
text: '$ (thousands)'
}
},
fill: {
opacity: 1
},
tooltip: {
y: {
formatter(val) {
return `$ ${val} thousands`;
}
}
},
legend: {
show: true,
fontFamily: `'Public Sans', sans-serif`,
offsetX: 10,
offsetY: 10,
labels: {
useSeriesColors: false
},
markers: {
width: 16,
height: 16,
radius: '50%',
offsexX: 2,
offsexY: 2
},
itemMargin: {
horizontal: 15,
vertical: 50
}
},
responsive: [
{
breakpoint: 600,
options: {
yaxis: {
show: false
}
}
}
]
};
// ==============================|| SALES COLUMN CHART ||============================== //
const SalesColumnChart = () => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const line = theme.palette.divider;
const warning = theme.palette.warning.main;
const primaryMain = theme.palette.primary.main;
const successDark = theme.palette.success.dark;
const [series] = useState([
{
name: 'Net Profit',
data: [180, 90, 135, 114, 120, 145]
},
{
name: 'Revenue',
data: [120, 45, 78, 150, 168, 99]
}
]);
const [options, setOptions] = useState(columnChartOptions);
useEffect(() => {
setOptions((prevState) => ({
...prevState,
colors: [warning, primaryMain],
xaxis: {
labels: {
style: {
colors: [secondary, secondary, secondary, secondary, secondary, secondary]
}
}
},
yaxis: {
labels: {
style: {
colors: [secondary]
}
}
},
grid: {
borderColor: line
},
tooltip: {
theme: 'light'
},
legend: {
position: 'top',
horizontalAlign: 'right',
labels: {
colors: 'grey.500'
}
}
}));
}, [primary, secondary, line, warning, primaryMain, successDark]);
return (
<div id="chart">
<ReactApexChart options={options} series={series} type="bar" height={430} />
</div>
);
};
export default SalesColumnChart;

View file

@ -0,0 +1,358 @@
import { useState } from 'react';
// material-ui
import {
Avatar,
AvatarGroup,
Box,
Button,
Grid,
List,
ListItemAvatar,
ListItemButton,
ListItemSecondaryAction,
ListItemText,
MenuItem,
Stack,
TextField,
Typography,
Alert
} from '@mui/material';
// project import
import OrdersTable from './OrdersTable';
import IncomeAreaChart from './IncomeAreaChart';
import MonthlyBarChart from './MonthlyBarChart';
import ReportAreaChart from './ReportAreaChart';
import SalesColumnChart from './SalesColumnChart';
import MainCard from '../../components/MainCard';
import AnalyticEcommerce from '../../components/cards/statistics/AnalyticEcommerce';
// assets
import { GiftOutlined, MessageOutlined, SettingOutlined } from '@ant-design/icons';
import avatar1 from '../../assets/images/users/avatar-1.png';
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 isLoggedIn from '../../isLoggedIn';
// avatar style
const avatarSX = {
width: 36,
height: 36,
fontSize: '1rem'
};
// action style
const actionSX = {
mt: 0.75,
ml: 1,
top: 'auto',
right: 'auto',
alignSelf: 'flex-start',
transform: 'none'
};
// sales report status
const status = [
{
value: 'today',
label: 'Today'
},
{
value: 'month',
label: 'This Month'
},
{
value: 'year',
label: 'This Year'
}
];
// ==============================|| DASHBOARD - DEFAULT ||============================== //
const DashboardDefault = () => {
const [value, setValue] = useState('today');
const [slot, setSlot] = useState('week');
isLoggedIn();
return (
<>
<div>
<Alert severity="info">Dashboard implementation currently in progress! If you want to voice your opinion on where Cosmos is going, please join us on Discord!</Alert>
</div>
<div style={{filter: 'blur(10px)', marginTop: '30px', pointerEvents: 'none'}}>
<Grid container rowSpacing={4.5} columnSpacing={2.75}>
{/* row 1 */}
<Grid item xs={12} sx={{ mb: -2.25 }}>
<Typography variant="h5">Dashboard</Typography>
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title="Total Page Views" count="4,42,236" percentage={59.3} extra="35,000" />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title="Total Users" count="78,250" percentage={70.5} extra="8,900" />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title="Total Order" count="18,800" percentage={27.4} isLoss color="warning" extra="1,943" />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title="Total Sales" count="$35,078" percentage={27.4} isLoss color="warning" extra="$20,395" />
</Grid>
<Grid item md={8} sx={{ display: { sm: 'none', md: 'block', lg: 'none' } }} />
{/* row 2 */}
<Grid item xs={12} md={7} lg={8}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">Unique Visitor</Typography>
</Grid>
<Grid item>
<Stack direction="row" alignItems="center" spacing={0}>
<Button
size="small"
onClick={() => setSlot('month')}
color={slot === 'month' ? 'primary' : 'secondary'}
variant={slot === 'month' ? 'outlined' : 'text'}
>
Month
</Button>
<Button
size="small"
onClick={() => setSlot('week')}
color={slot === 'week' ? 'primary' : 'secondary'}
variant={slot === 'week' ? 'outlined' : 'text'}
>
Week
</Button>
</Stack>
</Grid>
</Grid>
<MainCard content={false} sx={{ mt: 1.5 }}>
<Box sx={{ pt: 1, pr: 2 }}>
<IncomeAreaChart slot={slot} />
</Box>
</MainCard>
</Grid>
<Grid item xs={12} md={5} lg={4}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">Income Overview</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<Box sx={{ p: 3, pb: 0 }}>
<Stack spacing={2}>
<Typography variant="h6" color="textSecondary">
This Week Statistics
</Typography>
<Typography variant="h3">$7,650</Typography>
</Stack>
</Box>
<MonthlyBarChart />
</MainCard>
</Grid>
{/* row 3 */}
<Grid item xs={12} md={7} lg={8}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">Recent Orders</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<OrdersTable />
</MainCard>
</Grid>
<Grid item xs={12} md={5} lg={4}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">Analytics Report</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<List sx={{ p: 0, '& .MuiListItemButton-root': { py: 2 } }}>
<ListItemButton divider>
<ListItemText primary="Company Finance Growth" />
<Typography variant="h5">+45.14%</Typography>
</ListItemButton>
<ListItemButton divider>
<ListItemText primary="Company Expenses Ratio" />
<Typography variant="h5">0.58%</Typography>
</ListItemButton>
<ListItemButton>
<ListItemText primary="Business Risk Cases" />
<Typography variant="h5">Low</Typography>
</ListItemButton>
</List>
<ReportAreaChart />
</MainCard>
</Grid>
{/* row 4 */}
<Grid item xs={12} md={7} lg={8}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">Sales Report</Typography>
</Grid>
<Grid item>
<TextField
id="standard-select-currency"
size="small"
select
value={value}
onChange={(e) => setValue(e.target.value)}
sx={{ '& .MuiInputBase-input': { py: 0.5, fontSize: '0.875rem' } }}
>
{status.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
</Grid>
</Grid>
<MainCard sx={{ mt: 1.75 }}>
<Stack spacing={1.5} sx={{ mb: -12 }}>
<Typography variant="h6" color="secondary">
Net Profit
</Typography>
<Typography variant="h4">$1560</Typography>
</Stack>
<SalesColumnChart />
</MainCard>
</Grid>
<Grid item xs={12} md={5} lg={4}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">Transaction History</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<List
component="nav"
sx={{
px: 0,
py: 0,
'& .MuiListItemButton-root': {
py: 1.5,
'& .MuiAvatar-root': avatarSX,
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
}
}}
>
<ListItemButton divider>
<ListItemAvatar>
<Avatar
sx={{
color: 'success.main',
bgcolor: 'success.lighter'
}}
>
<GiftOutlined />
</Avatar>
</ListItemAvatar>
<ListItemText primary={<Typography variant="subtitle1">Order #002434</Typography>} secondary="Today, 2:00 AM" />
<ListItemSecondaryAction>
<Stack alignItems="flex-end">
<Typography variant="subtitle1" noWrap>
+ $1,430
</Typography>
<Typography variant="h6" color="secondary" noWrap>
78%
</Typography>
</Stack>
</ListItemSecondaryAction>
</ListItemButton>
<ListItemButton divider>
<ListItemAvatar>
<Avatar
sx={{
color: 'primary.main',
bgcolor: 'primary.lighter'
}}
>
<MessageOutlined />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={<Typography variant="subtitle1">Order #984947</Typography>}
secondary="5 August, 1:45 PM"
/>
<ListItemSecondaryAction>
<Stack alignItems="flex-end">
<Typography variant="subtitle1" noWrap>
+ $302
</Typography>
<Typography variant="h6" color="secondary" noWrap>
8%
</Typography>
</Stack>
</ListItemSecondaryAction>
</ListItemButton>
<ListItemButton>
<ListItemAvatar>
<Avatar
sx={{
color: 'error.main',
bgcolor: 'error.lighter'
}}
>
<SettingOutlined />
</Avatar>
</ListItemAvatar>
<ListItemText primary={<Typography variant="subtitle1">Order #988784</Typography>} secondary="7 hours ago" />
<ListItemSecondaryAction>
<Stack alignItems="flex-end">
<Typography variant="subtitle1" noWrap>
+ $682
</Typography>
<Typography variant="h6" color="secondary" noWrap>
16%
</Typography>
</Stack>
</ListItemSecondaryAction>
</ListItemButton>
</List>
</MainCard>
<MainCard sx={{ mt: 2 }}>
<Stack spacing={3}>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Stack>
<Typography variant="h5" noWrap>
Help & Support Chat
</Typography>
<Typography variant="caption" color="secondary" noWrap>
Typical replay within 5 min
</Typography>
</Stack>
</Grid>
<Grid item>
<AvatarGroup sx={{ '& .MuiAvatar-root': { width: 32, height: 32 } }}>
<Avatar alt="Remy Sharp" src={avatar1} />
<Avatar alt="Travis Howard" src={avatar2} />
<Avatar alt="Cindy Baker" src={avatar3} />
<Avatar alt="Agnes Walker" src={avatar4} />
</AvatarGroup>
</Grid>
</Grid>
<Button size="small" variant="contained" sx={{ textTransform: 'capitalize' }}>
Need Help?
</Button>
</Stack>
</MainCard>
</Grid>
</Grid>
</div>
</>
);
};
export default DashboardDefault;

View file

@ -0,0 +1,20 @@
// material-ui
import { Typography } from '@mui/material';
// project import
import MainCard from '../../components/MainCard';
// ==============================|| SAMPLE PAGE ||============================== //
const SamplePage = () => (
<MainCard title="Sample Card">
<Typography variant="body2">
Lorem ipsum dolor sit amen, consenter nipissing eli, sed do elusion tempos incident ut laborers et doolie magna alissa. Ut enif
ad minim venice, quin nostrum exercitation illampu laborings nisi ut liquid ex ea commons construal. Duos aube grue dolor in
reprehended in voltage veil esse colum doolie eu fujian bulla parian. Exceptive sin ocean cuspidate non president, sunk in culpa
qui officiate descent molls anim id est labours.
</Typography>
</MainCard>
);
export default SamplePage;

View file

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View file

@ -0,0 +1,13 @@
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View file

@ -0,0 +1,28 @@
import { lazy } from 'react';
// project import
import Loadable from '../components/Loadable';
import MinimalLayout from '../layout/MinimalLayout';
// render - login
const AuthLogin = Loadable(lazy(() => import('../pages/authentication/Login')));
const AuthRegister = Loadable(lazy(() => import('../pages/authentication/Register')));
// ==============================|| AUTH ROUTING ||============================== //
const LoginRoutes = {
path: '/',
element: <MinimalLayout />,
children: [
{
path: 'login',
element: <AuthLogin />
},
{
path: 'register',
element: <AuthRegister />
}
]
};
export default LoginRoutes;

View file

@ -0,0 +1,61 @@
import { lazy } from 'react';
// project import
import Loadable from '../components/Loadable';
import MainLayout from '../layout/MainLayout';
// render - dashboard
const DashboardDefault = Loadable(lazy(() => import('../pages/dashboard')));
// render - sample page
const SamplePage = Loadable(lazy(() => import('../pages/extra-pages/SamplePage')));
// render - utilities
const Typography = Loadable(lazy(() => import('../pages/components-overview/Typography')));
const Color = Loadable(lazy(() => import('../pages/components-overview/Color')));
const Shadow = Loadable(lazy(() => import('../pages/components-overview/Shadow')));
const AntIcons = Loadable(lazy(() => import('../pages/components-overview/AntIcons')));
// ==============================|| MAIN ROUTING ||============================== //
const MainRoutes = {
path: '/',
element: <MainLayout />,
children: [
{
path: '/',
element: <DashboardDefault />
},
{
path: 'color',
element: <Color />
},
{
path: 'dashboard',
children: [
{
path: 'default',
element: <DashboardDefault />
}
]
},
{
path: 'sample-page',
element: <SamplePage />
},
{
path: 'shadow',
element: <Shadow />
},
{
path: 'typography',
element: <Typography />
},
{
path: 'icons/ant',
element: <AntIcons />
}
]
};
export default MainRoutes;

View file

@ -0,0 +1,11 @@
import { useRoutes } from 'react-router-dom';
// project import
import LoginRoutes from './LoginRoutes';
import MainRoutes from './MainRoutes';
// ==============================|| ROUTING RENDER ||============================== //
export default function ThemeRoutes() {
return useRoutes([MainRoutes, LoginRoutes]);
}

View file

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View file

@ -0,0 +1,15 @@
// third-party
import { configureStore } from '@reduxjs/toolkit';
// project import
import reducers from './reducers';
// ==============================|| REDUX TOOLKIT - MAIN STORE ||============================== //
const store = configureStore({
reducer: reducers
});
const { dispatch } = store;
export { store, dispatch };

View file

@ -0,0 +1,4 @@
// action - account reducer
export const LOGIN = '@auth/LOGIN';
export const LOGOUT = '@auth/LOGOUT';
export const REGISTER = '@auth/REGISTER';

View file

@ -0,0 +1,11 @@
// third-party
import { combineReducers } from 'redux';
// project import
import menu from './menu';
// ==============================|| COMBINE REDUCERS ||============================== //
const reducers = combineReducers({ menu });
export default reducers;

View file

@ -0,0 +1,38 @@
// types
import { createSlice } from '@reduxjs/toolkit';
// initial state
const initialState = {
openItem: ['dashboard'],
openComponent: 'buttons',
drawerOpen: false,
componentDrawerOpen: true
};
// ==============================|| SLICE - MENU ||============================== //
const menu = createSlice({
name: 'menu',
initialState,
reducers: {
activeItem(state, action) {
state.openItem = action.payload.openItem;
},
activeComponent(state, action) {
state.openComponent = action.payload.openComponent;
},
openDrawer(state, action) {
state.drawerOpen = action.payload.drawerOpen;
},
openComponentDrawer(state, action) {
state.componentDrawerOpen = action.payload.componentDrawerOpen;
}
}
});
export default menu.reducer;
export const { activeItem, activeComponent, openDrawer, openComponentDrawer } = menu.actions;

View file

@ -0,0 +1,64 @@
import PropTypes from 'prop-types';
import { useMemo } from 'react';
// material-ui
import { CssBaseline, StyledEngineProvider } from '@mui/material';
import { createTheme, ThemeProvider } from '@mui/material/styles';
// project import
import Palette from './palette';
import Typography from './typography';
import CustomShadows from './shadows';
import componentsOverride from './overrides';
// ==============================|| DEFAULT THEME - MAIN ||============================== //
export default function ThemeCustomization({ children }) {
const theme = Palette('light', 'default');
// eslint-disable-next-line react-hooks/exhaustive-deps
const themeTypography = Typography(`'Public Sans', sans-serif`);
const themeCustomShadows = useMemo(() => CustomShadows(theme), [theme]);
const themeOptions = useMemo(
() => ({
breakpoints: {
values: {
xs: 0,
sm: 768,
md: 1024,
lg: 1266,
xl: 1536
}
},
direction: 'ltr',
mixins: {
toolbar: {
minHeight: 60,
paddingTop: 8,
paddingBottom: 8
}
},
palette: theme.palette,
customShadows: themeCustomShadows,
typography: themeTypography
}),
[theme, themeTypography, themeCustomShadows]
);
const themes = createTheme(themeOptions);
themes.components = componentsOverride(themes);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={themes}>
<CssBaseline />
{children}
</ThemeProvider>
</StyledEngineProvider>
);
}
ThemeCustomization.propTypes = {
children: PropTypes.node
};

View file

@ -0,0 +1,15 @@
// ==============================|| OVERRIDES - BADGE ||============================== //
export default function Badge(theme) {
return {
MuiBadge: {
styleOverrides: {
standard: {
minWidth: theme.spacing(2),
height: theme.spacing(2),
padding: theme.spacing(0.5)
}
}
}
};
}

View file

@ -0,0 +1,28 @@
// ==============================|| OVERRIDES - BUTTON ||============================== //
export default function Button(theme) {
const disabledStyle = {
'&.Mui-disabled': {
backgroundColor: theme.palette.grey[200]
}
};
return {
MuiButton: {
defaultProps: {
disableElevation: true
},
styleOverrides: {
root: {
fontWeight: 400
},
contained: {
...disabledStyle
},
outlined: {
...disabledStyle
}
}
}
};
}

View file

@ -0,0 +1,16 @@
// ==============================|| OVERRIDES - CARD CONTENT ||============================== //
export default function CardContent() {
return {
MuiCardContent: {
styleOverrides: {
root: {
padding: 20,
'&:last-child': {
paddingBottom: 20
}
}
}
}
};
}

View file

@ -0,0 +1,13 @@
// ==============================|| OVERRIDES - CHECKBOX ||============================== //
export default function Checkbox(theme) {
return {
MuiCheckbox: {
styleOverrides: {
root: {
color: theme.palette.secondary[300]
}
}
}
};
}

View file

@ -0,0 +1,40 @@
// ==============================|| OVERRIDES - CHIP ||============================== //
export default function Chip(theme) {
return {
MuiChip: {
styleOverrides: {
root: {
borderRadius: 4,
'&:active': {
boxShadow: 'none'
}
},
sizeLarge: {
fontSize: '1rem',
height: 40
},
light: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.primary.lighter,
borderColor: theme.palette.primary.light,
'&.MuiChip-lightError': {
color: theme.palette.error.main,
backgroundColor: theme.palette.error.lighter,
borderColor: theme.palette.error.light
},
'&.MuiChip-lightSuccess': {
color: theme.palette.success.main,
backgroundColor: theme.palette.success.lighter,
borderColor: theme.palette.success.light
},
'&.MuiChip-lightWarning': {
color: theme.palette.warning.main,
backgroundColor: theme.palette.warning.lighter,
borderColor: theme.palette.warning.light
}
}
}
}
};
}

View file

@ -0,0 +1,28 @@
// ==============================|| OVERRIDES - ICON BUTTON ||============================== //
export default function IconButton(theme) {
return {
MuiIconButton: {
styleOverrides: {
root: {
borderRadius: 4
},
sizeLarge: {
width: theme.spacing(5.5),
height: theme.spacing(5.5),
fontSize: '1.25rem'
},
sizeMedium: {
width: theme.spacing(4.5),
height: theme.spacing(4.5),
fontSize: '1rem'
},
sizeSmall: {
width: theme.spacing(3.75),
height: theme.spacing(3.75),
fontSize: '0.75rem'
}
}
}
};
}

Some files were not shown because too many files have changed in this diff Show more