From 40e5e0cd811427c6c61745b3ab103b82065a1a2b Mon Sep 17 00:00:00 2001 From: Hasan Mohammad Fawzi Date: Fri, 18 Sep 2026 12:38:48 +0200 Subject: [PATCH 1/5] comment(docs): add deployed application link --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 4549a7d..d3acdd4 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,13 @@ Prosjekt 1 i **IT2810 Webutvikling** ved NTNU. Turvær Norge er en responsiv React-applikasjon som henter oppdaterte værdata for norske byer. Brukeren kan velge aktivitet, sammenligne byer og finne ut hvor været passer best til for eksempel gåtur, sykling eller utendørsaktiviteter. +## Publisert applikasjon + +Applikasjonen er tilgjengelig på **[http://it2810-31.idi.ntnu.no/project1/](http://it2810-31.idi.ntnu.no/project1/)**. + +> **Merk:** Nettsiden er kun tilgjengelig når du er koblet til NTNUs nettverk eller bruker [NTNU VPN](https://i.ntnu.no/wiki/-/wiki/Norsk/Installere+VPN). + + ## Kom i gang Bruk Node **24.6+** og npm **11**. Med nvm: From 7ff396570dccf79ef060d22793849777e563bd94 Mon Sep 17 00:00:00 2001 From: Hasan Mohammad Fawzi Date: Fri, 18 Sep 2026 13:24:30 +0200 Subject: [PATCH 2/5] comment(docs): add deployed application link --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d3acdd4..5f1e9d7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Applikasjonen er tilgjengelig på **[http://it2810-31.idi.ntnu.no/project1/](htt > **Merk:** Nettsiden er kun tilgjengelig når du er koblet til NTNUs nettverk eller bruker [NTNU VPN](https://i.ntnu.no/wiki/-/wiki/Norsk/Installere+VPN). - ## Kom i gang Bruk Node **24.6+** og npm **11**. Med nvm: From 89b929fa0d1c92a751f6fc8dbbcf6b7cf86a4f70 Mon Sep 17 00:00:00 2001 From: Hasan Mohammad Fawzi Date: Fri, 18 Sep 2026 14:53:58 +0200 Subject: [PATCH 3/5] fix(weather): filter cities and disable unused controls --- web/src/App.tsx | 107 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index 1783441..740b5f5 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -22,6 +22,7 @@ import type { ActivityId } from './types/activity'; const sortModes = ['default', 'best', 'temperature', 'rain', 'wind'] as const; type SortMode = (typeof sortModes)[number]; +type CityFilter = 'all' | CityId; const cityIds = new Set(cities.map(({ id }) => id)); const activitySchema = z.enum(['walking', 'cycling', 'running', 'skiing']); const sortModeSchema = z.enum(sortModes); @@ -30,6 +31,11 @@ const cityIdSchema = z .refine((id): id is CityId => cityIds.has(id as CityId)); const favoriteIdsSchema = z.array(cityIdSchema); +const cityFilterSchema = z.union([ + z.literal('all'), + cityIdSchema, +]); + function Header({ selectedCityId, activity, @@ -38,10 +44,10 @@ function Header({ onActivityChange, onSortChange, }: { - selectedCityId: CityId; + selectedCityId: CityFilter; activity: ActivityId; sortMode: SortMode; - onCityChange: (cityId: CityId) => void; + onCityChange: (cityId: CityFilter) => void; onActivityChange: (activity: ActivityId) => void; onSortChange: (sortMode: SortMode) => void; }) { @@ -61,8 +67,12 @@ function Header({ By - onActivityChange(event.target.value as ActivityId) - } - > - {activities.map((activityOption) => ( - - ))} - + value={activity} + disabled={selectedCityId !== 'all' || sortMode !== 'best'} + aria-label="Velg aktivitet" + title={ + sortMode !== 'best' + ? 'Velg «Best vær for aktivitet» for å velge aktivitet' + : undefined + } + onChange={(event) => + onActivityChange(event.target.value as ActivityId) + } + > + {activities.map((activityOption) => ( + + ))} + + Sorter etter + + @@ -340,11 +360,12 @@ function MainWeather({ } export function App() { - const [selectedCityId, setSelectedCityId] = useStoredState( - 'sessionStorage', - 't31-selected-city', - cityIdSchema, - exampleCity.id, + const [selectedCityId, setSelectedCityId] = + useStoredState( + 'sessionStorage', + 't31-selected-city', + cityFilterSchema, + 'all', ); const [activity, setActivity] = useStoredState( 'sessionStorage', @@ -421,6 +442,12 @@ export function App() { ...item, rank: sortMode === 'best' && Number.isFinite(item.score) ? index + 1 : null, })); + const visibleItems = + selectedCityId === 'all' + ? rankedItems + : rankedItems.filter( + (item) => item.city.id === selectedCityId, + ); const detailCity = cities.find(({ id }) => id === detailCityId) ?? null; const detailItem = rankedItems.find(({ city }) => city.id === detailCityId); const toggleFavorite = (city: City) => @@ -434,19 +461,27 @@ export function App() { <>
{ + setSelectedCityId(cityId); + + if (cityId !== 'all') { + setSortMode('default'); + } + }} onActivityChange={setActivity} onSortChange={setSortMode} /> setSelectedCityId(city.id)} - onOpenDetails={(city) => { + onCitySelect={(city) => { setSelectedCityId(city.id); + setSortMode('default'); + }} + onOpenDetails={(city) => { setDetailCityId(city.id); }} onToggleFavorite={toggleFavorite} From 52d44cddbe525a374461bbe90230017e630b700b Mon Sep 17 00:00:00 2001 From: olestang Date: Fri, 18 Sep 2026 16:44:08 +0200 Subject: [PATCH 4/5] fix: removed best for aktivitet in details overlay, unless sorted by best for aktivitet --- web/src/App.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index 740b5f5..3052267 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -295,12 +295,14 @@ function MainWeather({ city, weather, activity, + sortMode, feedback, onClose, }: { city: City; weather: CityWeather | null; activity: ActivityId; + sortMode: SortMode, feedback: ReactNode; onClose: () => void; }) { @@ -315,9 +317,11 @@ function MainWeather({

Været i {city.name}

-

- Vurdert for {getActivityName(activity).toLowerCase()}. -

+ {sortMode === 'best' && ( +

+ Vurdert for {getActivityName(activity).toLowerCase()}. +

+ )} {feedback} {weather && ( <> @@ -493,6 +497,7 @@ export function App() { city={detailCity} weather={detailItem?.weather ?? null} activity={activity} + sortMode={sortMode} feedback={detailItem?.feedback} onClose={() => setDetailCityId(null)} /> From ed23517fd60efc822d6a4d7a75ba405211f8b4f9 Mon Sep 17 00:00:00 2001 From: olestang Date: Fri, 18 Sep 2026 16:47:45 +0200 Subject: [PATCH 5/5] fix: run prettier to fix failing CI quality check --- web/src/App.tsx | 93 +++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index 3052267..2be28ec 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -31,10 +31,7 @@ const cityIdSchema = z .refine((id): id is CityId => cityIds.has(id as CityId)); const favoriteIdsSchema = z.array(cityIdSchema); -const cityFilterSchema = z.union([ - z.literal('all'), - cityIdSchema, -]); +const cityFilterSchema = z.union([z.literal('all'), cityIdSchema]); function Header({ selectedCityId, @@ -83,41 +80,39 @@ function Header({ + Sorter etter + +
@@ -302,7 +297,7 @@ function MainWeather({ city: City; weather: CityWeather | null; activity: ActivityId; - sortMode: SortMode, + sortMode: SortMode; feedback: ReactNode; onClose: () => void; }) { @@ -319,7 +314,8 @@ function MainWeather({ {sortMode === 'best' && (

- Vurdert for {getActivityName(activity).toLowerCase()}. + Vurdert for {getActivityName(activity).toLowerCase()} + .

)} {feedback} @@ -364,12 +360,11 @@ function MainWeather({ } export function App() { - const [selectedCityId, setSelectedCityId] = - useStoredState( - 'sessionStorage', - 't31-selected-city', - cityFilterSchema, - 'all', + const [selectedCityId, setSelectedCityId] = useStoredState( + 'sessionStorage', + 't31-selected-city', + cityFilterSchema, + 'all', ); const [activity, setActivity] = useStoredState( 'sessionStorage', @@ -447,11 +442,9 @@ export function App() { rank: sortMode === 'best' && Number.isFinite(item.score) ? index + 1 : null, })); const visibleItems = - selectedCityId === 'all' - ? rankedItems - : rankedItems.filter( - (item) => item.city.id === selectedCityId, - ); + selectedCityId === 'all' + ? rankedItems + : rankedItems.filter((item) => item.city.id === selectedCityId); const detailCity = cities.find(({ id }) => id === detailCityId) ?? null; const detailItem = rankedItems.find(({ city }) => city.id === detailCityId); const toggleFavorite = (city: City) =>