|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<title>Modern Weather Dashboard</title> |
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet" /> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap'); |
|
|
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
:root { |
|
--primary-green: #10b981; |
|
--light-green: #d1fae5; |
|
--dark-green: #065f46; |
|
--gradient-start: #059669; |
|
--gradient-end: #047857; |
|
} |
|
|
|
body { |
|
font-family: 'Plus Jakarta Sans', sans-serif; |
|
background: #f0fdf4; |
|
color: #1f2937; |
|
line-height: 1.5; |
|
} |
|
|
|
|
|
@keyframes fadeInUp { |
|
from { |
|
opacity: 0; |
|
transform: translateY(20px); |
|
} |
|
to { |
|
opacity: 1; |
|
transform: translateY(0); |
|
} |
|
} |
|
|
|
@keyframes slideInLeft { |
|
from { |
|
opacity: 0; |
|
transform: translateX(-20px); |
|
} |
|
to { |
|
opacity: 1; |
|
transform: translateX(0); |
|
} |
|
} |
|
|
|
@keyframes pulse { |
|
0% { transform: scale(1); } |
|
50% { transform: scale(1.05); } |
|
100% { transform: scale(1); } |
|
} |
|
|
|
|
|
.container { |
|
display: flex; |
|
min-height: 100vh; |
|
flex-direction: row; |
|
} |
|
|
|
|
|
.sidebar { |
|
width: 300px; |
|
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end)); |
|
padding: 2rem; |
|
color: white; |
|
position: fixed; |
|
height: 100vh; |
|
overflow-y: auto; |
|
animation: slideInLeft 0.5s ease-out; |
|
box-shadow: 4px 0 15px rgba(0,0,0,0.1); |
|
z-index: 10; |
|
} |
|
|
|
.sidebar-header { |
|
display: flex; |
|
align-items: center; |
|
gap: 0.75rem; |
|
margin-bottom: 2rem; |
|
padding-bottom: 1rem; |
|
border-bottom: 1px solid rgba(255,255,255,0.2); |
|
} |
|
|
|
.sidebar-header i { |
|
font-size: 1.5rem; |
|
} |
|
|
|
.input-group { |
|
margin-bottom: 1.5rem; |
|
} |
|
|
|
.input-group label { |
|
display: block; |
|
margin-bottom: 0.5rem; |
|
font-weight: 500; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.input-field { |
|
width: 100%; |
|
padding: 0.75rem; |
|
border-radius: 8px; |
|
border: 1px solid rgba(255,255,255,0.2); |
|
background: rgba(255,255,255,0.1); |
|
color: white; |
|
transition: all 0.3s ease; |
|
} |
|
.input-field::placeholder { |
|
color: rgba(255,255,255,0.7); |
|
} |
|
|
|
.input-field:focus { |
|
outline: none; |
|
border-color: rgba(255,255,255,0.5); |
|
background: rgba(255,255,255,0.15); |
|
} |
|
|
|
.search-button { |
|
width: 100%; |
|
padding: 0.75rem; |
|
border-radius: 8px; |
|
background: white; |
|
color: var(--dark-green); |
|
font-weight: 600; |
|
border: none; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
gap: 0.5rem; |
|
} |
|
|
|
.search-button:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1); |
|
} |
|
|
|
|
|
.main-content { |
|
flex: 1; |
|
margin-left: 300px; |
|
padding: 2rem; |
|
animation: fadeInUp 0.5s ease-out; |
|
} |
|
|
|
|
|
.header-card { |
|
background: white; |
|
border-radius: 16px; |
|
padding: 2rem; |
|
margin-bottom: 2rem; |
|
box-shadow: 0 4px 20px rgba(0,0,0,0.05); |
|
animation: fadeInUp 0.5s ease-out; |
|
display: flex; |
|
flex-wrap: wrap; |
|
justify-content: space-between; |
|
align-items: center; |
|
} |
|
|
|
.header-location { |
|
font-size: 1.5rem; |
|
font-weight: 700; |
|
color: var(--dark-green); |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.timestamp { |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
padding: 0.5rem 1rem; |
|
background: var(--light-green); |
|
border-radius: 8px; |
|
font-size: 0.9rem; |
|
color: var(--dark-green); |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.current-temp { |
|
font-size: 3.5rem; |
|
font-weight: 700; |
|
color: var(--dark-green); |
|
display: flex; |
|
align-items: center; |
|
gap: 1rem; |
|
} |
|
|
|
.weather-icon { |
|
font-size: 3rem; |
|
color: var(--primary-green); |
|
} |
|
|
|
.weather-details { |
|
display: flex; |
|
gap: 2rem; |
|
margin-top: 1rem; |
|
flex-wrap: wrap; |
|
} |
|
|
|
.weather-detail { |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
color: var(--dark-green); |
|
} |
|
|
|
|
|
.alert-notification { |
|
animation: fadeInUp 0.5s ease-out; |
|
border-radius: 8px; |
|
} |
|
|
|
|
|
.highlights { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); |
|
gap: 1.5rem; |
|
margin-bottom: 2rem; |
|
} |
|
|
|
.highlight-card { |
|
background: white; |
|
border-radius: 16px; |
|
padding: 1.5rem; |
|
box-shadow: 0 4px 15px rgba(0,0,0,0.05); |
|
transition: all 0.3s ease; |
|
animation: fadeInUp 0.5s ease-out; |
|
} |
|
|
|
.highlight-card:hover { |
|
transform: translateY(-5px); |
|
box-shadow: 0 8px 25px rgba(0,0,0,0.1); |
|
} |
|
|
|
.highlight-icon { |
|
font-size: 2rem; |
|
color: var(--primary-green); |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.highlight-title { |
|
font-size: 1rem; |
|
font-weight: 600; |
|
color: var(--dark-green); |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
.highlight-value { |
|
font-size: 1.75rem; |
|
font-weight: 700; |
|
color: var(--primary-green); |
|
} |
|
|
|
|
|
.forecast-section { |
|
margin-top: 2rem; |
|
} |
|
|
|
.section-title { |
|
font-size: 1.5rem; |
|
font-weight: 700; |
|
color: var(--dark-green); |
|
margin-bottom: 1.5rem; |
|
display: flex; |
|
align-items: center; |
|
gap: 0.75rem; |
|
} |
|
|
|
.forecast-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); |
|
gap: 1.5rem; |
|
} |
|
|
|
.forecast-card { |
|
background: white; |
|
border-radius: 16px; |
|
padding: 1.5rem; |
|
text-align: center; |
|
box-shadow: 0 4px 15px rgba(0,0,0,0.05); |
|
transition: all 0.3s ease; |
|
animation: fadeInUp 0.5s ease-out; |
|
} |
|
|
|
.forecast-card:hover { |
|
transform: translateY(-5px); |
|
box-shadow: 0 8px 25px rgba(0,0,0,0.1); |
|
cursor: pointer; |
|
} |
|
|
|
.forecast-day { |
|
font-weight: 700; |
|
color: var(--dark-green); |
|
font-size: 1.1rem; |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
.forecast-date { |
|
color: #6b7280; |
|
font-size: 0.9rem; |
|
margin-bottom: 1rem; |
|
} |
|
|
|
|
|
.forecast-icon { |
|
font-size: 2.5rem; |
|
margin: 1rem 0; |
|
} |
|
|
|
.forecast-temp { |
|
font-size: 1.5rem; |
|
font-weight: 700; |
|
color: var(--primary-green); |
|
margin: 0.5rem 0; |
|
} |
|
|
|
.temp-range { |
|
display: flex; |
|
justify-content: center; |
|
gap: 1rem; |
|
color: #6b7280; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.sunrise-sunset { |
|
margin-top: 1rem; |
|
padding-top: 1rem; |
|
border-top: 1px solid #e5e7eb; |
|
font-size: 0.9rem; |
|
color: #6b7280; |
|
} |
|
|
|
|
|
.footer-note { |
|
margin-top: 2rem; |
|
color: #777; |
|
font-size: 0.8rem; |
|
text-align: center; |
|
} |
|
|
|
|
|
|
|
|
|
@media (max-width: 1024px) { |
|
.sidebar { |
|
width: 260px; |
|
padding: 1.5rem; |
|
} |
|
.main-content { |
|
margin-left: 260px; |
|
padding: 1.5rem; |
|
} |
|
.header-location { |
|
font-size: 1.3rem; |
|
} |
|
.current-temp { |
|
font-size: 3rem; |
|
} |
|
.weather-icon { |
|
font-size: 2.5rem; |
|
} |
|
.highlight-value { |
|
font-size: 1.5rem; |
|
} |
|
.section-title { |
|
font-size: 1.3rem; |
|
} |
|
.forecast-temp { |
|
font-size: 1.3rem; |
|
} |
|
} |
|
|
|
|
|
@media (max-width: 768px) { |
|
.container { |
|
flex-direction: column; |
|
} |
|
.sidebar { |
|
width: 100%; |
|
height: auto; |
|
position: relative; |
|
padding: 1.5rem 1rem; |
|
box-shadow: 0 4px 15px rgba(0,0,0,0.1); |
|
border-bottom-left-radius: 0; |
|
border-bottom-right-radius: 0; |
|
} |
|
.main-content { |
|
margin-left: 0; |
|
padding: 1.5rem 1rem; |
|
} |
|
.sidebar-header { |
|
margin-bottom: 1.5rem; |
|
justify-content: center; |
|
} |
|
.input-group { |
|
margin-bottom: 1rem; |
|
} |
|
.search-button { |
|
margin-top: 1rem; |
|
} |
|
.header-card { |
|
flex-direction: column; |
|
align-items: flex-start; |
|
padding: 1.5rem; |
|
} |
|
.header-location, .timestamp { |
|
margin-bottom: 0.75rem; |
|
width: 100%; |
|
} |
|
.header-location { |
|
font-size: 1.2rem; |
|
} |
|
.current-temp { |
|
font-size: 2.5rem; |
|
gap: 0.75rem; |
|
margin-top: 1rem; |
|
flex-wrap: wrap; |
|
} |
|
.weather-icon { |
|
font-size: 2.2rem; |
|
} |
|
.header-card .flex.items-center.gap-4 { |
|
flex-direction: column; |
|
align-items: flex-start; |
|
width: 100%; |
|
margin-top: 1rem; |
|
} |
|
.header-card .text-right { |
|
text-align: left; |
|
width: 100%; |
|
margin-top: 0.5rem; |
|
} |
|
.weather-details { |
|
flex-direction: column; |
|
gap: 0.75rem; |
|
margin-top: 0.75rem; |
|
} |
|
.highlights { |
|
grid-template-columns: 1fr; |
|
gap: 1rem; |
|
} |
|
.forecast-grid { |
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); |
|
gap: 1rem; |
|
} |
|
.highlight-icon { |
|
font-size: 1.75rem; |
|
margin-bottom: 0.75rem; |
|
} |
|
.highlight-value { |
|
font-size: 1.4rem; |
|
} |
|
.section-title { |
|
font-size: 1.2rem; |
|
margin-bottom: 1rem; |
|
} |
|
.forecast-card { |
|
padding: 1rem; |
|
} |
|
.forecast-icon { |
|
font-size: 2rem; |
|
} |
|
.forecast-day { |
|
font-size: 1rem; |
|
} |
|
.forecast-temp { |
|
font-size: 1.2rem; |
|
} |
|
.footer-note { |
|
margin-top: 1.5rem; |
|
} |
|
} |
|
|
|
|
|
@media (max-width: 480px) { |
|
.sidebar { |
|
padding: 1rem; |
|
} |
|
.main-content { |
|
padding: 1rem; |
|
} |
|
.sidebar-header { |
|
margin-bottom: 1rem; |
|
} |
|
.sidebar-header h1 { |
|
font-size: 1.2rem; |
|
} |
|
.header-location { |
|
font-size: 1rem; |
|
} |
|
.timestamp { |
|
font-size: 0.8rem; |
|
padding: 0.4rem 0.8rem; |
|
} |
|
.current-temp { |
|
font-size: 2rem; |
|
} |
|
.weather-icon { |
|
font-size: 1.8rem; |
|
} |
|
.header-card .text-right .text-lg { |
|
font-size: 1rem; |
|
} |
|
.weather-detail { |
|
font-size: 0.9rem; |
|
} |
|
.highlight-card { |
|
padding: 1.2rem; |
|
} |
|
.highlight-icon { |
|
font-size: 1.5rem; |
|
} |
|
.highlight-title { |
|
font-size: 0.9rem; |
|
} |
|
.highlight-value { |
|
font-size: 1.2rem; |
|
} |
|
.section-title { |
|
font-size: 1.1rem; |
|
} |
|
.forecast-grid { |
|
grid-template-columns: 1fr; |
|
} |
|
.forecast-icon { |
|
font-size: 1.8rem; |
|
} |
|
.forecast-temp { |
|
font-size: 1.1rem; |
|
} |
|
.temp-range, .sunrise-sunset { |
|
font-size: 0.85rem; |
|
} |
|
.footer-note { |
|
font-size: 0.75rem; |
|
margin-top: 1rem; |
|
} |
|
} |
|
|
|
|
|
.loading { |
|
animation: pulse 1.5s infinite; |
|
} |
|
|
|
|
|
.sidebar::-webkit-scrollbar { |
|
width: 8px; |
|
} |
|
|
|
.sidebar::-webkit-scrollbar-track { |
|
background: rgba(255,255,255,0.1); |
|
border-radius: 4px; |
|
} |
|
|
|
.sidebar::-webkit-scrollbar-thumb { |
|
background: rgba(255,255,255,0.3); |
|
border-radius: 4px; |
|
} |
|
|
|
.sidebar::-webkit-scrollbar-thumb:hover { |
|
background: rgba(255,255,255,0.4); |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
|
|
<aside class="sidebar"> |
|
<div class="sidebar-header"> |
|
<i class="fas fa-cloud-sun"></i> |
|
<h1 class="text-xl font-bold">Weather Dashboard</h1> |
|
</div> |
|
<form method="POST" action="/"> |
|
<div class="input-group"> |
|
<label for="lat">Latitude</label> |
|
<input type="number" step="any" id="lat" name="lat" class="input-field" value="{{ lat }}" placeholder="Enter latitude..." /> |
|
</div> |
|
<div class="input-group"> |
|
<label for="lon">Longitude</label> |
|
<input type="number" step="any" id="lon" name="lon" class="input-field" value="{{ lon }}" placeholder="Enter longitude..." /> |
|
</div> |
|
<button type="submit" class="search-button"> |
|
<i class="fas fa-search"></i> |
|
Get Weather |
|
</button> |
|
</form> |
|
</aside> |
|
|
|
|
|
<main class="main-content"> |
|
|
|
<section class="header-card"> |
|
<div> |
|
<div class="header-location"> |
|
<i class="fas fa-map-marker-alt"></i> |
|
{{ location_address }} |
|
</div> |
|
<div class="timestamp"> |
|
<i class="far fa-clock"></i> |
|
{{ current_time }} |
|
<span class="text-sm opacity-75">{{ timezone }}</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center gap-4"> |
|
<div class="current-temp"> |
|
{{ current_temp }}°C |
|
{% if current_icon == "☀️" %} |
|
<i class="fas fa-sun weather-icon"></i> |
|
{% elif current_icon == "⛅" %} |
|
<i class="fas fa-cloud-sun weather-icon"></i> |
|
{% elif current_icon == "🌫️" %} |
|
<i class="fas fa-smog weather-icon"></i> |
|
{% elif current_icon == "🌦️" %} |
|
<i class="fas fa-cloud-showers-heavy weather-icon"></i> |
|
{% elif current_icon == "🌧️" %} |
|
<i class="fas fa-cloud-rain weather-icon"></i> |
|
{% elif current_icon == "❄️" %} |
|
<i class="fas fa-snowflake weather-icon"></i> |
|
{% elif current_icon == "⛈️" %} |
|
<i class="fas fa-bolt weather-icon"></i> |
|
{% else %} |
|
<i class="fas fa-question weather-icon"></i> |
|
{% endif %} |
|
</div> |
|
<div class="text-right"> |
|
<div class="text-lg font-medium text-green-700">{{ current_desc }}</div> |
|
<div class="weather-details"> |
|
<div class="weather-detail"> |
|
<i class="fas fa-temperature-low"></i> |
|
Feels like: {{ feels_like }}°C |
|
</div> |
|
<div class="weather-detail"> |
|
<i class="fas fa-wind"></i> |
|
{{ current_wind_speed }} km/h |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</section> |
|
|
|
|
|
{% if alerts_sent %} |
|
<div class="alert-notification bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-4" role="alert"> |
|
<p class="font-bold"><i class="fas fa-exclamation-triangle"></i> Weather Alert Sent</p> |
|
<p>Alerts for upcoming hazardous weather conditions Near Your Farm have been sent to your WhatsApp.</p> |
|
</div> |
|
{% endif %} |
|
|
|
|
|
<section> |
|
<h2 class="section-title"> |
|
<i class="fas fa-chart-line"></i> |
|
Today's Highlights |
|
</h2> |
|
<div class="highlights"> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-tint highlight-icon"></i> |
|
<div class="highlight-title">Humidity</div> |
|
<div class="highlight-value">{{ today_highlights.humidity }}%</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-wind highlight-icon"></i> |
|
<div class="highlight-title">Wind Speed</div> |
|
<div class="highlight-value">{{ today_highlights.windspeed }} km/h</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-sun highlight-icon"></i> |
|
<div class="highlight-title">UV Index</div> |
|
<div class="highlight-value">{{ today_highlights.uv_index }}</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-compress-alt highlight-icon"></i> |
|
<div class="highlight-title">Pressure</div> |
|
<div class="highlight-value">{{ today_highlights.pressure|round(1) }} hPa</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-cloud highlight-icon"></i> |
|
<div class="highlight-title">Cloud Cover</div> |
|
<div class="highlight-value">{{ today_highlights.clouds }}%</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-cloud-rain highlight-icon"></i> |
|
<div class="highlight-title">Precipitation</div> |
|
<div class="highlight-value">{{ today_highlights.precipitation|round(2) }} mm</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<i class="fas fa-water highlight-icon"></i> |
|
<div class="highlight-title">Soil Moisture</div> |
|
<div class="highlight-value">{{ today_highlights.soil_moisture|round(3) }} m³/m³</div> |
|
</div> |
|
|
|
<div class="highlight-card"> |
|
<div class="flex justify-between items-center"> |
|
<div class="text-center flex-1"> |
|
<i class="fas fa-sunrise highlight-icon"></i> |
|
<div class="highlight-title">Sunrise</div> |
|
<div class="highlight-value text-lg">{{ today_highlights.sunrise }}</div> |
|
</div> |
|
<div class="border-r border-gray-200 h-16 mx-4"></div> |
|
<div class="text-center flex-1"> |
|
<i class="fas fa-sunset highlight-icon"></i> |
|
<div class="highlight-title">Sunset</div> |
|
<div class="highlight-value text-lg">{{ today_highlights.sunset }}</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</section> |
|
|
|
|
|
<section class="forecast-section"> |
|
<h2 class="section-title"> |
|
<i class="fas fa-calendar-alt"></i> |
|
14-Day Forecast |
|
</h2> |
|
<div class="forecast-grid"> |
|
{% for day in forecast_list %} |
|
<div class="forecast-card"> |
|
<div class="forecast-day">{{ day.day_name }}</div> |
|
<div class="forecast-date">{{ day.date_str }}</div> |
|
|
|
{% if day.icon == "☀️" %} |
|
<i class="fas fa-sun forecast-icon"></i> |
|
{% elif day.icon == "⛅" %} |
|
<i class="fas fa-cloud-sun forecast-icon"></i> |
|
{% elif day.icon == "🌫️" %} |
|
<i class="fas fa-smog forecast-icon"></i> |
|
{% elif day.icon == "🌦️" %} |
|
<i class="fas fa-cloud-showers-heavy forecast-icon"></i> |
|
{% elif day.icon == "🌧️" %} |
|
<i class="fas fa-cloud-rain forecast-icon"></i> |
|
{% elif day.icon == "❄️" %} |
|
<i class="fas fa-snowflake forecast-icon"></i> |
|
{% elif day.icon == "⛈️" %} |
|
<i class="fas fa-bolt forecast-icon"></i> |
|
{% else %} |
|
<i class="fas fa-question forecast-icon"></i> |
|
{% endif %} |
|
<div class="text-sm text-green-700 font-medium">{{ day.desc }}</div> |
|
<div class="forecast-temp">{{ day.avg_temp }}°C</div> |
|
<div class="temp-range"> |
|
<span><i class="fas fa-arrow-down text-blue-500"></i> {{ day.tmin }}°C</span> |
|
<span><i class="fas fa-arrow-up text-red-500"></i> {{ day.tmax }}°C</span> |
|
</div> |
|
<div class="mt-3"> |
|
<div class="text-sm font-medium mb-1">Day Temperatures</div> |
|
<div class="text-sm">Morning: {{ day.morning_temp }}°C</div> |
|
<div class="text-sm">Evening: {{ day.evening_temp }}°C</div> |
|
</div> |
|
<div class="sunrise-sunset"> |
|
<div class="flex items-center justify-center gap-1 mb-1"> |
|
<i class="fas fa-sun text-yellow-500"></i> |
|
{{ day.sunrise }} |
|
</div> |
|
<div class="flex items-center justify-center gap-1"> |
|
<i class="fas fa-moon text-gray-600"></i> |
|
{{ day.sunset }} |
|
</div> |
|
</div> |
|
</div> |
|
{% endfor %} |
|
</div> |
|
</section> |
|
|
|
|
|
<footer class="footer-note"> |
|
<p>Last updated: {{ current_time }}</p> |
|
</footer> |
|
</main> |
|
</div> |
|
|
|
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
|
<script> |
|
|
|
$(document).ready(function() { |
|
|
|
$('form').on('submit', function() { |
|
$('.main-content').addClass('loading'); |
|
}); |
|
|
|
|
|
const observerOptions = { |
|
threshold: 0.1, |
|
rootMargin: '0px 0px -50px 0px' |
|
}; |
|
const observer = new IntersectionObserver((entries) => { |
|
entries.forEach(entry => { |
|
if (entry.isIntersecting) { |
|
entry.target.style.opacity = '1'; |
|
entry.target.style.transform = 'translateY(0)'; |
|
} |
|
}); |
|
}, observerOptions); |
|
document.querySelectorAll('.highlight-card, .forecast-card').forEach(card => { |
|
card.style.opacity = '0'; |
|
card.style.transform = 'translateY(20px)'; |
|
observer.observe(card); |
|
}); |
|
|
|
|
|
const validateInput = (input) => { |
|
const value = parseFloat(input.value); |
|
if (input.id === 'lat') { |
|
if (value < -90 || value > 90) { |
|
input.setCustomValidity('Latitude must be between -90 and 90 degrees'); |
|
} else { |
|
input.setCustomValidity(''); |
|
} |
|
} else if (input.id === 'lon') { |
|
if (value < -180 || value > 180) { |
|
input.setCustomValidity('Longitude must be between -180 and 180 degrees'); |
|
} else { |
|
input.setCustomValidity(''); |
|
} |
|
} |
|
}; |
|
document.querySelectorAll('.input-field').forEach(input => { |
|
input.addEventListener('input', () => validateInput(input)); |
|
}); |
|
}); |
|
</script> |
|
</body> |
|
</html> |