Compare commits

...

24 Commits
0.2.0 ... main

Author SHA1 Message Date
01cc7eb197 Revert "dockerfile"
This reverts commit 2d4663ef7416ea2f3efe808f0cfca9a0557f0dc3.
2024-05-21 22:35:19 -04:00
2d4663ef74 dockerfile 2024-05-21 22:35:13 -04:00
Damien Ostler
0807d6d11d fix: removed logo that is broken 2024-03-17 06:56:35 -04:00
Damien Ostler
2a405067d8 feat: added pages for viewing artist access requests, users, and artists in the admin menus 2024-03-17 06:47:37 -04:00
Damien Ostler
23ea8eed79 fix: admin menus dont show up for non admins now 2024-03-03 16:23:40 -05:00
Damien Ostler
8569290c0e chore: cleaned up the naming of components, apis, and pgaes. 2024-03-03 16:19:47 -05:00
Damien Ostler
5a2f43768d feat: admin list pages and apis 2024-03-03 16:03:22 -05:00
Damien Ostler
ff465278ad fix: filtering and sorting disabled now 2024-03-03 14:11:43 -05:00
Damien Ostler
a63d7f35b8 fix: upload button enabled when not paid 2024-03-02 01:23:02 -05:00
Damien Ostler
93d4518500 feat: refactoring + assets and reference images and reviews 2024-03-02 00:51:43 -05:00
Damien Ostler
d52018f7eb removed login override 2024-02-28 21:28:30 -05:00
Damien Ostler
aac4ec902f feat: rating/reviews 2024-02-27 21:37:43 -05:00
Damien Ostler
238477665d feat: alot of shit i forgot to push lol woops 2024-02-25 18:34:51 -05:00
Damien Ostler
b9aed8f3bb fix: name not being returned 2024-02-18 07:23:34 -05:00
Damien Ostler
565cb5dffb feat: shop builder 2024-02-18 06:59:55 -05:00
Damien Ostler
539e1607bd fix: fixed how the wizard for onboarding works. it still skips the manage portfolio step but meh. 2024-02-18 02:31:54 -05:00
Damien Ostler
481bcd2d6c feat: new templates 2024-02-18 01:44:48 -05:00
Damien Ostler
13bd42ecbd save 2024-02-15 22:18:20 -05:00
Damien Ostler
6c2acada86 fix: made header less see through 2024-02-15 21:57:55 -05:00
Damien Ostler
f90378b991 fix: made header less roundy 2024-02-15 21:56:37 -05:00
Damien Ostler
9169063a08 fix: landing page user buttons 2024-02-15 21:52:31 -05:00
Damien Ostler
937faf1847 feat:landing page 2024-02-15 21:49:12 -05:00
Damien Ostler
c8b1dfb3ba feat: added portfolio to artist dashboard 2024-02-15 02:16:38 -05:00
Damien Ostler
ed48b67e3c ui 2024-02-14 23:48:46 -05:00
233 changed files with 15382 additions and 3705 deletions

View File

@ -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
View 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
View 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
View 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
View 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
View 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&nbsp;</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 }}>
&nbsp;&nbsp;
</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
View 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&nbsp;
<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 &quot;Become an artist&quot; you agree to our&nbsp;
<Link href="#" color="primary">
Terms & Conditions
</Link>
.
</Typography>
</Stack>
</Container>
</Box>
);
}

122
components/Highlights.tsx Normal file
View 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
View 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&apos;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">
&nbsp; 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
View 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;

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

View File

@ -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

View File

@ -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

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

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

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

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

View 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

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

View File

@ -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>
)
) )
) )
} }

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

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

View 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

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

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

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

View File

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

View File

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

View File

@ -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
View File

36
configs/themeConfig.tsx Normal file
View 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

View File

@ -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'
}

View 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'
}

View 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

View 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

View 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

View File

@ -0,0 +1,4 @@
import { useContext } from 'react'
import { SettingsContext, SettingsContextValue } from '../../core/context/settingsContext'
export const useSettings = (): SettingsContextValue => useContext(SettingsContext)

View 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

View 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

View 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

View File

@ -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

View 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

View File

@ -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

View 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

View 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

View 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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,11 @@
const breakpoints = () => ({
values: {
xs: 0,
sm: 600,
md: 900,
lg: 1200,
xl: 1536
}
})
export default breakpoints

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,9 @@
export default {
MuiLink: {
styleOverrides: {
root: {
textDecoration: 'none'
}
}
}
}

View 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

View 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

View 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

View File

@ -0,0 +1,9 @@
export default {
MuiPaper: {
styleOverrides: {
root: {
backgroundImage: 'none'
}
}
}
}

View 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

View 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

View File

@ -0,0 +1,12 @@
export default {
MuiSelect: {
styleOverrides: {
select: {
minWidth: '6rem !important',
'&.MuiTablePagination-select': {
minWidth: '1rem !important'
}
}
}
}
}

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,16 @@
export default {
MuiToggleButtonGroup: {
styleOverrides: {
root: {
borderRadius: 4
}
}
},
MuiToggleButton: {
styleOverrides: {
root: {
borderRadius: 4
}
}
}
}

View 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

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

View 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

View File

@ -0,0 +1,3 @@
export default {
spacing: (factor: number) => `${0.25 * factor}rem`
}

18
core/theme/types.ts Normal file
View 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 {}

View 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

View 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
View 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})`
}

View File

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

View 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

View 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

View 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

View 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