fix: calendar strip NaN days and 3-day weather forecast

This commit is contained in:
2026-06-29 20:37:50 -05:00
parent e0a82b945f
commit 8cc1d4bb96
2 changed files with 80 additions and 18 deletions
+16
View File
@@ -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) {
+64 -18
View File
@@ -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;