diff --git a/src/app/api/weather/route.ts b/src/app/api/weather/route.ts index 8ef53ab..94166b5 100644 --- a/src/app/api/weather/route.ts +++ b/src/app/api/weather/route.ts @@ -45,6 +45,22 @@ export async function GET() { weathercode: daily.weathercode?.[0], 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) { diff --git a/src/components/CalendarStrip.tsx b/src/components/CalendarStrip.tsx index e921a41..ce37609 100644 --- a/src/components/CalendarStrip.tsx +++ b/src/components/CalendarStrip.tsx @@ -12,8 +12,32 @@ interface CalendarEvent { } interface WeatherData { - current?: { celsius: number; fahrenheit: number; summary: string }; - today?: { maxC: number; minC: number; maxF: number; minF: number; summary: string }; + current?: { celsius: number; fahrenheit: number; summary: string; weathercode: number }; + 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 = { + 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'; @@ -35,8 +59,11 @@ function formatMonthDay(dateStr: string) { function daysUntil(dateStr: string) { const today = new Date(); 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)); + if (isNaN(diff)) return 'Unknown'; if (diff === 0) return 'Today'; if (diff === 1) return 'Tomorrow'; if (diff < 0) return 'Past'; @@ -61,21 +88,40 @@ export default function CalendarStrip() { .catch(() => {}); }, []); - const cards = events.map((e) => ({ ...e, isWeather: false })).concat( - weather - ? [ - { - id: 9999, - date: new Date().toISOString().split('T')[0], - title: `${weather.current?.celsius ?? '--'}°C / ${weather.current?.fahrenheit ?? '--'}°F ${weather.current?.summary ?? ''}`, - type: 'weather' as const, - description: `Today ${weather.today?.maxC ?? '--'}°C / ${weather.today?.maxF ?? '--'}°F · ${weather.today?.minC ?? '--'}°C / ${weather.today?.minF ?? '--'}°F`, - color: '#4FC3F7', - isWeather: true, - }, - ] - : [] - ); + // Build weather cards: today, tomorrow, day after + const weatherCards = weather + ? [ + { + id: 9997, + date: new Date().toISOString().split('T')[0], + title: `${getWeatherIcon(weather.today?.weathercode)} ${weather.current?.celsius ?? '--'}°C`, + type: 'weather' as const, + description: `Today: ${weather.today?.maxC ?? '--'}° / ${weather.today?.minC ?? '--'}°C · ${weather.today?.summary ?? ''}`, + color: '#4FC3F7', + 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;