@loafmarkets/ui 0.1.411 → 0.1.413

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.
package/dist/index.d.mts CHANGED
@@ -1062,6 +1062,18 @@ type PortfolioActivityPanelProps = {
1062
1062
  /** Click handler for the asset name in the Open Orders / Order History / Trade History rows — opens that asset. */
1063
1063
  onAssetClick?: (tokenName: string) => void;
1064
1064
  onClosePosition?: (tokenName: string, type: 'market' | 'limit', quantity: number, marketPrice: number) => void;
1065
+ /**
1066
+ * Close EVERY open position, in one handover to the host page. Sell All only
1067
+ * renders when this is provided.
1068
+ *
1069
+ * The panel deliberately does not sequence this itself: `onClosePosition` is
1070
+ * the per-ROW action, and on a trading page it re-points the page at that
1071
+ * row's market — so fanning it out per position navigated once per position
1072
+ * and closed nothing. The host owns ordering, per-order submission and the
1073
+ * result report (and, once the backend session signer is retired, the
1074
+ * signature prompt each order needs).
1075
+ */
1076
+ onCloseAllPositions?: (type: 'market' | 'limit') => Promise<void> | void;
1065
1077
  /** Open the shareable position card for a held position. */
1066
1078
  onSharePosition?: (pos: PortfolioPosition) => void;
1067
1079
  offeringOrders?: readonly OfferingOrder[];
@@ -1080,7 +1092,7 @@ type PortfolioActivityPanelProps = {
1080
1092
  className?: string;
1081
1093
  style?: React.CSSProperties;
1082
1094
  };
1083
- declare function PortfolioActivityPanel({ positions, showPositionsTab, showSubscriptionsTab, onPositionClick, onSharePosition, onAssetClick, onClosePosition, offeringOrders, openOrders, orderHistory, tradeHistory, transfers, onCancelOrder, onCancelAllOrders, cancellingOrderId, compactPositions, defaultTab, onTabChange, pageSize, blockExplorerBaseUrl: _blockExplorerBaseUrl, className, style, }: PortfolioActivityPanelProps): react_jsx_runtime.JSX.Element;
1095
+ declare function PortfolioActivityPanel({ positions, showPositionsTab, showSubscriptionsTab, onPositionClick, onSharePosition, onAssetClick, onClosePosition, onCloseAllPositions, offeringOrders, openOrders, orderHistory, tradeHistory, transfers, onCancelOrder, onCancelAllOrders, cancellingOrderId, compactPositions, defaultTab, onTabChange, pageSize, blockExplorerBaseUrl: _blockExplorerBaseUrl, className, style, }: PortfolioActivityPanelProps): react_jsx_runtime.JSX.Element;
1084
1096
 
1085
1097
  type SelectorItem = {
1086
1098
  readonly tokenName: string;
package/dist/index.d.ts CHANGED
@@ -1062,6 +1062,18 @@ type PortfolioActivityPanelProps = {
1062
1062
  /** Click handler for the asset name in the Open Orders / Order History / Trade History rows — opens that asset. */
1063
1063
  onAssetClick?: (tokenName: string) => void;
1064
1064
  onClosePosition?: (tokenName: string, type: 'market' | 'limit', quantity: number, marketPrice: number) => void;
1065
+ /**
1066
+ * Close EVERY open position, in one handover to the host page. Sell All only
1067
+ * renders when this is provided.
1068
+ *
1069
+ * The panel deliberately does not sequence this itself: `onClosePosition` is
1070
+ * the per-ROW action, and on a trading page it re-points the page at that
1071
+ * row's market — so fanning it out per position navigated once per position
1072
+ * and closed nothing. The host owns ordering, per-order submission and the
1073
+ * result report (and, once the backend session signer is retired, the
1074
+ * signature prompt each order needs).
1075
+ */
1076
+ onCloseAllPositions?: (type: 'market' | 'limit') => Promise<void> | void;
1065
1077
  /** Open the shareable position card for a held position. */
1066
1078
  onSharePosition?: (pos: PortfolioPosition) => void;
1067
1079
  offeringOrders?: readonly OfferingOrder[];
@@ -1080,7 +1092,7 @@ type PortfolioActivityPanelProps = {
1080
1092
  className?: string;
1081
1093
  style?: React.CSSProperties;
1082
1094
  };
1083
- declare function PortfolioActivityPanel({ positions, showPositionsTab, showSubscriptionsTab, onPositionClick, onSharePosition, onAssetClick, onClosePosition, offeringOrders, openOrders, orderHistory, tradeHistory, transfers, onCancelOrder, onCancelAllOrders, cancellingOrderId, compactPositions, defaultTab, onTabChange, pageSize, blockExplorerBaseUrl: _blockExplorerBaseUrl, className, style, }: PortfolioActivityPanelProps): react_jsx_runtime.JSX.Element;
1095
+ declare function PortfolioActivityPanel({ positions, showPositionsTab, showSubscriptionsTab, onPositionClick, onSharePosition, onAssetClick, onClosePosition, onCloseAllPositions, offeringOrders, openOrders, orderHistory, tradeHistory, transfers, onCancelOrder, onCancelAllOrders, cancellingOrderId, compactPositions, defaultTab, onTabChange, pageSize, blockExplorerBaseUrl: _blockExplorerBaseUrl, className, style, }: PortfolioActivityPanelProps): react_jsx_runtime.JSX.Element;
1084
1096
 
1085
1097
  type SelectorItem = {
1086
1098
  readonly tokenName: string;
package/dist/index.js CHANGED
@@ -15122,6 +15122,9 @@ var STATUS_COLOR2 = {
15122
15122
  CLOSED: "#848e9c",
15123
15123
  CANCELLED: "#f6465d"
15124
15124
  };
15125
+ var STUCK_SETTLING_MS = 7e3;
15126
+ var SETTLING_MESSAGE = "Trade settling on-chain";
15127
+ var STUCK_SETTLING_MESSAGE = "Attention: Trade taking longer than usual to settle on chain. Please wait up to 10 seconds before trading. This is reflected in your locked balance.";
15125
15128
  var settlingSpin = styled10.keyframes`
15126
15129
  to { transform: rotate(360deg); }
15127
15130
  `;
@@ -15135,41 +15138,55 @@ var SettlingSpinner = styled10__default.default.span`
15135
15138
  animation: ${settlingSpin} 0.7s linear infinite;
15136
15139
  flex-shrink: 0;
15137
15140
  `;
15141
+ var StuckSettlingGlyph = styled10__default.default.span`
15142
+ display: inline-block;
15143
+ font-size: 11px;
15144
+ line-height: 1;
15145
+ flex-shrink: 0;
15146
+ `;
15138
15147
  var SettlingTip = styled10__default.default.span`
15139
15148
  position: relative;
15140
15149
  display: inline-flex;
15141
15150
  align-items: center;
15142
15151
  cursor: help;
15143
- &::after {
15144
- content: 'Trade settling on-chain';
15145
- position: absolute;
15146
- left: 50%;
15147
- bottom: calc(100% + 8px);
15148
- transform: translateX(-50%) translateY(3px);
15149
- padding: 6px 8px;
15150
- background: rgba(20, 22, 28, 0.97);
15151
- border: 1px solid rgba(255, 255, 255, 0.1);
15152
- border-radius: 6px;
15153
- color: rgba(255, 255, 255, 0.78);
15154
- font-size: 0.66rem;
15155
- font-weight: 400;
15156
- line-height: 1.3;
15157
- letter-spacing: normal;
15158
- text-transform: none;
15159
- white-space: nowrap;
15160
- box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
15161
- opacity: 0;
15162
- visibility: hidden;
15163
- transition: opacity 0.15s ease, transform 0.15s ease;
15164
- pointer-events: none;
15165
- z-index: 2000;
15166
- }
15167
- &:hover::after {
15152
+ `;
15153
+ var SettlingTipBubble = styled10__default.default.span`
15154
+ position: absolute;
15155
+ bottom: calc(100% + 8px);
15156
+ /* Stuck copy is a paragraph, so it anchors to the icon's left edge instead of
15157
+ centring centred it would hang off the left of the row. */
15158
+ left: ${(p) => p.$stuck ? "0" : "50%"};
15159
+ transform: ${(p) => p.$stuck ? "translateX(0)" : "translateX(-50%)"} translateY(3px);
15160
+ width: ${(p) => p.$stuck ? "224px" : "auto"};
15161
+ white-space: ${(p) => p.$stuck ? "normal" : "nowrap"};
15162
+ padding: 6px 8px;
15163
+ background: rgba(20, 22, 28, 0.97);
15164
+ border: 1px solid rgba(255, 255, 255, 0.1);
15165
+ border-radius: 6px;
15166
+ color: rgba(255, 255, 255, 0.78);
15167
+ font-size: 0.66rem;
15168
+ font-weight: 400;
15169
+ line-height: 1.35;
15170
+ letter-spacing: normal;
15171
+ text-transform: none;
15172
+ text-align: left;
15173
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
15174
+ opacity: 0;
15175
+ visibility: hidden;
15176
+ transition: opacity 0.15s ease, transform 0.15s ease;
15177
+ pointer-events: none;
15178
+ z-index: 2000;
15179
+
15180
+ ${SettlingTip}:hover & {
15168
15181
  opacity: 1;
15169
15182
  visibility: visible;
15170
- transform: translateX(-50%) translateY(0);
15183
+ transform: ${(p) => p.$stuck ? "translateX(0)" : "translateX(-50%)"} translateY(0);
15171
15184
  }
15172
15185
  `;
15186
+ var SettlingIndicator = ({ stuck, style }) => /* @__PURE__ */ jsxRuntime.jsxs(SettlingTip, { role: "img", "aria-label": stuck ? "Settlement delayed" : SETTLING_MESSAGE, style, children: [
15187
+ stuck ? /* @__PURE__ */ jsxRuntime.jsx(StuckSettlingGlyph, { "aria-hidden": "true", children: "\u26A0\uFE0F" }) : /* @__PURE__ */ jsxRuntime.jsx(SettlingSpinner, { "aria-hidden": "true" }),
15188
+ /* @__PURE__ */ jsxRuntime.jsx(SettlingTipBubble, { $stuck: stuck, children: stuck ? STUCK_SETTLING_MESSAGE : SETTLING_MESSAGE })
15189
+ ] });
15173
15190
  var ACTIVITY_PAGE_SIZE_DEFAULT = 8;
15174
15191
  var formatCurrency4 = (value, opts) => {
15175
15192
  const min = opts?.minimumFractionDigits ?? 2;
@@ -15177,14 +15194,21 @@ var formatCurrency4 = (value, opts) => {
15177
15194
  return `$${value.toLocaleString("en-US", { minimumFractionDigits: min, maximumFractionDigits: max })}`;
15178
15195
  };
15179
15196
  var formatNumber2 = (value, maximumFractionDigits = 2) => value.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits });
15197
+ var toMillis = (unix) => {
15198
+ if (unix > 1e18) return Math.floor(unix / 1e6);
15199
+ if (unix > 1e14) return Math.floor(unix / 1e3);
15200
+ if (unix > 1e12) return unix;
15201
+ return unix * 1e3;
15202
+ };
15203
+ var SETTLED_TRADE_STATUSES = ["SETTLED", "CONFIRMED", "FILLED", "ALLOCATED"];
15204
+ var FAILED_TRADE_STATUSES = ["SETTLEMENT_FAILED", "REJECTED", "CANCELLED", "CANCELED"];
15205
+ var tradeIsSettling = (status) => {
15206
+ const s = status?.toUpperCase();
15207
+ return !!s && !SETTLED_TRADE_STATUSES.includes(s) && !FAILED_TRADE_STATUSES.includes(s);
15208
+ };
15180
15209
  var formatTimestamp = (unix) => {
15181
15210
  if (!unix) return "\u2014";
15182
- let sec;
15183
- if (unix > 1e18) sec = Math.floor(unix / 1e9);
15184
- else if (unix > 1e14) sec = Math.floor(unix / 1e6);
15185
- else if (unix > 1e12) sec = Math.floor(unix / 1e3);
15186
- else sec = unix;
15187
- return new Date(sec * 1e3).toLocaleString("en-AU", {
15211
+ return new Date(toMillis(unix)).toLocaleString("en-AU", {
15188
15212
  day: "2-digit",
15189
15213
  month: "short",
15190
15214
  hour: "2-digit",
@@ -15222,6 +15246,7 @@ function PortfolioActivityPanel({
15222
15246
  onSharePosition,
15223
15247
  onAssetClick,
15224
15248
  onClosePosition,
15249
+ onCloseAllPositions,
15225
15250
  offeringOrders = [],
15226
15251
  openOrders = [],
15227
15252
  orderHistory = [],
@@ -15246,6 +15271,42 @@ function PortfolioActivityPanel({
15246
15271
  const [cancellingAll, setCancellingAll] = React5.useState(false);
15247
15272
  const [closeAllOpen, setCloseAllOpen] = React5.useState(false);
15248
15273
  const [closeAllType, setCloseAllType] = React5.useState("market");
15274
+ const [closingAll, setClosingAll] = React5.useState(false);
15275
+ const settlingPositionIds = positions.filter((p) => p.settling).map((p) => p.propertyId);
15276
+ const settlingKey = settlingPositionIds.join(",");
15277
+ const settlingTradeStartByProperty = /* @__PURE__ */ new Map();
15278
+ const settlingTradeDeadlines = [];
15279
+ for (const trade of tradeHistory) {
15280
+ if (!tradeIsSettling(trade.status)) continue;
15281
+ const startedAt = toMillis(trade.executedAt);
15282
+ settlingTradeDeadlines.push(startedAt + STUCK_SETTLING_MS);
15283
+ const earliest = settlingTradeStartByProperty.get(trade.propertyId);
15284
+ if (earliest === void 0 || startedAt < earliest) settlingTradeStartByProperty.set(trade.propertyId, startedAt);
15285
+ }
15286
+ const tradeDeadlineKey = settlingTradeDeadlines.join(",");
15287
+ const settlingSinceRef = React5.useRef(/* @__PURE__ */ new Map());
15288
+ const [now, setNow] = React5.useState(() => Date.now());
15289
+ React5.useEffect(() => {
15290
+ const since = settlingSinceRef.current;
15291
+ const seenAt = Date.now();
15292
+ for (const id of settlingPositionIds) if (!since.has(id)) since.set(id, seenAt);
15293
+ for (const id of [...since.keys()]) if (!settlingPositionIds.includes(id)) since.delete(id);
15294
+ setNow(seenAt);
15295
+ }, [settlingKey]);
15296
+ const positionSettlingStart = (propertyId) => settlingTradeStartByProperty.get(propertyId) ?? settlingSinceRef.current.get(propertyId) ?? now;
15297
+ React5.useEffect(() => {
15298
+ const pending = [
15299
+ ...settlingPositionIds.map((id) => positionSettlingStart(id) + STUCK_SETTLING_MS),
15300
+ ...settlingTradeDeadlines
15301
+ ].filter((deadline) => deadline > now);
15302
+ if (pending.length === 0) return;
15303
+ const delay = Math.min(Math.min(...pending) - now + 50, 2147483647);
15304
+ const timer = setTimeout(() => setNow(Date.now()), delay);
15305
+ return () => clearTimeout(timer);
15306
+ }, [settlingKey, tradeDeadlineKey, now]);
15307
+ const isPositionStuck = (pos) => !!pos.settling && now - positionSettlingStart(pos.propertyId) >= STUCK_SETTLING_MS;
15308
+ const isTradeStuck = (trade) => now - toMillis(trade.executedAt) >= STUCK_SETTLING_MS;
15309
+ const hasPositionActions = !!(onClosePosition || onSharePosition || onCloseAllPositions);
15249
15310
  const [posSortKey, setPosSortKey] = React5.useState("asset");
15250
15311
  const [posSortAsc, setPosSortAsc] = React5.useState(true);
15251
15312
  const handlePosSort = (key) => {
@@ -15424,7 +15485,6 @@ function PortfolioActivityPanel({
15424
15485
  const pagedTradeHistory = sortedTradeHistory.slice(currentPage * pageSize, (currentPage + 1) * pageSize);
15425
15486
  const pagedTransfers = sortedTransfers.slice(currentPage * pageSize, (currentPage + 1) * pageSize);
15426
15487
  return /* @__PURE__ */ jsxRuntime.jsxs(Container2, { className, style, children: [
15427
- /* @__PURE__ */ jsxRuntime.jsx(PanelTitle, { children: "Activity" }),
15428
15488
  /* @__PURE__ */ jsxRuntime.jsxs(TabContainer, { children: [
15429
15489
  showPositionsTab && /* @__PURE__ */ jsxRuntime.jsxs(Tab, { $active: activeTab === "positions", onClick: () => handleTabChange("positions"), children: [
15430
15490
  "Trading Positions",
@@ -15480,7 +15540,7 @@ function PortfolioActivityPanel({
15480
15540
  ] }),
15481
15541
  /* @__PURE__ */ jsxRuntime.jsxs(CToolbarActions, { children: [
15482
15542
  /* @__PURE__ */ jsxRuntime.jsx(CToolbarLink, { onClick: toggleExpandAll, children: allExpanded ? "Collapse All" : "Expand All" }),
15483
- onClosePosition && /* @__PURE__ */ jsxRuntime.jsx(CToolbarLink, { onClick: () => setCloseAllOpen(true), children: "Sell All" })
15543
+ onCloseAllPositions && /* @__PURE__ */ jsxRuntime.jsx(CToolbarLink, { onClick: () => setCloseAllOpen(true), children: "Sell All" })
15484
15544
  ] })
15485
15545
  ] }),
15486
15546
  sortedPositions.map((pos) => {
@@ -15500,7 +15560,7 @@ function PortfolioActivityPanel({
15500
15560
  /* @__PURE__ */ jsxRuntime.jsx(CCellLabel, { children: "Asset" }),
15501
15561
  /* @__PURE__ */ jsxRuntime.jsxs(CAssetName, { children: [
15502
15562
  pos.tokenName,
15503
- pos.settling && /* @__PURE__ */ jsxRuntime.jsx(SettlingTip, { style: { marginLeft: 6 }, children: /* @__PURE__ */ jsxRuntime.jsx(SettlingSpinner, {}) })
15563
+ pos.settling && /* @__PURE__ */ jsxRuntime.jsx(SettlingIndicator, { stuck: isPositionStuck(pos), style: { marginLeft: 6 } })
15504
15564
  ] })
15505
15565
  ] }),
15506
15566
  /* @__PURE__ */ jsxRuntime.jsxs(CUnitsCell, { children: [
@@ -15556,7 +15616,7 @@ function PortfolioActivityPanel({
15556
15616
  })
15557
15617
  ] }),
15558
15618
  positions.length > 0 && !compactPositions && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15559
- /* @__PURE__ */ jsxRuntime.jsxs(PositionsHeader, { $hasClose: !!(onClosePosition || onSharePosition), children: [
15619
+ /* @__PURE__ */ jsxRuntime.jsxs(PositionsHeader, { $hasClose: hasPositionActions, children: [
15560
15620
  /* @__PURE__ */ jsxRuntime.jsxs(SortableHeaderCell, { $active: posSortKey === "asset", onClick: () => handlePosSort("asset"), children: [
15561
15621
  "Asset ",
15562
15622
  posSortKey === "asset" && (posSortAsc ? "\u2191" : "\u2193")
@@ -15581,7 +15641,7 @@ function PortfolioActivityPanel({
15581
15641
  "PNL (%) ",
15582
15642
  posSortKey === "pnl" && (posSortAsc ? "\u2191" : "\u2193")
15583
15643
  ] }),
15584
- onClosePosition && /* @__PURE__ */ jsxRuntime.jsx(PositionsHeaderCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CloseAllHeaderBtn, { onClick: () => setCloseAllOpen(true), children: "Sell All" }) })
15644
+ onCloseAllPositions && /* @__PURE__ */ jsxRuntime.jsx(PositionsHeaderCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CloseAllHeaderBtn, { onClick: () => setCloseAllOpen(true), children: "Sell All" }) })
15585
15645
  ] }),
15586
15646
  sortedPositions.map((pos) => {
15587
15647
  const currentValue = pos.totalQuantity * pos.marketPrice;
@@ -15589,10 +15649,10 @@ function PortfolioActivityPanel({
15589
15649
  const pnl = pos.propertyPnl ?? currentValue - costBasis;
15590
15650
  const pnlPercent = pos.propertyPnlPercent ?? pos.percentChange ?? 0;
15591
15651
  const isPositive = pnl >= 0;
15592
- return /* @__PURE__ */ jsxRuntime.jsxs(PositionsRow, { $hasClose: !!(onClosePosition || onSharePosition), children: [
15652
+ return /* @__PURE__ */ jsxRuntime.jsxs(PositionsRow, { $hasClose: hasPositionActions, children: [
15593
15653
  /* @__PURE__ */ jsxRuntime.jsx(PositionsCell, { children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
15594
15654
  /* @__PURE__ */ jsxRuntime.jsx(HoldingsPropertyLink, { onClick: () => onPositionClick?.(pos.tokenName, pos.isIpoAllocation), children: pos.tokenName }),
15595
- pos.settling && /* @__PURE__ */ jsxRuntime.jsx(SettlingTip, { children: /* @__PURE__ */ jsxRuntime.jsx(SettlingSpinner, {}) })
15655
+ pos.settling && /* @__PURE__ */ jsxRuntime.jsx(SettlingIndicator, { stuck: isPositionStuck(pos) })
15596
15656
  ] }) }),
15597
15657
  /* @__PURE__ */ jsxRuntime.jsx(PositionsCell, { children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "#fff", fontSize: "0.8rem" }, children: [
15598
15658
  formatNumber2(pos.totalQuantity),
@@ -15636,11 +15696,15 @@ function PortfolioActivityPanel({
15636
15696
  ] }, pos.tokenName);
15637
15697
  })
15638
15698
  ] }),
15639
- positions.length > 0 && closeAllOpen && onClosePosition && typeof document !== "undefined" && ReactDOM.createPortal(
15640
- /* @__PURE__ */ jsxRuntime.jsx(CloseAllOverlay, { onClick: () => setCloseAllOpen(false), children: /* @__PURE__ */ jsxRuntime.jsxs(CloseAllModal, { onClick: (e) => e.stopPropagation(), children: [
15641
- /* @__PURE__ */ jsxRuntime.jsx(CloseAllModalX, { onClick: () => setCloseAllOpen(false), children: "\xD7" }),
15699
+ positions.length > 0 && closeAllOpen && onCloseAllPositions && typeof document !== "undefined" && ReactDOM.createPortal(
15700
+ /* @__PURE__ */ jsxRuntime.jsx(CloseAllOverlay, { onClick: () => {
15701
+ if (!closingAll) setCloseAllOpen(false);
15702
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(CloseAllModal, { onClick: (e) => e.stopPropagation(), children: [
15703
+ /* @__PURE__ */ jsxRuntime.jsx(CloseAllModalX, { onClick: () => {
15704
+ if (!closingAll) setCloseAllOpen(false);
15705
+ }, children: "\xD7" }),
15642
15706
  /* @__PURE__ */ jsxRuntime.jsx(CloseAllTitle, { children: "Confirm Sell All" }),
15643
- /* @__PURE__ */ jsxRuntime.jsx(CloseAllDesc, { children: "This will sell all your positions and cancel their associated TP/SL orders." }),
15707
+ /* @__PURE__ */ jsxRuntime.jsx(CloseAllDesc, { children: "This places a sell order for every position you hold, one at a time." }),
15644
15708
  /* @__PURE__ */ jsxRuntime.jsxs(CloseAllOption, { onClick: () => setCloseAllType("market"), children: [
15645
15709
  /* @__PURE__ */ jsxRuntime.jsx(CloseAllRadio, { $selected: closeAllType === "market" }),
15646
15710
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Market Sell" })
@@ -15649,14 +15713,22 @@ function PortfolioActivityPanel({
15649
15713
  /* @__PURE__ */ jsxRuntime.jsx(CloseAllRadio, { $selected: closeAllType === "limit" }),
15650
15714
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Limit Sell at Mid Price" })
15651
15715
  ] }),
15652
- /* @__PURE__ */ jsxRuntime.jsxs(CloseAllConfirmBtn, { onClick: () => {
15653
- positions.forEach((pos) => onClosePosition(pos.tokenName, closeAllType, pos.quantity, pos.marketPrice));
15654
- setCloseAllOpen(false);
15655
- }, children: [
15656
- "Confirm ",
15657
- closeAllType === "market" ? "Market" : "Limit",
15658
- " Sell"
15659
- ] })
15716
+ /* @__PURE__ */ jsxRuntime.jsx(
15717
+ CloseAllConfirmBtn,
15718
+ {
15719
+ disabled: closingAll,
15720
+ onClick: async () => {
15721
+ setClosingAll(true);
15722
+ try {
15723
+ await onCloseAllPositions(closeAllType);
15724
+ } finally {
15725
+ setClosingAll(false);
15726
+ setCloseAllOpen(false);
15727
+ }
15728
+ },
15729
+ children: closingAll ? "Selling\u2026" : `Confirm ${closeAllType === "market" ? "Market" : "Limit"} Sell`
15730
+ }
15731
+ )
15660
15732
  ] }) }),
15661
15733
  document.body
15662
15734
  )
@@ -15961,7 +16033,8 @@ function PortfolioActivityPanel({
15961
16033
  const s = trade.status?.toUpperCase();
15962
16034
  const isSettled = s === "SETTLED" || s === "CONFIRMED" || s === "FILLED" || s === "ALLOCATED";
15963
16035
  const isFailed = s === "SETTLEMENT_FAILED" || s === "REJECTED" || s === "CANCELLED" || s === "CANCELED";
15964
- const isSettling = !isSettled && !isFailed && !!trade.status;
16036
+ const isSettling = tradeIsSettling(trade.status);
16037
+ const stuckSettling = isSettling && isTradeStuck(trade);
15965
16038
  const settlementLabel = isSettled ? "Settled" : isFailed ? "Settlement Failed" : isSettling ? prettyLabel(trade.status) : "\u2014";
15966
16039
  const settlementMeta = isSettled ? { color: "#0ecb81", bg: "rgba(14,203,129,0.1)" } : isFailed ? { color: "#f6465d", bg: "rgba(246,70,93,0.12)" } : isSettling ? { color: "#E6C87E", bg: "rgba(240,185,11,0.15)" } : null;
15967
16040
  const sideColor = sideLabel(trade.side) === "Buy" ? "#0ecb81" : "#f6465d";
@@ -16004,7 +16077,10 @@ function PortfolioActivityPanel({
16004
16077
  ] }),
16005
16078
  /* @__PURE__ */ jsxRuntime.jsxs(CDetailItem, { children: [
16006
16079
  /* @__PURE__ */ jsxRuntime.jsx(CDLabel, { children: "Settlement" }),
16007
- /* @__PURE__ */ jsxRuntime.jsx(CDValue, { children: settlementMeta ? /* @__PURE__ */ jsxRuntime.jsx(ActivityTag, { $color: settlementMeta.color, $bg: settlementMeta.bg, children: settlementLabel }) : "\u2014" })
16080
+ /* @__PURE__ */ jsxRuntime.jsxs(CDValue, { children: [
16081
+ settlementMeta ? /* @__PURE__ */ jsxRuntime.jsx(ActivityTag, { $color: settlementMeta.color, $bg: settlementMeta.bg, children: settlementLabel }) : "\u2014",
16082
+ stuckSettling && /* @__PURE__ */ jsxRuntime.jsx(SettlingIndicator, { stuck: true, style: { marginLeft: 6 } })
16083
+ ] })
16008
16084
  ] }),
16009
16085
  /* @__PURE__ */ jsxRuntime.jsxs(CDetailItem, { children: [
16010
16086
  /* @__PURE__ */ jsxRuntime.jsx(CDLabel, { children: "Date" }),
@@ -16029,7 +16105,8 @@ function PortfolioActivityPanel({
16029
16105
  const s = trade.status?.toUpperCase();
16030
16106
  const isSettled = s === "SETTLED" || s === "CONFIRMED" || s === "FILLED" || s === "ALLOCATED";
16031
16107
  const isFailed = s === "SETTLEMENT_FAILED" || s === "REJECTED" || s === "CANCELLED" || s === "CANCELED";
16032
- const isSettling = !isSettled && !isFailed && !!trade.status;
16108
+ const isSettling = tradeIsSettling(trade.status);
16109
+ const stuckSettling = isSettling && isTradeStuck(trade);
16033
16110
  const settlementLabel = isSettled ? "Settled" : isFailed ? "Settlement Failed" : isSettling ? prettyLabel(trade.status) : "\u2014";
16034
16111
  const settlementMeta = isSettled ? { color: "#0ecb81", bg: "rgba(14,203,129,0.1)" } : isFailed ? { color: "#f6465d", bg: "rgba(246,70,93,0.12)" } : isSettling ? { color: "#E6C87E", bg: "rgba(240,185,11,0.15)" } : null;
16035
16112
  const sideColor = sideLabel(trade.side) === "Buy" ? "#0ecb81" : "#f6465d";
@@ -16046,7 +16123,10 @@ function PortfolioActivityPanel({
16046
16123
  ] }) }),
16047
16124
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { children: formatCurrency4(trade.price) }) }),
16048
16125
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { children: formatCurrency4(trade.price * trade.quantity) }) }),
16049
- /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: settlementMeta ? /* @__PURE__ */ jsxRuntime.jsx(ActivityTag, { $color: settlementMeta.color, $bg: settlementMeta.bg, children: settlementLabel }) : /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: "\u2014" }) }),
16126
+ /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.35rem" }, children: [
16127
+ settlementMeta ? /* @__PURE__ */ jsxRuntime.jsx(ActivityTag, { $color: settlementMeta.color, $bg: settlementMeta.bg, children: settlementLabel }) : /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: "\u2014" }),
16128
+ stuckSettling && /* @__PURE__ */ jsxRuntime.jsx(SettlingIndicator, { stuck: true })
16129
+ ] }) }),
16050
16130
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: /* @__PURE__ */ jsxRuntime.jsx(CellText, { $muted: true, children: formatTimestamp(trade.executedAt) }) }),
16051
16131
  /* @__PURE__ */ jsxRuntime.jsx(GridCell, { children: trade.txHash && /* @__PURE__ */ jsxRuntime.jsx(TxLink, { href: `${_blockExplorerBaseUrl}/${trade.txHash}`, target: "_blank", rel: "noopener noreferrer", title: "View on Arbiscan", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16052
16132
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
@@ -16215,17 +16295,6 @@ var ContentScroll = styled10__default.default.div`
16215
16295
  &::-webkit-scrollbar-track { background: transparent; }
16216
16296
  &::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 2px; }
16217
16297
  `;
16218
- var PanelTitle = styled10__default.default.h3`
16219
- font-size: 1rem;
16220
- font-weight: 600;
16221
- color: var(--color-text, #fff);
16222
- margin: 0 0 0.75rem;
16223
- padding-left: 1rem;
16224
- /* Redundant on mobile — the tab row already says what this is. Reclaim the space. */
16225
- @media (max-width: 768px) {
16226
- display: none;
16227
- }
16228
- `;
16229
16298
  var TabContainer = styled10__default.default.div`
16230
16299
  display: flex;
16231
16300
  gap: 0;
@@ -16678,9 +16747,14 @@ var CloseAllConfirmBtn = styled10__default.default.button`
16678
16747
  cursor: pointer;
16679
16748
  transition: all 0.15s ease;
16680
16749
 
16681
- &:hover {
16750
+ &:hover:not(:disabled) {
16682
16751
  background: rgba(246, 70, 93, 0.25);
16683
16752
  }
16753
+
16754
+ &:disabled {
16755
+ opacity: 0.5;
16756
+ cursor: not-allowed;
16757
+ }
16684
16758
  `;
16685
16759
  var CToolbar = styled10__default.default.div`
16686
16760
  display: flex;