"use client"; import * as React from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import useDebounce from "@/hooks/use-debounce"; import { OpenStreetMapLocation } from "@/types/types"; import getOsmEmbedUrl from "@/lib/osmEmbed"; import { Location } from "@/types/types"; // Zod schema for validation const locationFormSchema = z.object({ id: z.number().optional(), street: z.string().min(1, "Street is required"), city: z.string().min(1, "City is required"), postalCode: z.string().min(1, "Postal code is required"), latitude: z.number().optional(), longitude: z.number().optional(), }); export type LocationFormValues = z.infer; export const LocationForm: React.FC<{ location?: Location; setForm: React.Dispatch< React.SetStateAction< ReturnType> | undefined > >; }> = ({ location, setForm }) => { const [osmQuery, setOsmQuery] = React.useState(""); const [suggestions, setSuggestions] = React.useState< OpenStreetMapLocation[] >([]); const [isLoading, setIsLoading] = React.useState(false); const form = useForm({ resolver: zodResolver(locationFormSchema), defaultValues: location || { street: "", city: "", postalCode: "", }, }); React.useEffect(() => { setForm(form); }, [form, setForm]); // Fetch suggestions from OpenStreetMap Nominatim API const fetchSuggestions = async (query: string) => { if (!query || query.length < 3) { setSuggestions([]); return; } setIsLoading(true); try { const url = new URL("https://nominatim.openstreetmap.org/search"); url.searchParams.append("q", query); url.searchParams.append("format", "json"); url.searchParams.append("addressdetails", "1"); url.searchParams.append("limit", "5"); const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data: OpenStreetMapLocation[] = await response.json(); setSuggestions(data); } catch (error) { console.error("Error fetching OSM suggestions:", error); setSuggestions([]); } finally { setIsLoading(false); } }; const debouncedFetchSuggestions = useDebounce(fetchSuggestions, 300); // Handle form submission const onSubmit = (data: LocationFormValues) => { console.log("New Location:", data); // Here you can send the data to your backend (e.g., via API call) }; const longitude = form.watch("longitude"); const latitude = form.watch("latitude"); // Helper function to construct street from OSM address const getStreetFromAddress = ( address: OpenStreetMapLocation["address"], ): string => { const houseNumber = address.house_number || ""; const road = address.road || ""; return ( [houseNumber, road].filter(Boolean).join(" ").trim() || "Unknown Street" ); }; return (
{/* Autocomplete Input */} { setOsmQuery(value); debouncedFetchSuggestions(value); }} /> {isLoading && Loading...} {!isLoading && suggestions.length === 0 && osmQuery.length < 1 && ( No results found. )} {!isLoading && suggestions.length > 0 && ( {suggestions.map((suggestion) => ( { const address = suggestion.address; form.setValue( "street", getStreetFromAddress(address), ); form.setValue( "city", address.city || address.town || address.village || "", ); form.setValue( "postalCode", address.postcode || "", ); form.setValue( "latitude", parseFloat(suggestion.lat), ); form.setValue( "longitude", parseFloat(suggestion.lon), ); }} > {suggestion.display_name} ))} )} {/* Editable Fields */} ( Rue )} /> ( Ville )} /> ( Code postal )} /> {/* OSM Embed Map Preview */} {latitude && longitude && (

Prévisualisation