updates on the minor changes like zone updates and datepicker fix
This commit is contained in:
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Clock, Compass, Crosshair, Loader2, MapPin, Navigation, X } from 'lucide-react';
|
import { Clock, Compass, Crosshair, Loader2, MapPin, Navigation, X } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
|
cityNamedIn,
|
||||||
extractLandmark,
|
extractLandmark,
|
||||||
getAddressSuggestions,
|
getAddressSuggestions,
|
||||||
geocodeAddress as serviceGeocodeAddress,
|
geocodeAddress as serviceGeocodeAddress,
|
||||||
@@ -38,9 +39,17 @@ const AddressAutocomplete = ({
|
|||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
onPlaceSelected,
|
onPlaceSelected,
|
||||||
|
// Fired when the text stops describing the place that was picked — cleared,
|
||||||
|
// or rewritten to another area. The page should drop that place's pin: it
|
||||||
|
// used to stay, so the form saved one address with another address's pin.
|
||||||
|
onPlaceCleared,
|
||||||
onOpenMapPin,
|
onOpenMapPin,
|
||||||
onLandmarkExtracted,
|
onLandmarkExtracted,
|
||||||
bias,
|
bias,
|
||||||
|
// A city scope from buildCityScope(): suggestions only from that city. The
|
||||||
|
// operator can widen one search to all cities from the list's footer, for
|
||||||
|
// an intercity drop.
|
||||||
|
scope,
|
||||||
fullWidth = true,
|
fullWidth = true,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
className = '',
|
className = '',
|
||||||
@@ -60,21 +69,100 @@ const AddressAutocomplete = ({
|
|||||||
|
|
||||||
const wrapRef = useRef(null);
|
const wrapRef = useRef(null);
|
||||||
const activeRef = useRef(0);
|
const activeRef = useRef(0);
|
||||||
const skipNextSearchRef = useRef(false);
|
// True only while the text is the operator's typing. Text the page sets (a
|
||||||
|
// hub location filling the pickup, a chosen customer's address) or a picked
|
||||||
|
// suggestion is not searched, not even when the hub's city scope changes
|
||||||
|
// underneath it: that re-search opened an unasked-for list of
|
||||||
|
// "124 Gandhipuram Main Rd" matches on every Create Order load.
|
||||||
|
const typedRef = useRef(false);
|
||||||
|
// The in-flight search, aborted when a newer one starts. Ignoring stale
|
||||||
|
// answers was not enough: the requests still ran against the free services'
|
||||||
|
// allowance and slowed the one that mattered.
|
||||||
|
const abortRef = useRef(null);
|
||||||
|
// Name of the picked place. Typing that keeps it ("12/A, " + it) keeps the
|
||||||
|
// pin; text that no longer contains it means the pin no longer applies.
|
||||||
|
const pickedNameRef = useRef('');
|
||||||
|
|
||||||
|
const editText = (text) => {
|
||||||
|
typedRef.current = true;
|
||||||
|
setInputValue(text);
|
||||||
|
onChange?.(text);
|
||||||
|
const picked = pickedNameRef.current;
|
||||||
|
if (picked && !text.toLowerCase().includes(picked)) {
|
||||||
|
pickedNameRef.current = '';
|
||||||
|
onPlaceCleared?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => () => abortRef.current?.abort(), []);
|
||||||
const listId = `${id || 'address'}-suggestions`;
|
const listId = `${id || 'address'}-suggestions`;
|
||||||
const optionId = (index) => `${listId}-option-${index}`;
|
const optionId = (index) => `${listId}-option-${index}`;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value !== undefined && value !== inputValue) {
|
if (value !== undefined && value !== inputValue) {
|
||||||
|
typedRef.current = false;
|
||||||
setInputValue(value || '');
|
setInputValue(value || '');
|
||||||
}
|
}
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
const biasKey = `${bias?.lat ?? ''},${bias?.lng ?? ''},${bias?.city ?? ''}`;
|
// "Search all cities" for this field. Reset when the scope itself changes —
|
||||||
|
// choosing another hub location means searching that city again.
|
||||||
|
const [scopeOff, setScopeOff] = useState(false);
|
||||||
|
const scopeCity = scope?.city || '';
|
||||||
|
useEffect(() => setScopeOff(false), [scopeCity]);
|
||||||
|
const activeScope = scope && !scopeOff ? scope : undefined;
|
||||||
|
|
||||||
|
const biasKey = `${bias?.lat ?? ''},${bias?.lng ?? ''},${bias?.city ?? ''}|${activeScope ? activeScope.city : 'all'}`;
|
||||||
|
|
||||||
|
// A city named at the end of the text ("gandhi nagar coimbatore") is searched
|
||||||
|
// in that city whatever the hub; the footer says so rather than naming the hub.
|
||||||
|
const typedCity = cityNamedIn(inputValue);
|
||||||
|
const scopeFooter = scope ? (
|
||||||
|
<div className="px-3 py-1.5 bg-slate-50 border-t border-slate-100 flex items-center justify-between gap-2 text-[11px] text-slate-600">
|
||||||
|
<span className="flex items-center gap-1.5 min-w-0">
|
||||||
|
<MapPin className="w-3 h-3 text-slate-500 shrink-0" />
|
||||||
|
<span className="truncate">
|
||||||
|
{typedCity && typedCity !== scope.city
|
||||||
|
? `Showing ${typedCity}, from your text`
|
||||||
|
: activeScope
|
||||||
|
? `Showing ${scope.city || 'this hub’s city'} only`
|
||||||
|
: 'Showing all cities'}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
// mousedown, so the input keeps focus and the list stays open.
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setScopeOff((off) => !off);
|
||||||
|
}}
|
||||||
|
className="font-semibold text-blue-600 hover:underline cursor-pointer shrink-0"
|
||||||
|
>
|
||||||
|
{activeScope ? 'Search all cities' : `Only ${scope.city || 'this city'}`}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
const fetchPredictions = useDebouncedCallback((query) => {
|
const fetchPredictions = useDebouncedCallback((query) => {
|
||||||
const seq = ++activeRef.current;
|
const seq = ++activeRef.current;
|
||||||
getAddressSuggestions(query, { bias, limit: 12 })
|
abortRef.current?.abort();
|
||||||
|
const controller = new AbortController();
|
||||||
|
abortRef.current = controller;
|
||||||
|
getAddressSuggestions(query, {
|
||||||
|
bias,
|
||||||
|
scope: activeScope,
|
||||||
|
limit: 12,
|
||||||
|
signal: controller.signal,
|
||||||
|
// Recents, Nominatim and India Post answer in ~1-2s; Photon can take 8s
|
||||||
|
// when throttled. Show the fast ones now; the spinner stays until the rest.
|
||||||
|
onPartial: (partial) => {
|
||||||
|
if (seq !== activeRef.current) return;
|
||||||
|
setOptions(partial);
|
||||||
|
setIsRecentMode(false);
|
||||||
|
setNoResults(false);
|
||||||
|
setIsOpen(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
if (seq !== activeRef.current) return;
|
if (seq !== activeRef.current) return;
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -109,13 +197,11 @@ const AddressAutocomplete = ({
|
|||||||
|
|
||||||
// Trigger search when input changes
|
// Trigger search when input changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (skipNextSearchRef.current) {
|
if (!typedRef.current) return;
|
||||||
skipNextSearchRef.current = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setNoResults(false);
|
setNoResults(false);
|
||||||
if (!inputValue || inputValue.trim().length < 2) {
|
if (!inputValue || inputValue.trim().length < 2) {
|
||||||
activeRef.current++;
|
activeRef.current++;
|
||||||
|
abortRef.current?.abort();
|
||||||
const recents = getRecentAddresses(5);
|
const recents = getRecentAddresses(5);
|
||||||
if (recents.length > 0 && (!inputValue || inputValue.length === 0)) {
|
if (recents.length > 0 && (!inputValue || inputValue.length === 0)) {
|
||||||
setOptions(recents);
|
setOptions(recents);
|
||||||
@@ -128,6 +214,12 @@ const AddressAutocomplete = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop the previous search's list. It stayed on screen, clickable, while
|
||||||
|
// the new search ran: typing a Coimbatore address after a Bengaluru one
|
||||||
|
// showed Bengaluru results under "Showing Coimbatore only".
|
||||||
|
setOptions([]);
|
||||||
|
setIsRecentMode(false);
|
||||||
|
setIsOpen(true);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
fetchPredictions(inputValue);
|
fetchPredictions(inputValue);
|
||||||
}, [inputValue, biasKey]);
|
}, [inputValue, biasKey]);
|
||||||
@@ -148,8 +240,10 @@ const AddressAutocomplete = ({
|
|||||||
|
|
||||||
const pick = (option) => {
|
const pick = (option) => {
|
||||||
const formatted = option.formatted_address || option.name || '';
|
const formatted = option.formatted_address || option.name || '';
|
||||||
skipNextSearchRef.current = true;
|
typedRef.current = false;
|
||||||
activeRef.current++;
|
activeRef.current++;
|
||||||
|
abortRef.current?.abort();
|
||||||
|
pickedNameRef.current = String(option.name || formatted.split(',')[0] || '').trim().toLowerCase();
|
||||||
setInputValue(formatted);
|
setInputValue(formatted);
|
||||||
saveRecentAddress(option);
|
saveRecentAddress(option);
|
||||||
onChange?.(formatted);
|
onChange?.(formatted);
|
||||||
@@ -212,9 +306,11 @@ const AddressAutocomplete = ({
|
|||||||
} else if (e.key === 'ArrowUp') {
|
} else if (e.key === 'ArrowUp') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setActiveOption((current) => (current <= 0 ? options.length - 1 : current - 1));
|
setActiveOption((current) => (current <= 0 ? options.length - 1 : current - 1));
|
||||||
} else if (e.key === 'Enter' && activeOption >= 0) {
|
} else if (e.key === 'Enter') {
|
||||||
|
// Enter with nothing highlighted takes the top suggestion, so the field
|
||||||
|
// can be filled without leaving the keyboard.
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
pick(options[activeOption]);
|
pick(options[activeOption >= 0 ? activeOption : 0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -269,10 +365,7 @@ const AddressAutocomplete = ({
|
|||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-activedescendant={activeOption >= 0 ? optionId(activeOption) : undefined}
|
aria-activedescendant={activeOption >= 0 ? optionId(activeOption) : undefined}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onChange={(e) => {
|
onChange={(e) => editText(e.target.value)}
|
||||||
setInputValue(e.target.value);
|
|
||||||
onChange?.(e.target.value);
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
onKeyDown(e);
|
onKeyDown(e);
|
||||||
if (e.key === 'Escape') setIsOpen(false);
|
if (e.key === 'Escape') setIsOpen(false);
|
||||||
@@ -290,8 +383,11 @@ const AddressAutocomplete = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setInputValue('');
|
// Cleared text is always a cleared place, picked here or filled
|
||||||
onChange?.('');
|
// in by the page (a chosen customer's saved address).
|
||||||
|
const hadPick = Boolean(pickedNameRef.current);
|
||||||
|
editText('');
|
||||||
|
if (!hadPick) onPlaceCleared?.();
|
||||||
setOptions([]);
|
setOptions([]);
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -315,13 +411,37 @@ const AddressAutocomplete = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isOpen && loading && options.length === 0 && (
|
||||||
|
<div
|
||||||
|
role="status"
|
||||||
|
className="absolute z-[9999] left-0 right-0 mt-1.5 bg-white border border-slate-200 rounded-xl shadow-2xl px-3.5 py-2.5 text-[11px] text-slate-500 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||||
|
<span>Searching{activeScope?.city ? ` in ${activeScope.city}` : ''}…</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{isOpen && noResults && options.length === 0 && !loading && (
|
{isOpen && noResults && options.length === 0 && !loading && (
|
||||||
<div
|
<div
|
||||||
id={listId}
|
id={listId}
|
||||||
role="status"
|
role="status"
|
||||||
className="absolute z-[9999] left-0 right-0 mt-1.5 bg-white border border-slate-200 rounded-xl shadow-2xl px-3.5 py-3 text-[11px] text-slate-600"
|
className="absolute z-[9999] left-0 right-0 mt-1.5 bg-white border border-slate-200 rounded-xl shadow-2xl px-3.5 py-3 text-[11px] text-slate-600"
|
||||||
>
|
>
|
||||||
<div className="font-semibold text-slate-800">No matching address found</div>
|
<div className="font-semibold text-slate-800">
|
||||||
|
{activeScope ? `No matching address in ${activeScope.city || 'this city'}` : 'No matching address found'}
|
||||||
|
</div>
|
||||||
|
{activeScope && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setScopeOff(true);
|
||||||
|
}}
|
||||||
|
className="mt-1 font-semibold text-blue-600 hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
|
Search all cities
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<div className="mt-0.5">
|
<div className="mt-0.5">
|
||||||
Try just the area name or the 6-digit pincode
|
Try just the area name or the 6-digit pincode
|
||||||
{onOpenMapPin ? (
|
{onOpenMapPin ? (
|
||||||
@@ -429,6 +549,7 @@ const AddressAutocomplete = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
{!isRecentMode && scopeFooter}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -442,9 +563,11 @@ AddressAutocomplete.propTypes = {
|
|||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onPlaceSelected: PropTypes.func,
|
onPlaceSelected: PropTypes.func,
|
||||||
|
onPlaceCleared: PropTypes.func,
|
||||||
onOpenMapPin: PropTypes.func,
|
onOpenMapPin: PropTypes.func,
|
||||||
onLandmarkExtracted: PropTypes.func,
|
onLandmarkExtracted: PropTypes.func,
|
||||||
bias: PropTypes.object,
|
bias: PropTypes.object,
|
||||||
|
scope: PropTypes.object,
|
||||||
fullWidth: PropTypes.bool,
|
fullWidth: PropTypes.bool,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
|||||||
@@ -89,11 +89,15 @@ export default function MapPinPickerModal({
|
|||||||
initialLat,
|
initialLat,
|
||||||
initialLng,
|
initialLng,
|
||||||
initialAddress = '',
|
initialAddress = '',
|
||||||
title = 'Pin Exact Location on Map'
|
title = 'Pin Exact Location on Map',
|
||||||
|
// Where to open when there is no pin yet: the hub's city. It always opened
|
||||||
|
// on Bengaluru, so a Coimbatore operator started every pin 350 km away.
|
||||||
|
defaultCenter
|
||||||
}) {
|
}) {
|
||||||
// Default coordinates (Bengaluru / Central fallback if none provided)
|
const fallbackLat = Number(defaultCenter?.lat) || 12.9716;
|
||||||
const defaultLat = Number(initialLat) || 12.9716;
|
const fallbackLng = Number(defaultCenter?.lng) || 77.5946;
|
||||||
const defaultLng = Number(initialLng) || 77.5946;
|
const defaultLat = Number(initialLat) || fallbackLat;
|
||||||
|
const defaultLng = Number(initialLng) || fallbackLng;
|
||||||
|
|
||||||
const [coords, setCoords] = useState({ lat: defaultLat, lng: defaultLng });
|
const [coords, setCoords] = useState({ lat: defaultLat, lng: defaultLng });
|
||||||
const [mapCenter, setMapCenter] = useState([defaultLat, defaultLng]);
|
const [mapCenter, setMapCenter] = useState([defaultLat, defaultLng]);
|
||||||
@@ -109,8 +113,8 @@ export default function MapPinPickerModal({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
const lat = Number(initialLat) || 12.9716;
|
const lat = Number(initialLat) || fallbackLat;
|
||||||
const lng = Number(initialLng) || 77.5946;
|
const lng = Number(initialLng) || fallbackLng;
|
||||||
setCoords({ lat, lng });
|
setCoords({ lat, lng });
|
||||||
setMapCenter([lat, lng]);
|
setMapCenter([lat, lng]);
|
||||||
setZoomLevel(initialLat && initialLng ? 16 : 13);
|
setZoomLevel(initialLat && initialLng ? 16 : 13);
|
||||||
@@ -346,5 +350,6 @@ MapPinPickerModal.propTypes = {
|
|||||||
initialLat: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
initialLat: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
initialLng: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
initialLng: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
initialAddress: PropTypes.string,
|
initialAddress: PropTypes.string,
|
||||||
|
defaultCenter: PropTypes.shape({ lat: PropTypes.number, lng: PropTypes.number }),
|
||||||
title: PropTypes.string
|
title: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -304,6 +304,7 @@ export default function CreateCustomer() {
|
|||||||
bias={bias}
|
bias={bias}
|
||||||
onChange={(text) => set('address')(text)}
|
onChange={(text) => set('address')(text)}
|
||||||
onPlaceSelected={applyPlace}
|
onPlaceSelected={applyPlace}
|
||||||
|
onPlaceCleared={() => setForm((current) => ({ ...current, latitude: '', longitude: '', suburb: '', postcode: '' }))}
|
||||||
onOpenMapPin={() => setIsMapOpen(true)}
|
onOpenMapPin={() => setIsMapOpen(true)}
|
||||||
onLandmarkExtracted={(lmk) => setForm((current) => (current.landmark ? current : { ...current, landmark: lmk }))}
|
onLandmarkExtracted={(lmk) => setForm((current) => (current.landmark ? current : { ...current, landmark: lmk }))}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ import {
|
|||||||
calculateDrivingRoute,
|
calculateDrivingRoute,
|
||||||
calculateTotalCharge
|
calculateTotalCharge
|
||||||
} from '@/lib/distance';
|
} from '@/lib/distance';
|
||||||
import { geocodeAddress, saveRecentAddress } from '@/lib/geocodingService';
|
import { buildCityScope, geocodeAddress, saveRecentAddress } from '@/lib/geocodingService';
|
||||||
import { useHubs } from '@/lib/doormileHooks';
|
import { useHubs } from '@/lib/doormileHooks';
|
||||||
import {
|
import {
|
||||||
PICKUP_SOURCE,
|
PICKUP_SOURCE,
|
||||||
@@ -618,13 +618,33 @@ export default function CreateOrder() {
|
|||||||
return Math.max(base, calculated);
|
return Math.max(base, calculated);
|
||||||
}, [route, clientRate]);
|
}, [route, clientRate]);
|
||||||
|
|
||||||
|
// The selected hub location's city. Address suggestions come only from it:
|
||||||
|
// a Coimbatore location searches Coimbatore, a Bengaluru or Hyderabad one
|
||||||
|
// searches that city. It used to be a bias alone, which ranked the hub's
|
||||||
|
// city first and still filled the list with Bengaluru and Chennai. An
|
||||||
|
// intercity drop widens its own search from the suggestion list's footer.
|
||||||
|
const cityScope = useMemo(
|
||||||
|
() =>
|
||||||
|
selectedLocation
|
||||||
|
? buildCityScope({
|
||||||
|
latitude: selectedLocation.latitude ?? selectedLocation.lat,
|
||||||
|
longitude: selectedLocation.longitude ?? selectedLocation.lng,
|
||||||
|
city: selectedLocation.city,
|
||||||
|
pincode: selectedLocation.pincode || selectedLocation.postcode
|
||||||
|
})
|
||||||
|
: null,
|
||||||
|
[selectedLocation]
|
||||||
|
);
|
||||||
|
|
||||||
// Active City & Hub Bias for smart address autocomplete
|
// Active City & Hub Bias for smart address autocomplete
|
||||||
const activeCityBias = useMemo(() => {
|
const activeCityBias = useMemo(() => {
|
||||||
if (selectedLocation?.latitude && selectedLocation?.longitude) {
|
if (selectedLocation?.latitude && selectedLocation?.longitude) {
|
||||||
return {
|
return {
|
||||||
lat: Number(selectedLocation.latitude),
|
lat: Number(selectedLocation.latitude),
|
||||||
lng: Number(selectedLocation.longitude),
|
lng: Number(selectedLocation.longitude),
|
||||||
city: selectedLocation.city || selectedLocation.locationname || ''
|
// Not the location's name: "RS Puram Kitchen" is not a city, and the
|
||||||
|
// ranking compares this against each suggestion's city.
|
||||||
|
city: selectedLocation.city || cityScope?.city || ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (pickCust?.latitude && pickCust?.longitude) {
|
if (pickCust?.latitude && pickCust?.longitude) {
|
||||||
@@ -635,7 +655,7 @@ export default function CreateOrder() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [selectedLocation, pickCust.latitude, pickCust.longitude, pickCust.city]);
|
}, [selectedLocation, cityScope, pickCust.latitude, pickCust.longitude, pickCust.city]);
|
||||||
|
|
||||||
// Place selection helpers
|
// Place selection helpers
|
||||||
const handlePlaceSelect = (partyType, place) => {
|
const handlePlaceSelect = (partyType, place) => {
|
||||||
@@ -1141,8 +1161,10 @@ export default function CreateOrder() {
|
|||||||
placeholder="Search area, street, society, pincode or paste link…"
|
placeholder="Search area, street, society, pincode or paste link…"
|
||||||
value={pickCust.address}
|
value={pickCust.address}
|
||||||
bias={activeCityBias}
|
bias={activeCityBias}
|
||||||
|
scope={cityScope}
|
||||||
onChange={(text) => setPickCust((prev) => ({ ...prev, address: text }))}
|
onChange={(text) => setPickCust((prev) => ({ ...prev, address: text }))}
|
||||||
onPlaceSelected={(place) => handlePlaceSelect('pickup', place)}
|
onPlaceSelected={(place) => handlePlaceSelect('pickup', place)}
|
||||||
|
onPlaceCleared={() => setPickCust((prev) => ({ ...prev, latitude: '', longitude: '', suburb: '', postcode: '' }))}
|
||||||
onOpenMapPin={() => setMapPickerTarget('pickup')}
|
onOpenMapPin={() => setMapPickerTarget('pickup')}
|
||||||
onLandmarkExtracted={(lmk) => {
|
onLandmarkExtracted={(lmk) => {
|
||||||
setPickCust((prev) => (!prev.landmark ? { ...prev, landmark: lmk } : prev));
|
setPickCust((prev) => (!prev.landmark ? { ...prev, landmark: lmk } : prev));
|
||||||
@@ -1177,7 +1199,8 @@ export default function CreateOrder() {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Pincode"
|
placeholder="Pincode"
|
||||||
value={pickCust.postcode}
|
value={pickCust.postcode}
|
||||||
onChange={(e) => setPickCust({ ...pickCust, postcode: e.target.value })}
|
onChange={(e) => setPickCust({ ...pickCust, postcode: e.target.value.replace(/\D/g, '').slice(0, 6) })}
|
||||||
|
inputMode="numeric"
|
||||||
className="w-full h-9 px-3 bg-white border border-slate-200 rounded-lg text-xs font-medium text-slate-900 focus:outline-none focus:ring-2 focus:ring-black/10 focus:border-black transition-colors font-mono"
|
className="w-full h-9 px-3 bg-white border border-slate-200 rounded-lg text-xs font-medium text-slate-900 focus:outline-none focus:ring-2 focus:ring-black/10 focus:border-black transition-colors font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1276,8 +1299,10 @@ export default function CreateOrder() {
|
|||||||
placeholder="Search area, street, society, pincode or paste link…"
|
placeholder="Search area, street, society, pincode or paste link…"
|
||||||
value={dropCust.address}
|
value={dropCust.address}
|
||||||
bias={activeCityBias}
|
bias={activeCityBias}
|
||||||
|
scope={cityScope}
|
||||||
onChange={(text) => setDropCust((prev) => ({ ...prev, address: text }))}
|
onChange={(text) => setDropCust((prev) => ({ ...prev, address: text }))}
|
||||||
onPlaceSelected={(place) => handlePlaceSelect('drop', place)}
|
onPlaceSelected={(place) => handlePlaceSelect('drop', place)}
|
||||||
|
onPlaceCleared={() => setDropCust((prev) => ({ ...prev, latitude: '', longitude: '', suburb: '', postcode: '' }))}
|
||||||
onOpenMapPin={() => setMapPickerTarget('drop')}
|
onOpenMapPin={() => setMapPickerTarget('drop')}
|
||||||
onLandmarkExtracted={(lmk) => {
|
onLandmarkExtracted={(lmk) => {
|
||||||
setDropCust((prev) => (!prev.landmark ? { ...prev, landmark: lmk } : prev));
|
setDropCust((prev) => (!prev.landmark ? { ...prev, landmark: lmk } : prev));
|
||||||
@@ -1312,7 +1337,8 @@ export default function CreateOrder() {
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder="Pincode"
|
placeholder="Pincode"
|
||||||
value={dropCust.postcode}
|
value={dropCust.postcode}
|
||||||
onChange={(e) => setDropCust({ ...dropCust, postcode: e.target.value })}
|
onChange={(e) => setDropCust({ ...dropCust, postcode: e.target.value.replace(/\D/g, '').slice(0, 6) })}
|
||||||
|
inputMode="numeric"
|
||||||
className="w-full h-9 px-3 bg-white border border-slate-200 rounded-lg text-xs font-medium text-slate-900 focus:outline-none focus:ring-2 focus:ring-black/10 focus:border-black transition-colors font-mono"
|
className="w-full h-9 px-3 bg-white border border-slate-200 rounded-lg text-xs font-medium text-slate-900 focus:outline-none focus:ring-2 focus:ring-black/10 focus:border-black transition-colors font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1934,6 +1960,7 @@ export default function CreateOrder() {
|
|||||||
initialAddress={mapPickerTarget === 'pickup' ? pickCust.address : dropCust.address}
|
initialAddress={mapPickerTarget === 'pickup' ? pickCust.address : dropCust.address}
|
||||||
initialLat={mapPickerTarget === 'pickup' ? pickCust.latitude : dropCust.latitude}
|
initialLat={mapPickerTarget === 'pickup' ? pickCust.latitude : dropCust.latitude}
|
||||||
initialLng={mapPickerTarget === 'pickup' ? pickCust.longitude : dropCust.longitude}
|
initialLng={mapPickerTarget === 'pickup' ? pickCust.longitude : dropCust.longitude}
|
||||||
|
defaultCenter={cityScope ? { lat: cityScope.lat, lng: cityScope.lng } : undefined}
|
||||||
onConfirm={(place) => {
|
onConfirm={(place) => {
|
||||||
if (mapPickerTarget) {
|
if (mapPickerTarget) {
|
||||||
handlePlaceSelect(mapPickerTarget, place);
|
handlePlaceSelect(mapPickerTarget, place);
|
||||||
|
|||||||
Reference in New Issue
Block a user