Commit 08883c76 authored by janithGamage's avatar janithGamage

fix: update

parent fae08620
.apexcharts-legend-series .apexcharts-legend-marker {
left: -4px !important;
right: -4px !important;
}
.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series,
.apexcharts-legend.apx-legend-position-top .apexcharts-legend-series {
gap: 8px;
}
.apexcharts-legend-series {
display: flex;
gap: 8px;
}
.cell-center {
text-align: center;
}
.cell-center > * {
margin: 0 auto;
}
.cell-right {
text-align: right;
}
.cell-right > * {
margin: 0 0 0 auto;
}
import { ReactNode } from 'react';
// third-party
import { motion, useCycle } from 'framer-motion';
// ==============================|| ANIMATION BUTTON ||============================== //
export type ScaleProps = {
hover: number | string | undefined;
tap: number | string | undefined;
};
export interface Props {
children?: ReactNode;
type?: 'slide' | 'scale' | 'rotate';
direction?: 'up' | 'down' | 'left' | 'right';
offset?: number;
scale?: ScaleProps;
}
export default function AnimateButton({
children,
type = 'scale',
direction = 'right',
offset = 10,
scale = { hover: 1.05, tap: 0.954 }
}: Props) {
let offset1;
let offset2;
switch (direction) {
case 'up':
case 'left':
offset1 = offset;
offset2 = 0;
break;
case 'right':
case 'down':
default:
offset1 = 0;
offset2 = offset;
break;
}
const [x, cycleX] = useCycle(offset1, offset2);
const [y, cycleY] = useCycle(offset1, offset2);
switch (type) {
case 'rotate':
return (
<motion.div
animate={{ rotate: 360 }}
transition={{
repeat: Infinity,
repeatType: 'loop',
duration: 2,
repeatDelay: 0
}}
>
{children}
</motion.div>
);
case 'slide':
if (direction === 'up' || direction === 'down') {
return (
<motion.div animate={{ y: y !== undefined ? y : '' }} onHoverEnd={() => cycleY()} onHoverStart={() => cycleY()}>
{children}
</motion.div>
);
}
return (
<motion.div animate={{ x: x !== undefined ? x : '' }} onHoverEnd={() => cycleX()} onHoverStart={() => cycleX()}>
{children}
</motion.div>
);
case 'scale':
default:
if (typeof scale === 'number') {
scale = {
hover: scale,
tap: scale
};
}
return (
<motion.div whileHover={{ scale: scale?.hover }} whileTap={{ scale: scale?.tap }}>
{children}
</motion.div>
);
}
}
import { ReactNode } from 'react';
// material-ui
import { styled, useTheme, Theme } from '@mui/material/styles';
import MuiAvatar from '@mui/material/Avatar';
import { AvatarProps } from '@mui/material';
// project import
import getColors from 'utils/getColors';
// types
import { AvatarTypeProps, ColorProps, ExtendedStyleProps, SizeProps } from 'types/extended';
// ==============================|| AVATAR - COLOR STYLE ||============================== //
interface AvatarStyleProps extends ExtendedStyleProps {
variant?: AvatarProps['variant'];
type?: AvatarTypeProps;
}
function getColorStyle({ variant, theme, color, type }: AvatarStyleProps) {
const colors = getColors(theme, color);
const { lighter, light, main, contrastText } = colors;
switch (type) {
case 'filled':
return {
color: contrastText,
backgroundColor: main
};
case 'outlined':
return {
color: main,
border: '1px solid',
borderColor: main,
backgroundColor: 'transparent'
};
case 'combined':
return {
color: main,
border: '1px solid',
borderColor: light,
backgroundColor: lighter
};
default:
return {
color: main,
backgroundColor: lighter
};
}
}
// ==============================|| AVATAR - SIZE STYLE ||============================== //
function getSizeStyle(size?: SizeProps) {
switch (size) {
case 'badge':
return {
border: '2px solid',
fontSize: '0.675rem',
width: 20,
height: 20
};
case 'xs':
return {
fontSize: '0.75rem',
width: 24,
height: 24
};
case 'sm':
return {
fontSize: '0.875rem',
width: 32,
height: 32
};
case 'lg':
return {
fontSize: '1.2rem',
width: 52,
height: 52
};
case 'xl':
return {
fontSize: '1.5rem',
width: 64,
height: 64
};
case 'md':
default:
return {
fontSize: '1rem',
width: 40,
height: 40
};
}
}
// ==============================|| STYLED - AVATAR ||============================== //
interface StyleProps {
color: ColorProps;
variant?: AvatarProps['variant'];
type?: AvatarTypeProps;
theme: Theme;
size?: SizeProps;
}
const AvatarStyle = styled(MuiAvatar, { shouldForwardProp: (prop) => prop !== 'color' && prop !== 'type' && prop !== 'size' })(
({ theme, variant, color, type, size }: StyleProps) => ({
...getSizeStyle(size),
...getColorStyle({ variant, theme, color, type }),
...(size === 'badge' && {
borderColor: theme.palette.background.default
})
})
);
// ==============================|| EXTENDED - AVATAR ||============================== //
export interface Props extends AvatarProps {
color?: ColorProps;
children?: ReactNode | string;
type?: AvatarTypeProps;
size?: SizeProps;
}
export default function Avatar({ variant = 'circular', children, color = 'primary', type, size = 'md', ...others }: Props) {
const theme = useTheme();
return (
<AvatarStyle variant={variant} theme={theme} color={color} type={type} size={size} {...others}>
{children}
</AvatarStyle>
);
}
import { CSSProperties, ReactElement, useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Divider, Grid, Typography } from '@mui/material';
import MuiBreadcrumbs from '@mui/material/Breadcrumbs';
// project import
import MainCard from 'components/MainCard';
// assets
import { ApartmentOutlined, HomeOutlined, HomeFilled } from '@ant-design/icons';
// types
import { OverrideIcon } from 'types/root';
import { NavItemType } from 'types/menu';
// ==============================|| BREADCRUMBS ||============================== //
export interface BreadCrumbSxProps extends CSSProperties {
mb?: string;
bgcolor?: string;
}
interface Props {
card?: boolean;
divider?: boolean;
icon?: boolean;
icons?: boolean;
maxItems?: number;
navigation?: { items: NavItemType[] };
rightAlign?: boolean;
separator?: OverrideIcon;
title?: boolean;
titleBottom?: boolean;
sx?: BreadCrumbSxProps;
}
const Breadcrumbs = ({
card,
divider = true,
icon,
icons,
maxItems,
navigation,
rightAlign,
separator,
title,
titleBottom,
sx,
...others
}: Props) => {
const theme = useTheme();
const location = useLocation();
const [main, setMain] = useState<NavItemType | undefined>();
const [item, setItem] = useState<NavItemType>();
const iconSX = {
marginRight: theme.spacing(0.75),
marginTop: `-${theme.spacing(0.25)}`,
width: '1rem',
height: '1rem',
color: theme.palette.secondary.main
};
useEffect(() => {
navigation?.items?.map((menu: NavItemType, index: number) => {
if (menu.type && menu.type === 'group') {
getCollapse(menu as { children: NavItemType[]; type?: string });
}
return false;
});
});
let customLocation = location.pathname;
// only used for component demo breadcrumbs
if (customLocation.includes('/components-overview/breadcrumbs')) {
customLocation = '/apps/kanban/board';
}
if (customLocation.includes('/apps/kanban/backlogs')) {
customLocation = '/apps/kanban/board';
}
useEffect(() => {
if (customLocation.includes('/apps/profiles/user/payment')) {
setItem(undefined);
}
}, [item, customLocation]);
// set active item state
const getCollapse = (menu: NavItemType) => {
if (menu.children) {
menu.children.filter((collapse: NavItemType) => {
if (collapse.type && collapse.type === 'collapse') {
getCollapse(collapse as { children: NavItemType[]; type?: string });
if (collapse.url === customLocation) {
setMain(collapse);
setItem(collapse);
}
} else if (collapse.type && collapse.type === 'item') {
if (customLocation === collapse.url) {
setMain(menu);
setItem(collapse);
}
}
return false;
});
}
};
// item separator
const SeparatorIcon = separator!;
const separatorIcon = separator ? <SeparatorIcon style={{ fontSize: '0.75rem', marginTop: 2 }} /> : '/';
let mainContent;
let itemContent;
let breadcrumbContent: ReactElement = <Typography />;
let itemTitle: NavItemType['title'] = '';
let CollapseIcon;
let ItemIcon;
// collapse item
if (main && main.type === 'collapse' && main.breadcrumbs === true) {
CollapseIcon = main.icon ? main.icon : ApartmentOutlined;
mainContent = (
<Typography component={Link} to={document.location.pathname} variant="h6" sx={{ textDecoration: 'none' }} color="textSecondary">
{icons && <CollapseIcon style={iconSX} />}
{main.title}
</Typography>
);
breadcrumbContent = (
<MainCard
border={card}
sx={card === false ? { mb: 3, bgcolor: 'transparent', ...sx } : { mb: 3, ...sx }}
{...others}
content={card}
shadow="none"
>
<Grid
container
direction={rightAlign ? 'row' : 'column'}
justifyContent={rightAlign ? 'space-between' : 'flex-start'}
alignItems={rightAlign ? 'center' : 'flex-start'}
spacing={1}
>
<Grid item>
<MuiBreadcrumbs aria-label="breadcrumb" maxItems={maxItems || 8} separator={separatorIcon}>
<Typography component={Link} to="/" color="textSecondary" variant="h6" sx={{ textDecoration: 'none' }}>
{icons && <HomeOutlined style={iconSX} />}
{icon && !icons && <HomeFilled style={{ ...iconSX, marginRight: 0 }} />}
{(!icon || icons) && 'Home'}
</Typography>
{mainContent}
</MuiBreadcrumbs>
</Grid>
{title && titleBottom && (
<Grid item sx={{ mt: card === false ? 0.25 : 1 }}>
<Typography variant="h2">{main.title}</Typography>
</Grid>
)}
</Grid>
{card === false && divider !== false && <Divider sx={{ mt: 2 }} />}
</MainCard>
);
}
// items
if (item && item.type === 'item') {
itemTitle = item.title;
ItemIcon = item.icon ? item.icon : ApartmentOutlined;
itemContent = (
<Typography variant="subtitle1" color="textPrimary">
{icons && <ItemIcon style={iconSX} />}
{itemTitle}
</Typography>
);
// main
if (item.breadcrumbs !== false) {
breadcrumbContent = (
<MainCard
border={card}
sx={card === false ? { mb: 3, bgcolor: 'transparent', ...sx } : { mb: 3, ...sx }}
{...others}
content={card}
shadow="none"
>
<Grid
container
direction={rightAlign ? 'row' : 'column'}
justifyContent={rightAlign ? 'space-between' : 'flex-start'}
alignItems={rightAlign ? 'center' : 'flex-start'}
spacing={1}
>
{title && !titleBottom && (
<Grid item>
<Typography variant="h2">{item.title}</Typography>
</Grid>
)}
<Grid item>
<MuiBreadcrumbs aria-label="breadcrumb" maxItems={maxItems || 8} separator={separatorIcon}>
<Typography component={Link} to="/" color="textSecondary" variant="h6" sx={{ textDecoration: 'none' }}>
{icons && <HomeOutlined style={iconSX} />}
{icon && !icons && <HomeFilled style={{ ...iconSX, marginRight: 0 }} />}
{(!icon || icons) && 'Home'}
</Typography>
{mainContent}
{itemContent}
</MuiBreadcrumbs>
</Grid>
{title && titleBottom && (
<Grid item sx={{ mt: card === false ? 0.25 : 1 }}>
<Typography variant="h2">{item.title}</Typography>
</Grid>
)}
</Grid>
{card === false && divider !== false && <Divider sx={{ mt: 2 }} />}
</MainCard>
);
}
}
return breadcrumbContent;
};
export default Breadcrumbs;
// material-ui
import { CSSObject, useTheme } from '@mui/material/styles';
import { Box } from '@mui/material';
// project import
import { ColorProps } from 'types/extended';
import getColors from 'utils/getColors';
interface Props {
color?: ColorProps;
size?: number;
variant?: string;
sx?: CSSObject;
}
const Dot = ({ color, size, variant, sx }: Props) => {
const theme = useTheme();
const colors = getColors(theme, color || 'primary');
const { main } = colors;
return (
<Box
component="span"
sx={{
width: size || 8,
height: size || 8,
borderRadius: '50%',
bgcolor: variant === 'outlined' ? '' : main,
...(variant === 'outlined' && {
border: `1px solid ${main}`
}),
...sx
}}
/>
);
};
export default Dot;
import { forwardRef, ReactNode, ReactChild, ReactFragment, ReactPortal, Ref } from 'react';
// material-ui
import MuiIconButton from '@mui/material/IconButton';
import { alpha, styled, useTheme } from '@mui/material/styles';
import { IconButtonProps } from '@mui/material';
// project imports
import getColors from 'utils/getColors';
import getShadow from 'utils/getShadow';
// types
import { ButtonVariantProps, ExtendedStyleProps, IconButtonShapeProps } from 'types/extended';
// ==============================|| ICON BUTTON - COLOR STYLE ||============================== //
interface IconButtonStyleProps extends ExtendedStyleProps {
variant?: ButtonVariantProps;
}
function getColorStyle({ variant, theme, color }: IconButtonStyleProps) {
const colors = getColors(theme, color);
const { lighter, light, dark, main, contrastText } = colors;
const buttonShadow = `${color}Button`;
const shadows = getShadow(theme, buttonShadow);
const commonShadow = {
'&::after': {
boxShadow: `0 0 6px 6px ${alpha(main, 0.9)}`
},
'&:active::after': {
boxShadow: `0 0 0 0 ${alpha(main, 0.9)}`
},
'&:focus-visible': {
outline: `2px solid ${dark}`,
outlineOffset: 2
}
};
switch (variant) {
case 'contained':
return {
color: contrastText,
backgroundColor: main,
'&:hover': {
backgroundColor: dark
},
...commonShadow
};
case 'light':
return {
color: main,
backgroundColor: lighter,
'&:hover': {
backgroundColor: light
},
...commonShadow
};
case 'shadow':
return {
boxShadow: shadows,
color: contrastText,
backgroundColor: main,
'&:hover': {
boxShadow: 'none',
backgroundColor: dark
},
...commonShadow
};
case 'outlined':
return {
'&:hover': {
backgroundColor: 'transparent',
color: dark,
borderColor: dark
},
...commonShadow
};
case 'dashed':
return {
backgroundColor: lighter,
'&:hover': {
color: dark,
borderColor: dark
},
...commonShadow
};
case 'text':
default:
return {
'&:hover': {
color: dark,
backgroundColor: lighter
},
...commonShadow
};
}
}
// ==============================|| STYLED - ICON BUTTON ||============================== //
interface StyleProps extends IconButtonStyleProps {
shape?: IconButtonShapeProps;
}
const IconButtonStyle = styled(MuiIconButton, { shouldForwardProp: (prop) => prop !== 'variant' && prop !== 'shape' })(
({ theme, variant, shape, color }: StyleProps) => ({
position: 'relative',
'::after': {
content: '""',
display: 'block',
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
borderRadius: shape === 'rounded' ? '50%' : 4,
opacity: 0,
transition: 'all 0.5s'
},
':active::after': {
position: 'absolute',
borderRadius: shape === 'rounded' ? '50%' : 4,
left: 0,
top: 0,
opacity: 1,
transition: '0s'
},
...(shape === 'rounded' && {
borderRadius: '50%'
}),
...(variant === 'outlined' && {
border: '1px solid',
borderColor: 'inherit'
}),
...(variant === 'dashed' && {
border: '1px dashed',
borderColor: 'inherit'
}),
...(variant !== 'text' && {
'&.Mui-disabled': {
backgroundColor: theme.palette.grey[200]
}
}),
...getColorStyle({ variant, theme, color })
})
);
// ==============================|| EXTENDED - ICON BUTTON ||============================== //
export interface Props extends IconButtonProps {
shape?: IconButtonShapeProps;
variant?: ButtonVariantProps;
children: ReactNode;
tooltip?: boolean | ReactChild | ReactFragment | ReactPortal;
}
const IconButton = forwardRef(
({ variant = 'text', shape = 'square', children, color = 'primary', tooltip, ...others }: Props, ref: Ref<HTMLButtonElement>) => {
const theme = useTheme();
return (
<IconButtonStyle ref={ref} disableRipple variant={variant} shape={shape} theme={theme} color={color} {...others}>
{children}
</IconButtonStyle>
);
}
);
IconButton.displayName = 'IconButton';
export default IconButton;
import { forwardRef, ReactNode, Ref } from 'react';
// material-ui
import MuiLoadingButton from '@mui/lab/LoadingButton';
import { alpha, styled, useTheme } from '@mui/material/styles';
import { ButtonProps } from '@mui/material';
import { LoadingButtonProps } from '@mui/lab';
// project imports
import getColors from 'utils/getColors';
import getShadow from 'utils/getShadow';
// types
import { ButtonVariantProps, ExtendedStyleProps, IconButtonShapeProps } from 'types/extended';
// ==============================|| LOADING BUTTON - COLOR STYLE ||============================== //
interface LoadingButtonStyleProps extends ExtendedStyleProps {
variant: ButtonVariantProps;
loadingPosition?: LoadingButtonProps['loadingPosition'];
}
function getColorStyle({ variant, theme, color, loadingPosition }: LoadingButtonStyleProps) {
const colors = getColors(theme, color);
const { lighter, main, dark, contrastText } = colors;
const buttonShadow = `${color}Button`;
const shadows = getShadow(theme, buttonShadow);
const loadingIndicator = {
'& .MuiLoadingButton-loadingIndicator': {
color: main
}
};
const loadingColor = {
...(loadingPosition &&
loadingPosition !== 'center' && {
color: main
})
};
const commonShadow = {
'&::after': {
boxShadow: `0 0 6px 6px ${alpha(main, 0.9)}`
},
'&:active::after': {
boxShadow: `0 0 0 0 ${alpha(main, 0.9)}`
},
'&:focus-visible': {
outline: `2px solid ${dark}`,
outlineOffset: 2
}
};
switch (variant) {
case 'contained':
return {
backgroundColor: main,
...(loadingPosition &&
loadingPosition !== 'center' && {
color: contrastText
}),
'& .MuiLoadingButton-loadingIndicator': {
color: contrastText
},
'&:hover': {
backgroundColor: dark,
color: contrastText
},
...commonShadow
};
case 'light':
return {
backgroundColor: main,
...(loadingPosition &&
loadingPosition !== 'center' && {
color: contrastText
}),
'& .MuiLoadingButton-loadingIndicator': {
color: contrastText
},
'&:hover': {
backgroundColor: dark,
color: contrastText
},
...commonShadow
};
case 'shadow':
return {
boxShadow: shadows,
backgroundColor: main,
...(loadingPosition &&
loadingPosition !== 'center' && {
color: contrastText
}),
'& .MuiLoadingButton-loadingIndicator': {
color: contrastText
},
'&:hover': {
boxShadow: 'none',
backgroundColor: dark,
color: contrastText
},
...commonShadow
};
case 'outlined':
return {
backgroundColor: 'transparent',
borderColor: main,
...loadingColor,
...loadingIndicator
};
case 'dashed':
return {
backgroundColor: lighter,
borderColor: main,
...loadingColor,
...loadingIndicator,
...commonShadow
};
case 'text':
default:
return {
color: main,
...loadingIndicator,
...commonShadow
};
}
}
// ==============================|| STYLED - LOADING BUTTON ||============================== //
interface StyleProps extends LoadingButtonStyleProps {
shape?: IconButtonShapeProps;
loading: LoadingButtonProps['loading'];
}
const LoadingButtonStyle = styled(MuiLoadingButton, { shouldForwardProp: (prop) => prop !== 'shape' && prop !== 'variant' })(
({ theme, variant, shape, color, loading, loadingPosition }: StyleProps) => ({
'::after': {
content: '""',
display: 'block',
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
borderRadius: shape === 'rounded' ? '50%' : 4,
opacity: 0,
transition: 'all 0.5s'
},
':active::after': {
position: 'absolute',
borderRadius: shape === 'rounded' ? '50%' : 4,
left: 0,
top: 0,
opacity: 1,
transition: '0s'
},
...(variant === 'text' && {
...getColorStyle({ variant, theme, color, loadingPosition }),
'&.MuiButton-sizeMedium': {
height: 36
},
'&.MuiButton-sizeSmall': {
height: 30
},
'&.MuiButton-sizeLarge': {
height: 44
}
}),
...(shape && {
minWidth: 0,
'&.MuiButton-sizeMedium': {
width: 36,
height: 36
},
'&.MuiButton-sizeSmall': {
width: 30,
height: 30
},
'&.MuiButton-sizeLarge': {
width: 44,
height: 44
},
...(shape === 'rounded' && {
borderRadius: '50%'
})
}),
...(variant === 'outlined' && {
border: '1px solid'
}),
...(variant === 'dashed' && {
border: '1px dashed'
}),
...((variant === 'contained' || variant === 'shadow') &&
!loading && {
color: '#fff'
}),
...(variant !== 'text' && {
...getColorStyle({ variant, theme, color, loadingPosition })
}),
'&.Mui-disabled': {
...(variant !== 'text' && {
...getColorStyle({ variant, theme, color, loadingPosition })
})
}
})
);
// ==============================|| EXTENDED - LOADING BUTTON ||============================== //
interface Props extends LoadingButtonProps {
color?: ButtonProps['color'];
variant?: ButtonVariantProps;
shape?: IconButtonShapeProps;
children: ReactNode;
}
const LoadingButton = forwardRef(
({ variant = 'text', shape, children, color = 'primary', ...others }: Props, ref: Ref<HTMLButtonElement>) => {
const theme = useTheme();
return (
<LoadingButtonStyle
ref={ref}
variant={variant!}
shape={shape}
theme={theme}
loadingPosition={others.loadingPosition}
loading={others.loading}
color={color}
{...others}
>
{children}
</LoadingButtonStyle>
);
}
);
LoadingButton.displayName = 'LoadingButton';
export default LoadingButton;
import { SyntheticEvent } from 'react';
// material-ui
import { Alert, Button, Fade, Grow, Slide, SlideProps } from '@mui/material';
import MuiSnackbar from '@mui/material/Snackbar';
// project import
import IconButton from './IconButton';
import { dispatch, useSelector } from 'store';
import { closeSnackbar } from 'store/reducers/snackbar';
// assets
import { CloseOutlined } from '@ant-design/icons';
// types
import { KeyedObject } from 'types/root';
// animation function
function TransitionSlideLeft(props: SlideProps) {
return <Slide {...props} direction="left" />;
}
function TransitionSlideUp(props: SlideProps) {
return <Slide {...props} direction="up" />;
}
function TransitionSlideRight(props: SlideProps) {
return <Slide {...props} direction="right" />;
}
function TransitionSlideDown(props: SlideProps) {
return <Slide {...props} direction="down" />;
}
function GrowTransition(props: SlideProps) {
return <Grow {...props} />;
}
// animation options
const animation: KeyedObject = {
SlideLeft: TransitionSlideLeft,
SlideUp: TransitionSlideUp,
SlideRight: TransitionSlideRight,
SlideDown: TransitionSlideDown,
Grow: GrowTransition,
Fade
};
// ==============================|| SNACKBAR ||============================== //
const Snackbar = () => {
const snackbar = useSelector((state) => state.snackbar);
const { actionButton, anchorOrigin, alert, close, message, open, transition, variant } = snackbar;
const handleClose = (event: SyntheticEvent | Event, reason?: string) => {
if (reason === 'clickaway') {
return;
}
dispatch(closeSnackbar());
};
return (
<>
{/* default snackbar */}
{variant === 'default' && (
<MuiSnackbar
anchorOrigin={anchorOrigin}
open={open}
autoHideDuration={6000}
onClose={handleClose}
message={message}
TransitionComponent={animation[transition]}
action={
<>
<Button color="secondary" size="small" onClick={handleClose}>
UNDO
</Button>
<IconButton size="small" aria-label="close" color="inherit" onClick={handleClose} sx={{ mt: 0.25 }}>
<CloseOutlined />
</IconButton>
</>
}
/>
)}
{/* alert snackbar */}
{variant === 'alert' && (
<MuiSnackbar
TransitionComponent={animation[transition]}
anchorOrigin={anchorOrigin}
open={open}
autoHideDuration={6000}
onClose={handleClose}
>
<Alert
variant={alert.variant}
color={alert.color}
action={
<>
{actionButton !== false && (
<Button color={alert.color} size="small" onClick={handleClose}>
UNDO
</Button>
)}
{close !== false && (
<IconButton
sx={{ mt: 0.25 }}
size="small"
aria-label="close"
variant="contained"
color={alert.color}
onClick={handleClose}
>
<CloseOutlined />
</IconButton>
)}
</>
}
sx={{
...(alert.variant === 'outlined' && {
bgcolor: 'grey.0'
})
}}
>
{message}
</Alert>
</MuiSnackbar>
)}
</>
);
};
export default Snackbar;
// material-ui
import { styled, useTheme, Theme } from '@mui/material/styles';
import { Box, tooltipClasses, Tooltip as MuiTooltip, TooltipProps } from '@mui/material';
// project import
import getColors from 'utils/getColors';
// type
import { ColorProps } from 'types/extended';
// ==============================|| TOOLTIP - VARIANT ||============================== //
interface TooltipStyleProps {
color?: ColorProps | string;
labelColor?: ColorProps | string;
theme: Theme;
}
function getVariantStyle({ color, theme, labelColor }: TooltipStyleProps) {
const colors = getColors(theme, color as ColorProps);
const { main, contrastText } = colors;
let colorValue = color ? color : '';
if (['primary', 'secondary', 'info', 'success', 'warning', 'error'].includes(colorValue)) {
return {
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: main,
color: labelColor ? labelColor : contrastText
},
[`& .${tooltipClasses.arrow}`]: {
color: main
}
};
} else {
return {
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: colorValue,
color: labelColor ? labelColor : contrastText,
boxShadow: theme.shadows[1]
},
[`& .${tooltipClasses.arrow}`]: {
color: colorValue
}
};
}
}
// ==============================|| STYLED - TOOLTIP COLOR ||============================== //
interface StyleProps {
theme: Theme;
arrow: TooltipProps['arrow'];
labelColor?: ColorProps | string;
color?: ColorProps | string;
}
const TooltipStyle = styled(({ className, ...props }: TooltipProps) => <MuiTooltip {...props} classes={{ popper: className }} />, {
shouldForwardProp: (prop) => prop !== 'color' && prop !== 'labelColor'
})(({ theme, color, labelColor }: StyleProps) => ({
...(color && getVariantStyle({ color, theme, labelColor }))
}));
// ==============================|| EXTENDED - TOOLTIP ||============================== //
interface Props extends TooltipProps {
color?: ColorProps | string;
labelColor?: ColorProps | string;
children: TooltipProps['children'];
}
export default function CustomTooltip({ children, arrow, labelColor = '', ...rest }: Props) {
const theme = useTheme();
return (
<Box display="flex">
<TooltipStyle arrow={arrow} {...rest} theme={theme} labelColor={labelColor}>
{children}
</TooltipStyle>
</Box>
);
}
import { forwardRef, CSSProperties, ExoticComponent, ReactElement, Ref } from 'react';
// material-ui
import { Collapse, Fade, Box, Grow, Slide, Zoom, ZoomProps } from '@mui/material';
// ==============================|| TRANSITIONS ||============================== //
interface Props {
children?: ReactElement;
position?: string;
sx?: CSSProperties;
in?: boolean;
type?: string;
direction?: 'up' | 'right' | 'left' | 'down';
[others: string]: any;
}
const Transitions = forwardRef(
({ children, position = 'top-left', sx, type = 'grow', direction = 'up', ...others }: Props, ref: Ref<ExoticComponent>) => {
let positionSX = {
transformOrigin: '0 0 0'
};
switch (position) {
case 'top-right':
positionSX = {
transformOrigin: 'top right'
};
break;
case 'top':
positionSX = {
transformOrigin: 'top'
};
break;
case 'bottom-left':
positionSX = {
transformOrigin: 'bottom left'
};
break;
case 'bottom-right':
positionSX = {
transformOrigin: 'bottom right'
};
break;
case 'bottom':
positionSX = {
transformOrigin: 'bottom'
};
break;
case 'top-left':
default:
positionSX = {
transformOrigin: '0 0 0'
};
break;
}
return (
<Box ref={ref}>
{type === 'grow' && (
<Grow
{...others}
timeout={{
appear: 0,
enter: 150,
exit: 150
}}
>
<Box sx={positionSX}>{children}</Box>
</Grow>
)}
{type === 'collapse' && (
<Collapse {...others} sx={positionSX}>
{children}
</Collapse>
)}
{type === 'fade' && (
<Fade
{...others}
timeout={{
appear: 0,
enter: 300,
exit: 150
}}
>
<Box sx={positionSX}>{children}</Box>
</Fade>
)}
{type === 'slide' && (
<Slide
{...others}
timeout={{
appear: 0,
enter: 150,
exit: 150
}}
direction={direction}
>
<Box sx={positionSX}>{children}</Box>
</Slide>
)}
{type === 'zoom' && (
<Zoom {...others}>
<Box sx={positionSX}>{children}</Box>
</Zoom>
)}
</Box>
);
}
);
export default Transitions;
export const PopupTransition = forwardRef(function Transition(props: ZoomProps, ref: Ref<unknown>) {
return <Zoom ref={ref} timeout={200} {...props} />;
});
// material-ui
import { Box, CircularProgress, CircularProgressProps, Typography } from '@mui/material';
// ==============================|| PROGRESS - CIRCULAR LABEL ||============================== //
export default function CircularWithLabel({ value, ...others }: CircularProgressProps) {
return (
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress variant="determinate" value={value} {...others} />
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography variant="caption" component="div" color="text.secondary">{`${Math.round(value!)}%`}</Typography>
</Box>
</Box>
);
}
// material-ui
import { Box, CircularProgress, CircularProgressProps, Typography, circularProgressClasses } from '@mui/material';
// ==============================|| PROGRESS - CIRCULAR PATH ||============================== //
interface Props extends CircularProgressProps {
showLabel?: boolean;
pathColor?: string;
}
export default function CircularWithPath({ value, size, variant, thickness, showLabel, pathColor, sx, ...others }: Props) {
return (
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress
variant="determinate"
sx={{ color: pathColor ? pathColor : 'grey.200' }}
size={size}
thickness={thickness}
{...others}
value={100}
/>
{showLabel && (
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography variant="caption" component="div" color="text.secondary">
{value ? `${Math.round(value)}%` : '0%'}
</Typography>
</Box>
)}
<CircularProgress
variant={variant}
sx={{
...sx,
position: 'absolute',
left: 0,
[`& .${circularProgressClasses.circle}`]: {
strokeLinecap: 'round'
}
}}
size={size}
thickness={thickness}
value={value}
{...others}
/>
</Box>
);
}
import { ReactNode } from 'react';
// material-ui
import { Box, LinearProgress, LinearProgressProps } from '@mui/material';
// ==============================|| PROGRESS - LINEAR ICON ||============================== //
export default function LinearWithIcon({ icon, value, ...others }: LinearProgressProps & { icon: ReactNode }) {
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ width: '100%', mr: 1 }}>
<LinearProgress variant="determinate" value={value} {...others} />
</Box>
<Box sx={{ minWidth: 35 }}>{icon}</Box>
</Box>
);
}
// material-ui
import { Box, LinearProgress, LinearProgressProps, Typography } from '@mui/material';
// ==============================|| PROGRESS - LINEAR WITH LABEL ||============================== //
export default function LinearWithLabel({ value, ...others }: LinearProgressProps) {
return (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ width: '100%', mr: 1 }}>
<LinearProgress variant="determinate" value={value} {...others} />
</Box>
<Box sx={{ minWidth: 35 }}>
<Typography variant="body2" color="text.secondary">{`${Math.round(value!)}%`}</Typography>
</Box>
</Box>
);
}
import { ElementType, Suspense } from 'react';
// project import
import Loader from './Loader';
// ==============================|| LOADABLE - LAZY LOADING ||============================== //
const Loadable = (Component: ElementType) => (props: any) =>
(
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
export default Loadable;
// material-ui
import { styled } from '@mui/material/styles';
import LinearProgress, { LinearProgressProps } from '@mui/material/LinearProgress';
// loader style
const LoaderWrapper = styled('div')(({ theme }) => ({
position: 'fixed',
top: 0,
left: 0,
zIndex: 2001,
width: '100%',
'& > * + *': {
marginTop: theme.spacing(2)
}
}));
// ==============================|| Loader ||============================== //
export interface LoaderProps extends LinearProgressProps {}
const Loader = () => (
<LoaderWrapper>
<LinearProgress color="primary" />
</LoaderWrapper>
);
export default Loader;
import { ReactNode, useEffect, useState } from 'react';
// third-party
import { IntlProvider, MessageFormatElement } from 'react-intl';
// project import
import useConfig from 'hooks/useConfig';
import { I18n } from 'types/config';
// load locales files
const loadLocaleData = (locale: I18n) => {
switch (locale) {
case 'fr':
return import('utils/locales/fr.json');
case 'ro':
return import('utils/locales/ro.json');
case 'zh':
return import('utils/locales/zh.json');
case 'en':
default:
return import('utils/locales/en.json');
}
};
// ==============================|| LOCALIZATION ||============================== //
interface Props {
children: ReactNode;
}
const Locales = ({ children }: Props) => {
const { i18n } = useConfig();
const [messages, setMessages] = useState<Record<string, string> | Record<string, MessageFormatElement[]> | undefined>();
useEffect(() => {
loadLocaleData(i18n).then((d: { default: Record<string, string> | Record<string, MessageFormatElement[]> | undefined }) => {
setMessages(d.default);
});
}, [i18n]);
return (
<>
{messages && (
<IntlProvider locale={i18n} defaultLocale="en" messages={messages}>
{children}
</IntlProvider>
)}
</>
);
};
export default Locales;
import { forwardRef, CSSProperties, ReactNode, Ref } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Card, CardContent, CardHeader, Divider, Typography, CardProps, CardHeaderProps, CardContentProps } from '@mui/material';
// project import
import Highlighter from './third-party/Highlighter';
// types
import { KeyedObject } from 'types/root';
import { ThemeMode } from 'types/config';
// header style
const headerSX = {
p: 2.5,
'& .MuiCardHeader-action': { m: '0px auto', alignSelf: 'center' }
};
// ==============================|| CUSTOM - MAIN CARD ||============================== //
export interface MainCardProps extends KeyedObject {
border?: boolean;
boxShadow?: boolean;
children: ReactNode | string;
subheader?: ReactNode | string;
style?: CSSProperties;
content?: boolean;
contentSX?: CardContentProps['sx'];
darkTitle?: boolean;
divider?: boolean;
sx?: CardProps['sx'];
secondary?: CardHeaderProps['action'];
shadow?: string;
elevation?: number;
title?: ReactNode | string;
codeHighlight?: boolean;
codeString?: string;
modal?: boolean;
}
const MainCard = forwardRef(
(
{
border = true,
boxShadow,
children,
subheader,
content = true,
contentSX = {},
darkTitle,
divider = true,
elevation,
secondary,
shadow,
sx = {},
title,
codeHighlight = false,
codeString,
modal = false,
...others
}: MainCardProps,
ref: Ref<HTMLDivElement>
) => {
const theme = useTheme();
boxShadow = theme.palette.mode === ThemeMode.DARK ? boxShadow || true : boxShadow;
return (
<Card
elevation={elevation || 0}
ref={ref}
{...others}
sx={{
position: 'relative',
border: border ? '1px solid' : 'none',
borderRadius: 1,
borderColor: theme.palette.mode === ThemeMode.DARK ? theme.palette.divider : theme.palette.grey.A800,
boxShadow: boxShadow && (!border || theme.palette.mode === ThemeMode.DARK) ? shadow || theme.customShadows.z1 : 'inherit',
':hover': {
boxShadow: boxShadow ? shadow || theme.customShadows.z1 : 'inherit'
},
...(codeHighlight && {
'& pre': {
m: 0,
p: '12px !important',
fontFamily: theme.typography.fontFamily,
fontSize: '0.75rem'
}
}),
...(modal && {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: { xs: `calc( 100% - 50px)`, sm: 'auto' },
'& .MuiCardContent-root': {
overflowY: 'auto',
minHeight: 'auto',
maxHeight: `calc(100vh - 200px)`
}
}),
...sx
}}
>
{/* card header and action */}
{!darkTitle && title && (
<CardHeader
sx={headerSX}
titleTypographyProps={{ variant: 'subtitle1' }}
title={title}
action={secondary}
subheader={subheader}
/>
)}
{darkTitle && title && <CardHeader sx={headerSX} title={<Typography variant="h4">{title}</Typography>} action={secondary} />}
{/* content & header divider */}
{title && divider && <Divider />}
{/* card content */}
{content && <CardContent sx={contentSX}>{children}</CardContent>}
{!content && children}
{/* card footer - clipboard & highlighter */}
{codeString && (
<>
<Divider sx={{ borderStyle: 'dashed' }} />
<Highlighter codeString={codeString} codeHighlight={codeHighlight} />
</>
)}
</Card>
);
}
);
export default MainCard;
import { useEffect, ReactNode } from 'react';
// material-ui
import { CacheProvider } from '@emotion/react';
import createCache, { StylisPlugin } from '@emotion/cache';
// third-party
import rtlPlugin from 'stylis-plugin-rtl';
// project import
import useConfig from 'hooks/useConfig';
// types
import { ThemeDirection } from 'types/config';
// ==============================|| RTL LAYOUT ||============================== //
interface Props {
children: ReactNode;
}
const RTLLayout = ({ children }: Props) => {
const { themeDirection } = useConfig();
useEffect(() => {
document.dir = themeDirection;
}, [themeDirection]);
const cacheRtl = createCache({
key: themeDirection === ThemeDirection.RTL ? 'rtl' : 'css',
prepend: true,
stylisPlugins: themeDirection === ThemeDirection.RTL ? [rtlPlugin as StylisPlugin] : []
});
return <CacheProvider value={cacheRtl}>{children}</CacheProvider>;
};
export default RTLLayout;
import { ReactElement, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
// ==============================|| NAVIGATION - SCROLL TO TOP ||============================== //
const ScrollTop = ({ children }: { children: ReactElement | null }) => {
const location = useLocation();
const { pathname } = location;
useEffect(() => {
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
}, [pathname]);
return children || null;
};
export default ScrollTop;
// material-ui
import { styled } from '@mui/material/styles';
const ScrollX = styled('div')({
width: '100%',
overflowX: 'auto',
display: 'block'
});
export default ScrollX;
import { ReactNode } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Avatar, ButtonBase, Link, Tooltip } from '@mui/material';
// ==============================|| CARD - SECONDARY ACTION ||============================== //
interface Props {
title?: string;
link?: string;
icon?: ReactNode | string;
}
const CardSecondaryAction = ({ title, link, icon }: Props) => {
const theme = useTheme();
return (
<Tooltip title={title || 'Reference'} placement="left">
<ButtonBase disableRipple>
{!icon && (
<Avatar
component={Link}
href={link}
target="_blank"
alt="MUI Logo"
sx={{ width: 28, height: 28, bgcolor: 'transparent', border: `2px solid ${theme.palette.primary.light}` }}
>
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0)">
<path d="M100 260.9V131L212.5 195.95V239.25L137.5 195.95V282.55L100 260.9Z" fill={theme.palette.primary.dark} />
<path
d="M212.5 195.95L325 131V260.9L250 304.2L212.5 282.55L287.5 239.25V195.95L212.5 239.25V195.95Z"
fill={theme.palette.primary.main}
/>
<path d="M212.5 282.55V325.85L287.5 369.15V325.85L212.5 282.55Z" fill={theme.palette.primary.dark} />
<path
d="M287.5 369.15L400 304.2V217.6L362.5 239.25V282.55L287.5 325.85V369.15ZM362.5 195.95V152.65L400 131V174.3L362.5 195.95Z"
fill={theme.palette.primary.main}
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="300" height="238.3" fill="white" transform="translate(100 131)" />
</clipPath>
</defs>
</svg>
</Avatar>
)}
{icon && (
<Avatar
component={Link}
href={link}
target="_blank"
sx={{ width: 28, height: 28, bgcolor: 'transparent', border: `2px solid ${theme.palette.primary.light}` }}
>
{icon}
</Avatar>
)}
</ButtonBase>
</Tooltip>
);
};
export default CardSecondaryAction;
// material-ui
import { Theme } from '@mui/material/styles';
import { useMediaQuery, Container, Link, Typography, Stack } from '@mui/material';
// ==============================|| FOOTER - AUTHENTICATION ||============================== //
const AuthFooter = () => {
const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
return (
<Container maxWidth="xl">
<Stack
direction={matchDownSM ? 'column' : 'row'}
justifyContent={matchDownSM ? 'center' : 'space-between'}
spacing={2}
textAlign={matchDownSM ? 'center' : 'inherit'}
>
<Typography variant="subtitle2" color="secondary" component="span">
This site is protected by{' '}
<Typography component={Link} variant="subtitle2" href="#mantis-privacy" target="_blank" underline="hover">
Privacy Policy
</Typography>
</Typography>
<Stack direction={matchDownSM ? 'column' : 'row'} spacing={matchDownSM ? 1 : 3} textAlign={matchDownSM ? 'center' : 'inherit'}>
<Typography
variant="subtitle2"
color="secondary"
component={Link}
href="https://codedthemes.com"
target="_blank"
underline="hover"
>
Terms and Conditions
</Typography>
<Typography
variant="subtitle2"
color="secondary"
component={Link}
href="https://codedthemes.com"
target="_blank"
underline="hover"
>
Privacy Policy
</Typography>
<Typography
variant="subtitle2"
color="secondary"
component={Link}
href="https://codedthemes.com"
target="_blank"
underline="hover"
>
CA Privacy Notice
</Typography>
</Stack>
</Stack>
</Container>
);
};
export default AuthFooter;
// material-ui
import { Box, Grid, Link, Stack, Typography } from '@mui/material';
// assets
import { GlobalOutlined, NodeExpandOutlined } from '@ant-design/icons';
// ==============================|| COMPONENTS - BREADCRUMBS ||============================== //
interface Props {
title: string;
caption?: string;
directory?: string;
link?: string;
}
const ComponentHeader = ({ title, caption, directory, link }: Props) => (
<Box sx={{ pl: 3 }}>
<Stack spacing={1.25}>
<Typography variant="h2">{title}</Typography>
{caption && (
<Typography variant="h6" color="textSecondary">
{caption}
</Typography>
)}
</Stack>
<Grid container spacing={0.75} sx={{ mt: 1.75 }}>
{directory && (
<Grid item xs={12}>
<Typography variant="caption" color="textSecondary">
<NodeExpandOutlined style={{ marginRight: 10 }} />
{directory}
</Typography>
</Grid>
)}
{link && (
<Grid item xs={12}>
<Link variant="caption" color="primary" href={link} target="_blank">
<GlobalOutlined style={{ marginRight: 10 }} />
{link}
</Link>
</Grid>
)}
</Grid>
</Box>
);
export default ComponentHeader;
import { sum } from 'lodash';
import { Link } from 'react-router-dom';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Fab, Badge } from '@mui/material';
// types
import { CartProductStateProps } from 'types/cart';
// project import
import { useSelector } from 'store';
// assets
import { ShoppingCartOutlined } from '@ant-design/icons';
// ==============================|| CART ITEMS - FLOATING BUTTON ||============================== //
const FloatingCart = () => {
const theme = useTheme();
const cart = useSelector((state) => state.cart);
const totalQuantity = sum(cart.checkout.products.map((item: CartProductStateProps) => item.quantity));
return (
<Fab
component={Link}
to="/apps/e-commerce/checkout"
size="large"
sx={{
top: '75%',
position: 'fixed',
right: 0,
zIndex: theme.zIndex.speedDial,
boxShadow: theme.customShadows.primary,
bgcolor: 'primary.lighter',
color: 'primary.main',
borderRadius: '25%',
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
'&:hover': {
bgcolor: 'primary.100',
boxShadow: theme.customShadows.primary
},
'&:focus-visible': {
outline: `2px solid ${theme.palette.primary.dark}`,
outlineOffset: 2
}
}}
>
<Badge showZero badgeContent={totalQuantity} color="error">
<ShoppingCartOutlined style={{ color: theme.palette.primary.main, fontSize: '1.5rem' }} />
</Badge>
</Fab>
);
};
export default FloatingCart;
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box, Button, CardContent, CardMedia, Chip, Divider, Grid, Rating, Stack, Typography } from '@mui/material';
// types
import { ProductCardProps } from 'types/cart';
// project import
import MainCard from 'components/MainCard';
import IconButton from 'components/@extended/IconButton';
import SkeletonProductPlaceholder from 'components/cards/skeleton/ProductPlaceholder';
import { useDispatch, useSelector } from 'store';
import { addProduct } from 'store/reducers/cart';
import { openSnackbar } from 'store/reducers/snackbar';
// assets
import { HeartOutlined, HeartFilled } from '@ant-design/icons';
const prodImage = require.context('assets/images/e-commerce', true);
// ==============================|| PRODUCT CARD ||============================== //
const ProductCard = ({
id,
color,
name,
brand,
offer,
isStock,
image,
description,
offerPrice,
salePrice,
rating,
open
}: ProductCardProps) => {
const theme = useTheme();
const dispatch = useDispatch();
const prodProfile = image && prodImage(`./${image}`);
const [productRating] = useState<number | undefined>(rating);
const [wishlisted, setWishlisted] = useState<boolean>(false);
const cart = useSelector((state) => state.cart);
const addCart = () => {
dispatch(addProduct({ id, name, image, salePrice, offerPrice, color, size: 8, quantity: 1, description }, cart.checkout.products));
dispatch(
openSnackbar({
open: true,
message: 'Add To Cart Success',
variant: 'alert',
alert: {
color: 'success'
},
close: false
})
);
};
const addToFavourite = () => {
setWishlisted(!wishlisted);
dispatch(
openSnackbar({
open: true,
message: 'Added to favourites',
variant: 'alert',
alert: {
color: 'success'
},
close: false
})
);
};
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
return (
<>
{isLoading ? (
<SkeletonProductPlaceholder />
) : (
<MainCard
content={false}
boxShadow
sx={{
'&:hover': {
transform: 'scale3d(1.02, 1.02, 1)',
transition: 'all .4s ease-in-out'
}
}}
>
<Box sx={{ width: 250, m: 'auto' }}>
<CardMedia
sx={{ height: 250, textDecoration: 'none', opacity: isStock ? 1 : 0.25 }}
image={prodProfile}
component={Link}
to={`/apps/e-commerce/product-details/${id}`}
/>
</Box>
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
sx={{ width: '100%', position: 'absolute', top: 0, pt: 1.75, pl: 2, pr: 1 }}
>
{!isStock && <Chip variant="light" color="error" size="small" label="Sold out" />}
{offer && <Chip label={offer} variant="combined" color="success" size="small" />}
<IconButton color="secondary" sx={{ ml: 'auto', '&:hover': { background: 'transparent' } }} onClick={addToFavourite}>
{wishlisted ? (
<HeartFilled style={{ fontSize: '1.15rem', color: theme.palette.error.main }} />
) : (
<HeartOutlined style={{ fontSize: '1.15rem' }} />
)}
</IconButton>
</Stack>
<Divider />
<CardContent sx={{ p: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack>
<Typography
component={Link}
to={`/apps/e-commerce/product-details/${id}`}
color="textPrimary"
variant="h5"
sx={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block', textDecoration: 'none' }}
>
{name}
</Typography>
<Typography variant="h6" color="textSecondary">
{brand}
</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="flex-end" flexWrap="wrap" rowGap={1.75}>
<Stack>
<Stack direction="row" spacing={1} alignItems="center">
<Typography variant="h5">${offerPrice}</Typography>
{salePrice && (
<Typography variant="h6" color="textSecondary" sx={{ textDecoration: 'line-through' }}>
${salePrice}
</Typography>
)}
</Stack>
<Stack direction="row" alignItems="flex-start">
<Rating precision={0.5} name="size-small" value={productRating} size="small" readOnly />
<Typography variant="caption">({productRating?.toFixed(1)})</Typography>
</Stack>
</Stack>
<Button variant="contained" onClick={addCart} disabled={!isStock}>
{!isStock ? 'Sold Out' : 'Add to Cart'}
</Button>
</Stack>
</Grid>
</Grid>
</CardContent>
</MainCard>
)}
</>
);
};
export default ProductCard;
// material-ui
import { Grid, Rating, Stack, Typography } from '@mui/material';
// project imports
import Avatar from 'components/@extended/Avatar';
// assets
import { StarFilled, StarOutlined } from '@ant-design/icons';
import { ReactNode } from 'react';
const avatarImage = require.context('assets/images/users', true);
// ==============================|| PRODUCT DETAILS - REVIEW ||============================== //
interface ReviewProps {
avatar: string;
date: Date | string;
name: string;
rating: number;
review: string;
}
const ProductReview = ({ avatar, date, name, rating, review }: ReviewProps) => (
<Grid item xs={12}>
<Stack direction="row" spacing={1}>
<Avatar alt={name} src={avatar && avatarImage(`./${avatar}`)} />
<Stack spacing={2}>
<Stack>
<Typography variant="subtitle1" sx={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}>
{name}
</Typography>
<Typography variant="caption" color="textSecondary">
{date as ReactNode}
</Typography>
<Rating
size="small"
name="simple-controlled"
value={rating < 4 ? rating + 1 : rating}
icon={<StarFilled style={{ fontSize: 'inherit' }} />}
emptyIcon={<StarOutlined style={{ fontSize: 'inherit' }} />}
precision={0.1}
readOnly
/>
</Stack>
<Typography variant="body2">{review}</Typography>
</Stack>
</Stack>
</Grid>
);
export default ProductReview;
// material-ui
import { Box, Grid, Stack, Typography } from '@mui/material';
// assets
import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
// ==============================|| INVOICE - CARD ||============================== //
interface Props {
title: string;
count: string;
percentage?: number;
isLoss?: boolean;
color?: any;
children: any;
invoice: string;
}
const TableWidgetCard = ({ color, title, count, percentage, isLoss, children, invoice }: Props) => {
return (
<Grid container direction="row" spacing={2}>
<Grid item md={5}>
<Stack direction="column" spacing={2}>
<Typography variant="subtitle1">{title}</Typography>
<Stack direction="column" spacing={1}>
<Typography variant="h4" color="inherit">
{count}
</Typography>
<Stack direction="row" spacing={1}>
<Typography variant="subtitle1">{invoice}</Typography>
<Typography color="secondary">invoices</Typography>
</Stack>
</Stack>
</Stack>
</Grid>
<Grid item md={7}>
<Box>
<Stack direction="column" alignItems="flex-end" justifyContent="space-evenly">
{percentage && (
<Stack sx={{ ml: 1.25, pl: 1 }} direction="row" alignItems="center" spacing={1}>
{!isLoss && <CaretUpOutlined style={{ fontSize: '0.75rem', color: `${color}` }} />}
{isLoss && <CaretDownOutlined style={{ fontSize: '0.75rem', color: `${color}` }} />}
<Typography color="secondary">{percentage}%</Typography>
</Stack>
)}
{children}
</Stack>
</Box>
</Grid>
</Grid>
);
};
export default TableWidgetCard;
import { useState, useEffect } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart, { Props as ChartProps } from 'react-apexcharts';
// project import
import useConfig from 'hooks/useConfig';
// types
import { ThemeMode } from 'types/config';
// ==============================|| INVOICE - CHART ||============================== //
const InvoiceChart = ({ color, data }: any) => {
const theme = useTheme();
const { mode } = useConfig();
// chart options
const areaChartOptions = {
chart: {
id: 'new-stack-chart',
height: 100,
type: 'area',
toolbar: {
show: false
},
sparkline: {
enabled: true
}
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
type: 'vertical',
inverseColors: false,
opacityFrom: 0.5,
opacityTo: 0
}
},
plotOptions: {
bar: {
borderRadius: 0
}
},
dataLabels: {
enabled: false
},
xaxis: {
axisBorder: {
show: false
},
axisTicks: {
show: false
},
labels: {
show: false
},
tooltip: {
enabled: false
}
},
stroke: {
width: 1,
curve: 'smooth'
},
grid: {
show: false
},
yaxis: {
axisBorder: {
show: false
},
axisTicks: {
show: false
},
labels: {
show: false
}
},
tooltip: {
x: {
show: false
},
y: {
formatter(val: number) {
return `$ ${val}`;
}
}
}
};
const { primary, secondary } = theme.palette.text;
const line = theme.palette.divider;
const [options, setOptions] = useState<ChartProps>(areaChartOptions);
useEffect(() => {
setOptions((prevState) => ({
...prevState,
colors: [color.main],
tooltip: {
theme: mode === ThemeMode.DARK ? 'dark' : 'light'
}
}));
}, [mode, primary, secondary, line, theme, color]);
const [series] = useState([
{
name: 'Sales',
data: data
}
]);
return <ReactApexChart options={options} series={series} type="area" height={80} />;
};
export default InvoiceChart;
// material-ui
import { Box, Grid, Stack, Typography } from '@mui/material';
// project import
import UserCard from './UserCard';
interface Props {
title: string;
}
// ==============================|| EMPTY STATE ||============================== //
const EmptyUserCard = ({ title }: Props) => {
return (
<Grid container spacing={3}>
<Grid item xs={12}>
<Box
sx={{
p: { xs: 2.5, sm: 6 },
height: `calc(100vh - 192px)`,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
bgcolor: 'transparent'
}}
>
<Grid container direction="column" justifyContent="center" alignItems="center">
<Grid item>
<Box sx={{ ml: -9, mb: { xs: -8, sm: -5 } }}>
<Box sx={{ position: 'relative' }}>
<UserCard />
</Box>
<Box sx={{ position: 'relative', top: -120, left: 72 }}>
<UserCard />
</Box>
</Box>
</Grid>
<Grid item>
<Stack spacing={1}>
<Typography align="center" variant="h4">
{title}
</Typography>
</Stack>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
);
};
export default EmptyUserCard;
// material-ui
import { CardContent, Grid, Skeleton, Stack } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// ===========================|| SKELETON - PRODUCT CARD ||=========================== //
const ProductPlaceholder = () => (
<MainCard content={false} boxShadow>
<Skeleton variant="rectangular" height={220} />
<CardContent sx={{ p: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Skeleton variant="rectangular" height={20} />
</Grid>
<Grid item xs={12}>
<Skeleton variant="rectangular" height={45} />
</Grid>
<Grid item xs={12} sx={{ pt: '8px !important' }}>
<Stack direction="row" alignItems="center" spacing={1}>
<Skeleton variant="rectangular" height={20} width={90} />
<Skeleton variant="rectangular" height={20} width={38} />
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Grid container spacing={1}>
<Grid item>
<Skeleton variant="rectangular" height={20} width={40} />
</Grid>
<Grid item>
<Skeleton variant="rectangular" height={17} width={20} />
</Grid>
</Grid>
<Skeleton variant="rectangular" height={32} width={47} />
</Stack>
</Grid>
</Grid>
</CardContent>
</MainCard>
);
export default ProductPlaceholder;
// material-ui
import { CardContent, Grid, Skeleton, Stack, Avatar } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// assets
import { ContactsOutlined } from '@ant-design/icons';
// ===========================|| SKELETON - USER EMPTY CARD ||=========================== //
const UserCard = () => {
return (
<MainCard
border={false}
content={false}
boxShadow
sx={{ boxShadow: `rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px`, borderRadius: 2 }}
>
<CardContent sx={{ p: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack flexDirection="row" alignItems="center">
<Avatar>
<ContactsOutlined style={{ visibility: 'inherit' }} />
</Avatar>
<Stack sx={{ width: '100%', pl: 2.5 }}>
<Skeleton animation={false} height={20} width="80%" />
<Skeleton animation={false} height={20} width="40%" />
</Stack>
</Stack>
</Grid>
<Grid item xs={12}>
<Skeleton animation={false} height={20} width={45} />
<Skeleton animation={false} height={20} />
<Stack direction="row" alignItems="center" spacing={1}>
<Skeleton animation={false} height={20} width={90} />
<Skeleton animation={false} height={20} width={38} />
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Grid container spacing={1}>
<Grid item>
<Skeleton animation={false} height={20} width={40} />
</Grid>
<Grid item>
<Skeleton animation={false} height={17} width={20} />
</Grid>
</Grid>
<Skeleton animation={false} height={32} width={47} />
</Stack>
</Grid>
</Grid>
</CardContent>
</MainCard>
);
};
export default UserCard;
// material-ui
import { Box, Chip, ChipProps, Grid, Stack, Typography } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// assets
import { RiseOutlined, FallOutlined } from '@ant-design/icons';
// ==============================|| STATISTICS - ECOMMERCE CARD ||============================== //
interface Props {
title: string;
count: string;
percentage?: number;
isLoss?: boolean;
color?: ChipProps['color'];
extra: string;
}
const AnalyticEcommerce = ({ color = 'primary', title, count, percentage, isLoss, extra }: Props) => (
<MainCard contentSX={{ p: 2.25 }}>
<Stack spacing={0.5}>
<Typography variant="h6" color="textSecondary">
{title}
</Typography>
<Grid container alignItems="center">
<Grid item>
<Typography variant="h4" color="inherit">
{count}
</Typography>
</Grid>
{percentage && (
<Grid item>
<Chip
variant="combined"
color={color}
icon={
<>
{!isLoss && <RiseOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
{isLoss && <FallOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
</>
}
label={`${percentage}%`}
sx={{ ml: 1.25, pl: 1 }}
size="small"
/>
</Grid>
)}
</Grid>
</Stack>
<Box sx={{ pt: 2.25 }}>
<Typography variant="caption" color="textSecondary">
You made an extra{' '}
<Typography component="span" variant="caption" sx={{ color: `${color || 'primary'}.main` }}>
{extra}
</Typography>{' '}
this year
</Typography>
</Box>
</MainCard>
);
export default AnalyticEcommerce;
// material-ui
import { Box, Chip, ChipProps, Stack, Typography } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// assets
import { RiseOutlined, FallOutlined } from '@ant-design/icons';
// ==============================|| STATISTICS - ECOMMERCE CARD ||============================== //
interface Props {
title: string;
count: string;
percentage?: number;
isLoss?: boolean;
color?: ChipProps['color'];
children: any;
}
const AnalyticsDataCard = ({ color = 'primary', title, count, percentage, isLoss, children }: Props) => (
<MainCard content={false}>
<Box sx={{ p: 2.25 }}>
<Stack spacing={0.5}>
<Typography variant="h6" color="textSecondary">
{title}
</Typography>
<Stack direction="row" alignItems="center">
<Typography variant="h4" color="inherit">
{count}
</Typography>
{percentage && (
<Chip
variant="combined"
color={color}
icon={
<>
{!isLoss && <RiseOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
{isLoss && <FallOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
</>
}
label={`${percentage}%`}
sx={{ ml: 1.25, pl: 1 }}
size="small"
/>
)}
</Stack>
</Stack>
</Box>
{children}
</MainCard>
);
export default AnalyticsDataCard;
// material-ui
import { Box, Grid, Stack, Typography } from '@mui/material';
// project imports
import MainCard from 'components/MainCard';
import { GenericCardProps } from 'types/root';
// ==============================|| REPORT CARD ||============================== //
interface EcommerceMetrixProps extends GenericCardProps {}
const EcommerceMetrix = ({ primary, secondary, content, iconPrimary, color }: EcommerceMetrixProps) => {
const IconPrimary = iconPrimary!;
const primaryIcon = iconPrimary ? <IconPrimary fontSize="large" /> : null;
return (
<MainCard
content={false}
sx={{
bgcolor: color,
position: 'relative',
'&:before, &:after': {
content: '""',
width: 1,
height: 1,
position: 'absolute',
background: 'linear-gradient(90deg, rgba(255, 255, 255, 0.0001) 22.07%, rgba(255, 255, 255, 0.15) 83.21%)',
transform: 'matrix(0.9, 0.44, -0.44, 0.9, 0, 0)'
},
'&:after': {
top: '50%',
right: '-20px'
},
'&:before': {
right: '-70px',
bottom: '80%'
}
}}
>
<Box sx={{ px: 4.5, py: 4 }}>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography style={{ color: '#fff', opacity: 0.23, fontSize: 56, lineHeight: 0 }}>{primaryIcon}</Typography>
</Grid>
<Grid item>
<Stack spacing={1} alignItems="flex-end">
<Typography variant="h4" color="common.white" sx={{ fontWeight: 500 }}>
{primary}
</Typography>
<Typography variant="h2" color="common.white">
{secondary}
</Typography>
</Stack>
</Grid>
</Grid>
<Stack spacing={1} direction="row" justifyContent="flex-end" sx={{ pt: 2.25 }}>
<Typography variant="h5" color="common.white" sx={{ fontWeight: 400 }}>
{content}
</Typography>
</Stack>
</Box>
</MainCard>
);
};
export default EcommerceMetrix;
// material-ui
import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
// types
import { GenericCardProps } from 'types/root';
// ===========================|| HOVER SOCIAL CARD ||=========================== //
interface HoverSocialCardProps extends GenericCardProps {}
const HoverSocialCard = ({ primary, secondary, iconPrimary, color }: HoverSocialCardProps) => {
const IconPrimary = iconPrimary!;
const primaryIcon = iconPrimary ? <IconPrimary /> : null;
return (
<Card
elevation={0}
sx={{
background: color,
position: 'relative',
color: '#fff',
'&:hover svg': {
opacity: 1,
transform: 'scale(1.1)'
}
}}
>
<CardContent>
<Box
sx={{
position: 'absolute',
right: 15,
top: 25,
color: '#fff',
'& svg': {
width: 36,
height: 36,
opacity: 0.5,
transition: 'all .3s ease-in-out'
}
}}
>
{primaryIcon}
</Box>
<Grid container spacing={0}>
<Grid item xs={12}>
<Typography variant="h3" color="inherit">
{secondary}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle2" color="inherit">
{primary}
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
);
};
export default HoverSocialCard;
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project imports
import MainCard from 'components/MainCard';
import { GenericCardProps } from 'types/root';
// ==============================|| REPORT CARD ||============================== //
interface ReportCardProps extends GenericCardProps {}
const ReportCard = ({ primary, secondary, iconPrimary, color }: ReportCardProps) => {
const IconPrimary = iconPrimary!;
const primaryIcon = iconPrimary ? <IconPrimary fontSize="large" /> : null;
return (
<MainCard>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Stack spacing={1}>
<Typography variant="h4">{primary}</Typography>
<Typography variant="body1" color="secondary">
{secondary}
</Typography>
</Stack>
</Grid>
<Grid item>
<Typography variant="h2" style={{ color }}>
{primaryIcon}
</Typography>
</Grid>
</Grid>
</MainCard>
);
};
export default ReportCard;
// material-ui
import { Grid, Stack, Typography } from '@mui/material';
// project imports
import MainCard from 'components/MainCard';
import IconButton from 'components/@extended/IconButton';
import { GenericCardProps } from 'types/root';
// ============================|| ROUND ICON CARD ||============================ //
interface Props {
primary: string;
secondary: string;
content: string;
iconPrimary: GenericCardProps['iconPrimary'];
color: string;
bgcolor: string;
}
const RoundIconCard = ({ primary, secondary, content, iconPrimary, color, bgcolor }: Props) => {
const IconPrimary = iconPrimary!;
const primaryIcon = iconPrimary ? <IconPrimary fontSize="large" /> : null;
return (
<MainCard>
<Grid container alignItems="center" spacing={0} justifyContent="space-between">
<Grid item>
<Stack spacing={1}>
<Typography variant="h5" color="inherit">
{primary}
</Typography>
<Typography variant="h3">{secondary}</Typography>
<Typography variant="subtitle2" color="inherit">
{content}
</Typography>
</Stack>
</Grid>
<Grid item>
<IconButton sx={{ bgcolor, color, '& .MuiSvgIcon-root': { fontSize: '1.5rem' } }} size="large">
{primaryIcon}
</IconButton>
</Grid>
</Grid>
</MainCard>
);
};
export default RoundIconCard;
// material-ui
import { styled } from '@mui/material/styles';
import { Card, CardContent, Grid, Typography } from '@mui/material';
// types
import { GenericCardProps } from 'types/root';
// styles
const IconWrapper = styled('div')({
position: 'absolute',
left: '-17px',
bottom: '-27px',
color: '#fff',
transform: 'rotate(25deg)',
'& svg': {
width: '100px',
height: '100px',
opacity: '0.35'
}
});
interface UserCountCardProps {
primary: string;
secondary: string;
iconPrimary: GenericCardProps['iconPrimary'];
color: string;
}
// =============================|| USER NUM CARD ||============================= //
const UserCountCard = ({ primary, secondary, iconPrimary, color }: UserCountCardProps) => {
const IconPrimary = iconPrimary!;
const primaryIcon = iconPrimary ? <IconPrimary fontSize="large" /> : null;
return (
<Card elevation={0} sx={{ background: color, position: 'relative', color: '#fff' }}>
<CardContent>
<IconWrapper>{primaryIcon}</IconWrapper>
<Grid container direction="column" justifyContent="center" alignItems="center" spacing={1}>
<Grid item sm={12}>
<Typography variant="h3" align="center" color="inherit">
{secondary}
</Typography>
</Grid>
<Grid item sm={12}>
<Typography variant="body1" align="center" color="inherit">
{primary}
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
);
};
export default UserCountCard;
// material-ui
/**
* if you want to use image instead of <svg> uncomment following.
*
* import logoIconDark from 'assets/images/logo-icon-dark.svg';
* import logoIcon from 'assets/images/logo-icon.svg';
* import { ThemeMode } from 'types/config';
*
*/
import logo from 'assets/images/sign_connect_plus_icon_3.png';
// ==============================|| LOGO ICON SVG ||============================== //
const LogoIcon = () => {
// const theme = useTheme();
return (
/**
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
*
* <img src={theme.palette.mode === ThemeMode.DARK ? logoIconDark : logoIcon} alt="Mantis" width="100" />
*
*/
<>
<img src={logo} alt="FitsPro-ERP" width="50" height="40" />
{/* <svg width="129" height="129" viewBox="0 0 129 129" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.27577 57.2242L17.5616 46.9384L17.5724 46.9276H36.9234L29.2238 54.6273L27.2358 56.6152L19.3511 64.5L20.3276 65.4792L64.5 109.649L109.649 64.5L101.761 56.6152L101.206 56.0572L92.0766 46.9276H111.428L111.438 46.9384L119.5 55.0002L129 64.5L64.5 129L0 64.5L7.27577 57.2242ZM64.5 0L101.77 37.2695H82.4185L64.5 19.3511L46.5816 37.2695H27.2305L64.5 0Z"
fill={theme.palette.primary.dark}
/>
<path
d="M19.3509 64.5L27.2357 56.6152L29.2236 54.6273L21.5267 46.9276H17.5722L17.5615 46.9384L7.27561 57.2242L17.1483 67.0487L19.3509 64.5Z"
fill="url(#paint0_linear)"
/>
<path
d="M101.762 56.6152L109.649 64.5L108.868 65.2807L108.871 65.2834L119.5 55.0002L111.438 46.9384L111.428 46.9276H110.644L101.206 56.0572L101.762 56.6152Z"
fill="url(#paint1_linear)"
/>
<path
d="M17.5508 46.9276L17.5615 46.9384L27.2357 56.6152L64.4999 93.8767L111.449 46.9276H17.5508Z"
fill={theme.palette.primary.main}
/>
<defs>
<linearGradient id="paint0_linear" x1="25.0225" y1="49.3259" x2="11.4189" y2="62.9295" gradientUnits="userSpaceOnUse">
<stop stopColor={theme.palette.primary.darker} />
<stop offset="0.9637" stopColor={theme.palette.primary.dark} stopOpacity="0" />
</linearGradient>
<linearGradient id="paint1_linear" x1="103.5" y1="49.5" x2="114.5" y2="62" gradientUnits="userSpaceOnUse">
<stop stopColor={theme.palette.primary.darker} />
<stop offset="1" stopColor={theme.palette.primary.dark} stopOpacity="0" />
</linearGradient>
</defs>
</svg> */}
</>
);
};
export default LogoIcon;
// material-ui
// import { useTheme } from '@mui/material/styles';
// import { ThemeMode } from 'types/config';
/**
* if you want to use image instead of <svg> uncomment following.
*
* import logoDark from 'assets/images/logo-dark.svg';
* import logo from 'assets/images/logo.svg';
*
*/
import logo from 'assets/images/sign_connect_plus_logo_4.png';
// ==============================|| LOGO SVG ||============================== //
const LogoMain = ({ reverse, ...others }: { reverse?: boolean }) => {
// const theme = useTheme();
return (
/**
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
*
* <img src={theme.palette.mode === ThemeMode.DARK ? logoDark : logo} alt="Mantis" width="100" />
*
*/
<>
<img src={logo} alt="FitsPro-ERP" width="200" height={50} />
{/* <svg width="118" height="35" viewBox="0 0 118 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.63564 15.8644L6.94797 13.552L6.95038 13.5496H11.3006L9.56969 15.2806L9.12278 15.7275L7.35024 17.5L7.56977 17.7201L17.5 27.6498L27.6498 17.5L25.8766 15.7275L25.7518 15.602L23.6994 13.5496H28.0496L28.052 13.552L29.8644 15.3644L32 17.5L17.5 32L3 17.5L4.63564 15.8644ZM17.5 3L25.8784 11.3784H21.5282L17.5 7.35024L13.4718 11.3784H9.12158L17.5 3Z"
fill={theme.palette.primary.dark}
/>
<path
d="M7.35025 17.5L9.1228 15.7275L9.5697 15.2805L7.83937 13.5496H6.95039L6.94798 13.552L4.63564 15.8644L6.8551 18.073L7.35025 17.5Z"
fill="url(#paint0_linear)"
/>
<path
d="M25.8767 15.7275L27.6498 17.5L27.4743 17.6755L27.4749 17.6761L29.8644 15.3644L28.0521 13.552L28.0497 13.5496H27.8736L25.7518 15.602L25.8767 15.7275Z"
fill="url(#paint1_linear)"
/>
<path
d="M6.94549 13.5496L6.9479 13.552L9.12272 15.7275L17.4999 24.1041L28.0544 13.5496H6.94549Z"
fill={theme.palette.primary.main}
/>
<path
d="M46.5781 10V26H49.3594V14.9844H49.5078L53.9297 25.9531H56.0078L60.4297 15.0078H60.5781V26H63.3594V10H59.8125L55.0625 21.5937H54.875L50.125 10H46.5781ZM69.8438 26.2422C71.7266 26.2422 72.8516 25.3594 73.3672 24.3516H73.4609V26H76.1797V17.9687C76.1797 14.7969 73.5937 13.8438 71.3047 13.8438C68.7813 13.8438 66.8437 14.9687 66.2188 17.1562L68.8594 17.5312C69.1406 16.7109 69.9375 16.0078 71.3203 16.0078C72.6328 16.0078 73.3516 16.6797 73.3516 17.8594V17.9062C73.3516 18.7188 72.5 18.7578 70.3828 18.9844C68.0547 19.2344 65.8281 19.9297 65.8281 22.6328C65.8281 24.9922 67.5547 26.2422 69.8438 26.2422ZM70.5781 24.1641C69.3984 24.1641 68.5547 23.625 68.5547 22.5859C68.5547 21.5 69.5 21.0469 70.7656 20.8672C71.5078 20.7656 72.9922 20.5781 73.3594 20.2812V21.6953C73.3594 23.0312 72.2813 24.1641 70.5781 24.1641ZM81.8516 18.9687C81.8516 17.2344 82.8984 16.2344 84.3906 16.2344C85.8516 16.2344 86.7266 17.1953 86.7266 18.7969V26H89.5547V18.3594C89.5625 15.4844 87.9219 13.8438 85.4453 13.8438C83.6484 13.8438 82.4141 14.7031 81.8672 16.0391H81.7266V14H79.0234V26H81.8516V18.9687ZM98.4219 14H96.0547V11.125H93.2266V14H91.5234V16.1875H93.2266V22.8594C93.2109 25.1172 94.8516 26.2266 96.9766 26.1641C97.7813 26.1406 98.3359 25.9844 98.6406 25.8828L98.1641 23.6719C98.0078 23.7109 97.6875 23.7812 97.3359 23.7812C96.625 23.7812 96.0547 23.5312 96.0547 22.3906V16.1875H98.4219V14ZM100.787 26H103.615V14H100.787V26ZM102.209 12.2969C103.107 12.2969 103.842 11.6094 103.842 10.7656C103.842 9.91406 103.107 9.22656 102.209 9.22656C101.303 9.22656 100.568 9.91406 100.568 10.7656C100.568 11.6094 101.303 12.2969 102.209 12.2969ZM116.008 17.1719C115.617 15.1406 113.992 13.8438 111.18 13.8438C108.289 13.8438 106.32 15.2656 106.328 17.4844C106.32 19.2344 107.398 20.3906 109.703 20.8672L111.75 21.2969C112.852 21.5391 113.367 21.9844 113.367 22.6641C113.367 23.4844 112.477 24.1016 111.133 24.1016C109.836 24.1016 108.992 23.5391 108.75 22.4609L105.992 22.7266C106.344 24.9297 108.195 26.2344 111.141 26.2344C114.141 26.2344 116.258 24.6797 116.266 22.4062C116.258 20.6953 115.156 19.6484 112.891 19.1562L110.844 18.7188C109.625 18.4453 109.141 18.0234 109.148 17.3281C109.141 16.5156 110.039 15.9531 111.219 15.9531C112.523 15.9531 113.211 16.6641 113.43 17.4531L116.008 17.1719Z"
fill={theme.palette.mode === ThemeMode.DARK || reverse ? theme.palette.common.white : theme.palette.common.black}
fillOpacity="0.85"
/>
<defs>
<linearGradient id="paint0_linear" x1="8.62526" y1="14.0888" x2="5.56709" y2="17.1469" gradientUnits="userSpaceOnUse">
<stop stopColor={theme.palette.primary.darker} />
<stop offset="0.9637" stopColor={theme.palette.primary.dark} stopOpacity="0" />
</linearGradient>
<linearGradient id="paint1_linear" x1="26.2675" y1="14.1279" x2="28.7404" y2="16.938" gradientUnits="userSpaceOnUse">
<stop stopColor={theme.palette.primary.darker} />
<stop offset="1" stopColor={theme.palette.primary.dark} stopOpacity="0" />
</linearGradient>
</defs>
</svg> */}
</>
);
};
export default LogoMain;
import { Link } from 'react-router-dom';
import { To } from 'history';
// material-ui
import { ButtonBase } from '@mui/material';
import { SxProps } from '@mui/system';
// project import
import Logo from './LogoMain';
import LogoIcon from './LogoIcon';
import { APP_DEFAULT_PATH } from 'config';
// ==============================|| MAIN LOGO ||============================== //
interface Props {
reverse?: boolean;
isIcon?: boolean;
sx?: SxProps;
to?: To;
}
const LogoSection = ({ reverse, isIcon, sx, to }: Props) => (
<ButtonBase disableRipple component={Link} to={!to ? APP_DEFAULT_PATH : to} sx={sx}>
{isIcon ? <LogoIcon /> : <Logo reverse={reverse} />}
</ButtonBase>
);
export default LogoSection;
import { useState } from 'react';
// material-ui
import { Box, CardActions, Collapse, Divider, Tooltip } from '@mui/material';
// third-party
import { CopyToClipboard } from 'react-copy-to-clipboard';
// project import
import SyntaxHighlight from 'utils/SyntaxHighlight';
import IconButton from 'components/@extended/IconButton';
// assets
import { CodeOutlined, CopyOutlined } from '@ant-design/icons';
// ==============================|| CLIPBOARD & HIGHLIGHTER ||============================== //
interface Props {
codeString: string;
codeHighlight: boolean;
}
const Highlighter = ({ codeString, codeHighlight }: Props) => {
const [highlight, setHighlight] = useState(codeHighlight);
return (
<>
<CardActions sx={{ justifyContent: 'flex-end' }}>
<Box sx={{ display: 'flex' }}>
<CopyToClipboard text={codeString}>
<Tooltip title="Copy the source" placement="top-end">
<IconButton color="secondary" size="small" sx={{ fontSize: '0.875rem' }}>
<CopyOutlined />
</IconButton>
</Tooltip>
</CopyToClipboard>
<Divider orientation="vertical" variant="middle" flexItem sx={{ mx: 1 }} />
<Tooltip title="Show the source" placement="top-end">
<IconButton
sx={{ fontSize: '0.875rem' }}
size="small"
color={highlight ? 'primary' : 'secondary'}
onClick={() => setHighlight(!highlight)}
>
<CodeOutlined />
</IconButton>
</Tooltip>
</Box>
</CardActions>
<Collapse in={highlight}>{highlight && <SyntaxHighlight>{codeString}</SyntaxHighlight>}</Collapse>
</>
);
};
export default Highlighter;
//material-ui
import { styled } from '@mui/material/styles';
// third-party
import { SnackbarProvider } from 'notistack';
// project import
import { useSelector } from 'store';
// assets
import { CheckCircleOutlined, CloseCircleOutlined, InfoCircleOutlined, WarningOutlined } from '@ant-design/icons';
// custom styles
const StyledSnackbarProvider = styled(SnackbarProvider)(({ theme }) => ({
'&.notistack-MuiContent-default': {
backgroundColor: theme.palette.primary.main
},
'&.notistack-MuiContent-error': {
backgroundColor: theme.palette.error.main
},
'&.notistack-MuiContent-success': {
backgroundColor: theme.palette.success.main
},
'&.notistack-MuiContent-info': {
backgroundColor: theme.palette.info.main
},
'&.notistack-MuiContent-warning': {
backgroundColor: theme.palette.warning.main
}
}));
// ===========================|| SNACKBAR - NOTISTACK ||=========================== //
const Notistack = ({ children }: any) => {
const snackbar = useSelector((state) => state.snackbar);
const iconSX = { marginRight: 8, fontSize: '1.15rem' };
return (
<StyledSnackbarProvider
maxSnack={snackbar.maxStack}
dense={snackbar.dense}
iconVariant={
snackbar.iconVariant === 'useemojis'
? {
success: <CheckCircleOutlined style={iconSX} />,
error: <CloseCircleOutlined style={iconSX} />,
warning: <WarningOutlined style={iconSX} />,
info: <InfoCircleOutlined style={iconSX} />
}
: undefined
}
hideIconVariant={snackbar.iconVariant === 'hide' ? true : false}
>
{children}
</StyledSnackbarProvider>
);
};
export default Notistack;
// material-ui
import { alpha, styled } from '@mui/material/styles';
import { Box, Theme } from '@mui/material';
// third-party
import SimpleBar, { Props } from 'simplebar-react';
import { BrowserView, MobileView } from 'react-device-detect';
import { MUIStyledCommonProps } from '@mui/system';
import { ReactNode } from 'react';
// root style
const RootStyle = styled(BrowserView)({
flexGrow: 1,
height: '100%',
overflow: 'hidden'
});
// scroll bar wrapper
const SimpleBarStyle = styled(SimpleBar)(({ theme }) => ({
maxHeight: '100%',
'& .simplebar-scrollbar': {
'&:before': {
backgroundColor: alpha(theme.palette.grey[500], 0.48)
},
'&.simplebar-visible:before': {
opacity: 1
}
},
'& .simplebar-track.simplebar-vertical': {
width: 10
},
'& .simplebar-track.simplebar-horizontal .simplebar-scrollbar': {
height: 6
},
'& .simplebar-mask': {
zIndex: 'inherit'
}
}));
// ==============================|| SIMPLE SCROLL BAR ||============================== //
export default function SimpleBarScroll({ children, sx, ...other }: MUIStyledCommonProps<Theme> & Props) {
return (
<>
<RootStyle>
<SimpleBarStyle clickOnTrack={false} sx={sx} {...other}>
{children as ReactNode}
</SimpleBarStyle>
</RootStyle>
<MobileView>
<Box sx={{ overflowX: 'auto', ...sx }} {...other}>
{children as ReactNode}
</Box>
</MobileView>
</>
);
}
// material-ui
import { alpha, styled, useTheme } from '@mui/material/styles';
import { Stack, Typography } from '@mui/material';
// third-party
import { useDropzone } from 'react-dropzone';
// project import
import RejectionFiles from './RejectionFiles';
// assets
import { CameraOutlined } from '@ant-design/icons';
// types
import { CustomFile, UploadProps } from 'types/dropzone';
const RootWrapper = styled('div')(({ theme }) => ({
width: 124,
height: 124,
borderRadius: '50%',
border: `1px dashed ${theme.palette.primary.main}`,
background: theme.palette.primary.lighter
}));
const DropzoneWrapper = styled('div')({
zIndex: 0,
width: '100%',
height: '100%',
outline: 'none',
display: 'flex',
overflow: 'hidden',
borderRadius: '50%',
position: 'relative',
alignItems: 'center',
justifyContent: 'center',
'& > *': { width: '100%', height: '100%' },
'&:hover': {
cursor: 'pointer',
'& .placeholder': {
zIndex: 9
}
}
});
const PlaceholderWrapper = styled('div')(({ theme }) => ({
display: 'flex',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
backgroundColor: alpha(theme.palette.primary.lighter, 0.75),
transition: theme.transitions.create('opacity', {
easing: theme.transitions.easing.easeInOut,
duration: theme.transitions.duration.shorter
}),
'&:hover': { opacity: 0.85 }
}));
// ==============================|| UPLOAD - AVATAR ||============================== //
const AvatarUpload = ({ error, file, setFieldValue, sx, ...other }: UploadProps) => {
const theme = useTheme();
const { getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({
accept: {
'image/*': []
},
multiple: false,
onDrop: (acceptedFiles: CustomFile[]) => {
setFieldValue(
'files',
acceptedFiles.map((file: CustomFile) =>
Object.assign(file, {
preview: URL.createObjectURL(file)
})
)
);
}
});
const thumbs =
file &&
file.map((item: CustomFile) => (
<img
key={item.name}
alt={item.name}
src={item.preview}
onLoad={() => {
URL.revokeObjectURL(item.preview!);
}}
/>
));
return (
<>
<RootWrapper
sx={{
...((isDragReject || error) && {
borderColor: 'error.light'
}),
...sx
}}
>
<DropzoneWrapper {...getRootProps()} sx={{ ...(isDragActive && { opacity: 0.6 }) }}>
<input {...getInputProps()} />
{thumbs}
<PlaceholderWrapper
className="placeholder"
sx={{
...(thumbs && {
opacity: 0,
color: 'common.white',
bgcolor: 'grey.900'
}),
...((isDragReject || error) && {
bgcolor: 'error.lighter'
})
}}
>
<Stack spacing={0.5} alignItems="center">
<CameraOutlined style={{ color: theme.palette.secondary.main, fontSize: '2rem' }} />
<Typography color="secondary">{file ? 'Update' : 'Upload'}</Typography>
</Stack>
</PlaceholderWrapper>
</DropzoneWrapper>
</RootWrapper>
{fileRejections.length > 0 && <RejectionFiles fileRejections={fileRejections} />}
</>
);
};
export default AvatarUpload;
// material-ui
import { useTheme } from '@mui/material/styles';
import { List, ListItemText, ListItem } from '@mui/material';
// project import
import IconButton from 'components/@extended/IconButton';
// utils
import getDropzoneData from 'utils/getDropzoneData';
// type
import { DropzopType, FilePreviewProps } from 'types/dropzone';
// assets
import { CloseCircleFilled, FileFilled } from '@ant-design/icons';
// ==============================|| MULTI UPLOAD - PREVIEW ||============================== //
export default function FilesPreview({ showList = false, files, onRemove, type }: FilePreviewProps) {
const theme = useTheme();
const hasFile = files.length > 0;
const layoutType = type;
return (
<List
disablePadding
sx={{
...(hasFile && type !== DropzopType.standard && { my: 3 }),
...(type === DropzopType.standard && { width: 'calc(100% - 84px)' })
}}
>
{files.map((file, index) => {
const { key, name, size, preview, type } = getDropzoneData(file, index);
if (showList) {
return (
<ListItem
key={key}
sx={{
p: 0,
m: 0.5,
width: layoutType === DropzopType.standard ? 64 : 80,
height: layoutType === DropzopType.standard ? 64 : 80,
borderRadius: 1.25,
position: 'relative',
display: 'inline-flex',
verticalAlign: 'text-top',
border: `solid 1px ${theme.palette.divider}`,
overflow: 'hidden'
}}
>
{type?.includes('image') && <img alt="preview" src={preview} style={{ width: '100%' }} />}
{!type?.includes('image') && <FileFilled style={{ width: '100%', fontSize: '1.5rem' }} />}
{onRemove && (
<IconButton
size="small"
color="error"
shape="rounded"
onClick={() => onRemove(file)}
sx={{
fontSize: '0.875rem',
bgcolor: 'background.paper',
p: 0,
width: 'auto',
height: 'auto',
top: 2,
right: 2,
position: 'absolute'
}}
>
<CloseCircleFilled />
</IconButton>
)}
</ListItem>
);
}
return (
<ListItem
key={key}
sx={{
my: 1,
px: 2,
py: 0.75,
borderRadius: 0.75,
border: (theme) => `solid 1px ${theme.palette.divider}`
}}
>
<FileFilled style={{ width: '30px', height: '30px', fontSize: '1.15rem', marginRight: 4 }} />
<ListItemText
primary={typeof file === 'string' ? file : name}
secondary={typeof file === 'string' ? '' : size}
primaryTypographyProps={{ variant: 'subtitle2' }}
secondaryTypographyProps={{ variant: 'caption' }}
/>
{onRemove && (
<IconButton edge="end" size="small" onClick={() => onRemove(file)}>
<CloseCircleFilled style={{ fontSize: '1.15rem' }} />
</IconButton>
)}
</ListItem>
);
})}
</List>
);
}
// material-ui
import { styled } from '@mui/material/styles';
import { Box, Button, Stack } from '@mui/material';
// third-party
import { useDropzone } from 'react-dropzone';
// project import
import RejectionFiles from './RejectionFiles';
import PlaceholderContent from './PlaceholderContent';
import FilesPreview from './FilesPreview';
// types
import { CustomFile, DropzopType, UploadMultiFileProps } from 'types/dropzone';
const DropzoneWrapper = styled('div')(({ theme }) => ({
outline: 'none',
padding: theme.spacing(5, 1),
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.paper,
border: `1px dashed ${theme.palette.secondary.main}`,
'&:hover': { opacity: 0.72, cursor: 'pointer' }
}));
// ==============================|| UPLOAD - MULTIPLE FILE ||============================== //
const MultiFileUpload = ({ error, showList = false, files, type, setFieldValue, sx, onUpload, ...other }: UploadMultiFileProps) => {
const { getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({
multiple: true,
onDrop: (acceptedFiles: CustomFile[]) => {
if (files) {
setFieldValue('files', [
...files,
...acceptedFiles.map((file: CustomFile) =>
Object.assign(file, {
preview: URL.createObjectURL(file)
})
)
]);
} else {
setFieldValue(
'files',
acceptedFiles.map((file: CustomFile) =>
Object.assign(file, {
preview: URL.createObjectURL(file)
})
)
);
}
}
});
const onRemoveAll = () => {
setFieldValue('files', null);
};
const onRemove = (file: File | string) => {
const filteredItems = files && files.filter((_file) => _file !== file);
setFieldValue('files', filteredItems);
};
return (
<>
<Box
sx={{
width: '100%',
...(type === DropzopType.standard && { width: 'auto', display: 'flex' }),
...sx
}}
>
<Stack {...(type === DropzopType.standard && { alignItems: 'center' })}>
<DropzoneWrapper
{...getRootProps()}
sx={{
...(type === DropzopType.standard && {
p: 0,
m: 1,
width: 64,
height: 64
}),
...(isDragActive && { opacity: 0.72 }),
...((isDragReject || error) && {
color: 'error.main',
borderColor: 'error.light',
bgcolor: 'error.lighter'
})
}}
>
<input {...getInputProps()} />
<PlaceholderContent type={type} />
</DropzoneWrapper>
{type === DropzopType.standard && files && files.length > 1 && (
<Button variant="contained" color="error" size="extraSmall" onClick={onRemoveAll}>
Remove all
</Button>
)}
</Stack>
{fileRejections.length > 0 && <RejectionFiles fileRejections={fileRejections} />}
{files && files.length > 0 && <FilesPreview files={files} showList={showList} onRemove={onRemove} type={type} />}
</Box>
{type !== DropzopType.standard && files && files.length > 0 && (
<Stack direction="row" justifyContent="flex-end" spacing={1.5} sx={{ mt: 1.5 }}>
<Button color="inherit" size="small" onClick={onRemoveAll}>
Remove all
</Button>
<Button size="small" variant="contained" onClick={onUpload}>
Upload files
</Button>
</Stack>
)}
</>
);
};
export default MultiFileUpload;
// material-ui
import { CameraOutlined } from '@ant-design/icons';
import { Typography, Stack, CardMedia } from '@mui/material';
// assets
import UploadCover from 'assets/images/upload/upload.svg';
import { DropzopType } from 'types/dropzone';
// ==============================|| UPLOAD - PLACEHOLDER ||============================== //
export default function PlaceholderContent({ type }: { type?: string }) {
return (
<>
{type !== DropzopType.standard && (
<Stack
spacing={2}
alignItems="center"
justifyContent="center"
direction={{ xs: 'column', md: 'row' }}
sx={{ width: 1, textAlign: { xs: 'center', md: 'left' } }}
>
<CardMedia component="img" image={UploadCover} sx={{ width: 150 }} />
<Stack sx={{ p: 3 }} spacing={1}>
<Typography variant="h5">Drag & Drop or Select file</Typography>
<Typography color="secondary">
Drop files here or click&nbsp;
<Typography component="span" color="primary" sx={{ textDecoration: 'underline' }}>
browse
</Typography>
&nbsp;thorough your machine
</Typography>
</Stack>
</Stack>
)}
{type === DropzopType.standard && (
<Stack alignItems="center" justifyContent="center" sx={{ height: 1 }}>
<CameraOutlined style={{ fontSize: '32px' }} />
</Stack>
)}
</>
);
}
// material-ui
import { alpha } from '@mui/material/styles';
import { Box, Paper, Typography } from '@mui/material';
// third-party
import { FileRejection } from 'react-dropzone';
// utils
import getDropzoneData from 'utils/getDropzoneData';
// ==============================|| DROPZONE - REJECTION FILES ||============================== //
type Props = {
fileRejections: FileRejection[];
};
export default function RejectionFiles({ fileRejections }: Props) {
return (
<Paper
variant="outlined"
sx={{
py: 1,
px: 2,
mt: 3,
borderColor: 'error.light',
bgcolor: (theme) => alpha(theme.palette.error.main, 0.08)
}}
>
{fileRejections.map(({ file, errors }) => {
const { path, size } = getDropzoneData(file);
return (
<Box key={path} sx={{ my: 1 }}>
<Typography variant="subtitle2" noWrap>
{path} - {size ? size : ''}
</Typography>
{errors.map((error) => (
<Box key={error.code} component="li" sx={{ typography: 'caption' }}>
{error.message}
</Box>
))}
</Box>
);
})}
</Paper>
);
}
// material-ui
import { styled, useTheme } from '@mui/material/styles';
import { Box, Button, Stack } from '@mui/material';
// third-party
import { useDropzone } from 'react-dropzone';
// project import
import RejectionFiles from './RejectionFiles';
import PlaceholderContent from './PlaceholderContent';
// types
import { CustomFile, UploadProps } from 'types/dropzone';
const DropzoneWrapper = styled('div')(({ theme }) => ({
outline: 'none',
overflow: 'hidden',
position: 'relative',
padding: theme.spacing(5, 1),
borderRadius: theme.shape.borderRadius,
transition: theme.transitions.create('padding'),
backgroundColor: theme.palette.background.paper,
border: `1px dashed ${theme.palette.secondary.main}`,
'&:hover': { opacity: 0.72, cursor: 'pointer' }
}));
// ==============================|| UPLOAD - SINGLE FILE ||============================== //
const SingleFileUpload = ({ error, file, setFieldValue, sx, ...other }: UploadProps) => {
const theme = useTheme();
const { getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({
accept: {
'image/*': []
},
multiple: false,
onDrop: (acceptedFiles: CustomFile[]) => {
setFieldValue(
'files',
acceptedFiles.map((file: CustomFile) =>
Object.assign(file, {
preview: URL.createObjectURL(file)
})
)
);
}
});
const thumbs =
file &&
file.map((item: CustomFile) => (
<img
key={item.name}
alt={item.name}
src={item.preview}
style={{
top: 8,
left: 8,
borderRadius: 2,
position: 'absolute',
width: 'calc(100% - 16px)',
height: 'calc(100% - 16px)',
background: theme.palette.background.paper
}}
onLoad={() => {
URL.revokeObjectURL(item.preview!);
}}
/>
));
const onRemove = () => {
setFieldValue('files', null);
};
return (
<Box sx={{ width: '100%', ...sx }}>
<DropzoneWrapper
{...getRootProps()}
sx={{
...(isDragActive && { opacity: 0.72 }),
...((isDragReject || error) && {
color: 'error.main',
borderColor: 'error.light',
bgcolor: 'error.lighter'
}),
...(file && {
padding: '12% 0'
})
}}
>
<input {...getInputProps()} />
<PlaceholderContent />
{thumbs}
</DropzoneWrapper>
{fileRejections.length > 0 && <RejectionFiles fileRejections={fileRejections} />}
{file && file.length > 0 && (
<Stack direction="row" justifyContent="flex-end" sx={{ mt: 1.5 }}>
<Button variant="contained" color="error" onClick={onRemove}>
Remove
</Button>
</Stack>
)}
</Box>
);
};
export default SingleFileUpload;
import React, { createContext, useEffect, useReducer } from 'react';
// third-party
import { CognitoUser, CognitoUserPool, CognitoUserSession, CognitoUserAttribute, AuthenticationDetails } from 'amazon-cognito-identity-js';
// project imports
import Loader from 'components/Loader';
import { LOGIN, LOGOUT } from 'store/reducers/actions';
import authReducer from 'store/reducers/auth';
// types
import { AWSCognitoContextType, InitialLoginContextProps } from 'types/auth';
// constant
const initialState: InitialLoginContextProps = {
isLoggedIn: false,
isInitialized: false,
user: null
};
export const userPool = new CognitoUserPool({
UserPoolId: process.env.REACT_APP_AWS_POOL_ID || '',
ClientId: process.env.REACT_APP_AWS_APP_CLIENT_ID || ''
});
const setSession = (serviceToken?: string | null) => {
if (serviceToken) {
localStorage.setItem('serviceToken', serviceToken);
} else {
localStorage.removeItem('serviceToken');
}
};
// ==============================|| AWS COGNITO - CONTEXT & PROVIDER ||============================== //
const AWSCognitoContext = createContext<AWSCognitoContextType | null>(null);
export const AWSCognitoProvider = ({ children }: { children: React.ReactElement }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
useEffect(() => {
const init = async () => {
try {
const serviceToken = window.localStorage.getItem('serviceToken');
if (serviceToken) {
setSession(serviceToken);
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
name: 'Betty'
}
}
});
} else {
dispatch({
type: LOGOUT
});
}
} catch (err) {
console.error(err);
dispatch({
type: LOGOUT
});
}
};
init();
}, []);
const login = async (email: string, password: string) => {
const usr = new CognitoUser({
Username: email,
Pool: userPool
});
const authData = new AuthenticationDetails({
Username: email,
Password: password
});
usr.authenticateUser(authData, {
onSuccess: (session: CognitoUserSession) => {
setSession(session.getAccessToken().getJwtToken());
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
email: authData.getUsername(),
name: 'John AWS'
}
}
});
},
onFailure: (_err) => {},
newPasswordRequired: () => {
// // User was signed up by an admin and must provide new
// // password and required attributes, if any, to complete
// // authentication.
// // the api doesn't accept this field back
// delete userAttributes.email_verified;
// // unsure about this field, but I don't send this back
// delete userAttributes.phone_number_verified;
// // Get these details and call
// usr.completeNewPasswordChallenge(password, userAttributes, requiredAttributes);
}
});
};
const register = (email: string, password: string, firstName: string, lastName: string) =>
new Promise((success, rej) => {
userPool.signUp(
email,
password,
[
new CognitoUserAttribute({ Name: 'email', Value: email }),
new CognitoUserAttribute({ Name: 'name', Value: `${firstName} ${lastName}` })
],
[],
async (err, result) => {
if (err) {
rej(err);
return;
}
success(result);
}
);
});
const logout = () => {
const loggedInUser = userPool.getCurrentUser();
if (loggedInUser) {
setSession(null);
loggedInUser.signOut();
dispatch({ type: LOGOUT });
}
};
const forgotPassword = async (email: string) => {
const user = new CognitoUser({
Username: email,
Pool: userPool
});
user.forgotPassword({
onSuccess: function () {},
onFailure: function () {}
});
};
const resetPassword = async (verificationCode: string, newPassword: string) => {
const email = localStorage.getItem('email');
const user = new CognitoUser({
Username: email as string,
Pool: userPool
});
return new Promise((resolve, reject) => {
user.confirmPassword(verificationCode, newPassword, {
onSuccess: function (data) {
localStorage.removeItem('email');
resolve(data);
},
onFailure: function (error) {
reject(error.message);
}
});
});
};
const updateProfile = () => {};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return (
<AWSCognitoContext.Provider value={{ ...state, login, logout, register, forgotPassword, resetPassword, updateProfile }}>
{children}
</AWSCognitoContext.Provider>
);
};
export default AWSCognitoContext;
import React, { createContext, useEffect, useReducer } from 'react';
// third-party
import { Auth0Client } from '@auth0/auth0-spa-js';
// reducer - state management
import { LOGIN, LOGOUT } from 'store/reducers/actions';
import authReducer from 'store/reducers/auth';
// project import
import Loader from 'components/Loader';
import { KeyedObject } from 'types/root';
import { Auth0ContextType, AuthProps } from 'types/auth';
// constant
let auth0Client: Auth0Client;
const initialState: AuthProps = {
isLoggedIn: false,
isInitialized: false,
user: null
};
// ==============================|| AUTH0 CONTEXT & PROVIDER ||============================== //
const Auth0Context = createContext<Auth0ContextType | null>(null);
export const Auth0Provider = ({ children }: { children: React.ReactElement }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
useEffect(() => {
const init = async () => {
try {
auth0Client = new Auth0Client({
clientId: process.env.REACT_APP_AUTH0_CLIENT_ID as string,
domain: process.env.REACT_APP_AUTH0_DOMAIN as string,
authorizationParams: {
redirect_uri: window.location.origin
}
});
await auth0Client.checkSession();
const isLoggedIn = await auth0Client.isAuthenticated();
if (isLoggedIn) {
const user = await auth0Client.getUser();
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
id: user?.sub,
email: user?.email
}
}
});
} else {
dispatch({
type: LOGOUT
});
}
} catch (err) {
dispatch({
type: LOGOUT
});
}
};
init();
}, []);
const login = async (options?: KeyedObject) => {
await auth0Client.loginWithPopup(options);
const isLoggedIn = await auth0Client.isAuthenticated();
if (isLoggedIn) {
const user = await auth0Client.getUser();
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
id: user?.sub,
avatar: user?.picture,
email: user?.email,
name: user?.name,
tier: 'Premium'
}
}
});
}
};
const logout = () => {
auth0Client.logout();
dispatch({
type: LOGOUT
});
};
const resetPassword = async (email: string) => {};
const updateProfile = () => {};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return <Auth0Context.Provider value={{ ...state, login, logout, resetPassword, updateProfile }}>{children}</Auth0Context.Provider>;
};
export default Auth0Context;
import { createContext, ReactNode } from 'react';
// project import
import config from 'config';
import useLocalStorage from 'hooks/useLocalStorage';
// types
import { CustomizationProps, FontFamily, I18n, MenuOrientation, PresetColor, ThemeDirection, ThemeMode } from 'types/config';
// initial state
const initialState: CustomizationProps = {
...config,
onChangeContainer: () => {},
onChangeLocalization: (lang: I18n) => {},
onChangeMode: (mode: ThemeMode) => {},
onChangePresetColor: (theme: PresetColor) => {},
onChangeDirection: (direction: ThemeDirection) => {},
onChangeMiniDrawer: (miniDrawer: boolean) => {},
onChangeMenuOrientation: (menuOrientation: MenuOrientation) => {},
onChangeFontFamily: (fontFamily: FontFamily) => {}
};
// ==============================|| CONFIG CONTEXT & PROVIDER ||============================== //
const ConfigContext = createContext(initialState);
type ConfigProviderProps = {
children: ReactNode;
};
function ConfigProvider({ children }: ConfigProviderProps) {
const [config, setConfig] = useLocalStorage('mantis-react-ts-config', initialState);
const onChangeContainer = () => {
setConfig({
...config,
container: !config.container
});
};
const onChangeLocalization = (lang: I18n) => {
setConfig({
...config,
i18n: lang
});
};
const onChangeMode = (mode: ThemeMode) => {
setConfig({
...config,
mode
});
};
const onChangePresetColor = (theme: PresetColor) => {
setConfig({
...config,
presetColor: theme
});
};
const onChangeDirection = (direction: ThemeDirection) => {
setConfig({
...config,
themeDirection: direction
});
};
const onChangeMiniDrawer = (miniDrawer: boolean) => {
setConfig({
...config,
miniDrawer
});
};
const onChangeMenuOrientation = (layout: MenuOrientation) => {
setConfig({
...config,
menuOrientation: layout
});
};
const onChangeFontFamily = (fontFamily: FontFamily) => {
setConfig({
...config,
fontFamily
});
};
return (
<ConfigContext.Provider
value={{
...config,
onChangeContainer,
onChangeLocalization,
onChangeMode,
onChangePresetColor,
onChangeDirection,
onChangeMiniDrawer,
onChangeMenuOrientation,
onChangeFontFamily
}}
>
{children}
</ConfigContext.Provider>
);
}
export { ConfigProvider, ConfigContext };
import React, { createContext, useEffect, useReducer } from 'react';
// third-party
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// action - state management
import { LOGIN, LOGOUT } from 'store/reducers/actions';
import authReducer from 'store/reducers/auth';
// project import
import Loader from 'components/Loader';
import { AuthProps, FirebaseContextType } from 'types/auth';
// firebase initialize
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_FIREBASE_APP_ID,
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID
});
}
// const
const initialState: AuthProps = {
isLoggedIn: false,
isInitialized: false,
user: null
};
// ==============================|| FIREBASE CONTEXT & PROVIDER ||============================== //
const FirebaseContext = createContext<FirebaseContextType | null>(null);
export const FirebaseProvider = ({ children }: { children: React.ReactElement }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
useEffect(
() =>
firebase.auth().onAuthStateChanged((user: any) => {
if (user) {
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
id: user.uid,
email: user.email!,
name: user.displayName || 'Stebin Ben',
role: 'UI/UX Designer'
}
}
});
} else {
dispatch({
type: LOGOUT
});
}
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch]
);
const firebaseEmailPasswordSignIn = (email: string, password: string) => firebase.auth().signInWithEmailAndPassword(email, password);
const firebaseGoogleSignIn = () => {
const provider = new firebase.auth.GoogleAuthProvider();
return firebase.auth().signInWithPopup(provider);
};
const firebaseTwitterSignIn = () => {
const provider = new firebase.auth.TwitterAuthProvider();
return firebase.auth().signInWithPopup(provider);
};
const firebaseFacebookSignIn = () => {
const provider = new firebase.auth.FacebookAuthProvider();
return firebase.auth().signInWithPopup(provider);
};
const firebaseRegister = async (email: string, password: string) => firebase.auth().createUserWithEmailAndPassword(email, password);
const logout = () => firebase.auth().signOut();
const resetPassword = async (email: string) => {
await firebase.auth().sendPasswordResetEmail(email);
};
const updateProfile = () => {};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return (
<FirebaseContext.Provider
value={{
...state,
firebaseRegister,
firebaseEmailPasswordSignIn,
login: () => {},
firebaseGoogleSignIn,
firebaseTwitterSignIn,
firebaseFacebookSignIn,
logout,
resetPassword,
updateProfile
}}
>
{children}
</FirebaseContext.Provider>
);
};
export default FirebaseContext;
import React, { createContext, useEffect, useReducer } from 'react';
// third-party
import { Chance } from 'chance';
import jwtDecode from 'jwt-decode';
// reducer - state management
import { LOGIN, LOGOUT } from 'store/reducers/actions';
import authReducer from 'store/reducers/auth';
// project import
import Loader from 'components/Loader';
import { AuthProps, JWTContextType } from 'types/auth';
import { KeyedObject } from 'types/root';
import { axiosServices_Mock as axios } from 'utils/axios';
const chance = new Chance();
// constant
const initialState: AuthProps = {
isLoggedIn: false,
isInitialized: false,
user: null
};
const verifyToken: (st: string) => boolean = (serviceToken) => {
if (!serviceToken) {
return false;
}
const decoded: KeyedObject = jwtDecode(serviceToken);
/**
* Property 'exp' does not exist on type '<T = unknown>(token: string, options?: JwtDecodeOptions | undefined) => T'.
*/
return decoded.exp > Date.now() / 1000;
};
const setSession = (serviceToken?: string | null) => {
if (serviceToken) {
localStorage.setItem('serviceToken', serviceToken);
axios.defaults.headers.common.Authorization = `Bearer ${serviceToken}`;
} else {
localStorage.removeItem('serviceToken');
delete axios.defaults.headers.common.Authorization;
}
};
// ==============================|| JWT CONTEXT & PROVIDER ||============================== //
const JWTContext = createContext<JWTContextType | null>(null);
export const JWTProvider = ({ children }: { children: React.ReactElement }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
useEffect(() => {
const init = async () => {
try {
const serviceToken = window.localStorage.getItem('serviceToken');
if (serviceToken && verifyToken(serviceToken)) {
setSession(serviceToken);
const response = await axios.get('/api/account/me');
const { user } = response.data;
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user
}
});
} else {
dispatch({
type: LOGOUT
});
}
} catch (err) {
console.error(err);
dispatch({
type: LOGOUT
});
}
};
init();
}, []);
const login = async (email: string, password: string) => {
const response = await axios.post('/api/account/login', { email, password });
const { serviceToken, user } = response.data;
setSession(serviceToken);
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user
}
});
};
const register = async (email: string, password: string, firstName: string, lastName: string) => {
// todo: this flow need to be recode as it not verified
const id = chance.bb_pin();
const response = await axios.post('/api/account/register', {
id,
email,
password,
firstName,
lastName
});
let users = response.data;
if (window.localStorage.getItem('users') !== undefined && window.localStorage.getItem('users') !== null) {
const localUsers = window.localStorage.getItem('users');
users = [
...JSON.parse(localUsers!),
{
id,
email,
password,
name: `${firstName} ${lastName}`
}
];
}
window.localStorage.setItem('users', JSON.stringify(users));
};
const logout = () => {
setSession(null);
dispatch({ type: LOGOUT });
};
const resetPassword = async (email: string) => {};
const updateProfile = () => {};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return <JWTContext.Provider value={{ ...state, login, logout, register, resetPassword, updateProfile }}>{children}</JWTContext.Provider>;
};
export default JWTContext;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import { useContext } from 'react';
import { ConfigContext } from 'contexts/ConfigContext';
// ==============================|| CONFIG - HOOKS ||============================== //
const useConfig = () => useContext(ConfigContext);
export default useConfig;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment