import type { NextPage } from "next"; import { useState } from "react"; import { useDebounce } from "react-use"; import z from "zod"; import Input from "~/components/custom_ui/input"; import { LoadingPage } from "~/components/loading"; import { Button } from "~/components/ui/button"; type NominatimResponse = z.infer; export const responseSchema = z.object({ type: z.string(), geocoding: z.object({ version: z.string(), attribution: z.string(), licence: z.string(), query: z.string(), }), features: z.array( z.object({ type: z.string(), properties: z.object({ geocoding: z.object({ place_id: z.number(), osm_type: z.string(), osm_id: z.number(), osm_key: z.string(), osm_value: z.string(), type: z.string(), label: z.string(), name: z.string(), postcode: z.string().optional(), county: z.string().optional(), state: z.string().optional(), country: z.string(), country_code: z.string(), admin: z.any(), district: z.string().optional(), city: z.string().optional(), }), }), geometry: z.object({ type: z.string(), coordinates: z.array(z.number()), }), }), ), }); const Test: NextPage = () => { const [value, setValue] = useState(""); const [isLoading, setLoading] = useState(false); const [options, setOptions] = useState([]); useDebounce( () => { if (value !== "") { handleFetch(); } }, 1000, [value], ); const handleFetch = () => { setLoading(true); const api = `https://nominatim.openstreetmap.org/search?format=geocodejson&limit=5&addressdetails=1&q=${encodeURI( value, )}`; fetch(api) .then((response) => response.json()) .then((data) => { const parsed = responseSchema.safeParse(data); if (parsed.success) { const filtered = parsed.data.features.filter( (f) => f.properties.geocoding.osm_type === "way" && f.properties.geocoding.type === "street" && f.properties.geocoding.postcode, ); // Then deduplicate by city const uniqueResults = filtered.reduce( (acc, current) => { const city = current.properties.geocoding.city; // If this city doesn't exist or we haven't seen it yet if ( !city || !acc.some((item) => item.properties.geocoding.city === city) ) { acc.push(current); } return acc; }, [] as typeof filtered, ); setOptions(uniqueResults); } else { console.error("Failed to fetch location data", parsed.error); } setLoading(false); }) .catch((error) => { console.error(error); }); }; return (
{ setValue(currentTarget.value); }} value={value} /> {isLoading ? ( ) : ( options.map((option, idx) => ( )) )}
); }; export default Test;