import { useMemo } from 'react'; import { format, isSameDay, startOfDay, addDays, isAfter } from 'date-fns'; import { Sparkles } from 'lucide-react'; import type { UpcomingItem, DashboardData } from '@/types'; interface DayBriefingProps { upcomingItems: UpcomingItem[]; dashboardData: DashboardData; weatherData?: { rain_chance: number; description: string } | null; } function getItemTime(item: UpcomingItem): string { if (item.datetime) { return format(new Date(item.datetime), 'h:mm a'); } return ''; } export default function DayBriefing({ upcomingItems, dashboardData, weatherData }: DayBriefingProps) { const briefing = useMemo(() => { const now = new Date(); const hour = now.getHours(); const today = startOfDay(now); const tomorrow = addDays(today, 1); const todayItems = upcomingItems.filter((item) => { const d = item.datetime ? new Date(item.datetime) : new Date(item.date); return isSameDay(startOfDay(d), today); }); const tomorrowItems = upcomingItems.filter((item) => { const d = item.datetime ? new Date(item.datetime) : new Date(item.date); return isSameDay(startOfDay(d), tomorrow); }); const todayEvents = dashboardData.todays_events; const activeReminders = dashboardData.active_reminders; const todayTodos = dashboardData.upcoming_todos.filter((t) => { if (!t.due_date) return false; return isSameDay(startOfDay(new Date(t.due_date)), today); }); const parts: string[] = []; // Night (9PM–5AM): Show today if items remain, then preview tomorrow if (hour >= 21 || hour < 5) { // Before 5 AM, "today" still matters — mention remaining items if (todayItems.length > 0 && hour < 5) { const remainingToday = todayEvents.filter((e) => e.end_datetime && isAfter(new Date(e.end_datetime), now)); if (remainingToday.length > 0) { parts.push(`${remainingToday.length} event${remainingToday.length > 1 ? 's' : ''} still on today.`); } else if (todayTodos.length > 0) { parts.push(`${todayTodos.length} task${todayTodos.length > 1 ? 's' : ''} due today.`); } else if (activeReminders.length > 0) { parts.push(`${activeReminders.length} reminder${activeReminders.length > 1 ? 's' : ''} set for today.`); } } if (tomorrowItems.length === 0) { parts.push('Tomorrow is clear — nothing scheduled.'); } else { const firstEvent = tomorrowItems.find((i) => i.type === 'event' && i.datetime); if (firstEvent) { parts.push( `You have ${tomorrowItems.length} item${tomorrowItems.length > 1 ? 's' : ''} tomorrow, starting with ${firstEvent.title} at ${getItemTime(firstEvent)}.` ); } else { parts.push( `You have ${tomorrowItems.length} item${tomorrowItems.length > 1 ? 's' : ''} lined up for tomorrow.` ); } } } // Morning (5AM–12PM) else if (hour < 12) { if (todayItems.length === 0) { parts.push('Your day is wide open — no events or tasks scheduled.'); } else { const eventCount = todayEvents.length; const todoCount = todayTodos.length; const segments: string[] = []; if (eventCount > 0) segments.push(`${eventCount} event${eventCount > 1 ? 's' : ''}`); if (todoCount > 0) segments.push(`${todoCount} task${todoCount > 1 ? 's' : ''} due`); if (segments.length > 0) { parts.push(`Today: ${segments.join(' and ')}.`); } const firstEvent = todayEvents.find((e) => { const d = new Date(e.start_datetime); return isAfter(d, now); }); if (firstEvent) { parts.push(`Up next is ${firstEvent.title} at ${format(new Date(firstEvent.start_datetime), 'h:mm a')}.`); } } } // Afternoon (12PM–5PM) else if (hour < 17) { const remainingEvents = todayEvents.filter((e) => e.end_datetime && isAfter(new Date(e.end_datetime), now)); const completedTodos = todayTodos.length === 0; if (remainingEvents.length === 0 && completedTodos) { parts.push('The rest of your afternoon is clear.'); } else { if (remainingEvents.length > 0) { parts.push( `${remainingEvents.length} event${remainingEvents.length > 1 ? 's' : ''} remaining this afternoon.` ); } if (todayTodos.length > 0) { parts.push(`${todayTodos.length} task${todayTodos.length > 1 ? 's' : ''} still due today.`); } } } // Evening (5PM–9PM) else { const eveningEvents = todayEvents.filter((e) => e.end_datetime && isAfter(new Date(e.end_datetime), now)); if (eveningEvents.length === 0 && tomorrowItems.length === 0) { parts.push('Nothing left tonight, and tomorrow is clear too.'); } else { if (eveningEvents.length > 0) { parts.push(`${eveningEvents.length} event${eveningEvents.length > 1 ? 's' : ''} left this evening.`); } if (tomorrowItems.length > 0) { parts.push(`Tomorrow has ${tomorrowItems.length} item${tomorrowItems.length > 1 ? 's' : ''} ahead.`); } } } // Reminder callout if (activeReminders.length > 0) { const nextReminder = activeReminders[0]; const remindTime = format(new Date(nextReminder.remind_at), 'h:mm a'); parts.push(`Don't forget: ${nextReminder.title} at ${remindTime}.`); } // Weather rain warning if (weatherData && weatherData.rain_chance > 40) { if (hour >= 5 && hour < 12) { parts.push(`There's a ${weatherData.rain_chance}% chance of rain today — might want to grab an umbrella.`); } else if (hour >= 12 && hour < 17) { parts.push("Heads up, rain's looking likely this afternoon."); } else { parts.push(`Forecasts show ${weatherData.rain_chance}% chance of rain tomorrow, you might want to plan ahead.`); } } return parts.join(' '); }, [upcomingItems, dashboardData, weatherData]); if (!briefing) return null; return (

{briefing}

); }