updates on the map change and the configurations and removed the google map api key

This commit is contained in:
2026-08-04 12:35:56 +05:30
parent d5bde7d1ac
commit e1584e3a4b
17 changed files with 1030 additions and 1620 deletions

3
.env
View File

@@ -4,13 +4,10 @@ GENERATE_SOURCEMAP = false
## Backend API URL ## Backend API URL
REACT_APP_API_URL=https://mock-data-api-nextjs.vercel.app/ REACT_APP_API_URL=https://mock-data-api-nextjs.vercel.app/
## Google Map Key
REACT_APP_URL=https://jupiter.nearle.app/live/api/v1 REACT_APP_URL=https://jupiter.nearle.app/live/api/v1
REACT_APP_URL2=https://jupiter.nearle.app/live/api/v2 REACT_APP_URL2=https://jupiter.nearle.app/live/api/v2
REACT_APP_URL3=https://jupiter.nearle.app/live/api/v3 REACT_APP_URL3=https://jupiter.nearle.app/live/api/v3
REACT_APP_STAFF_TOKEN= REACT_APP_STAFF_TOKEN=
REACT_APP_GOOGLE_MAPS_API_KEY=AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8
REACT_APP_RIDER_ACCESS_ID=AAAAILMpCEU:APA91bEavuOllBI6sFgYtxXAgNmAVwNA-MnCMHLGlR4_t7UqpLajAkdn3T0CZr_zaLBknLyim9ytFLMZgbeXmKqTad_PKCbqlYjHpaizVrLXtecxqyEy4UktIacK2UvHVUATHL-7VQQk REACT_APP_RIDER_ACCESS_ID=AAAAILMpCEU:APA91bEavuOllBI6sFgYtxXAgNmAVwNA-MnCMHLGlR4_t7UqpLajAkdn3T0CZr_zaLBknLyim9ytFLMZgbeXmKqTad_PKCbqlYjHpaizVrLXtecxqyEy4UktIacK2UvHVUATHL-7VQQk

View File

@@ -2,7 +2,6 @@ REACT_APP_URL=https://jupiter.nearle.app/live/api/v1
REACT_APP_URL2=https://jupiter.nearle.app/live/api/v2 REACT_APP_URL2=https://jupiter.nearle.app/live/api/v2
REACT_APP_URL3=https://jupiter.nearle.app/live/api/v3 REACT_APP_URL3=https://jupiter.nearle.app/live/api/v3
REACT_APP_STAFF_TOKEN= REACT_APP_STAFF_TOKEN=
REACT_APP_GOOGLE_MAPS_API_KEY=AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8
REACT_APP_RIDER_ACCESS_ID=AAAAILMpCEU:APA91bEavuOllBI6sFgYtxXAgNmAVwNA-MnCMHLGlR4_t7UqpLajAkdn3T0CZr_zaLBknLyim9ytFLMZgbeXmKqTad_PKCbqlYjHpaizVrLXtecxqyEy4UktIacK2UvHVUATHL-7VQQk REACT_APP_RIDER_ACCESS_ID=AAAAILMpCEU:APA91bEavuOllBI6sFgYtxXAgNmAVwNA-MnCMHLGlR4_t7UqpLajAkdn3T0CZr_zaLBknLyim9ytFLMZgbeXmKqTad_PKCbqlYjHpaizVrLXtecxqyEy4UktIacK2UvHVUATHL-7VQQk

View File

@@ -13,12 +13,10 @@
"@mui/lab": "^5.0.0-alpha.127", "@mui/lab": "^5.0.0-alpha.127",
"@mui/material": "^5.12.1", "@mui/material": "^5.12.1",
"@mui/x-date-pickers": "^6.18.2", "@mui/x-date-pickers": "^6.18.2",
"@react-google-maps/api": "^2.20.7",
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.5",
"@svgr/webpack": "^7.0.0", "@svgr/webpack": "^7.0.0",
"@tanstack/react-query": "^5.17.9", "@tanstack/react-query": "^5.17.9",
"antd": "^5.11.5", "antd": "^5.11.5",
"autosuggest-highlight": "^3.3.4",
"axios": "^1.3.5", "axios": "^1.3.5",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chance": "^1.1.11", "chance": "^1.1.11",
@@ -45,8 +43,6 @@
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1", "react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-geocode": "^0.2.3",
"react-google-autocomplete": "^2.7.3",
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-intl": "^6.4.1", "react-intl": "^6.4.1",
"react-leaflet": "^4.2.1", "react-leaflet": "^4.2.1",

View File

@@ -0,0 +1,193 @@
import React, { useEffect, useMemo, useState } from 'react';
import { Autocomplete, TextField, CircularProgress } from '@mui/material';
import { debounce } from '@mui/material/utils';
// Free OSM Nominatim geocoder — replaces Google Places Autocomplete /
// 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
// debounced (500ms) and cancels stale in-flight predictions.
const NOMINATIM_SEARCH_URL = 'https://nominatim.openstreetmap.org/search';
// Builds a Google-Places-shaped `address_components` array (each entry
// `{ long_name, short_name, types: [] }`) from Nominatim's flat `address`
// object, so existing call sites' `place.address_components.forEach(...)`
// type-switch parsing keeps working unchanged.
const buildAddressComponents = (addr = {}) => {
const components = [];
const push = (longName, types) => {
if (longName) components.push({ long_name: longName, short_name: longName, types });
};
push(addr.house_number, ['street_number']);
push(addr.road || addr.pedestrian, ['route']);
push(addr.suburb || addr.neighbourhood || addr.quarter, ['sublocality_level_1', 'sublocality']);
push(addr.city_district || addr.county, ['administrative_area_level_3']);
push(addr.city || addr.town || addr.village, ['locality']);
push(addr.state, ['administrative_area_level_1']);
push(addr.country, ['country']);
push(addr.postcode, ['postal_code']);
return components;
};
// Shapes a Nominatim result close enough to a Google Places `place` object
// (formatted_address, geometry.location.lat()/lng(), address_components)
// that call sites need only mechanical edits, not rewrites.
export const toPlace = (result) => ({
formatted_address: result.display_name,
name: result.display_name?.split(',')[0] || '',
geometry: {
location: {
lat: () => parseFloat(result.lat),
lng: () => parseFloat(result.lon)
}
},
address_components: buildAddressComponents(result.address)
});
const buildParams = (query, bias) => {
const params = new URLSearchParams({ q: query, format: 'json', addressdetails: '1', limit: '5' });
if (bias?.lat && bias?.lng) {
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}`);
}
return params;
};
// Standalone forward-geocode helper — replaces `Geocode.fromAddress(address)`
// for call sites that resolve a plain text address without a predictions
// dropdown (e.g. re-geocoding on submit). Returns a `toPlace`-shaped object,
// or null if nothing matched.
export async function geocodeAddress(address, { bias } = {}) {
if (!address) return null;
try {
const params = buildParams(address, bias);
params.set('limit', '1');
const res = await fetch(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } });
if (!res.ok) return null;
const results = await res.json();
if (!results?.length) return null;
return toPlace(results[0]);
} catch (err) {
console.error('geocodeAddress error:', err);
return null;
}
}
// Address search-as-you-type — replaces Google Places Autocomplete
// (usePlacesWidget / window.google.maps.places.Autocomplete /
// AutocompleteService) across the console. Renders as an MUI Autocomplete
// with freeSolo so the operator can still submit free text.
//
// Props:
// value / onChange(text) — controlled free-text input value
// onPlaceSelected(place) — fired when a suggestion is chosen; `place`
// is shaped like a Google Places `place`
// bias { lat, lng } — optional soft bias toward the operator's zone
const AddressAutocomplete = ({
id,
label,
placeholder = 'Search address',
value,
onChange,
onPlaceSelected,
bias,
sx,
textFieldSx,
fullWidth = true,
disabled,
inputRef,
TextFieldProps = {}
}) => {
const [inputValue, setInputValue] = useState(value || '');
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (value !== undefined && value !== inputValue) setInputValue(value || '');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);
const fetchPredictions = useMemo(
() =>
debounce((query, bias1, callback) => {
if (!query) {
callback([]);
return;
}
const params = buildParams(query, bias1);
fetch(`${NOMINATIM_SEARCH_URL}?${params.toString()}`, { headers: { Accept: 'application/json' } })
.then((res) => (res.ok ? res.json() : []))
.then((results) => callback(results || []))
.catch(() => callback([]));
}, 500),
[]
);
useEffect(() => {
let active = true;
if (!inputValue) {
setOptions([]);
setLoading(false);
return undefined;
}
setLoading(true);
fetchPredictions(inputValue, bias, (results) => {
if (!active) return;
setLoading(false);
setOptions(results);
});
return () => {
active = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputValue, bias?.lat, bias?.lng, fetchPredictions]);
return (
<Autocomplete
id={id}
freeSolo
fullWidth={fullWidth}
disabled={disabled}
sx={sx}
options={options}
filterOptions={(x) => x}
getOptionLabel={(option) => (typeof option === 'string' ? option : option.display_name || '')}
isOptionEqualToValue={(option, val) => option.place_id === val?.place_id}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
onChange?.(newInputValue);
}}
onChange={(event, selected) => {
if (selected && typeof selected !== 'string') {
setInputValue(selected.display_name || '');
onPlaceSelected?.(toPlace(selected));
}
}}
renderInput={(params) => (
<TextField
{...params}
{...TextFieldProps}
inputRef={inputRef}
label={label}
placeholder={placeholder}
sx={textFieldSx}
autoComplete="off"
InputProps={{
...params.InputProps,
...TextFieldProps.InputProps,
startAdornment: TextFieldProps.InputProps?.startAdornment ?? params.InputProps.startAdornment,
endAdornment: (
<>
{loading ? <CircularProgress color="inherit" size={16} /> : null}
{TextFieldProps.InputProps?.endAdornment ?? params.InputProps.endAdornment}
</>
)
}}
/>
)}
/>
);
};
export default AddressAutocomplete;

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState, useRef, useMemo, Fragment } from 'react'; import React, { useEffect, useState, useRef, Fragment } from 'react';
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import axios from 'axios'; import axios from 'axios';
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
@@ -6,10 +6,7 @@ import Loader from 'components/Loader';
import { Empty } from 'antd'; import { Empty } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import Geocode from 'react-geocode'; import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
import LocationOnIcon from '@mui/icons-material/LocationOn';
import parse from 'autosuggest-highlight/parse';
import { debounce } from '@mui/material/utils';
import { import {
Stack, Stack,
Table, Table,
@@ -184,7 +181,6 @@ const Clients1 = () => {
const [appId, setAppId] = useState(0); const [appId, setAppId] = useState(0);
const [locaName, setLocoName] = useState(''); const [locaName, setLocoName] = useState('');
const [locations] = useState('All'); const [locations] = useState('All');
const [value, setValue] = React.useState(null);
const [value0, setValue0] = useState(0); const [value0, setValue0] = useState(0);
const [value1, setValue1] = useState(0); const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0); const [value2, setValue2] = useState(0);
@@ -196,7 +192,6 @@ const Clients1 = () => {
const [appPricing, setAppPricing] = useState([]); const [appPricing, setAppPricing] = useState([]);
const [selectedPricing, setSelectedPricing] = useState({}); const [selectedPricing, setSelectedPricing] = useState({});
const [isPrice, setIsprice] = useState(true); const [isPrice, setIsprice] = useState(true);
const [address, setAddress] = useState('');
const [city, setCity] = useState(''); const [city, setCity] = useState('');
const [zipcode, setZipcode] = useState(''); const [zipcode, setZipcode] = useState('');
const [latlong, setLatlong] = useState({}); const [latlong, setLatlong] = useState({});
@@ -258,144 +253,50 @@ const Clients1 = () => {
autoHideDuration: duration autoHideDuration: duration
}); });
}; };
// ==============================|| google address ||============================== // // ==============================|| address autocomplete ||============================== //
const GOOGLE_MAPS_API_KEY = process.env.REACT_APP_GOOGLE_MAPS_API_KEY; const handleAddressPlaceSelected = (place) => {
const lat = place.geometry.location.lat();
function loadScript(src, position, id) { const lng = place.geometry.location.lng();
if (!position) { setLatlong({ lat, lng });
return; setEditClient((prev) => ({
} ...prev,
address: place.formatted_address,
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
const autocompleteService = { current: null };
// ==============================|| for google address ||============================== //
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState([]);
const loaded = useRef(false);
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places`,
document.querySelector('head'),
'google-maps'
);
}
loaded.current = true;
}
const fetch = useMemo(
() =>
debounce((request, callback) => {
autocompleteService.current.getPlacePredictions(request, callback);
}, 400),
[]
);
useEffect(() => {
let active = true;
if (!autocompleteService.current && window.google) {
autocompleteService.current = new window.google.maps.places.AutocompleteService();
}
if (!autocompleteService.current) {
return undefined;
}
if (inputValue === '') {
setOptions(value ? [value] : []);
return undefined;
}
fetch({ input: inputValue }, (results) => {
if (active) {
let newOptions = [];
if (value) {
newOptions = [value];
}
if (results) {
newOptions = [...newOptions, ...results];
}
setOptions(newOptions);
}
});
return () => {
active = false;
};
}, [value, inputValue, fetch]);
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
useEffect(() => {
try {
console.log('selected address =>', address);
Geocode.fromAddress(address).then(
(response) => {
console.log('lat long response =>', response.results[0]);
if (response.status == 'OK') {
const { lat, lng } = response.results[0].geometry.location;
setLatlong({
lat,
lng
});
setEditClient({
...editClient,
latitude: lat.toString(), latitude: lat.toString(),
longitude: lng.toString() longitude: lng.toString()
}); }));
// setSelectedCustomer({
// ...selectedCustomer,
// latitude: lat,
// longitude: lng,
// });
if (response.results[0].address_components) {
let place = response.results[0];
let city1, zipcode1, state1, suburb1; let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) { place.address_components.forEach((component) => {
for (let j = 0; j < place.address_components[i].types.length; j++) { component.types.forEach((type) => {
switch (place.address_components[i].types[j]) { switch (type) {
case 'locality': case 'locality':
city1 = place.address_components[i].long_name; city1 = component.long_name;
break; break;
case 'administrative_area_level_1': case 'administrative_area_level_1':
state1 = place.address_components[i].long_name; state1 = component.long_name;
break; break;
case 'postal_code': case 'postal_code':
zipcode1 = place.address_components[i].long_name; zipcode1 = component.long_name;
break; break;
case 'sublocality': case 'sublocality':
suburb1 = place.address_components[i].long_name; case 'sublocality_level_1':
suburb1 = component.long_name;
break; break;
} }
} });
} });
setCity(city1 || ''); setCity(city1 || '');
setState(state1 || ''); setState(state1 || '');
setZipcode(zipcode1 || ''); setZipcode(zipcode1 || '');
setSuburb(suburb1 || ''); setSuburb(suburb1 || '');
setEditClient({ setEditClient((prev) => ({
...editClient, ...prev,
city: city1 || '', city: city1 || '',
state: state1 || '', state: state1 || '',
postcode: zipcode || '', postcode: zipcode1 || '',
suburb: suburb1 || '' suburb: suburb1 || ''
}); }));
} };
}
},
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]);
useEffect(() => { useEffect(() => {
selectedCustomer && selectedCustomer &&
@@ -1562,85 +1463,19 @@ const Clients1 = () => {
/> />
</Grid> </Grid>
<Grid item xs zeroMinWidth> <Grid item xs zeroMinWidth>
<Autocomplete <AddressAutocomplete
id="google-map-demo" id="address-autocomplete"
sx={{}}
fullWidth fullWidth
label={'Address'} label="Address"
getOptionLabel={(option) => (typeof option === 'string' ? option : option.description)} value={editClient?.address ?? selectedCustomer.address ?? ''}
filterOptions={(x) => x} onChange={(text) =>
options={options} setEditClient((prev) => ({
autoComplete ...prev,
includeInputInList address: text
filterSelectedOptions }))
defaultValue={selectedCustomer.address} }
noOptionsText="No locations" onPlaceSelected={handleAddressPlaceSelected}
onChange={(event, newValue) => { TextFieldProps={{ variant: 'outlined' }}
setOptions(newValue ? [newValue, ...options] : options);
setValue(newValue || '');
console.log('newValue', newValue);
setAddress(newValue?.description || '');
setEditClient({
...editClient,
address: newValue?.description || ''
});
}}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => (
<TextField {...params} fullWidth label="Address" variant="outlined" />
)}
renderOption={(props, option) => {
const matches = option.structured_formatting.main_text_matched_substrings || [];
const parts = parse(
option.structured_formatting.main_text,
matches.map((match) => [match.offset, match.offset + match.length])
);
return (
<li {...props}>
<Grid container alignItems="center">
<Grid
item
sx={{
display: 'flex',
width: 44
}}
>
<LocationOnIcon
sx={{
color: 'text.secondary'
}}
/>
</Grid>
<Grid
item
sx={{
width: 'calc(100% - 44px)',
wordWrap: 'break-word'
}}
>
{parts.map((part, index) => (
<Box
key={index}
component="span"
sx={{
fontWeight: part.highlight ? 'bold' : 'regular'
}}
>
{part.text}
</Box>
))}
<Typography variant="body2" color="text.secondary">
{option.structured_formatting.secondary_text}
</Typography>
</Grid>
</Grid>
</li>
);
}}
/> />
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -4,7 +4,7 @@ import { Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typograph
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import axios from 'axios'; import axios from 'axios';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import Geocode from 'react-geocode'; import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
@@ -25,7 +25,6 @@ const CreateCustomer = () => {
const [inputValue2, setInputValue2] = useState(''); const [inputValue2, setInputValue2] = useState('');
const [appLocaLat, setAppLocaLat] = useState(); const [appLocaLat, setAppLocaLat] = useState();
const [appLocaLng, setAppLocaLng] = useState(); const [appLocaLng, setAppLocaLng] = useState();
const [appLocaRadius, setAppLocaRadius] = useState();
const [locaName, setLocoName] = useState('Select Location'); const [locaName, setLocoName] = useState('Select Location');
const [tenantlist, setTenantlist] = useState([]); const [tenantlist, setTenantlist] = useState([]);
const [tid, setTid] = useState(0); const [tid, setTid] = useState(0);
@@ -34,29 +33,8 @@ const CreateCustomer = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY); const handlePlaceSelected = (place) => {
useEffect(() => {
// Initialize Google Maps Autocomplete
if (inputValue2) {
const autocompleteInput = document.getElementById('addressAuto1');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
// radius: 100000
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000
}).getBounds()
});
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue2(`${place.name}, ${place.formatted_address}`); setInputValue2(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
// to trigger getDistance // to trigger getDistance
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setAddress(`${place.name} ${place.formatted_address}`); setAddress(`${place.name} ${place.formatted_address}`);
@@ -116,10 +94,7 @@ const CreateCustomer = () => {
latitude: place.geometry.location.lat(), latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng() longitude: place.geometry.location.lng()
}); });
console.log('Pick Address:', address); };
});
}
}, [inputValue2]);
// ==================================================== || getapplocations || ==================================================== // ==================================================== || getapplocations || ====================================================
const getapplocations = async () => { const getapplocations = async () => {
setLoading(true); setLoading(true);
@@ -127,12 +102,10 @@ const CreateCustomer = () => {
.get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`)
.then((res) => { .then((res) => {
console.log('getapplocations', res); console.log('getapplocations', res);
const { latitude, longitude, radius } = res.data.details[0]; const { latitude, longitude } = res.data.details[0];
if (res.data.status) { if (res.data.status) {
setAppLocaLat(latitude); setAppLocaLat(latitude);
setAppLocaLng(longitude); setAppLocaLng(longitude);
setAppLocaRadius(radius);
console.log('radius', radius);
} }
setLoading(false); setLoading(false);
}) })
@@ -380,19 +353,22 @@ const CreateCustomer = () => {
<Grid item xs={12}> <Grid item xs={12}>
<Stack spacing={1.25}> <Stack spacing={1.25}>
<InputLabel htmlFor="personal-email"> Address</InputLabel> <InputLabel htmlFor="personal-email"> Address</InputLabel>
<TextField <AddressAutocomplete
variant="outlined"
id="addressAuto1" id="addressAuto1"
fullWidth fullWidth
value={inputValue2} value={inputValue2}
onChange={(e) => { onChange={(text) => {
if (appId) { if (appId) {
appId && setInputValue2(e.target.value); setInputValue2(text);
} else { } else {
OpenToast('Select Location First', 'warning', 3000); OpenToast('Select Location First', 'warning', 3000);
} }
}} }}
InputProps={{ onPlaceSelected={handlePlaceSelected}
bias={{ lat: appLocaLat, lng: appLocaLng }}
TextFieldProps={{
variant: 'outlined',
InputProps: {
endAdornment: ( endAdornment: (
<IconButton <IconButton
onClick={() => { onClick={() => {
@@ -412,6 +388,7 @@ const CreateCustomer = () => {
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
) )
}
}} }}
/> />
</Stack> </Stack>

View File

@@ -10,12 +10,10 @@ import { Box, Button, FormLabel, Grid, InputLabel, MenuItem, Select, Stack, Text
// project import // project import
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import axios from 'axios'; import axios from 'axios';
import { usePlacesWidget } from 'react-google-autocomplete'; import AddressAutocomplete, { geocodeAddress } from 'components/nearle_components/AddressAutocomplete';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import Geocode from 'react-geocode';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
// import { setLocationType } from 'react-geocode';
// const avatarImage = require.context('assets/images/users', true); // const avatarImage = require.context('assets/images/users', true);
@@ -49,9 +47,6 @@ const Createclient = () => {
const navigate = useNavigate(); const navigate = useNavigate();
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// Geocode.setApiKey('AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
@@ -63,25 +58,15 @@ const Createclient = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
try { let active = true;
Geocode.fromAddress(address).then( geocodeAddress(address).then((place) => {
(response) => { if (active && place) {
if (response.status == 'OK') { setLatlong({ lat: place.geometry.location.lat(), lng: place.geometry.location.lng() });
const { lat, lng } = response.results[0].geometry.location; }
setLatlong({
lat,
lng
}); });
console.log(response); return () => {
} active = false;
}, };
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]); }, [address]);
const opentoast = (message) => { const opentoast = (message) => {
@@ -151,11 +136,7 @@ const Createclient = () => {
} }
}, [selectedImage]); }, [selectedImage]);
const { ref: materialRef } = usePlacesWidget({ const handleAddressPlaceSelected = (place) => {
apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
onPlaceSelected: (place) => {
console.log(place);
setAddress(place.formatted_address); setAddress(place.formatted_address);
let city1, zipcode1, state1, suburb1; let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) { for (let i = 0; i < place.address_components.length; i++) {
@@ -171,6 +152,7 @@ const Createclient = () => {
zipcode1 = place.address_components[i].long_name; zipcode1 = place.address_components[i].long_name;
break; break;
case 'sublocality': case 'sublocality':
case 'sublocality_level_1':
suburb1 = place.address_components[i].long_name; suburb1 = place.address_components[i].long_name;
break; break;
} }
@@ -180,16 +162,7 @@ const Createclient = () => {
setState(state1 || ''); setState(state1 || '');
setZipcode(zipcode1 || ''); setZipcode(zipcode1 || '');
setSuburb(suburb1 || ''); setSuburb(suburb1 || '');
};
// setAddress(place.formatted_address)
},
// inputAutocompleteValue: "country",
options: {
// componentRestrictions: 'us',
// types: ["establishment"]
types: ['address' || 'geocode']
}
});
const createprofile = async () => { const createprofile = async () => {
console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode); console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode);
@@ -486,14 +459,13 @@ const Createclient = () => {
<Grid item xs={12}> <Grid item xs={12}>
<Stack spacing={1.25}> <Stack spacing={1.25}>
<InputLabel htmlFor="personal-address">Address</InputLabel> <InputLabel htmlFor="personal-address">Address</InputLabel>
<TextField <AddressAutocomplete
fullWidth
// defaultValue="Street 110-B Kalians Bag, Dewan, M.P. New York"
id="personal-address" id="personal-address"
fullWidth
placeholder="Address" placeholder="Address"
value={address} value={address}
onChange={(e) => setAddress(e.target.value)} onChange={setAddress}
inputRef={materialRef} onPlaceSelected={handleAddressPlaceSelected}
/> />
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -1,4 +1,4 @@
import { React, useState, useEffect, useRef, useMemo } from 'react'; import { React, useState, useEffect, useRef } from 'react';
import axios from 'axios'; import axios from 'axios';
import { FaRegEdit } from 'react-icons/fa'; import { FaRegEdit } from 'react-icons/fa';
import LoaderWithImage from 'components/nearle_components/LoaderWithImage'; import LoaderWithImage from 'components/nearle_components/LoaderWithImage';
@@ -23,7 +23,6 @@ import {
DialogContent, DialogContent,
Button, Button,
TextField, TextField,
Autocomplete,
Avatar, Avatar,
Paper, Paper,
useMediaQuery, useMediaQuery,
@@ -42,12 +41,9 @@ import {
MdOutlineHowToReg, MdOutlineHowToReg,
MdOutlinePlace MdOutlinePlace
} from 'react-icons/md'; } from 'react-icons/md';
import Geocode from 'react-geocode';
import LocationOnIcon from '@mui/icons-material/LocationOn';
import parse from 'autosuggest-highlight/parse';
import { debounce } from '@mui/material/utils';
// project imports // project imports
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
import PageHeader from 'components/nearle_components/PageHeader'; import PageHeader from 'components/nearle_components/PageHeader';
@@ -111,23 +107,6 @@ const AccentAvatar = ({ color, selected, size = 24, children }) => (
</Avatar> </Avatar>
); );
// ==============================|| google address ||============================== //
const GOOGLE_MAPS_API_KEY = process.env.REACT_APP_GOOGLE_MAPS_API_KEY;
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
const autocompleteService = { current: null };
// ==============================|| MUI TABLE - ENHANCED ||============================== // // ==============================|| MUI TABLE - ENHANCED ||============================== //
export default function Customers() { export default function Customers() {
@@ -141,7 +120,6 @@ export default function Customers() {
const [locaName, setLocoName] = useState('All'); const [locaName, setLocoName] = useState('All');
const [selectedCustomer, setSelectedCustomer] = useState({}); // to edit const [selectedCustomer, setSelectedCustomer] = useState({}); // to edit
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [address, setAddress] = useState('');
const [latlong, setLatlong] = useState({}); const [latlong, setLatlong] = useState({});
const [city, setCity] = useState(''); const [city, setCity] = useState('');
const [postcode, setPostcode] = useState(''); const [postcode, setPostcode] = useState('');
@@ -150,110 +128,39 @@ export default function Customers() {
const [searchword, setSearchword] = useState(''); const [searchword, setSearchword] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState('');
// ==============================|| for google address ||============================== // // ==============================|| address autocomplete ||============================== //
const [value, setValue] = useState(null); const handleAddressPlaceSelected = (place) => {
const [inputValue, setInputValue] = useState(''); const lat = place.geometry.location.lat();
const [options, setOptions] = useState([]); const lng = place.geometry.location.lng();
const loaded = useRef(false); setLatlong({ lat, lng });
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places`,
document.querySelector('head'),
'google-maps'
);
}
loaded.current = true;
}
const fetch = useMemo(
() =>
debounce((request, callback) => {
autocompleteService.current.getPlacePredictions(request, callback);
}, 400),
[]
);
useEffect(() => {
let active = true;
if (!autocompleteService.current && window.google) {
autocompleteService.current = new window.google.maps.places.AutocompleteService();
}
if (!autocompleteService.current) {
return undefined;
}
if (inputValue === '') {
setOptions(value ? [value] : []);
return undefined;
}
fetch({ input: inputValue }, (results) => {
if (active) {
let newOptions = [];
if (value) {
newOptions = [value];
}
if (results) {
newOptions = [...newOptions, ...results];
}
setOptions(newOptions);
}
});
return () => {
active = false;
};
}, [value, inputValue, fetch]);
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
useEffect(() => {
try {
console.log('selected address =>', address);
Geocode.fromAddress(address).then(
(response) => {
console.log('lat long response =>', response.results[0]);
if (response.status == 'OK') {
const { lat, lng } = response.results[0].geometry.location;
setLatlong({
lat,
lng
});
// setSelectedCustomer({
// ...selectedCustomer,
// latitude: lat,
// longitude: lng
// });
if (response.results[0].address_components) {
let place = response.results[0];
let city1, zipcode1, state1, suburb1; let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) { place.address_components.forEach((component) => {
for (let j = 0; j < place.address_components[i].types.length; j++) { component.types.forEach((type) => {
switch (place.address_components[i].types[j]) { switch (type) {
case 'locality': case 'locality':
city1 = place.address_components[i].long_name; city1 = component.long_name;
break; break;
case 'administrative_area_level_1': case 'administrative_area_level_1':
state1 = place.address_components[i].long_name; state1 = component.long_name;
break; break;
case 'postal_code': case 'postal_code':
zipcode1 = place.address_components[i].long_name; zipcode1 = component.long_name;
break; break;
case 'sublocality': case 'sublocality':
suburb1 = place.address_components[i].long_name; case 'sublocality_level_1':
suburb1 = component.long_name;
break; break;
} }
} });
} });
setCity(city1 || ''); setCity(city1 || '');
setState(state1 || ''); setState(state1 || '');
setPostcode(zipcode1 || ''); setPostcode(zipcode1 || '');
setSuburb(suburb1 || ''); setSuburb(suburb1 || '');
setSelectedCustomer((prev) => ({ setSelectedCustomer((prev) => ({
...prev, ...prev,
address: place.formatted_address,
city: city1 || '', city: city1 || '',
state: state1 || '', state: state1 || '',
postcode: zipcode1 || '', postcode: zipcode1 || '',
@@ -261,25 +168,7 @@ export default function Customers() {
latitude: lat || '', latitude: lat || '',
longitude: lng || '' longitude: lng || ''
})); }));
} };
}
},
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]);
// useEffect(() => {
// selectedCustomer &&
// setLatlong({
// lat: selectedCustomer.latitude,
// lng: selectedCustomer.longitude
// });
// }, [selectedCustomer]);
// ==============================|| getallcustomers (customers) ||============================== // // ==============================|| getallcustomers (customers) ||============================== //
@@ -891,72 +780,17 @@ export default function Customers() {
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Typography sx={{ mb: 1 }}>Address</Typography> <Typography sx={{ mb: 1 }}>Address</Typography>
<Autocomplete <AddressAutocomplete
id="google-map-demo" id="address-autocomplete"
sx={{}}
fullWidth fullWidth
getOptionLabel={(option) => (typeof option === 'string' ? option : option?.description || '')} value={selectedCustomer?.address || ''}
filterOptions={(x) => x} onChange={(text) =>
options={options} setSelectedCustomer((prev) => ({
autoComplete ...prev,
includeInputInList address: text
filterSelectedOptions }))
value={selectedCustomer?.address} }
noOptionsText="No locations" onPlaceSelected={handleAddressPlaceSelected}
onChange={(event, newValue) => {
setOptions(newValue ? [newValue, ...options] : options);
setValue(newValue);
console.log('newValue', newValue || '');
setAddress(newValue?.description);
setSelectedCustomer({
...selectedCustomer,
address: newValue?.description
});
}}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => <TextField {...params} fullWidth />}
renderOption={(props, option) => {
const matches = option.structured_formatting.main_text_matched_substrings || [];
const parts = parse(
option.structured_formatting.main_text,
matches.map((match) => [match.offset, match.offset + match.length])
);
return (
<li {...props}>
<Grid container alignItems="center">
<Grid item sx={{ display: 'flex', width: 44 }}>
<LocationOnIcon sx={{ color: 'text.secondary' }} />
</Grid>
<Grid
item
sx={{
width: 'calc(100% - 44px)',
wordWrap: 'break-word'
}}
>
{parts?.map((part, index) => (
<Box
key={index}
component="span"
sx={{
fontWeight: part.highlight ? 'bold' : 'regular'
}}
>
{part.text}
</Box>
))}
<Typography variant="body2" color="text.secondary">
{option?.structured_formatting.secondary_text}
</Typography>
</Grid>
</Grid>
</li>
);
}}
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>

View File

@@ -37,7 +37,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 Geocode from 'react-geocode'; import AddressAutocomplete 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';
@@ -169,22 +169,7 @@ const OrderMap = ({ startPoint, endPoint, appLocaLat, appLocaLng }) => {
); );
}; };
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
const Createorder1 = () => { const Createorder1 = () => {
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// ================================================= || GoogleMaps (Drawer) || =================================================
const loaded = React.useRef(false);
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md')); const isMobile = useMediaQuery(theme.breakpoints.down('md'));
@@ -275,17 +260,6 @@ const Createorder1 = () => {
console.log('pickupSlot', pickupSlot); console.log('pickupSlot', pickupSlot);
}, [pickupSlotsList, pickupSlot]); }, [pickupSlotsList, pickupSlot]);
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&libraries=places&location=10.3656,77.9690&radius=50000&components=country:IN&strictbounds=true`,
document.querySelector('head'),
'google-maps'
);
}
loaded.current = true;
}
// ==============================|| fetchAppLocations ||============================== // // ==============================|| fetchAppLocations ||============================== //
const fetchAppLocations = async () => { const fetchAppLocations = async () => {
try { try {
@@ -760,28 +734,9 @@ const Createorder1 = () => {
setLoading(false); setLoading(false);
}); });
}; };
// ============================================= || Google Maps Autocomplete(pick) || ============================================= // ============================================= || Address Autocomplete (pick) || =============================================
useEffect(() => { const handlePickPlaceSelected = (place) => {
// Initialize Google Maps Autocomplete
if (inputValue1) {
const autocompleteInput = document.getElementById('addressAuto1');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
// radius: 100000
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000
}).getBounds()
});
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue1(`${place.name}, ${place.formatted_address}`); setInputValue1(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
// to trigger getDistance // to trigger getDistance
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` }); setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` });
@@ -839,33 +794,11 @@ const Createorder1 = () => {
latitude: place.geometry.location.lat(), latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng() longitude: place.geometry.location.lng()
}); });
console.log('Pick Address:', address); };
}); // ============================================= || Address Autocomplete (Drop) || =============================================
}
}, [inputValue1]);
// ============================================= || Google Maps Autocomplete(Drop) || =============================================
useEffect(() => { const handleDropPlaceSelected = (place) => {
if (inputValue2) {
// Initialize Google Maps Autocomplete
const autocompleteInput = document.getElementById('addressAuto2');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000 //km to m
// radius: 100000 //km to m
}).getBounds()
});
let arr = [];
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue2(`${place.name}, ${place.formatted_address}`); setInputValue2(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log('drop (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` }); setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` });
@@ -923,10 +856,7 @@ const Createorder1 = () => {
latitude: place.geometry.location.lat(), latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng() longitude: place.geometry.location.lng()
}); });
console.log('Drop Address:', address); };
});
}
}, [inputValue2]);
// ============================================= || gettenantlocations (branches) || ============================================= // ============================================= || gettenantlocations (branches) || =============================================
const gettenantlocations = async (id) => { const gettenantlocations = async (id) => {
@@ -1375,17 +1305,20 @@ const Createorder1 = () => {
Address Lookup Address Lookup
</Typography> </Typography>
{addId1 == 0 ? ( {addId1 == 0 ? (
<TextField <AddressAutocomplete
id="addressAuto1" id="addressAuto1"
fullWidth fullWidth
size="small" placeholder="Search for an address..."
placeholder="Search Google Maps for an address..."
variant="outlined"
value={inputValue1 || ''} value={inputValue1 || ''}
onChange={(e) => setInputValue1(e.target.value)} onChange={setInputValue1}
helperText="Start typing to auto-fill the address details below" onPlaceSelected={handlePickPlaceSelected}
FormHelperTextProps={{ className: 'address-search-helper pickup-helper' }} bias={{ lat: appLocaLat, lng: appLocaLng }}
InputProps={{ TextFieldProps={{
size: 'small',
variant: 'outlined',
helperText: 'Start typing to auto-fill the address details below',
FormHelperTextProps: { className: 'address-search-helper pickup-helper' },
InputProps: {
startAdornment: ( startAdornment: (
<InputAdornment position="start"> <InputAdornment position="start">
<SearchOutlined style={{ fontSize: 15, color: '#662582' }} /> <SearchOutlined style={{ fontSize: 15, color: '#662582' }} />
@@ -1412,6 +1345,7 @@ const Createorder1 = () => {
</IconButton> </IconButton>
), ),
style: { borderRadius: '10px', background: '#fff' } style: { borderRadius: '10px', background: '#fff' }
}
}} }}
/> />
) : ( ) : (
@@ -1693,17 +1627,20 @@ const Createorder1 = () => {
Address Lookup Address Lookup
</Typography> </Typography>
{addId2 == 0 ? ( {addId2 == 0 ? (
<TextField <AddressAutocomplete
id="addressAuto2" id="addressAuto2"
placeholder="Search Google Maps for an address..." placeholder="Search for an address..."
variant="outlined"
size="small"
fullWidth fullWidth
value={inputValue2 || ''} value={inputValue2 || ''}
onChange={(e) => setInputValue2(e.target.value)} onChange={setInputValue2}
helperText="Start typing to auto-fill the address details below" onPlaceSelected={handleDropPlaceSelected}
FormHelperTextProps={{ className: 'address-search-helper drop-helper' }} bias={{ lat: appLocaLat, lng: appLocaLng }}
InputProps={{ TextFieldProps={{
size: 'small',
variant: 'outlined',
helperText: 'Start typing to auto-fill the address details below',
FormHelperTextProps: { className: 'address-search-helper drop-helper' },
InputProps: {
startAdornment: ( startAdornment: (
<InputAdornment position="start"> <InputAdornment position="start">
<SearchOutlined style={{ fontSize: 15, color: '#65387a' }} /> <SearchOutlined style={{ fontSize: 15, color: '#65387a' }} />
@@ -1730,6 +1667,7 @@ const Createorder1 = () => {
</IconButton> </IconButton>
), ),
style: { borderRadius: '10px', background: '#fff' } style: { borderRadius: '10px', background: '#fff' }
}
}} }}
/> />
) : ( ) : (

View File

@@ -37,7 +37,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 Geocode from 'react-geocode'; import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import { FaUser } from 'react-icons/fa6'; import { FaUser } from 'react-icons/fa6';
@@ -280,22 +280,7 @@ const OrderMap = ({ startPoint, endPoint, appLocaLat, appLocaLng }) => {
}; };
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
const Createorder1 = () => { const Createorder1 = () => {
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// ================================================= || GoogleMaps (Drawer) || =================================================
const loaded = React.useRef(false);
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const locationRef = useRef(null); const locationRef = useRef(null);
@@ -380,17 +365,6 @@ const Createorder1 = () => {
} }
}; };
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&libraries=places&location=10.3656,77.9690&radius=50000&components=country:IN&strictbounds=true`,
document.querySelector('head'),
'google-maps'
);
}
loaded.current = true;
}
// ==============================|| fetchAppLocations ||============================== // // ==============================|| fetchAppLocations ||============================== //
const fetchAppLocations = async () => { const fetchAppLocations = async () => {
try { try {
@@ -863,28 +837,9 @@ const Createorder1 = () => {
setLoading(false); setLoading(false);
}); });
}; };
// ============================================= || Google Maps Autocomplete(pick) || ============================================= // ============================================= || Address Autocomplete (pick) || =============================================
useEffect(() => { const handlePickPlaceSelected = (place) => {
// Initialize Google Maps Autocomplete
if (inputValue1) {
const autocompleteInput = document.getElementById('addressAuto1');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
// radius: 100000
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000
}).getBounds()
});
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue1(`${place.name}, ${place.formatted_address}`); setInputValue1(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log(' pick (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
// to trigger getDistance // to trigger getDistance
setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); setStartPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` }); setPickCust({ ...pickCust, address: `${place.name} ${place.formatted_address}` });
@@ -942,33 +897,11 @@ const Createorder1 = () => {
latitude: place.geometry.location.lat(), latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng() longitude: place.geometry.location.lng()
}); });
console.log('Pick Address:', address); };
}); // ============================================= || Address Autocomplete (Drop) || =============================================
}
}, [inputValue1]);
// ============================================= || Google Maps Autocomplete(Drop) || =============================================
useEffect(() => { const handleDropPlaceSelected = (place) => {
if (inputValue2) {
// Initialize Google Maps Autocomplete
const autocompleteInput = document.getElementById('addressAuto2');
const autocomplete = new window.google.maps.places.Autocomplete(autocompleteInput, {
// types: ['(cities)'], // You can adjust the types parameter based on your requirements
strictBounds: true,
bounds: new window.google.maps.Circle({
// center: new window.google.maps.LatLng(11.0050707, 76.9509083),
center: new window.google.maps.LatLng(appLocaLat, appLocaLng),
radius: appLocaRadius * 1000 //km to m
// radius: 100000 //km to m
}).getBounds()
});
let arr = [];
// Event listener for autocomplete place changed
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
setInputValue2(`${place.name}, ${place.formatted_address}`); setInputValue2(`${place.name}, ${place.formatted_address}`);
console.log('new place', place); // Do something with the selected place
console.log('drop (new place) lat lng', { lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Do something with the selected place
setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() }); setEndPoint({ latitude: place.geometry.location.lat(), longitude: place.geometry.location.lng() });
setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` }); setDropCust({ ...dropCust, address: `${place.name} ${place.formatted_address}` });
@@ -1026,10 +959,7 @@ const Createorder1 = () => {
latitude: place.geometry.location.lat(), latitude: place.geometry.location.lat(),
longitude: place.geometry.location.lng() longitude: place.geometry.location.lng()
}); });
console.log('Drop Address:', address); };
});
}
}, [inputValue2]);
// ============================================= || gettenantlocations (branches) || ============================================= // ============================================= || gettenantlocations (branches) || =============================================
const gettenantlocations = async (id) => { const gettenantlocations = async (id) => {
@@ -1422,15 +1352,17 @@ const Createorder1 = () => {
<Stack spacing={1.25} sx={{ mt: 0 }}> <Stack spacing={1.25} sx={{ mt: 0 }}>
{addId1 == 0 ? ( {addId1 == 0 ? (
<div> <div>
<TextField <AddressAutocomplete
// disabled={!appId || !tenantid || !locationid}
id="addressAuto1" id="addressAuto1"
fullWidth fullWidth
label={'Address'} label={'Address'}
variant="outlined"
value={inputValue1 || ''} value={inputValue1 || ''}
onChange={(e) => setInputValue1(e.target.value)} onChange={setInputValue1}
InputProps={{ onPlaceSelected={handlePickPlaceSelected}
bias={{ lat: appLocaLat, lng: appLocaLng }}
TextFieldProps={{
variant: 'outlined',
InputProps: {
endAdornment: ( endAdornment: (
<IconButton <IconButton
onClick={() => { onClick={() => {
@@ -1451,6 +1383,7 @@ const Createorder1 = () => {
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
) )
}
}} }}
/> />
</div> </div>
@@ -1799,15 +1732,17 @@ const Createorder1 = () => {
<Stack spacing={1.25} sx={{ mt: 0 }}> <Stack spacing={1.25} sx={{ mt: 0 }}>
{addId2 == 0 ? ( {addId2 == 0 ? (
<div> <div>
<TextField <AddressAutocomplete
// disabled={!appId || !tenantid || !locationid}
id="addressAuto2" id="addressAuto2"
label="Address " label="Address "
variant="outlined"
fullWidth fullWidth
value={inputValue2 || ''} value={inputValue2 || ''}
onChange={(e) => setInputValue2(e.target.value)} onChange={setInputValue2}
InputProps={{ onPlaceSelected={handleDropPlaceSelected}
bias={{ lat: appLocaLat, lng: appLocaLng }}
TextFieldProps={{
variant: 'outlined',
InputProps: {
endAdornment: ( endAdornment: (
<IconButton <IconButton
onClick={() => { onClick={() => {
@@ -1828,6 +1763,7 @@ const Createorder1 = () => {
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
) )
}
}} }}
/> />
</div> </div>

View File

@@ -1,91 +0,0 @@
import React, { useEffect, useRef } from 'react';
import { LoadScriptNext, GoogleMap } from '@react-google-maps/api';
const containerStyle = {
width: '100%',
height: '90vh'
};
const MapWithRouteGoogle = ({ coordinates, additionalProps, setMapOpen }) => {
const mapRef = useRef(null);
/** Convert coordinates to numbers */
const numericCoordinates = coordinates
.map((c) => {
const lat = Number(c.lat);
const lng = Number(c.lng);
return isNaN(lat) || isNaN(lng) ? null : { lat, lng };
})
.filter(Boolean);
if (numericCoordinates.length < 2) {
return <div>No route data available</div>;
}
const start = numericCoordinates[0];
const end = numericCoordinates[numericCoordinates.length - 1];
/** Map loaded callback */
const onMapLoad = (map) => {
// draw markers
new window.google.maps.Marker({
position: start,
map,
label: 'S',
title: `Start: ${additionalProps?.riderStart}`
});
new window.google.maps.Marker({
position: end,
map,
label: 'E',
title: `End: ${additionalProps?.riderEnd}`
});
// draw rider route (point-to-point)
const route = new window.google.maps.Polyline({
path: numericCoordinates,
geodesic: false,
strokeColor: '#1A73E8',
strokeOpacity: 1.0,
strokeWeight: 4
});
route.setMap(map);
// auto fit
const bounds = new window.google.maps.LatLngBounds();
numericCoordinates.forEach((p) => bounds.extend(p));
map.fitBounds(bounds);
};
return (
<>
<button
onClick={() => setMapOpen(false)}
style={{
position: 'absolute',
top: 10,
right: 10,
zIndex: 999,
padding: '6px 12px',
background: '#1A73E8',
color: 'white',
borderRadius: 6,
cursor: 'pointer',
border: 'none'
}}
>
Close
</button>
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<GoogleMap mapContainerStyle={containerStyle} center={start} zoom={14} onLoad={onMapLoad}>
{/* Polyline and markers added via onLoad */}
</GoogleMap>
</LoadScriptNext>
</>
);
};
export default MapWithRouteGoogle;

View File

@@ -1,76 +1,55 @@
import { Button } from '@mui/material'; import { Button } from '@mui/material';
import { LoadScriptNext, GoogleMap, Marker, OverlayView } from '@react-google-maps/api'; import { MapContainer, TileLayer, Marker, Tooltip } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
const containerStyle = { const containerStyle = {
width: '100%', width: '100%',
height: 'calc(100vh - 150px)' height: 'calc(100vh - 150px)'
}; };
// Green marker
const GreenIcon = new L.Icon({
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
shadowSize: [41, 41]
});
// Red marker
const RedIcon = new L.Icon({
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
shadowSize: [41, 41]
});
export default function RiderLocationMap({ riderLocations }) { export default function RiderLocationMap({ riderLocations }) {
console.log('riderLocations', riderLocations); console.log('riderLocations', riderLocations);
const center = { const center = [Number(riderLocations?.[0]?.latitude || 11.0056), Number(riderLocations?.[0]?.longitude || 76.9661)];
lat: Number(riderLocations?.[0]?.latitude || 11.0056),
lng: Number(riderLocations?.[0]?.longitude || 76.9661)
};
const GreenIcon = {
url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
scaledSize: new window.google.maps.Size(25, 41),
anchor: new window.google.maps.Point(12, 41)
};
const RedIcon = {
url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
scaledSize: new window.google.maps.Size(25, 41),
anchor: new window.google.maps.Point(12, 41)
};
return ( return (
<LoadScriptNext googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}> <MapContainer center={center} zoom={12} style={containerStyle}>
<GoogleMap mapContainerStyle={containerStyle} zoom={12} center={center}> <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" attribution="&copy; OpenStreetMap contributors" />
{riderLocations && {riderLocations &&
riderLocations?.map((r, index) => { riderLocations.map((r, index) => {
const lat = Number(r.latitude); const lat = Number(r.latitude);
const lng = Number(r.longitude); const lng = Number(r.longitude);
return ( return (
<div key={index}> <Marker key={index} position={[lat, lng]} icon={r.status == 'active' ? GreenIcon : RedIcon}>
{/* Marker */} <Tooltip permanent direction="top" offset={[0, -35]} opacity={1} className="rider-location-tooltip">
<Marker
position={{ lat, lng }}
icon={r.status == 'active' ? GreenIcon : RedIcon}
label={{
fontSize: '14px',
fontWeight: 'bold'
}}
/>
<OverlayView position={{ lat, lng }} mapPaneName={OverlayView.OVERLAY_LAYER}>
<div
style={{
background: 'none',
color: 'green',
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 600,
whiteSpace: 'nowrap',
transform: 'translate(-50%, -140%)',
pointerEvents: 'none',
ml: 20
}}
>
<Button variant="contained" color="primary" size="small"> <Button variant="contained" color="primary" size="small">
{` ${r.username} `} {` ${r.username} `}
{/* <br /> */}
{/* {`${r.contactno || '##### ##### '} `} */}
<br /> <br />
{`(${r.orderid || ''}) `} {`(${r.orderid || ''}) `}
</Button> </Button>
</div> </Tooltip>
</OverlayView> </Marker>
</div>
); );
})} })}
</GoogleMap> </MapContainer>
</LoadScriptNext>
); );
} }

View File

@@ -1,110 +1,15 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { GoogleMap, Polyline, Marker, InfoWindow, useJsApiLoader } from '@react-google-maps/api'; import { MapContainer, TileLayer, Polyline, Marker, Popup, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { Box, IconButton, Stack, Typography, CircularProgress } from '@mui/material'; import { Box, IconButton, Stack, Typography, CircularProgress } from '@mui/material';
import { MdClose, MdRoute } from 'react-icons/md'; import { MdClose, MdRoute } from 'react-icons/md';
const containerStyle = { width: '100%', height: '100%' }; const containerStyle = { width: '100%', height: '100%' };
// Renders a single rider's PLANNED route for the date range chosen on the // Numbered step icon — brand purple to match the planned-route polyline below.
// Riders Summary page. `details` is an ordered array of waypoints (sorted by // Drawn fresh per render as a data URL so the step number can be baked into
// the planning step number) shaped as: // the SVG without juggling external marker assets.
// { step, orderid, deliveryid, customer, address,
// dropLat, dropLng, pickLat, pickLng, expectedTime }
// `dropLat/dropLng` are required; pickup coords are optional and rendered as
// faded pre-stops if present.
export default function RidersRoutes({ details, loading, riderName, dateRange, onClose }) {
const mapRef = useRef(null);
const [focusedStep, setFocusedStep] = useState(null);
const [routePath, setRoutePath] = useState([]);
const [routeLoading, setRouteLoading] = useState(false);
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAPS_KEY
});
// Step-pin coordinates in planning order — what the polyline connects.
const dropPath = useMemo(
() => (details || []).map((d) => ({ lat: d.dropLat, lng: d.dropLng })),
[details]
);
// Auto-fit map bounds to the full planned path once the map and data are
// both ready. Re-runs whenever the route changes (different rider / date).
useEffect(() => {
if (!isLoaded || !mapRef.current || dropPath.length === 0) return;
const bounds = new window.google.maps.LatLngBounds();
dropPath.forEach((p) => bounds.extend(p));
mapRef.current.fitBounds(bounds, 48);
}, [isLoaded, dropPath]);
// Resolve the rider's planned waypoints into an actual road-following path
// via the Directions API. Without this, the polyline would cut across
// buildings / aerial lines — operators have no way to read the real route.
// Directions has a 25-waypoint limit per request, so we chunk and stitch.
useEffect(() => {
if (!isLoaded || dropPath.length < 2) {
setRoutePath([]);
return;
}
let cancelled = false;
const ds = new window.google.maps.DirectionsService();
const MAX_WPS = 23; // origin + 23 waypoints + destination = 25 stops/chunk
const fetchSegment = (origin, destination, waypoints) =>
new Promise((resolve, reject) => {
ds.route(
{
origin,
destination,
waypoints: waypoints.map((p) => ({ location: p, stopover: true })),
travelMode: window.google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === 'OK') resolve(result);
else reject(new Error(status));
}
);
});
(async () => {
setRouteLoading(true);
try {
const points = dropPath;
const all = [];
let i = 0;
while (i < points.length - 1) {
const remaining = points.length - 1 - i;
const take = Math.min(remaining, MAX_WPS + 1);
const origin = points[i];
const destination = points[i + take];
const waypoints = points.slice(i + 1, i + take);
const res = await fetchSegment(origin, destination, waypoints);
const seg = res.routes[0].overview_path.map((ll) => ({
lat: ll.lat(),
lng: ll.lng()
}));
// Avoid duplicating the join point between adjacent chunks.
if (all.length > 0 && seg.length > 0) seg.shift();
all.push(...seg);
i += take;
}
if (!cancelled) setRoutePath(all);
} catch {
// Fall back to the straight-line skeleton on failure (quota, no route, etc.).
if (!cancelled) setRoutePath([]);
} finally {
if (!cancelled) setRouteLoading(false);
}
})();
return () => {
cancelled = true;
};
}, [isLoaded, dropPath]);
// Numbered step icon as a data URL — drawn fresh per render so we can pass
// the step number into the SVG without juggling external assets. Color is
// brand purple to match the planned-route polyline below.
const stepIcon = (n, isFocused) => { const stepIcon = (n, isFocused) => {
const size = isFocused ? 38 : 32; const size = isFocused ? 38 : 32;
const color = isFocused ? '#4D1C61' : '#662582'; const color = isFocused ? '#4D1C61' : '#662582';
@@ -114,9 +19,79 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
`<text x="16" y="21" text-anchor="middle" font-family="Arial,sans-serif" font-size="14" font-weight="700" fill="white">${n}</text>` + `<text x="16" y="21" text-anchor="middle" font-family="Arial,sans-serif" font-size="14" font-weight="700" fill="white">${n}</text>` +
`</svg>` `</svg>`
); );
return `data:image/svg+xml;charset=UTF-8,${svg}`; return new L.Icon({
iconUrl: `data:image/svg+xml;charset=UTF-8,${svg}`,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2]
});
}; };
// Fits the map to the planned path once both the map and data are ready.
// Re-runs whenever the route changes (different rider / date).
const FitBoundsController = ({ dropPath }) => {
const map = useMap();
useEffect(() => {
if (!dropPath.length) return;
if (dropPath.length === 1) {
map.setView(dropPath[0], 14);
} else {
map.fitBounds(dropPath, { padding: [48, 48] });
}
}, [dropPath, map]);
return null;
};
// Renders a single rider's PLANNED route for the date range chosen on the
// Riders Summary page. `details` is an ordered array of waypoints (sorted by
// the planning step number) shaped as:
// { step, orderid, deliveryid, customer, address,
// dropLat, dropLng, pickLat, pickLng, expectedTime }
// `dropLat/dropLng` are required; pickup coords are optional and rendered as
// faded pre-stops if present.
export default function RidersRoutes({ details, loading, riderName, dateRange, onClose }) {
const [focusedStep, setFocusedStep] = useState(null);
const [routePath, setRoutePath] = useState([]);
const [routeLoading, setRouteLoading] = useState(false);
// Step-pin coordinates in planning order — what the polyline connects.
const dropPath = useMemo(() => (details || []).map((d) => [d.dropLat, d.dropLng]), [details]);
// Resolve the rider's planned waypoints into an actual road-following path
// via OSRM. Without this, the polyline would cut across buildings / aerial
// lines — operators have no way to read the real route.
useEffect(() => {
if (dropPath.length < 2) {
setRoutePath([]);
return;
}
let cancelled = false;
(async () => {
setRouteLoading(true);
try {
const coords = dropPath.map(([lat, lng]) => `${lng},${lat}`).join(';');
const url = `https://router.project-osrm.org/route/v1/driving/${coords}?overview=full&geometries=geojson`;
const res = await fetch(url);
const data = await res.json();
if (!cancelled && data.routes?.length) {
const points = data.routes[0].geometry.coordinates.map(([lng, lat]) => [lat, lng]);
setRoutePath(points);
} else if (!cancelled) {
setRoutePath([]);
}
} catch (e) {
console.warn('OSRM route error:', e);
if (!cancelled) setRoutePath([]);
} finally {
if (!cancelled) setRouteLoading(false);
}
})();
return () => {
cancelled = true;
};
}, [dropPath]);
const headerBar = ( const headerBar = (
<Stack <Stack
direction="row" direction="row"
@@ -133,12 +108,8 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
> >
<MdRoute size={20} /> <MdRoute size={20} />
<Stack sx={{ flex: 1, minWidth: 0 }}> <Stack sx={{ flex: 1, minWidth: 0 }}>
<Typography sx={{ fontWeight: 700, fontSize: 15, lineHeight: 1.2 }}> <Typography sx={{ fontWeight: 700, fontSize: 15, lineHeight: 1.2 }}>Planned route{riderName ? `${riderName}` : ''}</Typography>
Planned route{riderName ? `${riderName}` : ''} {dateRange && <Typography sx={{ fontSize: 12, opacity: 0.85 }}>{dateRange}</Typography>}
</Typography>
{dateRange && (
<Typography sx={{ fontSize: 12, opacity: 0.85 }}>{dateRange}</Typography>
)}
</Stack> </Stack>
{details && details.length > 0 && ( {details && details.length > 0 && (
<Typography sx={{ fontSize: 12, opacity: 0.9, fontWeight: 600 }}> <Typography sx={{ fontSize: 12, opacity: 0.9, fontWeight: 600 }}>
@@ -154,16 +125,14 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
</Stack> </Stack>
); );
// Loading state — route fetch in flight OR Google Maps script not ready yet. // Loading state — parent is still fetching the planned route data.
if (loading || !isLoaded) { if (loading) {
return ( return (
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{headerBar} {headerBar}
<Stack alignItems="center" justifyContent="center" sx={{ flex: 1, gap: 1.5 }}> <Stack alignItems="center" justifyContent="center" sx={{ flex: 1, gap: 1.5 }}>
<CircularProgress size={32} /> <CircularProgress size={32} />
<Typography sx={{ color: '#64748b', fontSize: 13 }}> <Typography sx={{ color: '#64748b', fontSize: 13 }}>Loading planned route</Typography>
{loading ? 'Loading planned route…' : 'Loading map…'}
</Typography>
</Stack> </Stack>
</Box> </Box>
); );
@@ -176,9 +145,7 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{headerBar} {headerBar}
<Stack alignItems="center" justifyContent="center" sx={{ flex: 1, gap: 1, p: 3 }}> <Stack alignItems="center" justifyContent="center" sx={{ flex: 1, gap: 1, p: 3 }}>
<Typography sx={{ color: '#1e293b', fontWeight: 700, fontSize: 16 }}> <Typography sx={{ color: '#1e293b', fontWeight: 700, fontSize: 16 }}>No planned route for this rider</Typography>
No planned route for this rider
</Typography>
<Typography sx={{ color: '#64748b', fontSize: 13, textAlign: 'center', maxWidth: 360 }}> <Typography sx={{ color: '#64748b', fontSize: 13, textAlign: 'center', maxWidth: 360 }}>
There are no deliveries with drop coordinates assigned to this rider for the selected date range. There are no deliveries with drop coordinates assigned to this rider for the selected date range.
</Typography> </Typography>
@@ -191,53 +158,21 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{headerBar} {headerBar}
<Box sx={{ flex: 1, minHeight: 0 }}> <Box sx={{ flex: 1, minHeight: 0 }}>
<GoogleMap <MapContainer center={dropPath[0]} zoom={14} style={containerStyle} zoomControl={false}>
mapContainerStyle={containerStyle} <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" attribution="&copy; OpenStreetMap contributors" />
onLoad={(map) => (mapRef.current = map)} <FitBoundsController dropPath={dropPath} />
center={dropPath[0]}
zoom={14}
options={{
streetViewControl: false,
mapTypeControl: false,
fullscreenControl: false
}}
>
{routePath.length > 0 ? ( {routePath.length > 0 ? (
<> <>
{/* Translucent backdrop so the route stays legible on busy tiles. */} {/* Translucent backdrop so the route stays legible on busy tiles. */}
<Polyline <Polyline positions={routePath} pathOptions={{ color: '#662582', opacity: 0.25, weight: 8 }} />
path={routePath} {/* Road-following planned route from OSRM. */}
options={{ strokeColor: '#662582', strokeOpacity: 0.25, strokeWeight: 8 }} <Polyline positions={routePath} pathOptions={{ color: '#662582', opacity: 0.95, weight: 4 }} />
/>
{/* Road-following planned route from the Directions API. */}
<Polyline
path={routePath}
options={{ strokeColor: '#662582', strokeOpacity: 0.95, strokeWeight: 4 }}
/>
</> </>
) : ( ) : (
// Fallback while Directions is in flight (or if it fails) — dashed // Fallback while OSRM is in flight (or if it fails) — dashed
// straight-line skeleton between drop pins in step order. // straight-line skeleton between drop pins in step order.
<Polyline <Polyline positions={dropPath} pathOptions={{ color: '#662582', opacity: 0.6, weight: 3, dashArray: '2 10', lineCap: 'round' }} />
path={dropPath}
options={{
strokeColor: '#662582',
strokeOpacity: 0,
strokeWeight: 0,
icons: [
{
icon: {
path: 'M 0,-1 0,1',
strokeOpacity: 0.6,
strokeColor: '#662582',
scale: 3
},
offset: '0',
repeat: '14px'
}
]
}}
/>
)} )}
{details.map((d, i) => { {details.map((d, i) => {
@@ -246,39 +181,29 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
return ( return (
<Marker <Marker
key={`step-${d.deliveryid || d.orderid || i}`} key={`step-${d.deliveryid || d.orderid || i}`}
position={{ lat: d.dropLat, lng: d.dropLng }} position={[d.dropLat, d.dropLng]}
icon={{ url: stepIcon(stepNum, isFocused) }} icon={stepIcon(stepNum, isFocused)}
onClick={() => setFocusedStep(isFocused ? null : d.deliveryid)} eventHandlers={{
zIndex={isFocused ? 1000 : stepNum} click: () => setFocusedStep(isFocused ? null : d.deliveryid)
}}
zIndexOffset={isFocused ? 1000 : stepNum}
> >
{isFocused && ( <Popup onClose={() => setFocusedStep(null)}>
<InfoWindow onCloseClick={() => setFocusedStep(null)}>
<Box sx={{ minWidth: 180, fontFamily: 'inherit' }}> <Box sx={{ minWidth: 180, fontFamily: 'inherit' }}>
<Typography sx={{ fontWeight: 800, fontSize: 13, color: '#0f172a' }}> <Typography sx={{ fontWeight: 800, fontSize: 13, color: '#0f172a' }}>
Step {stepNum} · {d.customer} Step {stepNum} · {d.customer}
</Typography> </Typography>
{d.address && ( {d.address && <Typography sx={{ fontSize: 12, color: '#475569', mt: 0.5 }}>{d.address}</Typography>}
<Typography sx={{ fontSize: 12, color: '#475569', mt: 0.5 }}>
{d.address}
</Typography>
)}
{d.expectedTime && ( {d.expectedTime && (
<Typography sx={{ fontSize: 12, color: '#64748b', mt: 0.5 }}> <Typography sx={{ fontSize: 12, color: '#64748b', mt: 0.5 }}>ETA {String(d.expectedTime).slice(11, 16) || d.expectedTime}</Typography>
ETA {String(d.expectedTime).slice(11, 16) || d.expectedTime}
</Typography>
)}
{d.orderid && (
<Typography sx={{ fontSize: 11, color: '#94a3b8', mt: 0.5 }}>
Order #{d.orderid}
</Typography>
)} )}
{d.orderid && <Typography sx={{ fontSize: 11, color: '#94a3b8', mt: 0.5 }}>Order #{d.orderid}</Typography>}
</Box> </Box>
</InfoWindow> </Popup>
)}
</Marker> </Marker>
); );
})} })}
</GoogleMap> </MapContainer>
</Box> </Box>
</Box> </Box>
); );

View File

@@ -50,7 +50,7 @@ import { useState, useEffect } from 'react';
import axios from 'axios'; import axios from 'axios';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import Transitions from 'components/@extended/Transitions'; import Transitions from 'components/@extended/Transitions';
import Autocomplete from 'react-google-autocomplete'; import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
import * as React from 'react'; import * as React from 'react';
@@ -60,8 +60,6 @@ import TablePagination from '@mui/material/TablePagination';
import TableSortLabel from '@mui/material/TableSortLabel'; import TableSortLabel from '@mui/material/TableSortLabel';
import { visuallyHidden } from '@mui/utils'; import { visuallyHidden } from '@mui/utils';
import Geocode from 'react-geocode';
const Requests = () => { const Requests = () => {
// let dispatch = useDispatch(); // let dispatch = useDispatch();
@@ -201,7 +199,6 @@ const Requests = () => {
const [suburb, setSuburb] = useState(''); const [suburb, setSuburb] = useState('');
const [currenttenantid] = useState(''); const [currenttenantid] = useState('');
const [latlong, setLatlong] = useState({}); const [latlong, setLatlong] = useState({});
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// const [alertmessage, setAlertmessage] = useState(''); // const [alertmessage, setAlertmessage] = useState('');
// const [toast, setToast] = useState(false); // const [toast, setToast] = useState(false);
const [rolesarr, setRolesarr] = useState([]); const [rolesarr, setRolesarr] = useState([]);
@@ -277,27 +274,34 @@ const Requests = () => {
// } // }
useEffect(() => { const handleAddressPlaceSelected = (place) => {
try { setAddress(place.formatted_address);
Geocode.fromAddress(address).then( setLatlong({ lat: place.geometry.location.lat(), lng: place.geometry.location.lng() });
(response) => { let city1, state, zipcode1, suburb1;
if (response.status == 'OK') { place.address_components.forEach((component) => {
const { lat, lng } = response.results[0].geometry.location; component.types.forEach((type) => {
setLatlong({ switch (type) {
lat, case 'locality':
lng city1 = component.long_name;
break;
case 'administrative_area_level_1':
state = component.long_name;
break;
case 'postal_code':
zipcode1 = component.long_name;
break;
case 'sublocality':
case 'sublocality_level_1':
suburb1 = component.long_name;
break;
}
}); });
console.log(response); });
} setCity(city1 || '');
}, setState1(state || '');
(error) => { setZipcode(zipcode1 || '');
console.log(error); setSuburb(suburb1 || '');
} };
);
} catch (err) {
console.log(err);
}
}, [address]);
useEffect(() => { useEffect(() => {
console.log('rolesarr'); console.log('rolesarr');
@@ -1458,53 +1462,22 @@ const Requests = () => {
{/* } */} {/* } */}
<Autocomplete <AddressAutocomplete
className="automap" id="request-address-autocomplete"
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY} fullWidth
style={{
width: '100%',
height: '40px',
borderRadius: '5px',
border: '1px solid #e0e0e0',
textIndent: '10px',
outline: 'none'
// ':hover': {
// border: '1px solid #00b0ff !important',
// backgroundColor:'blue'
// }
}}
onPlaceSelected={(place) => {
setAddress(place.formatted_address);
let city1, state, zipcode1, suburb1;
for (let i = 0; i < place.address_components.length; i++) {
for (let j = 0; j < place.address_components[i].types.length; j++) {
switch (place.address_components[i].types[j]) {
case 'locality':
city1 = place.address_components[i].long_name;
break;
case 'administrative_area_level_1':
state = place.address_components[i].long_name;
break;
case 'postal_code':
zipcode1 = place.address_components[i].long_name;
break;
case 'sublocality':
suburb1 = place.address_components[i].long_name;
break;
}
}
}
setCity(city1 || '');
setState1(state || '');
setZipcode(zipcode1 || '');
setSuburb(suburb1 || '');
}}
options={{
types: ['address' || 'geocode']
}}
placeholder="Address" placeholder="Address"
value={address} value={address}
onChange={(e) => setAddress(e.target.value)} onChange={setAddress}
onPlaceSelected={handleAddressPlaceSelected}
TextFieldProps={{
className: 'automap',
InputProps: {
style: {
borderRadius: '5px',
border: '1px solid #e0e0e0'
}
}
}}
/> />
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -11,12 +11,10 @@ import { Box, Button, Grid, InputLabel, MenuItem, Select, Stack, TextField, Typo
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import axios from 'axios'; import axios from 'axios';
// assets // assets
import { usePlacesWidget } from 'react-google-autocomplete'; import AddressAutocomplete, { geocodeAddress } from 'components/nearle_components/AddressAutocomplete';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import Geocode from 'react-geocode';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
// import { setLocationType } from 'react-geocode';
// const avatarImage = require.context('assets/images/users', true); // const avatarImage = require.context('assets/images/users', true);
@@ -50,9 +48,6 @@ const Createrider = () => {
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md')); const isMobile = useMediaQuery(theme.breakpoints.down('md'));
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
// Geocode.setApiKey('AIzaSyCF4KatYCI3vqz1_H3kiHeyS3yCMfYToh8');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
@@ -64,25 +59,15 @@ const Createrider = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
try { let active = true;
Geocode.fromAddress(address).then( geocodeAddress(address).then((place) => {
(response) => { if (active && place) {
if (response.status == 'OK') { setLatlong({ lat: place.geometry.location.lat(), lng: place.geometry.location.lng() });
const { lat, lng } = response.results[0].geometry.location; }
setLatlong({
lat,
lng
}); });
console.log(response); return () => {
} active = false;
}, };
(error) => {
console.log(error);
}
);
} catch (err) {
console.log(err);
}
}, [address]); }, [address]);
const opentoast = (message) => { const opentoast = (message) => {
@@ -119,11 +104,7 @@ const Createrider = () => {
} }
}, [selectedImage]); }, [selectedImage]);
const { ref: materialRef } = usePlacesWidget({ const handleAddressPlaceSelected = (place) => {
apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
onPlaceSelected: (place) => {
console.log(place);
setAddress(place.formatted_address); setAddress(place.formatted_address);
let city1, zipcode1, state1, suburb1; let city1, zipcode1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) { for (let i = 0; i < place.address_components.length; i++) {
@@ -139,6 +120,7 @@ const Createrider = () => {
zipcode1 = place.address_components[i].long_name; zipcode1 = place.address_components[i].long_name;
break; break;
case 'sublocality': case 'sublocality':
case 'sublocality_level_1':
suburb1 = place.address_components[i].long_name; suburb1 = place.address_components[i].long_name;
break; break;
} }
@@ -148,16 +130,7 @@ const Createrider = () => {
setState(state1 || ''); setState(state1 || '');
setZipcode(zipcode1 || ''); setZipcode(zipcode1 || '');
setSuburb(suburb1 || ''); setSuburb(suburb1 || '');
};
// setAddress(place.formatted_address)
},
// inputAutocompleteValue: "country",
options: {
// componentRestrictions: 'us',
// types: ["establishment"]
types: ['address' || 'geocode']
}
});
const createprofile = async () => { const createprofile = async () => {
console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode); console.log('res', businessname, businessno, mobilenumber, emailaddress, address, city, zipcode);
@@ -340,14 +313,13 @@ const Createrider = () => {
<Grid item xs={12}> <Grid item xs={12}>
<Stack spacing={1.25}> <Stack spacing={1.25}>
<InputLabel htmlFor="personal-address">Address</InputLabel> <InputLabel htmlFor="personal-address">Address</InputLabel>
<TextField <AddressAutocomplete
fullWidth
// defaultValue="Street 110-B Kalians Bag, Dewan, M.P. New York"
id="personal-address" id="personal-address"
fullWidth
placeholder="Address" placeholder="Address"
value={address} value={address}
onChange={(e) => setAddress(e.target.value)} onChange={setAddress}
inputRef={materialRef} onPlaceSelected={handleAddressPlaceSelected}
/> />
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -28,9 +28,8 @@ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
// project import // project import
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import axios from 'axios'; import axios from 'axios';
import { usePlacesWidget } from 'react-google-autocomplete'; import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import Geocode from 'react-geocode';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import CircularLoader from 'components/CircularLoader'; import CircularLoader from 'components/CircularLoader';
@@ -55,8 +54,6 @@ const EditRider = () => {
const [locaName, setLocoName] = useState(); const [locaName, setLocoName] = useState();
const userid = localStorage.getItem('userid'); const userid = localStorage.getItem('userid');
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const fetchRiderData = async (id) => { const fetchRiderData = async (id) => {
@@ -237,13 +234,10 @@ const EditRider = () => {
} }
}, [selectedImage]); }, [selectedImage]);
const { ref: materialRef } = usePlacesWidget({ const handleAddressPlaceSelected = (place) => {
apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
onPlaceSelected: (place) => {
console.log(place);
setAddress(place.formatted_address); setAddress(place.formatted_address);
let city1, zipcode1, state1, suburb1; let city1, state1, suburb1;
for (let i = 0; i < place.address_components.length; i++) { for (let i = 0; i < place.address_components.length; i++) {
for (let j = 0; j < place.address_components[i].types.length; j++) { for (let j = 0; j < place.address_components[i].types.length; j++) {
switch (place.address_components[i].types[j]) { switch (place.address_components[i].types[j]) {
@@ -253,30 +247,17 @@ const EditRider = () => {
case 'administrative_area_level_1': case 'administrative_area_level_1':
state1 = place.address_components[i].long_name; state1 = place.address_components[i].long_name;
break; break;
case 'postal_code':
zipcode1 = place.address_components[i].long_name;
break;
case 'sublocality': case 'sublocality':
case 'sublocality_level_1':
suburb1 = place.address_components[i].long_name; suburb1 = place.address_components[i].long_name;
break; break;
} }
} }
} }
setCity(city1 || ''); setCity(city1 || '');
setState(state1 || ''); setState(state1 || '');
setZipcode(zipcode1 || '');
setSuburb(suburb1 || ''); setSuburb(suburb1 || '');
};
// setAddress(place.formatted_address)
},
// inputAutocompleteValue: "country",
options: {
// componentRestrictions: 'us',
// types: ["establishment"]
types: ['address' || 'geocode']
}
});
const updateRider = async () => { const updateRider = async () => {
setLoading(true); setLoading(true);
@@ -483,16 +464,13 @@ const EditRider = () => {
<Grid item xs={12}> <Grid item xs={12}>
<Stack spacing={1.25}> <Stack spacing={1.25}>
<InputLabel htmlFor="personal-address">Address</InputLabel> <InputLabel htmlFor="personal-address">Address</InputLabel>
<TextField <AddressAutocomplete
fullWidth
// defaultValue="Street 110-B Kalians Bag, Dewan, M.P. New York"
id="personal-address" id="personal-address"
fullWidth
placeholder="Address" placeholder="Address"
value={address} value={address}
onChange={(e) => { onChange={setAddress}
setAddress(e.target.value); onPlaceSelected={handleAddressPlaceSelected}
}}
inputRef={materialRef}
/> />
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { useState, useEffect, useRef, Fragment } from 'react'; import { useState, useEffect, useRef, Fragment } from 'react';
import Geocode from 'react-geocode';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import axios from 'axios'; import axios from 'axios';
import { import {
@@ -491,8 +490,6 @@ const Riders = () => {
} }
}); });
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
const handleChangetab = (i) => { const handleChangetab = (i) => {
setTabvalue(i); setTabvalue(i);
setLogsRow(null); setLogsRow(null);