Add UI and login screen
@@ -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
|
||||
|
||||
@@ -8,8 +8,10 @@ localcert.crt
|
||||
localcert.key
|
||||
.vite
|
||||
dev.json
|
||||
static
|
||||
.bin
|
||||
client/dist
|
||||
client/.vite
|
||||
config_dev.json
|
||||
tests
|
||||
todo.txt
|
||||
|
||||
@@ -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.
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
import * as auth from './authentication.jsx';
|
||||
export {
|
||||
auth
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
@@ -0,0 +1,4 @@
|
||||
.apexcharts-legend-series .apexcharts-legend-marker {
|
||||
left: -4px !important;
|
||||
top: 2px !important;
|
||||
}
|
||||
@@ -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'
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
};
|
||||
@@ -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';
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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's{' '}
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Cristina danny's
|
||||
</Typography>{' '}
|
||||
birthday today.
|
||||
</Typography>
|
||||
}
|
||||
secondary="2 min ago"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
3:00 AM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'primary.lighter'
|
||||
}}
|
||||
>
|
||||
<MessageOutlined />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Aida Burg
|
||||
</Typography>{' '}
|
||||
commented your post.
|
||||
</Typography>
|
||||
}
|
||||
secondary="5 August"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
6:00 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'error.main',
|
||||
bgcolor: 'error.lighter'
|
||||
}}
|
||||
>
|
||||
<SettingOutlined />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
Your Profile is Complete
|
||||
<Typography component="span" variant="subtitle1">
|
||||
60%
|
||||
</Typography>{' '}
|
||||
</Typography>
|
||||
}
|
||||
secondary="7 hours ago"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
2:45 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'primary.lighter'
|
||||
}}
|
||||
>
|
||||
C
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6">
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Cristina Danny
|
||||
</Typography>{' '}
|
||||
invited to join{' '}
|
||||
<Typography component="span" variant="subtitle1">
|
||||
Meeting.
|
||||
</Typography>
|
||||
</Typography>
|
||||
}
|
||||
secondary="Daily scrum meeting time"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Typography variant="caption" noWrap>
|
||||
9:10 PM
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListItemButton sx={{ textAlign: 'center', py: `${12}px !important` }}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography variant="h6" color="primary">
|
||||
View All
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</MainCard>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Notification;
|
||||
@@ -0,0 +1,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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// ==============================|| MINIMAL LAYOUT ||============================== //
|
||||
|
||||
const MinimalLayout = () => (
|
||||
<>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
|
||||
export default MinimalLayout;
|
||||
@@ -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>
|
||||
)
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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't have an account?
|
||||
</Typography> */}
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<AuthLogin />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AuthWrapper>
|
||||
);
|
||||
|
||||
export default Login;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
<Link variant="subtitle2" component={RouterLink} to="#">
|
||||
Terms of Service
|
||||
</Link>
|
||||
and
|
||||
<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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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 };
|
||||
@@ -0,0 +1,4 @@
|
||||
// action - account reducer
|
||||
export const LOGIN = '@auth/LOGIN';
|
||||
export const LOGOUT = '@auth/LOGOUT';
|
||||
export const REGISTER = '@auth/REGISTER';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
};
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// ==============================|| OVERRIDES - CARD CONTENT ||============================== //
|
||||
|
||||
export default function CardContent() {
|
||||
return {
|
||||
MuiCardContent: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 20,
|
||||
'&:last-child': {
|
||||
paddingBottom: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// ==============================|| OVERRIDES - CHECKBOX ||============================== //
|
||||
|
||||
export default function Checkbox(theme) {
|
||||
return {
|
||||
MuiCheckbox: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: theme.palette.secondary[300]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||