make watchlist fully manual; add price + day-change, two-block overview
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 35s
Deploy / deploy (push) Successful in 24s

Per design decision: the watchlist is now purely user-curated (no auto-seeding
of the top-10), so the auto_populate/dismissed machinery is removed and removals
are plain deletes. Each entry is enriched with latest close + day-over-day move.

Overview now shows two clear blocks: Top Setups (what to trade) and My Watchlist
(my names with current price and today's %). Market watchlist table drops the
now-meaningless auto/manual Type column in favour of Price and Day columns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 14:25:04 +02:00
parent 0e9f1846f6
commit 6e06f51bb6
6 changed files with 164 additions and 195 deletions
+19 -10
View File
@@ -78,7 +78,7 @@ export default function DashboardPage() {
() =>
[...(watchlist.data ?? [])]
.sort((a, b) => (b.composite_score ?? -1) - (a.composite_score ?? -1))
.slice(0, 6),
.slice(0, 10),
[watchlist.data],
);
@@ -212,13 +212,15 @@ export default function DashboardPage() {
</Section>
</div>
{/* Watchlist pulse */}
{/* My watchlist */}
<div className="xl:col-span-2">
<Section title="Watchlist Pulse" hint="top by score">
<Section title="My Watchlist" hint="today's move">
{watchlist.isLoading && <SkeletonTable rows={6} cols={3} />}
{watchlist.isError && <Callout variant="error">Failed to load watchlist</Callout>}
{watchlist.data && topWatchlist.length === 0 && (
<Callout variant="empty">Watchlist is empty add tickers on the Market page.</Callout>
<Callout variant="empty">
Your watchlist is empty open any ticker and tap Add to watchlist.
</Callout>
)}
{topWatchlist.length > 0 && (
<div className="glass overflow-hidden">
@@ -229,13 +231,20 @@ export default function DashboardPage() {
to={`/ticker/${entry.symbol}`}
className="flex items-center justify-between px-4 py-3 transition-colors duration-150 hover:bg-white/[0.03]"
>
<span className="font-medium text-gray-200">{entry.symbol}</span>
<span className="flex items-center gap-4">
{entry.rr_ratio != null && (
<span className="num text-xs text-gray-500">{entry.rr_ratio.toFixed(1)}:1</span>
<span className="flex items-baseline gap-2">
<span className="font-medium text-gray-200">{entry.symbol}</span>
{entry.composite_score != null && (
<span className="num text-[10px] text-gray-500">score {entry.composite_score.toFixed(0)}</span>
)}
<span className="num text-sm font-semibold text-blue-300">
{entry.composite_score != null ? entry.composite_score.toFixed(0) : '—'}
</span>
<span className="flex items-baseline gap-3">
<span className="num text-sm text-gray-200">
{entry.last_close != null ? formatPrice(entry.last_close) : '—'}
</span>
<span className={`num w-16 text-right text-sm font-semibold ${rColor(entry.change_pct)}`}>
{entry.change_pct != null
? `${entry.change_pct >= 0 ? '+' : ''}${entry.change_pct.toFixed(2)}%`
: '—'}
</span>
</span>
</Link>