initial commit
This commit is contained in:
91
src/pages/nearle/reports/MapWithRouteGoogle.js
Normal file
91
src/pages/nearle/reports/MapWithRouteGoogle.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { LoadScriptNext, GoogleMap } from '@react-google-maps/api';
|
||||
|
||||
const containerStyle = {
|
||||
width: '100%',
|
||||
height: '90vh'
|
||||
};
|
||||
|
||||
const MapWithRouteGoogle = ({ coordinates, additionalProps, setMapOpen }) => {
|
||||
const mapRef = useRef(null);
|
||||
|
||||
/** Convert coordinates to numbers */
|
||||
const numericCoordinates = coordinates
|
||||
.map((c) => {
|
||||
const lat = Number(c.lat);
|
||||
const lng = Number(c.lng);
|
||||
return isNaN(lat) || isNaN(lng) ? null : { lat, lng };
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
if (numericCoordinates.length < 2) {
|
||||
return <div>No route data available</div>;
|
||||
}
|
||||
|
||||
const start = numericCoordinates[0];
|
||||
const end = numericCoordinates[numericCoordinates.length - 1];
|
||||
|
||||
/** Map loaded callback */
|
||||
const onMapLoad = (map) => {
|
||||
// draw markers
|
||||
new window.google.maps.Marker({
|
||||
position: start,
|
||||
map,
|
||||
label: 'S',
|
||||
title: `Start: ${additionalProps?.riderStart}`
|
||||
});
|
||||
|
||||
new window.google.maps.Marker({
|
||||
position: end,
|
||||
map,
|
||||
label: 'E',
|
||||
title: `End: ${additionalProps?.riderEnd}`
|
||||
});
|
||||
|
||||
// draw rider route (point-to-point)
|
||||
const route = new window.google.maps.Polyline({
|
||||
path: numericCoordinates,
|
||||
geodesic: false,
|
||||
strokeColor: '#1A73E8',
|
||||
strokeOpacity: 1.0,
|
||||
strokeWeight: 4
|
||||
});
|
||||
|
||||
route.setMap(map);
|
||||
|
||||
// auto fit
|
||||
const bounds = new window.google.maps.LatLngBounds();
|
||||
numericCoordinates.forEach((p) => bounds.extend(p));
|
||||
map.fitBounds(bounds);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setMapOpen(false)}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
right: 10,
|
||||
zIndex: 999,
|
||||
padding: '6px 12px',
|
||||
background: '#1A73E8',
|
||||
color: 'white',
|
||||
borderRadius: 6,
|
||||
cursor: 'pointer',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
||||
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
|
||||
<GoogleMap mapContainerStyle={containerStyle} center={start} zoom={14} onLoad={onMapLoad}>
|
||||
{/* Polyline and markers added via onLoad */}
|
||||
</GoogleMap>
|
||||
</LoadScriptNext>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapWithRouteGoogle;
|
||||
76
src/pages/nearle/reports/RiderLocationMap.js
Normal file
76
src/pages/nearle/reports/RiderLocationMap.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Button } from '@mui/material';
|
||||
import { LoadScriptNext, GoogleMap, Marker, OverlayView } from '@react-google-maps/api';
|
||||
|
||||
const containerStyle = {
|
||||
width: '100%',
|
||||
height: 'calc(100vh - 150px)'
|
||||
};
|
||||
|
||||
export default function RiderLocationMap({ riderLocations }) {
|
||||
console.log('riderLocations', riderLocations);
|
||||
|
||||
const center = {
|
||||
lat: Number(riderLocations?.[0]?.latitude || 11.0056),
|
||||
lng: Number(riderLocations?.[0]?.longitude || 76.9661)
|
||||
};
|
||||
const GreenIcon = {
|
||||
url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
|
||||
scaledSize: new window.google.maps.Size(25, 41),
|
||||
anchor: new window.google.maps.Point(12, 41)
|
||||
};
|
||||
|
||||
const RedIcon = {
|
||||
url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
|
||||
scaledSize: new window.google.maps.Size(25, 41),
|
||||
anchor: new window.google.maps.Point(12, 41)
|
||||
};
|
||||
|
||||
return (
|
||||
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
|
||||
<GoogleMap mapContainerStyle={containerStyle} zoom={12} center={center}>
|
||||
{riderLocations &&
|
||||
riderLocations?.map((r, index) => {
|
||||
const lat = Number(r.latitude);
|
||||
const lng = Number(r.longitude);
|
||||
return (
|
||||
<div key={index}>
|
||||
{/* Marker */}
|
||||
<Marker
|
||||
position={{ lat, lng }}
|
||||
icon={r.status == 'active' ? GreenIcon : RedIcon}
|
||||
label={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
/>
|
||||
<OverlayView position={{ lat, lng }} mapPaneName={OverlayView.OVERLAY_LAYER}>
|
||||
<div
|
||||
style={{
|
||||
background: 'none',
|
||||
color: 'green',
|
||||
padding: '2px 8px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
whiteSpace: 'nowrap',
|
||||
transform: 'translate(-50%, -140%)',
|
||||
pointerEvents: 'none',
|
||||
ml: 20
|
||||
}}
|
||||
>
|
||||
<Button variant="contained" color="primary" size="small">
|
||||
{` ${r.username} `}
|
||||
{/* <br /> */}
|
||||
{/* {`${r.contactno || '##### ##### '} `} */}
|
||||
<br />
|
||||
{`(${r.orderid || ''}) `}
|
||||
</Button>
|
||||
</div>
|
||||
</OverlayView>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</GoogleMap>
|
||||
</LoadScriptNext>
|
||||
);
|
||||
}
|
||||
69
src/pages/nearle/reports/RidersRoutes.js
Normal file
69
src/pages/nearle/reports/RidersRoutes.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import { GoogleMap, Polyline, Marker, useJsApiLoader } from '@react-google-maps/api';
|
||||
|
||||
const containerStyle = {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
};
|
||||
|
||||
export default function RidersRoutes({ details }) {
|
||||
const mapRef = useRef(null);
|
||||
|
||||
const { isLoaded } = useJsApiLoader({
|
||||
googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAPS_KEY
|
||||
});
|
||||
|
||||
// Convert dataset
|
||||
const routePath = useMemo(
|
||||
() =>
|
||||
details?.map((p) => ({
|
||||
lat: Number(p.latitude),
|
||||
lng: Number(p.longitude)
|
||||
})),
|
||||
[details]
|
||||
);
|
||||
const bikeIcon = {
|
||||
path: 'M12 2c-2.2 0-4 1.8-4 4v3H5l-1 2h2l3.6 7.59c.34.58.96.94 1.64.94h2.52c.68 0 1.3-.36 1.64-.94L19 11h2l-1-2h-3V6c0-2.2-1.8-4-4-4z',
|
||||
fillColor: '#9c27b0', // 🔥 purple
|
||||
fillOpacity: 1,
|
||||
strokeWeight: 0,
|
||||
scale: 1.4,
|
||||
anchor: new window.google.maps.Point(12, 24)
|
||||
};
|
||||
|
||||
// Auto fit bounds
|
||||
useEffect(() => {
|
||||
if (!mapRef.current || routePath.length === 0) return;
|
||||
|
||||
const bounds = new window.google.maps.LatLngBounds();
|
||||
routePath.forEach((p) => bounds.extend(p));
|
||||
mapRef.current.fitBounds(bounds);
|
||||
}, [routePath]);
|
||||
|
||||
if (!isLoaded) return <div>Loading map...</div>;
|
||||
|
||||
return (
|
||||
<GoogleMap mapContainerStyle={containerStyle} onLoad={(map) => (mapRef.current = map)} center={routePath[0]} zoom={16}>
|
||||
{/* Route line */}
|
||||
<Polyline
|
||||
path={routePath}
|
||||
options={{
|
||||
strokeColor: '#196fd2',
|
||||
strokeOpacity: 0.9,
|
||||
strokeWeight: 5
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Start marker */}
|
||||
<Marker
|
||||
position={routePath[0]}
|
||||
icon={{
|
||||
url: 'http://maps.google.com/mapfiles/ms/icons/green-dot.png'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* End marker */}
|
||||
<Marker position={routePath[routePath.length - 1]} icon={bikeIcon} />
|
||||
</GoogleMap>
|
||||
);
|
||||
}
|
||||
173
src/pages/nearle/reports/mapWithRoute.js
Normal file
173
src/pages/nearle/reports/mapWithRoute.js
Normal file
@@ -0,0 +1,173 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { MapContainer, TileLayer, Marker, Polyline, Tooltip } from 'react-leaflet';
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import dayjs from 'dayjs';
|
||||
import { Chip, Stack, Typography, Box } from '@mui/material';
|
||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import CircularLoader from 'components/CircularLoader';
|
||||
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
|
||||
// Start marker
|
||||
const startIcon = new L.Icon({
|
||||
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
|
||||
// End marker
|
||||
const endIcon = new L.Icon({
|
||||
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
|
||||
const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => {
|
||||
console.log('additionalProps', additionalProps);
|
||||
|
||||
const mapRef = useRef(null);
|
||||
const theme = useTheme();
|
||||
const [routePoints, setRoutePoints] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Fit the map to bounds
|
||||
useEffect(() => {
|
||||
if (mapRef.current && coordinates.length > 0) {
|
||||
const bounds = [
|
||||
[Math.min(...coordinates.map((c) => c.lat)), Math.min(...coordinates.map((c) => c.lng))],
|
||||
[Math.max(...coordinates.map((c) => c.lat)), Math.max(...coordinates.map((c) => c.lng))]
|
||||
];
|
||||
mapRef.current.fitBounds(bounds);
|
||||
}
|
||||
}, [coordinates]);
|
||||
|
||||
// Fetch OSRM Route → REAL ROAD ROUTE
|
||||
useEffect(() => {
|
||||
const getOSRMRoute = async () => {
|
||||
setLoading(true);
|
||||
|
||||
// FIX: If only one coordinate, stop loader and exit
|
||||
if (coordinates.length < 2) {
|
||||
setRoutePoints([]); // no route
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const start = coordinates[0];
|
||||
const end = coordinates[coordinates.length - 1];
|
||||
|
||||
const url = `https://router.project-osrm.org/route/v1/driving/${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;
|
||||
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.routes?.length) {
|
||||
const points = data.routes[0].geometry.coordinates.map(([lng, lat]) => ({
|
||||
lat,
|
||||
lng
|
||||
}));
|
||||
setRoutePoints(points);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('OSRM Error:', err);
|
||||
} finally {
|
||||
setLoading(false); // ALWAYS STOP LOADING
|
||||
}
|
||||
};
|
||||
|
||||
getOSRMRoute();
|
||||
}, [coordinates]);
|
||||
|
||||
if (!coordinates || coordinates.length === 0) return null;
|
||||
|
||||
const start = coordinates[0];
|
||||
const end = coordinates[coordinates.length - 1];
|
||||
const center = coordinates[Math.floor(coordinates.length / 2)];
|
||||
|
||||
const InfoItem = ({ label, value }) => (
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||
{label}:
|
||||
</Typography>
|
||||
<Chip label={value || '0.00'} color="primary" sx={{ fontWeight: 700 }} />
|
||||
</Stack>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', height: '100vh', position: 'relative', overflow: 'hidden' }}>
|
||||
{loading && <CircularLoader />}
|
||||
{/* CLOSE BUTTON */}
|
||||
<Chip
|
||||
label="Close"
|
||||
icon={<CloseCircleOutlined style={{ fontSize: 18 }} />}
|
||||
onClick={() => setMapOpen(false)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
right: 12,
|
||||
zIndex: 2000,
|
||||
bgcolor: theme.palette.error.main,
|
||||
color: '#fff',
|
||||
fontWeight: 600,
|
||||
borderRadius: '12px',
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
boxShadow: theme.shadows[4],
|
||||
cursor: 'pointer',
|
||||
'& .MuiChip-icon': { color: '#fff' }
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* MAP */}
|
||||
<MapContainer center={center} zoom={14} scrollWheelZoom style={{ height: '100%', width: '100%' }} ref={mapRef}>
|
||||
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
||||
|
||||
{/* START MARKER */}
|
||||
<Marker position={start} icon={startIcon}>
|
||||
<Tooltip direction="bottom">{`Pickup: ${dayjs(additionalProps.riderStart).format('DD-MM-YYYY hh:mm A')}`}</Tooltip>
|
||||
</Marker>
|
||||
|
||||
{/* END MARKER */}
|
||||
<Marker position={end} icon={endIcon}>
|
||||
<Tooltip direction="bottom">{`Drop: ${dayjs(additionalProps.riderEnd).format('DD-MM-YYYY hh:mm A')}`}</Tooltip>
|
||||
</Marker>
|
||||
|
||||
{/* REAL OSRM ROUTE */}
|
||||
{routePoints.length > 0 && <Polyline positions={routePoints} pathOptions={{ color: 'blue', weight: 5 }} />}
|
||||
</MapContainer>
|
||||
|
||||
{/* BOTTOM DETAILS */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
bgcolor: 'rgba(255,255,255,0.96)',
|
||||
p: 2,
|
||||
boxShadow: theme.shadows[3],
|
||||
zIndex: 1500
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" flexWrap="wrap" rowGap={1.5} columnGap={3} alignItems="center">
|
||||
<InfoItem label="Tenant" value={order?.tenantname} />
|
||||
<InfoItem label="Rider" value={order?.ridername} />
|
||||
<InfoItem label="Pickup" value={order?.pickupcustomer} />
|
||||
<InfoItem label="Drop" value={order?.deliverycustomer} />
|
||||
<InfoItem label="Kms" value={order?.kms} />
|
||||
<InfoItem label="Actual Kms" value={order?.actualkms} />
|
||||
<InfoItem label="Rider Kms" value={order?.riderkms} />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapWithRoute;
|
||||
1301
src/pages/nearle/reports/ordersDetails.js
Normal file
1301
src/pages/nearle/reports/ordersDetails.js
Normal file
File diff suppressed because it is too large
Load Diff
935
src/pages/nearle/reports/ordersSummary.js
Normal file
935
src/pages/nearle/reports/ordersSummary.js
Normal file
@@ -0,0 +1,935 @@
|
||||
import { React, useState, useEffect, useRef } from 'react';
|
||||
import TitleCard from 'pages/titleCard';
|
||||
import axios from 'axios';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Empty } from 'antd';
|
||||
// material-ui
|
||||
import {
|
||||
Box,
|
||||
Divider,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
Stack,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Chip,
|
||||
Autocomplete,
|
||||
TextField
|
||||
} from '@mui/material';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import dayjs from 'dayjs';
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
import { CalendarMonth } from '@mui/icons-material';
|
||||
import { getreportlocationsummary, getreportsummary, gettenantlocations, getTenants } from 'pages/api/api';
|
||||
import MainCard from 'components/MainCard';
|
||||
import Loader from 'components/Loader';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import DateFilterDialog from 'components/DateFilterDialog';
|
||||
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
|
||||
import { OpenToast } from 'components/third-party/OpenToast';
|
||||
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
|
||||
|
||||
function formatNumberToRupees(value) {
|
||||
return new Intl.NumberFormat('en-IN', {
|
||||
style: 'currency',
|
||||
currency: 'INR',
|
||||
minimumFractionDigits: 2
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
// ==============================|| MUI TABLE - ENHANCED ||============================== //
|
||||
|
||||
export default function OrdersReport() {
|
||||
// const [rows, setRows] = useState([]);
|
||||
const theme = useTheme();
|
||||
const locationRef = useRef(null);
|
||||
const tenantRef = useRef(null);
|
||||
const [appId, setAppId] = useState(0);
|
||||
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
|
||||
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
|
||||
const [locaName, setLocoName] = useState('All');
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openRow, setOpenRow] = useState(null);
|
||||
const [datestatus, setDatestatus] = useState('Today');
|
||||
const [total, settotal] = useState(0);
|
||||
const [totalOrders, settotalOrders] = useState(0);
|
||||
const [totalOrderPend, setTotalOrderPend] = useState(0);
|
||||
const [totalOrderComplete, setTotalOrderComplete] = useState(0);
|
||||
const [totalOrderCancel, setTotalOrderCancel] = useState(0);
|
||||
const [totalDeliPend, setTotalDeliPend] = useState(0);
|
||||
const [totalDeliComplete, setTotalDeliComplete] = useState(0);
|
||||
const [totalDeliCancel, setTotalDeliCancel] = useState(0);
|
||||
const [ridersdata, setRidersdata] = useState([]);
|
||||
const userid = localStorage.getItem('userid');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [locationid, setLocationid] = useState(0);
|
||||
const [tenantid, setTenantid] = useState(0);
|
||||
const [tenantValue, setTenantValue] = useState(null);
|
||||
const [locationValue, setLocationValue] = useState(null);
|
||||
const [page, setPage] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('openRow', openRow);
|
||||
}, [openRow]);
|
||||
|
||||
// to clear the tenant and location and rider autocomplete
|
||||
useEffect(() => {
|
||||
setTenantid(0);
|
||||
setTenantValue(null);
|
||||
setLocationid(0);
|
||||
setLocationValue(null);
|
||||
setOpenRow(null);
|
||||
}, [appId]);
|
||||
// to clear the location and rider autocomplete
|
||||
useEffect(() => {
|
||||
setLocationid(0);
|
||||
setLocationValue(null);
|
||||
setOpenRow(null);
|
||||
}, [tenantid]);
|
||||
|
||||
// table header
|
||||
const headCells = [
|
||||
{
|
||||
id: 's.no',
|
||||
numeric: false,
|
||||
disablePadding: true,
|
||||
label: '#',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
},
|
||||
{
|
||||
id: 'clients',
|
||||
numeric: 'true',
|
||||
disablePadding: false,
|
||||
label: tenantid ? 'Location' : 'Tenant',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
},
|
||||
{
|
||||
id: 'all',
|
||||
numeric: 'center',
|
||||
disablePadding: false,
|
||||
label: 'All',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
},
|
||||
|
||||
{
|
||||
id: 'orders',
|
||||
numeric: 'center',
|
||||
disablePadding: false,
|
||||
label: 'orders',
|
||||
rowSpan: 1,
|
||||
colSpan: 3,
|
||||
bgcolor: '#ffcdd2'
|
||||
},
|
||||
{
|
||||
id: 'deliveries',
|
||||
numeric: 'center',
|
||||
disablePadding: false,
|
||||
label: 'deliveries',
|
||||
rowSpan: 1,
|
||||
colSpan: 3,
|
||||
bgcolor: '#f8bbd0'
|
||||
},
|
||||
{
|
||||
id: 'Charges',
|
||||
numeric: 'center',
|
||||
disablePadding: false,
|
||||
label: 'Collection Amt',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
},
|
||||
{
|
||||
id: 'kilometer',
|
||||
numeric: 'center',
|
||||
disablePadding: false,
|
||||
label: 'Kms/Actual Kms',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
},
|
||||
|
||||
{
|
||||
id: 'Amount',
|
||||
numeric: 'center',
|
||||
disablePadding: true,
|
||||
label: 'Amount',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
},
|
||||
{
|
||||
id: 'action',
|
||||
numeric: 'center',
|
||||
disablePadding: true,
|
||||
label: 'Action',
|
||||
rowSpan: 2,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f3e5f5'
|
||||
}
|
||||
];
|
||||
|
||||
const headCells1 = [
|
||||
{
|
||||
id: 'pending',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'pending',
|
||||
rowSpan: 1,
|
||||
colSpan: 1,
|
||||
bgcolor: '#ffcdd2'
|
||||
},
|
||||
{
|
||||
id: 'cancelled',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'cancelled',
|
||||
rowSpan: 1,
|
||||
colSpan: 1,
|
||||
bgcolor: '#ffcdd2'
|
||||
},
|
||||
{
|
||||
id: 'completed',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'completed',
|
||||
rowSpan: 1,
|
||||
colSpan: 1,
|
||||
bgcolor: '#ffcdd2'
|
||||
},
|
||||
|
||||
{
|
||||
id: 'pending',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'pending',
|
||||
rowSpan: 1,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f8bbd0'
|
||||
},
|
||||
|
||||
{
|
||||
id: 'cancelled',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'cancelled',
|
||||
rowSpan: 1,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f8bbd0'
|
||||
},
|
||||
{
|
||||
id: 'completed',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'completed',
|
||||
rowSpan: 1,
|
||||
colSpan: 1,
|
||||
bgcolor: '#f8bbd0'
|
||||
}
|
||||
];
|
||||
const getColorByValue = (value) => {
|
||||
return Number(value) !== 0 ? 'red' : 'inherit';
|
||||
};
|
||||
const coloredCell = (value) => <Typography sx={{ color: getColorByValue(value) }}>{value}</Typography>;
|
||||
|
||||
// ==============================|| MUI TABLE - HEADER ||============================== //
|
||||
|
||||
function EnhancedTableHead() {
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{headCells.map((headCell) => (
|
||||
<TableCell
|
||||
key={headCell.id}
|
||||
align={headCell.numeric == 'center' ? 'center' : headCell.numeric == 'true' ? 'left' : 'right'}
|
||||
padding={headCell.disablePadding ? 'none' : 'normal'}
|
||||
rowSpan={headCell.rowSpan}
|
||||
colSpan={headCell.colSpan}
|
||||
sx={{ bgcolor: headCell.bgcolor }}
|
||||
>
|
||||
{headCell.label}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
{headCells1.map((headCell) => (
|
||||
<TableCell
|
||||
key={headCell.id}
|
||||
align={headCell.numeric ? 'right' : 'left'}
|
||||
padding={headCell.disablePadding ? 'none' : 'normal'}
|
||||
rowSpan={headCell.rowSpan}
|
||||
colSpan={headCell.colSpan}
|
||||
sx={{ bgcolor: headCell.bgcolor }}
|
||||
>
|
||||
{headCell.label}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
}
|
||||
|
||||
// ==============================|| fetchOrdersSummary (orders summary)||============================== //
|
||||
const {
|
||||
data: rows,
|
||||
isLoading: isLoadingReports,
|
||||
isError: isErrorReports, //true or false
|
||||
error: reportsError
|
||||
} = useQuery({
|
||||
queryKey: [appId, tenantid, locationid, startdate, enddate],
|
||||
queryFn: tenantid ? getreportlocationsummary : getreportsummary
|
||||
});
|
||||
// ==============================|| getTenants ||============================== //
|
||||
|
||||
const {
|
||||
data: tenantlist
|
||||
} = useQuery({
|
||||
queryKey: ['tenantlist', appId],
|
||||
queryFn: () => getTenants(appId), // Ensure appId is passed
|
||||
enabled: appId !== 0 // Ensures query runs only when appId is valid
|
||||
});
|
||||
// ==============================|| gettenantlocations ||============================== //
|
||||
|
||||
const {
|
||||
data: locationlist
|
||||
} = useQuery({
|
||||
queryKey: ['gettenantlocations', tenantid],
|
||||
queryFn: () => gettenantlocations(tenantid), // Ensure appId is passed
|
||||
enabled: tenantid !== 0 // Ensures query runs only when appId is valid
|
||||
});
|
||||
// ==============================|| fetchridersummary by tenid (orders summary)||============================== //
|
||||
|
||||
// ==============================|| getuserreportsummary by tenid (orders summary)||============================== //
|
||||
const getuserreportsummary = async (tenantid) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const riderRes = await axios.get(
|
||||
`${process.env.REACT_APP_URL}/deliveries/getuserreportsummary/?&tenantid=${tenantid}&fromdate=${startdate}&todate=${enddate}`
|
||||
);
|
||||
console.log('riderRes', riderRes.data.details);
|
||||
setRidersdata(riderRes.data.details);
|
||||
} catch (error) {
|
||||
console.log('riderRes', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// ==============================|| getriderlocationsummary by tenid (orders summary)||============================== //
|
||||
const getriderlocationsummary = async (id) => {
|
||||
try {
|
||||
const riderRes = await axios.get(
|
||||
`${process.env.REACT_APP_URL}/deliveries/getriderlocationsummary/?&tenantid=${tenantid}&locationid=${id}&fromdate=${startdate}&todate=${enddate}`
|
||||
);
|
||||
console.log('riderRes', riderRes.data.details);
|
||||
setRidersdata(riderRes.data.details);
|
||||
} catch (error) {
|
||||
console.log('riderRes', error);
|
||||
}
|
||||
};
|
||||
|
||||
// ==============================|| calculate||============================== //
|
||||
const calculate = () => {
|
||||
let calculatedTotal = 0;
|
||||
let ordersTotal = 0;
|
||||
let Orderpending = 0;
|
||||
let OrderComplete = 0;
|
||||
let OrderCancel = 0;
|
||||
let deliverypending = 0;
|
||||
let deliverycomplete = 0;
|
||||
let deliverycancel = 0;
|
||||
rows &&
|
||||
rows.forEach((row) => {
|
||||
calculatedTotal += row.charges;
|
||||
ordersTotal += row.totalorders;
|
||||
Orderpending += row.Orderspending;
|
||||
OrderComplete += row.orderscompleted;
|
||||
OrderCancel += row.orderscancelled;
|
||||
deliverypending += row.deliveriespending;
|
||||
deliverycomplete += row.deliveriescompleted;
|
||||
deliverycancel += row.deliveriescancelled;
|
||||
});
|
||||
// Update the state after the calculation is done
|
||||
settotal(calculatedTotal);
|
||||
settotalOrders(ordersTotal);
|
||||
setTotalOrderPend(Orderpending);
|
||||
setTotalOrderComplete(OrderComplete);
|
||||
setTotalOrderCancel(OrderCancel);
|
||||
setTotalDeliPend(deliverypending);
|
||||
setTotalDeliComplete(deliverycomplete);
|
||||
setTotalDeliCancel(deliverycancel);
|
||||
};
|
||||
useEffect(() => {
|
||||
calculate();
|
||||
}, [rows]);
|
||||
|
||||
// ==============================|| fetchAppLocations ||============================== //
|
||||
const fetchAppLocations = async () => {
|
||||
try {
|
||||
const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`);
|
||||
const updatedLocations = [
|
||||
...locationRes.data.details,
|
||||
{ locationname: 'All', applocationid: 0 } // Add your new object here
|
||||
];
|
||||
console.log('fetchAppLocations', updatedLocations);
|
||||
setLocations(updatedLocations);
|
||||
} catch (err) {
|
||||
console.log('locationRes', err);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchAppLocations();
|
||||
}, []);
|
||||
|
||||
if (isErrorReports) console.log('An error has occurred:(isErrorReports) ' + reportsError.message);
|
||||
|
||||
return (
|
||||
<>
|
||||
{(loading || isLoadingReports) && (
|
||||
<>
|
||||
{/* <CircularLoader /> */}
|
||||
<Loader />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* <Backdrop
|
||||
sx={{
|
||||
color: '#fff',
|
||||
zIndex: (theme) => theme.zIndex.drawer + 1
|
||||
}}
|
||||
open={loading || isLoadingReports} // when loader = true, backdrop covers the page
|
||||
>
|
||||
<CircularLoader color="inherit" />
|
||||
</Backdrop> */}
|
||||
|
||||
<TitleCard
|
||||
title="Orders Summary"
|
||||
secondary={
|
||||
<LocationAutocomplete
|
||||
ref={locationRef}
|
||||
locaName={locaName}
|
||||
setAppId={setAppId}
|
||||
setLocoName={setLocoName}
|
||||
setPage={setPage}
|
||||
sx={{ minWidth: 280, width: '100%', flex: { custom550: 0 } }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Stack
|
||||
display={'flex'}
|
||||
flexDirection={'row'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'space-between'}
|
||||
flexWrap={'wrap'}
|
||||
gap={1}
|
||||
sx={{ border: '1px solid', borderColor: 'bg.main', p: 2 }}
|
||||
>
|
||||
<Stack>
|
||||
{startdate && enddate && (
|
||||
<Stack direction="row" gap={2} flexWrap={'wrap'}>
|
||||
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" size="small" />
|
||||
<Tooltip title="Date Filter" placement="top">
|
||||
<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' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: 2,
|
||||
flexWrap: 'wrap',
|
||||
flexGrow: 1,
|
||||
justifyContent: { xs: 'start', custom950: 'right' }
|
||||
}}
|
||||
>
|
||||
<Autocomplete
|
||||
options={tenantlist || []}
|
||||
value={tenantValue}
|
||||
sx={{ minWidth: 250, flex: { xs: 1, custom950: 0 } }}
|
||||
onOpen={(event) => {
|
||||
if (!appId) {
|
||||
event.preventDefault();
|
||||
OpenToast('Please select a your app location first!', 'warning', 3000);
|
||||
setTimeout(() => {
|
||||
locationRef.current?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}}
|
||||
onChange={(e, val, reason) => {
|
||||
if (reason === 'clear') {
|
||||
setTenantid(0);
|
||||
setTenantValue(null);
|
||||
setLocationid(0);
|
||||
setLocationValue(null);
|
||||
} else {
|
||||
setTenantid(val?.tenantid || 0);
|
||||
setTenantValue(val);
|
||||
setLocationid(val.locationid);
|
||||
setLocationValue(null);
|
||||
}
|
||||
}}
|
||||
renderInput={(params) => <TextField {...params} inputRef={tenantRef} label="Select Tenant" />}
|
||||
/>
|
||||
{/* ==================================================== || Location Autocomplete || ==================================================== */}
|
||||
<Autocomplete
|
||||
options={locationlist || []}
|
||||
getOptionLabel={(option) => `${option.locationname} (${option.suburb})` || ''}
|
||||
value={locationValue}
|
||||
sx={{ minWidth: 250, flex: { xs: 1, custom950: 0 } }}
|
||||
onOpen={(event) => {
|
||||
if (!appId && !tenantid) {
|
||||
event.preventDefault();
|
||||
OpenToast('Please select a your Location and Tenant first!', 'warning', 3000);
|
||||
setTimeout(() => {
|
||||
locationRef.current?.focus();
|
||||
}, 0);
|
||||
} else if (!tenantid) {
|
||||
event.preventDefault();
|
||||
OpenToast('Please select a your Tenant first!', 'warning', 3000);
|
||||
setTimeout(() => {
|
||||
tenantRef.current?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}}
|
||||
onChange={(e, val, reason) => {
|
||||
if (reason === 'clear') {
|
||||
setLocationid(0);
|
||||
setLocationValue(null);
|
||||
} else {
|
||||
setLocationid(val.locationid || 0);
|
||||
setLocationValue(val);
|
||||
}
|
||||
}}
|
||||
renderInput={(params) => <TextField {...params} label="Select Location" />}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<MainCard content={false}>
|
||||
<TableContainer
|
||||
sx={{
|
||||
overflow: 'auto',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '12px', // scroll bar width
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: theme.palette.primary.main, // thumb color
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
backgroundColor: theme.palette.primary.dark, // hover color
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
backgroundColor: theme.palette.primary.lighter,
|
||||
cursor: 'pointer'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Table>
|
||||
{/* ============================================ ||EnhancedTableHead || ============================================ */}
|
||||
|
||||
<EnhancedTableHead />
|
||||
{/* ============================================ || TableBody || ============================================ */}
|
||||
|
||||
<TableBody>
|
||||
{isLoadingReports && <OrdersTableSkeleton col={8} />}
|
||||
{rows?.length == 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={13} rowSpan={40}>
|
||||
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
|
||||
<Empty />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{(rows?.length != 0 || rows) &&
|
||||
rows?.map((row, index) => (
|
||||
<>
|
||||
{/* ============================================ || tablerow 1 || ============================================ */}
|
||||
|
||||
<TableRow
|
||||
key={row.tenantname || row.locationname}
|
||||
// sx={{
|
||||
// cursor: openRow === row.tenantname ? 'pointer' : null
|
||||
// }}
|
||||
>
|
||||
<TableCell>{index + 1}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Stack spacing={1}>
|
||||
<Typography sx={{ whiteSpace: 'noWrap' }}>{tenantid ? row.locationname : row.tenantname} </Typography>
|
||||
<Typography variant="subtitle2">Id : {tenantid ? row.locationid : row.tenantid} </Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.totalorders)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.Orderspending)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.orderscancelled)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.orderscompleted)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriespending)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriescancelled)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriescompleted)}</TableCell>
|
||||
<TableCell align="center">
|
||||
{/* <Tooltip title="Cash on Delivery" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={`₹${Number(row.payondelivery).toFixed(2)}`}
|
||||
variant="combined"
|
||||
color={row.payondelivery ? 'error' : 'secondary'}
|
||||
sx={{
|
||||
mb: 1,
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<br /> */}
|
||||
{/* <Tooltip title="Pay Later" placement="bottom"> */}
|
||||
<Chip
|
||||
size="small"
|
||||
label={`₹${Number(row.collectionamt).toFixed(2)}`}
|
||||
variant="combined"
|
||||
color={row.collectionamt ? 'warning' : 'secondary'}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
{/* </Tooltip> */}
|
||||
</TableCell>{' '}
|
||||
<TableCell align="center">
|
||||
<Tooltip title="kms" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${Number(row.kms).toFixed(2)}`}
|
||||
color={row.kms ? 'error' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
mb: 1,
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<br />
|
||||
<Tooltip title="Actual kms" placement="bottom">
|
||||
<Chip
|
||||
size="small"
|
||||
label={Number(row.cumulativekms).toFixed(2)}
|
||||
color={row.cumulativekms ? 'success' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Tooltip title="Delivery Amount" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
|
||||
color={row.deliveryamt || row.charges ? 'primary' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 100
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<IconButton
|
||||
aria-label="expand row"
|
||||
size="small"
|
||||
// onClick={() => {
|
||||
// setRidersdata([]); // yo avoid appending new data list to exiting
|
||||
// setTimeout(() => {
|
||||
// openRow !== row.tenantname && (tenantid
|
||||
// ? getriderlocationsummary(row.locationid)
|
||||
// : getuserreportsummary(row.tenantid));
|
||||
// setOpenRow(openRow === row.tenantname ? null : row.tenantname);
|
||||
// }, 0);
|
||||
// }}
|
||||
onClick={() => {
|
||||
if (tenantid) {
|
||||
// if tenant selected and shows the location list
|
||||
const isOpening = openRow !== row.tenantname;
|
||||
// toggle row
|
||||
setOpenRow(isOpening ? row.tenantname : null);
|
||||
if (!isOpening) return; // ❌ closing → don't call API
|
||||
// clear old data only when opening
|
||||
setRidersdata([]);
|
||||
// ✅ call correct API
|
||||
getriderlocationsummary(row.locationid);
|
||||
} else {
|
||||
const isOpening = openRow !== row.locationname;
|
||||
|
||||
// toggle row
|
||||
setOpenRow(isOpening ? row.locationname : null);
|
||||
|
||||
if (!isOpening) return; // ❌ closing → don't call API
|
||||
|
||||
// clear old data only when opening
|
||||
setRidersdata([]);
|
||||
|
||||
// ✅ call correct API
|
||||
|
||||
getuserreportsummary(row.tenantid);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
bgcolor: openRow === row.tenantname ? 'primary.main' : null,
|
||||
color: openRow === row.tenantname ? 'white' : null,
|
||||
'&:hover': {
|
||||
bgcolor: openRow === row.tenantname ? 'primary.main' : '#e1bee7'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Tooltip title="View Riders" placement="top">
|
||||
{tenantid ? (
|
||||
openRow === row.tenantname ? (
|
||||
<KeyboardArrowUpIcon />
|
||||
) : (
|
||||
<KeyboardArrowDownIcon />
|
||||
)
|
||||
) : openRow === row.locationname ? (
|
||||
<KeyboardArrowUpIcon />
|
||||
) : (
|
||||
<KeyboardArrowDownIcon />
|
||||
)}
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* ============================================ || collapsive row || ============================================ */}
|
||||
|
||||
{(tenantid ? openRow === row.tenantname : openRow === row.locationname) && (
|
||||
<TableRow
|
||||
// sx={{
|
||||
// cursor: openRow === row.tenantname ? 'pointer' : null,
|
||||
// '&:hover': {
|
||||
// bgcolor: 'white!important'
|
||||
// }
|
||||
// }}
|
||||
>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
|
||||
<Box sx={{ margin: 1, border: '1px solid', borderColor: 'secondary.lighter' }}>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow sx={{ bgcolor: 'primary.lighter' }}>
|
||||
<TableCell>#</TableCell>
|
||||
<TableCell>Rider</TableCell>
|
||||
<TableCell align="center">Orders</TableCell>
|
||||
<TableCell align="center">Deliveries</TableCell>
|
||||
<TableCell align="center">Pending</TableCell>
|
||||
<TableCell align="center">Cancelled</TableCell>
|
||||
<TableCell align="center">Completed</TableCell>
|
||||
<TableCell align="center">Collection Amt</TableCell>
|
||||
<TableCell align="center">kms/Actual kms</TableCell>
|
||||
<TableCell align="center">Charges</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{loading && <OrdersTableSkeleton col={5} />}
|
||||
{/* {ridersdata?.length == 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={13} rowSpan={40}>
|
||||
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
|
||||
<Empty />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)} */}
|
||||
{ridersdata?.map((row, index) => (
|
||||
<TableRow key={row.tenantname}>
|
||||
<TableCell>{index + 1}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Stack>
|
||||
<Typography sx={{ whiteSpace: 'noWrap' }}>{` ${row.firstname} ${row.lastname}`}</Typography>
|
||||
<Typography variant="subtitle2" sx={{ whiteSpace: 'noWrap' }}>
|
||||
{row.ridercontact}
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" sx={{ whiteSpace: 'noWrap' }}>
|
||||
ID : {row.userid}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center">{coloredCell(row.orderscreated)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.totalorders)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriespending)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriescancelled)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriescompleted)}</TableCell>
|
||||
{/* <TableCell align="center">{coloredCell(row.picked)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.active)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.skipped)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.cancelled)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.delivered)}</TableCell> */}
|
||||
<TableCell align="center">
|
||||
{/* <Tooltip title="Cash on Delivery" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={formatNumberToRupees(row.payondelivery)}
|
||||
color={row.payondelivery ? 'error' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
mb: 1,
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<br /> */}
|
||||
{/* <Tooltip title="Paylater" placement="top"> */}
|
||||
<Chip
|
||||
size="small"
|
||||
label={formatNumberToRupees(row.collectionamt)}
|
||||
color={row.collectionamt ? 'warning' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
{/* </Tooltip> */}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Tooltip title="kms" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={row.kms.toFixed(2)}
|
||||
color={row.kms ? 'error' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
mb: 1,
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<br />
|
||||
<Tooltip title="Actual kms" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={row.cumulativekms.toFixed(2)}
|
||||
color={row.cumulativekms ? 'success' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<Tooltip title="Delivery Amount" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
|
||||
color={row.deliveryamt || row.charges ? 'primary' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 100
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
|
||||
{rows?.length != 0 && rows && (
|
||||
<TableRow sx={{}}>
|
||||
<TableCell colSpan={2}>
|
||||
<Typography variant="h5">Total</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5">{totalOrders}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5">{totalOrderPend}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5"> {totalOrderCancel}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5">{totalOrderComplete}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5">{totalDeliPend}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5"> {totalDeliCancel}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="h5">{totalDeliComplete}</Typography>
|
||||
</TableCell>
|
||||
<TableCell></TableCell>
|
||||
<TableCell></TableCell>
|
||||
<TableCell align="right" sx={{ pr: -2 }}>
|
||||
<Typography variant="h5">{formatNumberToRupees(total)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell></TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
<Divider />
|
||||
</MainCard>
|
||||
{/* ============================================ || date filter dialog || ============================================ */}
|
||||
<DateFilterDialog
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
onSelect={(range) => {
|
||||
setStartdate(range.startDate);
|
||||
setEnddate(range.endDate);
|
||||
setDatestatus(range.label);
|
||||
console.log('Selected Date Range:', range);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
296
src/pages/nearle/reports/ridersLogs.js
Normal file
296
src/pages/nearle/reports/ridersLogs.js
Normal file
@@ -0,0 +1,296 @@
|
||||
import React, { useState, useEffect, Fragment } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Drawer,
|
||||
IconButton,
|
||||
Toolbar,
|
||||
Typography,
|
||||
AppBar,
|
||||
useMediaQuery,
|
||||
Divider,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
useTheme,
|
||||
ListItemAvatar,
|
||||
Stack,
|
||||
Button,
|
||||
Checkbox,
|
||||
Skeleton
|
||||
} from '@mui/material';
|
||||
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import SearchBar from 'components/nearle_components/SearchBar';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchRidersLogs } from 'pages/api/api';
|
||||
import RiderLocationMap from './RiderLocationMap';
|
||||
import MainCard from 'components/MainCard';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import error500 from 'assets/images/maintenance/Error500.png';
|
||||
|
||||
const drawerWidth = 350;
|
||||
|
||||
const RidersLogs = () => {
|
||||
const theme = useTheme();
|
||||
const isDesktop = useMediaQuery('(min-width:900px)');
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedRiders, setSelectedRiders] = useState([]);
|
||||
const [riderSearch, setRiderSearch] = useState('');
|
||||
const appId = 1;
|
||||
const {
|
||||
data: riders,
|
||||
isLoading: ridersIsLoading,
|
||||
isFetching: riderIsFetching,
|
||||
refetch: riderLogsRefetch,
|
||||
error: riderLogsError
|
||||
} = useQuery({
|
||||
queryKey: [appId, dayjs().format('YYYY-MM-DD'), riderSearch],
|
||||
queryFn: fetchRidersLogs,
|
||||
refetchInterval: 5 * 60 * 1000
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// const sortedRiders = riders?.sort((a, b) => a.firstname.localeCompare(b.firstname));
|
||||
setSelectedRiders(riders);
|
||||
}, [riders]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('selectedRiders', selectedRiders);
|
||||
}, [selectedRiders]);
|
||||
|
||||
useEffect(() => {
|
||||
setOpen(isDesktop);
|
||||
}, [isDesktop]);
|
||||
|
||||
return (
|
||||
<MainCard content={false}>
|
||||
<Box sx={{ display: 'flex', width: '100%', height: '100%', position: 'relative' }}>
|
||||
{/* Drawer */}
|
||||
<Drawer
|
||||
variant={isDesktop ? 'persistent' : 'temporary'}
|
||||
open={open}
|
||||
onClose={() => !isDesktop && setOpen(false)}
|
||||
ModalProps={{ keepMounted: true }}
|
||||
sx={{
|
||||
'& .MuiDrawer-paper': {
|
||||
width: drawerWidth,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
height: '100%',
|
||||
overflowY: 'auto',
|
||||
transition: 'transform 0.35s ease-in-out',
|
||||
zIndex: 13
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Search */}
|
||||
<Box sx={{ position: 'sticky', top: 0, zIndex: 1 }}>
|
||||
<SearchBar
|
||||
value={riderSearch}
|
||||
placeholder="Search Rider"
|
||||
onChange={(e) => setRiderSearch(e.target.value)}
|
||||
sx={{
|
||||
height: 60,
|
||||
bgcolor: 'white',
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderBottom: '1px solid',
|
||||
borderColor: theme.palette.secondary.light
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<List>
|
||||
<ListItem sx={{ cursor: 'pointer', '&:hover': { bgcolor: theme.palette.secondary.lighter }, bgcolor: 'white', mt: -1 }}>
|
||||
<ListItemAvatar>
|
||||
<Checkbox
|
||||
checked={riders?.length == selectedRiders?.length}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedRiders(riders);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary="All" />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</List>
|
||||
</Box>
|
||||
{/* Rider List */}
|
||||
<List>
|
||||
{/* Individuals */}
|
||||
{ridersIsLoading || riderIsFetching
|
||||
? Array.from({ length: 10 }).map((_, index) => (
|
||||
<Fragment key={index}>
|
||||
<ListItem sx={{ py: 1.5, px: 2 }}>
|
||||
<ListItemAvatar>
|
||||
<Skeleton variant="circular" width={24} height={24} />
|
||||
</ListItemAvatar>
|
||||
|
||||
<ListItemText
|
||||
primary={<Skeleton variant="text" width="60%" height={22} />}
|
||||
secondary={<Skeleton variant="text" width="40%" height={18} />}
|
||||
/>
|
||||
|
||||
<Stack spacing={0.5} textAlign="right">
|
||||
<Skeleton variant="text" width={50} height={18} />
|
||||
<Skeleton variant="text" width={80} height={16} />
|
||||
</Stack>
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
</Fragment>
|
||||
))
|
||||
: riders?.map((row) => {
|
||||
return (
|
||||
<Fragment key={row.userid}>
|
||||
<ListItem
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
py: 1,
|
||||
px: 2,
|
||||
borderRadius: 1,
|
||||
'&:hover': { bgcolor: theme.palette.secondary.lighter }
|
||||
}}
|
||||
secondaryAction={
|
||||
<Stack textAlign="right" spacing={0.5}>
|
||||
<Typography variant="body2" noWrap sx={{ color: row.status == 'active' ? 'success.main' : 'error.main' }}>
|
||||
{row.userid}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" noWrap>
|
||||
{dayjs(row.logdate).format('DD/MM/YYYY hh:mm A')}
|
||||
</Typography>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Checkbox
|
||||
sx={{
|
||||
color: row.status == 'active' ? 'green' : 'red',
|
||||
'&.Mui-checked': {
|
||||
color: row.status == 'active' ? 'green' : 'red'
|
||||
}
|
||||
}}
|
||||
checked={
|
||||
// INDIVIDUAL CHECKED CONDITION
|
||||
selectedRiders?.length === 1 && selectedRiders[0]?.userid === row?.userid
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
// SELECT ONE RIDER
|
||||
setSelectedRiders([row]);
|
||||
} else {
|
||||
// UNCHECK -> SELECT ALL
|
||||
setSelectedRiders(riders);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography noWrap>
|
||||
{row.username?.slice(0, 25) || ''}
|
||||
{row.username?.length > 25 && '...'}
|
||||
|
||||
{/* {row.status === 'active' && <TaskAltIcon fontSize="small" color="success" sx={{ ml: 1 }} />} */}
|
||||
</Typography>
|
||||
}
|
||||
secondary={
|
||||
<Typography variant="caption" color="text.secondary" noWrap>
|
||||
{row.contactno || '##########'}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Drawer>
|
||||
|
||||
{/* 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',
|
||||
backgroundColor: 'white',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: theme.palette.secondary.light
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: '100%' }}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<IconButton color="primary" onClick={() => setOpen(!open)}>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h5" color="primary" sx={{ ml: 2 }}>
|
||||
Riders Locations
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
riderLogsRefetch();
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</Stack>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
{/* Map */}
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
overflow: 'auto',
|
||||
pt: '64px',
|
||||
pl: open && isDesktop ? `${drawerWidth}px` : 0,
|
||||
transition: 'padding-left 0.3s ease',
|
||||
minHeight: '80vh'
|
||||
}}
|
||||
>
|
||||
{(ridersIsLoading || riderIsFetching) && (
|
||||
<Box position="relative" width="100%" height="80vh" display="grid" placeItems="center">
|
||||
{/* <CircularLoader /> */}
|
||||
<Skeleton
|
||||
variant="rectangular"
|
||||
width="100%"
|
||||
height="100%"
|
||||
animation="wave"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
borderRadius: 1,
|
||||
zIndex: 1
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{selectedRiders?.length > 0 && <RiderLocationMap riderLocations={selectedRiders} />}
|
||||
{riderLogsError && (
|
||||
<Box sx={{ width: '100% ', height: '100%' }}>
|
||||
<img src={error500} alt="mantis" style={{ height: '100%', width: '100%' }} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</MainCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default RidersLogs;
|
||||
454
src/pages/nearle/reports/ridersSummary.js
Normal file
454
src/pages/nearle/reports/ridersSummary.js
Normal file
@@ -0,0 +1,454 @@
|
||||
import { React, useState, useEffect } from 'react';
|
||||
import TitleCard from 'pages/titleCard';
|
||||
import axios from 'axios';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchRidersSummary } from 'pages/api/api';
|
||||
|
||||
import { Empty } from 'antd';
|
||||
// material-ui
|
||||
import {
|
||||
Divider,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
Stack,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Chip,
|
||||
Collapse,
|
||||
Dialog,
|
||||
DialogContent
|
||||
} from '@mui/material';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import { IoLocationOutline } from 'react-icons/io5';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
import { CalendarMonth } from '@mui/icons-material';
|
||||
import MainCard from 'components/MainCard';
|
||||
import Loader from 'components/Loader';
|
||||
import DateFilterDialog from 'components/DateFilterDialog';
|
||||
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
|
||||
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
|
||||
import RidersRoutes from './RidersRoutes';
|
||||
import { OpenToast } from 'components/third-party/OpenToast';
|
||||
|
||||
function formatNumberToRupees(value) {
|
||||
return new Intl.NumberFormat('en-IN', {
|
||||
style: 'currency',
|
||||
currency: 'INR',
|
||||
minimumFractionDigits: 2
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
const getColorByValue = (value) => {
|
||||
return Number(value) !== 0 ? 'red' : 'inherit';
|
||||
};
|
||||
const coloredCell = (value) => <Typography sx={{ color: getColorByValue(value) }}>{value}</Typography>;
|
||||
|
||||
// ==============================|| MUI TABLE - ENHANCED ||============================== //
|
||||
|
||||
export default function RidersSummary() {
|
||||
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
|
||||
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
|
||||
const [locaName, setLocoName] = useState('All');
|
||||
const [open, setOpen] = useState(false);
|
||||
const [datestatus, setDatestatus] = useState('Today');
|
||||
const [total, settotal] = useState(0);
|
||||
const [tenantData, setTenantData] = useState([]);
|
||||
const [openRow, setOpenRow] = useState(null); // Initially no row is open
|
||||
// const [appId, setAppId] = useState(localStorage.getItem('applocationid'));
|
||||
const [appId, setAppId] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [mapOpen, setMapOpen] = useState(false);
|
||||
const [logDetails, setLogDetails] = useState(null);
|
||||
|
||||
// ==============================|| fetchRidersSummary (riders summary)||============================== //
|
||||
const { isLoading: isLoadingReports, data: rows } = useQuery({
|
||||
queryKey: ['ridersummary', appId, startdate, enddate],
|
||||
queryFn: fetchRidersSummary
|
||||
});
|
||||
|
||||
// ==============================|| fetchTenantSummary by rider (rider summary)||============================== //
|
||||
const fetchTenantSummary = async (riderUserid) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const tenantRes = await axios.get(
|
||||
`${process.env.REACT_APP_URL}/deliveries/getreportsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}`
|
||||
);
|
||||
console.log('tenantRes', tenantRes.data.details);
|
||||
setTenantData(tenantRes.data.details);
|
||||
} catch (error) {
|
||||
console.log('tenantRes', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ==============================|| getuserdeliverylogs (rider summary)||============================== //
|
||||
|
||||
const getuserdeliverylogs = async (userid) => {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${process.env.REACT_APP_URL}/deliveries/getuserdeliverylogs/?userid=${userid}&fromdate=2026-01-28&todate=2026-01-28 `
|
||||
);
|
||||
setLogDetails(response.data.details);
|
||||
} catch (err) {
|
||||
OpenToast(err?.message, 'error', 2000);
|
||||
console.log('getuserdeliverylogs', err.message);
|
||||
}
|
||||
};
|
||||
|
||||
// ==============================|| calculate||============================== //
|
||||
const calculate = async () => {
|
||||
let calculatedTotal = 0;
|
||||
rows &&
|
||||
rows.forEach((row, index) => {
|
||||
console.log(index, row.deliveryamt);
|
||||
calculatedTotal += row.deliveryamt;
|
||||
});
|
||||
// Update the state after the calculation is done
|
||||
settotal(calculatedTotal);
|
||||
console.log('calculatedTotal', calculatedTotal);
|
||||
};
|
||||
useEffect(() => {
|
||||
calculate();
|
||||
}, [rows]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{(isLoadingReports || loading) && (
|
||||
<>
|
||||
<Loader />
|
||||
{/* <CircularLoader /> */}
|
||||
</>
|
||||
)}
|
||||
<TitleCard title="Riders Summary" />
|
||||
|
||||
<MainCard
|
||||
content={false}
|
||||
title={
|
||||
<Stack
|
||||
display={'flex'}
|
||||
flexDirection={'row'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'space-between'}
|
||||
flexWrap={'wrap'}
|
||||
gap={1}
|
||||
sx={{ border: '1px solid', borderColor: 'bg.main', p: 2 }}
|
||||
>
|
||||
<Stack direction="column" alignItems="flex-start" spacing={1} sx={{}}>
|
||||
{startdate && enddate && (
|
||||
<Stack direction="row" spacing={2} flexWrap={'wrap'} gap={1}>
|
||||
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" />
|
||||
<Chip
|
||||
label={
|
||||
<Typography noWrap color="secondary">
|
||||
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
|
||||
</Typography>
|
||||
}
|
||||
variant="combined"
|
||||
color="warning"
|
||||
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" />
|
||||
{/* <Chip label={<Typography noWrap color="secondary">ALL</Typography>} variant="combined" color='warning' size='small' /> */}
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<LocationAutocomplete
|
||||
// ref={locationRef}
|
||||
locaName={locaName}
|
||||
setAppId={setAppId}
|
||||
setLocoName={setLocoName}
|
||||
// setPage={setPage}
|
||||
sx={{ minWidth: 250, maxWidth: 1000, flex: 1 }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
{/* table */}
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableCell># </TableCell>
|
||||
<TableCell>Rider </TableCell>
|
||||
<TableCell> Orders</TableCell>
|
||||
<TableCell> Pending</TableCell>
|
||||
{/* <TableCell> Assigned</TableCell>
|
||||
<TableCell>Accepted </TableCell>
|
||||
<TableCell>Arrived </TableCell>
|
||||
<TableCell>Picked </TableCell>
|
||||
<TableCell>Active </TableCell>
|
||||
<TableCell>Skipped </TableCell> */}
|
||||
<TableCell>Cancelled </TableCell>
|
||||
<TableCell>Delivered </TableCell>
|
||||
<TableCell align="center">KMS </TableCell>
|
||||
<TableCell align="center">Amount </TableCell>
|
||||
<TableCell align="center">Action </TableCell>
|
||||
</TableHead>
|
||||
{/* ============================================ || TableBody || ============================================ */}
|
||||
|
||||
{isLoadingReports && <OrdersTableSkeleton col={10} />}
|
||||
|
||||
<TableBody>
|
||||
{rows?.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={15}>
|
||||
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
|
||||
<Empty />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
{rows?.length != 0 &&
|
||||
rows?.map((row, index) => (
|
||||
<>
|
||||
{/* // ============================================ || tablerow 1 || ============================================ */}
|
||||
<TableRow
|
||||
key={row.tenantname}
|
||||
sx={{
|
||||
cursor: openRow === row.userid ? 'pointer' : null
|
||||
}}
|
||||
>
|
||||
<TableCell align="left">{index + 1}</TableCell>
|
||||
<TableCell>
|
||||
<Stack spacing={1}>
|
||||
<Typography sx={{ whiteSpace: 'noWrap' }}> {` ${row?.firstname} ${row?.lastname}`}</Typography>
|
||||
<Typography variant="subtitle2">Id : {row.userid} </Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{coloredCell(row.totalorders)}</TableCell>
|
||||
<TableCell>{coloredCell(row.pending)}</TableCell>
|
||||
{/* <TableCell >{coloredCell(row.assigned)}</TableCell>
|
||||
<TableCell >{coloredCell(row.accepted)}</TableCell>
|
||||
<TableCell >{coloredCell(row.arrived)}</TableCell>
|
||||
<TableCell >{coloredCell(row.picked)}</TableCell>
|
||||
<TableCell >{coloredCell(row.active)}</TableCell>
|
||||
<TableCell >{coloredCell(row.skipped)}</TableCell> */}
|
||||
<TableCell>{coloredCell(row.cancelled)}</TableCell>
|
||||
<TableCell>{coloredCell(row.delivered)}</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<Stack direction="column" spacing={1}>
|
||||
<Tooltip title="Kms" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${Number(row.kms).toFixed(2)}`}
|
||||
color={row.kms ? 'error' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Actual Kms" placement="bottom">
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${Number(row.cumulativekms).toFixed(2)}`}
|
||||
color={row.cumulativekms ? 'success' : 'secondary'}
|
||||
variant="combined"
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<Tooltip title="Total Amount" placement="top">
|
||||
<Chip
|
||||
size="small"
|
||||
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
|
||||
variant="combined"
|
||||
color={row.deliveryamt ? 'primary' : 'secondary'}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
minWidth: 100
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Stack display={'flex'} flexDirection={'row'} gap={1} alignItems={'center'}>
|
||||
<IconButton
|
||||
aria-label="expand row"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
const isOpening = openRow !== row.userid;
|
||||
|
||||
// toggle row
|
||||
setOpenRow(isOpening ? row.userid : null);
|
||||
|
||||
// ❌ closing → no API
|
||||
if (!isOpening) return;
|
||||
|
||||
// ✅ opening → call API
|
||||
fetchTenantSummary(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>
|
||||
<IconButton>
|
||||
<IoLocationOutline
|
||||
onClick={() => {
|
||||
setMapOpen(true);
|
||||
getuserdeliverylogs(row?.userid);
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* // ============================================ || collapsive row || ============================================ */}
|
||||
{openRow === row.userid && (
|
||||
<TableRow
|
||||
sx={{
|
||||
cursor: openRow === row.userid ? 'pointer' : null
|
||||
}}
|
||||
>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
|
||||
<Collapse in={true} timeout="auto" unmountOnExit>
|
||||
<MainCard content={false} sx={{ margin: 1 }}>
|
||||
<Table size="small" aria-label="purchases">
|
||||
<TableHead sx={{ bgcolor: 'primary.lighter' }}>
|
||||
<TableRow>
|
||||
<TableCell>#</TableCell>
|
||||
<TableCell>Client</TableCell>
|
||||
<TableCell align="center">All</TableCell>
|
||||
<TableCell align="center">Pending</TableCell>
|
||||
<TableCell align="center">Completed</TableCell>
|
||||
<TableCell align="center">Cancelled</TableCell>
|
||||
<TableCell align="center">Kms</TableCell>
|
||||
<TableCell align="center">Amount</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{loading && <OrdersTableSkeleton col={3} />}
|
||||
{tenantData?.map((row, index) => (
|
||||
<TableRow key={row.tenantname}>
|
||||
<TableCell align="left">{index + 1}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Stack direction="row" sx={{ ml: -2 }}>
|
||||
{row.tenantname}
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.totalorders)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriespending)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriescompleted)}</TableCell>
|
||||
<TableCell align="center">{coloredCell(row.deliveriescancelled)}</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<Chip
|
||||
size="small"
|
||||
label={row.kms}
|
||||
color="error"
|
||||
variant="combined"
|
||||
sx={{
|
||||
mr: 1,
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
<Chip
|
||||
size="small"
|
||||
label={row.cumulativekms}
|
||||
color="success"
|
||||
variant="combined"
|
||||
sx={{
|
||||
minWidth: 80
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<Chip
|
||||
size="small"
|
||||
label={formatNumberToRupees(row.charges >= row.deliveryamt ? row.charges : row.deliveryamt)}
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: '#e1bee7',
|
||||
border: '1px solid #662582 ',
|
||||
minWidth: 100
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</MainCard>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Divider />
|
||||
{rows?.length != 0 && (
|
||||
<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>
|
||||
)}
|
||||
</MainCard>
|
||||
<Dialog
|
||||
maxWidth={'xl'}
|
||||
fullScreen
|
||||
open={mapOpen}
|
||||
onClose={() => {
|
||||
setMapOpen(false);
|
||||
}}
|
||||
>
|
||||
<DialogContent>{logDetails && <RidersRoutes details={logDetails} />}</DialogContent>
|
||||
</Dialog>
|
||||
{/* ============================================= || filter Dialog | ============================================= */}
|
||||
<DateFilterDialog
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
onSelect={(range) => {
|
||||
setStartdate(range.startDate);
|
||||
setEnddate(range.endDate);
|
||||
setDatestatus(range.label);
|
||||
console.log('Selected Date Range:', range);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user