updates on the activeSection and dispatch
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
MdMyLocation,
|
||||
MdInventory2
|
||||
} from 'react-icons/md';
|
||||
import dayjs from 'dayjs';
|
||||
import { getStatusStyle, getActiveOrder } from './dispatchShared';
|
||||
|
||||
const ActiveSection = ({
|
||||
@@ -83,6 +84,7 @@ const ActiveSection = ({
|
||||
<span className="adcard-tx">{riderName}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: '4px' }}>
|
||||
<span
|
||||
className="adcard-status"
|
||||
style={{ background: `${statusStyle.bg}1a`, color: statusStyle.bg }}
|
||||
@@ -90,6 +92,12 @@ const ActiveSection = ({
|
||||
>
|
||||
{statusStyle.label}
|
||||
</span>
|
||||
{o.deliverytime && (
|
||||
<span className="adcard-time" style={{ fontSize: '11px', color: '#64748b' }}>
|
||||
{dayjs(o.deliverytime).isValid() ? dayjs(o.deliverytime).format('HH:mm:ss') : String(o.deliverytime)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dropArea && (
|
||||
|
||||
@@ -800,8 +800,46 @@ const MapController = ({ focusedItem, viewMode, orders, kitchens, locationKey })
|
||||
// automatically. Either way motion is smooth and never freezes mid-trip.
|
||||
// `duration` is only the seed used for the very first segment (no prior fix to
|
||||
// measure against yet).
|
||||
const MIN_GLIDE_MS = 800; // floor: don't animate faster than this even on rapid fixes
|
||||
const MAX_GLIDE_MS = 32_000; // ceiling: cover the ~30s backend cadence + small buffer
|
||||
const MIN_GLIDE_MS = 200; // floor: don't animate faster than this even on rapid fixes
|
||||
const MAX_GLIDE_MS = 2500; // ceiling: tighter ceiling for 1s update cadence
|
||||
const interpolatePath = (path, t) => {
|
||||
if (!path || path.length === 0) return null;
|
||||
if (path.length === 1) return path[0];
|
||||
if (t <= 0) return path[0];
|
||||
if (t >= 1) return path[path.length - 1];
|
||||
|
||||
const segmentDistances = [];
|
||||
let totalDistance = 0;
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const p1 = path[i];
|
||||
const p2 = path[i + 1];
|
||||
const dLat = p2[0] - p1[0];
|
||||
const dLng = p2[1] - p1[1];
|
||||
const dist = Math.sqrt(dLat * dLat + dLng * dLng);
|
||||
segmentDistances.push(dist);
|
||||
totalDistance += dist;
|
||||
}
|
||||
|
||||
if (totalDistance === 0) return path[0];
|
||||
|
||||
const targetDistance = t * totalDistance;
|
||||
let accumulatedDistance = 0;
|
||||
for (let i = 0; i < segmentDistances.length; i++) {
|
||||
const nextDistance = accumulatedDistance + segmentDistances[i];
|
||||
if (nextDistance >= targetDistance) {
|
||||
const segT = (targetDistance - accumulatedDistance) / segmentDistances[i];
|
||||
const p1 = path[i];
|
||||
const p2 = path[i + 1];
|
||||
return [
|
||||
p1[0] + (p2[0] - p1[0]) * segT,
|
||||
p1[1] + (p2[1] - p1[1]) * segT
|
||||
];
|
||||
}
|
||||
accumulatedDistance = nextDistance;
|
||||
}
|
||||
return path[path.length - 1];
|
||||
};
|
||||
|
||||
const AnimatedRiderMarker = ({ target, icon, duration = 950, zIndexOffset, eventHandlers, children, markerRef: externalRef }) => {
|
||||
const markerRef = useRef(null);
|
||||
const rafRef = useRef(null);
|
||||
@@ -861,17 +899,46 @@ const AnimatedRiderMarker = ({ target, icon, duration = 950, zIndexOffset, event
|
||||
prevFixTsRef.current = startTs;
|
||||
const startLat = from.lat;
|
||||
const startLng = from.lng;
|
||||
|
||||
let active = true;
|
||||
let osrmPath = null;
|
||||
|
||||
// Fetch OSRM route for this step so the bike follows the roads
|
||||
const fetchOsrmPath = async () => {
|
||||
const url = `https://router.project-osrm.org/route/v1/driving/${from.lng},${from.lat};${to.lng},${to.lat}?overview=full&geometries=geojson`;
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
if (active && json.routes && json.routes[0]) {
|
||||
osrmPath = json.routes[0].geometry.coordinates.map(c => [c[1], c[0]]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('OSRM animated rider marker fetch error:', e);
|
||||
}
|
||||
};
|
||||
fetchOsrmPath();
|
||||
|
||||
const step = (now) => {
|
||||
if (!active) return;
|
||||
const t = Math.min(1, (now - startTs) / segMs);
|
||||
// Linear interpolation → constant speed. Spanning the glide across the
|
||||
// full inter-fix interval chains consecutive segments into one continuous
|
||||
// motion (a vehicle moving down the road) rather than the accelerate/brake
|
||||
// feel an easing curve gives, or the dart-then-freeze of a fixed duration.
|
||||
|
||||
if (osrmPath && osrmPath.length >= 2) {
|
||||
const pos = interpolatePath(osrmPath, t);
|
||||
if (pos) {
|
||||
marker.setLatLng(pos);
|
||||
} else {
|
||||
marker.setLatLng([startLat + dLat * t, startLng + dLng * t]);
|
||||
}
|
||||
} else {
|
||||
// Fallback to linear interpolation if OSRM route isn't available
|
||||
marker.setLatLng([startLat + dLat * t, startLng + dLng * t]);
|
||||
}
|
||||
|
||||
if (t < 1) rafRef.current = requestAnimationFrame(step);
|
||||
};
|
||||
rafRef.current = requestAnimationFrame(step);
|
||||
return () => {
|
||||
active = false;
|
||||
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
||||
};
|
||||
}, [lat, lon, duration]);
|
||||
@@ -1455,13 +1522,13 @@ const Dispatch = ({
|
||||
// hub (latitude/longitude/logdate/status). We render those positions as
|
||||
// markers on the main dispatch map so the operator sees where each rider
|
||||
// actually is — matching the Reports → Riders Logs page.
|
||||
const RIDER_LOG_POLL_MS = 15000;
|
||||
const RIDER_LOG_POLL_MS = 1000;
|
||||
const { data: ridersLocationLogs } = useQuery({
|
||||
queryKey: [selectedAppLocationId, selectedDate, ''],
|
||||
queryFn: fetchRidersLogs,
|
||||
refetchInterval: RIDER_LOG_POLL_MS,
|
||||
refetchIntervalInBackground: false,
|
||||
staleTime: 5 * 1000,
|
||||
staleTime: 0,
|
||||
refetchOnWindowFocus: false
|
||||
});
|
||||
|
||||
@@ -4143,7 +4210,7 @@ const Dispatch = ({
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{(focusedRider || focusedKitchen) ? (
|
||||
{((focusedRider && !isAllActiveView) || focusedKitchen) ? (
|
||||
<div id="route-detail">
|
||||
<button className="rd-back" onClick={() => { handleRiderFocus(null); setFocusedKitchen(null); }}>← Back to {focusedZone ? focusedZone.name : 'list'}</button>
|
||||
{focusedRider ? (
|
||||
@@ -5028,7 +5095,7 @@ const Dispatch = ({
|
||||
// existing views behave exactly as they did before.
|
||||
const LiveMarker = isAllActiveView ? AnimatedRiderMarker : Marker;
|
||||
const positionProps = isAllActiveView
|
||||
? { target: [r.lat, r.lon], duration: 15000 }
|
||||
? { target: [r.lat, r.lon], duration: 1000 }
|
||||
: { position: [r.lat, r.lon] };
|
||||
return (
|
||||
<LiveMarker
|
||||
|
||||
Reference in New Issue
Block a user