mirror of
https://github.com/D4M13N-D3V/comissions-app-ui.git
synced 2025-03-14 08:15:08 +00:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
01cc7eb197 | |||
2d4663ef74 | |||
![]() |
0807d6d11d | ||
![]() |
2a405067d8 | ||
![]() |
23ea8eed79 | ||
![]() |
8569290c0e | ||
![]() |
5a2f43768d | ||
![]() |
ff465278ad | ||
![]() |
a63d7f35b8 | ||
![]() |
93d4518500 | ||
![]() |
d52018f7eb | ||
![]() |
aac4ec902f | ||
![]() |
238477665d | ||
![]() |
b9aed8f3bb | ||
![]() |
565cb5dffb | ||
![]() |
539e1607bd | ||
![]() |
481bcd2d6c | ||
![]() |
13bd42ecbd | ||
![]() |
6c2acada86 | ||
![]() |
f90378b991 | ||
![]() |
9169063a08 | ||
![]() |
937faf1847 | ||
![]() |
c8b1dfb3ba | ||
![]() |
ed48b67e3c |
@ -4,6 +4,6 @@ AUTH0_CLIENT_SECRET="rCbTzrXdwZermb-N2GGqnCVI8YSTa32CA3GtuqZcmDRA6X0OGQGyiP4cRkh
|
|||||||
AUTH0_BASE_URL="http://localhost:3000"
|
AUTH0_BASE_URL="http://localhost:3000"
|
||||||
AUTH0_SECRET="rCbTzrXdwZermb-N2GGqnCVI8YSTa32CA3GtuqZcmDRA6X0OGQGyiP4cRkhBSIsd"
|
AUTH0_SECRET="rCbTzrXdwZermb-N2GGqnCVI8YSTa32CA3GtuqZcmDRA6X0OGQGyiP4cRkhBSIsd"
|
||||||
AUTH0_AUDIENCE="https://api.artplatform.com"
|
AUTH0_AUDIENCE="https://api.artplatform.com"
|
||||||
AUTH0_SCOPE="openid profile email read:user write:user read:billing-information write:billing-information read:seller-profile write:seller-profile read:seller-profile-request write:seller-profile-request read:seller-service write:seller-service read:orders write:orders read:seller-orders write:seller-orders"
|
AUTH0_SCOPE="openid profile email read:user write:user read:request write:request read:artist write:artist"
|
||||||
NEXT_PUBLIC_API_URL="https://core-api.development.comissions.app"
|
NEXT_PUBLIC_API_URL="https://core-api.development.comissions.app"
|
||||||
REACT_EDITOR=atom
|
REACT_EDITOR=atom
|
||||||
|
264
components/AppAppBar.tsx
Normal file
264
components/AppAppBar.tsx
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { PaletteMode } from '@mui/material';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import AppBar from '@mui/material/AppBar';
|
||||||
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import Drawer from '@mui/material/Drawer';
|
||||||
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
|
import { Logout, OpenInNew } from '@mui/icons-material';
|
||||||
|
|
||||||
|
const logoStyle = {
|
||||||
|
width: '140px',
|
||||||
|
height: 'auto',
|
||||||
|
cursor: 'pointer',
|
||||||
|
};
|
||||||
|
|
||||||
|
interface AppAppBarProps {
|
||||||
|
user: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppAppBar({ user }: AppAppBarProps) {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
const toggleDrawer = (newOpen: boolean) => () => {
|
||||||
|
setOpen(newOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollToSection = (sectionId: string) => {
|
||||||
|
const sectionElement = document.getElementById(sectionId);
|
||||||
|
const offset = 128;
|
||||||
|
if (sectionElement) {
|
||||||
|
const targetScroll = sectionElement.offsetTop - offset;
|
||||||
|
sectionElement.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
window.scrollTo({
|
||||||
|
top: targetScroll,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AppBar
|
||||||
|
position="fixed"
|
||||||
|
sx={{
|
||||||
|
boxShadow: 0,
|
||||||
|
bgcolor: 'transparent',
|
||||||
|
backgroundImage: 'none',
|
||||||
|
mt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="lg">
|
||||||
|
<Toolbar
|
||||||
|
variant="regular"
|
||||||
|
sx={(theme) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
flexShrink: 0,
|
||||||
|
borderRadius: '10px',
|
||||||
|
bgcolor:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? 'rgba(255, 255, 255, 0.6)'
|
||||||
|
: 'rgba(0, 0, 0, 0.4)',
|
||||||
|
backdropFilter: 'blur(24px)',
|
||||||
|
maxHeight: 40,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
boxShadow:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? `0 0 1px rgba(85, 166, 246, 0.1), 1px 1.5px 2px -1px rgba(85, 166, 246, 0.15), 4px 4px 12px -2.5px rgba(85, 166, 246, 0.15)`
|
||||||
|
: '0 0 1px rgba(2, 31, 59, 0.7), 1px 1.5px 2px -1px rgba(2, 31, 59, 0.65), 4px 4px 12px -2.5px rgba(2, 31, 59, 0.65)',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
ml: '-18px',
|
||||||
|
px: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6" color="primary" sx={{paddingLeft:"25px",paddingRight:"15px"}}>REQUEST.BOX</Typography>
|
||||||
|
<Box sx={{ display: { xs: 'none', md: 'flex' } }}>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => scrollToSection('home')}
|
||||||
|
sx={{ py: '6px', px: '12px' }}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" color="text.primary">
|
||||||
|
Home
|
||||||
|
</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => scrollToSection('features')}
|
||||||
|
sx={{ py: '6px', px: '12px' }}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" color="text.primary">
|
||||||
|
Features
|
||||||
|
</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => scrollToSection('beta-access')}
|
||||||
|
sx={{ py: '6px', px: '12px' }}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" color="text.primary">
|
||||||
|
Beta Testing
|
||||||
|
</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => scrollToSection('faq')}
|
||||||
|
sx={{ py: '6px', px: '12px' }}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" color="text.primary">
|
||||||
|
FAQ
|
||||||
|
</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
sx={{ py: '6px', px: '12px' }}
|
||||||
|
>
|
||||||
|
<Button href='https://discord.gg/hgcH4mvxFg'
|
||||||
|
target='_blank' variant="outlined" size="small">
|
||||||
|
Discord
|
||||||
|
</Button>
|
||||||
|
</MenuItem>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: { xs: 'none', md: 'flex' },
|
||||||
|
gap: 0.5,
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(user ? (
|
||||||
|
<>
|
||||||
|
<Button color="primary">{user.nickname}</Button>
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
variant="contained"
|
||||||
|
size="small"
|
||||||
|
component="a"
|
||||||
|
href="/dashboard"
|
||||||
|
startIcon={<OpenInNew />}
|
||||||
|
>
|
||||||
|
Dashboard
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="error"
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
component="a"
|
||||||
|
href="/api/auth/logout"
|
||||||
|
startIcon={<Logout />}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="text"
|
||||||
|
size="small"
|
||||||
|
component="a"
|
||||||
|
href="/api/auth/login"
|
||||||
|
>
|
||||||
|
Sign in
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
size="small"
|
||||||
|
component="a"
|
||||||
|
href="/api/auth/login"
|
||||||
|
>
|
||||||
|
Sign up
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: { sm: '', md: 'none' } }}>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
color="primary"
|
||||||
|
aria-label="menu"
|
||||||
|
onClick={toggleDrawer(true)}
|
||||||
|
sx={{ minWidth: '30px', p: '4px' }}
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</Button>
|
||||||
|
<Drawer anchor="right" open={open} onClose={toggleDrawer(false)}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
minWidth: '60dvw',
|
||||||
|
p: 2,
|
||||||
|
backgroundColor: 'background.paper',
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'end',
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</Box>
|
||||||
|
<MenuItem onClick={() => scrollToSection('features')}>
|
||||||
|
Features
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => scrollToSection('testimonials')}>
|
||||||
|
Testimonials
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => scrollToSection('highlights')}>
|
||||||
|
Highlights
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => scrollToSection('pricing')}>
|
||||||
|
Pricing
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => scrollToSection('faq')}>FAQ</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
component="a"
|
||||||
|
href="/material-ui/getting-started/templates/sign-up/"
|
||||||
|
target="_blank"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
>
|
||||||
|
Sign up
|
||||||
|
</Button>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="outlined"
|
||||||
|
component="a"
|
||||||
|
href="/material-ui/getting-started/templates/sign-in/"
|
||||||
|
target="_blank"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
>
|
||||||
|
Sign in
|
||||||
|
</Button>
|
||||||
|
</MenuItem>
|
||||||
|
</Box>
|
||||||
|
</Drawer>
|
||||||
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
</Container>
|
||||||
|
</AppBar>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppAppBar;
|
178
components/ArtistStats.tsx
Normal file
178
components/ArtistStats.tsx
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactElement } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Grid from '@mui/material/Grid'
|
||||||
|
import Card from '@mui/material/Card'
|
||||||
|
import Avatar from '@mui/material/Avatar'
|
||||||
|
import CardHeader from '@mui/material/CardHeader'
|
||||||
|
import IconButton from '@mui/material/IconButton'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import CardContent from '@mui/material/CardContent'
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import TrendingUp from 'mdi-material-ui/TrendingUp'
|
||||||
|
import CurrencyUsd from 'mdi-material-ui/CurrencyUsd'
|
||||||
|
import DotsVertical from 'mdi-material-ui/DotsVertical'
|
||||||
|
import CellphoneLink from 'mdi-material-ui/CellphoneLink'
|
||||||
|
import AccountOutline from 'mdi-material-ui/AccountOutline'
|
||||||
|
import CompareArrowsIcon from '@mui/icons-material/CompareArrows';
|
||||||
|
|
||||||
|
// ** Types
|
||||||
|
import { ThemeColor } from '../core/layouts/types'
|
||||||
|
import { Person } from '@mui/icons-material'
|
||||||
|
|
||||||
|
interface DataType {
|
||||||
|
stats: string
|
||||||
|
title: string
|
||||||
|
color: ThemeColor
|
||||||
|
icon: ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
|
const salesData: DataType[] = [
|
||||||
|
{
|
||||||
|
stats: '0',
|
||||||
|
title: 'Pending',
|
||||||
|
color: 'secondary',
|
||||||
|
icon: <Person sx={{ fontSize: '1.75rem' }} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stats: '0',
|
||||||
|
title: 'Accepted',
|
||||||
|
color: 'info',
|
||||||
|
icon: <CompareArrowsIcon sx={{ fontSize: '1.75rem' }} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stats: '0',
|
||||||
|
color: 'error',
|
||||||
|
title: 'Declined',
|
||||||
|
icon: <CompareArrowsIcon sx={{ fontSize: '1.75rem' }} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stats: '0',
|
||||||
|
color: 'success',
|
||||||
|
title: 'Completed',
|
||||||
|
icon: <CurrencyUsd sx={{ fontSize: '1.75rem' }} />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const ArtistStats = ({profileData, stats}) => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader
|
||||||
|
title='Artist Statistics'
|
||||||
|
subheader={
|
||||||
|
<>
|
||||||
|
<Typography variant='body2'>
|
||||||
|
<Box component='span' sx={{ fontWeight: 600, color: 'text.primary' }}>
|
||||||
|
You have earned ${stats["revenue"]}
|
||||||
|
</Box>{' '}
|
||||||
|
😎 from {stats["paidRequests"]} requests.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant='body2'>
|
||||||
|
You have <b>{stats["pendingRequests"]}</b> pending requests.
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
titleTypographyProps={{
|
||||||
|
sx: {
|
||||||
|
mb: 2.5,
|
||||||
|
lineHeight: '2rem !important',
|
||||||
|
letterSpacing: '0.15px !important'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<CardContent sx={{ pt: theme => `${theme.spacing(3)} !important` }}>
|
||||||
|
<Grid container spacing={[5, 0]}>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar
|
||||||
|
variant='rounded'
|
||||||
|
sx={{
|
||||||
|
mr: 3,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
boxShadow: 3,
|
||||||
|
color: 'common.white',
|
||||||
|
backgroundColor: `error.main`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Person sx={{ fontSize: '1.75rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Typography variant='caption'>Declined</Typography>
|
||||||
|
<Typography variant='h6'>{stats["declinedRequests"]}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar
|
||||||
|
variant='rounded'
|
||||||
|
sx={{
|
||||||
|
mr: 3,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
boxShadow: 3,
|
||||||
|
color: 'common.white',
|
||||||
|
backgroundColor: `info.main`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Person sx={{ fontSize: '1.75rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Typography variant='caption'>Accepted</Typography>
|
||||||
|
<Typography variant='h6'>{stats["acceptedRequests"]}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar
|
||||||
|
variant='rounded'
|
||||||
|
sx={{
|
||||||
|
mr: 3,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
boxShadow: 3,
|
||||||
|
color: 'common.white',
|
||||||
|
backgroundColor: `success.light`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Person sx={{ fontSize: '1.75rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Typography variant='caption'>Paid For</Typography>
|
||||||
|
<Typography variant='h6'>{stats["completedRequests"]}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar
|
||||||
|
variant='rounded'
|
||||||
|
sx={{
|
||||||
|
mr: 3,
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
boxShadow: 3,
|
||||||
|
color: 'common.white',
|
||||||
|
backgroundColor: `success.dark`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Person sx={{ fontSize: '1.75rem' }} />
|
||||||
|
</Avatar>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Typography variant='caption'>Completed</Typography>
|
||||||
|
<Typography variant='h6'>{stats["completedRequests"]}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArtistStats
|
68
components/BetaAccess.tsx
Normal file
68
components/BetaAccess.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import { useMediaQuery } from '@mui/material';
|
||||||
|
import { useTheme } from '@mui/system';
|
||||||
|
import { OpenInNew } from '@mui/icons-material';
|
||||||
|
|
||||||
|
const whiteLogos = [
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560628e8573c43893fe0ace_Sydney-white.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f4d520d0517ae8e8ddf13_Bern-white.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f46794c159024c1af6d44_Montreal-white.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/61f12e891fa22f89efd7477a_TerraLight.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560a09d1f6337b1dfed14ab_colorado-white.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f5caa77bf7d69fb78792e_Ankara-white.svg',
|
||||||
|
];
|
||||||
|
|
||||||
|
const darkLogos = [
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560628889c3bdf1129952dc_Sydney-black.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f4d4d8b829a89976a419c_Bern-black.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f467502f091ccb929529d_Montreal-black.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/61f12e911fa22f2203d7514c_TerraDark.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560a0990f3717787fd49245_colorado-black.svg',
|
||||||
|
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f5ca4e548b0deb1041c33_Ankara-black.svg',
|
||||||
|
];
|
||||||
|
|
||||||
|
const logoStyle = {
|
||||||
|
width: '64px',
|
||||||
|
opacity: 0.3,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function BetaAccess() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isSmallScreen = useMediaQuery('(max-width:600px)');
|
||||||
|
const columns = isSmallScreen ? 1 : 3;
|
||||||
|
const logos = theme.palette.mode === 'light' ? darkLogos : whiteLogos;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
id="beta-access"
|
||||||
|
sx={{
|
||||||
|
pt: { xs: 4, sm: 12 },
|
||||||
|
pb: { xs: 8, sm: 16 },
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: { xs: 3, sm: 6 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: { sm: '100%', md: '60%' },
|
||||||
|
textAlign: { sm: 'left', md: 'center' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography component="h2" variant="h4" color="text.primary">
|
||||||
|
Founders Club
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" color="text.secondary" sx={{marginTop:"3%"}}>
|
||||||
|
Join the discord to get access to the beta and help shape the future of Request.Box. Founders club members will receive perks and early access to features. They will also vote in special community polls that influence the platforms development.
|
||||||
|
</Typography>
|
||||||
|
<Button color="primary" variant="contained" href="https://discord.gg/8v6j6j6C" target="_blank" rel="noopener" startIcon={<OpenInNew/>} sx={{marginTop:"3%"}}>OPEN DISCORD</Button>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
140
components/FAQ.tsx
Normal file
140
components/FAQ.tsx
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Accordion from '@mui/material/Accordion';
|
||||||
|
import AccordionDetails from '@mui/material/AccordionDetails';
|
||||||
|
import AccordionSummary from '@mui/material/AccordionSummary';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
|
|
||||||
|
export default function FAQ() {
|
||||||
|
const [expanded, setExpanded] = React.useState<string | false>(false);
|
||||||
|
|
||||||
|
const handleChange =
|
||||||
|
(panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => {
|
||||||
|
setExpanded(isExpanded ? panel : false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
id="faq"
|
||||||
|
sx={{
|
||||||
|
pt: { xs: 4, sm: 12 },
|
||||||
|
pb: { xs: 8, sm: 16 },
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: { xs: 3, sm: 6 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
component="h2"
|
||||||
|
variant="h4"
|
||||||
|
color="text.primary"
|
||||||
|
sx={{
|
||||||
|
width: { sm: '100%', md: '60%' },
|
||||||
|
textAlign: { sm: 'left', md: 'center' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Frequently asked questions
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ width: '100%' }}>
|
||||||
|
<Accordion
|
||||||
|
expanded={expanded === 'panel1'}
|
||||||
|
onChange={handleChange('panel1')}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
aria-controls="panel1d-content"
|
||||||
|
id="panel1d-header"
|
||||||
|
>
|
||||||
|
<Typography component="h3" variant="subtitle2">
|
||||||
|
What if I am banned on Stripe?
|
||||||
|
</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
gutterBottom
|
||||||
|
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||||
|
>
|
||||||
|
You can reach out to our customer support on discord. We will help you figure out a solution tailored to your situation.
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion
|
||||||
|
expanded={expanded === 'panel2'}
|
||||||
|
onChange={handleChange('panel2')}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
aria-controls="panel2d-content"
|
||||||
|
id="panel2d-header"
|
||||||
|
>
|
||||||
|
<Typography component="h3" variant="subtitle2">
|
||||||
|
What happens if I dont like the art the artist provided?
|
||||||
|
</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
gutterBottom
|
||||||
|
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||||
|
>
|
||||||
|
There are no refunds what so ever on this platform. We are not responsible for the art that is provided by the artist. We are simply a platform that connects artists with clients. We are not responsible for the quality of the art that is provided by the artist.
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion
|
||||||
|
expanded={expanded === 'panel3'}
|
||||||
|
onChange={handleChange('panel3')}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
aria-controls="panel3d-content"
|
||||||
|
id="panel3d-header"
|
||||||
|
>
|
||||||
|
<Typography component="h3" variant="subtitle2">
|
||||||
|
What makes your product stand out from others in the market?
|
||||||
|
</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
gutterBottom
|
||||||
|
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||||
|
>
|
||||||
|
We provide support for the artists incase of chargebacks and other problems caused by problematic clients.
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion
|
||||||
|
expanded={expanded === 'panel4'}
|
||||||
|
onChange={handleChange('panel4')}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
aria-controls="panel4d-content"
|
||||||
|
id="panel4d-header"
|
||||||
|
>
|
||||||
|
<Typography component="h3" variant="subtitle2">
|
||||||
|
What countries are supported on the platforms for clients or for artists?
|
||||||
|
</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
gutterBottom
|
||||||
|
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||||
|
>
|
||||||
|
This list will be changing often. We currently support all countries that Stripe supports. We are working on adding more payment processors to support more countries.
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
99
components/Footer.tsx
Normal file
99
components/Footer.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
import FacebookIcon from '@mui/icons-material/GitHub';
|
||||||
|
import LinkedInIcon from '@mui/icons-material/LinkedIn';
|
||||||
|
import TwitterIcon from '@mui/icons-material/X';
|
||||||
|
|
||||||
|
const logoStyle = {
|
||||||
|
width: '140px',
|
||||||
|
height: 'auto',
|
||||||
|
};
|
||||||
|
|
||||||
|
function Copyright() {
|
||||||
|
return (
|
||||||
|
<Typography variant="body2" color="text.secondary" mt={1}>
|
||||||
|
{'Copyright © '}
|
||||||
|
<Link href="https://mui.com/">Request.Box </Link>
|
||||||
|
{new Date().getFullYear()}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: { xs: 4, sm: 8 },
|
||||||
|
py: { xs: 8, sm: 10 },
|
||||||
|
textAlign: { sm: 'center', md: 'left' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
pt: { xs: 4, sm: 8 },
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Link color="text.secondary" href="#">
|
||||||
|
Privacy Policy
|
||||||
|
</Link>
|
||||||
|
<Typography display="inline" sx={{ mx: 0.5, opacity: 0.5 }}>
|
||||||
|
•
|
||||||
|
</Typography>
|
||||||
|
<Link color="text.secondary" href="#">
|
||||||
|
Terms of Service
|
||||||
|
</Link>
|
||||||
|
<Copyright />
|
||||||
|
</div>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
justifyContent="left"
|
||||||
|
spacing={1}
|
||||||
|
useFlexGap
|
||||||
|
sx={{
|
||||||
|
color: 'text.secondary',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
href="https://github.com/mui"
|
||||||
|
aria-label="GitHub"
|
||||||
|
sx={{ alignSelf: 'center' }}
|
||||||
|
>
|
||||||
|
<FacebookIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
href="https://twitter.com/MaterialUI"
|
||||||
|
aria-label="X"
|
||||||
|
sx={{ alignSelf: 'center' }}
|
||||||
|
>
|
||||||
|
<TwitterIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
href="https://www.linkedin.com/company/mui/"
|
||||||
|
aria-label="LinkedIn"
|
||||||
|
sx={{ alignSelf: 'center' }}
|
||||||
|
>
|
||||||
|
<LinkedInIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
82
components/Hero.tsx
Normal file
82
components/Hero.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { alpha } from '@mui/material';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
export default function Hero() {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
id="home"
|
||||||
|
sx={(theme) => ({
|
||||||
|
width: '100%',
|
||||||
|
backgroundImage:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? 'linear-gradient(180deg, #CEE5FD, #FFF)'
|
||||||
|
: 'linear-gradient(#02294F, #090E10)',
|
||||||
|
backgroundSize: '100% 20%',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Container
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
pt: { xs: 14, sm: 20 },
|
||||||
|
pb: { xs: 8, sm: 12 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack spacing={2} useFlexGap sx={{ width: { xs: '100%', sm: '70%' } }}>
|
||||||
|
<Typography
|
||||||
|
component="h2"
|
||||||
|
variant="h2"
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: { xs: 'column', md: 'row' },
|
||||||
|
alignSelf: 'center',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Artists are
|
||||||
|
<Typography
|
||||||
|
component="span"
|
||||||
|
variant="h2"
|
||||||
|
sx={{
|
||||||
|
color: (theme) =>
|
||||||
|
theme.palette.mode === 'light' ? 'primary.main' : 'primary.light',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
protected.
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" textAlign="center" color="text.secondary">
|
||||||
|
We take a 13.5% precentage of all transactions that go through the platform. Stripe takes a 2.9% and 30 cents per transaction. We intend to take another 3.5% and add it to a fund. The intention of this fund will be to pay for claims that artists make for charge backs against their accounts and other such issues. The funds may be appropriated for other on platform enhancements that are voted in through community polls . The rest of the transaction fee will go to the platform and will be reinvested into the software and used to compensate the team.
|
||||||
|
</Typography>
|
||||||
|
<Stack
|
||||||
|
direction={{ xs: 'column', sm: 'row' }}
|
||||||
|
alignSelf="center"
|
||||||
|
spacing={1}
|
||||||
|
useFlexGap
|
||||||
|
sx={{ pt: 2, width: { xs: '100%', sm: 'auto' } }}
|
||||||
|
>
|
||||||
|
<Button variant="contained" color="primary">
|
||||||
|
Become an artist
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
<Typography variant="caption" textAlign="center" sx={{ opacity: 0.8 }}>
|
||||||
|
By clicking "Become an artist" you agree to our
|
||||||
|
<Link href="#" color="primary">
|
||||||
|
Terms & Conditions
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
122
components/Highlights.tsx
Normal file
122
components/Highlights.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import AutoFixHighRoundedIcon from '@mui/icons-material/AutoFixHighRounded';
|
||||||
|
import ConstructionRoundedIcon from '@mui/icons-material/ConstructionRounded';
|
||||||
|
import QueryStatsRoundedIcon from '@mui/icons-material/QueryStatsRounded';
|
||||||
|
import SettingsSuggestRoundedIcon from '@mui/icons-material/SettingsSuggestRounded';
|
||||||
|
import SupportAgentRoundedIcon from '@mui/icons-material/SupportAgentRounded';
|
||||||
|
import ThumbUpAltRoundedIcon from '@mui/icons-material/ThumbUpAltRounded';
|
||||||
|
import { CreditCard, Lock, MoneyRounded } from '@mui/icons-material';
|
||||||
|
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
icon: <VisibilityOffIcon />,
|
||||||
|
title: 'Refund Policy/Artist Protection',
|
||||||
|
description:
|
||||||
|
'Payments are made upfront with no refunds. No delivery dates or unwanted communication with the artist. Any chargebacks can be disputed with evidence.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <CreditCard />,
|
||||||
|
title: 'Multiple Payment Platforms',
|
||||||
|
description:
|
||||||
|
'We support credit cards, google pay, and apple pay. More payment providers are coming soon. These are subject to change! All transactions on the platform show up as "Request.Box Request".',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <AutoFixHighRoundedIcon />,
|
||||||
|
title: 'Skeb-like System',
|
||||||
|
description:
|
||||||
|
'Requests are made with a message, reference material, and amount of money. Artists can accept or decline the request.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <QueryStatsRoundedIcon />,
|
||||||
|
title: 'Payout Management',
|
||||||
|
description:
|
||||||
|
'Manage information about how you recieve your payments and view statistics about them in a user friendly dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <SupportAgentRoundedIcon />,
|
||||||
|
title: 'Reliable support',
|
||||||
|
description:
|
||||||
|
'Get support directly from the developers and other staff members in the community discord.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <ThumbUpAltRoundedIcon />,
|
||||||
|
title: 'Small Business',
|
||||||
|
description:
|
||||||
|
'Software and its community is developed and maintained by its developer and founder.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Highlights() {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
id="highlights"
|
||||||
|
sx={{
|
||||||
|
pt: { xs: 4, sm: 12 },
|
||||||
|
pb: { xs: 8, sm: 16 },
|
||||||
|
color: 'white',
|
||||||
|
bgcolor: '#06090a',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container
|
||||||
|
sx={{
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: { xs: 3, sm: 6 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: { sm: '100%', md: '60%' },
|
||||||
|
textAlign: { sm: 'left', md: 'center' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography component="h2" variant="h4">
|
||||||
|
Highlights
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" sx={{ color: 'grey.400' }}>
|
||||||
|
Explore why our product stands out: we keep the artist in mind at all times, and we are always looking for ways to improve the platform.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Grid container spacing={2.5}>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<Grid item xs={12} sm={6} md={4} key={index}>
|
||||||
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
color="inherit"
|
||||||
|
component={Card}
|
||||||
|
spacing={1}
|
||||||
|
useFlexGap
|
||||||
|
sx={{
|
||||||
|
p: 3,
|
||||||
|
height: '100%',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'grey.800',
|
||||||
|
background: 'transparent',
|
||||||
|
backgroundColor: 'grey.900',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ opacity: '50%' }}>{item.icon}</Box>
|
||||||
|
<div>
|
||||||
|
<Typography fontWeight="medium" gutterBottom>
|
||||||
|
{item.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: 'grey.400' }}>
|
||||||
|
{item.description}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
219
components/Pricing.tsx
Normal file
219
components/Pricing.tsx
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import CardActions from '@mui/material/CardActions';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
|
||||||
|
import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';
|
||||||
|
|
||||||
|
const tiers = [
|
||||||
|
{
|
||||||
|
title: 'Free',
|
||||||
|
price: '0',
|
||||||
|
description: [
|
||||||
|
'10 users included',
|
||||||
|
'2 GB of storage',
|
||||||
|
'Help center access',
|
||||||
|
'Email support',
|
||||||
|
],
|
||||||
|
buttonText: 'Sign up for free',
|
||||||
|
buttonVariant: 'outlined',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Professional',
|
||||||
|
subheader: 'Recommended',
|
||||||
|
price: '15',
|
||||||
|
description: [
|
||||||
|
'20 users included',
|
||||||
|
'10 GB of storage',
|
||||||
|
'Help center access',
|
||||||
|
'Priority email support',
|
||||||
|
'Dedicated team',
|
||||||
|
'Best deals',
|
||||||
|
],
|
||||||
|
buttonText: 'Start now',
|
||||||
|
buttonVariant: 'contained',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Enterprise',
|
||||||
|
price: '30',
|
||||||
|
description: [
|
||||||
|
'50 users included',
|
||||||
|
'30 GB of storage',
|
||||||
|
'Help center access',
|
||||||
|
'Phone & email support',
|
||||||
|
],
|
||||||
|
buttonText: 'Contact us',
|
||||||
|
buttonVariant: 'outlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Pricing() {
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
id="pricing"
|
||||||
|
sx={{
|
||||||
|
pt: { xs: 4, sm: 12 },
|
||||||
|
pb: { xs: 8, sm: 16 },
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: { xs: 3, sm: 6 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: { sm: '100%', md: '60%' },
|
||||||
|
textAlign: { sm: 'left', md: 'center' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography component="h2" variant="h4" color="text.primary">
|
||||||
|
Pricing
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" color="text.secondary">
|
||||||
|
Quickly build an effective pricing table for your potential customers with
|
||||||
|
this layout. <br />
|
||||||
|
It's built with default Material UI components with little
|
||||||
|
customization.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Grid container spacing={3} alignItems="center" justifyContent="center">
|
||||||
|
{tiers.map((tier) => (
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
key={tier.title}
|
||||||
|
xs={12}
|
||||||
|
sm={tier.title === 'Enterprise' ? 12 : 6}
|
||||||
|
md={4}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 4,
|
||||||
|
border: tier.title === 'Professional' ? '1px solid' : undefined,
|
||||||
|
borderColor:
|
||||||
|
tier.title === 'Professional' ? 'primary.main' : undefined,
|
||||||
|
background:
|
||||||
|
tier.title === 'Professional'
|
||||||
|
? 'linear-gradient(#033363, #021F3B)'
|
||||||
|
: undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
mb: 1,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
color:
|
||||||
|
tier.title === 'Professional' ? 'primary.contrastText' : '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography component="h3" variant="h6">
|
||||||
|
{tier.title}
|
||||||
|
</Typography>
|
||||||
|
{tier.title === 'Professional' && (
|
||||||
|
<Chip
|
||||||
|
icon={<AutoAwesomeIcon />}
|
||||||
|
label={tier.subheader}
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
background: (theme) =>
|
||||||
|
theme.palette.mode === 'light' ? '' : 'none',
|
||||||
|
backgroundColor: 'primary.contrastText',
|
||||||
|
'& .MuiChip-label': {
|
||||||
|
color: 'primary.dark',
|
||||||
|
},
|
||||||
|
'& .MuiChip-icon': {
|
||||||
|
color: 'primary.dark',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'baseline',
|
||||||
|
color:
|
||||||
|
tier.title === 'Professional'
|
||||||
|
? 'primary.contrastText'
|
||||||
|
: undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography component="h3" variant="h2">
|
||||||
|
${tier.price}
|
||||||
|
</Typography>
|
||||||
|
<Typography component="h3" variant="h6">
|
||||||
|
per month
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Divider
|
||||||
|
sx={{
|
||||||
|
my: 2,
|
||||||
|
opacity: 0.2,
|
||||||
|
borderColor: 'grey.500',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{tier.description.map((line) => (
|
||||||
|
<Box
|
||||||
|
key={line}
|
||||||
|
sx={{
|
||||||
|
py: 1,
|
||||||
|
display: 'flex',
|
||||||
|
gap: 1.5,
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckCircleRoundedIcon
|
||||||
|
sx={{
|
||||||
|
width: 20,
|
||||||
|
color:
|
||||||
|
tier.title === 'Professional'
|
||||||
|
? 'primary.light'
|
||||||
|
: 'primary.main',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
component="text"
|
||||||
|
variant="subtitle2"
|
||||||
|
sx={{
|
||||||
|
color:
|
||||||
|
tier.title === 'Professional'
|
||||||
|
? 'primary.contrastText'
|
||||||
|
: undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{line}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant={tier.buttonVariant as 'outlined' | 'contained'}
|
||||||
|
component="a"
|
||||||
|
href="/material-ui/getting-started/templates/checkout/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{tier.buttonText}
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
13
components/RootSvg.tsx
Normal file
13
components/RootSvg.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { styled, Theme } from '@mui/material/styles';
|
||||||
|
import { SxProps } from '@mui/system';
|
||||||
|
|
||||||
|
export type RootSvgProps<P = unknown> = Omit<React.SVGProps<SVGSVGElement>, 'ref'> & {
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
|
ref?: React.Ref<SVGSVGElement>;
|
||||||
|
} & P;
|
||||||
|
|
||||||
|
const Svg = styled('svg')({
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Svg;
|
21
components/SvgMaterialDesign.tsx
Normal file
21
components/SvgMaterialDesign.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import RootSvg, { RootSvgProps } from './RootSvg';
|
||||||
|
|
||||||
|
function SvgMaterialDesign(props: RootSvgProps) {
|
||||||
|
return (
|
||||||
|
<RootSvg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<circle cx={12} cy={12} r={12} fill="#737373" />
|
||||||
|
<path fill="#BDBDBD" d="M4 4h16v16H4z" />
|
||||||
|
<path fillRule="evenodd" clipRule="evenodd" d="M12 20l8-16H4l8 16z" fill="#fff" />
|
||||||
|
</RootSvg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SvgMaterialDesign;
|
@ -1,77 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import Card from '@mui/material/Card';
|
|
||||||
import CardActions from '@mui/material/CardActions';
|
|
||||||
import CardContent from '@mui/material/CardContent';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import CardMedia from '@mui/material/CardMedia';
|
|
||||||
import StarBorderOutlinedIcon from '@mui/icons-material/StarBorderOutlined';
|
|
||||||
import ShoppingCartCheckoutOutlinedIcon from '@mui/icons-material/ShoppingCartCheckoutOutlined';
|
|
||||||
import AccountCircleOutlinedIcon from '@mui/icons-material/AccountCircleOutlined';
|
|
||||||
import Grid from '@mui/material/Grid';
|
|
||||||
import Item from '@mui/material/Grid';
|
|
||||||
import Accordion from '@mui/material/Accordion';
|
|
||||||
import AccordionSummary from '@mui/material/AccordionSummary';
|
|
||||||
import AccordionDetails from '@mui/material/AccordionDetails';
|
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
||||||
import ArtistPortfolio from './artistPortfolio';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
|
|
||||||
import { IconButton } from '@mui/material';
|
|
||||||
|
|
||||||
const Artist = ({user, artistId}) => {
|
|
||||||
const [sellerData, setSellerData] = useState([]);
|
|
||||||
useEffect(() => {
|
|
||||||
const getData = async () => {
|
|
||||||
const response = await fetch('/api/discovery/artist/'+artistId);
|
|
||||||
const data = await response.json();
|
|
||||||
setSellerData(data);
|
|
||||||
}
|
|
||||||
getData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card color="primary" sx={{margin:5}}>
|
|
||||||
<CardContent>
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item xs={6} md={8}>
|
|
||||||
<Item>
|
|
||||||
<Typography variant="h5" component="h2">
|
|
||||||
{sellerData.name}
|
|
||||||
</Typography>
|
|
||||||
<Typography color="primary">
|
|
||||||
{sellerData.averageRating ? `${sellerData.averageRating} Stars (${sellerData.reviewCount} Reviews)` : "No Reviews"}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" component="p">
|
|
||||||
{sellerData.biography}
|
|
||||||
</Typography>
|
|
||||||
</Item>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} md={4}>
|
|
||||||
<Grid item xs={6} md={4}>
|
|
||||||
<Button href={"artist/"+artistId} color="primary" variant='contained' sx={{width:160}}>View Profile</Button>
|
|
||||||
{user ? (
|
|
||||||
<Button color="secondary" variant='contained' href={"/artist/"+artistId+"/request"} sx={{ width: 160, marginTop:2 }}>Submit Request</Button>
|
|
||||||
) : (
|
|
||||||
<Tooltip title="Log in order to place a request.">
|
|
||||||
<span>
|
|
||||||
<Button disabled color="secondary" variant='contained' sx={{ width: 160, marginTop:2 }}>Submit Request</Button>
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={12}>
|
|
||||||
|
|
||||||
<Item>
|
|
||||||
</Item>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
</Card>)
|
|
||||||
}
|
|
||||||
export default Artist
|
|
@ -1,28 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import ImageList from '@mui/material/ImageList';
|
|
||||||
import ImageListItem from '@mui/material/ImageListItem';
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
import { CircularProgress } from '@mui/material';
|
|
||||||
|
|
||||||
import { IconButton } from '@mui/material';
|
|
||||||
|
|
||||||
const ArtistPortfolioImage = ({artistId,itemId}) => {
|
|
||||||
const [loaded, setLoaded] = useState(false);
|
|
||||||
const handleImageLoaded = () => {
|
|
||||||
setLoaded(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ImageListItem key={itemId }>
|
|
||||||
<img
|
|
||||||
srcSet={process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Sellers/${artistId}/Portfolio/${itemId}`}
|
|
||||||
src={process.env.NEXT_PUBLIC_API_URL+`/api/Discovery/Sellers/${artistId}/Portfolio/${itemId}`}
|
|
||||||
alt={itemId}
|
|
||||||
loading="lazy"
|
|
||||||
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor:'grey' }}
|
|
||||||
onLoad={handleImageLoaded}
|
|
||||||
/>
|
|
||||||
</ImageListItem>)
|
|
||||||
}
|
|
||||||
export default ArtistPortfolioImage
|
|
211
components/dashboard/Onboarding.tsx
Normal file
211
components/dashboard/Onboarding.tsx
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import {Box,Stepper,Step,StepLabel,StepContent,Button,Paper,Typography,Grid,TextField} from "@mui/material"
|
||||||
|
import ArtistDashboardRequest from './artistRequest';
|
||||||
|
import EditableArtistPortfolio from './artist/editablePortfolio';
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
|
||||||
|
import {Card, CardContent, CardHeader, Divider } from '@mui/material';
|
||||||
|
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
label: 'Request Access As Artist',
|
||||||
|
description: `In order to start selling your art on our platform, you need to request access. Please include links to your social media and tag or DM us on the platform (@RequestDotBox). We may reach out for further verification and examples of your work.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Onboard On Stripe',
|
||||||
|
description:
|
||||||
|
'Our platform uses Stripe as a payment processor. You will be required to onboard with them with all of your payout information and business information.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Setup Your Portfolio',
|
||||||
|
description: `This is where you can setup your initial portfolio. You can upload any image format file to your portfolio. It will be automatically displayed on your artist page. You can add and remove from this later.`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Onboarding() {
|
||||||
|
const [activeStep, setActiveStep] = React.useState(0);
|
||||||
|
const [sellerRequestData, setArtistRequestData] = React.useState(null);
|
||||||
|
const [profileData, setArtistData] = React.useState(null);
|
||||||
|
const [isStripeOnboarded, setIsStripeOnboarded] = React.useState(false);
|
||||||
|
const [onBoardUrl, setOnBoardUrl] = React.useState("");
|
||||||
|
|
||||||
|
const [requestMessage, setRequestMessage] = React.useState("");
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
setActiveStep((prevActiveStep) => prevActiveStep - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setActiveStep(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRequestMessage = (event) => {
|
||||||
|
setRequestMessage(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const onboardCheckRequest = await fetch('/api/artist/onboarded', { method: "GET" });
|
||||||
|
const onboardCheckResponse = await onboardCheckRequest.json();
|
||||||
|
setIsStripeOnboarded(onboardCheckResponse["onboarded"]);
|
||||||
|
const onboardUrlRequest = await fetch('/api/artist/onboardurl', { method: "GET" });
|
||||||
|
const onboardUrlResponse = await onboardUrlRequest.json();
|
||||||
|
setOnBoardUrl(onboardUrlResponse["onboardUrl"]);
|
||||||
|
const response = await fetch('/api/artist/request');
|
||||||
|
const sellerRequest = await response.json();
|
||||||
|
setArtistRequestData(sellerRequest);
|
||||||
|
const profileResponse = await fetch('/api/artist/profile');
|
||||||
|
const sellerProfile = await profileResponse.json();
|
||||||
|
setArtistData(sellerProfile); // Poll every 5 seconds (adjust as needed)
|
||||||
|
}
|
||||||
|
React.useEffect(() => {
|
||||||
|
getData();
|
||||||
|
|
||||||
|
setTimeout(getData, 30000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const requestButton = () => {
|
||||||
|
fetch('/api/artist/newRequest', {headers:{ "Content-Header":"application/json"},method:"POST",body:JSON.stringify(requestMessage)}).then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
fetch('/api/artist/request').then((requestResponse) => {
|
||||||
|
requestResponse.json().then((sellerRequest) => {
|
||||||
|
setArtistRequestData(sellerRequest);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let formattedTime = ""
|
||||||
|
if (sellerRequestData) {
|
||||||
|
const date = new Date(sellerRequestData["requestDate"]);
|
||||||
|
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card sx={{ width:"100%", padding:"2%" }}>
|
||||||
|
<Stepper activeStep={activeStep} orientation="vertical">
|
||||||
|
{steps.map((step, index) => (
|
||||||
|
<Step key={step.label}>
|
||||||
|
<StepLabel
|
||||||
|
optional={
|
||||||
|
index === 2 ? (
|
||||||
|
<Typography variant="caption">Last step</Typography>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{step.label}
|
||||||
|
</StepLabel>
|
||||||
|
{(index==0) ? (
|
||||||
|
<StepContent>
|
||||||
|
<Grid container >
|
||||||
|
<Grid item xs={12} lg={12}>
|
||||||
|
<Typography>{step.description}</Typography>
|
||||||
|
</Grid>
|
||||||
|
{(sellerRequestData && Object.keys(sellerRequestData).length>0) ? (
|
||||||
|
<Grid item xs={12} lg={12} sx={{paddingTop:"2%"}}>
|
||||||
|
<ArtistDashboardRequest/>
|
||||||
|
</Grid>
|
||||||
|
):(
|
||||||
|
<Grid item xs={12} lg={12}>
|
||||||
|
<TextField value={requestMessage} onChange={handleRequestMessage} fullWidth rows={4} multiline label="Application Message" variant="outlined" />
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
<Box sx={{ mb: 2 ,paddingTop:"2%"}}>
|
||||||
|
<div>
|
||||||
|
{(sellerRequestData && Object.keys(sellerRequestData).length>0) ? (
|
||||||
|
(sellerRequestData["accepted"]) ? (
|
||||||
|
<Button variant="contained" onClick={handleNext}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
|
||||||
|
<Button variant="contained" disabled>
|
||||||
|
Request Pending
|
||||||
|
</Button> )
|
||||||
|
):
|
||||||
|
(
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={requestButton}
|
||||||
|
sx={{ mt: 1, mr: 1 }}
|
||||||
|
>
|
||||||
|
Request Access
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</StepContent>
|
||||||
|
): null}
|
||||||
|
{(index==1) ? (
|
||||||
|
<StepContent>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} lg={12}>
|
||||||
|
<Typography>{step.description}</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Box sx={{ mb: 2 }}>
|
||||||
|
<div>
|
||||||
|
{isStripeOnboarded==true ? (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleNext}
|
||||||
|
sx={{ mt: 1, mr: 1 }}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
):(
|
||||||
|
|
||||||
|
<Button
|
||||||
|
color='success'
|
||||||
|
variant="contained"
|
||||||
|
href={onBoardUrl}
|
||||||
|
sx={{ mt: 1, mr: 1 }}
|
||||||
|
>
|
||||||
|
ONBOARD WITH STRIPE
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</StepContent>
|
||||||
|
): null}
|
||||||
|
{(index==2) ? (
|
||||||
|
<StepContent>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} lg={12}>
|
||||||
|
<Typography>{step.description}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} lg={12}>
|
||||||
|
<EditableArtistPortfolio artistId={profileData ? profileData["id"] : null}/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Box sx={{ mb: 2 }}>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleNext}
|
||||||
|
sx={{ mt: 1, mr: 1 }}
|
||||||
|
>
|
||||||
|
{index === steps.length - 1 ? 'Finish' : 'Continue'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</StepContent>
|
||||||
|
): null}
|
||||||
|
</Step>
|
||||||
|
))}
|
||||||
|
</Stepper>
|
||||||
|
{activeStep === steps.length && (
|
||||||
|
<Paper square elevation={0} sx={{ p: 3 }}>
|
||||||
|
<Typography>We are setting up your account please wait.</Typography>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
49
components/dashboard/admin/artistRequest.tsx
Normal file
49
components/dashboard/admin/artistRequest.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid';
|
||||||
|
import { Button } from '@mui/material';
|
||||||
|
import { useUser,withPageAuthRequired } from "@auth0/nextjs-auth0/client";
|
||||||
|
import { Grid, Typography } from "@mui/material";
|
||||||
|
import Card from "@mui/material/Card";
|
||||||
|
import CardContent from "@mui/material/CardContent";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function AdminArtistRequest({id,userid,username,message,date,reload}) {
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getData()
|
||||||
|
}
|
||||||
|
, []);
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAccept = () => {
|
||||||
|
fetch("/api/admin/requests/"+userid, {method:"PUT"}).then(response => response.json().then(data => {
|
||||||
|
reload();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
const handleDeny = () => {
|
||||||
|
fetch("/api/admin/requests/"+userid, {method:"DELETE"}).then(response => response.json().then(data => {
|
||||||
|
reload();
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Grid item xs={12} md={4}>
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography>ID: {id}</Typography>
|
||||||
|
<Typography>User: {username}</Typography>
|
||||||
|
<Typography>Message</Typography>
|
||||||
|
<Typography>{message}</Typography>
|
||||||
|
<Typography>Submitted Date {date}</Typography>
|
||||||
|
<Button variant="contained" onClick={handleAccept} color="primary">Accept</Button>
|
||||||
|
<Button variant="contained" onClick={handleDeny} color="secondary">Reject</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
41
components/dashboard/artist/AssetImage.tsx
Normal file
41
components/dashboard/artist/AssetImage.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ImageListItem from '@mui/material/ImageListItem';
|
||||||
|
import { useState } from "react";
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
|
||||||
|
const AssetImage = ({ assetId, requestId }) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
const url = "/api/artist/requests/"+requestId+"/assets/"+assetId
|
||||||
|
const handleImageLoaded = () => {
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ImageListItem key={assetId} onClick={() => setOpenDialog(true)}>
|
||||||
|
<img
|
||||||
|
srcSet={url}
|
||||||
|
src={url}
|
||||||
|
alt={assetId}
|
||||||
|
loading="lazy"
|
||||||
|
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
|
||||||
|
onLoad={handleImageLoaded}
|
||||||
|
/>
|
||||||
|
</ImageListItem>
|
||||||
|
{/* Dialog for displaying full-screen image */}
|
||||||
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
|
||||||
|
<DialogContent>
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt={assetId}
|
||||||
|
style={{ width: '100%', height: 'auto' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssetImage;
|
41
components/dashboard/artist/ReferenceImage.tsx
Normal file
41
components/dashboard/artist/ReferenceImage.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ImageListItem from '@mui/material/ImageListItem';
|
||||||
|
import { useState } from "react";
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
|
||||||
|
const ReferenceImage = ({ referenceId, requestId }) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
const url = "/api/artist/requests/"+requestId+"/references/"+referenceId
|
||||||
|
const handleImageLoaded = () => {
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ImageListItem key={referenceId} onClick={() => setOpenDialog(true)}>
|
||||||
|
<img
|
||||||
|
srcSet={url}
|
||||||
|
src={url}
|
||||||
|
alt={referenceId}
|
||||||
|
loading="lazy"
|
||||||
|
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
|
||||||
|
onLoad={handleImageLoaded}
|
||||||
|
/>
|
||||||
|
</ImageListItem>
|
||||||
|
{/* Dialog for displaying full-screen image */}
|
||||||
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
|
||||||
|
<DialogContent>
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt={referenceId}
|
||||||
|
style={{ width: '100%', height: 'auto' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReferenceImage;
|
96
components/dashboard/artist/editablePortfolio.tsx
Normal file
96
components/dashboard/artist/editablePortfolio.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Grid, ImageList, Box, Tooltip, CircularProgress, Slider, IconButton } from '@mui/material';
|
||||||
|
import { FileUpload } from '@mui/icons-material';
|
||||||
|
import EditableArtistPortfolioImage from './editablePortfolioImage';
|
||||||
|
|
||||||
|
const EditableArtistPortfolio = ({ artistId }) => {
|
||||||
|
const [portfolioData, setPortfolioData] = useState([]);
|
||||||
|
const [columns, setColumns] = useState(2);
|
||||||
|
const [loading, setLoading] = useState(true); // State for loading indicator
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleColumns = (event, value)=>{
|
||||||
|
setColumns(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const response = await fetch('/api/discovery/artist/' + artistId + '/portfolio');
|
||||||
|
const data = await response.json();
|
||||||
|
setPortfolioData(data);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePortfolioUploadImageChange(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('newImage', file);
|
||||||
|
|
||||||
|
fetch('/api/artist/portfolio', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData // Don't set Content-Type, FormData will handle it
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
getData();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error uploading file:', error);
|
||||||
|
// Handle error appropriately
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(loading) ? (
|
||||||
|
<Box sx={{ textAlign: "center", paddingTop: 20 }}>
|
||||||
|
<CircularProgress sx={{ paddingTop: 5 }} />
|
||||||
|
</Box>
|
||||||
|
) :
|
||||||
|
(
|
||||||
|
|
||||||
|
<Grid container spacing={2} sm={12}>
|
||||||
|
<Grid item xs={12} sm={1} sx={{ textAlign: "center" }}>
|
||||||
|
<input
|
||||||
|
id="portfolioUploadInput"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
accept="image/*"
|
||||||
|
type="file"
|
||||||
|
onChange={handlePortfolioUploadImageChange}
|
||||||
|
/>
|
||||||
|
<label htmlFor="portfolioUploadInput">
|
||||||
|
<Tooltip arrow title="Upload Image To Portfolio">
|
||||||
|
<IconButton color="primary" component="span">
|
||||||
|
<FileUpload sx={{fontSize:"2rem"}}/>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</label>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} sm={11} sx={{ textAlign: "center" }}>
|
||||||
|
<Tooltip arrow title="Amount of columns">
|
||||||
|
<Slider
|
||||||
|
defaultValue={columns}
|
||||||
|
aria-labelledby="discrete-slider"
|
||||||
|
valueLabelDisplay="auto"
|
||||||
|
onChange={handleColumns}
|
||||||
|
step={1}
|
||||||
|
marks
|
||||||
|
min={1}
|
||||||
|
max={5}/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} sm={12} sx={{maxHeight:"45rem",overflowY:"scroll"}}>
|
||||||
|
<ImageList variant='masonry' cols={columns} sx={{ width:"100%" }}>
|
||||||
|
{portfolioData.map((item) => (
|
||||||
|
<EditableArtistPortfolioImage artistId={artistId} itemId={item.id} reload={getData}/>
|
||||||
|
))}
|
||||||
|
</ImageList>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default EditableArtistPortfolio
|
59
components/dashboard/artist/editablePortfolioImage.tsx
Normal file
59
components/dashboard/artist/editablePortfolioImage.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
|
import { Dialog, DialogContent, ImageList,ImageListItem, ImageListItemBar } from '@mui/material';
|
||||||
|
import { IconButton } from '@mui/material';
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const EditableArtistPortfolioImage = ({ artistId, itemId, reload }) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [deleting, setDeleting] = useState(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState(false); // State for controlling the dialog
|
||||||
|
|
||||||
|
const handleImageLoaded = () => {
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteButton = () => {
|
||||||
|
setDeleting(true);
|
||||||
|
fetch('/api/artist/portfolio/' + itemId + "/delete", {
|
||||||
|
method: 'DELETE'
|
||||||
|
}).then(response => {
|
||||||
|
reload().then(data => {
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ImageListItem key={itemId} onClick={() => setOpenDialog(true)}>
|
||||||
|
<img
|
||||||
|
srcSet={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
|
||||||
|
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
|
||||||
|
alt={itemId}
|
||||||
|
loading="lazy"
|
||||||
|
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
|
||||||
|
onLoad={handleImageLoaded}
|
||||||
|
/>
|
||||||
|
<ImageListItemBar
|
||||||
|
actionIcon={
|
||||||
|
<IconButton onClick={deleteButton} color="error">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
}>
|
||||||
|
</ImageListItemBar>
|
||||||
|
</ImageListItem>
|
||||||
|
{/* Dialog for displaying full-screen image */}
|
||||||
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
|
||||||
|
<DialogContent>
|
||||||
|
<img
|
||||||
|
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
|
||||||
|
alt={itemId}
|
||||||
|
style={{ width: '100%', height: 'auto' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditableArtistPortfolioImage;
|
@ -1,36 +1,45 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {ImageList, Box, Typography, CircularProgress} from '@mui/material';
|
import {ImageList, Box, Typography, CircularProgress} from '@mui/material';
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ArtistPortfolioImage from './artistPortfolioImage';
|
import ArtistPortfolioImage from './portfolioImage';
|
||||||
|
const ArtistPortfolio = ({masonry,columns,artistId}) => {
|
||||||
const ArtistPortfolio = ({artistId}) => {
|
|
||||||
const [portfolioData, setPortfolioData] = useState([]);
|
const [portfolioData, setPortfolioData] = useState([]);
|
||||||
|
const [profileId, setArtistId] = useState(artistId)
|
||||||
const [loading, setLoading] = useState(true); // State for loading indicator
|
const [loading, setLoading] = useState(true); // State for loading indicator
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const response = await fetch('/api/discovery/artist/'+artistId+'/portfolio');
|
const response = await fetch('/api/discovery/artist/'+profileId+'/portfolio');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setPortfolioData(data);
|
setPortfolioData(data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
console.log(portfolioData)
|
(portfolioData)
|
||||||
getData();
|
getData();
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
(loading) ? (
|
(loading) ? (
|
||||||
<Box sx={{textAlign:"center", paddingTop:20}}>
|
<Box sx={{textAlign:"center", paddingTop:20}}>
|
||||||
<Typography variant="h4" sx={{textAlign:"center"}}>
|
<Typography variant="h4" sx={{textAlign:"center"}}>
|
||||||
Loading...
|
Loading
|
||||||
</Typography>
|
</Typography>
|
||||||
<CircularProgress sx={{paddingTop:5}} />
|
<Box sx={{paddingTop:"2%"}}/>
|
||||||
|
<CircularProgress />
|
||||||
</Box>
|
</Box>
|
||||||
) :
|
) :
|
||||||
(
|
(
|
||||||
<ImageList cols={2} rowHeight={200} sx={{maxHeight:400}}>
|
(masonry) ? (
|
||||||
|
<ImageList variant='masonry' cols={columns} sx={{ width:"100%" }}>
|
||||||
{portfolioData.map((item) => (
|
{portfolioData.map((item) => (
|
||||||
<ArtistPortfolioImage artistId={artistId} itemId={item.id} />
|
<ArtistPortfolioImage artistId={profileId} itemId={item.id} />
|
||||||
))}
|
))}
|
||||||
</ImageList>
|
</ImageList>
|
||||||
|
):(
|
||||||
|
<ImageList cols={columns} sx={{ width:"100%" }}>
|
||||||
|
{portfolioData.map((item) => (
|
||||||
|
<ArtistPortfolioImage artistId={profileId} itemId={item.id} />
|
||||||
|
))}
|
||||||
|
</ImageList>
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
41
components/dashboard/artist/portfolioImage.tsx
Normal file
41
components/dashboard/artist/portfolioImage.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ImageListItem from '@mui/material/ImageListItem';
|
||||||
|
import { useState } from "react";
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
|
||||||
|
const ArtistPortfolioImage = ({ artistId, itemId }) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
|
const handleImageLoaded = () => {
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ImageListItem key={itemId} onClick={() => setOpenDialog(true)}>
|
||||||
|
<img
|
||||||
|
srcSet={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
|
||||||
|
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
|
||||||
|
alt={itemId}
|
||||||
|
loading="lazy"
|
||||||
|
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
|
||||||
|
onLoad={handleImageLoaded}
|
||||||
|
/>
|
||||||
|
</ImageListItem>
|
||||||
|
{/* Dialog for displaying full-screen image */}
|
||||||
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
|
||||||
|
<DialogContent>
|
||||||
|
<img
|
||||||
|
src={process.env.NEXT_PUBLIC_API_URL + `/api/Discovery/Artists/${artistId}/Portfolio/${itemId}`}
|
||||||
|
alt={itemId}
|
||||||
|
style={{ width: '100%', height: 'auto' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArtistPortfolioImage;
|
85
components/dashboard/artist/reviews.tsx
Normal file
85
components/dashboard/artist/reviews.tsx
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { DataGrid } from '@mui/x-data-grid';
|
||||||
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { Rating } from '@mui/material';
|
||||||
|
|
||||||
|
export default function Reviews({artistId}) {
|
||||||
|
const router = useRouter();
|
||||||
|
const columns = [
|
||||||
|
{ field: 'requestId', headerName: 'ID', flex: 0.1},
|
||||||
|
{ field: 'message', headerName: 'Review', flex: 0.5},
|
||||||
|
{ field: 'rating', headerName: 'Rating', flex: 0.2, renderCell: (params) => {
|
||||||
|
return <Rating value={params.row.rating} readOnly />;
|
||||||
|
}}
|
||||||
|
];
|
||||||
|
const [isLoading, setIsLoading] = React.useState(true);
|
||||||
|
const [reviewCount, setReviewCount] = React.useState(null);
|
||||||
|
const [reviewData, setReviewData] = React.useState({});
|
||||||
|
const [paginationModel, setPaginationModel] = React.useState({
|
||||||
|
page: 0,
|
||||||
|
pageSize: 15,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const getReviews = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await fetch('/api/discovery/artist/'+artistId+'/reviews', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
// Assuming your API returns an array under a key like 'reviews'
|
||||||
|
// Adjust this according to your actual API response structure
|
||||||
|
const rows = data.reviews || []; // If 'reviews' doesn't exist, default to an empty array
|
||||||
|
setReviewData(rows);
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getReviewsCount = async () => {
|
||||||
|
const response = await fetch('/api/discovery/artist/'+artistId+'/reviewscount', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
setReviewCount(data);
|
||||||
|
setRowCountState((prevRowCountState) =>
|
||||||
|
data !== undefined
|
||||||
|
? data
|
||||||
|
: prevRowCountState,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some API clients return undefined while loading
|
||||||
|
// Following lines are here to prevent `rowCountState` from being undefined during the loading
|
||||||
|
const [rowCountState, setRowCountState] = React.useState(0);
|
||||||
|
React.useEffect(() => {
|
||||||
|
getReviews();
|
||||||
|
getReviewsCount();
|
||||||
|
}, [reviewCount, setRowCountState,paginationModel]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div style={{ height: '100%', width: '100%' }}>
|
||||||
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||||
|
<DataGrid
|
||||||
|
getRowId={(row) => row.requestId}
|
||||||
|
rows={reviewData}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={rowCountState}
|
||||||
|
loading={isLoading}
|
||||||
|
pageSizeOptions={[15]}
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
paginationMode="server"
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
/>
|
||||||
|
</LocalizationProvider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
53
components/dashboard/artistRequest.tsx
Normal file
53
components/dashboard/artistRequest.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Grid, Card, CardContent, Typography } from '@mui/material';
|
||||||
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
|
|
||||||
|
const ArtistOnboardRequest = () => {
|
||||||
|
const [sellerRequestData, setArtistRequestData] = useState(null);
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const response = await fetch('/api/artist/request');
|
||||||
|
const sellerProfile = await response.json();
|
||||||
|
setArtistRequestData(sellerProfile);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let formattedTime = ""
|
||||||
|
if (sellerRequestData) {
|
||||||
|
const date = new Date(sellerRequestData["requestDate"]);
|
||||||
|
formattedTime = date.toLocaleTimeString('en-US', { month: 'long', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); // Example format
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(sellerRequestData ? (
|
||||||
|
|
||||||
|
<Card sx={{ minWidth: 275 }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
Request Status
|
||||||
|
</Typography>
|
||||||
|
{(sellerRequestData["accepted"] ? (
|
||||||
|
<Typography variant="body2" color="text.warning" component="div">Accepted</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography variant="h6" color="text.warning" component="div">Pending</Typography>
|
||||||
|
))}
|
||||||
|
<Typography variant="body2" color="text.secondary" component="div">Request submitted on {formattedTime ?? ''}</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>) : (
|
||||||
|
|
||||||
|
<Box sx={{textAlign:"center", paddingTop:"5%"}}>
|
||||||
|
<Typography variant="h4" sx={{textAlign:"center"}}>
|
||||||
|
Loading
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ paddingTop: 5 }} />
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default ArtistOnboardRequest
|
41
components/dashboard/customer/AssetImage.tsx
Normal file
41
components/dashboard/customer/AssetImage.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ImageListItem from '@mui/material/ImageListItem';
|
||||||
|
import { useState } from "react";
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
|
||||||
|
const AssetImage = ({ assetId, requestId }) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
const url = "/api/requests/"+requestId+"/assets/"+assetId
|
||||||
|
const handleImageLoaded = () => {
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ImageListItem key={assetId} onClick={() => setOpenDialog(true)}>
|
||||||
|
<img
|
||||||
|
srcSet={url}
|
||||||
|
src={url}
|
||||||
|
alt={assetId}
|
||||||
|
loading="lazy"
|
||||||
|
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
|
||||||
|
onLoad={handleImageLoaded}
|
||||||
|
/>
|
||||||
|
</ImageListItem>
|
||||||
|
{/* Dialog for displaying full-screen image */}
|
||||||
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
|
||||||
|
<DialogContent>
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt={assetId}
|
||||||
|
style={{ width: '100%', height: 'auto' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssetImage;
|
41
components/dashboard/customer/ReferenceImage.tsx
Normal file
41
components/dashboard/customer/ReferenceImage.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ImageListItem from '@mui/material/ImageListItem';
|
||||||
|
import { useState } from "react";
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
|
||||||
|
const ReferenceImage = ({ referenceId, requestId }) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
const url = "/api/requests/"+requestId+"/references/"+referenceId
|
||||||
|
const handleImageLoaded = () => {
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ImageListItem key={referenceId} onClick={() => setOpenDialog(true)}>
|
||||||
|
<img
|
||||||
|
srcSet={url}
|
||||||
|
src={url}
|
||||||
|
alt={referenceId}
|
||||||
|
loading="lazy"
|
||||||
|
style={{ filter: loaded ? 'blur(0)' : 'blur(10px)', backgroundColor: 'grey', cursor: 'pointer' }}
|
||||||
|
onLoad={handleImageLoaded}
|
||||||
|
/>
|
||||||
|
</ImageListItem>
|
||||||
|
{/* Dialog for displaying full-screen image */}
|
||||||
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)}>
|
||||||
|
<DialogContent>
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt={referenceId}
|
||||||
|
style={{ width: '100%', height: 'auto' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReferenceImage;
|
137
components/dashboard/customer/orders.tsx
Normal file
137
components/dashboard/customer/orders.tsx
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { DataGrid } from '@mui/x-data-grid';
|
||||||
|
import { GridColDef } from '@mui/x-data-grid';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import { Button, Typography } from '@mui/material';
|
||||||
|
import CurrencyTextField from '@lupus-ai/mui-currency-textfield';
|
||||||
|
import { DateField } from '@mui/x-date-pickers/DateField';
|
||||||
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import {Check, Refresh } from '@mui/icons-material';
|
||||||
|
import PriceCheckIcon from '@mui/icons-material/PriceCheck';
|
||||||
|
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
|
||||||
|
import AssignmentLateIcon from '@mui/icons-material/AssignmentLate';
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useMediaQuery } from '@mui/material';
|
||||||
|
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
|
||||||
|
export default function CustomerOrders() {
|
||||||
|
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('sm')); // Check if the screen size is small
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ field: 'id', headerName: 'ID', flex: 0.1},
|
||||||
|
{ field: 'status', headerName: 'Status', flex: 0.15,
|
||||||
|
renderCell: (params) => {
|
||||||
|
if(params.row.completed){
|
||||||
|
return <Chip icon={<Check />} label="Completed" variant="outlined" color="success" />
|
||||||
|
}
|
||||||
|
else if(params.row.paid){
|
||||||
|
return <Chip icon={<PriceCheckIcon />} label="Paid" variant="outlined" color="success" />
|
||||||
|
}
|
||||||
|
else if(params.row.accepted && params.row.paid==false){
|
||||||
|
return <Chip icon={<PriceCheckIcon />} label="Pending Payment" variant="outlined" color="warning" />
|
||||||
|
}
|
||||||
|
else if(params.row.accepted && params.row.paid){
|
||||||
|
return <Chip icon={<AssignmentTurnedInIcon />} label="Accepted" variant="outlined" color="info" />
|
||||||
|
}
|
||||||
|
else if(params.row.declined){
|
||||||
|
return <Chip icon={<AssignmentLateIcon />} label="Declined" variant="outlined" color="error" />
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return <Chip icon={<Refresh />} label="Pending" variant="outlined" color="secondary" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ field: 'amount', headerName: '$', flex: 0.1, renderCell: (params) => {
|
||||||
|
return <CurrencyTextField size="small" fullWidth value={params.row.amount} currencySymbol="$" disabled />;
|
||||||
|
}},
|
||||||
|
];
|
||||||
|
if(!isSmallScreen){
|
||||||
|
columns.push(
|
||||||
|
{ field: 'requestDate', headerName: 'Request Date', flex:0.15, type: 'date' ,
|
||||||
|
valueGetter: (params) => { return new Date(params.row.requestDate); }} );
|
||||||
|
}
|
||||||
|
const [isLoading, setIsLoading] = React.useState(true);
|
||||||
|
const [requestCount, setRequestCount] = React.useState(null);
|
||||||
|
const [requestData, setRequestData] = React.useState({});
|
||||||
|
const [paginationModel, setPaginationModel] = React.useState({
|
||||||
|
page: 0,
|
||||||
|
pageSize: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const getRequests = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await fetch('/api/requests', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
completed: true, // Example query parameter
|
||||||
|
declined: true, // Example query parameter
|
||||||
|
accepted: true, // Example query parameter
|
||||||
|
paid: true, // Example query parameter
|
||||||
|
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
|
||||||
|
pageSize: paginationModel.pageSize
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
setRequestData(data);
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
const getRequestsCount = async () => {
|
||||||
|
const response = await fetch('/api/requestcount', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
completed: true, // Example query parameter
|
||||||
|
declined: true, // Example query parameter
|
||||||
|
accepted: true, // Example query parameter
|
||||||
|
paid: true, // Example query parameter
|
||||||
|
offset: paginationModel.page*paginationModel.pageSize, // Example query parameter
|
||||||
|
pageSize: paginationModel.pageSize
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
setRequestCount(data);
|
||||||
|
setRowCountState((prevRowCountState) =>
|
||||||
|
data !== undefined
|
||||||
|
? data
|
||||||
|
: prevRowCountState,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some API clients return undefined while loading
|
||||||
|
// Following lines are here to prevent `rowCountState` from being undefined during the loading
|
||||||
|
const [rowCountState, setRowCountState] = React.useState(0);
|
||||||
|
React.useEffect(() => {
|
||||||
|
getRequests();
|
||||||
|
getRequestsCount();
|
||||||
|
}, [requestCount, setRowCountState,paginationModel]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
|
||||||
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||||
|
<DataGrid
|
||||||
|
minHeight={"500px"}
|
||||||
|
rows={requestData}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={rowCountState}
|
||||||
|
loading={isLoading}
|
||||||
|
pageSizeOptions={[5]}
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
paginationMode="server"
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
/>
|
||||||
|
</LocalizationProvider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,139 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { useUser } from "@auth0/nextjs-auth0/client";
|
|
||||||
import AppBar from '@mui/material/AppBar';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import Menu from '@mui/material/Menu';
|
|
||||||
import Container from '@mui/material/Container';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
|
||||||
import { Chip, Icon } from '@mui/material';
|
|
||||||
import {
|
|
||||||
NovuProvider,
|
|
||||||
PopoverNotificationCenter,
|
|
||||||
NotificationBell,
|
|
||||||
} from '@novu/notification-center';
|
|
||||||
|
|
||||||
|
|
||||||
type HeaderProps = {
|
|
||||||
user?: any;
|
|
||||||
loading: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const settings = ['Seller Dashboard', 'Account', 'Dashboard', 'Logout'];
|
|
||||||
|
|
||||||
function ResponsiveAppBar() {
|
|
||||||
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
|
|
||||||
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(null);
|
|
||||||
|
|
||||||
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
|
||||||
setAnchorElNav(event.currentTarget);
|
|
||||||
};
|
|
||||||
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
|
||||||
setAnchorElUser(event.currentTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCloseNavMenu = () => {
|
|
||||||
setAnchorElNav(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCloseUserMenu = () => {
|
|
||||||
setAnchorElUser(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { user, isLoading } = useUser();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppBar color="primary" position="static">
|
|
||||||
<Container maxWidth="xl">
|
|
||||||
<Toolbar disableGutters>
|
|
||||||
<Box sx={{ flex:1, textAlign:"center" }}>
|
|
||||||
<Typography
|
|
||||||
href="/"
|
|
||||||
variant="h6"
|
|
||||||
noWrap
|
|
||||||
color="secondary"
|
|
||||||
component="a"
|
|
||||||
sx={{
|
|
||||||
mr: 2,
|
|
||||||
paddingLeft: '1rem',
|
|
||||||
display: { xs: 'flex', md: 'flex' },
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
fontWeight: 700,
|
|
||||||
letterSpacing: '0rem',
|
|
||||||
textDecoration: 'none',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
REQUEST.BOX
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{
|
|
||||||
user ? (
|
|
||||||
<>
|
|
||||||
<Box>
|
|
||||||
<NovuProvider subscriberId={'on-boarding-subscriber-id-123'} applicationIdentifier={'9SKjzgN_odAF'}>
|
|
||||||
<PopoverNotificationCenter colorScheme={'light'}>
|
|
||||||
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
|
|
||||||
</PopoverNotificationCenter>
|
|
||||||
</NovuProvider>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Chip
|
|
||||||
onClick={handleOpenUserMenu}
|
|
||||||
label={user.name}
|
|
||||||
color="secondary"
|
|
||||||
variant={'outlined'}
|
|
||||||
style={{ marginLeft: '10px', minWidth: '5rem', maxWidth:'10rem' }}
|
|
||||||
/>
|
|
||||||
<Menu
|
|
||||||
sx={{ mt: '45px' }}
|
|
||||||
id="menu-appbar"
|
|
||||||
anchorEl={anchorElUser}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: 'top',
|
|
||||||
horizontal: 'right',
|
|
||||||
}}
|
|
||||||
keepMounted
|
|
||||||
transformOrigin={{
|
|
||||||
vertical: 'top',
|
|
||||||
horizontal: 'right',
|
|
||||||
}}
|
|
||||||
open={Boolean(anchorElUser)}
|
|
||||||
onClose={handleCloseUserMenu}
|
|
||||||
>
|
|
||||||
<MenuItem key="artistDashboard" onClick={handleCloseUserMenu}>
|
|
||||||
<Button fullWidth color="secondary" variant='contained' href="/artistDashboard">Artist Dashboard</Button>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="myOrders" onClick={handleCloseUserMenu}>
|
|
||||||
<Button fullWidth color="primary" href="profile">My Orders</Button>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="settings" onClick={handleCloseUserMenu}>
|
|
||||||
<Button fullWidth color="primary" href="/settings">Settings</Button>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem key="logout" onClick={handleCloseUserMenu}>
|
|
||||||
<Button fullWidth color="error" href="/api/auth/logout">Logout</Button>
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Button key="login" href='/api/auth/login' onClick={handleCloseNavMenu} sx={{ my: 2, display: 'block', color:"white" }}>
|
|
||||||
Login
|
|
||||||
</Button>
|
|
||||||
<Button key="signup" href='/api/auth/login' onClick={handleCloseNavMenu} sx={{ my: 2, display: 'block', color:"white" }}>
|
|
||||||
Signup
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
|
|
||||||
)}
|
|
||||||
|
|
||||||
</Toolbar>
|
|
||||||
</Container>
|
|
||||||
</AppBar>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default ResponsiveAppBar;
|
|
@ -1,41 +0,0 @@
|
|||||||
import Head from "next/head";
|
|
||||||
import Header from "./header";
|
|
||||||
|
|
||||||
type LayoutProps = {
|
|
||||||
user?: any;
|
|
||||||
loading?: boolean;
|
|
||||||
children: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Layout = ({ user, loading = false, children }: LayoutProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>comissions.app</title>
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
<Header user={user} loading={loading} />
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div className="container">{children}</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<style jsx>{`
|
|
||||||
.container {
|
|
||||||
max-width: 42rem;
|
|
||||||
margin: 1.5rem auto;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
<style jsx global>{`
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
color: #333;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Layout;
|
|
@ -1,31 +0,0 @@
|
|||||||
import { useEffect, useRef } from "react"
|
|
||||||
|
|
||||||
export default function SwipeableViews(
|
|
||||||
{ className = "", index, onChangeIndex, ...rootProps }:
|
|
||||||
{ index: number, onChangeIndex: (index: number) => void } & React.HTMLProps<HTMLDivElement>
|
|
||||||
) {
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
|
||||||
const scrollTimeout = useRef<number>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
containerRef.current?.children[index]?.scrollIntoView({ behavior: "smooth" })
|
|
||||||
}, [index])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
{...rootProps}
|
|
||||||
ref={containerRef}
|
|
||||||
className={
|
|
||||||
"flex snap-x snap-mandatory items-stretch overflow-x-scroll " +
|
|
||||||
"*:w-full *:flex-shrink-0 *:snap-center " + className
|
|
||||||
}
|
|
||||||
onScroll={({ currentTarget }) => {
|
|
||||||
if (scrollTimeout.current) clearTimeout(scrollTimeout.current)
|
|
||||||
scrollTimeout.current = window.setTimeout(() => {
|
|
||||||
const pageWidth = currentTarget.scrollWidth / currentTarget.children.length
|
|
||||||
onChangeIndex(Math.round(currentTarget.scrollLeft / pageWidth))
|
|
||||||
}, 100)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
0
components/test.tsx
Normal file
0
components/test.tsx
Normal file
36
configs/themeConfig.tsx
Normal file
36
configs/themeConfig.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { PaletteMode } from '@mui/material'
|
||||||
|
|
||||||
|
// ** Types
|
||||||
|
import { ContentWidth } from '../core/layouts/types'
|
||||||
|
|
||||||
|
type ThemeConfig = {
|
||||||
|
mode: PaletteMode
|
||||||
|
templateName: string
|
||||||
|
routingLoader: boolean
|
||||||
|
disableRipple: boolean
|
||||||
|
navigationSize: number
|
||||||
|
menuTextTruncate: boolean
|
||||||
|
contentWidth: ContentWidth
|
||||||
|
responsiveFontSizes: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeConfig: ThemeConfig = {
|
||||||
|
// ** Layout Configs
|
||||||
|
templateName: 'Request.Box' /* App Name */,
|
||||||
|
mode: 'dark' /* light | dark */,
|
||||||
|
contentWidth: 'boxed' /* full | boxed */,
|
||||||
|
|
||||||
|
// ** Routing Configs
|
||||||
|
routingLoader: true /* true | false */,
|
||||||
|
|
||||||
|
// ** Navigation (Menu) Configs
|
||||||
|
menuTextTruncate: true /* true | false */,
|
||||||
|
navigationSize: 260 /* Number in PX(Pixels) /*! Note: This is for Vertical navigation menu only */,
|
||||||
|
|
||||||
|
// ** Other Configs
|
||||||
|
responsiveFontSizes: true /* true | false */,
|
||||||
|
disableRipple: false /* true | false */
|
||||||
|
}
|
||||||
|
|
||||||
|
export default themeConfig
|
@ -0,0 +1,54 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Card from '@mui/material/Card'
|
||||||
|
import Avatar from '@mui/material/Avatar'
|
||||||
|
import IconButton from '@mui/material/IconButton'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import CardContent from '@mui/material/CardContent'
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import DotsVertical from 'mdi-material-ui/DotsVertical'
|
||||||
|
|
||||||
|
// ** Types Imports
|
||||||
|
import { CardStatsVerticalProps } from '../core/components/card-statistics/types'
|
||||||
|
|
||||||
|
const CardStatsVertical = (props: CardStatsVerticalProps) => {
|
||||||
|
// ** Props
|
||||||
|
const { title, subtitle, color, icon, stats, trend, trendNumber } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Box sx={{ display: 'flex', marginBottom: 5.5, alignItems: 'flex-start', justifyContent: 'space-between' }}>
|
||||||
|
<Avatar sx={{ boxShadow: 3, marginRight: 4, color: 'common.white', backgroundColor: `${color}.main` }}>
|
||||||
|
{icon}
|
||||||
|
</Avatar>
|
||||||
|
<IconButton size='small' aria-label='settings' className='card-more-options' sx={{ color: 'text.secondary' }}>
|
||||||
|
<DotsVertical />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
<Typography sx={{ fontWeight: 600, fontSize: '0.875rem' }}>{title}</Typography>
|
||||||
|
<Box sx={{ marginTop: 1.5, display: 'flex', flexWrap: 'wrap', marginBottom: 1.5, alignItems: 'flex-start' }}>
|
||||||
|
<Typography variant='h6' sx={{ mr: 2 }}>
|
||||||
|
{stats}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
component='sup'
|
||||||
|
variant='caption'
|
||||||
|
sx={{ color: trend === 'positive' ? 'success.main' : 'error.main' }}
|
||||||
|
>
|
||||||
|
{trendNumber}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption'>{subtitle}</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardStatsVertical
|
||||||
|
|
||||||
|
CardStatsVertical.defaultProps = {
|
||||||
|
color: 'primary',
|
||||||
|
trend: 'positive'
|
||||||
|
}
|
15
core/components/card-statistics/types.ts
Normal file
15
core/components/card-statistics/types.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** Types
|
||||||
|
import { ThemeColor } from '../core/layouts/types'
|
||||||
|
|
||||||
|
export type CardStatsVerticalProps = {
|
||||||
|
title: string
|
||||||
|
stats: string
|
||||||
|
icon: ReactNode
|
||||||
|
subtitle: string
|
||||||
|
color?: ThemeColor
|
||||||
|
trendNumber: string
|
||||||
|
trend?: 'positive' | 'negative'
|
||||||
|
}
|
7
core/components/react-apexcharts/index.tsx
Normal file
7
core/components/react-apexcharts/index.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// ** Next Import
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
|
// ! To avoid 'Window is not defined' error
|
||||||
|
const ReactApexcharts = dynamic(() => import('react-apexcharts'), { ssr: false })
|
||||||
|
|
||||||
|
export default ReactApexcharts
|
47
core/components/scroll-to-top/index.tsx
Normal file
47
core/components/scroll-to-top/index.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Zoom from '@mui/material/Zoom'
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import useScrollTrigger from '@mui/material/useScrollTrigger'
|
||||||
|
|
||||||
|
interface ScrollToTopProps {
|
||||||
|
className?: string
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScrollToTopStyled = styled('div')(({ theme }) => ({
|
||||||
|
zIndex: 11,
|
||||||
|
position: 'fixed',
|
||||||
|
right: theme.spacing(6),
|
||||||
|
bottom: theme.spacing(10)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const ScrollToTop = (props: ScrollToTopProps) => {
|
||||||
|
// ** Props
|
||||||
|
const { children, className } = props
|
||||||
|
|
||||||
|
// ** init trigger
|
||||||
|
const trigger = useScrollTrigger({
|
||||||
|
threshold: 400,
|
||||||
|
disableHysteresis: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
const anchor = document.querySelector('body')
|
||||||
|
if (anchor) {
|
||||||
|
anchor.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Zoom in={trigger}>
|
||||||
|
<ScrollToTopStyled className={className} onClick={handleClick} role='presentation'>
|
||||||
|
{children}
|
||||||
|
</ScrollToTopStyled>
|
||||||
|
</Zoom>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScrollToTop
|
47
core/context/settingsContext.tsx
Normal file
47
core/context/settingsContext.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { createContext, useState, ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import { PaletteMode } from '@mui/material'
|
||||||
|
|
||||||
|
// ** ThemeConfig Import
|
||||||
|
import themeConfig from '../../configs/themeConfig'
|
||||||
|
|
||||||
|
// ** Types Import
|
||||||
|
import { ThemeColor, ContentWidth } from '../../core/layouts/types'
|
||||||
|
|
||||||
|
export type Settings = {
|
||||||
|
mode: PaletteMode
|
||||||
|
themeColor: ThemeColor
|
||||||
|
contentWidth: ContentWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SettingsContextValue = {
|
||||||
|
settings: Settings
|
||||||
|
saveSettings: (updatedSettings: Settings) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialSettings: Settings = {
|
||||||
|
themeColor: 'primary',
|
||||||
|
mode: themeConfig.mode,
|
||||||
|
contentWidth: themeConfig.contentWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** Create Context
|
||||||
|
export const SettingsContext = createContext<SettingsContextValue>({
|
||||||
|
saveSettings: () => null,
|
||||||
|
settings: initialSettings
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
// ** State
|
||||||
|
const [settings, setSettings] = useState<Settings>({ ...initialSettings })
|
||||||
|
|
||||||
|
const saveSettings = (updatedSettings: Settings) => {
|
||||||
|
setSettings(updatedSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <SettingsContext.Provider value={{ settings, saveSettings }}>{children}</SettingsContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingsConsumer = SettingsContext.Consumer
|
4
core/hooks/useSettings.ts
Normal file
4
core/hooks/useSettings.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { useContext } from 'react'
|
||||||
|
import { SettingsContext, SettingsContextValue } from '../../core/context/settingsContext'
|
||||||
|
|
||||||
|
export const useSettings = (): SettingsContextValue => useContext(SettingsContext)
|
40
core/layouts/BlankLayout.tsx
Normal file
40
core/layouts/BlankLayout.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import Box, { BoxProps } from '@mui/material/Box'
|
||||||
|
|
||||||
|
// ** Types
|
||||||
|
import { BlankLayoutProps } from './types'
|
||||||
|
|
||||||
|
// Styled component for Blank Layout component
|
||||||
|
const BlankLayoutWrapper = styled(Box)<BoxProps>(({ theme }) => ({
|
||||||
|
height: '100vh',
|
||||||
|
|
||||||
|
// For V1 Blank layout pages
|
||||||
|
'& .content-center': {
|
||||||
|
display: 'flex',
|
||||||
|
minHeight: '100vh',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: theme.spacing(5)
|
||||||
|
},
|
||||||
|
|
||||||
|
// For V2 Blank layout pages
|
||||||
|
'& .content-right': {
|
||||||
|
display: 'flex',
|
||||||
|
minHeight: '100vh',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
position: 'relative'
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const BlankLayout = ({ children }: BlankLayoutProps) => {
|
||||||
|
return (
|
||||||
|
<BlankLayoutWrapper className='layout-wrapper'>
|
||||||
|
<Box className='app-content' sx={{ minHeight: '100vh', overflowX: 'hidden', position: 'relative' }}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
</BlankLayoutWrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlankLayout
|
118
core/layouts/VerticalLayout.tsx
Normal file
118
core/layouts/VerticalLayout.tsx
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Fab from '@mui/material/Fab'
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import Box, { BoxProps } from '@mui/material/Box'
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import ArrowUp from 'mdi-material-ui/ArrowUp'
|
||||||
|
|
||||||
|
// ** Theme Config Import
|
||||||
|
import themeConfig from '../../configs/themeConfig'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { LayoutProps } from '../../core/layouts/types'
|
||||||
|
|
||||||
|
// ** Components
|
||||||
|
import AppBar from './components/vertical/appBar'
|
||||||
|
import Navigation from './components/vertical/navigation'
|
||||||
|
import Footer from './components/shared-components/footer'
|
||||||
|
import ScrollToTop from '../../core/components/scroll-to-top'
|
||||||
|
|
||||||
|
// ** Styled Component
|
||||||
|
import DatePickerWrapper from '../../core/styles/libs/react-datepicker'
|
||||||
|
|
||||||
|
const VerticalLayoutWrapper = styled('div')({
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex'
|
||||||
|
})
|
||||||
|
|
||||||
|
const MainContentWrapper = styled(Box)<BoxProps>({
|
||||||
|
flexGrow: 1,
|
||||||
|
minWidth: 0,
|
||||||
|
display: 'flex',
|
||||||
|
minHeight: '100vh',
|
||||||
|
flexDirection: 'column'
|
||||||
|
})
|
||||||
|
|
||||||
|
const ContentWrapper = styled('main')(({ theme }) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
width: '100%',
|
||||||
|
padding: theme.spacing(6),
|
||||||
|
transition: 'padding .25s ease-in-out',
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
paddingLeft: theme.spacing(4),
|
||||||
|
paddingRight: theme.spacing(4)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const VerticalLayout = (props: LayoutProps) => {
|
||||||
|
// ** Props
|
||||||
|
const { settings, children, scrollToTop } = props
|
||||||
|
|
||||||
|
// ** Vars
|
||||||
|
const { contentWidth } = settings
|
||||||
|
const navWidth = themeConfig.navigationSize
|
||||||
|
|
||||||
|
// ** States
|
||||||
|
const [navVisible, setNavVisible] = useState<boolean>(false)
|
||||||
|
|
||||||
|
// ** Toggle Functions
|
||||||
|
const toggleNavVisibility = () => setNavVisible(!navVisible)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<VerticalLayoutWrapper className='layout-wrapper'>
|
||||||
|
{/* Navigation Menu */}
|
||||||
|
<Navigation
|
||||||
|
navWidth={navWidth}
|
||||||
|
navVisible={navVisible}
|
||||||
|
setNavVisible={setNavVisible}
|
||||||
|
toggleNavVisibility={toggleNavVisibility}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<MainContentWrapper className='layout-content-wrapper'>
|
||||||
|
{/* AppBar Component */}
|
||||||
|
<AppBar toggleNavVisibility={toggleNavVisibility} {...props} />
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<ContentWrapper
|
||||||
|
className='layout-page-content'
|
||||||
|
sx={{
|
||||||
|
...(contentWidth === 'boxed' && {
|
||||||
|
mx: 'auto',
|
||||||
|
'@media (min-width:1440px)': { maxWidth: 1440 },
|
||||||
|
'@media (min-width:1200px)': { maxWidth: '100%' }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ContentWrapper>
|
||||||
|
|
||||||
|
{/* Footer Component */}
|
||||||
|
<Footer {...props} />
|
||||||
|
|
||||||
|
{/* Portal for React Datepicker */}
|
||||||
|
<DatePickerWrapper sx={{ zIndex: 11 }}>
|
||||||
|
<Box id='react-datepicker-portal'></Box>
|
||||||
|
</DatePickerWrapper>
|
||||||
|
</MainContentWrapper>
|
||||||
|
</VerticalLayoutWrapper>
|
||||||
|
|
||||||
|
{/* Scroll to top button */}
|
||||||
|
{scrollToTop ? (
|
||||||
|
scrollToTop(props)
|
||||||
|
) : (
|
||||||
|
<ScrollToTop className='mui-fixed'>
|
||||||
|
<Fab color='primary' size='small' aria-label='scroll back to top'>
|
||||||
|
<ArrowUp />
|
||||||
|
</Fab>
|
||||||
|
</ScrollToTop>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerticalLayout
|
40
core/layouts/components/shared-components/ModeToggler.tsx
Normal file
40
core/layouts/components/shared-components/ModeToggler.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { PaletteMode } from '@mui/material'
|
||||||
|
import IconButton from '@mui/material/IconButton'
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import WeatherNight from 'mdi-material-ui/WeatherNight'
|
||||||
|
import WeatherSunny from 'mdi-material-ui/WeatherSunny'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../core/context/settingsContext'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
settings: Settings
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModeToggler = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { settings, saveSettings } = props
|
||||||
|
|
||||||
|
const handleModeChange = (mode: PaletteMode) => {
|
||||||
|
saveSettings({ ...settings, mode })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleModeToggle = () => {
|
||||||
|
if (settings.mode === 'light') {
|
||||||
|
handleModeChange('dark')
|
||||||
|
} else {
|
||||||
|
handleModeChange('light')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton color='inherit' aria-haspopup='true' onClick={handleModeToggle}>
|
||||||
|
{settings.mode === 'dark' ? <WeatherSunny /> : <WeatherNight />}
|
||||||
|
</IconButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModeToggler
|
@ -0,0 +1,217 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { useState, SyntheticEvent, Fragment, ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Chip from '@mui/material/Chip'
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
|
import IconButton from '@mui/material/IconButton'
|
||||||
|
import { styled, Theme } from '@mui/material/styles'
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||||
|
import MuiMenu, { MenuProps } from '@mui/material/Menu'
|
||||||
|
import MuiAvatar, { AvatarProps } from '@mui/material/Avatar'
|
||||||
|
import MuiMenuItem, { MenuItemProps } from '@mui/material/MenuItem'
|
||||||
|
import Typography, { TypographyProps } from '@mui/material/Typography'
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import BellOutline from 'mdi-material-ui/BellOutline'
|
||||||
|
|
||||||
|
// ** Third Party Components
|
||||||
|
import PerfectScrollbarComponent from 'react-perfect-scrollbar'
|
||||||
|
|
||||||
|
// ** Styled Menu component
|
||||||
|
const Menu = styled(MuiMenu)<MenuProps>(({ theme }) => ({
|
||||||
|
'& .MuiMenu-paper': {
|
||||||
|
width: 380,
|
||||||
|
overflow: 'hidden',
|
||||||
|
marginTop: theme.spacing(4),
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
width: '100%'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .MuiMenu-list': {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// ** Styled MenuItem component
|
||||||
|
const MenuItem = styled(MuiMenuItem)<MenuItemProps>(({ theme }) => ({
|
||||||
|
paddingTop: theme.spacing(3),
|
||||||
|
paddingBottom: theme.spacing(3),
|
||||||
|
borderBottom: `1px solid ${theme.palette.divider}`
|
||||||
|
}))
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
maxHeight: 349,
|
||||||
|
'& .MuiMenuItem-root:last-of-type': {
|
||||||
|
border: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** Styled PerfectScrollbar component
|
||||||
|
const PerfectScrollbar = styled(PerfectScrollbarComponent)({
|
||||||
|
...styles
|
||||||
|
})
|
||||||
|
|
||||||
|
// ** Styled Avatar component
|
||||||
|
const Avatar = styled(MuiAvatar)<AvatarProps>({
|
||||||
|
width: '2.375rem',
|
||||||
|
height: '2.375rem',
|
||||||
|
fontSize: '1.125rem'
|
||||||
|
})
|
||||||
|
|
||||||
|
// ** Styled component for the title in MenuItems
|
||||||
|
const MenuItemTitle = styled(Typography)<TypographyProps>(({ theme }) => ({
|
||||||
|
fontWeight: 600,
|
||||||
|
flex: '1 1 100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
marginBottom: theme.spacing(0.75)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// ** Styled component for the subtitle in MenuItems
|
||||||
|
const MenuItemSubtitle = styled(Typography)<TypographyProps>({
|
||||||
|
flex: '1 1 100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
})
|
||||||
|
|
||||||
|
const NotificationDropdown = () => {
|
||||||
|
// ** States
|
||||||
|
const [anchorEl, setAnchorEl] = useState<(EventTarget & Element) | null>(null)
|
||||||
|
|
||||||
|
// ** Hook
|
||||||
|
const hidden = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'))
|
||||||
|
|
||||||
|
const handleDropdownOpen = (event: SyntheticEvent) => {
|
||||||
|
setAnchorEl(event.currentTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDropdownClose = () => {
|
||||||
|
setAnchorEl(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScrollWrapper = ({ children }: { children: ReactNode }) => {
|
||||||
|
if (hidden) {
|
||||||
|
return <Box sx={{ ...styles, overflowY: 'auto', overflowX: 'hidden' }}>{children}</Box>
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<PerfectScrollbar options={{ wheelPropagation: false, suppressScrollX: true }}>{children}</PerfectScrollbar>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<IconButton color='inherit' aria-haspopup='true' onClick={handleDropdownOpen} aria-controls='customized-menu'>
|
||||||
|
<BellOutline />
|
||||||
|
</IconButton>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={Boolean(anchorEl)}
|
||||||
|
onClose={handleDropdownClose}
|
||||||
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||||
|
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||||
|
>
|
||||||
|
<MenuItem disableRipple>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
||||||
|
<Typography sx={{ fontWeight: 600 }}>Notifications</Typography>
|
||||||
|
<Chip
|
||||||
|
size='small'
|
||||||
|
label='8 New'
|
||||||
|
color='primary'
|
||||||
|
sx={{ height: 20, fontSize: '0.75rem', fontWeight: 500, borderRadius: '10px' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<ScrollWrapper>
|
||||||
|
<MenuItem onClick={handleDropdownClose}>
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar alt='Flora' src='/images/avatars/4.png' />
|
||||||
|
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
|
||||||
|
<MenuItemTitle>Congratulation Flora! 🎉</MenuItemTitle>
|
||||||
|
<MenuItemSubtitle variant='body2'>Won the monthly best seller badge</MenuItemSubtitle>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption' sx={{ color: 'text.disabled' }}>
|
||||||
|
Today
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleDropdownClose}>
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar sx={{ color: 'common.white', backgroundColor: 'primary.main' }}>VU</Avatar>
|
||||||
|
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
|
||||||
|
<MenuItemTitle>New user registered.</MenuItemTitle>
|
||||||
|
<MenuItemSubtitle variant='body2'>5 hours ago</MenuItemSubtitle>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption' sx={{ color: 'text.disabled' }}>
|
||||||
|
Yesterday
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleDropdownClose}>
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar alt='message' src='/images/avatars/5.png' />
|
||||||
|
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
|
||||||
|
<MenuItemTitle>New message received 👋🏻</MenuItemTitle>
|
||||||
|
<MenuItemSubtitle variant='body2'>You have 10 unread messages</MenuItemSubtitle>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption' sx={{ color: 'text.disabled' }}>
|
||||||
|
11 Aug
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleDropdownClose}>
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||||
|
<img width={38} height={38} alt='paypal' src='/images/misc/paypal.png' />
|
||||||
|
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
|
||||||
|
<MenuItemTitle>Paypal</MenuItemTitle>
|
||||||
|
<MenuItemSubtitle variant='body2'>Received Payment</MenuItemSubtitle>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption' sx={{ color: 'text.disabled' }}>
|
||||||
|
25 May
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleDropdownClose}>
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Avatar alt='order' src='/images/avatars/3.png' />
|
||||||
|
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
|
||||||
|
<MenuItemTitle>Revised Order 📦</MenuItemTitle>
|
||||||
|
<MenuItemSubtitle variant='body2'>New order revised from john</MenuItemSubtitle>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption' sx={{ color: 'text.disabled' }}>
|
||||||
|
19 Mar
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleDropdownClose}>
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||||
|
<img width={38} height={38} alt='chart' src='/images/misc/chart.png' />
|
||||||
|
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
|
||||||
|
<MenuItemTitle>Finance report has been generated</MenuItemTitle>
|
||||||
|
<MenuItemSubtitle variant='body2'>25 hrs ago</MenuItemSubtitle>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption' sx={{ color: 'text.disabled' }}>
|
||||||
|
27 Dec
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
</ScrollWrapper>
|
||||||
|
<MenuItem
|
||||||
|
disableRipple
|
||||||
|
sx={{ py: 3.5, borderBottom: 0, borderTop: theme => `1px solid ${theme.palette.divider}` }}
|
||||||
|
>
|
||||||
|
<Button fullWidth variant='contained' onClick={handleDropdownClose}>
|
||||||
|
Read All Notifications
|
||||||
|
</Button>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotificationDropdown
|
142
core/layouts/components/shared-components/UserDropdown.tsx
Normal file
142
core/layouts/components/shared-components/UserDropdown.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { useState, SyntheticEvent, Fragment, useEffect } from 'react'
|
||||||
|
|
||||||
|
// ** Next Import
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Menu from '@mui/material/Menu'
|
||||||
|
import Badge from '@mui/material/Badge'
|
||||||
|
import Avatar from '@mui/material/Avatar'
|
||||||
|
import Divider from '@mui/material/Divider'
|
||||||
|
import MenuItem from '@mui/material/MenuItem'
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import CogOutline from 'mdi-material-ui/CogOutline'
|
||||||
|
import CurrencyUsd from 'mdi-material-ui/CurrencyUsd'
|
||||||
|
import EmailOutline from 'mdi-material-ui/EmailOutline'
|
||||||
|
import LogoutVariant from 'mdi-material-ui/LogoutVariant'
|
||||||
|
import AccountOutline from 'mdi-material-ui/AccountOutline'
|
||||||
|
import MessageOutline from 'mdi-material-ui/MessageOutline'
|
||||||
|
import HelpCircleOutline from 'mdi-material-ui/HelpCircleOutline'
|
||||||
|
|
||||||
|
import { useUser } from '@auth0/nextjs-auth0/client'
|
||||||
|
|
||||||
|
// ** Styled Components
|
||||||
|
const BadgeContentSpan = styled('span')(({ theme }) => ({
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: theme.palette.success.main,
|
||||||
|
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`
|
||||||
|
}))
|
||||||
|
|
||||||
|
const UserDropdown = () => {
|
||||||
|
const { user, isLoading } = useUser();
|
||||||
|
|
||||||
|
const [appUser, setAppUser] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getData()
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
var userResponse = await fetch('/api/me');
|
||||||
|
var user = await userResponse.json();
|
||||||
|
setAppUser(user);
|
||||||
|
}
|
||||||
|
// ** States
|
||||||
|
const [anchorEl, setAnchorEl] = useState<Element | null>(null)
|
||||||
|
|
||||||
|
// ** Hooks
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const handleDropdownOpen = (event: SyntheticEvent) => {
|
||||||
|
setAnchorEl(event.currentTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDropdownClose = (url?: string) => {
|
||||||
|
if (url) {
|
||||||
|
router.push(url)
|
||||||
|
}
|
||||||
|
setAnchorEl(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
py: 2,
|
||||||
|
px: 4,
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
color: 'text.primary',
|
||||||
|
textDecoration: 'none',
|
||||||
|
'& svg': {
|
||||||
|
fontSize: '1.375rem',
|
||||||
|
color: 'text.secondary'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(!isLoading && user && appUser) ? (
|
||||||
|
<Fragment>
|
||||||
|
<Badge
|
||||||
|
overlap='circular'
|
||||||
|
onClick={handleDropdownOpen}
|
||||||
|
sx={{ ml: 2, cursor: 'pointer' }}
|
||||||
|
badgeContent={<BadgeContentSpan />}
|
||||||
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
alt={user.nickname}
|
||||||
|
onClick={handleDropdownOpen}
|
||||||
|
sx={{ width: 40, height: 40 }}
|
||||||
|
src={user.picture}
|
||||||
|
/>
|
||||||
|
</Badge>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={Boolean(anchorEl)}
|
||||||
|
onClose={() => handleDropdownClose()}
|
||||||
|
sx={{ '& .MuiMenu-paper': { width: 230, marginTop: 4 } }}
|
||||||
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||||
|
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||||
|
>
|
||||||
|
<Box sx={{ pt: 2, pb: 3, px: 4 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Badge
|
||||||
|
overlap='circular'
|
||||||
|
badgeContent={<BadgeContentSpan />}
|
||||||
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||||
|
>
|
||||||
|
<Avatar alt={appUser["displayName"]} src={user.picture} sx={{ width: '2.5rem', height: '2.5rem' }} />
|
||||||
|
</Badge>
|
||||||
|
<Box sx={{ display: 'flex', marginLeft: 3, alignItems: 'flex-start', flexDirection: 'column' }}>
|
||||||
|
<Typography sx={{ fontWeight: 600 }}>{appUser["displayName"]}</Typography>
|
||||||
|
<Typography variant='body2' sx={{ fontSize: '0.8rem', color: 'text.disabled' }}>
|
||||||
|
Welcome back!
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Divider sx={{ mt: 0, mb: 1 }} />
|
||||||
|
<MenuItem sx={{ p: 0 }} onClick={() => router.push("/dashboard/settings")}>
|
||||||
|
<Box sx={styles}>
|
||||||
|
<CogOutline sx={{ marginRight: 2 }} />
|
||||||
|
Settings
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem sx={{ py: 2 }} onClick={() => handleDropdownClose('/api/auth/logout')}>
|
||||||
|
<LogoutVariant sx={{ marginRight: 2, fontSize: '1.375rem', color: 'text.secondary' }} />
|
||||||
|
Logout
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</Fragment>
|
||||||
|
): null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserDropdown
|
@ -0,0 +1,18 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Link from '@mui/material/Link'
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||||
|
|
||||||
|
const FooterContent = () => {
|
||||||
|
// ** Var
|
||||||
|
const hidden = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FooterContent
|
57
core/layouts/components/shared-components/footer/index.tsx
Normal file
57
core/layouts/components/shared-components/footer/index.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../core/context/settingsContext'
|
||||||
|
|
||||||
|
// ** Footer Content Component
|
||||||
|
import FooterContent from './FooterContent'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
settings: Settings
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
footerContent?: (props?: any) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const Footer = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { settings, footerContent: userFooterContent } = props
|
||||||
|
|
||||||
|
// ** Hook
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
// ** Vars
|
||||||
|
const { contentWidth } = settings
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
component='footer'
|
||||||
|
className='layout-footer'
|
||||||
|
sx={{
|
||||||
|
zIndex: 10,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
className='footer-content-container'
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
borderTopLeftRadius: 14,
|
||||||
|
borderTopRightRadius: 14,
|
||||||
|
padding: theme.spacing(4, 6),
|
||||||
|
...(contentWidth === 'boxed' && { '@media (min-width:1440px)': { maxWidth: 1440 } })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{userFooterContent ? userFooterContent(props) : <FooterContent />}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer
|
70
core/layouts/components/vertical/appBar/index.tsx
Normal file
70
core/layouts/components/vertical/appBar/index.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import { styled, useTheme } from '@mui/material/styles'
|
||||||
|
import MuiAppBar, { AppBarProps } from '@mui/material/AppBar'
|
||||||
|
import MuiToolbar, { ToolbarProps } from '@mui/material/Toolbar'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../../../../context/settingsContext'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hidden: boolean
|
||||||
|
settings: Settings
|
||||||
|
toggleNavVisibility: () => void
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
verticalAppBarContent?: (props?: any) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppBar = styled(MuiAppBar)<AppBarProps>(({ theme }) => ({
|
||||||
|
transition: 'none',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: theme.spacing(0, 6),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
minHeight: theme.mixins.toolbar.minHeight,
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
paddingLeft: theme.spacing(4),
|
||||||
|
paddingRight: theme.spacing(4)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const Toolbar = styled(MuiToolbar)<ToolbarProps>(({ theme }) => ({
|
||||||
|
width: '100%',
|
||||||
|
borderBottomLeftRadius: 10,
|
||||||
|
borderBottomRightRadius: 10,
|
||||||
|
padding: `${theme.spacing(0)} !important`,
|
||||||
|
minHeight: `${theme.mixins.toolbar.minHeight}px !important`,
|
||||||
|
transition:
|
||||||
|
'padding .25s ease-in-out, box-shadow .25s ease-in-out, backdrop-filter .25s ease-in-out, background-color .25s ease-in-out'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const LayoutAppBar = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { settings, verticalAppBarContent: userVerticalAppBarContent } = props
|
||||||
|
|
||||||
|
// ** Hooks
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
// ** Vars
|
||||||
|
const { contentWidth } = settings
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppBar elevation={0} color='default' className='layout-navbar' position='static'>
|
||||||
|
<Toolbar
|
||||||
|
className='navbar-content-container'
|
||||||
|
sx={{
|
||||||
|
...(contentWidth === 'boxed' && {
|
||||||
|
'@media (min-width:1440px)': { maxWidth: `calc(1440px - ${theme.spacing(6)} * 2)` }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(userVerticalAppBarContent && userVerticalAppBarContent(props)) || null}
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LayoutAppBar
|
82
core/layouts/components/vertical/navigation/Drawer.tsx
Normal file
82
core/layouts/components/vertical/navigation/Drawer.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import { styled, useTheme } from '@mui/material/styles'
|
||||||
|
import MuiSwipeableDrawer, { SwipeableDrawerProps } from '@mui/material/SwipeableDrawer'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../core/context/settingsContext'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hidden: boolean
|
||||||
|
navWidth: number
|
||||||
|
settings: Settings
|
||||||
|
navVisible: boolean
|
||||||
|
children: ReactNode
|
||||||
|
setNavVisible: (value: boolean) => void
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SwipeableDrawer = styled(MuiSwipeableDrawer)<SwipeableDrawerProps>({
|
||||||
|
overflowX: 'hidden',
|
||||||
|
transition: 'width .25s ease-in-out',
|
||||||
|
'& ul': {
|
||||||
|
listStyle: 'none'
|
||||||
|
},
|
||||||
|
'& .MuiListItem-gutters': {
|
||||||
|
paddingLeft: 4,
|
||||||
|
paddingRight: 4
|
||||||
|
},
|
||||||
|
'& .MuiDrawer-paper': {
|
||||||
|
left: 'unset',
|
||||||
|
right: 'unset',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
transition: 'width .25s ease-in-out, box-shadow .25s ease-in-out'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const Drawer = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { hidden, children, navWidth, navVisible, setNavVisible } = props
|
||||||
|
|
||||||
|
// ** Hook
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
// Drawer Props for Mobile & Tablet screens
|
||||||
|
const MobileDrawerProps = {
|
||||||
|
open: navVisible,
|
||||||
|
onOpen: () => setNavVisible(true),
|
||||||
|
onClose: () => setNavVisible(false),
|
||||||
|
ModalProps: {
|
||||||
|
keepMounted: true // Better open performance on mobile.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawer Props for Desktop screens
|
||||||
|
const DesktopDrawerProps = {
|
||||||
|
open: true,
|
||||||
|
onOpen: () => null,
|
||||||
|
onClose: () => null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SwipeableDrawer
|
||||||
|
className='layout-vertical-nav'
|
||||||
|
variant={hidden ? 'temporary' : 'permanent'}
|
||||||
|
{...(hidden ? { ...MobileDrawerProps } : { ...DesktopDrawerProps })}
|
||||||
|
PaperProps={{ sx: { width: navWidth } }}
|
||||||
|
sx={{
|
||||||
|
width: navWidth,
|
||||||
|
'& .MuiDrawer-paper': {
|
||||||
|
borderRight: 0,
|
||||||
|
backgroundColor: theme.palette.background.default
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SwipeableDrawer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Drawer
|
@ -0,0 +1,74 @@
|
|||||||
|
// ** React Import
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** Next Import
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box, { BoxProps } from '@mui/material/Box'
|
||||||
|
import { styled, useTheme } from '@mui/material/styles'
|
||||||
|
import Typography, { TypographyProps } from '@mui/material/Typography'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../../../../../core/context/settingsContext'
|
||||||
|
|
||||||
|
// ** Configs
|
||||||
|
import themeConfig from '../../../../../configs/themeConfig'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hidden: boolean
|
||||||
|
settings: Settings
|
||||||
|
toggleNavVisibility: () => void
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
verticalNavMenuBranding?: (props?: any) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** Styled Components
|
||||||
|
const MenuHeaderWrapper = styled(Box)<BoxProps>(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingRight: theme.spacing(4.5),
|
||||||
|
transition: 'padding .25s ease-in-out',
|
||||||
|
minHeight: theme.mixins.toolbar.minHeight
|
||||||
|
}))
|
||||||
|
|
||||||
|
const HeaderTitle = styled(Typography)<TypographyProps>(({ theme }) => ({
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 'normal',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
transition: 'opacity .25s ease-in-out, margin .25s ease-in-out'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const StyledLink = styled('a')({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
textDecoration: 'none'
|
||||||
|
})
|
||||||
|
|
||||||
|
const VerticalNavHeader = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { verticalNavMenuBranding: userVerticalNavMenuBranding } = props
|
||||||
|
|
||||||
|
// ** Hooks
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuHeaderWrapper className='nav-header' sx={{ pl: 6 }}>
|
||||||
|
{userVerticalNavMenuBranding ? (
|
||||||
|
userVerticalNavMenuBranding(props)
|
||||||
|
) : (
|
||||||
|
<Link href='/' passHref>
|
||||||
|
<StyledLink>
|
||||||
|
<HeaderTitle variant='h6' sx={{ ml: 3 }}>
|
||||||
|
{themeConfig.templateName}
|
||||||
|
</HeaderTitle>
|
||||||
|
</StyledLink>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</MenuHeaderWrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerticalNavHeader
|
@ -0,0 +1,39 @@
|
|||||||
|
// ** Types Import
|
||||||
|
import { Settings } from '../core/context/settingsContext'
|
||||||
|
import { NavLink, NavSectionTitle, VerticalNavItemsType } from '../core/layouts/types'
|
||||||
|
|
||||||
|
// ** Custom Menu Components
|
||||||
|
import VerticalNavLink from './VerticalNavLink'
|
||||||
|
import VerticalNavSectionTitle from './VerticalNavSectionTitle'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
settings: Settings
|
||||||
|
navVisible?: boolean
|
||||||
|
groupActive: string[]
|
||||||
|
currentActiveGroup: string[]
|
||||||
|
verticalNavItems?: VerticalNavItemsType
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
setGroupActive: (value: string[]) => void
|
||||||
|
setCurrentActiveGroup: (item: string[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveNavItemComponent = (item: NavLink | NavSectionTitle) => {
|
||||||
|
if ((item as NavSectionTitle).sectionTitle) return VerticalNavSectionTitle
|
||||||
|
|
||||||
|
return VerticalNavLink
|
||||||
|
}
|
||||||
|
|
||||||
|
const VerticalNavItems = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { verticalNavItems } = props
|
||||||
|
|
||||||
|
const RenderMenuItems = verticalNavItems?.map((item: NavLink | NavSectionTitle, index: number) => {
|
||||||
|
const TagName: any = resolveNavItemComponent(item)
|
||||||
|
|
||||||
|
return <TagName {...props} key={index} item={item} />
|
||||||
|
})
|
||||||
|
|
||||||
|
return <>{RenderMenuItems}</>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerticalNavItems
|
136
core/layouts/components/vertical/navigation/VerticalNavLink.tsx
Normal file
136
core/layouts/components/vertical/navigation/VerticalNavLink.tsx
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ElementType, ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** Next Imports
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Chip from '@mui/material/Chip'
|
||||||
|
import ListItem from '@mui/material/ListItem'
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import Box, { BoxProps } from '@mui/material/Box'
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon'
|
||||||
|
import ListItemButton, { ListItemButtonProps } from '@mui/material/ListItemButton'
|
||||||
|
|
||||||
|
// ** Configs Import
|
||||||
|
import themeConfig from '../../../../../configs/themeConfig'
|
||||||
|
|
||||||
|
// ** Types
|
||||||
|
import { NavLink } from '../../../../../core/layouts/types'
|
||||||
|
import { Settings } from '../../../../../core/context/settingsContext'
|
||||||
|
|
||||||
|
// ** Custom Components Imports
|
||||||
|
import UserIcon from '../../../../../layouts/components/UserIcon'
|
||||||
|
|
||||||
|
// ** Utils
|
||||||
|
import { handleURLQueries } from '../../../../../core/layouts/utils'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
item: NavLink
|
||||||
|
settings: Settings
|
||||||
|
navVisible?: boolean
|
||||||
|
toggleNavVisibility: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** Styled Components
|
||||||
|
const MenuNavLink = styled(ListItemButton)<
|
||||||
|
ListItemButtonProps & { component?: ElementType; target?: '_blank' | undefined }
|
||||||
|
>(({ theme }) => ({
|
||||||
|
width: '100%',
|
||||||
|
borderTopRightRadius: 100,
|
||||||
|
borderBottomRightRadius: 100,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
padding: theme.spacing(2.25, 3.5),
|
||||||
|
transition: 'opacity .25s ease-in-out',
|
||||||
|
'&.active, &.active:hover': {
|
||||||
|
boxShadow: theme.shadows[3],
|
||||||
|
backgroundImage: `linear-gradient(98deg, ${theme.palette.customColors.primaryGradient}, ${theme.palette.primary.main} 94%)`
|
||||||
|
},
|
||||||
|
'&.active .MuiTypography-root, &.active .MuiSvgIcon-root': {
|
||||||
|
color: `${theme.palette.common.white} !important`
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const MenuItemTextMetaWrapper = styled(Box)<BoxProps>({
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
transition: 'opacity .25s ease-in-out',
|
||||||
|
...(themeConfig.menuTextTruncate && { overflow: 'hidden' })
|
||||||
|
})
|
||||||
|
|
||||||
|
const VerticalNavLink = ({ item, navVisible, toggleNavVisibility }: Props) => {
|
||||||
|
// ** Hooks
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const IconTag: ReactNode = item.icon
|
||||||
|
|
||||||
|
const isNavLinkActive = () => {
|
||||||
|
if (router.pathname === item.path || handleURLQueries(router, item.path)) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
disablePadding
|
||||||
|
className='nav-link'
|
||||||
|
disabled={item.disabled || false}
|
||||||
|
sx={{ mt: 1.5, px: '0 !important' }}
|
||||||
|
>
|
||||||
|
<Link passHref href={item.path === undefined ? '/' : `${item.path}`}>
|
||||||
|
<MenuNavLink
|
||||||
|
component={'a'}
|
||||||
|
className={isNavLinkActive() ? 'active' : ''}
|
||||||
|
{...(item.openInNewTab ? { target: '_blank' } : null)}
|
||||||
|
onClick={e => {
|
||||||
|
if (item.path === undefined) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
if (navVisible) {
|
||||||
|
toggleNavVisibility()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
pl: 5.5,
|
||||||
|
...(item.disabled ? { pointerEvents: 'none' } : { cursor: 'pointer' })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemIcon
|
||||||
|
sx={{
|
||||||
|
mr: 2.5,
|
||||||
|
color: 'text.primary',
|
||||||
|
transition: 'margin .25s ease-in-out'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UserIcon icon={IconTag} />
|
||||||
|
</ListItemIcon>
|
||||||
|
|
||||||
|
<MenuItemTextMetaWrapper>
|
||||||
|
<Typography {...(themeConfig.menuTextTruncate && { noWrap: true })}>{item.title}</Typography>
|
||||||
|
{item.badgeContent ? (
|
||||||
|
<Chip
|
||||||
|
label={item.badgeContent}
|
||||||
|
color={item.badgeColor || 'primary'}
|
||||||
|
sx={{
|
||||||
|
height: 20,
|
||||||
|
fontWeight: 500,
|
||||||
|
marginLeft: 1.25,
|
||||||
|
'& .MuiChip-label': { px: 1.5, textTransform: 'capitalize' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</MenuItemTextMetaWrapper>
|
||||||
|
</MenuNavLink>
|
||||||
|
</Link>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerticalNavLink
|
@ -0,0 +1,72 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import Divider from '@mui/material/Divider'
|
||||||
|
import { styled, useTheme } from '@mui/material/styles'
|
||||||
|
import Typography, { TypographyProps } from '@mui/material/Typography'
|
||||||
|
import MuiListSubheader, { ListSubheaderProps } from '@mui/material/ListSubheader'
|
||||||
|
|
||||||
|
// ** Types
|
||||||
|
import { NavSectionTitle } from '../core/layouts/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
item: NavSectionTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** Styled Components
|
||||||
|
const ListSubheader = styled((props: ListSubheaderProps) => <MuiListSubheader component='li' {...props} />)(
|
||||||
|
({ theme }) => ({
|
||||||
|
lineHeight: 1,
|
||||||
|
display: 'flex',
|
||||||
|
position: 'relative',
|
||||||
|
marginTop: theme.spacing(7),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
transition: 'padding-left .25s ease-in-out'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const TypographyHeaderText = styled(Typography)<TypographyProps>(({ theme }) => ({
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
lineHeight: 'normal',
|
||||||
|
letterSpacing: '0.21px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
color: theme.palette.text.disabled,
|
||||||
|
fontWeight: theme.typography.fontWeightMedium
|
||||||
|
}))
|
||||||
|
|
||||||
|
const VerticalNavSectionTitle = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { item } = props
|
||||||
|
|
||||||
|
// ** Hook
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListSubheader
|
||||||
|
className='nav-section-title'
|
||||||
|
sx={{
|
||||||
|
px: 0,
|
||||||
|
py: 1.75,
|
||||||
|
color: theme.palette.text.disabled,
|
||||||
|
'& .MuiDivider-root:before, & .MuiDivider-root:after, & hr': {
|
||||||
|
borderColor: `rgba(${theme.palette.customColors.main}, 0.12)`
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Divider
|
||||||
|
textAlign='left'
|
||||||
|
sx={{
|
||||||
|
m: 0,
|
||||||
|
width: '100%',
|
||||||
|
lineHeight: 'normal',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
'&:before, &:after': { top: 7, transform: 'none' },
|
||||||
|
'& .MuiDivider-wrapper': { px: 2.5, fontSize: '0.75rem', letterSpacing: '0.21px' }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TypographyHeaderText noWrap>{item.sectionTitle}</TypographyHeaderText>
|
||||||
|
</Divider>
|
||||||
|
</ListSubheader>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerticalNavSectionTitle
|
153
core/layouts/components/vertical/navigation/index.tsx
Normal file
153
core/layouts/components/vertical/navigation/index.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// ** React Import
|
||||||
|
import { ReactNode, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Import
|
||||||
|
import List from '@mui/material/List'
|
||||||
|
import Box, { BoxProps } from '@mui/material/Box'
|
||||||
|
import { styled, useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Third Party Components
|
||||||
|
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../../../../../core/context/settingsContext'
|
||||||
|
import { VerticalNavItemsType } from '../../../../../core/layouts/types'
|
||||||
|
|
||||||
|
// ** Component Imports
|
||||||
|
import Drawer from './Drawer'
|
||||||
|
import VerticalNavItems from './VerticalNavItems'
|
||||||
|
import VerticalNavHeader from './VerticalNavHeader'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hidden: boolean
|
||||||
|
navWidth: number
|
||||||
|
settings: Settings
|
||||||
|
children: ReactNode
|
||||||
|
navVisible: boolean
|
||||||
|
toggleNavVisibility: () => void
|
||||||
|
setNavVisible: (value: boolean) => void
|
||||||
|
verticalNavItems?: VerticalNavItemsType
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
verticalNavMenuContent?: (props?: any) => ReactNode
|
||||||
|
afterVerticalNavMenuContent?: (props?: any) => ReactNode
|
||||||
|
beforeVerticalNavMenuContent?: (props?: any) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledBoxForShadow = styled(Box)<BoxProps>({
|
||||||
|
top: 50,
|
||||||
|
left: -8,
|
||||||
|
zIndex: 2,
|
||||||
|
height: 75,
|
||||||
|
display: 'none',
|
||||||
|
position: 'absolute',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
width: 'calc(100% + 15px)',
|
||||||
|
'&.d-block': {
|
||||||
|
display: 'block'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const Navigation = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const {
|
||||||
|
hidden,
|
||||||
|
afterVerticalNavMenuContent,
|
||||||
|
beforeVerticalNavMenuContent,
|
||||||
|
verticalNavMenuContent: userVerticalNavMenuContent
|
||||||
|
} = props
|
||||||
|
|
||||||
|
// ** States
|
||||||
|
const [groupActive, setGroupActive] = useState<string[]>([])
|
||||||
|
const [currentActiveGroup, setCurrentActiveGroup] = useState<string[]>([])
|
||||||
|
|
||||||
|
// ** Ref
|
||||||
|
const shadowRef = useRef(null)
|
||||||
|
|
||||||
|
// ** Hooks
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
// ** Fixes Navigation InfiniteScroll
|
||||||
|
const handleInfiniteScroll = (ref: HTMLElement) => {
|
||||||
|
if (ref) {
|
||||||
|
// @ts-ignore
|
||||||
|
ref._getBoundingClientRect = ref.getBoundingClientRect
|
||||||
|
|
||||||
|
ref.getBoundingClientRect = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
const original = ref._getBoundingClientRect()
|
||||||
|
|
||||||
|
return { ...original, height: Math.floor(original.height) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** Scroll Menu
|
||||||
|
const scrollMenu = (container: any) => {
|
||||||
|
container = hidden ? container.target : container
|
||||||
|
if (shadowRef && container.scrollTop > 0) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (!shadowRef.current.classList.contains('d-block')) {
|
||||||
|
// @ts-ignore
|
||||||
|
shadowRef.current.classList.add('d-block')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
shadowRef.current.classList.remove('d-block')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScrollWrapper = hidden ? Box : PerfectScrollbar
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer {...props}>
|
||||||
|
<VerticalNavHeader {...props} />
|
||||||
|
<StyledBoxForShadow
|
||||||
|
ref={shadowRef}
|
||||||
|
sx={{
|
||||||
|
background: `linear-gradient(${theme.palette.background.default} 40%,${hexToRGBA(
|
||||||
|
theme.palette.background.default,
|
||||||
|
0.1
|
||||||
|
)} 95%,${hexToRGBA(theme.palette.background.default, 0.05)})`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box sx={{ height: '100%', position: 'relative', overflow: 'hidden' }}>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<ScrollWrapper
|
||||||
|
{...(hidden
|
||||||
|
? {
|
||||||
|
onScroll: (container: any) => scrollMenu(container),
|
||||||
|
sx: { height: '100%', overflowY: 'auto', overflowX: 'hidden' }
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
options: { wheelPropagation: false },
|
||||||
|
onScrollY: (container: any) => scrollMenu(container),
|
||||||
|
containerRef: (ref: any) => handleInfiniteScroll(ref)
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{beforeVerticalNavMenuContent ? beforeVerticalNavMenuContent(props) : null}
|
||||||
|
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
|
||||||
|
{userVerticalNavMenuContent ? (
|
||||||
|
userVerticalNavMenuContent(props)
|
||||||
|
) : (
|
||||||
|
<List className='nav-items' sx={{ transition: 'padding .25s ease', pr: 4.5 }}>
|
||||||
|
<VerticalNavItems
|
||||||
|
groupActive={groupActive}
|
||||||
|
setGroupActive={setGroupActive}
|
||||||
|
currentActiveGroup={currentActiveGroup}
|
||||||
|
setCurrentActiveGroup={setCurrentActiveGroup}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</ScrollWrapper>
|
||||||
|
</Box>
|
||||||
|
{afterVerticalNavMenuContent ? afterVerticalNavMenuContent(props) : null}
|
||||||
|
</Drawer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Navigation
|
46
core/layouts/types.ts
Normal file
46
core/layouts/types.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { ReactNode } from 'react'
|
||||||
|
import { Settings } from '../core/context/settingsContext'
|
||||||
|
|
||||||
|
export type ContentWidth = 'full' | 'boxed'
|
||||||
|
|
||||||
|
export type ThemeColor = 'primary' | 'secondary' | 'error' | 'warning' | 'info' | 'success'
|
||||||
|
|
||||||
|
export type NavLink = {
|
||||||
|
path?: string
|
||||||
|
title: string
|
||||||
|
action?: string
|
||||||
|
subject?: string
|
||||||
|
disabled?: boolean
|
||||||
|
badgeContent?: string
|
||||||
|
externalLink?: boolean
|
||||||
|
openInNewTab?: boolean
|
||||||
|
icon?: string | string[] | ReactNode
|
||||||
|
badgeColor?: 'default' | 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NavSectionTitle = {
|
||||||
|
sectionTitle: string
|
||||||
|
action?: string
|
||||||
|
subject?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VerticalNavItemsType = (NavLink | NavSectionTitle)[]
|
||||||
|
|
||||||
|
export type LayoutProps = {
|
||||||
|
hidden: boolean
|
||||||
|
settings: Settings
|
||||||
|
children: ReactNode
|
||||||
|
verticalNavItems?: VerticalNavItemsType
|
||||||
|
scrollToTop?: (props?: any) => ReactNode
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
footerContent?: (props?: any) => ReactNode
|
||||||
|
verticalAppBarContent?: (props?: any) => ReactNode
|
||||||
|
verticalNavMenuContent?: (props?: any) => ReactNode
|
||||||
|
verticalNavMenuBranding?: (props?: any) => ReactNode
|
||||||
|
afterVerticalNavMenuContent?: (props?: any) => ReactNode
|
||||||
|
beforeVerticalNavMenuContent?: (props?: any) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BlankLayoutProps = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
19
core/layouts/utils.ts
Normal file
19
core/layouts/utils.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// ** Types
|
||||||
|
import { NextRouter } from 'next/router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for URL queries as well for matching
|
||||||
|
* Current URL & Item Path
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @param activeItem
|
||||||
|
*/
|
||||||
|
export const handleURLQueries = (router: NextRouter, path: string | undefined): boolean => {
|
||||||
|
if (Object.keys(router.query).length && path) {
|
||||||
|
const arr = Object.keys(router.query)
|
||||||
|
|
||||||
|
return router.asPath.includes(path) && router.asPath.includes(router.query[arr[0]] as string) && path !== '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
103
core/styles/libs/react-apexcharts/index.ts
Normal file
103
core/styles/libs/react-apexcharts/index.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// ** MUI imports
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const ApexChartWrapper = styled('div')(({ theme }) => ({
|
||||||
|
'& .apexcharts-canvas': {
|
||||||
|
"& line[stroke='transparent']": {
|
||||||
|
display: 'none'
|
||||||
|
},
|
||||||
|
'& .apexcharts-xaxis > line, & .apexcharts-yaxis > line': {
|
||||||
|
stroke: theme.palette.divider
|
||||||
|
},
|
||||||
|
'& .apexcharts-xaxis-tick, & .apexcharts-yaxis-tick': {
|
||||||
|
stroke: theme.palette.divider
|
||||||
|
},
|
||||||
|
'& .apexcharts-tooltip': {
|
||||||
|
boxShadow: theme.shadows[3],
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
'& .apexcharts-tooltip-title': {
|
||||||
|
fontWeight: 600,
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
background: theme.palette.background.paper
|
||||||
|
},
|
||||||
|
'&.apexcharts-theme-dark': {
|
||||||
|
'& .apexcharts-tooltip-text-label, & .apexcharts-tooltip-text-value': {
|
||||||
|
color: theme.palette.common.white
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .bar-chart': {
|
||||||
|
padding: theme.spacing(2, 2.5)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .apexcharts-xaxistooltip': {
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
background: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
|
||||||
|
'& .apexcharts-xaxistooltip-text': {
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
'&:after': {
|
||||||
|
borderBottomColor: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default
|
||||||
|
},
|
||||||
|
'&:before': {
|
||||||
|
borderBottomColor: theme.palette.divider
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .apexcharts-yaxistooltip': {
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
background: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
|
||||||
|
'& .apexcharts-yaxistooltip-text': {
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
'&:after': {
|
||||||
|
borderLeftColor: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default
|
||||||
|
},
|
||||||
|
'&:before': {
|
||||||
|
borderLeftColor: theme.palette.divider
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .apexcharts-text, & .apexcharts-tooltip-text, & .apexcharts-datalabel-label, & .apexcharts-datalabel': {
|
||||||
|
filter: 'none',
|
||||||
|
fontWeight: 400,
|
||||||
|
fill: theme.palette.text.primary,
|
||||||
|
fontFamily: `${theme.typography.fontFamily} !important`
|
||||||
|
},
|
||||||
|
'& .apexcharts-pie-label': {
|
||||||
|
filter: 'none',
|
||||||
|
fill: theme.palette.common.white
|
||||||
|
},
|
||||||
|
'& .apexcharts-pie': {
|
||||||
|
'& .apexcharts-datalabel-label, .apexcharts-datalabel-value': {
|
||||||
|
fontSize: '1.5rem'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .apexcharts-marker': {
|
||||||
|
boxShadow: 'none'
|
||||||
|
},
|
||||||
|
'& .apexcharts-legend-series': {
|
||||||
|
margin: `${theme.spacing(0.75, 2)} !important`,
|
||||||
|
'& .apexcharts-legend-text': {
|
||||||
|
marginLeft: theme.spacing(0.75),
|
||||||
|
color: `${theme.palette.text.primary} !important`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .apexcharts-xcrosshairs, & .apexcharts-ycrosshairs, & .apexcharts-gridline': {
|
||||||
|
stroke: theme.palette.divider
|
||||||
|
},
|
||||||
|
'& .apexcharts-heatmap-rect': {
|
||||||
|
stroke: theme.palette.mode === 'light' ? theme.palette.background.paper : theme.palette.background.default
|
||||||
|
},
|
||||||
|
'& .apexcharts-radialbar > g > g:first-of-type .apexcharts-radialbar-area': {
|
||||||
|
stroke: theme.palette.background.default
|
||||||
|
},
|
||||||
|
'& .apexcharts-radar-series polygon': {
|
||||||
|
stroke: theme.palette.divider,
|
||||||
|
fill: theme.palette.background.paper
|
||||||
|
},
|
||||||
|
'& .apexcharts-radar-series line': {
|
||||||
|
stroke: theme.palette.divider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default ApexChartWrapper
|
361
core/styles/libs/react-datepicker/index.ts
Normal file
361
core/styles/libs/react-datepicker/index.ts
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
// ** MUI imports
|
||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import Box, { BoxProps } from '@mui/material/Box'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const DatePickerWrapper = styled(Box)<BoxProps>(({ theme }) => {
|
||||||
|
return {
|
||||||
|
'& .react-datepicker-popper': {
|
||||||
|
zIndex: 5
|
||||||
|
},
|
||||||
|
'& .react-datepicker-wrapper': {
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
'& .react-datepicker': {
|
||||||
|
border: 'none',
|
||||||
|
boxShadow: theme.shadows[7],
|
||||||
|
padding: theme.spacing(2, 0),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
fontFamily: theme.typography.fontFamily,
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
'& .react-datepicker__header': {
|
||||||
|
padding: 0,
|
||||||
|
border: 'none',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
'& .react-datepicker__day-name': {
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker-year-header': {
|
||||||
|
lineHeight: 2.1,
|
||||||
|
marginBottom: '0.5rem',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
'& .react-datepicker__triangle': {
|
||||||
|
display: 'none'
|
||||||
|
},
|
||||||
|
'& > .react-datepicker__navigation': {
|
||||||
|
top: theme.spacing(3),
|
||||||
|
'&.react-datepicker__navigation--previous': {
|
||||||
|
border: 'none',
|
||||||
|
backgroundImage: `${"url('data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' style=\\'width:24px;height:24px\\' viewBox=\\'0 0 24 24\\'%3E%3Cpath fill=\\'currentColor\\' d=\\'M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z\\' /%3E%3C/svg%3E')"
|
||||||
|
.replace('currentColor', theme.palette.text.secondary)
|
||||||
|
.replace('#', '%23')}`,
|
||||||
|
height: '24px',
|
||||||
|
width: '24px',
|
||||||
|
'& .react-datepicker__navigation-icon': {
|
||||||
|
display: 'none'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&.react-datepicker__navigation--next': {
|
||||||
|
border: 'none',
|
||||||
|
backgroundImage: `${"url('data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' style=\\'width:24px;height:24px\\' viewBox=\\'0 0 24 24\\'%3E%3Cpath fill=\\'currentColor\\' d=\\'M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z\\' /%3E%3C/svg%3E')"
|
||||||
|
.replace('currentColor', theme.palette.text.secondary)
|
||||||
|
.replace('#', '%23')}`,
|
||||||
|
height: '24px',
|
||||||
|
width: '24px',
|
||||||
|
'& .react-datepicker__navigation-icon': {
|
||||||
|
display: 'none'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&.react-datepicker__navigation--next--with-time': {
|
||||||
|
right: '122px'
|
||||||
|
},
|
||||||
|
'&:focus, &:active': {
|
||||||
|
outline: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__current-month': {
|
||||||
|
lineHeight: 2.1,
|
||||||
|
fontSize: '1rem',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
letterSpacing: '0.15px',
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
'& .react-datepicker__day-name': {
|
||||||
|
lineHeight: 1.5,
|
||||||
|
width: '2.25rem',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
letterSpacing: '0.4px',
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
'& .react-datepicker__day': {
|
||||||
|
margin: 0,
|
||||||
|
width: '2.25rem',
|
||||||
|
lineHeight: 2.75,
|
||||||
|
height: '2.25rem',
|
||||||
|
borderRadius: '50%',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
'&.react-datepicker__day--selected, &.react-datepicker__day--keyboard-selected': {
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
backgroundColor: `${theme.palette.primary.main} !important`
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--in-range, &.react-datepicker__day--in-selecting-range': {
|
||||||
|
borderRadius: 0,
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
backgroundColor: `${hexToRGBA(theme.palette.primary.main, 0.06)} !important`,
|
||||||
|
'&:empty': {
|
||||||
|
backgroundColor: 'transparent !important'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--selected.react-datepicker__day--in-selecting-range.react-datepicker__day--selecting-range-start, &.react-datepicker__day--selected.react-datepicker__day--range-start.react-datepicker__day--in-range, &.react-datepicker__day--range-start':
|
||||||
|
{
|
||||||
|
borderTopLeftRadius: '50%',
|
||||||
|
borderBottomLeftRadius: '50%',
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
backgroundColor: `${theme.palette.primary.main} !important`
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--range-end': {
|
||||||
|
borderTopRightRadius: '50%',
|
||||||
|
borderBottomRightRadius: '50%',
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
backgroundColor: `${theme.palette.primary.main} !important`
|
||||||
|
},
|
||||||
|
'&:focus, &:active': {
|
||||||
|
outline: 0
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--outside-month': {
|
||||||
|
height: 'auto'
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--outside-month, &.react-datepicker__day--disabled:not(.react-datepicker__day--selected)':
|
||||||
|
{
|
||||||
|
color: theme.palette.text.disabled,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--highlighted, &.react-datepicker__day--highlighted:hover': {
|
||||||
|
color: theme.palette.success.main,
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.success.main, 0.12)
|
||||||
|
},
|
||||||
|
'&.react-datepicker__day--today': {
|
||||||
|
fontWeight: 'normal'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__header__dropdown': {
|
||||||
|
'& .react-datepicker__month-dropdown-container:not(:last-child)': {
|
||||||
|
marginRight: theme.spacing(8)
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-dropdown-container, & .react-datepicker__year-dropdown-container': {
|
||||||
|
marginBottom: theme.spacing(4)
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-read-view--selected-month, & .react-datepicker__year-read-view--selected-year': {
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow, & .react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow':
|
||||||
|
{
|
||||||
|
borderTopColor: theme.palette.text.secondary,
|
||||||
|
borderRightColor: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-read-view--down-arrow, & .react-datepicker__year-read-view--down-arrow': {
|
||||||
|
top: 4,
|
||||||
|
borderTopColor: theme.palette.text.disabled,
|
||||||
|
borderRightColor: theme.palette.text.disabled
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-dropdown, & .react-datepicker__year-dropdown': {
|
||||||
|
paddingTop: theme.spacing(1.5),
|
||||||
|
paddingBottom: theme.spacing(1.5),
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
boxShadow: theme.palette.mode === 'light' ? theme.shadows[8] : theme.shadows[9]
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-option, & .react-datepicker__year-option': {
|
||||||
|
paddingTop: theme.spacing(0.5),
|
||||||
|
paddingBottom: theme.spacing(0.5),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: theme.palette.action.hover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-option.react-datepicker__month-option--selected_month': {
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.08),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month-option--selected': {
|
||||||
|
display: 'none'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__year-option.react-datepicker__year-option--selected_year': {
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.08),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .react-datepicker__year-option--selected': {
|
||||||
|
display: 'none'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__year-option': {
|
||||||
|
// TODO: Remove some of the following styles for arrow in Year dropdown when react-datepicker give arrows in Year dropdown
|
||||||
|
'& .react-datepicker__navigation--years-upcoming': {
|
||||||
|
width: 9,
|
||||||
|
height: 9,
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderWidth: '3px 3px 0 0',
|
||||||
|
transform: 'rotate(-45deg)',
|
||||||
|
borderTopColor: theme.palette.text.disabled,
|
||||||
|
borderRightColor: theme.palette.text.disabled,
|
||||||
|
margin: `${theme.spacing(2.75)} auto ${theme.spacing(0)}`
|
||||||
|
},
|
||||||
|
'&:hover .react-datepicker__navigation--years-upcoming': {
|
||||||
|
borderTopColor: theme.palette.text.secondary,
|
||||||
|
borderRightColor: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
'& .react-datepicker__navigation--years-previous': {
|
||||||
|
width: 9,
|
||||||
|
height: 9,
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderWidth: '0 0 3px 3px',
|
||||||
|
transform: 'rotate(-45deg)',
|
||||||
|
borderLeftColor: theme.palette.text.disabled,
|
||||||
|
borderBottomColor: theme.palette.text.disabled,
|
||||||
|
margin: `${theme.spacing(0)} auto ${theme.spacing(2.75)}`
|
||||||
|
},
|
||||||
|
'&:hover .react-datepicker__navigation--years-previous': {
|
||||||
|
borderLeftColor: theme.palette.text.secondary,
|
||||||
|
borderBottomColor: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month': {
|
||||||
|
marginTop: theme.spacing(3)
|
||||||
|
},
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
'& .react-datepicker__month': {
|
||||||
|
marginLeft: 0,
|
||||||
|
marginRight: 0,
|
||||||
|
marginBottom: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__month, & .react-datepicker__year': {
|
||||||
|
'& .react-datepicker__month-text, & .react-datepicker__year-text, & .react-datepicker__quarter-text': {
|
||||||
|
height: '2rem',
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'inline-flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
'&:hover': {
|
||||||
|
borderRadius: theme.shape.borderRadius
|
||||||
|
},
|
||||||
|
'&:focus, &:active': {
|
||||||
|
outline: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__quarter--selected, & .react-datepicker__year-text--selected, & .react-datepicker__month--selected, & .react-datepicker__quarter-text--keyboard-selected, & .react-datepicker__month-text--keyboard-selected, & .react-datepicker__year-text--keyboard-selected':
|
||||||
|
{
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
backgroundColor: `${theme.palette.primary.main} !important`
|
||||||
|
},
|
||||||
|
'& .react-datepicker__week-number': {
|
||||||
|
fontWeight: 600,
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__year-wrapper': {
|
||||||
|
maxWidth: 205,
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
'& .react-datepicker__input-time-container': {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
'& .react-datepicker__today-button': {
|
||||||
|
borderRadius: '1rem',
|
||||||
|
margin: '0 1rem 0.3rem',
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
backgroundColor: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
|
||||||
|
// ** Time Picker
|
||||||
|
'& .react-datepicker__time-container': {
|
||||||
|
borderLeftColor: theme.palette.divider
|
||||||
|
},
|
||||||
|
'&.react-datepicker--time-only, & .react-datepicker__time-container': {
|
||||||
|
width: '7rem',
|
||||||
|
padding: theme.spacing(1.2, 0),
|
||||||
|
'& .react-datepicker-time__header': {
|
||||||
|
marginBottom: theme.spacing(3),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
fontSize: theme.typography.body2.fontSize
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .react-datepicker__time': {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
'& .react-datepicker__time-box .react-datepicker__time-list-item--disabled': {
|
||||||
|
color: theme.palette.text.disabled
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .react-datepicker__time-list-item': {
|
||||||
|
lineHeight: 1.75,
|
||||||
|
height: 'auto !important',
|
||||||
|
marginLeft: theme.spacing(3.2),
|
||||||
|
marginRight: theme.spacing(1.2),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
'&:focus, &:active': {
|
||||||
|
outline: 0
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: `${theme.palette.action.hover} !important`
|
||||||
|
},
|
||||||
|
'&.react-datepicker__time-list-item--selected': {
|
||||||
|
color: `${theme.palette.common.white} !important`,
|
||||||
|
backgroundColor: `${theme.palette.primary.main} !important`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .react-datepicker__time-box': {
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
'& .react-datepicker__time-list': {
|
||||||
|
'&::-webkit-scrollbar': {
|
||||||
|
width: 8
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Track */
|
||||||
|
'&::-webkit-scrollbar-track': {
|
||||||
|
background: theme.palette.background.paper
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Handle */
|
||||||
|
'&::-webkit-scrollbar-thumb': {
|
||||||
|
background: '#aaa',
|
||||||
|
borderRadius: '10px'
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Handle on hover */
|
||||||
|
'&::-webkit-scrollbar-thumb:hover': {
|
||||||
|
background: '#999'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&.react-datepicker--time-only .react-datepicker__time-container': {
|
||||||
|
width: 'calc(7rem - 2px)'
|
||||||
|
},
|
||||||
|
'& .react-datepicker__day:hover, & .react-datepicker__month-text:hover, & .react-datepicker__quarter-text:hover, & .react-datepicker__year-text:hover':
|
||||||
|
{
|
||||||
|
backgroundColor: theme.palette.action.hover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .react-datepicker__close-icon': {
|
||||||
|
paddingRight: theme.spacing(4),
|
||||||
|
'&:after': {
|
||||||
|
width: 'unset',
|
||||||
|
height: 'unset',
|
||||||
|
fontSize: '1.5rem',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
backgroundColor: 'transparent !important'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default DatePickerWrapper
|
60
core/theme/ThemeComponent.tsx
Normal file
60
core/theme/ThemeComponent.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import CssBaseline from '@mui/material/CssBaseline'
|
||||||
|
import GlobalStyles from '@mui/material/GlobalStyles'
|
||||||
|
import { ThemeProvider, createTheme, responsiveFontSizes } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Type Imports
|
||||||
|
import { Settings } from '../../core/context/settingsContext'
|
||||||
|
|
||||||
|
// ** Theme Config
|
||||||
|
import themeConfig from '../../configs/themeConfig'
|
||||||
|
|
||||||
|
// ** Theme Override Imports
|
||||||
|
import overrides from './overrides'
|
||||||
|
import typography from './typography'
|
||||||
|
|
||||||
|
// ** Theme
|
||||||
|
import themeOptions from './ThemeOptions'
|
||||||
|
|
||||||
|
// ** Global Styles
|
||||||
|
import GlobalStyling from './globalStyles'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
settings: Settings
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeComponent = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { settings, children } = props
|
||||||
|
|
||||||
|
// ** Merged ThemeOptions of Core and User
|
||||||
|
const coreThemeConfig = themeOptions(settings)
|
||||||
|
|
||||||
|
// ** Pass ThemeOptions to CreateTheme Function to create partial theme without component overrides
|
||||||
|
let theme = createTheme(coreThemeConfig)
|
||||||
|
|
||||||
|
// ** Continue theme creation and pass merged component overrides to CreateTheme function
|
||||||
|
theme = createTheme(theme, {
|
||||||
|
components: { ...overrides(theme) },
|
||||||
|
typography: { ...typography(theme) }
|
||||||
|
})
|
||||||
|
|
||||||
|
// ** Set responsive font sizes to true
|
||||||
|
if (themeConfig.responsiveFontSizes) {
|
||||||
|
theme = responsiveFontSizes(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<CssBaseline />
|
||||||
|
<GlobalStyles styles={() => GlobalStyling(theme) as any} />
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ThemeComponent
|
58
core/theme/ThemeOptions.ts
Normal file
58
core/theme/ThemeOptions.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// ** MUI Theme Provider
|
||||||
|
import { deepmerge } from '@mui/utils'
|
||||||
|
import { ThemeOptions } from '@mui/material'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../core/context/settingsContext'
|
||||||
|
|
||||||
|
// ** Theme Override Imports
|
||||||
|
import palette from './palette'
|
||||||
|
import spacing from './spacing'
|
||||||
|
import shadows from './shadows'
|
||||||
|
import breakpoints from './breakpoints'
|
||||||
|
|
||||||
|
const themeOptions = (settings: Settings): ThemeOptions => {
|
||||||
|
// ** Vars
|
||||||
|
const { mode, themeColor } = settings
|
||||||
|
|
||||||
|
const themeConfig = {
|
||||||
|
palette: palette(mode, themeColor),
|
||||||
|
typography: {
|
||||||
|
fontFamily: [
|
||||||
|
'Inter',
|
||||||
|
'sans-serif',
|
||||||
|
'-apple-system',
|
||||||
|
'BlinkMacSystemFont',
|
||||||
|
'"Segoe UI"',
|
||||||
|
'Roboto',
|
||||||
|
'"Helvetica Neue"',
|
||||||
|
'Arial',
|
||||||
|
'sans-serif',
|
||||||
|
'"Apple Color Emoji"',
|
||||||
|
'"Segoe UI Emoji"',
|
||||||
|
'"Segoe UI Symbol"'
|
||||||
|
].join(',')
|
||||||
|
},
|
||||||
|
shadows: shadows(mode),
|
||||||
|
...spacing,
|
||||||
|
breakpoints: breakpoints(),
|
||||||
|
shape: {
|
||||||
|
borderRadius: 6
|
||||||
|
},
|
||||||
|
mixins: {
|
||||||
|
toolbar: {
|
||||||
|
minHeight: 64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deepmerge(themeConfig, {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
...themeConfig.palette[themeColor]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default themeOptions
|
11
core/theme/breakpoints/index.ts
Normal file
11
core/theme/breakpoints/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const breakpoints = () => ({
|
||||||
|
values: {
|
||||||
|
xs: 0,
|
||||||
|
sm: 600,
|
||||||
|
md: 900,
|
||||||
|
lg: 1200,
|
||||||
|
xl: 1536
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default breakpoints
|
47
core/theme/globalStyles.ts
Normal file
47
core/theme/globalStyles.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const GlobalStyles = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
'.ps__rail-y': {
|
||||||
|
zIndex: 1,
|
||||||
|
right: '0 !important',
|
||||||
|
left: 'auto !important',
|
||||||
|
'&:hover, &:focus, &.ps--clicking': {
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? '#E4E5EB !important' : '#423D5D !important'
|
||||||
|
},
|
||||||
|
'& .ps__thumb-y': {
|
||||||
|
right: '3px !important',
|
||||||
|
left: 'auto !important',
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? '#C2C4D1 !important' : '#504B6D !important'
|
||||||
|
},
|
||||||
|
'.layout-vertical-nav &': {
|
||||||
|
'& .ps__thumb-y': {
|
||||||
|
width: 4,
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? '#C2C4D1 !important' : '#504B6D !important'
|
||||||
|
},
|
||||||
|
'&:hover, &:focus, &.ps--clicking': {
|
||||||
|
backgroundColor: 'transparent !important',
|
||||||
|
'& .ps__thumb-y': {
|
||||||
|
width: 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#nprogress': {
|
||||||
|
pointerEvents: 'none',
|
||||||
|
'& .bar': {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
height: 3,
|
||||||
|
width: '100%',
|
||||||
|
zIndex: 2000,
|
||||||
|
position: 'fixed',
|
||||||
|
backgroundColor: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GlobalStyles
|
49
core/theme/overrides/accordion.ts
Normal file
49
core/theme/overrides/accordion.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Accordion = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiAccordion: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&.Mui-disabled': {
|
||||||
|
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.12)`
|
||||||
|
},
|
||||||
|
'&.Mui-expanded': {
|
||||||
|
boxShadow: theme.shadows[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiAccordionSummary: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: `0 ${theme.spacing(5)}`,
|
||||||
|
'& + .MuiCollapse-root': {
|
||||||
|
'& .MuiAccordionDetails-root:first-child': {
|
||||||
|
paddingTop: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
margin: `${theme.spacing(2.5)} 0`
|
||||||
|
},
|
||||||
|
expandIconWrapper: {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiAccordionDetails: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5),
|
||||||
|
'& + .MuiAccordionDetails-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Accordion
|
112
core/theme/overrides/alerts.ts
Normal file
112
core/theme/overrides/alerts.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
import { lighten, darken } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const Alert = (theme: Theme) => {
|
||||||
|
const getColor = theme.palette.mode === 'light' ? darken : lighten
|
||||||
|
|
||||||
|
return {
|
||||||
|
MuiAlert: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: 5,
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
marginBottom: theme.spacing(1.6)
|
||||||
|
},
|
||||||
|
'& a': {
|
||||||
|
color: 'inherit',
|
||||||
|
fontWeight: 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
standardSuccess: {
|
||||||
|
color: getColor(theme.palette.success.main, 0.12),
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.success.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.success.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.success.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
standardInfo: {
|
||||||
|
color: getColor(theme.palette.info.main, 0.12),
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.info.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.info.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.info.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
standardWarning: {
|
||||||
|
color: getColor(theme.palette.warning.main, 0.12),
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.warning.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.warning.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.warning.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
standardError: {
|
||||||
|
color: getColor(theme.palette.error.main, 0.12),
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.error.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.error.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.error.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlinedSuccess: {
|
||||||
|
borderColor: theme.palette.success.main,
|
||||||
|
color: getColor(theme.palette.success.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.success.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.success.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlinedInfo: {
|
||||||
|
borderColor: theme.palette.info.main,
|
||||||
|
color: getColor(theme.palette.info.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.info.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.info.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlinedWarning: {
|
||||||
|
borderColor: theme.palette.warning.main,
|
||||||
|
color: getColor(theme.palette.warning.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.warning.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.warning.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlinedError: {
|
||||||
|
borderColor: theme.palette.error.main,
|
||||||
|
color: getColor(theme.palette.error.main, 0.12),
|
||||||
|
'& .MuiAlertTitle-root': {
|
||||||
|
color: getColor(theme.palette.error.main, 0.12)
|
||||||
|
},
|
||||||
|
'& .MuiAlert-icon': {
|
||||||
|
color: getColor(theme.palette.error.main, 0.12)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filled: {
|
||||||
|
fontWeight: 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Alert
|
30
core/theme/overrides/avatars.ts
Normal file
30
core/theme/overrides/avatars.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Avatar = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiAvatar: {
|
||||||
|
styleOverrides: {
|
||||||
|
colorDefault: {
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? theme.palette.grey[200] : theme.palette.grey[700]
|
||||||
|
},
|
||||||
|
rounded: {
|
||||||
|
borderRadius: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiAvatarGroup: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
'.MuiCard-root & .MuiAvatar-root': {
|
||||||
|
borderColor: theme.palette.background.paper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Avatar
|
25
core/theme/overrides/backdrop.ts
Normal file
25
core/theme/overrides/backdrop.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const Backdrop = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiBackdrop: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? `rgba(${theme.palette.customColors.main}, 0.7)`
|
||||||
|
: hexToRGBA(theme.palette.background.default, 0.7)
|
||||||
|
},
|
||||||
|
invisible: {
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Backdrop
|
53
core/theme/overrides/button.ts
Normal file
53
core/theme/overrides/button.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Theme Config Imports
|
||||||
|
import themeConfig from '../../../configs/themeConfig'
|
||||||
|
|
||||||
|
const Button = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
fontWeight: 500,
|
||||||
|
borderRadius: 5,
|
||||||
|
lineHeight: 1.71,
|
||||||
|
letterSpacing: '0.3px',
|
||||||
|
padding: `${theme.spacing(1.875, 3)}`
|
||||||
|
},
|
||||||
|
contained: {
|
||||||
|
boxShadow: theme.shadows[3],
|
||||||
|
padding: `${theme.spacing(1.875, 5.5)}`
|
||||||
|
},
|
||||||
|
outlined: {
|
||||||
|
padding: `${theme.spacing(1.625, 5.25)}`
|
||||||
|
},
|
||||||
|
sizeSmall: {
|
||||||
|
padding: `${theme.spacing(1, 2.25)}`,
|
||||||
|
'&.MuiButton-contained': {
|
||||||
|
padding: `${theme.spacing(1, 3.5)}`
|
||||||
|
},
|
||||||
|
'&.MuiButton-outlined': {
|
||||||
|
padding: `${theme.spacing(0.75, 3.25)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sizeLarge: {
|
||||||
|
padding: `${theme.spacing(2.125, 5.5)}`,
|
||||||
|
'&.MuiButton-contained': {
|
||||||
|
padding: `${theme.spacing(2.125, 6.5)}`
|
||||||
|
},
|
||||||
|
'&.MuiButton-outlined': {
|
||||||
|
padding: `${theme.spacing(1.875, 6.25)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiButtonBase: {
|
||||||
|
defaultProps: {
|
||||||
|
disableRipple: themeConfig.disableRipple
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Button
|
86
core/theme/overrides/card.ts
Normal file
86
core/theme/overrides/card.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Card = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiCard: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
boxShadow: theme.shadows[6],
|
||||||
|
'& .card-more-options': {
|
||||||
|
marginTop: theme.spacing(-1),
|
||||||
|
marginRight: theme.spacing(-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiCardHeader: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5),
|
||||||
|
'& + .MuiCardContent-root, & + .MuiCollapse-root .MuiCardContent-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
},
|
||||||
|
'& .MuiCardHeader-subheader': {
|
||||||
|
fontSize: '0.875rem'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
lineHeight: 1,
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: '1.25rem',
|
||||||
|
letterSpacing: '0.0125em'
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
marginTop: 0,
|
||||||
|
marginRight: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiCardContent: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5),
|
||||||
|
'& + .MuiCardContent-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
},
|
||||||
|
'&:last-of-type': {
|
||||||
|
paddingBottom: theme.spacing(5)
|
||||||
|
},
|
||||||
|
'& + .MuiCardActions-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiCardActions: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5),
|
||||||
|
'&.card-action-dense': {
|
||||||
|
padding: theme.spacing(0, 2.5, 2.5),
|
||||||
|
'.MuiCard-root .MuiCardMedia-root + &': {
|
||||||
|
paddingTop: theme.spacing(2.5)
|
||||||
|
},
|
||||||
|
'.MuiCard-root &:first-of-type': {
|
||||||
|
paddingTop: theme.spacing(5),
|
||||||
|
paddingBottom: theme.spacing(5),
|
||||||
|
'& + .MuiCardContent-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
},
|
||||||
|
'& + .MuiCardHeader-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .MuiButton-text': {
|
||||||
|
paddingLeft: theme.spacing(2.5),
|
||||||
|
paddingRight: theme.spacing(2.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Card
|
22
core/theme/overrides/chip.ts
Normal file
22
core/theme/overrides/chip.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Chip = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiChip: {
|
||||||
|
styleOverrides: {
|
||||||
|
outlined: {
|
||||||
|
'&.MuiChip-colorDefault': {
|
||||||
|
borderColor: `rgba(${theme.palette.customColors.main}, 0.22)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteIcon: {
|
||||||
|
width: 18,
|
||||||
|
height: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Chip
|
64
core/theme/overrides/dateTimePicker.ts
Normal file
64
core/theme/overrides/dateTimePicker.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const DateTimePicker = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiCalendarPicker: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& [role="presentation"]': {
|
||||||
|
fontWeight: 400,
|
||||||
|
'& .PrivatePickersFadeTransitionGroup-root + .PrivatePickersFadeTransitionGroup-root > div': {
|
||||||
|
marginRight: 0
|
||||||
|
},
|
||||||
|
'& .MuiIconButton-sizeSmall': {
|
||||||
|
padding: theme.spacing(0.5)
|
||||||
|
},
|
||||||
|
'& + div .MuiIconButton-root:not(.Mui-disabled)': {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .PrivatePickersSlideTransition-root': {
|
||||||
|
minHeight: 240
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiPickersDay: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
fontSize: '0.875rem'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiClockPicker: {
|
||||||
|
styleOverrides: {
|
||||||
|
arrowSwitcher: {
|
||||||
|
'& .MuiIconButton-root:not(.Mui-disabled)': {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
'& + div': {
|
||||||
|
'& > div': {
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
|
||||||
|
'& ~ .MuiIconButton-root span.MuiTypography-caption': {
|
||||||
|
color: 'inherit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiMonthPicker: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& > .MuiTypography-root.Mui-selected': {
|
||||||
|
fontSize: '1rem'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DateTimePicker
|
107
core/theme/overrides/dialog.ts
Normal file
107
core/theme/overrides/dialog.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const Dialog = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiDialog: {
|
||||||
|
styleOverrides: {
|
||||||
|
paper: {
|
||||||
|
boxShadow: theme.shadows[6],
|
||||||
|
'&:not(.MuiDialog-paperFullScreen)': {
|
||||||
|
'@media (max-width:599px)': {
|
||||||
|
margin: theme.spacing(4),
|
||||||
|
width: `calc(100% - ${theme.spacing(8)})`,
|
||||||
|
maxWidth: `calc(100% - ${theme.spacing(8)}) !important`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& > .MuiList-root': {
|
||||||
|
paddingLeft: theme.spacing(1),
|
||||||
|
paddingRight: theme.spacing(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiDialogTitle: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiDialogContent: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5),
|
||||||
|
'& + .MuiDialogContent-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
},
|
||||||
|
'& + .MuiDialogActions-root': {
|
||||||
|
paddingTop: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
// Styling for Mobile Date Picker starts
|
||||||
|
'& .PrivatePickersToolbar-root': {
|
||||||
|
padding: theme.spacing(4, 5),
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
'& .MuiTypography-root': {
|
||||||
|
color: theme.palette.primary.contrastText
|
||||||
|
},
|
||||||
|
'& span.MuiTypography-overline': {
|
||||||
|
fontSize: '1rem',
|
||||||
|
lineHeight: '24px',
|
||||||
|
letterSpacing: '0.15px'
|
||||||
|
},
|
||||||
|
'& ~ div[class^="css-"] > div[class^="css-"]': {
|
||||||
|
marginTop: theme.spacing(6),
|
||||||
|
marginBottom: theme.spacing(6),
|
||||||
|
'& > div[class^="css-"]': {
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
|
||||||
|
'& ~ .MuiIconButton-root span.MuiTypography-caption': {
|
||||||
|
color: 'inherit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .PrivateTimePickerToolbar-hourMinuteLabel': {
|
||||||
|
alignItems: 'center',
|
||||||
|
'& > .MuiButton-root span.MuiTypography-root': {
|
||||||
|
fontWeight: 300,
|
||||||
|
lineHeight: '72px',
|
||||||
|
fontSize: '3.75rem',
|
||||||
|
letterSpacing: '-0.5px'
|
||||||
|
},
|
||||||
|
'& > .MuiTypography-root': {
|
||||||
|
color: hexToRGBA(theme.palette.primary.contrastText, 0.54),
|
||||||
|
'& + .MuiButton-root > span.MuiTypography-root': {
|
||||||
|
color: hexToRGBA(theme.palette.primary.contrastText, 0.54)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& .PrivateTimePickerToolbar-ampmSelection span.MuiTypography-root:not(.Mui-selected)': {
|
||||||
|
color: hexToRGBA(theme.palette.primary.contrastText, 0.54)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styling for Mobile Date Picker ends
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiDialogActions: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(5),
|
||||||
|
'&.dialog-actions-dense': {
|
||||||
|
padding: theme.spacing(2.5),
|
||||||
|
paddingTop: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dialog
|
16
core/theme/overrides/divider.ts
Normal file
16
core/theme/overrides/divider.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Divider = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiDivider: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
margin: `${theme.spacing(2)} 0`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Divider
|
88
core/theme/overrides/index.ts
Normal file
88
core/theme/overrides/index.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Overrides Imports
|
||||||
|
import MuiCard from './card'
|
||||||
|
import MuiChip from './chip'
|
||||||
|
import MuiLink from './link'
|
||||||
|
import MuiList from './list'
|
||||||
|
import MuiMenu from './menu'
|
||||||
|
import MuiTabs from './tabs'
|
||||||
|
import MuiInput from './input'
|
||||||
|
import MuiPaper from './paper'
|
||||||
|
import MuiTable from './table'
|
||||||
|
import MuiAlerts from './alerts'
|
||||||
|
import MuiButton from './button'
|
||||||
|
import MuiDialog from './dialog'
|
||||||
|
import MuiRating from './rating'
|
||||||
|
import MuiSelect from './select'
|
||||||
|
import MuiAvatar from './avatars'
|
||||||
|
import MuiDivider from './divider'
|
||||||
|
import MuiPopover from './popover'
|
||||||
|
import MuiTooltip from './tooltip'
|
||||||
|
import MuiBackdrop from './backdrop'
|
||||||
|
import MuiSnackbar from './snackbar'
|
||||||
|
import MuiSwitches from './switches'
|
||||||
|
import MuiTimeline from './timeline'
|
||||||
|
import MuiAccordion from './accordion'
|
||||||
|
import MuiPagination from './pagination'
|
||||||
|
import MuiTypography from './typography'
|
||||||
|
import MuiToggleButton from './toggleButton'
|
||||||
|
import MuiDateTimePicker from './dateTimePicker'
|
||||||
|
|
||||||
|
const Overrides = (theme: Theme) => {
|
||||||
|
const chip = MuiChip(theme)
|
||||||
|
const list = MuiList(theme)
|
||||||
|
const menu = MuiMenu(theme)
|
||||||
|
const tabs = MuiTabs(theme)
|
||||||
|
const cards = MuiCard(theme)
|
||||||
|
const input = MuiInput(theme)
|
||||||
|
const tables = MuiTable(theme)
|
||||||
|
const alerts = MuiAlerts(theme)
|
||||||
|
const button = MuiButton(theme)
|
||||||
|
const rating = MuiRating(theme)
|
||||||
|
const avatars = MuiAvatar(theme)
|
||||||
|
const divider = MuiDivider(theme)
|
||||||
|
const dialog = MuiDialog(theme)
|
||||||
|
const popover = MuiPopover(theme)
|
||||||
|
const tooltip = MuiTooltip(theme)
|
||||||
|
const backdrop = MuiBackdrop(theme)
|
||||||
|
const snackbar = MuiSnackbar(theme)
|
||||||
|
const switches = MuiSwitches(theme)
|
||||||
|
const timeline = MuiTimeline(theme)
|
||||||
|
const accordion = MuiAccordion(theme)
|
||||||
|
const pagination = MuiPagination(theme)
|
||||||
|
const dateTimePicker = MuiDateTimePicker(theme)
|
||||||
|
|
||||||
|
return Object.assign(
|
||||||
|
chip,
|
||||||
|
list,
|
||||||
|
menu,
|
||||||
|
tabs,
|
||||||
|
cards,
|
||||||
|
input,
|
||||||
|
alerts,
|
||||||
|
button,
|
||||||
|
dialog,
|
||||||
|
rating,
|
||||||
|
tables,
|
||||||
|
avatars,
|
||||||
|
divider,
|
||||||
|
MuiLink,
|
||||||
|
popover,
|
||||||
|
tooltip,
|
||||||
|
backdrop,
|
||||||
|
MuiPaper,
|
||||||
|
snackbar,
|
||||||
|
switches,
|
||||||
|
timeline,
|
||||||
|
accordion,
|
||||||
|
MuiSelect,
|
||||||
|
pagination,
|
||||||
|
MuiTypography,
|
||||||
|
dateTimePicker,
|
||||||
|
MuiToggleButton
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Overrides
|
65
core/theme/overrides/input.ts
Normal file
65
core/theme/overrides/input.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const input = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiInputLabel: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiInput: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&:before': {
|
||||||
|
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.22)`
|
||||||
|
},
|
||||||
|
'&:hover:not(.Mui-disabled):before': {
|
||||||
|
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.32)`
|
||||||
|
},
|
||||||
|
'&.Mui-disabled:before': {
|
||||||
|
borderBottom: `1px solid ${theme.palette.text.disabled}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiFilledInput: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.04)`,
|
||||||
|
'&:hover:not(.Mui-disabled)': {
|
||||||
|
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.08)`
|
||||||
|
},
|
||||||
|
'&:before': {
|
||||||
|
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.22)`
|
||||||
|
},
|
||||||
|
'&:hover:not(.Mui-disabled):before': {
|
||||||
|
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.32)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiOutlinedInput: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&:hover:not(.Mui-focused) .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: `rgba(${theme.palette.customColors.main}, 0.32)`
|
||||||
|
},
|
||||||
|
'&:hover.Mui-error .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: theme.palette.error.main
|
||||||
|
},
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: `rgba(${theme.palette.customColors.main}, 0.22)`
|
||||||
|
},
|
||||||
|
'&.Mui-disabled .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: theme.palette.text.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default input
|
9
core/theme/overrides/link.ts
Normal file
9
core/theme/overrides/link.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
MuiLink: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
textDecoration: 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
core/theme/overrides/list.ts
Normal file
44
core/theme/overrides/list.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const List = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiListItemIcon: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
minWidth: 0,
|
||||||
|
marginRight: theme.spacing(2.25),
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiListItemAvatar: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
minWidth: 0,
|
||||||
|
marginRight: theme.spacing(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiListItemText: {
|
||||||
|
styleOverrides: {
|
||||||
|
dense: {
|
||||||
|
'& .MuiListItemText-primary': {
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiListSubheader: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
fontWeight: 600,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default List
|
19
core/theme/overrides/menu.ts
Normal file
19
core/theme/overrides/menu.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Menu = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiMenu: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& .MuiMenu-paper': {
|
||||||
|
borderRadius: 5,
|
||||||
|
boxShadow: theme.palette.mode === 'light' ? theme.shadows[8] : theme.shadows[9]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Menu
|
41
core/theme/overrides/pagination.ts
Normal file
41
core/theme/overrides/pagination.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const Pagination = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiPaginationItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&.Mui-selected:not(.Mui-disabled):not(.MuiPaginationItem-textPrimary):not(.MuiPaginationItem-textSecondary):hover':
|
||||||
|
{
|
||||||
|
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.12)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlined: {
|
||||||
|
borderColor: `rgba(${theme.palette.customColors.main}, 0.22)`
|
||||||
|
},
|
||||||
|
outlinedPrimary: {
|
||||||
|
'&.Mui-selected': {
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.12),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: `${hexToRGBA(theme.palette.primary.main, 0.2)} !important`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlinedSecondary: {
|
||||||
|
'&.Mui-selected': {
|
||||||
|
backgroundColor: hexToRGBA(theme.palette.secondary.main, 0.12),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: `${hexToRGBA(theme.palette.secondary.main, 0.2)} !important`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Pagination
|
9
core/theme/overrides/paper.ts
Normal file
9
core/theme/overrides/paper.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
MuiPaper: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
backgroundImage: 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
core/theme/overrides/popover.ts
Normal file
18
core/theme/overrides/popover.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Popover = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiPopover: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& .MuiPopover-paper': {
|
||||||
|
boxShadow: theme.shadows[6]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Popover
|
16
core/theme/overrides/rating.ts
Normal file
16
core/theme/overrides/rating.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Rating = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiRating: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
color: theme.palette.warning.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Rating
|
12
core/theme/overrides/select.ts
Normal file
12
core/theme/overrides/select.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export default {
|
||||||
|
MuiSelect: {
|
||||||
|
styleOverrides: {
|
||||||
|
select: {
|
||||||
|
minWidth: '6rem !important',
|
||||||
|
'&.MuiTablePagination-select': {
|
||||||
|
minWidth: '1rem !important'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
core/theme/overrides/snackbar.ts
Normal file
16
core/theme/overrides/snackbar.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Snackbar = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiSnackbarContent: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
backgroundColor: theme.palette.mode === 'light' ? theme.palette.grey[900] : theme.palette.grey[100]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Snackbar
|
18
core/theme/overrides/switches.ts
Normal file
18
core/theme/overrides/switches.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Switch = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiSwitch: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& .MuiSwitch-track': {
|
||||||
|
backgroundColor: `rgb(${theme.palette.customColors.main})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Switch
|
69
core/theme/overrides/table.ts
Normal file
69
core/theme/overrides/table.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Table = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiTableContainer: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
boxShadow: theme.shadows[0],
|
||||||
|
borderTopColor: theme.palette.divider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTableHead: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
'& .MuiTableCell-head': {
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: '0.13px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTableBody: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& .MuiTableCell-body': {
|
||||||
|
letterSpacing: '0.25px',
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
'&:not(.MuiTableCell-sizeSmall):not(.MuiTableCell-paddingCheckbox):not(.MuiTableCell-paddingNone)': {
|
||||||
|
paddingTop: theme.spacing(3.5),
|
||||||
|
paddingBottom: theme.spacing(3.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTableRow: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'& .MuiTableCell-head:first-child, & .MuiTableCell-root:first-child ': {
|
||||||
|
paddingLeft: theme.spacing(5)
|
||||||
|
},
|
||||||
|
'& .MuiTableCell-head:last-child, & .MuiTableCell-root:last-child': {
|
||||||
|
paddingRight: theme.spacing(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTableCell: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||||
|
'& .MuiButton-root': {
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stickyHeader: {
|
||||||
|
backgroundColor: theme.palette.customColors.tableHeaderBg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Table
|
30
core/theme/overrides/tabs.ts
Normal file
30
core/theme/overrides/tabs.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Tabs = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiTabs: {
|
||||||
|
styleOverrides: {
|
||||||
|
vertical: {
|
||||||
|
minWidth: 130,
|
||||||
|
marginRight: theme.spacing(4),
|
||||||
|
borderRight: `1px solid ${theme.palette.divider}`,
|
||||||
|
'& .MuiTab-root': {
|
||||||
|
minWidth: 130
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTab: {
|
||||||
|
styleOverrides: {
|
||||||
|
textColorSecondary: {
|
||||||
|
'&.Mui-selected': {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tabs
|
83
core/theme/overrides/timeline.ts
Normal file
83
core/theme/overrides/timeline.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const Timeline = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiTimelineItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&:not(:last-of-type)': {
|
||||||
|
'& .MuiTimelineContent-root': {
|
||||||
|
marginBottom: theme.spacing(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTimelineConnector: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
backgroundColor: theme.palette.divider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTimelineContent: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
marginTop: theme.spacing(0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiTimelineDot: {
|
||||||
|
styleOverrides: {
|
||||||
|
filledPrimary: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.primary.main, 0.12)}`
|
||||||
|
},
|
||||||
|
filledSecondary: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.secondary.main, 0.12)}`
|
||||||
|
},
|
||||||
|
filledSuccess: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.success.main, 0.12)}`
|
||||||
|
},
|
||||||
|
filledError: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.error.main, 0.12)}`
|
||||||
|
},
|
||||||
|
filledWarning: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.warning.main, 0.12)}`
|
||||||
|
},
|
||||||
|
filledInfo: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.info.main, 0.12)}`
|
||||||
|
},
|
||||||
|
filledGrey: {
|
||||||
|
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.grey[400], 0.12)}`
|
||||||
|
},
|
||||||
|
outlinedPrimary: {
|
||||||
|
'& svg': { color: theme.palette.primary.main }
|
||||||
|
},
|
||||||
|
outlinedSecondary: {
|
||||||
|
'& svg': { color: theme.palette.secondary.main }
|
||||||
|
},
|
||||||
|
outlinedSuccess: {
|
||||||
|
'& svg': { color: theme.palette.success.main }
|
||||||
|
},
|
||||||
|
outlinedError: {
|
||||||
|
'& svg': { color: theme.palette.error.main }
|
||||||
|
},
|
||||||
|
outlinedWarning: {
|
||||||
|
'& svg': { color: theme.palette.warning.main }
|
||||||
|
},
|
||||||
|
outlinedInfo: {
|
||||||
|
'& svg': { color: theme.palette.info.main }
|
||||||
|
},
|
||||||
|
outlinedGrey: {
|
||||||
|
'& svg': { color: theme.palette.grey[500] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Timeline
|
16
core/theme/overrides/toggleButton.ts
Normal file
16
core/theme/overrides/toggleButton.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export default {
|
||||||
|
MuiToggleButtonGroup: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiToggleButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
core/theme/overrides/tooltip.ts
Normal file
28
core/theme/overrides/tooltip.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
// ** Util Import
|
||||||
|
import { hexToRGBA } from '../../../core/utils/hex-to-rgba'
|
||||||
|
|
||||||
|
const Tooltip = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiTooltip: {
|
||||||
|
styleOverrides: {
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? hexToRGBA(theme.palette.grey[900], 0.9)
|
||||||
|
: hexToRGBA(theme.palette.grey[700], 0.9)
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
color:
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? hexToRGBA(theme.palette.grey[900], 0.9)
|
||||||
|
: hexToRGBA(theme.palette.grey[700], 0.9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tooltip
|
16
core/theme/overrides/typography.ts
Normal file
16
core/theme/overrides/typography.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Typography = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
MuiTypography: {
|
||||||
|
styleOverrides: {
|
||||||
|
gutterBottom: {
|
||||||
|
marginBottom: theme.spacing(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Typography
|
111
core/theme/palette/index.ts
Normal file
111
core/theme/palette/index.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// ** Type Imports
|
||||||
|
import { PaletteMode } from '@mui/material'
|
||||||
|
import { ThemeColor } from '../core/layouts/types'
|
||||||
|
|
||||||
|
const DefaultPalette = (mode: PaletteMode, themeColor: ThemeColor) => {
|
||||||
|
// ** Vars
|
||||||
|
const lightColor = '58, 53, 65'
|
||||||
|
const darkColor = '231, 227, 252'
|
||||||
|
const mainColor = mode === 'light' ? lightColor : darkColor
|
||||||
|
|
||||||
|
const primaryGradient = () => {
|
||||||
|
if (themeColor === 'primary') {
|
||||||
|
return '#C6A7FE'
|
||||||
|
} else if (themeColor === 'secondary') {
|
||||||
|
return '#9C9FA4'
|
||||||
|
} else if (themeColor === 'success') {
|
||||||
|
return '#93DD5C'
|
||||||
|
} else if (themeColor === 'error') {
|
||||||
|
return '#FF8C90'
|
||||||
|
} else if (themeColor === 'warning') {
|
||||||
|
return '#FFCF5C'
|
||||||
|
} else {
|
||||||
|
return '#6ACDFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
customColors: {
|
||||||
|
main: mainColor,
|
||||||
|
primaryGradient: primaryGradient(),
|
||||||
|
tableHeaderBg: mode === 'light' ? '#F9FAFC' : '#3D3759'
|
||||||
|
},
|
||||||
|
common: {
|
||||||
|
black: '#000',
|
||||||
|
white: '#FFF'
|
||||||
|
},
|
||||||
|
mode: mode,
|
||||||
|
primary: {
|
||||||
|
light: '#9E69FD',
|
||||||
|
main: '#9155FD',
|
||||||
|
dark: '#804BDF',
|
||||||
|
contrastText: '#FFF'
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
light: '#9C9FA4',
|
||||||
|
main: '#8A8D93',
|
||||||
|
dark: '#777B82',
|
||||||
|
contrastText: '#FFF'
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
light: '#6AD01F',
|
||||||
|
main: '#56CA00',
|
||||||
|
dark: '#4CB200',
|
||||||
|
contrastText: '#FFF'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
light: '#FF6166',
|
||||||
|
main: '#FF4C51',
|
||||||
|
dark: '#E04347',
|
||||||
|
contrastText: '#FFF'
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
light: '#FFCA64',
|
||||||
|
main: '#FFB400',
|
||||||
|
dark: '#E09E00',
|
||||||
|
contrastText: '#FFF'
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
light: '#32BAFF',
|
||||||
|
main: '#16B1FF',
|
||||||
|
dark: '#139CE0',
|
||||||
|
contrastText: '#FFF'
|
||||||
|
},
|
||||||
|
grey: {
|
||||||
|
50: '#FAFAFA',
|
||||||
|
100: '#F5F5F5',
|
||||||
|
200: '#EEEEEE',
|
||||||
|
300: '#E0E0E0',
|
||||||
|
400: '#BDBDBD',
|
||||||
|
500: '#9E9E9E',
|
||||||
|
600: '#757575',
|
||||||
|
700: '#616161',
|
||||||
|
800: '#424242',
|
||||||
|
900: '#212121',
|
||||||
|
A100: '#D5D5D5',
|
||||||
|
A200: '#AAAAAA',
|
||||||
|
A400: '#616161',
|
||||||
|
A700: '#303030'
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: `rgba(${mainColor}, 0.87)`,
|
||||||
|
secondary: `rgba(${mainColor}, 0.68)`,
|
||||||
|
disabled: `rgba(${mainColor}, 0.38)`
|
||||||
|
},
|
||||||
|
divider: `rgba(${mainColor}, 0.12)`,
|
||||||
|
background: {
|
||||||
|
paper: mode === 'light' ? '#FFF' : '#312D4B',
|
||||||
|
default: mode === 'light' ? '#F4F5FA' : '#28243D'
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
active: `rgba(${mainColor}, 0.54)`,
|
||||||
|
hover: `rgba(${mainColor}, 0.04)`,
|
||||||
|
selected: `rgba(${mainColor}, 0.08)`,
|
||||||
|
disabled: `rgba(${mainColor}, 0.3)`,
|
||||||
|
disabledBackground: `rgba(${mainColor}, 0.18)`,
|
||||||
|
focus: `rgba(${mainColor}, 0.12)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DefaultPalette
|
63
core/theme/shadows/index.ts
Normal file
63
core/theme/shadows/index.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// ** Theme Type Import
|
||||||
|
import { PaletteMode, ThemeOptions } from '@mui/material'
|
||||||
|
|
||||||
|
const Shadows = (mode: PaletteMode): ThemeOptions['shadows'] => {
|
||||||
|
if (mode === 'light') {
|
||||||
|
return [
|
||||||
|
'none',
|
||||||
|
'0px 2px 1px -1px rgba(58, 53, 65, 0.2), 0px 1px 1px 0px rgba(58, 53, 65, 0.14), 0px 1px 3px 0px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 3px 1px -2px rgba(58, 53, 65, 0.2), 0px 2px 2px 0px rgba(58, 53, 65, 0.14), 0px 1px 5px 0px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 4px 8px -4px rgba(58, 53, 65, 0.42)',
|
||||||
|
'0px 6px 18px -8px rgba(58, 53, 65, 0.56)',
|
||||||
|
'0px 3px 5px -1px rgba(58, 53, 65, 0.2), 0px 5px 8px 0px rgba(58, 53, 65, 0.14), 0px 1px 14px 0px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 2px 10px 0px rgba(58, 53, 65, 0.1)',
|
||||||
|
'0px 4px 5px -2px rgba(58, 53, 65, 0.2), 0px 7px 10px 1px rgba(58, 53, 65, 0.14), 0px 2px 16px 1px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 5px 5px -3px rgba(58, 53, 65, 0.2), 0px 8px 10px 1px rgba(58, 53, 65, 0.14), 0px 3px 14px 2px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 5px 6px -3px rgba(58, 53, 65, 0.2), 0px 9px 12px 1px rgba(58, 53, 65, 0.14), 0px 3px 16px 2px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 6px 6px -3px rgba(58, 53, 65, 0.2), 0px 10px 14px 1px rgba(58, 53, 65, 0.14), 0px 4px 18px 3px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 6px 7px -4px rgba(58, 53, 65, 0.2), 0px 11px 15px 1px rgba(58, 53, 65, 0.14), 0px 4px 20px 3px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 7px 8px -4px rgba(58, 53, 65, 0.2), 0px 12px 17px 2px rgba(58, 53, 65, 0.14), 0px 5px 22px 4px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 7px 8px -4px rgba(58, 53, 65, 0.2), 0px 13px 19px 2px rgba(58, 53, 65, 0.14), 0px 5px 24px 4px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 7px 9px -4px rgba(58, 53, 65, 0.2), 0px 14px 21px 2px rgba(58, 53, 65, 0.14), 0px 5px 26px 4px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 8px 9px -5px rgba(58, 53, 65, 0.2), 0px 15px 22px 2px rgba(58, 53, 65, 0.14), 0px 6px 28px 5px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 8px 10px -5px rgba(58, 53, 65, 0.2), 0px 16px 24px 2px rgba(58, 53, 65, 0.14), 0px 6px 30px 5px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 8px 11px -5px rgba(58, 53, 65, 0.2), 0px 17px 26px 2px rgba(58, 53, 65, 0.14), 0px 6px 32px 5px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 9px 11px -5px rgba(58, 53, 65, 0.2), 0px 18px 28px 2px rgba(58, 53, 65, 0.14), 0px 7px 34px 6px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 9px 12px -6px rgba(58, 53, 65, 0.2), 0px 19px 29px 2px rgba(58, 53, 65, 0.14), 0px 7px 36px 6px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 10px 13px -6px rgba(58, 53, 65, 0.2), 0px 20px 31px 3px rgba(58, 53, 65, 0.14), 0px 8px 38px 7px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 10px 13px -6px rgba(58, 53, 65, 0.2), 0px 21px 33px 3px rgba(58, 53, 65, 0.14), 0px 8px 40px 7px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 10px 14px -6px rgba(58, 53, 65, 0.2), 0px 22px 35px 3px rgba(58, 53, 65, 0.14), 0px 8px 42px 7px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 11px 14px -7px rgba(58, 53, 65, 0.2), 0px 23px 36px 3px rgba(58, 53, 65, 0.14), 0px 9px 44px 8px rgba(58, 53, 65, 0.12)',
|
||||||
|
'0px 11px 15px -7px rgba(58, 53, 65, 0.2), 0px 24px 38px 3px rgba(58, 53, 65, 0.14), 0px 9px 46px 8px rgba(58, 53, 65, 0.12)'
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'none',
|
||||||
|
'0px 2px 1px -1px rgba(19, 17, 32, 0.2), 0px 1px 1px 0px rgba(19, 17, 32, 0.14), 0px 1px 3px 0px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 3px 1px -2px rgba(19, 17, 32, 0.2), 0px 2px 2px 0px rgba(19, 17, 32, 0.14), 0px 1px 5px 0px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 4px 8px -4px rgba(19, 17, 32, 0.42)',
|
||||||
|
'0px 6px 18px -8px rgba(19, 17, 32, 0.56)',
|
||||||
|
'0px 3px 5px -1px rgba(19, 17, 32, 0.2), 0px 5px 8px rgba(19, 17, 32, 0.14), 0px 1px 14px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 2px 10px 0px rgba(19, 17, 32, 0.1)',
|
||||||
|
'0px 4px 5px -2px rgba(19, 17, 32, 0.2), 0px 7px 10px 1px rgba(19, 17, 32, 0.14), 0px 2px 16px 1px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 5px 5px -3px rgba(19, 17, 32, 0.2), 0px 8px 10px 1px rgba(19, 17, 32, 0.14), 0px 3px 14px 2px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 5px 6px -3px rgba(19, 17, 32, 0.2), 0px 9px 12px 1px rgba(19, 17, 32, 0.14), 0px 3px 16px 2px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 6px 6px -3px rgba(19, 17, 32, 0.2), 0px 10px 14px 1px rgba(19, 17, 32, 0.14), 0px 4px 18px 3px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 6px 7px -4px rgba(19, 17, 32, 0.2), 0px 11px 15px 1px rgba(19, 17, 32, 0.14), 0px 4px 20px 3px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 7px 8px -4px rgba(19, 17, 32, 0.2), 0px 12px 17px 2px rgba(19, 17, 32, 0.14), 0px 5px 22px 4px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 7px 8px -4px rgba(19, 17, 32, 0.2), 0px 13px 19px 2px rgba(19, 17, 32, 0.14), 0px 5px 24px 4px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 7px 9px -4px rgba(19, 17, 32, 0.2), 0px 14px 21px 2px rgba(19, 17, 32, 0.14), 0px 5px 26px 4px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 8px 9px -5px rgba(19, 17, 32, 0.2), 0px 15px 22px 2px rgba(19, 17, 32, 0.14), 0px 6px 28px 5px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 8px 10px -5px rgba(19, 17, 32, 0.2), 0px 16px 24px 2px rgba(19, 17, 32, 0.14), 0px 6px 30px 5px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 8px 11px -5px rgba(19, 17, 32, 0.2), 0px 17px 26px 2px rgba(19, 17, 32, 0.14), 0px 6px 32px 5px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 9px 11px -5px rgba(19, 17, 32, 0.2), 0px 18px 28px 2px rgba(19, 17, 32, 0.14), 0px 7px 34px 6px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 9px 12px -6px rgba(19, 17, 32, 0.2), 0px 19px 29px 2px rgba(19, 17, 32, 0.14), 0px 7px 36px 6px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 10px 13px -6px rgba(19, 17, 32, 0.2), 0px 20px 31px 3px rgba(19, 17, 32, 0.14), 0px 8px 38px 7px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 10px 13px -6px rgba(19, 17, 32, 0.2), 0px 21px 33px 3px rgba(19, 17, 32, 0.14), 0px 8px 40px 7px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 10px 14px -6px rgba(19, 17, 32, 0.2), 0px 22px 35px 3px rgba(19, 17, 32, 0.14), 0px 8px 42px 7px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 11px 14px -7px rgba(19, 17, 32, 0.2), 0px 23px 36px 3px rgba(19, 17, 32, 0.14), 0px 9px 44px 8px rgba(19, 17, 32, 0.12)',
|
||||||
|
'0px 11px 15px -7px rgba(19, 17, 32, 0.2), 0px 24px 38px 3px rgba(19, 17, 32, 0.14), 0px 9px 46px 8px rgba(19, 17, 32, 0.12)'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Shadows
|
3
core/theme/spacing/index.ts
Normal file
3
core/theme/spacing/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
spacing: (factor: number) => `${0.25 * factor}rem`
|
||||||
|
}
|
18
core/theme/types.ts
Normal file
18
core/theme/types.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
declare module '@mui/material/styles' {
|
||||||
|
interface Palette {
|
||||||
|
customColors: {
|
||||||
|
main: string
|
||||||
|
tableHeaderBg: string
|
||||||
|
primaryGradient: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface PaletteOptions {
|
||||||
|
customColors?: {
|
||||||
|
main?: string
|
||||||
|
tableHeaderBg?: string
|
||||||
|
primaryGradient?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {}
|
67
core/theme/typography/index.ts
Normal file
67
core/theme/typography/index.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// ** Theme Type Import
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const Typography = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
h1: {
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '-1.5px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
h2: {
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '-0.5px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
h3: {
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
h4: {
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: '0.25px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
h5: {
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
h6: {
|
||||||
|
letterSpacing: '0.15px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
subtitle1: {
|
||||||
|
letterSpacing: '0.15px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
subtitle2: {
|
||||||
|
letterSpacing: '0.1px',
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
body1: {
|
||||||
|
letterSpacing: '0.15px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
body2: {
|
||||||
|
lineHeight: 1.5,
|
||||||
|
letterSpacing: '0.15px',
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
letterSpacing: '0.3px',
|
||||||
|
color: theme.palette.text.primary
|
||||||
|
},
|
||||||
|
caption: {
|
||||||
|
letterSpacing: '0.4px',
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
overline: {
|
||||||
|
letterSpacing: '1px',
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Typography
|
5
core/utils/create-emotion-cache.ts
Normal file
5
core/utils/create-emotion-cache.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import createCache from '@emotion/cache'
|
||||||
|
|
||||||
|
export const createEmotionCache = () => {
|
||||||
|
return createCache({ key: 'css' })
|
||||||
|
}
|
16
core/utils/hex-to-rgba.ts
Normal file
16
core/utils/hex-to-rgba.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
** Hex color to RGBA color
|
||||||
|
*/
|
||||||
|
export const hexToRGBA = (hexCode: string, opacity: number) => {
|
||||||
|
let hex = hexCode.replace('#', '')
|
||||||
|
|
||||||
|
if (hex.length === 3) {
|
||||||
|
hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = parseInt(hex.substring(0, 2), 16)
|
||||||
|
const g = parseInt(hex.substring(2, 4), 16)
|
||||||
|
const b = parseInt(hex.substring(4, 6), 16)
|
||||||
|
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
export type User = {
|
|
||||||
email: string;
|
|
||||||
email_verified: boolean;
|
|
||||||
name: string;
|
|
||||||
nickname: string;
|
|
||||||
picture: string;
|
|
||||||
sub: string;
|
|
||||||
updated_at: string;
|
|
||||||
};
|
|
77
layouts/UserLayout.tsx
Normal file
77
layouts/UserLayout.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||||
|
|
||||||
|
// ** Layout Imports
|
||||||
|
// !Do not remove this Layout import
|
||||||
|
import VerticalLayout from '../core/layouts/VerticalLayout'
|
||||||
|
|
||||||
|
// ** Navigation Imports
|
||||||
|
import VerticalNavItems from '../navigation/vertical'
|
||||||
|
|
||||||
|
// ** Component Import
|
||||||
|
import UpgradeToProButton from './components/UpgradeToProButton'
|
||||||
|
import VerticalAppBarContent from './components/vertical/AppBarContent'
|
||||||
|
|
||||||
|
// ** Hook Import
|
||||||
|
import { useSettings } from '../core/hooks/useSettings'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserLayout = ({ children }: Props) => {
|
||||||
|
// ** Hooks
|
||||||
|
const { settings, saveSettings } = useSettings()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The below variable will hide the current layout menu at given screen size.
|
||||||
|
* The menu will be accessible from the Hamburger icon only (Vertical Overlay Menu).
|
||||||
|
* You can change the screen size from which you want to hide the current layout menu.
|
||||||
|
* Please refer useMediaQuery() hook: https://mui.com/components/use-media-query/,
|
||||||
|
* to know more about what values can be passed to this hook.
|
||||||
|
* ! Do not change this value unless you know what you are doing. It can break the template.
|
||||||
|
*/
|
||||||
|
const hidden = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'))
|
||||||
|
|
||||||
|
const UpgradeToProImg = () => {
|
||||||
|
return (
|
||||||
|
<Box sx={{ mx: 'auto' }}>
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
href='https://themeselection.com/products/materio-mui-react-nextjs-admin-template/'
|
||||||
|
>
|
||||||
|
<img width={230} alt='upgrade to premium' src={`/images/misc/upgrade-banner-${settings.mode}.png`} />
|
||||||
|
</a>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VerticalLayout
|
||||||
|
hidden={hidden}
|
||||||
|
settings={settings}
|
||||||
|
saveSettings={saveSettings}
|
||||||
|
verticalNavItems={VerticalNavItems()} // Navigation Items
|
||||||
|
verticalAppBarContent={(
|
||||||
|
props // AppBar Content
|
||||||
|
) => (
|
||||||
|
<VerticalAppBarContent
|
||||||
|
hidden={hidden}
|
||||||
|
settings={settings}
|
||||||
|
saveSettings={saveSettings}
|
||||||
|
toggleNavVisibility={props.toggleNavVisibility}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</VerticalLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserLayout
|
111
layouts/components/UpgradeToProButton.tsx
Normal file
111
layouts/components/UpgradeToProButton.tsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// ** React Import
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Fade from '@mui/material/Fade'
|
||||||
|
import Paper from '@mui/material/Paper'
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import CardContent from '@mui/material/CardContent'
|
||||||
|
|
||||||
|
// ** Third Party Imports
|
||||||
|
import { usePopper } from 'react-popper'
|
||||||
|
|
||||||
|
const BuyNowButton = () => {
|
||||||
|
// ** States
|
||||||
|
const [open, setOpen] = useState<boolean>(false)
|
||||||
|
const [popperElement, setPopperElement] = useState(null)
|
||||||
|
const [referenceElement, setReferenceElement] = useState(null)
|
||||||
|
|
||||||
|
const { styles, attributes, update } = usePopper(referenceElement, popperElement, {
|
||||||
|
placement: 'top-end'
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleOpen = () => {
|
||||||
|
setOpen(true)
|
||||||
|
update ? update() : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className='upgrade-to-pro-button mui-fixed'
|
||||||
|
sx={{ right: theme => theme.spacing(20), bottom: theme => theme.spacing(10), zIndex: 11, position: 'fixed' }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
component='a'
|
||||||
|
target='_blank'
|
||||||
|
variant='contained'
|
||||||
|
onMouseEnter={handleOpen}
|
||||||
|
onMouseLeave={handleClose}
|
||||||
|
ref={(e: any) => setReferenceElement(e)}
|
||||||
|
href='https://themeselection.com/products/materio-mui-react-nextjs-admin-template/'
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#ff3e1d',
|
||||||
|
boxShadow: '0 1px 20px 1px #ff3e1d',
|
||||||
|
'&:hover': {
|
||||||
|
boxShadow: 'none',
|
||||||
|
backgroundColor: '#e6381a'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Upgrade To Pro
|
||||||
|
</Button>
|
||||||
|
<Fade in={open} timeout={700}>
|
||||||
|
<Box
|
||||||
|
style={styles.popper}
|
||||||
|
ref={setPopperElement}
|
||||||
|
{...attributes.popper}
|
||||||
|
onMouseEnter={handleOpen}
|
||||||
|
onMouseLeave={handleClose}
|
||||||
|
sx={{ pb: 4, minWidth: theme => (theme.breakpoints.down('sm') ? 400 : 300) }}
|
||||||
|
>
|
||||||
|
<Paper elevation={9} sx={{ borderRadius: 1, overflow: 'hidden' }}>
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel='noreferrer'
|
||||||
|
href='https://themeselection.com/products/materio-mui-react-nextjs-admin-template/'
|
||||||
|
>
|
||||||
|
<img width='100%' alt='materio-pro-banner' src='/images/misc/materio-pro-banner.png' />
|
||||||
|
</a>
|
||||||
|
<CardContent>
|
||||||
|
<Typography sx={{ mb: 4 }} variant='h6'>
|
||||||
|
Materio - React Admin Template
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: 4 }} variant='body2'>
|
||||||
|
Materio Admin is the most developer friendly & highly customizable Admin Dashboard Template based on MUI
|
||||||
|
and NextJS.
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: 4 }} variant='body2'>
|
||||||
|
Click on below buttons to explore PRO version.
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
component='a'
|
||||||
|
sx={{ mr: 4 }}
|
||||||
|
target='_blank'
|
||||||
|
variant='contained'
|
||||||
|
href='https://demos.themeselection.com/materio-mui-react-nextjs-admin-template/landing/'
|
||||||
|
>
|
||||||
|
Demo
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
component='a'
|
||||||
|
target='_blank'
|
||||||
|
variant='outlined'
|
||||||
|
href='https://themeselection.com/products/materio-mui-react-nextjs-admin-template/'
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Fade>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BuyNowButton
|
29
layouts/components/UserIcon.tsx
Normal file
29
layouts/components/UserIcon.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// ** React Imports
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
// ** MUI Imports
|
||||||
|
import { SvgIconProps } from '@mui/material'
|
||||||
|
|
||||||
|
interface UserIconProps {
|
||||||
|
iconProps?: SvgIconProps
|
||||||
|
icon: string | ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserIcon = (props: UserIconProps) => {
|
||||||
|
// ** Props
|
||||||
|
const { icon, iconProps } = props
|
||||||
|
|
||||||
|
const IconTag = icon
|
||||||
|
|
||||||
|
let styles
|
||||||
|
|
||||||
|
/* styles = {
|
||||||
|
color: 'red',
|
||||||
|
fontSize: '2rem'
|
||||||
|
} */
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return <IconTag {...iconProps} style={{ ...styles }} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserIcon
|
79
layouts/components/vertical/AppBarContent.tsx
Normal file
79
layouts/components/vertical/AppBarContent.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// ** MUI Imports
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import { Theme } from '@mui/material/styles'
|
||||||
|
import TextField from '@mui/material/TextField'
|
||||||
|
import IconButton from '@mui/material/IconButton'
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||||
|
import InputAdornment from '@mui/material/InputAdornment'
|
||||||
|
import {
|
||||||
|
NovuProvider,
|
||||||
|
PopoverNotificationCenter,
|
||||||
|
NotificationBell,
|
||||||
|
} from '@novu/notification-center';
|
||||||
|
|
||||||
|
|
||||||
|
// ** Icons Imports
|
||||||
|
import Menu from 'mdi-material-ui/Menu'
|
||||||
|
import Magnify from 'mdi-material-ui/Magnify'
|
||||||
|
|
||||||
|
// ** Type Import
|
||||||
|
import { Settings } from '../../../core/context/settingsContext'
|
||||||
|
|
||||||
|
// ** Components
|
||||||
|
import ModeToggler from '../../../core/layouts/components/shared-components/ModeToggler'
|
||||||
|
import UserDropdown from '../../../core/layouts/components/shared-components/UserDropdown'
|
||||||
|
import NotificationDropdown from '../../../core/layouts/components/shared-components/NotificationDropdown'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
interface Props {
|
||||||
|
hidden: boolean
|
||||||
|
settings: Settings
|
||||||
|
toggleNavVisibility: () => void
|
||||||
|
saveSettings: (values: Settings) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppBarContent = (props: Props) => {
|
||||||
|
// ** Props
|
||||||
|
const { hidden, settings, saveSettings, toggleNavVisibility } = props
|
||||||
|
|
||||||
|
// ** Hook
|
||||||
|
const hiddenSm = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'))
|
||||||
|
|
||||||
|
const [profileData, setProfileData] = useState(null);
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const artistProfileRequest = await fetch('/api/me', { method: "GET" });
|
||||||
|
const artistProfileResponse = await artistProfileRequest.json();
|
||||||
|
setProfileData(artistProfileResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log(profileData)
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, []); return (
|
||||||
|
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
<Box className='actions-left' sx={{ mr: 2, display: 'flex', alignItems: 'center' }}>
|
||||||
|
{hidden ? (
|
||||||
|
<IconButton
|
||||||
|
color='inherit'
|
||||||
|
onClick={toggleNavVisibility}
|
||||||
|
sx={{ ml: -2.75, ...(hiddenSm ? {} : { mr: 3.5 }) }}
|
||||||
|
>
|
||||||
|
<Menu />
|
||||||
|
</IconButton>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
<Box className='actions-right' sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
{(profileData ? (
|
||||||
|
<NovuProvider subscriberId={profileData["id"]} applicationIdentifier={'9SKjzgN_odAF'}>
|
||||||
|
<PopoverNotificationCenter colorScheme={'dark'}>
|
||||||
|
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
|
||||||
|
</PopoverNotificationCenter>
|
||||||
|
</NovuProvider>
|
||||||
|
):null)}
|
||||||
|
<UserDropdown />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppBarContent
|
131
navigation/vertical/index.ts
Normal file
131
navigation/vertical/index.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// ** Icon imports
|
||||||
|
import Login from 'mdi-material-ui/Login'
|
||||||
|
import Table from 'mdi-material-ui/Table'
|
||||||
|
import CubeOutline from 'mdi-material-ui/CubeOutline'
|
||||||
|
import HomeOutline from 'mdi-material-ui/HomeOutline'
|
||||||
|
import SettingsApplicationsIcon from '@mui/icons-material/SettingsApplications';
|
||||||
|
import ListIcon from '@mui/icons-material/List';
|
||||||
|
// ** Type import
|
||||||
|
import { VerticalNavItemsType } from '../../core/layouts/types'
|
||||||
|
import { BankTransfer, Cart, Clipboard, PageFirst, StarOutline } from 'mdi-material-ui'
|
||||||
|
import { DocumentScanner, FileOpen, LockPerson, OpenInBrowser, People, PeopleOutline, Settings, WebAsset } from '@mui/icons-material'
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
const navigation = (): VerticalNavItemsType => {
|
||||||
|
const [isStripeOnboarded, setIsStripeOnboarded] = useState(false);
|
||||||
|
const [profileData, setProfileData] = useState(null);
|
||||||
|
const [userData, setUserData] = useState(null);
|
||||||
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
|
||||||
|
const adminCheck = await fetch('/api/admin/check', { method: "GET" });
|
||||||
|
if (adminCheck.status === 200) {
|
||||||
|
setIsAdmin(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onboardCheckRequest = await fetch('/api/artist/onboarded', { method: "GET" });
|
||||||
|
const onboardCheckResponse = await onboardCheckRequest.json();
|
||||||
|
setIsStripeOnboarded(onboardCheckResponse["onboarded"]);
|
||||||
|
|
||||||
|
const artistProfileRequest = await fetch('/api/artist/profile', { method: "GET" });
|
||||||
|
const artistProfileResponse = await artistProfileRequest.json();
|
||||||
|
setProfileData(artistProfileResponse);
|
||||||
|
|
||||||
|
const userRequest = await fetch('/api/me', { method: "GET" });
|
||||||
|
const userResponse = await userRequest.json();
|
||||||
|
setUserData(userResponse);
|
||||||
|
////console.log(roleResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
var result = [
|
||||||
|
{
|
||||||
|
title: 'Dashboard',
|
||||||
|
icon: HomeOutline,
|
||||||
|
path: '/dashboard'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sectionTitle: 'General'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Account Settings',
|
||||||
|
icon: Settings,
|
||||||
|
path: '/dashboard/settings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Your Requests',
|
||||||
|
icon: ListIcon,
|
||||||
|
path: '/dashboard/requests'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isAdmin) {
|
||||||
|
result.push(
|
||||||
|
{
|
||||||
|
sectionTitle: 'Admin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Manage Artist Access',
|
||||||
|
icon: LockPerson,
|
||||||
|
path: '/dashboard/admin/requests'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Manage Users',
|
||||||
|
icon: People,
|
||||||
|
path: '/dashboard/admin/users'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Manage Artists',
|
||||||
|
icon: PeopleOutline,
|
||||||
|
path: '/dashboard/admin/artists'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStripeOnboarded) {
|
||||||
|
result.push(
|
||||||
|
{
|
||||||
|
sectionTitle: 'Artist'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Request Reviews',
|
||||||
|
icon: StarOutline,
|
||||||
|
path: '/dashboard/artist/reviews'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Incoming Requests',
|
||||||
|
icon: ListIcon,
|
||||||
|
path: '/dashboard/artist/requests'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Payments/Payouts',
|
||||||
|
icon: BankTransfer,
|
||||||
|
path: '/dashboard/artist/payout'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Artist Settings',
|
||||||
|
icon: Settings,
|
||||||
|
path: '/dashboard/artist/artistsettings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Page Settings',
|
||||||
|
icon: WebAsset,
|
||||||
|
path: '/dashboard/artist/pagesettings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Your Page',
|
||||||
|
icon: OpenInBrowser,
|
||||||
|
path: '/box/' + (userData ? userData["displayName"] : "")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default navigation;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user