From ff17c635e3200a4d501fd6f3104f197f14a3fbb3 Mon Sep 17 00:00:00 2001 From: Saravana Achu Mac Date: Sat, 9 May 2026 02:22:10 -0700 Subject: [PATCH] refactor(ui): standardize history ledger surfaces --- web/src/tabs/HistoryTab.tsx | 202 ++++++++++++++++++------------------ 1 file changed, 102 insertions(+), 100 deletions(-) diff --git a/web/src/tabs/HistoryTab.tsx b/web/src/tabs/HistoryTab.tsx index a65426a..d79519c 100644 --- a/web/src/tabs/HistoryTab.tsx +++ b/web/src/tabs/HistoryTab.tsx @@ -7,7 +7,20 @@ import { import { useCanonicalLifecycle } from '../hooks/useCanonicalLifecycle'; import { fetchTradeHistory } from '../lib/tradeHistoryApi'; import { fetchPositionsBootstrap } from '../lib/positionsApi'; -import { AlertBanner, Badge, Button, Input, Select } from '../components/ui/Primitives'; +import { + AlertBanner, + Badge, + Button, + DataTable, + DataTableBody, + DataTableCell, + DataTableHead, + DataTableHeader, + DataTableRow, + MetricCard, + Input, + Select, +} from '../components/ui/Primitives'; interface TradeRecord { @@ -449,63 +462,53 @@ export const HistoryTab = ({ botState }: HistoryTabProps) => { )} - {/* Performance Metrics Bar */} -
-
- Total Trades - {metrics.total} -
-
- Win Rate - {metrics.winRate.toFixed(1)}% -
-
- Realized P&L - = 0 ? 'text-green-400' : 'text-red-400'}`}> - {metrics.netPnl >= 0 ? '+' : '-'}${Math.abs(metrics.netPnl).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} - -
-
- Profit Factor - = 1 ? 'text-green-400' : 'text-red-400'}`}> - {metrics.expectancy.profitFactor === null - ? '-' - : Number.isFinite(metrics.expectancy.profitFactor) - ? metrics.expectancy.profitFactor.toFixed(2) - : '∞'} - -
-
- Expectancy / Trade - = 0 ? 'text-green-400' : 'text-red-400'}`}> - {metrics.expectancy.expectancyPerTrade >= 0 ? '+' : '-'}${Math.abs(metrics.expectancy.expectancyPerTrade).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} - - - Avg win ${metrics.expectancy.avgWin.toFixed(2)} / Avg loss ${metrics.expectancy.avgLossAbs.toFixed(2)} - -
+
+ + + = 0 ? '+' : '-'}$${Math.abs(metrics.netPnl).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`} + tone={metrics.netPnl >= 0 ? 'success' : 'danger'} + /> + = 1 ? 'success' : 'danger'} + /> + = 0 ? '+' : '-'}$${Math.abs(metrics.expectancy.expectancyPerTrade).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`} + helper={`Avg win $${metrics.expectancy.avgWin.toFixed(2)} / Avg loss $${metrics.expectancy.avgLossAbs.toFixed(2)}`} + tone={metrics.expectancy.expectancyPerTrade >= 0 ? 'success' : 'danger'} + />
- {/* Trade Ledger Table - Matched exactly with Active Orders Style */} -
- - - - - - - - - - - - - - - - - - - - - - + + + + {sortedHistory.length === 0 ? ( - - - + + ) : ( paginatedHistory.map((t) => { const pnlValue = Number(t.pnl ?? 0); @@ -585,48 +588,48 @@ export const HistoryTab = ({ botState }: HistoryTabProps) => { : null; return ( - - - - - - - - - - - - - + + )}) )} - + - - - + + -
SourceTimeTrade IDSub-tagAssetSideCapital Used ($)EntryExitP/L (%)Reason
+ + + + Source + Time + Trade ID + Sub-tag + Asset + Side + Capital Used ($) + Entry + Exit + P/L (%) + Reason + + + + +
{
-
+ + Filters and sorting are applied directly from table headers. -
+ + No transaction journal found. -
+ {t.source === 'BOT' ? (t.profile_id ? (profiles.find(p => p.id === t.profile_id)?.name || 'BOT') : 'BOT') : 'MANUAL'} - + + {(t.timestamp || t.created_at) ? new Date(t.timestamp || t.created_at!).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '-'} - + + {t.trade_id || '-'} - + + {compactTag(t.sub_tag || t.subTag)} - {t.symbol} + + {t.symbol} + {t.side} - + + {capitalUsed !== null ? `$${capitalUsed.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '-'} - + + {Number.isFinite(Number(t.entry_price)) && Number(t.entry_price) > 0 ? `$${Number(t.entry_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '-'} - + + {Number.isFinite(Number(t.exit_price)) && Number(t.exit_price) > 0 ? `$${Number(t.exit_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '-'} - + +
{pnlValue >= 0 ? '+' : ''}{pnlPercentValue.toFixed(2)}%
@@ -638,20 +641,20 @@ export const HistoryTab = ({ botState }: HistoryTabProps) => { )}
-
- + + + {t.reason} -
-
+ + +
Showing {sortedHistory.length === 0 ? 0 : ((historyPage - 1) * HISTORY_PAGE_SIZE) + 1} -{Math.min(historyPage * HISTORY_PAGE_SIZE, sortedHistory.length)} of {sortedHistory.length} @@ -680,11 +683,10 @@ export const HistoryTab = ({ botState }: HistoryTabProps) => {
-
-
+
); };