import { LatLng } from "leaflet"; import { MapPinIcon, XIcon } from "lucide-react"; import { useEffect, useState } from "react"; import LeafletMap from "@/components/LeafletMap"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Textarea } from "@/components/ui/textarea"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Location } from "@/types/proto/api/v1/memo_service"; import { useTranslate } from "@/utils/i18n"; interface Props { location?: Location; onChange: (location?: Location) => void; } interface State { initialized: boolean; placeholder: string; position?: LatLng; latInput: string; lngInput: string; } const LocationSelector = (props: Props) => { const t = useTranslate(); const [state, setState] = useState({ initialized: false, placeholder: props.location?.placeholder || "", position: props.location ? new LatLng(props.location.latitude, props.location.longitude) : undefined, latInput: props.location ? String(props.location.latitude) : "", lngInput: props.location ? String(props.location.longitude) : "", }); const [popoverOpen, setPopoverOpen] = useState(false); useEffect(() => { setState((state) => ({ ...state, placeholder: props.location?.placeholder || "", position: new LatLng(props.location?.latitude || 0, props.location?.longitude || 0), latInput: String(props.location?.latitude) || "", lngInput: String(props.location?.longitude) || "", })); }, [props.location]); useEffect(() => { if (popoverOpen && !props.location) { const handleError = (error: any) => { setState((prev) => ({ ...prev, initialized: true })); console.error("Geolocation error:", error); }; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; setState((prev) => ({ ...prev, position: new LatLng(lat, lng), latInput: String(lat), lngInput: String(lng), initialized: true, })); }, (error) => { handleError(error); }, ); } else { handleError("Geolocation is not supported by this browser."); } } }, [popoverOpen, props.location]); useEffect(() => { if (!state.position) { setState((prev) => ({ ...prev, placeholder: "" })); return; } // Sync lat/lng input values from position const newLat = String(state.position.lat); const newLng = String(state.position.lng); if (state.latInput !== newLat || state.lngInput !== newLng) { setState((prev) => ({ ...prev, latInput: newLat, lngInput: newLng })); } // Fetch reverse geocoding data. const lat = state.position.lat; const lng = state.position.lng; fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json`) .then((response) => response.json()) .then((data) => { if (data && data.display_name) { setState((prev) => ({ ...prev, placeholder: data.display_name })); } else { // Fallback to coordinates if no display name setState((prev) => ({ ...prev, placeholder: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, })); } }) .catch((error) => { // Silent fallback: use coordinates as placeholder when geocoding fails console.error("Failed to fetch reverse geocoding data:", error); setState((prev) => ({ ...prev, placeholder: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, })); }); }, [state.position]); // Update position when lat/lng inputs change (if valid numbers) useEffect(() => { const lat = parseFloat(state.latInput); const lng = parseFloat(state.lngInput); // Validate coordinate ranges: lat must be -90 to 90, lng must be -180 to 180 if (Number.isFinite(lat) && Number.isFinite(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) { if (!state.position || state.position.lat !== lat || state.position.lng !== lng) { setState((prev) => ({ ...prev, position: new LatLng(lat, lng) })); } } }, [state.latInput, state.lngInput]); const onPositionChanged = (position: LatLng) => { setState((prev) => ({ ...prev, position })); }; const removeLocation = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); props.onChange(undefined); }; return ( {!props.location && (

{t("tooltip.select-location")}

)}
setState((prev) => ({ ...prev, latInput: e.target.value }))} className="h-9" />
setState((prev) => ({ ...prev, lngInput: e.target.value }))} className="h-9" />