import React, { useState, useEffect, useRef, Fragment } from 'react';
import axios from 'axios';
import { useQuery } from '@tanstack/react-query';
import { enqueueSnackbar } from 'notistack';
import {
Avatar,
Box,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Dialog,
DialogContent,
Typography,
Stack,
Button,
IconButton,
Tooltip,
Autocomplete,
TextField,
Collapse,
Skeleton,
Paper,
Grid,
InputBase
} from '@mui/material';
import {
MdLocalShipping,
MdHourglassEmpty,
MdCheckCircle,
MdCancel,
MdMyLocation,
MdPlace,
MdSearch,
MdClear,
MdCalendarMonth,
MdReceiptLong,
MdStraighten,
MdCurrencyRupee,
MdInventory2,
MdKeyboardArrowDown,
MdKeyboardArrowUp,
MdTaskAlt,
MdHighlightOff,
MdInsights,
MdLocalOffer
} from 'react-icons/md';
import dayjs from 'dayjs';
var utc = require('dayjs/plugin/utc');
dayjs.extend(utc);
import { DateRangePicker } from 'mui-daterange-picker';
import { addDays, addMonths, addWeeks, endOfMonth, endOfWeek, startOfMonth, startOfWeek } from 'date-fns';
import Loader from 'components/Loader';
import { useTheme } from '@mui/material/styles';
import { getreportlocationsummary, gettenantlocations } from '../api/api';
import CircularLoader from 'components/nearle_components/CircularLoader';
// ============================================================================
// Design tokens — shared with the rest of the redesigned operator 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 dtA = (c, suffix) => `${c}${suffix}`;
const tint = (c) => dtA(c, '08');
const soft = (c) => dtA(c, '18');
const ring = (c) => dtA(c, '26');
const edge = (c) => dtA(c, '55');
const BRAND = '#662582';
const BRAND_LIGHT = '#9255AB';
// Semantic palette per metric column.
const C_ORDERS = '#0ea5e9';
const C_DELIVERIES = BRAND;
const C_PENDING = '#f59e0b';
const C_COMPLETED = '#10b981';
const C_CANCELLED = '#ef4444';
const C_ACCEPTED = '#6366f1';
const C_PICKED = '#8b5cf6';
const C_ARRIVED = '#06b6d4';
const C_SKIPPED = '#f97316';
const C_KMS = '#f59e0b';
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 }
}
});
// Inline metric pill used in cells; faded slate when zero.
const MetricPill = ({ value, color, icon, isMoney = false }) => {
const n = Number(value);
const display = isMoney ? formatNumberToRupees(n) : Number.isFinite(n) ? n : value || 0;
const isZero = !Number.isFinite(n) || n === 0;
if (isZero) {
return (
{display}
);
}
return (
{icon}
{display}
);
};
function formatNumberToRupees(value) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2
}).format(value || 0);
}
const opentoast = (message, variant, time) => {
enqueueSnackbar(message, {
variant: variant,
anchorOrigin: { vertical: 'top', horizontal: 'right' },
autoHideDuration: time ? time : 1500
});
};
// ==============================|| OrdersReport ||============================== //
export default function OrdersReport() {
const theme = useTheme();
const tenantid = localStorage.getItem('tenantid');
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
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 [searchword, setSearchword] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState('');
const textFieldRef = useRef(null);
const [ridersdata, setRidersdata] = useState(null);
const [selectedLocation, setSelectedLocation] = useState(null);
const [locationId, setLocationId] = useState(0);
const [locoName, setLocoName] = useState('All Locations');
const [searchLocation] = useState('');
// Debounce search.
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearch(searchword);
}, 400);
return () => clearTimeout(handler);
}, [searchword]);
// Ctrl/Cmd+K to focus the search.
useEffect(() => {
const handleKeyPress = (event) => {
if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
textFieldRef.current && textFieldRef.current.focus();
}
if (event.key === 'Escape' && document.activeElement === textFieldRef.current) {
textFieldRef.current.blur();
}
};
document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
}, []);
// ============================================= || gettenantlocations || =============================================
const {
data: tenantLocations,
isLoading: tenantLocationsIsLoading,
isError: tenantLocationsIsError,
error: tenantLocationsError
} = useQuery({
queryKey: ['tenantlocations', searchLocation],
queryFn: gettenantlocations
});
// ============================================= || getreportlocationsummary || =============================================
const {
isLoading: isLoadingReports,
isError: isErrorReports,
data: rows,
error: reportsError
} = useQuery({
queryKey: [startdate, enddate, locationId, debouncedSearch],
queryFn: getreportlocationsummary
});
// ============================================= || getriderlocationsummary || =============================================
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}`
);
setRidersdata(riderRes.data.details);
} catch (error) {
console.log('riderRes', error);
}
};
// ============================================= || calculate totals || =============================================
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;
});
settotal(calculatedTotal);
settotalOrders(ordersTotal);
setTotalOrderPend(Orderpending);
setTotalOrderComplete(OrderComplete);
setTotalOrderCancel(OrderCancel);
setTotalDeliPend(deliverypending);
setTotalDeliComplete(deliverycomplete);
setTotalDeliCancel(deliverycancel);
};
useEffect(() => {
calculate();
}, [rows]);
let errormessage = '';
if (isErrorReports && reportsError?.message) {
errormessage = `An error has occurred: (isErrorReports) ${reportsError.message}`;
} else if (tenantLocationsIsError && tenantLocationsError?.message) {
errormessage = `An error has occurred: (tenantLocationsIsError) ${tenantLocationsError.message}`;
}
useEffect(() => {
if (errormessage) opentoast(errormessage, 'warning', 2000);
}, [errormessage]);
// KPI tiles — derived from the calculated totals.
const kpiCards = [
{ key: 'total', label: 'Total Orders', color: BRAND, icon: MdLocalShipping, value: totalOrders },
{ key: 'pending', label: 'Pending', color: C_PENDING, icon: MdHourglassEmpty, value: totalOrderPend },
{ key: 'completed', label: 'Completed', color: C_COMPLETED, icon: MdCheckCircle, value: totalOrderComplete },
{ key: 'cancelled', label: 'Cancelled', color: C_CANCELLED, icon: MdCancel, value: totalOrderCancel },
{ key: 'charges', label: 'Total Charges', color: C_ACCEPTED, icon: MdLocalOffer, value: total, isMoney: true }
];
return (
<>
{(isLoadingReports || tenantLocationsIsLoading) && }
{(isLoadingReports || tenantLocationsIsLoading) && }
{/* ============================================= || Header || ============================================= */}
Orders Summary
Live · {locoName} · {datestatus}
setOpen(true)}
sx={{
display: 'inline-flex',
alignItems: 'center',
gap: 0.75,
px: 1.5,
py: 0.875,
borderRadius: 999,
cursor: 'pointer',
bgcolor: '#fff',
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')}` }
}}
>
{startdate && enddate
? `${dayjs(startdate).format('DD/MM/YY')} – ${dayjs(enddate).format('DD/MM/YY')}`
: 'All time'}
{/* ============================================= || KPI Cards || ============================================= */}
{kpiCards.map((item) => {
const Icon = item.icon;
return (
{item.label}
{isLoadingReports ? (
) : item.isMoney ? (
formatNumberToRupees(item.value)
) : (
item.value
)}
);
})}
{/* ============================================= || Filter Bar || ============================================= */}
{/* Search */}
setSearchword(e.target.value)}
autoComplete="off"
sx={{
flex: 1,
fontSize: 13,
fontWeight: 600,
color: DT.textPrimary,
'& input::placeholder': { color: DT.textMuted, opacity: 1 }
}}
/>
{searchword && (
setSearchword('')} sx={{ p: 0.25, color: BRAND }}>
)}
{/* Location filter */}
{tenantLocations?.length === 1 ? (
{tenantLocations[0]?.locationname}
) : (
(o ? `${o.locationname} (${o.suburb || ''})` : '')}
PaperComponent={SoftPaper}
onChange={(event, value) => {
setSelectedLocation(value);
setLocationId(value ? value.locationid : 0);
setLocoName(value ? value.locationname : 'All Locations');
}}
renderInput={(params) => (
)
}}
/>
)}
sx={{ width: { xs: '100%', md: 320 } }}
/>
)}
{/* ============================================= || Table || ============================================= */}
#
Location
All
Orders
Deliveries
Kms
Amount
Action
Pending
Completed
Cancelled
Pending
Completed
Cancelled
{rows && rows.length !== 0 ? (
rows.map((row, index) => (
{/* ===================== || Main row || ===================== */}
{index + 1}
{row.locationname}
Id : {row.locationid}
} />
} />
} />
} />
} />
} />
} />
}
/>
} isMoney />
{
getriderlocationsummary(row.locationid);
setOpenRow(openRow === row.locationid ? null : row.locationid);
}}
sx={{
bgcolor: openRow === row.locationid ? BRAND : tint(BRAND),
border: `1px solid ${openRow === row.locationid ? BRAND : edge(BRAND)}`,
color: openRow === row.locationid ? '#fff' : BRAND,
borderRadius: 999,
p: 0.75,
transition: 'all 0.18s',
'&:hover': {
bgcolor: openRow === row.locationid ? BRAND : soft(BRAND),
borderColor: BRAND,
boxShadow: `0 0 0 3px ${ring(BRAND)}`
}
}}
>
{openRow === row.locationid ? : }
{/* ===================== || Collapsible riders row || ===================== */}
{openRow === row.locationid && (
Riders Summary · {row.locationname}
#
Rider
Deliveries
Pending
Assigned
Accepted
Arrived
Picked
Skipped
Delivered
Kms
Charges
{ridersdata && ridersdata.length > 0 ? (
ridersdata.map((rider, ri) => (
{ri + 1}
{rider?.firstname}
{rider?.status == 'Active' ? (
) : (
)}
} />
} />
} />
} />
} />
} />
} />
} />
} />
} isMoney />
))
) : (
No rider data for this location
)}
)}
))
) : (
No summary data
Adjust the date range or location filter above.
)}
{/* ===================== || Totals row || ===================== */}
{rows && rows.length !== 0 && (
Total
{totalOrders}
{totalOrderPend}
{totalOrderComplete}
{totalOrderCancel}
{totalDeliPend}
{totalDeliComplete}
{totalDeliCancel}
{formatNumberToRupees(total)}
)}
{/* ============================================= || Date Filter Dialog || ============================================= */}
>
);
}