overall updates

This commit is contained in:
joshikannan
2025-11-26 18:24:03 +05:30
parent 12df2e9dc4
commit e71e44319c
35 changed files with 3145 additions and 2404 deletions

156
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": {
"@ant-design/colors": "^7.0.0",
"@ant-design/icons": "^5.0.1",
"@custom-react-hooks/use-network": "^1.0.1",
"@emotion/cache": "^11.10.7",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
@@ -18,6 +19,7 @@
"@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",
"@svgr/webpack": "^7.0.0",
"@tanstack/react-query": "^5.22.2",
@@ -2267,6 +2269,15 @@
"node": ">=10"
}
},
"node_modules/@custom-react-hooks/use-network": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@custom-react-hooks/use-network/-/use-network-1.0.1.tgz",
"integrity": "sha512-WbFVxsC18hjJiVIONAExVORlJlrOeYwzKsiYUCzrt5zCP0u9eLUPJuftF3DvEpoaQdjufazllWCPWKXT+dTY2g==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@emotion/babel-plugin": {
"version": "11.10.6",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz",
@@ -2624,6 +2635,22 @@
"tslib": "^2.4.0"
}
},
"node_modules/@googlemaps/js-api-loader": {
"version": "1.16.8",
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.8.tgz",
"integrity": "sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ==",
"license": "Apache-2.0"
},
"node_modules/@googlemaps/markerclusterer": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.5.3.tgz",
"integrity": "sha512-x7lX0R5yYOoiNectr10wLgCBasNcXFHiADIBdmn7jQllF2B5ENQw5XtZK+hIw4xnV0Df0xhN4LN98XqA5jaiOw==",
"license": "Apache-2.0",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"supercluster": "^8.0.1"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@@ -4111,6 +4138,36 @@
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="
},
"node_modules/@react-google-maps/api": {
"version": "2.20.7",
"resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.7.tgz",
"integrity": "sha512-ys7uri3V6gjhYZUI43srHzSKDC6/jiKTwHNlwXFTvjeaJE3M3OaYBt9FZKvJs8qnOhL6i6nD1BKJoi1KrnkCkg==",
"license": "MIT",
"dependencies": {
"@googlemaps/js-api-loader": "1.16.8",
"@googlemaps/markerclusterer": "2.5.3",
"@react-google-maps/infobox": "2.20.0",
"@react-google-maps/marker-clusterer": "2.20.0",
"@types/google.maps": "3.58.1",
"invariant": "2.2.4"
},
"peerDependencies": {
"react": "^16.8 || ^17 || ^18 || ^19",
"react-dom": "^16.8 || ^17 || ^18 || ^19"
}
},
"node_modules/@react-google-maps/infobox": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.20.0.tgz",
"integrity": "sha512-03PJHjohhaVLkX6+NHhlr8CIlvUxWaXhryqDjyaZ8iIqqix/nV8GFdz9O3m5OsjtxtNho09F/15j14yV0nuyLQ==",
"license": "MIT"
},
"node_modules/@react-google-maps/marker-clusterer": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.20.0.tgz",
"integrity": "sha512-tieX9Va5w1yP88vMgfH1pHTacDQ9TgDTjox3tLlisKDXRQWdjw+QeVVghhf5XqqIxXHgPdcGwBvKY6UP+SIvLw==",
"license": "MIT"
},
"node_modules/@react-leaflet/core": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
@@ -4905,6 +4962,12 @@
"@types/range-parser": "*"
}
},
"node_modules/@types/google.maps": {
"version": "3.58.1",
"resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz",
"integrity": "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==",
"license": "MIT"
},
"node_modules/@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@@ -10699,6 +10762,15 @@
"tslib": "^2.4.0"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/ipaddr.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
@@ -13433,6 +13505,12 @@
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"node_modules/kdbush": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
"integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==",
"license": "ISC"
},
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -18872,6 +18950,15 @@
"stylis": "4.x"
}
},
"node_modules/supercluster": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
"license": "ISC",
"dependencies": {
"kdbush": "^4.0.2"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -22001,6 +22088,12 @@
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA=="
},
"@custom-react-hooks/use-network": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@custom-react-hooks/use-network/-/use-network-1.0.1.tgz",
"integrity": "sha512-WbFVxsC18hjJiVIONAExVORlJlrOeYwzKsiYUCzrt5zCP0u9eLUPJuftF3DvEpoaQdjufazllWCPWKXT+dTY2g==",
"requires": {}
},
"@emotion/babel-plugin": {
"version": "11.10.6",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz",
@@ -22292,6 +22385,20 @@
"tslib": "^2.4.0"
}
},
"@googlemaps/js-api-loader": {
"version": "1.16.8",
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.8.tgz",
"integrity": "sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ=="
},
"@googlemaps/markerclusterer": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.5.3.tgz",
"integrity": "sha512-x7lX0R5yYOoiNectr10wLgCBasNcXFHiADIBdmn7jQllF2B5ENQw5XtZK+hIw4xnV0Df0xhN4LN98XqA5jaiOw==",
"requires": {
"fast-deep-equal": "^3.1.3",
"supercluster": "^8.0.1"
}
},
"@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@@ -23231,6 +23338,29 @@
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="
},
"@react-google-maps/api": {
"version": "2.20.7",
"resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.7.tgz",
"integrity": "sha512-ys7uri3V6gjhYZUI43srHzSKDC6/jiKTwHNlwXFTvjeaJE3M3OaYBt9FZKvJs8qnOhL6i6nD1BKJoi1KrnkCkg==",
"requires": {
"@googlemaps/js-api-loader": "1.16.8",
"@googlemaps/markerclusterer": "2.5.3",
"@react-google-maps/infobox": "2.20.0",
"@react-google-maps/marker-clusterer": "2.20.0",
"@types/google.maps": "3.58.1",
"invariant": "2.2.4"
}
},
"@react-google-maps/infobox": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.20.0.tgz",
"integrity": "sha512-03PJHjohhaVLkX6+NHhlr8CIlvUxWaXhryqDjyaZ8iIqqix/nV8GFdz9O3m5OsjtxtNho09F/15j14yV0nuyLQ=="
},
"@react-google-maps/marker-clusterer": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.20.0.tgz",
"integrity": "sha512-tieX9Va5w1yP88vMgfH1pHTacDQ9TgDTjox3tLlisKDXRQWdjw+QeVVghhf5XqqIxXHgPdcGwBvKY6UP+SIvLw=="
},
"@react-leaflet/core": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
@@ -23772,6 +23902,11 @@
"@types/range-parser": "*"
}
},
"@types/google.maps": {
"version": "3.58.1",
"resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz",
"integrity": "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ=="
},
"@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@@ -28074,6 +28209,14 @@
"tslib": "^2.4.0"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"ipaddr.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
@@ -30040,6 +30183,11 @@
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"kdbush": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
"integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -33782,6 +33930,14 @@
"cssjanus": "^2.0.1"
}
},
"supercluster": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
"requires": {
"kdbush": "^4.0.2"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@@ -5,6 +5,7 @@
"dependencies": {
"@ant-design/colors": "^7.0.0",
"@ant-design/icons": "^5.0.1",
"@custom-react-hooks/use-network": "^1.0.1",
"@emotion/cache": "^11.10.7",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
@@ -13,6 +14,7 @@
"@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",
"@svgr/webpack": "^7.0.0",
"@tanstack/react-query": "^5.22.2",

View File

@@ -8,6 +8,7 @@ import Snackbar from 'components/@extended/Snackbar';
import Notistack from 'components/third-party/Notistack';
import { useNavigate } from 'react-router';
import { useEffect } from 'react';
import InternetStatus from 'components/nearle_components/updateNetworkStatus';
// auth-provider
// import { JWTProvider as AuthProvider } from 'contexts/JWTContext';
@@ -15,16 +16,15 @@ import { useEffect } from 'react';
const App = () => {
let navigate = useNavigate();
useEffect(() => {
if (!localStorage.getItem('authname')) {
navigate('/login');
}
}, []);
return (
<>
<ThemeCustomization>
<InternetStatus />
{/* <RTLLayout> */}
<Locales>
<ScrollTop>

View File

@@ -5,11 +5,13 @@ import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
// ===========================|| HOVER SOCIAL CARD ||=========================== //
const HoverSocialCard = ({ primary, secondary,
percentage,
const HoverSocialCard = ({
primary,
secondary,
percentage,
// iconPrimary,
color
}) => {
color
}) => {
// const IconPrimary = iconPrimary;
// const primaryIcon = iconPrimary ? <IconPrimary /> : null;
@@ -20,10 +22,10 @@ const HoverSocialCard = ({ primary, secondary,
background: color,
position: 'relative',
color: '#fff',
// '&:hover svg': {
// opacity: 1,
// transform: 'scale(1.1)'
// }
'&:hover svg': {
opacity: 1,
transform: 'scale(1.1)'
}
}}
>
<CardContent>
@@ -33,19 +35,18 @@ const HoverSocialCard = ({ primary, secondary,
right: 15,
top: 25,
color: '#fff',
// '& svg': {
// width: 36,
// height: 36,
// opacity: 0.5,
// transition: 'all .3s ease-in-out'
// }
'& svg': {
width: 36,
height: 36,
opacity: 0.5,
transition: 'all .3s ease-in-out'
}
}}
>
<Typography variant="h2" color="inherit">
{/* {percentage.toString()} % */}
{(percentage)? `${percentage.toString()} %`:''}
</Typography>
{/* {percentage.toString()} % */}
{percentage ? `${percentage.toString()} %` : ''}
</Typography>
{/* {primaryIcon} */}
</Box>
<Grid container spacing={0}>
@@ -69,7 +70,7 @@ HoverSocialCard.propTypes = {
primary: PropTypes.string,
secondary: PropTypes.string,
// iconPrimary: PropTypes.object,
percentage:PropTypes.string,
percentage: PropTypes.string,
color: PropTypes.string
};

View File

@@ -0,0 +1,55 @@
import { Dialog, DialogTitle, DialogContent, Typography, Stack, Button, DialogActions, Paper } from '@mui/material';
import { DateRangePicker } from 'mui-daterange-picker';
import dayjs from 'dayjs';
import { addDays, addWeeks, addMonths, startOfWeek, endOfWeek, startOfMonth, endOfMonth } from 'date-fns';
export default function DateFilterDialog({ open, onClose, onApply }) {
const definedRanges = [
{ label: 'Today', startDate: new Date(), endDate: new Date() },
{ label: 'Yesterday', startDate: addDays(new Date(), -1), endDate: addDays(new Date(), -1) },
{ label: 'Tomorrow', startDate: addDays(new Date(), 1), endDate: addDays(new Date(), 1) },
{ label: 'This Week', startDate: startOfWeek(new Date()), endDate: endOfWeek(new Date()) },
{ label: 'Last Week', startDate: startOfWeek(addWeeks(new Date(), -1)), endDate: endOfWeek(addWeeks(new Date(), -1)) },
{ label: 'Last 7 Days', startDate: addDays(new Date(), -7), endDate: new Date() },
{ label: 'This Month', startDate: startOfMonth(new Date()), endDate: endOfMonth(new Date()) },
{ 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 || ''
});
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'
}
}}
>
<DateRangePicker open={open} toggle={onClose} definedRanges={definedRanges} onChange={handleSelect} closeOnClickOutside={false} />
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={onClose} sx={{ cursor: 'pointer' }}>
Close
</Button>
</DialogActions>
</Dialog>
);
}

View File

@@ -0,0 +1,58 @@
// 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';
const SearchBar = ({ value, onChange, sx, placeholder = 'Search (Ctrl + K)' }) => {
const theme = useTheme();
const inputRef = useRef(null);
/* ============================================= || handleKeyPress (CTRL + K) and ESC ||============================================= */
useEffect(() => {
const handleKeyPress = (event) => {
// Focus input with CTRL + K / CMD + K
if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
inputRef.current?.focus();
}
// ESC removes focus
if (event.key === 'Escape' && document.activeElement === inputRef.current) {
inputRef.current.blur();
}
};
document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
}, []);
return (
<FormControl fullWidth sx={{}}>
<OutlinedInput
inputRef={inputRef}
placeholder={placeholder}
sx={{
borderRadius: 0,
...sx
}}
value={value}
onChange={onChange}
autoComplete="off"
size="large"
startAdornment={
<InputAdornment position="start" sx={{ mr: -0.5, color: theme.palette.primary.main }}>
<SearchOutlined />
</InputAdornment>
}
endAdornment={
<IconButton sx={{ visibility: value ? 'visible' : 'hidden' }} onClick={() => onChange({ target: { value: '' } })}>
<ClearIcon style={{ fontSize: 'large', color: theme.palette.primary.main }} />
</IconButton>
}
/>
</FormControl>
);
};
export default SearchBar;

View File

@@ -0,0 +1,96 @@
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';
const NOTIFICATION_STYLE = {
position: 'fixed',
top: '10px', // Keep top spacing
left: '50%', // Center horizontally
transform: 'translateX(-50%)', // Offset to truly center
padding: '12px 40px',
borderRadius: '8px',
color: 'white',
fontWeight: '500',
fontSize: '14px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
zIndex: 9999,
maxWidth: '90%',
minWidth: 'auto',
textAlign: 'center'
};
const InternetStatus = () => {
const theme = useTheme();
const [isOnline, setIsOnline] = useState(navigator.onLine);
const [showBackOnline, setShowBackOnline] = useState(false);
const wasOffline = useRef(!navigator.onLine);
useEffect(() => {
const handleNetworkChange = () => {
const online = navigator.onLine;
setIsOnline(online);
if (online && wasOffline.current) {
console.log('✅ Back online');
setShowBackOnline(true);
wasOffline.current = false;
// window.location.reload();
setTimeout(() => {
setShowBackOnline(false);
}, 2000);
}
if (!online) {
console.log('❌ No internet connection');
wasOffline.current = true;
setShowBackOnline(false);
}
};
window.addEventListener('online', handleNetworkChange);
window.addEventListener('offline', handleNetworkChange);
return () => {
window.removeEventListener('online', handleNetworkChange);
window.removeEventListener('offline', handleNetworkChange);
};
}, []);
return (
<>
{!isOnline && (
<div
style={{
...NOTIFICATION_STYLE,
backgroundColor: theme.palette.error.main,
display: 'flex',
alignItems: 'center',
gap: '10px'
}}
>
<WifiOffIcon style={{ color: '#fff' }} />
No Internet Connection
</div>
)}
{showBackOnline && (
<div
style={{
...NOTIFICATION_STYLE,
backgroundColor: theme.palette.success.main,
display: 'flex',
alignItems: 'center',
gap: '10px'
}}
>
<WifiIcon style={{ color: '#fff' }} />
Back Online
</div>
)}
</>
);
};
export default InternetStatus;

View File

@@ -0,0 +1,15 @@
import { useEffect, useState } from 'react';
export const useDebounce = (value, delay = 500) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(handler); // cleanup on every value change
}, [value, delay]);
return debouncedValue;
};

View File

@@ -0,0 +1,26 @@
import { useEffect } from 'react';
export const useHotkeyFocus = (ref, hotkey = 'k') => {
useEffect(() => {
if (!ref?.current) return;
const handleKeyPress = (event) => {
const isHotkey = event.key.toLowerCase() === hotkey.toLowerCase();
// CTRL + Hotkey
if (isHotkey && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
ref.current?.focus();
}
// ESC to blur
if (event.key === 'Escape' && document.activeElement === ref.current) {
ref.current.blur();
}
};
document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
}, [ref, hotkey]);
};

View File

@@ -11,7 +11,7 @@ import 'simplebar/dist/simplebar.css';
import 'assets/third-party/apex-chart.css';
import 'assets/third-party/react-table.css';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
// project import
import App from './App';

View File

@@ -211,7 +211,16 @@ const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, s
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 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;
@@ -237,11 +246,10 @@ const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, s
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: theme.palette.mode === ThemeMode.DARK ? 'divider' : '#7b1fa2'
// bgcolor: theme.palette.mode === ThemeMode.DARK ? 'divider' : 'primary.lighter',
bgcolor: '#7b1fa2'
},
'&.Mui-selected': {
bgcolor: 'transparent',
@@ -250,14 +258,14 @@ const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, s
}
}),
...(!drawerOpen && {
bgcolor: theme.palette.primary.main,
'&:hover': {
bgcolor: 'transparent'
bgcolor: theme.palette.primary.light
},
'&.Mui-selected': {
'&:hover': {
bgcolor: 'transparent'
},
bgcolor: 'transparent'
bgcolor: theme.palette.primary.light
}
}
})
}}
@@ -269,30 +277,25 @@ const NavCollapse = ({ menu, level, parentId, setSelectedItems, selectedItems, s
minWidth: 28,
// color: selected === menu.id ? 'primary.main' : textColor,
// color: selected === menu.id ? textColor : textColor,
// bgcolor:'white',
// color:'white',
color: 'white',
...(!drawerOpen && {
borderRadius: 1.5,
width: 36,
height: 36,
alignItems: 'center',
justifyContent: 'center',
color: 'white',
'&:hover': {
// bgcolor: theme.palette.mode === ThemeMode.DARK ? 'secondary.light' : 'secondary.lighter'
bgcolor: '#7b1fa2',
color: 'white'
bgcolor: theme.palette.primary.light
}
}),
...(!drawerOpen &&
selected === menu.id && {
// bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.900' : 'primary.lighter',
// bgcolor:'white',
bgcolor: '#7b1fa2',
bgcolor: theme.palette.primary.lighter,
color: theme.palette.primary.main,
'&:hover': {
// bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.darker' : 'primary.lighter'
bgcolor: '#7b1fa2'
// color:'white'
bgcolor: theme.palette.primary.lighter
}
})
}}

View File

@@ -78,7 +78,7 @@ const NavItem = ({ item, level }) => {
{...listItemProps}
disabled={item.disabled}
selected={isSelected}
onMouseEnter={(e, val) => {
onMouseEnter={(e) => {
console.log(e);
}}
onMouseLeave={() => {
@@ -104,18 +104,15 @@ const NavItem = ({ item, level }) => {
}
}),
...(!drawerOpen && {
bgcolor: '#662582',
bgcolor: theme.palette.primary.main,
'&:hover': {
// bgcolor: 'transparent',
// bgcolor:'#7b1fa2'
bgcolor: '#662582'
bgcolor: theme.palette.primary.light
},
'&.Mui-selected': {
'&:hover': {
bgcolor: 'transparent'
bgcolor: 'white'
},
bgcolor: 'transparent'
// bgcolor:'#7b1fa2'
bgcolor: 'white'
}
})
}}
@@ -133,15 +130,13 @@ const NavItem = ({ item, level }) => {
color: isSelected ? iconSelectedColor : textColor,
...(!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'
// bgcolor: theme.palette.primary.light
}
}),
...(!drawerOpen &&
@@ -149,7 +144,8 @@ const NavItem = ({ item, level }) => {
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.900' : 'primary.lighter',
'&:hover': {
bgcolor: theme.palette.mode === ThemeMode.DARK ? 'primary.darker' : 'primary.lighter'
}
},
borderRadius: 2
})
}}
>
@@ -164,7 +160,7 @@ const NavItem = ({ item, level }) => {
// sx={{ color: isSelected ? iconSelectedColor : textColor }}
sx={{
color: isSelected && !drawerOpen ? theme.palette.primary.main : !isSelected ? 'white' : theme.palette.primary.main,
bgcolor: isSelected && !drawerOpen ? theme.palette.primary.lighter : 'none',
bgcolor: isSelected && theme.palette.primary.lighter,
padding: isSelected && !drawerOpen ? 0.8 : 'none',
borderRadius: isSelected && !drawerOpen ? 2 : 'none',
ml: isSelected && !drawerOpen ? 0.5 : 'none'

View File

@@ -5,53 +5,54 @@ import { useState } from 'react';
import { List, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
// assets
import { EditOutlined, ProfileOutlined, LogoutOutlined, UserOutlined, WalletOutlined,CommentOutlined } from '@ant-design/icons';
import { EditOutlined, ProfileOutlined, LogoutOutlined, UserOutlined, WalletOutlined, CommentOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router';
// ==============================|| HEADER PROFILE - PROFILE TAB ||============================== //
const ProfileTab = ({ handleLogout }) => {
const [selectedIndex, setSelectedIndex] = useState(0);
const navigate=useNavigate();
const navigate = useNavigate();
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
if(index == 1){
if (index == 1) {
navigate('/accountsettings');
}
};
return (
<List component="nav" sx={{ p: 0, '& .MuiListItemIcon-root': { minWidth: 32 } }}>
<ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 0)}>
{/* <ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 0)}>
<ListItemIcon>
<EditOutlined />
</ListItemIcon>
<ListItemText primary="Edit Profile" />
</ListItemButton>
<ListItemButton selected={selectedIndex === 1} onClick={(event) => handleListItemClick(event, 1)}>
</ListItemButton> */}
<ListItemButton selected={selectedIndex === 0} onClick={(event) => handleListItemClick(event, 1)}>
<ListItemIcon>
<UserOutlined />
</ListItemIcon>
<ListItemText primary="View Profile" />
</ListItemButton>
<ListItemButton selected={selectedIndex === 2} onClick={(event) => handleListItemClick(event, 2)}>
{/* <ListItemButton selected={selectedIndex === 1} onClick={(event) => handleListItemClick(event, 2)}>
<ListItemIcon>
<CommentOutlined />
<CommentOutlined />
</ListItemIcon>
<ListItemText primary="Support Ticket" />
</ListItemButton>
</ListItemButton> */}
{/* <ListItemButton selected={selectedIndex === 4} onClick={(event) => handleListItemClick(event, 4)}>
<ListItemIcon>
<WalletOutlined />
</ListItemIcon>
<ListItemText primary="Billing" />
</ListItemButton> */}
<ListItemButton selected={selectedIndex === 3}
onClick={handleLogout}
// onClick={()=>{
// navigate('/login')
// }}
<ListItemButton
selected={selectedIndex === 1}
onClick={handleLogout}
// onClick={()=>{
// navigate('/login')
// }}
>
<ListItemIcon>
<LogoutOutlined />

View File

@@ -71,7 +71,7 @@ const HeaderContent = () => {
<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('fullname') || ''}
{localStorage.getItem('tenantname') || ''}
</Typography>
{matchesXs && <Box sx={{ width: '100%', ml: 1 }} />}
<Stack direction={'row'} spacing={2}>
@@ -169,7 +169,7 @@ const HeaderContent = () => {
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
<TbBoxMultiple1 />
</ListItemIcon>
<Typography color="textPrimary">Create Order</Typography>
<Typography color="textPrimary"> Orders</Typography>
</Grid>
}
/>
@@ -178,7 +178,7 @@ const HeaderContent = () => {
selected={location.pathname === 'nearle/orders/create'}
onClick={() => {
// console.log(const location = useLocation();)
navigate('nearle/orders/createorders');
navigate('nearle/orders/create/grouporders');
handleClickAway();
}}
>
@@ -188,7 +188,7 @@ const HeaderContent = () => {
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
<GrMultiple />
</ListItemIcon>
<Typography color="textPrimary">Create Group Order</Typography>
<Typography color="textPrimary"> Group Orders</Typography>
</Grid>
}
/>
@@ -224,7 +224,7 @@ const HeaderContent = () => {
<ListItemIcon sx={{ mr: 1, fontSize: '20px' }}>
<TbUserEdit />
</ListItemIcon>
<Typography color="textPrimary">Create Customer</Typography>
<Typography color="textPrimary"> Customers</Typography>
</Grid>
}
/>
@@ -273,7 +273,7 @@ const HeaderContent = () => {
</Popper>
</Box>
<Notification />
{/* <Notification /> */}
{/* <Message /> */}
{/* {!matchesXs && <Profile />}

View File

@@ -23,6 +23,7 @@ import {
SettingOutlined
} from '@ant-design/icons';
import { Path } from 'leaflet';
import { LocationOnOutlined } from '@mui/icons-material';
// icons
const icons = {
@@ -56,12 +57,19 @@ const nearle = {
icon: AiOutlineDashboard
},
{
id: 'customers',
title: <FormattedMessage id="Customers" />,
id: 'locations',
title: <FormattedMessage id="Locations" />,
type: 'item',
url: '/nearle/customers',
icon: icons.UserOutlined
url: '/nearle/locations',
icon: LocationOnOutlined
},
// {
// id: 'customers',
// title: <FormattedMessage id="Customers" />,
// type: 'item',
// url: '/nearle/customers',
// icon: icons.UserOutlined
// },
{
id: 'reports',
title: <FormattedMessage id="Reports" />,

View File

@@ -1,6 +1,28 @@
import axios from 'axios';
const tenid = localStorage.getItem('tenantid');
export const fetchOrders = async ({ pageParam = 1, queryKey }) => {
const [, { tenantId, locationId, status, startdate, enddate, searchword, rowsPerPage }] = queryKey;
const url =
`${process.env.REACT_APP_URL}/orders/tenant/getorders/` +
`?tenantid=${tenantId}` +
`&locationid=${locationId}` +
`&status=${status}` +
`&fromdate=${startdate}` +
`&todate=${enddate}` +
`&pageno=${pageParam}` + // 👈 pageParam comes from TanStack
`&pagesize=${rowsPerPage}` +
`&keyword=${searchword}`;
const res = await axios.get(url);
return {
details: res.data.details,
nextPage: res.data.details.length === rowsPerPage ? pageParam + 1 : undefined
// 👈 API must return this flag
};
};
// ==============================|| fetchOrderSummary (orders)||============================== //
export const fetchOrderSummary = async () => {
const response = await axios.get(`${process.env.REACT_APP_URL}/orders/getordersummary`);
@@ -38,6 +60,21 @@ export const fetchDeliveryLocationSummary = async () => {
return response.data.details;
};
// ==============================|| getorders (Locations)||============================== //
// fetchOrders.js
export const fetchOrders1 = async ({ pageParam = 1, queryKey }) => {
const [_key, tenantid, locationid, status, startdate, enddate, searchword, rowsPerPage] = queryKey;
const res = await axios.get(
`${process.env.REACT_APP_URL}/orders/tenant/getorders/?tenantid=${tenantid}&locationid=${locationid}&status=${status}&fromdate=${startdate}&todate=${enddate}&pageno=${pageParam}&pagesize=${rowsPerPage}&keyword=${searchword}`
);
return {
details: res.data.details,
nextPage: res.data.details.length === rowsPerPage ? pageParam + 1 : undefined
};
};
// ==============================|| fetchAllTenants (clients)||============================== //
@@ -73,7 +110,7 @@ export const fetchCustomersListBySearch = async ({ queryKey }) => {
console.log('fetchCustomersListBySearch', response.data.details);
return response.data.details;
};
// ==============================|| fetchOrdersSummary (orders summary)||============================== //
// ==============================|| fetchOrdersSummary (rider summary)||============================== //
export const fetchOrdersSummary = async ({ queryKey }) => {
console.log('queryKey for fetchOrdersSummary', queryKey);
const [startdate, enddate] = queryKey;
@@ -95,6 +132,17 @@ export const getreportlocationsummary = async ({ queryKey }) => {
return response.data.details;
};
// ==============================|| getriderlocationreportsummary (orders summary)||============================== //
export const getriderlocationreportsummary = async ({ queryKey }) => {
console.log('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);
return response.data.details;
};
// ==============================|| fetchLocations (orders summary))||============================== //
export const fetchLocations = async () => {
@@ -107,6 +155,19 @@ export const fetchLocations = async () => {
return updatedLocations;
};
// ==============================|| gettenantlocations (orders summary))||============================== //
export const gettenantlocations = async ({ queryKey }) => {
const [, searchLocation] = queryKey;
try {
const response = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations?tenantid=${tenid}&keyword=${searchLocation}`);
return response.data?.details || []; // safe fallback
} catch (error) {
console.error('Error fetching tenant locations:', error);
return error.message;
}
};
// ==============================|| fetchDeliverySummary (orders summary)||============================== //
export const fetchDeliverySummary = async ({ queryKey }) => {
const [, startdate, enddate, currentStatus, locationId] = queryKey;
@@ -133,11 +194,9 @@ export const fetchAppLocations = async () => {
export const fetchRidersSummary = async ({ queryKey }) => {
console.log('queryKey for fetchRidersSummary', queryKey);
const [id, startdate, enddate] = queryKey;
const [tenantid, startdate, enddate] = queryKey;
const response = await axios.get(
id == -1
? `${process.env.REACT_APP_URL}/deliveries/getridersummary/?&fromdate=${startdate}&todate=${enddate}`
: `${process.env.REACT_APP_URL}/deliveries/getridersummary/?applocationid=${id}&fromdate=${startdate}&todate=${enddate}`
`${process.env.REACT_APP_URL}/deliveries/getridersummary/?tenantid=${tenantid}&fromdate=${startdate}&todate=${enddate}`
);
console.log('fetchRidersSummary', response.data.details);
@@ -146,7 +205,7 @@ export const fetchRidersSummary = async ({ queryKey }) => {
// ==============================|| fetchorderdetails (orders detail)||============================== //
export const fetchorderdetails = async ({ pageParam = 0, queryKey }) => {
const [startdate, enddate, currentStatus, locationId] = queryKey;
const [startdate, enddate, currentStatus, locationId, searchword] = queryKey;
const limit = 10;
const pageno = pageParam + 1; // ✅ increment page by 1 each time
@@ -156,7 +215,7 @@ export const fetchorderdetails = async ({ pageParam = 0, queryKey }) => {
process.env.REACT_APP_URL
}/deliveries/getdeliveries/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}&status=${
currentStatus === 'All' ? '' : currentStatus
}&pageno=${pageno}&pagesize=${limit}`
}&pageno=${pageno}&pagesize=${limit}&keyword=${searchword}`
);
const details = response.data.details || [];
@@ -206,3 +265,14 @@ export const fetchCount = async ({ queryKey }) => {
return calculateOrderCounts();
};
// ==============================|| fetchRidersLogs (RiderLogs)||============================== //
export const fetchRidersLogs = async ({ queryKey }) => {
const [tenantid, startdate] = queryKey;
const riderLogsResponse = await axios.get(
`${process.env.REACT_APP_URL}/partners/getriderlogs/?tenantid=${tenantid}&fromdate=${startdate || ''}`
);
console.log('fetchRidersLogs', riderLogsResponse.data.details);
return riderLogsResponse.data.details;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
import React from 'react';
import { Box, useTheme } from '@mui/material';
import ResponsiveLocationDrawer from './ResponsiveLocationDrawer';
const Locations = () => {
const theme = useTheme();
return (
<Box
sx={{
width: '100%',
height: 'calc(100vh - 144px)', // below global header
overflow: 'hidden',
position: 'relative',
border: '0.1px solid',
borderColor: theme.palette.secondary.light
}}
>
<ResponsiveLocationDrawer />
</Box>
);
};
export default Locations;

View File

@@ -0,0 +1,596 @@
import React, { useState, useEffect, Fragment, useRef } from 'react';
import {
Box,
Drawer,
IconButton,
Toolbar,
Typography,
AppBar,
useMediaQuery,
Divider,
List,
ListItem,
ListItemText,
useTheme,
ListItemAvatar,
Avatar,
Tooltip,
TableCell,
Chip,
Stack,
TableRow,
TableBody,
TableHead,
Table,
TableContainer,
Tabs,
Tab,
CircularProgress
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import SearchBar from 'components/nearle_components/SearchBar';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import { fetchOrders1, gettenantlocations } from '../api/api';
import Loader from 'components/Loader';
import CircularLoader from 'components/nearle_components/CircularLoader';
import { Empty, Skeleton } from 'antd';
import MainCard from 'components/MainCard';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import { CancelOutlined, CheckCircleOutline } from '@mui/icons-material';
import axios from 'axios';
import dayjs from 'dayjs';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
const drawerWidth = 300;
const ResponsiveLocationDrawer = () => {
const loadMoreRef = useRef();
const containerRef = useRef();
const theme = useTheme();
const tenantid = localStorage.getItem('tenantid');
const isDesktop = useMediaQuery('(min-width:900px)');
const [open, setOpen] = useState(false);
const [selectedLocation, setSelectedLocation] = useState(null);
const [currentStatus, setCurrentStatus] = useState('created');
const [tabvalue, setTabvalue] = useState(0);
const [createdLenght, setCreatedLenght] = useState();
const [pendingLenght, setPendingLenght] = useState();
const [deliveredlenght, setDeliveredlenght] = useState();
const [cancelledLenght, setCancelledLenght] = useState();
const [loading, setLoading] = useState(false);
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const [pageCount, setPageCount] = React.useState(0);
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
const [searchLocation, setSearchLocation] = useState('');
const [debouncedSearchLocation, setDebouncedSearchLocation] = useState('');
const [searchword, setSearchword] = useState('');
const [debouncedSearchword, setDebouncedSearchword] = useState('');
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchLocation(searchLocation);
}, 400);
return () => clearTimeout(handler);
}, [searchLocation]);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchword(searchword);
}, 400);
return () => clearTimeout(handler);
}, [searchword]);
const statusMap = [
{
label: 'Created',
value: 'created',
count: createdLenght,
icon: <AccessTimeIcon color="primary" fontSize="small" />
},
{
label: 'Pending',
value: 'pending',
count: pendingLenght,
icon: <LocalShippingOutlinedIcon color="primary" fontSize="small" />
},
{
label: 'Delivered',
value: 'delivered',
count: deliveredlenght,
icon: <CheckCircleOutline color="primary" fontSize="small" />
},
{
label: 'Cancelled',
value: 'cancelled',
count: cancelledLenght,
icon: <CancelOutlined color="primary" fontSize="small" />
}
];
const handleChangetab = (e, i) => {
setSearchword('');
setRowsPerPage(10);
setTabvalue(i);
setCurrentStatus(statusMap[i].value);
setPage(0);
};
const {
data: locations,
isLoading: locationIsLoading,
isError: locationIsError,
error: locationError
} = useQuery({
queryKey: ['locations', debouncedSearchLocation],
queryFn: gettenantlocations
});
useEffect(() => {
if (!searchLocation) locations?.length > 0 ? setSelectedLocation(locations[0]) : null;
}, [locations]);
const {
data: ordersData,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
error
} = useInfiniteQuery({
queryKey: [
'orders',
tenantid,
selectedLocation?.locationid ?? null, // stable
currentStatus,
startdate,
enddate,
debouncedSearchLocation,
rowsPerPage
],
queryFn: fetchOrders1,
getNextPageParam: (lastPage) => lastPage.nextPage
});
const rows = ordersData?.pages?.flatMap((page) => page.details) || [];
useEffect(() => {
if (!hasNextPage) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
fetchNextPage();
}
},
{
root: document.querySelector('.MuiTableContainer-root'), // 👈 or explicitly TableContainer
rootMargin: '0px',
threshold: 1.0
}
);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
return () => {
if (loadMoreRef.current) observer.unobserve(loadMoreRef.current);
};
}, [hasNextPage, fetchNextPage]);
const handleScroll = (event) => {
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
if (scrollTop + clientHeight >= scrollHeight - 50) {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}
};
const fetchorderscount = async () => {
setLoading(true);
try {
await axios
.get(
`${process.env.REACT_APP_URL}/orders/getordersummary/?tenantid=${tenantid}&locationid=${selectedLocation?.locationid}&fromdate=${startdate}&todate=${enddate}`
)
.then((res) => {
console.log('fetchorderscount', res.data.details);
setCreatedLenght(res.data.details.created);
setPendingLenght(res.data.details.pending);
setDeliveredlenght(res.data.details.delivered);
setCancelledLenght(res.data.details.cancelled);
tabvalue === 0 && setPageCount(res.data.details.created);
tabvalue === 1 && setPageCount(res.data.details.pending);
tabvalue === 2 && setPageCount(res.data.details.delivered);
tabvalue === 3 && setPageCount(res.data.details.cancelled);
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
} catch (err) {
console.log(err);
setLoading(false);
}
};
useEffect(() => {
fetchorderscount();
}, [currentStatus, selectedLocation, startdate, enddate]);
useEffect(() => {
setOpen(isDesktop);
}, [isDesktop]);
const toggleDrawer = () => setOpen(!open);
const errMessage = locationIsError ? `${locationError.message}` : null;
useEffect(() => {
errMessage && console.log(errMessage);
}, [errMessage]);
return (
<React.Fragment>
{locationIsLoading && (
<>
<Loader /> <CircularLoader />
</>
)}
<Box sx={{ display: 'flex', width: '100%', height: '100%', position: 'relative' }}>
{/* ---------------- LOCAL DRAWER ---------------- */}
<Drawer
variant={isDesktop ? 'persistent' : 'temporary'}
open={open}
onClose={() => !isDesktop && toggleDrawer()}
ModalProps={{ keepMounted: true }}
sx={{
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
position: 'absolute',
left: 0,
top: 0,
height: '100%',
overflowY: 'auto',
transition: 'transform 0.35s ease-in-out',
zIndex: 10,
/* vertical scrollbar */
'&::-webkit-scrollbar:vertical': {
width: '7px',
opacity: 0,
transition: 'opacity 0.3s'
},
/* horizontal scrollbar */
'&::-webkit-scrollbar:horizontal': {
height: '6px', // thinner horizontal bar
opacity: 0,
transition: 'opacity 0.3s'
},
/* show scrollbar when hovering drawer */
'&:hover::-webkit-scrollbar': {
opacity: 1
},
/* thumb styling */
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main,
borderRadius: '8px'
},
'&::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.dark
},
/* track styling */
'&::-webkit-scrollbar-track': {
backgroundColor: theme.palette.primary.lighter
}
}
}}
>
<Box sx={{ position: 'sticky', top: 0, zIndex: 11, border: 'none' }}>
<SearchBar
value={searchLocation}
placeholder="Search Location"
onChange={(e) => setSearchLocation(e.target.value)}
sx={{
width: 'auto',
height: 60,
bgcolor: theme.palette.primary.lighter,
'& .MuiOutlinedInput-notchedOutline': {
border: 'none',
borderBottom: '1px solid ',
borderColor: theme.palette.secondary.light
}
}}
/>
</Box>
<List sx={{ mt: -1 }}>
{locations?.map((row, index) => (
<React.Fragment key={index}>
<ListItem
sx={{
cursor: 'pointer',
bgcolor: row.locationid == selectedLocation?.locationid ? theme.palette.secondary[200] : 'none',
'&:hover': {
bgcolor: theme.palette.secondary.lighter
}
}}
onClick={() => {
setSelectedLocation(row);
}}
>
<ListItemAvatar>
<Avatar
sx={{
bgcolor: 'primary.main', // background color
color: 'white' // text color
}}
>
{row.locationname[0].toUpperCase()}
</Avatar>{' '}
</ListItemAvatar>
<ListItemText primary={row.locationname} secondary={row.suburb} />
</ListItem>
<Divider />
</React.Fragment>
))}
</List>
</Drawer>
{/* -------------- LOCAL PAGE APPBAR -------------- */}
<AppBar
elevation={0}
position="absolute"
sx={{
top: 0,
left: open && isDesktop ? `${drawerWidth}px` : 0,
width: open && isDesktop ? `calc(100% - ${drawerWidth}px)` : '100%',
transition: 'left 0.3s ease, width 0.3s ease',
zIndex: 1100, // BELOW drawer, ABOVE content
backgroundColor: theme.palette.primary.lighter
}}
>
<Toolbar>
<Stack
sx={{ width: '100%' }}
display={'flex'}
flexDirection={'row'}
alignItems={'center'}
justifyContent={'space-between'}
flexWrap={'wrap'}
>
<Stack display={'flex'} flexDirection={'row'} alignItems={'center'}>
<IconButton color="primary" onClick={toggleDrawer} sx={{ mr: 1 }}>
<MenuIcon />
</IconButton>
<Typography variant="h5" color={'primary'} sx={{ whiteSpace: 'nowrap', ml: 2 }}>
{selectedLocation?.locationname}
</Typography>
</Stack>
<Stack flexGrow={1} sx={{ mx: { xs: 0, custom600: 3 } }}>
<SearchBar
value={searchword}
placeholder={'Search Order Details'}
onChange={(e) => setSearchword(e.target.value)}
sx={{
width: 'auto',
height: 40,
bgcolor: theme.palette.primary.lighter,
'& .MuiOutlinedInput-notchedOutline': {
border: 'none',
borderBottom: '1px solid ',
borderColor: theme.palette.secondary.light
}
}}
/>
</Stack>
</Stack>
</Toolbar>
</AppBar>
{/* ---------------- PAGE SCROLLABLE CONTENT ---------------- */}
<Box
sx={{
flexGrow: 1,
overflow: 'auto',
pt: '64px', // Height of AppBar
pl: isDesktop && open ? `${drawerWidth}px` : 0,
transition: 'padding-left 0.3s ease',
mt: -1
}}
>
<Stack
display={'flex'}
flexDirection={'row'}
justifyContent={'space-between'}
alignItems={'center'}
flexWrap={'wrap-reverse'}
gap={2}
sx={{
border: '1px solid ',
borderBottom: 0,
borderColor: 'bg.main',
p: 1.5
}}
>
{/* Tabs Wrapper */}
<Tabs value={tabvalue} onChange={handleChangetab} variant="scrollable" scrollButtons="auto" allowScrollButtonsMobile>
{statusMap.map((item, index) => (
<Tab
key={index}
label={
<Stack direction="row" alignItems="center" spacing={1}>
{item.icon}
<span>{item.label}</span>
<Chip label={item.count} color="primary" variant="light" size="small" />
</Stack>
}
/>
))}
</Tabs>
</Stack>
<MainCard
content={false}
sx={{
overflow: 'hidden',
height: 'calc(100vh - 200px)', // adjust as needed
display: 'flex',
flexDirection: 'column'
}}
>
<Fragment>
{/* Scrollable table container */}
<TableContainer
onScroll={handleScroll}
ref={containerRef}
sx={{
width: '100%',
flex: 1,
overflow: 'auto',
borderBottom: 1,
maxHeight: 'calc(100vh - 225px)',
borderColor: 'divider',
'&::-webkit-scrollbar': { width: '12px' },
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main,
borderRadius: '8px'
},
'&::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.dark
},
'&::-webkit-scrollbar-track': {
backgroundColor: theme.palette.primary.lighter
}
}}
>
<Table stickyHeader>
{/* HEADER */}
<TableHead>
<TableRow>
<TableCell sx={{ backgroundColor: theme.palette.secondary.light, position: 'sticky !important' }}>S.No</TableCell>
<TableCell sx={{ backgroundColor: theme.palette.secondary.light, position: 'sticky !important' }}>Orders</TableCell>
<TableCell sx={{ backgroundColor: theme.palette.secondary.light, position: 'sticky !important' }}>Pickup</TableCell>
<TableCell sx={{ backgroundColor: theme.palette.secondary.light, position: 'sticky !important' }}>Drop</TableCell>
<TableCell sx={{ backgroundColor: theme.palette.secondary.light, position: 'sticky !important' }}>Notes</TableCell>
<TableCell sx={{ backgroundColor: theme.palette.secondary.light, position: 'sticky !important' }}>Status</TableCell>
</TableRow>
</TableHead>
{/* BODY */}
<TableBody>
{/* LOADING STATE */}
{loading &&
[...Array(10)].map((_, index) => (
<TableRow key={index}>
{[...Array(6)].map((__, i) => (
<TableCell key={i}>
<Skeleton animation="wave" />
</TableCell>
))}
</TableRow>
))}
{/* EMPTY STATE */}
{!loading && rows?.length === 0 && (
<TableRow>
<TableCell colSpan={6} sx={{ minWidth: '100%', height: 500 }} align="center">
<Empty description={'No Orders'} />
</TableCell>
</TableRow>
)}
{/* DATA ROWS */}
{!loading &&
rows?.map((row, index) => (
<TableRow key={index} sx={{ cursor: 'pointer' }}>
<TableCell>{page * rowsPerPage + index + 1}</TableCell>
{/* Order Info */}
<TableCell>
<Typography variant="body2" noWrap>
{row.orderid}
</Typography>
<Typography variant="caption" noWrap>
{dayjs(row.deliverydate).utc().format('DD/MM/YYYY')}
</Typography>
<Typography variant="caption" noWrap>
{dayjs(row.deliverydate).utc().format('hh:mm A')}
</Typography>
</TableCell>
{/* Pickup */}
<TableCell>
<Stack direction="row" spacing={1}>
<Avatar sx={{ width: 25, height: 25 }} />
<Stack>
<Typography variant="caption">{row.pickupcustomer}</Typography>
<Typography variant="caption">{row.pickupcontactno}</Typography>
<Tooltip title={row.pickupaddress}>
<Typography variant="caption">{row.pickupsuburb || row.pickupaddress.slice(0, 20)}</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
{/* Drop */}
<TableCell>
<Stack direction="row" spacing={1}>
<Avatar sx={{ width: 25, height: 25 }} />
<Stack>
<Typography variant="caption">{row.deliverycustomer}</Typography>
<Typography variant="caption">{row.deliverycontactno}</Typography>
<Tooltip title={row.deliveryaddress}>
<Typography variant="caption">{row.deliverysuburb || row.deliveryaddress.slice(0, 20)}</Typography>
</Tooltip>
</Stack>
</Stack>
</TableCell>
{/* Notes */}
<TableCell>{row.ordernotes}</TableCell>
{/* Status */}
<TableCell>
<Stack direction="row" spacing={1}>
{row.orderstatus === 'pending' && <Chip label="Pending" color="warning" size="small" />}
{row.orderstatus === 'confirmed' && <Chip label="Confirmed" color="success" size="small" />}
{row.orderstatus === 'cancelled' && <Chip label="Cancelled" color="error" size="small" />}
{row.orderstatus === 'delivered' && <Chip label="Completed" color="primary" size="small" />}
{row.orderstatus === 'processing' && <Chip label="Processing" color="primary" size="small" />}
{row.orderstatus === 'ready' && <Chip label="Accepted" color="info" size="small" />}
{row.orderstatus === 'active' && <Chip label="Picked" color="info" size="small" />}
{row.orderstatus === 'closed' && <Chip label="Closed" color="info" size="small" />}
{row.orderstatus === 'created' && <Chip label="Created" color="secondary" size="small" />}
</Stack>
</TableCell>
</TableRow>
))}
{rows?.length != 0 && (
<TableRow>
<TableCell colSpan={6} rowSpan={3}>
<div ref={loadMoreRef} style={{ height: 40, textAlign: 'center' }}>
{isFetchingNextPage ? <CircularProgress /> : hasNextPage ? <CircularProgress /> : 'No More Orders'}
</div>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</Fragment>
</MainCard>
</Box>
</Box>
</React.Fragment>
);
};
export default ResponsiveLocationDrawer;

View File

@@ -132,6 +132,7 @@ const Login = () => {
});
setUsername('');
setPassword('');
localStorage.setItem('tenantname', res.data.details.tenantname);
localStorage.setItem('firstname', res.data.details.tenantname);
localStorage.setItem('authname', res.data.details.authname);
localStorage.setItem('appuserid', res.data.details.userid);

View File

@@ -0,0 +1,78 @@
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
// distance function (same)
function distance(lat1, lng1, lat2, lng2) {
const R = 6371;
const dLat = (lat2 - lat1) * (Math.PI / 180);
const dLng = (lng2 - lng1) * (Math.PI / 180);
const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLng / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
const center = [11.015181, 76.953682];
const riders = [
{ id: 1, lat: 11.04362, lng: 76.924667 },
{ id: 2, lat: 11.00988, lng: 76.949966 },
{ id: 3, lat: 11.020983, lng: 76.966331 }
];
export default function RidersPinPointOSM() {
const sortedRiders = riders
.map((r) => ({
...r,
distance: distance(center[0], center[1], r.lat, r.lng)
}))
.sort((a, b) => a.distance - b.distance);
// purple center marker
const centerIcon = L.icon({
iconUrl: 'https://maps.google.com/mapfiles/ms/icons/purple-dot.png',
iconSize: [32, 32]
});
// basic numbered marker icon
const createMarkerIcon = (number) =>
L.divIcon({
className: 'custom-marker',
html: `
<div style="
background:#007bff;
color:white;
width:28px;
height:28px;
border-radius:50%;
display:flex;
align-items:center;
justify-content:center;
font-weight:bold;
border:2px solid white;
">
${number}
</div>
`,
iconSize: [30, 30],
iconAnchor: [15, 15]
});
return (
<MapContainer center={center} zoom={14} style={{ height: '300px', width: '100%' }}>
{/* OSM tiles */}
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
{/* Purple center marker */}
<Marker position={center} icon={centerIcon} />
{/* Sorted rider markers */}
{sortedRiders.map((r, index) => (
<Marker key={r.id} position={[r.lat, r.lng]} icon={createMarkerIcon(index + 1)}>
<Popup>Rider {index + 1}</Popup>
</Marker>
))}
</MapContainer>
);
}

View File

@@ -940,7 +940,7 @@ const Createorder1 = () => {
sendnotifications();
}
navigate('/orders');
navigate('/nearle/orders');
} else {
opentoast('Something went wrong, Cannot create order', 'warning');
}

View File

@@ -16,6 +16,7 @@ var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router';
import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api';
import {
FormControl,
@@ -43,9 +44,12 @@ import {
TableBody,
TableRow,
Paper,
TableHead
TableHead,
Box
} from '@mui/material';
import CircularLoader from 'components/nearle_components/CircularLoader';
// import RidersPinPointOSM from './RidersPinPointOSM';
import RidersPinPoint from './ridersPinPoint';
const MultipleOrders = () => {
const navigate = useNavigate();
@@ -62,7 +66,7 @@ const MultipleOrders = () => {
const [basePrice, setBasePrice] = useState(0);
const [pricePerKm, setPricePerKm] = useState(0);
const [minKm, setMinKm] = useState(0);
const [pickCust, setPickCust] = useState({});
const [pickCust, setPickCust] = useState(null);
const [dropCust, setDropCust] = useState([]);
const [isCustomerOpen, setIsCustomerOpen] = useState(false);
const [searchCustList, setSearchCustList] = useState('');
@@ -77,9 +81,10 @@ const MultipleOrders = () => {
const [totaldist, settotaldist] = useState(0);
const [totalAmt, settotalAmt] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [showMap, setShowMap] = useState(false);
useEffect(() => {
console.log('dropCust', dropCust);
dropCust && console.log('dropCust', dropCust);
}, [dropCust]);
// =============================================== || opentoast || ===============================================
@@ -180,7 +185,7 @@ const MultipleOrders = () => {
const calculateTotal = () => {
let a1 = 0;
let a2 = 0;
dropCust.map((customer) => {
dropCust?.map((customer) => {
a1 += customer.distance;
a2 += customer.totalcharge;
});
@@ -188,7 +193,7 @@ const MultipleOrders = () => {
settotalAmt(a2);
};
useEffect(() => {
calculateTotal();
dropCust && calculateTotal();
}, [dropCust]);
// ========================================================= || handleCheckboxChange || =========================================================
@@ -360,9 +365,7 @@ const MultipleOrders = () => {
useEffect(() => {
console.log('pickCust', pickCust);
}, [pickCust]);
useEffect(() => {
console.log('dropCust', dropCust);
}, [dropCust]);
// ==================================================== || fetchtenantinfo || ====================================================
const fetchtenantinfo = async () => {
setLoading(true);
@@ -424,9 +427,9 @@ const MultipleOrders = () => {
setLoading(false);
});
};
// =============================================== || createorders || ===============================================
const createorders = async () => {
const arr = dropCust.map((customer) => ({
// =============================================== || creategrouporders || ===============================================
const creategrouporders = async () => {
const arr = dropCust?.map((customer) => ({
applocationid: pickCust.applocationid,
cancellled: '',
// categoryid: +tenant.categoryid,
@@ -500,7 +503,7 @@ const MultipleOrders = () => {
// notifyadmin(admintoken);
sendnotifications();
}
navigate('/orders');
navigate('/nearle/orders');
} else {
opentoast(res.data.message, 'warning');
}
@@ -515,101 +518,93 @@ const MultipleOrders = () => {
}
console.log(arr);
};
return (
<>
{loading && <Loader />}
<Grid
container
spacing={3}
sx={{
position: 'sticky',
top: '60px',
bgcolor: theme.palette.background.default,
zIndex: 100,
display: 'flex',
flexDirection: 'column',
justifyContent: 'start',
alignItems: 'center',
spacing: 5
}}
>
<Grid item xs={12} sx={{ width: '100%', mt: -1 }}>
<Stack direction={'row'} alignItems={'center'} justifyContent={'space-between'}>
<Typography variant="h3"> Group Orders</Typography>
{tenantLocations.length == 1 ? (
<TextField
// disabled={!isAppLocation || !isClient}
variant="outlined"
fullWidth
label={'Business Location'}
value={tenantLocations[0].locationname}
focused
sx={{ width: '350px' }}
InputProps={{
style: { color: theme.palette.primary.main },
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
)
}}
/>
) : (
<Autocomplete
fullWidth
// disabled={!isAppLocation || !isClient}
id="combo-box-demo"
options={tenantLocations || []}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
renderInput={(params) => <TextField {...params} label="Select Business Locations" color="primary" />}
sx={{ width: '350px' }}
onChange={(event, value, reason) => {
if (value) {
console.log('Business Locations', value);
setTid(value.tenantid);
setIsLocation(true);
setPickCust(value);
}
if (reason == 'clear') {
setIsLocation(false);
}
}}
/>
)}
{/* <RidersPinPointOSM /> */}
<Grid container sx={{ mb: 2 }}>
<Grid item xs={12} sm={3} md={6}>
<Stack>
<Typography variant="h3" whiteSpace="nowrap">
Group Orders
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={9} md={6}>
<Stack
sx={{}}
width={'100%'}
direction="row"
alignItems="center"
spacing={2}
justifyContent={'flex-end'}
flexWrap={{ xs: 'wrap', custom550: 'nowrap' }}
gap={2}
>
{/* Business Location */}
<Stack sx={{ width: '100%' }}>
{tenantLocations?.length === 1 ? (
<TextField
label="Business Location"
fullWidth
focused
value={tenantLocations[0]?.locationname}
InputProps={{
style: { color: theme.palette.primary.main },
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
)
}}
/>
) : (
<Autocomplete
fullWidth
options={tenantLocations || []}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})`}
onChange={(event, value, reason) => {
if (value) {
setTid(value.tenantid);
setIsLocation(true);
setPickCust(value);
}
if (reason === 'clear') setIsLocation(false);
}}
renderInput={(params) => <TextField {...params} label="Select Business Location" color="primary" fullWidth />}
/>
)}
</Stack>
{/* ===================================================== || Pickup || ===================================================== */}
<Grid item xs={12} sx={{ width: '100%' }}>
<MainCard
sx={{ height: '100%' }}
title={'Pickup'}
secondary={
<LocalizationProvider dateAdapter={AdapterDayjs} sx={{}}>
{/* Date Picker */}
<Stack sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
format="DD-MM-YYYY"
disablePast
value={dayjs(startdate)}
sx={{ width: 150 }}
onChange={(e) => {
let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd');
console.log('dateres11');
console.log(dateres11);
let diff = dayjs().diff(dayjs(dayjs(e).format('YYYY-MM-DD')), 'd');
if (dateres11 <= 0) {
console.log('startdate', e);
if (diff <= 0) {
setStartdate(e);
let arr = [];
timeslotarr.map((val) => {
timeslotarr.forEach((val) => {
if (dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) {
arr.push(val);
}
});
if (arr[0]) {
setOrderarr([
{
sno: 1,
address: '',
customerid: '',
deliverytime: dayjs(arr[0]) || '',
deliverytime: dayjs(arr[0]),
deliverylocationid: '',
clientname: '',
contactno: '',
@@ -621,160 +616,171 @@ const MultipleOrders = () => {
setOrderarr([]);
}
} else {
setAlertmessage('choose Upcoming Date');
opentoast('choose Upcoming Date', 'warning');
setStartdate(NaN);
}
}}
value={dayjs(startdate)}
sx={{ width: '100%' }}
disablePast
/>
</LocalizationProvider>
}
>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Tenant</TableCell>
<TableCell>Address</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell>{pickCust.locationname}</TableCell>
<TableCell>{pickCust.address}</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</MainCard>
</Stack>
</Stack>
</Grid>
{/* ===================================================== || Drop || ===================================================== */}
<Grid item xs={12} sx={{ width: '100%', mt: -2 }}>
<MainCard
sx={{ height: '100%' }}
title={`Drop (${dropCust?.length || 0})`}
secondary={
</Grid>
{/* ===================================================== || Pickup || ===================================================== */}
{pickCust && (
<TableContainer component={Paper} sx={{ mb: 2 }}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Pickup Location</TableCell>
<TableCell>Address</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell>{pickCust?.locationname}</TableCell>
<TableCell>{pickCust?.address}</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
)}
{/* ===================================================== || Drop || ===================================================== */}
<MainCard
sx={{ height: '100%' }}
title={`Drop (${dropCust?.length || 0})`}
secondary={
<Button
variant="outlined"
size="small"
sx={{
'&:hover': {
bgcolor: theme.palette.primary.main,
color: 'white'
}
}}
onClick={() => {
if (!isLocation) {
opentoast('Select Business Location', 'warning');
} else {
setIsCustomerOpen(true);
setSearchCustList('');
}
}}
>
Select Customers
</Button>
}
>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>S.No</TableCell>
<TableCell>Customer</TableCell>
<TableCell>Address</TableCell>
<TableCell>Kms</TableCell>
<TableCell align="right">Charge</TableCell>
<TableCell>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{!dropCust && (
<TableRow>
<TableCell colSpan={6}>
<Empty description={' Drop Customers Not Selected'} />
</TableCell>
</TableRow>
)}
{dropCust?.map((customer, index) => (
<TableRow key={index}>
<TableCell>{index + 1}</TableCell>
<TableCell>{customer.firstname}</TableCell>
<TableCell>{customer.address}</TableCell>
<TableCell>{customer.distance}</TableCell>
<TableCell align="right">{`${customer.totalcharge}.00`}</TableCell>
<TableCell align="center">
{
<CloseOutlined
style={{ cursor: 'pointer', color: 'red' }}
onClick={(event) => handleCheckboxChange(event, customer)}
/>
}
</TableCell>
</TableRow>
))}
{dropCust?.length != 0 && (
<TableRow>
<TableCell>
<Typography variant="h5">Total</Typography>
</TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Typography variant="h5">{`${totaldist} `}</Typography>
</TableCell>
<TableCell align="right">
<Typography variant="h5"> {`${totalAmt}.00`}</Typography>
</TableCell>
<TableCell></TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</MainCard>
{/* ================================================= || Riders Map || ================================================= */}
{/* {showMap && dropCust.length >= 1 && <RidersPinPoint pickCust={pickCust} dropCust={dropCust} />} */}
{/* ================================================= || Notes || ================================================= */}
{dropCust && (
<MainCard sx={{ mt: 2 }} title={'Notes'}>
<Grid container>
<Grid item xs={12}>
<TextField
focused
id="outlined-multiline-static"
sx={{ width: '100%', height: '100%', mb: 2 }}
multiline
rows={1}
placeholder="Notes"
value={otherinstructions}
onChange={(e) => setOtherinstructions(e.target.value)}
/>
</Grid>
<Stack direction="row" justifyContent={'end'} sx={{ mt: 2, width: '100%' }}>
<Button
disabled={dropCust?.length == 0}
size="medium"
variant="outlined"
size="small"
onClick={() => {
setLoading(true);
setBtnLoading(true);
creategrouporders();
setTimeout(() => {
setLoading(false);
setBtnLoading(false);
}, 2000);
}}
sx={{
'&:hover': {
bgcolor: theme.palette.primary.main,
color: 'white'
}
}}
onClick={() => {
if (!isLocation) {
opentoast('Select Business Location', 'warning');
} else {
setIsCustomerOpen(true);
setSearchCustList('');
transform: 'scale(1.05)',
transition: 'transform 0.3s ease'
}
}}
>
Select Customers
{btnLoading ? <CircularProgress color="primary" size={20} thickness={10} /> : 'Create'}
</Button>
}
>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>S.No</TableCell>
<TableCell>Customer</TableCell>
<TableCell>Address</TableCell>
<TableCell>Kms</TableCell>
<TableCell align="right">Charge</TableCell>
<TableCell>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{dropCust.map((customer, index) => (
<TableRow key={index}>
<TableCell>{index + 1}</TableCell>
<TableCell>{customer.firstname}</TableCell>
<TableCell>{customer.address}</TableCell>
<TableCell>{customer.distance}</TableCell>
<TableCell align="right">{`${customer.totalcharge}.00`}</TableCell>
<TableCell align="center">
{
<CloseOutlined
style={{ cursor: 'pointer', color: 'red' }}
onClick={(event) => handleCheckboxChange(event, customer)}
/>
}
</TableCell>
</TableRow>
))}
{dropCust.length != 0 && (
<TableRow>
<TableCell>
<Typography variant="h5">Total</Typography>
</TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Typography variant="h5">{`${totaldist} `}</Typography>
</TableCell>
<TableCell align="right">
<Typography variant="h5"> {`${totalAmt}.00`}</Typography>
</TableCell>
<TableCell></TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</MainCard>
</Grid>
</Stack>
</Grid>
</MainCard>
)}
<Grid item xs={12} sx={{ width: '100%', mt: -2 }}>
{/* ================================================= || Notes || ================================================= */}
<MainCard sx={{ mt: 2 }} title={'Notes'}>
<Grid container>
<Grid item xs={12}>
<TextField
focused
id="outlined-multiline-static"
sx={{ width: '100%', height: '100%', mb: 2 }}
multiline
rows={1}
placeholder="Notes"
value={otherinstructions}
onChange={(e) => setOtherinstructions(e.target.value)}
/>
</Grid>
<Stack direction="row" justifyContent={'end'} sx={{ mt: 2, width: '100%' }}>
<Button
disabled={dropCust.length == 0}
size="medium"
variant="outlined"
onClick={() => {
setLoading(true);
setBtnLoading(true);
createorders();
setTimeout(() => {
setLoading(false);
setBtnLoading(false);
}, 2000);
}}
sx={{
'&:hover': {
transform: 'scale(1.05)',
transition: 'transform 0.3s ease'
}
}}
>
{btnLoading ? <CircularProgress color="primary" size={20} thickness={10} /> : 'Create'}
</Button>
</Stack>
</Grid>
</MainCard>
</Grid>
</Grid>
{/* ============================================= || saved address Dialog || ============================================= */}
<Dialog
open={isCustomerOpen}
@@ -787,7 +793,7 @@ const MultipleOrders = () => {
{isLoading && <CircularLoader />}
<DialogTitle sx={{ bgcolor: theme.palette.primary.main, color: 'white' }}>
<Stack>
<Typography variant="h4"> {`Select Drop Customers (${dropCust.length || 0})`}</Typography>
<Typography variant="h4"> {`Select Drop Customers (${dropCust?.length || 0})`}</Typography>
<FormControl
sx={{
width: '100%',
@@ -842,7 +848,7 @@ const MultipleOrders = () => {
<FormControlLabel
control={
<Checkbox
checked={dropCust.some((cust) => cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust`
checked={dropCust?.some((cust) => cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust`
onChange={(event) => handleCheckboxChange(event, customer)}
/>
}
@@ -866,19 +872,22 @@ const MultipleOrders = () => {
<Divider />
<DialogActions sx={{ p: 2.5 }}>
<Button
color={dropCust.length !== 0 ? 'primary' : 'error'}
color={dropCust?.length !== 0 ? 'primary' : 'error'}
variant="outlined"
sx={{
'&:hover': {
bgcolor: dropCust.length !== 0 ? theme.palette.primary.main : theme.palette.error.main,
bgcolor: dropCust?.length !== 0 ? theme.palette.primary.main : theme.palette.error.main,
color: 'white'
}
}}
onClick={() => {
setIsCustomerOpen(false);
{
dropCust?.length !== 0 && setShowMap(true);
}
}}
>
{dropCust.length !== 0 ? 'Continue' : 'Close'}
{dropCust?.length !== 0 ? 'Continue' : 'Close'}
</Button>
</DialogActions>
</Dialog>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
import { LoadScriptNext, GoogleMap, Marker } from '@react-google-maps/api';
// distance function
function distance(lat1, lng1, lat2, lng2) {
const R = 6371;
const dLat = (lat2 - lat1) * (Math.PI / 180);
const dLng = (lng2 - lng1) * (Math.PI / 180);
const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLng / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
const containerStyle = {
width: '100%',
height: '300px'
};
export default function RidersPinPoint({ pickCust, dropCust }) {
// Ensure valid lat/lng
const center = pickCust?.latitude && pickCust?.longitude ? { lat: Number(pickCust.latitude), lng: Number(pickCust.longitude) } : null;
// If center missing, don't render map
if (!center) return null;
const sortedRiders = dropCust
?.map((r) => ({
...r,
distance: distance(center.lat, center.lng, Number(r.latitude), Number(r.longitude))
}))
.sort((a, b) => a.distance - b.distance);
return (
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<GoogleMap mapContainerStyle={containerStyle} zoom={11} center={center}>
<Marker position={center} icon={{ url: 'http://maps.google.com/mapfiles/ms/icons/purple-dot.png' }} />
{sortedRiders?.map((r, index) => (
<Marker
key={index}
position={{ lat: Number(r.latitude), lng: Number(r.longitude) }}
label={{
text: (index + 1).toString(),
color: 'white',
fontSize: '14px',
fontWeight: 'bold'
}}
/>
))}
</GoogleMap>
</LoadScriptNext>
);
}

View File

@@ -740,7 +740,7 @@ const Createorder1 = () => {
setLoading(true);
await axios
.post(`${process.env.REACT_APP_URL}/orders/createorders`, arr)
.post(`${process.env.REACT_APP_URL}/orders/create/grouporders`, arr)
.then((res) => {
if (res.data.status) {
enqueueSnackbar('Order Created Successfully', {
@@ -753,7 +753,7 @@ const Createorder1 = () => {
sendnotifications();
}
navigate('/orders');
navigate('/nearle/orders');
} else {
opentoast(res.data.message, 'warning');
}

View File

@@ -6,6 +6,8 @@ import { Empty } from 'antd';
import TaskAltIcon from '@mui/icons-material/TaskAlt';
import MyLocationIcon from '@mui/icons-material/MyLocation';
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
import { enqueueSnackbar } from 'notistack';
import { CalendarMonth } from '@mui/icons-material';
// material-ui
import {
@@ -60,7 +62,8 @@ import MainCard from 'components/MainCard';
import Loader from 'components/Loader';
import { useTheme } from '@mui/material/styles';
import TitleCard from '../titleCard';
import { getreportlocationsummary } from '../api/api';
import { getreportlocationsummary, gettenantlocations } from '../api/api';
import CircularLoader from 'components/nearle_components/CircularLoader';
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
@@ -70,6 +73,14 @@ function formatNumberToRupees(value) {
}).format(value);
}
const opentoast = (message, variant, time) => {
enqueueSnackbar(message, {
variant: variant,
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: time ? time : 1500
});
};
// ==============================|| MUI TABLE - ENHANCED ||============================== //
export default function OrdersReport() {
@@ -95,7 +106,6 @@ export default function OrdersReport() {
const [searchword, setSearchword] = useState('');
const textFieldRef = useRef(null);
const [ridersdata, setRidersdata] = useState(null);
const [tenantLocations, setTenantlocations] = useState([]);
const [selectedLocation, setSelectedLocation] = useState(null);
const [locationId, setLocationId] = useState(0);
@@ -125,18 +135,16 @@ export default function OrdersReport() {
}, []);
// ============================================= || gettenantlocations (branches) || =============================================
const gettenantlocations = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations?tenantid=${tenantid}`);
console.log('gettenantlocations', res.data.details);
setTenantlocations(res.data.details);
} catch (err) {
console.log('gettenantlocations', err);
}
};
useEffect(() => {
gettenantlocations();
}, []);
const {
data: tenantLocations,
isLoading: tenantLocationsIsLoading,
isError: tenantLocationsIsError,
error: tenantLocationsError
} = useQuery({
queryKey: ['tenantlocations'],
queryFn: gettenantlocations
});
// ==============================|| fetchOrdersSummary (orders summary)||============================== //
const {
@@ -196,139 +204,153 @@ export default function OrdersReport() {
calculate();
}, [rows]);
// if (isLoadingReports) return <Loader />;
if (isErrorReports) return 'An error has occurred:(isErrorReports) ' + reportsError.message;
let errormessage = '';
if (isErrorReports && reportsError?.message) {
errormessage = `An error has occurred: (isErrorReports) ${reportsError.message}`;
} else if (tenantLocationsIsError && tenantLocationsError?.message) {
errormessage = `An error has occurred: (tenantLocationsIsError) ${tenantLocationsError.message}`;
}
useEffect(() => {
if (errormessage) {
console.log('errormessage', errormessage);
opentoast(errormessage, 'warning', 2000);
}
}, [errormessage]);
return (
<>
{isLoadingReports && <Loader />}
<TitleCard title="Orders Summary" />{' '}
{(isLoadingReports || tenantLocationsIsLoading) && <Loader />}
{(isLoadingReports || tenantLocationsIsLoading) && <CircularLoader />}
<TitleCard
title="Orders Summary"
secondary={
<Stack>
{startdate && enddate && (
<Stack direction="row" spacing={2} flexWrap={'wrap'} gap={1}>
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" size="small" />
<Chip
label={
<Typography noWrap color="secondary">
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
</Typography>
}
variant="combined"
color="warning"
size="small"
deleteIcon={<CalendarMonth style={{ fontSize: 18 }} />}
onDelete={() => {
setOpen(true);
}}
onClick={() => setOpen(true)}
sx={{ cursor: 'pointer' }}
/>
</Stack>
)}
{(!startdate || !enddate) && (
<>
<Stack direction="row" spacing={2}>
<Chip label="Orders-All" color="primary" variant="light" size="small" />
</Stack>
</>
)}
</Stack>
}
/>{' '}
<MainCard
content={false}
title={
<Stack display={'flex'} flexDirection={'row'} alignItems={'center'} justifyContent={'space-between'} flexWrap={'wrap'} gap={1}>
<Stack>
{startdate && enddate && (
<Stack direction="row" spacing={2} flexWrap={'wrap'} gap={1}>
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" size="small" />
<Chip
label={
<Typography noWrap color="secondary">
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
</Typography>
}
variant="combined"
color="warning"
size="small"
/>
</Stack>
)}
{(!startdate || !enddate) && (
<>
<Stack direction="row" spacing={2}>
<Chip label="Orders-All" color="primary" variant="light" size="small" />
</Stack>
</>
)}
</Stack>
<Stack
sx={{
display: 'flex',
flexDirection: 'row',
gap: 2,
flexWrap: 'wrap',
flexGrow: 1,
justifyContent: 'right'
}}
>
<FormControl sx={{ width: 250 }}>
<OutlinedInput
inputRef={textFieldRef}
aria-describedby="header-search-text"
inputProps={{
'aria-label': 'weight'
}}
sx={{ background: 'white' }}
size="large"
id="header-search"
placeholder="Search (ctrl+k)"
value={searchword}
onChange={(e) => {
setSearchword(e.target.value);
}}
autoComplete="off"
startAdornment={
<InputAdornment position="start" sx={{ mr: -0.5 }}>
<SearchOutlined />
</InputAdornment>
}
endAdornment={
<IconButton
sx={{ visibility: searchword ? 'visible' : 'hidden' }}
onClick={() => {
setSearchword('');
}}
>
<ClearIcon style={{ fontSize: 'medium', color: '#65387A' }} />
<Stack
direction="row"
gap={2}
flexWrap="wrap"
sx={{
alignItems: 'center',
width: '100%',
justifyContent: 'flex-end'
}}
>
{/* Search Input */}
<FormControl sx={{ minWidth: 250, maxWidth: 400, flex: 1 }}>
<OutlinedInput
inputRef={textFieldRef}
id="header-search"
placeholder="Search (Ctrl + K)"
value={searchword}
onChange={(e) => setSearchword(e.target.value)}
autoComplete="off"
size="medium"
sx={{
bgcolor: 'white',
borderRadius: 2
}}
startAdornment={
<InputAdornment position="start">
<SearchOutlined />
</InputAdornment>
}
endAdornment={
<InputAdornment position="end">
<IconButton sx={{ visibility: searchword ? 'visible' : 'hidden' }} onClick={() => setSearchword('')}>
<ClearIcon sx={{ fontSize: 20, color: '#65387A' }} />
</IconButton>
}
/>
</FormControl>
<Stack>
{tenantLocations.length == 1 ? (
</InputAdornment>
}
/>
</FormControl>
{/* Location Input */}
{tenantLocations?.length === 1 ? (
<TextField
variant="outlined"
label="Business Location"
value={tenantLocations[0]?.locationname}
fullWidth
focused
sx={{
minWidth: 250,
maxWidth: 400,
flex: 1,
borderRadius: 2
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
),
sx: { bgcolor: 'white' }
}}
/>
) : (
<Autocomplete
fullWidth
options={tenantLocations || []}
value={selectedLocation}
getOptionLabel={(o) => `${o.locationname} (${o.suburb})`}
onChange={(event, value, reason) => {
setSelectedLocation(value);
setLocationId(value ? value.locationid : 0);
}}
sx={{
minWidth: 250,
maxWidth: 400,
flex: 1
}}
renderInput={(params) => (
<TextField
variant="outlined"
fullWidth
label={'Business Location'}
value={tenantLocations[0].locationname}
focused
sx={{ minWidth: 250 }}
InputProps={{
style: { color: theme.palette.primary.main },
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
)
{...params}
label="Choose Location"
color="primary"
sx={{
bgcolor: 'white',
borderRadius: 2
}}
/>
) : (
<Autocomplete
fullWidth
id="combo-box-demo"
options={tenantLocations || []}
value={selectedLocation}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
sx={{ minWidth: 250 }}
onChange={(event, value, reason) => {
setSelectedLocation(value);
if (value) {
console.log('Business Locations', value);
setLocationId(value.locationid);
}
if (reason == 'clear') {
setLocationId(0);
}
}}
renderInput={(params) => <TextField {...params} label="Choose Location" color="primary" />}
/>
)}
</Stack>
<Tooltip title="Order Filter">
<IconButton
color="secondary"
variant="light"
sx={{
color: 'text.primary',
bgcolor: 'grey.200'
}}
onClick={() => setOpen(true)}
>
<FilterList />
</IconButton>
</Tooltip>
</Stack>
/>
)}
</Stack>
}
>
@@ -336,8 +358,9 @@ export default function OrdersReport() {
sx={{
overflow: 'auto',
'&::-webkit-scrollbar': {
width: '3px',
height: '3px'
width: '12px', // Width of vertical scrollbar
height: '14px', // Height of horizontal scrollbar,
borderRadius: 10
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: '#65387A' // Color of the scrollbar thumb
@@ -357,7 +380,7 @@ export default function OrdersReport() {
Deliveries{' '}
</TableCell>
<TableCell rowSpan={2}> Kilometer</TableCell>
<TableCell rowSpan={2}>Charges </TableCell>
{/* <TableCell rowSpan={2}>Charges </TableCell> */}
<TableCell rowSpan={2}>Amount </TableCell>
<TableCell rowSpan={2}> Action</TableCell>
</TableRow>
@@ -380,10 +403,6 @@ export default function OrdersReport() {
<TableRow
key={row.locationid}
sx={{
bgcolor: openRow === row.locationid ? '#e1bee7' : null,
'&:hover': {
bgcolor: openRow === row.locationid ? '#e1bee7!important' : null
},
cursor: openRow === row.locationid ? 'pointer' : null
}}
>
@@ -418,7 +437,7 @@ export default function OrdersReport() {
/>
</Tooltip>
<br />
{/* <br />
<Tooltip title="Actual kms" placement="bottom">
<Chip
size="small"
@@ -431,10 +450,10 @@ export default function OrdersReport() {
minWidth: 80
}}
/>
</Tooltip>
</Tooltip> */}
</TableCell>
<TableCell align="left">
<Tooltip title="Pay on Delivery" placement="top">
{/* <TableCell align="left"> */}
{/* <Tooltip title="Pay on Delivery" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.payondelivery)}
@@ -450,8 +469,8 @@ export default function OrdersReport() {
}}
/>
</Tooltip>
<br />
<Tooltip title="Pay Later" placement="bottom">
<br /> */}
{/* <Tooltip title="Pay Later" placement="bottom">
<Chip
size="small"
label={formatNumberToRupees(row.paylater)}
@@ -465,8 +484,8 @@ export default function OrdersReport() {
minWidth: 80
}}
/>
</Tooltip>
</TableCell>
</Tooltip> */}
{/* </TableCell> */}
<TableCell align="right">
<Tooltip title="Total Charges" placement="top">
<Chip
@@ -506,9 +525,9 @@ export default function OrdersReport() {
{openRow === row.locationid && (
<TableRow
sx={{
bgcolor: openRow === row.locationid ? '#f3e5f5' : null,
bgcolor: 'white',
'&:hover': {
bgcolor: openRow === row.locationid ? '#f3e5f5!important' : null
bgcolor: 'white!important'
},
cursor: openRow === row.locationid ? 'pointer' : null
}}
@@ -516,35 +535,56 @@ export default function OrdersReport() {
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse in={openRow === row.locationid} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Typography variant="h5" gutterBottom component="div">
Riders Summary
</Typography>
<Table size="small" aria-label="purchases">
<TableHead
sx={{
bgcolor: '#f3e5f5',
bgcolor: 'white',
'&:onhover': {
bgcolor: '#f3e5f5!important'
bgcolor: 'white !important'
}
}}
>
<TableRow>
<TableCell>#</TableCell>
<TableRow
sx={{
bgcolor: openRow === row.locationid ? theme.palette.secondary.lighter : null
}}
>
<TableCell align="center">#</TableCell>
<TableCell>Rider</TableCell>
<TableCell align="left">Deliveries</TableCell>
<TableCell align="left">Assigned</TableCell>
<TableCell align="left">Accepted</TableCell>
<TableCell align="left">Picked</TableCell>
<TableCell align="left">Delivered</TableCell>
<TableCell align="center">kms/Actualkms</TableCell>
<TableCell align="center">POD/PLA</TableCell>
<TableCell align="center">Deliveries</TableCell>
<TableCell align="center">Pending</TableCell>
<TableCell align="center">Assigned</TableCell>
<TableCell align="center">Accepted</TableCell>
<TableCell align="center">Arrived</TableCell>
<TableCell align="center">Picked</TableCell>
<TableCell align="center">Skipped</TableCell>
<TableCell align="center">Delivered</TableCell>
<TableCell align="center">kms</TableCell>
{/* <TableCell align="center">POD/PLA</TableCell> */}
<TableCell align="center">Charges</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableBody
sx={{
bgcolor: 'white',
'&:onhover': {
bgcolor: 'white !important'
}
}}
>
{ridersdata?.map((rider, index) => (
<TableRow key={rider?.tenantname}>
<TableCell padding="none">{index + 1}</TableCell>
<TableRow
key={rider?.tenantname}
// sx={{
// bgcolor: openRow === row.locationid ? '#f3e5f5' : null,
// '&:hover': {
// bgcolor: openRow === row.locationid ? '#f3e5f5!important' : null
// }
// }}
>
<TableCell align="center" padding="none">
{index + 1}
</TableCell>
<TableCell align="left">
<Stack direction="row" sx={{ ml: -2 }}>
{rider?.firstname}
@@ -555,27 +595,30 @@ export default function OrdersReport() {
)}
</Stack>
</TableCell>
<TableCell align="left">{rider?.deliveries}</TableCell>
<TableCell align="left">{rider?.Assigened}</TableCell>
<TableCell align="left">{rider?.Accepted}</TableCell>
<TableCell align="left">{rider?.Picked}</TableCell>
<TableCell align="left">{rider?.delivered}</TableCell>
<TableCell align="center">{rider?.totalorders}</TableCell>
<TableCell align="center">{rider?.pending}</TableCell>
<TableCell align="center">{rider?.assigned}</TableCell>
<TableCell align="center">{rider?.accepted}</TableCell>
<TableCell align="center">{rider?.picked}</TableCell>
<TableCell align="center">{rider?.arrived}</TableCell>
<TableCell align="center">{rider?.skipped}</TableCell>
<TableCell align="center">{rider?.delivered}</TableCell>
<TableCell align="center">
<Tooltip title="kms" placement="top">
<Chip
size="small"
label={rider?.kms}
sx={{
color: '#1976d2',
bgcolor: '#e3f2fd',
mr: 1,
border: '1px solid #1976d2',
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
<Tooltip title="Actual kms" placement="top">
{/* <Tooltip title="kms" placement="top"> */}
<Chip
size="small"
label={rider?.kms}
sx={{
color: '#1976d2',
bgcolor: '#e3f2fd',
mr: 1,
border: '1px solid #1976d2',
cursor: 'pointer',
minWidth: 80
}}
/>
{/* </Tooltip> */}
{/* <Tooltip title="Actual kms" placement="top">
<Chip
size="small"
label={rider?.actualkms}
@@ -587,11 +630,11 @@ export default function OrdersReport() {
minWidth: 80
}}
/>
</Tooltip>
</Tooltip> */}
</TableCell>
<TableCell align="center">
<Tooltip title="Pay on Delivery" placement="top">
{/* <TableCell align="center"> */}
{/* <Tooltip title="Pay on Delivery" placement="top">
<Chip
size="small"
label={formatNumberToRupees(rider?.payondelivery)}
@@ -604,26 +647,26 @@ export default function OrdersReport() {
minWidth: 80
}}
/>
</Tooltip>
<Tooltip title="Paylater" placement="top">
<Chip
size="small"
label={formatNumberToRupees(rider?.Paylater)}
sx={{
color: '#43a047',
bgcolor: '#c8e6c9',
border: '1px solid #43a047',
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</TableCell>
</Tooltip> */}
{/* <Tooltip title="Paylater" placement="top"> */}
{/* <Chip
size="small"
label={formatNumberToRupees(rider?.Paylater)}
sx={{
color: '#43a047',
bgcolor: '#c8e6c9',
border: '1px solid #43a047',
cursor: 'pointer',
minWidth: 80
}}
/> */}
{/* </Tooltip> */}
{/* </TableCell> */}
<TableCell align="center">
<Tooltip title="Amount" placement="top">
<Chip
size="small"
label={formatNumberToRupees(rider?.Deliveryamt)}
label={formatNumberToRupees(rider?.deliveryamt)}
sx={{
color: 'primary.main',
bgcolor: '#e1bee7',

View File

@@ -53,13 +53,14 @@ import {
startOfWeek
// startOfYear,
} from 'date-fns';
import { FilterList } from '@mui/icons-material';
import { CalendarMonth, FilterList } from '@mui/icons-material';
// project imports
import { fetchDeliverySummary, fetchorderdetails } from '../api/api';
import MainCard from 'components/MainCard';
import { CSVExport } from 'components/third-party/ReactTable';
import Loader from 'components/Loader';
import { useDebounce } from 'components/nearle_components/useDebounce';
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
@@ -95,6 +96,7 @@ export default function OrdersDetails() {
const [locationId, setLocationId] = useState(0);
const [locoName, setLocoName] = useState('Select Location');
const debouncedSearch = useDebounce(searchword, 500);
const status = [
{ id: 0, status: 'All', statusLow: 'All' },
{ id: 1, status: 'Pending', statusLow: 'pending' },
@@ -186,15 +188,15 @@ export default function OrdersDetails() {
isFetchingNextPage
// status: rowdataStatus
} = useInfiniteQuery({
queryKey: [startdate, enddate, currentStatus, locationId],
queryKey: [startdate, enddate, currentStatus, locationId, debouncedSearch],
queryFn: fetchorderdetails,
getNextPageParam: (lastPage) => lastPage.nextPage
});
const rows = rowdata?.pages.flatMap((page) => page.details) || [];
useEffect(() => {
if (!hasNextPage) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
@@ -207,9 +209,7 @@ export default function OrdersDetails() {
threshold: 1.0
}
);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
return () => {
if (loadMoreRef.current) observer.unobserve(loadMoreRef.current);
};
@@ -245,6 +245,7 @@ export default function OrdersDetails() {
}, [currentStatus, deliverycount]);
// ==============================|| calculate||============================== //
const calculate = () => {
let calculatedTotalCharge = 0;
let calculatedTotalAmount = 0;
@@ -268,23 +269,9 @@ export default function OrdersDetails() {
if (isErrorOrderDetails) return 'An error has occurred:(isErrorOrderDetails) ' + orderDetailsError.message;
const filteredOrders = rows.filter((row) =>
row.orderstatus == ''
? row.orderstatus.toLowerCase().includes(searchword.toLowerCase())
: row.orderstatus.toLowerCase().includes(searchword.toLowerCase()) ||
row.ordernotes.toLowerCase().includes(searchword.toLowerCase()) ||
row.tenantname.toLowerCase().includes(searchword.toLowerCase()) ||
row.orderid.toLowerCase().includes(searchword.toLowerCase()) ||
row.pickupcustomer.toLowerCase().includes(searchword.toLowerCase()) ||
row.pickupcontactno.toLowerCase().includes(searchword.toLowerCase()) ||
row.pickuplocation.toLowerCase().includes(searchword.toLowerCase()) ||
row.deliverycustomer.toLowerCase().includes(searchword.toLowerCase()) ||
row.deliverycontactno.toLowerCase().includes(searchword.toLowerCase()) ||
row.deliverylocation.toLowerCase().includes(searchword.toLowerCase()) ||
row.ridername.toLowerCase().includes(searchword.toLowerCase())
);
// to download ex format filtered data
const csvData = filteredOrders.map((order) => ({
const csvData = rows.map((order) => ({
tenantname: order.tenantname,
tenantcity: order.tenantcity,
tenantcontactno: order.tenantcontactno,
@@ -345,161 +332,166 @@ export default function OrdersDetails() {
return (
<>
{isLoadingOrderDetails && <Loader />}
<TitleCard title="Orders Details" />
<TitleCard
title="Orders Details"
secondary={
<Stack display={'flex'} flexDirection={'row'} alignItems={'center'} gap={2}>
{startdate && enddate && (
<Stack direction="row" spacing={2}>
<Badge
badgeContent={`${statusCount}`}
max={99999}
color="primary"
sx={{
bgcolor: '#f3e5f5',
p: 0.2,
borderRadius: '3px',
color: '#7b1fa2'
}}
>{`Orders-${datestatus}`}</Badge>
<Chip
label={
<Typography noWrap color="secondary">
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
</Typography>
}
variant="combined"
color="warning"
size="small"
deleteIcon={<CalendarMonth style={{ fontSize: 18 }} />}
onDelete={() => {
setOpen(true);
}}
onClick={() => setOpen(true)}
sx={{ cursor: 'pointer' }}
/>
</Stack>
)}
{(!startdate || !enddate) && (
<div>
<Stack direction="row" spacing={2}>
<Chip label="Orders-All" color="primary" variant="light" size="small" />
{/* <Chip label={<Typography noWrap color="secondary">ALL</Typography>} variant="combined" color='warning' size='small' /> */}
</Stack>
</div>
)}
<CSVExport data={csvData} filename={`Orders_Detail_${dayjs().format('YYYY-MM-DD_HHmmss')}.csv`} />
</Stack>
}
/>
<MainCard
content={false}
title={
<Stack display={'flex'} flexDirection={'row'} alignItems={'center'} justifyContent={'space-between'} flexWrap="wrap">
<Stack alignItems="flex-start" spacing={1}>
{startdate && enddate && (
<Stack direction="row" spacing={2}>
<Badge
badgeContent={`${statusCount}`}
max={99999}
color="primary"
sx={{
bgcolor: '#f3e5f5',
p: 0.2,
borderRadius: '3px',
color: '#7b1fa2'
}}
>{`Orders-${datestatus}`}</Badge>
<Chip
label={
<Typography noWrap color="secondary">
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
</Typography>
}
variant="combined"
color="warning"
size="small"
/>
</Stack>
)}
{(!startdate || !enddate) && (
<div>
<Stack direction="row" spacing={2}>
<Chip label="Orders-All" color="primary" variant="light" size="small" />
{/* <Chip label={<Typography noWrap color="secondary">ALL</Typography>} variant="combined" color='warning' size='small' /> */}
</Stack>
</div>
)}
</Stack>
<Stack sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
<Stack direction={'row'} alignItems={'center'} justifyContent={'space-between'}>
{tenantLocations.length == 1 ? (
<TextField
// disabled={!isAppLocation || !isClient}
variant="outlined"
fullWidth
label={'Business Location'}
value={tenantLocations[0].locationname}
focused
sx={{ width: '350px' }}
InputProps={{
style: { color: theme.palette.primary.main },
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
)
}}
/>
) : (
<Autocomplete
fullWidth
// disabled={!isAppLocation || !isClient}
id="combo-box-demo"
options={tenantLocations || []}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
renderInput={(params) => <TextField {...params} label={locoName} color="primary" />}
sx={{ width: '300px' }}
onChange={(event, value, reason) => {
if (value) {
console.log('Business Locations', value);
setLocationId(value.locationid);
setLocoName(value.locationname);
}
if (reason == 'clear') {
setLocationId(0);
setLocoName('Select Location');
}
}}
/>
)}
</Stack>
<Stack sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
{tenantLocations.length == 1 ? (
<TextField
// disabled={!isAppLocation || !isClient}
variant="outlined"
fullWidth
label={'Business Location'}
value={tenantLocations[0].locationname}
focused
sx={{ minWidth: 250, maxWidth: 400, flex: 1 }}
InputProps={{
style: { color: theme.palette.primary.main },
startAdornment: (
<InputAdornment position="start">
<MyLocationIcon color="primary" />
</InputAdornment>
)
}}
/>
) : (
<Autocomplete
sx={{ minWidth: 200 }}
disablePortal
fullWidth
// disabled={!isAppLocation || !isClient}
id="combo-box-demo"
options={status}
getOptionLabel={(option) => `${option.status}`}
options={tenantLocations || []}
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
renderInput={(params) => <TextField {...params} label={locoName} color="primary" />}
sx={{ minWidth: 250, maxWidth: 400, flex: 1 }}
onChange={(event, value, reason) => {
if (reason === 'clear') {
setCurrentStatus('All');
} else {
console.log('status', value);
setCurrentStatus(value.statusLow);
if (value) {
console.log('Business Locations', value);
setLocationId(value.locationid);
setLocoName(value.locationname);
}
if (reason == 'clear') {
setLocationId(0);
setLocoName('Select Location');
}
}}
renderInput={(params) => <TextField {...params} label={'Select Status'} />}
/>
<FormControl sx={{ width: 250 }}>
<OutlinedInput
inputRef={textFieldRef}
aria-describedby="header-search-text"
inputProps={{
'aria-label': 'weight'
}}
sx={{ background: 'white' }}
size="large"
id="header-search"
placeholder="Search (ctrl+k)"
value={searchword}
onChange={(e) => {
setSearchword(e.target.value);
}}
autoComplete="off"
startAdornment={
<InputAdornment position="start" sx={{ mr: -0.5 }}>
<SearchOutlined />
</InputAdornment>
}
endAdornment={
<Tooltip title="clear">
<IconButton
sx={{ visibility: searchword ? 'visible' : 'hidden' }}
onClick={() => {
setSearchword('');
}}
>
<ClearIcon style={{ fontSize: 'medium', color: '#65387A' }} />
</IconButton>
</Tooltip>
}
/>
</FormControl>
)}
<Tooltip title="Order Filter">
<IconButton
color="secondary"
variant="light"
sx={{
color: 'text.primary',
bgcolor: 'grey.200',
mx: 2
}}
aria-haspopup="true"
onClick={() => setOpen(true)}
>
<FilterList />
</IconButton>
</Tooltip>
<Autocomplete
sx={{ minWidth: 250, maxWidth: 400, flex: 1 }}
disablePortal
id="combo-box-demo"
options={status}
getOptionLabel={(option) => `${option.status}`}
onChange={(event, value, reason) => {
if (reason === 'clear') {
setCurrentStatus('All');
} else {
console.log('status', value);
setCurrentStatus(value.statusLow);
}
}}
renderInput={(params) => <TextField {...params} label={'Select Status'} />}
/>
<FormControl sx={{ minWidth: 250, maxWidth: 400, flex: 1 }}>
<OutlinedInput
inputRef={textFieldRef}
aria-describedby="header-search-text"
inputProps={{
'aria-label': 'weight'
}}
sx={{ background: 'white' }}
size="large"
id="header-search"
placeholder="Search (ctrl+k)"
value={searchword}
onChange={(e) => {
setSearchword(e.target.value);
}}
autoComplete="off"
startAdornment={
<InputAdornment position="start" sx={{ mr: -0.5 }}>
<SearchOutlined />
</InputAdornment>
}
endAdornment={
<Tooltip title="clear">
<IconButton
sx={{ visibility: searchword ? 'visible' : 'hidden' }}
onClick={() => {
setSearchword('');
}}
>
<ClearIcon style={{ fontSize: 'medium', color: '#65387A' }} />
</IconButton>
</Tooltip>
}
/>
</FormControl>
<CSVExport data={csvData} filename={`Orders_Detail_${dayjs().format('YYYY-MM-DD_HHmmss')}.csv`} />
</Stack>
{/* <Tooltip title="Order Filter">
<IconButton
color="secondary"
variant="light"
sx={{
color: 'text.primary',
bgcolor: 'grey.200',
mx: 2
}}
aria-haspopup="true"
onClick={() => setOpen(true)}
>
<FilterList />
</IconButton>
</Tooltip> */}
</Stack>
}
>
@@ -531,24 +523,24 @@ export default function OrdersDetails() {
>
<TableRow>
<TableCell sx={{ position: 'sticky !important' }}># </TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Map</TableCell>
<TableCell sx={{ whiteSpace: 'nowrap', position: 'sticky !important' }}>Tenant Name </TableCell>
{/* <TableCell sx={{ position: 'sticky !important' }}> {<SlLocationPin style={{ fontWeight: 1000 }} />}</TableCell> */}
<TableCell sx={{ whiteSpace: 'nowrap', position: 'sticky !important' }}>Location </TableCell>
<TableCell sx={{ position: 'sticky !important' }}>Pickup </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Drop </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Status </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Assignes </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Assigned </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Accepted </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Arrived </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Picked </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Delivered</TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Cancelled </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Notes </TableCell>
{/* <TableCell sx={{ position: 'sticky !important' }}> Notes </TableCell> */}
<TableCell sx={{ position: 'sticky !important' }}> Kms </TableCell>
<TableCell sx={{ position: 'sticky !important' }}> Charges </TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredOrders.length == 0 && (
{rows?.length == 0 && (
<>
<TableCell colSpan={15}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
@@ -557,12 +549,12 @@ export default function OrdersDetails() {
</TableCell>
</>
)}
{filteredOrders.map((row, index) => {
{rows?.map((row, index) => {
return (
filteredOrders.length !== 0 && (
rows?.length !== 0 && (
<TableRow>
<TableCell sx={{ width: '10px' }}>{index + 1}</TableCell>
<TableCell
{/* <TableCell
sx={{ width: '10px', cursor: 'pointer' }}
onClick={() => {
console.log('row', row);
@@ -572,13 +564,14 @@ export default function OrdersDetails() {
}}
>
{<SlLocationPin />}
</TableCell>
</TableCell> */}
<TableCell
sx={{
textAlign: 'start'
textAlign: 'start',
whiteSpace: 'nowrap'
}}
>
{row.tenantname}
{row.locationname}
<Typography variant="body2" noWrap>
{row.orderid}
</Typography>
@@ -591,7 +584,7 @@ export default function OrdersDetails() {
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Stack direction="column" sx={{ whiteSpace: 'nowrap' }}>
<Typography variant="caption">{row.pickupcustomer}</Typography>
<Typography variant="caption" color="textSecondary">
{row.pickupcontactno}
@@ -606,7 +599,7 @@ export default function OrdersDetails() {
</TableCell>
<TableCell align="left">
<Stack direction={'row'} spacing={1}>
<Stack direction="column">
<Stack direction="column" sx={{ whiteSpace: 'nowrap' }}>
<Typography variant="caption">{row.deliverycustomer}</Typography>
<Typography variant="caption" color="textSecondary">
{row.deliverycontactno}
@@ -695,25 +688,24 @@ export default function OrdersDetails() {
</Typography>
</TableCell>
<TableCell align="left">
{/* <TableCell align="left">
<Typography variant="caption">{row.ordernotes}</Typography>
</TableCell>
</TableCell> */}
<TableCell align="left">
<Stack direction={'column'} spacing={1}>
<Tooltip title=" KMS" placement="top">
<Chip
label={
row.orderstatus === 'cancelled' || row.orderstatus === 'cancelled' || row.kms == ''
? parseInt(0).toFixed(1)
: parseInt(row.kms).toFixed(1)
}
size="small"
variant="light"
color="error"
/>
</Tooltip>
<Tooltip title="Actual KMS">
<Chip
label={
row.orderstatus === 'cancelled' || row.orderstatus === 'cancelled' || row.kms == ''
? parseInt(0).toFixed(1)
: parseInt(row.kms).toFixed(1)
}
size="small"
variant="contained"
color="warning"
/>
{/* <Tooltip title="Actual KMS">
<Chip
label={
row.deliverystatus === 'cancelled' || row.actualkms === '' || row.actualkms === 'null'
@@ -725,42 +717,44 @@ export default function OrdersDetails() {
color="success"
variant="light"
/>
</Tooltip>
</Tooltip> */}
</Stack>
</TableCell>
<TableCell align="right">
<Stack direction={'column'} spacing={1}>
<Tooltip title=" Delivery Charge" placement="top">
<Chip
label={
row.orderstatus === 'cancelled' || row.deliverycharges == '' ? `${0}.00` : `${row.deliverycharges}.00`
}
size="small"
variant="light"
color="error"
/>
</Tooltip>
<Tooltip title="Delivery Amount">
<Chip
label={
row.orderstatus === 'cancelled' || row.deliverycharges == '' ? `${0}.00` : `${row.deliverycharges}.00`
}
size="small"
variant="contained"
color="primary"
/>
{/* <Tooltip title="Delivery Amount">
<Chip
label={row.deliveryamt == '' ? `₹ ${0}.00` : `₹ ${row.deliveryamt}.00`}
size="small"
color="success"
variant="light"
/>
</Tooltip>
</Tooltip> */}
</Stack>
</TableCell>
</TableRow>
)
);
})}
<TableRow>
<TableCell colSpan={15} rowSpan={3}>
<div ref={loadMoreRef} style={{ height: 40, textAlign: 'center' }}>
{isFetchingNextPage ? <CircularProgress /> : hasNextPage ? <CircularProgress /> : 'No more results'}
</div>
</TableCell>
</TableRow>
{rows?.length != 0 && (
<TableRow>
<TableCell colSpan={15} rowSpan={3}>
<div ref={loadMoreRef} style={{ height: 40, textAlign: 'center' }}>
{isFetchingNextPage ? <CircularProgress /> : hasNextPage ? <CircularProgress /> : 'No More Orders'}
</div>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>

View File

@@ -1,7 +1,209 @@
import React from 'react';
import * as React from 'react';
import { useState } from 'react';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { useQuery } from '@tanstack/react-query';
// material-ui
import {
Divider,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
FormControl,
OutlinedInput,
InputAdornment,
Chip,
Stack
} from '@mui/material';
const riderLogs = () => {
return <div>riderLogs</div>;
};
import dayjs from 'dayjs';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { SearchOutlined, CloseOutlined } from '@ant-design/icons';
import Loader from 'components/Loader';
// project imports
import MainCard from 'components/MainCard';
import { Empty } from 'antd';
import TitleCard from '../titleCard';
import { fetchAppLocations, fetchRidersLogs } from '../api/api';
export default riderLogs;
function formatDate(dateString) {
const date = dayjs(dateString);
const formattedDate = date.format('DD-MM-YYYY ');
return formattedDate;
}
const formatTime = (timeString) =>
new Date('2024-01-01T' + timeString + 'Z').toLocaleTimeString('en-US', {
timeZone: 'UTC',
hour12: true,
hour: '2-digit',
minute: '2-digit'
});
// ==============================|| RidersLogs ||============================== //
export default function RidersLogs() {
const tenantid = localStorage.getItem('tenantid');
const [rowsPerPage, setRowsPerPage] = useState(10);
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
const [searchword, setSearchword] = useState('');
const [showClose, SetShowClose] = useState(false);
/* ============================================= || fetchRidersLogs| ============================================= */
const {
data: rows = [], // Default to empty array
isLoading: IsRiderLogsLoading,
isError: IsRiderLogsError,
error: RiderLogsError
} = useQuery({
queryKey: [tenantid, startdate], // Meaningful query key
queryFn: fetchRidersLogs,
enabled: !!tenantid && !!startdate, // Fetch only if appId & startdate exist
refetchInterval: 300000 // Auto-fetch every 5 minutes
});
React.useEffect(() => {
setRowsPerPage(rows?.length + 1);
}, [rows]);
{
IsRiderLogsError && console.log('RiderLogsError', RiderLogsError);
}
return (
<>
{IsRiderLogsLoading && <Loader />}
<TitleCard title="Riders Logs" />
<MainCard
content={false}
title={
<Stack display={'flex'} flexDirection={'row'} alignItems={'center'} justifyContent={'space-between'} flexWrap={'wrap'} gap={1}>
<Stack>
<FormControl sx={{ width: 250 }}>
<OutlinedInput
sx={{ background: 'white' }}
size="medium"
id="header-search"
startAdornment={
<InputAdornment position="start" sx={{ mr: -0.5 }}>
<SearchOutlined />
</InputAdornment>
}
endAdornment={
showClose && (
<InputAdornment position="end" sx={{ mr: -0.5 }}>
<CloseOutlined
onClick={() => {
setSearchword('');
SetShowClose(false);
}}
/>
</InputAdornment>
)
}
aria-describedby="header-search-text"
inputProps={{
'aria-label': 'weight'
}}
placeholder="Search"
value={searchword}
onChange={(e) => {
setSearchword(e.target.value);
if (e.target.value == '') {
SetShowClose(false);
} else {
SetShowClose(true);
}
}}
autoComplete="off"
/>
</FormControl>{' '}
</Stack>
<Stack flexDirection="row" alignItems="center" gap={2}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
label="Choose Date"
value={dayjs(startdate)}
format="DD-MM-YYYY"
onChange={(e) => {
if (e) {
setStartdate(dayjs(e.$d).format('YYYY-MM-DD'));
}
}}
/>
</LocalizationProvider>
</Stack>
</Stack>
}
>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>S.No</TableCell>
<TableCell>ID</TableCell>
<TableCell>Rider</TableCell>
<TableCell>LogDate</TableCell>
<TableCell>Shift(HRS)</TableCell>
<TableCell>Login</TableCell>
<TableCell>Logout</TableCell>
<TableCell>WRK(HRS)</TableCell>
<TableCell>Shift(HRS)</TableCell>
<TableCell>BRK(HRS)</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.length == 0 ? (
<TableRow>
<TableCell colSpan={11}>
<Empty />
</TableCell>
</TableRow>
) : (
rows.map((row, index) => (
<TableRow key={index + 1}>
<TableCell align="left">{index + 1}</TableCell>
<TableCell align="left">{row.userid}</TableCell>
<TableCell align="left">{row.username}</TableCell>
<TableCell align="left">
{' '}
<Chip label={formatDate(row.logdate)} color="warning" variant="outlined" size="small" sx={{ bgcolor: '#fffde7' }} />
</TableCell>
<TableCell align="left">{row.shifthours}</TableCell>
<TableCell align="left">
{row.login != '' && (
<Chip label={formatTime(row.login)} color="info" variant="outlined" size="small" sx={{ bgcolor: '#e0f7fa' }} />
)}
</TableCell>
<TableCell align="left">
{row.logout != '' && (
<Chip label={formatTime(row.logout)} color="info" variant="outlined" size="small" sx={{ bgcolor: '#e0f7fa' }} />
)}
</TableCell>
<TableCell align="left">{row.workhours}</TableCell>
<TableCell align="left">{row.shorthours}</TableCell>
<TableCell align="left">{row.breakhours}</TableCell>
<TableCell align="left">
{row.logstatus == 0 ? (
<Chip label="Active" color="success" variant="outlined" size="small" sx={{ bgcolor: '#e8f5e9' }} />
) : (
<Chip label="Inactive" color="error" variant="outlined" size="small" sx={{ bgcolor: '#fce4ec' }} />
)}
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
</MainCard>
</>
);
}

View File

@@ -23,8 +23,6 @@ import {
IconButton,
Tooltip,
Chip,
Autocomplete,
TextField,
Collapse
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
@@ -45,12 +43,12 @@ import {
startOfWeek
// startOfYear,
} from 'date-fns';
import { FilterList } from '@mui/icons-material';
// project imports
import MainCard from 'components/MainCard';
import Loader from 'components/Loader';
import TitleCard from '../titleCard';
import { fetchRidersSummary } from '../api/api';
import { useTheme } from '@mui/material/styles';
// table filter
function descendingComparator(a, b, orderBy) {
@@ -88,6 +86,8 @@ function formatNumberToRupees(value) {
export default function RidersSummary() {
// const [rows, setRows] = useState([]);
const theme = useTheme();
const tenantid = localStorage.getItem('tenantid');
const [order, setOrder] = useState('asc');
const [orderBy, setOrderBy] = useState('calories');
const [selected, setSelected] = useState([]);
@@ -141,9 +141,12 @@ export default function RidersSummary() {
data: rows,
error: reportsError
} = useQuery({
queryKey: [appId, startdate, enddate],
queryKey: [tenantid, startdate, enddate],
queryFn: fetchRidersSummary
});
useEffect(() => {
rows && console.log('rows', rows);
}, [rows]);
// ==============================|| calculate||============================== //
const calculate = async () => {
let calculatedTotal = 0;
@@ -157,7 +160,7 @@ export default function RidersSummary() {
console.log('calculatedTotal', calculatedTotal);
};
useEffect(() => {
calculate();
rows && calculate();
}, [rows]);
if (isLoadingReports) return <Loader />;
@@ -167,7 +170,8 @@ export default function RidersSummary() {
const fetchTenantSummary = async (riderUserid) => {
try {
const tenantRes = await axios.get(
`${process.env.REACT_APP_URL}/deliveries/getreportsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}`
// `${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}&tenantid=${tenantid}`
`${process.env.REACT_APP_URL}/deliveries/getriderlocationreportsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}&tenantid=${tenantid}`
);
console.log('tenantRes', tenantRes.data.details);
setTenantData(tenantRes.data.details);
@@ -176,42 +180,9 @@ export default function RidersSummary() {
}
};
const handleClick = (event, name) => {
const selectedIndex = selected.indexOf(name);
let newSelected = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1));
}
const selectedRowData = rows.filter((row) => newSelected.includes(row.name));
setSelectedValue(selectedRowData);
setSelected(newSelected);
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event?.target.value, 10));
setPage(0);
};
const isSelected = (name) => selected.indexOf(name) !== -1;
// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
return (
<>
<TitleCard title="Riders Summary" />
<MainCard
content={false}
title={
@@ -249,15 +220,19 @@ export default function RidersSummary() {
<Table>
<TableHead>
<TableRow>
<TableCell>S.No </TableCell>
<TableCell># </TableCell>
<TableCell> Rider</TableCell>
<TableCell> Deliveries</TableCell>
<TableCell>Total </TableCell>
{/* <TableCell> Pending</TableCell>
<TableCell> Assigned</TableCell>
<TableCell> Accepted</TableCell>
<TableCell>Arrived </TableCell>
<TableCell>Picked </TableCell>
<TableCell>Active </TableCell>
<TableCell>Skipped </TableCell> */}
<TableCell>Delivered </TableCell>
<TableCell>KMS/Actualkms </TableCell>
<TableCell> POD/PLA</TableCell>
<TableCell>kms </TableCell>
<TableCell> COD/PLA</TableCell>
<TableCell> Charges</TableCell>
<TableCell>Action </TableCell>
</TableRow>
@@ -265,7 +240,7 @@ export default function RidersSummary() {
{/* ============================================ || TableBody || ============================================ */}
<TableBody>
{rows.length === 0 && (
{/* {rows?.length === 0 && (
<TableRow>
<TableCell colSpan={11}>
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
@@ -273,57 +248,59 @@ export default function RidersSummary() {
</Stack>
</TableCell>
</TableRow>
)}
)} */}
{stableSort(rows, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
if (typeof row === 'number') return null;
const isItemSelected = isSelected(row.name);
const labelId = `enhanced-table-checkbox-${index}`;
{rows?.map((row, index) => (
<>
{/* // ============================================ || tablerow 1 || ============================================ */}
return (
rows.length !== 0 && (
<>
{/* // ============================================ || tablerow 1 || ============================================ */}
<TableRow
sx={{
cursor: openRow === row.userid ? 'pointer' : null
}}
>
<TableCell component="th" scope="row" padding="none">
{index + 1}
</TableCell>
<TableCell align="left">
<Stack direction="row" sx={{ ml: -2 }}>
{row.firstname}
{row.status == 'Active' ? (
<TaskAltIcon fontSize="small" color="success" sx={{ ml: 1 }} />
) : (
<HighlightOffIcon fontSize="small" color="error" sx={{ ml: 1 }} />
)}
</Stack>
</TableCell>
<TableRow
hover
onClick={(event) => handleClick(event, row.name)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.tenantname}
selected={isItemSelected}
sx={{
bgcolor: openRow === row.userid ? '#e1bee7' : null,
'&:hover': {
bgcolor: openRow === row.userid ? '#e1bee7!important' : null
},
cursor: openRow === row.userid ? 'pointer' : null
}}
>
<TableCell component="th" id={labelId} scope="row" padding="none">
{index + 1}
<TableCell align="left">{row.totalorders}</TableCell>
{/* <TableCell align="left">
{row.pending ? <Chip color="primary" variant="light" label={row.pending} /> : row.pending}
</TableCell>
<TableCell align="left">
<Stack direction="row" sx={{ ml: -2 }}>
{row.firstname}
{row.status == 'Active' ? (
<TaskAltIcon fontSize="small" color="success" sx={{ ml: 1 }} />
) : (
<HighlightOffIcon fontSize="small" color="error" sx={{ ml: 1 }} />
)}
</Stack>
{row.assigned ? <Chip color="primary" variant="light" label={row.assigned} /> : row.assigned}
</TableCell>
<TableCell align="left">{row.deliveries}</TableCell>
<TableCell align="left">{row.Assigened}</TableCell>
<TableCell align="left">{row.Accepted}</TableCell>
<TableCell align="left">{row.Picked}</TableCell>
<TableCell align="left">{row.delivered}</TableCell>
<TableCell align="left">
<Stack direction={'row'}>
<Tooltip title="kms" placement="top">
{row.accepted ? <Chip color="primary" variant="light" label={row.accepted} /> : row.accepted}
</TableCell>
<TableCell align="left">
{row.arrived ? <Chip color="primary" variant="light" label={row.arrived} /> : row.arrived}
</TableCell>
<TableCell align="left">
{row.picked ? <Chip color="primary" variant="light" label={row.picked} /> : row.picked}
</TableCell>
<TableCell align="left">
{row.active ? <Chip color="primary" variant="light" label={row.active} /> : row.active}
</TableCell>
<TableCell align="left">
{row.skipped ? <Chip color="primary" variant="light" label={row.skipped} /> : row.skipped}
</TableCell> */}
<TableCell align="left">{row.delivered}</TableCell>
<TableCell align="left">
<Stack direction={'row'}>
{/* <Tooltip title="kms" placement="top">
<Chip
size="small"
label={row.kms}
@@ -336,165 +313,149 @@ export default function RidersSummary() {
minWidth: 80
}}
/>
</Tooltip>
<Tooltip title="Actual kms" placement="bottom">
<Chip
size="small"
label={row.actualkms}
sx={{
color: '#ff8f00',
bgcolor: '#ffecb3',
border: '1px solid #ff8f00',
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="right">
<Stack direction={'row'}>
<Tooltip title="Pay on Delivery" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.payondelivery)}
sx={{
color: '#f44336',
bgcolor: '#ffcdd2',
border: '1px solid #f44336',
cursor: 'pointer',
minWidth: 80,
mr: 1
}}
/>
</Tooltip>
<Tooltip title="Pay Later" placement="bottom">
<Chip
size="small"
label={formatNumberToRupees(row.Paylater)}
sx={{
color: '#43a047',
bgcolor: '#c8e6c9',
border: '1px solid #43a047',
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="right">
<Tooltip title="Total Charges" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.Deliveryamt)}
sx={{
color: 'primary.main',
bgcolor: '#e1bee7',
border: '1px solid #662582 ',
cursor: 'pointer',
minWidth: 100
}}
/>
</Tooltip>
</TableCell>
<TableCell align="center">
<IconButton
aria-label="expand row"
size="small"
onClick={() => {
fetchTenantSummary(row.userid);
setOpenRow(openRow === row.userid ? null : row.userid);
}}
sx={{
bgcolor: openRow === row.userid ? 'primary.main' : null,
color: openRow === row.userid ? 'white' : null,
'&:hover': {
bgcolor: openRow === row.userid ? 'primary.main' : '#e1bee7'
}
}}
>
{openRow === row.userid ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
</TableRow>
{/* // ============================================ || collapsive row || ============================================ */}
{openRow === row.userid && (
<TableRow
</Tooltip> */}
<Tooltip title="Actual kms" placement="bottom">
<Chip
size="small"
label={row.actualkms}
sx={{
bgcolor: openRow === row.userid ? '#f3e5f5' : null,
'&:hover': {
bgcolor: openRow === row.userid ? '#f3e5f5!important' : null
},
cursor: openRow === row.userid ? 'pointer' : null
color: '#ff8f00',
bgcolor: '#ffecb3',
border: '1px solid #ff8f00',
cursor: 'pointer',
minWidth: 80
}}
>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse
// in={openRow[row.tenantname]}
in={true}
timeout="auto"
unmountOnExit
>
<Box sx={{ margin: 1 }}>
<Typography variant="h5" gutterBottom component="div">
Tenant Summary
</Typography>
<Table size="small" aria-label="purchases">
<TableHead
sx={{
bgcolor: '#f3e5f5',
'&:onhover': {
bgcolor: '#f3e5f5!important'
}
}}
>
<TableRow>
<TableCell>#</TableCell>
<TableCell>Client</TableCell>
<TableCell align="left">All</TableCell>
<TableCell align="left">Pending</TableCell>
<TableCell align="left">Completed</TableCell>
<TableCell align="left">Cancelled</TableCell>
<TableCell align="center">Kms/Actual Kms</TableCell>
<TableCell align="center">POD / PLA</TableCell>
<TableCell align="center">Amount</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tenantData.map((row, index) => (
<TableRow
hover
onClick={(event) => handleClick(event, row.name)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.tenantname}
selected={isItemSelected}
>
{/* <TableCell sx={{ pl: 3 }} padding="checkbox">
<Checkbox
color="primary"
checked={isItemSelected}
inputProps={{
"aria-labelledby": labelId,
}}
/>
</TableCell> */}
<TableCell component="th" id={labelId} scope="row" padding="none">
{index + 1}
</TableCell>
<TableCell align="left">
<Stack direction="row" sx={{ ml: -2 }}>
{row.tenantname}
</Stack>
</TableCell>
<TableCell align="left">{row.totalorders}</TableCell>
<TableCell align="left">{row.deliveriespending}</TableCell>
<TableCell align="left">{row.deliveriescompleted}</TableCell>
<TableCell align="left">{row.deliveriescancelled}</TableCell>
<TableCell align="center">
<Chip
/>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="right">
<Stack direction={'row'}>
<Tooltip title="Pay on Delivery" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.payondelivery)}
sx={{
color: '#f44336',
bgcolor: '#ffcdd2',
border: '1px solid #f44336',
cursor: 'pointer',
minWidth: 80,
mr: 1
}}
/>
</Tooltip>
<Tooltip title="Pay Later" placement="bottom">
<Chip
size="small"
label={formatNumberToRupees(row.Paylater)}
sx={{
color: '#43a047',
bgcolor: '#c8e6c9',
border: '1px solid #43a047',
cursor: 'pointer',
minWidth: 80
}}
/>
</Tooltip>
</Stack>
</TableCell>
<TableCell align="left">
<Tooltip title="Total Charges" placement="top">
<Chip
size="small"
label={formatNumberToRupees(row.deliveryamt)}
sx={{
color: '#1976d2',
bgcolor: '#e3f2fd',
border: '1px solid #1976d2',
cursor: 'pointer',
minWidth: 100
}}
/>
</Tooltip>
</TableCell>
<TableCell align="center">
<IconButton
aria-label="expand row"
size="small"
onClick={() => {
fetchTenantSummary(row.userid);
setOpenRow(openRow === row.userid ? null : row.userid);
}}
sx={{
bgcolor: openRow === row.userid ? 'primary.main' : null,
color: openRow === row.userid ? 'white' : null,
'&:hover': {
bgcolor: openRow === row.userid ? 'primary.main' : '#e1bee7'
}
}}
>
{openRow === row.userid ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
</TableRow>
{/* // ============================================ || collapsive row || ============================================ */}
{openRow === row.userid && (
<TableRow
sx={{
bgcolor: openRow === row.userid ? 'white' : null,
'&:hover': {
bgcolor: openRow === row.userid ? 'white!important' : null
},
cursor: openRow === row.userid ? 'pointer' : null
}}
>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse in={true} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow
sx={{
bgcolor: theme.palette.secondary.lighter,
'&:hover': {
bgcolor: `${theme.palette.secondary.lighter} !important`
}
}}
>
<TableCell>#</TableCell>
<TableCell>Location</TableCell>
<TableCell align="left">All</TableCell>
{/* <TableCell align="left">Pending</TableCell> */}
<TableCell align="left">Completed</TableCell>
{/* <TableCell align="left">Cancelled</TableCell> */}
<TableCell align="left">Actual Kms</TableCell>
<TableCell align="left">COD / PLA</TableCell>
<TableCell align="left">Amount</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tenantData?.map((row, index) => (
<TableRow
sx={{
bgcolor: 'white!important',
'&:onhover': {
bgcolor: 'white!important'
}
}}
key={row.tenantname}
>
<TableCell component="th" scope="row" padding="none">
{index + 1}
</TableCell>
<TableCell align="left">
<Stack direction="row" sx={{ ml: -2 }}>
{row.locationname}
</Stack>
</TableCell>
<TableCell align="left">{row.totalorders}</TableCell>
{/* <TableCell align="left">{row.deliveriespending}</TableCell> */}
<TableCell align="left">{row.deliveriescompleted}</TableCell>
{/* <TableCell align="left">{row.deliveriescancelled}</TableCell> */}
<TableCell align="left">
{/* <Chip
size="small"
label={row.kms}
sx={{
@@ -504,93 +465,77 @@ export default function RidersSummary() {
border: '1px solid #1976d2',
minWidth: 80
}}
/>
<Chip
size="small"
label={row.actualkms}
sx={{
color: '#ff8f00',
bgcolor: '#ffecb3',
border: '1px solid #ff8f00',
minWidth: 80
}}
/>
</TableCell>
/> */}
<Chip
size="small"
label={row.actualkms}
sx={{
color: '#ff8f00',
bgcolor: '#ffecb3',
border: '1px solid #ff8f00',
minWidth: 80
}}
/>
</TableCell>
<TableCell align="center">
<Chip
size="small"
label={formatNumberToRupees(row.payondelivery)}
sx={{
color: '#f44336',
bgcolor: '#ffcdd2',
border: '1px solid #f44336',
minWidth: 80
}}
/>{' '}
<Chip
size="small"
label={formatNumberToRupees(row.paylater)}
sx={{
color: '#43a047',
bgcolor: '#c8e6c9',
border: '1px solid #43a047',
minWidth: 80
}}
/>
</TableCell>
<TableCell align="left">
<Chip
size="small"
label={formatNumberToRupees(row.payondelivery)}
sx={{
color: '#f44336',
bgcolor: '#ffcdd2',
border: '1px solid #f44336',
minWidth: 80
}}
/>{' '}
<Chip
size="small"
label={formatNumberToRupees(row.paylater)}
sx={{
color: '#43a047',
bgcolor: '#c8e6c9',
border: '1px solid #43a047',
minWidth: 80
}}
/>
</TableCell>
<TableCell align="center">
<Chip
size="small"
label={formatNumberToRupees(row.charges)}
sx={{
color: 'primary.main',
bgcolor: '#e1bee7',
border: '1px solid #662582 ',
minWidth: 100
}}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
)}
</>
)
);
})}
{emptyRows > 0 && (
<TableRow sx={{ height: (dense ? 33 : 53) * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
<TableCell align="left">
<Chip
size="small"
label={formatNumberToRupees(row.charges)}
sx={{
color: 'primary.main',
bgcolor: '#e1bee7',
border: '1px solid #662582 ',
minWidth: 100
}}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
)}
</>
))}
</TableBody>
</Table>
</TableContainer>
<Divider />
<Stack direction={'row'} sx={{ display: 'flex', justifyContent: 'end', px: 1, py: 2 }}>
{/* <Stack direction={'row'} sx={{ display: 'flex', justifyContent: 'end', px: 1, py: 2 }}>
<Typography variant="h5">Total :</Typography>
<Typography variant="h5" sx={{ ml: 5, mr: 1.5 }}>
{formatNumberToRupees(total)}
</Typography>
</Stack>
{/* table pagination */}
{/* <TablePagination
rowsPerPageOptions={[10, 25, 50]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/> */}
</Stack> */}
</MainCard>
{/* ================================================ || Date Filter || ================================================ */}
<Dialog open={open}>
<DialogTitle align="left">
<Typography variant="h4">Select Filter Options</Typography>
@@ -605,7 +550,6 @@ export default function RidersSummary() {
setDateselect('all');
setStartdate('');
setEnddate('');
setOpen(false);
} else {
setDateselect('select');

View File

@@ -20,7 +20,7 @@ import {
Button,
List
} from '@mui/material';
const TitleCard = ({ title }) => {
const TitleCard = ({ title, secondary, sx }) => {
const theme = useTheme();
return (
<Grid container spacing={2}>
@@ -32,12 +32,14 @@ const TitleCard = ({ title }) => {
bgcolor: theme.palette.background.default,
zIndex: 1,
// borderBottom: `1px solid ${theme.palette.divider}`,
width: '100%'
width: '100%',
...sx
}}
>
<Grid item xs={12}>
<Stack direction={'row'} justifyContent={'space-between'} sx={{ p: 1 }}>
<Stack direction={'row'} justifyContent={'space-between'} sx={{ p: 1, flexWrap: 'wrap' }}>
<Typography variant="h3">{title}</Typography>
{secondary && secondary}
</Stack>
</Grid>
</CardActions>

View File

@@ -19,6 +19,8 @@ const Login = Loadable(lazy(() => import('pages/nearle/login')));
const Customers = Loadable(lazy(() => import('pages/nearle/clients/customers')));
const Locations = Loadable(lazy(() => import('pages/nearle/locations/Locations')));
const Orders = Loadable(lazy(() => import('pages/nearle/orders/orders')));
const Details = Loadable(lazy(() => import('pages/nearle/orders/details')));
@@ -56,16 +58,18 @@ const MainRoutes = {
children: [
{
path: 'orders',
element: <Orders />,
children: [
// use Outlet in orders page to render the child, because order render in app, but chidren createorder, multipleorder have nowhere, outlet make parent as to render
{
path: '', // /orders
element: <Orders />
},
{
path: 'create/grouporders', // /orders/create/grouporders
element: <MultipleOrders />
},
{
path: 'create',
element: <Createorder1 />
},
{
path: 'createorders',
element: <MultipleOrders />
}
]
},
@@ -115,6 +119,10 @@ const MainRoutes = {
element: <InvoicePreview />
}
]
},
{
path: 'locations',
element: <Locations />
}
]
},

View File

@@ -27,11 +27,30 @@ export default function ThemeCustomization({ children }) {
() => ({
breakpoints: {
values: {
xs: 0,
sm: 768,
md: 1024,
lg: 1266,
xl: 1440
xs: 0, // Extra-small: 0px and up
custom300: 300, // above 350
custom350: 350, // above 350
custom400: 400, // above 400
custom450: 450, // above 450
custom500: 500, // above 450
custom550: 550, // above 450
custom600: 600, // above 450
custom650: 650, // above 450
custom700: 700, // above 450
custom750: 750, // above 450
sm: 768, // Small: 768px and up
custom800: 800, // above 450,
custom850: 850, // above 450,
custom900: 900, // above 450
custom950: 950, // above 450
custom1000: 1000, // above 450
md: 1024, // Medium: 1024px and up
custom1050: 1050, //
custom1100: 1100, //
lg: 1266, // Large: 1266px and up
custom1300: 1300, // Large: 1266px and up
custom1350: 1350, // Large: 1266px and up
xl: 1440 // Extra-large: 1440px and up
}
},
direction: themeDirection,

View File

@@ -40,17 +40,17 @@ const Default = (colors) => {
// contrastText
// },
primary: {
lighter: purple[0],
100: purple[1],
200: purple[2],
light: purple[3],
400: purple[4],
lighter: '#E8D9EF',
100: '#CBA7DA',
200: '#AE76C4',
light: '#9255AB',
400: '#9255AB',
main: '#662582',
dark: purple[6],
700: purple[7],
darker: purple[8],
900: purple[9],
contrastText
dark: '#4D1C61',
700: '#3A1549',
darker: '#260E30',
900: '#17081D',
contrastText: '#FFFFFF'
},
secondary: {
lighter: greyColors[100],
@@ -105,7 +105,11 @@ const Default = (colors) => {
darker: green[9],
contrastText
},
grey: greyColors
grey: greyColors,
bg: {
main: '#E0E0E0',
light: '#fafafb'
}
};
};

View File

@@ -1243,6 +1243,11 @@
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz"
integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
"@custom-react-hooks/use-network@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@custom-react-hooks/use-network/-/use-network-1.0.1.tgz"
integrity sha512-WbFVxsC18hjJiVIONAExVORlJlrOeYwzKsiYUCzrt5zCP0u9eLUPJuftF3DvEpoaQdjufazllWCPWKXT+dTY2g==
"@emotion/babel-plugin@^11.10.6":
version "11.10.6"
resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz"
@@ -1501,6 +1506,19 @@
intl-messageformat "10.3.4"
tslib "^2.4.0"
"@googlemaps/js-api-loader@1.16.8":
version "1.16.8"
resolved "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.8.tgz"
integrity sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ==
"@googlemaps/markerclusterer@2.5.3":
version "2.5.3"
resolved "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.5.3.tgz"
integrity sha512-x7lX0R5yYOoiNectr10wLgCBasNcXFHiADIBdmn7jQllF2B5ENQw5XtZK+hIw4xnV0Df0xhN4LN98XqA5jaiOw==
dependencies:
fast-deep-equal "^3.1.3"
supercluster "^8.0.1"
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz"
@@ -2069,6 +2087,28 @@
resolved "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz"
integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==
"@react-google-maps/api@^2.20.7":
version "2.20.7"
resolved "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.7.tgz"
integrity sha512-ys7uri3V6gjhYZUI43srHzSKDC6/jiKTwHNlwXFTvjeaJE3M3OaYBt9FZKvJs8qnOhL6i6nD1BKJoi1KrnkCkg==
dependencies:
"@googlemaps/js-api-loader" "1.16.8"
"@googlemaps/markerclusterer" "2.5.3"
"@react-google-maps/infobox" "2.20.0"
"@react-google-maps/marker-clusterer" "2.20.0"
"@types/google.maps" "3.58.1"
invariant "2.2.4"
"@react-google-maps/infobox@2.20.0":
version "2.20.0"
resolved "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.20.0.tgz"
integrity sha512-03PJHjohhaVLkX6+NHhlr8CIlvUxWaXhryqDjyaZ8iIqqix/nV8GFdz9O3m5OsjtxtNho09F/15j14yV0nuyLQ==
"@react-google-maps/marker-clusterer@2.20.0":
version "2.20.0"
resolved "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.20.0.tgz"
integrity sha512-tieX9Va5w1yP88vMgfH1pHTacDQ9TgDTjox3tLlisKDXRQWdjw+QeVVghhf5XqqIxXHgPdcGwBvKY6UP+SIvLw==
"@react-leaflet/core@^2.1.0":
version "2.1.0"
resolved "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz"
@@ -2503,6 +2543,11 @@
"@types/qs" "*"
"@types/serve-static" "*"
"@types/google.maps@3.58.1":
version "3.58.1"
resolved "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz"
integrity sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==
"@types/graceful-fs@^4.1.2":
version "4.1.5"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz"
@@ -6238,6 +6283,13 @@ intl-messageformat@10.3.4:
"@formatjs/icu-messageformat-parser" "2.3.1"
tslib "^2.4.0"
invariant@2.2.4:
version "2.2.4"
resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
ipaddr.js@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz"
@@ -7199,6 +7251,11 @@ jwt-decode@^3.1.2:
resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
kdbush@^4.0.2:
version "4.0.2"
resolved "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz"
integrity sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==
kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"
@@ -7343,7 +7400,7 @@ lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -9186,7 +9243,7 @@ react-dnd@^16.0.1:
fast-deep-equal "^3.1.3"
hoist-non-react-statics "^3.3.2"
react-dom@*, "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, react-dom@^18.0.0, react-dom@^18.2.0, "react-dom@>= 0.14.0", "react-dom@>=16 || >=17 || >= 18", react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0, "react-dom@16.2.0 - 18":
react-dom@*, "react-dom@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8 || ^17 || ^18 || ^19", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, react-dom@^18.0.0, react-dom@^18.2.0, "react-dom@>= 0.14.0", "react-dom@>=16 || >=17 || >= 18", react-dom@>=16.0.0, react-dom@>=16.11.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0, "react-dom@16.2.0 - 18":
version "18.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
@@ -9400,7 +9457,7 @@ react-virtuoso@^4.7.0:
resolved "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.7.0.tgz"
integrity sha512-cpgvI1rSOETGDMhqVAVDuH+XHbWO1uIGKv5I6l4CyC71xWYUeGrE5n7sgTZklROB4+Vbv85pcgfWloTlY48HGQ==
react@*, "react@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || 17 || 18", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^17.0.0 || ^18.0.0", react@^17.0.2, react@^18.0.0, react@^18.2.0, "react@>= 0.14.0", "react@>= 16", "react@>= 16.14", "react@>=16 || >=17 || >= 18", react@>=16.0.0, react@>=16.11.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=16.9.0, "react@16.2.0 - 18":
react@*, "react@^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || 17 || 18", "react@^16.8 || ^17 || ^18 || ^19", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^17.0.0 || ^18.0.0", react@^17.0.2, react@^18.0.0, react@^18.2.0, "react@>= 0.14.0", "react@>= 16", "react@>= 16.14", "react@>=16 || >=17 || >= 18", react@>=16.0.0, react@>=16.11.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=16.9.0, "react@16.2.0 - 18":
version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
@@ -10293,6 +10350,13 @@ stylis@^4.0.13, stylis@4.1.3, stylis@4.x:
resolved "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz"
integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==
supercluster@^8.0.1:
version "8.0.1"
resolved "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz"
integrity sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==
dependencies:
kdbush "^4.0.2"
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"