From 04c62984203516a15ee60e92ad59c40247cd7ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Gr=C3=A6sberg?= Date: Thu, 17 Sep 2026 19:40:53 +0200 Subject: [PATCH 1/2] Add filtering by country code to city search --- package-lock.json | 23 +- package.json | 1 + src/components/ViewCity.tsx | 5 +- src/hooks/fetchCities/types.ts | 268 ++++++++++++++++++ .../fetchCities/useGetCityCoordinates.ts | 20 ++ src/hooks/useGetCityCoordinates.ts | 27 -- 6 files changed, 302 insertions(+), 42 deletions(-) create mode 100644 src/hooks/fetchCities/types.ts create mode 100644 src/hooks/fetchCities/useGetCityCoordinates.ts delete mode 100644 src/hooks/useGetCityCoordinates.ts diff --git a/package-lock.json b/package-lock.json index 9b4d0fb..c06388b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@tanstack/react-query": "^5.102.8", + "country-codes-list": "^3.2.0", "react": "^19.2.8", "react-dom": "^19.2.8" }, @@ -59,7 +60,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -886,7 +886,6 @@ "integrity": "sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -897,7 +896,6 @@ "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -957,7 +955,6 @@ "integrity": "sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.70.0", "@typescript-eslint/types": "8.70.0", @@ -1192,7 +1189,6 @@ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1283,7 +1279,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.23", "caniuse-lite": "^1.0.30001810", @@ -1340,6 +1335,15 @@ "dev": true, "license": "MIT" }, + "node_modules/country-codes-list": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/country-codes-list/-/country-codes-list-3.2.0.tgz", + "integrity": "sha512-gR0IFGPRtqOSifQfFYlUodjJLCuRg36wBCYRpR81rZ2Z2swwVZF9v1If2z18bWuhXmOZhmIw9+L/Ikw1ojpkNw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1433,7 +1437,6 @@ "integrity": "sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==", "dev": true, "license": "MIT", - "peer": true, "workspaces": [ "packages/*" ], @@ -1889,7 +1892,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2337,7 +2339,6 @@ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2435,7 +2436,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2584,7 +2584,6 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2671,7 +2670,6 @@ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", @@ -2796,7 +2794,6 @@ "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 1782863..4c50b2a 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@tanstack/react-query": "^5.102.8", + "country-codes-list": "^3.2.0", "react": "^19.2.8", "react-dom": "^19.2.8" }, diff --git a/src/components/ViewCity.tsx b/src/components/ViewCity.tsx index f11f128..a3f03bb 100644 --- a/src/components/ViewCity.tsx +++ b/src/components/ViewCity.tsx @@ -1,12 +1,13 @@ import useGetWeather from "../hooks/fetchWeather/useGetWeather" -import useGetCityCoordinates from "../hooks/useGetCityCoordinates" +import useGetCityCoordinates from "../hooks/fetchCities/useGetCityCoordinates" interface ViewCityProp { city: string } export default function ViewCity(arg: ViewCityProp) { - const data = useGetCityCoordinates(arg.city) + const cityFilter = {cityName: arg.city} + const data = useGetCityCoordinates(cityFilter) const { isLoading, error, result } = useGetWeather( data.result[0]?.latitude, diff --git a/src/hooks/fetchCities/types.ts b/src/hooks/fetchCities/types.ts new file mode 100644 index 0000000..e1685ec --- /dev/null +++ b/src/hooks/fetchCities/types.ts @@ -0,0 +1,268 @@ +export type citySearchResult = { + results: { + name: string + latitude: number + longitude: number + elevation: number + timezone: string + }[] +} + +export const countryCodes = [ + "AF", + "AX", + "AL", + "DZ", + "AS", + "AD", + "AO", + "AI", + "AQ", + "AG", + "AR", + "AM", + "AW", + "AU", + "AT", + "AZ", + "BH", + "BD", + "BB", + "BY", + "BE", + "BZ", + "BJ", + "BM", + "BT", + "BO", + "BQ", + "BA", + "BW", + "BV", + "BR", + "IO", + "BN", + "BG", + "BF", + "BI", + "CV", + "KH", + "CM", + "CA", + "KY", + "CF", + "TD", + "CL", + "CN", + "CX", + "CC", + "CO", + "BS", + "MP", + "KM", + "CK", + "CR", + "CI", + "HR", + "CU", + "CW", + "CY", + "CZ", + "CD", + "DK", + "DJ", + "DM", + "DO", + "EC", + "EG", + "SV", + "GQ", + "ER", + "EE", + "SZ", + "ET", + "FK", + "FO", + "FJ", + "FI", + "FR", + "GF", + "PF", + "TF", + "GA", + "GM", + "GE", + "DE", + "GH", + "GI", + "GR", + "GL", + "GD", + "GP", + "GU", + "GT", + "GG", + "GN", + "GW", + "GY", + "HT", + "VA", + "HN", + "HK", + "HU", + "IS", + "IN", + "ID", + "IR", + "IQ", + "IE", + "IM", + "IL", + "IT", + "JM", + "JP", + "JE", + "JO", + "KZ", + "KE", + "KI", + "KW", + "KG", + "LA", + "LV", + "LB", + "LS", + "LR", + "LY", + "LI", + "LT", + "LU", + "MO", + "MG", + "MW", + "MY", + "MV", + "ML", + "MT", + "MQ", + "MR", + "MU", + "YT", + "MX", + "FM", + "MC", + "MN", + "ME", + "MS", + "MA", + "MZ", + "MM", + "NA", + "NR", + "NP", + "NL", + "NC", + "NZ", + "NI", + "NE", + "NG", + "NU", + "NF", + "KP", + "MK", + "NO", + "OM", + "PK", + "PW", + "PS", + "PA", + "PG", + "PY", + "PE", + "PH", + "PN", + "PL", + "PT", + "PR", + "QA", + "MD", + "SM", + "CG", + "MH", + "RE", + "RO", + "RU", + "RW", + "BL", + "SH", + "KN", + "LC", + "MF", + "PM", + "VC", + "WS", + "ST", + "SA", + "SN", + "RS", + "SC", + "SL", + "SG", + "SX", + "SK", + "SI", + "SB", + "SO", + "ZA", + "GS", + "KR", + "SS", + "ES", + "LK", + "SD", + "SR", + "SJ", + "SE", + "CH", + "SY", + "TW", + "TJ", + "HM", + "TH", + "TL", + "TG", + "TK", + "TO", + "TT", + "TN", + "TR", + "TM", + "TC", + "TV", + "UG", + "UA", + "AE", + "GB", + "TZ", + "UM", + "US", + "UY", + "UZ", + "VU", + "VE", + "VN", + "VG", + "VI", + "WF", + "EH", + "YE", + "ZM", + "ZW", +] as const + +export type CountryCode = (typeof countryCodes)[number] + +export type citySearchFilter = { + cityName: string + countryCode?: CountryCode +} diff --git a/src/hooks/fetchCities/useGetCityCoordinates.ts b/src/hooks/fetchCities/useGetCityCoordinates.ts new file mode 100644 index 0000000..6199fd7 --- /dev/null +++ b/src/hooks/fetchCities/useGetCityCoordinates.ts @@ -0,0 +1,20 @@ +import { useQuery } from "@tanstack/react-query" +import type { citySearchResult, citySearchFilter } from "./types" + +const useGetCityCoordinates = (filter: citySearchFilter) => { + const queryString = `?name=${filter.cityName}${filter.countryCode ? `&countryCode=${filter.countryCode}` : ""}` + + const { isLoading, error, data } = useQuery({ + queryKey: ["citySearch", filter.cityName], + queryFn: () => + fetch( + `https://geocoding-api.open-meteo.com/v1/search${queryString}` + ).then((res) => res.json() as Promise), + }) + + const result = data?.results ?? [] + + return { isLoading, error, result } +} + +export default useGetCityCoordinates diff --git a/src/hooks/useGetCityCoordinates.ts b/src/hooks/useGetCityCoordinates.ts deleted file mode 100644 index ffa0b74..0000000 --- a/src/hooks/useGetCityCoordinates.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useQuery } from "@tanstack/react-query" - -export type citySearchResult = { - results: { - name: string - latitude: number - longitude: number - elevation: number - timezone: string - }[] -} - -const useGetCityCoordinates = (cityName: string) => { - const { isLoading, error, data } = useQuery({ - queryKey: ["citySearch", cityName], - queryFn: () => - fetch( - `https://geocoding-api.open-meteo.com/v1/search?name=${cityName}` - ).then((res) => res.json() as Promise), - }) - - const result = data?.results ?? [] - - return { isLoading, error, result } -} - -export default useGetCityCoordinates From bfa6fde223ddbc0ec49e656292b6dd7cbef71475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Gr=C3=A6sberg?= Date: Thu, 17 Sep 2026 19:53:11 +0200 Subject: [PATCH 2/2] Remove country codes that API does not support --- src/components/ViewCity.tsx | 2 +- src/hooks/fetchCities/types.ts | 223 +-------------------------------- 2 files changed, 2 insertions(+), 223 deletions(-) diff --git a/src/components/ViewCity.tsx b/src/components/ViewCity.tsx index a3f03bb..9941306 100644 --- a/src/components/ViewCity.tsx +++ b/src/components/ViewCity.tsx @@ -6,7 +6,7 @@ interface ViewCityProp { } export default function ViewCity(arg: ViewCityProp) { - const cityFilter = {cityName: arg.city} + const cityFilter = { cityName: arg.city } const data = useGetCityCoordinates(cityFilter) const { isLoading, error, result } = useGetWeather( diff --git a/src/hooks/fetchCities/types.ts b/src/hooks/fetchCities/types.ts index e1685ec..bd79d27 100644 --- a/src/hooks/fetchCities/types.ts +++ b/src/hooks/fetchCities/types.ts @@ -9,255 +9,34 @@ export type citySearchResult = { } export const countryCodes = [ - "AF", - "AX", - "AL", - "DZ", - "AS", - "AD", - "AO", - "AI", - "AQ", - "AG", - "AR", - "AM", - "AW", "AU", "AT", "AZ", - "BH", - "BD", - "BB", - "BY", "BE", - "BZ", - "BJ", - "BM", - "BT", - "BO", - "BQ", - "BA", - "BW", - "BV", "BR", - "IO", - "BN", - "BG", - "BF", - "BI", - "CV", - "KH", - "CM", "CA", - "KY", - "CF", - "TD", - "CL", "CN", - "CX", - "CC", - "CO", - "BS", - "MP", - "KM", - "CK", - "CR", - "CI", - "HR", - "CU", - "CW", - "CY", - "CZ", - "CD", "DK", - "DJ", - "DM", - "DO", - "EC", - "EG", - "SV", - "GQ", - "ER", - "EE", - "SZ", - "ET", - "FK", - "FO", - "FJ", "FI", "FR", - "GF", - "PF", - "TF", - "GA", - "GM", - "GE", "DE", - "GH", - "GI", "GR", - "GL", - "GD", - "GP", - "GU", - "GT", - "GG", - "GN", - "GW", - "GY", - "HT", - "VA", - "HN", - "HK", "HU", - "IS", - "IN", - "ID", - "IR", - "IQ", "IE", - "IM", - "IL", "IT", - "JM", "JP", - "JE", - "JO", - "KZ", - "KE", - "KI", - "KW", - "KG", - "LA", - "LV", - "LB", - "LS", - "LR", - "LY", - "LI", - "LT", - "LU", - "MO", - "MG", - "MW", - "MY", - "MV", - "ML", - "MT", - "MQ", - "MR", - "MU", - "YT", "MX", - "FM", - "MC", - "MN", - "ME", - "MS", - "MA", - "MZ", - "MM", - "NA", - "NR", - "NP", "NL", - "NC", - "NZ", - "NI", - "NE", - "NG", - "NU", - "NF", - "KP", - "MK", "NO", - "OM", - "PK", - "PW", - "PS", - "PA", - "PG", - "PY", - "PE", - "PH", - "PN", "PL", "PT", - "PR", - "QA", - "MD", - "SM", - "CG", - "MH", - "RE", - "RO", "RU", - "RW", - "BL", - "SH", - "KN", - "LC", - "MF", - "PM", - "VC", - "WS", - "ST", - "SA", - "SN", - "RS", - "SC", - "SL", - "SG", - "SX", - "SK", - "SI", - "SB", - "SO", - "ZA", - "GS", - "KR", - "SS", "ES", - "LK", - "SD", - "SR", - "SJ", "SE", "CH", - "SY", - "TW", - "TJ", - "HM", - "TH", - "TL", - "TG", - "TK", - "TO", - "TT", - "TN", "TR", - "TM", - "TC", - "TV", - "UG", - "UA", - "AE", - "GB", - "TZ", - "UM", + "UK", "US", - "UY", - "UZ", - "VU", - "VE", - "VN", - "VG", - "VI", - "WF", - "EH", - "YE", - "ZM", - "ZW", ] as const export type CountryCode = (typeof countryCodes)[number]