feat: enable weather widget for Otavalo using Open-Meteo API (free, no key required)
This commit is contained in:
@@ -6,61 +6,31 @@ export default function WeatherWidget() {
|
||||
const [weather, setWeather] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [config, setConfig] = useState<any>(null);
|
||||
|
||||
// Load weather configuration
|
||||
useEffect(() => {
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/site-assets?key=weather_config');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.data) {
|
||||
const parsedConfig = JSON.parse(data.data);
|
||||
setConfig(parsedConfig);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load weather config:', err);
|
||||
}
|
||||
};
|
||||
loadConfig();
|
||||
}, []);
|
||||
|
||||
// Fetch weather data when config is loaded and enabled
|
||||
useEffect(() => {
|
||||
if (!config || !config.enabled || config.cities.length === 0) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchWeather = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// Use the first city in the list for now
|
||||
const city = config.cities[0];
|
||||
const res = await fetch(`/api/weather?lat=${city.lat}&lon=${city.lon}`);
|
||||
const res = await fetch('/api/weather');
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
setWeather(data);
|
||||
setWeather(data.data);
|
||||
} else {
|
||||
throw new Error('Failed to fetch weather data');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Weather API error:', err);
|
||||
setError('Failed to load weather data');
|
||||
setError('Failed to load weather');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchWeather();
|
||||
}, [config]);
|
||||
|
||||
if (!config?.enabled) return null;
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -78,49 +48,54 @@ export default function WeatherWidget() {
|
||||
}
|
||||
|
||||
if (error || !weather) {
|
||||
return (
|
||||
<div style={{
|
||||
background: 'rgba(255, 255, 255, 0.9)',
|
||||
borderRadius: '12px',
|
||||
padding: '12px 16px',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
border: '1px solid rgba(255,255,255,0.2)'
|
||||
}}>
|
||||
<div style={{ fontSize: '14px', color: '#666' }}>Weather unavailable</div>
|
||||
</div>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const current = weather.current;
|
||||
const temp = current.celsius;
|
||||
const icon = '🌤️';
|
||||
const today = weather.today;
|
||||
|
||||
const weatherIcons: Record<number, string> = {
|
||||
0: '☀️', 1: '🌤️', 2: '⛅', 3: '☁️',
|
||||
45: '🌫️', 48: '🌫️', 51: '🌦️', 53: '🌦️', 55: '🌦️',
|
||||
61: '🌧️', 63: '🌧️', 65: '🌧️', 71: '🌨️', 73: '🌨️',
|
||||
75: '🌨️', 80: '🌦️', 81: '🌦️', 82: '🌧️',
|
||||
95: '⛈️', 96: '⛈️', 99: '⛈️'
|
||||
};
|
||||
|
||||
const icon = weatherIcons[current.weathercode] || '🌤️';
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
background: 'rgba(255, 255, 255, 0.9)',
|
||||
background: 'rgba(255, 255, 255, 0.95)',
|
||||
borderRadius: '12px',
|
||||
padding: '12px 16px',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
border: '1px solid rgba(255,255,255,0.2)',
|
||||
minWidth: '180px'
|
||||
border: '1px solid rgba(255,255,255,0.3)',
|
||||
minWidth: '160px'
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
|
||||
<span style={{ fontSize: '20px' }}>{icon}</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
|
||||
<span style={{ fontSize: '28px' }}>{icon}</span>
|
||||
<div>
|
||||
<div style={{ fontSize: '18px', fontWeight: '600', color: '#333' }}>
|
||||
{temp}°C
|
||||
<div style={{ fontSize: '22px', fontWeight: '600', color: '#1a1a2e' }}>
|
||||
{current.celsius}°C
|
||||
</div>
|
||||
<div style={{ fontSize: '11px', color: '#666', marginTop: '2px' }}>
|
||||
{current.summary}
|
||||
</div>
|
||||
{config.cities[0] && (
|
||||
<div style={{ fontSize: '12px', color: '#666', marginTop: '2px' }}>
|
||||
{config.cities[0].name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: '11px', color: '#888', marginTop: '4px' }}>
|
||||
Updated: {new Date(current.time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: '8px',
|
||||
paddingTop: '8px',
|
||||
borderTop: '1px solid rgba(0,0,0,0.1)',
|
||||
fontSize: '11px',
|
||||
color: '#555'
|
||||
}}>
|
||||
<span>📍 Otavalo</span>
|
||||
<span>↑{today.maxC}° ↓{today.minC}°</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user