Overview: restore wide focus card and half-width positions
The focus|radar 3/5-2/5 pairing made the top-pick card cramped and the full-width positions panel stretched its trade charts. Back to the layout that worked: focus card full width, account ribbons, then positions | radar side by side, performance chart at the bottom. Kept from the rework: radar rows still swap the focus card - and now reliably, because the ticker symbol inside a row is no longer a navigation link (clicking anywhere in the row, symbol included, selects it; the ticker page is reachable via the focus card's Ticker details button). Below-gate inspection, badges, and the reset link unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -96,13 +96,7 @@ function RadarSetupRow({ setup, rank, reason, name, selected, onSelect }: RadarR
|
||||
>
|
||||
<span className="num text-[11px] text-gray-500">{rank}</span>
|
||||
<span className="min-w-0">
|
||||
<Link
|
||||
to={`/ticker/${setup.symbol}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="block font-medium text-blue-300 transition-colors hover:text-blue-200"
|
||||
>
|
||||
{setup.symbol}
|
||||
</Link>
|
||||
<span className="block font-medium text-blue-300">{setup.symbol}</span>
|
||||
{name && <span className="block truncate text-[10.5px] text-gray-500">{name}</span>}
|
||||
</span>
|
||||
<DirectionTag direction={setup.direction} />
|
||||
@@ -413,9 +407,8 @@ export default function DashboardPage() {
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Setup in focus | Radar — the decision pair */}
|
||||
<div className="grid items-start gap-8 xl:grid-cols-5">
|
||||
<div className="xl:col-span-3">
|
||||
{/* Setup in focus — full width; select a radar row below to swap it */}
|
||||
<div>
|
||||
{(trades.isLoading || activation.isLoading) && <SkeletonCard />}
|
||||
{trades.isError && <Callout variant="error">Failed to load setups</Callout>}
|
||||
{trades.data && activation.data && (
|
||||
@@ -446,7 +439,52 @@ export default function DashboardPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="xl:col-span-2">
|
||||
{/* Metric strip — the account ribbons, right above the positions they describe */}
|
||||
{(trades.isLoading || openTrades.isLoading) ? (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<SkeletonCard /><SkeletonCard /><SkeletonCard /><SkeletonCard />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Metric
|
||||
label="Open Risk"
|
||||
value={exposure.count > 0 ? `$${exposure.riskUsd.toFixed(0)}` : '—'}
|
||||
sub={exposure.count > 0 ? `${exposure.count} open · distance to stops` : 'no open trades'}
|
||||
/>
|
||||
<Metric
|
||||
label="Unrealized"
|
||||
value={exposure.rPriced > 0 ? fmtR(exposure.unrealR) : '—'}
|
||||
valueClass={rColor(exposure.rPriced > 0 ? exposure.unrealR : null)}
|
||||
sub={
|
||||
exposure.count > 0
|
||||
? `${money(exposure.unrealUsd)} · ${exposure.winners}▲ ${exposure.losers}▼`
|
||||
: 'mark-to-market'
|
||||
}
|
||||
/>
|
||||
<Metric
|
||||
label="Alpha vs S&P 500"
|
||||
value={exposure.alphaPriced > 0 ? money(exposure.alphaUsd) : '—'}
|
||||
valueClass={
|
||||
exposure.alphaPriced > 0
|
||||
? exposure.alphaUsd >= 0 ? 'text-emerald-300' : 'text-red-300'
|
||||
: 'text-gray-100'
|
||||
}
|
||||
sub="open trades vs buy-and-hold SPY"
|
||||
/>
|
||||
<Metric
|
||||
label="Gate"
|
||||
value={trades.data ? `${qualifiedCount} / ${liveCount}` : '—'}
|
||||
valueClass={qualifiedCount > 0 ? 'text-blue-300' : 'text-gray-100'}
|
||||
sub={activation.data ? activationSummary(activation.data) : 'setups clearing the gate'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open positions | Radar — side by side, half width each */}
|
||||
<div className="grid items-start gap-8 xl:grid-cols-2">
|
||||
<OpenTradesPanel />
|
||||
|
||||
<div>
|
||||
<Section title="Radar" hint="ranked by strategy score · select a row to focus it">
|
||||
{trades.isLoading && <SkeletonTable rows={5} cols={5} />}
|
||||
{trades.data && radar.qualified.length === 0 && radar.below.length === 0 && (
|
||||
@@ -535,50 +573,6 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metric strip — the account ribbons, right above the positions they describe */}
|
||||
{(trades.isLoading || openTrades.isLoading) ? (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<SkeletonCard /><SkeletonCard /><SkeletonCard /><SkeletonCard />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Metric
|
||||
label="Open Risk"
|
||||
value={exposure.count > 0 ? `$${exposure.riskUsd.toFixed(0)}` : '—'}
|
||||
sub={exposure.count > 0 ? `${exposure.count} open · distance to stops` : 'no open trades'}
|
||||
/>
|
||||
<Metric
|
||||
label="Unrealized"
|
||||
value={exposure.rPriced > 0 ? fmtR(exposure.unrealR) : '—'}
|
||||
valueClass={rColor(exposure.rPriced > 0 ? exposure.unrealR : null)}
|
||||
sub={
|
||||
exposure.count > 0
|
||||
? `${money(exposure.unrealUsd)} · ${exposure.winners}▲ ${exposure.losers}▼`
|
||||
: 'mark-to-market'
|
||||
}
|
||||
/>
|
||||
<Metric
|
||||
label="Alpha vs S&P 500"
|
||||
value={exposure.alphaPriced > 0 ? money(exposure.alphaUsd) : '—'}
|
||||
valueClass={
|
||||
exposure.alphaPriced > 0
|
||||
? exposure.alphaUsd >= 0 ? 'text-emerald-300' : 'text-red-300'
|
||||
: 'text-gray-100'
|
||||
}
|
||||
sub="open trades vs buy-and-hold SPY"
|
||||
/>
|
||||
<Metric
|
||||
label="Gate"
|
||||
value={trades.data ? `${qualifiedCount} / ${liveCount}` : '—'}
|
||||
valueClass={qualifiedCount > 0 ? 'text-blue-300' : 'text-gray-100'}
|
||||
sub={activation.data ? activationSummary(activation.data) : 'setups clearing the gate'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open positions — full width, right under their ribbons */}
|
||||
<OpenTradesPanel />
|
||||
|
||||
{/* Performance — the paper book vs the same dollars in SPY */}
|
||||
<PerfChart />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user