updates on the changes on warning and lat and long fixed to make it good

This commit is contained in:
2026-09-18 11:59:43 +05:30
parent bc79ba4912
commit 05f08efc64
10 changed files with 669 additions and 28 deletions

View File

@@ -32,7 +32,22 @@ import {
getAdminTenants,
getTenantLocations
} from '@/api/doormile/endpoints';
import { calculateDrivingRoute, calculateTotalCharge, getLastRouteDurationMin } from '@/lib/distance';
// calculateDrivingDistance was used by the deadhead-leg effect below without
// ever being imported — a ReferenceError on every customer-pickup order that
// had a pinned collection address, thrown synchronously inside the effect so
// the `.catch()` on that promise chain could never see it. Neither the build
// nor the lint config catches an unbound identifier in a .jsx file, which is
// why it survived. Found while auditing the coordinate changes; unrelated to
// them.
//
// getLastRouteDurationMin is gone from this list: it was imported and never
// called, which the project's own lint reports as an error. Dropped while
// rewriting the statement rather than left behind in it.
import {
calculateDrivingDistance,
calculateDrivingRoute,
calculateTotalCharge
} from '@/lib/distance';
import { saveRecentAddress } from '@/lib/geocodingService';
import { useHubs } from '@/lib/doormileHooks';
import {

View File

@@ -37,6 +37,7 @@ import {
normalizeHeader,
requiredSheetColumns
} from '@/lib/bulkOrderColumns';
import { coord, hasCoords } from '@/lib/coords';
import { calculateDrivingDistance, calculateTotalCharge } from '@/lib/distance';
import { useHubs } from '@/lib/doormileHooks';
import { PICKUP_SOURCE, buildAnchors } from '@/lib/orderFlow';
@@ -465,8 +466,20 @@ export default function MultipleOrders() {
});
});
const latitude = place.geometry.location.lat();
const longitude = place.geometry.location.lng();
// Read the place's own fields first and fall back to the Google-shaped
// accessors, matching how line 343 already does it. Reading the accessor
// alone was the entry point for the 0,0 defect: geocodingService used to
// return 0 from `geometry.location.lat()` for a suggestion with no
// coordinates, this assignment stored that 0 verbatim, and the pre-submit
// guard's `== null` test could not see it.
const latitude = coord(
place?.latitude ?? place?.geometry?.location?.lat?.(),
'lat'
);
const longitude = coord(
place?.longitude ?? place?.geometry?.location?.lng?.(),
'lng'
);
const address = place.formatted_address || '';
setDropCust((prev) =>
@@ -477,6 +490,20 @@ export default function MultipleOrders() {
)
);
// No pin, no quote. Pricing an unpinned row is what produced the wrong
// charges: the distance came back from a fallback measurement to 0,0 and
// the row carried it as `totalcharge`, which the backend then honours as
// `finalprice`. Clearing the charge leaves the row visibly incomplete, and
// the submit guard below refuses it.
if (latitude === null || longitude === null) {
setDropCust((prev) =>
prev.map((c) =>
c._rowkey === rowkey ? { ...c, distance: null, totalcharge: null } : c
)
);
return;
}
try {
const { roundedDistance, totalcharge } = await calculateDistance({ latitude, longitude });
setDropCust((prev) =>
@@ -532,7 +559,17 @@ export default function MultipleOrders() {
return;
}
const incomplete = dropCust.find((c) => c.latitude == null || c.longitude == null || !c.address);
// Checked with hasCoords, not `== null`.
//
// `== null` matches null and undefined only, so a row carrying latitude 0
// — which is what a suggestion with no coordinates used to produce —
// passed this guard and was submitted. It then priced either as an
// 11,000 km run or, with no charge quoted, at base rate with no distance
// component. hasCoords also rejects '' and unparseable sheet cells, which
// reach here from an uploaded file.
const incomplete = dropCust.find(
(c) => !hasCoords(c.latitude, c.longitude) || !c.address
);
if (incomplete) {
OpenToast(
`Please search and complete address for ${incomplete.firstname || 'one of the drops'}`,
@@ -542,6 +579,31 @@ export default function MultipleOrders() {
return;
}
// The collection end, which was never checked here at all. CreateOrder
// gained this check already; the bulk form did not, so a hand-typed
// pickup that failed to geocode submitted every row in the file against a
// pickup the backend has to rescue from the hub record.
//
// Checked per row through rowPickup, not once against the shared pickup:
// a row that resolved its own sender address collects from THAT, and
// buildBulkBookingPayload reads the same `row.__pickup || sharedPickup`.
// Validating only the shared one would leave a per-row collection point
// unchecked, which is the half of the fan-out that varies.
const unpinnedPickup = dropCust.find((c) => {
const origin = rowPickup(c);
return !hasCoords(origin?.latitude, origin?.longitude);
});
if (unpinnedPickup) {
OpenToast(
rowPickup(unpinnedPickup) === pickCust
? 'The collection address has no coordinates — pick it from the suggestions or drop a map pin'
: `The sender address for ${unpinnedPickup.firstname || 'one of the drops'} has no coordinates`,
'warning',
4000
);
return;
}
const deliverytime = dayjs(`${startDate} ${selectedSlotTime}`, 'YYYY-MM-DD HH:mm').format(
'YYYY-MM-DD HH:mm:ss'
);
@@ -1048,7 +1110,14 @@ export default function MultipleOrders() {
</thead>
<tbody className="divide-y divide-slate-100 bg-white">
{dropCust.map((row, idx) => {
const needsAddress = row.latitude == null || row.longitude == null || !row.address;
// Same rule as the submit guard, deliberately. When this
// used `== null` and the guard used hasCoords, a row
// carrying latitude 0 rendered as complete and was then
// refused on dispatch — an error with nothing on screen
// pointing at the row that caused it. Both ends ask the
// same question now, so an unusable row always shows its
// address search.
const needsAddress = !hasCoords(row.latitude, row.longitude) || !row.address;
return (
<tr key={row._rowkey || idx} className="hover:bg-slate-50/70 transition-colors">
<td className="py-3 pl-4 pr-2 text-slate-600 font-mono text-xs font-bold">{idx + 1}</td>