1- const form = document . getElementById ( 'form' ) ; const city = document . getElementById ( 'city' ) ; const out = document . getElementById ( 'out' ) ;
1+ const form = document . getElementById ( 'form' ) ;
2+ const city = document . getElementById ( 'city' ) ;
3+ const out = document . getElementById ( 'out' ) ;
4+ const geoBtn = document . getElementById ( 'geoBtn' ) ;
5+ const unitBtn = document . getElementById ( 'unitBtn' ) ;
6+
7+ // State
8+ let unit = 'C' ; // C or F
9+ const cache = { } ;
10+ const CACHE_DURATION = 30 * 60 * 1000 ; // 30 minutes
11+
12+ // Weather icons mapping
13+ const weatherIcons = {
14+ 'Sunny' : '☀️' ,
15+ 'Clear' : '🌙' ,
16+ 'Partly cloudy' : '⛅' ,
17+ 'Cloudy' : '☁️' ,
18+ 'Overcast' : '☁️' ,
19+ 'Mist' : '🌫️' ,
20+ 'Fog' : '🌫️' ,
21+ 'Light rain' : '🌦️' ,
22+ 'Moderate rain' : '🌧️' ,
23+ 'Heavy rain' : '🌧️' ,
24+ 'Light snow' : '🌨️' ,
25+ 'Moderate snow' : '❄️' ,
26+ 'Heavy snow' : '❄️' ,
27+ 'Thunderstorm' : '⛈️' ,
28+ 'default' : '🌤️'
29+ } ;
30+
31+ function getWeatherIcon ( desc ) {
32+ for ( const [ key , icon ] of Object . entries ( weatherIcons ) ) {
33+ if ( desc . includes ( key ) ) return icon ;
34+ }
35+ return weatherIcons . default ;
36+ }
37+
38+ function getCacheKey ( cityName ) {
39+ return `weather_${ cityName . toLowerCase ( ) } ` ;
40+ }
41+
42+ function isCacheValid ( timestamp ) {
43+ return Date . now ( ) - timestamp < CACHE_DURATION ;
44+ }
45+
46+ function convertTemp ( tempC ) {
47+ if ( unit === 'F' ) {
48+ return Math . round ( tempC * 9 / 5 + 32 ) ;
49+ }
50+ return tempC ;
51+ }
52+
53+ function displayWeather ( data , cityName ) {
54+ const cur = data . current_condition ?. [ 0 ] ;
55+ if ( ! cur ) {
56+ out . innerHTML = '<div class="error">No weather data available</div>' ;
57+ return ;
58+ }
59+
60+ const desc = cur . weatherDesc ?. [ 0 ] ?. value || 'Unknown' ;
61+ const icon = getWeatherIcon ( desc ) ;
62+ const temp = convertTemp ( parseInt ( cur . temp_C ) ) ;
63+ const feelsLike = convertTemp ( parseInt ( cur . FeelsLikeC ) ) ;
64+ const unitSymbol = unit === 'C' ? '°C' : '°F' ;
65+
66+ const cacheKey = getCacheKey ( cityName ) ;
67+ const cacheTime = cache [ cacheKey ] ?. timestamp
68+ ? new Date ( cache [ cacheKey ] . timestamp ) . toLocaleTimeString ( )
69+ : 'just now' ;
70+
71+ out . innerHTML = `
72+ <div class="weather-card">
73+ <div class="weather-header">
74+ <div>
75+ <h2 class="weather-city">${ cityName } </h2>
76+ <p class="weather-desc">${ desc } </p>
77+ </div>
78+ <div class="weather-icon">${ icon } </div>
79+ </div>
80+ <div class="weather-temp">${ temp } ${ unitSymbol } </div>
81+ <div class="weather-details">
82+ <div class="detail-item">
83+ <span class="detail-label">Feels like</span>
84+ <span class="detail-value">${ feelsLike } ${ unitSymbol } </span>
85+ </div>
86+ <div class="detail-item">
87+ <span class="detail-label">Humidity</span>
88+ <span class="detail-value">${ cur . humidity } %</span>
89+ </div>
90+ <div class="detail-item">
91+ <span class="detail-label">Wind</span>
92+ <span class="detail-value">${ cur . windspeedKmph } km/h</span>
93+ </div>
94+ <div class="detail-item">
95+ <span class="detail-label">Pressure</span>
96+ <span class="detail-value">${ cur . pressure } mb</span>
97+ </div>
98+ </div>
99+ <div class="cache-info">Last updated: ${ cacheTime } </div>
100+ </div>
101+ ` ;
102+ }
103+
104+ async function fetchWeather ( cityName ) {
105+ const cacheKey = getCacheKey ( cityName ) ;
106+
107+ // Check cache
108+ if ( cache [ cacheKey ] && isCacheValid ( cache [ cacheKey ] . timestamp ) ) {
109+ displayWeather ( cache [ cacheKey ] . data , cityName ) ;
110+ return ;
111+ }
112+
113+ out . innerHTML = '<div class="loading">Loading weather data...</div>' ;
114+
115+ try {
116+ const q = encodeURIComponent ( cityName ) ;
117+ const res = await fetch ( `https://wttr.in/${ q } ?format=j1` ) ;
118+
119+ if ( ! res . ok ) {
120+ throw new Error ( `Failed to fetch: ${ res . status } ` ) ;
121+ }
122+
123+ const data = await res . json ( ) ;
124+
125+ // Cache the result
126+ cache [ cacheKey ] = {
127+ data : data ,
128+ timestamp : Date . now ( )
129+ } ;
130+
131+ displayWeather ( data , cityName ) ;
132+ } catch ( err ) {
133+ out . innerHTML = `<div class="error">Failed to fetch weather data. Please check the city name and try again.</div>` ;
134+ console . error ( err ) ;
135+ }
136+ }
137+
138+ // Form submit handler
2139form . addEventListener ( 'submit' , async e => {
3- e . preventDefault ( ) ; out . textContent = 'Loading...' ; try { const q = encodeURIComponent ( city . value ) ; const res = await fetch ( `https://wttr.in/${ q } ?format=j1` ) ; const data = await res . json ( ) ; const cur = data . current_condition ?. [ 0 ] ; out . innerHTML = cur ? `<strong>${ city . value } </strong>: ${ cur . temp_C } °C, ${ cur . weatherDesc ?. [ 0 ] ?. value } ` : 'No data' ; } catch ( err ) { out . textContent = 'Failed to fetch weather' ; }
140+ e . preventDefault ( ) ;
141+ const cityName = city . value . trim ( ) ;
142+ if ( cityName ) {
143+ await fetchWeather ( cityName ) ;
144+ }
4145} ) ;
5- // TODOs: use icons; toggle units; cache; geolocation; error states
146+
147+ // Geolocation handler
148+ geoBtn . addEventListener ( 'click' , async ( ) => {
149+ if ( ! navigator . geolocation ) {
150+ out . innerHTML = '<div class="error">Geolocation is not supported by your browser</div>' ;
151+ return ;
152+ }
153+
154+ geoBtn . disabled = true ;
155+ out . innerHTML = '<div class="loading">Getting your location...</div>' ;
156+
157+ navigator . geolocation . getCurrentPosition (
158+ async ( position ) => {
159+ const { latitude, longitude } = position . coords ;
160+ const cityName = `${ latitude } ,${ longitude } ` ;
161+ city . value = 'My Location' ;
162+ await fetchWeather ( cityName ) ;
163+ geoBtn . disabled = false ;
164+ } ,
165+ ( error ) => {
166+ out . innerHTML = `<div class="error">Unable to get location: ${ error . message } </div>` ;
167+ geoBtn . disabled = false ;
168+ }
169+ ) ;
170+ } ) ;
171+
172+ // Unit toggle handler
173+ unitBtn . addEventListener ( 'click' , ( ) => {
174+ unit = unit === 'C' ? 'F' : 'C' ;
175+ unitBtn . textContent = `°${ unit } ` ;
176+
177+ // Re-render current weather if exists
178+ const currentCity = city . value . trim ( ) ;
179+ if ( currentCity ) {
180+ const cacheKey = getCacheKey ( currentCity ) ;
181+ if ( cache [ cacheKey ] ) {
182+ displayWeather ( cache [ cacheKey ] . data , currentCity ) ;
183+ }
184+ }
185+ } ) ;
0 commit comments