diff --git "a/v\303\246r/package-lock.json" "b/v\303\246r/package-lock.json" index f28570c..9b4d0fb 100644 --- "a/v\303\246r/package-lock.json" +++ "b/v\303\246r/package-lock.json" @@ -59,6 +59,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -680,9 +681,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -700,9 +698,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -720,9 +715,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -740,9 +732,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -760,9 +749,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -780,9 +766,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -903,6 +886,7 @@ "integrity": "sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -913,6 +897,7 @@ "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -972,6 +957,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", @@ -1206,6 +1192,7 @@ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1296,6 +1283,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.23", "caniuse-lite": "^1.0.30001810", @@ -1445,6 +1433,7 @@ "integrity": "sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==", "dev": true, "license": "MIT", + "peer": true, "workspaces": [ "packages/*" ], @@ -1900,6 +1889,7 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2061,9 +2051,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2085,9 +2072,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2109,9 +2093,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2133,9 +2114,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2359,6 +2337,7 @@ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2456,6 +2435,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" } @@ -2604,6 +2584,7 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2690,6 +2671,7 @@ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", @@ -2814,6 +2796,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 4da300d..6f346a9 100644 --- "a/v\303\246r/src/App.tsx" +++ "b/v\303\246r/src/App.tsx" @@ -1,17 +1,14 @@ -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 LocationList from './components/location-list' import ViewCity from './components/ViewCity' import useGetWeather from './hooks/fetchWeather/useGetWeather' function App() { - const [count, setCount] = useState(0) return ( <>
+
diff --git "a/v\303\246r/src/components/listItem.tsx" "b/v\303\246r/src/components/listItem.tsx" new file mode 100644 index 0000000..91f3436 --- /dev/null +++ "b/v\303\246r/src/components/listItem.tsx" @@ -0,0 +1,36 @@ + + +type cityWeather = { + name: string + temperature?: number + precipitation?: number + nextDays?: { time: string; temperature: number | undefined}[] +} + + +const listItem = ({ city: item }: { city: cityWeather }) => { + + return ( +
  • + + {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.toFixed()}°` : '—'} + + ))} + +
    +
  • + ) +} + +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..d28f8ff --- /dev/null +++ "b/v\303\246r/src/components/location-list.tsx" @@ -0,0 +1,82 @@ +import type { WeatherApiResponse } from '../hooks/fetchWeather/types' +import useGetWeather from '../hooks/fetchWeather/useGetWeather' +import ListItem from './listItem' + +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 + +const getPrecipitation = (data?: WeatherApiResponse) => + data?.properties?.timeseries?.[0]?.data?.next_6_hours?.details?.precipitation_amount + +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…
    + } + + 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), + precipitation: getPrecipitation(weatherResults[index]?.result), + nextDays: getNextDays(weatherResults[index]?.result), + })) + + return ( +
    +
    + City + Next 3 Days +
    +
    + + +
    + ) +} + +export default LocationList diff --git "a/v\303\246r/src/index.css" "b/v\303\246r/src/index.css" index 5fb3313..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%; @@ -109,3 +89,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