refactor(ui): normalize trade profile controls

This commit is contained in:
Saravana Achu Mac 2026-05-09 01:58:29 -07:00
parent 1300de98a9
commit cdc0e57ae9

View File

@ -28,6 +28,7 @@ import { Button } from './ui/button';
import { Card } from './ui/card';
import { Input } from './ui/input';
import { Select } from './ui/select';
import { Textarea } from './ui/Primitives';
import { cn } from '../lib/utils';
// ChatControl is now rendered globally in App.tsx
@ -254,16 +255,19 @@ export const summarizePortfolioStats = (
// --- UI COMPONENTS ---
const ToggleSwitch = ({ checked, onChange }: { checked: boolean, onChange: (v: boolean) => void }) => (
<button
<Button
type="button"
variant="ghost"
role="switch"
aria-checked={checked}
onClick={(e) => { e.stopPropagation(); onChange(!checked); }}
className={cn(
'relative inline-flex h-5 w-9 items-center rounded-full border transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]',
'relative inline-flex h-5 min-h-0 w-9 items-center rounded-full border px-0 py-0 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]',
checked ? 'border-[var(--accent)] bg-[var(--accent)]' : 'border-[var(--border)] bg-[var(--muted)]'
)}
>
<span className={`${checked ? 'translate-x-5 bg-white' : 'translate-x-1 bg-[var(--muted-foreground)]'} inline-block h-3 w-3 transform rounded-full transition-transform duration-200`} />
</button>
</Button>
);
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 }) => (
@ -272,10 +276,11 @@ const Slider = ({ value, onChange, min, max, step, unit, label }: { value: numbe
<span className={labelClass}>{label}</span>
<span className="rounded-lg bg-[var(--accent-soft)] px-2.5 py-0.5 font-mono text-xs font-bold text-[var(--accent)]">{value}{unit}</span>
</div>
<input
<Input
type="range" min={min} max={max} step={step || 1} value={value}
aria-label={label}
onChange={(e) => onChange(Number(e.target.value))}
className="h-1 w-full accent-[var(--accent)]"
className="h-1 min-h-0 w-full px-0 py-0 accent-[var(--accent)]"
/>
<div className="flex justify-between font-mono text-[9px] text-[var(--muted-foreground)]">
<span>{min}{unit}</span>
@ -934,17 +939,20 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{ id: 'logic' as const, label: 'Rules', icon: Cpu },
{ id: 'advanced' as const, label: 'Risk & Execution', icon: Shield },
]).map(t => (
<button
<Button
type="button"
variant="ghost"
size="sm"
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
className={`flex-1 flex items-center justify-center gap-1.5 rounded-md py-2 text-[10px] font-bold uppercase tracking-wider transition-all ${drawerTab === t.id
? 'bg-[var(--card)] text-[var(--foreground)] shadow-sm'
: 'text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
}`}
>
<t.icon size={11} />
{t.label}
</button>
</Button>
))}
</div>
</div>
@ -979,10 +987,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{/* Trading Symbols */}
<div className="space-y-1.5">
<label className={labelClass}>Trading Symbols</label>
<textarea
<Textarea
value={editingProfile.symbols || ''}
onChange={e => setEditingProfile({ ...editingProfile, symbols: e.target.value })}
className="h-20 w-full resize-none rounded-xl border border-[var(--border)] bg-[var(--input)] px-4 py-2.5 font-mono text-sm text-[var(--foreground)] outline-none transition placeholder:text-[var(--muted-foreground)] focus:border-[var(--ring)] focus:ring-2 focus:ring-[var(--ring-soft)]"
className="h-20 w-full resize-none font-mono text-sm"
placeholder="BTC/USDT, ETH/USDT, SOL/USDT"
/>
<p className="text-[9px] text-[var(--muted-foreground)]">Comma-separated trading pairs</p>
@ -1046,7 +1054,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
const isMandatory = currentType === 'mandatory';
return (
<button
<Button
type="button"
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation();
const newRules = editingProfile.strategy_config?.rules?.map(r =>
@ -1058,13 +1069,13 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
});
}}
title="Click to toggle Mandatory/Voting"
className={`px-1.5 py-0.5 rounded text-[8px] font-bold uppercase tracking-tighter border transition-all ${isMandatory
className={`min-h-0 rounded border px-1.5 py-0.5 text-[8px] font-bold uppercase tracking-tighter transition-all ${isMandatory
? 'bg-amber-500/10 text-amber-500 border-amber-500/20 hover:bg-amber-500/20'
: 'bg-blue-500/10 text-blue-400 border-blue-500/20 hover:bg-blue-500/20'
}`}
>
{isMandatory ? 'Mandatory' : 'Voting'}
</button>
</Button>
);
})()}
</div>
@ -1231,7 +1242,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
<label className={labelClass}>Order Type</label>
<div className="flex gap-2">
{(['market', 'limit'] as const).map(t => (
<button
<Button
type="button"
variant="ghost"
size="sm"
key={t}
onClick={() => setEditingProfile({
...editingProfile,
@ -1246,7 +1260,7 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
}`}
>
{t}
</button>
</Button>
))}
</div>
</div>
@ -1257,7 +1271,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{ value: 'both', label: 'Both Sides' },
{ value: 'long_only', label: 'Long Only' }
] as const).map(mode => (
<button
<Button
type="button"
variant="ghost"
size="sm"
key={mode.value}
onClick={() => setEditingProfile({
...editingProfile,
@ -1272,7 +1289,7 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
}`}
>
{mode.label}
</button>
</Button>
))}
</div>
<p className={helpTextClass}>