@@ -2,10 +2,8 @@ import React, { useEffect, useState } from 'react';
2
2
import { WiHumidity , WiBarometer , WiStrongWind , WiThermometer , WiSunrise , WiSunset , WiCloudyGusts } from 'react-icons/wi' ;
3
3
import { Search } from 'lucide-react' ;
4
4
5
- // API key (Note: In a production environment, this should be stored securely)
6
5
const API_KEY = 'af0348ed3ad216d028627277b50db13f' ;
7
6
8
- // Utility functions
9
7
const fetchData = async ( URL ) => {
10
8
const response = await fetch ( `${ URL } &appid=${ API_KEY } ` ) ;
11
9
if ( ! response . ok ) {
@@ -14,28 +12,23 @@ const fetchData = async (URL) => {
14
12
return response . json ( ) ;
15
13
} ;
16
14
17
- // OpenWeatherAPI Endpoints
18
15
const url = {
19
- currentWeather : ( lat , lon ) =>
20
- `https://api.openweathermap.org/data/2.5/weather?lat=${ lat } &lon=${ lon } ` ,
21
- airPollution : ( lat , lon ) =>
22
- `https://api.openweathermap.org/data/2.5/air_pollution?lat=${ lat } &lon=${ lon } ` ,
23
- forecast : ( lat , lon ) =>
24
- `https://api.openweathermap.org/data/2.5/forecast?lat=${ lat } &lon=${ lon } ` ,
16
+ currentWeather : ( lat , lon ) => `https://api.openweathermap.org/data/2.5/weather?lat=${ lat } &lon=${ lon } ` ,
17
+ airPollution : ( lat , lon ) => `https://api.openweathermap.org/data/2.5/air_pollution?lat=${ lat } &lon=${ lon } ` ,
18
+ forecast : ( lat , lon ) => `https://api.openweathermap.org/data/2.5/forecast?lat=${ lat } &lon=${ lon } ` ,
25
19
geocoding : ( query ) => `https://api.openweathermap.org/geo/1.0/direct?q=${ query } &limit=5` ,
26
20
} ;
27
21
28
- // Helper functions
29
- const getDate = ( dateUnix , timezone ) => {
30
- const date = new Date ( ( dateUnix + timezone ) * 1000 ) ;
31
- return date . toLocaleDateString ( undefined , { weekday : 'short' , month : 'short' , day : 'numeric' } ) ;
32
- } ;
33
-
34
22
const getTime = ( timeUnix , timezone ) => {
35
23
const date = new Date ( ( timeUnix + timezone ) * 1000 ) ;
36
24
return date . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) ;
37
25
} ;
38
26
27
+ const getDate = ( timeUnix , timezone ) => {
28
+ const date = new Date ( ( timeUnix + timezone ) * 1000 ) ;
29
+ return date . toLocaleDateString ( [ ] , { weekday : 'long' , day : 'numeric' , month : 'short' } ) ;
30
+ } ;
31
+
39
32
export default function Climate ( ) {
40
33
const [ loading , setLoading ] = useState ( false ) ;
41
34
const [ weatherData , setWeatherData ] = useState ( null ) ;
@@ -46,7 +39,6 @@ export default function Climate() {
46
39
const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
47
40
const [ selectedLocation , setSelectedLocation ] = useState ( '' ) ;
48
41
49
- // Fetch weather data
50
42
const fetchWeatherData = async ( lat , lon ) => {
51
43
setLoading ( true ) ;
52
44
setError ( false ) ;
@@ -60,7 +52,6 @@ export default function Climate() {
60
52
61
53
const now = new Date ( ) ;
62
54
const cutoffTime = new Date ( now . getTime ( ) + 24 * 60 * 60 * 1000 ) ;
63
-
64
55
const next24HoursForecast = forecast . list . filter ( ( entry ) => {
65
56
const entryDate = new Date ( entry . dt * 1000 ) ;
66
57
return entryDate >= now && entryDate <= cutoffTime ;
@@ -76,7 +67,6 @@ export default function Climate() {
76
67
}
77
68
} ;
78
69
79
- // Handle search
80
70
const handleSearch = async ( e ) => {
81
71
e . preventDefault ( ) ;
82
72
if ( ! searchTerm ) {
@@ -99,7 +89,6 @@ export default function Climate() {
99
89
return ;
100
90
}
101
91
102
- // Assuming the first result is the most relevant
103
92
const { lat, lon, name, state, country } = locations [ 0 ] ;
104
93
setSelectedLocation ( `${ name } ${ state ? ', ' + state : '' } , ${ country } ` ) ;
105
94
fetchWeatherData ( lat , lon ) ;
@@ -110,7 +99,6 @@ export default function Climate() {
110
99
}
111
100
} ;
112
101
113
- // Handle current location
114
102
const handleCurrentLocation = ( ) => {
115
103
if ( ! navigator . geolocation ) {
116
104
alert ( 'Geolocation is not supported by your browser.' ) ;
@@ -139,10 +127,9 @@ export default function Climate() {
139
127
) ;
140
128
} ;
141
129
142
- // Use effect to fetch current location weather on component mount
143
130
useEffect ( ( ) => {
144
131
handleCurrentLocation ( ) ;
145
- } , [ ] ) ; // Empty dependency array to run only once when the component mounts
132
+ } , [ ] ) ;
146
133
147
134
return (
148
135
< div className = "min-h-screen flex flex-col items-center bg-gradient-to-br from-blue-100 to-green-100 p-5 w-full mt-14" >
@@ -174,29 +161,11 @@ export default function Climate() {
174
161
{ error && < p className = "text-red-500" > Error fetching data. Please try again.</ p > }
175
162
176
163
{ weatherData && (
177
- < div className = "grid grid-cols-1 md:grid-cols-2 gap-8 w-full max-w-4xl" >
178
- { /* Current Weather */ }
179
- < div className = "bg-white rounded-xl shadow-lg p-6 transition duration-300 hover:shadow-xl" >
180
- < h2 className = "text-3xl font-bold text-center text-gray-800 mb-4" >
181
- { selectedLocation || weatherData . name }
182
- </ h2 >
183
- < div className = "flex flex-col items-center" >
184
- < img
185
- src = { `https://openweathermap.org/img/wn/${ weatherData . weather [ 0 ] . icon } @4x.png` }
186
- alt = { weatherData . weather [ 0 ] . description }
187
- className = "w-32 h-32 mb-4"
188
- />
189
- < p className = "text-6xl font-semibold text-gray-800 mb-2" >
190
- { Math . round ( weatherData . main . temp - 273.15 ) } °C
191
- </ p >
192
- < p className = "text-xl text-gray-600 capitalize" > { weatherData . weather [ 0 ] . description } </ p >
193
- </ div >
194
- </ div >
195
-
196
- { /* Weather Details */ }
197
- < div className = "bg-white rounded-xl shadow-lg p-6 transition duration-300 hover:shadow-xl" >
198
- < h3 className = "text-2xl font-semibold mb-4 text-center text-gray-800" > Current Conditions</ h3 >
199
- < div className = "grid grid-cols-2 gap-4" >
164
+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-8 w-full max-w-4xl mt-16" >
165
+ < WeatherCard weatherData = { weatherData } airQualityIndex = { airQualityIndex } selectedLocation = { selectedLocation } />
166
+ < div className = "rounded-xl p-6 transition duration-300 hover:shadow-xl group hover:-rotate-0 [transform:rotate3d(1_,-1,_1,_15deg)] duration-500 overflow-hidden bg-gradient-to-bl from-green-400 via-green-500 to-green-700 p-6 rounded-lg hover:shadow-lg [box-shadow:12px_12px_0px_0px_#0d0d0d] backdrop-filter backdrop-blur-md border border-neutral-600" >
167
+ < h3 className = "text-2xl font-semibold mb-4 text-center text-gray-800 " > Current Conditions</ h3 >
168
+ < div className = "grid grid-cols-2 gap-4 group" >
200
169
< WeatherDetail icon = { WiHumidity } label = "Humidity" value = { `${ weatherData . main . humidity } %` } />
201
170
< WeatherDetail icon = { WiThermometer } label = "Feels Like" value = { `${ Math . round ( weatherData . main . feels_like - 273.15 ) } °C` } />
202
171
< WeatherDetail icon = { WiStrongWind } label = "Wind Speed" value = { `${ Math . round ( weatherData . wind . speed * 3.6 ) } km/h` } />
@@ -211,7 +180,7 @@ export default function Climate() {
211
180
212
181
{ /* 5-Day Forecast */ }
213
182
{ forecastData . length > 0 && (
214
- < div className = "mt-8 w-full max-w-4xl bg-white rounded-xl shadow-lg p-6 transition duration-300 hover:shadow-xl" >
183
+ < div className = "mt-16 w-full max-w-4xl bg-white rounded-xl shadow-lg p-6 transition duration-300 hover:shadow-xl duration-500 overflow-hidden bg-gradient-to-bl from-green-400 via-green-500 to-green-700 p-6 rounded-lg hover:shadow-lg [box-shadow:12px_12px_0px_0px_#0d0d0d] backdrop-filter backdrop-blur-md border border-neutral-600 " >
215
184
< h3 className = "text-2xl font-semibold mb-4 text-center text-gray-800" > 5-Day Forecast</ h3 >
216
185
< div className = "grid grid-cols-2 md:grid-cols-5 gap-4" >
217
186
{ forecastData . map ( ( forecast , index ) => (
@@ -232,7 +201,7 @@ export default function Climate() {
232
201
233
202
{ /* Hourly Forecast */ }
234
203
{ currentWeather . length > 0 && (
235
- < div className = "mt-8 w-full max-w-4xl bg-white rounded-xl shadow-lg p-6 transition duration-300 hover:shadow-xl " >
204
+ < div className = "mt-16 mb-10 w-full max-w-4xl bg-white rounded-xl shadow-lg p-6 transition duration-300 duration-500 overflow-hidden bg-gradient-to-bl from-green-400 via-green-500 to-green-700 p-6 rounded-lg hover:shadow-lg [box-shadow:12px_12px_0px_0px_#0d0d0d] backdrop-filter backdrop-blur-md border border-neutral-600 " >
236
205
< h3 className = "text-2xl font-semibold mb-4 text-center text-gray-800" > Hourly Forecast</ h3 >
237
206
< div className = "overflow-x-auto" >
238
207
< div className = "inline-flex space-x-4 pb-4" >
@@ -256,13 +225,36 @@ export default function Climate() {
256
225
) ;
257
226
}
258
227
228
+ function WeatherCard ( { weatherData, airQualityIndex, selectedLocation } ) {
229
+ return (
230
+ < div className = "bg-gradient-to-br from-sky-500 to-blue-500 p-8 rounded-xl shadow-lg transition duration-300 hover:-rotate-0 [transform:rotate3d(1_,-1,_1,_15deg)] duration-500 overflow-hidden bg-gradient-to-bl from-green-400 via-green-500 to-green-700 p-6 rounded-lg hover:shadow-lg [box-shadow:12px_12px_0px_0px_#0d0d0d] backdrop-filter backdrop-blur-md border border-neutral-600" >
231
+ < div className = "flex flex-col items-center justify-center text-center" >
232
+ < div >
233
+ < h2 className = "text-4xl font-bold text-white mt-2" > { selectedLocation || weatherData . name } </ h2 >
234
+ < p className = "text-xl text-gray-200 mt-4" > { weatherData . weather [ 0 ] . description } </ p >
235
+ </ div >
236
+ < img
237
+ src = { `http://openweathermap.org/img/w/${ weatherData . weather [ 0 ] . icon } .png` }
238
+ alt = { weatherData . weather [ 0 ] . description }
239
+ className = "w-36 h-36 my-4"
240
+ />
241
+ </ div >
242
+ < div className = "mt-4 text-white text-center" >
243
+ < p className = "text-5xl font-semibold" >
244
+ { Math . round ( weatherData . main . temp - 273.15 ) } °C
245
+ </ p >
246
+ </ div >
247
+ </ div >
248
+ ) ;
249
+ }
250
+
259
251
function WeatherDetail ( { icon : Icon , label, value } ) {
260
252
return (
261
- < div className = "flex items-center space-x-2 " >
262
- < Icon className = "w-8 h-8 text-blue -500" />
253
+ < div className = "flex items-center space-x-3 bg-white p-4 rounded-lg shadow-md hover:shadow-lg transition duration-300 ease-in-out " >
254
+ < Icon className = "w-8 h-8 text-sky -500" />
263
255
< div >
264
- < p className = "text-sm text-gray-600 " > { label } </ p >
265
- < p className = "text-lg font-semibold text-gray-800 " > { value } </ p >
256
+ < h3 className = "text-lg font-medium text-gray-800 " > { label } </ h3 >
257
+ < p className = "text-sm text-gray-600 " > { value } </ p >
266
258
</ div >
267
259
</ div >
268
260
) ;
0 commit comments