added the api for the real data
This commit is contained in:
@@ -9,6 +9,9 @@ import { MapContainer, TileLayer, Marker, Popup, Polyline, Tooltip as LTooltip,
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
|
||||
import { getRiderRoutes } from '@/api/hub';
|
||||
import { getHubContext } from '@/auth/session';
|
||||
|
||||
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
|
||||
import PlayArrowRoundedIcon from '@mui/icons-material/PlayArrowRounded';
|
||||
import PauseRoundedIcon from '@mui/icons-material/PauseRounded';
|
||||
@@ -130,94 +133,53 @@ const moverIcon = (color) => new L.DivIcon({
|
||||
//
|
||||
// Delivery: hub → customer drops. Pickup: merchant collections → hub.
|
||||
// ════════════════════════════════════════════════════════════════════════════════
|
||||
const HUB = { lat: 28.6139, lng: 77.2090, label: 'Delhi Operations Hub' };
|
||||
const hubStop = (time) => ({ kind: 'hub', label: HUB.label, lat: HUB.lat, lng: HUB.lng, time });
|
||||
// The hub all pickup legs return to. Label comes from the logged-in hub context;
|
||||
// the coordinate is only the initial map centre (the map auto-fits to the stops).
|
||||
const HUB = { lat: 11.0168, lng: 76.9558, label: getHubContext().hubname || 'Hub' };
|
||||
|
||||
const RIDERS = [
|
||||
{
|
||||
id: 'RDR-8012', name: 'Muthu Kumar', color: '#1A73E8', vehicle: 'Electric Bike', vehicleNo: 'DL-04-EB-1234', phone: '+91 98765 43210',
|
||||
delivery: {
|
||||
startTime: '08:12', endTime: '13:40', distanceKm: 18.4,
|
||||
stops: [
|
||||
hubStop('08:12'),
|
||||
{ kind: 'order', orderId: 'ORD-100231', customer: 'Aarav Mehta', phone: '+91 90011 22334', address: 'Flat 402, Dwarka Sector 12, New Delhi', lat: 28.5921, lng: 77.0460, time: '08:48', status: 'Picked', items: 2, weight: '1.4 kg', slot: '08:00–10:00', cod: 0, payment: 'Prepaid', legKm: 5.2, instructions: 'Leave at reception if not home.' },
|
||||
{ kind: 'order', orderId: 'ORD-100244', customer: 'Priya Nair', phone: '+91 90022 33445', address: 'House 18, Janakpuri B-Block, New Delhi', lat: 28.6219, lng: 77.0878, time: '09:36', status: 'Picked', items: 1, weight: '0.6 kg', slot: '09:00–11:00', cod: 1200, payment: 'COP', legKm: 4.1, instructions: 'Call on arrival.' },
|
||||
{ kind: 'order', orderId: 'ORD-100258', customer: 'Rohit Sethi', phone: '+91 90033 44556', address: 'Shop 7, Rajouri Garden Market, New Delhi', lat: 28.6492, lng: 77.1207, time: '10:25', status: 'Failed', items: 3, weight: '2.8 kg', slot: '10:00–12:00', cod: 0, payment: 'Prepaid', legKm: 4.6, instructions: 'Customer unreachable — reattempt tomorrow.' },
|
||||
{ kind: 'order', orderId: 'ORD-100269', customer: 'Sana Kapoor', phone: '+91 90044 55667', address: 'A-22, Karol Bagh, New Delhi', lat: 28.6512, lng: 77.1907, time: '11:30', status: 'Picked', items: 1, weight: '0.9 kg', slot: '11:00–13:00', cod: 850, payment: 'COP', legKm: 4.5, instructions: '' },
|
||||
],
|
||||
},
|
||||
// Palette assigned to milers round-robin so each route line is a distinct colour.
|
||||
const ROUTE_COLORS = ['#1A73E8', '#8E24AA', '#1E8E3E', '#E8710A', '#C01227', '#00838F'];
|
||||
|
||||
// Map an API rider-route (from GET /hub/rider-routes) into the structure this
|
||||
// page renders: a `pickup` trip with a list of order stops. Fields the API does
|
||||
// not provide (customer, weight, COD, slot, instructions) default gracefully.
|
||||
const mapRoute = (r, i) => {
|
||||
const apiStops = Array.isArray(r.stops) ? r.stops : [];
|
||||
const stops = apiStops.map((s) => ({
|
||||
kind: 'order',
|
||||
orderId: s.bookingid != null ? `BK-${s.bookingid}` : `Stop ${s.seq}`,
|
||||
bookingid: s.bookingid,
|
||||
customer: s.customer || '',
|
||||
phone: s.phone || '',
|
||||
address: s.address || '—',
|
||||
lat: s.lat,
|
||||
lng: s.lon,
|
||||
time: s.eta_minutes != null ? `${s.eta_minutes} min` : '',
|
||||
status: s.status === 'completed' ? 'Picked' : s.status === 'in_progress' ? 'In progress' : 'Pending',
|
||||
items: s.items ?? 0,
|
||||
weight: '',
|
||||
slot: '',
|
||||
cod: 0,
|
||||
payment: '',
|
||||
legKm: 0,
|
||||
instructions: ''
|
||||
}));
|
||||
return {
|
||||
id: `RDR-${r.mileruserid}`,
|
||||
mileruserid: r.mileruserid,
|
||||
name: r.milername || `Miler ${r.mileruserid}`,
|
||||
color: ROUTE_COLORS[i % ROUTE_COLORS.length],
|
||||
vehicle: '—',
|
||||
vehicleNo: '—',
|
||||
phone: '',
|
||||
pickup: {
|
||||
startTime: '14:10', endTime: '16:50', distanceKm: 11.2,
|
||||
stops: [
|
||||
{ kind: 'order', orderId: 'PCK-50118', customer: 'TrendKart Store', phone: '+91 98801 11222', address: 'Tilak Nagar Main Rd, New Delhi', lat: 28.6363, lng: 77.0945, time: '14:35', status: 'Picked', items: 6, weight: '4.2 kg', slot: '14:00–16:00', cod: 0, payment: 'Merchant', legKm: 6.0, instructions: 'Collect from back gate.' },
|
||||
{ kind: 'order', orderId: 'PCK-50126', customer: 'FreshLeaf Organics', phone: '+91 98802 22333', address: 'Subhash Nagar, New Delhi', lat: 28.6404, lng: 77.1199, time: '15:30', status: 'Picked', items: 3, weight: '5.5 kg', slot: '15:00–17:00', cod: 0, payment: 'Merchant', legKm: 3.0, instructions: '' },
|
||||
{ kind: 'order', orderId: 'PCK-50131', customer: 'GadgetHub', phone: '+91 98803 33444', address: 'Moti Nagar, New Delhi', lat: 28.6580, lng: 77.1450, time: '16:15', status: 'Missed', items: 2, weight: '1.1 kg', slot: '16:00–18:00', cod: 0, payment: 'Merchant', legKm: 2.2, instructions: 'Shop was closed at pickup time.' },
|
||||
hubStop('16:50'),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'RDR-8041', name: 'Sana Sheikh', color: '#8E24AA', vehicle: 'Motorcycle', vehicleNo: 'DL-02-MC-1199', phone: '+91 98765 43214',
|
||||
delivery: {
|
||||
startTime: '08:00', endTime: '14:05', distanceKm: 22.9,
|
||||
stops: [
|
||||
hubStop('08:00'),
|
||||
{ kind: 'order', orderId: 'ORD-100277', customer: 'Imran Qureshi', phone: '+91 90055 66778', address: 'N-Block, Connaught Place, New Delhi', lat: 28.6315, lng: 77.2167, time: '08:30', status: 'Picked', items: 1, weight: '0.4 kg', slot: '08:00–10:00', cod: 0, payment: 'Prepaid', legKm: 2.1, instructions: '' },
|
||||
{ kind: 'order', orderId: 'ORD-100283', customer: 'Neha Gupta', phone: '+91 90066 77889', address: 'C-44, Lajpat Nagar II, New Delhi', lat: 28.5677, lng: 77.2433, time: '09:20', status: 'Picked', items: 2, weight: '1.7 kg', slot: '09:00–11:00', cod: 1450, payment: 'COP', legKm: 7.4, instructions: 'Ring twice.' },
|
||||
{ kind: 'order', orderId: 'ORD-100291', customer: 'Vikas Rao', phone: '+91 90077 88990', address: 'J-Block, Saket, New Delhi', lat: 28.5245, lng: 77.2066, time: '10:40', status: 'Picked', items: 1, weight: '0.8 kg', slot: '10:00–12:00', cod: 0, payment: 'Prepaid', legKm: 6.2, instructions: '' },
|
||||
{ kind: 'order', orderId: 'ORD-100305', customer: 'Diya Shah', phone: '+91 90088 99001', address: 'Malviya Nagar Main Market, New Delhi', lat: 28.5355, lng: 77.2110, time: '11:55', status: 'Picked', items: 4, weight: '3.1 kg', slot: '11:00–13:00', cod: 2300, payment: 'COP', legKm: 1.4, instructions: 'Heavy parcel — handle with care.' },
|
||||
],
|
||||
},
|
||||
pickup: {
|
||||
startTime: '14:30', endTime: '17:20', distanceKm: 14.6,
|
||||
stops: [
|
||||
{ kind: 'order', orderId: 'PCK-50140', customer: 'Bloom & Co Florists', phone: '+91 98804 44555', address: 'Greater Kailash I, New Delhi', lat: 28.5494, lng: 77.2426, time: '15:00', status: 'Picked', items: 4, weight: '2.0 kg', slot: '14:30–16:30', cod: 0, payment: 'Merchant', legKm: 8.1, instructions: '' },
|
||||
{ kind: 'order', orderId: 'PCK-50147', customer: 'BookNook', phone: '+91 98805 55666', address: 'Hauz Khas Village, New Delhi', lat: 28.5535, lng: 77.1944, time: '16:05', status: 'Picked', items: 9, weight: '7.8 kg', slot: '15:30–17:30', cod: 0, payment: 'Merchant', legKm: 4.5, instructions: 'Multiple boxes.' },
|
||||
hubStop('17:20'),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'RDR-8015', name: 'Rajesh Sharma', color: '#1E8E3E', vehicle: 'Cargo Van', vehicleNo: 'DL-01-CV-9876', phone: '+91 98765 43211',
|
||||
delivery: {
|
||||
startTime: '07:50', endTime: '15:10', distanceKm: 31.2,
|
||||
stops: [
|
||||
hubStop('07:50'),
|
||||
{ kind: 'order', orderId: 'ORD-100312', customer: 'Anil Verma', phone: '+91 90099 00112', address: 'Sector 7, Rohini, New Delhi', lat: 28.7042, lng: 77.1025, time: '08:55', status: 'Picked', items: 5, weight: '6.2 kg', slot: '08:00–10:00', cod: 0, payment: 'Prepaid', legKm: 12.4, instructions: '' },
|
||||
{ kind: 'order', orderId: 'ORD-100320', customer: 'Meera Iyer', phone: '+91 90100 11223', address: 'NSP, Pitampura, New Delhi', lat: 28.6996, lng: 77.1314, time: '09:50', status: 'Picked', items: 2, weight: '2.0 kg', slot: '09:00–11:00', cod: 990, payment: 'COP', legKm: 3.2, instructions: '' },
|
||||
{ kind: 'order', orderId: 'ORD-100334', customer: 'Sahil Khan', phone: '+91 90111 22334', address: 'Model Town III, New Delhi', lat: 28.7158, lng: 77.1910, time: '11:10', status: 'Failed', items: 1, weight: '0.7 kg', slot: '10:00–12:00', cod: 0, payment: 'Prepaid', legKm: 6.1, instructions: 'Wrong address provided.' },
|
||||
{ kind: 'order', orderId: 'ORD-100349', customer: 'Tara Bose', phone: '+91 90122 33445', address: 'Civil Lines, New Delhi', lat: 28.6796, lng: 77.2240, time: '12:30', status: 'Picked', items: 3, weight: '4.4 kg', slot: '12:00–14:00', cod: 3100, payment: 'COP', legKm: 5.0, instructions: '' },
|
||||
],
|
||||
},
|
||||
pickup: {
|
||||
startTime: '15:40', endTime: '18:30', distanceKm: 19.8,
|
||||
stops: [
|
||||
{ kind: 'order', orderId: 'PCK-50155', customer: 'MegaMart Warehouse', phone: '+91 98806 66777', address: 'Wazirpur Industrial Area, New Delhi', lat: 28.6991, lng: 77.1612, time: '16:20', status: 'Picked', items: 24, weight: '38.0 kg', slot: '16:00–18:00', cod: 0, payment: 'Merchant', legKm: 11.0, instructions: 'Use loading dock 4.' },
|
||||
{ kind: 'order', orderId: 'PCK-50163', customer: 'HomeStyle Furnishings', phone: '+91 98807 77888', address: 'Ashok Vihar, New Delhi', lat: 28.6924, lng: 77.1760, time: '17:25', status: 'Picked', items: 8, weight: '22.5 kg', slot: '17:00–19:00', cod: 0, payment: 'Merchant', legKm: 3.0, instructions: '' },
|
||||
hubStop('18:30'),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'RDR-8044', name: 'Harpreet Gill', color: '#E8710A', vehicle: 'Cycle', vehicleNo: '—', phone: '+91 98765 43215',
|
||||
delivery: {
|
||||
startTime: '09:20', endTime: '13:15', distanceKm: 9.7,
|
||||
stops: [
|
||||
hubStop('09:20'),
|
||||
{ kind: 'order', orderId: 'ORD-100356', customer: 'Kabir Anand', phone: '+91 90133 44556', address: 'Mayur Vihar Phase I, New Delhi', lat: 28.6090, lng: 77.2920, time: '10:05', status: 'Picked', items: 1, weight: '0.5 kg', slot: '10:00–12:00', cod: 600, payment: 'COP', legKm: 9.0, instructions: '' },
|
||||
{ kind: 'order', orderId: 'ORD-100361', customer: 'Ritu Saxena', phone: '+91 90144 55667', address: 'Mayur Vihar Phase III, New Delhi', lat: 28.6135, lng: 77.3215, time: '11:10', status: 'Picked', items: 2, weight: '1.2 kg', slot: '11:00–13:00', cod: 0, payment: 'Prepaid', legKm: 3.0, instructions: '' },
|
||||
{ kind: 'order', orderId: 'ORD-100370', customer: 'Farhan Ali', phone: '+91 90155 66778', address: 'Patparganj, New Delhi', lat: 28.6280, lng: 77.2960, time: '12:20', status: 'Picked', items: 1, weight: '0.8 kg', slot: '12:00–14:00', cod: 450, payment: 'COP', legKm: 2.9, instructions: '' },
|
||||
],
|
||||
},
|
||||
pickup: {
|
||||
startTime: '13:40', endTime: '15:30', distanceKm: 6.4,
|
||||
stops: [
|
||||
{ kind: 'order', orderId: 'PCK-50170', customer: 'Cafe Mosaic', phone: '+91 98808 88999', address: 'Mayur Vihar Phase I Market, New Delhi', lat: 28.6055, lng: 77.2985, time: '14:10', status: 'Picked', items: 2, weight: '1.5 kg', slot: '14:00–16:00', cod: 0, payment: 'Merchant', legKm: 4.0, instructions: '' },
|
||||
hubStop('15:30'),
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
distanceKm: r.totaldistance_km || 0,
|
||||
stops
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// ── helpers ────────────────────────────────────────────────────────────────────
|
||||
const initials = (n) => n.split(' ').map((w) => w[0]).slice(0, 2).join('').toUpperCase();
|
||||
@@ -227,6 +189,8 @@ const lerpPoint = (a, b, t) => ({ lat: a.lat + (b.lat - a.lat) * t, lng: a.lng +
|
||||
const STATUS_META = {
|
||||
Picked: { color: '#1A73E8', icon: CheckCircleRoundedIcon, label: 'Picked up' },
|
||||
Missed: { color: '#D93025', icon: CancelRoundedIcon, label: 'Missed' },
|
||||
'In progress': { color: '#F29900', icon: DeliveryDiningRoundedIcon, label: 'In progress' },
|
||||
Pending: { color: '#80868B', icon: ScheduleOutlinedIcon, label: 'Pending' },
|
||||
};
|
||||
|
||||
const MODES = {
|
||||
@@ -395,9 +359,11 @@ function OrderDetailPanel({ data, onBack }) {
|
||||
// ════════════════════════════════════════════════════════════════════════════════
|
||||
export default function RiderRoutes() {
|
||||
const [mode] = useState('pickup'); // pickups only
|
||||
const [visible, setVisible] = useState(() => Object.fromEntries(RIDERS.map((r) => [r.id, true])));
|
||||
const [riders, setRiders] = useState([]); // loaded from /hub/rider-routes
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [visible, setVisible] = useState({});
|
||||
const [routes, setRoutes] = useState({}); // keyed `${id}__${mode}`
|
||||
const [expanded, setExpanded] = useState(RIDERS[0].id);
|
||||
const [expanded, setExpanded] = useState(null);
|
||||
const [focusedStop, setFocusedStop] = useState(null); // `${riderId}-${index}`
|
||||
const [detail, setDetail] = useState(null); // { order, rider, mode, index }
|
||||
const [flyTarget, setFlyTarget] = useState(null);
|
||||
@@ -413,13 +379,29 @@ export default function RiderRoutes() {
|
||||
|
||||
const modeCfg = MODES[mode];
|
||||
|
||||
// Load today's rider routes for this hub.
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
getRiderRoutes()
|
||||
.then((res) => {
|
||||
if (cancelled) return;
|
||||
const mapped = (res?.data || []).map(mapRoute);
|
||||
setRiders(mapped);
|
||||
setVisible(Object.fromEntries(mapped.map((r) => [r.id, true])));
|
||||
setExpanded(mapped[0]?.id ?? null);
|
||||
})
|
||||
.catch(() => !cancelled && setRiders([]))
|
||||
.finally(() => !cancelled && setLoading(false));
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
// Resolve road routes for the active mode (cached per id+mode). A ref guards
|
||||
// against re-fetching keys we've already resolved when the tab is revisited.
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
await Promise.all(
|
||||
RIDERS.map(async (r) => {
|
||||
riders.map(async (r) => {
|
||||
const key = `${r.id}__${mode}`;
|
||||
if (resolvedRef.current[key]) return;
|
||||
resolvedRef.current[key] = true;
|
||||
@@ -432,7 +414,7 @@ export default function RiderRoutes() {
|
||||
);
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [mode]);
|
||||
}, [mode, riders]);
|
||||
|
||||
const pathFor = useCallback(
|
||||
(r) => routes[`${r.id}__${mode}`] || r[mode].stops.map((s) => ({ lat: s.lat, lng: s.lng })),
|
||||
@@ -444,14 +426,14 @@ export default function RiderRoutes() {
|
||||
// Auto-fit points = visible riders' stops for the active mode.
|
||||
const fitPoints = useMemo(() => {
|
||||
const pts = [];
|
||||
RIDERS.forEach((r) => { if (visible[r.id]) stopsOf(r).forEach((s) => pts.push(s)); });
|
||||
riders.forEach((r) => { if (visible[r.id]) stopsOf(r).forEach((s) => pts.push(s)); });
|
||||
return pts;
|
||||
}, [visible, stopsOf]);
|
||||
}, [riders, visible, stopsOf]);
|
||||
|
||||
// ── Analysis KPIs for the active mode ─────────────────────────────────────────
|
||||
const kpi = useMemo(() => {
|
||||
let orders = 0, done = 0, fail = 0, km = 0, cod = 0, activeRiders = 0;
|
||||
RIDERS.forEach((r) => {
|
||||
riders.forEach((r) => {
|
||||
const trip = r[mode];
|
||||
const orderStops = trip.stops.filter((s) => s.kind === 'order');
|
||||
if (orderStops.length) activeRiders += 1;
|
||||
@@ -462,7 +444,7 @@ export default function RiderRoutes() {
|
||||
cod += orderStops.reduce((s, o) => s + (o.cod || 0), 0);
|
||||
});
|
||||
return { orders, done, fail, km: km.toFixed(1), cod, activeRiders };
|
||||
}, [mode, modeCfg]);
|
||||
}, [riders, mode, modeCfg]);
|
||||
|
||||
// ── Animation driver ──────────────────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
@@ -506,7 +488,8 @@ export default function RiderRoutes() {
|
||||
|
||||
const playState = useMemo(() => {
|
||||
if (!playing) return null;
|
||||
const rider = RIDERS.find((r) => r.id === playing);
|
||||
const rider = riders.find((r) => r.id === playing);
|
||||
if (!rider) return null;
|
||||
const path = pathFor(rider);
|
||||
if (path.length < 2) return null;
|
||||
const totalSegs = path.length - 1;
|
||||
@@ -516,7 +499,7 @@ export default function RiderRoutes() {
|
||||
const pos = lerpPoint(path[i], path[i + 1], frac);
|
||||
const travelled = [...path.slice(0, i + 1), pos];
|
||||
return { rider, path, travelled, pos };
|
||||
}, [playing, progress, pathFor]);
|
||||
}, [playing, progress, pathFor, riders]);
|
||||
|
||||
return (
|
||||
<Box sx={{ pt: { xs: 1, md: 1 }, pb: 1 }}>
|
||||
@@ -536,10 +519,20 @@ export default function RiderRoutes() {
|
||||
|
||||
</Stack>
|
||||
|
||||
{!loading && riders.length === 0 && (
|
||||
<Card elevation={0} sx={{ borderRadius: 2, border: '1px dashed #CED4DA', py: 8, textAlign: 'center', mt: 3 }}>
|
||||
<RouteOutlinedIcon sx={{ fontSize: 56, color: '#CED4DA', mb: 1.5 }} />
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, color: '#495057' }}>No rider routes today</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
|
||||
Once milers are assigned pickups, their planned stops will show up here.
|
||||
</Typography>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* ── Analysis KPI strip ── */}
|
||||
<Grid container spacing={2.5} mt={4} sx={{ mb: 2 }}>
|
||||
{[
|
||||
{ icon: GroupsOutlinedIcon, label: 'Active Milers', value: kpi.activeRiders, sub: `of ${RIDERS.length}`, color: '#1A73E8', bg: '#E8F0FE' },
|
||||
{ icon: GroupsOutlinedIcon, label: 'Active Milers', value: kpi.activeRiders, sub: `of ${riders.length}`, color: '#1A73E8', bg: '#E8F0FE' },
|
||||
{ icon: modeCfg.icon, label: modeCfg.label, value: kpi.orders, sub: 'pickups covered', color: '#C01227', bg: alpha('#C01227', 0.1) },
|
||||
{ icon: CheckCircleRoundedIcon, label: modeCfg.doneLabel, value: kpi.done, sub: `${kpi.fail} missed`, color: '#1E8E3E', bg: '#E6F4EA' },
|
||||
{ icon: StraightenRoundedIcon, label: 'Distance', value: `${kpi.km} km`, sub: 'fleet total today', color: '#8E24AA', bg: '#F3E5F5' },
|
||||
@@ -561,7 +554,7 @@ export default function RiderRoutes() {
|
||||
</Box>
|
||||
|
||||
<List disablePadding sx={{ maxHeight: { lg: 620 }, overflow: 'auto' }}>
|
||||
{RIDERS.map((rider) => {
|
||||
{riders.map((rider) => {
|
||||
const trip = rider[mode];
|
||||
const orders = trip.stops.filter((s) => s.kind === 'order');
|
||||
const done = orders.filter((o) => o.status === modeCfg.doneStatus).length;
|
||||
@@ -749,7 +742,7 @@ export default function RiderRoutes() {
|
||||
<Popup><b>{HUB.label}</b><br />Pickups return here</Popup>
|
||||
</Marker>
|
||||
|
||||
{RIDERS.map((rider) => {
|
||||
{riders.map((rider) => {
|
||||
if (!visible[rider.id]) return null;
|
||||
const path = pathFor(rider);
|
||||
const stops = stopsOf(rider);
|
||||
@@ -759,7 +752,7 @@ export default function RiderRoutes() {
|
||||
<React.Fragment key={rider.id}>
|
||||
<Polyline positions={path.map((p) => [p.lat, p.lng])} pathOptions={{ color: rider.color, weight: 5, opacity: dimmed ? 0.15 : 0.85, dashArray: modeCfg.dash }} />
|
||||
{stops.map((s, i) => {
|
||||
if (s.kind === 'hub') return null;
|
||||
if (s.kind === 'hub' || s.lat == null || s.lng == null) return null;
|
||||
const key = `${rider.id}-${i}`;
|
||||
return (
|
||||
<Marker
|
||||
@@ -779,7 +772,7 @@ export default function RiderRoutes() {
|
||||
</Marker>
|
||||
);
|
||||
})}
|
||||
{!dimmed && (
|
||||
{!dimmed && endStop && (
|
||||
<Marker position={[endStop.lat, endStop.lng]} icon={flagIcon} zIndexOffset={-100}>
|
||||
<LTooltip direction="top" offset={[0, -14]}>{mode === 'pickup' ? 'Returned to hub' : 'Trip end'} · {rider[mode].endTime}</LTooltip>
|
||||
</Marker>
|
||||
@@ -801,7 +794,7 @@ export default function RiderRoutes() {
|
||||
|
||||
{/* Legend */}
|
||||
<Stack direction="row" flexWrap="wrap" gap={2} sx={{ mt: 2 }}>
|
||||
{RIDERS.map((r) => (
|
||||
{riders.map((r) => (
|
||||
<Stack key={r.id} direction="row" alignItems="center" gap={0.75} sx={{ opacity: visible[r.id] ? 1 : 0.4, cursor: 'pointer' }} onClick={() => toggleVisible(r.id)}>
|
||||
<Box sx={{ width: 18, height: 4, borderRadius: 2, bgcolor: r.color }} />
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#5F6368' }}>{r.name}</Typography>
|
||||
|
||||
Reference in New Issue
Block a user