@@ -85,6 +85,34 @@ export default function Weather() {
8585 // Helper to convert °C to °F
8686 const displayTemp = ( c ) => ( unit === "C" ? c : Math . round ( ( c * 9 ) / 5 + 32 ) ) ;
8787
88+ // Normalize icon URL returned by wttr.in (they sometimes return // links)
89+ const getIconUrl = ( iconArrOrStr ) => {
90+ if ( ! iconArrOrStr ) return null ;
91+
92+ // If API returned an array like [{ value: "//..." }]
93+ if ( Array . isArray ( iconArrOrStr ) ) {
94+ const url = iconArrOrStr ?. [ 0 ] ?. value ;
95+ if ( ! url ) return null ;
96+ return url . startsWith ( "//" ) ? `https:${ url } ` : url ;
97+ }
98+
99+ // If API returned a plain string (rare) or already a URL
100+ if ( typeof iconArrOrStr === "string" ) {
101+ return iconArrOrStr . startsWith ( "//" )
102+ ? `https:${ iconArrOrStr } `
103+ : iconArrOrStr ;
104+ }
105+
106+ return null ;
107+ } ;
108+
109+ // Format wttr.in hourly time values like "0", "300", "1500" -> "00:00", "03:00", "15:00"
110+ const formatWttTime = ( t ) => {
111+ if ( t == null ) return "" ;
112+ const s = String ( t ) . padStart ( 4 , "0" ) ;
113+ return `${ s . slice ( 0 , 2 ) } :${ s . slice ( 2 ) } ` ;
114+ } ;
115+
88116 const getBadgeStyle = ( condition ) => {
89117 if ( ! condition ) return { color : "#E0E0E0" , label : "Clear 🌤️" } ;
90118
@@ -142,9 +170,18 @@ export default function Weather() {
142170 { /* Current Weather */ }
143171 < Card title = "Current Weather" size = "large" >
144172 < h2 > { data . nearest_area ?. [ 0 ] ?. areaName ?. [ 0 ] ?. value || city } </ h2 >
145- < p >
146- < strong > Temperature:</ strong > { " " }
147- { displayTemp ( Number ( current . temp_C ) ) } °{ unit }
173+ < p style = { { display : "flex" , alignItems : "center" , gap : "12px" } } >
174+ { current && getIconUrl ( current . weatherIconUrl ) && (
175+ < img
176+ src = { getIconUrl ( current . weatherIconUrl ) }
177+ alt = { current . weatherDesc ?. [ 0 ] ?. value || "weather icon" }
178+ style = { { width : 48 , height : 48 , objectFit : "contain" } }
179+ />
180+ ) }
181+ < span >
182+ < strong > Temperature:</ strong > { " " }
183+ { displayTemp ( Number ( current . temp_C ) ) } °{ unit }
184+ </ span >
148185 </ p >
149186 < p >
150187 < strong > Humidity:</ strong > { current . humidity } %
@@ -163,7 +200,98 @@ export default function Weather() {
163200 return (
164201 < Card key = { i } title = { i === 0 ? "Today" : `Day ${ i + 1 } ` } >
165202 { /* Badge Section */ }
166- < div
203+ { /* Forecast icon (use first hourly entry icon) */ }
204+ { day . hourly ?. [ 0 ] &&
205+ getIconUrl ( day . hourly ?. [ 0 ] ?. weatherIconUrl ) && (
206+ < div style = { { marginTop : 8 } } >
207+ < img
208+ src = { getIconUrl ( day . hourly ?. [ 0 ] ?. weatherIconUrl ) }
209+ alt = {
210+ day . hourly ?. [ 0 ] ?. weatherDesc ?. [ 0 ] ?. value ||
211+ "forecast icon"
212+ }
213+ style = { { width : 40 , height : 40 , objectFit : "contain" } }
214+ onError = { ( e ) =>
215+ ( e . currentTarget . style . display = "none" )
216+ }
217+ />
218+ </ div >
219+ ) }
220+
221+ { /* Full-day hourly timeline (0:00 - 23:00) */ }
222+ { day . hourly && (
223+ < div
224+ style = { {
225+ display : "block" ,
226+ marginTop : 8 ,
227+ } }
228+ >
229+ < div
230+ style = { {
231+ display : "flex" ,
232+ gap : 12 ,
233+ overflowX : "auto" ,
234+ padding : "8px 4px" ,
235+ WebkitOverflowScrolling : "touch" ,
236+ } }
237+ >
238+ { day . hourly . map ( ( h , idx ) => {
239+ const icon = getIconUrl ( h . weatherIconUrl ) ;
240+ const t = h . time ?? h . Time ?? "" ;
241+ const temp =
242+ h . tempC ?? h . temp_C ?? h . tempC ?? h . tempF ?? "" ;
243+
244+ return (
245+ < div
246+ key = { idx }
247+ style = { {
248+ minWidth : 72 ,
249+ padding : 6 ,
250+ borderRadius : 6 ,
251+ background : "rgba(0,0,0,0.03)" ,
252+ display : "flex" ,
253+ flexDirection : "column" ,
254+ alignItems : "center" ,
255+ justifyContent : "center" ,
256+ fontSize : 12 ,
257+ } }
258+ >
259+ { icon ? (
260+ < img
261+ src = { icon }
262+ alt = { h . weatherDesc ?. [ 0 ] ?. value || "hour icon" }
263+ style = { {
264+ width : 36 ,
265+ height : 36 ,
266+ objectFit : "contain" ,
267+ } }
268+ loading = "lazy"
269+ onError = { ( e ) =>
270+ ( e . currentTarget . style . display = "none" )
271+ }
272+ />
273+ ) : (
274+ < div style = { { fontSize : 18 } } > 🌤️</ div >
275+ ) }
276+ < div style = { { marginTop : 6 } } >
277+ { formatWttTime ( t ) }
278+ </ div >
279+ < div style = { { fontWeight : 700 } } >
280+ { displayTemp ( Number ( temp ) ) } °{ unit }
281+ </ div >
282+ </ div >
283+ ) ;
284+ } ) }
285+ </ div >
286+ </ div >
287+ ) }
288+
289+ < div style = {
290+ { display :"flex" , gap :"8px" , marginTop :"17px" }
291+ } >
292+ < strong > Avg Temp:</ strong > { displayTemp ( Number ( day . avgtempC ) ) }
293+ °{ unit }
294+ < div
167295 style = { {
168296 backgroundColor : badge . color ,
169297 borderRadius : "8px" ,
@@ -177,11 +305,7 @@ export default function Weather() {
177305 >
178306 { badge . label }
179307 </ div >
180-
181- < p >
182- < strong > Avg Temp:</ strong > { displayTemp ( Number ( day . avgtempC ) ) }
183- °{ unit }
184- </ p >
308+ </ div >
185309 < p >
186310 < strong > Sunrise:</ strong > { day . astronomy ?. [ 0 ] ?. sunrise }
187311 </ p >
0 commit comments