@forgecharts/sdk 1.1.31 → 1.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9338,8 +9338,9 @@ function VisibilityTool({
9338
9338
  )) })
9339
9339
  ] });
9340
9340
  }
9341
- function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavoritesChange, onVisibilityAction, visibilityActiveAction, onVisibilityDeactivate, onLinkClick, onDeleteClick }) {
9341
+ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavoritesChange, onVisibilityAction, visibilityActiveAction, onVisibilityDeactivate, onLinkClick, onDeleteClick, onLogout, user }) {
9342
9342
  const [favorites, setFavorites] = useState(drawingFavorites ?? []);
9343
+ const [profileOpen, setProfileOpen] = useState(false);
9343
9344
  const handleFavoritesChange = (favs) => {
9344
9345
  setFavorites(favs);
9345
9346
  onDrawingFavoritesChange?.(favs);
@@ -9358,7 +9359,8 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
9358
9359
  borderRadius: 8,
9359
9360
  width: 40,
9360
9361
  flexShrink: 0,
9361
- userSelect: "none"
9362
+ userSelect: "none",
9363
+ height: "100%"
9362
9364
  },
9363
9365
  children: [
9364
9366
  /* @__PURE__ */ jsx(PointerGroup, { activeTool, onSelectTool }),
@@ -9439,6 +9441,29 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
9439
9441
  /* @__PURE__ */ jsx("line", { x1: "9", y1: "6.5", x2: "9", y2: "11.5" })
9440
9442
  ] })
9441
9443
  }
9444
+ ),
9445
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
9446
+ (user || onLogout) && /* @__PURE__ */ jsx(
9447
+ "button",
9448
+ {
9449
+ className: "drawing-tool-btn profile-avatar-btn",
9450
+ title: "Profile",
9451
+ onClick: () => setProfileOpen(true),
9452
+ children: (user?.username?.[0] ?? user?.email?.[0] ?? "?").toUpperCase()
9453
+ }
9454
+ ),
9455
+ onLogout && /* @__PURE__ */ jsx(
9456
+ "button",
9457
+ {
9458
+ className: "drawing-tool-btn",
9459
+ title: "Log out",
9460
+ onClick: onLogout,
9461
+ children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "15", height: "15", stroke: "currentColor", fill: "none", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
9462
+ /* @__PURE__ */ jsx("path", { d: "M6 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3" }),
9463
+ /* @__PURE__ */ jsx("polyline", { points: "11 11 14 8 11 5" }),
9464
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "8", x2: "6", y2: "8" })
9465
+ ] })
9466
+ }
9442
9467
  )
9443
9468
  ]
9444
9469
  }
@@ -9450,6 +9475,49 @@ function LeftToolbar({ activeTool, onSelectTool, drawingFavorites, onDrawingFavo
9450
9475
  activeTool,
9451
9476
  onSelectTool
9452
9477
  }
9478
+ ),
9479
+ profileOpen && ReactDOM.createPortal(
9480
+ /* @__PURE__ */ jsx("div", { className: "profile-drawer-overlay", onClick: () => setProfileOpen(false), children: /* @__PURE__ */ jsxs("div", { className: "profile-drawer", onClick: (e) => e.stopPropagation(), children: [
9481
+ /* @__PURE__ */ jsxs("div", { className: "profile-drawer-header", children: [
9482
+ /* @__PURE__ */ jsx("span", { children: "Profile" }),
9483
+ /* @__PURE__ */ jsx(
9484
+ "button",
9485
+ {
9486
+ className: "profile-drawer-close-btn",
9487
+ onClick: () => setProfileOpen(false),
9488
+ title: "Close",
9489
+ children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "12", height: "12", stroke: "currentColor", fill: "none", strokeWidth: "2", strokeLinecap: "round", children: [
9490
+ /* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
9491
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
9492
+ ] })
9493
+ }
9494
+ )
9495
+ ] }),
9496
+ /* @__PURE__ */ jsx("div", { className: "profile-drawer-avatar", children: (user?.username?.[0] ?? user?.email?.[0] ?? "?").toUpperCase() }),
9497
+ user?.username && /* @__PURE__ */ jsx("div", { className: "profile-drawer-name", children: user.username }),
9498
+ user?.email && /* @__PURE__ */ jsx("div", { className: "profile-drawer-email", children: user.email }),
9499
+ user?.role && /* @__PURE__ */ jsx("div", { className: "profile-drawer-role", children: /* @__PURE__ */ jsx("span", { className: "profile-drawer-role-badge", children: user.role }) }),
9500
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
9501
+ onLogout && /* @__PURE__ */ jsx("div", { className: "profile-drawer-footer", children: /* @__PURE__ */ jsxs(
9502
+ "button",
9503
+ {
9504
+ className: "profile-drawer-logout-btn",
9505
+ onClick: () => {
9506
+ setProfileOpen(false);
9507
+ onLogout();
9508
+ },
9509
+ children: [
9510
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "14", height: "14", stroke: "currentColor", fill: "none", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", children: [
9511
+ /* @__PURE__ */ jsx("path", { d: "M6 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3" }),
9512
+ /* @__PURE__ */ jsx("polyline", { points: "11 11 14 8 11 5" }),
9513
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "8", x2: "6", y2: "8" })
9514
+ ] }),
9515
+ "Log out"
9516
+ ]
9517
+ }
9518
+ ) })
9519
+ ] }) }),
9520
+ document.body
9453
9521
  )
9454
9522
  ] });
9455
9523
  }
@@ -9906,7 +9974,9 @@ var ChartCanvas = forwardRef(
9906
9974
  priceFraction: priceFractionProp,
9907
9975
  onPriceFractionChange,
9908
9976
  getExchangeLogoUrl,
9909
- tradingBridge
9977
+ tradingBridge,
9978
+ onLogout,
9979
+ user
9910
9980
  }, ref) {
9911
9981
  const outerRef = useRef(null);
9912
9982
  const priceRef = useRef(null);
@@ -10289,7 +10359,9 @@ var ChartCanvas = forwardRef(
10289
10359
  onVisibilityAction: handleVisibilityAction,
10290
10360
  visibilityActiveAction,
10291
10361
  onVisibilityDeactivate: handleVisibilityDeactivate,
10292
- onDeleteClick: () => chartRef.current?.deleteSelectedDrawing()
10362
+ onDeleteClick: () => chartRef.current?.deleteSelectedDrawing(),
10363
+ ...onLogout ? { onLogout } : {},
10364
+ ...user ? { user } : {}
10293
10365
  }
10294
10366
  ),
10295
10367
  /* @__PURE__ */ jsxs(
@@ -10314,6 +10386,29 @@ var ChartCanvas = forwardRef(
10314
10386
  crosshairXRef.current = null;
10315
10387
  },
10316
10388
  children: [
10389
+ /* @__PURE__ */ jsx(
10390
+ "div",
10391
+ {
10392
+ style: {
10393
+ position: "absolute",
10394
+ bottom: 36,
10395
+ left: 8,
10396
+ zIndex: 5,
10397
+ pointerEvents: "none",
10398
+ fontFamily: "system-ui, -apple-system, 'Segoe UI', sans-serif",
10399
+ fontSize: 16,
10400
+ fontWeight: 900,
10401
+ letterSpacing: "-0.03em",
10402
+ background: "linear-gradient(90deg, var(--text, #fff) 20%, rgba(128,128,128,0.7) 100%)",
10403
+ WebkitBackgroundClip: "text",
10404
+ WebkitTextFillColor: "transparent",
10405
+ backgroundClip: "text",
10406
+ opacity: 0.45,
10407
+ userSelect: "none"
10408
+ },
10409
+ children: "ForgeCharts"
10410
+ }
10411
+ ),
10317
10412
  /* @__PURE__ */ jsxs(
10318
10413
  "div",
10319
10414
  {
@@ -11622,8 +11717,6 @@ function TopToolbar({
11622
11717
  flexShrink: 0
11623
11718
  },
11624
11719
  children: [
11625
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 700, letterSpacing: "-0.5px", color: "var(--accent)" }, children: "ForgeCharts" }),
11626
- /* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
11627
11720
  /* @__PURE__ */ jsxs(
11628
11721
  "button",
11629
11722
  {
@@ -11824,6 +11917,11 @@ function TopToolbar({
11824
11917
  }
11825
11918
  );
11826
11919
  }
11920
+ var OrderEntryIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round", children: [
11921
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1.5" }),
11922
+ /* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
11923
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
11924
+ ] });
11827
11925
  var WatchListIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "16", height: "16", stroke: "currentColor", fill: "none", strokeWidth: "1.5", children: [
11828
11926
  /* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "12", height: "1.5", rx: "0.5", fill: "currentColor", stroke: "none" }),
11829
11927
  /* @__PURE__ */ jsx("rect", { x: "2", y: "7", width: "9", height: "1.5", rx: "0.5", fill: "currentColor", stroke: "none" }),
@@ -11832,8 +11930,8 @@ var WatchListIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", wi
11832
11930
  /* @__PURE__ */ jsx("line", { x1: "13", y1: "10.75", x2: "13", y2: "12.75", strokeWidth: "1.2" }),
11833
11931
  /* @__PURE__ */ jsx("line", { x1: "12", y1: "11.75", x2: "14", y2: "11.75", strokeWidth: "1.2" })
11834
11932
  ] });
11835
- function RightToolbar({ watchlistOpen, onToggleWatchlist }) {
11836
- return /* @__PURE__ */ jsx(
11933
+ function RightToolbar({ watchlistOpen, onToggleWatchlist, orderEntryOpen, onToggleOrderEntry, showOrderEntry }) {
11934
+ return /* @__PURE__ */ jsxs(
11837
11935
  "div",
11838
11936
  {
11839
11937
  style: {
@@ -11848,15 +11946,26 @@ function RightToolbar({ watchlistOpen, onToggleWatchlist }) {
11848
11946
  flexShrink: 0,
11849
11947
  userSelect: "none"
11850
11948
  },
11851
- children: /* @__PURE__ */ jsx(
11852
- "button",
11853
- {
11854
- title: "Watch List",
11855
- className: `drawing-tool-btn${watchlistOpen ? " is-active" : ""}`,
11856
- onClick: onToggleWatchlist,
11857
- children: /* @__PURE__ */ jsx(WatchListIcon, {})
11858
- }
11859
- )
11949
+ children: [
11950
+ showOrderEntry !== false && /* @__PURE__ */ jsx(
11951
+ "button",
11952
+ {
11953
+ title: "Order Entry",
11954
+ className: `drawing-tool-btn${orderEntryOpen ? " is-active" : ""}`,
11955
+ onClick: onToggleOrderEntry,
11956
+ children: /* @__PURE__ */ jsx(OrderEntryIcon, {})
11957
+ }
11958
+ ),
11959
+ /* @__PURE__ */ jsx(
11960
+ "button",
11961
+ {
11962
+ title: "Watch List",
11963
+ className: `drawing-tool-btn${watchlistOpen ? " is-active" : ""}`,
11964
+ onClick: onToggleWatchlist,
11965
+ children: /* @__PURE__ */ jsx(WatchListIcon, {})
11966
+ }
11967
+ )
11968
+ ]
11860
11969
  }
11861
11970
  );
11862
11971
  }
@@ -12248,6 +12357,9 @@ function ChartWorkspace({
12248
12357
  // RightToolbar
12249
12358
  watchlistOpen,
12250
12359
  onToggleWatchlist,
12360
+ orderEntryOpen,
12361
+ onToggleOrderEntry,
12362
+ showOrderEntry,
12251
12363
  // BottomToolbar
12252
12364
  activeSymbol,
12253
12365
  timezone,
@@ -12366,7 +12478,11 @@ function ChartWorkspace({
12366
12478
  RightToolbar,
12367
12479
  {
12368
12480
  watchlistOpen,
12369
- onToggleWatchlist
12481
+ onToggleWatchlist,
12482
+ orderEntryOpen: orderEntryOpen ?? false,
12483
+ onToggleOrderEntry: onToggleOrderEntry ?? (() => {
12484
+ }),
12485
+ showOrderEntry
12370
12486
  }
12371
12487
  )
12372
12488
  ] }),
@@ -13467,45 +13583,930 @@ function WatchlistDrawer({ onClose, onSelectSymbol, symbolResolver }) {
13467
13583
  );
13468
13584
  }
13469
13585
  var BROKERS = [
13470
- { id: "rithmic", name: "Rithmic", description: "Futures & Forex", status: "available" },
13471
- { id: "ibkr", name: "Interactive Brokers", description: "Stocks, Options & More", status: "coming-soon" },
13472
- { id: "tradovate", name: "Tradovate", description: "Futures & Options", status: "coming-soon" },
13473
13586
  { id: "alpaca", name: "Alpaca", description: "Stocks & Crypto", status: "coming-soon" },
13587
+ { id: "ibkr", name: "Interactive Brokers", description: "Stocks, Options & More", status: "coming-soon" },
13588
+ { id: "ninjabrokerage", name: "NinjaTrader Brokerage", description: "Futures & Forex", status: "coming-soon" },
13589
+ { id: "rithmic", name: "Rithmic", description: "Futures & Forex", status: "available" },
13590
+ { id: "tastytrade", name: "tastytrade", description: "Options & Futures", status: "coming-soon" },
13474
13591
  { id: "tradestation", name: "TradeStation", description: "Stocks, Futures & Options", status: "coming-soon" },
13475
13592
  { id: "tradier", name: "Tradier", description: "Stocks & Options", status: "coming-soon" },
13476
- { id: "ninjabrokerage", name: "NinjaTrader Brokerage", description: "Futures & Forex", status: "coming-soon" },
13477
- { id: "tastytrade", name: "tastytrade", description: "Options & Futures", status: "coming-soon" }
13593
+ { id: "tradovate", name: "Tradovate", description: "Futures & Options", status: "coming-soon" }
13594
+ ];
13595
+ function CloseIcon() {
13596
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "12", height: "12", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: [
13597
+ /* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
13598
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
13599
+ ] });
13600
+ }
13601
+ function BackIcon() {
13602
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 14 14", width: "13", height: "13", stroke: "currentColor", strokeWidth: "1.8", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "9,2 4,7 9,12" }) });
13603
+ }
13604
+ var RITHMIC_PROP_FIRMS = [
13605
+ { id: "apex", name: "Apex Trader Funding" },
13606
+ { id: "bulenox", name: "Bulenox" },
13607
+ { id: "direct", name: "Direct / Rithmic account" },
13608
+ { id: "earn2trade", name: "Earn2Trade" },
13609
+ { id: "fundedtrader", name: "The Funded Trader" },
13610
+ { id: "ftmo", name: "FTMO" },
13611
+ { id: "ninjatrader", name: "NinjaTrader Brokerage" },
13612
+ { id: "takeprofittrader", name: "Take Profit Trader" },
13613
+ { id: "topstep", name: "TopstepTrader" },
13614
+ { id: "tradeday", name: "Tradeday" },
13615
+ { id: "wedbush", name: "Wedbush Futures" }
13478
13616
  ];
13479
- function TradeDrawer({ onClose }) {
13480
- return /* @__PURE__ */ jsxs("div", { className: "trade-drawer", children: [
13617
+ var RITHMIC_GATEWAY_OPTIONS = [
13618
+ // Test / UAT
13619
+ { id: "test-01", name: "Rithmic Test 01", region: "N. America", group: "Test / UAT" },
13620
+ // Paper Trading
13621
+ { id: "paper-01", name: "Rithmic Paper Trading 01", region: "N. America", group: "Paper Trading" },
13622
+ { id: "paper-colo-75", name: "Rithmic Paper Trading Colo 75", region: "N. America (colo)", group: "Paper Trading" },
13623
+ // Production
13624
+ { id: "prod-chicago-01", name: "Rithmic 01", region: "Chicago, US", group: "Production" },
13625
+ { id: "prod-new-york-04", name: "Rithmic 04", region: "New York, US", group: "Production" },
13626
+ { id: "prod-frankfurt-01", name: "Rithmic 01 Europe", region: "Frankfurt, DE", group: "Production" },
13627
+ { id: "prod-sydney-01", name: "Rithmic 01 Asia", region: "Sydney, AU", group: "Production" },
13628
+ { id: "prod-colo-75", name: "Rithmic 01 Colo 75", region: "Chicago, US (colo)", group: "Production" }
13629
+ ];
13630
+ function validateCreds(f) {
13631
+ const errs = {};
13632
+ if (!f.propFirm) errs.propFirm = "Please select your broker / prop firm";
13633
+ if (!f.username.trim()) errs.username = "Username is required";
13634
+ if (!f.password.trim()) errs.password = "Password is required";
13635
+ return errs;
13636
+ }
13637
+ function RithmicConnectDrawer({ onClose, onConnected, getAuthToken }) {
13638
+ const [step, setStep] = useState("credentials");
13639
+ const [fields, setFields] = useState({
13640
+ propFirm: "",
13641
+ username: "",
13642
+ password: "",
13643
+ gatewayId: "test-01"
13644
+ });
13645
+ const [errors, setErrors] = useState({});
13646
+ const [loading, setLoading] = useState(false);
13647
+ const [accounts, setAccounts] = useState([]);
13648
+ const [apiError, setApiError] = useState("");
13649
+ function set(key, value) {
13650
+ setFields((prev) => ({ ...prev, [key]: value }));
13651
+ if (errors[key]) setErrors((prev) => ({ ...prev, [key]: void 0 }));
13652
+ setApiError("");
13653
+ }
13654
+ async function authHeaders() {
13655
+ const h = { "Content-Type": "application/json" };
13656
+ if (getAuthToken) {
13657
+ try {
13658
+ h["Authorization"] = `Bearer ${await getAuthToken()}`;
13659
+ } catch {
13660
+ }
13661
+ }
13662
+ return h;
13663
+ }
13664
+ async function handleConnect(e) {
13665
+ e.preventDefault();
13666
+ const errs = validateCreds(fields);
13667
+ if (Object.keys(errs).length > 0) {
13668
+ setErrors(errs);
13669
+ return;
13670
+ }
13671
+ setLoading(true);
13672
+ setApiError("");
13673
+ try {
13674
+ const res = await fetch("/api/broker/rithmic/verify", {
13675
+ method: "POST",
13676
+ headers: await authHeaders(),
13677
+ body: JSON.stringify({
13678
+ propFirm: fields.propFirm,
13679
+ username: fields.username,
13680
+ password: fields.password,
13681
+ gatewayId: fields.gatewayId
13682
+ })
13683
+ });
13684
+ const body = await res.json().catch(() => ({}));
13685
+ if (!res.ok) {
13686
+ setApiError(body.error ?? `Error ${res.status} \u2014 check your credentials and try again`);
13687
+ return;
13688
+ }
13689
+ setAccounts(body.accounts ?? []);
13690
+ setStep("accounts");
13691
+ } catch {
13692
+ setApiError("Network error \u2014 please try again");
13693
+ } finally {
13694
+ setLoading(false);
13695
+ }
13696
+ }
13697
+ async function handleSelectAccount(account) {
13698
+ setLoading(true);
13699
+ setApiError("");
13700
+ try {
13701
+ const res = await fetch("/api/broker/credentials", {
13702
+ method: "POST",
13703
+ headers: await authHeaders(),
13704
+ body: JSON.stringify({
13705
+ broker: "rithmic",
13706
+ propFirm: fields.propFirm,
13707
+ username: fields.username,
13708
+ password: fields.password,
13709
+ gatewayId: fields.gatewayId,
13710
+ accountId: account.id,
13711
+ accountName: account.name
13712
+ })
13713
+ });
13714
+ if (!res.ok) {
13715
+ const body2 = await res.json().catch(() => ({}));
13716
+ setApiError(body2.error ?? `Error ${res.status}`);
13717
+ return;
13718
+ }
13719
+ const body = await res.json().catch(() => ({}));
13720
+ onConnected(body.connectionId ?? "");
13721
+ setStep("done");
13722
+ } catch {
13723
+ setApiError("Network error \u2014 please try again");
13724
+ } finally {
13725
+ setLoading(false);
13726
+ }
13727
+ }
13728
+ return /* @__PURE__ */ jsxs("div", { className: "trade-drawer trade-drawer--connect", children: [
13481
13729
  /* @__PURE__ */ jsxs("div", { className: "trade-drawer-header", children: [
13482
13730
  /* @__PURE__ */ jsxs("span", { className: "trade-drawer-title", children: [
13483
- /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
13731
+ /* @__PURE__ */ jsx("span", { className: "broker-logo broker-logo--rithmic", children: "R" }),
13732
+ "Rithmic \u2014 Connect"
13733
+ ] }),
13734
+ /* @__PURE__ */ jsxs("button", { className: "trade-drawer-close", onClick: onClose, title: "Close drawer", children: [
13735
+ /* @__PURE__ */ jsx(BackIcon, {}),
13736
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 11, marginLeft: 3 }, children: "Close drawer" })
13737
+ ] })
13738
+ ] }),
13739
+ /* @__PURE__ */ jsxs("div", { className: "trade-drawer-body trade-drawer-body--form", children: [
13740
+ step === "credentials" && /* @__PURE__ */ jsxs("form", { className: "broker-connect-form", onSubmit: handleConnect, noValidate: true, children: [
13741
+ /* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "Your broker / prop firm" }),
13742
+ /* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
13743
+ /* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
13744
+ "Firm ",
13745
+ /* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
13746
+ ] }),
13747
+ /* @__PURE__ */ jsxs(
13748
+ "select",
13749
+ {
13750
+ className: `bcf-input bcf-select${errors.propFirm ? " bcf-input--error" : ""}`,
13751
+ value: fields.propFirm,
13752
+ onChange: (e) => set("propFirm", e.target.value),
13753
+ children: [
13754
+ /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select your firm \u2014" }),
13755
+ RITHMIC_PROP_FIRMS.map((f) => /* @__PURE__ */ jsx("option", { value: f.id, children: f.name }, f.id))
13756
+ ]
13757
+ }
13758
+ ),
13759
+ errors.propFirm && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.propFirm })
13760
+ ] }),
13761
+ /* @__PURE__ */ jsx("div", { className: "bcf-section-label", style: { marginTop: 16 }, children: "Rithmic credentials" }),
13762
+ /* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
13763
+ /* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
13764
+ "Username ",
13765
+ /* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
13766
+ ] }),
13767
+ /* @__PURE__ */ jsx(
13768
+ "input",
13769
+ {
13770
+ className: `bcf-input${errors.username ? " bcf-input--error" : ""}`,
13771
+ type: "text",
13772
+ value: fields.username,
13773
+ autoComplete: "username",
13774
+ onChange: (e) => set("username", e.target.value)
13775
+ }
13776
+ ),
13777
+ errors.username && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.username })
13778
+ ] }),
13779
+ /* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
13780
+ /* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
13781
+ "Password ",
13782
+ /* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
13783
+ ] }),
13784
+ /* @__PURE__ */ jsx(
13785
+ "input",
13786
+ {
13787
+ className: `bcf-input${errors.password ? " bcf-input--error" : ""}`,
13788
+ type: "password",
13789
+ value: fields.password,
13790
+ autoComplete: "current-password",
13791
+ onChange: (e) => set("password", e.target.value)
13792
+ }
13793
+ ),
13794
+ errors.password && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.password })
13795
+ ] }),
13796
+ /* @__PURE__ */ jsx("div", { className: "bcf-section-label", style: { marginTop: 16 }, children: "Gateway / Point-of-Presence" }),
13797
+ /* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
13798
+ /* @__PURE__ */ jsx("label", { className: "bcf-label", children: "Gateway" }),
13799
+ /* @__PURE__ */ jsx(
13800
+ "select",
13801
+ {
13802
+ className: "bcf-input bcf-select",
13803
+ value: fields.gatewayId,
13804
+ onChange: (e) => set("gatewayId", e.target.value),
13805
+ children: ["Test / UAT", "Paper Trading", "Production"].map((group) => /* @__PURE__ */ jsx("optgroup", { label: group, children: RITHMIC_GATEWAY_OPTIONS.filter((g) => g.group === group).map((g) => /* @__PURE__ */ jsxs("option", { value: g.id, children: [
13806
+ g.name,
13807
+ " \u2014 ",
13808
+ g.region
13809
+ ] }, g.id)) }, group))
13810
+ }
13811
+ )
13812
+ ] }),
13813
+ apiError && /* @__PURE__ */ jsx("div", { className: "bcf-api-error", children: apiError }),
13814
+ /* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx("button", { type: "submit", className: "bcf-btn-save", disabled: loading, children: loading ? "Connecting\u2026" : "Connect & Fetch Accounts" }) })
13815
+ ] }),
13816
+ step === "accounts" && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
13817
+ /* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "Select your trading account" }),
13818
+ accounts.length === 0 ? /* @__PURE__ */ jsx("p", { className: "bcf-empty", children: "No accounts found for these credentials." }) : /* @__PURE__ */ jsx("div", { className: "bcf-account-list", children: accounts.map((acct) => /* @__PURE__ */ jsxs("div", { className: "bcf-account-row", children: [
13819
+ /* @__PURE__ */ jsxs("div", { className: "bcf-account-info", children: [
13820
+ /* @__PURE__ */ jsx("span", { className: "bcf-account-name", children: acct.name }),
13821
+ acct.type && /* @__PURE__ */ jsx("span", { className: "bcf-account-type", children: acct.type })
13822
+ ] }),
13823
+ /* @__PURE__ */ jsx(
13824
+ "button",
13825
+ {
13826
+ className: "bcf-btn-select",
13827
+ disabled: loading,
13828
+ onClick: () => handleSelectAccount(acct),
13829
+ children: loading ? "\u2026" : "Select"
13830
+ }
13831
+ )
13832
+ ] }, acct.id)) }),
13833
+ apiError && /* @__PURE__ */ jsx("div", { className: "bcf-api-error", children: apiError }),
13834
+ /* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx(
13835
+ "button",
13836
+ {
13837
+ type: "button",
13838
+ className: "bcf-btn-back",
13839
+ onClick: () => {
13840
+ setStep("credentials");
13841
+ setApiError("");
13842
+ },
13843
+ children: "\u2190 Back"
13844
+ }
13845
+ ) })
13846
+ ] }),
13847
+ step === "done" && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
13848
+ /* @__PURE__ */ jsxs("div", { className: "bcf-success", children: [
13849
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", width: "28", height: "28", fill: "none", stroke: "#22c55e", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
13850
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "8" }),
13851
+ /* @__PURE__ */ jsx("polyline", { points: "6,10 9,13 14,7" })
13852
+ ] }),
13853
+ /* @__PURE__ */ jsx("span", { children: "Rithmic account connected successfully." })
13854
+ ] }),
13855
+ /* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "bcf-btn-save", onClick: onClose, children: "Done" }) })
13856
+ ] })
13857
+ ] })
13858
+ ] });
13859
+ }
13860
+ function BrokerCard({ broker, savedConnection, justConnected, onConnect, onStartTrading }) {
13861
+ const isConnected = !!(savedConnection || justConnected);
13862
+ return /* @__PURE__ */ jsxs("div", { className: `broker-tile${broker.status === "available" ? " broker-tile--available" : ""}${isConnected ? " broker-tile--connected" : ""}`, children: [
13863
+ /* @__PURE__ */ jsx("div", { className: "broker-tile-name", children: broker.name }),
13864
+ /* @__PURE__ */ jsx("div", { className: "broker-tile-desc", children: broker.description }),
13865
+ savedConnection && /* @__PURE__ */ jsxs("div", { className: "broker-tile-saved", children: [
13866
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "11", height: "11", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", style: { flexShrink: 0, opacity: 0.6 }, children: [
13867
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "5", r: "3" }),
13868
+ /* @__PURE__ */ jsx("path", { d: "M1 13c0-3.31 2.69-6 6-6s6 2.69 6 6" })
13869
+ ] }),
13870
+ /* @__PURE__ */ jsx("span", { className: "broker-tile-saved-account", children: savedConnection.accountName || savedConnection.accountId }),
13871
+ /* @__PURE__ */ jsx("span", { className: "broker-tile-saved-sep", children: "\xB7" }),
13872
+ /* @__PURE__ */ jsx("span", { className: "broker-tile-saved-user", children: savedConnection.username })
13873
+ ] }),
13874
+ /* @__PURE__ */ jsx("span", { className: `broker-card-status broker-card-status--${isConnected ? "connected" : broker.status}`, children: isConnected ? "Connected" : broker.status === "available" ? "Available" : "Coming Soon" }),
13875
+ isConnected ? /* @__PURE__ */ jsx(
13876
+ "button",
13877
+ {
13878
+ className: "broker-tile-connect broker-tile-connect--connected",
13879
+ onClick: savedConnection ? () => onStartTrading(savedConnection.id) : void 0,
13880
+ style: { pointerEvents: savedConnection ? "auto" : "none", cursor: savedConnection ? "pointer" : "default" },
13881
+ title: savedConnection ? `Start trading with ${broker.name}` : `${broker.name} connected`,
13882
+ children: "Start Trading"
13883
+ }
13884
+ ) : /* @__PURE__ */ jsx(
13885
+ "button",
13886
+ {
13887
+ className: "broker-tile-connect",
13888
+ disabled: broker.status !== "available",
13889
+ onClick: broker.status === "available" ? onConnect : void 0,
13890
+ title: broker.status === "available" ? `Connect ${broker.name}` : "Coming soon",
13891
+ children: broker.status === "available" ? "Connect" : "Coming Soon"
13892
+ }
13893
+ )
13894
+ ] });
13895
+ }
13896
+ function TradeDrawer({ onClose, savedConnections = [], onBrokerConnected, getAuthToken }) {
13897
+ const [connectingBroker, setConnectingBroker] = useState(null);
13898
+ const [connectedBrokers, setConnectedBrokers] = useState(/* @__PURE__ */ new Set());
13899
+ function markConnected(brokerId, connectionId) {
13900
+ setConnectedBrokers((prev) => new Set(prev).add(brokerId));
13901
+ onBrokerConnected?.(brokerId, connectionId);
13902
+ }
13903
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
13904
+ /* @__PURE__ */ jsx("div", { className: "trade-drawer-backdrop", onClick: onClose }),
13905
+ /* @__PURE__ */ jsxs("div", { className: "trade-drawer", children: [
13906
+ /* @__PURE__ */ jsxs("div", { className: "trade-drawer-header", children: [
13907
+ /* @__PURE__ */ jsxs("span", { className: "trade-drawer-title", children: [
13908
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
13909
+ /* @__PURE__ */ jsx("rect", { x: "1.5", y: "3", width: "13", height: "10", rx: "1.5" }),
13910
+ /* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
13911
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
13912
+ ] }),
13913
+ "Trade \u2014 Connect a broker"
13914
+ ] }),
13915
+ /* @__PURE__ */ jsx("button", { className: "trade-drawer-close", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
13916
+ ] }),
13917
+ /* @__PURE__ */ jsx("div", { className: "trade-drawer-body", children: /* @__PURE__ */ jsx("div", { className: "broker-grid", children: BROKERS.map((broker) => {
13918
+ const saved = savedConnections.find((c) => c.broker === broker.id);
13919
+ return /* @__PURE__ */ jsx(
13920
+ BrokerCard,
13921
+ {
13922
+ broker,
13923
+ savedConnection: saved,
13924
+ justConnected: connectedBrokers.has(broker.id),
13925
+ onConnect: () => setConnectingBroker(broker.id),
13926
+ onStartTrading: (connId) => {
13927
+ onBrokerConnected?.(broker.id, connId);
13928
+ onClose();
13929
+ }
13930
+ },
13931
+ broker.id
13932
+ );
13933
+ }) }) })
13934
+ ] }),
13935
+ connectingBroker === "rithmic" && /* @__PURE__ */ jsx(
13936
+ RithmicConnectDrawer,
13937
+ {
13938
+ onClose: () => setConnectingBroker(null),
13939
+ onConnected: (connId) => markConnected("rithmic", connId),
13940
+ ...getAuthToken ? { getAuthToken } : {}
13941
+ }
13942
+ )
13943
+ ] });
13944
+ }
13945
+ var ORDER_TYPES = [
13946
+ { value: "market", label: "Market" },
13947
+ { value: "limit", label: "Limit" },
13948
+ { value: "stop_market", label: "Stop Market" },
13949
+ { value: "stop_limit", label: "Stop Limit" },
13950
+ { value: "trailing_stop", label: "Trailing Stop" }
13951
+ ];
13952
+ var ORDER_TYPE_HINTS = {
13953
+ market: "Executes immediately at best available price",
13954
+ limit: "Executes at your price or better",
13955
+ stop_market: "Market order triggered when stop price is hit",
13956
+ stop_limit: "Limit order triggered when stop price is hit",
13957
+ trailing_stop: "Stop that trails price by the specified number of ticks"
13958
+ };
13959
+ var TIF_OPTIONS = [
13960
+ { value: "DAY", label: "DAY \u2014 Good for Day" },
13961
+ { value: "GTC", label: "GTC \u2014 Good till Cancel" },
13962
+ { value: "IOC", label: "IOC \u2014 Immediate or Cancel" },
13963
+ { value: "FOK", label: "FOK \u2014 Fill or Kill" }
13964
+ ];
13965
+ var TICK_SIZE_FALLBACK = {
13966
+ ES: 0.25,
13967
+ MES: 0.25,
13968
+ NQ: 0.25,
13969
+ MNQ: 0.25,
13970
+ YM: 1,
13971
+ MYM: 1,
13972
+ RTY: 0.1,
13973
+ M2K: 0.1,
13974
+ CL: 0.01,
13975
+ MCL: 0.01,
13976
+ GC: 0.1,
13977
+ MGC: 0.1,
13978
+ SI: 5e-3,
13979
+ ZB: 0.03125,
13980
+ ZN: 0.015625,
13981
+ ZF: 78125e-7,
13982
+ ZT: 390625e-8,
13983
+ ZC: 0.25,
13984
+ ZS: 0.25,
13985
+ ZW: 0.25,
13986
+ NG: 1e-3,
13987
+ HE: 25e-5,
13988
+ LE: 25e-5
13989
+ };
13990
+ function getTickSize(sym) {
13991
+ const base = sym.split(".")[0] ?? sym;
13992
+ const root = base.replace(/[FGHJKMNQUVXZ]\d{1,2}$/, "").toUpperCase();
13993
+ return TICK_SIZE_FALLBACK[root] ?? 0.25;
13994
+ }
13995
+ function stripPrefix(sym) {
13996
+ const idx = sym.indexOf(":");
13997
+ return idx >= 0 ? sym.slice(idx + 1) : sym;
13998
+ }
13999
+ function isFuturesSymbol(sym) {
14000
+ return sym.includes(".");
14001
+ }
14002
+ function OrderEntryPanel({
14003
+ sessionId,
14004
+ brokerSlug,
14005
+ accounts,
14006
+ selectedAccountId,
14007
+ defaultSymbol = "",
14008
+ apiUrl = "",
14009
+ getAuthToken,
14010
+ connecting = false,
14011
+ connectionError,
14012
+ onRetryConnect,
14013
+ onChangeAccount,
14014
+ onClose
14015
+ }) {
14016
+ const [side, setSide] = useState("buy");
14017
+ const [orderType, setOrderType] = useState("market");
14018
+ const [tif, setTif] = useState("DAY");
14019
+ const [symbol, setSymbol] = useState(stripPrefix(defaultSymbol));
14020
+ const [qty, setQty] = useState("1");
14021
+ const [limitPrice, setLimitPrice] = useState("");
14022
+ const [stopPrice, setStopPrice] = useState("");
14023
+ const [trailTicks, setTrailTicks] = useState("10");
14024
+ const [bracketOn, setBracketOn] = useState(false);
14025
+ const [tpTicks, setTpTicks] = useState("20");
14026
+ const [slTicks, setSlTicks] = useState("10");
14027
+ const [submitState, setSubmitState] = useState("idle");
14028
+ const [errorMsg, setErrorMsg] = useState("");
14029
+ const [drawerOpen, setDrawerOpen] = useState(false);
14030
+ const [draftTp, setDraftTp] = useState("20");
14031
+ const [draftSl, setDraftSl] = useState("10");
14032
+ const prevDefaultRef = useRef(defaultSymbol);
14033
+ useEffect(() => {
14034
+ if (defaultSymbol !== prevDefaultRef.current) {
14035
+ prevDefaultRef.current = defaultSymbol;
14036
+ setSymbol(stripPrefix(defaultSymbol));
14037
+ }
14038
+ }, [defaultSymbol]);
14039
+ const futures = isFuturesSymbol(symbol) || isFuturesSymbol(defaultSymbol);
14040
+ const showLimit = orderType === "limit" || orderType === "stop_limit";
14041
+ const showStop = orderType === "stop_market" || orderType === "stop_limit";
14042
+ const showTrailing = orderType === "trailing_stop";
14043
+ const showBracket = orderType === "market" || orderType === "limit";
14044
+ const tickSize = getTickSize(symbol || defaultSymbol);
14045
+ const tpPoints = (parseInt(draftTp, 10) || 0) * tickSize;
14046
+ const slPoints = (parseInt(draftSl, 10) || 0) * tickSize;
14047
+ const handleToggleBracket = useCallback(() => {
14048
+ if (bracketOn) {
14049
+ setBracketOn(false);
14050
+ } else {
14051
+ setDraftTp(tpTicks);
14052
+ setDraftSl(slTicks);
14053
+ setDrawerOpen(true);
14054
+ }
14055
+ }, [bracketOn, tpTicks, slTicks]);
14056
+ const handleOpenBracketSettings = useCallback(() => {
14057
+ setDraftTp(tpTicks);
14058
+ setDraftSl(slTicks);
14059
+ setDrawerOpen(true);
14060
+ }, [tpTicks, slTicks]);
14061
+ const handleDrawerSave = useCallback(() => {
14062
+ setTpTicks(draftTp);
14063
+ setSlTicks(draftSl);
14064
+ setBracketOn(true);
14065
+ setDrawerOpen(false);
14066
+ }, [draftTp, draftSl]);
14067
+ const handleDrawerCancel = useCallback(() => {
14068
+ setDrawerOpen(false);
14069
+ }, []);
14070
+ const handleSubmit = useCallback(async (e) => {
14071
+ e.preventDefault();
14072
+ setErrorMsg("");
14073
+ const qtyNum = futures ? parseInt(qty, 10) : parseFloat(qty);
14074
+ if (!symbol.trim()) {
14075
+ setErrorMsg("Symbol is required");
14076
+ return;
14077
+ }
14078
+ if (!qtyNum || qtyNum <= 0) {
14079
+ setErrorMsg("Quantity must be positive");
14080
+ return;
14081
+ }
14082
+ if (showLimit && !limitPrice) {
14083
+ setErrorMsg("Limit price is required");
14084
+ return;
14085
+ }
14086
+ if (showStop && !stopPrice) {
14087
+ setErrorMsg("Stop price is required");
14088
+ return;
14089
+ }
14090
+ if (showTrailing && parseInt(trailTicks, 10) < 1) {
14091
+ setErrorMsg("Trail ticks must be at least 1");
14092
+ return;
14093
+ }
14094
+ if (bracketOn && showBracket) {
14095
+ if (parseInt(tpTicks, 10) < 1) {
14096
+ setErrorMsg("Take-profit ticks are required");
14097
+ return;
14098
+ }
14099
+ if (parseInt(slTicks, 10) < 1) {
14100
+ setErrorMsg("Stop-loss ticks are required");
14101
+ return;
14102
+ }
14103
+ }
14104
+ setSubmitState("submitting");
14105
+ try {
14106
+ const headers = { "Content-Type": "application/json" };
14107
+ if (getAuthToken) {
14108
+ const token = await getAuthToken();
14109
+ headers["Authorization"] = `Bearer ${token}`;
14110
+ }
14111
+ const clientRef = crypto.randomUUID();
14112
+ if (bracketOn && showBracket) {
14113
+ const body = {
14114
+ sessionId,
14115
+ clientRef,
14116
+ symbol,
14117
+ accountId: selectedAccountId,
14118
+ side,
14119
+ type: orderType,
14120
+ qty: qtyNum,
14121
+ timeInForce: tif,
14122
+ ...showLimit ? { limitPrice: parseFloat(limitPrice) } : {},
14123
+ takeProfitTicks: parseInt(tpTicks, 10),
14124
+ stopLossTicks: parseInt(slTicks, 10)
14125
+ };
14126
+ const resp = await fetch(`${apiUrl}/api/broker/user-bracket`, {
14127
+ method: "POST",
14128
+ headers,
14129
+ body: JSON.stringify(body)
14130
+ });
14131
+ if (!resp.ok) {
14132
+ const err = await resp.json().catch(() => ({}));
14133
+ throw new Error(err.error ?? `Error ${resp.status}`);
14134
+ }
14135
+ } else {
14136
+ const body = {
14137
+ sessionId,
14138
+ clientRef,
14139
+ symbol,
14140
+ accountId: selectedAccountId,
14141
+ side,
14142
+ type: orderType,
14143
+ qty: qtyNum,
14144
+ timeInForce: tif,
14145
+ ...showLimit ? { limitPrice: parseFloat(limitPrice) } : {},
14146
+ ...showStop ? { stopPrice: parseFloat(stopPrice) } : {},
14147
+ ...showTrailing ? { trailTicks: parseInt(trailTicks, 10) } : {}
14148
+ };
14149
+ const resp = await fetch(`${apiUrl}/api/broker/user-order`, {
14150
+ method: "POST",
14151
+ headers,
14152
+ body: JSON.stringify(body)
14153
+ });
14154
+ if (!resp.ok) {
14155
+ const err = await resp.json().catch(() => ({}));
14156
+ throw new Error(err.error ?? `Error ${resp.status}`);
14157
+ }
14158
+ }
14159
+ setSubmitState("success");
14160
+ setTimeout(() => {
14161
+ setSubmitState("idle");
14162
+ setQty("1");
14163
+ setLimitPrice("");
14164
+ setStopPrice("");
14165
+ setTrailTicks("10");
14166
+ }, 1400);
14167
+ } catch (err) {
14168
+ setSubmitState("error");
14169
+ setErrorMsg(err instanceof Error ? err.message : "Order failed");
14170
+ }
14171
+ }, [
14172
+ symbol,
14173
+ side,
14174
+ orderType,
14175
+ tif,
14176
+ qty,
14177
+ limitPrice,
14178
+ stopPrice,
14179
+ trailTicks,
14180
+ bracketOn,
14181
+ tpTicks,
14182
+ slTicks,
14183
+ showLimit,
14184
+ showStop,
14185
+ showTrailing,
14186
+ showBracket,
14187
+ futures,
14188
+ sessionId,
14189
+ selectedAccountId,
14190
+ apiUrl,
14191
+ getAuthToken
14192
+ ]);
14193
+ return /* @__PURE__ */ jsxs("div", { className: "oep-panel", children: [
14194
+ /* @__PURE__ */ jsxs("div", { className: "oep-header", children: [
14195
+ /* @__PURE__ */ jsxs("span", { className: "oep-title", children: [
14196
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", style: { marginRight: 6 }, children: [
13484
14197
  /* @__PURE__ */ jsx("rect", { x: "1.5", y: "3", width: "13", height: "10", rx: "1.5" }),
13485
14198
  /* @__PURE__ */ jsx("line", { x1: "5", y1: "8", x2: "11", y2: "8" }),
13486
14199
  /* @__PURE__ */ jsx("line", { x1: "8", y1: "5", x2: "8", y2: "11" })
13487
14200
  ] }),
13488
- "Trade \u2014 Connect a broker"
14201
+ drawerOpen ? "Bracket Order Settings" : "Order Entry"
13489
14202
  ] }),
13490
- /* @__PURE__ */ jsx("button", { className: "trade-drawer-close", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "12", height: "12", stroke: "currentColor", strokeWidth: "1.8", children: [
13491
- /* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
13492
- /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
13493
- ] }) })
14203
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
14204
+ !drawerOpen && /* @__PURE__ */ jsx("span", { className: "oep-broker-badge", children: brokerSlug || "\u2014" }),
14205
+ /* @__PURE__ */ jsx(
14206
+ "button",
14207
+ {
14208
+ className: "oep-close",
14209
+ onClick: drawerOpen ? handleDrawerCancel : onClose,
14210
+ title: drawerOpen ? "Back" : "Close order panel",
14211
+ children: drawerOpen ? /* @__PURE__ */ jsx("svg", { viewBox: "0 0 14 14", width: "11", height: "11", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: /* @__PURE__ */ jsx("polyline", { points: "9,2 4,7 9,12" }) }) : /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "11", height: "11", stroke: "currentColor", strokeWidth: "1.8", fill: "none", children: [
14212
+ /* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12" }),
14213
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "2", y2: "12" })
14214
+ ] })
14215
+ }
14216
+ )
14217
+ ] })
13494
14218
  ] }),
13495
- /* @__PURE__ */ jsx("div", { className: "trade-drawer-body", children: BROKERS.map((broker) => /* @__PURE__ */ jsxs("div", { className: "broker-card", children: [
13496
- /* @__PURE__ */ jsx("div", { className: "broker-card-name", children: broker.name }),
13497
- /* @__PURE__ */ jsx("div", { className: "broker-card-desc", children: broker.description }),
13498
- /* @__PURE__ */ jsx("span", { className: `broker-card-status broker-card-status--${broker.status}`, children: broker.status === "available" ? "Available" : "Coming Soon" }),
13499
- /* @__PURE__ */ jsx(
14219
+ connecting && /* @__PURE__ */ jsxs("div", { className: "oep-body oep-body--status", children: [
14220
+ /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", style: { animation: "spin 0.9s linear infinite", flexShrink: 0 }, children: [
14221
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "var(--border)", strokeWidth: "2.5" }),
14222
+ /* @__PURE__ */ jsx("path", { d: "M12 3 A9 9 0 0 1 21 12", fill: "none", stroke: "var(--primary)", strokeWidth: "2.5", strokeLinecap: "round" })
14223
+ ] }),
14224
+ /* @__PURE__ */ jsx("span", { style: { color: "var(--text-muted)", fontSize: 13 }, children: "Connecting to broker\u2026" })
14225
+ ] }),
14226
+ !connecting && connectionError && /* @__PURE__ */ jsxs("div", { className: "oep-body oep-body--status", children: [
14227
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", width: "22", height: "22", fill: "none", stroke: "#ef4444", strokeWidth: "1.8", strokeLinecap: "round", children: [
14228
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "8" }),
14229
+ /* @__PURE__ */ jsx("line", { x1: "10", y1: "6", x2: "10", y2: "11" }),
14230
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "14", r: "0.8", fill: "#ef4444" })
14231
+ ] }),
14232
+ /* @__PURE__ */ jsx("span", { style: { color: "var(--text-muted)", fontSize: 13, textAlign: "center" }, children: connectionError }),
14233
+ onRetryConnect && /* @__PURE__ */ jsx("button", { className: "oep-retry-btn", onClick: onRetryConnect, children: "Retry Connection" })
14234
+ ] }),
14235
+ !connecting && !connectionError && drawerOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
14236
+ /* @__PURE__ */ jsxs("div", { className: "oep-drawer-body", children: [
14237
+ /* @__PURE__ */ jsx("p", { className: "oep-drawer-desc", children: "Set your TP and SL as tick offsets from entry price. The bracket order will automatically place both orders when your entry fills." }),
14238
+ /* @__PURE__ */ jsxs("p", { className: "oep-drawer-ticksize", children: [
14239
+ "Tick size for ",
14240
+ symbol || "\u2014",
14241
+ ": ",
14242
+ tickSize.toString()
14243
+ ] }),
14244
+ /* @__PURE__ */ jsxs("div", { className: "oep-drawer-field", children: [
14245
+ /* @__PURE__ */ jsx("label", { className: "oep-drawer-label oep-drawer-label--tp", children: "Take Profit (ticks)" }),
14246
+ /* @__PURE__ */ jsx(
14247
+ "input",
14248
+ {
14249
+ className: "oep-input",
14250
+ type: "number",
14251
+ min: 1,
14252
+ step: 1,
14253
+ value: draftTp,
14254
+ onChange: (e) => setDraftTp(e.target.value)
14255
+ }
14256
+ ),
14257
+ /* @__PURE__ */ jsx(
14258
+ "input",
14259
+ {
14260
+ className: "oep-slider oep-slider--tp",
14261
+ type: "range",
14262
+ min: 1,
14263
+ max: 100,
14264
+ value: parseInt(draftTp, 10) || 1,
14265
+ onChange: (e) => setDraftTp(e.target.value)
14266
+ }
14267
+ ),
14268
+ /* @__PURE__ */ jsxs("span", { className: "oep-tick-convert", children: [
14269
+ parseInt(draftTp, 10) || 0,
14270
+ " ticks = ",
14271
+ tpPoints.toFixed(tickSize < 0.01 ? 6 : tickSize < 0.1 ? 4 : 2),
14272
+ " points"
14273
+ ] })
14274
+ ] }),
14275
+ /* @__PURE__ */ jsxs("div", { className: "oep-drawer-field", children: [
14276
+ /* @__PURE__ */ jsx("label", { className: "oep-drawer-label oep-drawer-label--sl", children: "Stop Loss (ticks)" }),
14277
+ /* @__PURE__ */ jsx(
14278
+ "input",
14279
+ {
14280
+ className: "oep-input",
14281
+ type: "number",
14282
+ min: 1,
14283
+ step: 1,
14284
+ value: draftSl,
14285
+ onChange: (e) => setDraftSl(e.target.value)
14286
+ }
14287
+ ),
14288
+ /* @__PURE__ */ jsx(
14289
+ "input",
14290
+ {
14291
+ className: "oep-slider oep-slider--sl",
14292
+ type: "range",
14293
+ min: 1,
14294
+ max: 100,
14295
+ value: parseInt(draftSl, 10) || 1,
14296
+ onChange: (e) => setDraftSl(e.target.value)
14297
+ }
14298
+ ),
14299
+ /* @__PURE__ */ jsxs("span", { className: "oep-tick-convert", children: [
14300
+ parseInt(draftSl, 10) || 0,
14301
+ " ticks = ",
14302
+ slPoints.toFixed(tickSize < 0.01 ? 6 : tickSize < 0.1 ? 4 : 2),
14303
+ " points"
14304
+ ] })
14305
+ ] })
14306
+ ] }),
14307
+ /* @__PURE__ */ jsxs("div", { className: "oep-drawer-footer", children: [
14308
+ /* @__PURE__ */ jsx("button", { className: "oep-drawer-btn oep-drawer-btn--cancel", onClick: handleDrawerCancel, children: "Cancel" }),
14309
+ /* @__PURE__ */ jsx(
14310
+ "button",
14311
+ {
14312
+ className: "oep-drawer-btn oep-drawer-btn--save",
14313
+ onClick: handleDrawerSave,
14314
+ disabled: !draftTp || parseInt(draftTp, 10) < 1 || !draftSl || parseInt(draftSl, 10) < 1,
14315
+ children: "Save"
14316
+ }
14317
+ )
14318
+ ] })
14319
+ ] }),
14320
+ !connecting && !connectionError && !drawerOpen && /* @__PURE__ */ jsx("div", { className: "oep-body", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, noValidate: true, children: [
14321
+ accounts.length === 1 && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14322
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "Account" }),
14323
+ /* @__PURE__ */ jsx("div", { className: "oep-account-chip", children: accounts[0].name })
14324
+ ] }),
14325
+ accounts.length > 1 && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14326
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "Account" }),
14327
+ /* @__PURE__ */ jsx(
14328
+ "select",
14329
+ {
14330
+ className: "oep-select",
14331
+ value: selectedAccountId,
14332
+ onChange: (e) => onChangeAccount(e.target.value),
14333
+ children: accounts.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
14334
+ }
14335
+ )
14336
+ ] }),
14337
+ /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14338
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: futures ? "Contract" : "Symbol" }),
14339
+ /* @__PURE__ */ jsx(
14340
+ "input",
14341
+ {
14342
+ className: "oep-input",
14343
+ type: "text",
14344
+ value: symbol,
14345
+ onChange: (e) => setSymbol(e.target.value.toUpperCase()),
14346
+ placeholder: futures ? "e.g. ESH6" : "e.g. BTCUSDT",
14347
+ autoCapitalize: "characters"
14348
+ }
14349
+ ),
14350
+ futures && /* @__PURE__ */ jsx("span", { className: "oep-hint", children: "Include month + year code (e.g. H6 = Mar 2026)" })
14351
+ ] }),
14352
+ /* @__PURE__ */ jsx("div", { className: "oep-row", style: { marginTop: 6 }, children: /* @__PURE__ */ jsxs("div", { className: "oep-side-toggle", children: [
14353
+ /* @__PURE__ */ jsx(
14354
+ "button",
14355
+ {
14356
+ type: "button",
14357
+ className: `oep-side-btn oep-side-btn--buy${side === "buy" ? " oep-side-btn--active" : ""}`,
14358
+ onClick: () => setSide("buy"),
14359
+ children: "Buy"
14360
+ }
14361
+ ),
14362
+ /* @__PURE__ */ jsx(
14363
+ "button",
14364
+ {
14365
+ type: "button",
14366
+ className: `oep-side-btn oep-side-btn--sell${side === "sell" ? " oep-side-btn--active" : ""}`,
14367
+ onClick: () => setSide("sell"),
14368
+ children: "Sell"
14369
+ }
14370
+ )
14371
+ ] }) }),
14372
+ /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14373
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "TYPE" }),
14374
+ /* @__PURE__ */ jsx(
14375
+ "select",
14376
+ {
14377
+ className: "oep-select",
14378
+ value: orderType,
14379
+ onChange: (e) => setOrderType(e.target.value),
14380
+ children: ORDER_TYPES.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
14381
+ }
14382
+ ),
14383
+ ORDER_TYPE_HINTS[orderType] && /* @__PURE__ */ jsx("span", { className: "oep-hint", children: ORDER_TYPE_HINTS[orderType] })
14384
+ ] }),
14385
+ /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14386
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "QUANTITY" }),
14387
+ /* @__PURE__ */ jsx(
14388
+ "input",
14389
+ {
14390
+ className: "oep-input",
14391
+ type: "number",
14392
+ min: 1,
14393
+ step: futures ? 1 : "any",
14394
+ value: qty,
14395
+ onChange: (e) => setQty(e.target.value)
14396
+ }
14397
+ )
14398
+ ] }),
14399
+ showLimit && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14400
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "Limit Price" }),
14401
+ /* @__PURE__ */ jsx(
14402
+ "input",
14403
+ {
14404
+ className: "oep-input",
14405
+ type: "number",
14406
+ step: "any",
14407
+ value: limitPrice,
14408
+ onChange: (e) => setLimitPrice(e.target.value),
14409
+ placeholder: "0.00"
14410
+ }
14411
+ )
14412
+ ] }),
14413
+ showStop && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14414
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "Stop Price" }),
14415
+ /* @__PURE__ */ jsx(
14416
+ "input",
14417
+ {
14418
+ className: "oep-input",
14419
+ type: "number",
14420
+ step: "any",
14421
+ value: stopPrice,
14422
+ onChange: (e) => setStopPrice(e.target.value),
14423
+ placeholder: "0.00"
14424
+ }
14425
+ )
14426
+ ] }),
14427
+ showTrailing && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14428
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "Trail Distance" }),
14429
+ /* @__PURE__ */ jsxs("div", { className: "oep-input-tick-wrap", children: [
14430
+ /* @__PURE__ */ jsx(
14431
+ "input",
14432
+ {
14433
+ className: "oep-input",
14434
+ type: "number",
14435
+ min: 1,
14436
+ step: 1,
14437
+ value: trailTicks,
14438
+ onChange: (e) => setTrailTicks(e.target.value),
14439
+ placeholder: "10"
14440
+ }
14441
+ ),
14442
+ /* @__PURE__ */ jsx("span", { className: "oep-tick-unit", children: "ticks" })
14443
+ ] }),
14444
+ /* @__PURE__ */ jsx(
14445
+ "input",
14446
+ {
14447
+ className: "oep-slider oep-slider--trail",
14448
+ type: "range",
14449
+ min: 1,
14450
+ max: 100,
14451
+ value: parseInt(trailTicks, 10) || 1,
14452
+ onChange: (e) => setTrailTicks(e.target.value)
14453
+ }
14454
+ )
14455
+ ] }),
14456
+ orderType !== "trailing_stop" && /* @__PURE__ */ jsxs("div", { className: "oep-row", children: [
14457
+ /* @__PURE__ */ jsx("label", { className: "oep-label", children: "TIF" }),
14458
+ /* @__PURE__ */ jsx(
14459
+ "select",
14460
+ {
14461
+ className: "oep-select",
14462
+ value: tif,
14463
+ onChange: (e) => setTif(e.target.value),
14464
+ children: TIF_OPTIONS.map((t) => /* @__PURE__ */ jsx("option", { value: t.value, children: t.label }, t.value))
14465
+ }
14466
+ )
14467
+ ] }),
14468
+ showBracket && /* @__PURE__ */ jsxs("div", { className: "oep-bracket-row", children: [
14469
+ /* @__PURE__ */ jsx(
14470
+ "button",
14471
+ {
14472
+ type: "button",
14473
+ role: "switch",
14474
+ "aria-checked": bracketOn,
14475
+ className: `oep-toggle${bracketOn ? " oep-toggle--on" : ""}`,
14476
+ onClick: handleToggleBracket,
14477
+ children: /* @__PURE__ */ jsx("span", { className: "oep-toggle-thumb" })
14478
+ }
14479
+ ),
14480
+ /* @__PURE__ */ jsx("span", { className: "oep-bracket-label", children: "Bracket (OCO)" }),
14481
+ bracketOn && /* @__PURE__ */ jsx(
14482
+ "button",
14483
+ {
14484
+ type: "button",
14485
+ className: "oep-gear-btn",
14486
+ title: "Edit bracket settings",
14487
+ onClick: handleOpenBracketSettings,
14488
+ children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
14489
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "2.5" }),
14490
+ /* @__PURE__ */ jsx("path", { d: "M8 1.5v1M8 13.5v1M1.5 8h1M13.5 8h1M3.4 3.4l.7.7M11.9 11.9l.7.7M3.4 12.6l.7-.7M11.9 4.1l.7-.7", strokeLinecap: "round" })
14491
+ ] })
14492
+ }
14493
+ )
14494
+ ] }),
14495
+ errorMsg && submitState === "error" && /* @__PURE__ */ jsx("div", { className: "oep-error", children: errorMsg }),
14496
+ submitState === "success" && /* @__PURE__ */ jsxs("div", { className: "oep-success", children: [
14497
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsx("polyline", { points: "3,8 6,11 13,4" }) }),
14498
+ "Order submitted"
14499
+ ] }),
14500
+ /* @__PURE__ */ jsx("div", { className: "oep-actions", children: /* @__PURE__ */ jsx(
13500
14501
  "button",
13501
14502
  {
13502
- className: "broker-card-connect",
13503
- disabled: broker.status !== "available",
13504
- title: broker.status === "available" ? `Connect ${broker.name}` : "Coming soon",
13505
- children: broker.status === "available" ? "Connect" : "Coming Soon"
14503
+ type: "submit",
14504
+ disabled: submitState === "submitting",
14505
+ className: `oep-submit oep-submit--${side}`,
14506
+ children: submitState === "submitting" ? "Placing\u2026" : `${side === "buy" ? "Buy" : "Sell"} ${symbol || "\u2014"}`
13506
14507
  }
13507
- )
13508
- ] }, broker.id)) })
14508
+ ) })
14509
+ ] }) })
13509
14510
  ] });
13510
14511
  }
13511
14512
  function uid() {
@@ -13572,6 +14573,8 @@ var ManagedAppShell = forwardRef(
13572
14573
  getAuthToken,
13573
14574
  apiUrl,
13574
14575
  tradingBridge,
14576
+ onLogout,
14577
+ user,
13575
14578
  renderScriptDrawer,
13576
14579
  renderWatchlistDrawer,
13577
14580
  renderTradeDrawer
@@ -13591,9 +14594,82 @@ var ManagedAppShell = forwardRef(
13591
14594
  const [scriptDrawerOpen, setScriptDrawerOpen] = useState(false);
13592
14595
  const [watchlistOpen, setWatchlistOpen] = useState(false);
13593
14596
  const [tradeDrawerOpen, setTradeDrawerOpen] = useState(false);
14597
+ const [orderPanelOpen, setOrderPanelOpen] = useState(false);
14598
+ const [orderPanelConnecting, setOrderPanelConnecting] = useState(false);
14599
+ const [orderPanelError, setOrderPanelError] = useState("");
14600
+ const [connectedSession, setConnectedSession] = useState(null);
14601
+ const [selectedAccountId, setSelectedAccountId] = useState("");
14602
+ const [savedConnections, setSavedConnections] = useState([]);
13594
14603
  const [chartDirty, setChartDirty] = useState(0);
13595
14604
  const [isFullscreen, setIsFullscreen] = useState(false);
13596
14605
  const capabilities = useChartCapabilities(config);
14606
+ const handleBrokerConnected = useCallback(async (brokerId, connectionId) => {
14607
+ setTradeDrawerOpen(false);
14608
+ setConnectedSession({ brokerId, connectionId, sessionId: "", brokerSlug: brokerId, accounts: [] });
14609
+ setOrderPanelConnecting(true);
14610
+ setOrderPanelError("");
14611
+ setOrderPanelOpen(true);
14612
+ try {
14613
+ const headers = { "Content-Type": "application/json" };
14614
+ if (getAuthToken) {
14615
+ const tok = await getAuthToken();
14616
+ if (tok) headers["Authorization"] = `Bearer ${tok}`;
14617
+ }
14618
+ const resp = await fetch(`${apiUrl ?? ""}/api/broker/user-sessions`, {
14619
+ method: "POST",
14620
+ headers,
14621
+ body: JSON.stringify({ connectionId })
14622
+ });
14623
+ if (resp.ok) {
14624
+ const data = await resp.json();
14625
+ setConnectedSession({ brokerId, connectionId, ...data });
14626
+ setSelectedAccountId(data.accounts[0]?.id ?? "");
14627
+ setOrderPanelConnecting(false);
14628
+ } else {
14629
+ const errData = await resp.json().catch(() => ({}));
14630
+ setOrderPanelError(errData.error ?? "Failed to start broker session");
14631
+ setOrderPanelConnecting(false);
14632
+ }
14633
+ } catch {
14634
+ setOrderPanelError("Network error \u2014 could not connect to broker");
14635
+ setOrderPanelConnecting(false);
14636
+ }
14637
+ }, [apiUrl, getAuthToken]);
14638
+ const handleToggleOrderEntry = useCallback(() => {
14639
+ if (orderPanelOpen && !orderPanelConnecting) {
14640
+ setOrderPanelOpen(false);
14641
+ return;
14642
+ }
14643
+ if (connectedSession) {
14644
+ setOrderPanelOpen((o) => !o);
14645
+ return;
14646
+ }
14647
+ if (savedConnections.length > 0) {
14648
+ const first = savedConnections[0];
14649
+ void handleBrokerConnected(first.broker, first.id);
14650
+ return;
14651
+ }
14652
+ setTradeDrawerOpen(true);
14653
+ }, [orderPanelOpen, orderPanelConnecting, connectedSession, savedConnections, handleBrokerConnected]);
14654
+ useEffect(() => {
14655
+ if (!wsLoaded) return;
14656
+ const fetchConnections = async () => {
14657
+ try {
14658
+ const headers = {};
14659
+ if (getAuthToken) {
14660
+ const tok = await getAuthToken();
14661
+ if (tok) headers["Authorization"] = `Bearer ${tok}`;
14662
+ }
14663
+ const resp = await fetch(`${apiUrl ?? ""}/api/broker/user-connections`, { headers });
14664
+ if (resp.ok) {
14665
+ const data = await resp.json();
14666
+ setSavedConnections(data.connections ?? []);
14667
+ }
14668
+ } catch {
14669
+ }
14670
+ };
14671
+ void fetchConnections();
14672
+ }, [wsLoaded]);
13597
14673
  useEffect(() => {
13598
14674
  watchlistApi.configure({
13599
14675
  apiBase: `${apiUrl ?? ""}/api`,
@@ -13911,6 +14987,8 @@ var ManagedAppShell = forwardRef(
13911
14987
  ...tab.snapshot ? { layoutToRestore: tab.snapshot } : {},
13912
14988
  ...getExchangeLogoUrl ? { getExchangeLogoUrl } : {},
13913
14989
  ...tradingBridge ? { tradingBridge } : {},
14990
+ ...onLogout ? { onLogout } : {},
14991
+ ...user ? { user } : {},
13914
14992
  onIndicatorsChanged: () => setChartDirty((d) => d + 1)
13915
14993
  },
13916
14994
  `${tab.id}-${tab.remountCount}`
@@ -13920,64 +14998,90 @@ var ManagedAppShell = forwardRef(
13920
14998
  )) });
13921
14999
  const drawers = /* @__PURE__ */ jsxs(Fragment, { children: [
13922
15000
  scriptDrawerOpen && (renderScriptDrawer ? renderScriptDrawer({ onClose: () => setScriptDrawerOpen(false), onAddIndicator: handleAddIndicator }) : /* @__PURE__ */ jsx(ScriptDrawer, { onClose: () => setScriptDrawerOpen(false), onAddIndicator: handleAddIndicator })),
13923
- watchlistOpen && (renderWatchlistDrawer ? renderWatchlistDrawer({ onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange }) : /* @__PURE__ */ jsx(WatchlistDrawer, { onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange, symbolResolver }))
13924
- ] });
13925
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, style: { height: "100%" }, children: [
13926
- tradeDrawerOpen && capabilities.renderManagedTradingControls && (renderTradeDrawer ? renderTradeDrawer({ onClose: () => setTradeDrawerOpen(false) }) : /* @__PURE__ */ jsx(TradeDrawer, { onClose: () => setTradeDrawerOpen(false) })),
13927
- /* @__PURE__ */ jsx(
13928
- ChartWorkspace,
15001
+ watchlistOpen && (renderWatchlistDrawer ? renderWatchlistDrawer({ onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange }) : /* @__PURE__ */ jsx(WatchlistDrawer, { onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange, symbolResolver })),
15002
+ tradeDrawerOpen && capabilities.renderManagedTradingControls && (renderTradeDrawer ? renderTradeDrawer({ onClose: () => setTradeDrawerOpen(false) }) : /* @__PURE__ */ jsx(
15003
+ TradeDrawer,
13929
15004
  {
13930
- tabs: tabs.map((t) => ({ id: t.id, label: t.label, isSaved: !!t.savedLayoutId })),
13931
- activeTabId,
13932
- onSelectTab: selectTab,
13933
- onAddTab: addTab,
13934
- onCloseTab: closeTab,
13935
- onRenameTab: renameTab,
13936
- symbol: activeTab.symbol,
13937
- timeframe: activeTab.timeframe,
13938
- theme,
13939
- customTimeframes,
13940
- favoriteTfs,
13941
- onSymbolChange: handleSymbolChange,
13942
- onTimeframeChange: handleTimeframeChange,
13943
- onAddCustomTimeframe: handleAddCustomTimeframe,
13944
- onFavoriteTfsChange: setFavoriteTfs,
13945
- onAddIndicator: handleAddIndicator,
13946
- onToggleTheme: toggleTheme,
13947
- onCopyScreenshot: handleCopyScreenshot,
13948
- onDownloadScreenshot: handleDownloadScreenshot,
13949
- onFullscreen: handleFullscreen,
13950
- isFullscreen,
13951
- ...activeTab.savedLayoutId ? { currentLayoutName: activeTab.label, currentLayoutId: activeTab.savedLayoutId } : {},
13952
- autoSave,
13953
- onFetchLayouts: () => layouts.list(),
13954
- onSaveLayout: handleSaveLayout,
13955
- onLoadLayout: handleLoadLayout,
13956
- onRenameLayout: handleRenameLayout,
13957
- onCopyLayout: handleCopyLayout,
13958
- onToggleAutoSave: handleToggleAutoSave,
13959
- onDeleteLayout: handleDeleteLayout,
13960
- onOpenLayoutInNewTab: handleOpenLayoutInNewTab,
13961
- symbolResolver,
13962
- showTradeButton: capabilities.renderManagedTradingControls,
13963
- tradeDrawerOpen,
13964
- onToggleTradeDrawer: () => setTradeDrawerOpen((o) => !o),
13965
- watchlistOpen,
13966
- onToggleWatchlist: () => setWatchlistOpen((o) => !o),
13967
- activeSymbol: activeTab.symbol,
13968
- timezone,
13969
- onTimezoneChange: setTimezone,
13970
- session,
13971
- onSessionChange: setSession,
13972
- scriptDrawerOpen,
13973
- onToggleScriptDrawer: () => setScriptDrawerOpen((o) => !o),
13974
- isLicensed,
13975
- wsLoaded,
13976
- chartSlots,
13977
- drawers
15005
+ onClose: () => setTradeDrawerOpen(false),
15006
+ savedConnections,
15007
+ onBrokerConnected: handleBrokerConnected,
15008
+ ...getAuthToken ? { getAuthToken } : {}
15009
+ }
15010
+ )),
15011
+ orderPanelOpen && connectedSession && capabilities.renderManagedTradingControls && /* @__PURE__ */ jsx(
15012
+ OrderEntryPanel,
15013
+ {
15014
+ sessionId: connectedSession.sessionId,
15015
+ brokerSlug: connectedSession.brokerSlug,
15016
+ accounts: connectedSession.accounts,
15017
+ selectedAccountId,
15018
+ defaultSymbol: activeTab.symbol,
15019
+ apiUrl,
15020
+ getAuthToken,
15021
+ connecting: orderPanelConnecting,
15022
+ connectionError: orderPanelError || void 0,
15023
+ onRetryConnect: () => void handleBrokerConnected(connectedSession.brokerId, connectedSession.connectionId),
15024
+ onChangeAccount: setSelectedAccountId,
15025
+ onClose: () => setOrderPanelOpen(false)
13978
15026
  }
13979
15027
  )
13980
15028
  ] });
15029
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, style: { height: "100%", position: "relative" }, children: /* @__PURE__ */ jsx(
15030
+ ChartWorkspace,
15031
+ {
15032
+ tabs: tabs.map((t) => ({ id: t.id, label: t.label, isSaved: !!t.savedLayoutId })),
15033
+ activeTabId,
15034
+ onSelectTab: selectTab,
15035
+ onAddTab: addTab,
15036
+ onCloseTab: closeTab,
15037
+ onRenameTab: renameTab,
15038
+ symbol: activeTab.symbol,
15039
+ timeframe: activeTab.timeframe,
15040
+ theme,
15041
+ customTimeframes,
15042
+ favoriteTfs,
15043
+ onSymbolChange: handleSymbolChange,
15044
+ onTimeframeChange: handleTimeframeChange,
15045
+ onAddCustomTimeframe: handleAddCustomTimeframe,
15046
+ onFavoriteTfsChange: setFavoriteTfs,
15047
+ onAddIndicator: handleAddIndicator,
15048
+ onToggleTheme: toggleTheme,
15049
+ onCopyScreenshot: handleCopyScreenshot,
15050
+ onDownloadScreenshot: handleDownloadScreenshot,
15051
+ onFullscreen: handleFullscreen,
15052
+ isFullscreen,
15053
+ ...activeTab.savedLayoutId ? { currentLayoutName: activeTab.label, currentLayoutId: activeTab.savedLayoutId } : {},
15054
+ autoSave,
15055
+ onFetchLayouts: () => layouts.list(),
15056
+ onSaveLayout: handleSaveLayout,
15057
+ onLoadLayout: handleLoadLayout,
15058
+ onRenameLayout: handleRenameLayout,
15059
+ onCopyLayout: handleCopyLayout,
15060
+ onToggleAutoSave: handleToggleAutoSave,
15061
+ onDeleteLayout: handleDeleteLayout,
15062
+ onOpenLayoutInNewTab: handleOpenLayoutInNewTab,
15063
+ symbolResolver,
15064
+ showTradeButton: capabilities.renderManagedTradingControls,
15065
+ tradeDrawerOpen: tradeDrawerOpen || orderPanelOpen,
15066
+ onToggleTradeDrawer: () => setTradeDrawerOpen((o) => !o),
15067
+ watchlistOpen,
15068
+ onToggleWatchlist: () => setWatchlistOpen((o) => !o),
15069
+ orderEntryOpen: orderPanelOpen,
15070
+ onToggleOrderEntry: handleToggleOrderEntry,
15071
+ showOrderEntry: capabilities.renderManagedTradingControls,
15072
+ activeSymbol: activeTab.symbol,
15073
+ timezone,
15074
+ onTimezoneChange: setTimezone,
15075
+ session,
15076
+ onSessionChange: setSession,
15077
+ scriptDrawerOpen,
15078
+ onToggleScriptDrawer: () => setScriptDrawerOpen((o) => !o),
15079
+ isLicensed,
15080
+ wsLoaded,
15081
+ chartSlots,
15082
+ drawers
15083
+ }
15084
+ ) });
13981
15085
  }
13982
15086
  );
13983
15087
  function FloatingPanel({