@useinsider/guido 3.12.0-beta.840207a → 3.12.0-beta.86e34da

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 (54) hide show
  1. package/dist/@types/config/schemas.js +6 -6
  2. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +1 -1
  3. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +8 -8
  4. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  5. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +10 -10
  6. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.js +8 -6
  7. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue2.js +98 -71
  8. package/dist/composables/usePreviewInteractionGuard.js +36 -11
  9. package/dist/composables/useRecommendation.js +63 -78
  10. package/dist/composables/useRecommendationPreview.js +61 -60
  11. package/dist/enums/extensions/filteringV2.js +1017 -1
  12. package/dist/enums/extensions/recommendationBlock.js +34 -19
  13. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  14. package/dist/extensions/Blocks/Recommendation/controls/blockBackground/index.js +10 -0
  15. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +24 -20
  16. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +245 -106
  17. package/dist/extensions/Blocks/Recommendation/extension.js +23 -21
  18. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +17 -3
  19. package/dist/extensions/Blocks/Recommendation/recommendation.css.js +257 -0
  20. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +41 -40
  21. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +310 -117
  22. package/dist/extensions/Blocks/Recommendation/utils/strategyNavigation.js +8 -0
  23. package/dist/extensions/Blocks/Recommendation/utils/strategySummary.js +93 -8
  24. package/dist/extensions/Blocks/Recommendation/utils/strategyUrl.js +43 -0
  25. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +18 -16
  26. package/dist/extensions/Blocks/Unsubscribe/utils/constants.js +3 -2
  27. package/dist/extensions/Blocks/common-control.js +2 -1
  28. package/dist/extensions/Blocks/controlFactories.js +125 -75
  29. package/dist/guido.css +1 -1
  30. package/dist/services/recommendationApi.js +90 -45
  31. package/dist/services/stripoApi.js +18 -18
  32. package/dist/src/@types/config/schemas.d.ts +2 -2
  33. package/dist/src/composables/usePreviewInteractionGuard.d.ts +1 -1
  34. package/dist/src/enums/extensions/recommendationBlock.d.ts +25 -2
  35. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +1 -0
  36. package/dist/src/extensions/Blocks/Recommendation/controls/blockBackground/index.d.ts +14 -0
  37. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  38. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +7 -3
  39. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +59 -45
  40. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +301 -4
  41. package/dist/src/extensions/Blocks/Recommendation/utils/strategyNavigation.d.ts +10 -0
  42. package/dist/src/extensions/Blocks/Recommendation/utils/strategyUrl.d.ts +51 -0
  43. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +5 -0
  44. package/dist/src/extensions/Blocks/Unsubscribe/utils/constants.d.ts +1 -0
  45. package/dist/src/extensions/Blocks/common-control.d.ts +2 -1
  46. package/dist/src/extensions/Blocks/controlFactories.d.ts +36 -0
  47. package/dist/src/mock/api/recommendation-strategies.d.ts +2 -0
  48. package/dist/src/services/recommendationApi.d.ts +3 -1
  49. package/dist/src/utils/urlSchemes.d.ts +6 -0
  50. package/dist/static/styles/base.css.js +0 -15
  51. package/dist/static/styles/components/base-input.css.js +6 -7
  52. package/dist/utils/templatePreparation.js +56 -47
  53. package/dist/utils/urlSchemes.js +4 -0
  54. package/package.json +1 -1
@@ -1,16 +1,28 @@
1
1
  import { useTranslations as r } from "../../composables/useTranslations.js";
2
- const l = {
2
+ const c = {
3
3
  RECOMMENDATION_API_URL: "https://recommendationv2.api.useinsider.com",
4
4
  // Relative path → same-origin as the embedding inone dashboard.
5
5
  PRODUCT_ATTRIBUTES_PATH: "/product-attributes/get-attributes",
6
6
  // Reusable Recommendation Strategies (RRS), also same-origin on inone.
7
7
  RECOMMENDATION_STRATEGIES_PATH: "/api/recommendation-strategies",
8
- STRATEGY_RECOMMENDATION_URL_PATH: "/api/recommendation-strategies/strategy-recommendation-url"
9
- }, c = {
8
+ // POST. Builds a strategy's product-feed URL. Takes the WHOLE strategy record,
9
+ // not an id: the strategy's algorithm decides the feed's path segment
10
+ // (`/v2/top-sellers` vs `/v2/user-based` …) and which params it needs, so there
11
+ // is no fixed URL an id could be substituted into.
12
+ STRATEGY_ENDPOINT_GENERATE_PATH: "/api/recommendation-strategies/endpoint-generate",
13
+ // POST, no body. Answers `{ status, message, strategyId }` — the new id only,
14
+ // never the copy's name, so the listing has to be fetched again to display it.
15
+ STRATEGY_DUPLICATE_PATH: "/api/recommendation-strategies/{{id}}/duplicate"
16
+ }, l = {
17
+ LISTING: "/discovery/recommendation-strategies",
18
+ CREATE: "/discovery/recommendation-strategies/create",
19
+ EDIT: "/discovery/recommendation-strategies/{{id}}/edit"
20
+ }, d = {
10
21
  CLIENT_ID: "clientId",
11
- STRATEGY_ID: "strategy_id",
12
- FILTERS: "filters"
13
- }, d = "[sr_strategies.status][=][active]*[sr_strategies.suitable_channels][~][EMAIL]", m = () => {
22
+ FILTERS: "filters",
23
+ PAGE: "page",
24
+ PER_PAGE: "per_page"
25
+ }, m = 100, p = 50, v = "[sr_strategies.status][=][active]*[sr_strategies.suitable_channels][~][EMAIL]", E = () => {
14
26
  const e = r();
15
27
  return [
16
28
  {
@@ -74,14 +86,14 @@ const l = {
74
86
  path: "top-sellers"
75
87
  }
76
88
  ];
77
- }, p = ["discount", "omnibus_price", "omnibus_discount", "price", "original_price"], v = [
89
+ }, g = ["discount", "omnibus_price", "omnibus_discount", "price", "original_price"], h = [
78
90
  { text: "before the amount", value: "0" },
79
91
  { text: "after the amount", value: "1" }
80
- ], h = [
92
+ ], T = [
81
93
  { text: "dot(.)", value: "." },
82
94
  { text: "comma(,)", value: "," },
83
95
  { text: "space( )", value: " " }
84
- ], x = [
96
+ ], _ = [
85
97
  { text: "0", value: "0" },
86
98
  { text: "1", value: "1" },
87
99
  { text: "2", value: "2" },
@@ -110,7 +122,7 @@ const l = {
110
122
  ], i = [
111
123
  { text: "true", value: "==" },
112
124
  { text: "false", value: "!=" }
113
- ], g = (e) => {
125
+ ], x = (e) => {
114
126
  if (!e)
115
127
  return t;
116
128
  switch (e) {
@@ -130,15 +142,18 @@ const l = {
130
142
  };
131
143
  export {
132
144
  a as OP_ANY_OF,
133
- p as PriceAttributes,
134
- c as QUERY_PARAMS,
135
- d as RRS_EMAIL_ACTIVE_FILTER,
136
- l as URLS,
137
- x as currencyDecimalCounts,
138
- v as currencyLocationMaps,
139
- h as currencyOperators,
140
- g as getOperatorOptions,
141
- m as getRecommendationFeedSourceMaps,
145
+ g as PriceAttributes,
146
+ d as QUERY_PARAMS,
147
+ v as RRS_EMAIL_ACTIVE_FILTER,
148
+ p as RRS_STRATEGIES_MAX_PAGES,
149
+ m as RRS_STRATEGIES_PER_PAGE,
150
+ l as STRATEGY_PAGES,
151
+ c as URLS,
152
+ _ as currencyDecimalCounts,
153
+ h as currencyLocationMaps,
154
+ T as currencyOperators,
155
+ x as getOperatorOptions,
156
+ E as getRecommendationFeedSourceMaps,
142
157
  s as operatorOptionsForArrayOfStrings,
143
158
  i as operatorOptionsForBooleans,
144
159
  o as operatorOptionsForDates,
@@ -1,4 +1,4 @@
1
- var o = /* @__PURE__ */ ((c) => (c.BUTTON_ALIGN = "recommendation-block-button-align-control", c.BUTTON_BORDER = "recommendation-block-button-border-control", c.BUTTON_BORDER_RADIUS = "recommendation-block-button-border-radius-control", c.BUTTON_COLOR = "recommendation-block-button-color-control", c.BUTTON_FIT_TO_CONTENT = "recommendation-block-button-fit-to-content-control", c.BUTTON_FONT_FAMILY = "recommendation-block-button-font-family-control", c.BUTTON_MARGINS = "recommendation-block-button-margins-control", c.BUTTON_PADDINGS = "recommendation-block-button-paddings-control", c.BUTTON_TEXT = "recommendation-block-button-text-control", c.BUTTON_TEXT_SIZE = "recommendation-block-button-text-size-control", c.BUTTON_TEXT_STYLE_AND_FONT_COLOR = "recommendation-block-button-text-style-and-font-color-control", c.NAME_ALIGN = "recommendation-block-name-align-control", c.NAME_BACKGROUND = "recommendation-block-name-background-control", c.NAME_COLOR = "recommendation-block-name-color-control", c.NAME_FONT_FAMILY = "recommendation-block-name-font-family-control", c.NAME_PADDINGS = "recommendation-block-name-paddings-control", c.NAME_SIZE = "recommendation-block-name-size-control", c.NAME_STYLE = "recommendation-block-name-style-control", c.NAME_TEXT_TRIM = "recommendation-block-name-text-trim-control", c.PRICE_ALIGN = "recommendation-block-price-align-control", c.PRICE_BACKGROUND = "recommendation-block-price-background-control", c.PRICE_COLOR = "recommendation-block-price-color-control", c.PRICE_FONT_FAMILY = "recommendation-block-price-font-family-control", c.PRICE_PADDINGS = "recommendation-block-price-paddings-control", c.PRICE_SIZE = "recommendation-block-price-size-control", c.PRICE_STYLE = "recommendation-block-price-style-control", c.OLD_PRICE_ALIGN = "recommendation-block-old-price-align-control", c.OLD_PRICE_BACKGROUND = "recommendation-block-old-price-background-control", c.OLD_PRICE_COLOR = "recommendation-block-old-price-color-control", c.OLD_PRICE_FONT_FAMILY = "recommendation-block-old-price-font-family-control", c.OLD_PRICE_PADDINGS = "recommendation-block-old-price-paddings-control", c.OLD_PRICE_SIZE = "recommendation-block-old-price-size-control", c.OLD_PRICE_STYLE = "recommendation-block-old-price-style-control", c.OMNIBUS_PRICE_ALIGN = "recommendation-block-omnibus-price-align-control", c.OMNIBUS_PRICE_BACKGROUND = "recommendation-block-omnibus-price-background-control", c.OMNIBUS_PRICE_COLOR = "recommendation-block-omnibus-price-color-control", c.OMNIBUS_PRICE_FONT_FAMILY = "recommendation-block-omnibus-price-font-family-control", c.OMNIBUS_PRICE_PADDINGS = "recommendation-block-omnibus-price-paddings-control", c.OMNIBUS_PRICE_SIZE = "recommendation-block-omnibus-price-size-control", c.OMNIBUS_PRICE_STYLE = "recommendation-block-omnibus-price-style-control", c.OMNIBUS_PRICE_TEXT_BEFORE = "recommendation-block-omnibus-price-text-before-control", c.OMNIBUS_PRICE_TEXT_AFTER = "recommendation-block-omnibus-price-text-after-control", c.OMNIBUS_DISCOUNT_ALIGN = "recommendation-block-omnibus-discount-align-control", c.OMNIBUS_DISCOUNT_BACKGROUND = "recommendation-block-omnibus-discount-background-control", c.OMNIBUS_DISCOUNT_COLOR = "recommendation-block-omnibus-discount-color-control", c.OMNIBUS_DISCOUNT_FONT_FAMILY = "recommendation-block-omnibus-discount-font-family-control", c.OMNIBUS_DISCOUNT_PADDINGS = "recommendation-block-omnibus-discount-paddings-control", c.OMNIBUS_DISCOUNT_SIZE = "recommendation-block-omnibus-discount-size-control", c.OMNIBUS_DISCOUNT_STYLE = "recommendation-block-omnibus-discount-style-control", c.OMNIBUS_DISCOUNT_TEXT_BEFORE = "recommendation-block-omnibus-discount-text-before-control", c.OMNIBUS_DISCOUNT_TEXT_AFTER = "recommendation-block-omnibus-discount-text-after-control", c.IMAGE_SIZE = "recommendation-block-image-size-control", c.IMAGE_MARGINS = "recommendation-block-image-margins-control", c.CUSTOM_ATTR_ALIGN = "recommendation-block-custom-attr-align-control", c.CUSTOM_ATTR_BACKGROUND = "recommendation-block-custom-attr-background-control", c.CUSTOM_ATTR_COLOR = "recommendation-block-custom-attr-color-control", c.CUSTOM_ATTR_FONT_FAMILY = "recommendation-block-custom-attr-font-family-control", c.CUSTOM_ATTR_PADDINGS = "recommendation-block-custom-attr-paddings-control", c.CUSTOM_ATTR_SIZE = "recommendation-block-custom-attr-size-control", c.CUSTOM_ATTR_STYLE = "recommendation-block-custom-attr-style-control", c.CUSTOM_ATTR_TEXT_TRIM = "recommendation-block-custom-attr-text-trim-control", c.SYNC_INFO_MESSAGE = "recommendation-block-sync-info-message", c))(o || {});
1
+ var o = /* @__PURE__ */ ((c) => (c.BLOCK_BACKGROUND = "recommendation-block-background-color-control", c.BUTTON_ALIGN = "recommendation-block-button-align-control", c.BUTTON_BORDER = "recommendation-block-button-border-control", c.BUTTON_BORDER_RADIUS = "recommendation-block-button-border-radius-control", c.BUTTON_COLOR = "recommendation-block-button-color-control", c.BUTTON_FIT_TO_CONTENT = "recommendation-block-button-fit-to-content-control", c.BUTTON_FONT_FAMILY = "recommendation-block-button-font-family-control", c.BUTTON_MARGINS = "recommendation-block-button-margins-control", c.BUTTON_PADDINGS = "recommendation-block-button-paddings-control", c.BUTTON_TEXT = "recommendation-block-button-text-control", c.BUTTON_TEXT_SIZE = "recommendation-block-button-text-size-control", c.BUTTON_TEXT_STYLE_AND_FONT_COLOR = "recommendation-block-button-text-style-and-font-color-control", c.NAME_ALIGN = "recommendation-block-name-align-control", c.NAME_BACKGROUND = "recommendation-block-name-background-control", c.NAME_COLOR = "recommendation-block-name-color-control", c.NAME_FONT_FAMILY = "recommendation-block-name-font-family-control", c.NAME_PADDINGS = "recommendation-block-name-paddings-control", c.NAME_SIZE = "recommendation-block-name-size-control", c.NAME_STYLE = "recommendation-block-name-style-control", c.NAME_TEXT_TRIM = "recommendation-block-name-text-trim-control", c.PRICE_ALIGN = "recommendation-block-price-align-control", c.PRICE_BACKGROUND = "recommendation-block-price-background-control", c.PRICE_COLOR = "recommendation-block-price-color-control", c.PRICE_FONT_FAMILY = "recommendation-block-price-font-family-control", c.PRICE_PADDINGS = "recommendation-block-price-paddings-control", c.PRICE_SIZE = "recommendation-block-price-size-control", c.PRICE_STYLE = "recommendation-block-price-style-control", c.OLD_PRICE_ALIGN = "recommendation-block-old-price-align-control", c.OLD_PRICE_BACKGROUND = "recommendation-block-old-price-background-control", c.OLD_PRICE_COLOR = "recommendation-block-old-price-color-control", c.OLD_PRICE_FONT_FAMILY = "recommendation-block-old-price-font-family-control", c.OLD_PRICE_PADDINGS = "recommendation-block-old-price-paddings-control", c.OLD_PRICE_SIZE = "recommendation-block-old-price-size-control", c.OLD_PRICE_STYLE = "recommendation-block-old-price-style-control", c.OMNIBUS_PRICE_ALIGN = "recommendation-block-omnibus-price-align-control", c.OMNIBUS_PRICE_BACKGROUND = "recommendation-block-omnibus-price-background-control", c.OMNIBUS_PRICE_COLOR = "recommendation-block-omnibus-price-color-control", c.OMNIBUS_PRICE_FONT_FAMILY = "recommendation-block-omnibus-price-font-family-control", c.OMNIBUS_PRICE_PADDINGS = "recommendation-block-omnibus-price-paddings-control", c.OMNIBUS_PRICE_SIZE = "recommendation-block-omnibus-price-size-control", c.OMNIBUS_PRICE_STYLE = "recommendation-block-omnibus-price-style-control", c.OMNIBUS_PRICE_TEXT_BEFORE = "recommendation-block-omnibus-price-text-before-control", c.OMNIBUS_PRICE_TEXT_AFTER = "recommendation-block-omnibus-price-text-after-control", c.OMNIBUS_DISCOUNT_ALIGN = "recommendation-block-omnibus-discount-align-control", c.OMNIBUS_DISCOUNT_BACKGROUND = "recommendation-block-omnibus-discount-background-control", c.OMNIBUS_DISCOUNT_COLOR = "recommendation-block-omnibus-discount-color-control", c.OMNIBUS_DISCOUNT_FONT_FAMILY = "recommendation-block-omnibus-discount-font-family-control", c.OMNIBUS_DISCOUNT_PADDINGS = "recommendation-block-omnibus-discount-paddings-control", c.OMNIBUS_DISCOUNT_SIZE = "recommendation-block-omnibus-discount-size-control", c.OMNIBUS_DISCOUNT_STYLE = "recommendation-block-omnibus-discount-style-control", c.OMNIBUS_DISCOUNT_TEXT_BEFORE = "recommendation-block-omnibus-discount-text-before-control", c.OMNIBUS_DISCOUNT_TEXT_AFTER = "recommendation-block-omnibus-discount-text-after-control", c.IMAGE_SIZE = "recommendation-block-image-size-control", c.IMAGE_MARGINS = "recommendation-block-image-margins-control", c.CUSTOM_ATTR_ALIGN = "recommendation-block-custom-attr-align-control", c.CUSTOM_ATTR_BACKGROUND = "recommendation-block-custom-attr-background-control", c.CUSTOM_ATTR_COLOR = "recommendation-block-custom-attr-color-control", c.CUSTOM_ATTR_FONT_FAMILY = "recommendation-block-custom-attr-font-family-control", c.CUSTOM_ATTR_PADDINGS = "recommendation-block-custom-attr-paddings-control", c.CUSTOM_ATTR_SIZE = "recommendation-block-custom-attr-size-control", c.CUSTOM_ATTR_STYLE = "recommendation-block-custom-attr-style-control", c.CUSTOM_ATTR_TEXT_TRIM = "recommendation-block-custom-attr-text-trim-control", c.SYNC_INFO_MESSAGE = "recommendation-block-sync-info-message", c))(o || {});
2
2
  export {
3
3
  o as RecommendationControlId
4
4
  };
@@ -0,0 +1,10 @@
1
+ import { createBlockBackgroundColorControl as o } from "../../../controlFactories.js";
2
+ import { RecommendationControlId as r } from "../../constants/controlIds.js";
3
+ import { BLOCK_ROOT_SELECTOR as t } from "../../constants/selectors.js";
4
+ const c = o(
5
+ r.BLOCK_BACKGROUND,
6
+ t
7
+ );
8
+ export {
9
+ c as BlockBackgroundColorControl
10
+ };
@@ -1,6 +1,6 @@
1
- var p = Object.defineProperty;
2
- var g = (d, l, t) => l in d ? p(d, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[l] = t;
3
- var s = (d, l, t) => g(d, typeof l != "symbol" ? l + "" : l, t);
1
+ var g = Object.defineProperty;
2
+ var p = (d, l, t) => l in d ? g(d, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[l] = t;
3
+ var s = (d, l, t) => p(d, typeof l != "symbol" ? l + "" : l, t);
4
4
  import { useConfig as h } from "../../../../../composables/useConfig.js";
5
5
  import { CommonControl as y } from "../../../common-control.js";
6
6
  import { DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
@@ -19,7 +19,7 @@ import { LOCALE_CONTROL_ID as mt } from "./locale.js";
19
19
  import { PricePlacementControl as S } from "./pricePlacement.js";
20
20
  import { PRICE_PLACEMENT_CONTROL_ID as Ct } from "./pricePlacement.js";
21
21
  import { ProductCountControl as O } from "./productCount.js";
22
- import { PRODUCT_COUNT_CONTROL_ID as pt } from "./productCount.js";
22
+ import { PRODUCT_COUNT_CONTROL_ID as gt } from "./productCount.js";
23
23
  import { ProductLayoutControl as P } from "./productLayout.js";
24
24
  import { PRODUCT_LAYOUT_CONTROL_ID as yt } from "./productLayout.js";
25
25
  import { ShuffleControl as L } from "./shuffle.js";
@@ -101,14 +101,14 @@ class et extends y {
101
101
  n.api = this.api;
102
102
  }), `
103
103
  <div class="recommendation-controls-container">
104
- ${this.layoutOrientationControl.getTemplate()}
105
- ${this.productCountControl.getTemplate()}
106
- ${this.productLayoutControl.getTemplate()}
107
104
  ${((o = this.strategyControl ?? this.algorithmControl) == null ? void 0 : o.getTemplate()) ?? ""}
108
105
  ${this.localeControl.getTemplate()}
109
- ${this.pricePlacementControl.getTemplate()}
110
106
  ${this.currencyControl.getTemplate()}
111
107
  ${this.filtersControl.getTemplate()}
108
+ ${this.productCountControl.getTemplate()}
109
+ ${this.productLayoutControl.getTemplate()}
110
+ ${this.layoutOrientationControl.getTemplate()}
111
+ ${this.pricePlacementControl.getTemplate()}
112
112
  ${this.shuffleControl.getTemplate()}
113
113
  </div>
114
114
  `;
@@ -205,10 +205,10 @@ class et extends y {
205
205
  mobileCardsInRow: t.mobileCardsInRow,
206
206
  mobileLayoutEnabled: t.mobileLayoutEnabled,
207
207
  orientation: t.layout,
208
- // Only sync filters from node config during initial load.
209
- // After initialization, the Pinia store is the source of truth
210
- // for filters (edited via the filter drawer).
211
- ...o ? {} : { filters: t.filters },
208
+ // Only sync filters and strategy from node config during initial load.
209
+ // After initialization, the Pinia store is the source of truth for both
210
+ // (edited via the filter drawer and the strategy picker respectively).
211
+ ...o ? {} : { filters: t.filters, strategyId: t.strategyId },
212
212
  shuffleProducts: t.shuffleProducts,
213
213
  priceMovedToNextLine: t.priceMovedToNextLine,
214
214
  priceHideIfSameAsDiscounted: t.priceHideIfSameAsDiscounted,
@@ -346,24 +346,28 @@ class et extends y {
346
346
  return;
347
347
  }
348
348
  const i = t.$state.configVersion;
349
- i !== o && (o = i, this._persistFiltersToNodeConfig(), this._debouncedFetchProducts());
349
+ i !== o && (o = i, this._persistStoreConfigToNode(), this._debouncedFetchProducts());
350
350
  const c = t.recommendationProducts, u = c !== e, f = Array.isArray(c) && c.length > 0;
351
351
  u && f && (e = c, this._debouncedRegenerateWithProducts());
352
352
  });
353
353
  }
354
354
  /**
355
- * Persists the current filter state from Pinia store to the Stripo node config.
356
- * This ensures filters survive template save/reload cycles.
355
+ * Persists the Pinia-owned parts of the block config to the Stripo node config,
356
+ * so they survive template save/reload cycles.
357
+ *
358
+ * Filters are edited in a drawer and the strategy in an overlaid picker — neither
359
+ * is a Stripo form element, so neither can write to the node itself. Both ride
360
+ * this one update to keep the undo history to a single entry per change.
357
361
  */
358
- _persistFiltersToNodeConfig() {
362
+ _persistStoreConfigToNode() {
359
363
  if (!this.currentNode || !this.api)
360
364
  return;
361
- const { filters: t } = this.store.recommendationConfigs;
365
+ const { filters: t, strategyId: e } = this.store.recommendationConfigs;
362
366
  m.updateConfig(
363
367
  this.api,
364
368
  this.currentNode,
365
- { filters: t },
366
- "Update recommendation filters"
369
+ { filters: t, strategyId: e },
370
+ "Update recommendation settings"
367
371
  );
368
372
  }
369
373
  }
@@ -380,7 +384,7 @@ export {
380
384
  _ as LayoutOrientationControl,
381
385
  T as LocaleControl,
382
386
  Ct as PRICE_PLACEMENT_CONTROL_ID,
383
- pt as PRODUCT_COUNT_CONTROL_ID,
387
+ gt as PRODUCT_COUNT_CONTROL_ID,
384
388
  yt as PRODUCT_LAYOUT_CONTROL_ID,
385
389
  S as PricePlacementControl,
386
390
  O as ProductCountControl,
@@ -1,146 +1,285 @@
1
- var g = Object.defineProperty;
2
- var d = (o, s, t) => s in o ? g(o, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[s] = t;
3
- var r = (o, s, t) => d(o, typeof s != "symbol" ? s + "" : s, t);
4
- import { UEAttr as l } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as c } from "../../../common-control.js";
6
- import { RecommendationConfigService as a } from "../../services/configService.js";
7
- import { useRecommendationExtensionStore as S } from "../../store/recommendation.js";
8
- const m = "recommendation-strategy-control", e = {
9
- STRATEGY: "strategyId",
10
- DETAILS_BUTTON: "strategyDetails",
11
- MESSAGE: "strategyMessage"
12
- }, h = "guido__btn-strategy-details";
13
- class E extends c {
1
+ var $ = Object.defineProperty;
2
+ var O = (g, h, t) => h in g ? $(g, h, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[h] = t;
3
+ var u = (g, h, t) => O(g, typeof h != "symbol" ? h + "" : h, t);
4
+ import { useTranslations as f } from "../../../../../composables/useTranslations.js";
5
+ import { STRATEGY_PAGES as R } from "../../../../../enums/extensions/recommendationBlock.js";
6
+ import { formatShortDate as I } from "../../../../../utils/dateUtil.js";
7
+ import { UEAttr as b } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
8
+ import { CommonControl as L } from "../../../common-control.js";
9
+ import { useRecommendationExtensionStore as A } from "../../store/recommendation.js";
10
+ import { navigateToStrategyPage as w } from "../../utils/strategyNavigation.js";
11
+ import { buildStrategySummary as H } from "../../utils/strategySummary.js";
12
+ const D = "recommendation-strategy-control", T = {
13
+ CREATE_BUTTON: "strategyCreate",
14
+ DETAILS_BUTTON: "strategyDetails"
15
+ }, E = "guido__btn-strategy-create", C = "guido__btn-strategy-details", s = {
16
+ ROOT: "data-strategy-control",
17
+ TRIGGER: "data-strategy-trigger",
18
+ VALUE: "data-strategy-value",
19
+ MENU: "data-strategy-menu",
20
+ SEARCH: "data-strategy-search",
21
+ LIST: "data-strategy-list",
22
+ OPTION_ID: "data-strategy-id",
23
+ CARD: "data-strategy-card",
24
+ VIEW_MORE: "data-strategy-view-more"
25
+ }, _ = 8, M = 150, N = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zm0-2a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm-1-7a1 1 0 1 1 2 0v4a1 1 0 1 1-2 0v-4zm1-5a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path></svg>', G = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 15.999a1.042 1.042 0 0 1-.784-.305l-5.91-5.91a1.045 1.045 0 1 1 1.478-1.478L12 13.522l5.216-5.216a1.045 1.045 0 1 1 1.478 1.478l-5.91 5.91a1.042 1.042 0 0 1-.784.305z"></path></svg>', o = (g) => g.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
26
+ class j extends L {
14
27
  constructor() {
15
28
  super(...arguments);
16
- // Store is used ONLY for API-fetched data (strategy list), not for config
17
- r(this, "store", S());
18
- r(this, "detailsListener", () => {
19
- });
20
- r(this, "detailsButton", null);
29
+ u(this, "store", A());
30
+ /**
31
+ * InOne's dictionary, not Stripo's. Every dotted key here
32
+ * (`discovery-strategy.*`, `gpt-app-builder.*`) lives in the host panel's
33
+ * translations, which `this.api.translate` does not read — that one is only
34
+ * for the plain-English control labels the rest of this panel uses.
35
+ */
36
+ u(this, "trans", f());
37
+ u(this, "events", null);
38
+ u(this, "unsubscribeStore", null);
39
+ u(this, "searchTerm", "");
40
+ u(this, "isOpen", !1);
41
+ u(this, "cardHideTimer", null);
21
42
  }
22
43
  getId() {
23
- return m;
44
+ return D;
24
45
  }
25
46
  getTemplate() {
26
47
  return `
27
- <div class="strategy-control-container">
28
- ${this._GuTwoColumns([
48
+ <div class="strategy-control" ${s.ROOT}>
49
+ ${this._GuOneColumn([
29
50
  this._GuLabel({ text: this.api.translate("Recommendation Strategy") }),
30
- `<div class="strategy-control__select-row">
31
- ${this._GuSelect({
32
- name: e.STRATEGY,
33
- placeholder: this.api.translate("Select Recommendation Strategy"),
34
- options: this.store.getStrategyOptions
51
+ // The menu is absolutely positioned against THIS wrapper. Without
52
+ // it the menu is a grid item of the column container and its
53
+ // static position resolves to the container's top, so it opens
54
+ // over the label instead of under the trigger.
55
+ `<div class="strategy-control__field">
56
+ <div class="strategy-control__row">
57
+ <button type="button" class="strategy-select" ${s.TRIGGER}>
58
+ <span class="strategy-select__value" ${s.VALUE}></span>
59
+ <span class="strategy-select__caret">${G}</span>
60
+ </button>
61
+ ${this._GuIconButton({
62
+ name: T.DETAILS_BUTTON,
63
+ icon: "strategy-settings-icon",
64
+ className: "strategy-control__details",
65
+ id: C
35
66
  })}
36
- ${this._GuIconButton({
37
- name: e.DETAILS_BUTTON,
38
- icon: "strategy-details-icon",
39
- className: h
67
+ </div>
68
+ <div class="strategy-menu" ${s.MENU} hidden>
69
+ <div class="strategy-menu__search">
70
+ <input type="text" class="strategy-menu__search-input" ${s.SEARCH}
71
+ placeholder="${o(this.trans("discovery-strategy.search"))}">
72
+ </div>
73
+ <ul class="strategy-menu__list" ${s.LIST}></ul>
74
+ </div>
75
+ </div>`,
76
+ `<div class="strategy-control__create-row">
77
+ ${this._GuButton({
78
+ name: T.CREATE_BUTTON,
79
+ label: this.api.translate("Create New Strategy"),
80
+ id: E
40
81
  })}
41
82
  </div>`
42
83
  ])}
43
- ${this._GuOnPageMessage({ name: e.MESSAGE, icon: "migration-info-icon" })}
84
+ <div class="strategy-card" ${s.CARD} hidden></div>
44
85
  </div>
45
86
  `;
46
87
  }
47
88
  onRender() {
48
- this._initializeSelectItems(), this._setFormValues(), this._listenToFormUpdates(), this._setupDetailsButtonListener(), this._refreshSelectionState();
89
+ this._setupEventListeners(), this._subscribeToStoreChanges(), this._render();
49
90
  }
50
91
  onDestroy() {
51
- var t;
52
- (t = this.detailsButton) == null || t.removeEventListener("click", this.detailsListener);
53
- }
54
- onTemplateNodeUpdated(t) {
55
- super.onTemplateNodeUpdated(t), this._initializeSelectItems(), this._setFormValues(), this._refreshSelectionState();
92
+ var t, e;
93
+ this._cancelHideCard(), (t = this.events) == null || t.abort(), this.events = null, (e = this.unsubscribeStore) == null || e.call(this), this.unsubscribeStore = null, this.isOpen = !1, this.searchTerm = "", super.onDestroy();
56
94
  }
57
- _setFormValues() {
58
- const t = a.getConfig(this.currentNode);
59
- this.api.updateValues({ [e.STRATEGY]: t.strategyId });
95
+ _root() {
96
+ var t;
97
+ return ((t = this.getContainer()) == null ? void 0 : t.querySelector(`[${s.ROOT}]`)) ?? null;
60
98
  }
61
- _initializeSelectItems() {
62
- var i;
63
- const t = (i = this.store) == null ? void 0 : i.getStrategyOptions;
64
- if (t != null && t.length)
65
- try {
66
- this.api.setUIEAttribute(
67
- e.STRATEGY,
68
- l.SELECTPICKER.items,
69
- t
70
- );
71
- } catch (n) {
72
- console.warn("[StrategyControl] Failed to set strategy options:", n);
73
- }
99
+ /**
100
+ * All listeners are delegated onto nodes that survive `innerHTML` rebuilds and
101
+ * are torn down together by the AbortController the option rows themselves
102
+ * are replaced on every render and must never own a listener.
103
+ */
104
+ _setupEventListeners() {
105
+ var d, c, m, p, y, v;
106
+ (d = this.events) == null || d.abort(), this.events = new AbortController();
107
+ const { signal: t } = this.events, e = this._root();
108
+ if (!e)
109
+ return;
110
+ (c = e.querySelector(`[${s.TRIGGER}]`)) == null || c.addEventListener("click", () => this._toggleMenu(), { signal: t }), (m = e.querySelector(`#${C}`)) == null || m.addEventListener("click", () => this.store.openStrategyDrawer(), { signal: t }), (p = e.querySelector(`#${E}`)) == null || p.addEventListener("click", () => w(R.CREATE), { signal: t }), (y = e.querySelector(`[${s.SEARCH}]`)) == null || y.addEventListener("input", (l) => {
111
+ this.searchTerm = l.target.value, this._renderOptions();
112
+ }, { signal: t });
113
+ const r = e.querySelector(`[${s.LIST}]`);
114
+ r == null || r.addEventListener("click", (l) => {
115
+ const n = l.target.closest(`[${s.OPTION_ID}]`), a = n == null ? void 0 : n.getAttribute(s.OPTION_ID);
116
+ a && (this._closeMenu(), this.store.selectStrategy(a));
117
+ }, { signal: t }), r == null || r.addEventListener("mouseenter", (l) => {
118
+ var a, S;
119
+ const n = (S = (a = l.target) == null ? void 0 : a.closest) == null ? void 0 : S.call(a, `[${s.OPTION_ID}]`);
120
+ n && this._showCard(n);
121
+ }, { capture: !0, signal: t }), r == null || r.addEventListener("mouseleave", () => this._scheduleHideCard(), { signal: t });
122
+ const i = e.querySelector(`[${s.CARD}]`);
123
+ i == null || i.addEventListener("mouseenter", () => this._cancelHideCard(), { signal: t }), i == null || i.addEventListener("mouseleave", () => this._hideCard(), { signal: t }), i == null || i.addEventListener("click", (l) => {
124
+ const n = l.target.closest(`[${s.VIEW_MORE}]`), a = n == null ? void 0 : n.getAttribute(s.VIEW_MORE);
125
+ a && (this._closeMenu(), a !== this.store.recommendationConfigs.strategyId && this.store.selectStrategy(a), this.store.openStrategyDrawer());
126
+ }, { signal: t }), (v = this.getContainer()) == null || v.addEventListener("mousedown", (l) => {
127
+ var n, a;
128
+ this.isOpen && !((a = (n = l.target).closest) != null && a.call(n, `[${s.ROOT}]`)) && this._closeMenu();
129
+ }, { signal: t });
74
130
  }
75
131
  /**
76
- * Reconciles the panel with the strategy listing: loading, empty, and
77
- * deleted-strategy states.
132
+ * Keeps the trigger label and the gear's enabled state in step with the store.
78
133
  *
79
- * A strategy the partner deleted in Smart Recommender still lives in this
80
- * block's saved config. The id is deliberately left in place and surfaced
81
- * instead of being cleared — silently dropping it would make a template that
82
- * compiles a broken feed URL look correctly configured.
134
+ * Both depend on data that arrives after the first render: the strategy listing
135
+ * is fetched asynchronously, and `strategyId` changes when the user picks,
136
+ * duplicates, or switches blocks.
137
+ */
138
+ _subscribeToStoreChanges() {
139
+ var e;
140
+ (e = this.unsubscribeStore) == null || e.call(this);
141
+ let t = this._stateKey();
142
+ this.unsubscribeStore = this.store.$subscribe(() => {
143
+ const r = this._stateKey();
144
+ r !== t && (t = r, this._render());
145
+ });
146
+ }
147
+ /**
148
+ * Fingerprint of everything the rendered markup depends on.
83
149
  *
84
- * ponytail: one message element, three texts. Severity styling (warning vs
85
- * info) is not drawn in Figma and pending design sign-off see open question
86
- * 4 in the handoff. Vary `type` once that lands.
150
+ * Ids rather than a count: a refetch that renames a strategy or swaps one for
151
+ * another of the same length would otherwise leave the trigger and the card
152
+ * showing stale copy until some unrelated store change happened to repaint.
87
153
  */
88
- _refreshSelectionState() {
89
- const t = !!this.store.getStrategyOptions.length, { strategyId: i } = a.getConfig(this.currentNode);
90
- if (this._setDisabled(e.STRATEGY, this.store.strategiesLoading || !t), this._setDisabled(e.DETAILS_BUTTON, !this.store.getSelectedStrategy), this.store.strategiesLoading) {
91
- this._showMessage(this.api.translate("Loading recommendation strategies..."));
154
+ _stateKey() {
155
+ return [
156
+ this.store.recommendationConfigs.strategyId,
157
+ this.store.strategiesLoading,
158
+ this.store.strategies.map((t) => `${t.strategy_id}:${t.name}`).join(",")
159
+ ].join("|");
160
+ }
161
+ _render() {
162
+ this._renderTrigger(), this._renderOptions(), this.api.setUIEAttribute(
163
+ T.DETAILS_BUTTON,
164
+ b.BUTTON.disabled,
165
+ this.store.getSelectedStrategy ? "false" : "true"
166
+ );
167
+ }
168
+ _renderTrigger() {
169
+ var r;
170
+ const t = (r = this._root()) == null ? void 0 : r.querySelector(`[${s.VALUE}]`);
171
+ if (!t)
92
172
  return;
93
- }
94
- if (!t) {
95
- this._showMessage(this.api.translate(
96
- "No recommendation strategies are available for email. Create one in Smart Recommender, then reopen this block."
97
- ));
173
+ const e = this.store.getSelectedStrategy;
174
+ t.textContent = (e == null ? void 0 : e.name) ?? this.trans("smart-recommender.select-recommendation-strategy"), t.classList.toggle("strategy-select__value--placeholder", !e), t.classList.toggle("strategy-select__value--missing", this.store.hasMissingStrategy);
175
+ }
176
+ _renderOptions() {
177
+ var i;
178
+ const t = (i = this._root()) == null ? void 0 : i.querySelector(`[${s.LIST}]`);
179
+ if (!t)
180
+ return;
181
+ if (this._hideCard(), this.store.strategiesLoading) {
182
+ t.innerHTML = `<li class="strategy-menu__empty">${o(
183
+ this.trans("newsletter.loading")
184
+ )}</li>`;
98
185
  return;
99
186
  }
100
- if (this.store.hasMissingStrategy) {
101
- this._showMessage(this.api.translate(
102
- `The selected strategy (${i}) is no longer available. Pick another one, otherwise this block will not load products.`
103
- ));
187
+ const e = this._filteredStrategies();
188
+ if (!e.length) {
189
+ t.innerHTML = `<li class="strategy-menu__empty">${o(
190
+ this.trans("discovery-strategy.no-data-title-text")
191
+ )}</li>`;
104
192
  return;
105
193
  }
106
- this._hideMessage();
194
+ const r = this.store.recommendationConfigs.strategyId;
195
+ t.innerHTML = e.map((d) => {
196
+ const c = String(d.strategy_id);
197
+ return `<li class="strategy-menu__option${c === r ? " strategy-menu__option--selected" : ""}"
198
+ ${s.OPTION_ID}="${o(c)}">${o(d.name)}</li>`;
199
+ }).join("");
107
200
  }
108
- _setDisabled(t, i) {
109
- try {
110
- this.api.setUIEAttribute(t, l.SELECTPICKER.disabled, i);
111
- } catch (n) {
112
- console.warn(`[StrategyControl] Failed to set disabled on ${t}:`, n);
201
+ /**
202
+ * Client-side filtering: the listing is fetched whole and cached, so narrowing
203
+ * it here avoids a request per keystroke.
204
+ */
205
+ _filteredStrategies() {
206
+ const t = this.searchTerm.trim().toLowerCase();
207
+ return t ? this.store.strategies.filter((e) => e.name.toLowerCase().includes(t)) : this.store.strategies;
208
+ }
209
+ _toggleMenu() {
210
+ var e, r, i;
211
+ if (this.isOpen) {
212
+ this._closeMenu();
213
+ return;
113
214
  }
215
+ this.isOpen = !0;
216
+ const t = this._root();
217
+ (e = t == null ? void 0 : t.querySelector(`[${s.MENU}]`)) == null || e.removeAttribute("hidden"), (r = t == null ? void 0 : t.querySelector(`[${s.TRIGGER}]`)) == null || r.classList.add("strategy-select--open"), this._renderOptions(), (i = t == null ? void 0 : t.querySelector(`[${s.SEARCH}]`)) == null || i.focus();
114
218
  }
115
- _showMessage(t) {
116
- this._setInfoMessageValue(e.MESSAGE, t);
219
+ _closeMenu() {
220
+ var r, i;
221
+ this.isOpen = !1, this.searchTerm = "", this._hideCard();
222
+ const t = this._root(), e = t == null ? void 0 : t.querySelector(`[${s.SEARCH}]`);
223
+ e && (e.value = ""), (r = t == null ? void 0 : t.querySelector(`[${s.MENU}]`)) == null || r.setAttribute("hidden", ""), (i = t == null ? void 0 : t.querySelector(`[${s.TRIGGER}]`)) == null || i.classList.remove("strategy-select--open");
117
224
  }
118
- _hideMessage() {
119
- const t = this.getContainer().querySelector(`[data-info-message="${e.MESSAGE}"]`);
120
- t && (t.style.display = "none");
225
+ /**
226
+ * The hover card is `position: fixed`, so it can sit beside the panel instead of
227
+ * being clipped by it. Placed once per hover — the list cannot scroll under the
228
+ * pointer without the pointer leaving, so there is nothing to keep in sync.
229
+ */
230
+ _showCard(t) {
231
+ var y;
232
+ const e = (y = this._root()) == null ? void 0 : y.querySelector(`[${s.CARD}]`), r = t.getAttribute(s.OPTION_ID), i = this.store.strategies.find((v) => String(v.strategy_id) === r);
233
+ if (!e || !i)
234
+ return;
235
+ this._cancelHideCard(), e.innerHTML = this._buildCardHtml(i), e.removeAttribute("hidden");
236
+ const d = t.getBoundingClientRect(), c = e.getBoundingClientRect(), m = d.left - c.width - _ >= _ ? d.left - c.width - _ : d.right + _, p = window.innerHeight - c.height - _;
237
+ e.style.left = `${Math.max(_, m)}px`, e.style.top = `${Math.min(Math.max(_, d.top), Math.max(_, p))}px`;
121
238
  }
122
- _onDetailsClick() {
123
- this.store.openStrategyDrawer();
239
+ /** Grace period for the pointer to cross the gap between option and card. */
240
+ _scheduleHideCard() {
241
+ this._cancelHideCard(), this.cardHideTimer = setTimeout(() => this._hideCard(), M);
124
242
  }
125
- _setupDetailsButtonListener() {
126
- var t;
127
- this.detailsListener = this._onDetailsClick.bind(this), this.detailsButton = this.getContainer().querySelector(`.${h}`), (t = this.detailsButton) == null || t.addEventListener("click", this.detailsListener);
128
- }
129
- _onStrategyChange(t) {
130
- !this.currentNode || a.getConfig(this.currentNode).strategyId === t || (a.updateConfig(
131
- this.api,
132
- this.currentNode,
133
- { strategyId: t },
134
- `Changed recommendation strategy to ${t}`
135
- ), this.store.patchCurrentBlockConfig({ strategyId: t }), this.store.fetchStrategyUrlTemplate(t), this._refreshSelectionState());
136
- }
137
- _listenToFormUpdates() {
138
- this.api.onValueChanged(e.STRATEGY, (t) => {
139
- this._onStrategyChange(t);
140
- });
243
+ _cancelHideCard() {
244
+ this.cardHideTimer && (clearTimeout(this.cardHideTimer), this.cardHideTimer = null);
245
+ }
246
+ _hideCard() {
247
+ var t, e;
248
+ this._cancelHideCard(), (e = (t = this._root()) == null ? void 0 : t.querySelector(`[${s.CARD}]`)) == null || e.setAttribute("hidden", "");
249
+ }
250
+ /**
251
+ * Mirrors the design system's `InInfoBox`: coloured top rule, heading +
252
+ * "last updated" details, content, then a divided footer holding the View
253
+ * More text button. The component itself cannot render here, so the markup
254
+ * and the CSS reproduce its structure and tokens rather than approximating
255
+ * them — the values in `recommendation.css` are read from its stylesheet.
256
+ */
257
+ _buildCardHtml(t) {
258
+ const { trans: e } = this, r = H(t, e).map((i) => `
259
+ <div class="strategy-card__row">
260
+ <p class="strategy-card__row-title">${o(i.title)}</p>
261
+ <p class="strategy-card__row-value">${o(i.value)}</p>
262
+ </div>
263
+ `).join("");
264
+ return `
265
+ <div class="strategy-card__body">
266
+ <p class="strategy-card__title">${o(t.name)}</p>
267
+ <p class="strategy-card__updated">${o(e("gpt-app-builder.strategy-last-updated", {
268
+ date: I(t.updated_at)
269
+ }))}</p>
270
+ <div class="strategy-card__rows">${r}</div>
271
+ </div>
272
+ <div class="strategy-card__footer">
273
+ <button type="button" class="strategy-card__more"
274
+ ${s.VIEW_MORE}="${o(String(t.strategy_id))}">
275
+ ${N}
276
+ <span>${o(e("gpt-app-builder.view-more"))}</span>
277
+ </button>
278
+ </div>
279
+ `;
141
280
  }
142
281
  }
143
282
  export {
144
- m as STRATEGY_CONTROL_ID,
145
- E as StrategyControl
283
+ D as STRATEGY_CONTROL_ID,
284
+ j as StrategyControl
146
285
  };