@agg-build/ui 1.0.0 → 1.0.1

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.
Files changed (56) hide show
  1. package/dist/{chunk-2QJXRRYP.mjs → chunk-43K4PFBC.mjs} +3 -2
  2. package/dist/{chunk-I2WBGEWK.mjs → chunk-5ES2VJHO.mjs} +159 -93
  3. package/dist/{chunk-DMKKNK76.mjs → chunk-BYMLPLEZ.mjs} +791 -583
  4. package/dist/{chunk-C7T56TJR.mjs → chunk-HD6HBTK2.mjs} +1 -1
  5. package/dist/{chunk-CGNDMLQL.mjs → chunk-LPNZOX3E.mjs} +123 -52
  6. package/dist/{chunk-75IGOQ4N.mjs → chunk-WLXYCBZV.mjs} +62 -32
  7. package/dist/{chunk-6NS7D73S.mjs → chunk-YZNO6IUD.mjs} +740 -521
  8. package/dist/events.js +196 -93
  9. package/dist/events.mjs +3 -3
  10. package/dist/index.js +3396 -2806
  11. package/dist/index.mjs +13 -7
  12. package/dist/modals.js +798 -535
  13. package/dist/modals.mjs +3 -3
  14. package/dist/pages.js +378 -176
  15. package/dist/pages.mjs +6 -6
  16. package/dist/primitives.js +713 -531
  17. package/dist/primitives.mjs +7 -1
  18. package/dist/styles.css +1 -1
  19. package/dist/tailwind.css +1 -1
  20. package/dist/trading.js +242 -54
  21. package/dist/trading.mjs +4 -4
  22. package/dist/types/deposit/steps/card-payment-pending.d.mts +3 -1
  23. package/dist/types/deposit/steps/card-payment-pending.d.ts +3 -1
  24. package/dist/types/deposit/steps/crypto-transfer.d.mts +1 -1
  25. package/dist/types/deposit/steps/crypto-transfer.d.ts +1 -1
  26. package/dist/types/events/item/event-list-item.utils.d.mts +57 -0
  27. package/dist/types/events/item/event-list-item.utils.d.ts +57 -0
  28. package/dist/types/events/item-details/event-list-item-details.types.d.mts +16 -1
  29. package/dist/types/events/item-details/event-list-item-details.types.d.ts +16 -1
  30. package/dist/types/events/market-details/market-details.types.d.mts +8 -0
  31. package/dist/types/events/market-details/market-details.types.d.ts +8 -0
  32. package/dist/types/primitives/copy-button/index.d.mts +25 -0
  33. package/dist/types/primitives/copy-button/index.d.ts +25 -0
  34. package/dist/types/primitives/index.d.mts +2 -0
  35. package/dist/types/primitives/index.d.ts +2 -0
  36. package/dist/types/primitives/toast/index.d.mts +4 -0
  37. package/dist/types/primitives/toast/index.d.ts +4 -0
  38. package/dist/types/primitives/toast/toast.types.d.mts +31 -0
  39. package/dist/types/primitives/toast/toast.types.d.ts +31 -0
  40. package/dist/types/trading/place-order/index.place-order.constants.d.mts +8 -0
  41. package/dist/types/trading/place-order/index.place-order.constants.d.ts +8 -0
  42. package/dist/types/trading/place-order/index.place-order.types.d.mts +6 -0
  43. package/dist/types/trading/place-order/index.place-order.types.d.ts +6 -0
  44. package/dist/types/trading/place-order/index.place-order.utils.d.mts +4 -7
  45. package/dist/types/trading/place-order/index.place-order.utils.d.ts +4 -7
  46. package/dist/types/withdraw/index.d.mts +2 -2
  47. package/dist/types/withdraw/index.d.ts +2 -2
  48. package/dist/types/withdraw/steps/withdraw-amount.d.mts +6 -3
  49. package/dist/types/withdraw/steps/withdraw-amount.d.ts +6 -3
  50. package/dist/types/withdraw/steps/withdraw-success.d.mts +8 -1
  51. package/dist/types/withdraw/steps/withdraw-success.d.ts +8 -1
  52. package/dist/types/withdraw/steps/withdraw-success.utils.d.mts +36 -0
  53. package/dist/types/withdraw/steps/withdraw-success.utils.d.ts +36 -0
  54. package/dist/types/withdraw/withdraw-modal.types.d.mts +25 -15
  55. package/dist/types/withdraw/withdraw-modal.types.d.ts +25 -15
  56. package/package.json +6 -5
package/dist/trading.js CHANGED
@@ -115,6 +115,7 @@ var import_dayjs = __toESM(require("dayjs"));
115
115
 
116
116
  // src/constants.ts
117
117
  var AGG_ROOT_CLASS_NAME = "agg-root";
118
+ var AGG_TERMS_OF_SERVICE_URL = "https://build.agg.market/terms-and-conditions";
118
119
 
119
120
  // src/shared/utils.ts
120
121
  var cn = (...values) => values.filter(Boolean).join(" ");
@@ -4343,6 +4344,125 @@ var import_react8 = require("react");
4343
4344
  var import_sdk13 = require("@agg-build/sdk");
4344
4345
  var import_dayjs2 = __toESM(require("dayjs"));
4345
4346
 
4347
+ // src/events/shared/venue-market-cluster.ts
4348
+ var toNonEmptyString = (value) => {
4349
+ if (typeof value !== "string") return void 0;
4350
+ const trimmedValue = value.trim();
4351
+ return trimmedValue ? trimmedValue : void 0;
4352
+ };
4353
+ var addGraphEdge = (adjacencyById, fromMarketId, toMarketId) => {
4354
+ var _a, _b;
4355
+ if (fromMarketId === toMarketId) return;
4356
+ const fromNeighbors = (_a = adjacencyById.get(fromMarketId)) != null ? _a : /* @__PURE__ */ new Set();
4357
+ fromNeighbors.add(toMarketId);
4358
+ adjacencyById.set(fromMarketId, fromNeighbors);
4359
+ const toNeighbors = (_b = adjacencyById.get(toMarketId)) != null ? _b : /* @__PURE__ */ new Set();
4360
+ toNeighbors.add(fromMarketId);
4361
+ adjacencyById.set(toMarketId, toNeighbors);
4362
+ };
4363
+ var dedupeSourceMarketsById = (markets) => {
4364
+ const seenMarketIds = /* @__PURE__ */ new Set();
4365
+ return markets.filter((market) => {
4366
+ if (seenMarketIds.has(market.id)) {
4367
+ return false;
4368
+ }
4369
+ seenMarketIds.add(market.id);
4370
+ return true;
4371
+ });
4372
+ };
4373
+ var collectDirectRelationMarkets = (market) => {
4374
+ var _a, _b, _c;
4375
+ const relationMarkets = [];
4376
+ const seenRelationIds = /* @__PURE__ */ new Set();
4377
+ const addRelationMarket = (relationMarket) => {
4378
+ if (!relationMarket) return;
4379
+ if (seenRelationIds.has(relationMarket.id)) return;
4380
+ seenRelationIds.add(relationMarket.id);
4381
+ relationMarkets.push(relationMarket);
4382
+ };
4383
+ for (const matchedVenueMarket of (_a = market.matchedVenueMarkets) != null ? _a : []) {
4384
+ addRelationMarket(matchedVenueMarket);
4385
+ }
4386
+ addRelationMarket(
4387
+ (_c = (_b = market.matchEntry) == null ? void 0 : _b.targetVenueMarket) != null ? _c : void 0
4388
+ );
4389
+ return relationMarkets;
4390
+ };
4391
+ var collectUniqueRelatedMarkets = (markets, sourceMarketsById) => {
4392
+ const relatedMarketsById = /* @__PURE__ */ new Map();
4393
+ for (const market of markets) {
4394
+ for (const relatedMarket of collectDirectRelationMarkets(market)) {
4395
+ if (sourceMarketsById.has(relatedMarket.id) || relatedMarketsById.has(relatedMarket.id)) {
4396
+ continue;
4397
+ }
4398
+ relatedMarketsById.set(relatedMarket.id, relatedMarket);
4399
+ }
4400
+ }
4401
+ return Array.from(relatedMarketsById.values());
4402
+ };
4403
+ var normalizeVenueMarketCluster = (markets, selectedMarketId) => {
4404
+ var _a, _b;
4405
+ if (markets.length === 0) return [];
4406
+ const orderedSourceMarkets = dedupeSourceMarketsById(markets);
4407
+ const sourceMarketsById = new Map(orderedSourceMarkets.map((market) => [market.id, market]));
4408
+ const orderedRelatedMarkets = collectUniqueRelatedMarkets(
4409
+ orderedSourceMarkets,
4410
+ sourceMarketsById
4411
+ );
4412
+ const relatedMarketsById = new Map(orderedRelatedMarkets.map((market) => [market.id, market]));
4413
+ const resolvedSelectedMarketId = toNonEmptyString(selectedMarketId);
4414
+ if (!resolvedSelectedMarketId) {
4415
+ return [...orderedSourceMarkets, ...orderedRelatedMarkets];
4416
+ }
4417
+ const adjacencyById = /* @__PURE__ */ new Map();
4418
+ for (const market of orderedSourceMarkets) {
4419
+ for (const relatedMarket of collectDirectRelationMarkets(market)) {
4420
+ addGraphEdge(adjacencyById, market.id, relatedMarket.id);
4421
+ }
4422
+ const targetVenueMarketId = toNonEmptyString((_a = market.matchEntry) == null ? void 0 : _a.targetVenueMarketId);
4423
+ if (targetVenueMarketId) {
4424
+ addGraphEdge(adjacencyById, market.id, targetVenueMarketId);
4425
+ }
4426
+ }
4427
+ if (!sourceMarketsById.has(resolvedSelectedMarketId) && !relatedMarketsById.has(resolvedSelectedMarketId)) {
4428
+ return [];
4429
+ }
4430
+ const visitedMarketIds = /* @__PURE__ */ new Set();
4431
+ const queue = [resolvedSelectedMarketId];
4432
+ while (queue.length > 0) {
4433
+ const currentMarketId = queue.shift();
4434
+ if (!currentMarketId || visitedMarketIds.has(currentMarketId)) {
4435
+ continue;
4436
+ }
4437
+ visitedMarketIds.add(currentMarketId);
4438
+ const neighborMarketIds = adjacencyById.get(currentMarketId);
4439
+ for (const neighborMarketId of Array.from(neighborMarketIds != null ? neighborMarketIds : /* @__PURE__ */ new Set())) {
4440
+ if (!visitedMarketIds.has(neighborMarketId)) {
4441
+ queue.push(neighborMarketId);
4442
+ }
4443
+ }
4444
+ }
4445
+ const orderedClusterMarkets = [];
4446
+ const appendedMarketIds = /* @__PURE__ */ new Set();
4447
+ const appendMarket = (market) => {
4448
+ if (!market) return;
4449
+ if (!visitedMarketIds.has(market.id)) return;
4450
+ if (appendedMarketIds.has(market.id)) return;
4451
+ appendedMarketIds.add(market.id);
4452
+ orderedClusterMarkets.push(market);
4453
+ };
4454
+ appendMarket(
4455
+ (_b = sourceMarketsById.get(resolvedSelectedMarketId)) != null ? _b : relatedMarketsById.get(resolvedSelectedMarketId)
4456
+ );
4457
+ for (const market of orderedSourceMarkets) {
4458
+ appendMarket(market);
4459
+ }
4460
+ for (const market of orderedRelatedMarkets) {
4461
+ appendMarket(market);
4462
+ }
4463
+ return orderedClusterMarkets;
4464
+ };
4465
+
4346
4466
  // src/events/orderbook/orderbook.utils.ts
4347
4467
  var import_sdk12 = require("@agg-build/sdk");
4348
4468
  var formatProbabilityCents = (value) => {
@@ -4378,7 +4498,7 @@ var import_hooks22 = require("@agg-build/hooks");
4378
4498
  var import_jsx_runtime98 = require("react/jsx-runtime");
4379
4499
  var GeoBlockBanner = ({
4380
4500
  venue,
4381
- termsUrl = "#",
4501
+ termsUrl = AGG_TERMS_OF_SERVICE_URL,
4382
4502
  className
4383
4503
  }) => {
4384
4504
  const labels = (0, import_hooks22.useLabels)();
@@ -4539,8 +4659,8 @@ var Button = (_a) => {
4539
4659
  Button.displayName = "Button";
4540
4660
 
4541
4661
  // src/primitives/currency-input/index.tsx
4542
- var import_react4 = require("react");
4543
4662
  var import_hooks24 = require("@agg-build/hooks");
4663
+ var import_react4 = require("react");
4544
4664
 
4545
4665
  // src/primitives/currency-input/currency-input.constants.ts
4546
4666
  var CURRENCY_INPUT_DEFAULT_PREFIX = "$";
@@ -4651,13 +4771,13 @@ var CurrencyInput = ({
4651
4771
  general: { locale }
4652
4772
  } = (0, import_hooks24.useSdkUiConfig)();
4653
4773
  const parts = (0, import_react4.useMemo)(() => resolveCurrencyInputFormatParts(locale), [locale]);
4654
- const [raw, setRaw] = (0, import_react4.useState)(() => value > 0 ? String(value) : "");
4774
+ const [raw, setRaw] = (0, import_react4.useState)(() => value >= 0 ? String(value) : "");
4655
4775
  const isEditingRef = (0, import_react4.useRef)(false);
4656
4776
  const inputRef = (0, import_react4.useRef)(null);
4657
4777
  const caretPosRef = (0, import_react4.useRef)(null);
4658
4778
  (0, import_react4.useEffect)(() => {
4659
4779
  if (!isEditingRef.current) {
4660
- setRaw(value > 0 ? String(value) : "");
4780
+ setRaw(value >= 0 ? String(value) : "");
4661
4781
  }
4662
4782
  }, [value]);
4663
4783
  const numericDisplay = formatCurrencyInputRawValue(raw, locale, decimalPlaces, parts);
@@ -4755,7 +4875,7 @@ var CurrencyInput = ({
4755
4875
  isEditingRef.current = false;
4756
4876
  const numericValue = parseFloat(raw);
4757
4877
  const nextValue = isNaN(numericValue) ? min : clampCurrencyInputValue(numericValue, min, max);
4758
- setRaw(nextValue > 0 ? String(nextValue) : "");
4878
+ setRaw(nextValue >= 0 ? String(nextValue) : "");
4759
4879
  onChange == null ? void 0 : onChange(nextValue);
4760
4880
  onBlur == null ? void 0 : onBlur();
4761
4881
  };
@@ -5477,7 +5597,9 @@ var useKalshiKycFlow = ({
5477
5597
  };
5478
5598
 
5479
5599
  // src/trading/place-order/index.place-order.constants.ts
5600
+ var PLACE_ORDER_DEFAULT_AMOUNT = 20;
5480
5601
  var MIN_BUY_ORDER_AMOUNT = 1;
5602
+ var MIN_SELL_ORDER_SHARES = 1;
5481
5603
  var DEFAULT_SLIPPAGE_VALUE = "0.5";
5482
5604
  var HIGH_SLIPPAGE_THRESHOLD = 10;
5483
5605
  var PLACE_ORDER_ROUTE_COLLAPSED_CARD_COUNT = 3;
@@ -5688,10 +5810,12 @@ var resolvePlaceOrderQuoteStatus = ({
5688
5810
  var buildLiveRouteCards = ({
5689
5811
  labels,
5690
5812
  quoteData,
5691
- tradeSide
5813
+ tradeSide,
5814
+ eventVenues = []
5692
5815
  }) => {
5693
5816
  var _a, _b, _c;
5694
5817
  const geoBlockedVenues = extractGeoBlockedVenues(quoteData.warnings);
5818
+ const eventVenueSet = new Set(eventVenues);
5695
5819
  const primaryResult = resolvePlaceOrderQuoteResult({ labels, quoteData, tradeSide });
5696
5820
  const primaryVenue = (_a = quoteData.fills[0]) == null ? void 0 : _a.venue;
5697
5821
  const parsedPrimaryVenue = parseVenue(primaryVenue);
@@ -5789,24 +5913,26 @@ var buildLiveRouteCards = ({
5789
5913
  });
5790
5914
  const baseCards = primaryCard ? [primaryCard, ...soloCards] : soloCards;
5791
5915
  const coveredVenueStrings = new Set(
5792
- [...baseCards, ...unavailableCards].flatMap((c) => c.venue ? [c.venue] : [])
5916
+ [...baseCards, ...unavailableCards].flatMap((card) => card.venue ? [card.venue] : [])
5793
5917
  );
5794
- const geoBlockedOnlyCards = [...geoBlockedVenues].filter((venueStr) => !coveredVenueStrings.has(venueStr)).map((venueStr) => {
5795
- const parsedVenue = parseVenue(venueStr);
5796
- const venue = parsedVenue.success ? parsedVenue.data : void 0;
5797
- return {
5798
- id: `live-geo-blocked-${venueStr}`,
5799
- hint: "",
5800
- kind: "venue",
5801
- label: getTradingVenueLabel(venue),
5802
- numericValue: 0,
5803
- quoteData,
5804
- value: "",
5805
- venue,
5806
- isUnavailable: true
5807
- };
5808
- });
5809
- return [...baseCards, ...unavailableCards, ...geoBlockedOnlyCards];
5918
+ const parsedKalshiVenue = parseVenue("kalshi");
5919
+ const kalshiVenue = parsedKalshiVenue.success ? parsedKalshiVenue.data : void 0;
5920
+ const warningOnlyUnavailableCards = geoBlockedVenues.has("kalshi") && !!kalshiVenue && eventVenueSet.has(kalshiVenue) && !coveredVenueStrings.has("kalshi") ? (() => {
5921
+ return [
5922
+ {
5923
+ id: "live-geo-blocked-kalshi",
5924
+ hint: "",
5925
+ kind: "venue",
5926
+ label: getTradingVenueLabel(kalshiVenue),
5927
+ numericValue: 0,
5928
+ quoteData,
5929
+ value: "",
5930
+ venue: kalshiVenue,
5931
+ isUnavailable: true
5932
+ }
5933
+ ];
5934
+ })() : [];
5935
+ return [...baseCards, ...unavailableCards, ...warningOnlyUnavailableCards];
5810
5936
  };
5811
5937
  var resolveExecutionVenueFromQuote = (quoteData) => {
5812
5938
  if (!(quoteData == null ? void 0 : quoteData.fills.length)) return void 0;
@@ -5968,14 +6094,18 @@ var groupPlaceOrderFailureSteps = (steps) => {
5968
6094
  };
5969
6095
  var buildPlaceOrderExecutionStepGroupsFromFailureSummary = ({
5970
6096
  labels,
5971
- summary
6097
+ summary,
6098
+ // When the partial-fill steps are rendered inside the success view (auto
6099
+ // 95%+ skip or after the user clicks Skip), surface them as completed
6100
+ // checks instead of warnings — the order is being treated as complete.
6101
+ treatPartialAsComplete = false
5972
6102
  }) => {
5973
6103
  var _a;
5974
6104
  const failureStepGroups = groupPlaceOrderFailureSteps(summary.steps).map(
5975
6105
  (group) => group.map((step) => ({
5976
6106
  id: step.id,
5977
6107
  label: step.label,
5978
- tone: step.tone,
6108
+ tone: treatPartialAsComplete && (step.tone === "warning" || step.tone === "error") ? "complete" : step.tone,
5979
6109
  venue: step.venue
5980
6110
  }))
5981
6111
  );
@@ -6436,10 +6566,6 @@ var PlaceOrderFailureView = ({
6436
6566
  ] }),
6437
6567
  summary.eventSubtitle ? /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("p", { className: "text-agg-base leading-agg-6 text-agg-foreground", children: summary.eventSubtitle }) : null
6438
6568
  ] }),
6439
- /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)("div", { className: "flex min-h-12 w-full items-center justify-center gap-3 rounded-agg-full agg-bg-brand px-8 text-agg-base font-agg-bold leading-agg-6 text-agg-on-primary", children: [
6440
- /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(LoadingIcon, { size: "small", className: "text-agg-on-primary!" }),
6441
- /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("span", { children: summary.actionLabel })
6442
- ] }),
6443
6569
  /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("div", { className: "flex flex-col gap-4", children: failureStepGroups.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("div", { className: "flex flex-col gap-2", children: group.map((step) => renderPartialFailureStep(step)) }, `failure-group-${groupIndex}`)) }),
6444
6570
  /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("div", { className: "flex items-center gap-4", children: summary.actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
6445
6571
  Button,
@@ -6544,19 +6670,6 @@ var PlaceOrderSuccessView = ({
6544
6670
  ] }),
6545
6671
  summary.eventDateLabel ? /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("p", { className: "text-agg-base leading-agg-6 text-agg-foreground", children: summary.eventDateLabel }) : null
6546
6672
  ] }) : null,
6547
- /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
6548
- "div",
6549
- {
6550
- className: cn(
6551
- "agg-order-success-pill flex min-h-12 w-full items-center justify-center gap-2 rounded-agg-full px-6 py-2.5 text-center text-agg-base font-agg-bold leading-agg-6 text-agg-on-primary",
6552
- resolvedActionTone === "positive" ? "bg-agg-success" : "bg-agg-error"
6553
- ),
6554
- children: [
6555
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Icon, { name: "success-check", size: "small", className: "h-4 w-4 shrink-0 text-current" }),
6556
- /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { children: resolvedActionLabel })
6557
- ]
6558
- }
6559
- ),
6560
6673
  executionStepGroups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "agg-order-success-timeline flex flex-col gap-2", children: [
6561
6674
  /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
6562
6675
  "div",
@@ -6646,6 +6759,19 @@ var PlaceOrderSuccessView = ({
6646
6759
  /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("p", { className: "min-w-0 flex-1", children: finalStep.label })
6647
6760
  ] }) : null
6648
6761
  ] }) : null,
6762
+ /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
6763
+ "div",
6764
+ {
6765
+ className: cn(
6766
+ "agg-order-success-pill flex min-h-12 w-full items-center justify-center gap-2 rounded-agg-full px-6 py-2.5 text-center text-agg-base font-agg-bold leading-agg-6 text-agg-on-primary",
6767
+ resolvedActionTone === "positive" ? "bg-agg-success" : "bg-agg-error"
6768
+ ),
6769
+ children: [
6770
+ /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Icon, { name: "success-check", size: "small", className: "h-4 w-4 shrink-0 text-current" }),
6771
+ /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("span", { children: resolvedActionLabel })
6772
+ ]
6773
+ }
6774
+ ),
6649
6775
  /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)("div", { className: "agg-order-success-summary flex items-center justify-between gap-4", children: [
6650
6776
  /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("p", { className: "text-agg-base font-agg-bold leading-agg-6 text-agg-foreground", children: tradingLabels.amount(resolvedTradeSide) }),
6651
6777
  /* @__PURE__ */ (0, import_jsx_runtime106.jsx)("p", { className: "text-right text-[30px] font-agg-bold leading-[36px] text-agg-foreground", children: resolvedAmountLabel })
@@ -7355,7 +7481,7 @@ var InitiateKycButton = ({ label, onOpen }) => /* @__PURE__ */ (0, import_jsx_ru
7355
7481
  size: "large",
7356
7482
  variant: "primary",
7357
7483
  onClick: onOpen,
7358
- className: "agg-order-kyc-action h-12 w-full px-8 text-agg-base leading-agg-6 shadow-none mb-3",
7484
+ className: "agg-order-kyc-action h-12 w-full px-8 text-agg-base leading-agg-6 shadow-none",
7359
7485
  children: label
7360
7486
  }
7361
7487
  );
@@ -7552,6 +7678,14 @@ var PlaceOrder = ({
7552
7678
  const livePrices = (0, import_hooks28.useLiveOutcomePrices)(
7553
7679
  scopedSelectedMarket ? [scopedSelectedMarket] : void 0
7554
7680
  );
7681
+ const midpointsClusterMarkets = (0, import_react8.useMemo)(() => {
7682
+ if (!scopedSelectedMarket) return [];
7683
+ return normalizeVenueMarketCluster(
7684
+ resolvedEventTradingState.displayMarkets,
7685
+ scopedSelectedMarket.id
7686
+ );
7687
+ }, [resolvedEventTradingState.displayMarkets, scopedSelectedMarket]);
7688
+ const { prices: clusterMidpoints } = (0, import_hooks28.useMidpoints)(midpointsClusterMarkets);
7555
7689
  const outcomes = (0, import_react8.useMemo)(
7556
7690
  () => mapVenueMarketOutcomesToPlaceOrderOutcomes(scopedSelectedMarket == null ? void 0 : scopedSelectedMarket.venueMarketOutcomes),
7557
7691
  [scopedSelectedMarket == null ? void 0 : scopedSelectedMarket.venueMarketOutcomes]
@@ -7559,7 +7693,8 @@ var PlaceOrder = ({
7559
7693
  const buyTabRef = (0, import_react8.useRef)(null);
7560
7694
  const sellTabRef = (0, import_react8.useRef)(null);
7561
7695
  const [internalTab, setInternalTab] = (0, import_react8.useState)("buy");
7562
- const [internalAmount, setInternalAmount] = (0, import_react8.useState)(0);
7696
+ const [internalAmount, setInternalAmount] = (0, import_react8.useState)(PLACE_ORDER_DEFAULT_AMOUNT);
7697
+ const [sellFilledForOutcomeId, setSellFilledForOutcomeId] = (0, import_react8.useState)(null);
7563
7698
  const [internalSlippage, setInternalSlippage] = (0, import_react8.useState)(DEFAULT_SLIPPAGE_VALUE);
7564
7699
  const [isRoutesExpanded, setIsRoutesExpanded] = (0, import_react8.useState)(false);
7565
7700
  const [selectedRouteCardId, setSelectedRouteCardId] = (0, import_react8.useState)(null);
@@ -7629,13 +7764,28 @@ var PlaceOrder = ({
7629
7764
  if (!Number.isFinite(currentSellableShares)) return 0;
7630
7765
  return currentSellableShares;
7631
7766
  }, [currentSellableShares]);
7767
+ (0, import_react8.useEffect)(() => {
7768
+ if (!isSell || isCurrentSellableSharesLoading) return;
7769
+ if (scopedSelectedOutcomeId === sellFilledForOutcomeId) return;
7770
+ setInternalAmount(displayedCurrentSellableShares);
7771
+ setSellFilledForOutcomeId(scopedSelectedOutcomeId != null ? scopedSelectedOutcomeId : null);
7772
+ onAmountChange == null ? void 0 : onAmountChange(displayedCurrentSellableShares);
7773
+ }, [
7774
+ isSell,
7775
+ isCurrentSellableSharesLoading,
7776
+ scopedSelectedOutcomeId,
7777
+ sellFilledForOutcomeId,
7778
+ displayedCurrentSellableShares,
7779
+ onAmountChange
7780
+ ]);
7632
7781
  const routeCards = (0, import_react8.useMemo)(
7633
7782
  () => smartRoute.data ? buildLiveRouteCards({
7783
+ eventVenues: scopedSelectedEvent == null ? void 0 : scopedSelectedEvent.venueMarkets.map((market) => market.venue),
7634
7784
  labels: tradingLabels,
7635
7785
  quoteData: smartRoute.data,
7636
7786
  tradeSide: isSell ? "sell" : "buy"
7637
7787
  }) : [],
7638
- [smartRoute.data, tradingLabels, isSell]
7788
+ [smartRoute.data, tradingLabels, isSell, scopedSelectedEvent == null ? void 0 : scopedSelectedEvent.venueMarkets]
7639
7789
  );
7640
7790
  const resolvedSelectedRouteCardId = (0, import_react8.useMemo)(() => {
7641
7791
  var _a2, _b2, _c2, _d2;
@@ -7749,7 +7899,10 @@ var PlaceOrder = ({
7749
7899
  potentialReturnLabel,
7750
7900
  executionStepGroups: (partialFillSummary == null ? void 0 : partialFillSummary.kind) === "partial_fill" ? buildPlaceOrderExecutionStepGroupsFromFailureSummary({
7751
7901
  labels: tradingLabels,
7752
- summary: partialFillSummary
7902
+ summary: partialFillSummary,
7903
+ // Inside the success view (auto-95% skip or user-clicked
7904
+ // Skip), surface partial-fill steps as completed checks.
7905
+ treatPartialAsComplete: true
7753
7906
  }) : buildPlaceOrderExecutionStepGroups({
7754
7907
  labels: tradingLabels,
7755
7908
  orderId: orderId != null ? orderId : (_h2 = executionProgress.submittedOrders[0]) == null ? void 0 : _h2.orderId,
@@ -7922,13 +8075,24 @@ var PlaceOrder = ({
7922
8075
  }, [smartRoute.error, tradingLabels.quoteUnavailable]);
7923
8076
  const progressActionLabel = (internalTab === "buy" ? tradingLabels.buyingOutcome(selectedOutcomeLabel) : tradingLabels.sellingOutcome(selectedOutcomeLabel)).trim();
7924
8077
  const canRetryRemaining = (0, import_react8.useMemo)(() => {
7925
- if (isSell) return true;
7926
8078
  const remaining = getPlaceOrderUnfilledRemaining({
7927
8079
  originalAmount: internalAmount,
7928
8080
  terminalOrderEvents: executionProgress.terminalOrderEvents
7929
8081
  });
7930
- return remaining >= MIN_BUY_ORDER_AMOUNT;
8082
+ const minRemainder = isSell ? MIN_SELL_ORDER_SHARES : MIN_BUY_ORDER_AMOUNT;
8083
+ return remaining >= minRemainder;
7931
8084
  }, [executionProgress.terminalOrderEvents, internalAmount, isSell]);
8085
+ const PARTIAL_FILL_AUTO_SUCCESS_THRESHOLD = 0.95;
8086
+ const shouldAutoSkipPartialFill = (0, import_react8.useMemo)(() => {
8087
+ if (internalAmount <= 0) return false;
8088
+ const remaining = getPlaceOrderUnfilledRemaining({
8089
+ originalAmount: internalAmount,
8090
+ terminalOrderEvents: executionProgress.terminalOrderEvents
8091
+ });
8092
+ const filled = Math.max(0, internalAmount - remaining);
8093
+ const fillRatio = filled / internalAmount;
8094
+ return fillRatio >= PARTIAL_FILL_AUTO_SUCCESS_THRESHOLD || !canRetryRemaining;
8095
+ }, [canRetryRemaining, executionProgress.terminalOrderEvents, internalAmount]);
7932
8096
  const failureSummary = (0, import_react8.useMemo)(() => {
7933
8097
  var _a2;
7934
8098
  if ((submissionProgressState == null ? void 0 : submissionProgressState.phase) !== "failed") return void 0;
@@ -7985,6 +8149,7 @@ var PlaceOrder = ({
7985
8149
  setSubmissionProgressState(null);
7986
8150
  setSubmissionFeedback(null);
7987
8151
  setInternalAmount(0);
8152
+ setSellFilledForOutcomeId(null);
7988
8153
  setInternalSlippage(DEFAULT_SLIPPAGE_VALUE);
7989
8154
  setInternalTab("buy");
7990
8155
  setIsRoutesExpanded(false);
@@ -8048,6 +8213,17 @@ var PlaceOrder = ({
8048
8213
  failureSummary,
8049
8214
  selectedRouteCard == null ? void 0 : selectedRouteCard.quoteData
8050
8215
  ]);
8216
+ (0, import_react8.useEffect)(() => {
8217
+ if ((submissionProgressState == null ? void 0 : submissionProgressState.phase) !== "failed") return;
8218
+ if ((failureSummary == null ? void 0 : failureSummary.kind) !== "partial_fill") return;
8219
+ if (!shouldAutoSkipPartialFill) return;
8220
+ handleSkipToSuccess();
8221
+ }, [
8222
+ failureSummary == null ? void 0 : failureSummary.kind,
8223
+ handleSkipToSuccess,
8224
+ shouldAutoSkipPartialFill,
8225
+ submissionProgressState == null ? void 0 : submissionProgressState.phase
8226
+ ]);
8051
8227
  const handleRetrySubmission = (0, import_react8.useCallback)(() => __async(null, null, function* () {
8052
8228
  var _a2, _b2, _c2;
8053
8229
  if (!orderEligibility.canPlaceOrder) {
@@ -8090,6 +8266,7 @@ var PlaceOrder = ({
8090
8266
  throw new Error(tradingLabels.quoteUnavailable);
8091
8267
  }
8092
8268
  const refreshedCards = buildLiveRouteCards({
8269
+ eventVenues: scopedSelectedEvent == null ? void 0 : scopedSelectedEvent.venueMarkets.map((market) => market.venue),
8093
8270
  labels: tradingLabels,
8094
8271
  quoteData: refetchedQuoteData,
8095
8272
  tradeSide: internalTab
@@ -8126,6 +8303,7 @@ var PlaceOrder = ({
8126
8303
  isSell,
8127
8304
  onAmountChange,
8128
8305
  orderEligibility.canPlaceOrder,
8306
+ scopedSelectedEvent == null ? void 0 : scopedSelectedEvent.venueMarkets,
8129
8307
  scopedSelectedOutcomeId,
8130
8308
  selectedRouteCardId,
8131
8309
  smartRoute,
@@ -8184,11 +8362,20 @@ var PlaceOrder = ({
8184
8362
  const handleTabChange = (nextTab) => {
8185
8363
  if (!orderEligibility.canPlaceOrder) return;
8186
8364
  setInternalTab(nextTab);
8365
+ if (nextTab === "sell") {
8366
+ setInternalAmount(0);
8367
+ setSellFilledForOutcomeId(null);
8368
+ onAmountChange == null ? void 0 : onAmountChange(0);
8369
+ } else {
8370
+ setInternalAmount(PLACE_ORDER_DEFAULT_AMOUNT);
8371
+ onAmountChange == null ? void 0 : onAmountChange(PLACE_ORDER_DEFAULT_AMOUNT);
8372
+ }
8187
8373
  onTabChange == null ? void 0 : onTabChange(nextTab);
8188
8374
  };
8189
8375
  const handleOutcomeChange = (nextOutcomeId) => {
8190
8376
  if (isResolvedOutcomeCtaLocked) return;
8191
8377
  if (!orderEligibility.canPlaceOrder) return;
8378
+ if (isSell) setSellFilledForOutcomeId(null);
8192
8379
  onOutcomeChange == null ? void 0 : onOutcomeChange(nextOutcomeId);
8193
8380
  tradingContext == null ? void 0 : tradingContext.selectOutcome(nextOutcomeId);
8194
8381
  };
@@ -8196,6 +8383,7 @@ var PlaceOrder = ({
8196
8383
  if (!orderEligibility.canPlaceOrder) return;
8197
8384
  const resolvedNextValue = nextValue != null ? nextValue : 0;
8198
8385
  setInternalAmount(resolvedNextValue);
8386
+ if (isSell) setSellFilledForOutcomeId(scopedSelectedOutcomeId != null ? scopedSelectedOutcomeId : null);
8199
8387
  onAmountChange == null ? void 0 : onAmountChange(resolvedNextValue);
8200
8388
  };
8201
8389
  const handleFillSellableShares = () => {
@@ -8335,11 +8523,11 @@ var PlaceOrder = ({
8335
8523
  }
8336
8524
  ),
8337
8525
  outcomes.length >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("div", { className: "agg-outcomes flex w-full max-w-full gap-2", children: outcomes.map((outcome, index) => {
8338
- var _a2, _b2;
8339
- const price = (_a2 = livePrices.get(outcome.id)) != null ? _a2 : outcome.price;
8526
+ var _a2, _b2, _c2;
8527
+ const price = (_b2 = (_a2 = clusterMidpoints.get(outcome.id)) != null ? _a2 : livePrices.get(outcome.id)) != null ? _b2 : outcome.price;
8340
8528
  const isActive = outcome.id === scopedSelectedOutcomeId;
8341
8529
  const isPositive = resolveIsPositiveOutcome(outcome, index);
8342
- const displayLabel = ((_b2 = outcome.title) == null ? void 0 : _b2.trim()) || outcome.label;
8530
+ const displayLabel = ((_c2 = outcome.title) == null ? void 0 : _c2.trim()) || outcome.label;
8343
8531
  return /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(
8344
8532
  "button",
8345
8533
  {
@@ -8446,7 +8634,7 @@ var PlaceOrder = ({
8446
8634
  card,
8447
8635
  enableAnimations,
8448
8636
  isExpanded: card.kind === "split" ? isSplitDetailOpen : isRoutesExpanded,
8449
- isSelected: card.id === resolvedSelectedRouteCardId,
8637
+ isSelected: !card.isUnavailable && card.id === resolvedSelectedRouteCardId,
8450
8638
  onSelect: handleRouteCardSelect,
8451
8639
  tradingLabels
8452
8640
  }) }, card.id);
@@ -8577,7 +8765,7 @@ var PlaceOrder = ({
8577
8765
  {
8578
8766
  size: "large",
8579
8767
  variant: hasEnteredAmount && !shouldShowGeoBlockBanner ? "primary" : "secondary",
8580
- className: "agg-order-submit h-12 w-full px-8 text-agg-base leading-agg-6 shadow-none mb-3",
8768
+ className: "agg-order-submit h-12 w-full px-8 text-agg-base leading-agg-6 shadow-none",
8581
8769
  disabled: isActionDisabled || shouldShowGeoBlockBanner,
8582
8770
  isLoading: isActionLoading,
8583
8771
  "aria-label": actionLabel,
@@ -8587,7 +8775,7 @@ var PlaceOrder = ({
8587
8775
  children: actionLabel
8588
8776
  }
8589
8777
  ),
8590
- shouldShowGeoBlockBanner ? /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(GeoBlockBanner, { venue: geoBlockVenueLabel }) : null,
8778
+ shouldShowGeoBlockBanner ? /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(GeoBlockBanner, { venue: geoBlockVenueLabel, termsUrl: AGG_TERMS_OF_SERVICE_URL }) : null,
8591
8779
  !shouldShowGeoBlockBanner ? /* @__PURE__ */ (0, import_jsx_runtime107.jsx)("p", { className: "agg-order-disclaimer text-center text-agg-xs leading-agg-4 text-agg-muted-foreground", children: tradingLabels.disclaimer }) : null
8592
8780
  ]
8593
8781
  }
package/dist/trading.mjs CHANGED
@@ -19,12 +19,12 @@ import {
19
19
  mapVenueMarketsToSettlementSections,
20
20
  parseAmount,
21
21
  parseVenue
22
- } from "./chunk-CGNDMLQL.mjs";
22
+ } from "./chunk-LPNZOX3E.mjs";
23
23
  import {
24
24
  useEventTradingContext
25
- } from "./chunk-C7T56TJR.mjs";
26
- import "./chunk-2QJXRRYP.mjs";
27
- import "./chunk-6NS7D73S.mjs";
25
+ } from "./chunk-HD6HBTK2.mjs";
26
+ import "./chunk-43K4PFBC.mjs";
27
+ import "./chunk-YZNO6IUD.mjs";
28
28
  export {
29
29
  PlaceOrder,
30
30
  PlaceOrderFailureView,
@@ -1,7 +1,9 @@
1
1
  export interface CardPaymentPendingStepProps {
2
2
  providerName: string;
3
+ /** Optional platform deposit address shown so users can paste it into the provider's checkout. */
4
+ walletAddress?: string;
3
5
  onClose: () => void;
4
6
  onViewActivity: () => void;
5
7
  onChooseAnotherProvider: () => void;
6
8
  }
7
- export declare const CardPaymentPendingStep: ({ providerName, onClose, onViewActivity, onChooseAnotherProvider, }: CardPaymentPendingStepProps) => JSX.Element;
9
+ export declare const CardPaymentPendingStep: ({ providerName, walletAddress, onClose, onViewActivity, onChooseAnotherProvider, }: CardPaymentPendingStepProps) => JSX.Element;
@@ -1,7 +1,9 @@
1
1
  export interface CardPaymentPendingStepProps {
2
2
  providerName: string;
3
+ /** Optional platform deposit address shown so users can paste it into the provider's checkout. */
4
+ walletAddress?: string;
3
5
  onClose: () => void;
4
6
  onViewActivity: () => void;
5
7
  onChooseAnotherProvider: () => void;
6
8
  }
7
- export declare const CardPaymentPendingStep: ({ providerName, onClose, onViewActivity, onChooseAnotherProvider, }: CardPaymentPendingStepProps) => JSX.Element;
9
+ export declare const CardPaymentPendingStep: ({ providerName, walletAddress, onClose, onViewActivity, onChooseAnotherProvider, }: CardPaymentPendingStepProps) => JSX.Element;
@@ -13,7 +13,7 @@ export interface CryptoTransferStepProps {
13
13
  onBack: () => void;
14
14
  onTokenChange: (token: string) => void;
15
15
  onNetworkChange: (network: string) => void;
16
- onCopyAddress: () => void;
16
+ onCopyAddress?: () => void;
17
17
  onDone: () => void;
18
18
  }
19
19
  export declare const CryptoTransferStep: ({ tokenOptions, networkOptions, selectedToken, selectedNetwork, depositAddress, minDeposit, feeEstimate, eta, isLoadingAddress, addressError, onBack, onTokenChange, onNetworkChange, onCopyAddress, onDone, }: CryptoTransferStepProps) => JSX.Element;
@@ -13,7 +13,7 @@ export interface CryptoTransferStepProps {
13
13
  onBack: () => void;
14
14
  onTokenChange: (token: string) => void;
15
15
  onNetworkChange: (network: string) => void;
16
- onCopyAddress: () => void;
16
+ onCopyAddress?: () => void;
17
17
  onDone: () => void;
18
18
  }
19
19
  export declare const CryptoTransferStep: ({ tokenOptions, networkOptions, selectedToken, selectedNetwork, depositAddress, minDeposit, feeEstimate, eta, isLoadingAddress, addressError, onBack, onTokenChange, onNetworkChange, onCopyAddress, onDone, }: CryptoTransferStepProps) => JSX.Element;
@@ -15,6 +15,26 @@ export type EventListItemVisibleOutcome = {
15
15
  title: string;
16
16
  };
17
17
  export declare const resolveVisibleOutcomes: (venueMarkets: VenueMarket[]) => EventListItemVisibleOutcome[];
18
+ /**
19
+ * Dedupe venue markets while collapsing matched-cluster siblings to a single
20
+ * row. Walks left-to-right; for each market we see, mark its own id AND every
21
+ * `matchedVenueMarkets[].id` as "seen", and skip any later row whose own id
22
+ * already appears in the seen set.
23
+ *
24
+ * Why cluster-aware (not just id): the home card preview and the load-more
25
+ * (`/venue-markets`) endpoint can pick different cluster representatives for
26
+ * the same matched group (one keeps the highest-volume venue, the other keeps
27
+ * the canonical cluster target). When the EventItem component merges the two
28
+ * lists for cross-page rendering, a naive id-only dedup leaves both rows in
29
+ * place and the user sees the same logical market (e.g. "Bayern Munich")
30
+ * twice — once per representative. Tracking sibling ids closes that gap
31
+ * without needing the backend to expose `matchEntry.targetVenueMarketId` on
32
+ * every list shape.
33
+ *
34
+ * Order semantics: first occurrence wins per cluster. Callers should pass
35
+ * the more authoritative list first (e.g. `[...allVenueMarkets, ...lazyLoaded]`
36
+ * if the home-card row is preferred, swap if the lazy-loaded row is preferred).
37
+ */
18
38
  export declare const dedupeVenueMarketsById: (venueMarkets: VenueMarket[]) => VenueMarket[];
19
39
  /** Returns the outcome's display text. Prefers `title` (the descriptive
20
40
  * human-readable text — e.g. "Trump Wins") when present and non-empty,
@@ -42,5 +62,42 @@ type DisplayVolumeMarket = {
42
62
  targetVenueMarket?: DisplayVolumeMarket | null;
43
63
  } | null | undefined;
44
64
  };
65
+ export type BestMidpointResult = {
66
+ /** Raw midpoint value (0–1 range, not yet clamped). */
67
+ midpoint: number;
68
+ venueMarketId: string;
69
+ venue: string;
70
+ };
71
+ /**
72
+ * Builds a venue-market-id → spread lookup from the raw midpoint rows returned
73
+ * by `useVenueMarketMidpoints`. Matched sibling rows are included in the map;
74
+ * first-write wins to match the same priority as `midpointsByVenueMarketId`.
75
+ */
76
+ export declare function buildSpreadByVenueMarketId(midpointRows: ReadonlyArray<{
77
+ venueMarketId: string;
78
+ spread: number | null;
79
+ matched: ReadonlyArray<{
80
+ venueMarketId: string;
81
+ spread: number | null;
82
+ }>;
83
+ }>): Map<string, number | null>;
84
+ /**
85
+ * Returns the best available midpoint across a market and its matched venue
86
+ * markets. Candidates are collected in declaration order (primary first, then
87
+ * matched in array order) and deduplicated by ID. Among valid (non-null)
88
+ * midpoints, the candidate with the **lowest** YES midpoint wins — this gives
89
+ * the buyer the best available price across venues.
90
+ *
91
+ * The `spreadByVenueMarketId` parameter is accepted for call-site compatibility
92
+ * but is no longer used in the selection logic.
93
+ */
94
+ export declare function resolveBestMidpointForMarket(market: {
95
+ id: string;
96
+ venue: string;
97
+ matchedVenueMarkets?: ReadonlyArray<{
98
+ id: string;
99
+ venue: string;
100
+ }> | null;
101
+ }, midpointsByVenueMarketId: Map<string, number | null>, spreadByVenueMarketId?: Map<string, number | null>): BestMidpointResult | undefined;
45
102
  export declare const resolveDisplayVolume: (eventVolume: number | null | undefined, venueMarkets: ReadonlyArray<DisplayVolumeMarket>) => number | undefined;
46
103
  export {};