refactor(ui): redesign trade plans workflow shell

This commit is contained in:
Saravana Achu Mac 2026-05-07 10:13:37 -07:00
parent 0743c16b71
commit 0144124d0d

View File

@ -971,12 +971,14 @@ export function SimpleView() {
description="Create and manage short-term trade plans, then convert filled positions into long-term holds when you want to stop automated exits."
/>
<div className="grid gap-8 xl:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]">
<Card>
<CardHeader>
<div className="grid gap-8 xl:grid-cols-[minmax(0,1.05fr)_minmax(380px,0.95fr)]">
<Card className="overflow-hidden">
<CardHeader className="border-b border-[var(--border)] bg-[var(--card-elevated)]/80 p-6">
<div>
<CardTitle className="uppercase">{editingSetupId ? 'Edit setup' : 'New setup'}</CardTitle>
<CardDescription>Build a short-term buy plan or attach a managed profit exit to an existing holding.</CardDescription>
<CardTitle>{editingSetupId ? 'Edit trade plan' : 'Create trade plan'}</CardTitle>
<CardDescription className="mt-2 max-w-2xl leading-6">
Build a guided plan with clear setup, instrument, price, sizing, trigger, and review steps before automation is armed.
</CardDescription>
</div>
<Button
type="button"
@ -989,15 +991,21 @@ export function SimpleView() {
}}
variant="outline"
size="sm"
className="uppercase tracking-[0.2em]"
>
Reset
</Button>
</CardHeader>
<CardContent>
<form className="space-y-6" onSubmit={handleSubmit}>
<div className="grid gap-3 md:grid-cols-2">
<CardContent className="p-6">
<form className="space-y-5" onSubmit={handleSubmit}>
<section className="rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card-elevated)] p-5">
<div className="mb-4">
<div className="text-sm font-bold text-[var(--foreground)]">1. Setup type</div>
<p className="mt-1 text-sm leading-6 text-[var(--muted-foreground)]">
Choose whether this plan opens a new position or manages profit-taking for an existing holding.
</p>
</div>
<div className="grid gap-3 md:grid-cols-2">
<Button
type="button"
onClick={() => {
@ -1006,17 +1014,18 @@ export function SimpleView() {
updateDraft('side', 'buy');
}}
variant="ghost"
className={`rounded-[1.25rem] border px-4 py-4 text-left transition ${
className={`h-auto justify-start rounded-[1.25rem] border px-5 py-5 text-left transition ${
draft.side === 'buy'
? 'border-[var(--primary)] bg-[var(--accent-soft)]'
: 'border-[var(--border)] bg-[var(--card-elevated)]'
}`}
>
<div className="text-[11px] font-black uppercase tracking-[0.24em] text-[var(--muted-foreground)]">Create plan</div>
<div className="mt-1 text-sm font-semibold text-[var(--foreground)]">New short-term buy plan</div>
<div className="mt-1 text-sm text-[var(--muted-foreground)]">Arm a dip-buy trigger and let the app manage the profit exit after fill.</div>
</Button>
<Button
<div>
<div className="text-sm font-bold text-[var(--foreground)]">New short-term buy plan</div>
<div className="mt-2 text-sm font-normal leading-6 text-[var(--muted-foreground)]">Arm a dip-buy trigger and let the app manage the profit exit after fill.</div>
</div>
</Button>
<Button
type="button"
onClick={() => {
dispatch({ type: 'clear-feedback' });
@ -1027,21 +1036,23 @@ export function SimpleView() {
}
}}
variant="ghost"
className={`rounded-[1.25rem] border px-4 py-4 text-left transition ${
className={`h-auto justify-start rounded-[1.25rem] border px-5 py-5 text-left transition ${
draft.side === 'sell'
? 'border-[var(--primary)] bg-[var(--accent-soft)]'
: 'border-[var(--border)] bg-[var(--card-elevated)]'
}`}
>
<div className="text-[11px] font-black uppercase tracking-[0.24em] text-[var(--muted-foreground)]">Manage holding</div>
<div className="mt-1 text-sm font-semibold text-[var(--foreground)]">Attach an exit plan</div>
<div className="mt-1 text-sm text-[var(--muted-foreground)]">Choose an existing holding and place it back under managed profit-taking.</div>
</Button>
</div>
<div>
<div className="text-sm font-bold text-[var(--foreground)]">Manage an existing holding</div>
<div className="mt-2 text-sm font-normal leading-6 text-[var(--muted-foreground)]">Choose a filled holding and place it back under managed profit-taking.</div>
</div>
</Button>
</div>
</section>
{draft.side === 'sell' && (
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Existing holding</span>
<label className="ux-field-stack rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card-elevated)] p-5">
<span className="ux-field-label">Existing holding</span>
<Select
value={selectedHoldingTradeId || ''}
onChange={(e: ChangeEvent<HTMLSelectElement>) => {
@ -1056,15 +1067,22 @@ export function SimpleView() {
label: `${holding.symbol} · ${holding.size} @ ${holding.entryPrice.toFixed(4)}`,
}))}
/>
<span className="block text-[11px] text-[var(--muted-foreground)]">
<span className="ux-helper-text">
Trade Plans can manage an existing filled holding by attaching a profit exit target to it.
</span>
</label>
)}
<div className="grid gap-4 md:grid-cols-2">
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Symbol</span>
<section className="rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card-elevated)] p-5">
<div className="mb-4">
<div className="text-sm font-bold text-[var(--foreground)]">2. Instrument and market price</div>
<p className="mt-1 text-sm leading-6 text-[var(--muted-foreground)]">
Select the symbol and confirm the reference price that automation will use for trigger calculations.
</p>
</div>
<div className="grid gap-4 md:grid-cols-2">
<label className="ux-field-stack">
<span className="ux-field-label">Symbol</span>
<Input
value={draft.symbol}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
@ -1089,11 +1107,11 @@ export function SimpleView() {
<option key={symbol} value={symbol} />
))}
</datalist>
<span className="block text-[11px] text-[var(--muted-foreground)]">
<span className="ux-helper-text">
Start typing to pick a supported symbol. Suggestions come from live market symbols plus common supported assets.
</span>
{normalizedSymbol && !isSuggestedSymbol ? (
<span className="block text-[11px] text-amber-700 dark:text-amber-300">
<span className="block text-sm text-[var(--bl-warning)]">
This symbol is not in the current supported suggestions. Double-check it before saving.
</span>
) : null}
@ -1132,32 +1150,19 @@ export function SimpleView() {
) : null}
</label>
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Setup type</span>
<Select
value={draft.side}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('side', e.target.value as SimpleSide)}
options={[
{ value: 'buy', label: 'Buy the dip + profit exit' },
{ value: 'sell', label: 'Manage existing holding at profit' },
]}
/>
</label>
</div>
<div className="grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]">
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Market price (auto-fetched)</span>
<div className="grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]">
<label className="ux-field-stack">
<span className="ux-field-label">Market price</span>
<Input
value={draft.currentMarketPrice}
readOnly
className="bg-[var(--muted)] text-[var(--foreground)]"
/>
<div className="space-y-1">
<span className="block text-[11px] text-[var(--muted-foreground)]">
<span className="ux-helper-text">
Uses live market price when available. Outside market hours, it falls back to the latest close.
</span>
<span className="block text-[11px] text-[var(--muted-foreground)]">
<span className="block text-sm text-[var(--muted-foreground)]">
Price source:{' '}
<span className="font-semibold text-[var(--foreground)]">
{marketPriceSource === 'live'
@ -1174,7 +1179,7 @@ export function SimpleView() {
<Button
type="button"
onClick={() => void handleLoadMarketPrice()}
className="self-end uppercase tracking-[0.2em]"
className="self-start md:self-end"
variant="outline"
disabled={loadingPrice || !normalizedSymbol}
>
@ -1183,13 +1188,22 @@ export function SimpleView() {
Refresh
</span>
</Button>
</div>
</div>
</section>
<div className="grid gap-4 md:grid-cols-2">
<section className="rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card-elevated)] p-5">
<div className="mb-4">
<div className="text-sm font-bold text-[var(--foreground)]">3. Sizing and notes</div>
<p className="mt-1 text-sm leading-6 text-[var(--muted-foreground)]">
Define the planned exposure and add context for why the plan should run.
</p>
</div>
<div className="grid gap-4 md:grid-cols-2">
{draft.side === 'buy' ? (
<>
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Sizing method</span>
<label className="ux-field-stack">
<span className="ux-field-label">Sizing method</span>
<Select
value={draft.sizingMode}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('sizingMode', e.target.value as 'quantity' | 'amount')}
@ -1200,8 +1214,8 @@ export function SimpleView() {
/>
</label>
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-500">
<label className="ux-field-stack">
<span className="ux-field-label">
{draft.sizingMode === 'amount' ? 'Spend amount (USD)' : 'Planned quantity'}
</span>
<Input
@ -1215,17 +1229,17 @@ export function SimpleView() {
}}
placeholder={draft.sizingMode === 'amount' ? '250' : '10'}
/>
<span className="block text-[11px] text-[var(--muted-foreground)]">
<span className="ux-helper-text">
Quantity supports fractional shares/coins. Amount spends an approximate USD budget at trigger time.
</span>
<span className="block text-[11px] text-[var(--muted-foreground)]">
<span className="ux-helper-text">
Use quantity when you know the units you want. Use amount to budget dollars and let the app derive fractional size at entry.
</span>
</label>
</>
) : (
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-500">
<label className="ux-field-stack">
<span className="ux-field-label">
Holding size
</span>
<Input
@ -1238,8 +1252,8 @@ export function SimpleView() {
</label>
)}
<label className="space-y-2">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Notes</span>
<label className="ux-field-stack">
<span className="ux-field-label">Notes</span>
<Input
value={draft.notes}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraft('notes', e.target.value)}
@ -1247,11 +1261,13 @@ export function SimpleView() {
/>
</label>
</div>
</section>
{draft.side === 'buy' && (
<div className="grid gap-4 rounded-[1.5rem] border border-[var(--border)] bg-[var(--card-elevated)] p-5 md:grid-cols-[0.55fr_0.45fr]">
<div className="space-y-3">
<p className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Drop trigger</p>
<section className="grid gap-4 rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card-elevated)] p-5 md:grid-cols-[0.55fr_0.45fr]">
<div className="ux-field-stack">
<p className="text-sm font-bold text-[var(--foreground)]">4. Drop trigger</p>
<p className="ux-helper-text">Set the dip condition that should arm the buy entry.</p>
<Select
value={draft.dropMode}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('dropMode', e.target.value as TriggerMode)}
@ -1261,8 +1277,8 @@ export function SimpleView() {
]}
/>
</div>
<label className="space-y-3">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">
<label className="ux-field-stack">
<span className="ux-field-label">
{draft.dropMode === 'dollar' ? 'Drop amount ($)' : 'Drop amount (%)'}
</span>
<Input
@ -1271,12 +1287,13 @@ export function SimpleView() {
placeholder={draft.dropMode === 'dollar' ? '0.00' : '0'}
/>
</label>
</div>
</section>
)}
<div className="grid gap-4 rounded-[1.5rem] border border-[var(--border)] bg-[var(--card-elevated)] p-5 md:grid-cols-[0.55fr_0.45fr]">
<div className="space-y-3">
<p className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Profit exit</p>
<section className="grid gap-4 rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card-elevated)] p-5 md:grid-cols-[0.55fr_0.45fr]">
<div className="ux-field-stack">
<p className="text-sm font-bold text-[var(--foreground)]">{draft.side === 'buy' ? '5. Profit exit' : '4. Profit exit'}</p>
<p className="ux-helper-text">Define when automation should take profit and close or reduce risk.</p>
<Select
value={draft.profitMode}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('profitMode', e.target.value as TriggerMode)}
@ -1286,8 +1303,8 @@ export function SimpleView() {
]}
/>
</div>
<label className="space-y-3">
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">
<label className="ux-field-stack">
<span className="ux-field-label">
{draft.profitMode === 'dollar' ? 'Profit target ($)' : 'Profit target (%)'}
</span>
<Input
@ -1296,13 +1313,13 @@ export function SimpleView() {
placeholder={draft.profitMode === 'dollar' ? '7.50' : '10'}
/>
</label>
</div>
</section>
{draft.side === 'sell' && (
<div className={`rounded-[1.5rem] border px-4 py-4 text-sm ${
selectedSellHolding
? 'border-emerald-500/20 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300'
: 'border-red-500/20 bg-red-500/10 text-red-700 dark:text-red-300'
? 'border-[var(--bl-success)]/20 bg-[var(--bl-success-muted)] text-[var(--bl-success)]'
: 'border-[var(--bl-danger)]/20 bg-[var(--bl-danger-muted)] text-[var(--bl-danger)]'
}`}>
{selectedSellHolding
? `Holding ready: ${selectedSellHolding.symbol} · ${selectedSellHolding.size} units at ${selectedSellHolding.entryPrice.toFixed(4)}. Executed buys also appear in Portfolio as live positions.`
@ -1311,31 +1328,44 @@ export function SimpleView() {
)}
{previewText && (
<div className="rounded-[1.5rem] border border-[var(--border)] bg-[var(--accent-soft)] px-5 py-4 text-sm text-[var(--foreground)]">
{previewText}
</div>
<section className="rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--accent-soft)] p-5 text-sm leading-6 text-[var(--foreground)]">
<div className="mb-1 font-bold">{draft.side === 'buy' ? '6. Review and create' : '5. Review and create'}</div>
<div>{previewText}</div>
<div className="mt-3 text-[var(--muted-foreground)]">
Review the symbol, price source, sizing, and exit target before creating the plan.
</div>
</section>
)}
{message && (
<div className="rounded-2xl border border-emerald-500/20 bg-emerald-500/10 px-4 py-3 text-sm text-emerald-700 dark:text-emerald-300">
<div className="rounded-2xl border border-[var(--bl-success)]/20 bg-[var(--bl-success-muted)] px-4 py-3 text-sm text-[var(--bl-success)]">
{message}
</div>
)}
{error && (
<div className="rounded-2xl border border-red-500/20 bg-red-500/10 px-4 py-3 text-sm text-red-700 dark:text-red-300">
<div className="rounded-2xl border border-[var(--bl-danger)]/20 bg-[var(--bl-danger-muted)] px-4 py-3 text-sm text-[var(--bl-danger)]">
{error}
</div>
)}
<Button
type="submit"
disabled={saveButtonDisabled}
className="w-full uppercase tracking-[0.24em]"
size="lg"
>
{submitting ? 'Saving...' : saveButtonLabel}
</Button>
<div className="sticky bottom-4 z-[var(--bl-z-sticky)] rounded-[var(--bl-radius-card)] border border-[var(--border)] bg-[var(--card)]/95 p-4 shadow-[var(--bl-shadow-card)] backdrop-blur">
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div className="text-sm leading-6 text-[var(--muted-foreground)]">
{saveButtonDisabled
? 'Complete the required setup details before creating the plan.'
: 'Ready to save this plan and arm the next automation step.'}
</div>
<Button
type="submit"
disabled={saveButtonDisabled}
className="w-full md:w-auto"
size="lg"
>
{submitting ? 'Saving...' : saveButtonLabel}
</Button>
</div>
</div>
</form>
</CardContent>
</Card>