import { useData, usePanelState, usePublish, type PanelRef } from '@jaspers-ai/sdk' import { useEffect, useState } from 'react' import { fmt, int, sign } from './format' import './styles.css' interface Snap { symbol: string last: number | null bid: number | null ask: number | null change: number | null change_pct: number | null day_volume: number | null [field: string]: unknown } interface State { symbols?: string[] feed?: string } const DEFAULT = ['AAPL', 'MSFT', 'NVDA'] export function Watchlist({ panel }: { panel: PanelRef }) { const state = useData(`workspaces/${panel.workspaceId}/panels/${panel.id}/state`) as State | undefined if (state === undefined) return
Loading…
returnNo symbols. Add one above.
} function Rows(props: { panel: PanelRef; symbols: string[]; feed: string; onRemove: (s: string) => void }) { const { panel, symbols, feed, onRemove } = props const { data, error, refetch } = useData('alpaca/stock-snapshots', { symbols, feed }) // Refresh every 15 seconds while the view is open; each refresh is one quick read. useEffect(() => { const timer = setInterval(() => refetch?.(), 15_000) return () => clearInterval(timer) }, [refetch]) const rows = (data ?? []) as Snap[] usePublish(panel, { symbols: rows.map((r) => r.symbol), count: rows.length, rows: rows.slice(0, 50).map((r) => ({ symbol: r.symbol, last: r.last, change_pct: r.change_pct })), }) if (error) return{error}
if (!data) returnLoading…
const bySymbol = new Map(rows.map((r) => [r.symbol, r])) return (| Symbol | Last | Bid | Ask | Change | Change % | Volume | |
|---|---|---|---|---|---|---|---|
| {symbol} | {fmt(r?.last)} | {fmt(r?.bid)} | {fmt(r?.ask)} | {fmt(r?.change)} | {r ? `${fmt(r.change_pct)}%` : '–'} | {int(r?.day_volume)} |