fix watchlist remove (was undone by auto-populate); add watch toggle on ticker page
Removing a ticker did nothing because get_watchlist re-runs auto_populate on every read, instantly re-adding any top-ranked ticker the user had just removed. Removals are now tombstoned as a "dismissed" entry_type: auto-population skips them, the list hides them, and a later manual add revives the row. Also exposes an Add/Remove-watchlist toggle in the ticker detail header. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { useMemo, useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTickerDetail } from '../hooks/useTickerDetail';
|
||||
import { useFetchSymbolData } from '../hooks/useFetchSymbolData';
|
||||
import { useWatchlist, useAddToWatchlist, useRemoveFromWatchlist } from '../hooks/useWatchlist';
|
||||
import type { FetchSelector } from '../api/ingestion';
|
||||
import { CandlestickChart } from '../components/charts/CandlestickChart';
|
||||
import { ScoreCard } from '../components/ui/ScoreCard';
|
||||
@@ -98,6 +99,14 @@ export default function TickerDetailPage() {
|
||||
const { symbol = '' } = useParams<{ symbol: string }>();
|
||||
const { ohlcv, scores, srLevels, sentiment, fundamentals, trades } = useTickerDetail(symbol);
|
||||
const ingestion = useFetchSymbolData();
|
||||
const watchlist = useWatchlist();
|
||||
const addToWatchlist = useAddToWatchlist();
|
||||
const removeFromWatchlist = useRemoveFromWatchlist();
|
||||
const onWatchlist = useMemo(
|
||||
() => (watchlist.data ?? []).some((e) => e.symbol.toUpperCase() === symbol.toUpperCase()),
|
||||
[watchlist.data, symbol],
|
||||
);
|
||||
const watchlistBusy = addToWatchlist.isPending || removeFromWatchlist.isPending;
|
||||
const [activeTab, setActiveTab] = useState<DetailTab>('Analysis');
|
||||
const [refreshingLabel, setRefreshingLabel] = useState<string | null>(null);
|
||||
|
||||
@@ -216,12 +225,28 @@ export default function TickerDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => { setRefreshingLabel(null); ingestion.mutate(symbol); }}
|
||||
loading={ingestion.isPending}
|
||||
>
|
||||
{ingestion.isPending ? 'Fetching…' : 'Fetch All'}
|
||||
</Button>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
onWatchlist
|
||||
? removeFromWatchlist.mutate(symbol)
|
||||
: addToWatchlist.mutate(symbol)
|
||||
}
|
||||
loading={watchlistBusy}
|
||||
disabled={watchlist.isLoading}
|
||||
title={onWatchlist ? 'Remove from watchlist' : 'Add to watchlist'}
|
||||
className={onWatchlist ? '!text-amber-300' : ''}
|
||||
>
|
||||
{onWatchlist ? '★ Watching' : '☆ Add to watchlist'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => { setRefreshingLabel(null); ingestion.mutate(symbol); }}
|
||||
loading={ingestion.isPending}
|
||||
>
|
||||
{ingestion.isPending ? 'Fetching…' : 'Fetch All'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data freshness bar */}
|
||||
|
||||
Reference in New Issue
Block a user