import { React, useState, useEffect, useRef } from 'react'; import axios from 'axios'; import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; // material-ui import { Avatar, Backdrop, Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Grid, IconButton, List, ListItem, Paper, Skeleton, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Tooltip, Typography, Autocomplete } from '@mui/material'; import { MdAssignment, MdMyLocation, MdGroups, MdPlace, MdDirectionsBike, MdCalendarMonth, MdFileDownload, MdHourglassEmpty, MdPersonPin, MdLocationOn, MdInventory2, MdRoute, MdSkipNext, MdCheckCircle, MdCancel, MdList, MdLocalShipping, MdStraighten, MdCurrencyRupee, MdMap, MdNoteAlt, MdClose } from 'react-icons/md'; import { FaCircleCheck } from 'react-icons/fa6'; import MapWithRoute from './mapWithRoute'; import CircularLoader from 'components/CircularLoader'; import { fetchDeliveries, fetchRidersList, gettenantlocations, getTenants } from 'pages/api/api'; import { CSVExport } from 'components/third-party/ReactTable'; import Loader from 'components/Loader'; import { enqueueSnackbar } from 'notistack'; import DateFilterDialog from 'components/DateFilterDialog'; import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; import LoaderWithImage from 'components/nearle_components/LoaderWithImage'; import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; import dayjs from 'dayjs'; import { OpenToast } from 'components/third-party/OpenToast'; import TableLoader from 'components/nearle_components/TableLoader'; var utc = require('dayjs/plugin/utc'); dayjs.extend(utc); const opentoast = (message, variant, time) => { enqueueSnackbar(message, { variant: variant, anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: time ? time : 1500 }); }; // ============================================================================ // Design tokens — shared with deliveries / tenants / customers / pricing pages. // ============================================================================ const DT = { radiusPill: 999, radiusCard: 16, shadowSoft: '0 14px 40px rgba(15, 23, 42, 0.10)', shadowMd: '0 8px 24px rgba(15, 23, 42, 0.08)', shadowPop: '0 18px 50px rgba(15, 23, 42, 0.18)', textPrimary: '#0f172a', textSecondary: '#64748b', textMuted: '#94a3b8', borderSubtle: '#e2e8f0', divider: '#f1f5f9', surface: '#ffffff', surfaceAlt: '#f8fafc' }; const a = (c, suffix) => `${c}${suffix}`; const tint = (c) => a(c, '08'); const soft = (c) => a(c, '18'); const ring = (c) => a(c, '26'); const edge = (c) => a(c, '55'); const BRAND = '#662582'; const BRAND_LIGHT = '#9255AB'; const SoftPaper = (props) => ( ); const AccentAvatar = ({ color, selected, size = 24, children }) => ( {children} ); const pillFieldSx = (color) => ({ '& .MuiOutlinedInput-root': { borderRadius: DT.radiusPill + 'px', bgcolor: tint(color), fontWeight: 600, '& fieldset': { borderColor: edge(color), borderWidth: 1.5 }, '&:hover fieldset': { borderColor: color }, '&.Mui-focused': { boxShadow: `0 0 0 3px ${ring(color)}` }, '&.Mui-focused fieldset': { borderColor: color, borderWidth: 2 } } }); // Status visual meta — semantic colours, NOT brand. Each lifecycle state has // its own colour so operators can recognise it at a glance. const STATUS_META = { all: { label: 'All', color: BRAND, icon: MdList }, pending: { label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty }, accepted: { label: 'Accepted', color: '#6366f1', icon: MdPersonPin }, arrived: { label: 'Arrived', color: '#06b6d4', icon: MdLocationOn }, picked: { label: 'Picked', color: '#8b5cf6', icon: MdInventory2 }, active: { label: 'Active', color: '#14b8a6', icon: MdRoute }, delivered: { label: 'Delivered', color: '#10b981', icon: MdCheckCircle }, skipped: { label: 'Skipped', color: '#f97316', icon: MdSkipNext }, cancelled: { label: 'Cancelled', color: '#ef4444', icon: MdCancel } }; const STATUS_TABS = ['all', 'pending', 'accepted', 'arrived', 'picked', 'active', 'delivered', 'skipped', 'cancelled']; // Soft pill used for metric cells (km, charges) inside the table. const MetricPill = ({ color, icon, label, tooltip }) => ( {icon} {label} ); // Stamp cell — date + time stack with skeleton fallback for empty timestamps. const StampCell = ({ value, formatDate, formatTime, success }) => { if (!value) { return ( ); } return ( {formatDate(value)} {formatTime(value)} {success && } ); }; // ==============================|| Orders Details ||============================== // // Haversine distance between two [lat, lng] points in kilometers. function haversineKm(a, b) { const R = 6371; // km const toRad = (d) => (d * Math.PI) / 180; const lat1 = toRad(a[0]); const lat2 = toRad(b[0]); const dLat = toRad(b[0] - a[0]); const dLon = toRad(b[1] - a[1]); const s = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2; return 2 * R * Math.asin(Math.min(1, Math.sqrt(s))); } function kalmanSmoothGps(pings, options = {}) { if (!Array.isArray(pings) || pings.length === 0) return []; // 1. Filter out obviously invalid coordinate pings (e.g. 0,0 or NaN) const cleanedPings = pings.filter(p => Number.isFinite(p.lat) && Number.isFinite(p.lng) && (Math.abs(p.lat) > 0.1 || Math.abs(p.lng) > 0.1) ); if (cleanedPings.length === 0) return []; if (cleanedPings.length === 1) { return [{ lat: cleanedPings[0].lat, lng: cleanedPings[0].lng, logdate: cleanedPings[0].logdate, _ts: cleanedPings[0]._ts }]; } const processNoise = options.processNoise != null ? options.processNoise : 1e-10; const measurementNoise = options.measurementNoise != null ? options.measurementNoise : 2e-9; const outlierGate = options.outlierGate != null ? options.outlierGate : 9.0; const maxSpeedKmh = options.maxSpeedKmh != null ? options.maxSpeedKmh : 120; const tsOf = (p) => p._ts || (p.logdate ? new Date(p.logdate).getTime() : 0); // 2. Scan forward to find the first valid starting anchor let startIdx = 0; while (startIdx < cleanedPings.length - 1) { const p0 = cleanedPings[startIdx]; const p1 = cleanedPings[startIdx + 1]; const ts0 = tsOf(p0); const ts1 = tsOf(p1) || ts0 + 1000; const dtSec = Math.max(0.001, (ts1 - ts0) / 1000); const km = haversineKm([p0.lat, p0.lng], [p1.lat, p1.lng]); const speedKmh = (km / dtSec) * 3600; if (speedKmh <= maxSpeedKmh) { break; } else { // Speed is too high. Check if p1->p2 is normal (meaning p0 is the outlier) if (startIdx + 2 < cleanedPings.length) { const p2 = cleanedPings[startIdx + 2]; const ts2 = tsOf(p2) || ts1 + 1000; const dtSec12 = Math.max(0.001, (ts2 - ts1) / 1000); const km12 = haversineKm([p1.lat, p1.lng], [p2.lat, p2.lng]); const speedKmh12 = (km12 / dtSec12) * 3600; if (speedKmh12 <= maxSpeedKmh) { startIdx = startIdx + 1; continue; } } startIdx++; } } // 3. Teleport filter starting from the valid anchor const accepted = [cleanedPings[startIdx]]; let lastTs = tsOf(cleanedPings[startIdx]); for (let i = startIdx + 1; i < cleanedPings.length; i++) { const p = cleanedPings[i]; const ts = tsOf(p) || lastTs + 1000; const dtSec = Math.max(0.001, (ts - lastTs) / 1000); const prev = accepted[accepted.length - 1]; const km = haversineKm([prev.lat, prev.lng], [p.lat, p.lng]); const speedKmh = (km / dtSec) * 3600; if (speedKmh > maxSpeedKmh) continue; accepted.push(p); lastTs = ts; } if (accepted.length < 2) { return accepted.map((p) => ({ lat: p.lat, lng: p.lng, logdate: p.logdate, _ts: p._ts })); } // Run a 1D Kalman + RTS smoother over one axis. Returns smoothed // positions parallel to `accepted`. const smoothAxis = (axisKey) => { const N = accepted.length; const xPost = new Array(N); const pPost = new Array(N); const xPrior = new Array(N); const pPrior = new Array(N); const dtArr = new Array(N); const ts0 = tsOf(accepted[0]); const ts1 = tsOf(accepted[1]); const dt01 = Math.max(0.1, (ts1 - ts0) / 1000); const v0 = (accepted[1][axisKey] - accepted[0][axisKey]) / dt01; xPost[0] = [accepted[0][axisKey], v0]; pPost[0] = [measurementNoise, 0, 0, 1]; xPrior[0] = xPost[0].slice(); pPrior[0] = pPost[0].slice(); dtArr[0] = 0; let prevTs = ts0; for (let i = 1; i < N; i++) { const ts = tsOf(accepted[i]) || prevTs + 1000; const dt = Math.max(0.1, (ts - prevTs) / 1000); prevTs = ts; dtArr[i] = dt; // Predict const [xPrev, vPrev] = xPost[i - 1]; const xPredPos = xPrev + vPrev * dt; const xPredVel = vPrev; const [pp00, pp01, pp10, pp11] = pPost[i - 1]; const dt2 = dt * dt; const dt3 = dt2 * dt; const dt4 = dt3 * dt; const np00 = pp00 + dt * (pp01 + pp10) + dt2 * pp11 + (dt4 / 4) * processNoise; const np01 = pp01 + dt * pp11 + (dt3 / 2) * processNoise; const np10 = pp10 + dt * pp11 + (dt3 / 2) * processNoise; const np11 = pp11 + dt2 * processNoise; xPrior[i] = [xPredPos, xPredVel]; pPrior[i] = [np00, np01, np10, np11]; // Update const z = accepted[i][axisKey]; const y = z - xPredPos; const S = np00 + measurementNoise; const mahal2 = (y * y) / S; if (mahal2 > outlierGate) { xPost[i] = [xPredPos, xPredVel]; pPost[i] = [np00, np01, np10, np11]; continue; } const K0 = np00 / S; const K1 = np10 / S; const newPos = xPredPos + K0 * y; const newVel = xPredVel + K1 * y; xPost[i] = [newPos, newVel]; pPost[i] = [ (1 - K0) * np00, (1 - K0) * np01, np10 - K1 * np00, np11 - K1 * np01 ]; } // RTS backward smoother const xSmooth = new Array(N); xSmooth[N - 1] = xPost[N - 1].slice(); for (let i = N - 2; i >= 0; i--) { const dt = dtArr[i + 1]; const [pp00, pp01, pp10, pp11] = pPost[i]; const a = pp00 + dt * pp01; const b = pp01; const c = pp10 + dt * pp11; const d = pp11; const [q00, q01, q10, q11] = pPrior[i + 1]; const det = q00 * q11 - q01 * q10; if (!Number.isFinite(det) || Math.abs(det) < 1e-30) { xSmooth[i] = xPost[i].slice(); continue; } const inv00 = q11 / det; const inv01 = -q01 / det; const inv10 = -q10 / det; const inv11 = q00 / det; const c00 = a * inv00 + b * inv10; const c01 = a * inv01 + b * inv11; const c10 = c * inv00 + d * inv10; const c11 = c * inv01 + d * inv11; const dxPos = xSmooth[i + 1][0] - xPrior[i + 1][0]; const dxVel = xSmooth[i + 1][1] - xPrior[i + 1][1]; xSmooth[i] = [ xPost[i][0] + c00 * dxPos + c01 * dxVel, xPost[i][1] + c10 * dxPos + c11 * dxVel ]; } return xSmooth.map((s) => s[0]); }; const lats = smoothAxis('lat'); const lngs = smoothAxis('lng'); return accepted.map((p, i) => ({ lat: lats[i], lng: lngs[i], logdate: p.logdate, _ts: p._ts })); } export default function OrdersDetails() { const loadMoreRef = useRef(); const containerRef = useRef(); const locationRef = useRef(null); const tenantRef = useRef(null); const userid = localStorage.getItem('userid'); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(50); const [locaName, setLocoName] = useState('All'); const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD')); const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD')); const [open, setOpen] = useState(false); const [mapOpen, setMapOpen] = useState(false); const [datestatus, setDatestatus] = useState('Today'); const [appId, setAppId] = useState(0); const [searchword, setSearchword] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); const [riderCoordinates, setRiderCoordinates] = useState([]); const [riderStart, setRiderStart] = useState(); const [riderEnd, setRiderEnd] = useState(); const [mapTenant, setMapTenant] = useState({}); const [isLoading, setIsLoading] = useState(false); let [total, settotal] = useState(0); let [deliveredLenght, setDeliveredLenght] = useState(0); let [pendingLenght, setPendingLenght] = useState(0); let [cancelLenght, setCancelLenght] = useState(0); let [assignLenght, setAssignLenght] = useState(0); let [pickedLenght, setPickedLenght] = useState(0); let [activeLenght, setActiveLenght] = useState(0); let [arrivesLenght, setArrivedLenght] = useState(0); let [skippedLenght, setSkippedLenght] = useState(0); const [currentStatus, setCurrentStatus] = useState('All'); const [locationid, setLocationid] = useState(0); const [tenantid, setTenantid] = useState(0); const [tenantValue, setTenantValue] = useState(null); const [locationValue, setLocationValue] = useState(null); const [selectedRider, setSelectedRider] = useState(); const [riderValue, setRiderValue] = useState(null); const [reportDialog, setReportDialog] = useState(false); const [logsLoading, setLogsLoading] = useState(false); // Map status key (lowercase) → count value from the summary endpoint. const statusCountByKey = { all: total, pending: pendingLenght, accepted: assignLenght, arrived: arrivesLenght, picked: pickedLenght, active: activeLenght, delivered: deliveredLenght, skipped: skippedLenght, cancelled: cancelLenght }; // Cascading clears so changing a parent filter resets its children. useEffect(() => { setTenantid(0); setTenantValue(null); setLocationid(0); setLocationValue(null); setSelectedRider(null); setRiderValue(null); }, [appId]); useEffect(() => { setLocationid(0); setLocationValue(null); setRiderValue(null); }, [tenantid]); useEffect(() => { setRiderValue(null); }, [locationid]); // ============== Haversine distance calculation for the map route ============== function calculateDistance(lat1, lon1, lat2, lon2) { const R = 6371; const dLat = (lat2 - lat1) * (Math.PI / 180); const dLon = (lon2 - lon1) * (Math.PI / 180); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } function calculateTotalDistance(routeCoordinates) { let totalDistance = 0; for (let i = 0; i < routeCoordinates.length - 1; i++) { const { lat: lat1, lng: lon1 } = routeCoordinates[i]; const { lat: lat2, lng: lon2 } = routeCoordinates[i + 1]; totalDistance += calculateDistance(lat1, lon1, lat2, lon2); } return totalDistance; } const getdeliverylogs = async (id) => { setLogsLoading(true); try { const res = await axios.get(`${process.env.REACT_APP_URL3}/deliveries/getdeliverylogs/?deliveryid=${id}`); const datas = res.data.details; if (Array.isArray(datas) && datas.length !== 0) { // Sort chronologically by logdate const sorted = datas .map((r) => { const ts = r?.logdate ? dayjs(r.logdate) : null; return { lat: parseFloat(r?.latitude ?? r?.lat), lng: parseFloat(r?.longitude ?? r?.lng ?? r?.lon), logdate: r?.logdate, _ts: ts && ts.isValid() ? ts.valueOf() : Number.MAX_SAFE_INTEGER }; }) .filter((p) => Number.isFinite(p.lat) && Number.isFinite(p.lng)) .sort((a, b) => a._ts - b._ts); if (sorted.length !== 0) { setRiderStart(sorted[0].logdate); setRiderEnd(sorted[sorted.length - 1].logdate); // Apply Kalman filter const smoothed = kalmanSmoothGps(sorted); const coData = smoothed.map((data) => ({ lat: data.lat, lng: data.lng })); setRiderCoordinates(coData); calculateTotalDistance(coData); setMapOpen(true); } else { opentoast('No Valid Logs Found', 'error', 2000); } } else { opentoast('No Logs Found ', 'error', 2000); } } catch (error) { console.log('getdeliverylogs', error); } finally { setLogsLoading(false); } }; // ==============================|| fetchDeliveries (infinite) ||============================== // const { data: deliveriesData, isLoading: fetchDeliveriesIsLoading, isError: fetchDeliveriesIsError, error: fetchDeliveriesError, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({ queryKey: [ 'fetchdeliveries', appId, userid, currentStatus, startdate, enddate, rowsPerPage, debouncedSearch, tenantid, locationid, selectedRider?.userid || 0 ], queryFn: fetchDeliveries, getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined, refetchOnWindowFocus: true, refetchOnMount: true, refetchOnReconnect: true }); const rows = deliveriesData?.pages.flatMap((page) => page.rows) || []; useEffect(() => { if (!hasNextPage) return; const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { fetchNextPage(); } }, { root: document.querySelector('.MuiTableContainer-root'), 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(); } } }; // ==============================|| Tenant / Location / Rider lookups ||============================== // const { data: tenantlist, isLoading: fetchtenantsIsLoading, isError: fetchtenantsIsError, error: fetchtenantsError } = useQuery({ queryKey: ['tenantlist', appId], queryFn: () => getTenants(appId), enabled: appId !== 0 }); const { data: ridersList, isLoading: getriderbydeliveryIsLoading, isError: getriderbydeliveryIsError, error: getriderbydeliveryError } = useQuery({ queryKey: ['fetchRidersList', appId], queryFn: fetchRidersList, enabled: appId !== 0 }); const { data: locationlist, isLoading: fetchlocationsIsLoading, isError: fetchlocationsIsError, error: fetchlocationsError } = useQuery({ queryKey: ['gettenantlocations', tenantid], queryFn: () => gettenantlocations(tenantid), enabled: tenantid !== 0 }); // ==============================|| status summary counts ||============================== // const fetchcount = async () => { setIsLoading(true); try { await axios .get( appId == 0 ? `${process.env.REACT_APP_URL}/deliveries/deliverysummary/?fromdate=${startdate}&todate=${enddate}` : `${ process.env.REACT_APP_URL }/deliveries/deliverysummary/?applocationid=${appId}&tenantid=${tenantid}&locationid=${locationid}&fromdate=${startdate}&todate=${enddate}&userid=${ selectedRider?.userid || 0 }` ) .then((res) => { settotal(res.data.details.total); setPendingLenght(res.data.details.pending); setAssignLenght(res.data.details.accepted); setArrivedLenght(res.data.details.arrived); setPickedLenght(res.data.details.picked); setActiveLenght(res.data.details.active); setDeliveredLenght(res.data.details.delivered); setSkippedLenght(res.data.details.skipped); setCancelLenght(res.data.details.cancelled); }) .catch((err) => { enqueueSnackbar(err.message, { variant: 'error', anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 }); }); } catch (err) { console.log(err); } finally { setIsLoading(false); } }; useEffect(() => { fetchcount(); }, [appId, startdate, enddate, currentStatus, tenantid, locationid, selectedRider]); // CSV export payload — flat schema preserved for backwards compatibility. const csvData = rows?.map((order) => ({ tenantname: order.tenantname, tenantcity: order.tenantcity, tenantcontactno: order.tenantcontactno, rider: order.ridername, orderid: order.orderid, paymenttype: order.paymenttype == 64 ? 'Pay Later' : order.paymenttype == 42 ? 'Pay on Delivery' : 'Digital', deliverydate: order.deliverydate, orderstatus: order.orderstatus, ordernotes: order.ordernotes, kms: order.kms, cumulativekms: order.cumulativekms, assigntime: order.assigntime, starttime: order.starttime, arrivaltime: order.arrivaltime, pickuptime: order.pickuptime, deliverytime: order.deliverytime, canceltime: order.canceltime, deliverycharge: order.deliverycharges, deliveryamt: order.deliveryamt, pickupcustomer: order.pickupcustomer, pickupcontactno: order.pickupcontactno, Pickupaddress: order.pickupaddress, pickupsuburb: order.pickupsuburb, pickupcity: order.applocation, pickuplat: order.pickuplat, pickuplong: order.pickuplon, deliverycustomer: order.deliverycustomer, deliverycontactno: order.deliverycontactno, deliveryaddress: order.deliveryaddress, deliverysuburb: order.locationsuburb, deliverylat: order.deliverylat, deliverylong: order.deliverylong, locationname: order.locationname, locationsuburb: order.pickuplocation, deliverylocation: order.deliverylocation, locationcontactno: order.locationcontactno })); function formatDate(dateString) { return dayjs(dateString).format('DD/MM/YYYY '); } function formatTime(dateString) { return dayjs(dateString).format(' hh:mm A'); } const errormessage = fetchDeliveriesIsError ? `An error has occurred: (fetchDeliveries) ${fetchDeliveriesError.message}` : fetchtenantsIsError ? `An error has occurred: (getTenants) ${fetchtenantsError.message}` : fetchlocationsIsError ? `An error has occurred: (gettenantlocations) ${fetchlocationsError.message}` : getriderbydeliveryIsError ? `An error has occurred: (getriderbydelivery) ${getriderbydeliveryError.message}` : null; useEffect(() => { if (errormessage) { opentoast(errormessage, 'warning', 2000); } }, [errormessage]); const KPI_META = [ { key: 'total', label: 'Total Orders', color: BRAND, icon: MdLocalShipping, value: total }, { key: 'delivered', label: 'Delivered', color: '#10b981', icon: MdCheckCircle, value: deliveredLenght }, { key: 'pending', label: 'Pending', color: '#f59e0b', icon: MdHourglassEmpty, value: pendingLenght }, { key: 'cancelled', label: 'Cancelled', color: '#ef4444', icon: MdCancel, value: cancelLenght } ]; return ( <> {(isLoading || fetchtenantsIsLoading || fetchlocationsIsLoading || logsLoading || getriderbydeliveryIsLoading || isFetchingNextPage) && (
)} {fetchDeliveriesIsLoading && ( theme.zIndex.drawer + 1 }} open={fetchDeliveriesIsLoading} /> )} {/* ============================================= || Header || ============================================= */} Orders Details Live · {locaName || 'All Zones'} · {datestatus} } placeholder="Select Zone" paperComponent={SoftPaper} sx={{ width: { xs: '100%', sm: 280 }, zIndex: 100 }} /> {/* ============================================= || KPI Cards || ============================================= */} {KPI_META.map((item) => { const Icon = item.icon; return ( {item.label} {item.value} ); })} {/* ============================================= || Filter Bar (tenant, location, rider, date, export) || ============================================= */} option?.tenantname || ''} PaperComponent={SoftPaper} 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) => ( ) }} /> )} /> (option ? `${option.locationname} (${option.suburb})` : '')} value={locationValue} PaperComponent={SoftPaper} 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) => ( ) }} /> )} /> `${option.firstname} ${option.lastname}`} PaperComponent={SoftPaper} onOpen={() => { if (!appId) { OpenToast('Select App Location First', 'warning', 2000); } }} onChange={(event, value, reason) => { if (reason === 'clear') { setSelectedRider(null); setRiderValue(null); } else { setSelectedRider(value); setRiderValue(value); } }} renderInput={(params) => ( ) }} /> )} /> setOpen(true)} sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.75, px: 1.25, py: 0.75, borderRadius: 999, cursor: 'pointer', bgcolor: tint('#f59e0b'), border: `1.5px solid ${edge('#f59e0b')}`, color: '#f59e0b', fontWeight: 800, fontSize: 12, transition: 'all 0.18s', '&:hover': { borderColor: '#f59e0b', boxShadow: `0 0 0 3px ${ring('#f59e0b')}` } }} > {dayjs(startdate).format('DD/MM/YY')} – {dayjs(enddate).format('DD/MM/YY')} {/* ============================================= || Status Tabs + Search || ============================================= */} {STATUS_TABS.map((key) => { const meta = STATUS_META[key]; const Icon = meta.icon; const active = currentStatus.toLowerCase() === key; const count = statusCountByKey[key] ?? 0; return ( setCurrentStatus(key === 'all' ? 'All' : key)} sx={{ display: 'inline-flex', alignItems: 'center', gap: { xs: 0.625, md: 0.875 }, pl: 0.5, pr: { xs: 1, md: 1.25 }, py: 0.5, flexShrink: 0, cursor: 'pointer', borderRadius: 999, border: `1.5px solid ${active ? meta.color : edge(meta.color)}`, bgcolor: active ? meta.color : tint(meta.color), color: active ? '#fff' : meta.color, fontWeight: 700, boxShadow: active ? `0 6px 18px ${ring(meta.color)}` : 'none', transition: 'all 0.18s', '&:hover': { borderColor: meta.color, boxShadow: active ? `0 6px 18px ${ring(meta.color)}` : `0 0 0 3px ${ring(meta.color)}` } }} > {meta.label} {count} ); })} {/* ============================================= || Table || ============================================= */} # Map Client Pickup Drop Status / Rider Assigned Accepted Arrived Picked Active Delivered Cancelled Notes KMS Charges {fetchDeliveriesIsLoading ? ( ) : rows?.length == 0 ? ( No orders to show {searchword ? 'Try a different keyword.' : 'Adjust the filters above to load orders.'} ) : ( rows?.map((row, index) => { const statusKey = String(row.orderstatus || '').toLowerCase(); const rowStatusMeta = STATUS_META[statusKey] || { label: row.orderstatus || '—', color: BRAND, icon: MdAssignment }; const StatusIcon = rowStatusMeta.icon; const cancelled = statusKey === 'cancelled'; return ( {String(page * rowsPerPage + index + 1).padStart(2, '0')} {/* ====================== Map button ====================== */} { if (row.orderstatus === 'delivered') { getdeliverylogs(row.deliveryid); setMapTenant(row); } }} sx={{ bgcolor: row.orderstatus === 'delivered' ? soft(BRAND) : soft('#94a3b8'), color: row.orderstatus === 'delivered' ? BRAND : DT.textMuted, border: `1px solid ${row.orderstatus === 'delivered' ? edge(BRAND) : edge('#94a3b8')}`, '&:hover': { bgcolor: row.orderstatus === 'delivered' ? BRAND : soft('#94a3b8'), color: row.orderstatus === 'delivered' ? '#fff' : DT.textMuted } }} > {/* ====================== Client ====================== */} {row.tenantname} #{row.orderid} {dayjs(row.deliverydate).utc().format('DD/MM/YYYY · hh:mm A')} {/* ====================== Pickup ====================== */} {row.pickupcustomer || '—'} {row.pickupcontactno} {row.pickupsuburb || (row.Pickupaddress ? row.Pickupaddress.slice(0, 22) + '…' : '')} {row.applocation && ( {row.applocation} )} {/* ====================== Drop ====================== */} {row.deliverycustomer || '—'} {row.deliverycontactno} {row.deliverysuburb || (row.deliveryaddress ? row.deliveryaddress.slice(0, 22) + '…' : '')} {/* ====================== Status / Rider ====================== */} {rowStatusMeta.label} {row.ridername && ( {row.ridername} )} {/* ====================== Timestamps ====================== */} {/* ====================== Notes ====================== */} {row.ordernotes ? ( {row.ordernotes} ) : ( )} {/* ====================== KMS ====================== */} } label={cancelled || row.kms == '' ? '0 km' : `${row.kms} km`} tooltip="KMS" /> } label={`${row.cumulativekms ?? 0} km`} tooltip="Actual KMS" /> } label={`${row.previouskms || (cancelled ? '0.00' : row.kms) || 0} km`} tooltip="Rider KMS" /> {/* ====================== Charges ====================== */} } label={cancelled || row.deliverycharges == '' ? `0.00` : `${row.deliverycharges}.00`} tooltip="Delivery Charge" /> } label={row.deliveryamt == '' ? `0.00` : `${row.deliveryamt}.00`} tooltip="Delivery Amount" /> ); }) )}
{rows?.length !== 0 && ( {isFetchingNextPage || hasNextPage ? ( ) : ( No more orders )} )}
{/* ============================================= || Export Dialog || ============================================= */} setReportDialog(false)} fullWidth maxWidth="sm" PaperProps={{ sx: { borderRadius: 2.5, overflow: 'hidden' } }} > Report Export Orders setReportDialog(false)} sx={{ color: '#fff', bgcolor: 'rgba(255,255,255,0.18)', '&:hover': { bgcolor: 'rgba(255,255,255,0.3)' } }} > {fetchDeliveriesIsLoading && } {[ { label: 'App Location', value: locaName, color: BRAND }, { label: 'Tenant', value: tenantValue?.tenantname, color: '#0ea5e9' }, { label: 'Business Location', value: locationValue?.locationname, color: '#10b981' }, { label: 'Status', value: currentStatus, color: '#f59e0b' }, { label: 'Rider', value: riderValue ? `${riderValue.firstname} ${riderValue.lastname}` : null, color: '#8b5cf6' }, { label: 'Keyword', value: searchword, color: '#06b6d4' }, { label: 'Start Date', value: startdate, color: '#14b8a6' }, { label: 'End Date', value: enddate, color: '#ef4444' } ].map((item, idx) => ( {item.label} ))} { setTimeout(() => setReportDialog(false), 0); }} /> {/* ============================================= || Date Filter Dialog || ============================================= */} setOpen(false)} onSelect={(range) => { setStartdate(range.startDate); setEnddate(range.endDate); setDatestatus(range.label); }} /> {/* ============================================= || Map Dialog || ============================================= */} { setMapOpen(false); }} fullScreen fullWidth > {riderCoordinates && (
)}
); }