"use client"; import { CalendarDays, ChevronLeft, ChevronRight, Cloud, CloudFog, CloudLightning, CloudRain, CloudSun, MapPin, Minus, Newspaper, QrCode, Snowflake, Sparkles, Sun, Sunrise, Sunset, TrendingDown, TrendingUp, Wifi, X, } from "lucide-react"; import QRCode from "react-qr-code"; import { useEffect, useMemo, useRef, useState, type KeyboardEvent as ReactKeyboardEvent } from "react"; type CalendarEvent = { id: string; calendar: string; title: string; location?: string; start: string; end: string; allDay: boolean; tone: "cyan" | "amber" | "violet" | "emerald" | "rose"; }; type CalendarState = "loading" | "live" | "partial" | "stale" | "error"; type CalendarView = "agenda" | "week" | "month"; type WeatherDay = { date: string; code: number; condition: string; icon: "clear" | "partly-cloudy" | "cloudy" | "fog" | "rain" | "snow" | "storm"; high: number; low: number; sunrise: string; sunset: string; precipitationChance: number; }; type WeatherState = "loading" | "live" | "stale" | "error"; type Headline = { source: string; age: string; title: string; tone: "cyan" | "amber" | "violet"; url?: string; }; type Quote = { symbol: string; name: string; value: string; move: string; direction: "up" | "down" | "flat"; seriesId?: string; observedAt?: string; }; type MarketState = "loading" | "live" | "partial" | "stale" | "demo"; const demoHeadlines: Headline[] = [ { source: "OPENAI", age: "18m", title: "New tools push multimodal assistants deeper into everyday work", tone: "cyan" }, { source: "ARS TECHNICA", age: "42m", title: "Chipmakers rethink the data-center roadmap for efficient inference", tone: "amber" }, { source: "GOOGLE AI", age: "1h", title: "Research update explores smaller models with stronger reasoning", tone: "violet" }, { source: "WIRED", age: "2h", title: "The next wave of personal AI devices is taking shape", tone: "cyan" }, { source: "NVIDIA", age: "3h", title: "New accelerated-computing platform targets local AI workloads", tone: "amber" }, ]; const demoQuotes: Quote[] = [ { symbol: "S&P 500", name: "Previous close", value: "5,782.41", move: "+0.62%", direction: "up" }, { symbol: "NASDAQ", name: "Previous close", value: "18,439.17", move: "+0.91%", direction: "up" }, { symbol: "DOW", name: "Previous close", value: "42,114.40", move: "+0.38%", direction: "up" }, { symbol: "WTI", name: "Crude oil", value: "$67.42", move: "-1.18%", direction: "down" }, { symbol: "NAT GAS", name: "Henry Hub", value: "$3.12", move: "-0.54%", direction: "down" }, { symbol: "COPPER", name: "Per pound", value: "$4.31", move: "+0.21%", direction: "up" }, ]; const toneClasses = { cyan: "bg-cyan-300", amber: "bg-amber-300", violet: "bg-violet-300", emerald: "bg-emerald-300", rose: "bg-rose-300", }; const calendarToneColors = { cyan: "#67e8f9", amber: "#fcd34d", violet: "#c4b5fd", emerald: "#6ee7b7", rose: "#fda4af", }; const calendarViews: CalendarView[] = ["agenda", "week", "month"]; let organizerTimeZone = "America/Chicago"; const weatherIcons = { clear: Sun, "partly-cloudy": CloudSun, cloudy: Cloud, fog: CloudFog, rain: CloudRain, snow: Snowflake, storm: CloudLightning, }; function formatClock(date: Date | null) { if (!date) return { time: "--:--", period: "", date: "Loading dashboard…" }; const clock = new Intl.DateTimeFormat("en-US", { timeZone: organizerTimeZone, hour: "numeric", minute: "2-digit", }).formatToParts(date); return { time: `${clock.find((part) => part.type === "hour")?.value ?? ""}:${clock.find((part) => part.type === "minute")?.value ?? ""}`, period: clock.find((part) => part.type === "dayPeriod")?.value ?? "", date: new Intl.DateTimeFormat("en-US", { timeZone: organizerTimeZone, weekday: "long", month: "long", day: "numeric", }).format(date), }; } function organizerDateKey(date: Date) { const parts = new Intl.DateTimeFormat("en-US", { timeZone: organizerTimeZone, year: "numeric", month: "2-digit", day: "2-digit", }).formatToParts(date); const value = (part: string) => parts.find((entry) => entry.type === part)?.value ?? ""; return `${value("year")}-${value("month")}-${value("day")}`; } function allDayDate(value: string) { const [year, month, day] = value.split("-").map(Number); return new Date(Date.UTC(year, month - 1, day, 12)); } function addDateKey(value: string, days: number) { const date = allDayDate(value); date.setUTCDate(date.getUTCDate() + days); return date.toISOString().slice(0, 10); } function addMonthsToDateKey(value: string, months: number) { const [year, month] = value.split("-").map(Number); const date = new Date(Date.UTC(year, month - 1 + months, 1, 12)); return date.toISOString().slice(0, 10); } function startOfWeekKey(value: string) { const date = allDayDate(value); const mondayOffset = (date.getUTCDay() + 6) % 7; return addDateKey(value, -mondayOffset); } function weekDateKeys(value: string) { const start = startOfWeekKey(value); return Array.from({ length: 7 }, (_, index) => addDateKey(start, index)); } function monthDateKeys(value: string) { const first = `${value.slice(0, 7)}-01`; const start = startOfWeekKey(first); return Array.from({ length: 42 }, (_, index) => addDateKey(start, index)); } function eventDateKey(event: CalendarEvent) { return event.allDay ? event.start : organizerDateKey(new Date(event.start)); } function eventOccursOnDate(event: CalendarEvent, dateKey: string) { if (event.allDay) return event.start <= dateKey && event.end > dateKey; const start = organizerDateKey(new Date(event.start)); const inclusiveEnd = organizerDateKey(new Date(Math.max(Date.parse(event.start), Date.parse(event.end) - 1))); return start <= dateKey && inclusiveEnd >= dateKey; } function eventIsUpcoming(event: CalendarEvent, now: Date) { if (event.allDay) return event.end > organizerDateKey(now); return Date.parse(event.end) > now.getTime(); } function formatDateKey(value: string, options: Intl.DateTimeFormatOptions) { return new Intl.DateTimeFormat("en-US", { ...options, timeZone: "UTC" }).format(allDayDate(value)); } function compactEventTime(event: CalendarEvent, dateKey: string) { if (event.allDay) return "All day"; if (eventDateKey(event) !== dateKey) return "Continues"; return new Intl.DateTimeFormat("en-US", { timeZone: organizerTimeZone, hour: "numeric", minute: "2-digit", }).format(new Date(event.start)); } function formatSunTime(value: string) { const time = value.split("T")[1]?.slice(0, 5); if (!time) return "--"; const [hour, minute] = time.split(":").map(Number); const displayHour = hour % 12 || 12; return `${displayHour}:${String(minute).padStart(2, "0")}`; } function WeatherGlyph({ icon, className }: { icon: WeatherDay["icon"]; className?: string }) { const Glyph = weatherIcons[icon] ?? CloudSun; return