@agg-build/ui 2.5.2 → 2.6.0

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.
@@ -449,64 +449,15 @@ var SPORTS_LEGACY_PROP_CATEGORY_KEY_SET = /* @__PURE__ */ new Set([
449
449
  "assists",
450
450
  "shots"
451
451
  ]);
452
- var SPORTS_CATEGORY_OVERRIDE_TAB_KEY_SET = /* @__PURE__ */ new Set([
453
- "exact_score",
454
- ...SPORTS_LEGACY_PROP_CATEGORY_KEY_SET
455
- ]);
456
- var SPORTS_MARKET_GROUP_TO_TAB = {
457
- game_lines: "game_lines",
458
- spreads: "game_lines",
459
- totals: "game_lines",
460
- period_lines: "halves",
461
- exact_score: "exact_score",
462
- team_props: "other",
463
- player_props: "other",
464
- specials: "other",
465
- completion: "other",
466
- other: "other"
467
- };
468
- var SPORTS_SECTION_TAB_DEFINITIONS = [
469
- {
470
- key: "game_lines",
471
- // NO sportsMarketType filter: spread/total markets on some sports (e.g.
472
- // esports "Map 1 Rounds Handicap"/"Total Rounds") are sportsMarketType="other"
473
- // with marketGroup=spreads/totals; AND-ing the game-line types would drop them
474
- // and leave only the moneyline. marketGroup already scopes the tab. (Safe now
475
- // that classify_market no longer mis-groups prop/to-score-first/extra-innings
476
- // markets into game_lines/totals — see agg-venue-core market_group.rs.)
477
- marketGroup: ["game_lines", "spreads", "totals"]
478
- // No server-side period filter: many regular-time game-line markets arrive
479
- // with period=null (moneyline sides, team totals, BTTS), and a
480
- // period=["full"] filter would drop them server-side. The Game Lines tab
481
- // narrows to the regular-time group client-side instead (see SportsMarkets).
482
- },
483
- { key: "exact_score", marketGroup: ["exact_score"] },
484
- {
485
- key: "halves",
486
- // NO sportsMarketType filter: half-result legs ("France leading at halftime?")
487
- // are sportsMarketType="other"; AND-ing the game-line types would drop them.
488
- // marketGroup=period_lines + period already scope the tab.
489
- marketGroup: ["period_lines"],
490
- period: ["1h", "2h"]
491
- },
492
- { key: "corners", marketCategory: "corners" },
493
- { key: "goals", marketCategory: "goals" },
494
- { key: "assists", marketCategory: "assists" },
495
- { key: "shots", marketCategory: "shots" },
496
- {
497
- key: "other",
498
- // `player_props` is deliberately NOT fetched here: goals/assists/shots/corners
499
- // props already have dedicated tabs (resolveMarketSectionTabKey routes them
500
- // there by category), so including the group would re-fetch hundreds of props
501
- // already shown elsewhere. The handful with no stat category (e.g. "saves")
502
- // are back-filled from the game-wide set in resolveSportsSectionMarkets.
503
- marketGroup: ["other", "team_props", "specials", "completion"]
504
- }
452
+ var SPORTS_GAME_LINE_GROUPS = [
453
+ "game_lines",
454
+ "spreads",
455
+ "totals",
456
+ "team_props",
457
+ "specials",
458
+ "completion"
505
459
  ];
506
- var SPORTS_NON_STANDARD_VISIBLE_TAB_KEYS = ["game_lines", "other"];
507
- var SPORTS_NON_STANDARD_VISIBLE_TAB_KEY_SET = new Set(
508
- SPORTS_NON_STANDARD_VISIBLE_TAB_KEYS
509
- );
460
+ var SPORTS_GENERIC_STAT = "player_props";
510
461
  var ESPORTS_CATEGORY_TOKENS = /* @__PURE__ */ new Set([
511
462
  "esports",
512
463
  "video games",
@@ -517,9 +468,6 @@ var ESPORTS_CATEGORY_TOKENS = /* @__PURE__ */ new Set([
517
468
  ]);
518
469
  var ESPORTS_TITLE_PATTERN = /\b(?:esports?|valorant|counter-strike|cs2|league of legends|lol|dota|rocket league|overwatch|rainbow six|starcraft|call of duty)\b/i;
519
470
  var SERIES_SCOPE_PATTERN = /\b(?:bo[1-9]|best of \d+|map \d+|game \d+)\b/i;
520
- var SPORTS_SECTION_TAB_BY_KEY = new Map(
521
- SPORTS_SECTION_TAB_DEFINITIONS.map((definition) => [definition.key, definition])
522
- );
523
471
  var SPORTS_PERIOD_ORDER = [
524
472
  "full",
525
473
  "1h",
@@ -584,12 +532,13 @@ var getSportsMarketTypeOrderIndex = (sportsMarketType) => {
584
532
  const marketTypeIndex = SPORTS_MARKET_TYPE_ORDER.indexOf(sportsMarketType);
585
533
  return marketTypeIndex >= 0 ? marketTypeIndex : SPORTS_MARKET_TYPE_ORDER.length;
586
534
  };
587
- var resolveSportsSectionDefinition = (key) => {
588
- const resolvedDefinition = SPORTS_SECTION_TAB_BY_KEY.get(key);
589
- if (!resolvedDefinition) {
590
- return SPORTS_SECTION_TAB_DEFINITIONS[0];
591
- }
592
- return resolvedDefinition;
535
+ var resolveSportsPeriodFamily = (period) => {
536
+ if (period === "1h" || period === "2h") return "halves";
537
+ if (period === "q1" || period === "q2" || period === "q3" || period === "q4") return "quarters";
538
+ if (/^inning_\d{1,2}$/.test(period)) return "innings";
539
+ if (/^set_\d{1,2}$/.test(period)) return "sets";
540
+ if (/^(?:map|game)_\d{1,2}$/.test(period)) return "maps";
541
+ return period;
593
542
  };
594
543
  var normalizeSportsPeriod = (value) => {
595
544
  const resolvedValue = toTrimmedString(value).toLowerCase();
@@ -634,53 +583,182 @@ var resolveFallbackSportsMarketCategory = (market) => {
634
583
  if (question.includes("goal")) return "goals";
635
584
  return null;
636
585
  };
586
+ var resolveSportsStatToken = (market) => {
587
+ const subtype = toTrimmedString(market.marketSubtype).toLowerCase();
588
+ const playerMatch = /_player_(.+)$/.exec(subtype);
589
+ if (playerMatch) return playerMatch[1];
590
+ const category = normalizeSportsMarketCategory(market.marketCategory);
591
+ if (category && SPORTS_LEGACY_PROP_CATEGORY_KEY_SET.has(category)) return category;
592
+ return null;
593
+ };
637
594
  var resolveMarketSectionTabKey = (market) => {
595
+ var _a;
638
596
  if (normalizeSportsMarketType(market.sportsMarketType) === "to_advance") {
639
597
  return "game_lines";
640
598
  }
641
599
  const normalizedCategory = normalizeSportsMarketCategory(market.marketCategory);
642
600
  const fallbackCategory = !normalizedCategory || normalizedCategory === "other" ? resolveFallbackSportsMarketCategory(market) : null;
643
601
  const category = fallbackCategory != null ? fallbackCategory : normalizedCategory;
644
- if (category && SPORTS_CATEGORY_OVERRIDE_TAB_KEY_SET.has(category)) {
645
- return category;
646
- }
602
+ if (category && SPORTS_LEGACY_PROP_CATEGORY_KEY_SET.has(category)) return `stat:${category}`;
603
+ if (category === "exact_score") return "exact_score";
647
604
  const group = normalizeSportsMarketGroup(market.marketGroup);
648
- if (group) return SPORTS_MARKET_GROUP_TO_TAB[group];
605
+ if (group === "exact_score") return "exact_score";
606
+ if (group === "player_props") {
607
+ return `stat:${(_a = resolveSportsStatToken(market)) != null ? _a : SPORTS_GENERIC_STAT}`;
608
+ }
609
+ if (group === "period_lines") {
610
+ return `period:${resolveSportsPeriodFamily(normalizeSportsPeriod(market.period))}`;
611
+ }
612
+ if (group === "other") return "other";
613
+ if (group) return "game_lines";
649
614
  if (category === "game_lines") return "game_lines";
650
615
  if (category === "other") return "other";
651
616
  return null;
652
617
  };
653
- var resolveSportsSectionFilters = (key) => {
654
- var _a;
655
- const resolvedDefinition = resolveSportsSectionDefinition(key);
656
- const useMarketGroup = !!((_a = resolvedDefinition.marketGroup) == null ? void 0 : _a.length);
618
+ var resolveVisibleSportsSectionTabs = (event) => {
619
+ var _a, _b, _c;
620
+ const periodsByFamily = /* @__PURE__ */ new Map();
621
+ const statVolume = /* @__PURE__ */ new Map();
622
+ let hasGameLines = false;
623
+ let hasExactScore = false;
624
+ let hasOther = false;
625
+ for (const market of event.venueMarkets) {
626
+ const key = resolveMarketSectionTabKey(market);
627
+ if (!key) continue;
628
+ if (key === "game_lines") hasGameLines = true;
629
+ else if (key === "exact_score") hasExactScore = true;
630
+ else if (key === "other") hasOther = true;
631
+ else if (key.startsWith("period:")) {
632
+ const family = key.slice("period:".length);
633
+ const periods = (_a = periodsByFamily.get(family)) != null ? _a : /* @__PURE__ */ new Set();
634
+ periods.add(normalizeSportsPeriod(market.period));
635
+ periodsByFamily.set(family, periods);
636
+ } else if (key.startsWith("stat:")) {
637
+ const stat = key.slice("stat:".length);
638
+ statVolume.set(stat, ((_b = statVolume.get(stat)) != null ? _b : 0) + ((_c = market.volume) != null ? _c : 0));
639
+ }
640
+ }
641
+ const tabs = [];
642
+ if (hasGameLines) tabs.push({ key: "game_lines", kind: "game_lines" });
643
+ const periodTabs = Array.from(periodsByFamily.entries()).map(([periodFamily, periods]) => ({
644
+ key: `period:${periodFamily}`,
645
+ kind: "period",
646
+ periodFamily,
647
+ periods: Array.from(periods).sort(
648
+ (left, right) => getSportsPeriodOrderIndex(left) - getSportsPeriodOrderIndex(right)
649
+ )
650
+ })).sort(
651
+ (left, right) => getSportsPeriodOrderIndex(left.periods[0]) - getSportsPeriodOrderIndex(right.periods[0])
652
+ );
653
+ tabs.push(...periodTabs);
654
+ const statTabs = Array.from(statVolume.entries()).sort((left, right) => {
655
+ if (left[0] === SPORTS_GENERIC_STAT !== (right[0] === SPORTS_GENERIC_STAT)) {
656
+ return left[0] === SPORTS_GENERIC_STAT ? 1 : -1;
657
+ }
658
+ return right[1] - left[1];
659
+ }).map(([stat]) => ({ key: `stat:${stat}`, kind: "stat", stat }));
660
+ tabs.push(...statTabs);
661
+ if (hasExactScore) tabs.push({ key: "exact_score", kind: "exact_score" });
662
+ if (hasOther) tabs.push({ key: "other", kind: "other" });
663
+ if (isNonStandardSportsLikeEvent(event)) {
664
+ return tabs.filter((tab) => tab.kind === "game_lines" || tab.kind === "other");
665
+ }
666
+ return tabs;
667
+ };
668
+ var resolveSportsSectionFilters = (tab) => {
669
+ if (tab.kind === "period") {
670
+ return {
671
+ marketGroup: ["period_lines"],
672
+ marketCategory: void 0,
673
+ sportsMarketType: void 0,
674
+ period: tab.periods ? [...tab.periods] : void 0
675
+ };
676
+ }
677
+ if (tab.kind === "stat") {
678
+ const legacyCategory = SPORTS_LEGACY_PROP_CATEGORY_KEY_SET.has(
679
+ tab.stat
680
+ ) ? tab.stat : null;
681
+ return legacyCategory ? {
682
+ marketGroup: void 0,
683
+ marketCategory: [legacyCategory],
684
+ sportsMarketType: void 0,
685
+ period: void 0
686
+ } : {
687
+ marketGroup: ["player_props"],
688
+ marketCategory: void 0,
689
+ sportsMarketType: void 0,
690
+ period: void 0
691
+ };
692
+ }
693
+ if (tab.kind === "exact_score") {
694
+ return {
695
+ marketGroup: ["exact_score"],
696
+ marketCategory: void 0,
697
+ sportsMarketType: void 0,
698
+ period: void 0
699
+ };
700
+ }
701
+ if (tab.kind === "other") {
702
+ return {
703
+ marketGroup: ["other"],
704
+ marketCategory: void 0,
705
+ sportsMarketType: void 0,
706
+ period: void 0
707
+ };
708
+ }
657
709
  return {
658
- marketGroup: useMarketGroup ? [...resolvedDefinition.marketGroup] : void 0,
659
- // Only fall back to the deprecated marketCategory filter when the tab has no
660
- // marketGroup mapping. Sending both would AND them server-side and drop
661
- // markets that carry only one of the two.
662
- marketCategory: !useMarketGroup && resolvedDefinition.marketCategory ? [resolvedDefinition.marketCategory] : void 0,
663
- sportsMarketType: resolvedDefinition.sportsMarketType ? [...resolvedDefinition.sportsMarketType] : void 0,
664
- period: resolvedDefinition.period ? [...resolvedDefinition.period] : void 0
710
+ marketGroup: [...SPORTS_GAME_LINE_GROUPS],
711
+ marketCategory: void 0,
712
+ sportsMarketType: void 0,
713
+ period: void 0
665
714
  };
666
715
  };
716
+ var hasTeamTitledOutcomes = (market) => {
717
+ var _a;
718
+ const titles = ((_a = market.venueMarketOutcomes) != null ? _a : []).map(
719
+ (outcome) => toTrimmedString(outcome.title)
720
+ );
721
+ return titles.length > 1 && titles.every((title) => title && !YES_NO_LABELS.has(title.toLowerCase())) && new Set(titles).size === titles.length;
722
+ };
723
+ var isClusterRelation = (left, right) => {
724
+ var _a, _b;
725
+ return ((_a = left.matchedVenueMarkets) != null ? _a : []).some((market) => market.id === right.id) || ((_b = right.matchedVenueMarkets) != null ? _b : []).some((market) => market.id === left.id);
726
+ };
727
+ var isDecomposedLeg = (market) => {
728
+ var _a;
729
+ const labels = ((_a = market.venueMarketOutcomes) != null ? _a : []).map(
730
+ (outcome) => toTrimmedString(outcome.label).toLowerCase()
731
+ );
732
+ return labels.length === 2 && labels.every((label) => YES_NO_LABELS.has(label)) && !hasTeamTitledOutcomes(market);
733
+ };
734
+ var dropDecomposedLegsMergedIntoSingle = (markets) => {
735
+ const legs = markets.filter((market) => isDecomposedLeg(market));
736
+ if (legs.length === 0) return markets;
737
+ const hasMergedSingle = markets.some(
738
+ (market) => {
739
+ var _a, _b;
740
+ return hasTeamTitledOutcomes(market) && ((_b = (_a = market.venueMarketOutcomes) == null ? void 0 : _a.length) != null ? _b : 0) >= legs.length && markets.some((other) => other !== market && isClusterRelation(market, other));
741
+ }
742
+ );
743
+ return hasMergedSingle ? markets.filter((market) => !isDecomposedLeg(market)) : markets;
744
+ };
667
745
  var resolveSportsSectionMarkets = ({
668
- tabKey,
746
+ tab,
669
747
  eventMarkets,
670
748
  sectionMarkets
671
749
  }) => {
672
- const effectiveSectionMarkets = sectionMarkets.length > 0 ? sectionMarkets : eventMarkets.filter((market) => resolveMarketSectionTabKey(market) === tabKey);
750
+ const effectiveSectionMarkets = sectionMarkets.length > 0 ? sectionMarkets : eventMarkets.filter((market) => resolveMarketSectionTabKey(market) === tab.key);
673
751
  const filteredSectionMarkets = effectiveSectionMarkets.filter(
674
- (market) => resolveMarketSectionTabKey(market) === tabKey
752
+ (market) => resolveMarketSectionTabKey(market) === tab.key
675
753
  );
676
- if (tabKey === "other") {
754
+ if (tab.key === "other" || tab.key === `stat:${SPORTS_GENERIC_STAT}`) {
677
755
  const seenIds = new Set(filteredSectionMarkets.map((market) => market.id));
678
- const strayOtherMarkets = eventMarkets.filter(
679
- (market) => !seenIds.has(market.id) && resolveMarketSectionTabKey(market) === "other"
756
+ const strayMarkets = eventMarkets.filter(
757
+ (market) => !seenIds.has(market.id) && resolveMarketSectionTabKey(market) === tab.key
680
758
  );
681
- return [...filteredSectionMarkets, ...strayOtherMarkets];
759
+ return [...filteredSectionMarkets, ...strayMarkets];
682
760
  }
683
- if (tabKey !== "game_lines") return filteredSectionMarkets;
761
+ if (tab.kind !== "game_lines") return filteredSectionMarkets;
684
762
  const dedupeEventMarketsOfType = (type) => {
685
763
  const dedupedMarkets = [];
686
764
  const seenKeys = /* @__PURE__ */ new Set();
@@ -693,7 +771,7 @@ var resolveSportsSectionMarkets = ({
693
771
  seenKeys.add(key);
694
772
  dedupedMarkets.push(market);
695
773
  }
696
- return dedupedMarkets;
774
+ return dropDecomposedLegsMergedIntoSingle(dedupedMarkets);
697
775
  };
698
776
  const eventMoneylineMarkets = dedupeEventMarketsOfType("moneyline");
699
777
  const eventToAdvanceMarkets = dedupeEventMarketsOfType("to_advance");
@@ -780,7 +858,9 @@ var resolveSpreadOutcomeTeamNames = (activeMarket, groupMarkets) => {
780
858
  return toTrimmedString((_b = (_a = market.venueMarketOutcomes) == null ? void 0 : _a[index]) == null ? void 0 : _b.title);
781
859
  }).filter((title) => isSpreadTeamTitle(title));
782
860
  if (cleanTitles.length === 0) return null;
783
- return cleanTitles.reduce((longest, title) => title.length > longest.length ? title : longest);
861
+ return cleanTitles.reduce(
862
+ (longest, title) => title.length > longest.length ? title : longest
863
+ );
784
864
  };
785
865
  return { favored: pickName(0), dog: pickName(1) };
786
866
  };
@@ -789,9 +869,7 @@ var resolveCombinedMoneylineLegLabel = (market) => {
789
869
  return toTrimmedString(typedMarket.shortTitle) || toTrimmedString(market.question);
790
870
  };
791
871
  var areCombinedMoneylineLegsDistinct = (markets) => {
792
- const labels = markets.map(
793
- (market) => resolveCombinedMoneylineLegLabel(market).toLowerCase()
794
- );
872
+ const labels = markets.map((market) => resolveCombinedMoneylineLegLabel(market).toLowerCase());
795
873
  return labels.every((label) => label.length > 0) && new Set(labels).size === labels.length;
796
874
  };
797
875
  var resolveDisplayGroupFamilyKey = (market) => {
@@ -1076,6 +1154,15 @@ var buildSportsSlideFromGroup = (group) => {
1076
1154
  defaultStopIndex
1077
1155
  };
1078
1156
  };
1157
+ var formatSportsPeriodGrammarLabel = (period) => {
1158
+ const grammarMatch = SPORTS_PERIOD_GRAMMAR_PATTERN.exec(period);
1159
+ if (!grammarMatch) return null;
1160
+ if (grammarMatch[1]) {
1161
+ const family = grammarMatch[1].charAt(0).toUpperCase() + grammarMatch[1].slice(1);
1162
+ return `${family} ${Number(grammarMatch[2])}`;
1163
+ }
1164
+ return `First ${Number(grammarMatch[3])} Innings`;
1165
+ };
1079
1166
  var resolveSportsCategoryTokens = (event) => {
1080
1167
  return new Set(
1081
1168
  event.categories.flatMap((entry) => {
@@ -1098,39 +1185,20 @@ var isNonStandardSportsLikeEvent = (event) => {
1098
1185
  return ESPORTS_TITLE_PATTERN.test(question) || SERIES_SCOPE_PATTERN.test(question);
1099
1186
  });
1100
1187
  };
1101
- var resolveVisibleSportsSectionTabDefinitions = (event) => {
1102
- const presentTabKeys = /* @__PURE__ */ new Set();
1103
- for (const market of event.venueMarkets) {
1104
- const tabKey = resolveMarketSectionTabKey(market);
1105
- if (tabKey) presentTabKeys.add(tabKey);
1106
- }
1107
- let definitions = SPORTS_SECTION_TAB_DEFINITIONS.filter(
1108
- (definition) => presentTabKeys.has(definition.key)
1109
- );
1110
- if (isNonStandardSportsLikeEvent(event)) {
1111
- definitions = definitions.filter(
1112
- (definition) => SPORTS_NON_STANDARD_VISIBLE_TAB_KEY_SET.has(definition.key)
1113
- );
1114
- }
1115
- return definitions;
1116
- };
1117
1188
  var mergeSportsSectionMarketsIntoEvent = ({
1118
1189
  event,
1119
1190
  sectionMarketsByKey
1120
1191
  }) => {
1121
- var _a;
1122
1192
  const orderedVenueMarkets = [...event.venueMarkets];
1123
1193
  const seenMarketIds = new Set(orderedVenueMarkets.map((market) => market.id));
1124
- for (const sectionKey of SPORTS_SECTION_TAB_DEFINITIONS.map((definition) => definition.key)) {
1125
- for (const market of (_a = sectionMarketsByKey[sectionKey]) != null ? _a : []) {
1194
+ for (const markets of Object.values(sectionMarketsByKey)) {
1195
+ for (const market of markets != null ? markets : []) {
1126
1196
  if (seenMarketIds.has(market.id)) continue;
1127
1197
  seenMarketIds.add(market.id);
1128
1198
  orderedVenueMarkets.push(market);
1129
1199
  }
1130
1200
  }
1131
- return __spreadProps(__spreadValues({}, event), {
1132
- venueMarkets: orderedVenueMarkets
1133
- });
1201
+ return __spreadProps(__spreadValues({}, event), { venueMarkets: orderedVenueMarkets });
1134
1202
  };
1135
1203
 
1136
1204
  // src/pages/event-market/sports-market-card.tsx
@@ -1387,6 +1455,15 @@ function SportsLineRail({
1387
1455
 
1388
1456
  // src/pages/event-market/sports-market-card.tsx
1389
1457
  import { jsx as jsx3 } from "react/jsx-runtime";
1458
+ var resolveSportsLineOutcomeSide = (outcome) => {
1459
+ var _a;
1460
+ for (const candidate of [outcome.label, outcome.title]) {
1461
+ if (typeof candidate !== "string") continue;
1462
+ const matchedSide = (_a = candidate.trim().toLowerCase().match(/(?:^|\s)(over|under)(?=\s|$)/)) == null ? void 0 : _a[1];
1463
+ if (matchedSide === "over" || matchedSide === "under") return matchedSide;
1464
+ }
1465
+ return null;
1466
+ };
1390
1467
  var SportsMarketCard = ({
1391
1468
  group,
1392
1469
  venueMarkets,
@@ -1549,16 +1626,22 @@ var SportsMarketCard = ({
1549
1626
  setSelectedLineIndex(index);
1550
1627
  const market = (_a2 = slide.stops[index]) == null ? void 0 : _a2.market;
1551
1628
  if (!market) return;
1552
- const isGroupBeingTraded = group.markets.some(
1553
- (groupMarket) => groupMarket.id === (tradingContext == null ? void 0 : tradingContext.selectedMarketId)
1554
- );
1555
- if (!isGroupBeingTraded) return;
1556
1629
  const previousOutcomeIndex = previousMarket ? previousMarket.venueMarketOutcomes.findIndex(
1557
1630
  (outcome) => outcome.id === (tradingContext == null ? void 0 : tradingContext.selectedOutcomeId)
1558
1631
  ) : -1;
1559
- const nextOutcome = (_b2 = previousOutcomeIndex >= 0 ? market.venueMarketOutcomes[previousOutcomeIndex] : void 0) != null ? _b2 : market.venueMarketOutcomes[0];
1632
+ const previousOutcome = previousOutcomeIndex >= 0 ? previousMarket == null ? void 0 : previousMarket.venueMarketOutcomes[previousOutcomeIndex] : void 0;
1633
+ const previousOutcomeSide = previousOutcome ? resolveSportsLineOutcomeSide(previousOutcome) : null;
1634
+ const matchingSideOutcomes = previousOutcomeSide ? market.venueMarketOutcomes.filter(
1635
+ (outcome) => resolveSportsLineOutcomeSide(outcome) === previousOutcomeSide
1636
+ ) : [];
1637
+ const matchingSideOutcome = matchingSideOutcomes.length === 1 ? matchingSideOutcomes[0] : void 0;
1638
+ const nextOutcome = (_b2 = matchingSideOutcome != null ? matchingSideOutcome : previousOutcomeIndex >= 0 ? market.venueMarketOutcomes[previousOutcomeIndex] : void 0) != null ? _b2 : market.venueMarketOutcomes[0];
1560
1639
  if (nextOutcome) {
1561
1640
  tradingContext == null ? void 0 : tradingContext.selectMarketAndOutcome(market.id, nextOutcome.id);
1641
+ onOutcomeSelect == null ? void 0 : onOutcomeSelect(group.id, {
1642
+ marketId: market.id,
1643
+ outcomeId: nextOutcome.id
1644
+ });
1562
1645
  } else {
1563
1646
  tradingContext == null ? void 0 : tradingContext.selectMarket(market.id, market);
1564
1647
  }
@@ -1604,24 +1687,48 @@ SportsMarketCard.displayName = "SportsMarketCard";
1604
1687
 
1605
1688
  // src/pages/event-market/sports-markets.tsx
1606
1689
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1607
- var resolveSportsSectionLabel = (labels, key) => {
1608
- switch (key) {
1690
+ var STAT_LABEL_OVERRIDES = {
1691
+ rbis: "RBIs",
1692
+ hits_runs_rbis: "Hits + Runs + RBIs",
1693
+ ot: "OT"
1694
+ };
1695
+ var formatStatLabel = (stat) => {
1696
+ var _a;
1697
+ return (_a = STAT_LABEL_OVERRIDES[stat]) != null ? _a : stat.split("_").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1698
+ };
1699
+ var LEGACY_STAT_LABEL_KEYS = {
1700
+ corners: "cornersTab",
1701
+ goals: "goalsTab",
1702
+ assists: "assistsTab",
1703
+ shots: "shotsTab"
1704
+ };
1705
+ var resolveSportsSectionLabel = (labels, tab) => {
1706
+ var _a, _b, _c, _d, _e;
1707
+ const sports = labels.eventMarketPage.sports;
1708
+ switch (tab.kind) {
1609
1709
  case "game_lines":
1610
- return labels.eventMarketPage.sports.gameLinesTab;
1611
- case "halves":
1612
- return labels.eventMarketPage.sports.halvesTab;
1710
+ return sports.gameLinesTab;
1613
1711
  case "exact_score":
1614
- return labels.eventMarketPage.sports.exactScoreTab;
1615
- case "corners":
1616
- return labels.eventMarketPage.sports.cornersTab;
1617
- case "goals":
1618
- return labels.eventMarketPage.sports.goalsTab;
1619
- case "assists":
1620
- return labels.eventMarketPage.sports.assistsTab;
1621
- case "shots":
1622
- return labels.eventMarketPage.sports.shotsTab;
1712
+ return sports.exactScoreTab;
1623
1713
  case "other":
1624
- return labels.eventMarketPage.sports.otherTab;
1714
+ return sports.otherTab;
1715
+ case "stat": {
1716
+ if (tab.stat === "player_props") return sports.playerPropsTab;
1717
+ const legacyLabelKey = tab.stat ? LEGACY_STAT_LABEL_KEYS[tab.stat] : void 0;
1718
+ return legacyLabelKey ? sports[legacyLabelKey] : formatStatLabel((_a = tab.stat) != null ? _a : "");
1719
+ }
1720
+ case "period": {
1721
+ switch (tab.periodFamily) {
1722
+ case "halves":
1723
+ return sports.halvesTab;
1724
+ case "quarters":
1725
+ return sports.quartersTab;
1726
+ case "sets":
1727
+ return sports.setsTab;
1728
+ default:
1729
+ return (_e = (_c = formatSportsPeriodGrammarLabel((_b = tab.periodFamily) != null ? _b : "")) == null ? void 0 : _c.replace(/^First /, "")) != null ? _e : formatStatLabel((_d = tab.periodFamily) != null ? _d : "");
1730
+ }
1731
+ }
1625
1732
  }
1626
1733
  };
1627
1734
  var SportsTabButton = ({
@@ -1709,18 +1816,23 @@ var SportsMarkets = ({
1709
1816
  }
1710
1817
  return merged;
1711
1818
  }, [event.venueMarkets, gameMarkets]);
1712
- const visibleSectionDefinitions = useMemo2(
1713
- () => resolveVisibleSportsSectionTabDefinitions(__spreadProps(__spreadValues({}, event), { venueMarkets: tabSourceMarkets })),
1819
+ const visibleTabs = useMemo2(
1820
+ () => resolveVisibleSportsSectionTabs(__spreadProps(__spreadValues({}, event), { venueMarkets: tabSourceMarkets })),
1714
1821
  [event, tabSourceMarkets]
1715
1822
  );
1716
- const shouldRenderTabs = visibleSectionDefinitions.length > 1 && tabSourceMarkets.length > 1;
1717
- const [activeTabKey, setActiveTabKey] = useState4(
1718
- (_b = (_a = visibleSectionDefinitions[0]) == null ? void 0 : _a.key) != null ? _b : "game_lines"
1823
+ const shouldRenderTabs = visibleTabs.length > 1 && tabSourceMarkets.length > 1;
1824
+ const [activeTabKey, setActiveTabKey] = useState4((_b = (_a = visibleTabs[0]) == null ? void 0 : _a.key) != null ? _b : "game_lines");
1825
+ const activeTab = useMemo2(
1826
+ () => {
1827
+ var _a2, _b2;
1828
+ return (_b2 = (_a2 = visibleTabs.find((tab) => tab.key === activeTabKey)) != null ? _a2 : visibleTabs[0]) != null ? _b2 : { key: "game_lines", kind: "game_lines" };
1829
+ },
1830
+ [visibleTabs, activeTabKey]
1719
1831
  );
1720
1832
  const [expandedGroupId, setExpandedGroupId] = useState4(null);
1721
1833
  const [stableMarketsByTab, setStableMarketsByTab] = useState4({});
1722
- const activeTabLabel = resolveSportsSectionLabel(labels, activeTabKey);
1723
- const sectionFilters = resolveSportsSectionFilters(activeTabKey);
1834
+ const activeTabLabel = resolveSportsSectionLabel(labels, activeTab);
1835
+ const sectionFilters = resolveSportsSectionFilters(activeTab);
1724
1836
  const sectionMarketsQuery = useVenueMarkets({
1725
1837
  aggKey: event.aggKey ? [event.aggKey] : void 0,
1726
1838
  sportsMarketType: sectionFilters.sportsMarketType,
@@ -1743,26 +1855,25 @@ var SportsMarkets = ({
1743
1855
  } = sectionMarketsQuery;
1744
1856
  const resolvedSectionMarkets = useMemo2(
1745
1857
  () => resolveSportsSectionMarkets({
1746
- tabKey: activeTabKey,
1858
+ tab: activeTab,
1747
1859
  eventMarkets: tabSourceMarkets,
1748
1860
  sectionMarkets
1749
1861
  }),
1750
- [activeTabKey, tabSourceMarkets, sectionMarkets]
1862
+ [activeTab, tabSourceMarkets, sectionMarkets]
1751
1863
  );
1752
1864
  const isSectionSettled = !isSectionLoading && !hasNextPage && !isFetchingNextPage;
1753
1865
  const stableSectionMarkets = stableMarketsByTab[activeTabKey];
1754
- const displayedSectionMarkets = stableSectionMarkets != null ? stableSectionMarkets : isSectionSettled ? resolvedSectionMarkets : [];
1866
+ const displayedSectionMarkets = useMemo2(
1867
+ () => stableSectionMarkets != null ? stableSectionMarkets : isSectionSettled ? resolvedSectionMarkets : [],
1868
+ [stableSectionMarkets, isSectionSettled, resolvedSectionMarkets]
1869
+ );
1755
1870
  const periodGroups = useMemo2(
1756
1871
  () => groupSportsMarketsByPeriod(displayedSectionMarkets),
1757
1872
  [displayedSectionMarkets]
1758
1873
  );
1759
- const displayedPeriodGroups = useMemo2(
1760
- () => activeTabKey === "game_lines" ? periodGroups.filter((periodGroup) => periodGroup.key === "full") : periodGroups,
1761
- [activeTabKey, periodGroups]
1762
- );
1763
1874
  const allGroups = useMemo2(
1764
- () => displayedPeriodGroups.flatMap((periodGroup) => periodGroup.groups),
1765
- [displayedPeriodGroups]
1875
+ () => periodGroups.flatMap((periodGroup) => periodGroup.groups),
1876
+ [periodGroups]
1766
1877
  );
1767
1878
  useEffect4(() => {
1768
1879
  if (hasNextPage && !isFetchingNextPage) {
@@ -1796,9 +1907,9 @@ var SportsMarkets = ({
1796
1907
  }, [activeTabKey, displayedSectionMarkets, hasAggKey, onSectionMarketsChange]);
1797
1908
  useEffect4(() => {
1798
1909
  var _a2, _b2;
1799
- if (visibleSectionDefinitions.some((definition) => definition.key === activeTabKey)) return;
1800
- setActiveTabKey((_b2 = (_a2 = visibleSectionDefinitions[0]) == null ? void 0 : _a2.key) != null ? _b2 : "game_lines");
1801
- }, [activeTabKey, visibleSectionDefinitions]);
1910
+ if (visibleTabs.some((tab) => tab.key === activeTabKey)) return;
1911
+ setActiveTabKey((_b2 = (_a2 = visibleTabs[0]) == null ? void 0 : _a2.key) != null ? _b2 : "game_lines");
1912
+ }, [activeTabKey, visibleTabs]);
1802
1913
  const lastActiveTabRef = useRef3(null);
1803
1914
  useEffect4(() => {
1804
1915
  if (allGroups.length === 0) return;
@@ -1845,14 +1956,14 @@ var SportsMarkets = ({
1845
1956
  role: "tablist",
1846
1957
  "aria-label": labels.eventMarketPage.sports.sectionTabsAria,
1847
1958
  className: "flex flex-nowrap items-center gap-2 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1848
- children: visibleSectionDefinitions.map((definition) => /* @__PURE__ */ jsx4(
1959
+ children: visibleTabs.map((tab) => /* @__PURE__ */ jsx4(
1849
1960
  SportsTabButton,
1850
1961
  {
1851
- isActive: definition.key === activeTabKey,
1852
- label: resolveSportsSectionLabel(labels, definition.key),
1853
- onClick: () => setActiveTabKey(definition.key)
1962
+ isActive: tab.key === activeTabKey,
1963
+ label: resolveSportsSectionLabel(labels, tab),
1964
+ onClick: () => setActiveTabKey(tab.key)
1854
1965
  },
1855
- definition.key
1966
+ tab.key
1856
1967
  ))
1857
1968
  }
1858
1969
  ) : null,
@@ -1866,7 +1977,7 @@ var SportsMarkets = ({
1866
1977
  classNames: { root: "min-h-[220px]" }
1867
1978
  }
1868
1979
  ) : null,
1869
- displayedPeriodGroups.map((periodGroup) => {
1980
+ periodGroups.map((periodGroup) => {
1870
1981
  return /* @__PURE__ */ jsx4(
1871
1982
  "section",
1872
1983
  {
@@ -1923,7 +2034,7 @@ var SportsEventMarketPage = ({
1923
2034
  }, [event, sectionMarketsByKey]);
1924
2035
  const eventTradingState = useMemo3(() => resolveEventTradingState2(composedEvent), [composedEvent]);
1925
2036
  const hasSportsSections = useMemo3(
1926
- () => resolveVisibleSportsSectionTabDefinitions(event).length > 0,
2037
+ () => resolveVisibleSportsSectionTabs(event).length > 0,
1927
2038
  [event]
1928
2039
  );
1929
2040
  const stickyOrderPanelState = resolveEventMarketPageStickyState({