import { useMemo } from 'react';
import { Link } from 'react-router-dom';
import { useTrades } from '../hooks/useTrades';
import { useWatchlist } from '../hooks/useWatchlist';
import { usePerformance } from '../hooks/usePerformance';
import { Callout } from '../components/ui/Callout';
import { Section } from '../components/ui/Section';
import { SkeletonCard, SkeletonTable } from '../components/ui/Skeleton';
import { formatPrice } from '../lib/format';
import { recommendationActionLabel } from '../lib/recommendation';
import type { TradeSetup } from '../lib/types';
function fmtR(value: number | null): string {
if (value === null) return '—';
return `${value > 0 ? '+' : ''}${value.toFixed(2)}R`;
}
function rColor(value: number | null): string {
if (value === null) return 'text-gray-400';
if (value > 0) return 'text-emerald-400';
if (value < 0) return 'text-red-400';
return 'text-gray-300';
}
function Metric({ label, value, sub, valueClass = 'text-gray-100' }: {
label: string;
value: string;
sub?: string;
valueClass?: string;
}) {
return (
{label}
{value}
{sub &&
{sub}
}
);
}
function DirectionTag({ direction }: { direction: string }) {
const isLong = direction === 'long';
return (
{direction}
);
}
export default function DashboardPage() {
const trades = useTrades();
const watchlist = useWatchlist();
const performance = usePerformance();
const topSetups: TradeSetup[] = useMemo(
() => (trades.data ?? []).slice(0, 5),
[trades.data],
);
const highConfidenceCount = useMemo(
() => (trades.data ?? []).filter((t) => (t.confidence_score ?? 0) >= 70).length,
[trades.data],
);
const topWatchlist = useMemo(
() =>
[...(watchlist.data ?? [])]
.sort((a, b) => (b.composite_score ?? -1) - (a.composite_score ?? -1))
.slice(0, 6),
[watchlist.data],
);
const today = new Date().toLocaleDateString('en-US', {
weekday: 'long', month: 'long', day: 'numeric',
});
const stats = performance.data?.overall;
return (
{/* Hero */}
{/* Metric strip */}
{(trades.isLoading || performance.isLoading) ? (
) : (
0 ? 'text-blue-300' : 'text-gray-100'}
/>
)}
{/* Top setups */}
{trades.isLoading && }
{trades.isError && Failed to load setups }
{trades.data && topSetups.length === 0 && (
No active setups. Run the scanner from the Signals page.
)}
{topSetups.length > 0 && (
Ticker
Dir
Entry
R:R
Conf.
Action
{topSetups.map((setup) => (
{setup.symbol}
{formatPrice(setup.entry_price)}
{setup.rr_ratio.toFixed(1)}:1
{setup.confidence_score != null ? `${Math.round(setup.confidence_score)}%` : '—'}
{recommendationActionLabel(setup.recommended_action)}
))}
All setups →
)}
{/* Watchlist pulse */}
{watchlist.isLoading && }
{watchlist.isError && Failed to load watchlist }
{watchlist.data && topWatchlist.length === 0 && (
Watchlist is empty — add tickers on the Market page.
)}
{topWatchlist.length > 0 && (
{topWatchlist.map((entry) => (
{entry.symbol}
{entry.rr_ratio != null && (
{entry.rr_ratio.toFixed(1)}:1
)}
{entry.composite_score != null ? entry.composite_score.toFixed(0) : '—'}
))}
Full watchlist →
)}
);
}