updates on the build regarding the create order dropdown and fix on the customer and createorder page
This commit is contained in:
12
package-lock.json
generated
12
package-lock.json
generated
@@ -39,6 +39,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mui-daterange-picker": "^1.0.5",
|
"mui-daterange-picker": "^1.0.5",
|
||||||
"notistack": "^3.0.1",
|
"notistack": "^3.0.1",
|
||||||
|
"open-location-code": "^1.0.3",
|
||||||
"papaparse": "^5.5.3",
|
"papaparse": "^5.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
@@ -14285,6 +14286,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/open-location-code": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/open-location-code/-/open-location-code-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-DBm14BSn40Ee241n80zIFXIT6+y8Tb0I+jTdosLJ8Sidvr2qONvymwqymVbHV2nS+1gkDZ5eTNpnOIVV0Kn2fw==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.1",
|
"version": "0.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||||
@@ -30691,6 +30698,11 @@
|
|||||||
"is-wsl": "^2.2.0"
|
"is-wsl": "^2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"open-location-code": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/open-location-code/-/open-location-code-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-DBm14BSn40Ee241n80zIFXIT6+y8Tb0I+jTdosLJ8Sidvr2qONvymwqymVbHV2nS+1gkDZ5eTNpnOIVV0Kn2fw=="
|
||||||
|
},
|
||||||
"optionator": {
|
"optionator": {
|
||||||
"version": "0.9.1",
|
"version": "0.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mui-daterange-picker": "^1.0.5",
|
"mui-daterange-picker": "^1.0.5",
|
||||||
"notistack": "^3.0.1",
|
"notistack": "^3.0.1",
|
||||||
|
"open-location-code": "^1.0.3",
|
||||||
"papaparse": "^5.5.3",
|
"papaparse": "^5.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||||||
|
|
||||||
import { Autocomplete, TextField, CircularProgress } from '@mui/material';
|
import { Autocomplete, TextField, CircularProgress } from '@mui/material';
|
||||||
import { debounce } from '@mui/material/utils';
|
import { debounce } from '@mui/material/utils';
|
||||||
|
import { OpenLocationCode } from 'open-location-code';
|
||||||
|
|
||||||
// Free OSM Nominatim geocoder — replaces Google Places Autocomplete /
|
// Free OSM Nominatim geocoder — replaces Google Places Autocomplete /
|
||||||
// react-geocode across the console. No API key required. Nominatim's usage
|
// react-geocode across the console. No API key required. Nominatim's usage
|
||||||
// policy caps the public endpoint at ~1 req/sec, so every caller here is
|
// policy caps the public endpoint at ~1 req/sec, so every caller here is
|
||||||
// debounced (500ms) and cancels stale in-flight predictions.
|
// debounced (500ms) and cancels stale in-flight predictions.
|
||||||
const NOMINATIM_SEARCH_URL = 'https://nominatim.openstreetmap.org/search';
|
const NOMINATIM_SEARCH_URL = 'https://nominatim.openstreetmap.org/search';
|
||||||
|
const NOMINATIM_REVERSE_URL = 'https://nominatim.openstreetmap.org/reverse';
|
||||||
|
|
||||||
// Builds a Google-Places-shaped `address_components` array (each entry
|
// Builds a Google-Places-shaped `address_components` array (each entry
|
||||||
// `{ long_name, short_name, types: [] }`) from Nominatim's flat `address`
|
// `{ long_name, short_name, types: [] }`) from Nominatim's flat `address`
|
||||||
@@ -44,11 +46,99 @@ export const toPlace = (result) => ({
|
|||||||
address_components: buildAddressComponents(result.address)
|
address_components: buildAddressComponents(result.address)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Nominatim requests have no built-in timeout — a stalled connection (proxy
|
||||||
|
// silently dropping packets, an overloaded public endpoint) would otherwise
|
||||||
|
// leave `fetch` pending forever, so callers see the spinner spin with nothing
|
||||||
|
// ever happening. This bounds every request to REQUEST_TIMEOUT_MS.
|
||||||
|
const REQUEST_TIMEOUT_MS = 8000;
|
||||||
|
const fetchWithTimeout = (url, options) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
||||||
|
return fetch(url, { ...options, signal: controller.signal }).finally(() => clearTimeout(timer));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nominatim (and Google Places, previously) both do full-text search against
|
||||||
|
// a database of named places — neither understands Plus Codes (e.g.
|
||||||
|
// "5CM7+8Q7"), because a Plus Code isn't a name to look up, it's lat/lng
|
||||||
|
// encoded directly into the string. Decoding it is a pure local computation
|
||||||
|
// via the Open Location Code algorithm, so this resolves reliably even where
|
||||||
|
// OSM has zero data for the area (small towns, unlisted businesses).
|
||||||
|
const olc = new OpenLocationCode();
|
||||||
|
const PLUS_CODE_CHARS = '23456789CFGHJMPQRVWX';
|
||||||
|
const PLUS_CODE_RE = new RegExp(`[${PLUS_CODE_CHARS}]{2,8}\\+[${PLUS_CODE_CHARS}]{2,3}`, 'i');
|
||||||
|
|
||||||
|
// A decoded Plus Code is just coordinates — on its own it has no street/
|
||||||
|
// locality name to show the operator, only the code itself. Reverse-geocoding
|
||||||
|
// those coordinates fills in the human-readable side (road, suburb, city...)
|
||||||
|
// the same way a normal search result would, so the option in the dropdown
|
||||||
|
// reads as an actual address instead of a bare code. Best-effort: if Nominatim
|
||||||
|
// has no data for that exact point either, callers fall back to the code text.
|
||||||
|
async function reverseGeocode(lat, lon) {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({ lat: String(lat), lon: String(lon), format: 'json', addressdetails: '1' });
|
||||||
|
const res = await fetchWithTimeout(`${NOMINATIM_REVERSE_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } });
|
||||||
|
if (!res.ok) return null;
|
||||||
|
const result = await res.json();
|
||||||
|
if (!result || result.error) return null;
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short codes (the common case — "5CM7+8Q7" rather than the full
|
||||||
|
// "6JWV5CM7+8Q7") only encode an offset; recovering the actual location
|
||||||
|
// needs a nearby reference point, which is what `bias` (the operator's
|
||||||
|
// tenant/app location) provides. Returns a Nominatim-result-shaped object
|
||||||
|
// (same shape `toPlace()` expects) so it can flow through the exact same
|
||||||
|
// path as a real search hit, or null if no code is present / it can't be
|
||||||
|
// resolved without a reference point.
|
||||||
|
const plusCodeToResult = async (text, bias) => {
|
||||||
|
const match = text?.match(PLUS_CODE_RE);
|
||||||
|
if (!match) return null;
|
||||||
|
const code = match[0].toUpperCase();
|
||||||
|
if (!olc.isValid(code)) return null;
|
||||||
|
try {
|
||||||
|
let fullCode = code;
|
||||||
|
if (olc.isShort(code)) {
|
||||||
|
const refLat = Number(bias?.lat);
|
||||||
|
const refLng = Number(bias?.lng);
|
||||||
|
// (0,0) isn't a plausible tenant location — it's what `Number('')` and
|
||||||
|
// `Number(undefined)` half-produce (empty string coerces to 0), so treat
|
||||||
|
// it as "no reference point yet" rather than decoding against Null Island.
|
||||||
|
if (!Number.isFinite(refLat) || !Number.isFinite(refLng) || (refLat === 0 && refLng === 0)) return null;
|
||||||
|
fullCode = olc.recoverNearest(code, refLat, refLng);
|
||||||
|
}
|
||||||
|
if (!olc.isFull(fullCode)) return null;
|
||||||
|
const { latitudeCenter, longitudeCenter } = olc.decode(fullCode);
|
||||||
|
// The decoded centroid is exact — keep it as the authoritative coordinate
|
||||||
|
// even though `reverse` below carries its own (possibly snapped-to-nearest-
|
||||||
|
// feature) lat/lon; only its display_name/address are what we want.
|
||||||
|
const reverse = await reverseGeocode(latitudeCenter, longitudeCenter);
|
||||||
|
return {
|
||||||
|
...reverse,
|
||||||
|
place_id: `pluscode-${fullCode}`,
|
||||||
|
lat: String(latitudeCenter),
|
||||||
|
lon: String(longitudeCenter),
|
||||||
|
display_name: reverse?.display_name ? `${reverse.display_name} (near ${code})` : `${code} (Plus Code location)`,
|
||||||
|
address: reverse?.address || {}
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buildParams = (query, bias) => {
|
const buildParams = (query, bias) => {
|
||||||
const params = new URLSearchParams({ q: query, format: 'json', addressdetails: '1', limit: '5' });
|
const params = new URLSearchParams({ q: query, format: 'json', addressdetails: '1', limit: '5' });
|
||||||
if (bias?.lat && bias?.lng) {
|
// bias.lat/lng arrive as strings from the tenant-location API. `-` coerces to
|
||||||
|
// number automatically, but `+` on a string concatenates instead of adding
|
||||||
|
// (e.g. "8.17445" + 0.5 === "8.174450.5"), producing a malformed, non-numeric
|
||||||
|
// viewbox that Nominatim rejects with 400 Bad Request on every single search.
|
||||||
|
const lat = Number(bias?.lat);
|
||||||
|
const lng = Number(bias?.lng);
|
||||||
|
if (Number.isFinite(lat) && Number.isFinite(lng)) {
|
||||||
const d = 0.5; // ~55km box around the operator's zone — soft bias, not a hard filter
|
const d = 0.5; // ~55km box around the operator's zone — soft bias, not a hard filter
|
||||||
params.set('viewbox', `${bias.lng - d},${bias.lat + d},${bias.lng + d},${bias.lat - d}`);
|
params.set('viewbox', `${lng - d},${lat + d},${lng + d},${lat - d}`);
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
};
|
};
|
||||||
@@ -59,10 +149,12 @@ const buildParams = (query, bias) => {
|
|||||||
// or null if nothing matched.
|
// or null if nothing matched.
|
||||||
export async function geocodeAddress(address, { bias } = {}) {
|
export async function geocodeAddress(address, { bias } = {}) {
|
||||||
if (!address) return null;
|
if (!address) return null;
|
||||||
|
const plusCodeResult = await plusCodeToResult(address, bias);
|
||||||
|
if (plusCodeResult) return toPlace(plusCodeResult);
|
||||||
try {
|
try {
|
||||||
const params = buildParams(address, bias);
|
const params = buildParams(address, bias);
|
||||||
params.set('limit', '1');
|
params.set('limit', '1');
|
||||||
const res = await fetch(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } });
|
const res = await fetchWithTimeout(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } });
|
||||||
if (!res.ok) return null;
|
if (!res.ok) return null;
|
||||||
const results = await res.json();
|
const results = await res.json();
|
||||||
if (!results?.length) return null;
|
if (!results?.length) return null;
|
||||||
@@ -115,10 +207,13 @@ const AddressAutocomplete = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const params = buildParams(query, bias1);
|
const params = buildParams(query, bias1);
|
||||||
fetch(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } })
|
const searchPromise = fetchWithTimeout(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } })
|
||||||
.then((res) => (res.ok ? res.json() : []))
|
.then((res) => (res.ok ? res.json() : []))
|
||||||
.then((results) => callback(results || []))
|
.catch(() => []);
|
||||||
.catch(() => callback([]));
|
const plusCodePromise = plusCodeToResult(query, bias1).catch(() => null);
|
||||||
|
Promise.all([searchPromise, plusCodePromise]).then(([results, plusCodeResult]) => {
|
||||||
|
callback(plusCodeResult ? [plusCodeResult, ...(results || [])] : results || []);
|
||||||
|
});
|
||||||
}, 500),
|
}, 500),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -153,6 +248,13 @@ const AddressAutocomplete = ({
|
|||||||
filterOptions={(x) => x}
|
filterOptions={(x) => x}
|
||||||
getOptionLabel={(option) => (typeof option === 'string' ? option : option.display_name || '')}
|
getOptionLabel={(option) => (typeof option === 'string' ? option : option.display_name || '')}
|
||||||
isOptionEqualToValue={(option, val) => option.place_id === val?.place_id}
|
isOptionEqualToValue={(option, val) => option.place_id === val?.place_id}
|
||||||
|
noOptionsText={
|
||||||
|
loading
|
||||||
|
? 'Searching…'
|
||||||
|
: inputValue.trim().length >= 3
|
||||||
|
? 'No match found — drop the business name/Plus Code, or try just City + Postcode'
|
||||||
|
: 'Type an address to search'
|
||||||
|
}
|
||||||
inputValue={inputValue}
|
inputValue={inputValue}
|
||||||
onInputChange={(event, newInputValue) => {
|
onInputChange={(event, newInputValue) => {
|
||||||
setInputValue(newInputValue);
|
setInputValue(newInputValue);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import MainCard from 'components/MainCard';
|
|||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
|
import AddressAutocomplete, { geocodeAddress } from 'components/nearle_components/AddressAutocomplete';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from 'notistack';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
@@ -140,6 +140,30 @@ const CreateCustomer = () => {
|
|||||||
longitude: place.geometry.location.lng()
|
longitude: place.geometry.location.lng()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
// ============================================= || Fallback geocode for manually typed address || =============================================
|
||||||
|
// AddressAutocomplete only resolves lat/lng when a suggestion is actually clicked
|
||||||
|
// from its dropdown (handlePickPlaceSelected). The common real-world case is an
|
||||||
|
// operator typing/pasting the full address into that single Address box and never
|
||||||
|
// clicking a suggestion — that left startPoint at (0,0) forever, and because the
|
||||||
|
// submit validation below used to check pickCust.latitude for an empty string
|
||||||
|
// that's never actually set, customers were saved with latitude/longitude "0"/"0"
|
||||||
|
// with no warning at all. This covers both the raw Address-box text and the
|
||||||
|
// manually filled Location/City/Postcode fields as a fallback, reusing the same
|
||||||
|
// handler a real dropdown selection would call so the rest of the fields fill in too.
|
||||||
|
useEffect(() => {
|
||||||
|
if (startPoint.latitude !== 0 || startPoint.longitude !== 0) return undefined;
|
||||||
|
const manualFields = [doorno, pickCust.suburb, pickCust.city, pickCust.state, pickCust.postcode].filter(Boolean).join(', ');
|
||||||
|
const manualAddress =
|
||||||
|
(inputValue2 && inputValue2.trim().length >= 6 && inputValue2) || (pickCust.suburb && pickCust.city && manualFields);
|
||||||
|
if (!manualAddress) return undefined;
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
const place = await geocodeAddress(manualAddress, { bias: { lat: appLocaLat, lng: appLocaLng } });
|
||||||
|
if (place) handlePickPlaceSelected(place);
|
||||||
|
}, 800);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [inputValue2, doorno, pickCust.suburb, pickCust.city, pickCust.state, pickCust.postcode, appLocaLat, appLocaLng]);
|
||||||
|
|
||||||
// ==================================================== || getapplocations || ====================================================
|
// ==================================================== || getapplocations || ====================================================
|
||||||
const getapplocations = async () => {
|
const getapplocations = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -554,10 +578,8 @@ const CreateCustomer = () => {
|
|||||||
opentoast('Enter Post Code ');
|
opentoast('Enter Post Code ');
|
||||||
} else if (landmark === '') {
|
} else if (landmark === '') {
|
||||||
opentoast('Enter Land Mark ');
|
opentoast('Enter Land Mark ');
|
||||||
} else if (pickCust.latitude === '') {
|
} else if (!startPoint.latitude || !startPoint.longitude) {
|
||||||
opentoast('Invalid latitude ');
|
opentoast('Could not locate address on map, please recheck the address');
|
||||||
} else if (pickCust.longitude === '') {
|
|
||||||
opentoast('Invaiid Longitude ');
|
|
||||||
} else {
|
} else {
|
||||||
createprofile();
|
createprofile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import { TbMapPinCode } from 'react-icons/tb';
|
|||||||
import { FaLocationDot } from 'react-icons/fa6';
|
import { FaLocationDot } from 'react-icons/fa6';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
|
import AddressAutocomplete, { geocodeAddress } from 'components/nearle_components/AddressAutocomplete';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import * as geolib from 'geolib';
|
import * as geolib from 'geolib';
|
||||||
import MainCard from 'components/MainCard';
|
import MainCard from 'components/MainCard';
|
||||||
@@ -578,10 +578,10 @@ const Createorder1 = () => {
|
|||||||
deliverycustomer: dropCust.firstname || '',
|
deliverycustomer: dropCust.firstname || '',
|
||||||
deliveryid: isNumChange2 == 0 ? +dropCust.customerid || 0 : 0,
|
deliveryid: isNumChange2 == 0 ? +dropCust.customerid || 0 : 0,
|
||||||
deliverylandmark: dropCust.landmark || '',
|
deliverylandmark: dropCust.landmark || '',
|
||||||
deliverylat: dropCust.latitude.toString(),
|
deliverylat: (dropCust.latitude || 0).toString(),
|
||||||
deliverylocation: dropCust.suburb || '',
|
deliverylocation: dropCust.suburb || '',
|
||||||
deliverylocationid: dropCust.deliverylocationid || 0,
|
deliverylocationid: dropCust.deliverylocationid || 0,
|
||||||
deliverylong: dropCust.longitude.toString(),
|
deliverylong: (dropCust.longitude || 0).toString(),
|
||||||
deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`,
|
deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`,
|
||||||
deliverytype: pickCust.customerid !== 0 || dropCust.customerid !== 0 ? 'B' : 'C',
|
deliverytype: pickCust.customerid !== 0 || dropCust.customerid !== 0 ? 'B' : 'C',
|
||||||
delivered: '',
|
delivered: '',
|
||||||
@@ -606,10 +606,10 @@ const Createorder1 = () => {
|
|||||||
pickupcontactno: pickCust.contactno || '',
|
pickupcontactno: pickCust.contactno || '',
|
||||||
pickupcustomer: pickCust.firstname || '',
|
pickupcustomer: pickCust.firstname || '',
|
||||||
pickuplandmark: pickCust.landmark || '',
|
pickuplandmark: pickCust.landmark || '',
|
||||||
pickuplat: pickCust.latitude.toString(),
|
pickuplat: (pickCust.latitude || 0).toString(),
|
||||||
pickuplocation: pickCust.suburb || '',
|
pickuplocation: pickCust.suburb || '',
|
||||||
pickuplocationid: pickCust.deliverylocationid || 0,
|
pickuplocationid: pickCust.deliverylocationid || 0,
|
||||||
pickuplong: pickCust.longitude.toString(),
|
pickuplong: (pickCust.longitude || 0).toString(),
|
||||||
processing: '',
|
processing: '',
|
||||||
ready: '',
|
ready: '',
|
||||||
remarks: '',
|
remarks: '',
|
||||||
@@ -637,8 +637,8 @@ const Createorder1 = () => {
|
|||||||
email: pickCust.email || '',
|
email: pickCust.email || '',
|
||||||
firstname: pickCust.firstname || '',
|
firstname: pickCust.firstname || '',
|
||||||
landmark: pickCust.landmark || '',
|
landmark: pickCust.landmark || '',
|
||||||
latitude: pickCust.latitude.toString() || '',
|
latitude: (pickCust.latitude || 0).toString(),
|
||||||
longitude: pickCust.longitude.toString() || '',
|
longitude: (pickCust.longitude || 0).toString(),
|
||||||
locationid: pickCust.deliverylocationid || 0,
|
locationid: pickCust.deliverylocationid || 0,
|
||||||
postcode: pickCust.postcode || '',
|
postcode: pickCust.postcode || '',
|
||||||
primaryaddress: 1,
|
primaryaddress: 1,
|
||||||
@@ -663,8 +663,8 @@ const Createorder1 = () => {
|
|||||||
email: dropCust.email || '',
|
email: dropCust.email || '',
|
||||||
firstname: dropCust.firstname || '',
|
firstname: dropCust.firstname || '',
|
||||||
landmark: dropCust.landmark || '',
|
landmark: dropCust.landmark || '',
|
||||||
latitude: dropCust.latitude.toString(),
|
latitude: (dropCust.latitude || 0).toString(),
|
||||||
longitude: dropCust.longitude.toString(),
|
longitude: (dropCust.longitude || 0).toString(),
|
||||||
locationid: dropCust.deliverylocationid || 0,
|
locationid: dropCust.deliverylocationid || 0,
|
||||||
postcode: dropCust.postcode || '',
|
postcode: dropCust.postcode || '',
|
||||||
primaryaddress: 1,
|
primaryaddress: 1,
|
||||||
@@ -689,6 +689,8 @@ const Createorder1 = () => {
|
|||||||
opentoast('Enter Pickup Postcode ', 'warning', 2000);
|
opentoast('Enter Pickup Postcode ', 'warning', 2000);
|
||||||
} else if (!pickCust.landmark) {
|
} else if (!pickCust.landmark) {
|
||||||
opentoast('Enter Pickup Landmark ', 'warning', 2000);
|
opentoast('Enter Pickup Landmark ', 'warning', 2000);
|
||||||
|
} else if (!pickCust.latitude || !pickCust.longitude) {
|
||||||
|
opentoast('Could not locate Pickup address on map, please recheck the address', 'error', 3000);
|
||||||
} else if (!dropCust.firstname) {
|
} else if (!dropCust.firstname) {
|
||||||
opentoast('Enter Drop Contact Name ', 'warning', 2000);
|
opentoast('Enter Drop Contact Name ', 'warning', 2000);
|
||||||
} else if (!dropCust.contactno) {
|
} else if (!dropCust.contactno) {
|
||||||
@@ -703,6 +705,8 @@ const Createorder1 = () => {
|
|||||||
opentoast('Enter Drop postcode ', 'warning', 2000);
|
opentoast('Enter Drop postcode ', 'warning', 2000);
|
||||||
} else if (!dropCust.landmark) {
|
} else if (!dropCust.landmark) {
|
||||||
opentoast('Enter Drop Landmark ', 'warning', 2000);
|
opentoast('Enter Drop Landmark ', 'warning', 2000);
|
||||||
|
} else if (!dropCust.latitude || !dropCust.longitude) {
|
||||||
|
opentoast('Could not locate Drop address on map, please recheck the address', 'error', 3000);
|
||||||
} else if (!selectedtime) {
|
} else if (!selectedtime) {
|
||||||
opentoast('Choose deliverytime ', 'warning', 2000);
|
opentoast('Choose deliverytime ', 'warning', 2000);
|
||||||
} else if (!setSubCatId) {
|
} else if (!setSubCatId) {
|
||||||
@@ -991,6 +995,45 @@ const Createorder1 = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================= || Fallback geocode for manually typed addresses || =============================================
|
||||||
|
// AddressAutocomplete only resolves lat/lng when a suggestion is actually clicked
|
||||||
|
// from its dropdown (handlePickPlaceSelected/handleDropPlaceSelected). The common
|
||||||
|
// real-world case is an operator typing/pasting the full address into that single
|
||||||
|
// Address box and never clicking a suggestion (none showed up, or they just hit
|
||||||
|
// tab/moved on) — that left startPoint/endPoint at (0,0) forever, distance never
|
||||||
|
// calculated, and "Select Pickup and Drop" firing no matter what was typed.
|
||||||
|
// These effects cover that: once typing pauses, whatever address text exists —
|
||||||
|
// the raw Address-box text, or the manually filled Door No/Location/City/Postcode
|
||||||
|
// fields as a fallback — gets geocoded, reusing the same handler that a real
|
||||||
|
// dropdown selection would call so suburb/city/postcode get filled in too.
|
||||||
|
useEffect(() => {
|
||||||
|
if (startPoint.latitude !== 0 || startPoint.longitude !== 0) return undefined;
|
||||||
|
const manualAddress =
|
||||||
|
(inputValue2 && inputValue2.trim().length >= 6 && inputValue2) ||
|
||||||
|
(pickCust.suburb && pickCust.city && [pickCust.doorno, pickCust.suburb, pickCust.city, pickCust.postcode].filter(Boolean).join(', '));
|
||||||
|
if (!manualAddress) return undefined;
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
const place = await geocodeAddress(manualAddress, { bias: { lat: appLocaLat, lng: appLocaLng } });
|
||||||
|
if (place) handlePickPlaceSelected(place);
|
||||||
|
}, 800);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [inputValue2, pickCust.doorno, pickCust.suburb, pickCust.city, pickCust.postcode, appLocaLat, appLocaLng]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (endPoint.latitude !== 0 || endPoint.longitude !== 0) return undefined;
|
||||||
|
const manualAddress =
|
||||||
|
(inputValue3 && inputValue3.trim().length >= 6 && inputValue3) ||
|
||||||
|
(dropCust.suburb && dropCust.city && [dropCust.doorno, dropCust.suburb, dropCust.city, dropCust.postcode].filter(Boolean).join(', '));
|
||||||
|
if (!manualAddress) return undefined;
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
const place = await geocodeAddress(manualAddress, { bias: { lat: appLocaLat, lng: appLocaLng } });
|
||||||
|
if (place) handleDropPlaceSelected(place);
|
||||||
|
}, 800);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [inputValue3, dropCust.doorno, dropCust.suburb, dropCust.city, dropCust.postcode, appLocaLat, appLocaLng]);
|
||||||
|
|
||||||
// ============================================= || gettenantlocations (branches) || =============================================
|
// ============================================= || gettenantlocations (branches) || =============================================
|
||||||
const gettenantlocations = async () => {
|
const gettenantlocations = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -1749,7 +1792,7 @@ const Createorder1 = () => {
|
|||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
value={dropCust.address}
|
value={dropCust.address}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setPickCust({ ...dropCust, address: e.target.value });
|
setDropCust({ ...dropCust, address: e.target.value });
|
||||||
if (e.target.value == '') {
|
if (e.target.value == '') {
|
||||||
setAddId2(0);
|
setAddId2(0);
|
||||||
setShowDistance(false);
|
setShowDistance(false);
|
||||||
|
|||||||
@@ -7798,6 +7798,11 @@ onetime@^5.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn "^2.1.0"
|
mimic-fn "^2.1.0"
|
||||||
|
|
||||||
|
open-location-code@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/open-location-code/-/open-location-code-1.0.3.tgz"
|
||||||
|
integrity sha512-DBm14BSn40Ee241n80zIFXIT6+y8Tb0I+jTdosLJ8Sidvr2qONvymwqymVbHV2nS+1gkDZ5eTNpnOIVV0Kn2fw==
|
||||||
|
|
||||||
open@^8.0.9, open@^8.4.0:
|
open@^8.0.9, open@^8.4.0:
|
||||||
version "8.4.0"
|
version "8.4.0"
|
||||||
resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz"
|
resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user