diff --git a/docs/UX_THEME_NORMALIZATION_HANDOFF.md b/docs/UX_THEME_NORMALIZATION_HANDOFF.md index 9faa659..ae83ce0 100644 --- a/docs/UX_THEME_NORMALIZATION_HANDOFF.md +++ b/docs/UX_THEME_NORMALIZATION_HANDOFF.md @@ -95,8 +95,8 @@ Current public bundle: ## Phase 4: Remaining legacy component cleanup -- [ ] Normalize `web/src/components/TradeProfileManager.tsx` -- [ ] Normalize `web/src/components/StrategyWizard.tsx` +- [x] Normalize `web/src/components/TradeProfileManager.tsx` +- [x] Normalize `web/src/components/StrategyWizard.tsx` - [ ] Normalize `web/src/tabs/ReconciliationAuditPanel.tsx` - [ ] Normalize `web/src/components/GlobalConfigManager.tsx` - [ ] Normalize `web/src/components/EntryForm.tsx` diff --git a/web/src/components/StrategyWizard.tsx b/web/src/components/StrategyWizard.tsx index 90cdfb2..4b47d32 100644 --- a/web/src/components/StrategyWizard.tsx +++ b/web/src/components/StrategyWizard.tsx @@ -21,6 +21,9 @@ import { useAuth } from './AuthContext'; import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel'; import { useBacktestFeatureGate } from '../backtest/useBacktestFeatureGate'; import { createTradeProfile, updateTradeProfile } from '../lib/profileApi'; +import { Button } from './ui/button'; +import { Card } from './ui/card'; +import { Input } from './ui/input'; interface WizardState { step: number; @@ -43,6 +46,13 @@ const SESSION_MAP = { 'Asia only': 'TOK,SYD' }; +const sectionTitleClass = 'mb-2 text-2xl font-bold text-[var(--foreground)]'; +const sectionDescriptionClass = 'text-sm text-[var(--muted-foreground)]'; +const labelClass = 'text-xs font-bold uppercase tracking-widest text-[var(--muted-foreground)]'; +const optionBaseClass = 'w-full rounded-2xl border-2 p-5 text-left transition-all'; +const optionSelectedClass = 'border-[var(--accent)] bg-[var(--accent-soft)]'; +const optionIdleClass = 'border-[var(--border)] bg-[var(--card)] hover:border-[var(--border-strong)]'; + const buildStrategyConfig = (state: WizardState) => ({ rules: [ { ruleId: 'RiskManagementRule', enabled: true, ruleType: 'mandatory', params: {} }, @@ -148,16 +158,16 @@ export const StrategyWizard: React.FC<{ }; return ( -
- {/* Progress Bar */} -
-
- {[1, 2, 3, 4, 5].map(i => ( -
= i ? 'bg-[#00ff88] text-black scale-110' : 'bg-zinc-800 text-zinc-500 border border-zinc-700' - }`} - > +
+ {/* Progress Bar */} +
+
+ {[1, 2, 3, 4, 5].map(i => ( +
= i ? 'scale-110 bg-[var(--primary)] text-[var(--primary-foreground)]' : 'border border-[var(--border)] bg-[var(--card)] text-[var(--muted-foreground)]' + }`} + > {state.step > i ? : i}
))} @@ -166,9 +176,9 @@ export const StrategyWizard: React.FC<{ {/* Step 1: Risk Style */} {state.step === 1 && (
-
-

How should this bot behave?

-

Select a pre-configured risk style. High-frequency options seek more opportunities but require more flexibility.

+
+

How should this bot behave?

+

Select a pre-configured risk style. High-frequency options seek more opportunities but require more flexibility.

{RISK_STYLE_TEMPLATES.map(style => { @@ -178,27 +188,27 @@ export const StrategyWizard: React.FC<{ @@ -212,13 +222,13 @@ export const StrategyWizard: React.FC<{ })}
- +
)} @@ -226,14 +236,14 @@ export const StrategyWizard: React.FC<{ {/* Step 2: Assets & Capital */} {state.step === 2 && (
-
-

Assets & Capital

-

Define what to trade and how much capital to use.

+
+

Assets & Capital

+

Define what to trade and how much capital to use.

- +
{ASSETS.map(asset => ( @@ -257,33 +267,33 @@ export const StrategyWizard: React.FC<{
- -
-
- -
- setState({ ...state, capital: Number(e.target.value) })} - className="w-full bg-zinc-900 border-2 border-white/5 rounded-xl py-4 pl-10 pr-4 text-white font-bold outline-none focus:border-[#00ff88]/50 transition-all" - /> -
-

Total USD balance this bot is allowed to manage.

+ +
+
+ +
+ setState({ ...state, capital: Number(e.target.value) })} + className="h-14 pl-10 font-bold" + /> +
+

Total USD balance this bot is allowed to manage.

-
- - + +
)} @@ -319,8 +329,8 @@ export const StrategyWizard: React.FC<{ {state.step === 3 && (
-

Trading Hours

-

When should the bot look for signals?

+

Trading Hours

+

When should the bot look for signals?

@@ -328,18 +338,18 @@ export const StrategyWizard: React.FC<{ - + +
)} @@ -365,26 +375,26 @@ export const StrategyWizard: React.FC<{ {state.step === 4 && (
-

Safety Confirmation

-

Review the built-in safeguards protecting your capital.

+

Safety Confirmation

+

Review the built-in safeguards protecting your capital.

-
+
-
- Auto-Protective Stop Loss - Enabled (Mandatory) -
-
- Position Sizing Limit - Max {state.riskStyle?.riskPerTrade}% per trade -
-
- Daily Recovery Halt +
+ Auto-Protective Stop Loss + Enabled (Mandatory) +
+
+ Position Sizing Limit + Max {state.riskStyle?.riskPerTrade}% per trade +
+
+ Daily Recovery Halt -${Math.floor(state.capital * 0.05)} (5%)
- Market Volatility Guard + Market Volatility Guard Smart ATR Check
@@ -392,17 +402,17 @@ export const StrategyWizard: React.FC<{

Risk management and safety rules are coded into the engine core. These cannot be disabled or bypassed, ensuring your bot always operates within safety boundaries. -

-
-
+

+
+
- - + +
)} @@ -411,79 +421,78 @@ export const StrategyWizard: React.FC<{ {state.step === 5 && (
-

Ready to Deploy

-

Verify your bot strategy one last time.

-
- -
- {/* Decorative background element */} -
- -
-
- {state.riskStyle?.id === 'safe' ? : state.riskStyle?.id === 'balanced' ? : } -
-
-

{state.riskStyle?.label} Bot

-
- {state.assets.map(a => ( - {a.split('/')[0]} - ))} -
-
+

Ready to Deploy

+

Verify your bot strategy one last time.

+
+ + +
+
+ {state.riskStyle?.id === 'safe' ? : state.riskStyle?.id === 'balanced' ? : } +
+
+

{state.riskStyle?.label} Bot

+
+ {state.assets.map(a => ( + {a.split('/')[0]} + ))} +
+
- Target Capital -
+ Target Capital +
${state.capital}
- Profit Threshold -
+ Profit Threshold +
${state.profitTarget}/day
- Risk Level -
{(state.riskStyle?.riskPerTrade || 0)}% per trade
+ Risk Level +
{(state.riskStyle?.riskPerTrade || 0)}% per trade
- Active Schedule -
{state.hours}
+ Active Schedule +
{state.hours}
-
-
+
+
-

Bots are created in PAUSED mode by default. You must manually enable trading from your dashboard.

-
-
-
+

Bots are created in PAUSED mode by default. You must manually enable trading from your dashboard.

+
+
+
- +
{!backtestGateLoading && backtestEnabled && ( - + )} - +
diff --git a/web/src/components/TradeProfileManager.tsx b/web/src/components/TradeProfileManager.tsx index 2b0644a..7701949 100644 --- a/web/src/components/TradeProfileManager.tsx +++ b/web/src/components/TradeProfileManager.tsx @@ -24,6 +24,11 @@ import { import { useAuth } from './AuthContext'; import type { BotState } from '../hooks/useWebSocket'; import { DEFAULT_BOT_STATE } from '../hooks/useWebSocket'; +import { Button } from './ui/button'; +import { Card } from './ui/card'; +import { Input } from './ui/input'; +import { Select } from './ui/select'; +import { cn } from '../lib/utils'; // ChatControl is now rendered globally in App.tsx // --- TYPES --- @@ -126,6 +131,13 @@ const PARAM_LABELS: Record = { minConfidence: 'Min Confidence', }; +const labelClass = 'text-[10px] font-semibold text-[var(--muted-foreground)] uppercase tracking-wider'; +const helpTextClass = 'text-[10px] text-[var(--muted-foreground)]'; +const panelClass = 'rounded-xl border border-[var(--border)] bg-[var(--card-elevated)]'; +const subtlePanelClass = 'rounded-xl border border-[var(--border)] bg-[var(--muted)]/35'; +const iconButtonClass = 'h-8 w-8 rounded-xl'; +const accentTextClass = 'text-[var(--accent)]'; + export const normalizeSessionPresetValue = (raw: unknown): string => { if (raw === undefined || raw === null) return 'LDN,NY'; @@ -243,24 +255,27 @@ const ToggleSwitch = ({ checked, onChange }: { checked: boolean, onChange: (v: b ); const Slider = ({ value, onChange, min, max, step, unit, label }: { value: number, onChange: (n: number) => void, min: number, max: number, step?: number, unit?: string, label: string }) => ( -
+
- {label} - {value}{unit} + {label} + {value}{unit}
onChange(Number(e.target.value))} - className="w-full accent-[#00ff88] h-1" + className="h-1 w-full accent-[var(--accent)]" /> -
+
{min}{unit} {max}{unit}
@@ -268,27 +283,18 @@ const Slider = ({ value, onChange, min, max, step, unit, label }: { value: numbe ); const StatPill = ({ icon: Icon, label, value, color }: { icon: any, label: string, value: string, color: string }) => ( -
- {/* Top accent line */} -
+ +
-
+
-

{label}

-

{value}

+

{label}

+

{value}

-
+ ); interface TradeProfileManagerProps { @@ -532,51 +538,43 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
-
- +
+
-

Strategy Clusters

- {loading && } +

Strategy Clusters

+ {loading && }
-

Manage trading profiles, rules, and capital allocation

+

Manage trading profiles, rules, and capital allocation

- - + setSearchTerm(e.target.value)} - className="w-44 bg-[#12131a] border border-white/[0.06] rounded-xl pl-8 pr-3 py-2.5 text-[11px] text-white placeholder:text-zinc-600 focus:border-[#00ff88]/30 outline-none transition-colors" - style={{ boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.2)' }} + className="h-10 w-44 pl-8 pr-3 text-[11px]" />
- - +
@@ -591,26 +589,26 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi {/* ─── EMPTY STATE ─── */} {filtered.length === 0 && !loading && ( -
-
- + +
+
-

+

{searchTerm ? 'No matching profiles' : 'No profiles yet'}

-

+

{searchTerm ? 'Try a different search term' : 'Create your first strategy profile to start trading'}

{!searchTerm && ( - + )} -
+ )} {/* ─── PROFILE CARDS ─── */} @@ -644,24 +642,17 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi const lockStatus = (botState.health?.lockContentionCount || 0) > 0 ? 'Contended' : 'Stable'; return ( -
{/* Top accent bar */} -
@@ -669,96 +660,91 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
-

{p.name}

+

{p.name}

- + {p.is_active ? 'Active' : 'Paused'} {email && ( - {email} + {email} )}
{/* Action buttons */}
- - - + {profile?.role === 'admin' && ( - + )}
{/* ── Elevated inner panel: Key Metrics ── */} -
+
{/* Stats header row */}
{[ - { icon: DollarSign, label: 'Capital', val: `$${p.allocated_capital.toLocaleString()}`, color: '#3b82f6', valColor: 'white' }, - { icon: BarChart3, label: 'Realized', val: `${stats.totalPnl >= 0 ? '+' : ''}${stats.totalPnl.toFixed(2)}`, color: stats.totalPnl >= 0 ? '#00ff88' : '#ef4444', valColor: stats.totalPnl >= 0 ? '#00ff88' : '#ef4444' }, - { icon: AlertTriangle, label: 'Risk', val: `${p.risk_per_trade_percent}%`, color: '#f59e0b', valColor: 'white' }, + { icon: DollarSign, label: 'Capital', val: `$${p.allocated_capital.toLocaleString()}`, color: '#3b82f6', valClass: 'text-[var(--foreground)]' }, + { icon: BarChart3, label: 'Realized', val: `${stats.totalPnl >= 0 ? '+' : ''}${stats.totalPnl.toFixed(2)}`, color: stats.totalPnl >= 0 ? '#16a34a' : '#dc2626', valClass: stats.totalPnl >= 0 ? 'text-emerald-600 dark:text-emerald-400' : 'text-red-600 dark:text-red-400' }, + { icon: AlertTriangle, label: 'Risk', val: `${p.risk_per_trade_percent}%`, color: '#f59e0b', valClass: 'text-[var(--foreground)]' }, ].map((item, i) => ( -
- {/* Subtle column glow */} -
+
-
- {item.label} + {item.label}
- {item.val} + {item.val}
))}
-
+
Capital: {capitalHealth} {botState.accountSnapshot && ( @@ -771,30 +757,21 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{/* Win rate section */} -
+
- Win Rate - {stats.winRate.toFixed(1)}% · {stats.tradeCount} trades + Win Rate + {stats.winRate.toFixed(1)}% · {stats.tradeCount} trades
-
+
= 50 - ? 'linear-gradient(90deg, #00ff88, #00cc6a)' + ? '#16a34a' : stats.winRate >= 30 - ? 'linear-gradient(90deg, #f59e0b, #d97706)' - : 'linear-gradient(90deg, #ef4444, #dc2626)', - boxShadow: stats.winRate >= 50 - ? '0 0 10px rgba(0,255,136,0.35), inset 0 1px 0 rgba(255,255,255,0.2)' - : 'inset 0 1px 0 rgba(255,255,255,0.15)', + ? '#f59e0b' + : '#dc2626', }} />
@@ -802,30 +779,23 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{/* ── Elevated inner panel: Details Table ── */} -
+
{/* Table header */} -
- - Configuration +
+ + Configuration
- - + + - - + + - - + + - - + + - - + + - - + + {profitTarget && ( - - + + )} - - + + @@ -922,71 +875,58 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{p.symbols.split(',').slice(0, 5).map(s => ( - + {s.trim()} ))} {p.symbols.split(',').length > 5 && ( - + +{p.symbols.split(',').length - 5} )}
- + ); })} {/* ─── EDIT / CREATE DRAWER ─── */} {isDrawerOpen && createPortal( -
+
setIsDrawerOpen(false)} /> -
+
{/* Drawer header */} -
-
+
+
-
{isAddingMode - ? + ? : }
-

{isAddingMode ? 'Create Profile' : 'Edit Profile'}

-

{isAddingMode ? 'Configure a new trading strategy' : editingProfile.name}

+

{isAddingMode ? 'Create Profile' : 'Edit Profile'}

+

{isAddingMode ? 'Configure a new trading strategy' : editingProfile.name}

- +
{/* Drawer tab nav */} -
+
{([ { id: 'settings' as const, label: 'General', icon: Settings }, { id: 'logic' as const, label: 'Rules', icon: Cpu }, @@ -996,8 +936,8 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi key={t.id} onClick={() => setDrawerTab(t.id)} className={`flex-1 flex items-center justify-center gap-1.5 py-2 rounded-md text-[10px] font-bold uppercase tracking-wider transition-all ${drawerTab === t.id - ? 'bg-[#00ff88] text-black shadow-sm' - : 'text-zinc-500 hover:text-zinc-300' + ? 'bg-[var(--card)] text-[var(--foreground)] shadow-sm' + : 'text-[var(--muted-foreground)] hover:text-[var(--foreground)]' }`} > @@ -1008,78 +948,51 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{/* Drawer content */} -
+
{drawerTab === 'settings' && ( <> {/* Profile Name */}
- - Profile Name + setEditingProfile({ ...editingProfile, name: e.target.value })} - className="w-full rounded-lg px-4 py-2.5 text-sm outline-none transition-colors" placeholder="e.g. BTC Momentum Scalper" - style={{ - background: '#161722', - border: '1px solid rgba(255,255,255,0.12)', - color: '#ffffff', - caretColor: '#00ff88', - }} />
{/* Current User (read-only) */}
- -
-
+ +
+
{(authUser?.email || '?').charAt(0).toUpperCase()}
- {authUser?.email || 'Current User'} - You + {authUser?.email || 'Current User'} + You
{/* Trading Symbols */}
- +
Rules Active
Rules Active
{enabledRules.slice(0, 5).map(r => { const ruleDef = AVAILABLE_RULES.find(ar => ar.id === r.ruleId); return ruleDef ? ( @@ -834,84 +804,67 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi ) : null; })} {ruleCount > 5 && ( - +{ruleCount - 5} + +{ruleCount - 5} )}
Max Daily Loss
Max Daily Loss - ${maxLoss} + ${maxLoss}
Max Open Trades
Max Open Trades - {maxOpen} + {maxOpen}
Order Type
Order Type - + {orderType}
Cooldown
Cooldown - {cooldown}m + {cooldown}m
Entry Mode
Entry Mode - + {entryMode === 'long_only' ? 'LONG ONLY' : 'BOTH SIDES'}
Profit Target
Profit Target - ${profitTarget} + ${profitTarget}
Voting Threshold
Voting Threshold - {(minPassRatio * 100).toFixed(0)}% + {(minPassRatio * 100).toFixed(0)}%