From cbe46330bf03f6f1bc883e83fbfe60453e7800ae Mon Sep 17 00:00:00 2001 From: Nicolay Bennett Rennemo Date: Thu, 17 Sep 2026 21:35:42 +0200 Subject: [PATCH 1/5] Created Search Bar --- src/App.tsx | 11 +++++++++- src/components/SearchBar.tsx | 38 ++++++++++++++++++++++++++++++++++ src/components/ViewCity.tsx | 7 ++++--- src/hooks/fetchCities/types.ts | 2 ++ 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/components/SearchBar.tsx diff --git a/src/App.tsx b/src/App.tsx index a1fc0a6..c6b827e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,22 @@ import "./App.css" import LocationList from "./components/location-list" import ViewCity from "./components/ViewCity" +import { useState } from "react"; +import SearchBar from "./components/SearchBar"; function App() { + + const [selectedCity, setSelectedCity] = useState("Sørumsand"); + const handleSearch = (query: string) => { + setSelectedCity(query) + } + return ( <>
- + +
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx new file mode 100644 index 0000000..ac70299 --- /dev/null +++ b/src/components/SearchBar.tsx @@ -0,0 +1,38 @@ +import { useState } from "react" + +type SearchBarProps = { + onSearch: (query: string) => void +} + +export default function SearchBar({ onSearch }: SearchBarProps) { + const [value, setValue] = useState("") + + const handleSubmit = (e: React.SubmitEvent) => { + e.preventDefault() + + if (!value.trim()) return + + onSearch(value.trim()) + } + + return ( +
+ setValue(e.target.value)} + style={{ + width: "80%", + padding: "12px 18px", + borderRadius: "999px", + border: "none", + outline: "none", + fontSize: "1rem", + backgroundColor: "#ffffff", + boxShadow: "0 4px 12px rgba(0,0,0,0.12)", + }} + /> +
+ ) +} diff --git a/src/components/ViewCity.tsx b/src/components/ViewCity.tsx index 9941306..94bf5cd 100644 --- a/src/components/ViewCity.tsx +++ b/src/components/ViewCity.tsx @@ -23,12 +23,13 @@ export default function ViewCity(arg: ViewCityProp) { const firstTimeStep = result?.properties.timeseries[0] const windSpeed = firstTimeStep?.data.instant.details?.wind_speed const temperature = firstTimeStep?.data.instant.details?.air_temperature + const country = data.result[0]?.country return (
-
Været i {arg.city}
-

Temperatur: {temperature}

-

Vindhastighet: {windSpeed}

+
Været i {arg.city}, {country}
+

Temperatur: {temperature}°C

+

Vindhastighet: {windSpeed} m/s

) } diff --git a/src/hooks/fetchCities/types.ts b/src/hooks/fetchCities/types.ts index bd79d27..f901ee1 100644 --- a/src/hooks/fetchCities/types.ts +++ b/src/hooks/fetchCities/types.ts @@ -5,6 +5,8 @@ export type citySearchResult = { longitude: number elevation: number timezone: string + country: string + country_code: string }[] } From 3b979e2e491b5984a330efcd1754002509757464 Mon Sep 17 00:00:00 2001 From: Nicolay Bennett Rennemo Date: Thu, 17 Sep 2026 22:32:13 +0200 Subject: [PATCH 2/5] Dynamic search results --- src/components/SearchBar.tsx | 103 +++++++++++++++--- .../fetchCities/useGetCityCoordinates.ts | 2 +- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index ac70299..4355172 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -1,4 +1,5 @@ import { useState } from "react" +import { useQuery } from "@tanstack/react-query" type SearchBarProps = { onSearch: (query: string) => void @@ -7,6 +8,15 @@ type SearchBarProps = { export default function SearchBar({ onSearch }: SearchBarProps) { const [value, setValue] = useState("") + const { isLoading, error, data } = useQuery({ + queryKey: ["citySuggestions", value], + queryFn: () => + fetch( + `https://geocoding-api.open-meteo.com/v1/search?name=${value}&count=4&language=no` + ).then((res) => res.json()), + enabled: value.length >= 2, + }) + const results = data?.results ?? [] const handleSubmit = (e: React.SubmitEvent) => { e.preventDefault() @@ -15,24 +25,81 @@ export default function SearchBar({ onSearch }: SearchBarProps) { onSearch(value.trim()) } + const countryCodeToFlag = (code: string) => { + if (!code) return "🌍" + + return code + .toUpperCase() + .split("") + .map((c) => String.fromCodePoint(127397 + c.charCodeAt(0))) + .join("") + } + return ( -
- setValue(e.target.value)} - style={{ - width: "80%", - padding: "12px 18px", - borderRadius: "999px", - border: "none", - outline: "none", - fontSize: "1rem", - backgroundColor: "#ffffff", - boxShadow: "0 4px 12px rgba(0,0,0,0.12)", - }} - /> -
+
+
+ setValue(e.target.value)} + style={{ + width: "80%", + padding: "12px 18px", + borderRadius: "999px", + border: "none", + outline: "none", + fontSize: "1rem", + backgroundColor: "#ffffff", + boxShadow: "0 4px 12px rgba(0,0,0,0.12)", + }} + /> +
+ + {results.length > 0 && ( +
+ {results.map((city: any) => ( + + ))} +
+ )} +
) } diff --git a/src/hooks/fetchCities/useGetCityCoordinates.ts b/src/hooks/fetchCities/useGetCityCoordinates.ts index 6199fd7..633307e 100644 --- a/src/hooks/fetchCities/useGetCityCoordinates.ts +++ b/src/hooks/fetchCities/useGetCityCoordinates.ts @@ -2,7 +2,7 @@ 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 queryString = `?name=${filter.cityName}${filter.countryCode ? `&countryCode=${filter.countryCode}` : ""}&language=no` const { isLoading, error, data } = useQuery({ queryKey: ["citySearch", filter.cityName], From 6719451069f27d4930ee87dced9839cbfb599e7e Mon Sep 17 00:00:00 2001 From: Nicolay Bennett Rennemo Date: Thu, 17 Sep 2026 22:49:48 +0200 Subject: [PATCH 3/5] Improved CSS --- src/components/SearchBar.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 4355172..e881698 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -36,8 +36,20 @@ export default function SearchBar({ onSearch }: SearchBarProps) { } return ( -
-
+
+ { - setValue(city.name) onSearch(city.name) + setValue("") }} style={{ width: "100%", From b63fd3637f0b745f55e09fa66baab35b6fcf8dd5 Mon Sep 17 00:00:00 2001 From: Nicolay Bennett Rennemo Date: Thu, 17 Sep 2026 22:55:19 +0200 Subject: [PATCH 4/5] lint --- src/components/SearchBar.tsx | 5 +++-- src/hooks/fetchCities/types.ts | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index e881698..4399445 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -1,5 +1,6 @@ import { useState } from "react" import { useQuery } from "@tanstack/react-query" +import type { quickCityResult } from "../hooks/fetchCities/types" type SearchBarProps = { onSearch: (query: string) => void @@ -8,7 +9,7 @@ type SearchBarProps = { export default function SearchBar({ onSearch }: SearchBarProps) { const [value, setValue] = useState("") - const { isLoading, error, data } = useQuery({ + const { data } = useQuery({ queryKey: ["citySuggestions", value], queryFn: () => fetch( @@ -78,7 +79,7 @@ export default function SearchBar({ onSearch }: SearchBarProps) { zIndex: 100, }} > - {results.map((city: any) => ( + {results.map((city: quickCityResult) => (