updates on the dead codes removal

This commit is contained in:
2026-08-01 16:50:58 +05:30
parent 79fefda61a
commit 6ed0e577e0
20 changed files with 55 additions and 781 deletions

View File

@@ -1,10 +1,7 @@
import React, { useEffect, useMemo, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
import dayjs from 'dayjs';
import { HiOutlineArrowLeft } from 'react-icons/hi';
import { IoReload } from 'react-icons/io5';
import { MdTwoWheeler } from 'react-icons/md';
import { Button } from '@astryxdesign/core/Button';
import { IconButton } from '@astryxdesign/core/IconButton';
@@ -12,27 +9,12 @@ import { Tooltip } from '@astryxdesign/core/Tooltip';
import { Stack } from '@astryxdesign/core/Stack';
import { Heading } from '@astryxdesign/core/Heading';
import {
createAutomationDeliveries,
createOptimisationDeliveries,
finalCreatedeliveries,
notifyRider,
reconcileSteps
} from '../api/api';
import { finalCreatedeliveries, notifyRider } from '../api/api';
import { OpenToast } from 'components/nearle_components/OpenToast';
import CSVExport from 'components/third-party/ReactTable';
import CircularLoader from 'components/nearle_components/CircularLoader';
import Dispatch from './Dispatch';
import { stepColor } from './dispatchShared';
import { BRAND, DT } from '../_shared/ordersDesign';
const tuningTypes = [
{ tuneid: 1, type: 'Balanced', value: 'balanced' },
{ tuneid: 2, type: 'Aggressive Speed', value: 'aggressive_speed' },
{ tuneid: 3, type: 'Fuel Saver', value: 'fuel_saver' },
{ tuneid: 4, type: 'Zone Strict', value: 'zone_strict' }
];
// Flatten the API's zoned shape into [{ rider_id, rider_name, orders }] for
// the Reconcile tab UI and the reconcile-API payload.
const extractRiders = (previewData) => {
@@ -103,91 +85,6 @@ const flattenRiders = (riders) => {
return out;
};
// Merge a reconcile-API response { riders:[{rider_id, orders}] } back into
// dispatchPreviewData. Replaces each rider's orders[] in zones (preserving
// zone containment), then rebuilds the flat details list from the new tree.
const applyReconcileResponse = (preview, response) => {
if (!preview || !Array.isArray(response?.riders)) return preview;
const next = JSON.parse(JSON.stringify(preview));
const newOrdersByRider = new Map(
response.riders.map((r) => [String(r.rider_id), r.orders || []])
);
if (Array.isArray(next.zones) && next.zones.length) {
// Pass 1: wipe every existing copy of a responding rider's orders across
// ALL zones. The server's reconciled list is the single source of truth,
// and a rider can be present in multiple zones (one per delivery suburb).
// The previous "update first match, delete from map" loop left stale
// copies in the other zones, which extractRiders then concatenated into
// duplicate orderids — surfacing as duplicate deliveries on Assign.
next.zones.forEach((zone) => {
if (!Array.isArray(zone.riders)) return;
zone.riders.forEach((r) => {
const key = String(r.rider_id ?? r.userid);
if (newOrdersByRider.has(key)) r.orders = [];
});
});
// Pass 2: drop the reconciled orders onto the first zone that already
// lists the rider. If the rider isn't anywhere in the tree, append a
// fresh rider entry to zone[0].
newOrdersByRider.forEach((orders, riderKey) => {
let placed = false;
for (const zone of next.zones) {
if (!Array.isArray(zone.riders)) continue;
const target = zone.riders.find(
(r) => String(r.rider_id ?? r.userid) === riderKey
);
if (target) {
target.orders = orders;
placed = true;
break;
}
}
if (!placed) {
const target = next.zones[0];
target.riders = target.riders || [];
target.riders.push({
rider_id: Number(riderKey) || riderKey,
rider_name: orders[0]?.rider_name || `Rider ${riderKey}`,
orders
});
}
});
} else {
next.zones = [
{
zone_name: 'Reconciled',
riders: response.riders.map((r) => ({
rider_id: r.rider_id,
rider_name: r.rider_name || `Rider ${r.rider_id}`,
orders: r.orders || []
}))
}
];
}
// Rebuild flat details from the updated zones->riders->orders tree.
const flatDetails = [];
next.zones.forEach((zone) => {
(zone.riders || []).forEach((r) => {
(r.orders || []).forEach((o) => {
flatDetails.push({
...o,
rider_id: r.rider_id,
userid: r.rider_id,
rider_name: r.rider_name,
rider: r.rider_name
});
});
});
});
next.details = flatDetails;
return next;
};
const Preview = () => {
const navigate = useNavigate();
const location = useLocation();
@@ -197,7 +94,7 @@ const Preview = () => {
// through this state. The Dispatch tab renders from it, the Reconcile tab
// derives its rider list from it, and Assign Orders sends a flattened copy
// of it to the API.
const [dispatchPreviewData, setDispatchPreviewData] = useState(stateData.dispatchPreviewData || null);
const [dispatchPreviewData] = useState(stateData.dispatchPreviewData || null);
// The AI response arrives via location.state, which the browser stores in
// history.state and persists across reloads. That means a reload of
@@ -221,34 +118,11 @@ const Preview = () => {
document.body.style.overflow = 'hidden';
return () => { document.body.style.overflow = prev; };
}, []);
const [csvExportData, setCsvExportData] = useState([]);
const [, setCsvExportData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [tabValue, setTabValue] = useState(0);
const [reconcileLoading, setReconcileLoading] = useState(false);
const [hasReconciled, setHasReconciled] = useState(false);
// Tracks riders whose orders have been edited since the last AI response
// or successful reconcile. Only these are sent to the reconcile API — the
// server-side step re-ordering only needs to see what actually changed.
const [dirtyRiderIds, setDirtyRiderIds] = useState(() => new Set());
const aiMode = stateData.aiMode ?? 1;
const selectedMode = stateData.selectedMode || null;
const deliveryData = stateData.deliveryData || [];
const autoRiders = stateData.autoRiders || [];
const absentRidersPayload = stateData.absentRidersPayload || [];
const rider = stateData.rider || null;
const appId = useMemo(() => {
if (stateData.appId) return stateData.appId;
if (typeof window !== 'undefined') {
const v = localStorage.getItem('applocationid');
return v ? Number(v) : 0;
}
return 0;
}, [stateData.appId]);
// Derived: rider list for the Reconcile tab. Recomputes whenever the cache
// (dispatchPreviewData) changes — so Reconcile reflects here without a separate state.
const reconcileRiders = useMemo(() => extractRiders(dispatchPreviewData), [dispatchPreviewData]);
@@ -293,23 +167,6 @@ const Preview = () => {
onError: (error) => OpenToast(error.message, 'error', 2000)
});
const createDeliveryMutation = useMutation({
mutationFn: aiMode == 0 ? createOptimisationDeliveries : createAutomationDeliveries,
onSuccess: (data) => {
OpenToast('Orders Optimised Successfully', 'success', 2000);
// Brand new response = brand new source of truth.
setDispatchPreviewData(data);
setHasReconciled(false);
setDirtyRiderIds(new Set());
setIsLoading(false);
},
onError: (error) => {
OpenToast(error.message, 'error', 4000);
setIsLoading(false);
},
onSettled: () => setIsLoading(false)
});
const createFinalDeliveryMutation = useMutation({
mutationFn: finalCreatedeliveries,
onSuccess: () => {
@@ -325,57 +182,6 @@ const Preview = () => {
onSettled: () => setIsLoading(false)
});
const reconcileMutation = useMutation({
mutationFn: reconcileSteps,
onMutate: () => setReconcileLoading(true),
onSuccess: (data) => {
if (Array.isArray(data?.riders)) {
// Merge: applyReconcileResponse replaces orders for riders present
// in the response and leaves the rest of the cache untouched.
setDispatchPreviewData((prev) => applyReconcileResponse(prev, data));
setHasReconciled(true);
// Clear only the riders we just reconciled from the dirty set, so
// any unrelated edits made meanwhile are preserved.
setDirtyRiderIds((prev) => {
const next = new Set(prev);
data.riders.forEach((r) => next.delete(String(r.rider_id)));
return next;
});
OpenToast('Steps reconciled — preview updated', 'success', 2000);
} else {
OpenToast('Reconcile returned no rider data', 'warning', 3000);
}
},
onError: (error) => {
OpenToast(error.message || 'Reconcile failed', 'error', 4000);
},
onSettled: () => setReconcileLoading(false)
});
const handleCreateDelivery = (tune) => {
setIsLoading(true);
if (aiMode == 0) {
createDeliveryMutation.mutate({ deliveries: deliveryData });
} else if (selectedMode && selectedMode?.value == 1) {
createDeliveryMutation.mutate({
deliveries: deliveryData,
hypertuning_params: tune || null,
selectedMode,
absent_riders: absentRidersPayload
});
} else {
createDeliveryMutation.mutate({
data: {
orders: deliveryData,
riders: autoRiders,
config: { pay_type: 'hourly', base_pay: 300.0, strategy: 'multi_trip' },
absent_riders: absentRidersPayload
},
selectedMode
});
}
};
const handleFinalCreateDelivery = () => {
if (!finaldeliveryList?.length) {
OpenToast('No deliveries to assign', 'error', 3000);
@@ -385,29 +191,6 @@ const Preview = () => {
createFinalDeliveryMutation.mutate({ deliveries: finaldeliveryList });
};
const handleReconcile = () => {
if (!reconcileRiders.length) {
OpenToast('No riders to reconcile', 'warning', 3000);
return;
}
// Only send riders that were edited since the last AI response / reconcile.
// Their step ordering is the only thing that can be stale — untouched
// riders are skipped to keep the payload small.
const dirty = reconcileRiders.filter((r) =>
dirtyRiderIds.has(String(r.rider_id))
);
if (!dirty.length) {
OpenToast('No edits to reconcile', 'info', 2500);
return;
}
reconcileMutation.mutate({
riders: dirty.map((r) => ({
rider_id: r.rider_id,
orders: r.orders
}))
});
};
return (
<div
className="dispatch-preview-shell"