import { useState, useEffect, useCallback } from "react"; import { Link } from "react-router"; import type { MostActiveStock } from "../types"; function formatVolume(vol: number): string { if (vol >= 1_000_000_000) return `${(vol / 1_000_000_000).toFixed(1)}B`; if (vol >= 1_000_000) return `${(vol / 1_000_000).toFixed(1)}M`; if (vol >= 1_000) return `${(vol / 1_000).toFixed(1)}K`; return vol.toString(); } function formatPrice(price: number): string { return `$${price.toFixed(2)}`; } function formatChangePercent(pct: number): string { return `${pct >= 0 ? "+" : ""}${pct.toFixed(2)}%`; } export default function MostActiveStocks() { const [stocks, setStocks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [saving, setSaving] = useState>({}); const [saved, setSaved] = useState>({}); const fetchData = useCallback(async () => { try { setError(null); const res = await fetch("/api/stocks/most-actives"); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Failed to fetch data"); } const data = await res.json(); setStocks(data); } catch (err) { const message = err instanceof Error ? err.message : "Failed to fetch most active stocks."; setError(message); } finally { setLoading(false); } }, []); useEffect(() => { fetchData(); const interval = setInterval(fetchData, 30000); return () => clearInterval(interval); }, [fetchData]); const handleSave = async (symbol: string) => { setSaving((p) => ({ ...p, [symbol]: true })); setSaved((p) => ({ ...p, [symbol]: false })); try { const form = new FormData(); form.set("ticker", symbol); const res = await fetch("/api/stocks", { method: "POST", body: form, }); if (!res.ok) { const data = await res.json().catch(() => null); throw new Error(data?.error || "Failed to save stock"); } // trigger analysis in background (non-blocking) fetch(`/api/analyze`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ticker: symbol, background: true }) }).catch(() => {}); setSaved((p) => ({ ...p, [symbol]: true })); } catch (err) { console.error(err); } finally { setSaving((p) => ({ ...p, [symbol]: false })); } }; if (loading) { return (
{Array.from({ length: 8 }).map((_, i) => (
))}
); } if (error && stocks.length === 0) { return (

{error}

); } if (stocks.length === 0) { return (

No data available

); } return (
{error && (

{error}

)}
{stocks.map((stock) => ( ))}
Symbol Name Price Change % Volume Actions
{stock.symbol} {stock.name} {formatPrice(stock.price)} = 0 ? "text-green-600" : "text-red-600"}`}> {formatChangePercent(stock.changePercent)} {formatVolume(stock.volume)}
); }