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 { Card } from './ui/card';
import { Input } from './ui/input'; import { Input } from './ui/input';
import { Select } from './ui/select'; import { Select } from './ui/select';
import { Textarea } from './ui/Primitives';
import { cn } from '../lib/utils'; import { cn } from '../lib/utils';
// ChatControl is now rendered globally in App.tsx // ChatControl is now rendered globally in App.tsx
@ -254,16 +255,19 @@ export const summarizePortfolioStats = (
// --- UI COMPONENTS --- // --- UI COMPONENTS ---
const ToggleSwitch = ({ checked, onChange }: { checked: boolean, onChange: (v: boolean) => void }) => ( const ToggleSwitch = ({ checked, onChange }: { checked: boolean, onChange: (v: boolean) => void }) => (
<button <Button
type="button" type="button"
variant="ghost"
role="switch"
aria-checked={checked}
onClick={(e) => { e.stopPropagation(); onChange(!checked); }} onClick={(e) => { e.stopPropagation(); onChange(!checked); }}
className={cn( 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)]' 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`} /> <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 }) => ( 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={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> <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> </div>
<input <Input
type="range" min={min} max={max} step={step || 1} value={value} type="range" min={min} max={max} step={step || 1} value={value}
aria-label={label}
onChange={(e) => onChange(Number(e.target.value))} 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)]"> <div className="flex justify-between font-mono text-[9px] text-[var(--muted-foreground)]">
<span>{min}{unit}</span> <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: 'logic' as const, label: 'Rules', icon: Cpu },
{ id: 'advanced' as const, label: 'Risk & Execution', icon: Shield }, { id: 'advanced' as const, label: 'Risk & Execution', icon: Shield },
]).map(t => ( ]).map(t => (
<button <Button
type="button"
variant="ghost"
size="sm"
key={t.id} key={t.id}
onClick={() => setDrawerTab(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' ? 'bg-[var(--card)] text-[var(--foreground)] shadow-sm'
: 'text-[var(--muted-foreground)] hover:text-[var(--foreground)]' : 'text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
}`} }`}
> >
<t.icon size={11} /> <t.icon size={11} />
{t.label} {t.label}
</button> </Button>
))} ))}
</div> </div>
</div> </div>
@ -979,10 +987,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{/* Trading Symbols */} {/* Trading Symbols */}
<div className="space-y-1.5"> <div className="space-y-1.5">
<label className={labelClass}>Trading Symbols</label> <label className={labelClass}>Trading Symbols</label>
<textarea <Textarea
value={editingProfile.symbols || ''} value={editingProfile.symbols || ''}
onChange={e => setEditingProfile({ ...editingProfile, symbols: e.target.value })} 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" placeholder="BTC/USDT, ETH/USDT, SOL/USDT"
/> />
<p className="text-[9px] text-[var(--muted-foreground)]">Comma-separated trading pairs</p> <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'; const isMandatory = currentType === 'mandatory';
return ( return (
<button <Button
type="button"
variant="ghost"
size="sm"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
const newRules = editingProfile.strategy_config?.rules?.map(r => 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" 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-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' : 'bg-blue-500/10 text-blue-400 border-blue-500/20 hover:bg-blue-500/20'
}`} }`}
> >
{isMandatory ? 'Mandatory' : 'Voting'} {isMandatory ? 'Mandatory' : 'Voting'}
</button> </Button>
); );
})()} })()}
</div> </div>
@ -1231,7 +1242,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
<label className={labelClass}>Order Type</label> <label className={labelClass}>Order Type</label>
<div className="flex gap-2"> <div className="flex gap-2">
{(['market', 'limit'] as const).map(t => ( {(['market', 'limit'] as const).map(t => (
<button <Button
type="button"
variant="ghost"
size="sm"
key={t} key={t}
onClick={() => setEditingProfile({ onClick={() => setEditingProfile({
...editingProfile, ...editingProfile,
@ -1246,7 +1260,7 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
}`} }`}
> >
{t} {t}
</button> </Button>
))} ))}
</div> </div>
</div> </div>
@ -1257,7 +1271,10 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{ value: 'both', label: 'Both Sides' }, { value: 'both', label: 'Both Sides' },
{ value: 'long_only', label: 'Long Only' } { value: 'long_only', label: 'Long Only' }
] as const).map(mode => ( ] as const).map(mode => (
<button <Button
type="button"
variant="ghost"
size="sm"
key={mode.value} key={mode.value}
onClick={() => setEditingProfile({ onClick={() => setEditingProfile({
...editingProfile, ...editingProfile,
@ -1272,7 +1289,7 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
}`} }`}
> >
{mode.label} {mode.label}
</button> </Button>
))} ))}
</div> </div>
<p className={helpTextClass}> <p className={helpTextClass}>