pranit144 commited on
Commit
aa9649a
·
verified ·
1 Parent(s): ef628d3

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +843 -685
templates/index.html CHANGED
@@ -1,685 +1,843 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <title>Modern Weather Dashboard</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet" />
8
- <script src="https://cdn.tailwindcss.com"></script>
9
- <style>
10
- @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
11
-
12
- * {
13
- margin: 0;
14
- padding: 0;
15
- box-sizing: border-box;
16
- }
17
-
18
- :root {
19
- --primary-green: #10b981;
20
- --light-green: #d1fae5;
21
- --dark-green: #065f46;
22
- --gradient-start: #059669;
23
- --gradient-end: #047857;
24
- }
25
-
26
- body {
27
- font-family: 'Plus Jakarta Sans', sans-serif;
28
- background: #f0fdf4;
29
- color: #1f2937;
30
- line-height: 1.5;
31
- }
32
-
33
- /* Animations */
34
- @keyframes fadeInUp {
35
- from {
36
- opacity: 0;
37
- transform: translateY(20px);
38
- }
39
- to {
40
- opacity: 1;
41
- transform: translateY(0);
42
- }
43
- }
44
-
45
- @keyframes slideInLeft {
46
- from {
47
- opacity: 0;
48
- transform: translateX(-20px);
49
- }
50
- to {
51
- opacity: 1;
52
- transform: translateX(0);
53
- }
54
- }
55
-
56
- @keyframes pulse {
57
- 0% { transform: scale(1); }
58
- 50% { transform: scale(1.05); }
59
- 100% { transform: scale(1); }
60
- }
61
-
62
- /* Layout */
63
- .container {
64
- display: flex;
65
- min-height: 100vh;
66
- }
67
-
68
- /* Sidebar */
69
- .sidebar {
70
- width: 300px;
71
- background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
72
- padding: 2rem;
73
- color: white;
74
- position: fixed;
75
- height: 100vh;
76
- overflow-y: auto;
77
- animation: slideInLeft 0.5s ease-out;
78
- box-shadow: 4px 0 15px rgba(0,0,0,0.1);
79
- }
80
-
81
- .sidebar-header {
82
- display: flex;
83
- align-items: center;
84
- gap: 0.75rem;
85
- margin-bottom: 2rem;
86
- padding-bottom: 1rem;
87
- border-bottom: 1px solid rgba(255,255,255,0.2);
88
- }
89
-
90
- .sidebar-header i {
91
- font-size: 1.5rem;
92
- }
93
-
94
- .input-group {
95
- margin-bottom: 1.5rem;
96
- }
97
-
98
- .input-group label {
99
- display: block;
100
- margin-bottom: 0.5rem;
101
- font-weight: 500;
102
- font-size: 0.9rem;
103
- }
104
-
105
- .input-field {
106
- width: 100%;
107
- padding: 0.75rem;
108
- border-radius: 8px;
109
- border: 1px solid rgba(255,255,255,0.2);
110
- background: rgba(255,255,255,0.1);
111
- color: white;
112
- transition: all 0.3s ease;
113
- }
114
-
115
- .input-field:focus {
116
- outline: none;
117
- border-color: rgba(255,255,255,0.5);
118
- background: rgba(255,255,255,0.15);
119
- }
120
-
121
- .search-button {
122
- width: 100%;
123
- padding: 0.75rem;
124
- border-radius: 8px;
125
- background: white;
126
- color: var(--dark-green);
127
- font-weight: 600;
128
- border: none;
129
- cursor: pointer;
130
- transition: all 0.3s ease;
131
- display: flex;
132
- align-items: center;
133
- justify-content: center;
134
- gap: 0.5rem;
135
- }
136
-
137
- .search-button:hover {
138
- transform: translateY(-2px);
139
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
140
- }
141
-
142
- /* Main Content */
143
- .main-content {
144
- flex: 1;
145
- margin-left: 300px;
146
- padding: 2rem;
147
- animation: fadeInUp 0.5s ease-out;
148
- }
149
-
150
- /* Header: Current Conditions */
151
- .header-card {
152
- background: white;
153
- border-radius: 16px;
154
- padding: 2rem;
155
- margin-bottom: 2rem;
156
- box-shadow: 0 4px 20px rgba(0,0,0,0.05);
157
- animation: fadeInUp 0.5s ease-out;
158
- display: flex;
159
- flex-wrap: wrap;
160
- justify-content: space-between;
161
- align-items: center;
162
- }
163
-
164
- .header-location {
165
- font-size: 1.5rem;
166
- font-weight: 700;
167
- color: var(--dark-green);
168
- display: flex;
169
- align-items: center;
170
- gap: 0.5rem;
171
- }
172
-
173
- .timestamp {
174
- display: flex;
175
- align-items: center;
176
- gap: 0.5rem;
177
- padding: 0.5rem 1rem;
178
- background: var(--light-green);
179
- border-radius: 8px;
180
- font-size: 0.9rem;
181
- color: var(--dark-green);
182
- }
183
-
184
- .current-temp {
185
- font-size: 3.5rem;
186
- font-weight: 700;
187
- color: var(--dark-green);
188
- display: flex;
189
- align-items: center;
190
- gap: 1rem;
191
- }
192
-
193
- .weather-icon {
194
- font-size: 3rem;
195
- color: var(--primary-green);
196
- }
197
-
198
- .weather-details {
199
- display: flex;
200
- gap: 2rem;
201
- margin-top: 1rem;
202
- }
203
-
204
- .weather-detail {
205
- display: flex;
206
- align-items: center;
207
- gap: 0.5rem;
208
- color: var(--dark-green);
209
- }
210
-
211
- /* Alert Notification */
212
- .alert-notification {
213
- animation: fadeInUp 0.5s ease-out;
214
- }
215
-
216
- /* Highlights Grid */
217
- .highlights {
218
- display: grid;
219
- grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
220
- gap: 1.5rem;
221
- margin-bottom: 2rem;
222
- }
223
-
224
- .highlight-card {
225
- background: white;
226
- border-radius: 16px;
227
- padding: 1.5rem;
228
- box-shadow: 0 4px 15px rgba(0,0,0,0.05);
229
- transition: all 0.3s ease;
230
- animation: fadeInUp 0.5s ease-out;
231
- }
232
-
233
- .highlight-card:hover {
234
- transform: translateY(-5px);
235
- box-shadow: 0 8px 25px rgba(0,0,0,0.1);
236
- }
237
-
238
- .highlight-icon {
239
- font-size: 2rem;
240
- color: var(--primary-green);
241
- margin-bottom: 1rem;
242
- }
243
-
244
- .highlight-title {
245
- font-size: 1rem;
246
- font-weight: 600;
247
- color: var(--dark-green);
248
- margin-bottom: 0.5rem;
249
- }
250
-
251
- .highlight-value {
252
- font-size: 1.75rem;
253
- font-weight: 700;
254
- color: var(--primary-green);
255
- }
256
-
257
- /* Forecast Section */
258
- .forecast-section {
259
- margin-top: 2rem;
260
- }
261
-
262
- .section-title {
263
- font-size: 1.5rem;
264
- font-weight: 700;
265
- color: var(--dark-green);
266
- margin-bottom: 1.5rem;
267
- display: flex;
268
- align-items: center;
269
- gap: 0.75rem;
270
- }
271
-
272
- .forecast-grid {
273
- display: grid;
274
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
275
- gap: 1.5rem;
276
- }
277
-
278
- .forecast-card {
279
- background: white;
280
- border-radius: 16px;
281
- padding: 1.5rem;
282
- text-align: center;
283
- box-shadow: 0 4px 15px rgba(0,0,0,0.05);
284
- transition: all 0.3s ease;
285
- animation: fadeInUp 0.5s ease-out;
286
- }
287
-
288
- .forecast-card:hover {
289
- transform: translateY(-5px);
290
- box-shadow: 0 8px 25px rgba(0,0,0,0.1);
291
- cursor: pointer;
292
- }
293
-
294
- .forecast-day {
295
- font-weight: 700;
296
- color: var(--dark-green);
297
- font-size: 1.1rem;
298
- margin-bottom: 0.5rem;
299
- }
300
-
301
- .forecast-date {
302
- color: #6b7280;
303
- font-size: 0.9rem;
304
- margin-bottom: 1rem;
305
- }
306
-
307
- /* Conditional Forecast Icon */
308
- .forecast-icon {
309
- font-size: 2.5rem;
310
- margin: 1rem 0;
311
- }
312
-
313
- .forecast-temp {
314
- font-size: 1.5rem;
315
- font-weight: 700;
316
- color: var(--primary-green);
317
- margin: 0.5rem 0;
318
- }
319
-
320
- .temp-range {
321
- display: flex;
322
- justify-content: center;
323
- gap: 1rem;
324
- color: #6b7280;
325
- font-size: 0.9rem;
326
- }
327
-
328
- .sunrise-sunset {
329
- margin-top: 1rem;
330
- padding-top: 1rem;
331
- border-top: 1px solid #e5e7eb;
332
- font-size: 0.9rem;
333
- color: #6b7280;
334
- }
335
-
336
- /* Footer */
337
- .footer-note {
338
- margin-top: 2rem;
339
- color: #777;
340
- font-size: 0.8rem;
341
- text-align: center;
342
- }
343
-
344
- /* Responsive Design */
345
- @media (max-width: 1024px) {
346
- .sidebar {
347
- width: 260px;
348
- }
349
- .main-content {
350
- margin-left: 260px;
351
- }
352
- }
353
-
354
- @media (max-width: 768px) {
355
- .container {
356
- flex-direction: column;
357
- }
358
- .sidebar {
359
- width: 100%;
360
- height: auto;
361
- position: relative;
362
- padding: 1rem;
363
- }
364
- .main-content {
365
- margin-left: 0;
366
- padding: 1rem;
367
- }
368
- .current-temp {
369
- font-size: 2.5rem;
370
- }
371
- .weather-details {
372
- flex-direction: column;
373
- gap: 1rem;
374
- }
375
- .highlights {
376
- grid-template-columns: 1fr;
377
- }
378
- }
379
-
380
- /* Loading Animation */
381
- .loading {
382
- animation: pulse 1.5s infinite;
383
- }
384
-
385
- /* Custom Scrollbar */
386
- ::-webkit-scrollbar {
387
- width: 8px;
388
- }
389
-
390
- ::-webkit-scrollbar-track {
391
- background: rgba(255,255,255,0.1);
392
- }
393
-
394
- ::-webkit-scrollbar-thumb {
395
- background: rgba(255,255,255,0.3);
396
- border-radius: 4px;
397
- }
398
-
399
- ::-webkit-scrollbar-thumb:hover {
400
- background: rgba(255,255,255,0.4);
401
- }
402
- </style>
403
- </head>
404
- <body>
405
- <div class="container">
406
- <!-- Sidebar -->
407
- <aside class="sidebar">
408
- <div class="sidebar-header">
409
- <i class="fas fa-cloud-sun"></i>
410
- <h1 class="text-xl font-bold">Weather Dashboard</h1>
411
- </div>
412
- <form method="POST" action="/">
413
- <div class="input-group">
414
- <label for="lat">Latitude</label>
415
- <input type="number" step="any" id="lat" name="lat" class="input-field" value="{{ lat }}" placeholder="Enter latitude..." />
416
- </div>
417
- <div class="input-group">
418
- <label for="lon">Longitude</label>
419
- <input type="number" step="any" id="lon" name="lon" class="input-field" value="{{ lon }}" placeholder="Enter longitude..." />
420
- </div>
421
- <button type="submit" class="search-button">
422
- <i class="fas fa-search"></i>
423
- Get Weather
424
- </button>
425
- </form>
426
- </aside>
427
-
428
- <!-- Main Content -->
429
- <main class="main-content">
430
- <!-- Header: Current Weather & Location -->
431
- <section class="header-card">
432
- <div>
433
- <div class="location">
434
- <i class="fas fa-map-marker-alt"></i>
435
- {{ location_address }}
436
- </div>
437
- <div class="timestamp">
438
- <i class="far fa-clock"></i>
439
- {{ current_time }}
440
- <span class="text-sm opacity-75">{{ timezone }}</span>
441
- </div>
442
- </div>
443
- <div class="flex items-center gap-4">
444
- <div class="current-temp">
445
- {{ current_temp }}°C
446
- {% if current_icon == "☀️" %}
447
- <i class="fas fa-sun weather-icon"></i>
448
- {% elif current_icon == "⛅" %}
449
- <i class="fas fa-cloud-sun weather-icon"></i>
450
- {% elif current_icon == "🌫️" %}
451
- <i class="fas fa-smog weather-icon"></i>
452
- {% elif current_icon == "🌦️" %}
453
- <i class="fas fa-cloud-showers-heavy weather-icon"></i>
454
- {% elif current_icon == "🌧️" %}
455
- <i class="fas fa-cloud-rain weather-icon"></i>
456
- {% elif current_icon == "❄️" %}
457
- <i class="fas fa-snowflake weather-icon"></i>
458
- {% elif current_icon == "⛈️" %}
459
- <i class="fas fa-bolt weather-icon"></i>
460
- {% else %}
461
- <i class="fas fa-question weather-icon"></i>
462
- {% endif %}
463
- </div>
464
- <div class="text-right">
465
- <div class="text-lg font-medium text-green-700">{{ current_desc }}</div>
466
- <div class="weather-details">
467
- <div class="weather-detail">
468
- <i class="fas fa-temperature-low"></i>
469
- Feels like: {{ feels_like }}°C
470
- </div>
471
- <div class="weather-detail">
472
- <i class="fas fa-wind"></i>
473
- {{ current_wind_speed }} km/h
474
- </div>
475
- </div>
476
- </div>
477
- </div>
478
- </section>
479
-
480
- <!-- Alert Notification (if alerts were sent) -->
481
- {% if alerts_sent %}
482
- <div class="alert-notification bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-4" role="alert">
483
- <p class="font-bold"><i class="fas fa-exclamation-triangle"></i> Weather Alert Sent</p>
484
- <p>Alerts for upcoming hazardous weather conditions Near Your Farm have been sent to your WhatsApp.</p>
485
- </div>
486
- {% endif %}
487
-
488
- <!-- Today's Highlights -->
489
- <section>
490
- <h2 class="section-title">
491
- <i class="fas fa-chart-line"></i>
492
- Today's Highlights
493
- </h2>
494
- <div class="highlights">
495
- <!-- Humidity -->
496
- <div class="highlight-card">
497
- <i class="fas fa-tint highlight-icon"></i>
498
- <div class="highlight-title">Humidity</div>
499
- <div class="highlight-value">{{ today_highlights.humidity }}%</div>
500
- </div>
501
- <!-- Wind Speed -->
502
- <div class="highlight-card">
503
- <i class="fas fa-wind highlight-icon"></i>
504
- <div class="highlight-title">Wind Speed</div>
505
- <div class="highlight-value">{{ today_highlights.windspeed }} km/h</div>
506
- </div>
507
- <!-- UV Index -->
508
- <div class="highlight-card">
509
- <i class="fas fa-sun highlight-icon"></i>
510
- <div class="highlight-title">UV Index</div>
511
- <div class="highlight-value">{{ today_highlights.uv_index }}</div>
512
- </div>
513
- <!-- Pressure -->
514
- <div class="highlight-card">
515
- <i class="fas fa-compress-alt highlight-icon"></i>
516
- <div class="highlight-title">Pressure</div>
517
- <div class="highlight-value">{{ today_highlights.pressure|round(1) }} hPa</div>
518
- </div>
519
- <!-- Cloud Cover -->
520
- <div class="highlight-card">
521
- <i class="fas fa-cloud highlight-icon"></i>
522
- <div class="highlight-title">Cloud Cover</div>
523
- <div class="highlight-value">{{ today_highlights.clouds }}%</div>
524
- </div>
525
- <!-- Precipitation -->
526
- <div class="highlight-card">
527
- <i class="fas fa-cloud-rain highlight-icon"></i>
528
- <div class="highlight-title">Precipitation</div>
529
- <div class="highlight-value">{{ today_highlights.precipitation|round(2) }} mm</div>
530
- </div>
531
- <!-- Soil Moisture -->
532
- <div class="highlight-card">
533
- <i class="fas fa-water highlight-icon"></i>
534
- <div class="highlight-title">Soil Moisture</div>
535
- <div class="highlight-value">{{ today_highlights.soil_moisture|round(3) }} /m³</div>
536
- </div>
537
- <!-- Sun Timing -->
538
- <div class="highlight-card">
539
- <div class="flex justify-between items-center">
540
- <div class="text-center flex-1">
541
- <i class="fas fa-sunrise highlight-icon"></i>
542
- <div class="highlight-title">Sunrise</div>
543
- <div class="highlight-value text-lg">{{ today_highlights.sunrise }}</div>
544
- </div>
545
- <div class="border-r border-gray-200 h-16 mx-4"></div>
546
- <div class="text-center flex-1">
547
- <i class="fas fa-sunset highlight-icon"></i>
548
- <div class="highlight-title">Sunset</div>
549
- <div class="highlight-value text-lg">{{ today_highlights.sunset }}</div>
550
- </div>
551
- </div>
552
- </div>
553
- </div>
554
- </section>
555
-
556
- <!-- 14-Day Forecast -->
557
- <section class="forecast-section">
558
- <h2 class="section-title">
559
- <i class="fas fa-calendar-alt"></i>
560
- 14-Day Forecast
561
- </h2>
562
- <div class="forecast-grid">
563
- {% for day in forecast_list %}
564
- <div class="forecast-card">
565
- <div class="forecast-day">{{ day.day_name }}</div>
566
- <div class="forecast-date">{{ day.date_str }}</div>
567
- <!-- Conditional Forecast Icon -->
568
- {% if day.icon == "☀️" %}
569
- <i class="fas fa-sun forecast-icon"></i>
570
- {% elif day.icon == "⛅" %}
571
- <i class="fas fa-cloud-sun forecast-icon"></i>
572
- {% elif day.icon == "🌫️" %}
573
- <i class="fas fa-smog forecast-icon"></i>
574
- {% elif day.icon == "🌦️" %}
575
- <i class="fas fa-cloud-showers-heavy forecast-icon"></i>
576
- {% elif day.icon == "🌧️" %}
577
- <i class="fas fa-cloud-rain forecast-icon"></i>
578
- {% elif day.icon == "❄️" %}
579
- <i class="fas fa-snowflake forecast-icon"></i>
580
- {% elif day.icon == "⛈️" %}
581
- <i class="fas fa-bolt forecast-icon"></i>
582
- {% else %}
583
- <i class="fas fa-question forecast-icon"></i>
584
- {% endif %}
585
- <div class="text-sm text-green-700 font-medium">{{ day.desc }}</div>
586
- <div class="forecast-temp">{{ day.avg_temp }}°C</div>
587
- <div class="temp-range">
588
- <span><i class="fas fa-arrow-down text-blue-500"></i> {{ day.tmin }}°C</span>
589
- <span><i class="fas fa-arrow-up text-red-500"></i> {{ day.tmax }}°C</span>
590
- </div>
591
- <div class="mt-3">
592
- <div class="text-sm font-medium mb-1">Day Temperatures</div>
593
- <div class="text-sm">Morning: {{ day.morning_temp }}°C</div>
594
- <div class="text-sm">Evening: {{ day.evening_temp }}°C</div>
595
- </div>
596
- <div class="sunrise-sunset">
597
- <div class="flex items-center justify-center gap-1 mb-1">
598
- <i class="fas fa-sun text-yellow-500"></i>
599
- {{ day.sunrise }}
600
- </div>
601
- <div class="flex items-center justify-center gap-1">
602
- <i class="fas fa-moon text-gray-600"></i>
603
- {{ day.sunset }}
604
- </div>
605
- </div>
606
- </div>
607
- {% endfor %}
608
- </div>
609
- </section>
610
-
611
- <!-- Footer -->
612
- <footer class="footer-note">
613
- <p>Last updated: {{ current_time }}</p>
614
- </footer>
615
- </main>
616
- </div>
617
-
618
- <!-- Scripts -->
619
- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
620
- <script>
621
- // Initialize interactive behaviors
622
- $(document).ready(function() {
623
- // Add loading animation on form submission
624
- $('form').on('submit', function() {
625
- $('.main-content').addClass('loading');
626
- });
627
-
628
- // Animate cards on scroll using IntersectionObserver
629
- const observerOptions = {
630
- threshold: 0.1,
631
- rootMargin: '0px 0px -50px 0px'
632
- };
633
- const observer = new IntersectionObserver((entries) => {
634
- entries.forEach(entry => {
635
- if (entry.isIntersecting) {
636
- entry.target.style.opacity = '1';
637
- entry.target.style.transform = 'translateY(0)';
638
- }
639
- });
640
- }, observerOptions);
641
- document.querySelectorAll('.highlight-card, .forecast-card').forEach(card => {
642
- card.style.opacity = '0';
643
- card.style.transform = 'translateY(20px)';
644
- observer.observe(card);
645
- });
646
-
647
- // Responsive sidebar toggle for mobile devices
648
- const toggleSidebar = () => {
649
- const sidebar = document.querySelector('.sidebar');
650
- if (window.innerWidth <= 768) {
651
- sidebar.style.height = sidebar.classList.contains('expanded') ? 'auto' : '100%';
652
- sidebar.classList.toggle('expanded');
653
- }
654
- };
655
- window.addEventListener('resize', () => {
656
- if (window.innerWidth > 768) {
657
- const sidebar = document.querySelector('.sidebar');
658
- sidebar.style.height = '100vh';
659
- sidebar.classList.remove('expanded');
660
- }
661
- });
662
- // Real-time input validation for latitude and longitude
663
- const validateInput = (input) => {
664
- const value = parseFloat(input.value);
665
- if (input.id === 'lat') {
666
- if (value < -90 || value > 90) {
667
- input.setCustomValidity('Latitude must be between -90 and 90 degrees');
668
- } else {
669
- input.setCustomValidity('');
670
- }
671
- } else if (input.id === 'lon') {
672
- if (value < -180 || value > 180) {
673
- input.setCustomValidity('Longitude must be between -180 and 180 degrees');
674
- } else {
675
- input.setCustomValidity('');
676
- }
677
- }
678
- };
679
- document.querySelectorAll('.input-field').forEach(input => {
680
- input.addEventListener('input', () => validateInput(input));
681
- });
682
- });
683
- </script>
684
- </body>
685
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Modern Weather Dashboard</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
11
+
12
+ * {
13
+ margin: 0;
14
+ padding: 0;
15
+ box-sizing: border-box;
16
+ }
17
+
18
+ :root {
19
+ --primary-green: #10b981;
20
+ --light-green: #d1fae5;
21
+ --dark-green: #065f46;
22
+ --gradient-start: #059669;
23
+ --gradient-end: #047857;
24
+ }
25
+
26
+ body {
27
+ font-family: 'Plus Jakarta Sans', sans-serif;
28
+ background: #f0fdf4;
29
+ color: #1f2937;
30
+ line-height: 1.5;
31
+ }
32
+
33
+ /* Animations */
34
+ @keyframes fadeInUp {
35
+ from {
36
+ opacity: 0;
37
+ transform: translateY(20px);
38
+ }
39
+ to {
40
+ opacity: 1;
41
+ transform: translateY(0);
42
+ }
43
+ }
44
+
45
+ @keyframes slideInLeft {
46
+ from {
47
+ opacity: 0;
48
+ transform: translateX(-20px);
49
+ }
50
+ to {
51
+ opacity: 1;
52
+ transform: translateX(0);
53
+ }
54
+ }
55
+
56
+ @keyframes pulse {
57
+ 0% { transform: scale(1); }
58
+ 50% { transform: scale(1.05); }
59
+ 100% { transform: scale(1); }
60
+ }
61
+
62
+ /* Layout */
63
+ .container {
64
+ display: flex;
65
+ min-height: 100vh;
66
+ flex-direction: row; /* Default for larger screens */
67
+ }
68
+
69
+ /* Sidebar */
70
+ .sidebar {
71
+ width: 300px;
72
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
73
+ padding: 2rem;
74
+ color: white;
75
+ position: fixed; /* Fixed for larger screens */
76
+ height: 100vh;
77
+ overflow-y: auto;
78
+ animation: slideInLeft 0.5s ease-out;
79
+ box-shadow: 4px 0 15px rgba(0,0,0,0.1);
80
+ z-index: 10; /* Ensure sidebar is above main content if needed */
81
+ }
82
+
83
+ .sidebar-header {
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 0.75rem;
87
+ margin-bottom: 2rem;
88
+ padding-bottom: 1rem;
89
+ border-bottom: 1px solid rgba(255,255,255,0.2);
90
+ }
91
+
92
+ .sidebar-header i {
93
+ font-size: 1.5rem;
94
+ }
95
+
96
+ .input-group {
97
+ margin-bottom: 1.5rem;
98
+ }
99
+
100
+ .input-group label {
101
+ display: block;
102
+ margin-bottom: 0.5rem;
103
+ font-weight: 500;
104
+ font-size: 0.9rem;
105
+ }
106
+
107
+ .input-field {
108
+ width: 100%;
109
+ padding: 0.75rem;
110
+ border-radius: 8px;
111
+ border: 1px solid rgba(255,255,255,0.2);
112
+ background: rgba(255,255,255,0.1);
113
+ color: white;
114
+ transition: all 0.3s ease;
115
+ }
116
+ .input-field::placeholder {
117
+ color: rgba(255,255,255,0.7);
118
+ }
119
+
120
+ .input-field:focus {
121
+ outline: none;
122
+ border-color: rgba(255,255,255,0.5);
123
+ background: rgba(255,255,255,0.15);
124
+ }
125
+
126
+ .search-button {
127
+ width: 100%;
128
+ padding: 0.75rem;
129
+ border-radius: 8px;
130
+ background: white;
131
+ color: var(--dark-green);
132
+ font-weight: 600;
133
+ border: none;
134
+ cursor: pointer;
135
+ transition: all 0.3s ease;
136
+ display: flex;
137
+ align-items: center;
138
+ justify-content: center;
139
+ gap: 0.5rem;
140
+ }
141
+
142
+ .search-button:hover {
143
+ transform: translateY(-2px);
144
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
145
+ }
146
+
147
+ /* Main Content */
148
+ .main-content {
149
+ flex: 1;
150
+ margin-left: 300px; /* Offset for sidebar */
151
+ padding: 2rem;
152
+ animation: fadeInUp 0.5s ease-out;
153
+ }
154
+
155
+ /* Header: Current Conditions */
156
+ .header-card {
157
+ background: white;
158
+ border-radius: 16px;
159
+ padding: 2rem;
160
+ margin-bottom: 2rem;
161
+ box-shadow: 0 4px 20px rgba(0,0,0,0.05);
162
+ animation: fadeInUp 0.5s ease-out;
163
+ display: flex;
164
+ flex-wrap: wrap; /* Allow items to wrap */
165
+ justify-content: space-between;
166
+ align-items: center;
167
+ }
168
+
169
+ .header-location {
170
+ font-size: 1.5rem;
171
+ font-weight: 700;
172
+ color: var(--dark-green);
173
+ display: flex;
174
+ align-items: center;
175
+ gap: 0.5rem;
176
+ margin-bottom: 1rem; /* Add margin for stacking */
177
+ }
178
+
179
+ .timestamp {
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 0.5rem;
183
+ padding: 0.5rem 1rem;
184
+ background: var(--light-green);
185
+ border-radius: 8px;
186
+ font-size: 0.9rem;
187
+ color: var(--dark-green);
188
+ margin-bottom: 1rem; /* Add margin for stacking */
189
+ }
190
+
191
+ .current-temp {
192
+ font-size: 3.5rem;
193
+ font-weight: 700;
194
+ color: var(--dark-green);
195
+ display: flex;
196
+ align-items: center;
197
+ gap: 1rem;
198
+ }
199
+
200
+ .weather-icon {
201
+ font-size: 3rem;
202
+ color: var(--primary-green);
203
+ }
204
+
205
+ .weather-details {
206
+ display: flex;
207
+ gap: 2rem;
208
+ margin-top: 1rem;
209
+ flex-wrap: wrap; /* Allow details to wrap */
210
+ }
211
+
212
+ .weather-detail {
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 0.5rem;
216
+ color: var(--dark-green);
217
+ }
218
+
219
+ /* Alert Notification */
220
+ .alert-notification {
221
+ animation: fadeInUp 0.5s ease-out;
222
+ border-radius: 8px; /* Added border-radius */
223
+ }
224
+
225
+ /* Highlights Grid */
226
+ .highlights {
227
+ display: grid;
228
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
229
+ gap: 1.5rem;
230
+ margin-bottom: 2rem;
231
+ }
232
+
233
+ .highlight-card {
234
+ background: white;
235
+ border-radius: 16px;
236
+ padding: 1.5rem;
237
+ box-shadow: 0 4px 15px rgba(0,0,0,0.05);
238
+ transition: all 0.3s ease;
239
+ animation: fadeInUp 0.5s ease-out;
240
+ }
241
+
242
+ .highlight-card:hover {
243
+ transform: translateY(-5px);
244
+ box-shadow: 0 8px 25px rgba(0,0,0,0.1);
245
+ }
246
+
247
+ .highlight-icon {
248
+ font-size: 2rem;
249
+ color: var(--primary-green);
250
+ margin-bottom: 1rem;
251
+ }
252
+
253
+ .highlight-title {
254
+ font-size: 1rem;
255
+ font-weight: 600;
256
+ color: var(--dark-green);
257
+ margin-bottom: 0.5rem;
258
+ }
259
+
260
+ .highlight-value {
261
+ font-size: 1.75rem;
262
+ font-weight: 700;
263
+ color: var(--primary-green);
264
+ }
265
+
266
+ /* Forecast Section */
267
+ .forecast-section {
268
+ margin-top: 2rem;
269
+ }
270
+
271
+ .section-title {
272
+ font-size: 1.5rem;
273
+ font-weight: 700;
274
+ color: var(--dark-green);
275
+ margin-bottom: 1.5rem;
276
+ display: flex;
277
+ align-items: center;
278
+ gap: 0.75rem;
279
+ }
280
+
281
+ .forecast-grid {
282
+ display: grid;
283
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
284
+ gap: 1.5rem;
285
+ }
286
+
287
+ .forecast-card {
288
+ background: white;
289
+ border-radius: 16px;
290
+ padding: 1.5rem;
291
+ text-align: center;
292
+ box-shadow: 0 4px 15px rgba(0,0,0,0.05);
293
+ transition: all 0.3s ease;
294
+ animation: fadeInUp 0.5s ease-out;
295
+ }
296
+
297
+ .forecast-card:hover {
298
+ transform: translateY(-5px);
299
+ box-shadow: 0 8px 25px rgba(0,0,0,0.1);
300
+ cursor: pointer;
301
+ }
302
+
303
+ .forecast-day {
304
+ font-weight: 700;
305
+ color: var(--dark-green);
306
+ font-size: 1.1rem;
307
+ margin-bottom: 0.5rem;
308
+ }
309
+
310
+ .forecast-date {
311
+ color: #6b7280;
312
+ font-size: 0.9rem;
313
+ margin-bottom: 1rem;
314
+ }
315
+
316
+ /* Conditional Forecast Icon */
317
+ .forecast-icon {
318
+ font-size: 2.5rem;
319
+ margin: 1rem 0;
320
+ }
321
+
322
+ .forecast-temp {
323
+ font-size: 1.5rem;
324
+ font-weight: 700;
325
+ color: var(--primary-green);
326
+ margin: 0.5rem 0;
327
+ }
328
+
329
+ .temp-range {
330
+ display: flex;
331
+ justify-content: center;
332
+ gap: 1rem;
333
+ color: #6b7280;
334
+ font-size: 0.9rem;
335
+ }
336
+
337
+ .sunrise-sunset {
338
+ margin-top: 1rem;
339
+ padding-top: 1rem;
340
+ border-top: 1px solid #e5e7eb;
341
+ font-size: 0.9rem;
342
+ color: #6b7280;
343
+ }
344
+
345
+ /* Footer */
346
+ .footer-note {
347
+ margin-top: 2rem;
348
+ color: #777;
349
+ font-size: 0.8rem;
350
+ text-align: center;
351
+ }
352
+
353
+ /* --- Responsive Design --- */
354
+
355
+ /* Medium screens (e.g., small laptops, large tablets) */
356
+ @media (max-width: 1024px) {
357
+ .sidebar {
358
+ width: 260px;
359
+ padding: 1.5rem;
360
+ }
361
+ .main-content {
362
+ margin-left: 260px;
363
+ padding: 1.5rem;
364
+ }
365
+ .header-location {
366
+ font-size: 1.3rem;
367
+ }
368
+ .current-temp {
369
+ font-size: 3rem;
370
+ }
371
+ .weather-icon {
372
+ font-size: 2.5rem;
373
+ }
374
+ .highlight-value {
375
+ font-size: 1.5rem;
376
+ }
377
+ .section-title {
378
+ font-size: 1.3rem;
379
+ }
380
+ .forecast-temp {
381
+ font-size: 1.3rem;
382
+ }
383
+ }
384
+
385
+ /* Small screens (e.g., tablets in portrait, large phones) */
386
+ @media (max-width: 768px) {
387
+ .container {
388
+ flex-direction: column; /* Stack sidebar and main content vertically */
389
+ }
390
+ .sidebar {
391
+ width: 100%; /* Full width */
392
+ height: auto; /* Auto height */
393
+ position: relative; /* No longer fixed */
394
+ padding: 1.5rem 1rem; /* Adjust padding */
395
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1); /* Add bottom shadow */
396
+ border-bottom-left-radius: 0;
397
+ border-bottom-right-radius: 0;
398
+ }
399
+ .main-content {
400
+ margin-left: 0; /* Remove sidebar offset */
401
+ padding: 1.5rem 1rem; /* Adjust padding */
402
+ }
403
+ .sidebar-header {
404
+ margin-bottom: 1.5rem;
405
+ justify-content: center; /* Center header on mobile */
406
+ }
407
+ .input-group {
408
+ margin-bottom: 1rem;
409
+ }
410
+ .search-button {
411
+ margin-top: 1rem; /* Add some spacing */
412
+ }
413
+ .header-card {
414
+ flex-direction: column; /* Stack items in header card */
415
+ align-items: flex-start; /* Align left */
416
+ padding: 1.5rem;
417
+ }
418
+ .header-location, .timestamp {
419
+ margin-bottom: 0.75rem; /* Adjust spacing */
420
+ width: 100%; /* Full width for better stacking */
421
+ }
422
+ .header-location {
423
+ font-size: 1.2rem;
424
+ }
425
+ .current-temp {
426
+ font-size: 2.5rem;
427
+ gap: 0.75rem;
428
+ margin-top: 1rem;
429
+ flex-wrap: wrap; /* Allow temp and icon to wrap if needed */
430
+ }
431
+ .weather-icon {
432
+ font-size: 2.2rem;
433
+ }
434
+ .header-card .flex.items-center.gap-4 { /* Target the div containing current temp and description */
435
+ flex-direction: column; /* Stack current temp and description */
436
+ align-items: flex-start;
437
+ width: 100%;
438
+ margin-top: 1rem;
439
+ }
440
+ .header-card .text-right { /* Adjust description alignment */
441
+ text-align: left;
442
+ width: 100%;
443
+ margin-top: 0.5rem;
444
+ }
445
+ .weather-details {
446
+ flex-direction: column;
447
+ gap: 0.75rem;
448
+ margin-top: 0.75rem;
449
+ }
450
+ .highlights {
451
+ grid-template-columns: 1fr; /* Single column for highlights on smaller screens */
452
+ gap: 1rem;
453
+ }
454
+ .forecast-grid {
455
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); /* Adjust minwidth for forecast */
456
+ gap: 1rem;
457
+ }
458
+ .highlight-icon {
459
+ font-size: 1.75rem;
460
+ margin-bottom: 0.75rem;
461
+ }
462
+ .highlight-value {
463
+ font-size: 1.4rem;
464
+ }
465
+ .section-title {
466
+ font-size: 1.2rem;
467
+ margin-bottom: 1rem;
468
+ }
469
+ .forecast-card {
470
+ padding: 1rem;
471
+ }
472
+ .forecast-icon {
473
+ font-size: 2rem;
474
+ }
475
+ .forecast-day {
476
+ font-size: 1rem;
477
+ }
478
+ .forecast-temp {
479
+ font-size: 1.2rem;
480
+ }
481
+ .footer-note {
482
+ margin-top: 1.5rem;
483
+ }
484
+ }
485
+
486
+ /* Extra small screens (e.g., mobile phones) */
487
+ @media (max-width: 480px) {
488
+ .sidebar {
489
+ padding: 1rem;
490
+ }
491
+ .main-content {
492
+ padding: 1rem;
493
+ }
494
+ .sidebar-header {
495
+ margin-bottom: 1rem;
496
+ }
497
+ .sidebar-header h1 {
498
+ font-size: 1.2rem;
499
+ }
500
+ .header-location {
501
+ font-size: 1rem;
502
+ }
503
+ .timestamp {
504
+ font-size: 0.8rem;
505
+ padding: 0.4rem 0.8rem;
506
+ }
507
+ .current-temp {
508
+ font-size: 2rem;
509
+ }
510
+ .weather-icon {
511
+ font-size: 1.8rem;
512
+ }
513
+ .header-card .text-right .text-lg { /* Current description text */
514
+ font-size: 1rem;
515
+ }
516
+ .weather-detail {
517
+ font-size: 0.9rem;
518
+ }
519
+ .highlight-card {
520
+ padding: 1.2rem;
521
+ }
522
+ .highlight-icon {
523
+ font-size: 1.5rem;
524
+ }
525
+ .highlight-title {
526
+ font-size: 0.9rem;
527
+ }
528
+ .highlight-value {
529
+ font-size: 1.2rem;
530
+ }
531
+ .section-title {
532
+ font-size: 1.1rem;
533
+ }
534
+ .forecast-grid {
535
+ grid-template-columns: 1fr; /* Single column for forecast on very small screens */
536
+ }
537
+ .forecast-icon {
538
+ font-size: 1.8rem;
539
+ }
540
+ .forecast-temp {
541
+ font-size: 1.1rem;
542
+ }
543
+ .temp-range, .sunrise-sunset {
544
+ font-size: 0.85rem;
545
+ }
546
+ .footer-note {
547
+ font-size: 0.75rem;
548
+ margin-top: 1rem;
549
+ }
550
+ }
551
+
552
+ /* Loading Animation */
553
+ .loading {
554
+ animation: pulse 1.5s infinite;
555
+ }
556
+
557
+ /* Custom Scrollbar for sidebar */
558
+ .sidebar::-webkit-scrollbar {
559
+ width: 8px;
560
+ }
561
+
562
+ .sidebar::-webkit-scrollbar-track {
563
+ background: rgba(255,255,255,0.1);
564
+ border-radius: 4px;
565
+ }
566
+
567
+ .sidebar::-webkit-scrollbar-thumb {
568
+ background: rgba(255,255,255,0.3);
569
+ border-radius: 4px;
570
+ }
571
+
572
+ .sidebar::-webkit-scrollbar-thumb:hover {
573
+ background: rgba(255,255,255,0.4);
574
+ }
575
+ </style>
576
+ </head>
577
+ <body>
578
+ <div class="container">
579
+ <!-- Sidebar -->
580
+ <aside class="sidebar">
581
+ <div class="sidebar-header">
582
+ <i class="fas fa-cloud-sun"></i>
583
+ <h1 class="text-xl font-bold">Weather Dashboard</h1>
584
+ </div>
585
+ <form method="POST" action="/">
586
+ <div class="input-group">
587
+ <label for="lat">Latitude</label>
588
+ <input type="number" step="any" id="lat" name="lat" class="input-field" value="{{ lat }}" placeholder="Enter latitude..." />
589
+ </div>
590
+ <div class="input-group">
591
+ <label for="lon">Longitude</label>
592
+ <input type="number" step="any" id="lon" name="lon" class="input-field" value="{{ lon }}" placeholder="Enter longitude..." />
593
+ </div>
594
+ <button type="submit" class="search-button">
595
+ <i class="fas fa-search"></i>
596
+ Get Weather
597
+ </button>
598
+ </form>
599
+ </aside>
600
+
601
+ <!-- Main Content -->
602
+ <main class="main-content">
603
+ <!-- Header: Current Weather & Location -->
604
+ <section class="header-card">
605
+ <div>
606
+ <div class="header-location">
607
+ <i class="fas fa-map-marker-alt"></i>
608
+ {{ location_address }}
609
+ </div>
610
+ <div class="timestamp">
611
+ <i class="far fa-clock"></i>
612
+ {{ current_time }}
613
+ <span class="text-sm opacity-75">{{ timezone }}</span>
614
+ </div>
615
+ </div>
616
+ <div class="flex items-center gap-4">
617
+ <div class="current-temp">
618
+ {{ current_temp }}°C
619
+ {% if current_icon == "☀️" %}
620
+ <i class="fas fa-sun weather-icon"></i>
621
+ {% elif current_icon == "⛅" %}
622
+ <i class="fas fa-cloud-sun weather-icon"></i>
623
+ {% elif current_icon == "🌫️" %}
624
+ <i class="fas fa-smog weather-icon"></i>
625
+ {% elif current_icon == "🌦️" %}
626
+ <i class="fas fa-cloud-showers-heavy weather-icon"></i>
627
+ {% elif current_icon == "🌧️" %}
628
+ <i class="fas fa-cloud-rain weather-icon"></i>
629
+ {% elif current_icon == "❄️" %}
630
+ <i class="fas fa-snowflake weather-icon"></i>
631
+ {% elif current_icon == "⛈️" %}
632
+ <i class="fas fa-bolt weather-icon"></i>
633
+ {% else %}
634
+ <i class="fas fa-question weather-icon"></i>
635
+ {% endif %}
636
+ </div>
637
+ <div class="text-right">
638
+ <div class="text-lg font-medium text-green-700">{{ current_desc }}</div>
639
+ <div class="weather-details">
640
+ <div class="weather-detail">
641
+ <i class="fas fa-temperature-low"></i>
642
+ Feels like: {{ feels_like }}°C
643
+ </div>
644
+ <div class="weather-detail">
645
+ <i class="fas fa-wind"></i>
646
+ {{ current_wind_speed }} km/h
647
+ </div>
648
+ </div>
649
+ </div>
650
+ </div>
651
+ </section>
652
+
653
+ <!-- Alert Notification (if alerts were sent) -->
654
+ {% if alerts_sent %}
655
+ <div class="alert-notification bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-4" role="alert">
656
+ <p class="font-bold"><i class="fas fa-exclamation-triangle"></i> Weather Alert Sent</p>
657
+ <p>Alerts for upcoming hazardous weather conditions Near Your Farm have been sent to your WhatsApp.</p>
658
+ </div>
659
+ {% endif %}
660
+
661
+ <!-- Today's Highlights -->
662
+ <section>
663
+ <h2 class="section-title">
664
+ <i class="fas fa-chart-line"></i>
665
+ Today's Highlights
666
+ </h2>
667
+ <div class="highlights">
668
+ <!-- Humidity -->
669
+ <div class="highlight-card">
670
+ <i class="fas fa-tint highlight-icon"></i>
671
+ <div class="highlight-title">Humidity</div>
672
+ <div class="highlight-value">{{ today_highlights.humidity }}%</div>
673
+ </div>
674
+ <!-- Wind Speed -->
675
+ <div class="highlight-card">
676
+ <i class="fas fa-wind highlight-icon"></i>
677
+ <div class="highlight-title">Wind Speed</div>
678
+ <div class="highlight-value">{{ today_highlights.windspeed }} km/h</div>
679
+ </div>
680
+ <!-- UV Index -->
681
+ <div class="highlight-card">
682
+ <i class="fas fa-sun highlight-icon"></i>
683
+ <div class="highlight-title">UV Index</div>
684
+ <div class="highlight-value">{{ today_highlights.uv_index }}</div>
685
+ </div>
686
+ <!-- Pressure -->
687
+ <div class="highlight-card">
688
+ <i class="fas fa-compress-alt highlight-icon"></i>
689
+ <div class="highlight-title">Pressure</div>
690
+ <div class="highlight-value">{{ today_highlights.pressure|round(1) }} hPa</div>
691
+ </div>
692
+ <!-- Cloud Cover -->
693
+ <div class="highlight-card">
694
+ <i class="fas fa-cloud highlight-icon"></i>
695
+ <div class="highlight-title">Cloud Cover</div>
696
+ <div class="highlight-value">{{ today_highlights.clouds }}%</div>
697
+ </div>
698
+ <!-- Precipitation -->
699
+ <div class="highlight-card">
700
+ <i class="fas fa-cloud-rain highlight-icon"></i>
701
+ <div class="highlight-title">Precipitation</div>
702
+ <div class="highlight-value">{{ today_highlights.precipitation|round(2) }} mm</div>
703
+ </div>
704
+ <!-- Soil Moisture -->
705
+ <div class="highlight-card">
706
+ <i class="fas fa-water highlight-icon"></i>
707
+ <div class="highlight-title">Soil Moisture</div>
708
+ <div class="highlight-value">{{ today_highlights.soil_moisture|round(3) }} m³/m³</div>
709
+ </div>
710
+ <!-- Sun Timing -->
711
+ <div class="highlight-card">
712
+ <div class="flex justify-between items-center">
713
+ <div class="text-center flex-1">
714
+ <i class="fas fa-sunrise highlight-icon"></i>
715
+ <div class="highlight-title">Sunrise</div>
716
+ <div class="highlight-value text-lg">{{ today_highlights.sunrise }}</div>
717
+ </div>
718
+ <div class="border-r border-gray-200 h-16 mx-4"></div>
719
+ <div class="text-center flex-1">
720
+ <i class="fas fa-sunset highlight-icon"></i>
721
+ <div class="highlight-title">Sunset</div>
722
+ <div class="highlight-value text-lg">{{ today_highlights.sunset }}</div>
723
+ </div>
724
+ </div>
725
+ </div>
726
+ </div>
727
+ </section>
728
+
729
+ <!-- 14-Day Forecast -->
730
+ <section class="forecast-section">
731
+ <h2 class="section-title">
732
+ <i class="fas fa-calendar-alt"></i>
733
+ 14-Day Forecast
734
+ </h2>
735
+ <div class="forecast-grid">
736
+ {% for day in forecast_list %}
737
+ <div class="forecast-card">
738
+ <div class="forecast-day">{{ day.day_name }}</div>
739
+ <div class="forecast-date">{{ day.date_str }}</div>
740
+ <!-- Conditional Forecast Icon -->
741
+ {% if day.icon == "☀️" %}
742
+ <i class="fas fa-sun forecast-icon"></i>
743
+ {% elif day.icon == "⛅" %}
744
+ <i class="fas fa-cloud-sun forecast-icon"></i>
745
+ {% elif day.icon == "🌫️" %}
746
+ <i class="fas fa-smog forecast-icon"></i>
747
+ {% elif day.icon == "🌦️" %}
748
+ <i class="fas fa-cloud-showers-heavy forecast-icon"></i>
749
+ {% elif day.icon == "🌧️" %}
750
+ <i class="fas fa-cloud-rain forecast-icon"></i>
751
+ {% elif day.icon == "❄️" %}
752
+ <i class="fas fa-snowflake forecast-icon"></i>
753
+ {% elif day.icon == "⛈️" %}
754
+ <i class="fas fa-bolt forecast-icon"></i>
755
+ {% else %}
756
+ <i class="fas fa-question forecast-icon"></i>
757
+ {% endif %}
758
+ <div class="text-sm text-green-700 font-medium">{{ day.desc }}</div>
759
+ <div class="forecast-temp">{{ day.avg_temp }}°C</div>
760
+ <div class="temp-range">
761
+ <span><i class="fas fa-arrow-down text-blue-500"></i> {{ day.tmin }}°C</span>
762
+ <span><i class="fas fa-arrow-up text-red-500"></i> {{ day.tmax }}°C</span>
763
+ </div>
764
+ <div class="mt-3">
765
+ <div class="text-sm font-medium mb-1">Day Temperatures</div>
766
+ <div class="text-sm">Morning: {{ day.morning_temp }}°C</div>
767
+ <div class="text-sm">Evening: {{ day.evening_temp }}°C</div>
768
+ </div>
769
+ <div class="sunrise-sunset">
770
+ <div class="flex items-center justify-center gap-1 mb-1">
771
+ <i class="fas fa-sun text-yellow-500"></i>
772
+ {{ day.sunrise }}
773
+ </div>
774
+ <div class="flex items-center justify-center gap-1">
775
+ <i class="fas fa-moon text-gray-600"></i>
776
+ {{ day.sunset }}
777
+ </div>
778
+ </div>
779
+ </div>
780
+ {% endfor %}
781
+ </div>
782
+ </section>
783
+
784
+ <!-- Footer -->
785
+ <footer class="footer-note">
786
+ <p>Last updated: {{ current_time }}</p>
787
+ </footer>
788
+ </main>
789
+ </div>
790
+
791
+ <!-- Scripts -->
792
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
793
+ <script>
794
+ // Initialize interactive behaviors
795
+ $(document).ready(function() {
796
+ // Add loading animation on form submission
797
+ $('form').on('submit', function() {
798
+ $('.main-content').addClass('loading');
799
+ });
800
+
801
+ // Animate cards on scroll using IntersectionObserver
802
+ const observerOptions = {
803
+ threshold: 0.1,
804
+ rootMargin: '0px 0px -50px 0px'
805
+ };
806
+ const observer = new IntersectionObserver((entries) => {
807
+ entries.forEach(entry => {
808
+ if (entry.isIntersecting) {
809
+ entry.target.style.opacity = '1';
810
+ entry.target.style.transform = 'translateY(0)';
811
+ }
812
+ });
813
+ }, observerOptions);
814
+ document.querySelectorAll('.highlight-card, .forecast-card').forEach(card => {
815
+ card.style.opacity = '0';
816
+ card.style.transform = 'translateY(20px)';
817
+ observer.observe(card);
818
+ });
819
+
820
+ // Real-time input validation for latitude and longitude
821
+ const validateInput = (input) => {
822
+ const value = parseFloat(input.value);
823
+ if (input.id === 'lat') {
824
+ if (value < -90 || value > 90) {
825
+ input.setCustomValidity('Latitude must be between -90 and 90 degrees');
826
+ } else {
827
+ input.setCustomValidity('');
828
+ }
829
+ } else if (input.id === 'lon') {
830
+ if (value < -180 || value > 180) {
831
+ input.setCustomValidity('Longitude must be between -180 and 180 degrees');
832
+ } else {
833
+ input.setCustomValidity('');
834
+ }
835
+ }
836
+ };
837
+ document.querySelectorAll('.input-field').forEach(input => {
838
+ input.addEventListener('input', () => validateInput(input));
839
+ });
840
+ });
841
+ </script>
842
+ </body>
843
+ </html>