From 6e33897c1a82d772d4110738ba77fa24b36579b7 Mon Sep 17 00:00:00 2001 From: Dennis Thiessen Date: Fri, 10 Jul 2026 13:10:13 +0200 Subject: [PATCH] Mark-as-taken opens a modal dialog instead of expanding inline The inline take form under the price rail pushed the card apart. It is now a focused overlay dialog (portaled to body - the unified panel's backdrop-filter/overflow would trap a fixed element): - symbol + direction header, stop/target/suggested-size summary line - shares (autofocused), entry, and target selector - Confirm trade / Cancel; closes on success, Escape, or backdrop click - the card and panel never move Co-Authored-By: Claude Fable 5 --- .../components/ticker/RecommendationPanel.tsx | 148 +++++++++++------- 1 file changed, 89 insertions(+), 59 deletions(-) diff --git a/frontend/src/components/ticker/RecommendationPanel.tsx b/frontend/src/components/ticker/RecommendationPanel.tsx index 646934c..a6d1283 100644 --- a/frontend/src/components/ticker/RecommendationPanel.tsx +++ b/frontend/src/components/ticker/RecommendationPanel.tsx @@ -1,4 +1,5 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; import type { TradeSetup } from '../../lib/types'; import { formatPrice, formatPercent } from '../../lib/format'; import { useCreatePaperTrade } from '../../hooks/usePaperTrades'; @@ -220,6 +221,16 @@ function SetupCard({ setup, action, currentPrice, risk, regime, selectedPrice, o const activeRR = selected?.rr_ratio ?? setup.rr_ratio; const activeProb = selected?.probability ?? prob; + // Close the take dialog on Escape. + useEffect(() => { + if (!taking) return; + const onKey = (e: globalThis.KeyboardEvent) => { + if (e.key === 'Escape') setTaking(false); + }; + window.addEventListener('keydown', onKey); + return () => window.removeEventListener('keydown', onKey); + }, [taking]); + const confirmTake = () => { createTrade.mutate( { @@ -343,66 +354,85 @@ function SetupCard({ setup, action, currentPrice, risk, regime, selectedPrice, o scaleTo={(setup.targets ?? []).map((t) => t.price)} /> - {taking && ( -
-
- - -
- {setup.targets && setup.targets.length > 1 ? ( - + +
+ {setup.targets && setup.targets.length > 1 ? ( + + ) : null} + +
+ - + {createTrade.isPending ? 'Taking…' : 'Confirm trade'} + + +
- + , + document.body, )} {/* Target ladder — open by default; clicking a row previews it on the rail */}