176 lines
4.4 KiB
TypeScript
176 lines
4.4 KiB
TypeScript
|
|
import "@reach/combobox/styles.css";
|
||
|
|
import {
|
||
|
|
Combobox,
|
||
|
|
ComboboxInput,
|
||
|
|
ComboboxList,
|
||
|
|
ComboboxOption,
|
||
|
|
ComboboxPopover,
|
||
|
|
} from "@reach/combobox";
|
||
|
|
import { GoogleMap, Marker, useLoadScript } from "@react-google-maps/api";
|
||
|
|
import Cookies from "js-cookie";
|
||
|
|
import { useEffect, useState } from "react";
|
||
|
|
import usePlacesAutocomplete, {
|
||
|
|
getGeocode,
|
||
|
|
getLatLng,
|
||
|
|
} from "use-places-autocomplete";
|
||
|
|
import { GoogleMapsAPI } from "./client-config";
|
||
|
|
import Geocode from "react-geocode";
|
||
|
|
|
||
|
|
Geocode.setApiKey(GoogleMapsAPI);
|
||
|
|
|
||
|
|
export default function Places(props: {
|
||
|
|
center: { lat: number; lng: number };
|
||
|
|
draggable?: boolean;
|
||
|
|
onLocationChange?: (location: string) => void; // Tambahkan onLocationChange
|
||
|
|
}) {
|
||
|
|
const { isLoaded } = useLoadScript({
|
||
|
|
googleMapsApiKey: GoogleMapsAPI,
|
||
|
|
libraries: ["places"],
|
||
|
|
language: "id",
|
||
|
|
});
|
||
|
|
|
||
|
|
const { center, draggable, onLocationChange } = props;
|
||
|
|
|
||
|
|
if (!isLoaded) return <div>Loading...</div>;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Map
|
||
|
|
lat={center.lat}
|
||
|
|
lng={center.lng}
|
||
|
|
draggable={draggable}
|
||
|
|
onLocationChange={onLocationChange} // Kirimkan properti onLocationChange
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
interface MapProps {
|
||
|
|
lat: number;
|
||
|
|
lng: number;
|
||
|
|
draggable?: boolean;
|
||
|
|
onLocationChange?: (location: string) => void; // Tambahkan properti ini
|
||
|
|
}
|
||
|
|
|
||
|
|
function Map(props: MapProps) {
|
||
|
|
const containerStyle = {
|
||
|
|
width: "100%",
|
||
|
|
height: "400px",
|
||
|
|
};
|
||
|
|
|
||
|
|
const center = {
|
||
|
|
lat: -6.1754,
|
||
|
|
lng: 106.8272,
|
||
|
|
};
|
||
|
|
|
||
|
|
const [selected, setSelected] = useState<{ lat: number; lng: number } | null>(
|
||
|
|
null
|
||
|
|
);
|
||
|
|
|
||
|
|
const { lat, lng, draggable, onLocationChange } = props;
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (lat !== undefined && lng !== undefined) {
|
||
|
|
setSelected({ lat, lng });
|
||
|
|
getAddressFromLatLong(lat, lng);
|
||
|
|
}
|
||
|
|
}, [lat, lng]);
|
||
|
|
|
||
|
|
const onMarkerDragEnd = (e: google.maps.MapMouseEvent) => {
|
||
|
|
const lat = e.latLng?.lat() ?? 0;
|
||
|
|
const lng = e.latLng?.lng() ?? 0;
|
||
|
|
console.log(lat, lng);
|
||
|
|
getAddressFromLatLong(lat, lng);
|
||
|
|
if (onLocationChange) {
|
||
|
|
onLocationChange(`Latitude: ${lat}, Longitude: ${lng}`); // Kirimkan lokasi ke parent melalui onLocationChange
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
async function getAddressFromLatLong(lat: number, lng: number) {
|
||
|
|
try {
|
||
|
|
const response = await Geocode.fromLatLng(lat.toString(), lng.toString());
|
||
|
|
const address = response.results[0].formatted_address;
|
||
|
|
Cookies.set("map_lat", `${lat}`, { expires: 1 });
|
||
|
|
Cookies.set("map_long", `${lng}`, { expires: 1 });
|
||
|
|
console.log("Address:", address);
|
||
|
|
if (onLocationChange) {
|
||
|
|
onLocationChange(address); // Kirimkan alamat jika berhasil
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error(error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<div>
|
||
|
|
<PlacesAutocomplete setSelected={setSelected} />
|
||
|
|
</div>
|
||
|
|
<GoogleMap
|
||
|
|
zoom={selected == null ? 10 : 15}
|
||
|
|
center={selected == null ? center : selected}
|
||
|
|
mapContainerStyle={containerStyle}
|
||
|
|
>
|
||
|
|
{selected && (
|
||
|
|
<Marker
|
||
|
|
draggable={draggable}
|
||
|
|
position={selected}
|
||
|
|
onDragEnd={onMarkerDragEnd}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
</GoogleMap>
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
interface PlacesAutocompleteProps {
|
||
|
|
setSelected: (coords: { lat: number; lng: number }) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
function PlacesAutocomplete({ setSelected }: PlacesAutocompleteProps) {
|
||
|
|
const {
|
||
|
|
ready,
|
||
|
|
value,
|
||
|
|
setValue,
|
||
|
|
suggestions: { status, data },
|
||
|
|
clearSuggestions,
|
||
|
|
} = usePlacesAutocomplete();
|
||
|
|
|
||
|
|
const handleSelect = async (address: string) => {
|
||
|
|
setValue(address, false);
|
||
|
|
clearSuggestions();
|
||
|
|
|
||
|
|
try {
|
||
|
|
const results = await getGeocode({ address });
|
||
|
|
const { lat, lng } = await getLatLng(results[0]);
|
||
|
|
|
||
|
|
setSelected({ lat, lng });
|
||
|
|
console.log("Selected Lat/Lng:", { lat, lng });
|
||
|
|
Cookies.set("map_lat", `${lat}`, { expires: 1 });
|
||
|
|
Cookies.set("map_long", `${lng}`, { expires: 1 });
|
||
|
|
} catch (error) {
|
||
|
|
console.error("Error fetching coordinates:", error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Combobox onSelect={handleSelect}>
|
||
|
|
<ComboboxInput
|
||
|
|
value={value}
|
||
|
|
onChange={(e) => setValue(e.target.value)}
|
||
|
|
disabled={!ready}
|
||
|
|
placeholder="Cari Alamat"
|
||
|
|
style={{ width: "100%" }}
|
||
|
|
className="border"
|
||
|
|
height={20}
|
||
|
|
/>
|
||
|
|
<ComboboxPopover>
|
||
|
|
<ComboboxList>
|
||
|
|
{status === "OK" &&
|
||
|
|
data.map(({ place_id, description }) => (
|
||
|
|
<ComboboxOption key={place_id} value={description} />
|
||
|
|
))}
|
||
|
|
</ComboboxList>
|
||
|
|
</ComboboxPopover>
|
||
|
|
</Combobox>
|
||
|
|
);
|
||
|
|
}
|