File size: 10,973 Bytes
6cb7dfc
 
 
 
 
d12c2ff
6cb7dfc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d12c2ff
 
6cb7dfc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d12c2ff
 
6cb7dfc
d12c2ff
6cb7dfc
 
 
d12c2ff
 
6cb7dfc
 
 
 
d12c2ff
6cb7dfc
 
 
d12c2ff
6cb7dfc
 
 
d12c2ff
6cb7dfc
 
 
d12c2ff
6cb7dfc
 
 
 
 
 
 
d12c2ff
6cb7dfc
 
 
 
 
 
 
 
 
d12c2ff
6cb7dfc
d12c2ff
6cb7dfc
 
 
d12c2ff
8f6186d
 
d12c2ff
 
8f6186d
 
 
 
 
 
 
 
 
 
 
 
 
d12c2ff
 
6cb7dfc
d12c2ff
 
 
6cb7dfc
 
 
 
 
 
 
d12c2ff
6cb7dfc
83e924a
fbd81e2
83e924a
 
6cb7dfc
fbd81e2
d12c2ff
6cb7dfc
d12c2ff
8f6186d
 
d12c2ff
 
 
fbd81e2
d12c2ff
83e924a
6cb7dfc
d12c2ff
0df9153
 
d12c2ff
 
6cb7dfc
 
 
 
d12c2ff
6cb7dfc
 
 
d12c2ff
6cb7dfc
d12c2ff
6cb7dfc
fbd81e2
 
d12c2ff
8f6186d
d12c2ff
 
 
 
 
 
83e924a
d12c2ff
 
fbd81e2
 
 
 
6cb7dfc
 
d12c2ff
 
 
6cb7dfc
 
 
d12c2ff
83e924a
fbd81e2
 
6cb7dfc
fbd81e2
d12c2ff
 
6cb7dfc
 
 
 
fbd81e2
83e924a
6cb7dfc
fbd81e2
 
d12c2ff
 
6cb7dfc
0df9153
d12c2ff
 
 
6cb7dfc
8f6186d
6cb7dfc
 
d12c2ff
6cb7dfc
d12c2ff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Weather Dashboard</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://unpkg.com/feather-icons"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
        body {
            font-family: 'Poppins', sans-serif;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
        }
        .weather-card {
            backdrop-filter: blur(10px);
            background: rgba(255, 255, 255, 0.2);
            border-radius: 20px;
            box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.18);
        }
        .search-btn {
            transition: all 0.3s ease;
        }
        .search-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }
        .forecast-item {
            transition: all 0.3s ease;
        }
        .forecast-item:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
        }
    </style>
</head>
<body class="py-8 px-4">
    <div class="max-w-4xl mx-auto">
        <header class="text-center mb-8">
            <h1 class="text-4xl font-bold text-gray-800 mb-2">Weather Dashboard</h1>
            <p class="text-gray-600">Get real-time weather updates</p>
        </header>

        <div class="flex mb-8 justify-center">
            <div class="relative w-full max-w-md">
                <input 
                    type="text" 
                    id="location-input" 
                    placeholder="Enter city name..." 
                    class="w-full px-6 py-3 rounded-full border-0 shadow-md focus:outline-none focus:ring-2 focus:ring-blue-400"
                >
                <button 
                    id="search-btn"
                    class="absolute right-2 top-1/2 transform -translate-y-1/2 bg-blue-500 hover:bg-blue-600 text-white p-2 rounded-full search-btn"
                >
                    <i data-feather="search"></i>
                </button>
            </div>
        </div>

        <div id="current-weather" class="weather-card p-6 mb-8 text-center hidden">
            <div class="flex justify-between items-center mb-4">
                <div class="text-left">
                    <h2 id="location" class="text-2xl font-bold text-gray-800">Location</h2>
                    <p id="date" class="text-gray-600">Date</p>
                </div>
                <div id="weather-icon" class="w-20 h-20"></div>
            </div>
            <div class="flex justify-around items-center">
                <div>
                    <p id="temp" class="text-5xl font-bold text-gray-800">0°C</p>
                    <p id="weather-desc" class="text-gray-600">Weather</p>
                </div>
                <div class="grid grid-cols-2 gap-4 text-sm">
                    <div class="flex items-center">
                        <i data-feather="wind" class="mr-2 text-blue-500"></i>
                        <span id="wind-speed">0 km/h</span>
                    </div>
                    <div class="flex items-center">
                        <i data-feather="droplet" class="mr-2 text-blue-500"></i>
                        <span id="humidity">0%</span>
                    </div>
                    <div class="flex items-center">
                        <i data-feather="compass" class="mr-2 text-blue-500"></i>
                        <span id="pressure">0 hPa</span>
                    </div>
                    <div class="flex items-center">
                        <i data-feather="eye" class="mr-2 text-blue-500"></i>
                        <span id="visibility">0 km</span>
                    </div>
                </div>
            </div>
        </div>

        <div id="forecast-container" class="hidden">
            <h2 class="text-xl font-semibold text-gray-800 mb-4">5-Day Forecast</h2>
            <div id="forecast" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4"></div>
        </div>

        <div id="loading" class="text-center hidden">
            <div class="inline-block animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500 mb-2"></div>
            <p>Fetching weather data...</p>
        </div>

        <div id="error" class="text-center hidden">
            <i data-feather="alert-circle" class="text-red-500 w-12 h-12 mx-auto mb-2"></i>
            <p id="error-message" class="text-red-500">Location not found</p>
        </div>
    </div>

    <script>
        feather.replace();
        
        const locationInput = document.getElementById('location-input');
        const searchBtn = document.getElementById('search-btn');
        let locationName = '';

        // Debounce function to limit API calls
        function debounce(func, delay) {
            let timeout;
            return function() {
                const context = this;
                const args = arguments;
                clearTimeout(timeout);
                timeout = setTimeout(() => func.apply(context, args), delay);
            };
        }

        // Handle search button and enter key
        searchBtn.addEventListener('click', fetchWeather);
        locationInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') fetchWeather();
        });

        async function fetchWeather() {
            const location = locationInput.value.trim();
            if (!location) return;

            // Show loading state
            document.getElementById('loading').classList.remove('hidden');
            document.getElementById('current-weather').classList.add('hidden');
            document.getElementById('forecast-container').classList.add('hidden');
            document.getElementById('error').classList.add('hidden');

            try {
                // First get coordinates for location
                const geoResponse = await fetch(`https://api.openweathermap.org/geo/1.0/direct?q=${location}&limit=1&appid=3fd7e0e3b5f5bf7f3a6cee1c6af5f8e0`);
                if (!geoResponse.ok) throw new Error('Location service unavailable');
                const geoData = await geoResponse.json();
                
                if (!geoData || geoData.length === 0) {
                    throw new Error('City not found');
                }

                const city = geoData[0];
                const { lat: latitude, lon: longitude } = city;
                locationName = `${city.name}${city.state ? ', ' + city.state : ''}, ${city.country}`;

                // Fetch weather data
                const weatherResponse = await fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&exclude=minutely,hourly,alerts&units=metric&appid=3fd7e0e3b5f5bf7f3a6cee1c6af5f8e0`);
                if (!weatherResponse.ok) throw new Error('Weather service unavailable');
                const weatherData = await weatherResponse.json();

                // Update UI
                updateCurrentWeather(weatherData);
                updateForecast(weatherData);

                // Hide loading, show weather
                document.getElementById('loading').classList.add('hidden');
                document.getElementById('current-weather').classList.remove('hidden');
                document.getElementById('forecast-container').classList.remove('hidden');
            } catch (error) {
                console.error('Error:', error);
                document.getElementById('loading').classList.add('hidden');
                document.getElementById('error').classList.remove('hidden');
                document.getElementById('error-message').textContent = error.message;
            }
        }

        function updateCurrentWeather(data) {
            const current = data.current;
            const date = new Date(current.dt * 1000);
            
            document.getElementById('location').textContent = locationName;
            document.getElementById('date').textContent = date.toLocaleDateString('en-US', { 
                weekday: 'long', 
                year: 'numeric', 
                month: 'long', 
                day: 'numeric' 
            });
            
            document.getElementById('temp').textContent = `${Math.round(current.temp)}°C`;
            document.getElementById('weather-desc').textContent = current.weather[0].description;
            document.getElementById('wind-speed').textContent = `${current.wind_speed} km/h`;
            document.getElementById('humidity').textContent = `${current.humidity}%`;
            document.getElementById('pressure').textContent = `${current.pressure} hPa`;
            document.getElementById('visibility').textContent = `${current.visibility / 1000} km`;

            const weatherIcon = document.getElementById('weather-icon');
            weatherIcon.innerHTML = `<img src="https://openweathermap.org/img/wn/${current.weather[0].icon}@2x.png" alt="${current.weather[0].description}">`;
        }

        function updateForecast(data) {
            const forecastContainer = document.getElementById('forecast');
            forecastContainer.innerHTML = '';

            for (let i = 0; i < 5; i++) {
                const forecast = data.daily[i];
                const date = new Date(forecast.dt * 1000);
                const dayName = date.toLocaleDateString('en-US', { weekday: 'short' });
                const weatherDesc = forecast.weather[0].description;

                const forecastItem = document.createElement('div');
                forecastItem.className = 'weather-card p-4 text-center forecast-item';
                forecastItem.innerHTML = `
                    <p class="font-medium text-gray-800">${dayName}</p>
                    <p class="text-sm text-gray-600 mb-2">${date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}</p>
                    <img src="https://openweathermap.org/img/wn/${forecast.weather[0].icon}@2x.png" alt="${weatherDesc}" class="mx-auto w-16 h-16">
                    <p class="text-gray-600 text-sm capitalize">${weatherDesc}</p>
                    <div class="flex justify-center gap-4 mt-2">
                        <span class="font-bold text-gray-800">${Math.round(forecast.temp.max)}°</span>
                        <span class="text-gray-600">${Math.round(forecast.temp.min)}°</span>
                    </div>
                `;
                forecastContainer.appendChild(forecastItem);
            }
        }

        // Load default weather on page load
        window.addEventListener('load', () => {
            locationInput.value = 'London, UK';
            fetchWeather();
        });
    </script>
</body>
</html>