fix(dashboard): Strategy LAST SEEN = 'Invalid Date' contract drift
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

API GET /api/meet-kevin/strategy/tickers returns the field as
latest_mention_at, but the dashboard's StrategyTicker type + table
read t.last_mention_at — so new Date(undefined) produced
'Invalid Date' in every row.

Aligned StrategyTicker to the actual API shape and renamed the
table accessor. Also dropped two columns that referenced fields the
API never returned (mention_count, unrealized_pnl_pct, current_price)
and replaced them with what IS in the response (Horizon column +
trade_entry_price renamed 'Entry'). t.is_held -> t.has_open_trade.
This commit is contained in:
Viktor Barzin 2026-05-29 06:04:05 +00:00
parent 5fce576e33
commit 00a40c9d2f
2 changed files with 15 additions and 26 deletions

View file

@ -50,17 +50,14 @@ export function TickerScorecardTable({ tickers, isLoading, onClose }: Props) {
Conviction
</th>
<th className="px-5 py-3 text-left text-xs text-slate-400 uppercase tracking-wide font-semibold">
Price
Horizon
</th>
<th className="px-5 py-3 text-left text-xs text-slate-400 uppercase tracking-wide font-semibold">
P&amp;L
Entry
</th>
<th className="px-5 py-3 text-left text-xs text-slate-400 uppercase tracking-wide font-semibold">
Status
</th>
<th className="px-5 py-3 text-left text-xs text-slate-400 uppercase tracking-wide font-semibold">
Mentions
</th>
<th className="px-5 py-3 text-left text-xs text-slate-400 uppercase tracking-wide font-semibold">
Last seen
</th>
@ -70,12 +67,11 @@ export function TickerScorecardTable({ tickers, isLoading, onClose }: Props) {
<tbody className="divide-y divide-slate-700">
{tickers.map((t) => {
const badge = t.bridge_status ? BRIDGE_BADGE[t.bridge_status] : null;
const pnl = t.unrealized_pnl_pct;
return (
<tr key={t.symbol} className="hover:bg-slate-700/30 transition-colors">
<td className="px-5 py-3.5">
<span className="font-mono font-bold text-white">${t.symbol}</span>
{t.is_held && (
{t.has_open_trade && (
<span className="ml-2 px-1.5 py-0.5 text-xs bg-green-600/20 text-green-400 border border-green-500/30 rounded">
HOLDING
</span>
@ -94,20 +90,12 @@ export function TickerScorecardTable({ tickers, isLoading, onClose }: Props) {
</span>
</div>
</td>
<td className="px-5 py-3.5 text-slate-400">{t.latest_horizon}</td>
<td className="px-5 py-3.5 text-slate-300 font-mono">
{t.current_price != null
? `$${t.current_price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
{t.trade_entry_price != null
? `$${t.trade_entry_price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
: '—'}
</td>
<td className="px-5 py-3.5 font-mono">
{pnl != null ? (
<span className={pnl >= 0 ? 'text-green-400' : 'text-red-400'}>
{pnl >= 0 ? '+' : ''}{pnl.toFixed(2)}%
</span>
) : (
<span className="text-slate-500"></span>
)}
</td>
<td className="px-5 py-3.5">
{badge ? (
<span className={`px-2 py-0.5 text-xs font-semibold uppercase rounded-md border ${badge.cls}`}>
@ -117,12 +105,13 @@ export function TickerScorecardTable({ tickers, isLoading, onClose }: Props) {
<span className="text-slate-500"></span>
)}
</td>
<td className="px-5 py-3.5 text-slate-300">{t.mention_count}</td>
<td className="px-5 py-3.5 text-slate-400">
{new Date(t.last_mention_at).toLocaleDateString()}
{t.latest_mention_at
? new Date(t.latest_mention_at).toLocaleDateString()
: '—'}
</td>
<td className="px-5 py-3.5">
{t.is_held && (
{t.has_open_trade && (
<button
onClick={() => onClose(t.symbol)}
className="px-2.5 py-1 text-xs bg-red-600/20 hover:bg-red-600/40 text-red-300 border border-red-500/30 rounded transition-colors"

View file

@ -182,12 +182,12 @@ export interface StrategyTicker {
symbol: string;
latest_action: TickerAction;
latest_conviction: number;
mention_count: number;
last_mention_at: string;
latest_horizon: TimeHorizon;
latest_mention_at: string | null;
bridge_status: BridgeStatus | null;
is_held: boolean;
current_price: number | null;
unrealized_pnl_pct: number | null;
effective_conviction: number | null;
has_open_trade: boolean;
trade_entry_price: number | null;
}
export interface EquityCurvePoint {