Overview: regime to the nav bar, watchlist up top
Deploy / lint (push) Successful in 7s
Deploy / test (push) Successful in 1m5s
Deploy / deploy (push) Successful in 35s

The market regime banner took a full-width row on the dashboard for
ambient context. It is now a subtle status in the top bar (colored dot
+ "neutral regime", full headline and the longs/shorts caution in the
tooltip) linking to /regime - matching the mockup's nav.

The watchlist strip moves from the bottom of the dashboard (where it
got lost) into the freed spot right under the hero: a slim chip row
with today's move per symbol and the full-watchlist link. The empty
state is a one-line hint instead of a callout box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 15:31:41 +02:00
co-authored by Claude Fable 5
parent 72054d06b0
commit b0b691da9c
2 changed files with 51 additions and 49 deletions
+22
View File
@@ -3,6 +3,8 @@ import { useQuery } from '@tanstack/react-query';
import { useAuthStore } from '../../stores/authStore';
import { check as healthCheck } from '../../api/health';
import { getRunningJobs } from '../../api/jobs';
import { useMarketRegime } from '../../hooks/useMarketRegime';
import { regimeDot, regimeHeadline } from '../../lib/regime';
import TickerSearch from './TickerSearch';
const navItems = [
@@ -41,6 +43,7 @@ export default function TopBar() {
});
const running = jobs.data?.running ?? [];
const regime = useMarketRegime();
return (
<header className="glass sticky top-0 z-40 hidden items-center gap-7 rounded-none border-x-0 border-t-0 px-8 py-3 lg:flex">
@@ -73,6 +76,25 @@ export default function TopBar() {
</div>
<div className="ml-auto flex items-center gap-5">
{/* Market regime — ambient status; the full picture lives on /regime */}
{regime.data && (
<NavLink
to="/regime"
className="group flex items-center gap-2"
title={`${regimeHeadline(regime.data)}${
regime.data.label === 'bearish'
? ' — be cautious on new longs'
: regime.data.label === 'bullish'
? ' — shorts are counter-trend'
: ''
}`}
>
<span className={`inline-block h-1.5 w-1.5 rounded-full ${regimeDot(regime.data.label)}`} />
<span className="text-[11px] capitalize text-gray-500 transition-colors group-hover:text-gray-300">
{regime.data.label} regime
</span>
</NavLink>
)}
{/* Live background-job activity, condensed */}
{running.length > 0 && (
<span className="flex max-w-[220px] items-center gap-2" title={running.map((j) => j.label).join(' · ')}>
+29 -49
View File
@@ -6,8 +6,6 @@ import { useTrades } from '../hooks/useTrades';
import { useWatchlist } from '../hooks/useWatchlist';
import { usePaperTrades } from '../hooks/usePaperTrades';
import { useTickerNames } from '../hooks/useTickers';
import { useMarketRegime } from '../hooks/useMarketRegime';
import { regimeColor, regimeDot, regimeHeadline } from '../lib/regime';
import { Callout } from '../components/ui/Callout';
import { Section } from '../components/ui/Section';
import { OpenTradesPanel } from '../components/dashboard/OpenTradesPanel';
@@ -213,7 +211,6 @@ export default function DashboardPage() {
const tickerNames = useTickerNames();
const activation = useActivation();
const openTrades = usePaperTrades('open');
const regime = useMarketRegime();
const qualifiedSetups = useMemo(
() =>
@@ -342,21 +339,35 @@ export default function DashboardPage() {
)}
</div>
{/* Market regime banner */}
{regime.data && (
<div className="glass-sm flex items-center gap-2.5 px-4 py-2.5">
<span className={`inline-block h-2 w-2 rounded-full ${regimeDot(regime.data.label)}`} />
<span className="text-sm text-gray-400">Market regime:</span>
<span className={`text-sm font-semibold ${regimeColor(regime.data.label)}`}>
{regimeHeadline(regime.data)}
</span>
{regime.data.label === 'bearish' && (
<span className="text-xs text-gray-500"> be cautious on new longs</span>
)}
{regime.data.label === 'bullish' && (
<span className="text-xs text-gray-500"> shorts are counter-trend</span>
)}
</div>
{/* Watchlist — a slim glanceable strip right up top (regime moved to the nav bar) */}
{watchlist.data && (
topWatchlist.length > 0 ? (
<div className="flex flex-wrap items-center gap-2">
<span className="section-index mr-1">Watchlist</span>
{topWatchlist.map((entry) => (
<Link
key={entry.symbol}
to={`/ticker/${entry.symbol}`}
className="inline-flex items-baseline gap-2 rounded-full border border-white/[0.09] px-3 py-1 text-[12.5px] transition-colors hover:border-blue-400/40 hover:bg-white/[0.03]"
>
<b className="font-semibold text-gray-200">{entry.symbol}</b>
<span className={`num text-[11px] ${rColor(entry.change_pct)}`}>
{entry.change_pct != null
? `${entry.change_pct >= 0 ? '+' : ''}${entry.change_pct.toFixed(2)}%`
: '—'}
</span>
</Link>
))}
<Link to="/market" className="ml-1 text-xs font-medium text-blue-300 transition-colors hover:text-blue-200">
Full watchlist
</Link>
</div>
) : (
<p className="text-xs text-gray-600">
<span className="section-index mr-2">Watchlist</span>
empty open any ticker and tap to add it.
</p>
)
)}
{/* Focal setup */}
@@ -496,37 +507,6 @@ export default function DashboardPage() {
</Section>
</div>
{/* Watchlist — compact chips, drill into any ticker */}
<Section title="My Watchlist" hint="today's move">
{watchlist.isLoading && <SkeletonTable rows={2} cols={4} />}
{watchlist.isError && <Callout variant="error">Failed to load watchlist</Callout>}
{watchlist.data && topWatchlist.length === 0 && (
<Callout variant="empty">
Your watchlist is empty — open any ticker and tap “☆ Add to watchlist”.
</Callout>
)}
{topWatchlist.length > 0 && (
<div className="flex flex-wrap items-center gap-2.5">
{topWatchlist.map((entry) => (
<Link
key={entry.symbol}
to={`/ticker/${entry.symbol}`}
className="inline-flex items-baseline gap-2 rounded-full border border-white/[0.09] px-3.5 py-1.5 text-[13px] transition-colors hover:border-blue-400/40 hover:bg-white/[0.03]"
>
<b className="font-semibold text-gray-200">{entry.symbol}</b>
<span className={`num text-[11.5px] ${rColor(entry.change_pct)}`}>
{entry.change_pct != null
? `${entry.change_pct >= 0 ? '+' : ''}${entry.change_pct.toFixed(2)}%`
: '—'}
</span>
</Link>
))}
<Link to="/market" className="ml-1 text-xs font-medium text-blue-300 transition-colors hover:text-blue-200">
Full watchlist
</Link>
</div>
)}
</Section>
</div>
);
}