initialised commit on the ui converted in mui to astryx and changed the design to doormile
This commit is contained in:
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/
|
||||
build/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.development
|
||||
.env.staging
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
@@ -2,6 +2,54 @@ const webpack = require('webpack');
|
||||
const WorkBoxPlugin = require('workbox-webpack-plugin');
|
||||
|
||||
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 = {
|
||||
process: require.resolve('process/browser'),
|
||||
// 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": {
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"@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",
|
||||
"@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",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@reduxjs/toolkit": "^2.12.0",
|
||||
"@stylexjs/stylex": "^0.19.0",
|
||||
"@svgr/webpack": "^7.0.0",
|
||||
"@tanstack/react-query": "^5.22.2",
|
||||
"antd": "^5.11.5",
|
||||
@@ -24,43 +19,42 @@
|
||||
"buffer": "^6.0.3",
|
||||
"chance": "^1.1.11",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"env-cmd": "^10.1.0",
|
||||
"formik": "^2.2.9",
|
||||
"framer-motion": "^10.12.4",
|
||||
"framer-motion": "^12.42.2",
|
||||
"geolib": "^3.3.4",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash": "^4.17.21",
|
||||
"mui-daterange-picker": "^1.0.5",
|
||||
"notistack": "^3.0.1",
|
||||
"papaparse": "^5.5.3",
|
||||
"process": "^0.11.10",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react": "^19.2.8",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-csv": "^2.2.2",
|
||||
"react-device-detect": "^2.2.3",
|
||||
"react-dnd": "^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-google-autocomplete": "^2.7.3",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-intl": "^6.4.1",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-intl": "^7.1.14",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"react-loading-icons": "^1.1.0",
|
||||
"react-phone-number-input": "^3.3.10",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^6.10.0",
|
||||
"react-router-dom": "^6.10.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-timer-hook": "^3.0.5",
|
||||
"react-to-print": "^2.15.1",
|
||||
"react-to-print": "^3.3.0",
|
||||
"react-virtuoso": "^4.7.0",
|
||||
"react18-input-otp": "^1.1.3",
|
||||
"redux": "^4.2.1",
|
||||
"redux": "^5.0.1",
|
||||
"simplebar": "^6.2.5",
|
||||
"simplebar-react": "^3.2.4",
|
||||
"stream-browserify": "^3.0.0",
|
||||
@@ -109,6 +103,9 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.4",
|
||||
"@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-config-prettier": "^8.8.0",
|
||||
"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.
|
||||
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
|
||||
|
||||
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 181 KiB |
@@ -1,6 +1,5 @@
|
||||
// project import
|
||||
import Routes from 'routes';
|
||||
import ThemeCustomization from 'themes';
|
||||
import Locales from 'components/Locales';
|
||||
// import RTLLayout from 'components/RTLLayout';
|
||||
import ScrollTop from 'components/ScrollTop';
|
||||
@@ -45,7 +44,11 @@ const App = () => {
|
||||
|
||||
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 />
|
||||
{/* <RTLLayout> */}
|
||||
<Locales>
|
||||
@@ -61,7 +64,6 @@ const App = () => {
|
||||
</ScrollTop>
|
||||
</Locales>
|
||||
{/* </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 { forwardRef } from 'react';
|
||||
|
||||
// material-ui
|
||||
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 })
|
||||
})
|
||||
);
|
||||
import { IconButton as AstryxIconButton } from '@astryxdesign/core/IconButton';
|
||||
|
||||
// ==============================|| 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 theme = useTheme();
|
||||
const VARIANT_MAP = {
|
||||
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 (
|
||||
<IconButtonStyle ref={ref} disableRipple variant={variant} shape={shape} theme={theme} color={color} {...others}>
|
||||
{children}
|
||||
</IconButtonStyle>
|
||||
<AstryxIconButton
|
||||
ref={ref}
|
||||
icon={children}
|
||||
label={label || ariaLabel || 'Action'}
|
||||
variant={resolvedVariant}
|
||||
size={SIZE_MAP[size] || 'md'}
|
||||
style={{ ...(shape === 'rounded' ? { borderRadius: '50%' } : null), ...style }}
|
||||
{...others}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
IconButton.propTypes = {
|
||||
variant: PropTypes.string,
|
||||
shape: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
color: PropTypes.string
|
||||
color: PropTypes.string,
|
||||
size: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
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 { Alert, Button, Fade, Grow, Slide } from '@mui/material';
|
||||
import MuiSnackbar from '@mui/material/Snackbar';
|
||||
import { Toast } from '@astryxdesign/core/Toast';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
|
||||
// project-import
|
||||
import IconButton from './IconButton';
|
||||
import { dispatch, useSelector } from 'store';
|
||||
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 ||============================== //
|
||||
// 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 = 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) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
dispatch(closeSnackbar());
|
||||
};
|
||||
const handleClose = () => dispatch(closeSnackbar());
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
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 (
|
||||
<>
|
||||
{/* default snackbar */}
|
||||
{variant === 'default' && (
|
||||
<MuiSnackbar
|
||||
anchorOrigin={anchorOrigin}
|
||||
open={open}
|
||||
autoHideDuration={6000}
|
||||
onClose={handleClose}
|
||||
message={message}
|
||||
TransitionComponent={animation[transition]}
|
||||
action={
|
||||
<>
|
||||
<Button color="secondary" size="small" onClick={handleClose}>
|
||||
UNDO
|
||||
</Button>
|
||||
<IconButton size="small" aria-label="close" color="inherit" onClick={handleClose} sx={{ mt: 0.25 }}>
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* alert snackbar */}
|
||||
{variant === 'alert' && (
|
||||
<MuiSnackbar
|
||||
TransitionComponent={animation[transition]}
|
||||
anchorOrigin={anchorOrigin}
|
||||
open={open}
|
||||
autoHideDuration={6000}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<Alert
|
||||
variant={alert.variant}
|
||||
color={alert.color}
|
||||
action={
|
||||
<>
|
||||
{actionButton !== false && (
|
||||
<Button color={alert.color} size="small" onClick={handleClose}>
|
||||
UNDO
|
||||
</Button>
|
||||
)}
|
||||
{close !== false && (
|
||||
<IconButton
|
||||
sx={{ mt: 0.25 }}
|
||||
size="small"
|
||||
aria-label="close"
|
||||
variant="contained"
|
||||
color={alert.color}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<CloseOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
sx={{
|
||||
...(alert.variant === 'outlined' && {
|
||||
bgcolor: 'grey.0'
|
||||
})
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
[vertical]: 24,
|
||||
...(horizontal === 'center' ? { left: '50%', transform: 'translateX(-50%)' } : { [horizontal]: 24 }),
|
||||
zIndex: 1500
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
</MuiSnackbar>
|
||||
)}
|
||||
</>
|
||||
<Toast
|
||||
type={isError ? 'error' : 'info'}
|
||||
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 { 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)
|
||||
}
|
||||
}));
|
||||
import { ProgressBar } from '@astryxdesign/core/ProgressBar';
|
||||
|
||||
// ==============================|| 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 = () => (
|
||||
<LoaderWrapper>
|
||||
<LinearProgress color="primary" />
|
||||
</LoaderWrapper>
|
||||
<div style={{ position: 'fixed', top: 0, left: 0, zIndex: 2001, width: '100%', '--color-accent': '#C01227' }}>
|
||||
<ProgressBar label="Loading" isLabelHidden isIndeterminate variant="accent" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Loader;
|
||||
|
||||
@@ -1,113 +1,103 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material';
|
||||
import { Card } from '@astryxdesign/core/Card';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
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
|
||||
import { ThemeMode } from 'config';
|
||||
// Legacy pages still pass MUI `sx`-shaped objects (spacing shorthand + a few
|
||||
// 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 headerSX = {
|
||||
p: 2.5,
|
||||
'& .MuiCardHeader-action': { m: '0px auto', alignSelf: 'center' }
|
||||
const sxToStyle = (sx) => {
|
||||
if (!sx || typeof sx !== 'object') return undefined;
|
||||
const style = {};
|
||||
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 ||============================== //
|
||||
|
||||
const MainCard = forwardRef(
|
||||
(
|
||||
{
|
||||
border = true,
|
||||
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;
|
||||
({ boxShadow, children, subheader, content = true, contentSX = {}, darkTitle, divider = true, elevation, secondary, sx = {}, title, ...others }, ref) => {
|
||||
const style = sxToStyle(sx);
|
||||
const isTransparent = style?.backgroundColor === 'transparent';
|
||||
|
||||
return (
|
||||
<Card
|
||||
elevation={elevation || 0}
|
||||
ref={ref}
|
||||
variant={isTransparent ? 'transparent' : 'default'}
|
||||
elevation={boxShadow ? 'low' : elevation ? 'med' : 'none'}
|
||||
padding={0}
|
||||
style={style}
|
||||
{...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 */}
|
||||
{!darkTitle && title && (
|
||||
<CardHeader
|
||||
sx={headerSX}
|
||||
titleTypographyProps={{ variant: 'subtitle1' }}
|
||||
title={title}
|
||||
action={secondary}
|
||||
subheader={subheader}
|
||||
/>
|
||||
{title && (
|
||||
<VStack gap={0} padding={0}>
|
||||
<HStack justify="space-between" vAlign="center" padding={2.5}>
|
||||
{darkTitle ? (
|
||||
<Heading level={4}>{title}</Heading>
|
||||
) : typeof title === 'string' ? (
|
||||
<Text type="strong">{title}</Text>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
{darkTitle && title && <CardHeader sx={headerSX} title={<Typography variant="h4">{title}</Typography>} action={secondary} />}
|
||||
|
||||
{/* content & header divider */}
|
||||
{title && divider && <Divider />}
|
||||
|
||||
{/* card content */}
|
||||
{content && <CardContent sx={contentSX}>{children}</CardContent>}
|
||||
{!content && children}
|
||||
{secondary}
|
||||
</HStack>
|
||||
{subheader && <Text type="supporting">{subheader}</Text>}
|
||||
{divider && <Divider />}
|
||||
</VStack>
|
||||
)}
|
||||
{content ? <div style={{ padding: 20, ...sxToStyle(contentSX) }}>{children}</div> : children}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
MainCard.propTypes = {
|
||||
border: PropTypes.bool,
|
||||
boxShadow: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
subheader: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
|
||||
content: PropTypes.bool,
|
||||
contentClass: PropTypes.string,
|
||||
contentSX: PropTypes.object,
|
||||
darkTitle: PropTypes.bool,
|
||||
divider: PropTypes.bool,
|
||||
elevation: PropTypes.number,
|
||||
secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
||||
shadow: PropTypes.string,
|
||||
sx: PropTypes.object,
|
||||
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
|
||||
modal: PropTypes.bool
|
||||
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])
|
||||
};
|
||||
|
||||
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';
|
||||
|
||||
// material-ui
|
||||
import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
|
||||
|
||||
// ===========================|| HOVER SOCIAL CARD ||=========================== //
|
||||
|
||||
const HoverSocialCard = ({
|
||||
@@ -16,53 +13,42 @@ const HoverSocialCard = ({
|
||||
// const primaryIcon = iconPrimary ? <IconPrimary /> : null;
|
||||
|
||||
return (
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
<>
|
||||
<style>{`
|
||||
.hover-social-card:hover svg { opacity: 1; transform: scale(1.1); }
|
||||
`}</style>
|
||||
<div
|
||||
className="hover-social-card"
|
||||
style={{
|
||||
background: color,
|
||||
position: 'relative',
|
||||
color: '#fff',
|
||||
'&:hover svg': {
|
||||
opacity: 1,
|
||||
transform: 'scale(1.1)'
|
||||
}
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
<Box
|
||||
sx={{
|
||||
<div style={{ padding: 16 }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 15,
|
||||
top: 25,
|
||||
color: '#fff',
|
||||
'& svg': {
|
||||
width: 36,
|
||||
height: 36,
|
||||
opacity: 0.5,
|
||||
transition: 'all .3s ease-in-out'
|
||||
}
|
||||
color: '#fff'
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2" color="inherit">
|
||||
<span style={{ fontSize: '2.25rem', fontWeight: 600, color: 'inherit' }}>
|
||||
{/* {percentage.toString()} % */}
|
||||
{percentage ? `${percentage.toString()} %` : ''}
|
||||
</Typography>
|
||||
</span>
|
||||
{/* {primaryIcon} */}
|
||||
</Box>
|
||||
<Grid container spacing={0}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h3" color="inherit">
|
||||
{secondary}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2" color="inherit">
|
||||
{primary}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<span style={{ fontSize: '1.5rem', fontWeight: 600, color: 'inherit' }}>{secondary}</span>
|
||||
<span style={{ fontSize: '0.875rem', fontWeight: 600, color: 'inherit' }}>{primary}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
// Brand colors — this app runs in light mode only (see config.js `mode: ThemeMode.LIGHT`),
|
||||
// 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.
|
||||
@@ -13,8 +18,6 @@ import { useTheme } from '@mui/material/styles';
|
||||
// ==============================|| LOGO ICON SVG ||============================== //
|
||||
|
||||
const LogoIcon = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
/**
|
||||
* if you want to use image instead of svg uncomment following, and comment out <svg> element.
|
||||
@@ -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">
|
||||
<path
|
||||
d="M7.27577 57.2242L17.5616 46.9384L17.5724 46.9276H36.9234L29.2238 54.6273L27.2358 56.6152L19.3511 64.5L20.3276 65.4792L64.5 109.649L109.649 64.5L101.761 56.6152L101.206 56.0572L92.0766 46.9276H111.428L111.438 46.9384L119.5 55.0002L129 64.5L64.5 129L0 64.5L7.27577 57.2242ZM64.5 0L101.77 37.2695H82.4185L64.5 19.3511L46.5816 37.2695H27.2305L64.5 0Z"
|
||||
fill={theme.palette.primary.dark}
|
||||
fill={PRIMARY_DARK}
|
||||
/>
|
||||
<path
|
||||
d="M19.3509 64.5L27.2357 56.6152L29.2236 54.6273L21.5267 46.9276H17.5722L17.5615 46.9384L7.27561 57.2242L17.1483 67.0487L19.3509 64.5Z"
|
||||
@@ -37,16 +40,16 @@ const LogoIcon = () => {
|
||||
/>
|
||||
<path
|
||||
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>
|
||||
<linearGradient id="paint0_linear" x1="25.0225" y1="49.3259" x2="11.4189" y2="62.9295" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor={theme.palette.primary.darker} />
|
||||
<stop offset="0.9637" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
||||
<stop stopColor={PRIMARY_DARKER} />
|
||||
<stop offset="0.9637" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="103.5" y1="49.5" x2="114.5" y2="62" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor={theme.palette.primary.darker} />
|
||||
<stop offset="1" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
||||
<stop stopColor={PRIMARY_DARKER} />
|
||||
<stop offset="1" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { ThemeMode } from 'config';
|
||||
// Brand colors — this app runs in light mode only (see config.js `mode: ThemeMode.LIGHT`),
|
||||
// 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';
|
||||
const COMMON_WHITE = '#fff';
|
||||
const COMMON_BLACK = '#000';
|
||||
|
||||
/**
|
||||
* if you want to use image instead of <svg> uncomment following.
|
||||
@@ -15,7 +21,6 @@ import { ThemeMode } from 'config';
|
||||
// ==============================|| LOGO SVG ||============================== //
|
||||
|
||||
const LogoMain = ({ reverse }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
/**
|
||||
* 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">
|
||||
<path
|
||||
d="M4.63564 15.8644L6.94797 13.552L6.95038 13.5496H11.3006L9.56969 15.2806L9.12278 15.7275L7.35024 17.5L7.56977 17.7201L17.5 27.6498L27.6498 17.5L25.8766 15.7275L25.7518 15.602L23.6994 13.5496H28.0496L28.052 13.552L29.8644 15.3644L32 17.5L17.5 32L3 17.5L4.63564 15.8644ZM17.5 3L25.8784 11.3784H21.5282L17.5 7.35024L13.4718 11.3784H9.12158L17.5 3Z"
|
||||
fill={theme.palette.primary.dark}
|
||||
fill={PRIMARY_DARK}
|
||||
/>
|
||||
<path
|
||||
d="M7.35025 17.5L9.1228 15.7275L9.5697 15.2805L7.83937 13.5496H6.95039L6.94798 13.552L4.63564 15.8644L6.8551 18.073L7.35025 17.5Z"
|
||||
@@ -39,21 +44,21 @@ const LogoMain = ({ reverse }) => {
|
||||
/>
|
||||
<path
|
||||
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
|
||||
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"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="8.62526" y1="14.0888" x2="5.56709" y2="17.1469" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor={theme.palette.primary.darker} />
|
||||
<stop offset="0.9637" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
||||
<stop stopColor={PRIMARY_DARKER} />
|
||||
<stop offset="0.9637" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="26.2675" y1="14.1279" x2="28.7404" y2="16.938" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor={theme.palette.primary.darker} />
|
||||
<stop offset="1" stopColor={theme.palette.primary.dark} stopOpacity="0" />
|
||||
<stop stopColor={PRIMARY_DARKER} />
|
||||
<stop offset="1" stopColor={PRIMARY_DARK} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { ButtonBase } from '@mui/material';
|
||||
|
||||
// project import
|
||||
import LogoMain from './LogoMain';
|
||||
import LogoIcon from './LogoIcon';
|
||||
@@ -11,16 +8,28 @@ import { APP_DEFAULT_PATH } from 'config';
|
||||
|
||||
// ==============================|| MAIN LOGO ||============================== //
|
||||
|
||||
const LogoSection = ({ reverse, isIcon, sx, to }) => (
|
||||
<ButtonBase disableRipple component={Link} to={!to ? APP_DEFAULT_PATH : to} sx={sx}>
|
||||
const LogoSection = ({ reverse, isIcon, style, to }) => (
|
||||
<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} />}
|
||||
</ButtonBase>
|
||||
</Link>
|
||||
);
|
||||
|
||||
LogoSection.propTypes = {
|
||||
reverse: PropTypes.bool,
|
||||
isIcon: PropTypes.bool,
|
||||
sx: PropTypes.object,
|
||||
style: PropTypes.object,
|
||||
to: PropTypes.string
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { styled } from '@mui/material/styles';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import nelogo from '../../assets/images/logo-sm.png';
|
||||
import { Spinner } from '@astryxdesign/core/Spinner';
|
||||
import nelogo from '../../assets/images/doormile-mark.png';
|
||||
|
||||
// Styled Loader Wrapper
|
||||
const LoaderWrapper = styled('div')(() => ({
|
||||
// ==============================|| Custom Circular Loader with Logo ||============================== //
|
||||
const CircularLoader = () => (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
@@ -12,36 +13,19 @@ const LoaderWrapper = styled('div')(() => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'rgba(255, 255, 255, 0.7)', // Light overlay effect
|
||||
padding: '20px',
|
||||
borderRadius: '10px' // Soft rounded border
|
||||
}));
|
||||
background: 'rgba(255, 255, 255, 0.7)',
|
||||
padding: 20,
|
||||
borderRadius: 10
|
||||
}}
|
||||
>
|
||||
<div style={{ transform: 'scale(2.2)' }}>
|
||||
<Spinner size="xl" />
|
||||
</div>
|
||||
|
||||
// Styled Logo Wrapper
|
||||
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>
|
||||
<div style={{ position: 'absolute', width: 40, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<img src={nelogo} alt="Logo" style={{ width: '100%', height: '100%' }} />
|
||||
</LogoWrapper>
|
||||
</LoaderWrapper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CircularLoader;
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { Dialog, DialogTitle, DialogContent, Typography, Stack, Button, DialogActions, Paper } from '@mui/material';
|
||||
|
||||
import { DateRangePicker } from 'mui-daterange-picker';
|
||||
import { useState } from 'react';
|
||||
import { Dialog } from '@astryxdesign/core/Dialog';
|
||||
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 { 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 }) {
|
||||
const [range, setRange] = useState(null);
|
||||
|
||||
const definedRanges = [
|
||||
{ label: 'Today', startDate: new Date(), endDate: new Date() },
|
||||
{ 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)) }
|
||||
];
|
||||
|
||||
const handleSelect = (range) => {
|
||||
if (!range?.startDate || !range?.endDate) return;
|
||||
onApply({
|
||||
startDate: dayjs(range.startDate).format('YYYY-MM-DD'),
|
||||
endDate: dayjs(range.endDate).format('YYYY-MM-DD'),
|
||||
label: range.label || ''
|
||||
});
|
||||
const apply = (startDate, endDate, label = '') => {
|
||||
onApply({ startDate: iso(startDate), endDate: iso(endDate), label });
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOpenChange = (next) => !next && onClose();
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={() => onclose()} fullWidth maxWidth="sm">
|
||||
<DialogTitle disableTypography>
|
||||
<Stack display="flex" flexDirection="row" justifyContent="space-between" alignItems="center" sx={{ width: '100%' }}>
|
||||
<Typography variant="h4">Select Date Range</Typography>
|
||||
</Stack>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
sx={{
|
||||
'& .MuiPaper-root': {
|
||||
boxShadow: 'none !important'
|
||||
<Dialog isOpen={open} onOpenChange={handleOpenChange} width={560}>
|
||||
<Layout
|
||||
header={<DialogHeader title="Select Date Range" onOpenChange={handleOpenChange} />}
|
||||
content={
|
||||
<VStack gap={3} padding={4}>
|
||||
<HStack gap={1} wrap="wrap">
|
||||
{definedRanges.map((r) => (
|
||||
<Button key={r.label} label={r.label} variant="secondary" size="sm" onClick={() => apply(r.startDate, r.endDate, r.label)} />
|
||||
))}
|
||||
</HStack>
|
||||
|
||||
<Calendar mode="range" numberOfMonths={2} value={range || undefined} onChange={(v) => setRange(v)} />
|
||||
</VStack>
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DateRangePicker open={open} toggle={onClose} definedRanges={definedRanges} onChange={handleSelect} closeOnClickOutside={false} />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={onClose} sx={{ cursor: 'pointer' }}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
footer={
|
||||
<LayoutFooter>
|
||||
<HStack justify="end" gap={2}>
|
||||
<Button label="Close" variant="secondary" onClick={onClose} />
|
||||
<Button
|
||||
label="Apply"
|
||||
variant="primary"
|
||||
isDisabled={!range?.start || !range?.end}
|
||||
onClick={() => range?.start && range?.end && apply(range.start, range.end, 'Custom')}
|
||||
/>
|
||||
</HStack>
|
||||
</LayoutFooter>
|
||||
}
|
||||
/>
|
||||
</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 { Box, Paper, Stack, Typography } from '@mui/material';
|
||||
|
||||
// ============================================================================
|
||||
// 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>
|
||||
// on mobile. `scroll` makes it an internal scroll region (matches the table's
|
||||
// maxHeight behaviour); omit it to let the page scroll naturally.
|
||||
export const MobileCardList = ({ children, scroll = false, onScroll, sx, ...rest }) => (
|
||||
<Stack
|
||||
spacing={1.25}
|
||||
export const MobileCardList = ({ children, scroll = false, onScroll, style, ...rest }) => (
|
||||
<div
|
||||
onScroll={onScroll}
|
||||
sx={{
|
||||
p: 1.5,
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 10,
|
||||
padding: 12,
|
||||
...(scroll && { maxHeight: 'calc(100vh - 220px)', overflowY: 'auto', overflowX: 'hidden' }),
|
||||
...sx
|
||||
...style
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
|
||||
MobileCardList.propTypes = {
|
||||
children: PropTypes.node,
|
||||
scroll: PropTypes.bool,
|
||||
onScroll: PropTypes.func,
|
||||
sx: PropTypes.object
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
// Card shell — coloured accent rail on the left, a header slot (status badge /
|
||||
// title / action buttons), then any field grid / collapse content as children.
|
||||
export const MobileCard = ({ accent = '#662582', header, footer, selected = false, onClick, children, sx }) => (
|
||||
<Paper
|
||||
elevation={0}
|
||||
export const MobileCard = ({ accent = '#C01227', header, footer, selected = false, onClick, children, style }) => (
|
||||
<div
|
||||
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',
|
||||
overflow: 'hidden',
|
||||
// Cards live inside a flex-column list; without this, a scroll-capped
|
||||
// list (maxHeight) would SHRINK each card to a sliver (flex-shrink:1)
|
||||
// and clip its content instead of scrolling. Keep natural height.
|
||||
flexShrink: 0,
|
||||
borderRadius: 2.5,
|
||||
border: '1px solid',
|
||||
borderColor: selected ? accent : BORDER,
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${selected ? accent : BORDER}`,
|
||||
background: selected ? `${accent}0a` : '#fff',
|
||||
boxShadow: '0 4px 14px rgba(15,23,42,0.05)',
|
||||
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 }} />
|
||||
<Box sx={{ p: 1.5, pl: 2 }}>
|
||||
<div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: 3, backgroundColor: accent }} />
|
||||
<div style={{ padding: '12px 12px 12px 16px' }}>
|
||||
{header}
|
||||
{children}
|
||||
{footer}
|
||||
</Box>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
MobileCard.propTypes = {
|
||||
@@ -78,37 +80,37 @@ MobileCard.propTypes = {
|
||||
selected: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
children: PropTypes.node,
|
||||
sx: PropTypes.object
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
// Grid wrapper for MobileField cells. Two columns by default; pass `columns`
|
||||
// to change. Keeps every card's body alignment identical.
|
||||
export const MobileFieldGrid = ({ children, columns = 2, sx }) => (
|
||||
<Box
|
||||
sx={{
|
||||
export const MobileFieldGrid = ({ children, columns = 2, style }) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
||||
gap: 1,
|
||||
mt: 1.25,
|
||||
...sx
|
||||
gap: 8,
|
||||
marginTop: 10,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
|
||||
MobileFieldGrid.propTypes = {
|
||||
children: PropTypes.node,
|
||||
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 string/number or any node (chip, stack, etc.).
|
||||
export const MobileField = ({ label, value, children, full = false, align = 'left' }) => (
|
||||
<Box sx={{ gridColumn: full ? '1 / -1' : 'auto', minWidth: 0, textAlign: align }}>
|
||||
<Typography
|
||||
sx={{
|
||||
<div style={{ gridColumn: full ? '1 / -1' : 'auto', minWidth: 0, textAlign: align }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 9.5,
|
||||
fontWeight: 800,
|
||||
letterSpacing: 0.5,
|
||||
@@ -118,17 +120,17 @@ export const MobileField = ({ label, value, children, full = false, align = 'lef
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
<Box sx={{ mt: 0.25, minWidth: 0 }}>
|
||||
</div>
|
||||
<div style={{ marginTop: 2, minWidth: 0 }}>
|
||||
{children !== undefined ? (
|
||||
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 ?? '—'}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
MobileField.propTypes = {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// SearchBar.jsx
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FormControl, OutlinedInput, InputAdornment, IconButton, useTheme } from '@mui/material';
|
||||
import SearchOutlined from '@mui/icons-material/SearchOutlined';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import { TextInput } from '@astryxdesign/core/TextInput';
|
||||
import { MdSearch } from 'react-icons/md';
|
||||
|
||||
const SearchBar = ({ value, onChange, sx, placeholder = 'Search (Ctrl + K)' }) => {
|
||||
const theme = useTheme();
|
||||
const SearchBar = ({ value, onChange, style, placeholder = 'Search (Ctrl + K)' }) => {
|
||||
const inputRef = useRef(null);
|
||||
|
||||
/* ============================================= || handleKeyPress (CTRL + K) and ESC ||============================================= */
|
||||
@@ -28,30 +26,18 @@ const SearchBar = ({ value, onChange, sx, placeholder = 'Search (Ctrl + K)' }) =
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{}}>
|
||||
<OutlinedInput
|
||||
inputRef={inputRef}
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
label={placeholder}
|
||||
isLabelHidden
|
||||
placeholder={placeholder}
|
||||
sx={{
|
||||
borderRadius: 0,
|
||||
...sx
|
||||
}}
|
||||
startIcon={<MdSearch />}
|
||||
hasClear
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
autoComplete="off"
|
||||
size="large"
|
||||
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>
|
||||
}
|
||||
onChange={(v) => onChange({ target: { value: v } })}
|
||||
size="lg"
|
||||
style={style}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import WifiOffIcon from '@mui/icons-material/WifiOff';
|
||||
import WifiIcon from '@mui/icons-material/Wifi';
|
||||
import { MdWifiOff, MdWifi } from 'react-icons/md';
|
||||
|
||||
const NOTIFICATION_STYLE = {
|
||||
position: 'fixed',
|
||||
@@ -21,7 +19,6 @@ const NOTIFICATION_STYLE = {
|
||||
};
|
||||
|
||||
const InternetStatus = () => {
|
||||
const theme = useTheme();
|
||||
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
||||
const [showBackOnline, setShowBackOnline] = useState(false);
|
||||
const wasOffline = useRef(!navigator.onLine);
|
||||
@@ -64,13 +61,13 @@ const InternetStatus = () => {
|
||||
<div
|
||||
style={{
|
||||
...NOTIFICATION_STYLE,
|
||||
backgroundColor: theme.palette.error.main,
|
||||
backgroundColor: 'var(--color-error, #d32f2f)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px'
|
||||
}}
|
||||
>
|
||||
<WifiOffIcon style={{ color: '#fff' }} />
|
||||
<MdWifiOff style={{ color: '#fff' }} />
|
||||
No Internet Connection
|
||||
</div>
|
||||
)}
|
||||
@@ -79,13 +76,13 @@ const InternetStatus = () => {
|
||||
<div
|
||||
style={{
|
||||
...NOTIFICATION_STYLE,
|
||||
backgroundColor: theme.palette.success.main,
|
||||
backgroundColor: 'var(--color-success, #2e7d32)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px'
|
||||
}}
|
||||
>
|
||||
<WifiIcon style={{ color: '#fff' }} />
|
||||
<MdWifi style={{ color: '#fff' }} />
|
||||
Back Online
|
||||
</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';
|
||||
|
||||
//material-ui
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
// third-party
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
|
||||
@@ -12,24 +9,16 @@ import { useSelector } from 'store';
|
||||
// assets
|
||||
import { CheckCircleOutlined, CloseCircleOutlined, InfoCircleOutlined, WarningOutlined } from '@ant-design/icons';
|
||||
|
||||
// custom styles
|
||||
const StyledSnackbarProvider = styled(SnackbarProvider)(({ theme }) => ({
|
||||
'&.notistack-MuiContent-default': {
|
||||
backgroundColor: theme.palette.primary.main
|
||||
},
|
||||
'&.notistack-MuiContent-error': {
|
||||
backgroundColor: theme.palette.error.main
|
||||
},
|
||||
'&.notistack-MuiContent-success': {
|
||||
backgroundColor: theme.palette.success.main
|
||||
},
|
||||
'&.notistack-MuiContent-info': {
|
||||
backgroundColor: theme.palette.info.main
|
||||
},
|
||||
'&.notistack-MuiContent-warning': {
|
||||
backgroundColor: theme.palette.warning.main
|
||||
}
|
||||
}));
|
||||
// Semantic notification colors — static (this app runs light-mode only), matching the
|
||||
// brand palette used across the Astryx-converted pages (BRAND red for the default/
|
||||
// primary notistack variant, standard semantic tones for error/success/info/warning).
|
||||
const notistackColors = `
|
||||
.notistack-MuiContent-default { background-color: #C01227 !important; }
|
||||
.notistack-MuiContent-error { background-color: #ef4444 !important; }
|
||||
.notistack-MuiContent-success { background-color: #10b981 !important; }
|
||||
.notistack-MuiContent-info { background-color: #3b82f6 !important; }
|
||||
.notistack-MuiContent-warning { background-color: #f59e0b !important; }
|
||||
`;
|
||||
|
||||
// ===========================|| SNACKBAR - NOTISTACK ||=========================== //
|
||||
|
||||
@@ -38,7 +27,9 @@ const Notistack = ({ children }) => {
|
||||
const iconSX = { marginRight: 8, fontSize: '1.15rem' };
|
||||
|
||||
return (
|
||||
<StyledSnackbarProvider
|
||||
<>
|
||||
<style>{notistackColors}</style>
|
||||
<SnackbarProvider
|
||||
maxSnack={snackbar.maxStack}
|
||||
dense={snackbar.dense}
|
||||
iconVariant={
|
||||
@@ -54,7 +45,8 @@ const Notistack = ({ children }) => {
|
||||
hideIconVariant={snackbar.iconVariant === 'hide' ? true : false}
|
||||
>
|
||||
{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 React, { forwardRef, useEffect, useRef, useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { styled, useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
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';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import { Tooltip } from '@astryxdesign/core/Tooltip';
|
||||
import { Spinner } from '@astryxdesign/core/Spinner';
|
||||
|
||||
// third-party
|
||||
import { CSVLink } from 'react-csv';
|
||||
import { getEmptyImage } from 'react-dnd-html5-backend';
|
||||
import { useDrop, useDrag, useDragLayer } from 'react-dnd';
|
||||
|
||||
// assets
|
||||
import { CaretUpOutlined, CaretDownOutlined, DragOutlined, CheckOutlined, DownloadOutlined } from '@ant-design/icons';
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| HEADER HEADER ||============================== //
|
||||
|
||||
export const HeaderSort = ({ column, sort }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<Box sx={{ width: 'max-content' }}>{column.render('Header')}</Box>
|
||||
{!column.disableSortBy && (
|
||||
<Stack sx={{ color: 'secondary.light' }} {...(sort && { ...column.getHeaderProps(column.getSortByToggleProps()) })}>
|
||||
<CaretUpOutlined
|
||||
style={{
|
||||
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
|
||||
};
|
||||
// ============================================================================
|
||||
// This file previously held a large grab-bag of react-table + MUI + react-dnd
|
||||
// helper components (HeaderSort, TablePagination, IndeterminateCheckbox,
|
||||
// TableRowSelection, DraggableHeader/DragPreview/DraggableRow, HidingSelect,
|
||||
// SortingSelect, EmptyTable). None of them had any remaining consumers anywhere
|
||||
// in the app (verified via grep across src/) — they were leftover MUI/react-table
|
||||
// scaffolding from the original admin template, never wired into any of the
|
||||
// plain-HTML/Astryx tables actually used by this app's pages. Removed as part of
|
||||
// the MUI removal pass rather than converting dead code. `CSVExport` is the one
|
||||
// export with a real consumer (reports/ordersDetails.js) and is kept, rebuilt on
|
||||
// Astryx primitives.
|
||||
// ============================================================================
|
||||
|
||||
// ==============================|| CSV EXPORT ||============================== //
|
||||
|
||||
export const CSVExport = ({ data, filename, headers, label, style, btnLoading, onClick }) => {
|
||||
return (
|
||||
<CSVLink data={data} filename={filename} headers={headers}>
|
||||
<Tooltip title="CSV Export">
|
||||
<CSVLink data={data} filename={filename} headers={headers} style={{ textDecoration: 'none', lineHeight: 0 }}>
|
||||
<Tooltip content="CSV Export">
|
||||
<Button
|
||||
startIcon={!btnLoading && <DownloadOutlined />}
|
||||
variant={btnLoading ? 'outlined' : 'contained'}
|
||||
sx={{ ...style }}
|
||||
disabled={btnLoading}
|
||||
onClick={(e) => {
|
||||
onClick?.(e);
|
||||
}}
|
||||
label={typeof label === 'string' ? label : 'Download'}
|
||||
icon={!btnLoading ? <DownloadOutlined /> : undefined}
|
||||
variant={btnLoading ? 'secondary' : 'primary'}
|
||||
isDisabled={btnLoading}
|
||||
onClick={(e) => onClick?.(e)}
|
||||
style={style}
|
||||
>
|
||||
{btnLoading ? <CircularProgress size={20} thickness={5} /> : label || 'Download'}
|
||||
{btnLoading ? <Spinner size="sm" shade="inherit" /> : label || 'Download'}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</CSVLink>
|
||||
@@ -526,70 +55,3 @@ CSVExport.propTypes = {
|
||||
btnLoading: PropTypes.bool,
|
||||
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 { BrowserRouter } from 'react-router-dom';
|
||||
import { BrowserRouter, Link } from 'react-router-dom';
|
||||
|
||||
// third-party
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
@@ -7,6 +7,13 @@ import { Provider as ReduxProvider } from 'react-redux';
|
||||
// scroll bar
|
||||
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
|
||||
import 'assets/third-party/apex-chart.css';
|
||||
import 'assets/third-party/react-table.css';
|
||||
@@ -32,7 +39,12 @@ root.render(
|
||||
{/* <ConfigProvider> */}
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<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 />
|
||||
</LinkProvider>
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
{/* </ConfigProvider> */}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
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
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// project import
|
||||
import { ThemeDirection, ThemeMode } from 'config';
|
||||
import useConfig from 'hooks/useConfig';
|
||||
|
||||
// assets
|
||||
import { SendOutlined } from '@ant-design/icons';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import imgfooterlogo from 'assets/images/landing/codedthemes-logo.svg';
|
||||
import imgfootersoc1 from 'assets/images/landing/img-soc1.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);
|
||||
|
||||
// link - custom style
|
||||
const FooterLink = styled(Link)(({ theme }) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.main
|
||||
},
|
||||
'&:active': {
|
||||
color: theme.palette.primary.main
|
||||
}
|
||||
}));
|
||||
// Note: like Header.js, this footer only renders for CommonLayout's 'landing' variant,
|
||||
// which this app's actual routing never selects — it's leftover template-vendor marketing
|
||||
// chrome (CodedThemes/Mantis branding), kept dead-in-practice but converted off MUI since
|
||||
// it's still part of the FooterBlock lazy chunk. Static grey/white tones replace the old
|
||||
// theme.palette reads (this app runs light-mode only).
|
||||
|
||||
const GREY_A700 = '#1f1f1f';
|
||||
const GREY_800 = '#262626';
|
||||
const WHITE = '#fff';
|
||||
|
||||
const footerLinkStyle = { color: 'rgba(255,255,255,0.6)', textDecoration: 'none', fontSize: 14 };
|
||||
|
||||
// ==============================|| LANDING - FOOTER PAGE ||============================== //
|
||||
|
||||
const FooterBlock = ({ isFull }) => {
|
||||
const theme = useTheme();
|
||||
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 = [
|
||||
{ title: 'CodeIgniter', link: 'https://links.codedthemes.com/dEGKs' },
|
||||
{
|
||||
title: 'React MUI',
|
||||
link: 'https://links.codedthemes.com/Aprwb'
|
||||
},
|
||||
{
|
||||
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'
|
||||
}
|
||||
{ title: 'React MUI', link: 'https://links.codedthemes.com/Aprwb' },
|
||||
{ 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 (
|
||||
<>
|
||||
{isFull && (
|
||||
<Box
|
||||
sx={{
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
bgcolor: theme.palette.grey.A700,
|
||||
backgroundColor: GREY_A700,
|
||||
zIndex: 1,
|
||||
mt: { xs: 0, md: 13.75 },
|
||||
pt: { xs: 8, sm: 7.5, md: 18.75 },
|
||||
pb: { xs: 2.5, md: 10 },
|
||||
'&:after': {
|
||||
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%)`
|
||||
}
|
||||
marginTop: 110,
|
||||
paddingTop: 150,
|
||||
paddingBottom: 80,
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<CardMedia
|
||||
component="img"
|
||||
image={dashImage(`./img-footer-${presetColor}.png`)}
|
||||
sx={{
|
||||
display: { xs: 'none', md: 'block' },
|
||||
width: '55%',
|
||||
maxWidth: 700,
|
||||
position: 'absolute',
|
||||
top: '-28%',
|
||||
right: 0
|
||||
}}
|
||||
<img
|
||||
src={dashImage(`./img-footer-${presetColor}.png`)}
|
||||
alt=""
|
||||
style={{ display: 'block', width: '55%', maxWidth: 700, position: 'absolute', top: '-28%', right: 0 }}
|
||||
/>
|
||||
<Container>
|
||||
<Grid container alignItems="center" justifyContent="space-between" spacing={2}>
|
||||
<Grid item xs={12} md={6} sx={{ position: 'relative', zIndex: 1 }}>
|
||||
<Grid container spacing={2} sx={{ [theme.breakpoints.down('md')]: { pr: 0, textAlign: 'center' } }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" sx={{ color: theme.palette.common.white }}>
|
||||
Roadmap
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div style={{ maxWidth: 1200, marginInline: 'auto', paddingInline: 16 }}>
|
||||
<div style={{ position: 'relative', zIndex: 1, maxWidth: 600 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<span style={{ color: WHITE, fontWeight: 500 }}>Roadmap</span>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, translateY: 550 }}
|
||||
animate={{ opacity: 1, translateY: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 150,
|
||||
damping: 30
|
||||
}}
|
||||
transition={{ type: 'spring', stiffness: 150, damping: 30 }}
|
||||
>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
color: theme.palette.common.white,
|
||||
fontWeight: 700
|
||||
}}
|
||||
>
|
||||
Upcoming Release
|
||||
</Typography>
|
||||
<span style={{ color: WHITE, fontWeight: 700, fontSize: '2rem', display: 'block' }}>Upcoming Release</span>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<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' }}>
|
||||
<span style={{ color: WHITE }}>What is next? Checkout the Upcoming release of Mantis React.</span>
|
||||
<div style={{ marginBlock: 16 }}>
|
||||
<AnimateButton>
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
endIcon={<SendOutlined />}
|
||||
component={Link}
|
||||
label="Roadmap"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
endContent={<SendOutlined />}
|
||||
href="https://links.codedthemes.com/RXnKQ"
|
||||
target="_blank"
|
||||
>
|
||||
Roadmap
|
||||
</Button>
|
||||
</AnimateButton>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Box sx={{ pt: isFull ? 0 : 10, pb: 10, bgcolor: theme.palette.grey.A700 }}>
|
||||
<Container>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={4}>
|
||||
<div style={{ paddingTop: isFull ? 0 : 80, paddingBottom: 80, backgroundColor: GREY_A700 }}>
|
||||
<div style={{ maxWidth: 1200, marginInline: 'auto', paddingInline: 16 }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 24 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, translateY: 550 }}
|
||||
animate={{ opacity: 1, translateY: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 150,
|
||||
damping: 30
|
||||
}}
|
||||
transition={{ type: 'spring', stiffness: 150, damping: 30 }}
|
||||
>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<CardMedia component="img" image={imgfooterlogo} sx={{ width: 'auto' }} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 400, color: theme.palette.common.white }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<img src={imgfooterlogo} alt="Logo" style={{ width: 'auto' }} />
|
||||
<span style={{ color: WHITE }}>
|
||||
Since 2017, More than 50K+ Developers trust the CodedThemes Digital Product. Mantis React is Manage under their
|
||||
Experienced Team Players.
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={8}>
|
||||
<Grid container spacing={{ xs: 5, md: 2 }}>
|
||||
<Grid item xs={6} sm={3}>
|
||||
<Stack spacing={{ xs: 3, md: 5 }}>
|
||||
<Typography variant="h5" color={textColor} sx={{ fontWeight: 500 }}>
|
||||
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]
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))',
|
||||
gap: 24
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={8}>
|
||||
<Typography variant="subtitle2" color="secondary">
|
||||
© Made with love by Team CodedThemes
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Grid container spacing={2} alignItems="center" sx={{ justifyContent: 'flex-end' }}>
|
||||
<Grid item>
|
||||
<Link underline="none" sx={linkSX}>
|
||||
<CardMedia component="img" image={imgfootersoc1} />
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Link underline="none" sx={linkSX}>
|
||||
<CardMedia component="img" image={imgfootersoc2} />
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Link underline="none" sx={linkSX}>
|
||||
<CardMedia component="img" image={imgfootersoc3} />
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
<FooterColumn title="Help" links={helpLinks} />
|
||||
<FooterColumn title="Store Help" links={storeHelpLinks} />
|
||||
<FooterColumn title="Mantis Eco-System" links={frameworks} />
|
||||
<FooterColumn title="More Products" links={moreProducts} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ borderTop: '1px solid #434343' }} />
|
||||
|
||||
<div style={{ paddingBlock: 12, backgroundColor: GREY_800 }}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 1200,
|
||||
marginInline: 'auto',
|
||||
paddingInline: 16,
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'rgba(255,255,255,0.6)', fontSize: 13 }}>© Made with love by Team CodedThemes</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||||
{[imgfootersoc1, imgfootersoc2, imgfootersoc3].map((src, i) => (
|
||||
<a key={i} href="#" style={{ opacity: 0.6, display: 'inline-flex' }}>
|
||||
<img src={src} alt="" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,31 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
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';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import { Badge } from '@astryxdesign/core/Badge';
|
||||
|
||||
// project import
|
||||
import { APP_DEFAULT_PATH, ThemeMode } from 'config';
|
||||
import { APP_DEFAULT_PATH } from 'config';
|
||||
import IconButton from 'components/@extended/IconButton';
|
||||
import AnimateButton from 'components/@extended/AnimateButton';
|
||||
import Logo from 'components/logo';
|
||||
@@ -34,212 +15,170 @@ import Logo from 'components/logo';
|
||||
import { MenuOutlined, LineOutlined } from '@ant-design/icons';
|
||||
|
||||
// ==============================|| 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
|
||||
function ElevationScroll({ layout, children, window }) {
|
||||
const theme = useTheme();
|
||||
// const theme = useTheme();
|
||||
// Small local hook replacing MUI's useScrollTrigger — flips true once the page has
|
||||
// scrolled past `threshold` px, used to swap the header's background in on scroll.
|
||||
const useScrolled = (threshold = 10) => {
|
||||
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({
|
||||
disableHysteresis: true,
|
||||
threshold: 10,
|
||||
target: window ? window() : undefined
|
||||
});
|
||||
const useIsMobile = (breakpoint = 900) => {
|
||||
const [isMobile, setIsMobile] = useState(() => (typeof window !== 'undefined' ? window.innerWidth < breakpoint : false));
|
||||
useEffect(() => {
|
||||
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';
|
||||
|
||||
return React.cloneElement(children, {
|
||||
style: {
|
||||
backgroundColor: trigger ? backColorScroll : backColor
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
const drawerLinks = [
|
||||
{ label: 'Dashboard', href: '/login' },
|
||||
{ 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' }
|
||||
];
|
||||
|
||||
return (
|
||||
<ElevationScroll layout={layout} {...others}>
|
||||
<AppBar sx={{ bgcolor: 'transparent', color: theme.palette.text.primary, boxShadow: 'none' }}>
|
||||
<Container disableGutters={matchDownMd}>
|
||||
<Toolbar sx={{ px: { xs: 1.5, md: 0, lg: 0 }, py: 2 }}>
|
||||
<Stack direction="row" sx={{ flexGrow: 1, display: { xs: 'none', md: 'block' } }} alignItems="center">
|
||||
<Typography component="div" sx={{ textAlign: 'left', display: 'inline-block' }}>
|
||||
<Logo reverse to="/" />
|
||||
</Typography>
|
||||
<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' }
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: scrolled ? backColorScroll : backColor,
|
||||
color: '#fff',
|
||||
transition: 'background-color 0.2s ease',
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 1100
|
||||
}}
|
||||
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
|
||||
</Link>
|
||||
<Link
|
||||
className="header-link"
|
||||
color={handleDrawerOpen ? 'primary' : 'white'}
|
||||
component={RouterLink}
|
||||
to="/components-overview/buttons"
|
||||
underline="none"
|
||||
>
|
||||
</RouterLink>
|
||||
<RouterLink to="/components-overview/buttons" style={{ ...headerLinkStyle, color: handleDrawerOpen ? '#C01227' : '#fff' }}>
|
||||
Components
|
||||
</Link>
|
||||
<Link className="header-link" color="white" href="https://links.codedthemes.com/BQFrl" target="_blank" underline="none">
|
||||
</RouterLink>
|
||||
<a href="https://links.codedthemes.com/BQFrl" target="_blank" rel="noopener noreferrer" style={headerLinkStyle}>
|
||||
Documentation
|
||||
</Link>
|
||||
<Box sx={{ display: 'inline-block' }}>
|
||||
</a>
|
||||
<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
|
||||
</Button>
|
||||
</AnimateButton>
|
||||
</Box>
|
||||
</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>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{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
|
||||
color="secondary"
|
||||
{...(layout === 'component' ? { onClick: handleDrawerOpen } : { onClick: drawerToggler(true) })}
|
||||
sx={{
|
||||
'&:hover': { bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.lighter' : 'secondary.dark' }
|
||||
}}
|
||||
label="Open menu"
|
||||
variant="secondary"
|
||||
onClick={layout === 'component' ? handleDrawerOpen : () => setDrawerOpen(true)}
|
||||
>
|
||||
<MenuOutlined style={{ color: theme.palette.mode === ThemeMode.DARK ? 'inherit' : theme.palette.grey[100] }} />
|
||||
<MenuOutlined style={{ color: '#f5f5f5' }} />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
<Drawer
|
||||
anchor="top"
|
||||
open={drawerToggle}
|
||||
onClose={drawerToggler(false)}
|
||||
sx={{ '& .MuiDrawer-paper': { backgroundImage: 'none' } }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 'auto',
|
||||
'& .MuiListItemIcon-root': {
|
||||
fontSize: '1rem',
|
||||
minWidth: 28
|
||||
}
|
||||
}}
|
||||
</div>
|
||||
|
||||
{drawerOpen && (
|
||||
<>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Close menu"
|
||||
onClick={() => setDrawerOpen(false)}
|
||||
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setDrawerOpen(false)}
|
||||
style={{ position: 'fixed', inset: 0, backgroundColor: 'rgba(0,0,0,0.4)', zIndex: 1199, border: 'none' }}
|
||||
/>
|
||||
<div
|
||||
role="presentation"
|
||||
onClick={drawerToggler(false)}
|
||||
onKeyDown={drawerToggler(false)}
|
||||
onClick={() => setDrawerOpen(false)}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 1200,
|
||||
backgroundColor: '#1e1e1e',
|
||||
padding: '8px 0'
|
||||
}}
|
||||
>
|
||||
<List>
|
||||
<Link style={{ textDecoration: 'none' }} href="/login" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Dashboard" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="/components-overview/buttons" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="All Components" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/sTUAK" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Free Version" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
<Link style={{ textDecoration: 'none' }} href="https://links.codedthemes.com/BQFrl" target="_blank">
|
||||
<ListItemButton component="span">
|
||||
<ListItemIcon>
|
||||
<LineOutlined />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Documentation" primaryTypographyProps={{ variant: 'h6', color: 'text.primary' }} />
|
||||
</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>
|
||||
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
|
||||
{drawerLinks.map((item) => (
|
||||
<li key={item.label}>
|
||||
<a
|
||||
href={item.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
padding: '10px 16px',
|
||||
textDecoration: 'none',
|
||||
color: '#fff'
|
||||
}}
|
||||
>
|
||||
<LineOutlined style={{ fontSize: '1rem', minWidth: 28 }} />
|
||||
<span style={{ fontWeight: 600, flex: 1 }}>{item.label}</span>
|
||||
{item.badge && <Badge label={item.badge} />}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,37 +2,21 @@ import PropTypes from 'prop-types';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// material-ui
|
||||
import { Container, Toolbar } from '@mui/material';
|
||||
import { ProgressBar } from '@astryxdesign/core/ProgressBar';
|
||||
|
||||
// project import
|
||||
import { dispatch, useSelector } from 'store';
|
||||
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 FooterBlock = lazy(() => import('./FooterBlock'));
|
||||
|
||||
// ==============================|| Loader ||============================== //
|
||||
|
||||
const LoaderWrapper = styled('div')(({ theme }) => ({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 2001,
|
||||
width: '100%',
|
||||
'& > * + *': {
|
||||
marginTop: theme.spacing(2)
|
||||
}
|
||||
}));
|
||||
|
||||
const Loader = () => (
|
||||
<LoaderWrapper>
|
||||
<LinearProgress color="primary" />
|
||||
</LoaderWrapper>
|
||||
<div style={{ position: 'fixed', top: 0, left: 0, zIndex: 2001, width: '100%' }}>
|
||||
<ProgressBar label="Loading" isLabelHidden isIndeterminate />
|
||||
</div>
|
||||
);
|
||||
|
||||
// ==============================|| MINIMAL LAYOUT ||============================== //
|
||||
@@ -56,10 +40,10 @@ const CommonLayout = ({ layout = 'blank' }) => {
|
||||
)}
|
||||
{layout === 'component' && (
|
||||
<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" />
|
||||
<Toolbar sx={{ my: 2 }} />
|
||||
</Container>
|
||||
<div style={{ minHeight: 64, marginBlock: 16 }} />
|
||||
</div>
|
||||
</Suspense>
|
||||
)}
|
||||
{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';
|
||||
|
||||
// material-ui
|
||||
import { Link, Stack, Typography } from '@mui/material';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { Link } from '@astryxdesign/core/Link';
|
||||
|
||||
const Footer = () => (
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ p: '24px 16px 0px', mt: 'auto' }}>
|
||||
<Typography variant="caption">© All rights reserved</Typography>
|
||||
<Stack spacing={1.5} direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Link component={RouterLink} to="https://nearle.in/aboutus" target="_blank" variant="caption" color="textPrimary">
|
||||
<HStack justify="space-between" vAlign="center" style={{ padding: '24px 16px 0px', marginTop: 'auto' }}>
|
||||
<Text type="supporting">© All rights reserved</Text>
|
||||
<HStack gap={1.5} justify="space-between" vAlign="center">
|
||||
<Link href="https://nearle.in/aboutus" target="_blank" isExternalLink type="supporting">
|
||||
About us
|
||||
</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
|
||||
</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
|
||||
</Link>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</HStack>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
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,237 +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.removeItem('sessionStartTime');
|
||||
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';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useMediaQuery, Box, Container, Toolbar } from '@mui/material';
|
||||
// Astryx design system — see themes/astryx.js. This replaces the old MUI
|
||||
// Drawer/Header/HorizontalBar shell; horizontal menu orientation
|
||||
// (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 Drawer from './Drawer';
|
||||
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';
|
||||
import AppTopNav from './AppTopNav';
|
||||
import AppSideNav from './AppSideNav';
|
||||
|
||||
// ==============================|| MAIN LAYOUT ||============================== //
|
||||
|
||||
const MainLayout = () => {
|
||||
const theme = useTheme();
|
||||
// const matchDownXL = useMediaQuery(theme.breakpoints.down('xl'));
|
||||
const matchDownXL = useMediaQuery(`(max-width: 1440px)`);
|
||||
|
||||
const downLG = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const { container, miniDrawer, menuOrientation } = useConfig();
|
||||
|
||||
const isHorizontal = menuOrientation === MenuOrientation.HORIZONTAL && !downLG;
|
||||
|
||||
// sidebar should default to closed regardless of screen size
|
||||
useEffect(() => {
|
||||
if (!miniDrawer) {
|
||||
dispatch(openDrawer(false));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [matchDownXL]);
|
||||
// Lifted here (rather than left as SideNav's own uncontrolled state) so the
|
||||
// TopNav logo can track the sidebar's collapse state. Defaults to collapsed
|
||||
// on every load/reload (not persisted).
|
||||
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(true);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Header />
|
||||
{!isHorizontal ? <Drawer /> : <HorizontalBar />}
|
||||
<Box component="main" sx={{ width: 'calc(100% - 260px)', flexGrow: 1, p: { xs: 2, sm: 3 } }}>
|
||||
<Toolbar sx={{ mt: isHorizontal ? 8 : 'inherit' }} />
|
||||
<Container
|
||||
// maxWidth={container ? 'xl' : false}
|
||||
maxWidth
|
||||
sx={{
|
||||
...(container && { px: { xs: 0, sm: 2 } }),
|
||||
position: 'relative',
|
||||
minHeight: 'calc(100vh - 110px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}
|
||||
<Theme theme={dailygrubsTheme} mode="light">
|
||||
{/* height="auto" (not "fill"): "fill" scrolls the content area in its
|
||||
own container, and the app's useInfiniteQuery pattern observes a
|
||||
sentinel against the window's viewport (IntersectionObserver
|
||||
root=null), which only fires correctly with normal document-level
|
||||
scroll. */}
|
||||
<AppShell
|
||||
variant="section"
|
||||
height="auto"
|
||||
contentPadding={0}
|
||||
topNav={<AppTopNav />}
|
||||
sideNav={<AppSideNav isCollapsed={isSidebarCollapsed} onCollapsedChange={setIsSidebarCollapsed} />}
|
||||
mobileNav={{ breakpoint: 'lg' }}
|
||||
>
|
||||
{/* <Breadcrumbs navigation={navigation} title titleBottom card={false} divider={false} /> */}
|
||||
<div className="main-content-area" style={{ minHeight: '100%', boxSizing: 'border-box' }}>
|
||||
<Outlet />
|
||||
{/* <Footer /> */}
|
||||
</Container>
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
<style>{`
|
||||
.main-content-area {
|
||||
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 { TbListDetails } from 'react-icons/tb';
|
||||
import { LiaFileInvoiceSolid } from 'react-icons/lia';
|
||||
import DirectionsBikeOutlinedIcon from '@mui/icons-material/DirectionsBikeOutlined';
|
||||
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
|
||||
import MopedOutlinedIcon from '@mui/icons-material/MopedOutlined';
|
||||
import { MdDirectionsBike as DirectionsBikeOutlinedIcon, MdRoute as RouteOutlinedIcon, MdMoped as MopedOutlinedIcon } from 'react-icons/md';
|
||||
|
||||
// assets
|
||||
import {
|
||||
@@ -25,7 +23,7 @@ import {
|
||||
SettingOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Path } from 'leaflet';
|
||||
import { LocationOnOutlined } from '@mui/icons-material';
|
||||
import { MdLocationOn as LocationOnOutlined } from 'react-icons/md';
|
||||
|
||||
// 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 { Typography } from '@mui/material';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
|
||||
// project import
|
||||
import MainCard from 'components/MainCard';
|
||||
@@ -8,12 +7,12 @@ import MainCard from 'components/MainCard';
|
||||
|
||||
const SamplePage = () => (
|
||||
<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
|
||||
minim venice, quin nostrum exercitation illampu laborings nisi ut liquid ex ea commons construal. Duos aube grue dolor in reprehended
|
||||
in voltage veil esse colum doolie eu fujian bulla parian. Exceptive sin ocean cuspidate non president, sunk in culpa qui officiate
|
||||
descent molls anim id est labours.
|
||||
</Typography>
|
||||
</Text>
|
||||
</MainCard>
|
||||
);
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ import { Link } from 'react-router-dom';
|
||||
// project import
|
||||
import { APP_DEFAULT_PATH } from 'config';
|
||||
|
||||
// material-ui
|
||||
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
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
|
||||
import error404 from 'assets/images/maintenance/Error404.png';
|
||||
@@ -14,42 +17,25 @@ import TwoCone from 'assets/images/maintenance/TwoCone.png';
|
||||
|
||||
function Error404() {
|
||||
return (
|
||||
<>
|
||||
<Grid
|
||||
container
|
||||
spacing={10}
|
||||
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 } }}>
|
||||
<Center axis="both" style={{ minHeight: '100vh', paddingTop: 12, paddingBottom: 8, overflow: 'hidden' }}>
|
||||
<VStack gap={10} hAlign="center">
|
||||
<div style={{ display: 'flex', position: 'relative' }}>
|
||||
<div style={{ width: 'min(590px, 60vw)' }}>
|
||||
<img src={error404} alt="mantis" style={{ width: '100%', height: '100%' }} />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item sx={{ position: 'relative' }}>
|
||||
<Box sx={{ position: 'absolute', top: 60, left: -40, width: { xs: 130, sm: 390 }, height: { xs: 115, sm: 330 } }}>
|
||||
</div>
|
||||
<div style={{ position: 'absolute', top: 60, left: -40, width: 'min(390px, 40vw)' }}>
|
||||
<img src={TwoCone} alt="mantis" style={{ width: '100%', height: '100%' }} />
|
||||
</Box>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<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%' } }}>
|
||||
</div>
|
||||
</div>
|
||||
<VStack gap={2} hAlign="center">
|
||||
<Heading level={1}>Page Not Found</Heading>
|
||||
<Text type="supporting" align="center" style={{ width: '61%' }}>
|
||||
The page you are looking was moved, removed, renamed, or might never exist!
|
||||
</Typography>
|
||||
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained">
|
||||
Back To Home
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
</Text>
|
||||
<Button label="Back To Home" href={APP_DEFAULT_PATH} as={Link} variant="primary" />
|
||||
</VStack>
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@ import { Link } from 'react-router-dom';
|
||||
// project import
|
||||
import { APP_DEFAULT_PATH } from 'config';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useMediaQuery, Box, Button, Grid, Stack, Typography } from '@mui/material';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
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
|
||||
import error500 from 'assets/images/maintenance/Error500.png';
|
||||
@@ -13,32 +15,23 @@ import error500 from 'assets/images/maintenance/Error500.png';
|
||||
// ==============================|| ERROR 500 - MAIN ||============================== //
|
||||
|
||||
function Error500() {
|
||||
const theme = useTheme();
|
||||
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh' }}>
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ width: { xs: 350, sm: 396 } }}>
|
||||
<Center axis="both" style={{ minHeight: '100vh' }}>
|
||||
<VStack hAlign="center">
|
||||
<div style={{ width: 'min(396px, 90vw)' }}>
|
||||
<img src={error500} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack justifyContent="center" alignItems="center">
|
||||
<Typography align="center" variant={matchDownSM ? 'h2' : 'h1'}>
|
||||
</div>
|
||||
<VStack gap={1} hAlign="center">
|
||||
<Heading level={1} align="center">
|
||||
Internal Server Error
|
||||
</Typography>
|
||||
<Typography color="textSecondary" variant="body2" align="center" sx={{ width: { xs: '73%', sm: '70%' }, mt: 1 }}>
|
||||
</Heading>
|
||||
<Text type="supporting" align="center" style={{ width: '70%', marginTop: 8 }}>
|
||||
Server error 500. we fixing the problem. please try again at a later stage.
|
||||
</Typography>
|
||||
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained" sx={{ textTransform: 'none', mt: 4 }}>
|
||||
Back To Home
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
</Text>
|
||||
<Button label="Back To Home" href={APP_DEFAULT_PATH} as={Link} variant="primary" style={{ marginTop: 32 }} />
|
||||
</VStack>
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useMediaQuery, Box, Button, Grid, Stack, TextField, Typography } from '@mui/material';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
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
|
||||
const { useTimer } = require('react-timer-hook');
|
||||
@@ -13,25 +18,20 @@ import MainCard from 'components/MainCard';
|
||||
|
||||
// ==============================|| COMING SOON - MAIN ||============================== //
|
||||
|
||||
const TimerBox = ({ count, label }) => {
|
||||
const theme = useTheme();
|
||||
const matchDownSM = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
return (
|
||||
<MainCard content={false} sx={{ width: { xs: 60, sm: 80 } }}>
|
||||
<Stack justifyContent="center" alignItems="center">
|
||||
<Box sx={{ py: 1.75 }}>
|
||||
<Typography variant={matchDownSM ? 'h4' : 'h2'}>{count}</Typography>
|
||||
</Box>
|
||||
<Box sx={{ p: 0.5, bgcolor: 'secondary.lighter', width: '100%' }}>
|
||||
<Typography align="center" variant="subtitle2">
|
||||
const TimerBox = ({ count, label }) => (
|
||||
<MainCard content={false} sx={{ width: 80 }}>
|
||||
<VStack gap={0} hAlign="center" padding={0}>
|
||||
<div style={{ padding: '14px 0' }}>
|
||||
<Heading level={2}>{count}</Heading>
|
||||
</div>
|
||||
<div style={{ padding: 4, background: 'var(--color-background-muted)', width: '100%' }}>
|
||||
<Text type="supporting" align="center">
|
||||
{label}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Text>
|
||||
</div>
|
||||
</VStack>
|
||||
</MainCard>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
TimerBox.propTypes = {
|
||||
count: PropTypes.number,
|
||||
@@ -43,51 +43,42 @@ function ComingSoon() {
|
||||
time.setSeconds(time.getSeconds() + 3600 * 24 * 2 - 3600 * 15.5);
|
||||
|
||||
const { seconds, minutes, hours, days } = useTimer({ expiryTimestamp: time });
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={4} direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh', py: 2 }}>
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ height: { xs: 310, sm: 420 }, width: { xs: 360, sm: 'auto' } }}>
|
||||
<Center axis="both" style={{ minHeight: '100vh', paddingTop: 16, paddingBottom: 16 }}>
|
||||
<VStack gap={4} hAlign="center">
|
||||
<div style={{ height: 420, width: 'min(360px, 90vw)' }}>
|
||||
<img src={coming} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack spacing={1} justifyContent="center" alignItems="center" sx={{ mt: -2 }}>
|
||||
<Typography align="center" variant="h1">
|
||||
</div>
|
||||
<VStack gap={1} hAlign="center" style={{ marginTop: -16 }}>
|
||||
<Heading level={1} align="center">
|
||||
Coming Soon
|
||||
</Typography>
|
||||
<Typography align="center" color="textSecondary">
|
||||
</Heading>
|
||||
<Text type="supporting" align="center">
|
||||
Something new is on its way
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{ width: { xs: '95%', md: '40%' } }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="center" spacing={{ xs: 1, sm: 2 }}>
|
||||
</Text>
|
||||
</VStack>
|
||||
<HStack gap={2} vAlign="center" justify="center" style={{ width: 'min(40%, 380px)' }}>
|
||||
<TimerBox count={days} label="day" />
|
||||
<Typography variant="h1"> : </Typography>
|
||||
<Heading level={1}> : </Heading>
|
||||
<TimerBox count={hours} label="hour" />
|
||||
<Typography variant="h1"> : </Typography>
|
||||
<Heading level={1}> : </Heading>
|
||||
<TimerBox count={minutes} label="min" />
|
||||
<Typography variant="h1"> : </Typography>
|
||||
<Heading level={1}> : </Heading>
|
||||
<TimerBox count={seconds} label="sec" />
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={{ width: { xs: 380, md: '40%', lg: '30%' } }}>
|
||||
<Stack spacing={2} sx={{ mt: 2 }}>
|
||||
<Typography align="center" color="textSecondary">
|
||||
</HStack>
|
||||
<VStack gap={2} style={{ width: 'min(40%, 380px)', marginTop: 16 }}>
|
||||
<Text type="supporting" align="center">
|
||||
Be the first to be notified when Mantis launches.
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<TextField fullWidth placeholder="Email Address" />
|
||||
<Button variant="contained" sx={{ width: '50%' }}>
|
||||
Notify Me
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
</Text>
|
||||
<HStack gap={1}>
|
||||
<TextInput label="Email Address" isLabelHidden placeholder="Email Address" value={email} onChange={setEmail} width="100%" />
|
||||
<Button label="Notify Me" variant="primary" style={{ width: '50%' }} />
|
||||
</HStack>
|
||||
</VStack>
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ import { Link } from 'react-router-dom';
|
||||
// project import
|
||||
import { APP_DEFAULT_PATH } from 'config';
|
||||
|
||||
// material-ui
|
||||
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
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
|
||||
import construction from 'assets/images/maintenance/under-construction.svg';
|
||||
@@ -13,26 +16,22 @@ import construction from 'assets/images/maintenance/under-construction.svg';
|
||||
|
||||
function UnderConstruction() {
|
||||
return (
|
||||
<Grid container spacing={4} direction="column" alignItems="center" justifyContent="center" sx={{ minHeight: '100vh', py: 2 }}>
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ width: { xs: 300, sm: 480 } }}>
|
||||
<Center axis="both" style={{ minHeight: '100vh', paddingTop: 16, paddingBottom: 16 }}>
|
||||
<VStack gap={4} hAlign="center">
|
||||
<div style={{ width: 'min(480px, 90vw)' }}>
|
||||
<img src={construction} alt="mantis" style={{ width: '100%', height: 'auto' }} />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack spacing={2} justifyContent="center" alignItems="center">
|
||||
<Typography align="center" variant="h1">
|
||||
</div>
|
||||
<VStack gap={2} hAlign="center">
|
||||
<Heading level={1} align="center">
|
||||
Under Construction
|
||||
</Typography>
|
||||
<Typography color="textSecondary" align="center" sx={{ width: '85%' }}>
|
||||
</Heading>
|
||||
<Text type="supporting" align="center" style={{ width: '85%' }}>
|
||||
Hey! Please check out this site later. We are doing some maintenance on it right now.
|
||||
</Typography>
|
||||
<Button component={Link} to={APP_DEFAULT_PATH} variant="contained">
|
||||
Back To Home
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Text>
|
||||
<Button label="Back To Home" href={APP_DEFAULT_PATH} as={Link} variant="primary" />
|
||||
</VStack>
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
// ============================================================================
|
||||
|
||||
import React from 'react';
|
||||
import { Avatar, Box, Paper, Stack, Typography } from '@mui/material';
|
||||
import {
|
||||
MdLocalShipping,
|
||||
MdHourglassEmpty,
|
||||
@@ -50,8 +49,8 @@ export const DT = {
|
||||
surfaceAlt: '#f8fafc'
|
||||
};
|
||||
|
||||
export const BRAND = '#662582';
|
||||
export const BRAND_LIGHT = '#9255AB';
|
||||
export const BRAND = '#C01227';
|
||||
export const BRAND_LIGHT = '#D25463';
|
||||
|
||||
const dtA = (c, suffix) => `${c}${suffix}`;
|
||||
export const tint = (c) => dtA(c, '08');
|
||||
@@ -108,15 +107,14 @@ export const StatusBadge = ({ status, minWidth = 86 }) => {
|
||||
};
|
||||
const Icon = meta.icon;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
px: 1.125,
|
||||
py: 0.375,
|
||||
gap: 4,
|
||||
padding: '3px 9px',
|
||||
borderRadius: 999,
|
||||
bgcolor: meta.color,
|
||||
backgroundColor: meta.color,
|
||||
color: '#fff',
|
||||
fontSize: 11,
|
||||
fontWeight: 700,
|
||||
@@ -128,7 +126,7 @@ export const StatusBadge = ({ status, minWidth = 86 }) => {
|
||||
}}
|
||||
>
|
||||
<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.
|
||||
// No decorative dot.
|
||||
// ----------------------------------------------------------------------------
|
||||
const noWrapStyle = { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' };
|
||||
|
||||
export const TimelineCell = ({ value, utc: useUtc = false }) => {
|
||||
if (!value) {
|
||||
return (
|
||||
<Typography sx={{ fontSize: 12, color: DT.textMuted, fontWeight: 700 }}>—</Typography>
|
||||
);
|
||||
return <span style={{ fontSize: 12, color: DT.textMuted, fontWeight: 700 }}>—</span>;
|
||||
}
|
||||
const d = useUtc ? dayjs(value).utc() : dayjs(value);
|
||||
return (
|
||||
<Stack spacing={0} sx={{ lineHeight: 1.1 }}>
|
||||
<Typography
|
||||
sx={{
|
||||
<div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.1 }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 12.5,
|
||||
fontWeight: 800,
|
||||
color: DT.textPrimary,
|
||||
letterSpacing: 0.1,
|
||||
lineHeight: 1.15
|
||||
lineHeight: 1.15,
|
||||
...noWrapStyle
|
||||
}}
|
||||
noWrap
|
||||
>
|
||||
{d.format('hh:mm A')}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 10.5,
|
||||
fontWeight: 600,
|
||||
color: DT.textMuted,
|
||||
lineHeight: 1.2
|
||||
lineHeight: 1.2,
|
||||
...noWrapStyle
|
||||
}}
|
||||
noWrap
|
||||
>
|
||||
{d.format('DD MMM YYYY')}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</span>
|
||||
</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 isZero = !Number.isFinite(n) || n === 0;
|
||||
if (isZero) {
|
||||
return (
|
||||
<Typography sx={{ fontSize: 11.5, color: DT.textMuted, fontWeight: 700 }}>
|
||||
{display}
|
||||
</Typography>
|
||||
);
|
||||
return <span style={{ fontSize: 11.5, color: DT.textMuted, fontWeight: 700 }}>{display}</span>;
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
px: 0.875,
|
||||
py: 0.25,
|
||||
gap: 4,
|
||||
padding: '2px 7px',
|
||||
borderRadius: 999,
|
||||
bgcolor: tint(color),
|
||||
backgroundColor: tint(color),
|
||||
border: `1px solid ${edge(color)}`,
|
||||
color,
|
||||
fontSize: 12,
|
||||
@@ -205,23 +198,23 @@ export const MetricPill = ({ value, color, icon, isMoney = false }) => {
|
||||
>
|
||||
{icon}
|
||||
{display}
|
||||
</Box>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// SoftPaper — autocomplete dropdown surface.
|
||||
// ----------------------------------------------------------------------------
|
||||
export const SoftPaper = (props) => (
|
||||
<Paper
|
||||
export const SoftPaper = ({ style, ...props }) => (
|
||||
<div
|
||||
{...props}
|
||||
sx={{
|
||||
mt: 0.75,
|
||||
borderRadius: 2,
|
||||
style={{
|
||||
marginTop: 6,
|
||||
borderRadius: 8,
|
||||
boxShadow: DT.shadowPop,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
overflow: 'hidden'
|
||||
border: `1px solid ${DT.borderSubtle}`,
|
||||
overflow: 'hidden',
|
||||
...style
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -230,17 +223,22 @@ export const SoftPaper = (props) => (
|
||||
// AccentAvatar — colored circular icon used inside filter chips & headers.
|
||||
// ----------------------------------------------------------------------------
|
||||
export const AccentAvatar = ({ color, selected, size = 24, children }) => (
|
||||
<Avatar
|
||||
sx={{
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: size,
|
||||
height: size,
|
||||
bgcolor: selected ? color : soft(color),
|
||||
flexShrink: 0,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: selected ? color : soft(color),
|
||||
color: selected ? '#fff' : color,
|
||||
transition: 'background-color 0.15s, color 0.15s'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Avatar>
|
||||
</div>
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Grid, List, ListItem, Stack, Typography, useMediaQuery } from '@mui/material';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
|
||||
import axios from 'axios';
|
||||
@@ -9,8 +8,29 @@ import Loader from 'components/Loader';
|
||||
import Footer from 'layout/MainLayout/Footer';
|
||||
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 matchDownMD = useMediaQuery((theme) => theme.breakpoints.down('md'));
|
||||
const [info, setInfo] = useState({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -25,14 +45,14 @@ const Accountsettings = () => {
|
||||
await axios
|
||||
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
logger.info(res);
|
||||
if (res.data.status) {
|
||||
setInfo(res.data.details);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
enqueueSnackbar(err.message, {
|
||||
variant: 'error',
|
||||
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
||||
@@ -47,101 +67,31 @@ const Accountsettings = () => {
|
||||
{loading && <Loader />}
|
||||
<TitleCard title={'Profile'} />
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<MainCard>
|
||||
<List sx={{ py: 0 }}>
|
||||
<ListItem divider={!matchDownMD}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack spacing={0.5}>
|
||||
<Typography color="secondary">Name</Typography>
|
||||
<Typography>{info.tenantname || ''}</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack spacing={0.5}>
|
||||
<Typography color="secondary">Company Name</Typography>
|
||||
<Typography>{info.companyname || ''}</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">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 content={false}>
|
||||
<Row hasDivider>
|
||||
<Field label="Name" value={info.tenantname} />
|
||||
<Field label="Company Name" value={info.companyname} />
|
||||
</Row>
|
||||
<Row hasDivider>
|
||||
<Field label="Registration No" value={info.registrationno} />
|
||||
<Field label="Info" value={info.info} />
|
||||
</Row>
|
||||
<Row hasDivider>
|
||||
<Field label="Phone" value={info.primarycontact} />
|
||||
<Field label="State" value={info.state} />
|
||||
</Row>
|
||||
<Row hasDivider>
|
||||
<Field label="Email" value={info.primaryemail} />
|
||||
<Field label="City" value={info.city} />
|
||||
</Row>
|
||||
<Row hasDivider>
|
||||
<Field label="Suburb" value={info.suburb} />
|
||||
<Field label="Zip Code" value={info.postcode} />
|
||||
</Row>
|
||||
<Row>
|
||||
<Field label="Address" value={info.address} />
|
||||
</Row>
|
||||
</MainCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Footer />
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import { OpenToast } from 'components/nearle_components/OpenToast';
|
||||
import logger from 'utils/logger';
|
||||
const tenid = localStorage.getItem('tenantid');
|
||||
|
||||
export const fetchOrders = async ({ pageParam = 1, queryKey }) => {
|
||||
@@ -55,14 +56,14 @@ export const fetchorderscount = async ({ queryKey }) => {
|
||||
// ==============================|| fetchOrderSummary (orders)||============================== //
|
||||
export const fetchOrderSummary = async () => {
|
||||
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;
|
||||
};
|
||||
|
||||
// ==============================|| fetchLocationSummary (orders)||============================== //
|
||||
export const fetchLocationSummary = async () => {
|
||||
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;
|
||||
};
|
||||
@@ -70,7 +71,7 @@ export const fetchLocationSummary = async () => {
|
||||
// ==============================|| fetchOrderInsight (orders)||============================== //
|
||||
export const fetchOrderInsight = async () => {
|
||||
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;
|
||||
};
|
||||
@@ -78,14 +79,14 @@ export const fetchOrderInsight = async () => {
|
||||
// ==============================|| fetchDeliveryInsight (delivery)||============================== //
|
||||
export const fetchDeliveryInsight = async () => {
|
||||
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;
|
||||
};
|
||||
// ==============================|| fetchDeliveryLocationSummary (delivery)||============================== //
|
||||
export const fetchDeliveryLocationSummary = async () => {
|
||||
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;
|
||||
};
|
||||
@@ -121,7 +122,7 @@ export const fetchAllTenants = async ({ queryKey }) => {
|
||||
const response = await axios.get(url);
|
||||
|
||||
// tenants/search/?keyword=${search}
|
||||
console.log('fetchAllTenants', response.data.details);
|
||||
logger.info('fetchAllTenants', response.data.details);
|
||||
|
||||
return response.data.details;
|
||||
};
|
||||
@@ -129,7 +130,7 @@ export const fetchAllTenants = async ({ queryKey }) => {
|
||||
export const fetchCustomersList = async ({ queryKey }) => {
|
||||
const [pages] = queryKey;
|
||||
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;
|
||||
};
|
||||
// ==============================|| gettenantcustomers (customers)||============================== //
|
||||
@@ -158,39 +159,39 @@ export const gettenantcustomers = async ({ pageParam = 1, queryKey }) => {
|
||||
export const fetchCustomersListBySearch = async ({ queryKey }) => {
|
||||
const [search] = queryKey;
|
||||
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;
|
||||
};
|
||||
// ==============================|| fetchOrdersSummary (rider summary)||============================== //
|
||||
export const fetchOrdersSummary = async ({ queryKey }) => {
|
||||
console.log('queryKey for fetchOrdersSummary', queryKey);
|
||||
logger.info('queryKey for fetchOrdersSummary', queryKey);
|
||||
const [startdate, enddate] = queryKey;
|
||||
const response = await axios.get(
|
||||
`${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;
|
||||
};
|
||||
// ==============================|| getreportlocationsummary (orders summary)||============================== //
|
||||
export const getreportlocationsummary = async ({ queryKey }) => {
|
||||
console.log('queryKey for getreportlocationsummary', queryKey);
|
||||
logger.info('queryKey for getreportlocationsummary', queryKey);
|
||||
const [startdate, enddate, locationId, debouncedSearch] = queryKey;
|
||||
const response = await axios.get(
|
||||
`${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;
|
||||
};
|
||||
// ==============================|| getriderlocationreportsummary (orders summary)||============================== //
|
||||
export const getriderlocationreportsummary = async ({ queryKey }) => {
|
||||
console.log('queryKey for getriderlocationreportsummary', queryKey);
|
||||
logger.info('queryKey for getriderlocationreportsummary', queryKey);
|
||||
const [startdate, enddate, locationId] = queryKey;
|
||||
const response = await axios.get(
|
||||
`${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;
|
||||
};
|
||||
@@ -202,7 +203,7 @@ export const fetchLocations = async () => {
|
||||
...response.data.details,
|
||||
{ partnername: 'All', partnerid: -1 } // Add your new object here
|
||||
];
|
||||
console.log('fetchLocations', updatedLocations);
|
||||
logger.info('fetchLocations', updatedLocations);
|
||||
|
||||
return updatedLocations;
|
||||
};
|
||||
@@ -216,7 +217,7 @@ export const gettenantlocations = async ({ queryKey }) => {
|
||||
} catch (error) {
|
||||
// Must return an array — downstream consumers do `.map`/`.length` and a
|
||||
// string here crashes the entire Locations page.
|
||||
console.error('Error fetching tenant locations:', error);
|
||||
logger.error('Error fetching tenant locations:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
@@ -227,7 +228,7 @@ export const fetchDeliverySummary = async ({ queryKey }) => {
|
||||
const response = await axios.get(
|
||||
`${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;
|
||||
};
|
||||
// ==============================|| fetchAppLocations (report summary))||============================== //
|
||||
@@ -238,7 +239,7 @@ export const fetchAppLocations = async () => {
|
||||
...response.data.details,
|
||||
{ partnername: 'All', applocationid: -1 } // Add your new object here
|
||||
];
|
||||
console.log('fetchAppLocations', updatedLocations);
|
||||
logger.info('fetchAppLocations', updatedLocations);
|
||||
|
||||
return updatedLocations;
|
||||
};
|
||||
@@ -246,12 +247,12 @@ export const fetchAppLocations = async () => {
|
||||
// ==============================|| fetchRidersSummary (riders summary)||============================== //
|
||||
|
||||
export const fetchRidersSummary = async ({ queryKey }) => {
|
||||
console.log('queryKey for fetchRidersSummary', queryKey);
|
||||
logger.info('queryKey for fetchRidersSummary', queryKey);
|
||||
const [tenantid, startdate, enddate] = queryKey;
|
||||
const response = await axios.get(
|
||||
`${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;
|
||||
};
|
||||
@@ -281,19 +282,19 @@ export const fetchorderdetails = async ({ pageParam = 0, queryKey }) => {
|
||||
};
|
||||
|
||||
// export const fetchorderdetails = async ({ queryKey }) => {
|
||||
// console.log('queryKey of fetchorderdetails', queryKey);
|
||||
// logger.info('queryKey of fetchorderdetails', queryKey);
|
||||
// const [startdate, enddate, currentStatus] = queryKey;
|
||||
// const response = await axios.get(
|
||||
// `${process.env.REACT_APP_URL}/deliveries/getdeliveries/?tenantid=${tenid}&fromdate=${startdate}&todate=${enddate}&status=${
|
||||
// currentStatus == 'All' ? '' : currentStatus
|
||||
// }`
|
||||
// );
|
||||
// console.log('fetchorderdetails', response.data.details);
|
||||
// logger.info('fetchorderdetails', response.data.details);
|
||||
// return response.data.details;
|
||||
// };
|
||||
// ==============================|| fetchCount (orders detail)||============================== //
|
||||
export const fetchCount = async ({ queryKey }) => {
|
||||
console.log('queryKey of fetchCount', queryKey);
|
||||
logger.info('queryKey of fetchCount', queryKey);
|
||||
const [startdate, enddate] = queryKey;
|
||||
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getorders/?fromdate=${startdate}&todate=${enddate}`);
|
||||
const calculateOrderCounts = () => {
|
||||
@@ -314,7 +315,7 @@ export const fetchCount = async ({ queryKey }) => {
|
||||
|
||||
return { deliveredCount, pendingCount, cancelledCount };
|
||||
};
|
||||
console.log('fetchCount', calculateOrderCounts());
|
||||
logger.info('fetchCount', calculateOrderCounts());
|
||||
|
||||
return calculateOrderCounts();
|
||||
};
|
||||
@@ -333,7 +334,7 @@ export const fetchRidersLogs = async ({ queryKey }) => {
|
||||
startdate || ''
|
||||
}&keyword=${riderSearch || ''}`
|
||||
);
|
||||
console.log('fetchRidersLogs', riderLogsResponse.data.details);
|
||||
logger.info('fetchRidersLogs', 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`);
|
||||
return res.data.details;
|
||||
} catch (err) {
|
||||
console.log('getallriders', err.message);
|
||||
logger.error('getallriders', err.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,41 +1,29 @@
|
||||
import { React, useEffect, useState } from 'react';
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormLabel,
|
||||
Grid,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
Stack,
|
||||
TextField,
|
||||
Typography,
|
||||
IconButton,
|
||||
Autocomplete,
|
||||
InputAdornment
|
||||
} from '@mui/material';
|
||||
import MyLocationIcon from '@mui/icons-material/MyLocation';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import { TextInput } from '@astryxdesign/core/TextInput';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
|
||||
// project import
|
||||
import Avatar from 'components/@extended/Avatar';
|
||||
import MainCard from 'components/MainCard';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import { usePlacesWidget } from 'react-google-autocomplete';
|
||||
import Loader from 'components/Loader';
|
||||
import Geocode from 'react-geocode';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { useNavigate } from 'react-router';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
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 appId = localStorage.getItem('applocationid');
|
||||
const theme = useTheme();
|
||||
const [selectedImage, setSelectedImage] = useState(undefined);
|
||||
const [avatar, setAvatar] = useState();
|
||||
const [businessname, setBusinessname] = useState('');
|
||||
@@ -103,8 +91,8 @@ const CreateCustomer = () => {
|
||||
autocomplete.addListener('place_changed', () => {
|
||||
const place = autocomplete.getPlace();
|
||||
setInputValue2(`${place.name}, ${place.formatted_address}`);
|
||||
console.log('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('new place', place); // 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
|
||||
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
|
||||
setAddress(`${place.name} ${place.formatted_address}`);
|
||||
@@ -164,7 +152,7 @@ const CreateCustomer = () => {
|
||||
latitude: place.geometry.location.lat(),
|
||||
longitude: place.geometry.location.lng()
|
||||
});
|
||||
console.log('Pick Address:', address);
|
||||
logger.info('Pick Address:', address);
|
||||
});
|
||||
}
|
||||
}, [inputValue2]);
|
||||
@@ -174,18 +162,18 @@ const CreateCustomer = () => {
|
||||
await axios
|
||||
.get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`)
|
||||
.then((res) => {
|
||||
console.log('getapplocations', res);
|
||||
const { opentime, closetime, latitude, longitude, radius } = res.data.details[0];
|
||||
logger.info('getapplocations', res);
|
||||
const { opentime, closetime, latitude, longitude, radius } = res.data.details?.[0] || {};
|
||||
if (res.data.status) {
|
||||
setAppLocaLat(latitude);
|
||||
setAppLocaLng(longitude);
|
||||
setAppLocaRadius(radius);
|
||||
console.log('radius', radius);
|
||||
logger.info('radius', radius);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -201,11 +189,11 @@ const CreateCustomer = () => {
|
||||
// ...locationRes.data.details,
|
||||
// { partnername: 'All', applocationid: -1 }
|
||||
// ];
|
||||
// console.log('fetchAppLocations', updatedLocations);
|
||||
console.log('fetchAppLocations', locationRes.data.details);
|
||||
// logger.info('fetchAppLocations', updatedLocations);
|
||||
logger.info('fetchAppLocations', locationRes.data.details);
|
||||
setLocations(locationRes.data.details);
|
||||
} catch (err) {
|
||||
console.log('locationRes', err);
|
||||
logger.error('locationRes', err);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@@ -220,7 +208,7 @@ const CreateCustomer = () => {
|
||||
.get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${id}&status=active`)
|
||||
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
logger.info(res);
|
||||
if (res.data.status) {
|
||||
let arr = [];
|
||||
res.data.details.map((val) => {
|
||||
@@ -234,7 +222,7 @@ const CreateCustomer = () => {
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -242,7 +230,7 @@ const CreateCustomer = () => {
|
||||
const gettenantlocations = async (id) => {
|
||||
try {
|
||||
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) {
|
||||
setIsLocation(true);
|
||||
setTenantlocations(res.data.details);
|
||||
@@ -254,7 +242,7 @@ const CreateCustomer = () => {
|
||||
setIsBusiness(false); // became true after select from tenanatLocations
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('gettenantlocations', err);
|
||||
logger.error('gettenantlocations', err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,14 +259,14 @@ const CreateCustomer = () => {
|
||||
await axios
|
||||
.get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`)
|
||||
.then((res) => {
|
||||
console.log('fetchtenantinfo', res.data.details);
|
||||
logger.info('fetchtenantinfo', res.data.details);
|
||||
if (res.data.status) {
|
||||
setTenantinfo(res.data.details);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
@@ -314,21 +302,21 @@ const CreateCustomer = () => {
|
||||
customertoken: '',
|
||||
primaryaddress: 1
|
||||
};
|
||||
console.log(obj);
|
||||
logger.info(obj);
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
await axios
|
||||
.post(`${process.env.REACT_APP_URL}/customers/create`, obj)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
logger.info(res);
|
||||
if (res.data.status) {
|
||||
enqueueSnackbar(' Created Successfully ', {
|
||||
variant: 'success',
|
||||
anchorOrigin: { vertical: 'top', horizontal: 'right' },
|
||||
autoHideDuration: 2000
|
||||
});
|
||||
navigate('/customers');
|
||||
navigate('/nearle/customers');
|
||||
} else if (res.data.message == 'Customer Already available') {
|
||||
enqueueSnackbar('Customer Already available', {
|
||||
variant: 'error',
|
||||
@@ -339,7 +327,7 @@ const CreateCustomer = () => {
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
|
||||
setLoading(false);
|
||||
enqueueSnackbar(err.message, {
|
||||
@@ -349,7 +337,7 @@ const CreateCustomer = () => {
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -359,107 +347,67 @@ const CreateCustomer = () => {
|
||||
{loading && <Loader />}
|
||||
<TitleCard
|
||||
title={'Create Customer'}
|
||||
secondary={
|
||||
<Button variant="contained" color="secondary" onClick={() => navigate('/nearle/customers')}>
|
||||
Back
|
||||
</Button>
|
||||
}
|
||||
secondary={<Button label="Back" variant="secondary" onClick={() => navigate('/nearle/customers')} />}
|
||||
/>
|
||||
|
||||
<MainCard>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={3}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
<FieldGrid>
|
||||
{/* ===================================================== || Name|| ===================================================== */}
|
||||
<Grid item xs={12} sm={6}>
|
||||
<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>
|
||||
<TextInput label="Name" placeholder="Name" onChange={setFirstname} value={firstname} />
|
||||
|
||||
{/* ===================================================== || Phone Number || ===================================================== */}
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack spacing={1.25}>
|
||||
<InputLabel htmlFor="personal-phone">Phone Number</InputLabel>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
||||
<Select defaultValue="+1" disabled sx={{ cursor: 'not-allowed' }}>
|
||||
<MenuItem value="+1">+91</MenuItem>
|
||||
</Select>
|
||||
<TextField
|
||||
<div>
|
||||
<Text type="strong" size="sm" style={{ marginBottom: 6, display: 'block' }}>
|
||||
Phone Number
|
||||
</Text>
|
||||
<HStack gap={2} vAlign="center">
|
||||
<div
|
||||
style={{
|
||||
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"
|
||||
id="personal-phone"
|
||||
fullWidth
|
||||
placeholder="Phone Number"
|
||||
onChange={(e) => {
|
||||
if (e.target.value.toString().length <= 10) {
|
||||
setMobilenumber(e.target.value);
|
||||
}
|
||||
width="100%"
|
||||
onChange={(v) => {
|
||||
if (v.toString().length <= 10) setMobilenumber(v);
|
||||
}}
|
||||
value={mobilenumber}
|
||||
autoComplete="off"
|
||||
// disabled
|
||||
sx={{ cursor: 'not-allowed' }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</HStack>
|
||||
</div>
|
||||
|
||||
{/* ===================================================== || 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 || ===================================================== */}
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<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>
|
||||
<TextInput label="Door No" placeholder="Door No" onChange={setDoorno} value={doorno} />
|
||||
</FieldGrid>
|
||||
|
||||
{/* ===================================================== || Address || ===================================================== */}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Stack spacing={1.25}>
|
||||
<InputLabel htmlFor="personal-email"> Address</InputLabel>
|
||||
<TextField
|
||||
variant="outlined"
|
||||
<TextInput
|
||||
label="Address"
|
||||
id="addressAuto1"
|
||||
fullWidth
|
||||
width="100%"
|
||||
hasClear
|
||||
value={inputValue2}
|
||||
onChange={(e) => setInputValue2(e.target.value)}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setInputValue2('');
|
||||
onChange={(v) => {
|
||||
setInputValue2(v);
|
||||
if (!v) {
|
||||
setPickCust({
|
||||
...pickCust,
|
||||
doorno: '',
|
||||
@@ -469,96 +417,34 @@ const CreateCustomer = () => {
|
||||
landmark: ''
|
||||
});
|
||||
setStartPoint({ latitude: 0, longitude: 0 });
|
||||
}}
|
||||
size="small"
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack spacing={1.25}>
|
||||
<InputLabel htmlFor="personal-location">Location</InputLabel>
|
||||
<TextField
|
||||
fullWidth
|
||||
// defaultValue="New York"
|
||||
id="personal-location"
|
||||
<FieldGrid>
|
||||
<TextInput
|
||||
label="Location"
|
||||
placeholder="Location"
|
||||
onChange={(e) => setPickCust({ ...pickCust, suburb: e.target.value })}
|
||||
onChange={(v) => setPickCust({ ...pickCust, suburb: v })}
|
||||
value={pickCust.suburb}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack spacing={1.25}>
|
||||
<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"
|
||||
<TextInput label="City" placeholder="City" onChange={(v) => setPickCust({ ...pickCust, city: v })} value={pickCust.city} />
|
||||
<TextInput label="State" placeholder="State" onChange={(v) => setPickCust({ ...pickCust, state: v })} value={pickCust.state} />
|
||||
<TextInput
|
||||
label="Post Code"
|
||||
type="number"
|
||||
id="personal-zipcode"
|
||||
placeholder="Zipcode"
|
||||
onChange={(e) => setPickCust({ ...pickCust, postcode: e.target.value })}
|
||||
onChange={(v) => setPickCust({ ...pickCust, postcode: v })}
|
||||
value={pickCust.postcode}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</FieldGrid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Stack spacing={1.25}>
|
||||
<InputLabel htmlFor="personal-email">Landmark</InputLabel>
|
||||
<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}>
|
||||
<TextInput label="Landmark" placeholder="Landmark" width="100%" onChange={setLandmark} value={landmark} />
|
||||
|
||||
<HStack justify="end">
|
||||
<Button
|
||||
variant="contained"
|
||||
label="Create"
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
if (appId === '') {
|
||||
opentoast('Select Applocation ');
|
||||
@@ -588,12 +474,9 @@ const CreateCustomer = () => {
|
||||
createprofile();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
/>
|
||||
</HStack>
|
||||
</div>
|
||||
</MainCard>
|
||||
</>
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user