refactor(ui): redesign trade plans workflow shell
This commit is contained in:
parent
0743c16b71
commit
0144124d0d
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user