"use client"; import { useEffect, useState } from "react"; import { useActiveSymbol } from "@/stores/active-symbol-store"; import { api, type WatchlistEntry, type Quote } from "@/lib/trpc"; /** * Compact watchlist sidebar with mini sparklines and quick symbol switching. * Shows portfolio summary at top. Replaces the full WatchlistPanel on the main page. */ interface MiniQuote extends Quote { sparkline?: number[]; } export function WatchlistSidebar() { const activeSymbol = useActiveSymbol((s) => s.activeSymbol); const setActive = useActiveSymbol((s) => s.setActive); const [entries, setEntries] = useState([]); const [quotes, setQuotes] = useState>(new Map()); const [draft, setDraft] = useState(""); const [loading, setLoading] = useState(true); // Portfolio summary const { data: holdings } = api.portfolio.holdings.useQuery(); const totalValue = (holdings ?? []).reduce((sum, h) => sum + h.shares * h.avg_cost, 0); const load = async () => { try { setEntries(await api.watchlists.list()); } catch { /* ignore */ } setLoading(false); }; useEffect(() => { load(); }, []); // Fetch quotes for watchlist symbols useEffect(() => { const fetchQuotes = async () => { const quoteMap = new Map(); for (const entry of entries) { try { const snap = await api.market.snapshot(entry.symbol); if (snap.quote && snap.candles) { const sparkline = snap.candles.slice(-20).map(c => c.c); quoteMap.set(entry.symbol, { ...snap.quote, symbol: entry.symbol, sparkline }); } } catch { /* ignore */ } } setQuotes(quoteMap); }; if (entries.length > 0) fetchQuotes(); }, [entries]); const add = async () => { if (!draft.trim()) return; const sym = draft.trim().toUpperCase(); await api.watchlists.addSymbol(sym); setDraft(""); load(); }; const remove = async (symbol: string) => { await api.watchlists.removeSymbol(symbol); load(); }; return ( ); }