fix: calendar strip NaN days and 3-day weather forecast
This commit is contained in:
@@ -45,6 +45,22 @@ export async function GET() {
|
|||||||
weathercode: daily.weathercode?.[0],
|
weathercode: daily.weathercode?.[0],
|
||||||
summary: codeMap[daily.weathercode?.[0]] || 'Unknown',
|
summary: codeMap[daily.weathercode?.[0]] || 'Unknown',
|
||||||
},
|
},
|
||||||
|
tomorrow: {
|
||||||
|
maxC: celsius(daily.temperature_2m_max?.[1]),
|
||||||
|
minC: celsius(daily.temperature_2m_min?.[1]),
|
||||||
|
maxF: fahrenheit(daily.temperature_2m_max?.[1]),
|
||||||
|
minF: fahrenheit(daily.temperature_2m_min?.[1]),
|
||||||
|
weathercode: daily.weathercode?.[1],
|
||||||
|
summary: codeMap[daily.weathercode?.[1]] || 'Unknown',
|
||||||
|
},
|
||||||
|
dayAfter: {
|
||||||
|
maxC: celsius(daily.temperature_2m_max?.[2]),
|
||||||
|
minC: celsius(daily.temperature_2m_min?.[2]),
|
||||||
|
maxF: fahrenheit(daily.temperature_2m_max?.[2]),
|
||||||
|
minF: fahrenheit(daily.temperature_2m_min?.[2]),
|
||||||
|
weathercode: daily.weathercode?.[2],
|
||||||
|
summary: codeMap[daily.weathercode?.[2]] || 'Unknown',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -12,8 +12,32 @@ interface CalendarEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface WeatherData {
|
interface WeatherData {
|
||||||
current?: { celsius: number; fahrenheit: number; summary: string };
|
current?: { celsius: number; fahrenheit: number; summary: string; weathercode: number };
|
||||||
today?: { maxC: number; minC: number; maxF: number; minF: number; summary: string };
|
today?: { maxC: number; minC: number; maxF: number; minF: number; summary: string; weathercode: number };
|
||||||
|
tomorrow?: { maxC: number; minC: number; maxF: number; minF: number; summary: string; weathercode: number };
|
||||||
|
dayAfter?: { maxC: number; minC: number; maxF: number; minF: number; summary: string; weathercode: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
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: '⛈️'
|
||||||
|
};
|
||||||
|
|
||||||
|
function getWeatherIcon(code: number | undefined): string {
|
||||||
|
return code !== undefined ? (weatherIcons[code] || '🌤️') : '🌤️';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDateLabel(daysFromToday: number): { month: string; day: number; label: string } {
|
||||||
|
const d = new Date();
|
||||||
|
d.setDate(d.getDate() + daysFromToday);
|
||||||
|
return {
|
||||||
|
month: d.toLocaleString('en-US', { month: 'short' }),
|
||||||
|
day: d.getDate(),
|
||||||
|
label: daysFromToday === 0 ? 'Today' : daysFromToday === 1 ? 'Tomorrow' : 'Day After'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const gold = '#E8A849';
|
const gold = '#E8A849';
|
||||||
@@ -35,8 +59,11 @@ function formatMonthDay(dateStr: string) {
|
|||||||
function daysUntil(dateStr: string) {
|
function daysUntil(dateStr: string) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
const d = new Date(dateStr + 'T00:00:00');
|
// Handle both ISO timestamp and date-only formats
|
||||||
|
const d = dateStr.includes('T') ? new Date(dateStr) : new Date(dateStr + 'T00:00:00');
|
||||||
|
d.setHours(0, 0, 0, 0);
|
||||||
const diff = Math.ceil((d.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
|
const diff = Math.ceil((d.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
if (isNaN(diff)) return 'Unknown';
|
||||||
if (diff === 0) return 'Today';
|
if (diff === 0) return 'Today';
|
||||||
if (diff === 1) return 'Tomorrow';
|
if (diff === 1) return 'Tomorrow';
|
||||||
if (diff < 0) return 'Past';
|
if (diff < 0) return 'Past';
|
||||||
@@ -61,21 +88,40 @@ export default function CalendarStrip() {
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const cards = events.map((e) => ({ ...e, isWeather: false })).concat(
|
// Build weather cards: today, tomorrow, day after
|
||||||
weather
|
const weatherCards = weather
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
id: 9999,
|
id: 9997,
|
||||||
date: new Date().toISOString().split('T')[0],
|
date: new Date().toISOString().split('T')[0],
|
||||||
title: `${weather.current?.celsius ?? '--'}°C / ${weather.current?.fahrenheit ?? '--'}°F ${weather.current?.summary ?? ''}`,
|
title: `${getWeatherIcon(weather.today?.weathercode)} ${weather.current?.celsius ?? '--'}°C`,
|
||||||
type: 'weather' as const,
|
type: 'weather' as const,
|
||||||
description: `Today ${weather.today?.maxC ?? '--'}°C / ${weather.today?.maxF ?? '--'}°F · ${weather.today?.minC ?? '--'}°C / ${weather.today?.minF ?? '--'}°F`,
|
description: `Today: ${weather.today?.maxC ?? '--'}° / ${weather.today?.minC ?? '--'}°C · ${weather.today?.summary ?? ''}`,
|
||||||
color: '#4FC3F7',
|
color: '#4FC3F7',
|
||||||
isWeather: true,
|
isWeather: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 9998,
|
||||||
|
date: new Date(Date.now() + 86400000).toISOString().split('T')[0],
|
||||||
|
title: `${getWeatherIcon(weather.tomorrow?.weathercode)} ${weather.tomorrow?.maxC ?? '--'}° / ${weather.tomorrow?.minC ?? '--'}°C`,
|
||||||
|
type: 'weather' as const,
|
||||||
|
description: `Tomorrow: ${weather.tomorrow?.summary ?? ''}`,
|
||||||
|
color: '#81D4FA',
|
||||||
|
isWeather: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9999,
|
||||||
|
date: new Date(Date.now() + 172800000).toISOString().split('T')[0],
|
||||||
|
title: `${getWeatherIcon(weather.dayAfter?.weathercode)} ${weather.dayAfter?.maxC ?? '--'}° / ${weather.dayAfter?.minC ?? '--'}°C`,
|
||||||
|
type: 'weather' as const,
|
||||||
|
description: `Day After: ${weather.dayAfter?.summary ?? ''}`,
|
||||||
|
color: '#4DD0E1',
|
||||||
|
isWeather: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
: []
|
: [];
|
||||||
);
|
|
||||||
|
const cards = [...events.map((e) => ({ ...e, isWeather: false })), ...weatherCards];
|
||||||
|
|
||||||
if (cards.length === 0) return null;
|
if (cards.length === 0) return null;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user