From 3936b326d7b73f554dc16e18638c5c15be16035a Mon Sep 17 00:00:00 2001 From: Kristian Date: Tue, 15 Sep 2026 19:58:33 +0200 Subject: [PATCH 1/4] get weather from 4 cities --- "v\303\246r/package-lock.json" | 43 ++----- "v\303\246r/src/App.tsx" | 119 +----------------- "v\303\246r/src/components/listItem.tsx" | 16 +++ "v\303\246r/src/components/location-list.tsx" | 51 ++++++++ 4 files changed, 83 insertions(+), 146 deletions(-) create mode 100644 "v\303\246r/src/components/listItem.tsx" create mode 100644 "v\303\246r/src/components/location-list.tsx" diff --git "a/v\303\246r/package-lock.json" "b/v\303\246r/package-lock.json" index 3b7ca00..451fa35 100644 --- "a/v\303\246r/package-lock.json" +++ "b/v\303\246r/package-lock.json" @@ -58,6 +58,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -679,9 +680,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -699,9 +697,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -719,9 +714,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -739,9 +731,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -759,9 +748,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -779,9 +765,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -902,6 +885,7 @@ "integrity": "sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -912,6 +896,7 @@ "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -971,6 +956,7 @@ "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", @@ -1205,6 +1191,7 @@ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1295,6 +1282,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.23", "caniuse-lite": "^1.0.30001810", @@ -1444,6 +1432,7 @@ "integrity": "sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==", "dev": true, "license": "MIT", + "peer": true, "workspaces": [ "packages/*" ], @@ -1899,6 +1888,7 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2060,9 +2050,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2084,9 +2071,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2108,9 +2092,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2132,9 +2113,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2358,6 +2336,7 @@ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2439,6 +2418,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -2587,6 +2567,7 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2673,6 +2654,7 @@ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", @@ -2797,6 +2779,7 @@ "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git "a/v\303\246r/src/App.tsx" "b/v\303\246r/src/App.tsx" index 8ef557e..a1a048b 100644 --- "a/v\303\246r/src/App.tsx" +++ "b/v\303\246r/src/App.tsx" @@ -1,124 +1,11 @@ -import { useState } from 'react' -import heroImg from './assets/hero.png' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' import './App.css' -import useGetWeather from './hooks/fetchWeather/useGetWeather' +import LocationList from './components/location-list' function App() { - const [count, setCount] = useState(0) - // Example fetch, plz remove asap - const { result } = useGetWeather(67, 67, 420) - return ( <> -
-
- - React logo - Vite logo -
-
- {JSON.stringify(result)} -

Get started

-

- Edit src/App.tsx and save to test HMR -

-
- -
- -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
+

Weather App

+ ) } diff --git "a/v\303\246r/src/components/listItem.tsx" "b/v\303\246r/src/components/listItem.tsx" new file mode 100644 index 0000000..fb99ff8 --- /dev/null +++ "b/v\303\246r/src/components/listItem.tsx" @@ -0,0 +1,16 @@ + + + +function listItem({ item }) { + + return ( +
  • +

    {item.name}

    +
    + {item.temperature}° +
    +
  • + ) +} + +export default listItem diff --git "a/v\303\246r/src/components/location-list.tsx" "b/v\303\246r/src/components/location-list.tsx" new file mode 100644 index 0000000..b18f3ad --- /dev/null +++ "b/v\303\246r/src/components/location-list.tsx" @@ -0,0 +1,51 @@ +import type { WeatherApiResponse } from '../hooks/fetchWeather/types' +import useGetWeather from '../hooks/fetchWeather/useGetWeather' + +type City = { + name: string + lat: number + lon: number +} + +const cities: City[] = [ + { name: 'Oslo', lat: 59.91273, lon: 10.74609 }, + { name: 'Sørumsand', lat: 59.98621, lon: 11.24154 }, + { name: 'Trondheim', lat: 63.41667, lon: 10.41667 }, + { name: 'Las Vegas', lat: 36.1699, lon: -115.1398 }, +] + +const getTemperature = (data?: WeatherApiResponse) => + data?.properties?.timeseries?.[0]?.data?.instant?.details?.air_temperature + + +function LocationList() { + const weatherResults = cities.map((city) => useGetWeather(city.lat, city.lon)) + + + if (weatherResults.some((result) => result.isLoading)) { + return
    Loading weather…
    + } + + if (weatherResults.some((result) => result.error)) { + return
    Could not load weather data.
    + } + + const cityWeather = cities.map((city, index) => ({ + name: city.name, + temperature: getTemperature(weatherResults[index]?.result), + })) + + return ( +
    +
      + {cityWeather.map((item) => ( +
    • + {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°C` : '—'} +
    • + ))} +
    +
    + ) +} + +export default LocationList From aa4956d60b87f1b027c2d54585f5abb8782409a8 Mon Sep 17 00:00:00 2001 From: Kristian Date: Thu, 17 Sep 2026 17:42:38 +0200 Subject: [PATCH 2/4] get temperatures for next 3 days --- "v\303\246r/src/components/listItem.tsx" | 27 +++++++++++---- "v\303\246r/src/components/location-list.tsx" | 33 ++++++++++++++++--- "v\303\246r/src/index.css" | 5 +++ 3 files changed, 54 insertions(+), 11 deletions(-) diff --git "a/v\303\246r/src/components/listItem.tsx" "b/v\303\246r/src/components/listItem.tsx" index fb99ff8..edb4d6d 100644 --- "a/v\303\246r/src/components/listItem.tsx" +++ "b/v\303\246r/src/components/listItem.tsx" @@ -1,15 +1,28 @@ +type cityWeather = { + name: string + temperature?: number + precipitation?: number + nextDays?: { time: string; temperature: number | undefined}[] +} + -function listItem({ item }) { +const listItem = ({ city: item }: { city: cityWeather }) => { return ( -
  • -

    {item.name}

    -
    - {item.temperature}° -
    -
  • +
  • + {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°` : '—'} + {typeof item.precipitation === 'number' && item.precipitation > 0 ? `, ${item.precipitation} mm` : ''} +
    + {item.nextDays?.map((day) => ( + + {typeof day.temperature === 'number' ? `${day.temperature}°` : '—'} + + ))} + +
    +
  • ) } diff --git "a/v\303\246r/src/components/location-list.tsx" "b/v\303\246r/src/components/location-list.tsx" index b18f3ad..4055c21 100644 --- "a/v\303\246r/src/components/location-list.tsx" +++ "b/v\303\246r/src/components/location-list.tsx" @@ -1,5 +1,6 @@ import type { WeatherApiResponse } from '../hooks/fetchWeather/types' import useGetWeather from '../hooks/fetchWeather/useGetWeather' +import ListItem from './listItem' type City = { name: string @@ -17,10 +18,30 @@ const cities: City[] = [ const getTemperature = (data?: WeatherApiResponse) => data?.properties?.timeseries?.[0]?.data?.instant?.details?.air_temperature +const getPrecipitation = (data?: WeatherApiResponse) => + data?.properties?.timeseries?.[0]?.data?.next_1_hours?.details?.precipitation_amount -function LocationList() { +const getNextDays = (data?: WeatherApiResponse) => { + const now = new Date() + + const noonTemperatures = data?.properties.timeseries + .filter((entry) => { + const date = new Date(entry.time) + return date >= now && date.getUTCHours() === 12 && date < new Date(now.getTime() + 3* 24 * 60 * 60 * 1000) + }) + .map((entry) => ({ + time: entry.time, + temperature: entry.data.instant.details?.air_temperature, + })) + + return noonTemperatures +} + + +const LocationList = () => { const weatherResults = cities.map((city) => useGetWeather(city.lat, city.lon)) + console.log(weatherResults) if (weatherResults.some((result) => result.isLoading)) { return
    Loading weather…
    @@ -33,15 +54,19 @@ function LocationList() { const cityWeather = cities.map((city, index) => ({ name: city.name, temperature: getTemperature(weatherResults[index]?.result), + precipitation: getPrecipitation(weatherResults[index]?.result), + nextDays: getNextDays(weatherResults[index]?.result), })) return (
      {cityWeather.map((item) => ( -
    • - {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°C` : '—'} -
    • + + //
    • + // {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°` : '—'} + // {typeof item.precipitation === 'number' ? `, ${item.precipitation} mm` : ''} + //
    • ))}
    diff --git "a/v\303\246r/src/index.css" "b/v\303\246r/src/index.css" index 5fb3313..f2a4117 100644 --- "a/v\303\246r/src/index.css" +++ "b/v\303\246r/src/index.css" @@ -109,3 +109,8 @@ code { padding: 4px 8px; background: var(--code-bg); } + +ul { + list-style-type: none; /* Removes the dots */ + padding: 0; /* Removes default left indentation */ +} \ No newline at end of file From e29c3f8706ba36f2930c01c23bbfb110da72ed15 Mon Sep 17 00:00:00 2001 From: Kristian Date: Thu, 17 Sep 2026 18:43:09 +0200 Subject: [PATCH 3/4] make it look nice --- "v\303\246r/src/components/listItem.tsx" | 17 +++++++++++----- "v\303\246r/src/components/location-list.tsx" | 4 ++-- "v\303\246r/src/index.css" | 20 ------------------- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git "a/v\303\246r/src/components/listItem.tsx" "b/v\303\246r/src/components/listItem.tsx" index edb4d6d..f523955 100644 --- "a/v\303\246r/src/components/listItem.tsx" +++ "b/v\303\246r/src/components/listItem.tsx" @@ -11,13 +11,20 @@ type cityWeather = { const listItem = ({ city: item }: { city: cityWeather }) => { return ( -
  • - {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°` : '—'} - {typeof item.precipitation === 'number' && item.precipitation > 0 ? `, ${item.precipitation} mm` : ''} +
  • + + {item.name}: + + {typeof item.precipitation === 'number' && item.precipitation > 0.6 ? '🌧️' : '⛅'} + +

    {typeof item.temperature === 'number' ? `${item.temperature.toFixed()}°` : '—'}

    +
    + +
    {item.nextDays?.map((day) => ( - - {typeof day.temperature === 'number' ? `${day.temperature}°` : '—'} + + {typeof day.temperature === 'number' ? `${day.temperature.toFixed()}°` : '—'} ))} diff --git "a/v\303\246r/src/components/location-list.tsx" "b/v\303\246r/src/components/location-list.tsx" index 4055c21..8b350d9 100644 --- "a/v\303\246r/src/components/location-list.tsx" +++ "b/v\303\246r/src/components/location-list.tsx" @@ -19,7 +19,7 @@ const getTemperature = (data?: WeatherApiResponse) => data?.properties?.timeseries?.[0]?.data?.instant?.details?.air_temperature const getPrecipitation = (data?: WeatherApiResponse) => - data?.properties?.timeseries?.[0]?.data?.next_1_hours?.details?.precipitation_amount + data?.properties?.timeseries?.[0]?.data?.next_6_hours?.details?.precipitation_amount const getNextDays = (data?: WeatherApiResponse) => { const now = new Date() @@ -60,7 +60,7 @@ const LocationList = () => { return (
    -
      +
        {cityWeather.map((item) => ( //
      • diff --git "a/v\303\246r/src/index.css" "b/v\303\246r/src/index.css" index f2a4117..094bb6c 100644 --- "a/v\303\246r/src/index.css" +++ "b/v\303\246r/src/index.css" @@ -30,26 +30,6 @@ } } -@media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: - rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } - - #social .button-icon { - filter: invert(1) brightness(2); - } -} - #root { width: 1126px; max-width: 100%; From b3529e51eed540367ddf847b2a132db7e83dc2dd Mon Sep 17 00:00:00 2001 From: Kristian Date: Thu, 17 Sep 2026 18:57:43 +0200 Subject: [PATCH 4/4] titles and spacing --- "v\303\246r/src/components/listItem.tsx" | 2 +- "v\303\246r/src/components/location-list.tsx" | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git "a/v\303\246r/src/components/listItem.tsx" "b/v\303\246r/src/components/listItem.tsx" index f523955..91f3436 100644 --- "a/v\303\246r/src/components/listItem.tsx" +++ "b/v\303\246r/src/components/listItem.tsx" @@ -21,7 +21,7 @@ const listItem = ({ city: item }: { city: cityWeather }) => { -
        +
        {item.nextDays?.map((day) => ( {typeof day.temperature === 'number' ? `${day.temperature.toFixed()}°` : '—'} diff --git "a/v\303\246r/src/components/location-list.tsx" "b/v\303\246r/src/components/location-list.tsx" index 8b350d9..d28f8ff 100644 --- "a/v\303\246r/src/components/location-list.tsx" +++ "b/v\303\246r/src/components/location-list.tsx" @@ -59,10 +59,16 @@ const LocationList = () => { })) return ( -
        -
          +
          +
          + City + Next 3 Days +
          +
          + +
            {cityWeather.map((item) => ( - + //
          • // {item.name}: {typeof item.temperature === 'number' ? `${item.temperature}°` : '—'} // {typeof item.precipitation === 'number' ? `, ${item.precipitation} mm` : ''}