Compare commits
10 Commits
7b7c0b8c00
...
79fefda61a
| Author | SHA1 | Date | |
|---|---|---|---|
| 79fefda61a | |||
| 403b2cf5e7 | |||
| 89cf46ffb4 | |||
| cc901b4875 | |||
| d6136b3438 | |||
| 78eabd8d58 | |||
| c0d3dc35d3 | |||
| 55af1caad0 | |||
| 5f7df5b9ab | |||
| dc9548e8c9 |
31
.env
Normal file
31
.env
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
REACT_APP_VERSION = v2.1.0
|
||||||
|
GENERATE_SOURCEMAP = false
|
||||||
|
|
||||||
|
## Backend API URL
|
||||||
|
REACT_APP_API_URL=https://mock-data-api-nextjs.vercel.app/
|
||||||
|
|
||||||
|
## Google Map Key
|
||||||
|
|
||||||
|
REACT_APP_GOOGLE_MAPS_API_KEY=AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8
|
||||||
|
REACT_APP_RIDER_ACCESS_ID=AAAAILMpCEU:APA91bEavuOllBI6sFgYtxXAgNmAVwNA-MnCMHLGlR4_t7UqpLajAkdn3T0CZr_zaLBknLyim9ytFLMZgbeXmKqTad_PKCbqlYjHpaizVrLXtecxqyEy4UktIacK2UvHVUATHL-7VQQk
|
||||||
|
## Firebase - Google Auth
|
||||||
|
|
||||||
|
REACT_APP_FIREBASE_API_KEY=
|
||||||
|
REACT_APP_FIREBASE_AUTH_DOMAIN=
|
||||||
|
REACT_APP_FIREBASE_PROJECT_ID=
|
||||||
|
REACT_APP_FIREBASE_STORAGE_BUCKET=
|
||||||
|
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=
|
||||||
|
REACT_APP_FIREBASE_APP_ID=
|
||||||
|
REACT_APP_FIREBASE_MEASUREMENT_ID=
|
||||||
|
|
||||||
|
## AWS
|
||||||
|
|
||||||
|
REACT_APP_AWS_POOL_ID=
|
||||||
|
REACT_APP_AWS_APP_CLIENT_ID=
|
||||||
|
|
||||||
|
## Auth0
|
||||||
|
|
||||||
|
REACT_APP_AUTH0_CLIENT_ID=
|
||||||
|
REACT_APP_AUTH0_DOMAIN=
|
||||||
|
|
||||||
|
DISABLE_ESLINT_PLUGIN=true
|
||||||
3
.env.development
Normal file
3
.env.development
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REACT_APP_URL='https://jupiter.nearle.app/live/api/v1'
|
||||||
|
REACT_APP_URL2='https://jupiter.nearle.app/live/api/v2'
|
||||||
|
REACT_APP_STAFF_TOKEN=''
|
||||||
3
.env.staging
Normal file
3
.env.staging
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REACT_APP_URL='https://jupiter.nearle.app/live/api/v1'
|
||||||
|
REACT_APP_URL2='https://jupiter.nearle.app/live/api/v2'
|
||||||
|
REACT_APP_STAFF_TOKEN=''
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -5,15 +5,6 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
# Environment variables
|
|
||||||
.env
|
|
||||||
.env.development
|
|
||||||
.env.staging
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@@ -2,6 +2,54 @@ const webpack = require('webpack');
|
|||||||
const WorkBoxPlugin = require('workbox-webpack-plugin');
|
const WorkBoxPlugin = require('workbox-webpack-plugin');
|
||||||
|
|
||||||
module.exports = function override(config) {
|
module.exports = function override(config) {
|
||||||
|
// @astryxdesign/core ships as ESM with relative imports missing extensions
|
||||||
|
// (e.g. '../Tooltip/Tooltip'), which webpack 5 rejects under strict ESM
|
||||||
|
// resolution. Relax it for this package only rather than globally.
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.m?js$/,
|
||||||
|
include: /node_modules[\\/]@astryxdesign/,
|
||||||
|
resolve: { fullySpecified: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// react-scripts routes all other node_modules JS through
|
||||||
|
// babel-preset-react-app/dependencies (a lighter preset than the one used
|
||||||
|
// for src/), which doesn't include the import-attributes syntax plugin.
|
||||||
|
// @astryxdesign/core's i18n module uses `import x from './en.json' with
|
||||||
|
// { type: 'json' }` — the webpack version react-scripts@5 bundles predates
|
||||||
|
// native import-attributes support, so even once Babel can *parse* that
|
||||||
|
// syntax (via the syntax plugin) it still needs to be stripped from the
|
||||||
|
// output, or webpack's own parser trips on the untouched `with {...}`
|
||||||
|
// clause in babel-loader's output. Insert a dedicated oneOf rule ahead of
|
||||||
|
// the generic node_modules js/mjs rule — oneOf rules are mutually
|
||||||
|
// exclusive (first match wins), so the generic rule won't also try to
|
||||||
|
// process these files.
|
||||||
|
const stripImportAttributes = () => ({
|
||||||
|
visitor: {
|
||||||
|
ImportDeclaration(path) {
|
||||||
|
if (path.node.attributes) path.node.attributes = [];
|
||||||
|
if (path.node.assertions) path.node.assertions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const oneOfRule = config.module.rules.find((rule) => Array.isArray(rule.oneOf));
|
||||||
|
if (oneOfRule) {
|
||||||
|
oneOfRule.oneOf.unshift({
|
||||||
|
test: /\.m?js$/,
|
||||||
|
include: /node_modules[\\/]@astryxdesign/,
|
||||||
|
resolve: { fullySpecified: false },
|
||||||
|
loader: require.resolve('babel-loader'),
|
||||||
|
options: {
|
||||||
|
babelrc: false,
|
||||||
|
configFile: false,
|
||||||
|
compact: false,
|
||||||
|
presets: [[require.resolve('babel-preset-react-app/dependencies'), { helpers: true }]],
|
||||||
|
plugins: [require.resolve('@babel/plugin-syntax-import-attributes'), stripImportAttributes],
|
||||||
|
cacheDirectory: true,
|
||||||
|
cacheCompression: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
config.resolve.fallback = {
|
config.resolve.fallback = {
|
||||||
process: require.resolve('process/browser'),
|
process: require.resolve('process/browser'),
|
||||||
// zlib: require.resolve('browserify-zlib'),
|
// zlib: require.resolve('browserify-zlib'),
|
||||||
|
|||||||
484
orig_invoicePreview_check.js
Normal file
484
orig_invoicePreview_check.js
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
// import nearleLogo from '../../assets/images/nearleLogo.png';
|
||||||
|
import logo_nearle1 from '../../../assets/images/logo-nearle1.png';
|
||||||
|
import axios from 'axios';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import Loader from 'components/Loader';
|
||||||
|
import { enqueueSnackbar } from 'notistack';
|
||||||
|
import { DownloadOutlined, PrinterFilled } from '@ant-design/icons';
|
||||||
|
import ReactToPrint, { useReactToPrint } from 'react-to-print';
|
||||||
|
import { SearchOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
|
// import jsPDF from 'jspdf';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { FaArrowLeft } from 'react-icons/fa6';
|
||||||
|
import { FaIndianRupeeSign } from 'react-icons/fa6';
|
||||||
|
|
||||||
|
// import autoTable from 'jspdf-autotable';
|
||||||
|
import {
|
||||||
|
Grid,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableHead,
|
||||||
|
TablePagination,
|
||||||
|
TableRow,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
Typography,
|
||||||
|
Box,
|
||||||
|
OutlinedInput,
|
||||||
|
InputAdornment,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
Stack,
|
||||||
|
Chip,
|
||||||
|
DialogActions,
|
||||||
|
TextField
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
const InvoicePreview = () => {
|
||||||
|
const [selected, setselected] = useState({});
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
console.log('previewSelect', location.state);
|
||||||
|
const componentRef = useRef(null);
|
||||||
|
const [tabletype, settabletype] = useState(true);
|
||||||
|
const [paydialog, setpaydialog] = useState(false);
|
||||||
|
const [refnumber, setRefnumber] = useState('');
|
||||||
|
const [remarks, setRemarks] = useState('');
|
||||||
|
const theme = useTheme();
|
||||||
|
useEffect(() => {
|
||||||
|
setselected(location.state);
|
||||||
|
}, []);
|
||||||
|
// ================================================= || formatNumberToRupees || =================================================
|
||||||
|
|
||||||
|
function formatNumberToRupees(value) {
|
||||||
|
return new Intl.NumberFormat('en-IN', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'INR',
|
||||||
|
minimumFractionDigits: 2
|
||||||
|
}).format(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('refnumber', refnumber);
|
||||||
|
console.log('remarks', remarks);
|
||||||
|
}, [refnumber, remarks]);
|
||||||
|
|
||||||
|
// ================================================= || updatePayment || =================================================
|
||||||
|
|
||||||
|
const updatePayment = async () => {
|
||||||
|
try {
|
||||||
|
const updateResponse = await axios.put(`${process.env.REACT_APP_URL}/invoice/updatestatus`, {
|
||||||
|
salesid: selected.salesid,
|
||||||
|
referenceno: refnumber,
|
||||||
|
referencedate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
billstatus: 2,
|
||||||
|
paymentremarks: remarks
|
||||||
|
});
|
||||||
|
if (updateResponse.status) {
|
||||||
|
enqueueSnackbar(' Updated Successfully ', {
|
||||||
|
variant: 'success',
|
||||||
|
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
||||||
|
autoHideDuration: 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log('updateResponse', updateResponse);
|
||||||
|
} catch (error) {
|
||||||
|
console.log('updateResponse', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack direction="row" justifyContent="Space-between" alignItems={'center'} spacing={2} sx={{ px: 2.5, py: 1, bgcolor: '#eeeeee' }}>
|
||||||
|
{/* // ================================================= || Invoice Details || ================================================= */}
|
||||||
|
|
||||||
|
<Stack direction={'row'} alignItems={'center'} spacing={2}>
|
||||||
|
<Tooltip title="back">
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/nearle/invoice');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaArrowLeft size={'large'} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Stack alignItems={'center'}>
|
||||||
|
<Typography variant="h3" color={'primary'}>
|
||||||
|
Invoice Details
|
||||||
|
</Typography>
|
||||||
|
<Chip
|
||||||
|
size="small"
|
||||||
|
color="warning"
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ bgcolor: theme.palette.warning.lighter }}
|
||||||
|
label={`Invoice No :${'\u00a0\u00a0'}${selected.invoiceno}`}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack direction={'row'} spacing={2}>
|
||||||
|
{/* <Button
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
sx={{
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'primary.main',
|
||||||
|
color: 'primary.contrastText'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setpaydialog(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
<FaIndianRupeeSign />
|
||||||
|
Update Payment
|
||||||
|
</Button> */}
|
||||||
|
<ReactToPrint
|
||||||
|
trigger={() => (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
startIcon={<PrinterFilled />}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
sx={{
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'primary.main',
|
||||||
|
color: 'primary.contrastText'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Print
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
content={() => componentRef.current}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* // ================================================= || Date row || ================================================= */}
|
||||||
|
|
||||||
|
<Box sx={{ pb: 2.5, border: '1px solid #eee' }}>
|
||||||
|
<div ref={componentRef} style={{ width: '100%' }}>
|
||||||
|
<Box id="print" sx={{ p: 2.5 }}>
|
||||||
|
<Box sx={{ pb: 2.5 }}>
|
||||||
|
<Stack
|
||||||
|
sx={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
// bgcolor: theme.palette.primary.main,
|
||||||
|
border: '1px solid #eee',
|
||||||
|
px: 3
|
||||||
|
}}
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<Box sx={{ pt: 0.5 }}>
|
||||||
|
<Stack direction="row" spacing={2}>
|
||||||
|
<img src={logo_nearle1} style={{ width: '150px', height: '50px' }} />{' '}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
color: theme.palette.primary.main
|
||||||
|
}}
|
||||||
|
variant="subtitle1"
|
||||||
|
>
|
||||||
|
Invoice No :
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ color: theme.palette.primary.main }}>{`${'\u00a0\u00a0'}${selected.invoiceno}`}</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ pt: 2.5, pb: 1.75 }}>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography sx={{ pl: 4, color: theme.palette.primary.main }} variant="subtitle1">
|
||||||
|
Date :{' '}
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ color: theme.palette.primary.main }}>
|
||||||
|
{dayjs(selected.transactiondate).format('DD-MM-YYYY')}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
pr: 2,
|
||||||
|
overflow: 'hidden',
|
||||||
|
color: theme.palette.primary.main
|
||||||
|
}}
|
||||||
|
variant="subtitle1"
|
||||||
|
>
|
||||||
|
Due Date :
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ color: theme.palette.primary.main }}>{dayjs(selected.dueDate).format('DD-MM-YYYY')}</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
{/* // ================================================= || from to || ================================================= */}
|
||||||
|
|
||||||
|
<Box sx={{ pt: 2.5 }}>
|
||||||
|
<Grid container spacing={2} justifyContent="space-between" direction="row">
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
border: 1,
|
||||||
|
minHeight: 240,
|
||||||
|
borderColor: 'grey.200',
|
||||||
|
borderRadius: 0.5,
|
||||||
|
p: 2.5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid item md={8}>
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Typography variant="h5">From:</Typography>
|
||||||
|
<Stack sx={{ width: '100%' }}>
|
||||||
|
<Typography variant="subtitle1">Nearle Technology Privite Limited.</Typography>
|
||||||
|
<Typography color="secondary">
|
||||||
|
424, 4<sup>th</sup>floor,
|
||||||
|
</Typography>
|
||||||
|
<Typography color="secondary">Red rose towers,</Typography>
|
||||||
|
<Typography color="secondary">DB Road, RS Puram,</Typography>
|
||||||
|
<Typography color="secondary">641002.</Typography>
|
||||||
|
<Typography color="secondary">care@nearle.in</Typography>
|
||||||
|
<Typography color="secondary">9047968666</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
border: 1,
|
||||||
|
minHeight: 240,
|
||||||
|
borderColor: 'grey.200',
|
||||||
|
borderRadius: 0.5,
|
||||||
|
p: 2.5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid item md={8}>
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Typography variant="h5">To:</Typography>
|
||||||
|
<Stack sx={{ width: '100%' }}>
|
||||||
|
<Typography variant="subtitle1">{selected.tenantname}</Typography>
|
||||||
|
<Typography color="secondary">{selected.address}</Typography>
|
||||||
|
<Typography color="secondary">{selected.suburb}</Typography>
|
||||||
|
<Typography color="secondary">{selected.city}</Typography>
|
||||||
|
<Typography color="secondary">{selected.state}</Typography>{' '}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{/* // ================================================= || invoice table || ================================================= */}
|
||||||
|
|
||||||
|
<TableContainer
|
||||||
|
// sx={{
|
||||||
|
// ...(tabletype
|
||||||
|
// ? {
|
||||||
|
// maxHeight: 430,
|
||||||
|
// mt: -3,
|
||||||
|
// "&::-webkit-scrollbar": {
|
||||||
|
// width: "4px", // Width of vertical scrollbar
|
||||||
|
// height: "4px", // Height of horizontal scrollbar
|
||||||
|
// },
|
||||||
|
// "&::-webkit-scrollbar-thumb": {
|
||||||
|
// backgroundColor: "#65387A", // Color of the scrollbar thumb
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// : {}),
|
||||||
|
// }}
|
||||||
|
>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>S.No</TableCell>
|
||||||
|
<TableCell>Particulars</TableCell>
|
||||||
|
<TableCell>Unit</TableCell>
|
||||||
|
<TableCell>Quantity</TableCell>
|
||||||
|
<TableCell align="right">Rate</TableCell>
|
||||||
|
{/* {selected && selected.pricingtypeid === 73 && ( */}
|
||||||
|
<TableCell>Other Charges</TableCell>
|
||||||
|
{/* )} */}
|
||||||
|
<TableCell align="right">Amount</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
{selected.tenantsalesdetails && (
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>1</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography>
|
||||||
|
{`Invoice from ${dayjs(selected.tenantsalesdetails[0].fromdate).format('DD-MM-YYYY')} to ${dayjs(
|
||||||
|
selected.tenantsalesdetails[0].todate
|
||||||
|
).format('DD-MM-YYYY')}`}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography>{selected.tenantsalesdetails[0].pricingtype}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<Typography>{`${selected.tenantsalesdetails[0].quantity}km`}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Typography align="right">{`₹ ${selected.tenantsalesdetails[0].baserate.toFixed(2)}`}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
{/* {selected.tenantsalesdetails[0].pricingtypeid == 73 && ( */}
|
||||||
|
<TableCell align="center">
|
||||||
|
<Typography>{`₹ ${selected.tenantsalesdetails[0].othercharges}.00`}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
{/* )} */}
|
||||||
|
<TableCell align="right">
|
||||||
|
<Typography>{`₹ ${selected.tenantsalesdetails[0].amount}.00`}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
<Divider />
|
||||||
|
<Box sx={{ p: 2.5 }}>
|
||||||
|
<Grid container direction="row" justifyContent="flex-end">
|
||||||
|
<Grid item md={4}>
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography color="secondary">Sub Total:</Typography>
|
||||||
|
<Typography variant="h6">{formatNumberToRupees(selected.salesamount)}</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography color="secondary">Discount:</Typography>
|
||||||
|
<Typography variant="h6" color={theme.palette.error.main}>
|
||||||
|
- {formatNumberToRupees(selected.discountamt)}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography color={theme.palette.grey[500]}>Tax:</Typography>
|
||||||
|
<Typography variant="h6" color={theme.palette.success.main}>
|
||||||
|
+ {formatNumberToRupees(selected.taxamount)}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography sx={{ pr: 2 }} variant="subtitle1">
|
||||||
|
Grand Total:
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h6">{formatNumberToRupees(Math.round(selected.totalamount))}</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Divider />
|
||||||
|
<Box sx={{ p: 2.5 }}>
|
||||||
|
<Typography>Notes: {selected.remarks}</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* ================================================= || updatePayment Dialog || ================================================= */}
|
||||||
|
<Dialog
|
||||||
|
open={paydialog}
|
||||||
|
onClose={() => {
|
||||||
|
setpaydialog(false);
|
||||||
|
}}
|
||||||
|
maxWidth={'sm'}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ bgcolor: theme.palette.primary.main }}>
|
||||||
|
<Stack direction={'row'} spacing={1}>
|
||||||
|
<Typography variant="h2" sx={{ color: 'white' }}>
|
||||||
|
₹
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h3" sx={{ color: 'white' }}>
|
||||||
|
Update Payment
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<Stack spacing={1} sx={{ mb: 2 }}>
|
||||||
|
<Typography>Reference No</Typography>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
placeholder="Enter Reference Number"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
onChange={(e) => {
|
||||||
|
setRefnumber(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack spacing={2} sx={{ mb: 2 }}>
|
||||||
|
<Typography>Remarks</Typography>
|
||||||
|
<TextField
|
||||||
|
multiline
|
||||||
|
required
|
||||||
|
placeholder="Enter Remarks"
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
onChange={(e) => {
|
||||||
|
setRemarks(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
sx={{
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'primary.main',
|
||||||
|
color: 'primary.contrastText'
|
||||||
|
},
|
||||||
|
m: 2
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setpaydialog(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
disabled={refnumber == '' || remarks == ''}
|
||||||
|
sx={{
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'primary.main',
|
||||||
|
color: 'primary.contrastText'
|
||||||
|
},
|
||||||
|
m: 2
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setpaydialog(false);
|
||||||
|
updatePayment();
|
||||||
|
navigate('/invoice');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InvoicePreview;
|
||||||
6058
package-lock.json
generated
6058
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -5,17 +5,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^7.0.0",
|
"@ant-design/colors": "^7.0.0",
|
||||||
"@ant-design/icons": "^5.0.1",
|
"@ant-design/icons": "^5.0.1",
|
||||||
|
"@astryxdesign/core": "^0.1.9",
|
||||||
|
"@astryxdesign/theme-neutral": "^0.1.9",
|
||||||
"@custom-react-hooks/use-network": "^1.0.1",
|
"@custom-react-hooks/use-network": "^1.0.1",
|
||||||
"@emotion/cache": "^11.10.7",
|
|
||||||
"@emotion/react": "^11.10.6",
|
|
||||||
"@emotion/styled": "^11.10.6",
|
|
||||||
"@mui/base": "^5.0.0-alpha.126",
|
|
||||||
"@mui/icons-material": "^5.14.19",
|
|
||||||
"@mui/lab": "^5.0.0-alpha.127",
|
|
||||||
"@mui/material": "^5.12.1",
|
|
||||||
"@mui/x-date-pickers": "^6.18.2",
|
|
||||||
"@react-google-maps/api": "^2.20.7",
|
"@react-google-maps/api": "^2.20.7",
|
||||||
"@reduxjs/toolkit": "^1.9.5",
|
"@reduxjs/toolkit": "^2.12.0",
|
||||||
|
"@stylexjs/stylex": "^0.19.0",
|
||||||
"@svgr/webpack": "^7.0.0",
|
"@svgr/webpack": "^7.0.0",
|
||||||
"@tanstack/react-query": "^5.22.2",
|
"@tanstack/react-query": "^5.22.2",
|
||||||
"antd": "^5.11.5",
|
"antd": "^5.11.5",
|
||||||
@@ -24,43 +19,42 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"chance": "^1.1.11",
|
"chance": "^1.1.11",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
|
"date-fns": "^2.30.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"env-cmd": "^10.1.0",
|
"env-cmd": "^10.1.0",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"framer-motion": "^10.12.4",
|
"framer-motion": "^12.42.2",
|
||||||
"geolib": "^3.3.4",
|
"geolib": "^3.3.4",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mui-daterange-picker": "^1.0.5",
|
|
||||||
"notistack": "^3.0.1",
|
"notistack": "^3.0.1",
|
||||||
"papaparse": "^5.5.3",
|
"papaparse": "^5.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^19.2.8",
|
||||||
"react-app-rewired": "^2.2.1",
|
"react-app-rewired": "^2.2.1",
|
||||||
"react-csv": "^2.2.2",
|
"react-csv": "^2.2.2",
|
||||||
"react-device-detect": "^2.2.3",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^19.2.8",
|
||||||
"react-geocode": "^0.2.3",
|
"react-geocode": "^0.2.3",
|
||||||
"react-google-autocomplete": "^2.7.3",
|
"react-google-autocomplete": "^2.7.3",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
"react-intl": "^6.4.1",
|
"react-intl": "^7.1.14",
|
||||||
"react-leaflet": "^4.2.1",
|
"react-leaflet": "^5.0.0",
|
||||||
"react-loading-icons": "^1.1.0",
|
"react-loading-icons": "^1.1.0",
|
||||||
"react-phone-number-input": "^3.3.10",
|
"react-phone-number-input": "^3.3.10",
|
||||||
"react-redux": "^8.0.5",
|
"react-redux": "^9.2.0",
|
||||||
"react-router": "^6.10.0",
|
"react-router": "^6.10.0",
|
||||||
"react-router-dom": "^6.10.0",
|
"react-router-dom": "^6.10.0",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"react-timer-hook": "^3.0.5",
|
"react-timer-hook": "^3.0.5",
|
||||||
"react-to-print": "^2.15.1",
|
"react-to-print": "^3.3.0",
|
||||||
"react-virtuoso": "^4.7.0",
|
"react-virtuoso": "^4.7.0",
|
||||||
"react18-input-otp": "^1.1.3",
|
"redux": "^5.0.1",
|
||||||
"redux": "^4.2.1",
|
|
||||||
"simplebar": "^6.2.5",
|
"simplebar": "^6.2.5",
|
||||||
"simplebar-react": "^3.2.4",
|
"simplebar-react": "^3.2.4",
|
||||||
"stream-browserify": "^3.0.0",
|
"stream-browserify": "^3.0.0",
|
||||||
@@ -109,6 +103,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.21.4",
|
"@babel/core": "^7.21.4",
|
||||||
"@babel/eslint-parser": "^7.21.3",
|
"@babel/eslint-parser": "^7.21.3",
|
||||||
|
"@babel/plugin-syntax-import-attributes": "^7.29.7",
|
||||||
|
"@babel/preset-env": "^7.28.0",
|
||||||
|
"@babel/preset-react": "^7.27.1",
|
||||||
"eslint": "^8.38.0",
|
"eslint": "^8.38.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 181 KiB |
@@ -17,7 +17,7 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Nearle Express</title>
|
<title>Doormile Dailygrubs Console</title>
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||||
<link
|
<link
|
||||||
|
|||||||
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 181 KiB |
25
src/App.js
25
src/App.js
@@ -1,6 +1,5 @@
|
|||||||
// project import
|
// project import
|
||||||
import Routes from 'routes';
|
import Routes from 'routes';
|
||||||
import ThemeCustomization from 'themes';
|
|
||||||
import Locales from 'components/Locales';
|
import Locales from 'components/Locales';
|
||||||
// import RTLLayout from 'components/RTLLayout';
|
// import RTLLayout from 'components/RTLLayout';
|
||||||
import ScrollTop from 'components/ScrollTop';
|
import ScrollTop from 'components/ScrollTop';
|
||||||
@@ -16,19 +15,40 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||||||
|
|
||||||
// ==============================|| APP - THEME, ROUTER, LOCAL ||============================== //
|
// ==============================|| APP - THEME, ROUTER, LOCAL ||============================== //
|
||||||
|
|
||||||
|
const SESSION_CLEAR_INTERVAL_MS = 60 * 60 * 1000;
|
||||||
|
const SESSION_CHECK_INTERVAL_MS = 10000; // Check every 10 seconds
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!localStorage.getItem('authname')) {
|
if (!localStorage.getItem('authname')) {
|
||||||
navigate('/login');
|
navigate('/login');
|
||||||
|
} else if (!localStorage.getItem('sessionStartTime')) {
|
||||||
|
localStorage.setItem('sessionStartTime', String(Date.now()));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
if (!localStorage.getItem('authname')) return;
|
||||||
|
const sessionStart = Number(localStorage.getItem('sessionStartTime')) || Date.now();
|
||||||
|
if (Date.now() - sessionStart >= SESSION_CLEAR_INTERVAL_MS) {
|
||||||
|
localStorage.clear();
|
||||||
|
navigate('/login');
|
||||||
|
}
|
||||||
|
}, SESSION_CHECK_INTERVAL_MS);
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, []);
|
||||||
const { openItem } = useSelector((state) => state.menu);
|
const { openItem } = useSelector((state) => state.menu);
|
||||||
console.log('openItem', openItem);
|
console.log('openItem', openItem);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ThemeCustomization>
|
{/* MUI's <ThemeCustomization> ThemeProvider/CssBaseline wrapper was removed here —
|
||||||
|
every reachable component now renders through Astryx (whose own theme + reset.css
|
||||||
|
are loaded globally in src/index.js), so nothing left in the render tree consumes
|
||||||
|
MUI theme context. src/themes/* is unreachable dead code now (not deleted outright
|
||||||
|
without a full audit of its many override sub-files, but no longer mounted). */}
|
||||||
<InternetStatus />
|
<InternetStatus />
|
||||||
{/* <RTLLayout> */}
|
{/* <RTLLayout> */}
|
||||||
<Locales>
|
<Locales>
|
||||||
@@ -44,7 +64,6 @@ const App = () => {
|
|||||||
</ScrollTop>
|
</ScrollTop>
|
||||||
</Locales>
|
</Locales>
|
||||||
{/* </RTLLayout> */}
|
{/* </RTLLayout> */}
|
||||||
</ThemeCustomization>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Box } from '@mui/material';
|
|
||||||
|
|
||||||
// project-imports
|
|
||||||
import { ThemeDirection, ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// ==============================|| AUTH BLUR BACK SVG ||============================== //
|
|
||||||
|
|
||||||
const AuthBackground = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
filter: 'blur(18px)',
|
|
||||||
zIndex: -1,
|
|
||||||
bottom: 0,
|
|
||||||
transform: theme.direction === ThemeDirection.RTL ? 'rotate(180deg)' : 'inherit'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg width="100%" height="calc(100vh - 175px)" viewBox="0 0 405 809" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path
|
|
||||||
d="M-358.39 358.707L-293.914 294.23L-293.846 294.163H-172.545L-220.81 342.428L-233.272 354.889L-282.697 404.314L-276.575 410.453L0.316589 687.328L283.33 404.314L233.888 354.889L230.407 351.391L173.178 294.163H294.48L294.547 294.23L345.082 344.765L404.631 404.314L0.316589 808.629L-403.998 404.314L-358.39 358.707ZM0.316589 0L233.938 233.622H112.637L0.316589 121.301L-112.004 233.622H-233.305L0.316589 0Z"
|
|
||||||
fill={theme.palette.primary.light}
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M-516.39 358.707L-451.914 294.23L-451.846 294.163H-330.545L-378.81 342.428L-391.272 354.889L-440.697 404.314L-434.575 410.453L-157.683 687.328L125.33 404.314L75.8879 354.889L72.4068 351.391L15.1785 294.163H136.48L136.547 294.23L187.082 344.765L246.631 404.314L-157.683 808.629L-561.998 404.314L-516.39 358.707ZM-157.683 0L75.9383 233.622H-45.3627L-157.683 121.301L-270.004 233.622H-391.305L-157.683 0Z"
|
|
||||||
fill={theme.palette.success.light}
|
|
||||||
opacity="0.6"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M-647.386 358.707L-582.91 294.23L-582.842 294.163H-461.541L-509.806 342.428L-522.268 354.889L-571.693 404.314L-565.571 410.453L-288.68 687.328L-5.66624 404.314L-55.1082 354.889L-58.5893 351.391L-115.818 294.163H5.48342L5.5507 294.23L56.0858 344.765L115.635 404.314L-288.68 808.629L-692.994 404.314L-647.386 358.707ZM-288.68 0L-55.0578 233.622H-176.359L-288.68 121.301L-401 233.622H-522.301L-288.68 0Z"
|
|
||||||
fill={theme.palette.error.lighter}
|
|
||||||
opacity={theme.palette.mode === ThemeMode.DARK ? '0.9' : '1'}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AuthBackground;
|
|
||||||
BIN
src/assets/images/doormile-logo.png
Normal file
BIN
src/assets/images/doormile-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/doormile-mark.png
Normal file
BIN
src/assets/images/doormile-mark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -1,119 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled, useTheme } from '@mui/material/styles';
|
|
||||||
import MuiAvatar from '@mui/material/Avatar';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import getColors from 'utils/getColors';
|
|
||||||
|
|
||||||
// ==============================|| AVATAR - COLOR STYLE ||============================== //
|
|
||||||
|
|
||||||
function getColorStyle({ theme, color, type }) {
|
|
||||||
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) {
|
|
||||||
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 ||============================== //
|
|
||||||
|
|
||||||
const AvatarStyle = styled(MuiAvatar, { shouldForwardProp: (prop) => prop !== 'color' && prop !== 'type' && prop !== 'size' })(
|
|
||||||
({ theme, variant, color, type, size }) => ({
|
|
||||||
...getSizeStyle(size),
|
|
||||||
...getColorStyle({ variant, theme, color, type }),
|
|
||||||
...(size === 'badge' && {
|
|
||||||
borderColor: theme.palette.background.default
|
|
||||||
})
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// ==============================|| EXTENDED - AVATAR ||============================== //
|
|
||||||
|
|
||||||
export default function Avatar({ variant = 'circular', children, color = 'primary', type, size = 'md', ...others }) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AvatarStyle variant={variant} theme={theme} color={color} type={type} size={size} {...others}>
|
|
||||||
{children}
|
|
||||||
</AvatarStyle>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Avatar.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
color: PropTypes.string,
|
|
||||||
type: PropTypes.string,
|
|
||||||
size: PropTypes.string,
|
|
||||||
variant: PropTypes.string
|
|
||||||
};
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import MuiBreadcrumbs from '@mui/material/Breadcrumbs';
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Divider, Grid, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { ApartmentOutlined, HomeFilled, HomeOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| BREADCRUMBS ||============================== //
|
|
||||||
|
|
||||||
const Breadcrumbs = ({
|
|
||||||
card,
|
|
||||||
divider = true,
|
|
||||||
icon,
|
|
||||||
icons,
|
|
||||||
maxItems,
|
|
||||||
navigation,
|
|
||||||
rightAlign,
|
|
||||||
separator,
|
|
||||||
title,
|
|
||||||
titleBottom,
|
|
||||||
sx,
|
|
||||||
...others
|
|
||||||
}) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const location = useLocation();
|
|
||||||
const [main, setMain] = useState();
|
|
||||||
const [item, setItem] = useState();
|
|
||||||
|
|
||||||
let currentPath = location.pathname;
|
|
||||||
|
|
||||||
// only used for component demo breadcrumbs
|
|
||||||
if (currentPath.includes('/components-overview/breadcrumbs')) {
|
|
||||||
currentPath = '/apps/kanban/board';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPath.includes('/apps/kanban/backlogs')) {
|
|
||||||
currentPath = '/apps/kanban/board';
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentPath.includes('/apps/profiles/user/payment')) {
|
|
||||||
setItem(undefined);
|
|
||||||
}
|
|
||||||
}, [item, currentPath]);
|
|
||||||
|
|
||||||
const iconSX = {
|
|
||||||
marginRight: theme.spacing(0.75),
|
|
||||||
marginTop: `-${theme.spacing(0.25)}`,
|
|
||||||
width: '1rem',
|
|
||||||
height: '1rem',
|
|
||||||
color: theme.palette.secondary.main
|
|
||||||
};
|
|
||||||
|
|
||||||
// set active item state
|
|
||||||
const getCollapse = (menu) => {
|
|
||||||
if (menu.children) {
|
|
||||||
menu.children.filter((collapse) => {
|
|
||||||
if (collapse.type && collapse.type === 'collapse') {
|
|
||||||
getCollapse(collapse);
|
|
||||||
if (collapse.url === currentPath) {
|
|
||||||
setMain(collapse);
|
|
||||||
setItem(collapse);
|
|
||||||
}
|
|
||||||
} else if (collapse.type && collapse.type === 'item') {
|
|
||||||
if (currentPath.includes(collapse.url)) {
|
|
||||||
setMain(menu);
|
|
||||||
setItem(collapse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
navigation?.items?.map((menu) => {
|
|
||||||
if (menu.type && menu.type === 'group') {
|
|
||||||
getCollapse(menu);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// item separator
|
|
||||||
const SeparatorIcon = separator;
|
|
||||||
const separatorIcon = separator ? <SeparatorIcon style={{ fontSize: '0.75rem', marginTop: 2 }} /> : '/';
|
|
||||||
|
|
||||||
let mainContent;
|
|
||||||
let itemContent;
|
|
||||||
let breadcrumbContent = <Typography />;
|
|
||||||
let itemTitle = '';
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
Breadcrumbs.propTypes = {
|
|
||||||
card: PropTypes.bool,
|
|
||||||
divider: PropTypes.bool,
|
|
||||||
icon: PropTypes.bool,
|
|
||||||
icons: PropTypes.bool,
|
|
||||||
maxItems: PropTypes.number,
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
rightAlign: PropTypes.bool,
|
|
||||||
separator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
||||||
title: PropTypes.bool,
|
|
||||||
titleBottom: PropTypes.bool,
|
|
||||||
sx: PropTypes.oneOfType([PropTypes.object, PropTypes.string])
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Breadcrumbs;
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Box } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import getColors from 'utils/getColors';
|
|
||||||
|
|
||||||
const Dot = ({ color, size, variant, sx }) => {
|
|
||||||
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
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Dot.propTypes = {
|
|
||||||
color: PropTypes.string,
|
|
||||||
size: PropTypes.number,
|
|
||||||
variant: PropTypes.string,
|
|
||||||
sx: PropTypes.oneOfType([PropTypes.object, PropTypes.string])
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Dot;
|
|
||||||
@@ -1,160 +1,56 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
|
||||||
// material-ui
|
import { IconButton as AstryxIconButton } from '@astryxdesign/core/IconButton';
|
||||||
import MuiIconButton from '@mui/material/IconButton';
|
|
||||||
import { alpha, styled, useTheme } from '@mui/material/styles';
|
|
||||||
|
|
||||||
// project imports
|
|
||||||
import getColors from 'utils/getColors';
|
|
||||||
import getShadow from 'utils/getShadow';
|
|
||||||
|
|
||||||
// ==============================|| ICON BUTTON - COLOR STYLE ||============================== //
|
|
||||||
|
|
||||||
function getColorStyle({ variant, theme, color }) {
|
|
||||||
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 ||============================== //
|
|
||||||
|
|
||||||
const IconButtonStyle = styled(MuiIconButton, { shouldForwardProp: (prop) => prop !== 'variant' && prop !== 'shape' })(
|
|
||||||
({ theme, variant, shape, color }) => ({
|
|
||||||
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 ||============================== //
|
// ==============================|| EXTENDED - ICON BUTTON ||============================== //
|
||||||
|
// Thin Astryx-backed replacement for the old MUI-styled extended IconButton.
|
||||||
|
// Preserves the previous call shape (variant/shape/color/children-as-icon) so existing
|
||||||
|
// call sites don't need individual rewrites — the old MUI variant/color combinatorics
|
||||||
|
// (contained/light/shadow/outlined/dashed/text × primary/secondary/error/...) are
|
||||||
|
// collapsed onto Astryx's variant enum (primary/secondary/ghost/destructive).
|
||||||
|
|
||||||
const IconButton = forwardRef(({ variant = 'text', shape = 'square', children, color = 'primary', ...others }, ref) => {
|
const VARIANT_MAP = {
|
||||||
const theme = useTheme();
|
contained: 'primary',
|
||||||
|
shadow: 'primary',
|
||||||
|
light: 'secondary',
|
||||||
|
outlined: 'secondary',
|
||||||
|
dashed: 'secondary',
|
||||||
|
text: 'ghost'
|
||||||
|
};
|
||||||
|
|
||||||
|
const SIZE_MAP = {
|
||||||
|
small: 'sm',
|
||||||
|
medium: 'md',
|
||||||
|
large: 'lg'
|
||||||
|
};
|
||||||
|
|
||||||
|
const IconButton = forwardRef(
|
||||||
|
({ variant = 'text', shape, children, color = 'primary', size, 'aria-label': ariaLabel, label, style, ...others }, ref) => {
|
||||||
|
const resolvedVariant = color === 'error' || color === 'destructive' ? 'destructive' : VARIANT_MAP[variant] || 'ghost';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButtonStyle ref={ref} disableRipple variant={variant} shape={shape} theme={theme} color={color} {...others}>
|
<AstryxIconButton
|
||||||
{children}
|
ref={ref}
|
||||||
</IconButtonStyle>
|
icon={children}
|
||||||
|
label={label || ariaLabel || 'Action'}
|
||||||
|
variant={resolvedVariant}
|
||||||
|
size={SIZE_MAP[size] || 'md'}
|
||||||
|
style={{ ...(shape === 'rounded' ? { borderRadius: '50%' } : null), ...style }}
|
||||||
|
{...others}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
IconButton.propTypes = {
|
IconButton.propTypes = {
|
||||||
variant: PropTypes.string,
|
variant: PropTypes.string,
|
||||||
shape: PropTypes.string,
|
shape: PropTypes.string,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
color: PropTypes.string
|
color: PropTypes.string,
|
||||||
|
size: PropTypes.string,
|
||||||
|
label: PropTypes.string,
|
||||||
|
style: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
IconButton.displayName = 'IconButton';
|
IconButton.displayName = 'IconButton';
|
||||||
|
|||||||
@@ -1,231 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { forwardRef } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import MuiLoadingButton from '@mui/lab/LoadingButton';
|
|
||||||
import { alpha, styled, useTheme } from '@mui/material/styles';
|
|
||||||
|
|
||||||
// project imports
|
|
||||||
import getColors from 'utils/getColors';
|
|
||||||
import getShadow from 'utils/getShadow';
|
|
||||||
|
|
||||||
// ==============================|| LOADING BUTTON - COLOR STYLE ||============================== //
|
|
||||||
|
|
||||||
function getColorStyle({ variant, theme, color, loadingPosition }) {
|
|
||||||
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 ||============================== //
|
|
||||||
|
|
||||||
const LoadingButtonStyle = styled(MuiLoadingButton, { shouldForwardProp: (prop) => prop !== 'shape' && prop !== 'variant' })(
|
|
||||||
({ theme, variant, shape, color, loading, loadingPosition }) => ({
|
|
||||||
'::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 ||============================== //
|
|
||||||
|
|
||||||
const LoadingButton = forwardRef(({ variant = 'text', shape, children, color = 'primary', ...others }, ref) => {
|
|
||||||
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.propTypes = {
|
|
||||||
variant: PropTypes.string,
|
|
||||||
shape: PropTypes.string,
|
|
||||||
children: PropTypes.node,
|
|
||||||
color: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
LoadingButton.displayName = 'LoadingButton';
|
|
||||||
|
|
||||||
export default LoadingButton;
|
|
||||||
@@ -1,127 +1,55 @@
|
|||||||
// material-ui
|
import { Toast } from '@astryxdesign/core/Toast';
|
||||||
import { Alert, Button, Fade, Grow, Slide } from '@mui/material';
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
import MuiSnackbar from '@mui/material/Snackbar';
|
|
||||||
|
|
||||||
// project-import
|
// project-import
|
||||||
import IconButton from './IconButton';
|
|
||||||
import { dispatch, useSelector } from 'store';
|
import { dispatch, useSelector } from 'store';
|
||||||
import { closeSnackbar } from 'store/reducers/snackbar';
|
import { closeSnackbar } from 'store/reducers/snackbar';
|
||||||
|
|
||||||
// assets
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// animation function
|
|
||||||
function TransitionSlideLeft(props) {
|
|
||||||
return <Slide {...props} direction="left" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransitionSlideUp(props) {
|
|
||||||
return <Slide {...props} direction="up" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransitionSlideRight(props) {
|
|
||||||
return <Slide {...props} direction="right" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransitionSlideDown(props) {
|
|
||||||
return <Slide {...props} direction="down" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function GrowTransition(props) {
|
|
||||||
return <Grow {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// animation options
|
|
||||||
const animation = {
|
|
||||||
SlideLeft: TransitionSlideLeft,
|
|
||||||
SlideUp: TransitionSlideUp,
|
|
||||||
SlideRight: TransitionSlideRight,
|
|
||||||
SlideDown: TransitionSlideDown,
|
|
||||||
Grow: GrowTransition,
|
|
||||||
Fade
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| SNACKBAR ||============================== //
|
// ==============================|| SNACKBAR ||============================== //
|
||||||
|
// Redux-driven single-toast surface (separate from the notistack-based `enqueueSnackbar`
|
||||||
|
// calls used elsewhere in the app). Rebuilt on Astryx's `Toast` primitive — `variant`
|
||||||
|
// picks default vs. alert-style presentation, `alert.color` maps onto Toast's narrower
|
||||||
|
// info/error `type`, and `anchorOrigin` positions the fixed wrapper the same way the MUI
|
||||||
|
// Snackbar's anchorOrigin used to.
|
||||||
|
|
||||||
const Snackbar = () => {
|
const Snackbar = () => {
|
||||||
const snackbar = useSelector((state) => state.snackbar);
|
const snackbar = useSelector((state) => state.snackbar);
|
||||||
const { actionButton, anchorOrigin, alert, close, message, open, transition, variant } = snackbar;
|
const { actionButton, anchorOrigin, alert, message, open, variant } = snackbar;
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
const handleClose = () => dispatch(closeSnackbar());
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return;
|
if (!open) return null;
|
||||||
}
|
|
||||||
dispatch(closeSnackbar());
|
const isError = variant === 'alert' && alert?.color === 'error';
|
||||||
};
|
const showUndo = variant === 'default' ? true : actionButton !== false;
|
||||||
|
|
||||||
|
const vertical = anchorOrigin?.vertical === 'top' ? 'top' : 'bottom';
|
||||||
|
const horizontal = anchorOrigin?.horizontal === 'left' ? 'left' : anchorOrigin?.horizontal === 'center' ? 'center' : 'right';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
{/* default snackbar */}
|
style={{
|
||||||
{variant === 'default' && (
|
position: 'fixed',
|
||||||
<MuiSnackbar
|
[vertical]: 24,
|
||||||
anchorOrigin={anchorOrigin}
|
...(horizontal === 'center' ? { left: '50%', transform: 'translateX(-50%)' } : { [horizontal]: 24 }),
|
||||||
open={open}
|
zIndex: 1500
|
||||||
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}
|
<Toast
|
||||||
</Alert>
|
type={isError ? 'error' : 'info'}
|
||||||
</MuiSnackbar>
|
body={message}
|
||||||
)}
|
isAutoHide
|
||||||
</>
|
autoHideDuration={6000}
|
||||||
|
endContent={
|
||||||
|
showUndo ? (
|
||||||
|
<Button label="Undo" size="sm" variant="ghost" onClick={handleClose}>
|
||||||
|
UNDO
|
||||||
|
</Button>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
onDismiss={handleClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled, useTheme } from '@mui/material/styles';
|
|
||||||
import { Box, tooltipClasses, Tooltip as MuiTooltip } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import getColors from 'utils/getColors';
|
|
||||||
|
|
||||||
// ==============================|| TOOLTIP - VARIANT ||============================== //
|
|
||||||
|
|
||||||
function getVariantStyle({ color, theme, labelColor }) {
|
|
||||||
const colors = getColors(theme, color);
|
|
||||||
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 ||============================== //
|
|
||||||
|
|
||||||
const TooltipStyle = styled(({ className, ...props }) => <MuiTooltip {...props} classes={{ popper: className }} />, {
|
|
||||||
shouldForwardProp: (prop) => prop !== 'color' && prop !== 'labelColor'
|
|
||||||
})(({ theme, color, labelColor }) => ({
|
|
||||||
...(color && getVariantStyle({ color, theme, labelColor }))
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ==============================|| EXTENDED - TOOLTIP ||============================== //
|
|
||||||
|
|
||||||
export default function CustomTooltip({ children, arrow, labelColor = '', ...rest }) {
|
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
|
||||||
<Box display="flex">
|
|
||||||
<TooltipStyle arrow={arrow} {...rest} theme={theme} labelColor={labelColor}>
|
|
||||||
{children}
|
|
||||||
</TooltipStyle>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomTooltip.propTypes = {
|
|
||||||
children: PropTypes.element,
|
|
||||||
arrow: PropTypes.bool,
|
|
||||||
labelColor: PropTypes.string,
|
|
||||||
rest: PropTypes.array
|
|
||||||
};
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { forwardRef } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Collapse, Fade, Box, Grow, Slide, Zoom } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| TRANSITIONS ||============================== //
|
|
||||||
|
|
||||||
const Transitions = forwardRef(({ children, position = 'top-left', type = 'grow', direction = 'up', ...others }, ref) => {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Transitions.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']),
|
|
||||||
position: PropTypes.oneOf(['top-left', 'top-right', 'top', 'bottom-left', 'bottom-right', 'bottom']),
|
|
||||||
direction: PropTypes.oneOf(['up', 'down', 'left', 'right'])
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Transitions;
|
|
||||||
|
|
||||||
export const PopupTransition = forwardRef(function Transition(props, ref) {
|
|
||||||
return <Zoom ref={ref} timeout={200} {...props} />;
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, CircularProgress, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| PROGRESS - CIRCULAR LABEL ||============================== //
|
|
||||||
|
|
||||||
export default function CircularWithLabel({ value, ...others }) {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularWithLabel.propTypes = {
|
|
||||||
value: PropTypes.number
|
|
||||||
};
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, CircularProgress, Typography, circularProgressClasses } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| PROGRESS - CIRCULAR PATH ||============================== //
|
|
||||||
|
|
||||||
export default function CircularWithPath({ value, size, variant, thickness, showLabel, pathColor, sx, ...others }) {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularWithPath.propTypes = {
|
|
||||||
value: PropTypes.number,
|
|
||||||
size: PropTypes.number,
|
|
||||||
variant: PropTypes.string,
|
|
||||||
thickness: PropTypes.number,
|
|
||||||
showLabel: PropTypes.bool,
|
|
||||||
pathColor: PropTypes.string,
|
|
||||||
sx: PropTypes.array,
|
|
||||||
others: PropTypes.array
|
|
||||||
};
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, LinearProgress } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| PROGRESS - LINEAR ICON ||============================== //
|
|
||||||
|
|
||||||
export default function LinearWithIcon({ icon, value, ...others }) {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearWithIcon.propTypes = {
|
|
||||||
icon: PropTypes.node,
|
|
||||||
value: PropTypes.number
|
|
||||||
};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, LinearProgress, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| PROGRESS - LINEAR WITH LABEL ||============================== //
|
|
||||||
|
|
||||||
export default function LinearWithLabel({ value, ...others }) {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearWithLabel.propTypes = {
|
|
||||||
value: PropTypes.number
|
|
||||||
};
|
|
||||||
@@ -1,25 +1,18 @@
|
|||||||
// material-ui
|
import { ProgressBar } from '@astryxdesign/core/ProgressBar';
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
import LinearProgress from '@mui/material/LinearProgress';
|
|
||||||
|
|
||||||
// loader style
|
|
||||||
const LoaderWrapper = styled('div')(({ theme }) => ({
|
|
||||||
position: 'fixed',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
zIndex: 2001,
|
|
||||||
width: '100%',
|
|
||||||
'& > * + *': {
|
|
||||||
marginTop: theme.spacing(2)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ==============================|| Loader ||============================== //
|
// ==============================|| Loader ||============================== //
|
||||||
|
// Used as the <Suspense> fallback for every lazy-loaded route (see
|
||||||
|
// components/Loadable.js) — including while `layout/MainLayout` itself (which
|
||||||
|
// carries the app's <Theme theme={dailygrubsTheme}> brand-red wrapper) is
|
||||||
|
// still loading. During that window this renders OUTSIDE the theme boundary,
|
||||||
|
// so ProgressBar's "accent" variant (var(--color-accent)) would fall back to
|
||||||
|
// Astryx's stock default (blue) instead of the brand red used everywhere
|
||||||
|
// else. Pin --color-accent locally so this loader is always brand red
|
||||||
|
// regardless of whether an ancestor <Theme> has mounted yet.
|
||||||
const Loader = () => (
|
const Loader = () => (
|
||||||
<LoaderWrapper>
|
<div style={{ position: 'fixed', top: 0, left: 0, zIndex: 2001, width: '100%', '--color-accent': '#C01227' }}>
|
||||||
<LinearProgress color="primary" />
|
<ProgressBar label="Loading" isLabelHidden isIndeterminate variant="accent" />
|
||||||
</LoaderWrapper>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Loader;
|
export default Loader;
|
||||||
|
|||||||
@@ -1,113 +1,103 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
|
||||||
// material-ui
|
import { Card } from '@astryxdesign/core/Card';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { HStack } from '@astryxdesign/core/HStack';
|
||||||
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material';
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
|
import { Heading } from '@astryxdesign/core/Heading';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
import { Divider } from '@astryxdesign/core/Divider';
|
||||||
|
|
||||||
// project-imports
|
// Legacy pages still pass MUI `sx`-shaped objects (spacing shorthand + a few
|
||||||
import { ThemeMode } from 'config';
|
// literal CSS properties) into MainCard/contentSX. Rather than rewrite every
|
||||||
|
// call site, translate the handful of shorthand keys actually used in this
|
||||||
|
// codebase (p/m + directional variants) into real CSS and pass the rest
|
||||||
|
// through as-is — covers every current usage without a full sx engine.
|
||||||
|
const SPACING_UNIT = 8;
|
||||||
|
const SPACING_KEYS = {
|
||||||
|
p: ['padding'],
|
||||||
|
pt: ['paddingTop'],
|
||||||
|
pb: ['paddingBottom'],
|
||||||
|
pl: ['paddingLeft'],
|
||||||
|
pr: ['paddingRight'],
|
||||||
|
px: ['paddingLeft', 'paddingRight'],
|
||||||
|
py: ['paddingTop', 'paddingBottom'],
|
||||||
|
m: ['margin'],
|
||||||
|
mt: ['marginTop'],
|
||||||
|
mb: ['marginBottom'],
|
||||||
|
ml: ['marginLeft'],
|
||||||
|
mr: ['marginRight'],
|
||||||
|
mx: ['marginLeft', 'marginRight'],
|
||||||
|
my: ['marginTop', 'marginBottom']
|
||||||
|
};
|
||||||
|
|
||||||
// header style
|
const sxToStyle = (sx) => {
|
||||||
const headerSX = {
|
if (!sx || typeof sx !== 'object') return undefined;
|
||||||
p: 2.5,
|
const style = {};
|
||||||
'& .MuiCardHeader-action': { m: '0px auto', alignSelf: 'center' }
|
Object.entries(sx).forEach(([key, value]) => {
|
||||||
|
if (SPACING_KEYS[key] && typeof value === 'number') {
|
||||||
|
SPACING_KEYS[key].forEach((cssProp) => {
|
||||||
|
style[cssProp] = value * SPACING_UNIT;
|
||||||
|
});
|
||||||
|
} else if (key === 'bgcolor') {
|
||||||
|
style.backgroundColor = value;
|
||||||
|
} else if (typeof value !== 'object') {
|
||||||
|
style[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return style;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==============================|| CUSTOM - MAIN CARD ||============================== //
|
// ==============================|| CUSTOM - MAIN CARD ||============================== //
|
||||||
|
|
||||||
const MainCard = forwardRef(
|
const MainCard = forwardRef(
|
||||||
(
|
({ boxShadow, children, subheader, content = true, contentSX = {}, darkTitle, divider = true, elevation, secondary, sx = {}, title, ...others }, ref) => {
|
||||||
{
|
const style = sxToStyle(sx);
|
||||||
border = true,
|
const isTransparent = style?.backgroundColor === 'transparent';
|
||||||
boxShadow,
|
|
||||||
children,
|
|
||||||
subheader,
|
|
||||||
content = true,
|
|
||||||
contentSX = {},
|
|
||||||
darkTitle,
|
|
||||||
divider = true,
|
|
||||||
elevation,
|
|
||||||
secondary,
|
|
||||||
shadow,
|
|
||||||
sx = {},
|
|
||||||
title,
|
|
||||||
modal = false,
|
|
||||||
...others
|
|
||||||
},
|
|
||||||
ref
|
|
||||||
) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
boxShadow = theme.palette.mode === ThemeMode.DARK ? boxShadow || true : boxShadow;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
elevation={elevation || 0}
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
variant={isTransparent ? 'transparent' : 'default'}
|
||||||
|
elevation={boxShadow ? 'low' : elevation ? 'med' : 'none'}
|
||||||
|
padding={0}
|
||||||
|
style={style}
|
||||||
{...others}
|
{...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'
|
|
||||||
},
|
|
||||||
...(modal && {
|
|
||||||
position: '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 */}
|
{title && (
|
||||||
{!darkTitle && title && (
|
<VStack gap={0} padding={0}>
|
||||||
<CardHeader
|
<HStack justify="space-between" vAlign="center" padding={2.5}>
|
||||||
sx={headerSX}
|
{darkTitle ? (
|
||||||
titleTypographyProps={{ variant: 'subtitle1' }}
|
<Heading level={4}>{title}</Heading>
|
||||||
title={title}
|
) : typeof title === 'string' ? (
|
||||||
action={secondary}
|
<Text type="strong">{title}</Text>
|
||||||
subheader={subheader}
|
) : (
|
||||||
/>
|
title
|
||||||
)}
|
)}
|
||||||
{darkTitle && title && <CardHeader sx={headerSX} title={<Typography variant="h4">{title}</Typography>} action={secondary} />}
|
{secondary}
|
||||||
|
</HStack>
|
||||||
{/* content & header divider */}
|
{subheader && <Text type="supporting">{subheader}</Text>}
|
||||||
{title && divider && <Divider />}
|
{divider && <Divider />}
|
||||||
|
</VStack>
|
||||||
{/* card content */}
|
)}
|
||||||
{content && <CardContent sx={contentSX}>{children}</CardContent>}
|
{content ? <div style={{ padding: 20, ...sxToStyle(contentSX) }}>{children}</div> : children}
|
||||||
{!content && children}
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
MainCard.propTypes = {
|
MainCard.propTypes = {
|
||||||
border: PropTypes.bool,
|
|
||||||
boxShadow: PropTypes.bool,
|
boxShadow: PropTypes.bool,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
subheader: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
|
subheader: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
|
||||||
content: PropTypes.bool,
|
content: PropTypes.bool,
|
||||||
contentClass: PropTypes.string,
|
|
||||||
contentSX: PropTypes.object,
|
contentSX: PropTypes.object,
|
||||||
darkTitle: PropTypes.bool,
|
darkTitle: PropTypes.bool,
|
||||||
divider: PropTypes.bool,
|
divider: PropTypes.bool,
|
||||||
elevation: PropTypes.number,
|
elevation: PropTypes.number,
|
||||||
secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
||||||
shadow: PropTypes.string,
|
|
||||||
sx: PropTypes.object,
|
sx: PropTypes.object,
|
||||||
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])
|
||||||
modal: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MainCard;
|
export default MainCard;
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { CacheProvider } from '@emotion/react';
|
|
||||||
import createCache from '@emotion/cache';
|
|
||||||
|
|
||||||
// third-party
|
|
||||||
import rtlPlugin from 'stylis-plugin-rtl';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import { ThemeDirection } from 'config';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
|
|
||||||
// ==============================|| RTL LAYOUT ||============================== //
|
|
||||||
|
|
||||||
const RTLLayout = ({ children }) => {
|
|
||||||
const { themeDirection } = useConfig();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.dir = themeDirection;
|
|
||||||
}, [themeDirection]);
|
|
||||||
|
|
||||||
const cacheRtl = createCache({
|
|
||||||
key: themeDirection === ThemeDirection.RTL ? 'rtl' : 'css',
|
|
||||||
prepend: true,
|
|
||||||
stylisPlugins: themeDirection === ThemeDirection.RTL ? [rtlPlugin] : []
|
|
||||||
});
|
|
||||||
|
|
||||||
return <CacheProvider value={cacheRtl}>{children}</CacheProvider>;
|
|
||||||
};
|
|
||||||
|
|
||||||
RTLLayout.propTypes = {
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RTLLayout;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
|
|
||||||
const ScrollX = styled('div')({
|
|
||||||
width: '100%',
|
|
||||||
overflowX: 'auto',
|
|
||||||
display: 'block'
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ScrollX;
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Avatar, ButtonBase, Link, Tooltip } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| CARD - SECONDARY ACTION ||============================== //
|
|
||||||
|
|
||||||
const CardSecondaryAction = ({ title, link, icon }) => {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
CardSecondaryAction.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
link: PropTypes.string,
|
|
||||||
icon: PropTypes.oneOf([PropTypes.node, PropTypes.string])
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardSecondaryAction;
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { Container, Link, Stack, Typography, useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| FOOTER - AUTHENTICATION ||============================== //
|
|
||||||
|
|
||||||
const AuthFooter = () => {
|
|
||||||
const matchDownSM = useMediaQuery((theme) => theme.breakpoints.down('sm'));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container maxWidth="xl">
|
|
||||||
<Stack
|
|
||||||
direction={matchDownSM ? 'column' : 'row'}
|
|
||||||
justifyContent={matchDownSM ? 'center' : 'space-between'}
|
|
||||||
spacing={2}
|
|
||||||
textAlign={matchDownSM ? 'center' : 'inherit'}
|
|
||||||
>
|
|
||||||
<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;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, Grid, Link, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { GlobalOutlined, NodeExpandOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| COMPONENTS - BREADCRUMBS ||============================== //
|
|
||||||
|
|
||||||
const ComponentHeader = ({ title, caption, directory, link }) => (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
|
|
||||||
ComponentHeader.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
caption: PropTypes.string,
|
|
||||||
directory: PropTypes.string,
|
|
||||||
link: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ComponentHeader;
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { sum } from 'lodash';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Fab, Badge } from '@mui/material';
|
|
||||||
|
|
||||||
// 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) => 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;
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
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';
|
|
||||||
|
|
||||||
// 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 }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const prodProfile = image && prodImage(`./${image}`);
|
|
||||||
const [productRating] = useState(rating);
|
|
||||||
const [wishlisted, setWishlisted] = useState(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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProductCard.propTypes = {
|
|
||||||
id: PropTypes.number,
|
|
||||||
color: PropTypes.string,
|
|
||||||
name: PropTypes.string,
|
|
||||||
brand: PropTypes.string,
|
|
||||||
isStock: PropTypes.bool,
|
|
||||||
image: PropTypes.string,
|
|
||||||
description: PropTypes.string,
|
|
||||||
offerPrice: PropTypes.number,
|
|
||||||
salePrice: PropTypes.number,
|
|
||||||
offer: PropTypes.string,
|
|
||||||
rating: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProductCard;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// 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';
|
|
||||||
|
|
||||||
const avatarImage = require.context('assets/images/users', true);
|
|
||||||
|
|
||||||
// ==============================|| PRODUCT DETAILS - REVIEW ||============================== //
|
|
||||||
|
|
||||||
const ProductReview = ({ avatar, date, name, rating, review }) => (
|
|
||||||
<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}
|
|
||||||
</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>
|
|
||||||
);
|
|
||||||
|
|
||||||
ProductReview.propTypes = {
|
|
||||||
avatar: PropTypes.string,
|
|
||||||
date: PropTypes.string,
|
|
||||||
name: PropTypes.string,
|
|
||||||
rating: PropTypes.number,
|
|
||||||
review: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProductReview;
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
// 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;
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, Chip, Grid, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { FallOutlined, RiseOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| STATISTICS - ECOMMERCE CARD ||============================== //
|
|
||||||
|
|
||||||
const AnalyticEcommerce = ({ color = 'primary', title, count, percentage, isLoss, extra }) => (
|
|
||||||
<MainCard contentSX={{ p: 2.25 }}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography variant="h6" color="textSecondary">
|
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
<Grid container alignItems="center">
|
|
||||||
<Grid item>
|
|
||||||
<Typography variant="h4" color="inherit">
|
|
||||||
{count}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
{percentage && (
|
|
||||||
<Grid item>
|
|
||||||
<Chip
|
|
||||||
variant="combined"
|
|
||||||
color={color}
|
|
||||||
icon={
|
|
||||||
<>
|
|
||||||
{!isLoss && <RiseOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
|
|
||||||
{isLoss && <FallOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
label={`${percentage}%`}
|
|
||||||
sx={{ ml: 1.25, pl: 1 }}
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Stack>
|
|
||||||
<Box sx={{ pt: 2.25 }}>
|
|
||||||
<Typography variant="caption" color="textSecondary">
|
|
||||||
You made an extra{' '}
|
|
||||||
<Typography component="span" variant="caption" sx={{ color: `${color || 'primary'}.main` }}>
|
|
||||||
{extra}
|
|
||||||
</Typography>{' '}
|
|
||||||
this year
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</MainCard>
|
|
||||||
);
|
|
||||||
|
|
||||||
AnalyticEcommerce.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
count: PropTypes.string,
|
|
||||||
percentage: PropTypes.number,
|
|
||||||
isLoss: PropTypes.bool,
|
|
||||||
color: PropTypes.string,
|
|
||||||
extra: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AnalyticEcommerce;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, Chip, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { RiseOutlined, FallOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| STATISTICS - ECOMMERCE CARD ||============================== //
|
|
||||||
|
|
||||||
const AnalyticsDataCard = ({ color = 'primary', title, count, percentage, isLoss, children }) => (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
|
|
||||||
AnalyticsDataCard.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
count: PropTypes.string,
|
|
||||||
percentage: PropTypes.number,
|
|
||||||
isLoss: PropTypes.bool,
|
|
||||||
color: PropTypes.string,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AnalyticsDataCard;
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// ===========================|| HOVER SOCIAL CARD ||=========================== //
|
// ===========================|| HOVER SOCIAL CARD ||=========================== //
|
||||||
|
|
||||||
const HoverSocialCard = ({
|
const HoverSocialCard = ({
|
||||||
@@ -16,53 +13,42 @@ const HoverSocialCard = ({
|
|||||||
// const primaryIcon = iconPrimary ? <IconPrimary /> : null;
|
// const primaryIcon = iconPrimary ? <IconPrimary /> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<>
|
||||||
elevation={0}
|
<style>{`
|
||||||
sx={{
|
.hover-social-card:hover svg { opacity: 1; transform: scale(1.1); }
|
||||||
|
`}</style>
|
||||||
|
<div
|
||||||
|
className="hover-social-card"
|
||||||
|
style={{
|
||||||
background: color,
|
background: color,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
'&:hover svg': {
|
borderRadius: 12,
|
||||||
opacity: 1,
|
overflow: 'hidden'
|
||||||
transform: 'scale(1.1)'
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContent>
|
<div style={{ padding: 16 }}>
|
||||||
<Box
|
<div
|
||||||
sx={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 15,
|
right: 15,
|
||||||
top: 25,
|
top: 25,
|
||||||
color: '#fff',
|
color: '#fff'
|
||||||
'& svg': {
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
opacity: 0.5,
|
|
||||||
transition: 'all .3s ease-in-out'
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h2" color="inherit">
|
<span style={{ fontSize: '2.25rem', fontWeight: 600, color: 'inherit' }}>
|
||||||
{/* {percentage.toString()} % */}
|
{/* {percentage.toString()} % */}
|
||||||
{percentage ? `${percentage.toString()} %` : ''}
|
{percentage ? `${percentage.toString()} %` : ''}
|
||||||
</Typography>
|
</span>
|
||||||
{/* {primaryIcon} */}
|
{/* {primaryIcon} */}
|
||||||
</Box>
|
</div>
|
||||||
<Grid container spacing={0}>
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<Grid item xs={12}>
|
<span style={{ fontSize: '1.5rem', fontWeight: 600, color: 'inherit' }}>{secondary}</span>
|
||||||
<Typography variant="h3" color="inherit">
|
<span style={{ fontSize: '0.875rem', fontWeight: 600, color: 'inherit' }}>{primary}</span>
|
||||||
{secondary}
|
</div>
|
||||||
</Typography>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
<Grid item xs={12}>
|
</>
|
||||||
<Typography variant="subtitle2" color="inherit">
|
|
||||||
{primary}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
// material-ui
|
// Brand colors — this app runs in light mode only (see config.js `mode: ThemeMode.LIGHT`),
|
||||||
import { useTheme } from '@mui/material/styles';
|
// so the values previously read from the MUI theme's `primary` palette are inlined as
|
||||||
|
// static brand-red tones matching the BRAND/BRAND_LIGHT convention used across the
|
||||||
|
// Astryx-converted pages.
|
||||||
|
const PRIMARY_MAIN = '#C01227';
|
||||||
|
const PRIMARY_DARK = '#910E1D';
|
||||||
|
const PRIMARY_DARKER = '#6B0A15';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if you want to use image instead of <svg> uncomment following.
|
* if you want to use image instead of <svg> uncomment following.
|
||||||
@@ -13,8 +18,6 @@ import { useTheme } from '@mui/material/styles';
|
|||||||
// ==============================|| LOGO ICON SVG ||============================== //
|
// ==============================|| LOGO ICON SVG ||============================== //
|
||||||
|
|
||||||
const LogoIcon = () => {
|
const LogoIcon = () => {
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
/**
|
/**
|
||||||
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
|
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
|
||||||
@@ -25,7 +28,7 @@ const LogoIcon = () => {
|
|||||||
<svg width="129" height="129" viewBox="0 0 129 129" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="129" height="129" viewBox="0 0 129 129" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<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"
|
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}
|
fill={PRIMARY_DARK}
|
||||||
/>
|
/>
|
||||||
<path
|
<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"
|
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"
|
||||||
@@ -37,16 +40,16 @@ const LogoIcon = () => {
|
|||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M17.5508 46.9276L17.5615 46.9384L27.2357 56.6152L64.4999 93.8767L111.449 46.9276H17.5508Z"
|
d="M17.5508 46.9276L17.5615 46.9384L27.2357 56.6152L64.4999 93.8767L111.449 46.9276H17.5508Z"
|
||||||
fill={theme.palette.primary.main}
|
fill={PRIMARY_MAIN}
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="paint0_linear" x1="25.0225" y1="49.3259" x2="11.4189" y2="62.9295" gradientUnits="userSpaceOnUse">
|
<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 stopColor={PRIMARY_DARKER} />
|
||||||
<stop offset="0.9637" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
<stop offset="0.9637" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="paint1_linear" x1="103.5" y1="49.5" x2="114.5" y2="62" gradientUnits="userSpaceOnUse">
|
<linearGradient id="paint1_linear" x1="103.5" y1="49.5" x2="114.5" y2="62" gradientUnits="userSpaceOnUse">
|
||||||
<stop stopColor={theme.palette.primary.darker} />
|
<stop stopColor={PRIMARY_DARKER} />
|
||||||
<stop offset="1" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
<stop offset="1" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
// material-ui
|
// Brand colors — this app runs in light mode only (see config.js `mode: ThemeMode.LIGHT`),
|
||||||
import { useTheme } from '@mui/material/styles';
|
// so the values previously read from the MUI theme's `primary` palette are inlined as
|
||||||
import { ThemeMode } from 'config';
|
// static brand-red tones matching the BRAND/BRAND_LIGHT convention used across the
|
||||||
|
// Astryx-converted pages.
|
||||||
|
const PRIMARY_MAIN = '#C01227';
|
||||||
|
const PRIMARY_DARK = '#910E1D';
|
||||||
|
const PRIMARY_DARKER = '#6B0A15';
|
||||||
|
const COMMON_WHITE = '#fff';
|
||||||
|
const COMMON_BLACK = '#000';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if you want to use image instead of <svg> uncomment following.
|
* if you want to use image instead of <svg> uncomment following.
|
||||||
@@ -15,7 +21,6 @@ import { ThemeMode } from 'config';
|
|||||||
// ==============================|| LOGO SVG ||============================== //
|
// ==============================|| LOGO SVG ||============================== //
|
||||||
|
|
||||||
const LogoMain = ({ reverse }) => {
|
const LogoMain = ({ reverse }) => {
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
return (
|
||||||
/**
|
/**
|
||||||
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
|
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
|
||||||
@@ -27,7 +32,7 @@ const LogoMain = ({ reverse }) => {
|
|||||||
<svg width="118" height="35" viewBox="0 0 118 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="118" height="35" viewBox="0 0 118 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<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"
|
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}
|
fill={PRIMARY_DARK}
|
||||||
/>
|
/>
|
||||||
<path
|
<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"
|
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"
|
||||||
@@ -39,21 +44,21 @@ const LogoMain = ({ reverse }) => {
|
|||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M6.94549 13.5496L6.9479 13.552L9.12272 15.7275L17.4999 24.1041L28.0544 13.5496H6.94549Z"
|
d="M6.94549 13.5496L6.9479 13.552L9.12272 15.7275L17.4999 24.1041L28.0544 13.5496H6.94549Z"
|
||||||
fill={theme.palette.primary.main}
|
fill={PRIMARY_MAIN}
|
||||||
/>
|
/>
|
||||||
<path
|
<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"
|
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}
|
fill={reverse ? COMMON_WHITE : COMMON_BLACK}
|
||||||
fillOpacity="0.85"
|
fillOpacity="0.85"
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="paint0_linear" x1="8.62526" y1="14.0888" x2="5.56709" y2="17.1469" gradientUnits="userSpaceOnUse">
|
<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 stopColor={PRIMARY_DARKER} />
|
||||||
<stop offset="0.9637" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
<stop offset="0.9637" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="paint1_linear" x1="26.2675" y1="14.1279" x2="28.7404" y2="16.938" gradientUnits="userSpaceOnUse">
|
<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 stopColor={PRIMARY_DARKER} />
|
||||||
<stop offset="1" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
<stop offset="1" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { ButtonBase } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import LogoMain from './LogoMain';
|
import LogoMain from './LogoMain';
|
||||||
import LogoIcon from './LogoIcon';
|
import LogoIcon from './LogoIcon';
|
||||||
@@ -11,16 +8,28 @@ import { APP_DEFAULT_PATH } from 'config';
|
|||||||
|
|
||||||
// ==============================|| MAIN LOGO ||============================== //
|
// ==============================|| MAIN LOGO ||============================== //
|
||||||
|
|
||||||
const LogoSection = ({ reverse, isIcon, sx, to }) => (
|
const LogoSection = ({ reverse, isIcon, style, to }) => (
|
||||||
<ButtonBase disableRipple component={Link} to={!to ? APP_DEFAULT_PATH : to} sx={sx}>
|
<Link
|
||||||
|
to={!to ? APP_DEFAULT_PATH : to}
|
||||||
|
style={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
padding: 0,
|
||||||
|
textDecoration: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
...style
|
||||||
|
}}
|
||||||
|
>
|
||||||
{isIcon ? <LogoIcon /> : <LogoMain reverse={reverse} />}
|
{isIcon ? <LogoIcon /> : <LogoMain reverse={reverse} />}
|
||||||
</ButtonBase>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
LogoSection.propTypes = {
|
LogoSection.propTypes = {
|
||||||
reverse: PropTypes.bool,
|
reverse: PropTypes.bool,
|
||||||
isIcon: PropTypes.bool,
|
isIcon: PropTypes.bool,
|
||||||
sx: PropTypes.object,
|
style: PropTypes.object,
|
||||||
to: PropTypes.string
|
to: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { styled } from '@mui/material/styles';
|
import { Spinner } from '@astryxdesign/core/Spinner';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import nelogo from '../../assets/images/doormile-mark.png';
|
||||||
import nelogo from '../../assets/images/logo-sm.png';
|
|
||||||
|
|
||||||
// Styled Loader Wrapper
|
// ==============================|| Custom Circular Loader with Logo ||============================== //
|
||||||
const LoaderWrapper = styled('div')(() => ({
|
const CircularLoader = () => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: '50%',
|
top: '50%',
|
||||||
left: '50%',
|
left: '50%',
|
||||||
@@ -12,36 +13,19 @@ const LoaderWrapper = styled('div')(() => ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
background: 'rgba(255, 255, 255, 0.7)', // Light overlay effect
|
background: 'rgba(255, 255, 255, 0.7)',
|
||||||
padding: '20px',
|
padding: 20,
|
||||||
borderRadius: '10px' // Soft rounded border
|
borderRadius: 10
|
||||||
}));
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ transform: 'scale(2.2)' }}>
|
||||||
|
<Spinner size="xl" />
|
||||||
|
</div>
|
||||||
|
|
||||||
// Styled Logo Wrapper
|
<div style={{ position: 'absolute', width: 40, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
const LogoWrapper = styled('div')(() => ({
|
|
||||||
position: 'absolute',
|
|
||||||
width: '40px', // Adjust based on your logo size
|
|
||||||
height: '40px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ==============================|| Custom Circular Loader with Logo ||============================== //
|
|
||||||
const CircularLoader = () => (
|
|
||||||
<LoaderWrapper>
|
|
||||||
{/* Circular Loader */}
|
|
||||||
<CircularProgress
|
|
||||||
color="primary"
|
|
||||||
size={80} // Increase size
|
|
||||||
thickness={6} // Thicker border
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Logo Positioned at the Center */}
|
|
||||||
<LogoWrapper>
|
|
||||||
<img src={nelogo} alt="Logo" style={{ width: '100%', height: '100%' }} />
|
<img src={nelogo} alt="Logo" style={{ width: '100%', height: '100%' }} />
|
||||||
</LogoWrapper>
|
</div>
|
||||||
</LoaderWrapper>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default CircularLoader;
|
export default CircularLoader;
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { Dialog, DialogTitle, DialogContent, Typography, Stack, Button, DialogActions, Paper } from '@mui/material';
|
import { useState } from 'react';
|
||||||
|
import { Dialog } from '@astryxdesign/core/Dialog';
|
||||||
import { DateRangePicker } from 'mui-daterange-picker';
|
import { DialogHeader } from '@astryxdesign/core/Dialog';
|
||||||
|
import { Layout, LayoutFooter } from '@astryxdesign/core/Layout';
|
||||||
|
import { Calendar } from '@astryxdesign/core/Calendar';
|
||||||
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
|
import { HStack } from '@astryxdesign/core/HStack';
|
||||||
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { addDays, addWeeks, addMonths, startOfWeek, endOfWeek, startOfMonth, endOfMonth } from 'date-fns';
|
import { addDays, addWeeks, addMonths, startOfWeek, endOfWeek, startOfMonth, endOfMonth } from 'date-fns';
|
||||||
|
|
||||||
|
const iso = (d) => dayjs(d).format('YYYY-MM-DD');
|
||||||
|
|
||||||
export default function DateFilterDialog({ open, onClose, onApply }) {
|
export default function DateFilterDialog({ open, onClose, onApply }) {
|
||||||
|
const [range, setRange] = useState(null);
|
||||||
|
|
||||||
const definedRanges = [
|
const definedRanges = [
|
||||||
{ label: 'Today', startDate: new Date(), endDate: new Date() },
|
{ label: 'Today', startDate: new Date(), endDate: new Date() },
|
||||||
{ label: 'Yesterday', startDate: addDays(new Date(), -1), endDate: addDays(new Date(), -1) },
|
{ label: 'Yesterday', startDate: addDays(new Date(), -1), endDate: addDays(new Date(), -1) },
|
||||||
@@ -17,37 +25,42 @@ export default function DateFilterDialog({ open, onClose, onApply }) {
|
|||||||
{ label: 'Last Month', startDate: startOfMonth(addMonths(new Date(), -1)), endDate: endOfMonth(addMonths(new Date(), -1)) }
|
{ label: 'Last Month', startDate: startOfMonth(addMonths(new Date(), -1)), endDate: endOfMonth(addMonths(new Date(), -1)) }
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleSelect = (range) => {
|
const apply = (startDate, endDate, label = '') => {
|
||||||
if (!range?.startDate || !range?.endDate) return;
|
onApply({ startDate: iso(startDate), endDate: iso(endDate), label });
|
||||||
onApply({
|
|
||||||
startDate: dayjs(range.startDate).format('YYYY-MM-DD'),
|
|
||||||
endDate: dayjs(range.endDate).format('YYYY-MM-DD'),
|
|
||||||
label: range.label || ''
|
|
||||||
});
|
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenChange = (next) => !next && onClose();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={() => onclose()} fullWidth maxWidth="sm">
|
<Dialog isOpen={open} onOpenChange={handleOpenChange} width={560}>
|
||||||
<DialogTitle disableTypography>
|
<Layout
|
||||||
<Stack display="flex" flexDirection="row" justifyContent="space-between" alignItems="center" sx={{ width: '100%' }}>
|
header={<DialogHeader title="Select Date Range" onOpenChange={handleOpenChange} />}
|
||||||
<Typography variant="h4">Select Date Range</Typography>
|
content={
|
||||||
</Stack>
|
<VStack gap={3} padding={4}>
|
||||||
</DialogTitle>
|
<HStack gap={1} wrap="wrap">
|
||||||
<DialogContent
|
{definedRanges.map((r) => (
|
||||||
sx={{
|
<Button key={r.label} label={r.label} variant="secondary" size="sm" onClick={() => apply(r.startDate, r.endDate, r.label)} />
|
||||||
'& .MuiPaper-root': {
|
))}
|
||||||
boxShadow: 'none !important'
|
</HStack>
|
||||||
|
|
||||||
|
<Calendar mode="range" numberOfMonths={2} value={range || undefined} onChange={(v) => setRange(v)} />
|
||||||
|
</VStack>
|
||||||
}
|
}
|
||||||
}}
|
footer={
|
||||||
>
|
<LayoutFooter>
|
||||||
<DateRangePicker open={open} toggle={onClose} definedRanges={definedRanges} onChange={handleSelect} closeOnClickOutside={false} />
|
<HStack justify="end" gap={2}>
|
||||||
</DialogContent>
|
<Button label="Close" variant="secondary" onClick={onClose} />
|
||||||
<DialogActions>
|
<Button
|
||||||
<Button variant="contained" onClick={onClose} sx={{ cursor: 'pointer' }}>
|
label="Apply"
|
||||||
Close
|
variant="primary"
|
||||||
</Button>
|
isDisabled={!range?.start || !range?.end}
|
||||||
</DialogActions>
|
onClick={() => range?.start && range?.end && apply(range.start, range.end, 'Custom')}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</LayoutFooter>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
// LoaderWithImage.jsx
|
|
||||||
import React from 'react';
|
|
||||||
import { Box, CircularProgress } from '@mui/material';
|
|
||||||
import nelogo from '../../assets/images/logo-sm.png';
|
|
||||||
|
|
||||||
export default function LoaderWithImage({ size = 70, imgSize = 40, alt = 'loader' }) {
|
|
||||||
return (
|
|
||||||
<Box position="relative" display="inline-flex" justifyContent="center" alignItems="center">
|
|
||||||
<CircularProgress size={size} />
|
|
||||||
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
display="flex"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
sx={{
|
|
||||||
width: imgSize,
|
|
||||||
height: imgSize
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={nelogo}
|
|
||||||
alt={alt}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
borderRadius: '50%',
|
|
||||||
objectFit: 'contain'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Box, Paper, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// MobileCard — shared primitives that turn a desktop data-table row into an
|
// MobileCard — shared primitives that turn a desktop data-table row into an
|
||||||
@@ -18,57 +17,60 @@ const PRIMARY_TEXT = '#0f172a';
|
|||||||
// Vertical list wrapper — drop-in replacement for <TableContainer>/<TableBody>
|
// Vertical list wrapper — drop-in replacement for <TableContainer>/<TableBody>
|
||||||
// on mobile. `scroll` makes it an internal scroll region (matches the table's
|
// on mobile. `scroll` makes it an internal scroll region (matches the table's
|
||||||
// maxHeight behaviour); omit it to let the page scroll naturally.
|
// maxHeight behaviour); omit it to let the page scroll naturally.
|
||||||
export const MobileCardList = ({ children, scroll = false, onScroll, sx, ...rest }) => (
|
export const MobileCardList = ({ children, scroll = false, onScroll, style, ...rest }) => (
|
||||||
<Stack
|
<div
|
||||||
spacing={1.25}
|
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
sx={{
|
style={{
|
||||||
p: 1.5,
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 10,
|
||||||
|
padding: 12,
|
||||||
...(scroll && { maxHeight: 'calc(100vh - 220px)', overflowY: 'auto', overflowX: 'hidden' }),
|
...(scroll && { maxHeight: 'calc(100vh - 220px)', overflowY: 'auto', overflowX: 'hidden' }),
|
||||||
...sx
|
...style
|
||||||
}}
|
}}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Stack>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
MobileCardList.propTypes = {
|
MobileCardList.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
scroll: PropTypes.bool,
|
scroll: PropTypes.bool,
|
||||||
onScroll: PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
sx: PropTypes.object
|
style: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
// Card shell — coloured accent rail on the left, a header slot (status badge /
|
// Card shell — coloured accent rail on the left, a header slot (status badge /
|
||||||
// title / action buttons), then any field grid / collapse content as children.
|
// title / action buttons), then any field grid / collapse content as children.
|
||||||
export const MobileCard = ({ accent = '#662582', header, footer, selected = false, onClick, children, sx }) => (
|
export const MobileCard = ({ accent = '#C01227', header, footer, selected = false, onClick, children, style }) => (
|
||||||
<Paper
|
<div
|
||||||
elevation={0}
|
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
sx={{
|
role={onClick ? 'button' : undefined}
|
||||||
|
tabIndex={onClick ? 0 : undefined}
|
||||||
|
onKeyDown={onClick ? (e) => (e.key === 'Enter' || e.key === ' ') && onClick(e) : undefined}
|
||||||
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
// Cards live inside a flex-column list; without this, a scroll-capped
|
// Cards live inside a flex-column list; without this, a scroll-capped
|
||||||
// list (maxHeight) would SHRINK each card to a sliver (flex-shrink:1)
|
// list (maxHeight) would SHRINK each card to a sliver (flex-shrink:1)
|
||||||
// and clip its content instead of scrolling. Keep natural height.
|
// and clip its content instead of scrolling. Keep natural height.
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
borderRadius: 2.5,
|
borderRadius: 10,
|
||||||
border: '1px solid',
|
border: `1px solid ${selected ? accent : BORDER}`,
|
||||||
borderColor: selected ? accent : BORDER,
|
|
||||||
background: selected ? `${accent}0a` : '#fff',
|
background: selected ? `${accent}0a` : '#fff',
|
||||||
boxShadow: '0 4px 14px rgba(15,23,42,0.05)',
|
boxShadow: '0 4px 14px rgba(15,23,42,0.05)',
|
||||||
transition: 'border-color 0.15s, box-shadow 0.15s',
|
transition: 'border-color 0.15s, box-shadow 0.15s',
|
||||||
...sx
|
...style
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: 3, bgcolor: accent }} />
|
<div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: 3, backgroundColor: accent }} />
|
||||||
<Box sx={{ p: 1.5, pl: 2 }}>
|
<div style={{ padding: '12px 12px 12px 16px' }}>
|
||||||
{header}
|
{header}
|
||||||
{children}
|
{children}
|
||||||
{footer}
|
{footer}
|
||||||
</Box>
|
</div>
|
||||||
</Paper>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
MobileCard.propTypes = {
|
MobileCard.propTypes = {
|
||||||
@@ -78,37 +80,37 @@ MobileCard.propTypes = {
|
|||||||
selected: PropTypes.bool,
|
selected: PropTypes.bool,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
sx: PropTypes.object
|
style: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
// Grid wrapper for MobileField cells. Two columns by default; pass `columns`
|
// Grid wrapper for MobileField cells. Two columns by default; pass `columns`
|
||||||
// to change. Keeps every card's body alignment identical.
|
// to change. Keeps every card's body alignment identical.
|
||||||
export const MobileFieldGrid = ({ children, columns = 2, sx }) => (
|
export const MobileFieldGrid = ({ children, columns = 2, style }) => (
|
||||||
<Box
|
<div
|
||||||
sx={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
||||||
gap: 1,
|
gap: 8,
|
||||||
mt: 1.25,
|
marginTop: 10,
|
||||||
...sx
|
...style
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
MobileFieldGrid.propTypes = {
|
MobileFieldGrid.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
columns: PropTypes.number,
|
columns: PropTypes.number,
|
||||||
sx: PropTypes.object
|
style: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
// A single label/value cell. `full` makes it span the whole row; `value` can be
|
// A single label/value cell. `full` makes it span the whole row; `value` can be
|
||||||
// a string/number or any node (chip, stack, etc.).
|
// a string/number or any node (chip, stack, etc.).
|
||||||
export const MobileField = ({ label, value, children, full = false, align = 'left' }) => (
|
export const MobileField = ({ label, value, children, full = false, align = 'left' }) => (
|
||||||
<Box sx={{ gridColumn: full ? '1 / -1' : 'auto', minWidth: 0, textAlign: align }}>
|
<div style={{ gridColumn: full ? '1 / -1' : 'auto', minWidth: 0, textAlign: align }}>
|
||||||
<Typography
|
<div
|
||||||
sx={{
|
style={{
|
||||||
fontSize: 9.5,
|
fontSize: 9.5,
|
||||||
fontWeight: 800,
|
fontWeight: 800,
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
@@ -118,17 +120,17 @@ export const MobileField = ({ label, value, children, full = false, align = 'lef
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</div>
|
||||||
<Box sx={{ mt: 0.25, minWidth: 0 }}>
|
<div style={{ marginTop: 2, minWidth: 0 }}>
|
||||||
{children !== undefined ? (
|
{children !== undefined ? (
|
||||||
children
|
children
|
||||||
) : (
|
) : (
|
||||||
<Typography sx={{ fontSize: 13, fontWeight: 600, color: PRIMARY_TEXT }} noWrap>
|
<div style={{ fontSize: 13, fontWeight: 600, color: PRIMARY_TEXT, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{value ?? '—'}
|
{value ?? '—'}
|
||||||
</Typography>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</div>
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
MobileField.propTypes = {
|
MobileField.propTypes = {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
// SearchBar.jsx
|
// SearchBar.jsx
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { FormControl, OutlinedInput, InputAdornment, IconButton, useTheme } from '@mui/material';
|
import { TextInput } from '@astryxdesign/core/TextInput';
|
||||||
import SearchOutlined from '@mui/icons-material/SearchOutlined';
|
import { MdSearch } from 'react-icons/md';
|
||||||
import ClearIcon from '@mui/icons-material/Clear';
|
|
||||||
|
|
||||||
const SearchBar = ({ value, onChange, sx, placeholder = 'Search (Ctrl + K)' }) => {
|
const SearchBar = ({ value, onChange, style, placeholder = 'Search (Ctrl + K)' }) => {
|
||||||
const theme = useTheme();
|
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
/* ============================================= || handleKeyPress (CTRL + K) and ESC ||============================================= */
|
/* ============================================= || handleKeyPress (CTRL + K) and ESC ||============================================= */
|
||||||
@@ -28,30 +26,18 @@ const SearchBar = ({ value, onChange, sx, placeholder = 'Search (Ctrl + K)' }) =
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl fullWidth sx={{}}>
|
<TextInput
|
||||||
<OutlinedInput
|
ref={inputRef}
|
||||||
inputRef={inputRef}
|
label={placeholder}
|
||||||
|
isLabelHidden
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
sx={{
|
startIcon={<MdSearch />}
|
||||||
borderRadius: 0,
|
hasClear
|
||||||
...sx
|
|
||||||
}}
|
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={(v) => onChange({ target: { value: v } })}
|
||||||
autoComplete="off"
|
size="lg"
|
||||||
size="large"
|
style={style}
|
||||||
startAdornment={
|
|
||||||
<InputAdornment position="start" sx={{ mr: -0.5, color: theme.palette.secondary.main }}>
|
|
||||||
<SearchOutlined />
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
endAdornment={
|
|
||||||
<IconButton sx={{ visibility: value ? 'visible' : 'hidden' }} onClick={() => onChange({ target: { value: '' } })}>
|
|
||||||
<ClearIcon style={{ fontSize: 'large', color: theme.palette.primary.main }} />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { MdWifiOff, MdWifi } from 'react-icons/md';
|
||||||
import WifiOffIcon from '@mui/icons-material/WifiOff';
|
|
||||||
import WifiIcon from '@mui/icons-material/Wifi';
|
|
||||||
|
|
||||||
const NOTIFICATION_STYLE = {
|
const NOTIFICATION_STYLE = {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
@@ -21,7 +19,6 @@ const NOTIFICATION_STYLE = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InternetStatus = () => {
|
const InternetStatus = () => {
|
||||||
const theme = useTheme();
|
|
||||||
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
||||||
const [showBackOnline, setShowBackOnline] = useState(false);
|
const [showBackOnline, setShowBackOnline] = useState(false);
|
||||||
const wasOffline = useRef(!navigator.onLine);
|
const wasOffline = useRef(!navigator.onLine);
|
||||||
@@ -64,13 +61,13 @@ const InternetStatus = () => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
...NOTIFICATION_STYLE,
|
...NOTIFICATION_STYLE,
|
||||||
backgroundColor: theme.palette.error.main,
|
backgroundColor: 'var(--color-error, #d32f2f)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '10px'
|
gap: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WifiOffIcon style={{ color: '#fff' }} />
|
<MdWifiOff style={{ color: '#fff' }} />
|
||||||
No Internet Connection
|
No Internet Connection
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -79,13 +76,13 @@ const InternetStatus = () => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
...NOTIFICATION_STYLE,
|
...NOTIFICATION_STYLE,
|
||||||
backgroundColor: theme.palette.success.main,
|
backgroundColor: 'var(--color-success, #2e7d32)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '10px'
|
gap: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WifiIcon style={{ color: '#fff' }} />
|
<MdWifi style={{ color: '#fff' }} />
|
||||||
Back Online
|
Back Online
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
38
src/components/third-party/Notistack.js
vendored
38
src/components/third-party/Notistack.js
vendored
@@ -1,8 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
//material-ui
|
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
|
|
||||||
// third-party
|
// third-party
|
||||||
import { SnackbarProvider } from 'notistack';
|
import { SnackbarProvider } from 'notistack';
|
||||||
|
|
||||||
@@ -12,24 +9,16 @@ import { useSelector } from 'store';
|
|||||||
// assets
|
// assets
|
||||||
import { CheckCircleOutlined, CloseCircleOutlined, InfoCircleOutlined, WarningOutlined } from '@ant-design/icons';
|
import { CheckCircleOutlined, CloseCircleOutlined, InfoCircleOutlined, WarningOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
// custom styles
|
// Semantic notification colors — static (this app runs light-mode only), matching the
|
||||||
const StyledSnackbarProvider = styled(SnackbarProvider)(({ theme }) => ({
|
// brand palette used across the Astryx-converted pages (BRAND red for the default/
|
||||||
'&.notistack-MuiContent-default': {
|
// primary notistack variant, standard semantic tones for error/success/info/warning).
|
||||||
backgroundColor: theme.palette.primary.main
|
const notistackColors = `
|
||||||
},
|
.notistack-MuiContent-default { background-color: #C01227 !important; }
|
||||||
'&.notistack-MuiContent-error': {
|
.notistack-MuiContent-error { background-color: #ef4444 !important; }
|
||||||
backgroundColor: theme.palette.error.main
|
.notistack-MuiContent-success { background-color: #10b981 !important; }
|
||||||
},
|
.notistack-MuiContent-info { background-color: #3b82f6 !important; }
|
||||||
'&.notistack-MuiContent-success': {
|
.notistack-MuiContent-warning { background-color: #f59e0b !important; }
|
||||||
backgroundColor: theme.palette.success.main
|
`;
|
||||||
},
|
|
||||||
'&.notistack-MuiContent-info': {
|
|
||||||
backgroundColor: theme.palette.info.main
|
|
||||||
},
|
|
||||||
'&.notistack-MuiContent-warning': {
|
|
||||||
backgroundColor: theme.palette.warning.main
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ===========================|| SNACKBAR - NOTISTACK ||=========================== //
|
// ===========================|| SNACKBAR - NOTISTACK ||=========================== //
|
||||||
|
|
||||||
@@ -38,7 +27,9 @@ const Notistack = ({ children }) => {
|
|||||||
const iconSX = { marginRight: 8, fontSize: '1.15rem' };
|
const iconSX = { marginRight: 8, fontSize: '1.15rem' };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledSnackbarProvider
|
<>
|
||||||
|
<style>{notistackColors}</style>
|
||||||
|
<SnackbarProvider
|
||||||
maxSnack={snackbar.maxStack}
|
maxSnack={snackbar.maxStack}
|
||||||
dense={snackbar.dense}
|
dense={snackbar.dense}
|
||||||
iconVariant={
|
iconVariant={
|
||||||
@@ -54,7 +45,8 @@ const Notistack = ({ children }) => {
|
|||||||
hideIconVariant={snackbar.iconVariant === 'hide' ? true : false}
|
hideIconVariant={snackbar.iconVariant === 'hide' ? true : false}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</StyledSnackbarProvider>
|
</SnackbarProvider>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
588
src/components/third-party/ReactTable.js
vendored
588
src/components/third-party/ReactTable.js
vendored
@@ -1,514 +1,43 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
import { styled, useTheme } from '@mui/material/styles';
|
import { Tooltip } from '@astryxdesign/core/Tooltip';
|
||||||
import {
|
import { Spinner } from '@astryxdesign/core/Spinner';
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Chip,
|
|
||||||
CircularProgress,
|
|
||||||
FormControl,
|
|
||||||
Grid,
|
|
||||||
ListItemText,
|
|
||||||
MenuItem,
|
|
||||||
OutlinedInput,
|
|
||||||
Pagination,
|
|
||||||
Select,
|
|
||||||
Stack,
|
|
||||||
TableCell,
|
|
||||||
TableRow,
|
|
||||||
TextField,
|
|
||||||
Tooltip,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// project-import
|
|
||||||
import { ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// third-party
|
// third-party
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
import { getEmptyImage } from 'react-dnd-html5-backend';
|
|
||||||
import { useDrop, useDrag, useDragLayer } from 'react-dnd';
|
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import { CaretUpOutlined, CaretDownOutlined, DragOutlined, CheckOutlined, DownloadOutlined } from '@ant-design/icons';
|
import { DownloadOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
// ==============================|| HEADER HEADER ||============================== //
|
// ============================================================================
|
||||||
|
// This file previously held a large grab-bag of react-table + MUI + react-dnd
|
||||||
export const HeaderSort = ({ column, sort }) => {
|
// helper components (HeaderSort, TablePagination, IndeterminateCheckbox,
|
||||||
const theme = useTheme();
|
// TableRowSelection, DraggableHeader/DragPreview/DraggableRow, HidingSelect,
|
||||||
return (
|
// SortingSelect, EmptyTable). None of them had any remaining consumers anywhere
|
||||||
<Stack direction="row" spacing={1} alignItems="center">
|
// in the app (verified via grep across src/) — they were leftover MUI/react-table
|
||||||
<Box sx={{ width: 'max-content' }}>{column.render('Header')}</Box>
|
// scaffolding from the original admin template, never wired into any of the
|
||||||
{!column.disableSortBy && (
|
// plain-HTML/Astryx tables actually used by this app's pages. Removed as part of
|
||||||
<Stack sx={{ color: 'secondary.light' }} {...(sort && { ...column.getHeaderProps(column.getSortByToggleProps()) })}>
|
// the MUI removal pass rather than converting dead code. `CSVExport` is the one
|
||||||
<CaretUpOutlined
|
// export with a real consumer (reports/ordersDetails.js) and is kept, rebuilt on
|
||||||
style={{
|
// Astryx primitives.
|
||||||
fontSize: '0.625rem',
|
// ============================================================================
|
||||||
color: column.isSorted && !column.isSortedDesc ? theme.palette.text.secondary : 'inherit'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CaretDownOutlined
|
|
||||||
style={{
|
|
||||||
fontSize: '0.625rem',
|
|
||||||
marginTop: -2,
|
|
||||||
color: column.isSortedDesc ? theme.palette.text.secondary : 'inherit'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
HeaderSort.propTypes = {
|
|
||||||
column: PropTypes.any,
|
|
||||||
sort: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| TABLE PAGINATION ||============================== //
|
|
||||||
|
|
||||||
export const TablePagination = ({ gotoPage, rows, setPageSize, pageSize, pageIndex }) => {
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpen = () => {
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChangePagination = (event, value) => {
|
|
||||||
gotoPage(value - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (event) => {
|
|
||||||
setPageSize(+event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Grid container alignItems="center" justifyContent="space-between" sx={{ width: 'auto' }}>
|
|
||||||
<Grid item>
|
|
||||||
<Stack direction="row" spacing={1} alignItems="center">
|
|
||||||
<Stack direction="row" spacing={1} alignItems="center">
|
|
||||||
<Typography variant="caption" color="secondary">
|
|
||||||
Row per page
|
|
||||||
</Typography>
|
|
||||||
<FormControl sx={{ m: 1 }}>
|
|
||||||
<Select
|
|
||||||
id="demo-controlled-open-select"
|
|
||||||
open={open}
|
|
||||||
onClose={handleClose}
|
|
||||||
onOpen={handleOpen}
|
|
||||||
value={pageSize}
|
|
||||||
onChange={handleChange}
|
|
||||||
size="small"
|
|
||||||
sx={{ '& .MuiSelect-select': { py: 0.75, px: 1.25 } }}
|
|
||||||
>
|
|
||||||
<MenuItem value={5}>5</MenuItem>
|
|
||||||
<MenuItem value={10}>10</MenuItem>
|
|
||||||
<MenuItem value={25}>25</MenuItem>
|
|
||||||
<MenuItem value={50}>50</MenuItem>
|
|
||||||
<MenuItem value={100}>100</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Stack>
|
|
||||||
<Typography variant="caption" color="secondary">
|
|
||||||
Go to
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
size="small"
|
|
||||||
type="number"
|
|
||||||
value={pageIndex + 1}
|
|
||||||
onChange={(e) => {
|
|
||||||
const page = e.target.value ? Number(e.target.value) : 0;
|
|
||||||
gotoPage(page - 1);
|
|
||||||
}}
|
|
||||||
sx={{ '& .MuiOutlinedInput-input': { py: 0.75, px: 1.25, width: 36 } }}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item sx={{ mt: { xs: 2, sm: 0 } }}>
|
|
||||||
<Pagination
|
|
||||||
count={Math.ceil(rows.length / pageSize)}
|
|
||||||
page={pageIndex + 1}
|
|
||||||
onChange={handleChangePagination}
|
|
||||||
color="primary"
|
|
||||||
variant="combined"
|
|
||||||
showFirstButton
|
|
||||||
showLastButton
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
TablePagination.propTypes = {
|
|
||||||
gotoPage: PropTypes.func,
|
|
||||||
setPageSize: PropTypes.func,
|
|
||||||
pageIndex: PropTypes.number,
|
|
||||||
pageSize: PropTypes.number,
|
|
||||||
rows: PropTypes.array
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| SELECTION - PREVIEW ||============================== //
|
|
||||||
|
|
||||||
export const IndeterminateCheckbox = forwardRef(({ indeterminate, ...rest }, ref) => {
|
|
||||||
const defaultRef = useRef();
|
|
||||||
const resolvedRef = ref || defaultRef;
|
|
||||||
|
|
||||||
return <Checkbox indeterminate={indeterminate} ref={resolvedRef} {...rest} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
IndeterminateCheckbox.propTypes = {
|
|
||||||
indeterminate: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TableRowSelection = ({ selected }) => (
|
|
||||||
<>
|
|
||||||
{selected > 0 && (
|
|
||||||
<Chip
|
|
||||||
size="small"
|
|
||||||
label={`${selected} row(s) selected`}
|
|
||||||
color="secondary"
|
|
||||||
variant="light"
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: -1,
|
|
||||||
top: -1,
|
|
||||||
borderRadius: '0 4px 0 4px'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
TableRowSelection.propTypes = {
|
|
||||||
selected: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| DRAG & DROP - DRAGGABLE HEADR ||============================== //
|
|
||||||
|
|
||||||
export const DraggableHeader = ({ children, column, index, reorder }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const ref = useRef();
|
|
||||||
const { id, Header } = column;
|
|
||||||
|
|
||||||
const DND_ITEM_TYPE = 'column';
|
|
||||||
|
|
||||||
const [{ isOverCurrent }, drop] = useDrop({
|
|
||||||
accept: DND_ITEM_TYPE,
|
|
||||||
drop: (item) => {
|
|
||||||
reorder(item, index);
|
|
||||||
},
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isOver: monitor.isOver(),
|
|
||||||
isOverCurrent: monitor.isOver({ shallow: true })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const [{ isDragging }, drag, preview] = useDrag({
|
|
||||||
type: DND_ITEM_TYPE,
|
|
||||||
item: () => ({
|
|
||||||
id,
|
|
||||||
index,
|
|
||||||
header: Header
|
|
||||||
}),
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isDragging: monitor.isDragging()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
preview(getEmptyImage(), { captureDraggingState: true });
|
|
||||||
}, [preview]);
|
|
||||||
|
|
||||||
drag(drop(ref));
|
|
||||||
|
|
||||||
let borderColor = theme.palette.text.primary;
|
|
||||||
if (isOverCurrent) {
|
|
||||||
borderColor = theme.palette.primary.main;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ cursor: 'move', opacity: isDragging ? 0.5 : 1, color: borderColor }} ref={ref} {...isDragging}>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DraggableHeader.propTypes = {
|
|
||||||
column: PropTypes.any,
|
|
||||||
sort: PropTypes.bool,
|
|
||||||
reorder: PropTypes.func,
|
|
||||||
index: PropTypes.number,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| DRAG & DROP - DRAG PREVIEW ||============================== //
|
|
||||||
|
|
||||||
const DragHeader = styled('div')(({ theme, x, y }) => ({
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
position: 'fixed',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
left: 12,
|
|
||||||
top: 24,
|
|
||||||
transform: `translate(${x}px, ${y}px)`,
|
|
||||||
opacity: 0.6
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const DragPreview = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { isDragging, item, currentOffset } = useDragLayer((monitor) => ({
|
|
||||||
item: monitor.getItem(),
|
|
||||||
itemType: monitor.getItemType(),
|
|
||||||
initialOffset: monitor.getInitialSourceClientOffset(),
|
|
||||||
currentOffset: monitor.getSourceClientOffset(),
|
|
||||||
isDragging: monitor.isDragging()
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { x, y } = currentOffset || {};
|
|
||||||
|
|
||||||
return isDragging ? (
|
|
||||||
<DragHeader theme={theme} x={x} y={y}>
|
|
||||||
{item.header && (
|
|
||||||
<Stack direction="row" spacing={1} alignItems="center">
|
|
||||||
<DragOutlined style={{ fontSize: '1rem' }} />
|
|
||||||
<Typography>{item.header}</Typography>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</DragHeader>
|
|
||||||
) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| DRAG & DROP - DRAGGABLE ROW ||============================== //
|
|
||||||
|
|
||||||
export const DraggableRow = ({ index, moveRow, children }) => {
|
|
||||||
const DND_ITEM_TYPE = 'row';
|
|
||||||
|
|
||||||
const dropRef = useRef(null);
|
|
||||||
const dragRef = useRef(null);
|
|
||||||
|
|
||||||
const [, drop] = useDrop({
|
|
||||||
accept: DND_ITEM_TYPE,
|
|
||||||
hover(item, monitor) {
|
|
||||||
if (!dropRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const dragIndex = item.index;
|
|
||||||
const hoverIndex = index;
|
|
||||||
if (dragIndex === hoverIndex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hoverBoundingRect = dropRef.current.getBoundingClientRect();
|
|
||||||
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
||||||
const clientOffset = monitor.getClientOffset();
|
|
||||||
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
|
||||||
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveRow(dragIndex, hoverIndex);
|
|
||||||
item.index = hoverIndex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [{ isDragging }, drag, preview] = useDrag({
|
|
||||||
type: DND_ITEM_TYPE,
|
|
||||||
item: { index },
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isDragging: monitor.isDragging()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const opacity = isDragging ? 0 : 1;
|
|
||||||
|
|
||||||
preview(drop(dropRef));
|
|
||||||
drag(dragRef);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow ref={dropRef} style={{ opacity, backgroundColor: isDragging ? 'red' : 'inherit' }}>
|
|
||||||
<TableCell ref={dragRef} sx={{ cursor: 'pointer', textAlign: 'center' }}>
|
|
||||||
<DragOutlined />
|
|
||||||
</TableCell>
|
|
||||||
{children}
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DraggableRow.propTypes = {
|
|
||||||
moveRow: PropTypes.func,
|
|
||||||
index: PropTypes.number,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| COLUMN HIDING - SELECT ||============================== //
|
|
||||||
|
|
||||||
const ITEM_HEIGHT = 48;
|
|
||||||
const ITEM_PADDING_TOP = 8;
|
|
||||||
const MenuProps = {
|
|
||||||
PaperProps: {
|
|
||||||
style: {
|
|
||||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
|
||||||
width: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const HidingSelect = ({ hiddenColumns, setHiddenColumns, allColumns }) => {
|
|
||||||
const handleChange = (event) => {
|
|
||||||
const {
|
|
||||||
target: { value }
|
|
||||||
} = event;
|
|
||||||
|
|
||||||
setHiddenColumns(typeof value === 'string' ? value.split(',') : value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
let visible = allColumns.filter((c) => !hiddenColumns.includes(c.id)).length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormControl sx={{ width: 200 }}>
|
|
||||||
<Select
|
|
||||||
id="column-hiding"
|
|
||||||
multiple
|
|
||||||
displayEmpty
|
|
||||||
value={hiddenColumns}
|
|
||||||
onChange={handleChange}
|
|
||||||
input={<OutlinedInput id="select-column-hiding" placeholder="select column" />}
|
|
||||||
renderValue={(selected) => {
|
|
||||||
if (selected.length === 0) {
|
|
||||||
return <Typography variant="subtitle1">All columns visible</Typography>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected.length > 0 && selected.length === allColumns.length) {
|
|
||||||
return <Typography variant="subtitle1">All columns visible</Typography>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Typography variant="subtitle1">{visible} column(s) visible</Typography>;
|
|
||||||
}}
|
|
||||||
MenuProps={MenuProps}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
{allColumns.map((column) => {
|
|
||||||
let ToggleChecked = column.id === '#' ? true : hiddenColumns.indexOf(column.id) > -1 ? false : true;
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
key={column.id}
|
|
||||||
value={column.id}
|
|
||||||
sx={{ bgcolor: 'success.lighter', '&.Mui-selected': { bgcolor: 'background.paper' } }}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={ToggleChecked}
|
|
||||||
color="success"
|
|
||||||
checkedIcon={
|
|
||||||
<Box
|
|
||||||
className="icon"
|
|
||||||
sx={{
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: 'inherit',
|
|
||||||
borderRadius: 0.25,
|
|
||||||
position: 'relative',
|
|
||||||
backgroundColor: theme.palette.success.main
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CheckOutlined className="filled" style={{ position: 'absolute', color: theme.palette.common.white }} />
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ListItemText primary={typeof column.Header === 'string' ? column.Header : column?.title} />
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
HidingSelect.propTypes = {
|
|
||||||
setHiddenColumns: PropTypes.func,
|
|
||||||
hiddenColumns: PropTypes.array,
|
|
||||||
allColumns: PropTypes.array
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| COLUMN SORTING - SELECT ||============================== //
|
|
||||||
|
|
||||||
export const SortingSelect = ({ sortBy, setSortBy, allColumns }) => {
|
|
||||||
const [sort, setSort] = useState(sortBy);
|
|
||||||
|
|
||||||
const handleChange = (event) => {
|
|
||||||
const {
|
|
||||||
target: { value }
|
|
||||||
} = event;
|
|
||||||
setSort(value);
|
|
||||||
setSortBy([{ id: value, desc: false }]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormControl sx={{ width: 200 }}>
|
|
||||||
<Select
|
|
||||||
id="column-hiding"
|
|
||||||
displayEmpty
|
|
||||||
value={sort}
|
|
||||||
onChange={handleChange}
|
|
||||||
input={<OutlinedInput id="select-column-hiding" placeholder="Sort by" />}
|
|
||||||
renderValue={(selected) => {
|
|
||||||
const selectedColumn = allColumns.filter((column) => column.id === selected)[0];
|
|
||||||
if (!selected) {
|
|
||||||
return <Typography variant="subtitle1">Sort By</Typography>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Typography variant="subtitle2">
|
|
||||||
Sort by ({typeof selectedColumn.Header === 'string' ? selectedColumn.Header : selectedColumn?.title})
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
{allColumns
|
|
||||||
.filter((column) => column.canSort)
|
|
||||||
.map((column) => (
|
|
||||||
<MenuItem key={column.id} value={column.id}>
|
|
||||||
<ListItemText primary={typeof column.Header === 'string' ? column.Header : column?.title} />
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SortingSelect.propTypes = {
|
|
||||||
setSortBy: PropTypes.func,
|
|
||||||
sortBy: PropTypes.string,
|
|
||||||
allColumns: PropTypes.array
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| CSV EXPORT ||============================== //
|
// ==============================|| CSV EXPORT ||============================== //
|
||||||
|
|
||||||
export const CSVExport = ({ data, filename, headers, label, style, btnLoading, onClick }) => {
|
export const CSVExport = ({ data, filename, headers, label, style, btnLoading, onClick }) => {
|
||||||
return (
|
return (
|
||||||
<CSVLink data={data} filename={filename} headers={headers}>
|
<CSVLink data={data} filename={filename} headers={headers} style={{ textDecoration: 'none', lineHeight: 0 }}>
|
||||||
<Tooltip title="CSV Export">
|
<Tooltip content="CSV Export">
|
||||||
<Button
|
<Button
|
||||||
startIcon={!btnLoading && <DownloadOutlined />}
|
label={typeof label === 'string' ? label : 'Download'}
|
||||||
variant={btnLoading ? 'outlined' : 'contained'}
|
icon={!btnLoading ? <DownloadOutlined /> : undefined}
|
||||||
sx={{ ...style }}
|
variant={btnLoading ? 'secondary' : 'primary'}
|
||||||
disabled={btnLoading}
|
isDisabled={btnLoading}
|
||||||
onClick={(e) => {
|
onClick={(e) => onClick?.(e)}
|
||||||
onClick?.(e);
|
style={style}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{btnLoading ? <CircularProgress size={20} thickness={5} /> : label || 'Download'}
|
{btnLoading ? <Spinner size="sm" shade="inherit" /> : label || 'Download'}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</CSVLink>
|
</CSVLink>
|
||||||
@@ -526,70 +55,3 @@ CSVExport.propTypes = {
|
|||||||
btnLoading: PropTypes.bool,
|
btnLoading: PropTypes.bool,
|
||||||
onClick: PropTypes.func
|
onClick: PropTypes.func
|
||||||
};
|
};
|
||||||
// ==============================|| EMPTY TABLE - NO DATA ||============================== //
|
|
||||||
|
|
||||||
const StyledGridOverlay = styled(Stack)(({ theme }) => ({
|
|
||||||
height: '400px',
|
|
||||||
'& .ant-empty-img-1': {
|
|
||||||
fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary[200] : theme.palette.secondary[400]
|
|
||||||
},
|
|
||||||
'& .ant-empty-img-2': {
|
|
||||||
fill: theme.palette.secondary.light
|
|
||||||
},
|
|
||||||
'& .ant-empty-img-3': {
|
|
||||||
fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary.A200 : theme.palette.secondary[200]
|
|
||||||
},
|
|
||||||
'& .ant-empty-img-4': {
|
|
||||||
fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary.A300 : theme.palette.secondary.A100
|
|
||||||
},
|
|
||||||
'& .ant-empty-img-5': {
|
|
||||||
fillOpacity: theme.palette.mode === ThemeMode.DARK ? '0.09' : '0.95',
|
|
||||||
fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary.darker : theme.palette.secondary.light
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const EmptyTable = ({ msg, colSpan }) => {
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={colSpan}>
|
|
||||||
<StyledGridOverlay alignItems="center" justifyContent="center" spacing={1}>
|
|
||||||
<svg width="120" height="100" viewBox="0 0 184 152" aria-hidden focusable="false">
|
|
||||||
<g fill="none" fillRule="evenodd">
|
|
||||||
<g transform="translate(24 31.67)">
|
|
||||||
<ellipse className="ant-empty-img-5" cx="67.797" cy="106.89" rx="67.797" ry="12.668" />
|
|
||||||
<path
|
|
||||||
className="ant-empty-img-1"
|
|
||||||
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
className="ant-empty-img-2"
|
|
||||||
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
className="ant-empty-img-3"
|
|
||||||
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
className="ant-empty-img-3"
|
|
||||||
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
|
|
||||||
/>
|
|
||||||
<g className="ant-empty-img-4" transform="translate(149.65 15.383)">
|
|
||||||
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815" />
|
|
||||||
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<Typography align="center" color="secondary">
|
|
||||||
{msg}
|
|
||||||
</Typography>
|
|
||||||
</StyledGridOverlay>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
EmptyTable.propTypes = {
|
|
||||||
msg: PropTypes.string,
|
|
||||||
colSpan: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|||||||
62
src/components/third-party/SimpleBar.js
vendored
62
src/components/third-party/SimpleBar.js
vendored
@@ -1,62 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { alpha, styled } from '@mui/material/styles';
|
|
||||||
import { Box } from '@mui/material';
|
|
||||||
|
|
||||||
// third-party
|
|
||||||
import SimpleBar from 'simplebar-react';
|
|
||||||
import { BrowserView, MobileView } from 'react-device-detect';
|
|
||||||
|
|
||||||
// root style
|
|
||||||
const RootStyle = styled(BrowserView)({
|
|
||||||
flexGrow: 1,
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'hidden'
|
|
||||||
});
|
|
||||||
|
|
||||||
// scroll bar wrapper
|
|
||||||
const SimpleBarStyle = styled(SimpleBar)(({ theme }) => ({
|
|
||||||
maxHeight: '100%',
|
|
||||||
'& .simplebar-scrollbar': {
|
|
||||||
'&:before': {
|
|
||||||
backgroundColor: alpha(theme.palette.grey[500], 0.48)
|
|
||||||
},
|
|
||||||
'&.simplebar-visible:before': {
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'& .simplebar-track.simplebar-vertical': {
|
|
||||||
width: 10
|
|
||||||
},
|
|
||||||
'& .simplebar-track.simplebar-horizontal .simplebar-scrollbar': {
|
|
||||||
height: 6
|
|
||||||
},
|
|
||||||
'& .simplebar-mask': {
|
|
||||||
zIndex: 'inherit'
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ==============================|| SIMPLE SCROLL BAR ||============================== //
|
|
||||||
|
|
||||||
export default function SimpleBarScroll({ children, sx, ...other }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<RootStyle>
|
|
||||||
<SimpleBarStyle clickOnTrack={false} sx={sx} {...other}>
|
|
||||||
{children}
|
|
||||||
</SimpleBarStyle>
|
|
||||||
</RootStyle>
|
|
||||||
<MobileView>
|
|
||||||
<Box sx={{ overflowX: 'auto', ...sx }} {...other}>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
</MobileView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SimpleBarScroll.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
sx: PropTypes.object
|
|
||||||
};
|
|
||||||
14
src/index.js
14
src/index.js
@@ -1,5 +1,5 @@
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter, Link } from 'react-router-dom';
|
||||||
|
|
||||||
// third-party
|
// third-party
|
||||||
import { Provider as ReduxProvider } from 'react-redux';
|
import { Provider as ReduxProvider } from 'react-redux';
|
||||||
@@ -7,6 +7,13 @@ import { Provider as ReduxProvider } from 'react-redux';
|
|||||||
// scroll bar
|
// scroll bar
|
||||||
import 'simplebar/dist/simplebar.css';
|
import 'simplebar/dist/simplebar.css';
|
||||||
|
|
||||||
|
// Astryx design system — zero-specificity :where() reset + component base
|
||||||
|
// styles, safe to load alongside MUI/Emotion (see themes/astryx.js for the
|
||||||
|
// brand theme applied per-page during migration).
|
||||||
|
import '@astryxdesign/core/reset.css';
|
||||||
|
import '@astryxdesign/core/astryx.css';
|
||||||
|
import { LinkProvider } from '@astryxdesign/core/Link';
|
||||||
|
|
||||||
// apex-chart
|
// apex-chart
|
||||||
import 'assets/third-party/apex-chart.css';
|
import 'assets/third-party/apex-chart.css';
|
||||||
import 'assets/third-party/react-table.css';
|
import 'assets/third-party/react-table.css';
|
||||||
@@ -32,7 +39,12 @@ root.render(
|
|||||||
{/* <ConfigProvider> */}
|
{/* <ConfigProvider> */}
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
{/* Makes every Astryx nav/link component (SideNavItem, TopNavHeading,
|
||||||
|
DropdownMenu, ...) route through React Router's Link instead of a
|
||||||
|
hard <a> reload. */}
|
||||||
|
<LinkProvider component={Link}>
|
||||||
<App />
|
<App />
|
||||||
|
</LinkProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
{/* </ConfigProvider> */}
|
{/* </ConfigProvider> */}
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled, useTheme } from '@mui/material/styles';
|
|
||||||
import { Box, Button, Container, CardMedia, Divider, Stack, Grid, Link, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// third party
|
// third party
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import { ThemeDirection, ThemeMode } from 'config';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
import useConfig from 'hooks/useConfig';
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import { SendOutlined } from '@ant-design/icons';
|
import { SendOutlined } from '@ant-design/icons';
|
||||||
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
import imgfooterlogo from 'assets/images/landing/codedthemes-logo.svg';
|
import imgfooterlogo from 'assets/images/landing/codedthemes-logo.svg';
|
||||||
import imgfootersoc1 from 'assets/images/landing/img-soc1.svg';
|
import imgfootersoc1 from 'assets/images/landing/img-soc1.svg';
|
||||||
import imgfootersoc2 from 'assets/images/landing/img-soc2.svg';
|
import imgfootersoc2 from 'assets/images/landing/img-soc2.svg';
|
||||||
@@ -21,295 +17,172 @@ import AnimateButton from 'components/@extended/AnimateButton';
|
|||||||
|
|
||||||
const dashImage = require.context('assets/images/landing', true);
|
const dashImage = require.context('assets/images/landing', true);
|
||||||
|
|
||||||
// link - custom style
|
// Note: like Header.js, this footer only renders for CommonLayout's 'landing' variant,
|
||||||
const FooterLink = styled(Link)(({ theme }) => ({
|
// which this app's actual routing never selects — it's leftover template-vendor marketing
|
||||||
color: theme.palette.text.secondary,
|
// chrome (CodedThemes/Mantis branding), kept dead-in-practice but converted off MUI since
|
||||||
'&:hover': {
|
// it's still part of the FooterBlock lazy chunk. Static grey/white tones replace the old
|
||||||
color: theme.palette.primary.main
|
// theme.palette reads (this app runs light-mode only).
|
||||||
},
|
|
||||||
'&:active': {
|
const GREY_A700 = '#1f1f1f';
|
||||||
color: theme.palette.primary.main
|
const GREY_800 = '#262626';
|
||||||
}
|
const WHITE = '#fff';
|
||||||
}));
|
|
||||||
|
const footerLinkStyle = { color: 'rgba(255,255,255,0.6)', textDecoration: 'none', fontSize: 14 };
|
||||||
|
|
||||||
// ==============================|| LANDING - FOOTER PAGE ||============================== //
|
// ==============================|| LANDING - FOOTER PAGE ||============================== //
|
||||||
|
|
||||||
const FooterBlock = ({ isFull }) => {
|
const FooterBlock = ({ isFull }) => {
|
||||||
const theme = useTheme();
|
|
||||||
const { presetColor } = useConfig();
|
const { presetColor } = useConfig();
|
||||||
const textColor = theme.palette.mode === ThemeMode.DARK ? 'text.primary' : 'background.paper';
|
|
||||||
|
|
||||||
const linkSX = {
|
|
||||||
color: theme.palette.common.white,
|
|
||||||
fontSize: '0.875rem',
|
|
||||||
fontWeight: 400,
|
|
||||||
opacity: '0.6',
|
|
||||||
cursor: 'pointer',
|
|
||||||
'&:hover': {
|
|
||||||
opacity: '1'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const frameworks = [
|
const frameworks = [
|
||||||
{ title: 'CodeIgniter', link: 'https://links.codedthemes.com/dEGKs' },
|
{ title: 'CodeIgniter', link: 'https://links.codedthemes.com/dEGKs' },
|
||||||
{
|
{ title: 'React MUI', link: 'https://links.codedthemes.com/Aprwb' },
|
||||||
title: 'React MUI',
|
{ title: 'Angular', link: 'https://links.codedthemes.com/EIvof' },
|
||||||
link: 'https://links.codedthemes.com/Aprwb'
|
{ title: 'Bootstrap 5', link: 'https://codedthemes.com/item/mantis-bootstrap-admin-dashboard/' },
|
||||||
},
|
{ title: '.Net', link: 'https://links.codedthemes.com/GPZhD' }
|
||||||
{
|
|
||||||
title: 'Angular',
|
|
||||||
link: 'https://links.codedthemes.com/EIvof'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Bootstrap 5',
|
|
||||||
link: 'https://codedthemes.com/item/mantis-bootstrap-admin-dashboard/'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '.Net',
|
|
||||||
link: 'https://links.codedthemes.com/GPZhD'
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const helpLinks = [
|
||||||
|
{ title: 'Blog', link: 'https://links.codedthemes.com/nOuhw' },
|
||||||
|
{ title: 'Documentation', link: 'https://links.codedthemes.com/BQFrl' },
|
||||||
|
{ title: 'Change Log', link: 'https://links.codedthemes.com/rhzvh' },
|
||||||
|
{ title: 'Support', link: 'https://codedthemes.support-hub.io/' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const storeHelpLinks = [
|
||||||
|
{ title: 'License', link: 'https://mui.com/store/license/' },
|
||||||
|
{ title: 'Refund Policy', link: 'https://mui.com/store/customer-refund-policy/' },
|
||||||
|
{ title: 'Submit a Request', link: 'https://support.mui.com/hc/en-us/sections/360002564979-For-customers' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const moreProducts = [
|
||||||
|
{ title: 'Berry React Material', link: 'https://links.codedthemes.com/zyACc' },
|
||||||
|
{ title: 'Free Berry React', link: 'https://links.codedthemes.com/WztNI' },
|
||||||
|
{ title: 'Free Mantis React', link: 'https://links.codedthemes.com/sTUAK' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const FooterColumn = ({ title, links }) => (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||||
|
<span style={{ color: WHITE, fontWeight: 500, fontSize: '1.05rem' }}>{title}</span>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
|
{links.map((item) => (
|
||||||
|
<a key={item.title} href={item.link} target="_blank" rel="noopener noreferrer" style={footerLinkStyle}>
|
||||||
|
{item.title}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isFull && (
|
{isFull && (
|
||||||
<Box
|
<div
|
||||||
sx={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
bgcolor: theme.palette.grey.A700,
|
backgroundColor: GREY_A700,
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
mt: { xs: 0, md: 13.75 },
|
marginTop: 110,
|
||||||
pt: { xs: 8, sm: 7.5, md: 18.75 },
|
paddingTop: 150,
|
||||||
pb: { xs: 2.5, md: 10 },
|
paddingBottom: 80,
|
||||||
'&:after': {
|
overflow: 'hidden'
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '80%',
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
background:
|
|
||||||
theme.direction === ThemeDirection.RTL
|
|
||||||
? `linear-gradient(transparent 100%, rgb(31, 31, 31) 70%)`
|
|
||||||
: `linear-gradient(180deg, transparent 0%, ${theme.palette.grey.A700} 70%)`
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardMedia
|
<img
|
||||||
component="img"
|
src={dashImage(`./img-footer-${presetColor}.png`)}
|
||||||
image={dashImage(`./img-footer-${presetColor}.png`)}
|
alt=""
|
||||||
sx={{
|
style={{ display: 'block', width: '55%', maxWidth: 700, position: 'absolute', top: '-28%', right: 0 }}
|
||||||
display: { xs: 'none', md: 'block' },
|
|
||||||
width: '55%',
|
|
||||||
maxWidth: 700,
|
|
||||||
position: 'absolute',
|
|
||||||
top: '-28%',
|
|
||||||
right: 0
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Container>
|
<div style={{ maxWidth: 1200, marginInline: 'auto', paddingInline: 16 }}>
|
||||||
<Grid container alignItems="center" justifyContent="space-between" spacing={2}>
|
<div style={{ position: 'relative', zIndex: 1, maxWidth: 600 }}>
|
||||||
<Grid item xs={12} md={6} sx={{ position: 'relative', zIndex: 1 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
<Grid container spacing={2} sx={{ [theme.breakpoints.down('md')]: { pr: 0, textAlign: 'center' } }}>
|
<span style={{ color: WHITE, fontWeight: 500 }}>Roadmap</span>
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography variant="subtitle1" sx={{ color: theme.palette.common.white }}>
|
|
||||||
Roadmap
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, translateY: 550 }}
|
initial={{ opacity: 0, translateY: 550 }}
|
||||||
animate={{ opacity: 1, translateY: 0 }}
|
animate={{ opacity: 1, translateY: 0 }}
|
||||||
transition={{
|
transition={{ type: 'spring', stiffness: 150, damping: 30 }}
|
||||||
type: 'spring',
|
|
||||||
stiffness: 150,
|
|
||||||
damping: 30
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Typography
|
<span style={{ color: WHITE, fontWeight: 700, fontSize: '2rem', display: 'block' }}>Upcoming Release</span>
|
||||||
variant="h2"
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.common.white,
|
|
||||||
fontWeight: 700
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Upcoming Release
|
|
||||||
</Typography>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Grid>
|
<span style={{ color: WHITE }}>What is next? Checkout the Upcoming release of Mantis React.</span>
|
||||||
<Grid item xs={12}>
|
<div style={{ marginBlock: 16 }}>
|
||||||
<Typography variant="body1" sx={{ color: theme.palette.common.white }}>
|
|
||||||
What is next? Checkout the Upcoming release of Mantis React.
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sx={{ my: 2 }}>
|
|
||||||
<Box sx={{ display: 'inline-block' }}>
|
|
||||||
<AnimateButton>
|
<AnimateButton>
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
label="Roadmap"
|
||||||
variant="contained"
|
variant="primary"
|
||||||
endIcon={<SendOutlined />}
|
size="lg"
|
||||||
component={Link}
|
endContent={<SendOutlined />}
|
||||||
href="https://links.codedthemes.com/RXnKQ"
|
href="https://links.codedthemes.com/RXnKQ"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Roadmap
|
Roadmap
|
||||||
</Button>
|
</Button>
|
||||||
</AnimateButton>
|
</AnimateButton>
|
||||||
</Box>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box sx={{ pt: isFull ? 0 : 10, pb: 10, bgcolor: theme.palette.grey.A700 }}>
|
<div style={{ paddingTop: isFull ? 0 : 80, paddingBottom: 80, backgroundColor: GREY_A700 }}>
|
||||||
<Container>
|
<div style={{ maxWidth: 1200, marginInline: 'auto', paddingInline: 16 }}>
|
||||||
<Grid container spacing={2}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 24 }}>
|
||||||
<Grid item xs={12} md={4}>
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, translateY: 550 }}
|
initial={{ opacity: 0, translateY: 550 }}
|
||||||
animate={{ opacity: 1, translateY: 0 }}
|
animate={{ opacity: 1, translateY: 0 }}
|
||||||
transition={{
|
transition={{ type: 'spring', stiffness: 150, damping: 30 }}
|
||||||
type: 'spring',
|
|
||||||
stiffness: 150,
|
|
||||||
damping: 30
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Grid container spacing={2}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
<Grid item xs={12}>
|
<img src={imgfooterlogo} alt="Logo" style={{ width: 'auto' }} />
|
||||||
<CardMedia component="img" image={imgfooterlogo} sx={{ width: 'auto' }} />
|
<span style={{ color: WHITE }}>
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 400, color: theme.palette.common.white }}>
|
|
||||||
Since 2017, More than 50K+ Developers trust the CodedThemes Digital Product. Mantis React is Manage under their
|
Since 2017, More than 50K+ Developers trust the CodedThemes Digital Product. Mantis React is Manage under their
|
||||||
Experienced Team Players.
|
Experienced Team Players.
|
||||||
</Typography>
|
</span>
|
||||||
</Grid>
|
</div>
|
||||||
</Grid>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={8}>
|
<div
|
||||||
<Grid container spacing={{ xs: 5, md: 2 }}>
|
style={{
|
||||||
<Grid item xs={6} sm={3}>
|
display: 'grid',
|
||||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))',
|
||||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
gap: 24
|
||||||
Help
|
|
||||||
</Typography>
|
|
||||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
|
||||||
<FooterLink href="https://links.codedthemes.com/nOuhw" target="_blank" underline="none">
|
|
||||||
Blog
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink href="https://links.codedthemes.com/BQFrl" target="_blank" underline="none">
|
|
||||||
Documentation
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink href="https://links.codedthemes.com/rhzvh" target="_blank" underline="none">
|
|
||||||
Change Log
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink href="https://codedthemes.support-hub.io/" target="_blank" underline="none">
|
|
||||||
Support
|
|
||||||
</FooterLink>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} sm={3}>
|
|
||||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
|
||||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
|
||||||
Store Help
|
|
||||||
</Typography>
|
|
||||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
|
||||||
<FooterLink href="https://mui.com/store/license/" target="_blank" underline="none">
|
|
||||||
License
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink href="https://mui.com/store/customer-refund-policy/" target="_blank" underline="none">
|
|
||||||
Refund Policy
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink
|
|
||||||
href="https://support.mui.com/hc/en-us/sections/360002564979-For-customers"
|
|
||||||
target="_blank"
|
|
||||||
underline="none"
|
|
||||||
>
|
|
||||||
Submit a Request
|
|
||||||
</FooterLink>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} sm={3}>
|
|
||||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
|
||||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
|
||||||
Mantis Eco-System
|
|
||||||
</Typography>
|
|
||||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
|
||||||
{frameworks.map((item, index) => (
|
|
||||||
<FooterLink href={item.link} target="_blank" underline="none" key={index}>
|
|
||||||
{item.title}
|
|
||||||
{/* {item.isUpcoming && <Chip variant="outlined" size="small" label="Upcoming" sx={{ ml: 0.5 }} />} */}
|
|
||||||
</FooterLink>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6} sm={3}>
|
|
||||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
|
||||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
|
||||||
More Products
|
|
||||||
</Typography>
|
|
||||||
<Stack spacing={{ xs: 1.5, md: 2.5 }}>
|
|
||||||
<FooterLink href="https://links.codedthemes.com/zyACc" target="_blank" underline="none">
|
|
||||||
Berry React Material
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink href="https://links.codedthemes.com/WztNI" target="_blank" underline="none">
|
|
||||||
Free Berry React
|
|
||||||
</FooterLink>
|
|
||||||
<FooterLink href="https://links.codedthemes.com/sTUAK" target="_blank" underline="none">
|
|
||||||
Free Mantis React
|
|
||||||
</FooterLink>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
<Divider sx={{ borderColor: 'grey.700' }} />
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
py: 1.5,
|
|
||||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? theme.palette.grey[50] : theme.palette.grey[800]
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container>
|
<FooterColumn title="Help" links={helpLinks} />
|
||||||
<Grid container spacing={2}>
|
<FooterColumn title="Store Help" links={storeHelpLinks} />
|
||||||
<Grid item xs={12} sm={8}>
|
<FooterColumn title="Mantis Eco-System" links={frameworks} />
|
||||||
<Typography variant="subtitle2" color="secondary">
|
<FooterColumn title="More Products" links={moreProducts} />
|
||||||
© Made with love by Team CodedThemes
|
</div>
|
||||||
</Typography>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
<Grid item xs={12} sm={4}>
|
</div>
|
||||||
<Grid container spacing={2} alignItems="center" sx={{ justifyContent: 'flex-end' }}>
|
|
||||||
<Grid item>
|
<div style={{ borderTop: '1px solid #434343' }} />
|
||||||
<Link underline="none" sx={linkSX}>
|
|
||||||
<CardMedia component="img" image={imgfootersoc1} />
|
<div style={{ paddingBlock: 12, backgroundColor: GREY_800 }}>
|
||||||
</Link>
|
<div
|
||||||
</Grid>
|
style={{
|
||||||
<Grid item>
|
maxWidth: 1200,
|
||||||
<Link underline="none" sx={linkSX}>
|
marginInline: 'auto',
|
||||||
<CardMedia component="img" image={imgfootersoc2} />
|
paddingInline: 16,
|
||||||
</Link>
|
display: 'flex',
|
||||||
</Grid>
|
flexWrap: 'wrap',
|
||||||
<Grid item>
|
alignItems: 'center',
|
||||||
<Link underline="none" sx={linkSX}>
|
justifyContent: 'space-between',
|
||||||
<CardMedia component="img" image={imgfootersoc3} />
|
gap: 12
|
||||||
</Link>
|
}}
|
||||||
</Grid>
|
>
|
||||||
</Grid>
|
<span style={{ color: 'rgba(255,255,255,0.6)', fontSize: 13 }}>© Made with love by Team CodedThemes</span>
|
||||||
</Grid>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||||||
</Grid>
|
{[imgfootersoc1, imgfootersoc2, imgfootersoc3].map((src, i) => (
|
||||||
</Container>
|
<a key={i} href="#" style={{ opacity: 0.6, display: 'inline-flex' }}>
|
||||||
</Box>
|
<img src={src} alt="" />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,12 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as React from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useState } from 'react';
|
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
|
|
||||||
// material-ui
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import { Badge } from '@astryxdesign/core/Badge';
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import {
|
|
||||||
useMediaQuery,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Chip,
|
|
||||||
Container,
|
|
||||||
Drawer,
|
|
||||||
Link,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
Stack,
|
|
||||||
Toolbar,
|
|
||||||
Typography,
|
|
||||||
useScrollTrigger
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import { APP_DEFAULT_PATH, ThemeMode } from 'config';
|
import { APP_DEFAULT_PATH } from 'config';
|
||||||
import IconButton from 'components/@extended/IconButton';
|
import IconButton from 'components/@extended/IconButton';
|
||||||
import AnimateButton from 'components/@extended/AnimateButton';
|
import AnimateButton from 'components/@extended/AnimateButton';
|
||||||
import Logo from 'components/logo';
|
import Logo from 'components/logo';
|
||||||
@@ -34,212 +15,170 @@ import Logo from 'components/logo';
|
|||||||
import { MenuOutlined, LineOutlined } from '@ant-design/icons';
|
import { MenuOutlined, LineOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
// ==============================|| COMPONENTS - APP BAR ||============================== //
|
// ==============================|| COMPONENTS - APP BAR ||============================== //
|
||||||
|
// Note: this header only renders for CommonLayout's 'landing'/'simple'/'component' variants,
|
||||||
|
// none of which this app's routing actually selects (both routes.js call sites use the
|
||||||
|
// default 'blank' layout) — it's demo-template chrome pointing at the original template
|
||||||
|
// vendor's marketing links, kept dead-in-practice but still converted off MUI since it's
|
||||||
|
// still a separately-bundled lazy chunk.
|
||||||
|
|
||||||
// elevation scroll
|
// Small local hook replacing MUI's useScrollTrigger — flips true once the page has
|
||||||
function ElevationScroll({ layout, children, window }) {
|
// scrolled past `threshold` px, used to swap the header's background in on scroll.
|
||||||
const theme = useTheme();
|
const useScrolled = (threshold = 10) => {
|
||||||
// const theme = useTheme();
|
const [scrolled, setScrolled] = useState(() => (typeof window !== 'undefined' ? window.scrollY > threshold : false));
|
||||||
|
useEffect(() => {
|
||||||
|
const onScroll = () => setScrolled(window.scrollY > threshold);
|
||||||
|
window.addEventListener('scroll', onScroll, { passive: true });
|
||||||
|
return () => window.removeEventListener('scroll', onScroll);
|
||||||
|
}, [threshold]);
|
||||||
|
return scrolled;
|
||||||
|
};
|
||||||
|
|
||||||
const trigger = useScrollTrigger({
|
const useIsMobile = (breakpoint = 900) => {
|
||||||
disableHysteresis: true,
|
const [isMobile, setIsMobile] = useState(() => (typeof window !== 'undefined' ? window.innerWidth < breakpoint : false));
|
||||||
threshold: 10,
|
useEffect(() => {
|
||||||
target: window ? window() : undefined
|
const onResize = () => setIsMobile(window.innerWidth < breakpoint);
|
||||||
});
|
window.addEventListener('resize', onResize);
|
||||||
|
return () => window.removeEventListener('resize', onResize);
|
||||||
|
}, [breakpoint]);
|
||||||
|
return isMobile;
|
||||||
|
};
|
||||||
|
|
||||||
const backColorScroll = theme.palette.mode === ThemeMode.DARK ? theme.palette.grey[50] : theme.palette.grey[800];
|
const headerLinkStyle = { padding: '0 8px', color: '#fff', textDecoration: 'none', fontWeight: 600, fontSize: 14 };
|
||||||
|
|
||||||
|
const Header = ({ handleDrawerOpen, layout = 'landing' }) => {
|
||||||
|
const scrolled = useScrolled(10);
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||||
|
|
||||||
|
const backColorScroll = '#262626';
|
||||||
const backColor = layout !== 'landing' ? backColorScroll : 'transparent';
|
const backColor = layout !== 'landing' ? backColorScroll : 'transparent';
|
||||||
|
|
||||||
return React.cloneElement(children, {
|
const drawerLinks = [
|
||||||
style: {
|
{ label: 'Dashboard', href: '/login' },
|
||||||
backgroundColor: trigger ? backColorScroll : backColor
|
{ label: 'All Components', href: '/components-overview/buttons' },
|
||||||
}
|
{ label: 'Free Version', href: 'https://links.codedthemes.com/sTUAK' },
|
||||||
});
|
{ label: 'Documentation', href: 'https://links.codedthemes.com/BQFrl' },
|
||||||
}
|
{ label: 'Support', href: 'https://codedthemes.support-hub.io/' },
|
||||||
|
{ label: 'Purchase Now', href: 'https://links.codedthemes.com/Aprwb', badge: 'v1.0' }
|
||||||
const Header = ({ handleDrawerOpen, layout = 'landing', ...others }) => {
|
];
|
||||||
const theme = useTheme();
|
|
||||||
const matchDownMd = useMediaQuery(theme.breakpoints.down('md'));
|
|
||||||
const [drawerToggle, setDrawerToggle] = useState(false);
|
|
||||||
|
|
||||||
/** Method called on multiple components with different event types */
|
|
||||||
const drawerToggler = (open) => (event) => {
|
|
||||||
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setDrawerToggle(open);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElevationScroll layout={layout} {...others}>
|
<div
|
||||||
<AppBar sx={{ bgcolor: 'transparent', color: theme.palette.text.primary, boxShadow: 'none' }}>
|
style={{
|
||||||
<Container disableGutters={matchDownMd}>
|
backgroundColor: scrolled ? backColorScroll : backColor,
|
||||||
<Toolbar sx={{ px: { xs: 1.5, md: 0, lg: 0 }, py: 2 }}>
|
color: '#fff',
|
||||||
<Stack direction="row" sx={{ flexGrow: 1, display: { xs: 'none', md: 'block' } }} alignItems="center">
|
transition: 'background-color 0.2s ease',
|
||||||
<Typography component="div" sx={{ textAlign: 'left', display: 'inline-block' }}>
|
position: 'sticky',
|
||||||
<Logo reverse to="/" />
|
top: 0,
|
||||||
</Typography>
|
zIndex: 1100
|
||||||
<Chip
|
|
||||||
label={process.env.REACT_APP_VERSION}
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
color="secondary"
|
|
||||||
sx={{ mt: 0.5, ml: 1, fontSize: '0.725rem', height: 20, '& .MuiChip-label': { px: 0.5 } }}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
|
||||||
direction="row"
|
|
||||||
sx={{
|
|
||||||
'& .header-link': { px: 1, '&:hover': { color: theme.palette.primary.main } },
|
|
||||||
display: { xs: 'none', md: 'block' }
|
|
||||||
}}
|
}}
|
||||||
spacing={2}
|
|
||||||
>
|
>
|
||||||
<Link className="header-link" color="white" component={RouterLink} to="/login" target="_blank" underline="none">
|
<div style={{ maxWidth: 1200, marginInline: 'auto', paddingInline: isMobile ? 12 : 0 }}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', padding: '16px 0', minHeight: 64 }}>
|
||||||
|
{!isMobile && (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', flexGrow: 1, gap: 8 }}>
|
||||||
|
<Logo reverse to="/" />
|
||||||
|
<Badge label={process.env.REACT_APP_VERSION} style={{ marginTop: 2 }} />
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 16 }}>
|
||||||
|
<RouterLink to="/login" target="_blank" style={headerLinkStyle}>
|
||||||
Dashboard
|
Dashboard
|
||||||
</Link>
|
</RouterLink>
|
||||||
<Link
|
<RouterLink to="/components-overview/buttons" style={{ ...headerLinkStyle, color: handleDrawerOpen ? '#C01227' : '#fff' }}>
|
||||||
className="header-link"
|
|
||||||
color={handleDrawerOpen ? 'primary' : 'white'}
|
|
||||||
component={RouterLink}
|
|
||||||
to="/components-overview/buttons"
|
|
||||||
underline="none"
|
|
||||||
>
|
|
||||||
Components
|
Components
|
||||||
</Link>
|
</RouterLink>
|
||||||
<Link className="header-link" color="white" href="https://links.codedthemes.com/BQFrl" target="_blank" underline="none">
|
<a href="https://links.codedthemes.com/BQFrl" target="_blank" rel="noopener noreferrer" style={headerLinkStyle}>
|
||||||
Documentation
|
Documentation
|
||||||
</Link>
|
</a>
|
||||||
<Box sx={{ display: 'inline-block' }}>
|
|
||||||
<AnimateButton>
|
<AnimateButton>
|
||||||
<Button component={Link} href="https://links.codedthemes.com/Aprwb" disableElevation color="primary" variant="contained">
|
<Button
|
||||||
|
label="Purchase now"
|
||||||
|
variant="primary"
|
||||||
|
href="https://links.codedthemes.com/Aprwb"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
Purchase Now
|
Purchase Now
|
||||||
</Button>
|
</Button>
|
||||||
</AnimateButton>
|
</AnimateButton>
|
||||||
</Box>
|
</div>
|
||||||
</Stack>
|
</>
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: '100%',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
display: { xs: 'flex', md: 'none' }
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography component="div" sx={{ textAlign: 'left', display: 'inline-block' }}>
|
|
||||||
<Logo reverse to="/" />
|
|
||||||
</Typography>
|
|
||||||
<Stack direction="row" spacing={2}>
|
|
||||||
{layout === 'component' && (
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
color="warning"
|
|
||||||
component={RouterLink}
|
|
||||||
to={APP_DEFAULT_PATH}
|
|
||||||
sx={{ mt: 0.5, height: 28 }}
|
|
||||||
>
|
|
||||||
Dashboard
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{layout !== 'component' && (
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
color="warning"
|
|
||||||
component={RouterLink}
|
|
||||||
to="/components-overview/buttons"
|
|
||||||
sx={{ mt: 0.5, height: 28 }}
|
|
||||||
>
|
|
||||||
All Components
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isMobile && (
|
||||||
|
<div style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
<Logo reverse to="/" />
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 16 }}>
|
||||||
|
<RouterLink to={layout === 'component' ? APP_DEFAULT_PATH : '/components-overview/buttons'}>
|
||||||
|
<Button label={layout === 'component' ? 'Dashboard' : 'All Components'} variant="secondary" size="sm" style={{ marginTop: 4 }}>
|
||||||
|
{layout === 'component' ? 'Dashboard' : 'All Components'}
|
||||||
|
</Button>
|
||||||
|
</RouterLink>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
color="secondary"
|
label="Open menu"
|
||||||
{...(layout === 'component' ? { onClick: handleDrawerOpen } : { onClick: drawerToggler(true) })}
|
variant="secondary"
|
||||||
sx={{
|
onClick={layout === 'component' ? handleDrawerOpen : () => setDrawerOpen(true)}
|
||||||
'&:hover': { bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.lighter' : 'secondary.dark' }
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<MenuOutlined style={{ color: theme.palette.mode === ThemeMode.DARK ? 'inherit' : theme.palette.grey[100] }} />
|
<MenuOutlined style={{ color: '#f5f5f5' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Stack>
|
</div>
|
||||||
<Drawer
|
|
||||||
anchor="top"
|
{drawerOpen && (
|
||||||
open={drawerToggle}
|
<>
|
||||||
onClose={drawerToggler(false)}
|
<div
|
||||||
sx={{ '& .MuiDrawer-paper': { backgroundImage: 'none' } }}
|
role="button"
|
||||||
>
|
tabIndex={0}
|
||||||
<Box
|
aria-label="Close menu"
|
||||||
sx={{
|
onClick={() => setDrawerOpen(false)}
|
||||||
width: 'auto',
|
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setDrawerOpen(false)}
|
||||||
'& .MuiListItemIcon-root': {
|
style={{ position: 'fixed', inset: 0, backgroundColor: 'rgba(0,0,0,0.4)', zIndex: 1199, border: 'none' }}
|
||||||
fontSize: '1rem',
|
/>
|
||||||
minWidth: 28
|
<div
|
||||||
}
|
|
||||||
}}
|
|
||||||
role="presentation"
|
role="presentation"
|
||||||
onClick={drawerToggler(false)}
|
onClick={() => setDrawerOpen(false)}
|
||||||
onKeyDown={drawerToggler(false)}
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
zIndex: 1200,
|
||||||
|
backgroundColor: '#1e1e1e',
|
||||||
|
padding: '8px 0'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<List>
|
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
|
||||||
<Link style={{ textDecoration: 'none' }} href="/login" target="_blank">
|
{drawerLinks.map((item) => (
|
||||||
<ListItemButton component="span">
|
<li key={item.label}>
|
||||||
<ListItemIcon>
|
<a
|
||||||
<LineOutlined />
|
href={item.href}
|
||||||
</ListItemIcon>
|
target="_blank"
|
||||||
<ListItemText primary="Dashboard" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
rel="noopener noreferrer"
|
||||||
</ListItemButton>
|
style={{
|
||||||
</Link>
|
display: 'flex',
|
||||||
<Link style={{ textDecoration: 'none' }} href="/components-overview/buttons" target="_blank">
|
alignItems: 'center',
|
||||||
<ListItemButton component="span">
|
gap: 12,
|
||||||
<ListItemIcon>
|
padding: '10px 16px',
|
||||||
<LineOutlined />
|
textDecoration: 'none',
|
||||||
</ListItemIcon>
|
color: '#fff'
|
||||||
<ListItemText primary="All Components" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
}}
|
||||||
</ListItemButton>
|
>
|
||||||
</Link>
|
<LineOutlined style={{ fontSize: '1rem', minWidth: 28 }} />
|
||||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/sTUAK" target="_blank">
|
<span style={{ fontWeight: 600, flex: 1 }}>{item.label}</span>
|
||||||
<ListItemButton component="span">
|
{item.badge && <Badge label={item.badge} />}
|
||||||
<ListItemIcon>
|
</a>
|
||||||
<LineOutlined />
|
</li>
|
||||||
</ListItemIcon>
|
))}
|
||||||
<ListItemText primary="Free Version" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
</ul>
|
||||||
</ListItemButton>
|
</div>
|
||||||
</Link>
|
</>
|
||||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/BQFrl" target="_blank">
|
)}
|
||||||
<ListItemButton component="span">
|
</div>
|
||||||
<ListItemIcon>
|
)}
|
||||||
<LineOutlined />
|
</div>
|
||||||
</ListItemIcon>
|
</div>
|
||||||
<ListItemText primary="Documentation" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
</div>
|
||||||
</ListItemButton>
|
|
||||||
</Link>
|
|
||||||
<Link style={{ textDecoration: 'none' }} href="https://codedthemes.support-hub.io/" target="_blank">
|
|
||||||
<ListItemButton component="span">
|
|
||||||
<ListItemIcon>
|
|
||||||
<LineOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Support" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
|
||||||
</ListItemButton>
|
|
||||||
</Link>
|
|
||||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/Aprwb" target="_blank">
|
|
||||||
<ListItemButton component="span">
|
|
||||||
<ListItemIcon>
|
|
||||||
<LineOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Purchase Now" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
|
||||||
<Chip color="primary" label="v1.0" size="small" />
|
|
||||||
</ListItemButton>
|
|
||||||
</Link>
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</Drawer>
|
|
||||||
</Box>
|
|
||||||
</Toolbar>
|
|
||||||
</Container>
|
|
||||||
</AppBar>
|
|
||||||
</ElevationScroll>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,37 +2,21 @@ import PropTypes from 'prop-types';
|
|||||||
import { lazy, Suspense } from 'react';
|
import { lazy, Suspense } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
// material-ui
|
import { ProgressBar } from '@astryxdesign/core/ProgressBar';
|
||||||
import { Container, Toolbar } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import { dispatch, useSelector } from 'store';
|
import { dispatch, useSelector } from 'store';
|
||||||
import { openComponentDrawer } from 'store/reducers/menu';
|
import { openComponentDrawer } from 'store/reducers/menu';
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
import LinearProgress from '@mui/material/LinearProgress';
|
|
||||||
|
|
||||||
const Header = lazy(() => import('./Header'));
|
const Header = lazy(() => import('./Header'));
|
||||||
const FooterBlock = lazy(() => import('./FooterBlock'));
|
const FooterBlock = lazy(() => import('./FooterBlock'));
|
||||||
|
|
||||||
// ==============================|| Loader ||============================== //
|
// ==============================|| Loader ||============================== //
|
||||||
|
|
||||||
const LoaderWrapper = styled('div')(({ theme }) => ({
|
|
||||||
position: 'fixed',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
zIndex: 2001,
|
|
||||||
width: '100%',
|
|
||||||
'& > * + *': {
|
|
||||||
marginTop: theme.spacing(2)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const Loader = () => (
|
const Loader = () => (
|
||||||
<LoaderWrapper>
|
<div style={{ position: 'fixed', top: 0, left: 0, zIndex: 2001, width: '100%' }}>
|
||||||
<LinearProgress color="primary" />
|
<ProgressBar label="Loading" isLabelHidden isIndeterminate />
|
||||||
</LoaderWrapper>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// ==============================|| MINIMAL LAYOUT ||============================== //
|
// ==============================|| MINIMAL LAYOUT ||============================== //
|
||||||
@@ -56,10 +40,10 @@ const CommonLayout = ({ layout = 'blank' }) => {
|
|||||||
)}
|
)}
|
||||||
{layout === 'component' && (
|
{layout === 'component' && (
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<Container maxWidth="lg" sx={{ px: { xs: 0, sm: 2 } }}>
|
<div style={{ maxWidth: 1200, marginInline: 'auto', paddingInline: 8 }}>
|
||||||
<Header handleDrawerOpen={handleDrawerOpen} layout="component" />
|
<Header handleDrawerOpen={handleDrawerOpen} layout="component" />
|
||||||
<Toolbar sx={{ my: 2 }} />
|
<div style={{ minHeight: 64, marginBlock: 16 }} />
|
||||||
</Container>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
{layout === 'blank' && <Outlet />}
|
{layout === 'blank' && <Outlet />}
|
||||||
|
|||||||
176
src/layout/MainLayout/AppSideNav.js
Normal file
176
src/layout/MainLayout/AppSideNav.js
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { SideNav, SideNavSection, SideNavItem } from '@astryxdesign/core/SideNav';
|
||||||
|
import { Icon } from '@astryxdesign/core/Icon';
|
||||||
|
|
||||||
|
import nearle from 'menu-items/nearle';
|
||||||
|
import { DT } from 'themes/dt/tokens';
|
||||||
|
|
||||||
|
// Thin wrapper around Astryx's SideNav, driven by menu-items/nearle.js.
|
||||||
|
// react-router routing works via the app-root LinkProvider (see src/index.js)
|
||||||
|
// — no per-item Link bridging needed.
|
||||||
|
|
||||||
|
const AppSideNav = ({ isCollapsed, onCollapsedChange }) => {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const renderMenuItem = (item) => {
|
||||||
|
// item.id isn't the same as the message key in this app's menu config
|
||||||
|
// (e.g. id: 'dispatch' but title: <FormattedMessage id="Dispatch" />),
|
||||||
|
// so pull the real key off the title element rather than assuming they
|
||||||
|
// match.
|
||||||
|
const label = intl.formatMessage({ id: item.title.props.id });
|
||||||
|
// Wrap explicitly in Astryx's own <Icon> rather than passing the bare
|
||||||
|
// component and letting SideNavItem's renderIconSlot auto-detect it:
|
||||||
|
// that detection only matches plain functions/forwardRef objects, not
|
||||||
|
// React.memo-wrapped ones (several @ant-design/icons v5 icons, as well
|
||||||
|
// as @mui/icons-material icons, are memo-wrapped) — those fell through
|
||||||
|
// and got returned as a raw, uninstantiated element, crashing with
|
||||||
|
// "Objects are not valid as a React child". Wrapping here always
|
||||||
|
// renders through Icon's own component-mode path, which handles any
|
||||||
|
// component type and gives every icon the same normalised size/colour
|
||||||
|
// regardless of source library (this menu mixes @ant-design/icons and
|
||||||
|
// @mui/icons-material).
|
||||||
|
const icon = item.icon ? <Icon icon={item.icon} /> : undefined;
|
||||||
|
|
||||||
|
if (item.type === 'collapse') {
|
||||||
|
const isChildSelected = item.children.some((child) => pathname.startsWith(child.url));
|
||||||
|
return (
|
||||||
|
<SideNavItem key={item.id} label={label} icon={icon} collapsible={{ defaultIsCollapsed: !isChildSelected }} isSelected={isChildSelected}>
|
||||||
|
{item.children.map(renderMenuItem)}
|
||||||
|
</SideNavItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <SideNavItem key={item.id} label={label} icon={icon} href={item.url} isSelected={pathname.startsWith(item.url)} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SideNav
|
||||||
|
className="dailygrubs-side-nav"
|
||||||
|
collapsible={{ isCollapsed, onCollapsedChange, buttonLabel: 'Collapse navigation' }}
|
||||||
|
style={{
|
||||||
|
backgroundColor: DT.surface,
|
||||||
|
borderRight: `1px solid ${DT.borderSubtle}`,
|
||||||
|
boxShadow: '1px 0 3px rgba(15, 23, 42, 0.03)',
|
||||||
|
paddingBlock: '12px',
|
||||||
|
paddingInline: '8px',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
'--spacing-12': '72px',
|
||||||
|
// Pages with tall/independently-scrolling content still let the
|
||||||
|
// document scroll past the header, so pin the nav explicitly
|
||||||
|
// rather than relying only on AppShell's own auto-mode sticky
|
||||||
|
// wrapper.
|
||||||
|
position: 'sticky',
|
||||||
|
top: 'var(--appshell-header-height, 0px)',
|
||||||
|
height: 'calc(100dvh - var(--appshell-header-height, 0px))',
|
||||||
|
overflowY: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SideNavSection title="Daily Grubs">{nearle.children.map(renderMenuItem)}</SideNavSection>
|
||||||
|
</SideNav>
|
||||||
|
<style>{`
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-section > div:last-child {
|
||||||
|
gap: 6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label] {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 0 !important;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label] > * {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label] .astryx-icon {
|
||||||
|
width: 18px !important;
|
||||||
|
height: 18px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label]:hover {
|
||||||
|
background-color: ${DT.brand}14 !important;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label]:focus-visible {
|
||||||
|
outline: 2px solid ${DT.brand}66;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label][data-selected='selected'] {
|
||||||
|
background-color: ${DT.brand}1f !important;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item[aria-label][data-selected='selected'] .astryx-icon {
|
||||||
|
color: ${DT.brand} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label]) {
|
||||||
|
position: relative;
|
||||||
|
margin-inline: 2px;
|
||||||
|
height: auto;
|
||||||
|
padding-block: 12px !important;
|
||||||
|
transition: background-color 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label])::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -2px;
|
||||||
|
top: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
width: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: ${DT.brand};
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label]):hover {
|
||||||
|
background-color: ${DT.brand}0d !important;
|
||||||
|
transform: translateX(2px);
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label]):focus-visible {
|
||||||
|
outline: 2px solid ${DT.brand}66;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label])[data-selected='selected'] {
|
||||||
|
background-color: ${DT.brand}14 !important;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label])[data-selected='selected']::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav .astryx-side-nav-item:not([aria-label])[data-selected='selected'] .astryx-icon {
|
||||||
|
color: ${DT.brand} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dailygrubs-side-nav > div:last-child {
|
||||||
|
padding-block: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dailygrubs-side-nav button[aria-label*="sidebar"],
|
||||||
|
.dailygrubs-side-nav button[aria-label*="navigation"] {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
transition: background-color 0.15s ease !important;
|
||||||
|
}
|
||||||
|
.dailygrubs-side-nav button[aria-label*="sidebar"]:hover,
|
||||||
|
.dailygrubs-side-nav button[aria-label*="navigation"]:hover {
|
||||||
|
background-color: ${DT.brand}14 !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppSideNav.propTypes = {
|
||||||
|
isCollapsed: PropTypes.bool.isRequired,
|
||||||
|
onCollapsedChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppSideNav;
|
||||||
119
src/layout/MainLayout/AppTopNav.js
Normal file
119
src/layout/MainLayout/AppTopNav.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { TopNav, TopNavHeading } from '@astryxdesign/core/TopNav';
|
||||||
|
import { DropdownMenu } from '@astryxdesign/core/DropdownMenu';
|
||||||
|
import { Avatar } from '@astryxdesign/core/Avatar';
|
||||||
|
import { HStack } from '@astryxdesign/core/HStack';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
|
||||||
|
import { WindowsOutlined, EditOutlined, LogoutOutlined } from '@ant-design/icons';
|
||||||
|
import { TbBoxMultiple1 } from 'react-icons/tb';
|
||||||
|
import { GrMultiple } from 'react-icons/gr';
|
||||||
|
import { TbUserEdit } from 'react-icons/tb';
|
||||||
|
|
||||||
|
import avatar1 from 'assets/images/users/avatar-1.png';
|
||||||
|
import logo from 'assets/images/doormile-logo.png';
|
||||||
|
import { DT } from 'themes/dt/tokens';
|
||||||
|
|
||||||
|
// doormile-logo.png is a white asset; recolour to brand red for this
|
||||||
|
// light TopNav surface (same trick login.js uses for its white-card logo).
|
||||||
|
const logoStyle = {
|
||||||
|
height: 24,
|
||||||
|
width: 'auto',
|
||||||
|
filter: 'brightness(0) saturate(100%) invert(15%) sepia(93%) saturate(5437%) hue-rotate(346deg) brightness(81%) contrast(92%)'
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==============================|| MAIN LAYOUT - TOP NAV ||============================== //
|
||||||
|
|
||||||
|
const AppTopNav = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const topNavRef = useRef(null);
|
||||||
|
const [headerHeight, setHeaderHeight] = useState(null);
|
||||||
|
|
||||||
|
const tenantname = localStorage.getItem('tenantname') || '';
|
||||||
|
const fullname = localStorage.getItem('fullname') || '';
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
localStorage.clear();
|
||||||
|
navigate('/login');
|
||||||
|
};
|
||||||
|
|
||||||
|
// AppShell (height="auto") pins the sticky side nav below the header using
|
||||||
|
// a --appshell-header-height CSS var it measures itself via ResizeObserver.
|
||||||
|
// On some routes that measurement lags/settles wrong, so the side nav's
|
||||||
|
// sticky offset is off. Re-measure the header independently here and
|
||||||
|
// force the same variable with !important on the shell root — an
|
||||||
|
// author-stylesheet !important rule beats AppShell's own non-important
|
||||||
|
// inline style.setProperty() call on that same element, so this always
|
||||||
|
// wins regardless of what the internal measurement produced.
|
||||||
|
useEffect(() => {
|
||||||
|
const el = topNavRef.current;
|
||||||
|
if (!el) return undefined;
|
||||||
|
|
||||||
|
const updateHeight = () => setHeaderHeight(el.getBoundingClientRect().height);
|
||||||
|
updateHeight();
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(updateHeight);
|
||||||
|
observer.observe(el);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TopNav
|
||||||
|
ref={topNavRef}
|
||||||
|
label="Nearle Express"
|
||||||
|
style={{
|
||||||
|
backgroundColor: DT.surface,
|
||||||
|
borderBottom: `1px solid ${DT.borderSubtle}`,
|
||||||
|
boxShadow: DT.shadowMd,
|
||||||
|
position: 'sticky',
|
||||||
|
top: 0,
|
||||||
|
zIndex: 1200
|
||||||
|
}}
|
||||||
|
heading={
|
||||||
|
<HStack gap={1.5} vAlign="center" padding={0}>
|
||||||
|
<TopNavHeading logo={<img src={logo} alt="Nearle" style={logoStyle} />} headingHref="/nearle/dispatch" />
|
||||||
|
</HStack>
|
||||||
|
}
|
||||||
|
endContent={
|
||||||
|
<HStack gap={1} vAlign="center" padding={0}>
|
||||||
|
<DropdownMenu
|
||||||
|
hasChevron={false}
|
||||||
|
button={{ label: 'Create Menu', icon: <WindowsOutlined />, isIconOnly: true, variant: 'ghost' }}
|
||||||
|
items={[
|
||||||
|
{ label: 'Create Orders', icon: <TbBoxMultiple1 />, onClick: () => navigate('nearle/orders/create') },
|
||||||
|
{ label: 'Create Group Orders', icon: <GrMultiple />, onClick: () => navigate('nearle/orders/create/grouporders') },
|
||||||
|
{ label: 'Customers', icon: <TbUserEdit />, onClick: () => navigate('nearle/customers') }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DropdownMenu
|
||||||
|
hasChevron={false}
|
||||||
|
button={{
|
||||||
|
label: fullname || 'Profile',
|
||||||
|
icon: <Avatar src={avatar1} name={fullname} size="xsm" tooltip={false} />,
|
||||||
|
variant: 'ghost'
|
||||||
|
}}
|
||||||
|
items={[
|
||||||
|
{ label: 'View Profile', icon: <EditOutlined />, onClick: () => navigate('/accountsettings') },
|
||||||
|
{ type: 'divider' },
|
||||||
|
{ label: 'Logout', icon: <LogoutOutlined />, onClick: handleLogout }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{headerHeight != null && (
|
||||||
|
<style>{`
|
||||||
|
.astryx-app-shell {
|
||||||
|
--appshell-header-height: ${headerHeight}px !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppTopNav;
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { Button, Link, CardMedia, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import avatar from 'assets/images/users/avatar-group.png';
|
|
||||||
import AnimateButton from 'components/@extended/AnimateButton';
|
|
||||||
|
|
||||||
// ==============================|| DRAWER CONTENT - NAVIGATION CARD ||============================== //
|
|
||||||
|
|
||||||
const NavCard = () => (
|
|
||||||
<MainCard sx={{ bgcolor: 'grey.50', m: 3 }}>
|
|
||||||
<Stack alignItems="center" spacing={2.5}>
|
|
||||||
<CardMedia component="img" image={avatar} />
|
|
||||||
<Stack alignItems="center">
|
|
||||||
<Typography variant="h5">Help?</Typography>
|
|
||||||
<Typography variant="h6" color="secondary">
|
|
||||||
Get to resolve query
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
<AnimateButton>
|
|
||||||
<Button variant="shadow" size="small" component={Link} href="https://codedthemes.support-hub.io/" target="_blank">
|
|
||||||
Support
|
|
||||||
</Button>
|
|
||||||
</AnimateButton>
|
|
||||||
</Stack>
|
|
||||||
</MainCard>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default NavCard;
|
|
||||||
@@ -1,503 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { useEffect, useState, useMemo } from 'react';
|
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled, useTheme } from '@mui/material/styles';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Collapse,
|
|
||||||
ClickAwayListener,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
Paper,
|
|
||||||
Popper,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import NavItem from './NavItem';
|
|
||||||
import Dot from 'components/@extended/Dot';
|
|
||||||
import SimpleBar from 'components/third-party/SimpleBar';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
import { dispatch, useSelector } from 'store';
|
|
||||||
import { activeItem } from 'store/reducers/menu';
|
|
||||||
import { MenuOrientation, ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { BorderOutlined, DownOutlined, UpOutlined, RightOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// mini-menu - wrapper
|
|
||||||
const PopperStyled = styled(Popper)(({ theme }) => ({
|
|
||||||
overflow: 'visible',
|
|
||||||
zIndex: 1202,
|
|
||||||
minWidth: 180,
|
|
||||||
'&:before': {
|
|
||||||
content: '""',
|
|
||||||
display: 'block',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 38,
|
|
||||||
left: -5,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
|
||||||
transform: 'translateY(-50%) rotate(45deg)',
|
|
||||||
zIndex: 120,
|
|
||||||
borderLeft: `1px solid ${theme.palette.grey.A800}`,
|
|
||||||
borderBottom: `1px solid ${theme.palette.grey.A800}`
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ==============================|| NAVIGATION - LIST COLLAPSE ||============================== //
|
|
||||||
|
|
||||||
const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
const menuState = useSelector((state) => state.menu);
|
|
||||||
|
|
||||||
const { drawerOpen } = menuState;
|
|
||||||
const { menuOrientation } = useConfig();
|
|
||||||
const navigation = useNavigate();
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [selected, setSelected] = useState(null);
|
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
|
||||||
|
|
||||||
const handleClick = (event) => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
setSelectedLevel(level);
|
|
||||||
if (drawerOpen) {
|
|
||||||
setOpen(!open);
|
|
||||||
setSelected(!selected ? menu.id : null);
|
|
||||||
setSelectedItems(!selected ? menu.id : '');
|
|
||||||
if (menu.url) navigation(`${menu.url}`);
|
|
||||||
} else {
|
|
||||||
setAnchorEl(event?.currentTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlerIconLink = () => {
|
|
||||||
if (!drawerOpen) {
|
|
||||||
if (menu.url) navigation(`${menu.url}`);
|
|
||||||
setSelected(menu.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleHover = (event) => {
|
|
||||||
setAnchorEl(event?.currentTarget);
|
|
||||||
if (!drawerOpen) {
|
|
||||||
setSelected(menu.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const miniMenuOpened = Boolean(anchorEl);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setOpen(false);
|
|
||||||
if (!miniMenuOpened) {
|
|
||||||
if (!menu.url) {
|
|
||||||
setSelected(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setAnchorEl(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
if (selected === selectedItems) {
|
|
||||||
if (level === 1) {
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (level === selectedLevel) {
|
|
||||||
setOpen(false);
|
|
||||||
if (!miniMenuOpened && !drawerOpen && !selected) {
|
|
||||||
setSelected(null);
|
|
||||||
}
|
|
||||||
if (drawerOpen) {
|
|
||||||
setSelected(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [selectedItems, level, selected, miniMenuOpened, drawerOpen, selectedLevel]);
|
|
||||||
|
|
||||||
const { pathname } = useLocation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (pathname === menu.url) {
|
|
||||||
setSelected(menu.id);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [pathname]);
|
|
||||||
|
|
||||||
const checkOpenForParent = (child, id) => {
|
|
||||||
child.forEach((item) => {
|
|
||||||
if (item.url === pathname) {
|
|
||||||
setOpen(true);
|
|
||||||
setSelected(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setOpen(false);
|
|
||||||
if (!miniMenuOpened) {
|
|
||||||
setSelected(null);
|
|
||||||
}
|
|
||||||
if (miniMenuOpened) setAnchorEl(null);
|
|
||||||
if (menu.children) {
|
|
||||||
menu.children.forEach((item) => {
|
|
||||||
if (item.children?.length) {
|
|
||||||
checkOpenForParent(item.children, menu.id);
|
|
||||||
}
|
|
||||||
if (pathname && pathname.includes('product-details')) {
|
|
||||||
if (item.url && item.url.includes('product-details')) {
|
|
||||||
setSelected(menu.id);
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.url === pathname) {
|
|
||||||
setSelected(menu.id);
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [pathname, menu.children]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (menu.url === pathname) {
|
|
||||||
dispatch(activeItem({ openItem: [menu.id] }));
|
|
||||||
setSelected(menu.id);
|
|
||||||
setAnchorEl(null);
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
}, [pathname, menu]);
|
|
||||||
|
|
||||||
const navCollapse = menu.children?.map((item) => {
|
|
||||||
switch (item.type) {
|
|
||||||
case 'collapse':
|
|
||||||
return (
|
|
||||||
<NavCollapse
|
|
||||||
key={item.id}
|
|
||||||
setSelectedItems={setSelectedItems}
|
|
||||||
setSelectedLevel={setSelectedLevel}
|
|
||||||
selectedLevel={selectedLevel}
|
|
||||||
selectedItems={selectedItems}
|
|
||||||
menu={item}
|
|
||||||
level={level + 1}
|
|
||||||
parentId={parentId}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'item':
|
|
||||||
return <NavItem key={item.id} item={item} level={level + 1} />;
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Typography key={item.id} variant="h6" color="error" align="center">
|
|
||||||
Fix - Collapse or Item
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const isSelected = selected === menu.id;
|
|
||||||
const borderIcon = level === 1 ? <BorderOutlined style={{ fontSize: '1rem' }} /> : false;
|
|
||||||
const Icon = menu.icon;
|
|
||||||
const menuIcon = menu.icon ? <Icon style={{ fontSize: drawerOpen ? '1rem' : '1.25rem', color: 'white' }} /> : borderIcon;
|
|
||||||
// const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : 'text.primary';
|
|
||||||
// const iconSelectedColor = theme.palette.mode === ThemeMode.DARK && drawerOpen ? theme.palette.text.primary : theme.palette.primary.main;
|
|
||||||
const popperId = miniMenuOpened ? `collapse-pop-${menu.id}` : undefined;
|
|
||||||
const FlexBox = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' };
|
|
||||||
const textColor = 'white';
|
|
||||||
const iconSelectedColor = 'white';
|
|
||||||
// const isSelected = true;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{menuOrientation === MenuOrientation.VERTICAL || downLG ? (
|
|
||||||
<>
|
|
||||||
<ListItemButton
|
|
||||||
disableRipple
|
|
||||||
selected={selected === menu.id}
|
|
||||||
{...(!drawerOpen && { onMouseEnter: handleClick, onMouseLeave: handleClose })}
|
|
||||||
onClick={handleClick}
|
|
||||||
sx={{
|
|
||||||
pl: drawerOpen ? `${level * 28}px` : 1.5,
|
|
||||||
py: !drawerOpen && level === 1 ? 1.25 : 1,
|
|
||||||
|
|
||||||
...(drawerOpen && {
|
|
||||||
'&:hover': {
|
|
||||||
// bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter'
|
|
||||||
bgcolor: '#7b1fa2'
|
|
||||||
},
|
|
||||||
'&.Mui-selected': {
|
|
||||||
bgcolor: 'transparent',
|
|
||||||
color: iconSelectedColor,
|
|
||||||
'&:hover': { color: iconSelectedColor, bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'transparent' }
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen && {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
// bgcolor:'#7b1fa2'
|
|
||||||
},
|
|
||||||
'&.Mui-selected': {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
},
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{menuIcon && (
|
|
||||||
<ListItemIcon
|
|
||||||
onClick={handlerIconLink}
|
|
||||||
sx={{
|
|
||||||
minWidth: 28,
|
|
||||||
// color: selected === menu.id ? 'primary.main' : textColor,
|
|
||||||
// color: selected === menu.id ? textColor : textColor,
|
|
||||||
// bgcolor:'white',
|
|
||||||
// color:'white',
|
|
||||||
...(!drawerOpen && {
|
|
||||||
borderRadius: 1.5,
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
'&:hover': {
|
|
||||||
// bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.light' : 'secondary.lighter'
|
|
||||||
bgcolor: '#7b1fa2',
|
|
||||||
color: 'white'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen &&
|
|
||||||
selected === menu.id && {
|
|
||||||
bgcolor: 'primary.light',
|
|
||||||
color: 'primary.main',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: '#7b1fa2',
|
|
||||||
color: 'primary.main'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{menuIcon}
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
{(drawerOpen || (!drawerOpen && level !== 1)) && (
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
// color={selected === menu.id ? 'primary' : textColor}
|
|
||||||
// color={'white'}
|
|
||||||
color={selected === menu.id ? textColor : textColor}
|
|
||||||
>
|
|
||||||
{menu.title}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary={
|
|
||||||
menu.caption && (
|
|
||||||
<Typography variant="caption" color="secondary">
|
|
||||||
{menu.caption}
|
|
||||||
</Typography>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(drawerOpen || (!drawerOpen && level !== 1)) &&
|
|
||||||
(miniMenuOpened || open ? (
|
|
||||||
<UpOutlined
|
|
||||||
style={{
|
|
||||||
fontSize: '0.625rem',
|
|
||||||
marginLeft: 1,
|
|
||||||
// color: theme.palette.primary.main
|
|
||||||
color: 'white'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<DownOutlined
|
|
||||||
style={{
|
|
||||||
fontSize: '0.625rem',
|
|
||||||
marginLeft: 1,
|
|
||||||
color: 'white'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{!drawerOpen && (
|
|
||||||
<PopperStyled
|
|
||||||
open={miniMenuOpened}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
placement="right-start"
|
|
||||||
style={{
|
|
||||||
zIndex: 2001
|
|
||||||
}}
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [-12, 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions in={miniMenuOpened} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
overflow: 'hidden',
|
|
||||||
mt: 1.5,
|
|
||||||
boxShadow: theme.customShadows.z1,
|
|
||||||
backgroundImage: 'none',
|
|
||||||
border: `2px solid ${theme.palette.primary.main}`,
|
|
||||||
width: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<SimpleBar
|
|
||||||
sx={{
|
|
||||||
overflowX: 'hidden',
|
|
||||||
overflowY: 'auto',
|
|
||||||
maxHeight: 'calc(100vh - 170px)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{navCollapse}
|
|
||||||
</SimpleBar>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</PopperStyled>
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
|
||||||
{drawerOpen && (
|
|
||||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
|
||||||
<List sx={{ p: 0 }}>{navCollapse}</List>
|
|
||||||
</Collapse>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<ListItemButton
|
|
||||||
id={`boundary-${popperId}`}
|
|
||||||
disableRipple
|
|
||||||
selected={isSelected}
|
|
||||||
onMouseEnter={handleHover}
|
|
||||||
onMouseLeave={handleClose}
|
|
||||||
onClick={handleHover}
|
|
||||||
aria-describedby={popperId}
|
|
||||||
sx={{
|
|
||||||
'&.Mui-selected': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box onClick={handlerIconLink} sx={FlexBox}>
|
|
||||||
{menuIcon && (
|
|
||||||
<ListItemIcon
|
|
||||||
sx={{
|
|
||||||
my: 'auto',
|
|
||||||
minWidth: !menu.icon ? 18 : 36
|
|
||||||
// color: theme.palette.secondary.dark
|
|
||||||
// color:'white'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{menuIcon}
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
{!menuIcon && level !== 1 && (
|
|
||||||
<ListItemIcon
|
|
||||||
sx={{ my: 'auto', minWidth: !menu.icon ? 18 : 36, bgcolor: 'transparent', '&:hover': { bgcolor: 'transparent' } }}
|
|
||||||
>
|
|
||||||
<Dot size={4} color={isSelected ? 'primary' : 'secondary'} />
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
// color="inherit"
|
|
||||||
// color="white"
|
|
||||||
sx={{ my: 'auto' }}
|
|
||||||
>
|
|
||||||
{menu.title}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{miniMenuOpened ? <RightOutlined /> : <DownOutlined />}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{anchorEl && (
|
|
||||||
<PopperStyled
|
|
||||||
id={popperId}
|
|
||||||
open={miniMenuOpened}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
placement="right-start"
|
|
||||||
style={{
|
|
||||||
zIndex: 2001
|
|
||||||
}}
|
|
||||||
modifiers={[
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [-10, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions in={miniMenuOpened} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
overflow: 'hidden',
|
|
||||||
mt: 1.5,
|
|
||||||
py: 0.5,
|
|
||||||
boxShadow: theme.shadows[8],
|
|
||||||
backgroundImage: 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<SimpleBar
|
|
||||||
sx={{
|
|
||||||
overflowX: 'hidden',
|
|
||||||
overflowY: 'auto',
|
|
||||||
maxHeight: 'calc(100vh - 170px)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{navCollapse}
|
|
||||||
</SimpleBar>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</PopperStyled>
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
NavCollapse.propTypes = {
|
|
||||||
menu: PropTypes.object,
|
|
||||||
level: PropTypes.number,
|
|
||||||
parentId: PropTypes.string,
|
|
||||||
setSelectedItems: PropTypes.func,
|
|
||||||
selectedItems: PropTypes.string,
|
|
||||||
setSelectedLevel: PropTypes.func,
|
|
||||||
selectedLevel: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NavCollapse;
|
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Fragment, useEffect, useState } from 'react';
|
|
||||||
import { useLocation } from 'react-router';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled, useTheme } from '@mui/material/styles';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
ClickAwayListener,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
Paper,
|
|
||||||
Popper,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// third-party
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import NavItem from './NavItem';
|
|
||||||
import NavCollapse from './NavCollapse';
|
|
||||||
import SimpleBar from 'components/third-party/SimpleBar';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
|
|
||||||
import { MenuOrientation } from 'config';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
import { dispatch, useSelector } from 'store';
|
|
||||||
import { activeID } from 'store/reducers/menu';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { DownOutlined, RightOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| NAVIGATION - LIST GROUP ||============================== //
|
|
||||||
|
|
||||||
const PopperStyled = styled(Popper)(({ theme }) => ({
|
|
||||||
overflow: 'visible',
|
|
||||||
zIndex: 1202,
|
|
||||||
minWidth: 180,
|
|
||||||
'&:before': {
|
|
||||||
content: '""',
|
|
||||||
display: 'block',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 5,
|
|
||||||
left: 32,
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
transform: 'translateY(-50%) rotate(45deg)',
|
|
||||||
zIndex: 120,
|
|
||||||
borderWidth: '6px',
|
|
||||||
borderStyle: 'solid',
|
|
||||||
borderColor: `${theme.palette.background.paper} transparent transparent ${theme.palette.background.paper}`
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const NavGroup = ({ item, lastItem, remItems, lastItemId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { pathname } = useLocation();
|
|
||||||
|
|
||||||
const { menuOrientation } = useConfig();
|
|
||||||
const menu = useSelector((state) => state.menu);
|
|
||||||
const { drawerOpen, selectedID } = menu;
|
|
||||||
|
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
|
||||||
const [currentItem, setCurrentItem] = useState(item);
|
|
||||||
|
|
||||||
const openMini = Boolean(anchorEl);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (lastItem) {
|
|
||||||
if (item.id === lastItemId) {
|
|
||||||
const localItem = { ...item };
|
|
||||||
const elements = remItems.map((ele) => ele.elements);
|
|
||||||
localItem.children = elements.flat(1);
|
|
||||||
setCurrentItem(localItem);
|
|
||||||
} else {
|
|
||||||
setCurrentItem(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [item, lastItem, downLG]);
|
|
||||||
|
|
||||||
const checkOpenForParent = (child, id) => {
|
|
||||||
child.forEach((ele) => {
|
|
||||||
if (ele.children?.length) {
|
|
||||||
checkOpenForParent(ele.children, currentItem.id);
|
|
||||||
}
|
|
||||||
if (ele.url === pathname) {
|
|
||||||
dispatch(activeID(id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const checkSelectedOnload = (data) => {
|
|
||||||
const childrens = data.children ? data.children : [];
|
|
||||||
childrens.forEach((itemCheck) => {
|
|
||||||
if (itemCheck.children?.length) {
|
|
||||||
checkOpenForParent(itemCheck.children, currentItem.id);
|
|
||||||
}
|
|
||||||
if (itemCheck.url === pathname) {
|
|
||||||
dispatch(activeID(currentItem.id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
checkSelectedOnload(currentItem);
|
|
||||||
if (openMini) setAnchorEl(null);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [pathname, currentItem]);
|
|
||||||
|
|
||||||
const handleClick = (event) => {
|
|
||||||
if (!openMini) {
|
|
||||||
setAnchorEl(event?.currentTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Icon = currentItem?.icon;
|
|
||||||
const itemIcon = currentItem?.icon ? (
|
|
||||||
<Icon
|
|
||||||
style={{
|
|
||||||
fontSize: 20,
|
|
||||||
stroke: '1.5',
|
|
||||||
color: selectedID === currentItem.id ? theme.palette.primary.main : theme.palette.secondary.dark
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
const navCollapse = item.children?.map((menuItem) => {
|
|
||||||
switch (menuItem.type) {
|
|
||||||
case 'collapse':
|
|
||||||
return (
|
|
||||||
<NavCollapse
|
|
||||||
key={menuItem.id}
|
|
||||||
menu={menuItem}
|
|
||||||
setSelectedItems={setSelectedItems}
|
|
||||||
setSelectedLevel={setSelectedLevel}
|
|
||||||
selectedLevel={selectedLevel}
|
|
||||||
selectedItems={selectedItems}
|
|
||||||
level={1}
|
|
||||||
parentId={currentItem.id}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'item':
|
|
||||||
return <NavItem key={menuItem.id} item={menuItem} level={1} />;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const moreItems = remItems.map((itemRem, i) => (
|
|
||||||
<Fragment key={i}>
|
|
||||||
{itemRem.title && (
|
|
||||||
<Typography variant="caption" sx={{ pl: 2 }}>
|
|
||||||
{itemRem.title}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
{itemRem?.elements?.map((menu) => {
|
|
||||||
switch (menu.type) {
|
|
||||||
case 'collapse':
|
|
||||||
return (
|
|
||||||
<NavCollapse
|
|
||||||
key={menu.id}
|
|
||||||
menu={menu}
|
|
||||||
level={1}
|
|
||||||
parentId={currentItem.id}
|
|
||||||
setSelectedItems={setSelectedItems}
|
|
||||||
setSelectedLevel={setSelectedLevel}
|
|
||||||
selectedLevel={selectedLevel}
|
|
||||||
selectedItems={selectedItems}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'item':
|
|
||||||
return <NavItem key={menu.id} item={menu} level={1} />;
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Typography key={menu.id} variant="h6" color="error" align="center">
|
|
||||||
Menu Items Error
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</Fragment>
|
|
||||||
));
|
|
||||||
|
|
||||||
// menu list collapse & items
|
|
||||||
const items = currentItem.children?.map((menu) => {
|
|
||||||
switch (menu.type) {
|
|
||||||
case 'collapse':
|
|
||||||
return (
|
|
||||||
<NavCollapse
|
|
||||||
key={menu.id}
|
|
||||||
menu={menu}
|
|
||||||
level={1}
|
|
||||||
parentId={currentItem.id}
|
|
||||||
setSelectedItems={setSelectedItems}
|
|
||||||
setSelectedLevel={setSelectedLevel}
|
|
||||||
selectedLevel={selectedLevel}
|
|
||||||
selectedItems={selectedItems}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'item':
|
|
||||||
return <NavItem key={menu.id} item={menu} level={1} />;
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Typography key={menu.id} variant="h6" color="error" align="center">
|
|
||||||
Menu Items Error
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const popperId = openMini ? `group-pop-${item.id}` : undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{menuOrientation === MenuOrientation.VERTICAL || downLG ? (
|
|
||||||
<List
|
|
||||||
subheader={
|
|
||||||
item.title &&
|
|
||||||
drawerOpen && (
|
|
||||||
<Box sx={{ pl: 3, mb: 1.5 }}>
|
|
||||||
<Typography
|
|
||||||
variant="subtitle2"
|
|
||||||
sx={{ color: '#fff' }}
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</Typography>
|
|
||||||
{item.caption && (
|
|
||||||
<Typography variant="caption" color="secondary">
|
|
||||||
{item.caption}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{ mt: drawerOpen && item.title ? 1.5 : 0, py: 0, zIndex: 0 }}
|
|
||||||
>
|
|
||||||
{navCollapse}
|
|
||||||
</List>
|
|
||||||
) : (
|
|
||||||
<List>
|
|
||||||
<ListItemButton
|
|
||||||
selected={selectedID === currentItem.id}
|
|
||||||
sx={{
|
|
||||||
p: 1,
|
|
||||||
my: 0.5,
|
|
||||||
mr: 1,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: 'inherit',
|
|
||||||
'&.Mui-selected': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseEnter={handleClick}
|
|
||||||
onClick={handleClick}
|
|
||||||
onMouseLeave={handleClose}
|
|
||||||
aria-describedby={popperId}
|
|
||||||
>
|
|
||||||
{itemIcon && (
|
|
||||||
<ListItemIcon sx={{ minWidth: 28 }}>
|
|
||||||
{currentItem.id === lastItemId ? <DownOutlined style={{ fontSize: 20, stroke: '1.5' }} /> : itemIcon}
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
<ListItemText
|
|
||||||
sx={{ mr: 1 }}
|
|
||||||
primary={
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
color={selectedID === currentItem.id ? theme.palette.primary.main : theme.palette.secondary.dark}
|
|
||||||
>
|
|
||||||
{currentItem.id === lastItemId ? <FormattedMessage id="More Items" /> : currentItem.title}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{openMini ? (
|
|
||||||
<DownOutlined style={{ fontSize: 16, stroke: '1.5' }} />
|
|
||||||
) : (
|
|
||||||
<RightOutlined style={{ fontSize: 16, stroke: '1.5' }} />
|
|
||||||
)}
|
|
||||||
{anchorEl && (
|
|
||||||
<PopperStyled
|
|
||||||
id={popperId}
|
|
||||||
open={openMini}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
placement="bottom-start"
|
|
||||||
style={{
|
|
||||||
zIndex: 2001
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions in={openMini} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
mt: 0.5,
|
|
||||||
py: 1.25,
|
|
||||||
boxShadow: theme.shadows[8],
|
|
||||||
backgroundImage: 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<SimpleBar
|
|
||||||
sx={{
|
|
||||||
overflowX: 'hidden',
|
|
||||||
overflowY: 'auto',
|
|
||||||
maxHeight: 'calc(100vh - 170px)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{currentItem.id !== lastItemId ? items : moreItems}
|
|
||||||
</SimpleBar>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</PopperStyled>
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
NavGroup.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
lastItem: PropTypes.number,
|
|
||||||
remItems: PropTypes.array,
|
|
||||||
lastItemId: PropTypes.string,
|
|
||||||
setSelectedItems: PropTypes.func,
|
|
||||||
selectedItems: PropTypes.string,
|
|
||||||
setSelectedLevel: PropTypes.func,
|
|
||||||
selectedLevel: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NavGroup;
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { forwardRef, useEffect } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography, useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import Dot from 'components/@extended/Dot';
|
|
||||||
|
|
||||||
import { MenuOrientation, ThemeMode } from 'config';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
import { activeItem, openDrawer, setSelectedMenu } from 'store/reducers/menu';
|
|
||||||
|
|
||||||
// ==============================|| NAVIGATION - LIST ITEM ||============================== //
|
|
||||||
|
|
||||||
const NavItem = ({ item, level }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const { menuOrientation } = useConfig();
|
|
||||||
const { drawerOpen, openItem } = useSelector((state) => state.menu);
|
|
||||||
|
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
let itemTarget = '_self';
|
|
||||||
if (item.target) {
|
|
||||||
itemTarget = '_blank';
|
|
||||||
}
|
|
||||||
|
|
||||||
let listItemProps = { component: forwardRef((props, ref) => <Link {...props} to={item.url} target={itemTarget} ref={ref} />) };
|
|
||||||
if (item?.external) {
|
|
||||||
listItemProps = { component: 'a', href: item.url, target: itemTarget };
|
|
||||||
}
|
|
||||||
|
|
||||||
const Icon = item.icon;
|
|
||||||
|
|
||||||
const isSelected = openItem.findIndex((id) => id === item.id) > -1;
|
|
||||||
|
|
||||||
const itemIcon = item.icon ? (
|
|
||||||
<Icon style={{ fontSize: drawerOpen ? '1rem' : '1.25rem', color: isSelected ? '#662582' : '#fff' }} />
|
|
||||||
) : (
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
// const { pathname } = useLocation();
|
|
||||||
const pathname = document.location.pathname;
|
|
||||||
|
|
||||||
// active menu item on page load
|
|
||||||
useEffect(() => {
|
|
||||||
if (pathname && pathname.includes('product-details')) {
|
|
||||||
if (item.url && item.url.includes('product-details')) {
|
|
||||||
dispatch(activeItem({ openItem: [item.id] }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathname && pathname.includes('kanban')) {
|
|
||||||
if (item.url && item.url.includes('kanban')) {
|
|
||||||
dispatch(activeItem({ openItem: [item.id] }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathname.includes(item.url)) {
|
|
||||||
dispatch(activeItem({ openItem: [item.id] }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [pathname]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(setSelectedMenu(pathname));
|
|
||||||
}, [pathname]);
|
|
||||||
|
|
||||||
const textColor = theme.palette.mode === ThemeMode.DARK ? 'grey.400' : '#fff';
|
|
||||||
const iconSelectedColor = theme.palette.mode === ThemeMode.DARK && drawerOpen ? 'text.primary' : 'primary.main';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{menuOrientation === MenuOrientation.VERTICAL || downLG ? (
|
|
||||||
<ListItemButton
|
|
||||||
{...listItemProps}
|
|
||||||
disabled={item.disabled}
|
|
||||||
selected={isSelected}
|
|
||||||
onClick={() => {
|
|
||||||
// dispatch(setSelectedMenu(item));
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
zIndex: 1201,
|
|
||||||
pl: drawerOpen ? `${level * 28}px` : 1.5,
|
|
||||||
py: !drawerOpen && level === 1 ? 1.25 : 1,
|
|
||||||
...(drawerOpen && {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: '#7b1fa2'
|
|
||||||
},
|
|
||||||
'&.Mui-selected': {
|
|
||||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter',
|
|
||||||
borderRight: `2px solid ${theme.palette.primary.main}`,
|
|
||||||
color: iconSelectedColor,
|
|
||||||
'&:hover': {
|
|
||||||
color: iconSelectedColor,
|
|
||||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen && {
|
|
||||||
bgcolor: '#662582',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: '#662582'
|
|
||||||
},
|
|
||||||
'&.Mui-selected': {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
},
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
{...(downLG && {
|
|
||||||
onClick: () => {
|
|
||||||
dispatch(openDrawer(false));
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{itemIcon && (
|
|
||||||
<ListItemIcon
|
|
||||||
sx={{
|
|
||||||
minWidth: 28,
|
|
||||||
...(!drawerOpen && {
|
|
||||||
borderRadius: 1.5,
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: '#7b1fa2'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen &&
|
|
||||||
isSelected && {
|
|
||||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.900' : 'primary.lighter',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.darker' : 'primary.lighter'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{itemIcon}
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
{(drawerOpen || (!drawerOpen && level !== 1)) && (
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6" sx={{ color: isSelected ? iconSelectedColor : textColor, whiteSpace: 'nowrap' }}>
|
|
||||||
{item.title}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(drawerOpen || (!drawerOpen && level !== 1)) && item.chip && (
|
|
||||||
<Chip
|
|
||||||
color={item.chip.color}
|
|
||||||
variant={item.chip.variant}
|
|
||||||
size={item.chip.size}
|
|
||||||
label={item.chip.label}
|
|
||||||
avatar={item.chip.avatar && <Avatar>{item.chip.avatar}</Avatar>}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
|
||||||
) : (
|
|
||||||
<ListItemButton
|
|
||||||
{...listItemProps}
|
|
||||||
disabled={item.disabled}
|
|
||||||
selected={isSelected}
|
|
||||||
sx={{
|
|
||||||
zIndex: 1201,
|
|
||||||
...(drawerOpen && {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
},
|
|
||||||
'&.Mui-selected': {
|
|
||||||
bgcolor: 'transparent',
|
|
||||||
color: iconSelectedColor,
|
|
||||||
'&:hover': {
|
|
||||||
color: iconSelectedColor,
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen && {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
},
|
|
||||||
'&.Mui-selected': {
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
},
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{itemIcon && (
|
|
||||||
<ListItemIcon
|
|
||||||
sx={{
|
|
||||||
minWidth: 36,
|
|
||||||
...(!drawerOpen && {
|
|
||||||
borderRadius: 1.5,
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen &&
|
|
||||||
isSelected && {
|
|
||||||
bgcolor: 'transparent',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{itemIcon}
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!itemIcon && (
|
|
||||||
<ListItemIcon
|
|
||||||
sx={{
|
|
||||||
color: isSelected ? 'primary.main' : 'secondary.main',
|
|
||||||
...(!drawerOpen && {
|
|
||||||
borderRadius: 1.5,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...(!drawerOpen &&
|
|
||||||
isSelected && {
|
|
||||||
bgcolor: 'transparent',
|
|
||||||
'&:hover': {
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Dot size={4} color={isSelected ? 'primary' : 'secondary'} />
|
|
||||||
</ListItemIcon>
|
|
||||||
)}
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6" color="inherit">
|
|
||||||
{item.title}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{(drawerOpen || (!drawerOpen && level !== 1)) && item.chip && (
|
|
||||||
<Chip
|
|
||||||
color={item.chip.color}
|
|
||||||
variant={item.chip.variant}
|
|
||||||
size={item.chip.size}
|
|
||||||
label={item.chip.label}
|
|
||||||
avatar={item.chip.avatar && <Avatar>{item.chip.avatar}</Avatar>}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
NavItem.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
level: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NavItem;
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import { useLayoutEffect, useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Box, Typography, useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
import { HORIZONTAL_MAX_ITEM, MenuOrientation } from 'config';
|
|
||||||
import { useSelector } from 'store';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import NavGroup from './NavGroup';
|
|
||||||
import menuItem from 'menu-items';
|
|
||||||
|
|
||||||
// ==============================|| DRAWER CONTENT - NAVIGATION ||============================== //
|
|
||||||
|
|
||||||
const Navigation = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
const { menuOrientation } = useConfig();
|
|
||||||
const { drawerOpen } = useSelector((state) => state.menu);
|
|
||||||
const [selectedItems, setSelectedItems] = useState('');
|
|
||||||
const [selectedLevel, setSelectedLevel] = useState(0);
|
|
||||||
const [menuItems, setMenuItems] = useState({ items: [] });
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
setMenuItems(menuItem);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [menuItem]);
|
|
||||||
|
|
||||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
|
||||||
|
|
||||||
const lastItem = isHorizontal ? HORIZONTAL_MAX_ITEM : null;
|
|
||||||
let lastItemIndex = menuItems.items.length - 1;
|
|
||||||
let remItems = [];
|
|
||||||
let lastItemId;
|
|
||||||
|
|
||||||
// first it checks menu item is more than giving HORIZONTAL_MAX_ITEM after that get lastItemid by giving horizontal max
|
|
||||||
// item and it sets horizontal menu by giving horizontal max item lastly slice menuItem from array and set into remItems
|
|
||||||
|
|
||||||
if (lastItem && lastItem < menuItems.items.length) {
|
|
||||||
lastItemId = menuItems.items[lastItem - 1].id;
|
|
||||||
lastItemIndex = lastItem - 1;
|
|
||||||
remItems = menuItems.items.slice(lastItem - 1, menuItems.items.length).map((item) => ({
|
|
||||||
title: item.title,
|
|
||||||
elements: item.children,
|
|
||||||
icon: item.icon
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const navGroups = menuItems.items.slice(0, lastItemIndex + 1).map((item) => {
|
|
||||||
switch (item.type) {
|
|
||||||
case 'group':
|
|
||||||
return (
|
|
||||||
<NavGroup
|
|
||||||
key={item.id}
|
|
||||||
setSelectedItems={setSelectedItems}
|
|
||||||
setSelectedLevel={setSelectedLevel}
|
|
||||||
selectedLevel={selectedLevel}
|
|
||||||
selectedItems={selectedItems}
|
|
||||||
lastItem={lastItem}
|
|
||||||
remItems={remItems}
|
|
||||||
lastItemId={lastItemId}
|
|
||||||
item={item}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Typography key={item.id} variant="h6" color="error" align="center">
|
|
||||||
Fix - Navigation Group
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
pt: drawerOpen ? (isHorizontal ? 0 : 2) : 0,
|
|
||||||
'& > ul:first-of-type': { mt: 0 },
|
|
||||||
display: isHorizontal ? { xs: 'block', lg: 'flex' } : 'block'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{navGroups}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Navigation;
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { useMediaQuery, useTheme } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import NavCard from './NavCard';
|
|
||||||
import Navigation from './Navigation';
|
|
||||||
import SimpleBar from 'components/third-party/SimpleBar';
|
|
||||||
import { useSelector } from 'store';
|
|
||||||
|
|
||||||
// ==============================|| DRAWER CONTENT ||============================== //
|
|
||||||
|
|
||||||
const DrawerContent = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
const menu = useSelector((state) => state.menu);
|
|
||||||
const { drawerOpen } = menu;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SimpleBar
|
|
||||||
sx={{
|
|
||||||
'& .simplebar-content': {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column'
|
|
||||||
},
|
|
||||||
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Navigation />
|
|
||||||
{/* {drawerOpen && !matchDownMD && <NavCard />} */}
|
|
||||||
</SimpleBar>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DrawerContent;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
import { Box } from '@mui/material';
|
|
||||||
|
|
||||||
// ==============================|| DRAWER HEADER - STYLED ||============================== //
|
|
||||||
|
|
||||||
const DrawerHeaderStyled = styled(Box, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
|
|
||||||
...theme.mixins.toolbar,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: open ? 'flex-start' : 'center',
|
|
||||||
paddingLeft: theme.spacing(open ? 3 : 0)
|
|
||||||
}));
|
|
||||||
|
|
||||||
DrawerHeaderStyled.propTypes = {
|
|
||||||
theme: PropTypes.object,
|
|
||||||
open: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DrawerHeaderStyled;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import DrawerHeaderStyled from './DrawerHeaderStyled';
|
|
||||||
import Logo from 'components/logo';
|
|
||||||
|
|
||||||
import { MenuOrientation } from 'config';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
|
|
||||||
import logo from 'assets/images/logo-nearle9.png';
|
|
||||||
import logo1 from 'assets/images/logo-sm1.png';
|
|
||||||
// ==============================|| DRAWER HEADER ||============================== //
|
|
||||||
|
|
||||||
const DrawerHeader = ({ open }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
const { menuOrientation } = useConfig();
|
|
||||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DrawerHeaderStyled
|
|
||||||
theme={theme}
|
|
||||||
open={open}
|
|
||||||
sx={{
|
|
||||||
minHeight: isHorizontal ? 'unset' : '60px',
|
|
||||||
width: isHorizontal ? { xs: '100%', lg: '424px' } : 'inherit',
|
|
||||||
paddingTop: isHorizontal ? { xs: '10px', lg: '0' } : '8px',
|
|
||||||
paddingBottom: isHorizontal ? { xs: '18px', lg: '0' } : '8px',
|
|
||||||
paddingLeft: isHorizontal ? { xs: '24px', lg: '0' } : open ? '24px' : 0
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* <Logo isIcon={!open} sx={{ width: open ? 'auto' : 35, height: 35 }} /> */}
|
|
||||||
|
|
||||||
{open && (
|
|
||||||
<img
|
|
||||||
src={logo}
|
|
||||||
// width='160px'
|
|
||||||
// height='45px'
|
|
||||||
// width='170px'
|
|
||||||
alt="logo"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!open && <img src={logo1} width="40px" alt="logo" />}
|
|
||||||
</DrawerHeaderStyled>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DrawerHeader.propTypes = {
|
|
||||||
open: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DrawerHeader;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { AppBar, Box, Container, useScrollTrigger } from '@mui/material';
|
|
||||||
|
|
||||||
// project imports
|
|
||||||
import Navigation from './DrawerContent/Navigation';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
|
|
||||||
// ==============================|| HORIZONTAL MENU LIST ||============================== //
|
|
||||||
|
|
||||||
function ElevationScroll({ children, window }) {
|
|
||||||
const theme = useTheme();
|
|
||||||
// Note that you normally won't need to set the window ref as useScrollTrigger
|
|
||||||
// will default to window.
|
|
||||||
// This is only being set here because the demo is in an iframe.
|
|
||||||
const trigger = useScrollTrigger({
|
|
||||||
disableHysteresis: true,
|
|
||||||
threshold: 0,
|
|
||||||
target: window
|
|
||||||
});
|
|
||||||
|
|
||||||
theme.shadows[4] = theme.customShadows.z1;
|
|
||||||
|
|
||||||
return React.cloneElement(children, {
|
|
||||||
elevation: trigger ? 4 : 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==============================|| HORIZONTAL MENU LIST ||============================== //
|
|
||||||
|
|
||||||
const CustomAppBar = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { container } = useConfig();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ElevationScroll>
|
|
||||||
<AppBar
|
|
||||||
sx={{
|
|
||||||
top: 60,
|
|
||||||
bgcolor: theme.palette.background.paper,
|
|
||||||
width: '100%',
|
|
||||||
height: 62,
|
|
||||||
justifyContent: 'center',
|
|
||||||
borderTop: `1px solid ${theme.palette.divider}`,
|
|
||||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
||||||
zIndex: 1098,
|
|
||||||
color: theme.palette.grey[500]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Container maxWidth={container ? 'xl' : false}>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
<Navigation />
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
</AppBar>
|
|
||||||
</ElevationScroll>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomAppBar;
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
import Drawer from '@mui/material/Drawer';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import { DRAWER_WIDTH, ThemeMode } from 'config';
|
|
||||||
|
|
||||||
const openedMixin = (theme) => ({
|
|
||||||
width: DRAWER_WIDTH,
|
|
||||||
// borderRight: `1px solid ${theme.palette.divider}`,
|
|
||||||
borderRight: 'none',
|
|
||||||
transition: theme.transitions.create('width', {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.enteringScreen
|
|
||||||
}),
|
|
||||||
overflowX: 'hidden',
|
|
||||||
boxShadow: theme.palette.mode === ThemeMode.DARK ? theme.customShadows.z1 : 'none',
|
|
||||||
backgroundColor:'#662582',
|
|
||||||
});
|
|
||||||
|
|
||||||
const closedMixin = (theme) => ({
|
|
||||||
transition: theme.transitions.create('width', {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen
|
|
||||||
}),
|
|
||||||
// overflowX: 'hidden',
|
|
||||||
width: theme.spacing(7.5),
|
|
||||||
borderRight: 'none',
|
|
||||||
boxShadow: theme.customShadows.z1,
|
|
||||||
backgroundColor:'#662582',
|
|
||||||
});
|
|
||||||
|
|
||||||
// ==============================|| DRAWER - MINI STYLED ||============================== //
|
|
||||||
|
|
||||||
const MiniDrawerStyled = styled(Drawer, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
|
|
||||||
width: DRAWER_WIDTH,
|
|
||||||
flexShrink: 0,
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
|
|
||||||
...(open && {
|
|
||||||
...openedMixin(theme),
|
|
||||||
'& .MuiDrawer-paper': openedMixin(theme)
|
|
||||||
}),
|
|
||||||
...(!open && {
|
|
||||||
...closedMixin(theme),
|
|
||||||
'& .MuiDrawer-paper': closedMixin(theme)
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default MiniDrawerStyled;
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Box, Drawer, useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import DrawerHeader from './DrawerHeader';
|
|
||||||
import DrawerContent from './DrawerContent';
|
|
||||||
import MiniDrawerStyled from './MiniDrawerStyled';
|
|
||||||
|
|
||||||
import { DRAWER_WIDTH } from 'config';
|
|
||||||
import { dispatch, useSelector } from 'store';
|
|
||||||
import { openDrawer } from 'store/reducers/menu';
|
|
||||||
|
|
||||||
// ==============================|| MAIN LAYOUT - DRAWER ||============================== //
|
|
||||||
|
|
||||||
const MainDrawer = ({ window }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
const menu = useSelector((state) => state.menu);
|
|
||||||
const { drawerOpen } = menu;
|
|
||||||
|
|
||||||
// responsive drawer container
|
|
||||||
const container = window !== undefined ? () => window().document.body : undefined;
|
|
||||||
|
|
||||||
// header content
|
|
||||||
const drawerContent = useMemo(() => <DrawerContent />, []);
|
|
||||||
const drawerHeader = useMemo(() => <DrawerHeader open={drawerOpen} />, [drawerOpen]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box component="nav" sx={{ flexShrink: { md: 0 }, zIndex: 1200 }} aria-label="mailbox folders">
|
|
||||||
{!matchDownMD ? (
|
|
||||||
<MiniDrawerStyled
|
|
||||||
variant="permanent"
|
|
||||||
open={drawerOpen}
|
|
||||||
>
|
|
||||||
{drawerHeader}
|
|
||||||
{drawerContent}
|
|
||||||
</MiniDrawerStyled>
|
|
||||||
) : (
|
|
||||||
<Drawer
|
|
||||||
container={container}
|
|
||||||
variant="temporary"
|
|
||||||
open={drawerOpen}
|
|
||||||
onClose={() => dispatch(openDrawer(!drawerOpen))}
|
|
||||||
ModalProps={{ keepMounted: true }}
|
|
||||||
sx={{
|
|
||||||
display: { xs: 'block', lg: 'none' },
|
|
||||||
'& .MuiDrawer-paper': {
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
width: DRAWER_WIDTH,
|
|
||||||
borderRight: `1px solid ${theme.palette.divider}`,
|
|
||||||
backgroundImage: 'none',
|
|
||||||
boxShadow: 'inherit',
|
|
||||||
bgcolor:'#662582'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{drawerHeader}
|
|
||||||
{drawerContent}
|
|
||||||
</Drawer>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
MainDrawer.propTypes = {
|
|
||||||
window: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MainDrawer;
|
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { HStack } from '@astryxdesign/core/HStack';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
// material-ui
|
import { Link } from '@astryxdesign/core/Link';
|
||||||
import { Link, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
const Footer = () => (
|
const Footer = () => (
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ p: '24px 16px 0px', mt: 'auto' }}>
|
<HStack justify="space-between" vAlign="center" style={{ padding: '24px 16px 0px', marginTop: 'auto' }}>
|
||||||
<Typography variant="caption">© All rights reserved</Typography>
|
<Text type="supporting">© All rights reserved</Text>
|
||||||
<Stack spacing={1.5} direction="row" justifyContent="space-between" alignItems="center">
|
<HStack gap={1.5} justify="space-between" vAlign="center">
|
||||||
<Link component={RouterLink} to="https://nearle.in/aboutus" target="_blank" variant="caption" color="textPrimary">
|
<Link href="https://nearle.in/aboutus" target="_blank" isExternalLink type="supporting">
|
||||||
About us
|
About us
|
||||||
</Link>
|
</Link>
|
||||||
<Link component={RouterLink} to="https://nearle.in/privacy" target="_blank" variant="caption" color="textPrimary">
|
<Link href="https://nearle.in/privacy" target="_blank" isExternalLink type="supporting">
|
||||||
Privacy
|
Privacy
|
||||||
</Link>
|
</Link>
|
||||||
<Link component={RouterLink} to="https://nearle.in/terms" target="_blank" variant="caption" color="textPrimary">
|
<Link href="https://nearle.in/terms" target="_blank" isExternalLink type="supporting">
|
||||||
Terms
|
Terms
|
||||||
</Link>
|
</Link>
|
||||||
</Stack>
|
</HStack>
|
||||||
</Stack>
|
</HStack>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { styled } from '@mui/material/styles';
|
|
||||||
import AppBar from '@mui/material/AppBar';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import { DRAWER_WIDTH } from 'config';
|
|
||||||
|
|
||||||
// ==============================|| HEADER - APP BAR STYLED ||============================== //
|
|
||||||
|
|
||||||
const AppBarStyled = styled(AppBar, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
|
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
|
||||||
transition: theme.transitions.create(['width', 'margin'], {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen
|
|
||||||
}),
|
|
||||||
...(!open && {
|
|
||||||
width: `calc(100% - ${theme.spacing(7.5)})`
|
|
||||||
}),
|
|
||||||
...(open && {
|
|
||||||
marginLeft: DRAWER_WIDTH,
|
|
||||||
width: `calc(100% - ${DRAWER_WIDTH}px)`,
|
|
||||||
transition: theme.transitions.create(['width', 'margin'], {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.enteringScreen
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
|
|
||||||
AppBarStyled.propTypes = {
|
|
||||||
open: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppBarStyled;
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
import { useRef, useState } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Box,
|
|
||||||
CardMedia,
|
|
||||||
ClickAwayListener,
|
|
||||||
Grid,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
ListSubheader,
|
|
||||||
Paper,
|
|
||||||
Popper,
|
|
||||||
Stack,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
import Dot from 'components/@extended/Dot';
|
|
||||||
import IconButton from 'components/@extended/IconButton';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
import { DRAWER_WIDTH, ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { ArrowRightOutlined, WindowsOutlined } from '@ant-design/icons';
|
|
||||||
import backgroundVector from 'assets/images/mega-menu/back.svg';
|
|
||||||
import imageChart from 'assets/images/mega-menu/chart.svg';
|
|
||||||
import AnimateButton from 'components/@extended/AnimateButton';
|
|
||||||
|
|
||||||
// ==============================|| HEADER CONTENT - MEGA MENU SECTION ||============================== //
|
|
||||||
|
|
||||||
const MegaMenuSection = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const anchorRef = useRef(null);
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen((prevOpen) => !prevOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (event) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
|
||||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
|
||||||
<IconButton
|
|
||||||
color="secondary"
|
|
||||||
variant="light"
|
|
||||||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
|
||||||
aria-label="open profile"
|
|
||||||
ref={anchorRef}
|
|
||||||
aria-controls={open ? 'profile-grow' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
// onClick={handleToggle}
|
|
||||||
>
|
|
||||||
<WindowsOutlined />
|
|
||||||
</IconButton>
|
|
||||||
<Popper
|
|
||||||
placement="bottom"
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorRef.current}
|
|
||||||
role={undefined}
|
|
||||||
transition
|
|
||||||
disablePortal
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [-180, 9]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions type="grow" position="top" in={open} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
boxShadow: theme.customShadows.z1,
|
|
||||||
minWidth: 750,
|
|
||||||
width: {
|
|
||||||
md: `calc(100vw - 100px)`,
|
|
||||||
lg: `calc(100vw - ${DRAWER_WIDTH + 100}px)`,
|
|
||||||
xl: `calc(100vw - ${DRAWER_WIDTH + 140}px)`
|
|
||||||
},
|
|
||||||
maxWidth: 1024
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<MainCard elevation={0} border={false} content={false}>
|
|
||||||
<Grid container>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
md={4}
|
|
||||||
sx={{
|
|
||||||
background: `url(${backgroundVector}), linear-gradient(183.77deg, ${theme.palette.primary.main} 11.46%, ${theme.palette.primary[700]} 100.33%)`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ p: 4.5, pb: 3 }}>
|
|
||||||
<Stack sx={{ color: 'background.paper' }}>
|
|
||||||
<Typography variant="h2" sx={{ fontSize: '1.875rem', mb: 1 }}>
|
|
||||||
Explore Components
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h6">
|
|
||||||
Try our pre made component pages to check how it feels and suits as per your need.
|
|
||||||
</Typography>
|
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="flex-end" sx={{ mt: -1 }}>
|
|
||||||
<AnimateButton>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="secondary"
|
|
||||||
sx={{
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
color: 'text.primary',
|
|
||||||
'&:hover': { bgcolor: 'background.paper', color: 'text.primary' }
|
|
||||||
}}
|
|
||||||
endIcon={<ArrowRightOutlined />}
|
|
||||||
component={Link}
|
|
||||||
to="/components-overview/buttons"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
View All
|
|
||||||
</Button>
|
|
||||||
</AnimateButton>
|
|
||||||
<CardMedia component="img" src={imageChart} alt="Chart" sx={{ mr: -2.5, mb: -2.5, width: 124 }} />
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
<Grid item md={8}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
p: 4,
|
|
||||||
'& .MuiList-root': {
|
|
||||||
pb: 0
|
|
||||||
},
|
|
||||||
'& .MuiListSubheader-root': {
|
|
||||||
p: 0,
|
|
||||||
pb: 1.5
|
|
||||||
},
|
|
||||||
'& .MuiListItemButton-root': {
|
|
||||||
p: 0.5,
|
|
||||||
'&:hover': {
|
|
||||||
background: 'transparent',
|
|
||||||
'& .MuiTypography-root': {
|
|
||||||
color: 'primary.main'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid container spacing={6}>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<List
|
|
||||||
component="nav"
|
|
||||||
aria-labelledby="nested-list-user"
|
|
||||||
subheader={
|
|
||||||
<ListSubheader id="nested-list-user">
|
|
||||||
<Typography variant="subtitle1" color="textPrimary">
|
|
||||||
Authentication
|
|
||||||
</Typography>
|
|
||||||
</ListSubheader>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/login">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Login" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/register">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Register" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/reset-password">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Reset Password" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/forgot-password">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Forgot Password" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/auth/code-verification">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Verification Code" />
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<List
|
|
||||||
component="nav"
|
|
||||||
aria-labelledby="nested-list-user"
|
|
||||||
subheader={
|
|
||||||
<ListSubheader id="nested-list-user">
|
|
||||||
<Typography variant="subtitle1" color="textPrimary">
|
|
||||||
Other Pages
|
|
||||||
</Typography>
|
|
||||||
</ListSubheader>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="About us" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/contact-us">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Contact us" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} to="/pricing">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Pricing" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} to="/apps/profiles/user/payment">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Payment" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/maintenance/under-construction">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Construction" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/maintenance/coming-soon">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Coming Soon" />
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<List
|
|
||||||
component="nav"
|
|
||||||
aria-labelledby="nested-list-user"
|
|
||||||
subheader={
|
|
||||||
<ListSubheader id="nested-list-user">
|
|
||||||
<Typography variant="subtitle1" color="textPrimary">
|
|
||||||
SAAS Pages
|
|
||||||
</Typography>
|
|
||||||
</ListSubheader>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/maintenance/404">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="404 Error" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton disableRipple component={Link} target="_blank" to="/">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Dot size={7} color="secondary" variant="outlined" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Landing" />
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</MainCard>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MegaMenuSection;
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
import { useRef, useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
ClickAwayListener,
|
|
||||||
Divider,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText,
|
|
||||||
ListItemSecondaryAction,
|
|
||||||
Paper,
|
|
||||||
Popper,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
import IconButton from 'components/@extended/IconButton';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
import { ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import avatar2 from 'assets/images/users/avatar-2.png';
|
|
||||||
import avatar3 from 'assets/images/users/avatar-3.png';
|
|
||||||
import avatar4 from 'assets/images/users/avatar-4.png';
|
|
||||||
import avatar5 from 'assets/images/users/avatar-5.png';
|
|
||||||
import { MailOutlined, CloseOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// sx styles
|
|
||||||
const avatarSX = {
|
|
||||||
width: 48,
|
|
||||||
height: 48
|
|
||||||
};
|
|
||||||
|
|
||||||
const actionSX = {
|
|
||||||
mt: '6px',
|
|
||||||
ml: 1,
|
|
||||||
top: 'auto',
|
|
||||||
right: 'auto',
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
transform: 'none'
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| HEADER CONTENT - MESSAGES ||============================== //
|
|
||||||
|
|
||||||
const Message = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const matchesXs = useMediaQuery(theme.breakpoints.down('md'));
|
|
||||||
|
|
||||||
const anchorRef = useRef(null);
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen((prevOpen) => !prevOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (event) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
|
||||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
|
||||||
<IconButton
|
|
||||||
color="secondary"
|
|
||||||
variant="light"
|
|
||||||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
|
||||||
aria-label="open profile"
|
|
||||||
ref={anchorRef}
|
|
||||||
aria-controls={open ? 'profile-grow' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={handleToggle}
|
|
||||||
>
|
|
||||||
<MailOutlined />
|
|
||||||
</IconButton>
|
|
||||||
<Popper
|
|
||||||
placement={matchesXs ? 'bottom' : 'bottom-end'}
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorRef.current}
|
|
||||||
role={undefined}
|
|
||||||
transition
|
|
||||||
disablePortal
|
|
||||||
sx={{
|
|
||||||
maxHeight: 'calc(100vh - 250px)',
|
|
||||||
overflow: 'auto'
|
|
||||||
}}
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [matchesXs ? -60 : 0, 9]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions type="grow" position={matchesXs ? 'top' : 'top-right'} in={open} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
boxShadow: theme.customShadows.z1,
|
|
||||||
width: '100%',
|
|
||||||
minWidth: 285,
|
|
||||||
maxWidth: 420,
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
maxWidth: 285
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<MainCard
|
|
||||||
title="Message"
|
|
||||||
elevation={0}
|
|
||||||
border={false}
|
|
||||||
content={false}
|
|
||||||
secondary={
|
|
||||||
<IconButton size="small" onClick={handleToggle}>
|
|
||||||
<CloseOutlined />
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<List
|
|
||||||
component="nav"
|
|
||||||
sx={{
|
|
||||||
p: 0,
|
|
||||||
'& .MuiListItemButton-root': {
|
|
||||||
py: 1.5,
|
|
||||||
'& .MuiAvatar-root': avatarSX,
|
|
||||||
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar alt="profile user" src={avatar2} />
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
It's{' '}
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Cristina danny's
|
|
||||||
</Typography>{' '}
|
|
||||||
birthday today.
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="2 min ago"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
3:00 AM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar alt="profile user" src={avatar3} />
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Aida Burg
|
|
||||||
</Typography>{' '}
|
|
||||||
commented your post.
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="5 August"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
6:00 PM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar alt="profile user" src={avatar4} />
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
There was a failure to your setup.
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="7 hours ago"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
2:45 PM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar alt="profile user" src={avatar5} />
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Cristina Danny
|
|
||||||
</Typography>{' '}
|
|
||||||
invited to join{' '}
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Meeting.
|
|
||||||
</Typography>
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="Daily scrum meeting time"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
9:10 PM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton sx={{ textAlign: 'center' }}>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6" color="primary">
|
|
||||||
View All
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
</MainCard>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Message;
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { AppBar, Box, ClickAwayListener, Paper, Popper, Toolbar } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import Search from './Search';
|
|
||||||
import Profile from './Profile';
|
|
||||||
import IconButton from 'components/@extended/IconButton';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
import { ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { MoreOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| HEADER CONTENT - MOBILE ||============================== //
|
|
||||||
|
|
||||||
const MobileSection = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const anchorRef = useRef(null);
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen((prevOpen) => !prevOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (event) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevOpen = useRef(open);
|
|
||||||
useEffect(() => {
|
|
||||||
if (prevOpen.current === true && open === false) {
|
|
||||||
anchorRef.current.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
prevOpen.current = open;
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
|
||||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
|
||||||
<IconButton
|
|
||||||
// sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
|
||||||
sx={{ color: '#fff', bgcolor: 'transparent', ml: { xs: 0, lg: -2 },
|
|
||||||
fontSize:'25px',
|
|
||||||
':hover':{
|
|
||||||
color: '#fff', bgcolor: 'transparent'
|
|
||||||
} }}
|
|
||||||
aria-label="open more menu"
|
|
||||||
ref={anchorRef}
|
|
||||||
aria-controls={open ? 'menu-list-grow' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={handleToggle}
|
|
||||||
color="secondary"
|
|
||||||
variant="light"
|
|
||||||
>
|
|
||||||
<MoreOutlined />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
<Popper
|
|
||||||
placement="bottom-end"
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorRef.current}
|
|
||||||
role={undefined}
|
|
||||||
transition
|
|
||||||
disablePortal
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [0, 9]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions type="fade" in={open} {...TransitionProps}>
|
|
||||||
<Paper sx={{ boxShadow: theme.customShadows.z1 }}>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<AppBar color="inherit">
|
|
||||||
<Toolbar>
|
|
||||||
{/* <Search /> */}
|
|
||||||
<Profile />
|
|
||||||
</Toolbar>
|
|
||||||
</AppBar>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MobileSection;
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
import { useRef, useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Badge,
|
|
||||||
Box,
|
|
||||||
ClickAwayListener,
|
|
||||||
Divider,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText,
|
|
||||||
ListItemSecondaryAction,
|
|
||||||
Paper,
|
|
||||||
Popper,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
useMediaQuery
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
import IconButton from 'components/@extended/IconButton';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
import { ThemeMode } from 'config';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { BellOutlined, CheckCircleOutlined, GiftOutlined, MessageOutlined, SettingOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// sx styles
|
|
||||||
const avatarSX = {
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
fontSize: '1rem'
|
|
||||||
};
|
|
||||||
|
|
||||||
const actionSX = {
|
|
||||||
mt: '6px',
|
|
||||||
ml: 1,
|
|
||||||
top: 'auto',
|
|
||||||
right: 'auto',
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
|
|
||||||
transform: 'none'
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| HEADER CONTENT - NOTIFICATION ||============================== //
|
|
||||||
|
|
||||||
const Notification = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const matchesXs = useMediaQuery(theme.breakpoints.down('md'));
|
|
||||||
|
|
||||||
const anchorRef = useRef(null);
|
|
||||||
const [read, setRead] = useState(0);
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen((prevOpen) => !prevOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (event) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
|
||||||
const iconBackColor = theme.palette.mode === ThemeMode.DARK ? 'background.default' : 'grey.100';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
|
||||||
<Tooltip title='Notifications'>
|
|
||||||
<IconButton
|
|
||||||
// color="secondary"
|
|
||||||
// variant="light"
|
|
||||||
// sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
|
|
||||||
sx={{ color: '#fff',
|
|
||||||
fontSize:'20px',
|
|
||||||
// bgcolor: open ? iconBackColorOpen : iconBackColor
|
|
||||||
':hover':{
|
|
||||||
bgcolor:'transparent',
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
// bgcolor:'transparent'
|
|
||||||
}}
|
|
||||||
|
|
||||||
aria-label="open profile"
|
|
||||||
ref={anchorRef}
|
|
||||||
aria-controls={open ? 'profile-grow' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
// onClick={handleToggle}
|
|
||||||
>
|
|
||||||
<Badge badgeContent={read}
|
|
||||||
// color="primary"
|
|
||||||
sx={{
|
|
||||||
"& .MuiBadge-badge": {
|
|
||||||
color: "#662582",
|
|
||||||
backgroundColor: "white"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BellOutlined />
|
|
||||||
</Badge>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Popper
|
|
||||||
placement={matchesXs ? 'bottom' : 'bottom-end'}
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorRef.current}
|
|
||||||
role={undefined}
|
|
||||||
transition
|
|
||||||
disablePortal
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [matchesXs ? -5 : 0, 9]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions type="grow" position={matchesXs ? 'top' : 'top-right'} sx={{ overflow: 'hidden' }} in={open} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
boxShadow: theme.customShadows.z1,
|
|
||||||
width: '100%',
|
|
||||||
minWidth: 285,
|
|
||||||
maxWidth: 420,
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
maxWidth: 285
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<MainCard
|
|
||||||
title="Notification"
|
|
||||||
elevation={0}
|
|
||||||
border={false}
|
|
||||||
content={false}
|
|
||||||
secondary={
|
|
||||||
<>
|
|
||||||
{read > 0 && (
|
|
||||||
<Tooltip title="Mark as all read">
|
|
||||||
<IconButton color="success" size="small" onClick={() => setRead(0)}>
|
|
||||||
<CheckCircleOutlined style={{ fontSize: '1.15rem' }} />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<List
|
|
||||||
component="nav"
|
|
||||||
sx={{
|
|
||||||
p: 0,
|
|
||||||
'& .MuiListItemButton-root': {
|
|
||||||
py: 0.5,
|
|
||||||
'&.Mui-selected': { bgcolor: 'grey.50', color: 'text.primary' },
|
|
||||||
'& .MuiAvatar-root': avatarSX,
|
|
||||||
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemButton selected={read > 0}>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
color: 'success.main',
|
|
||||||
bgcolor: 'success.lighter'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GiftOutlined />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
It's{' '}
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Cristina danny's
|
|
||||||
</Typography>{' '}
|
|
||||||
birthday today.
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="2 min ago"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
3:00 AM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton selected={read > 0}>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
color: 'primary.main',
|
|
||||||
bgcolor: 'primary.lighter'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MessageOutlined />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Aida Burg
|
|
||||||
</Typography>{' '}
|
|
||||||
commented your post.
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="5 August"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
6:00 PM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
color: 'error.main',
|
|
||||||
bgcolor: 'error.lighter'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SettingOutlined />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
Your Profile is Complete
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
60%
|
|
||||||
</Typography>{' '}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="7 hours ago"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
2:45 PM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
color: 'primary.main',
|
|
||||||
bgcolor: 'primary.lighter'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
C
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6">
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Cristina Danny
|
|
||||||
</Typography>{' '}
|
|
||||||
invited to join{' '}
|
|
||||||
<Typography component="span" variant="subtitle1">
|
|
||||||
Meeting.
|
|
||||||
</Typography>
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary="Daily scrum meeting time"
|
|
||||||
/>
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Typography variant="caption" noWrap>
|
|
||||||
9:10 PM
|
|
||||||
</Typography>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItemButton>
|
|
||||||
<Divider />
|
|
||||||
<ListItemButton sx={{ textAlign: 'center', py: `${12}px !important` }}>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="h6" color="primary">
|
|
||||||
View All
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
</MainCard>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Notification;
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
|
|
||||||
import { useNavigate } from 'react-router';
|
|
||||||
|
|
||||||
// ==============================|| HEADER PROFILE - PROFILE TAB ||============================== //
|
|
||||||
|
|
||||||
const ProfileTab = ({ handleLogout }) => {
|
|
||||||
const [selectedIndex, setSelectedIndex] = useState(null);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const handleListItemClick = (event, index) => {
|
|
||||||
setSelectedIndex(index);
|
|
||||||
if (index == 0) {
|
|
||||||
navigate('/accountsettings');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32 } }}>
|
|
||||||
<ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 0)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<UserOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="View Profile" />
|
|
||||||
</ListItemButton>
|
|
||||||
|
|
||||||
<ListItemButton selected={selectedIndex === 1} onClick={handleLogout}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<LogoutOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Logout" />
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProfileTab.propTypes = {
|
|
||||||
handleLogout: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProfileTab;
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { CommentOutlined, LockOutlined, QuestionCircleOutlined, UserOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| HEADER PROFILE - SETTING TAB ||============================== //
|
|
||||||
|
|
||||||
const SettingTab = () => {
|
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
||||||
const handleListItemClick = (event, index) => {
|
|
||||||
setSelectedIndex(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32 } }}>
|
|
||||||
<ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 0)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<QuestionCircleOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Support" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton selected={selectedIndex === 1} onClick={(event) => handleListItemClick(event, 1)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<UserOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Account Settings" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton selected={selectedIndex === 2} onClick={(event) => handleListItemClick(event, 2)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<LockOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Privacy Center" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton selected={selectedIndex === 3} onClick={(event) => handleListItemClick(event, 3)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<CommentOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Feedback" />
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton selected={selectedIndex === 4} onClick={(event) => handleListItemClick(event, 4)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<UnorderedListOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="History" />
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SettingTab;
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useRef, useState } from 'react';
|
|
||||||
import { useNavigate } from 'react-router';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Box, ButtonBase, CardContent, ClickAwayListener, Grid, Paper, Popper, Stack, Tab, Tabs, Tooltip, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import ProfileTab from './ProfileTab';
|
|
||||||
import SettingTab from './SettingTab';
|
|
||||||
import Avatar from 'components/@extended/Avatar';
|
|
||||||
import MainCard from 'components/MainCard';
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
import IconButton from 'components/@extended/IconButton';
|
|
||||||
|
|
||||||
import { ThemeMode } from 'config';
|
|
||||||
// import useAuth from 'hooks/useAuth';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import avatar1 from 'assets/images/users/avatar-1.png';
|
|
||||||
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// tab panel wrapper
|
|
||||||
function TabPanel({ children, value, index, ...other }) {
|
|
||||||
return (
|
|
||||||
<div role="tabpanel" hidden={value !== index} id={`profile-tabpanel-${index}`} aria-labelledby={`profile-tab-${index}`} {...other}>
|
|
||||||
{value === index && children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TabPanel.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
index: PropTypes.any.isRequired,
|
|
||||||
value: PropTypes.any.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
function a11yProps(index) {
|
|
||||||
return {
|
|
||||||
id: `profile-tab-${index}`,
|
|
||||||
'aria-controls': `profile-tabpanel-${index}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==============================|| HEADER CONTENT - PROFILE ||============================== //
|
|
||||||
|
|
||||||
const Profile = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// const { logout, user } = useAuth();
|
|
||||||
const handleLogout = async () => {
|
|
||||||
try {
|
|
||||||
// await logout();
|
|
||||||
|
|
||||||
// navigate(`/login`, {
|
|
||||||
// state: {
|
|
||||||
// from: ''
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
localStorage.removeItem('firstname');
|
|
||||||
localStorage.removeItem('appuserid');
|
|
||||||
localStorage.removeItem('authname');
|
|
||||||
localStorage.removeItem('roleid');
|
|
||||||
localStorage.removeItem('tenantid');
|
|
||||||
localStorage.clear();
|
|
||||||
|
|
||||||
|
|
||||||
navigate('/login')
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const anchorRef = useRef(null);
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen((prevOpen) => !prevOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (event) => {
|
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const [value, setValue] = useState(0);
|
|
||||||
|
|
||||||
const handleChange = (event, newValue) => {
|
|
||||||
setValue(newValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
const iconBackColorOpen = theme.palette.mode === ThemeMode.DARK ? 'grey.200' : 'grey.300';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
|
||||||
<Tooltip title='Profile'>
|
|
||||||
<ButtonBase
|
|
||||||
sx={{
|
|
||||||
p: 0.25,
|
|
||||||
// bgcolor: open ? iconBackColorOpen : 'transparent',
|
|
||||||
borderRadius: 1,
|
|
||||||
// '&:hover': { bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.light' : 'secondary.lighter' },
|
|
||||||
// '&:focus-visible': {
|
|
||||||
// outline: `2px solid ${theme.palette.secondary.dark}`,
|
|
||||||
// outlineOffset: 2
|
|
||||||
// }
|
|
||||||
}}
|
|
||||||
aria-label="open profile"
|
|
||||||
ref={anchorRef}
|
|
||||||
aria-controls={open ? 'profile-grow' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={handleToggle}
|
|
||||||
>
|
|
||||||
<Stack direction="row" spacing={2} alignItems="center" sx={{ p: 0.5 }}>
|
|
||||||
<Avatar alt="profile user" src={avatar1} size="xs" />
|
|
||||||
<Typography variant="subtitle1">
|
|
||||||
{/* {user?.name} */}
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
</ButtonBase>
|
|
||||||
</Tooltip>
|
|
||||||
<Popper
|
|
||||||
placement="bottom-end"
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorRef.current}
|
|
||||||
role={undefined}
|
|
||||||
transition
|
|
||||||
disablePortal
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: [0, 9]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Transitions type="grow" position="top-right" in={open} {...TransitionProps}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
boxShadow: theme.customShadows.z1,
|
|
||||||
width: 290,
|
|
||||||
minWidth: 240,
|
|
||||||
maxWidth: 290,
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
maxWidth: 250
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<MainCard elevation={0} border={false} content={false}>
|
|
||||||
<CardContent sx={{ px: 2.5, pt: 3 }}>
|
|
||||||
<Grid container justifyContent="space-between" alignItems="center">
|
|
||||||
<Grid item>
|
|
||||||
<Stack direction="row" spacing={1.25} alignItems="center">
|
|
||||||
<Avatar alt="profile user" src={avatar1} sx={{ width: 32, height: 32 }} />
|
|
||||||
<Stack>
|
|
||||||
<Typography variant="h6">
|
|
||||||
{/* {user?.name} */}
|
|
||||||
{localStorage.getItem('fullname') || ''}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" color="textSecondary">
|
|
||||||
{/* UI/UX Designer */}
|
|
||||||
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Tooltip title="Logout">
|
|
||||||
<IconButton size="large" sx={{ color: 'text.primary' }}
|
|
||||||
// onClick={handleLogout}>
|
|
||||||
onClick={()=>{
|
|
||||||
handleLogout()
|
|
||||||
}}>
|
|
||||||
|
|
||||||
<LogoutOutlined />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
|
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
|
||||||
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label="profile tabs">
|
|
||||||
<Tab
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
// justifyContent: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
alignItems: 'center',
|
|
||||||
textTransform: 'capitalize'
|
|
||||||
}}
|
|
||||||
icon={<UserOutlined style={{ marginBottom: 0, marginRight: '10px' }} />}
|
|
||||||
label="Profile"
|
|
||||||
{...a11yProps(0)}
|
|
||||||
/>
|
|
||||||
{/* <Tab
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
textTransform: 'capitalize'
|
|
||||||
}}
|
|
||||||
icon={<SettingOutlined style={{ marginBottom: 0, marginRight: '10px' }} />}
|
|
||||||
label="Setting"
|
|
||||||
{...a11yProps(1)}
|
|
||||||
/> */}
|
|
||||||
</Tabs>
|
|
||||||
</Box>
|
|
||||||
<TabPanel value={value} index={0} dir={theme.direction}>
|
|
||||||
<ProfileTab handleLogout={handleLogout} />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={value} index={1} dir={theme.direction}>
|
|
||||||
<SettingTab />
|
|
||||||
</TabPanel>
|
|
||||||
</MainCard>
|
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Transitions>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Profile;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { Box, FormControl, InputAdornment, OutlinedInput } from '@mui/material';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { SearchOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| HEADER CONTENT - SEARCH ||============================== //
|
|
||||||
|
|
||||||
const Search = () => (
|
|
||||||
<Box sx={{ width: '100%', ml: { xs: 0, md: 1 } }}>
|
|
||||||
<FormControl sx={{ width: { xs: '100%', md: 224 } }}>
|
|
||||||
<OutlinedInput
|
|
||||||
size="small"
|
|
||||||
id="header-search"
|
|
||||||
startAdornment={
|
|
||||||
<InputAdornment position="start" sx={{ mr: -0.5 }}>
|
|
||||||
<SearchOutlined />
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
aria-describedby="header-search-text"
|
|
||||||
inputProps={{
|
|
||||||
'aria-label': 'weight'
|
|
||||||
}}
|
|
||||||
placeholder="Ctrl + K"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Search;
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
import { useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
useMediaQuery,
|
|
||||||
Stack,
|
|
||||||
Tooltip,
|
|
||||||
IconButton,
|
|
||||||
Popper,
|
|
||||||
ClickAwayListener,
|
|
||||||
List,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemText,
|
|
||||||
Grid,
|
|
||||||
ListItemIcon,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
import Transitions from 'components/@extended/Transitions';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import Search from './Search';
|
|
||||||
import Message from './Message';
|
|
||||||
import Profile from './Profile';
|
|
||||||
import Notification from './Notification';
|
|
||||||
import MobileSection from './MobileSection';
|
|
||||||
import MegaMenuSection from './MegaMenuSection';
|
|
||||||
import { useNavigate } from 'react-router';
|
|
||||||
import {
|
|
||||||
MailOutlined,
|
|
||||||
// DashboardOutlined,
|
|
||||||
UserOutlined,
|
|
||||||
CustomerServiceOutlined,
|
|
||||||
WindowsOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { TbBoxMultiple1 } from 'react-icons/tb';
|
|
||||||
import { GrMultiple } from 'react-icons/gr';
|
|
||||||
import { FaUserPen } from 'react-icons/fa6';
|
|
||||||
import { TbUserEdit } from 'react-icons/tb';
|
|
||||||
|
|
||||||
// ==============================|| HEADER - CONTENT ||============================== //
|
|
||||||
|
|
||||||
const HeaderContent = () => {
|
|
||||||
const matchesXs = useMediaQuery((theme) => theme.breakpoints.down('md'));
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
const megaMenu = useMemo(() => <MegaMenuSection />, []);
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
|
||||||
const theme = useTheme();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
// const megaMenu = useMemo(() => <MegaMenuSection />, []);
|
|
||||||
const iconBackColorOpen = 'grey.300';
|
|
||||||
const iconBackColor = 'grey.100';
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const handleToggle = (e) => {
|
|
||||||
setOpen(!open);
|
|
||||||
setAnchorEl(e.currentTarget);
|
|
||||||
};
|
|
||||||
const handleClickAway = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* {!matchesXs && <Search />} */}
|
|
||||||
<Stack width="100%" direction="row" justifyContent="space-between" spacing={2} alignItems="center">
|
|
||||||
{/* {!matchesXs && megaMenu} */}
|
|
||||||
<Typography variant="h5" sx={{ ml: 2, color: '#fff', whiteSpace: 'nowrap' }}>
|
|
||||||
{localStorage.getItem('tenantname') || ''}
|
|
||||||
</Typography>
|
|
||||||
{matchesXs && <Box sx={{ width: '100%', ml: 1 }} />}
|
|
||||||
<Stack direction={'row'} spacing={2}>
|
|
||||||
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
|
|
||||||
<Tooltip title="Create Menu" placement="left">
|
|
||||||
<IconButton
|
|
||||||
// color="secondary"
|
|
||||||
// variant="light"
|
|
||||||
// sx={{
|
|
||||||
// color: 'text.primary',
|
|
||||||
// bgcolor: open ? iconBackColorOpen : iconBackColor
|
|
||||||
// }}
|
|
||||||
sx={{
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: '20px',
|
|
||||||
// bgcolor: open ? iconBackColorOpen : iconBackColor
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
// border:'1px solid #fff'
|
|
||||||
}}
|
|
||||||
aria-label="open profile"
|
|
||||||
// ref={anchorRef}
|
|
||||||
// aria-controls={open ? 'profile-grow' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={handleToggle}
|
|
||||||
>
|
|
||||||
<WindowsOutlined />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Popper
|
|
||||||
open={open}
|
|
||||||
placement="bottom"
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
role={undefined}
|
|
||||||
// transition
|
|
||||||
disablePortal
|
|
||||||
popperOptions={{
|
|
||||||
modifiers: [
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
// backgroundColor:'white',
|
|
||||||
// border:1,
|
|
||||||
p: 0,
|
|
||||||
zIndex: 5000,
|
|
||||||
boxShadow: theme.customShadows.z1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Transitions type="grow" position="top" sx={{ overflow: 'hidden' }} in={open}>
|
|
||||||
{/* <Box sx={{
|
|
||||||
backgroundColor: 'white',
|
|
||||||
border: '1px solid #e0e0e0 !important',
|
|
||||||
borderRadius: 1,
|
|
||||||
}}> */}
|
|
||||||
<Box
|
|
||||||
sx={
|
|
||||||
{
|
|
||||||
// boxShadow: theme.customShadows.z1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ClickAwayListener onClickAway={handleClickAway}>
|
|
||||||
{/* <List disablePadding> */}
|
|
||||||
<List
|
|
||||||
component="nav"
|
|
||||||
sx={{
|
|
||||||
p: 0,
|
|
||||||
width: '100%',
|
|
||||||
minWidth: 200,
|
|
||||||
maxWidth: 290,
|
|
||||||
bgcolor: theme.palette.background.paper,
|
|
||||||
borderRadius: 0.5,
|
|
||||||
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
maxWidth: 250
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemButton
|
|
||||||
selected={location.pathname === 'nearle/orders/create'}
|
|
||||||
onClick={() => {
|
|
||||||
// console.log(const location = useLocation();)
|
|
||||||
navigate('nearle/orders/create');
|
|
||||||
handleClickAway();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Grid container>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<TbBoxMultiple1 />
|
|
||||||
</ListItemIcon>
|
|
||||||
<Typography color="textPrimary">Create Orders</Typography>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItemButton>
|
|
||||||
<ListItemButton
|
|
||||||
selected={location.pathname === 'nearle/orders/create'}
|
|
||||||
onClick={() => {
|
|
||||||
// console.log(const location = useLocation();)
|
|
||||||
navigate('nearle/orders/create/grouporders');
|
|
||||||
handleClickAway();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Grid container>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<GrMultiple />
|
|
||||||
</ListItemIcon>
|
|
||||||
<Typography color="textPrimary">Create Group Orders</Typography>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItemButton>
|
|
||||||
{/* <ListItemButton
|
|
||||||
selected={location.pathname === '/clients/create'}
|
|
||||||
onClick={() => {
|
|
||||||
navigate('/clients/create')
|
|
||||||
handleClickAway()
|
|
||||||
}} >
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Grid container>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<CustomerServiceOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<Typography color="textPrimary">Create Client</Typography>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItemButton> */}
|
|
||||||
<ListItemButton
|
|
||||||
selected={location.pathname === 'nearle/customers/create'}
|
|
||||||
onClick={() => {
|
|
||||||
navigate('nearle/customers');
|
|
||||||
handleClickAway();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Grid container>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<TbUserEdit />
|
|
||||||
</ListItemIcon>
|
|
||||||
<Typography color="textPrimary"> Customers</Typography>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItemButton>
|
|
||||||
|
|
||||||
{/* <ListItem disablePadding>
|
|
||||||
<ListItemButton sx={{ p: 2 }} onClick={() => {
|
|
||||||
navigate('/create_order')
|
|
||||||
handleClickAway()
|
|
||||||
}}>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<MailOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Create Order" />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem disablePadding>
|
|
||||||
<ListItemButton sx={{ p: 2 }} onClick={() => {
|
|
||||||
navigate('/create_client')
|
|
||||||
handleClickAway()
|
|
||||||
}}>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<CustomerServiceOutlined />
|
|
||||||
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Create Client" />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem disablePadding>
|
|
||||||
<ListItemButton sx={{ p: 2 }} onClick={() => {
|
|
||||||
navigate('/create_staff')
|
|
||||||
handleClickAway()
|
|
||||||
}}>
|
|
||||||
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
|
|
||||||
<UserOutlined />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Create Staff" />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem> */}
|
|
||||||
</List>
|
|
||||||
</ClickAwayListener>
|
|
||||||
{/* </Box> */}
|
|
||||||
</Box>
|
|
||||||
</Transitions>
|
|
||||||
</Popper>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* <Notification /> */}
|
|
||||||
|
|
||||||
{/* <Message /> */}
|
|
||||||
{/* {!matchesXs && <Profile />}
|
|
||||||
{matchesXs && <MobileSection />} */}
|
|
||||||
<Tooltip title="Notifications">
|
|
||||||
<Profile />
|
|
||||||
</Tooltip>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HeaderContent;
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { AppBar, Toolbar, useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import AppBarStyled from './AppBarStyled';
|
|
||||||
import HeaderContent from './HeaderContent';
|
|
||||||
import IconButton from 'components/@extended/IconButton';
|
|
||||||
|
|
||||||
import { MenuOrientation } from 'config';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
import { dispatch, useSelector } from 'store';
|
|
||||||
import { openDrawer } from 'store/reducers/menu';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
// ==============================|| MAIN LAYOUT - HEADER ||============================== //
|
|
||||||
|
|
||||||
const Header = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
const { menuOrientation } = useConfig();
|
|
||||||
|
|
||||||
const menu = useSelector((state) => state.menu);
|
|
||||||
const { drawerOpen } = menu;
|
|
||||||
|
|
||||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
|
||||||
|
|
||||||
// header content
|
|
||||||
const headerContent = useMemo(() => <HeaderContent />, []);
|
|
||||||
|
|
||||||
// common header
|
|
||||||
const mainHeader = (
|
|
||||||
<Toolbar>
|
|
||||||
{!isHorizontal ? (
|
|
||||||
<IconButton
|
|
||||||
aria-label="open drawer"
|
|
||||||
onClick={() => dispatch(openDrawer(!drawerOpen))}
|
|
||||||
edge="start"
|
|
||||||
sx={{
|
|
||||||
color: '#fff',
|
|
||||||
bgcolor: 'transparent',
|
|
||||||
ml: { xs: 0, lg: -2 },
|
|
||||||
fontSize: '25px',
|
|
||||||
':hover': {
|
|
||||||
color: '#fff',
|
|
||||||
bgcolor: 'transparent'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!drawerOpen ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
||||||
</IconButton>
|
|
||||||
) : null}
|
|
||||||
{headerContent}
|
|
||||||
</Toolbar>
|
|
||||||
);
|
|
||||||
|
|
||||||
// app-bar params
|
|
||||||
const appBar = {
|
|
||||||
position: 'fixed',
|
|
||||||
color: 'inherit',
|
|
||||||
elevation: 0,
|
|
||||||
sx: {
|
|
||||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
||||||
zIndex: 1200,
|
|
||||||
width: isHorizontal ? '100%' : drawerOpen ? 'calc(100% - 260px)' : { xs: '100%', lg: 'calc(100% - 60px)' },
|
|
||||||
// boxShadow: theme.customShadows.z1
|
|
||||||
bgcolor: '#662582'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{!downLG ? (
|
|
||||||
<AppBarStyled open={drawerOpen} {...appBar}>
|
|
||||||
{mainHeader}
|
|
||||||
</AppBarStyled>
|
|
||||||
) : (
|
|
||||||
<AppBar {...appBar}>{mainHeader}</AppBar>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
@@ -1,67 +1,64 @@
|
|||||||
import { useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
// material-ui
|
// Astryx design system — see themes/astryx.js. This replaces the old MUI
|
||||||
import { useTheme } from '@mui/material/styles';
|
// Drawer/Header/HorizontalBar shell; horizontal menu orientation
|
||||||
import { useMediaQuery, Box, Container, Toolbar } from '@mui/material';
|
// (config.menuOrientation) was dropped since it was never exposed as a
|
||||||
|
// user-facing toggle in this app — see AppSideNav.js.
|
||||||
|
import { AppShell } from '@astryxdesign/core/AppShell';
|
||||||
|
import { Theme } from '@astryxdesign/core/theme';
|
||||||
|
import { dailygrubsTheme } from 'themes/astryx';
|
||||||
|
|
||||||
// project import
|
import AppTopNav from './AppTopNav';
|
||||||
import Drawer from './Drawer';
|
import AppSideNav from './AppSideNav';
|
||||||
import Header from './Header';
|
|
||||||
import Footer from './Footer';
|
|
||||||
import HorizontalBar from './Drawer/HorizontalBar';
|
|
||||||
import Breadcrumbs from 'components/@extended/Breadcrumbs';
|
|
||||||
|
|
||||||
import { MenuOrientation } from 'config';
|
|
||||||
import navigation from 'menu-items';
|
|
||||||
import useConfig from 'hooks/useConfig';
|
|
||||||
import { dispatch } from 'store';
|
|
||||||
import { openDrawer } from 'store/reducers/menu';
|
|
||||||
|
|
||||||
// ==============================|| MAIN LAYOUT ||============================== //
|
// ==============================|| MAIN LAYOUT ||============================== //
|
||||||
|
|
||||||
const MainLayout = () => {
|
const MainLayout = () => {
|
||||||
const theme = useTheme();
|
// Lifted here (rather than left as SideNav's own uncontrolled state) so the
|
||||||
// const matchDownXL = useMediaQuery(theme.breakpoints.down('xl'));
|
// TopNav logo can track the sidebar's collapse state. Defaults to collapsed
|
||||||
const matchDownXL = useMediaQuery(`(max-width: 1440px)`);
|
// on every load/reload (not persisted).
|
||||||
|
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(true);
|
||||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
|
||||||
|
|
||||||
const { container, miniDrawer, menuOrientation } = useConfig();
|
|
||||||
|
|
||||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
|
||||||
|
|
||||||
// set media wise responsive drawer
|
|
||||||
useEffect(() => {
|
|
||||||
if (!miniDrawer) {
|
|
||||||
dispatch(openDrawer(!matchDownXL));
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [matchDownXL]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
<Theme theme={dailygrubsTheme} mode="light">
|
||||||
<Header />
|
{/* height="auto" (not "fill"): "fill" scrolls the content area in its
|
||||||
{!isHorizontal ? <Drawer /> : <HorizontalBar />}
|
own container, and the app's useInfiniteQuery pattern observes a
|
||||||
<Box component="main" sx={{ width: 'calc(100% - 260px)', flexGrow: 1, p: { xs: 2, sm: 3 } }}>
|
sentinel against the window's viewport (IntersectionObserver
|
||||||
<Toolbar sx={{ mt: isHorizontal ? 8 : 'inherit' }} />
|
root=null), which only fires correctly with normal document-level
|
||||||
<Container
|
scroll. */}
|
||||||
// maxWidth={container ? 'xl' : false}
|
<AppShell
|
||||||
maxWidth
|
variant="section"
|
||||||
sx={{
|
height="auto"
|
||||||
...(container && { px: { xs: 0, sm: 2 } }),
|
contentPadding={0}
|
||||||
position: 'relative',
|
topNav={<AppTopNav />}
|
||||||
minHeight: 'calc(100vh - 110px)',
|
sideNav={<AppSideNav isCollapsed={isSidebarCollapsed} onCollapsedChange={setIsSidebarCollapsed} />}
|
||||||
display: 'flex',
|
mobileNav={{ breakpoint: 'lg' }}
|
||||||
flexDirection: 'column'
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* <Breadcrumbs navigation={navigation} title titleBottom card={false} divider={false} /> */}
|
<div className="main-content-area" style={{ minHeight: '100%', boxSizing: 'border-box' }}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
{/* <Footer /> */}
|
</div>
|
||||||
</Container>
|
<style>{`
|
||||||
</Box>
|
.main-content-area {
|
||||||
</Box>
|
padding: 24px;
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.main-content-area {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force the AppShell's sticky header to have a high z-index so that Material-UI inputs (TextFields, labels) don't scroll over it */
|
||||||
|
.astryx-app-shell > header,
|
||||||
|
.astryx-app-shell header,
|
||||||
|
.astryx-app-shell [data-astryx-part="header"] {
|
||||||
|
position: sticky !important;
|
||||||
|
top: 0 !important;
|
||||||
|
z-index: 1200 !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</AppShell>
|
||||||
|
</Theme>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { AiOutlineBarChart } from 'react-icons/ai';
|
|||||||
import { AiOutlineDashboard } from 'react-icons/ai';
|
import { AiOutlineDashboard } from 'react-icons/ai';
|
||||||
import { TbListDetails } from 'react-icons/tb';
|
import { TbListDetails } from 'react-icons/tb';
|
||||||
import { LiaFileInvoiceSolid } from 'react-icons/lia';
|
import { LiaFileInvoiceSolid } from 'react-icons/lia';
|
||||||
import DirectionsBikeOutlinedIcon from '@mui/icons-material/DirectionsBikeOutlined';
|
import { MdDirectionsBike as DirectionsBikeOutlinedIcon, MdRoute as RouteOutlinedIcon, MdMoped as MopedOutlinedIcon } from 'react-icons/md';
|
||||||
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
|
|
||||||
import MopedOutlinedIcon from '@mui/icons-material/MopedOutlined';
|
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import {
|
import {
|
||||||
@@ -25,7 +23,7 @@ import {
|
|||||||
SettingOutlined
|
SettingOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Path } from 'leaflet';
|
import { Path } from 'leaflet';
|
||||||
import { LocationOnOutlined } from '@mui/icons-material';
|
import { MdLocationOn as LocationOnOutlined } from 'react-icons/md';
|
||||||
|
|
||||||
// icons
|
// icons
|
||||||
const icons = {
|
const icons = {
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
// third-party
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import { AiOutlineBarChart } from 'react-icons/ai';
|
|
||||||
import { AiOutlineDashboard } from 'react-icons/ai';
|
|
||||||
import { TbListDetails } from 'react-icons/tb';
|
|
||||||
import { LiaFileInvoiceSolid } from 'react-icons/lia';
|
|
||||||
import MopedOutlinedIcon from '@mui/icons-material/MopedOutlined';
|
|
||||||
|
|
||||||
// assets
|
|
||||||
import {
|
|
||||||
BorderOutlined,
|
|
||||||
BoxPlotOutlined,
|
|
||||||
ChromeOutlined,
|
|
||||||
DeploymentUnitOutlined,
|
|
||||||
GatewayOutlined,
|
|
||||||
MenuUnfoldOutlined,
|
|
||||||
QuestionOutlined,
|
|
||||||
SmileOutlined,
|
|
||||||
StopOutlined,
|
|
||||||
DashboardOutlined,
|
|
||||||
ClockCircleOutlined,
|
|
||||||
UserOutlined,
|
|
||||||
SettingOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
|
|
||||||
// icons
|
|
||||||
const icons = {
|
|
||||||
ChromeOutlined,
|
|
||||||
MenuUnfoldOutlined,
|
|
||||||
BoxPlotOutlined,
|
|
||||||
StopOutlined,
|
|
||||||
BorderOutlined,
|
|
||||||
SmileOutlined,
|
|
||||||
GatewayOutlined,
|
|
||||||
QuestionOutlined,
|
|
||||||
DeploymentUnitOutlined,
|
|
||||||
DashboardOutlined,
|
|
||||||
ClockCircleOutlined,
|
|
||||||
UserOutlined,
|
|
||||||
SettingOutlined
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==============================|| MENU ITEMS - SUPPORT ||============================== //
|
|
||||||
|
|
||||||
const other = {
|
|
||||||
id: 'other',
|
|
||||||
title: <FormattedMessage id="MENU" />,
|
|
||||||
type: 'group',
|
|
||||||
children: [
|
|
||||||
// {
|
|
||||||
// id: 'dashboard',
|
|
||||||
// title: <FormattedMessage id="Dashboard" />,
|
|
||||||
// type: 'item',
|
|
||||||
// url: '/dashboard',
|
|
||||||
// icon: icons.DashboardOutlined
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
id: 'orders',
|
|
||||||
title: <FormattedMessage id="Orders" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'nearle/orders',
|
|
||||||
icon: AiOutlineDashboard
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'deliveries',
|
|
||||||
title: <FormattedMessage id="Deliveries" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'nearle/deliveries',
|
|
||||||
icon: MopedOutlinedIcon
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'customers',
|
|
||||||
title: <FormattedMessage id="Customers" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'nearle/customers',
|
|
||||||
icon: icons.UserOutlined
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'reports',
|
|
||||||
title: <FormattedMessage id="Reports" />,
|
|
||||||
type: 'collapse',
|
|
||||||
icon: AiOutlineBarChart,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'OrderSummary',
|
|
||||||
title: <FormattedMessage id="OrderSummary" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'reports/ordersummary',
|
|
||||||
icon: TbListDetails
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'OrdersDetails',
|
|
||||||
title: <FormattedMessage id="OrdersDetails" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'reports/ordersdetails',
|
|
||||||
icon: TbListDetails
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'RiderSummary',
|
|
||||||
title: <FormattedMessage id="RiderSummary" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'reports/ridersummary',
|
|
||||||
icon: TbListDetails
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'RiderLogs',
|
|
||||||
title: <FormattedMessage id="RiderLogs" />,
|
|
||||||
type: 'item',
|
|
||||||
url: 'reports/riderlogs',
|
|
||||||
icon: TbListDetails
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'invoice',
|
|
||||||
title: <FormattedMessage id="Invoice" />,
|
|
||||||
type: 'item',
|
|
||||||
url: '/invoice',
|
|
||||||
icon: LiaFileInvoiceSolid
|
|
||||||
}
|
|
||||||
|
|
||||||
// {
|
|
||||||
// id: 'account',
|
|
||||||
// title: <FormattedMessage id="Settings" />,
|
|
||||||
// type: 'item',
|
|
||||||
// url: '/accountsettings',
|
|
||||||
// icon: icons.SettingOutlined
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: 'documentation',
|
|
||||||
// title: <FormattedMessage id="documentation" />,
|
|
||||||
// type: 'item',
|
|
||||||
// url: 'https://links.codedthemes.com/BQFrl',
|
|
||||||
// icon: icons.QuestionOutlined,
|
|
||||||
// external: true,
|
|
||||||
// target: true,
|
|
||||||
// chip: {
|
|
||||||
// label: 'gitbook',
|
|
||||||
// color: 'secondary',
|
|
||||||
// size: 'small'
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: 'roadmap',
|
|
||||||
// title: <FormattedMessage id="roadmap" />,
|
|
||||||
// type: 'item',
|
|
||||||
// url: 'https://links.codedthemes.com/RXnKQ',
|
|
||||||
// icon: icons.DeploymentUnitOutlined,
|
|
||||||
// external: true,
|
|
||||||
// target: true
|
|
||||||
// }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export default other;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
|
||||||
import { Box, Button, Grid, Divider, Typography, useMediaQuery } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import useAuth from 'hooks/useAuth';
|
|
||||||
import AnimateButton from 'components/@extended/AnimateButton';
|
|
||||||
import AuthWrapper from 'sections/auth/AuthWrapper';
|
|
||||||
|
|
||||||
// ================================|| CHECK MAIL ||================================ //
|
|
||||||
|
|
||||||
const CheckMail = () => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
|
|
||||||
|
|
||||||
const { isLoggedIn } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthWrapper>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Box sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
|
|
||||||
<Typography variant="h3">Hi, Check Your Mail</Typography>
|
|
||||||
<Typography color="secondary" sx={{ mb: 0.5, mt: 1.25 }}>
|
|
||||||
We have sent a password recover instructions to your email.
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<AnimateButton>
|
|
||||||
<Button
|
|
||||||
component={Link}
|
|
||||||
to={isLoggedIn ? '/auth/login' : '/login'}
|
|
||||||
disableElevation
|
|
||||||
fullWidth
|
|
||||||
size="large"
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</AnimateButton>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Divider>
|
|
||||||
<Typography variant={matchDownSM ? 'subtitle1' : 'h5'}>Sign up with</Typography>
|
|
||||||
</Divider>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</AuthWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CheckMail;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { Grid, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import AuthWrapper from 'sections/auth/AuthWrapper';
|
|
||||||
import AuthCodeVerification from 'sections/auth/auth-forms/AuthCodeVerification';
|
|
||||||
|
|
||||||
// ================================|| CODE VERIFICATION ||================================ //
|
|
||||||
|
|
||||||
const CodeVerification = () => (
|
|
||||||
<AuthWrapper>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack spacing={1}>
|
|
||||||
<Typography variant="h3">Enter Verification Code</Typography>
|
|
||||||
<Typography color="secondary">We send you on mail.</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography>We`ve send you code on jone. ****@company.com</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<AuthCodeVerification />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</AuthWrapper>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default CodeVerification;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Grid, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import useAuth from 'hooks/useAuth';
|
|
||||||
import AuthWrapper from 'sections/auth/AuthWrapper';
|
|
||||||
import AuthForgotPassword from 'sections/auth/auth-forms/AuthForgotPassword';
|
|
||||||
|
|
||||||
// ================================|| FORGOT PASSWORD ||================================ //
|
|
||||||
|
|
||||||
const ForgotPassword = () => {
|
|
||||||
const { isLoggedIn } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthWrapper>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
|
|
||||||
<Typography variant="h3">Forgot Password</Typography>
|
|
||||||
<Typography
|
|
||||||
component={Link}
|
|
||||||
to={isLoggedIn ? '/auth/login' : '/login'}
|
|
||||||
variant="body1"
|
|
||||||
sx={{ textDecoration: 'none' }}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
Back to Login
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<AuthForgotPassword />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</AuthWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ForgotPassword;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Grid, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import useAuth from 'hooks/useAuth';
|
|
||||||
import AuthWrapper from 'sections/auth/AuthWrapper';
|
|
||||||
import AuthLogin from 'sections/auth/auth-forms/AuthLogin';
|
|
||||||
|
|
||||||
// ================================|| LOGIN ||================================ //
|
|
||||||
|
|
||||||
const Login = () => {
|
|
||||||
const { isLoggedIn } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthWrapper>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
|
|
||||||
<Typography variant="h3">Login</Typography>
|
|
||||||
<Typography
|
|
||||||
component={Link}
|
|
||||||
to={isLoggedIn ? '/auth/register' : '/register'}
|
|
||||||
variant="body1"
|
|
||||||
sx={{ textDecoration: 'none' }}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
Don't have an account?
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<AuthLogin />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</AuthWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Login;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Grid, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import useAuth from 'hooks/useAuth';
|
|
||||||
import AuthWrapper from 'sections/auth/AuthWrapper';
|
|
||||||
import FirebaseRegister from 'sections/auth/auth-forms/AuthRegister';
|
|
||||||
|
|
||||||
// ================================|| REGISTER ||================================ //
|
|
||||||
|
|
||||||
const Register = () => {
|
|
||||||
const { isLoggedIn } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthWrapper>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
|
|
||||||
<Typography variant="h3">Sign up</Typography>
|
|
||||||
<Typography
|
|
||||||
component={Link}
|
|
||||||
to={isLoggedIn ? '/auth/login' : '/login'}
|
|
||||||
variant="body1"
|
|
||||||
sx={{ textDecoration: 'none' }}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
Already have an account?
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<FirebaseRegister />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</AuthWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Register;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
// material-ui
|
|
||||||
import { Grid, Stack, Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
|
||||||
import AuthWrapper from 'sections/auth/AuthWrapper';
|
|
||||||
import AuthResetPassword from 'sections/auth/auth-forms/AuthResetPassword';
|
|
||||||
|
|
||||||
// ================================|| RESET PASSWORD ||================================ //
|
|
||||||
|
|
||||||
const ResetPassword = () => (
|
|
||||||
<AuthWrapper>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack sx={{ mb: { xs: -0.5, sm: 0.5 } }} spacing={1}>
|
|
||||||
<Typography variant="h3">Reset Password</Typography>
|
|
||||||
<Typography color="secondary">Please choose your new password</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<AuthResetPassword />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</AuthWrapper>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default ResetPassword;
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
// material-ui
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
import { Typography } from '@mui/material';
|
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import MainCard from 'components/MainCard';
|
import MainCard from 'components/MainCard';
|
||||||
@@ -8,12 +7,12 @@ import MainCard from 'components/MainCard';
|
|||||||
|
|
||||||
const SamplePage = () => (
|
const SamplePage = () => (
|
||||||
<MainCard title="Sample Card">
|
<MainCard title="Sample Card">
|
||||||
<Typography variant="body2">
|
<Text>
|
||||||
Lorem ipsum dolor sit amen, consenter nipissing eli, sed do elusion tempos incident ut laborers et doolie magna alissa. Ut enif ad
|
Lorem ipsum dolor sit amen, consenter nipissing eli, sed do elusion tempos incident ut laborers et doolie magna alissa. Ut enif ad
|
||||||
minim venice, quin nostrum exercitation illampu laborings nisi ut liquid ex ea commons construal. Duos aube grue dolor in reprehended
|
minim venice, quin nostrum exercitation illampu laborings nisi ut liquid ex ea commons construal. Duos aube grue dolor in reprehended
|
||||||
in voltage veil esse colum doolie eu fujian bulla parian. Exceptive sin ocean cuspidate non president, sunk in culpa qui officiate
|
in voltage veil esse colum doolie eu fujian bulla parian. Exceptive sin ocean cuspidate non president, sunk in culpa qui officiate
|
||||||
descent molls anim id est labours.
|
descent molls anim id est labours.
|
||||||
</Typography>
|
</Text>
|
||||||
</MainCard>
|
</MainCard>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import { Link } from 'react-router-dom';
|
|||||||
// project import
|
// project import
|
||||||
import { APP_DEFAULT_PATH } from 'config';
|
import { APP_DEFAULT_PATH } from 'config';
|
||||||
|
|
||||||
// material-ui
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
|
import { Center } from '@astryxdesign/core/Center';
|
||||||
|
import { Heading } from '@astryxdesign/core/Heading';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import error404 from 'assets/images/maintenance/Error404.png';
|
import error404 from 'assets/images/maintenance/Error404.png';
|
||||||
@@ -14,42 +17,25 @@ import TwoCone from 'assets/images/maintenance/TwoCone.png';
|
|||||||
|
|
||||||
function Error404() {
|
function Error404() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Center axis="both" style={{ minHeight: '100vh', paddingTop: 12, paddingBottom: 8, overflow: 'hidden' }}>
|
||||||
<Grid
|
<VStack gap={10} hAlign="center">
|
||||||
container
|
<div style={{ display: 'flex', position: 'relative' }}>
|
||||||
spacing={10}
|
<div style={{ width: 'min(590px, 60vw)' }}>
|
||||||
direction="column"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
sx={{ minHeight: '100vh', pt: 1.5, pb: 1, overflow: 'hidden' }}
|
|
||||||
>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack direction="row">
|
|
||||||
<Grid item>
|
|
||||||
<Box sx={{ width: { xs: 250, sm: 590 }, height: { xs: 130, sm: 300 } }}>
|
|
||||||
<img src={error404} alt="mantis" style={{ width: '100%', height: '100%' }} />
|
<img src={error404} alt="mantis" style={{ width: '100%', height: '100%' }} />
|
||||||
</Box>
|
</div>
|
||||||
</Grid>
|
<div style={{ position: 'absolute', top: 60, left: -40, width: 'min(390px, 40vw)' }}>
|
||||||
<Grid item sx={{ position: 'relative' }}>
|
|
||||||
<Box sx={{ position: 'absolute', top: 60, left: -40, width: { xs: 130, sm: 390 }, height: { xs: 115, sm: 330 } }}>
|
|
||||||
<img src={TwoCone} alt="mantis" style={{ width: '100%', height: '100%' }} />
|
<img src={TwoCone} alt="mantis" style={{ width: '100%', height: '100%' }} />
|
||||||
</Box>
|
</div>
|
||||||
</Grid>
|
</div>
|
||||||
</Stack>
|
<VStack gap={2} hAlign="center">
|
||||||
</Grid>
|
<Heading level={1}>Page Not Found</Heading>
|
||||||
<Grid item xs={12}>
|
<Text type="supporting" align="center" style={{ width: '61%' }}>
|
||||||
<Stack spacing={2} justifyContent="center" alignItems="center">
|
|
||||||
<Typography variant="h1">Page Not Found</Typography>
|
|
||||||
<Typography color="textSecondary" align="center" sx={{ width: { xs: '73%', sm: '61%' } }}>
|
|
||||||
The page you are looking was moved, removed, renamed, or might never exist!
|
The page you are looking was moved, removed, renamed, or might never exist!
|
||||||
</Typography>
|
</Text>
|
||||||
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained">
|
<Button label="Back To Home" href={APP_DEFAULT_PATH} as={Link} variant="primary" />
|
||||||
Back To Home
|
</VStack>
|
||||||
</Button>
|
</VStack>
|
||||||
</Stack>
|
</Center>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { Link } from 'react-router-dom';
|
|||||||
// project import
|
// project import
|
||||||
import { APP_DEFAULT_PATH } from 'config';
|
import { APP_DEFAULT_PATH } from 'config';
|
||||||
|
|
||||||
// material-ui
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { Center } from '@astryxdesign/core/Center';
|
||||||
import { useMediaQuery, Box, Button, Grid, Stack, Typography } from '@mui/material';
|
import { Heading } from '@astryxdesign/core/Heading';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import error500 from 'assets/images/maintenance/Error500.png';
|
import error500 from 'assets/images/maintenance/Error500.png';
|
||||||
@@ -13,32 +15,23 @@ import error500 from 'assets/images/maintenance/Error500.png';
|
|||||||
// ==============================|| ERROR 500 - MAIN ||============================== //
|
// ==============================|| ERROR 500 - MAIN ||============================== //
|
||||||
|
|
||||||
function Error500() {
|
function Error500() {
|
||||||
const theme = useTheme();
|
|
||||||
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Center axis="both" style={{ minHeight: '100vh' }}>
|
||||||
<Grid container direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh' }}>
|
<VStack hAlign="center">
|
||||||
<Grid item xs={12}>
|
<div style={{ width: 'min(396px, 90vw)' }}>
|
||||||
<Box sx={{ width: { xs: 350, sm: 396 } }}>
|
|
||||||
<img src={error500} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
<img src={error500} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
||||||
</Box>
|
</div>
|
||||||
</Grid>
|
<VStack gap={1} hAlign="center">
|
||||||
<Grid item xs={12}>
|
<Heading level={1} align="center">
|
||||||
<Stack justifyContent="center" alignItems="center">
|
|
||||||
<Typography align="center" variant={matchDownSM ? 'h2' : 'h1'}>
|
|
||||||
Internal Server Error
|
Internal Server Error
|
||||||
</Typography>
|
</Heading>
|
||||||
<Typography color="textSecondary" variant="body2" align="center" sx={{ width: { xs: '73%', sm: '70%' }, mt: 1 }}>
|
<Text type="supporting" align="center" style={{ width: '70%', marginTop: 8 }}>
|
||||||
Server error 500. we fixing the problem. please try again at a later stage.
|
Server error 500. we fixing the problem. please try again at a later stage.
|
||||||
</Typography>
|
</Text>
|
||||||
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained" sx={{ textTransform: 'none', mt: 4 }}>
|
<Button label="Back To Home" href={APP_DEFAULT_PATH} as={Link} variant="primary" style={{ marginTop: 32 }} />
|
||||||
Back To Home
|
</VStack>
|
||||||
</Button>
|
</VStack>
|
||||||
</Stack>
|
</Center>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
// material-ui
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { HStack } from '@astryxdesign/core/HStack';
|
||||||
import { useMediaQuery, Box, Button, Grid, Stack, TextField, Typography } from '@mui/material';
|
import { Center } from '@astryxdesign/core/Center';
|
||||||
|
import { Heading } from '@astryxdesign/core/Heading';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
import { TextInput } from '@astryxdesign/core/TextInput';
|
||||||
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
|
|
||||||
// third party
|
// third party
|
||||||
import { useTimer } from 'react-timer-hook';
|
const { useTimer } = require('react-timer-hook');
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import coming from 'assets/images/maintenance/coming-soon.png';
|
import coming from 'assets/images/maintenance/coming-soon.png';
|
||||||
@@ -13,25 +18,20 @@ import MainCard from 'components/MainCard';
|
|||||||
|
|
||||||
// ==============================|| COMING SOON - MAIN ||============================== //
|
// ==============================|| COMING SOON - MAIN ||============================== //
|
||||||
|
|
||||||
const TimerBox = ({ count, label }) => {
|
const TimerBox = ({ count, label }) => (
|
||||||
const theme = useTheme();
|
<MainCard content={false} sx={{ width: 80 }}>
|
||||||
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
|
<VStack gap={0} hAlign="center" padding={0}>
|
||||||
|
<div style={{ padding: '14px 0' }}>
|
||||||
return (
|
<Heading level={2}>{count}</Heading>
|
||||||
<MainCard content={false} sx={{ width: { xs: 60, sm: 80 } }}>
|
</div>
|
||||||
<Stack justifyContent="center" alignItems="center">
|
<div style={{ padding: 4, background: 'var(--color-background-muted)', width: '100%' }}>
|
||||||
<Box sx={{ py: 1.75 }}>
|
<Text type="supporting" align="center">
|
||||||
<Typography variant={matchDownSM ? 'h4' : 'h2'}>{count}</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ p: 0.5, bgcolor: 'secondary.lighter', width: '100%' }}>
|
|
||||||
<Typography align="center" variant="subtitle2">
|
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Text>
|
||||||
</Box>
|
</div>
|
||||||
</Stack>
|
</VStack>
|
||||||
</MainCard>
|
</MainCard>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
TimerBox.propTypes = {
|
TimerBox.propTypes = {
|
||||||
count: PropTypes.number,
|
count: PropTypes.number,
|
||||||
@@ -43,51 +43,42 @@ function ComingSoon() {
|
|||||||
time.setSeconds(time.getSeconds() + 3600 * 24 * 2 - 3600 * 15.5);
|
time.setSeconds(time.getSeconds() + 3600 * 24 * 2 - 3600 * 15.5);
|
||||||
|
|
||||||
const { seconds, minutes, hours, days } = useTimer({ expiryTimestamp: time });
|
const { seconds, minutes, hours, days } = useTimer({ expiryTimestamp: time });
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Center axis="both" style={{ minHeight: '100vh', paddingTop: 16, paddingBottom: 16 }}>
|
||||||
<Grid container spacing={4} direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh', py: 2 }}>
|
<VStack gap={4} hAlign="center">
|
||||||
<Grid item xs={12}>
|
<div style={{ height: 420, width: 'min(360px, 90vw)' }}>
|
||||||
<Box sx={{ height: { xs: 310, sm: 420 }, width: { xs: 360, sm: 'auto' } }}>
|
|
||||||
<img src={coming} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
<img src={coming} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
||||||
</Box>
|
</div>
|
||||||
</Grid>
|
<VStack gap={1} hAlign="center" style={{ marginTop: -16 }}>
|
||||||
<Grid item xs={12}>
|
<Heading level={1} align="center">
|
||||||
<Stack spacing={1} justifyContent="center" alignItems="center" sx={{ mt: -2 }}>
|
|
||||||
<Typography align="center" variant="h1">
|
|
||||||
Coming Soon
|
Coming Soon
|
||||||
</Typography>
|
</Heading>
|
||||||
<Typography align="center" color="textSecondary">
|
<Text type="supporting" align="center">
|
||||||
Something new is on its way
|
Something new is on its way
|
||||||
</Typography>
|
</Text>
|
||||||
</Stack>
|
</VStack>
|
||||||
</Grid>
|
<HStack gap={2} vAlign="center" justify="center" style={{ width: 'min(40%, 380px)' }}>
|
||||||
<Grid item xs={12} sx={{ width: { xs: '95%', md: '40%' } }}>
|
|
||||||
<Stack direction="row" alignItems="center" justifyContent="center" spacing={{ xs: 1, sm: 2 }}>
|
|
||||||
<TimerBox count={days} label="day" />
|
<TimerBox count={days} label="day" />
|
||||||
<Typography variant="h1"> : </Typography>
|
<Heading level={1}> : </Heading>
|
||||||
<TimerBox count={hours} label="hour" />
|
<TimerBox count={hours} label="hour" />
|
||||||
<Typography variant="h1"> : </Typography>
|
<Heading level={1}> : </Heading>
|
||||||
<TimerBox count={minutes} label="min" />
|
<TimerBox count={minutes} label="min" />
|
||||||
<Typography variant="h1"> : </Typography>
|
<Heading level={1}> : </Heading>
|
||||||
<TimerBox count={seconds} label="sec" />
|
<TimerBox count={seconds} label="sec" />
|
||||||
</Stack>
|
</HStack>
|
||||||
</Grid>
|
<VStack gap={2} style={{ width: 'min(40%, 380px)', marginTop: 16 }}>
|
||||||
<Grid item xs={12} sx={{ width: { xs: 380, md: '40%', lg: '30%' } }}>
|
<Text type="supporting" align="center">
|
||||||
<Stack spacing={2} sx={{ mt: 2 }}>
|
|
||||||
<Typography align="center" color="textSecondary">
|
|
||||||
Be the first to be notified when Mantis launches.
|
Be the first to be notified when Mantis launches.
|
||||||
</Typography>
|
</Text>
|
||||||
<Stack direction="row" spacing={1}>
|
<HStack gap={1}>
|
||||||
<TextField fullWidth placeholder="Email Address" />
|
<TextInput label="Email Address" isLabelHidden placeholder="Email Address" value={email} onChange={setEmail} width="100%" />
|
||||||
<Button variant="contained" sx={{ width: '50%' }}>
|
<Button label="Notify Me" variant="primary" style={{ width: '50%' }} />
|
||||||
Notify Me
|
</HStack>
|
||||||
</Button>
|
</VStack>
|
||||||
</Stack>
|
</VStack>
|
||||||
</Stack>
|
</Center>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import { Link } from 'react-router-dom';
|
|||||||
// project import
|
// project import
|
||||||
import { APP_DEFAULT_PATH } from 'config';
|
import { APP_DEFAULT_PATH } from 'config';
|
||||||
|
|
||||||
// material-ui
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
|
import { Center } from '@astryxdesign/core/Center';
|
||||||
|
import { Heading } from '@astryxdesign/core/Heading';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
|
|
||||||
// assets
|
// assets
|
||||||
import construction from 'assets/images/maintenance/under-construction.svg';
|
import construction from 'assets/images/maintenance/under-construction.svg';
|
||||||
@@ -13,26 +16,22 @@ import construction from 'assets/images/maintenance/under-construction.svg';
|
|||||||
|
|
||||||
function UnderConstruction() {
|
function UnderConstruction() {
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={4} direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh', py: 2 }}>
|
<Center axis="both" style={{ minHeight: '100vh', paddingTop: 16, paddingBottom: 16 }}>
|
||||||
<Grid item xs={12}>
|
<VStack gap={4} hAlign="center">
|
||||||
<Box sx={{ width: { xs: 300, sm: 480 } }}>
|
<div style={{ width: 'min(480px, 90vw)' }}>
|
||||||
<img src={construction} alt="mantis" style={{ width: '100%', height: 'auto' }} />
|
<img src={construction} alt="mantis" style={{ width: '100%', height: 'auto' }} />
|
||||||
</Box>
|
</div>
|
||||||
</Grid>
|
<VStack gap={2} hAlign="center">
|
||||||
<Grid item xs={12}>
|
<Heading level={1} align="center">
|
||||||
<Stack spacing={2} justifyContent="center" alignItems="center">
|
|
||||||
<Typography align="center" variant="h1">
|
|
||||||
Under Construction
|
Under Construction
|
||||||
</Typography>
|
</Heading>
|
||||||
<Typography color="textSecondary" align="center" sx={{ width: '85%' }}>
|
<Text type="supporting" align="center" style={{ width: '85%' }}>
|
||||||
Hey! Please check out this site later. We are doing some maintenance on it right now.
|
Hey! Please check out this site later. We are doing some maintenance on it right now.
|
||||||
</Typography>
|
</Text>
|
||||||
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained">
|
<Button label="Back To Home" href={APP_DEFAULT_PATH} as={Link} variant="primary" />
|
||||||
Back To Home
|
</VStack>
|
||||||
</Button>
|
</VStack>
|
||||||
</Stack>
|
</Center>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Avatar, Box, Paper, Stack, Typography } from '@mui/material';
|
|
||||||
import {
|
import {
|
||||||
MdLocalShipping,
|
MdLocalShipping,
|
||||||
MdHourglassEmpty,
|
MdHourglassEmpty,
|
||||||
@@ -50,8 +49,8 @@ export const DT = {
|
|||||||
surfaceAlt: '#f8fafc'
|
surfaceAlt: '#f8fafc'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BRAND = '#662582';
|
export const BRAND = '#C01227';
|
||||||
export const BRAND_LIGHT = '#9255AB';
|
export const BRAND_LIGHT = '#D25463';
|
||||||
|
|
||||||
const dtA = (c, suffix) => `${c}${suffix}`;
|
const dtA = (c, suffix) => `${c}${suffix}`;
|
||||||
export const tint = (c) => dtA(c, '08');
|
export const tint = (c) => dtA(c, '08');
|
||||||
@@ -108,15 +107,14 @@ export const StatusBadge = ({ status, minWidth = 86 }) => {
|
|||||||
};
|
};
|
||||||
const Icon = meta.icon;
|
const Icon = meta.icon;
|
||||||
return (
|
return (
|
||||||
<Box
|
<span
|
||||||
sx={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 0.5,
|
gap: 4,
|
||||||
px: 1.125,
|
padding: '3px 9px',
|
||||||
py: 0.375,
|
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
bgcolor: meta.color,
|
backgroundColor: meta.color,
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
@@ -128,7 +126,7 @@ export const StatusBadge = ({ status, minWidth = 86 }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon size={12} /> {meta.label}
|
<Icon size={12} /> {meta.label}
|
||||||
</Box>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,39 +134,39 @@ export const StatusBadge = ({ status, minWidth = 86 }) => {
|
|||||||
// TimelineCell — time large/bold/high-contrast, date small/muted/secondary.
|
// TimelineCell — time large/bold/high-contrast, date small/muted/secondary.
|
||||||
// No decorative dot.
|
// No decorative dot.
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
const noWrapStyle = { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' };
|
||||||
|
|
||||||
export const TimelineCell = ({ value, utc: useUtc = false }) => {
|
export const TimelineCell = ({ value, utc: useUtc = false }) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return (
|
return <span style={{ fontSize: 12, color: DT.textMuted, fontWeight: 700 }}>—</span>;
|
||||||
<Typography sx={{ fontSize: 12, color: DT.textMuted, fontWeight: 700 }}>—</Typography>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const d = useUtc ? dayjs(value).utc() : dayjs(value);
|
const d = useUtc ? dayjs(value).utc() : dayjs(value);
|
||||||
return (
|
return (
|
||||||
<Stack spacing={0} sx={{ lineHeight: 1.1 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.1 }}>
|
||||||
<Typography
|
<span
|
||||||
sx={{
|
style={{
|
||||||
fontSize: 12.5,
|
fontSize: 12.5,
|
||||||
fontWeight: 800,
|
fontWeight: 800,
|
||||||
color: DT.textPrimary,
|
color: DT.textPrimary,
|
||||||
letterSpacing: 0.1,
|
letterSpacing: 0.1,
|
||||||
lineHeight: 1.15
|
lineHeight: 1.15,
|
||||||
|
...noWrapStyle
|
||||||
}}
|
}}
|
||||||
noWrap
|
|
||||||
>
|
>
|
||||||
{d.format('hh:mm A')}
|
{d.format('hh:mm A')}
|
||||||
</Typography>
|
</span>
|
||||||
<Typography
|
<span
|
||||||
sx={{
|
style={{
|
||||||
fontSize: 10.5,
|
fontSize: 10.5,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
color: DT.textMuted,
|
color: DT.textMuted,
|
||||||
lineHeight: 1.2
|
lineHeight: 1.2,
|
||||||
|
...noWrapStyle
|
||||||
}}
|
}}
|
||||||
noWrap
|
|
||||||
>
|
>
|
||||||
{d.format('DD MMM YYYY')}
|
{d.format('DD MMM YYYY')}
|
||||||
</Typography>
|
</span>
|
||||||
</Stack>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,22 +178,17 @@ export const MetricPill = ({ value, color, icon, isMoney = false }) => {
|
|||||||
const display = isMoney ? formatNumberToRupees(n) : Number.isFinite(n) ? n : value || 0;
|
const display = isMoney ? formatNumberToRupees(n) : Number.isFinite(n) ? n : value || 0;
|
||||||
const isZero = !Number.isFinite(n) || n === 0;
|
const isZero = !Number.isFinite(n) || n === 0;
|
||||||
if (isZero) {
|
if (isZero) {
|
||||||
return (
|
return <span style={{ fontSize: 11.5, color: DT.textMuted, fontWeight: 700 }}>{display}</span>;
|
||||||
<Typography sx={{ fontSize: 11.5, color: DT.textMuted, fontWeight: 700 }}>
|
|
||||||
{display}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box
|
<span
|
||||||
sx={{
|
style={{
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 0.5,
|
gap: 4,
|
||||||
px: 0.875,
|
padding: '2px 7px',
|
||||||
py: 0.25,
|
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
bgcolor: tint(color),
|
backgroundColor: tint(color),
|
||||||
border: `1px solid ${edge(color)}`,
|
border: `1px solid ${edge(color)}`,
|
||||||
color,
|
color,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -205,23 +198,23 @@ export const MetricPill = ({ value, color, icon, isMoney = false }) => {
|
|||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
{display}
|
{display}
|
||||||
</Box>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// SoftPaper — autocomplete dropdown surface.
|
// SoftPaper — autocomplete dropdown surface.
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
export const SoftPaper = (props) => (
|
export const SoftPaper = ({ style, ...props }) => (
|
||||||
<Paper
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
sx={{
|
style={{
|
||||||
mt: 0.75,
|
marginTop: 6,
|
||||||
borderRadius: 2,
|
borderRadius: 8,
|
||||||
boxShadow: DT.shadowPop,
|
boxShadow: DT.shadowPop,
|
||||||
border: '1px solid',
|
border: `1px solid ${DT.borderSubtle}`,
|
||||||
borderColor: 'divider',
|
overflow: 'hidden',
|
||||||
overflow: 'hidden'
|
...style
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -230,17 +223,22 @@ export const SoftPaper = (props) => (
|
|||||||
// AccentAvatar — colored circular icon used inside filter chips & headers.
|
// AccentAvatar — colored circular icon used inside filter chips & headers.
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
export const AccentAvatar = ({ color, selected, size = 24, children }) => (
|
export const AccentAvatar = ({ color, selected, size = 24, children }) => (
|
||||||
<Avatar
|
<div
|
||||||
sx={{
|
style={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
bgcolor: selected ? color : soft(color),
|
flexShrink: 0,
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: selected ? color : soft(color),
|
||||||
color: selected ? '#fff' : color,
|
color: selected ? '#fff' : color,
|
||||||
transition: 'background-color 0.15s, color 0.15s'
|
transition: 'background-color 0.15s, color 0.15s'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Avatar>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Grid, List, ListItem, Stack, Typography, useMediaQuery } from '@mui/material';
|
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from 'notistack';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
@@ -9,8 +8,29 @@ import Loader from 'components/Loader';
|
|||||||
import Footer from 'layout/MainLayout/Footer';
|
import Footer from 'layout/MainLayout/Footer';
|
||||||
import TitleCard from './titleCard';
|
import TitleCard from './titleCard';
|
||||||
|
|
||||||
|
import { VStack } from '@astryxdesign/core/VStack';
|
||||||
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
|
import { Divider } from '@astryxdesign/core/Divider';
|
||||||
|
|
||||||
|
import logger from 'utils/logger';
|
||||||
|
|
||||||
|
const Field = ({ label, value }) => (
|
||||||
|
<VStack gap={0.5} padding={0}>
|
||||||
|
<Text type="supporting">{label}</Text>
|
||||||
|
<Text>{value || ''}</Text>
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Row = ({ children, hasDivider }) => (
|
||||||
|
<VStack gap={0} padding={0}>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 24, padding: '20px 24px' }}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
{hasDivider && <Divider />}
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
|
||||||
const Accountsettings = () => {
|
const Accountsettings = () => {
|
||||||
const matchDownMD = useMediaQuery((theme) => theme.breakpoints.down('md'));
|
|
||||||
const [info, setInfo] = useState({});
|
const [info, setInfo] = useState({});
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@@ -25,14 +45,14 @@ const Accountsettings = () => {
|
|||||||
await axios
|
await axios
|
||||||
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
|
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
logger.info(res);
|
||||||
if (res.data.status) {
|
if (res.data.status) {
|
||||||
setInfo(res.data.details);
|
setInfo(res.data.details);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
logger.error(err);
|
||||||
enqueueSnackbar(err.message, {
|
enqueueSnackbar(err.message, {
|
||||||
variant: 'error',
|
variant: 'error',
|
||||||
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
||||||
@@ -47,101 +67,31 @@ const Accountsettings = () => {
|
|||||||
{loading && <Loader />}
|
{loading && <Loader />}
|
||||||
<TitleCard title={'Profile'} />
|
<TitleCard title={'Profile'} />
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
<MainCard content={false}>
|
||||||
<Grid item xs={12}>
|
<Row hasDivider>
|
||||||
<MainCard>
|
<Field label="Name" value={info.tenantname} />
|
||||||
<List sx={{ py: 0 }}>
|
<Field label="Company Name" value={info.companyname} />
|
||||||
<ListItem divider={!matchDownMD}>
|
</Row>
|
||||||
<Grid container spacing={3}>
|
<Row hasDivider>
|
||||||
<Grid item xs={12} sm={6}>
|
<Field label="Registration No" value={info.registrationno} />
|
||||||
<Stack spacing={0.5}>
|
<Field label="Info" value={info.info} />
|
||||||
<Typography color="secondary">Name</Typography>
|
</Row>
|
||||||
<Typography>{info.tenantname || ''}</Typography>
|
<Row hasDivider>
|
||||||
</Stack>
|
<Field label="Phone" value={info.primarycontact} />
|
||||||
</Grid>
|
<Field label="State" value={info.state} />
|
||||||
<Grid item xs={12} sm={6}>
|
</Row>
|
||||||
<Stack spacing={0.5}>
|
<Row hasDivider>
|
||||||
<Typography color="secondary">Company Name</Typography>
|
<Field label="Email" value={info.primaryemail} />
|
||||||
<Typography>{info.companyname || ''}</Typography>
|
<Field label="City" value={info.city} />
|
||||||
</Stack>
|
</Row>
|
||||||
</Grid>
|
<Row hasDivider>
|
||||||
</Grid>
|
<Field label="Suburb" value={info.suburb} />
|
||||||
</ListItem>
|
<Field label="Zip Code" value={info.postcode} />
|
||||||
<ListItem divider={!matchDownMD}>
|
</Row>
|
||||||
<Grid container spacing={3}>
|
<Row>
|
||||||
<Grid item xs={12} sm={6}>
|
<Field label="Address" value={info.address} />
|
||||||
<Stack spacing={0.5}>
|
</Row>
|
||||||
<Typography color="secondary">Registration No</Typography>
|
|
||||||
<Typography>{info.registrationno || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">Info</Typography>
|
|
||||||
<Typography>{info.info || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem divider={!matchDownMD}>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">Phone</Typography>
|
|
||||||
<Typography>{info.primarycontact || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">State</Typography>
|
|
||||||
<Typography>{info.state || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem divider={!matchDownMD}>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">Email</Typography>
|
|
||||||
<Typography>{info.primaryemail || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">City</Typography>
|
|
||||||
<Typography>{info.city || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem divider={!matchDownMD}>
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">Suburb</Typography>
|
|
||||||
<Typography>{info.suburb || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">Zip Code</Typography>
|
|
||||||
<Typography>{info.postcode || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<Stack spacing={0.5}>
|
|
||||||
<Typography color="secondary">Address</Typography>
|
|
||||||
<Typography>{info.address || ''}</Typography>
|
|
||||||
</Stack>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</MainCard>
|
</MainCard>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { OpenToast } from 'components/nearle_components/OpenToast';
|
import { OpenToast } from 'components/nearle_components/OpenToast';
|
||||||
|
import logger from 'utils/logger';
|
||||||
const tenid = localStorage.getItem('tenantid');
|
const tenid = localStorage.getItem('tenantid');
|
||||||
|
|
||||||
export const fetchOrders = async ({ pageParam = 1, queryKey }) => {
|
export const fetchOrders = async ({ pageParam = 1, queryKey }) => {
|
||||||
@@ -55,14 +56,14 @@ export const fetchorderscount = async ({ queryKey }) => {
|
|||||||
// ==============================|| fetchOrderSummary (orders)||============================== //
|
// ==============================|| fetchOrderSummary (orders)||============================== //
|
||||||
export const fetchOrderSummary = async () => {
|
export const fetchOrderSummary = async () => {
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getordersummary`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getordersummary`);
|
||||||
console.log('fetchOrderSummary', response.data.details);
|
logger.info('fetchOrderSummary', response.data.details);
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==============================|| fetchLocationSummary (orders)||============================== //
|
// ==============================|| fetchLocationSummary (orders)||============================== //
|
||||||
export const fetchLocationSummary = async () => {
|
export const fetchLocationSummary = async () => {
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getlocationsummary`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getlocationsummary`);
|
||||||
console.log('fetchLocationSummary', response.data.details);
|
logger.info('fetchLocationSummary', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
@@ -70,7 +71,7 @@ export const fetchLocationSummary = async () => {
|
|||||||
// ==============================|| fetchOrderInsight (orders)||============================== //
|
// ==============================|| fetchOrderInsight (orders)||============================== //
|
||||||
export const fetchOrderInsight = async () => {
|
export const fetchOrderInsight = async () => {
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getorderinsight`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getorderinsight`);
|
||||||
console.log('fetchOrderInsight', response.data.details);
|
logger.info('fetchOrderInsight', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
@@ -78,14 +79,14 @@ export const fetchOrderInsight = async () => {
|
|||||||
// ==============================|| fetchDeliveryInsight (delivery)||============================== //
|
// ==============================|| fetchDeliveryInsight (delivery)||============================== //
|
||||||
export const fetchDeliveryInsight = async () => {
|
export const fetchDeliveryInsight = async () => {
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/deliveries/getdeliveryinsight`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/deliveries/getdeliveryinsight`);
|
||||||
console.log('fetchDeliveryInsight', response.data.details);
|
logger.info('fetchDeliveryInsight', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
// ==============================|| fetchDeliveryLocationSummary (delivery)||============================== //
|
// ==============================|| fetchDeliveryLocationSummary (delivery)||============================== //
|
||||||
export const fetchDeliveryLocationSummary = async () => {
|
export const fetchDeliveryLocationSummary = async () => {
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/deliveries/getlocationsummary`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/deliveries/getlocationsummary`);
|
||||||
console.log('fetchDeliveryLocationSummary', response.data.details);
|
logger.info('fetchDeliveryLocationSummary', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
@@ -121,7 +122,7 @@ export const fetchAllTenants = async ({ queryKey }) => {
|
|||||||
const response = await axios.get(url);
|
const response = await axios.get(url);
|
||||||
|
|
||||||
// tenants/search/?keyword=${search}
|
// tenants/search/?keyword=${search}
|
||||||
console.log('fetchAllTenants', response.data.details);
|
logger.info('fetchAllTenants', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
@@ -129,7 +130,7 @@ export const fetchAllTenants = async ({ queryKey }) => {
|
|||||||
export const fetchCustomersList = async ({ queryKey }) => {
|
export const fetchCustomersList = async ({ queryKey }) => {
|
||||||
const [pages] = queryKey;
|
const [pages] = queryKey;
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/customers/getallcustomers/?pageno=${pages}&pagesize=10`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/customers/getallcustomers/?pageno=${pages}&pagesize=10`);
|
||||||
console.log('fetchCustomersList', response.data.details);
|
logger.info('fetchCustomersList', response.data.details);
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
// ==============================|| gettenantcustomers (customers)||============================== //
|
// ==============================|| gettenantcustomers (customers)||============================== //
|
||||||
@@ -158,39 +159,39 @@ export const gettenantcustomers = async ({ pageParam = 1, queryKey }) => {
|
|||||||
export const fetchCustomersListBySearch = async ({ queryKey }) => {
|
export const fetchCustomersListBySearch = async ({ queryKey }) => {
|
||||||
const [search] = queryKey;
|
const [search] = queryKey;
|
||||||
const response = await axios.get(search.lenght > 3 && `${process.env.REACT_APP_URL}/customers/search/?keyword=${search}`);
|
const response = await axios.get(search.lenght > 3 && `${process.env.REACT_APP_URL}/customers/search/?keyword=${search}`);
|
||||||
console.log('fetchCustomersListBySearch', response.data.details);
|
logger.info('fetchCustomersListBySearch', response.data.details);
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
// ==============================|| fetchOrdersSummary (rider summary)||============================== //
|
// ==============================|| fetchOrdersSummary (rider summary)||============================== //
|
||||||
export const fetchOrdersSummary = async ({ queryKey }) => {
|
export const fetchOrdersSummary = async ({ queryKey }) => {
|
||||||
console.log('queryKey for fetchOrdersSummary', queryKey);
|
logger.info('queryKey for fetchOrdersSummary', queryKey);
|
||||||
const [startdate, enddate] = queryKey;
|
const [startdate, enddate] = queryKey;
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${process.env.REACT_APP_URL}/deliveries/getreportsummary/?tenantid=${tenid}&fromdate=${startdate}&todate=${enddate}`
|
`${process.env.REACT_APP_URL}/deliveries/getreportsummary/?tenantid=${tenid}&fromdate=${startdate}&todate=${enddate}`
|
||||||
);
|
);
|
||||||
console.log('fetchOrdersSummary', response.data.details);
|
logger.info('fetchOrdersSummary', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
// ==============================|| getreportlocationsummary (orders summary)||============================== //
|
// ==============================|| getreportlocationsummary (orders summary)||============================== //
|
||||||
export const getreportlocationsummary = async ({ queryKey }) => {
|
export const getreportlocationsummary = async ({ queryKey }) => {
|
||||||
console.log('queryKey for getreportlocationsummary', queryKey);
|
logger.info('queryKey for getreportlocationsummary', queryKey);
|
||||||
const [startdate, enddate, locationId, debouncedSearch] = queryKey;
|
const [startdate, enddate, locationId, debouncedSearch] = queryKey;
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}&keyword=${debouncedSearch}`
|
`${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}&keyword=${debouncedSearch}`
|
||||||
);
|
);
|
||||||
console.log('getreportlocationsummary', response.data.details);
|
logger.info('getreportlocationsummary', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
// ==============================|| getriderlocationreportsummary (orders summary)||============================== //
|
// ==============================|| getriderlocationreportsummary (orders summary)||============================== //
|
||||||
export const getriderlocationreportsummary = async ({ queryKey }) => {
|
export const getriderlocationreportsummary = async ({ queryKey }) => {
|
||||||
console.log('queryKey for getriderlocationreportsummary', queryKey);
|
logger.info('queryKey for getriderlocationreportsummary', queryKey);
|
||||||
const [startdate, enddate, locationId] = queryKey;
|
const [startdate, enddate, locationId] = queryKey;
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${process.env.REACT_APP_URL}/deliveries/getriderlocationreportsummary/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}`
|
`${process.env.REACT_APP_URL}/deliveries/getriderlocationreportsummary/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}`
|
||||||
);
|
);
|
||||||
console.log('getriderlocationreportsummary', response.data.details);
|
logger.info('getriderlocationreportsummary', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
@@ -202,7 +203,7 @@ export const fetchLocations = async () => {
|
|||||||
...response.data.details,
|
...response.data.details,
|
||||||
{ partnername: 'All', partnerid: -1 } // Add your new object here
|
{ partnername: 'All', partnerid: -1 } // Add your new object here
|
||||||
];
|
];
|
||||||
console.log('fetchLocations', updatedLocations);
|
logger.info('fetchLocations', updatedLocations);
|
||||||
|
|
||||||
return updatedLocations;
|
return updatedLocations;
|
||||||
};
|
};
|
||||||
@@ -216,7 +217,7 @@ export const gettenantlocations = async ({ queryKey }) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Must return an array — downstream consumers do `.map`/`.length` and a
|
// Must return an array — downstream consumers do `.map`/`.length` and a
|
||||||
// string here crashes the entire Locations page.
|
// string here crashes the entire Locations page.
|
||||||
console.error('Error fetching tenant locations:', error);
|
logger.error('Error fetching tenant locations:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -227,7 +228,7 @@ export const fetchDeliverySummary = async ({ queryKey }) => {
|
|||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${process.env.REACT_APP_URL}/deliveries/deliverysummary?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}`
|
`${process.env.REACT_APP_URL}/deliveries/deliverysummary?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}`
|
||||||
);
|
);
|
||||||
console.log('fetchDeliverySummary', response.data.details);
|
logger.info('fetchDeliverySummary', response.data.details);
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
// ==============================|| fetchAppLocations (report summary))||============================== //
|
// ==============================|| fetchAppLocations (report summary))||============================== //
|
||||||
@@ -238,7 +239,7 @@ export const fetchAppLocations = async () => {
|
|||||||
...response.data.details,
|
...response.data.details,
|
||||||
{ partnername: 'All', applocationid: -1 } // Add your new object here
|
{ partnername: 'All', applocationid: -1 } // Add your new object here
|
||||||
];
|
];
|
||||||
console.log('fetchAppLocations', updatedLocations);
|
logger.info('fetchAppLocations', updatedLocations);
|
||||||
|
|
||||||
return updatedLocations;
|
return updatedLocations;
|
||||||
};
|
};
|
||||||
@@ -246,12 +247,12 @@ export const fetchAppLocations = async () => {
|
|||||||
// ==============================|| fetchRidersSummary (riders summary)||============================== //
|
// ==============================|| fetchRidersSummary (riders summary)||============================== //
|
||||||
|
|
||||||
export const fetchRidersSummary = async ({ queryKey }) => {
|
export const fetchRidersSummary = async ({ queryKey }) => {
|
||||||
console.log('queryKey for fetchRidersSummary', queryKey);
|
logger.info('queryKey for fetchRidersSummary', queryKey);
|
||||||
const [tenantid, startdate, enddate] = queryKey;
|
const [tenantid, startdate, enddate] = queryKey;
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${process.env.REACT_APP_URL}/deliveries/getridersummary/?tenantid=${tenantid}&fromdate=${startdate}&todate=${enddate}`
|
`${process.env.REACT_APP_URL}/deliveries/getridersummary/?tenantid=${tenantid}&fromdate=${startdate}&todate=${enddate}`
|
||||||
);
|
);
|
||||||
console.log('fetchRidersSummary', response.data.details);
|
logger.info('fetchRidersSummary', response.data.details);
|
||||||
|
|
||||||
return response.data.details;
|
return response.data.details;
|
||||||
};
|
};
|
||||||
@@ -281,19 +282,19 @@ export const fetchorderdetails = async ({ pageParam = 0, queryKey }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// export const fetchorderdetails = async ({ queryKey }) => {
|
// export const fetchorderdetails = async ({ queryKey }) => {
|
||||||
// console.log('queryKey of fetchorderdetails', queryKey);
|
// logger.info('queryKey of fetchorderdetails', queryKey);
|
||||||
// const [startdate, enddate, currentStatus] = queryKey;
|
// const [startdate, enddate, currentStatus] = queryKey;
|
||||||
// const response = await axios.get(
|
// const response = await axios.get(
|
||||||
// `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?tenantid=${tenid}&fromdate=${startdate}&todate=${enddate}&status=${
|
// `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?tenantid=${tenid}&fromdate=${startdate}&todate=${enddate}&status=${
|
||||||
// currentStatus == 'All' ? '' : currentStatus
|
// currentStatus == 'All' ? '' : currentStatus
|
||||||
// }`
|
// }`
|
||||||
// );
|
// );
|
||||||
// console.log('fetchorderdetails', response.data.details);
|
// logger.info('fetchorderdetails', response.data.details);
|
||||||
// return response.data.details;
|
// return response.data.details;
|
||||||
// };
|
// };
|
||||||
// ==============================|| fetchCount (orders detail)||============================== //
|
// ==============================|| fetchCount (orders detail)||============================== //
|
||||||
export const fetchCount = async ({ queryKey }) => {
|
export const fetchCount = async ({ queryKey }) => {
|
||||||
console.log('queryKey of fetchCount', queryKey);
|
logger.info('queryKey of fetchCount', queryKey);
|
||||||
const [startdate, enddate] = queryKey;
|
const [startdate, enddate] = queryKey;
|
||||||
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getorders/?fromdate=${startdate}&todate=${enddate}`);
|
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getorders/?fromdate=${startdate}&todate=${enddate}`);
|
||||||
const calculateOrderCounts = () => {
|
const calculateOrderCounts = () => {
|
||||||
@@ -314,7 +315,7 @@ export const fetchCount = async ({ queryKey }) => {
|
|||||||
|
|
||||||
return { deliveredCount, pendingCount, cancelledCount };
|
return { deliveredCount, pendingCount, cancelledCount };
|
||||||
};
|
};
|
||||||
console.log('fetchCount', calculateOrderCounts());
|
logger.info('fetchCount', calculateOrderCounts());
|
||||||
|
|
||||||
return calculateOrderCounts();
|
return calculateOrderCounts();
|
||||||
};
|
};
|
||||||
@@ -333,7 +334,7 @@ export const fetchRidersLogs = async ({ queryKey }) => {
|
|||||||
startdate || ''
|
startdate || ''
|
||||||
}&keyword=${riderSearch || ''}`
|
}&keyword=${riderSearch || ''}`
|
||||||
);
|
);
|
||||||
console.log('fetchRidersLogs', riderLogsResponse.data.details);
|
logger.info('fetchRidersLogs', riderLogsResponse.data.details);
|
||||||
return riderLogsResponse.data.details;
|
return riderLogsResponse.data.details;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -475,7 +476,7 @@ export const getallriders = async () => {
|
|||||||
const res = await axios.get(`${process.env.REACT_APP_URL}/partners/getallriders?partnerid=64`);
|
const res = await axios.get(`${process.env.REACT_APP_URL}/partners/getallriders?partnerid=64`);
|
||||||
return res.data.details;
|
return res.data.details;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('getallriders', err.message);
|
logger.error('getallriders', err.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,29 @@
|
|||||||
import { React, useEffect, useState } from 'react';
|
import { React, useEffect, useState } from 'react';
|
||||||
|
|
||||||
// material-ui
|
import { Button } from '@astryxdesign/core/Button';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { TextInput } from '@astryxdesign/core/TextInput';
|
||||||
import {
|
import { HStack } from '@astryxdesign/core/HStack';
|
||||||
Box,
|
import { Text } from '@astryxdesign/core/Text';
|
||||||
Button,
|
|
||||||
FormLabel,
|
|
||||||
Grid,
|
|
||||||
InputLabel,
|
|
||||||
MenuItem,
|
|
||||||
Select,
|
|
||||||
Stack,
|
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
IconButton,
|
|
||||||
Autocomplete,
|
|
||||||
InputAdornment
|
|
||||||
} from '@mui/material';
|
|
||||||
import MyLocationIcon from '@mui/icons-material/MyLocation';
|
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import Avatar from 'components/@extended/Avatar';
|
|
||||||
import MainCard from 'components/MainCard';
|
import MainCard from 'components/MainCard';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import { usePlacesWidget } from 'react-google-autocomplete';
|
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import Geocode from 'react-geocode';
|
import Geocode from 'react-geocode';
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from 'notistack';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
|
||||||
import TitleCard from '../titleCard';
|
import TitleCard from '../titleCard';
|
||||||
|
|
||||||
|
import logger from 'utils/logger';
|
||||||
|
|
||||||
|
const FieldGrid = ({ children }) => (
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 24 }}>{children}</div>
|
||||||
|
);
|
||||||
|
|
||||||
const CreateCustomer = () => {
|
const CreateCustomer = () => {
|
||||||
const appId = localStorage.getItem('applocationid');
|
const appId = localStorage.getItem('applocationid');
|
||||||
const theme = useTheme();
|
|
||||||
const [selectedImage, setSelectedImage] = useState(undefined);
|
const [selectedImage, setSelectedImage] = useState(undefined);
|
||||||
const [avatar, setAvatar] = useState();
|
const [avatar, setAvatar] = useState();
|
||||||
const [businessname, setBusinessname] = useState('');
|
const [businessname, setBusinessname] = useState('');
|
||||||
@@ -103,8 +91,8 @@ const CreateCustomer = () => {
|
|||||||
autocomplete.addListener('place_changed', () => {
|
autocomplete.addListener('place_changed', () => {
|
||||||
const place = autocomplete.getPlace();
|
const place = autocomplete.getPlace();
|
||||||
setInputValue2(`${place.name}, ${place.formatted_address}`);
|
setInputValue2(`${place.name}, ${place.formatted_address}`);
|
||||||
console.log('new place', place); // Do something with the selected place
|
logger.info('new place', place); // Do something with the selected place
|
||||||
console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
|
logger.info(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
|
||||||
// to trigger getDistance
|
// to trigger getDistance
|
||||||
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
|
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
|
||||||
setAddress(`${place.name} ${place.formatted_address}`);
|
setAddress(`${place.name} ${place.formatted_address}`);
|
||||||
@@ -164,7 +152,7 @@ const CreateCustomer = () => {
|
|||||||
latitude: place.geometry.location.lat(),
|
latitude: place.geometry.location.lat(),
|
||||||
longitude: place.geometry.location.lng()
|
longitude: place.geometry.location.lng()
|
||||||
});
|
});
|
||||||
console.log('Pick Address:', address);
|
logger.info('Pick Address:', address);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [inputValue2]);
|
}, [inputValue2]);
|
||||||
@@ -174,18 +162,18 @@ const CreateCustomer = () => {
|
|||||||
await axios
|
await axios
|
||||||
.get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`)
|
.get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log('getapplocations', res);
|
logger.info('getapplocations', res);
|
||||||
const { opentime, closetime, latitude, longitude, radius } = res.data.details[0];
|
const { opentime, closetime, latitude, longitude, radius } = res.data.details?.[0] || {};
|
||||||
if (res.data.status) {
|
if (res.data.status) {
|
||||||
setAppLocaLat(latitude);
|
setAppLocaLat(latitude);
|
||||||
setAppLocaLng(longitude);
|
setAppLocaLng(longitude);
|
||||||
setAppLocaRadius(radius);
|
setAppLocaRadius(radius);
|
||||||
console.log('radius', radius);
|
logger.info('radius', radius);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
logger.error(err);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -201,11 +189,11 @@ const CreateCustomer = () => {
|
|||||||
// ...locationRes.data.details,
|
// ...locationRes.data.details,
|
||||||
// { partnername: 'All', applocationid: -1 }
|
// { partnername: 'All', applocationid: -1 }
|
||||||
// ];
|
// ];
|
||||||
// console.log('fetchAppLocations', updatedLocations);
|
// logger.info('fetchAppLocations', updatedLocations);
|
||||||
console.log('fetchAppLocations', locationRes.data.details);
|
logger.info('fetchAppLocations', locationRes.data.details);
|
||||||
setLocations(locationRes.data.details);
|
setLocations(locationRes.data.details);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('locationRes', err);
|
logger.error('locationRes', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -220,7 +208,7 @@ const CreateCustomer = () => {
|
|||||||
.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${id}&status=active`)
|
.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${id}&status=active`)
|
||||||
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
logger.info(res);
|
||||||
if (res.data.status) {
|
if (res.data.status) {
|
||||||
let arr = [];
|
let arr = [];
|
||||||
res.data.details.map((val) => {
|
res.data.details.map((val) => {
|
||||||
@@ -234,7 +222,7 @@ const CreateCustomer = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
logger.error(err);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -242,7 +230,7 @@ const CreateCustomer = () => {
|
|||||||
const gettenantlocations = async (id) => {
|
const gettenantlocations = async (id) => {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`);
|
const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`);
|
||||||
console.log('gettenantlocations', res.data.details);
|
logger.info('gettenantlocations', res.data.details);
|
||||||
if (res.data.details.length == 1) {
|
if (res.data.details.length == 1) {
|
||||||
setIsLocation(true);
|
setIsLocation(true);
|
||||||
setTenantlocations(res.data.details);
|
setTenantlocations(res.data.details);
|
||||||
@@ -254,7 +242,7 @@ const CreateCustomer = () => {
|
|||||||
setIsBusiness(false); // became true after select from tenanatLocations
|
setIsBusiness(false); // became true after select from tenanatLocations
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('gettenantlocations', err);
|
logger.error('gettenantlocations', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -271,14 +259,14 @@ const CreateCustomer = () => {
|
|||||||
await axios
|
await axios
|
||||||
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
|
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log('fetchtenantinfo', res.data.details);
|
logger.info('fetchtenantinfo', res.data.details);
|
||||||
if (res.data.status) {
|
if (res.data.status) {
|
||||||
setTenantinfo(res.data.details);
|
setTenantinfo(res.data.details);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
logger.error(err);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -314,21 +302,21 @@ const CreateCustomer = () => {
|
|||||||
customertoken: '',
|
customertoken: '',
|
||||||
primaryaddress: 1
|
primaryaddress: 1
|
||||||
};
|
};
|
||||||
console.log(obj);
|
logger.info(obj);
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await axios
|
await axios
|
||||||
.post(`${process.env.REACT_APP_URL}/customers/create`, obj)
|
.post(`${process.env.REACT_APP_URL}/customers/create`, obj)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
logger.info(res);
|
||||||
if (res.data.status) {
|
if (res.data.status) {
|
||||||
enqueueSnackbar(' Created Successfully ', {
|
enqueueSnackbar(' Created Successfully ', {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
||||||
autoHideDuration: 2000
|
autoHideDuration: 2000
|
||||||
});
|
});
|
||||||
navigate('/customers');
|
navigate('/nearle/customers');
|
||||||
} else if (res.data.message == 'Customer Already available') {
|
} else if (res.data.message == 'Customer Already available') {
|
||||||
enqueueSnackbar('Customer Already available', {
|
enqueueSnackbar('Customer Already available', {
|
||||||
variant: 'error',
|
variant: 'error',
|
||||||
@@ -339,7 +327,7 @@ const CreateCustomer = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
logger.error(err);
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
enqueueSnackbar(err.message, {
|
enqueueSnackbar(err.message, {
|
||||||
@@ -349,7 +337,7 @@ const CreateCustomer = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
logger.error(err);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -359,107 +347,67 @@ const CreateCustomer = () => {
|
|||||||
{loading && <Loader />}
|
{loading && <Loader />}
|
||||||
<TitleCard
|
<TitleCard
|
||||||
title={'Create Customer'}
|
title={'Create Customer'}
|
||||||
secondary={
|
secondary={<Button label="Back" variant="secondary" onClick={() => navigate('/nearle/customers')} />}
|
||||||
<Button variant="contained" color="secondary" onClick={() => navigate('/nearle/customers')}>
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MainCard>
|
<MainCard>
|
||||||
<Grid container spacing={3}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||||
<Grid item xs={12}>
|
<FieldGrid>
|
||||||
<Grid container spacing={3}>
|
|
||||||
{/* ===================================================== || Name|| ===================================================== */}
|
{/* ===================================================== || Name|| ===================================================== */}
|
||||||
<Grid item xs={12} sm={6}>
|
<TextInput label="Name" placeholder="Name" onChange={setFirstname} value={firstname} />
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-last-name">Name</InputLabel>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
id="personal-last-name"
|
|
||||||
placeholder="Name"
|
|
||||||
onChange={(e) => setFirstname(e.target.value)}
|
|
||||||
value={firstname}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
{/* ===================================================== || Phone Number || ===================================================== */}
|
{/* ===================================================== || Phone Number || ===================================================== */}
|
||||||
<Grid item xs={12} sm={6}>
|
<div>
|
||||||
<Stack spacing={1.25}>
|
<Text type="strong" size="sm" style={{ marginBottom: 6, display: 'block' }}>
|
||||||
<InputLabel htmlFor="personal-phone">Phone Number</InputLabel>
|
Phone Number
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
</Text>
|
||||||
<Select defaultValue="+1" disabled sx={{ cursor: 'not-allowed' }}>
|
<HStack gap={2} vAlign="center">
|
||||||
<MenuItem value="+1">+91</MenuItem>
|
<div
|
||||||
</Select>
|
style={{
|
||||||
<TextField
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: 36,
|
||||||
|
padding: '0 12px',
|
||||||
|
borderRadius: 10,
|
||||||
|
border: '1px solid var(--color-border)',
|
||||||
|
color: 'var(--color-text-secondary)',
|
||||||
|
cursor: 'not-allowed'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+91
|
||||||
|
</div>
|
||||||
|
<TextInput
|
||||||
|
label="Phone Number"
|
||||||
|
isLabelHidden
|
||||||
type="number"
|
type="number"
|
||||||
id="personal-phone"
|
|
||||||
fullWidth
|
|
||||||
placeholder="Phone Number"
|
placeholder="Phone Number"
|
||||||
onChange={(e) => {
|
width="100%"
|
||||||
if (e.target.value.toString().length <= 10) {
|
onChange={(v) => {
|
||||||
setMobilenumber(e.target.value);
|
if (v.toString().length <= 10) setMobilenumber(v);
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
value={mobilenumber}
|
value={mobilenumber}
|
||||||
autoComplete="off"
|
|
||||||
// disabled
|
|
||||||
sx={{ cursor: 'not-allowed' }}
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</HStack>
|
||||||
</Stack>
|
</div>
|
||||||
</Grid>
|
|
||||||
{/* ===================================================== || Email|| ===================================================== */}
|
{/* ===================================================== || Email|| ===================================================== */}
|
||||||
|
<TextInput label="Email Address" type="email" placeholder="Email Address" onChange={setEmailaddress} value={emailaddress} />
|
||||||
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-email">Email Address</InputLabel>
|
|
||||||
<TextField
|
|
||||||
type="email"
|
|
||||||
fullWidth
|
|
||||||
// defaultValue="stebin.ben@gmail.com"
|
|
||||||
id="personal-email"
|
|
||||||
placeholder="Email Address"
|
|
||||||
onChange={(e) => setEmailaddress(e.target.value)}
|
|
||||||
value={emailaddress}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
{/* ===================================================== || door no || ===================================================== */}
|
{/* ===================================================== || door no || ===================================================== */}
|
||||||
|
<TextInput label="Door No" placeholder="Door No" onChange={setDoorno} value={doorno} />
|
||||||
<Grid item xs={12} sm={6}>
|
</FieldGrid>
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-location">Door No</InputLabel>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
// defaultValue="New York"
|
|
||||||
id="personal-location"
|
|
||||||
placeholder="Door No"
|
|
||||||
onChange={(e) => setDoorno(e.target.value)}
|
|
||||||
value={doorno}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* ===================================================== || Address || ===================================================== */}
|
{/* ===================================================== || Address || ===================================================== */}
|
||||||
|
<TextInput
|
||||||
<Grid item xs={12}>
|
label="Address"
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-email"> Address</InputLabel>
|
|
||||||
<TextField
|
|
||||||
variant="outlined"
|
|
||||||
id="addressAuto1"
|
id="addressAuto1"
|
||||||
fullWidth
|
width="100%"
|
||||||
|
hasClear
|
||||||
value={inputValue2}
|
value={inputValue2}
|
||||||
onChange={(e) => setInputValue2(e.target.value)}
|
onChange={(v) => {
|
||||||
InputProps={{
|
setInputValue2(v);
|
||||||
endAdornment: (
|
if (!v) {
|
||||||
<IconButton
|
|
||||||
onClick={() => {
|
|
||||||
setInputValue2('');
|
|
||||||
setPickCust({
|
setPickCust({
|
||||||
...pickCust,
|
...pickCust,
|
||||||
doorno: '',
|
doorno: '',
|
||||||
@@ -469,96 +417,34 @@ const CreateCustomer = () => {
|
|||||||
landmark: ''
|
landmark: ''
|
||||||
});
|
});
|
||||||
setStartPoint({ latitude: 0, longitude: 0 });
|
setStartPoint({ latitude: 0, longitude: 0 });
|
||||||
}}
|
}
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12} sm={6}>
|
<FieldGrid>
|
||||||
<Stack spacing={1.25}>
|
<TextInput
|
||||||
<InputLabel htmlFor="personal-location">Location</InputLabel>
|
label="Location"
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
// defaultValue="New York"
|
|
||||||
id="personal-location"
|
|
||||||
placeholder="Location"
|
placeholder="Location"
|
||||||
onChange={(e) => setPickCust({ ...pickCust, suburb: e.target.value })}
|
onChange={(v) => setPickCust({ ...pickCust, suburb: v })}
|
||||||
value={pickCust.suburb}
|
value={pickCust.suburb}
|
||||||
autoComplete="off"
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
<TextInput label="City" placeholder="City" onChange={(v) => setPickCust({ ...pickCust, city: v })} value={pickCust.city} />
|
||||||
</Grid>
|
<TextInput label="State" placeholder="State" onChange={(v) => setPickCust({ ...pickCust, state: v })} value={pickCust.state} />
|
||||||
<Grid item xs={12} sm={6}>
|
<TextInput
|
||||||
<Stack spacing={1.25}>
|
label="Post Code"
|
||||||
<InputLabel htmlFor="personal-zipcode">City</InputLabel>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
id="personal-zipcode"
|
|
||||||
placeholder="City"
|
|
||||||
onChange={(e) => setPickCust({ ...pickCust, city: e.target.value })}
|
|
||||||
value={pickCust.city}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-location">State</InputLabel>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
// defaultValue="New York"
|
|
||||||
id="personal-location"
|
|
||||||
placeholder="State"
|
|
||||||
onChange={(e) => setPickCust({ ...pickCust, state: e.target.value })}
|
|
||||||
value={pickCust.state}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-zipcode">Post Code</InputLabel>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
// defaultValue="956754"
|
|
||||||
type="number"
|
type="number"
|
||||||
id="personal-zipcode"
|
|
||||||
placeholder="Zipcode"
|
placeholder="Zipcode"
|
||||||
onChange={(e) => setPickCust({ ...pickCust, postcode: e.target.value })}
|
onChange={(v) => setPickCust({ ...pickCust, postcode: v })}
|
||||||
value={pickCust.postcode}
|
value={pickCust.postcode}
|
||||||
autoComplete="off"
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</FieldGrid>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<TextInput label="Landmark" placeholder="Landmark" width="100%" onChange={setLandmark} value={landmark} />
|
||||||
<Stack spacing={1.25}>
|
|
||||||
<InputLabel htmlFor="personal-email">Landmark</InputLabel>
|
<HStack justify="end">
|
||||||
<TextField
|
|
||||||
type="email"
|
|
||||||
fullWidth
|
|
||||||
// defaultValue="stebin.ben@gmail.com"
|
|
||||||
id="personal-email"
|
|
||||||
placeholder="Landmark"
|
|
||||||
onChange={(e) => setLandmark(e.target.value)}
|
|
||||||
value={landmark}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Stack direction="row" justifyContent="flex-end" alignItems="center" spacing={2}>
|
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
label="Create"
|
||||||
|
variant="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (appId === '') {
|
if (appId === '') {
|
||||||
opentoast('Select Applocation ');
|
opentoast('Select Applocation ');
|
||||||
@@ -588,12 +474,9 @@ const CreateCustomer = () => {
|
|||||||
createprofile();
|
createprofile();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
Create
|
</HStack>
|
||||||
</Button>
|
</div>
|
||||||
</Stack>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</MainCard>
|
</MainCard>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user