import React, { useEffect, useState } from 'react' import { Loader2, TrendingUp, TrendingDown, Minus } from 'lucide-react' import { fetchBatchPredictions, fetchStockInfo, BatchPredictionItem, StockInfo } from '../api/stockApi' // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- interface WatchlistEntry { code: string info: StockInfo | null prediction: BatchPredictionItem | null error: string | null } interface Props { onSelect: (code: string) => void } // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const DEFAULT_WATCHLIST = ['2330', '2317', '2454', '0050', '006208'] const SIGNAL_EMOJI: Record = { BUY: '📈', SELL: '📉', HOLD: '➡️', } const SIGNAL_TEXT_CLASS: Record = { BUY: 'text-green-400', SELL: 'text-red-400', HOLD: 'text-yellow-400', } const BUY_PROB_BG = (prob: number): string => { if (prob >= 0.6) return 'bg-green-600' if (prob <= 0.35) return 'bg-red-600' return 'bg-yellow-600' } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export const WatchlistPanel: React.FC = ({ onSelect }) => { const [entries, setEntries] = useState( DEFAULT_WATCHLIST.map((code) => ({ code, info: null, prediction: null, error: null })) ) const [loading, setLoading] = useState(true) useEffect(() => { let cancelled = false let firstLoad = true const loadAll = async () => { if (firstLoad && !cancelled) setLoading(true) // Fetch info and batch predictions in parallel (single batch request instead of 5 individual ones) const [infoResults, batchResult] = await Promise.allSettled([ Promise.allSettled(DEFAULT_WATCHLIST.map((code) => fetchStockInfo(code))), fetchBatchPredictions(DEFAULT_WATCHLIST), ]) if (cancelled) return const infos = infoResults.status === 'fulfilled' ? infoResults.value : [] const batchItems = batchResult.status === 'fulfilled' ? batchResult.value : [] // Build a lookup map from batch results const predMap = new Map() for (const item of batchItems) { predMap.set(item.code, item) } const resolved: WatchlistEntry[] = DEFAULT_WATCHLIST.map((code, i) => { const infoSettled = infos[i] const info = infoSettled?.status === 'fulfilled' ? infoSettled.value : null const batchItem = predMap.get(code) ?? null return { code, info, prediction: batchItem && !batchItem.error ? batchItem : null, error: batchItem?.error ?? null, } }) setEntries(resolved) setLoading(false) firstLoad = false } loadAll() const id = setInterval(loadAll, 30_000) return () => { cancelled = true clearInterval(id) } }, []) return (

觀察清單

Watchlist
{loading ? (
載入中...
) : (
    {entries.map(({ code, info, prediction, error }) => (
  • ))}
)}
) }