feat(simple): add manage existing holding flow
This commit is contained in:
parent
f51172518e
commit
1f03bb83cd
@ -667,6 +667,10 @@ export function SimpleView() {
|
||||
() => simpleHoldings.find((holding) => holding.symbol === normalizedSymbol) || null,
|
||||
[simpleHoldings, normalizedSymbol],
|
||||
);
|
||||
const availableSellHoldings = useMemo(
|
||||
() => [...simpleHoldings].sort((left, right) => left.symbol.localeCompare(right.symbol)),
|
||||
[simpleHoldings],
|
||||
);
|
||||
|
||||
const supportedSymbols = useMemo(() => {
|
||||
const fromState = Object.keys(symbolState || {}).map(normalizeKnownSymbol).filter(Boolean) as string[];
|
||||
@ -751,6 +755,24 @@ export function SimpleView() {
|
||||
setDraft((prev) => ({ ...prev, [key]: value }));
|
||||
}
|
||||
|
||||
function applyHoldingToDraft(holding: SimpleHolding) {
|
||||
setMarketPriceSource(null);
|
||||
setDraft((prev) => ({
|
||||
...prev,
|
||||
side: 'sell',
|
||||
symbol: holding.symbol,
|
||||
quantity: String(holding.size),
|
||||
currentMarketPrice: '',
|
||||
}));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (draft.side !== 'sell') return;
|
||||
if (matchingHolding) return;
|
||||
if (availableSellHoldings.length === 0) return;
|
||||
applyHoldingToDraft(availableSellHoldings[0]);
|
||||
}, [draft.side, matchingHolding, availableSellHoldings]);
|
||||
|
||||
async function copyIdentifier(kind: 'trade' | 'order', value: string | null | undefined) {
|
||||
if (!value) return;
|
||||
try {
|
||||
@ -950,7 +972,7 @@ export function SimpleView() {
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="uppercase">{editingSetupId ? 'Edit setup' : 'New setup'}</CardTitle>
|
||||
<CardDescription>Saved trigger workflow, not an immediate broker order</CardDescription>
|
||||
<CardDescription>Build a short-term buy plan or attach a managed profit exit to an existing holding.</CardDescription>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
@ -974,6 +996,74 @@ export function SimpleView() {
|
||||
|
||||
<CardContent>
|
||||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setError(null);
|
||||
setMessage(null);
|
||||
setDraft((prev) => ({ ...prev, side: 'buy' }));
|
||||
}}
|
||||
className={`rounded-[1.25rem] border px-4 py-4 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
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setError(null);
|
||||
setMessage(null);
|
||||
if (availableSellHoldings.length > 0) {
|
||||
applyHoldingToDraft(availableSellHoldings[0]);
|
||||
} else {
|
||||
setDraft((prev) => ({ ...prev, side: 'sell' }));
|
||||
}
|
||||
}}
|
||||
className={`rounded-[1.25rem] border px-4 py-4 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>
|
||||
|
||||
{draft.side === 'sell' && (
|
||||
<label className="space-y-2">
|
||||
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Existing holding</span>
|
||||
<Select
|
||||
value={matchingHolding?.symbol || normalizedSymbol}
|
||||
onChange={(e) => {
|
||||
const selected = availableSellHoldings.find((holding) => holding.symbol === e.target.value);
|
||||
if (selected) applyHoldingToDraft(selected);
|
||||
}}
|
||||
disabled={availableSellHoldings.length === 0}
|
||||
>
|
||||
{availableSellHoldings.length === 0 ? (
|
||||
<option value="">No eligible holdings available</option>
|
||||
) : (
|
||||
availableSellHoldings.map((holding) => (
|
||||
<option key={`${holding.symbol}:${holding.tradeId || 'holding'}`} value={holding.symbol}>
|
||||
{holding.symbol} · {holding.size} @ {holding.entryPrice.toFixed(4)}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</Select>
|
||||
<span className="block text-[11px] text-[var(--muted-foreground)]">
|
||||
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>
|
||||
@ -1037,7 +1127,7 @@ export function SimpleView() {
|
||||
onChange={(e) => updateDraft('side', e.target.value as SimpleSide)}
|
||||
>
|
||||
<option value="buy">Buy the dip + profit exit</option>
|
||||
<option value="sell">Sell existing Simple holding at profit</option>
|
||||
<option value="sell">Manage existing holding at profit</option>
|
||||
</Select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user