@useinsider/guido 3.17.0-beta.dc7f4d2 → 3.17.0-beta.e6704da

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 (75) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +12 -6
  3. package/dist/components/Guido.vue.js +5 -5
  4. package/dist/components/Guido.vue2.js +77 -77
  5. package/dist/components/organisms/AutoSaveController.vue2.js +8 -9
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +18 -0
  7. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +70 -0
  8. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue.js +20 -0
  9. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue2.js +55 -0
  10. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  11. package/dist/components/organisms/header/EditorActions.vue2.js +43 -74
  12. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  13. package/dist/components/organisms/header/LeftSlot.vue2.js +23 -46
  14. package/dist/components/wrappers/WpDrawer.vue.js +7 -9
  15. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  16. package/dist/composables/useCustomInterfaceAppearance.js +19 -16
  17. package/dist/composables/useHtmlCompiler.js +26 -37
  18. package/dist/composables/useHtmlValidator.js +0 -2
  19. package/dist/composables/useRecommendation.js +41 -34
  20. package/dist/composables/useSave.js +41 -84
  21. package/dist/composables/validators/useCouponBlockValidator.js +21 -24
  22. package/dist/composables/validators/useUnsubscribeBlockValidator.js +27 -34
  23. package/dist/config/migrator/recommendationMigrator.js +6 -4
  24. package/dist/enums/academy.js +2 -1
  25. package/dist/enums/date.js +4 -3
  26. package/dist/enums/extensions/recommendationBlock.js +25 -23
  27. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +13 -11
  28. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +96 -95
  29. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +156 -154
  30. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +141 -0
  31. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +223 -82
  32. package/dist/extensions/Blocks/common-control.js +22 -19
  33. package/dist/guido.css +1 -1
  34. package/dist/services/recommendationApi.js +78 -29
  35. package/dist/src/@types/config/schemas.d.ts +12 -0
  36. package/dist/src/components/Guido.vue.d.ts +1 -17
  37. package/dist/src/components/organisms/{validation/ValidationErrorDrawer.vue.d.ts → extensions/recommendation/AssignStrategyDrawer.vue.d.ts} +1 -0
  38. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBox.vue.d.ts +24 -0
  39. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -17
  40. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -17
  41. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -17
  42. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +3 -4
  43. package/dist/src/composables/useConfig.d.ts +2 -0
  44. package/dist/src/composables/useHtmlValidator.d.ts +0 -21
  45. package/dist/src/composables/useRecommendation.d.ts +1 -0
  46. package/dist/src/composables/useSave.d.ts +1 -6
  47. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +0 -3
  48. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +0 -10
  49. package/dist/src/enums/academy.d.ts +1 -0
  50. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  51. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +14 -3
  52. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +29 -0
  53. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +214 -15
  54. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  55. package/dist/src/extensions/Blocks/common-control.d.ts +4 -1
  56. package/dist/src/mock/api/recommendation-strategies.d.ts +43 -0
  57. package/dist/src/services/recommendationApi.d.ts +4 -1
  58. package/dist/src/stores/config.d.ts +18 -5
  59. package/dist/src/utils/templatePreparation.d.ts +0 -30
  60. package/dist/static/styles/components/recommendation-strategy.css.js +171 -0
  61. package/dist/stores/config.js +6 -13
  62. package/dist/utils/dateUtil.js +23 -10
  63. package/dist/utils/templatePreparation.js +50 -50
  64. package/package.json +2 -1
  65. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +0 -20
  66. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +0 -48
  67. package/dist/composables/useSaveValidation.js +0 -279
  68. package/dist/composables/useSaveValidationRollout.js +0 -9
  69. package/dist/enums/save-validation.js +0 -4
  70. package/dist/src/@types/save-validation.d.ts +0 -19
  71. package/dist/src/composables/useSaveValidation.d.ts +0 -6
  72. package/dist/src/composables/useSaveValidationRollout.d.ts +0 -3
  73. package/dist/src/enums/save-validation.d.ts +0 -27
  74. package/dist/src/stores/save-validation.d.ts +0 -22
  75. package/dist/stores/save-validation.js +0 -30
@@ -1,53 +1,57 @@
1
- var f = Object.defineProperty;
2
- var p = (d, l, t) => l in d ? f(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
- import { CommonControl as g } from "../../../common-control.js";
5
- import { DEFAULT_NODE_CONFIG as c } from "../../constants/defaultConfig.js";
6
- import { RecommendationConfigService as h } from "../../services/configService.js";
7
- import { useRecommendationExtensionStore as y } from "../../store/recommendation.js";
8
- import { AlgorithmControl as N } from "./algorithm.js";
9
- import { ALGORITHM_CONTROL_ID as tt } from "./algorithm.js";
10
- import { CurrencyControl as R } from "./currency.js";
11
- import { CURRENCY_CONTROL_ID as ot } from "./currency.js";
12
- import { FiltersControl as I } from "./filters.js";
13
- import { FILTERS_CONTROL_ID as nt } from "./filters.js";
14
- import { LayoutOrientationControl as _ } from "./layoutOrientation.js";
15
- import { LAYOUT_ORIENTATION_CONTROL_ID as st } from "./layoutOrientation.js";
1
+ var C = Object.defineProperty;
2
+ var g = (d, c, t) => c in d ? C(d, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[c] = t;
3
+ var n = (d, c, t) => g(d, typeof c != "symbol" ? c + "" : c, t);
4
+ import { useConfig as p } from "../../../../../composables/useConfig.js";
5
+ import { CommonControl as y } from "../../../common-control.js";
6
+ import { DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
7
+ import { RecommendationConfigService as m } from "../../services/configService.js";
8
+ import { useRecommendationExtensionStore as N } from "../../store/recommendation.js";
9
+ import { AlgorithmControl as _ } from "./algorithm.js";
10
+ import { ALGORITHM_CONTROL_ID as nt } from "./algorithm.js";
11
+ import { CurrencyControl as I } from "./currency.js";
12
+ import { CURRENCY_CONTROL_ID as st } from "./currency.js";
13
+ import { FiltersControl as R } from "./filters.js";
14
+ import { FILTERS_CONTROL_ID as ct } from "./filters.js";
15
+ import { LayoutOrientationControl as T } from "./layoutOrientation.js";
16
+ import { LAYOUT_ORIENTATION_CONTROL_ID as ut } from "./layoutOrientation.js";
16
17
  import { LocaleControl as b } from "./locale.js";
17
- import { LOCALE_CONTROL_ID as lt } from "./locale.js";
18
- import { PricePlacementControl as T } from "./pricePlacement.js";
19
- import { PRICE_PLACEMENT_CONTROL_ID as ut } from "./pricePlacement.js";
20
- import { ProductCountControl as O } from "./productCount.js";
21
- import { PRODUCT_COUNT_CONTROL_ID as ht } from "./productCount.js";
18
+ import { LOCALE_CONTROL_ID as mt } from "./locale.js";
19
+ import { PricePlacementControl as O } from "./pricePlacement.js";
20
+ import { PRICE_PLACEMENT_CONTROL_ID as ft } from "./pricePlacement.js";
21
+ import { ProductCountControl as S } from "./productCount.js";
22
+ import { PRODUCT_COUNT_CONTROL_ID as gt } from "./productCount.js";
22
23
  import { ProductLayoutControl as P } from "./productLayout.js";
23
- import { PRODUCT_LAYOUT_CONTROL_ID as Ct } from "./productLayout.js";
24
- import { ShuffleControl as S } from "./shuffle.js";
25
- import { SHUFFLE_CONTROL_ID as pt } from "./shuffle.js";
26
- import { setCurrencyAttributes as L, getBlockElement as k, isPartnerManagedBlock as D, updateProductContentInPlace as E, regenerateProductRowsWithStyles as A } from "./utils.js";
27
- import { adjustProductsToSize as yt, formatProductPrice as Nt, getCardComposition as Rt, getCurrentLayout as It, regenerateMobileProductRows as _t, updatePricesInPlace as bt, updateSingleProductContent as Tt } from "./utils.js";
28
- import { useDebounceFn as m } from "../../../../../node_modules/@vueuse/shared/index.js";
29
- const v = "recommendation-id", w = "ui-elements-recommendation-block";
30
- class Q extends g {
24
+ import { PRODUCT_LAYOUT_CONTROL_ID as yt } from "./productLayout.js";
25
+ import { ShuffleControl as L } from "./shuffle.js";
26
+ import { SHUFFLE_CONTROL_ID as _t } from "./shuffle.js";
27
+ import { StrategyControl as k } from "./strategy.js";
28
+ import { STRATEGY_CONTROL_ID as Rt } from "./strategy.js";
29
+ import { setCurrencyAttributes as D, getBlockElement as E, isPartnerManagedBlock as v, updateProductContentInPlace as A, regenerateProductRowsWithStyles as w } from "./utils.js";
30
+ import { adjustProductsToSize as bt, formatProductPrice as Ot, getCardComposition as St, getCurrentLayout as Pt, regenerateMobileProductRows as Lt, updatePricesInPlace as kt, updateSingleProductContent as Dt } from "./utils.js";
31
+ import { useDebounceFn as h } from "../../../../../node_modules/@vueuse/shared/index.js";
32
+ const x = "recommendation-id", B = "ui-elements-recommendation-block";
33
+ class et extends y {
31
34
  constructor() {
32
35
  super(...arguments);
33
- s(this, "store", y());
34
- s(this, "storeUnsubscription", () => {
36
+ n(this, "store", N());
37
+ n(this, "storeUnsubscription", () => {
35
38
  });
36
- s(this, "syncedBlockIds", /* @__PURE__ */ new Set());
39
+ n(this, "syncedBlockIds", /* @__PURE__ */ new Set());
37
40
  // Sub-control instances for lifecycle management
38
- s(this, "algorithmControl", null);
39
- s(this, "localeControl", null);
40
- s(this, "currencyControl", null);
41
- s(this, "productCountControl", null);
42
- s(this, "productLayoutControl", null);
43
- s(this, "filtersControl", null);
44
- s(this, "shuffleControl", null);
45
- s(this, "pricePlacementControl", null);
46
- s(this, "layoutOrientationControl", null);
41
+ n(this, "algorithmControl", null);
42
+ n(this, "localeControl", null);
43
+ n(this, "currencyControl", null);
44
+ n(this, "productCountControl", null);
45
+ n(this, "productLayoutControl", null);
46
+ n(this, "filtersControl", null);
47
+ n(this, "shuffleControl", null);
48
+ n(this, "pricePlacementControl", null);
49
+ n(this, "layoutOrientationControl", null);
50
+ n(this, "strategyControl", null);
47
51
  /**
48
52
  * Debounced product fetch to prevent rapid API calls during config changes
49
53
  */
50
- s(this, "_debouncedFetchProducts", m(() => {
54
+ n(this, "_debouncedFetchProducts", h(() => {
51
55
  this.store.fetchRecommendationProducts();
52
56
  }, 500));
53
57
  /**
@@ -61,16 +65,16 @@ class Q extends g {
61
65
  * count. The store pads products to the configured size, so in-place only
62
66
  * fails when the size actually changed.
63
67
  */
64
- s(this, "_debouncedRegenerateWithProducts", m(() => {
68
+ n(this, "_debouncedRegenerateWithProducts", h(() => {
65
69
  const t = this.store.recommendationProducts;
66
- if (!this.currentNode || !this.api || D(this.currentNode))
70
+ if (!this.currentNode || !this.api || v(this.currentNode))
67
71
  return;
68
72
  const e = this.api.getDocumentModifier();
69
- E({
73
+ A({
70
74
  currentNode: this.currentNode,
71
75
  documentModifier: e,
72
76
  products: t
73
- }) || A({
77
+ }) || w({
74
78
  currentNode: this.currentNode,
75
79
  documentModifier: e,
76
80
  products: t
@@ -78,22 +82,40 @@ class Q extends g {
78
82
  }, 100));
79
83
  }
80
84
  getId() {
81
- return w;
85
+ return B;
82
86
  }
83
- getTemplate() {
84
- return this.algorithmControl = new N(), this.localeControl = new b(), this.currencyControl = new R(), this.productCountControl = new O(), this.productLayoutControl = new P(), this.filtersControl = new I(), this.shuffleControl = new S(), this.pricePlacementControl = new T(), this.layoutOrientationControl = new _(), [
87
+ /** Every sub-control the coordinator owns, in panel order. */
88
+ get _subControls() {
89
+ return [
90
+ this.layoutOrientationControl,
91
+ this.productCountControl,
85
92
  this.algorithmControl,
86
93
  this.localeControl,
87
94
  this.currencyControl,
88
- this.productCountControl,
89
95
  this.productLayoutControl,
90
96
  this.filtersControl,
91
97
  this.shuffleControl,
92
98
  this.pricePlacementControl,
93
- this.layoutOrientationControl
94
- ].forEach((e) => {
95
- e.api = this.api;
96
- }), `
99
+ this.strategyControl
100
+ ];
101
+ }
102
+ getTemplate() {
103
+ const t = p().isFeatureEnabled("reusableRecommendationStrategy");
104
+ return this._subControls.forEach((e) => {
105
+ var o;
106
+ return (o = e == null ? void 0 : e.onDestroy) == null ? void 0 : o.call(e);
107
+ }), this.algorithmControl = t ? null : new _(), this.localeControl = new b(), this.currencyControl = new I(), this.productCountControl = t ? null : new S(), this.productLayoutControl = new P(), this.filtersControl = t ? null : new R(), this.shuffleControl = t ? null : new L(), this.pricePlacementControl = new O(), this.layoutOrientationControl = new T(), this.strategyControl = t ? new k() : null, this._subControls.forEach((e) => {
108
+ e && (e.api = this.api);
109
+ }), t ? `
110
+ <div class="recommendation-controls-container">
111
+ ${this.strategyControl.getTemplate()}
112
+ ${this.layoutOrientationControl.getTemplate()}
113
+ ${this.localeControl.getTemplate()}
114
+ ${this.currencyControl.getTemplate()}
115
+ ${this.pricePlacementControl.getTemplate()}
116
+ ${this.productLayoutControl.getTemplate()}
117
+ </div>
118
+ ` : `
97
119
  <div class="recommendation-controls-container">
98
120
  ${this.layoutOrientationControl.getTemplate()}
99
121
  ${this.productCountControl.getTemplate()}
@@ -117,40 +139,20 @@ class Q extends g {
117
139
  await this._fetchBlockData(e), this._initializeSubControls();
118
140
  }
119
141
  onTemplateNodeUpdated(t) {
120
- var r;
142
+ var o;
121
143
  super.onTemplateNodeUpdated(t);
122
144
  const e = this._getRecommendationIdFromNode(t);
123
- e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && !this.syncedBlockIds.has(e) && (this.syncedBlockIds.add(e), this._syncNodeConfigToStore()), e !== null && !((r = this.store.blockStates[e]) != null && r.isInitialized) && this._fetchBlockData(e).then(() => {
145
+ e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && !this.syncedBlockIds.has(e) && (this.syncedBlockIds.add(e), this._syncNodeConfigToStore()), e !== null && !((o = this.store.blockStates[e]) != null && o.isInitialized) && this._fetchBlockData(e).then(() => {
124
146
  this._initializeSubControls();
125
- }), [
126
- this.layoutOrientationControl,
127
- this.productCountControl,
128
- this.algorithmControl,
129
- this.localeControl,
130
- this.currencyControl,
131
- this.productLayoutControl,
132
- this.filtersControl,
133
- this.shuffleControl,
134
- this.pricePlacementControl
135
- ].forEach((n) => {
136
- var i;
137
- n != null && n.api && (n.currentNode = t, (i = n.onTemplateNodeUpdated) == null || i.call(n, t));
147
+ }), this._subControls.forEach((r) => {
148
+ var s;
149
+ r != null && r.api && (r.currentNode = t, (s = r.onTemplateNodeUpdated) == null || s.call(r, t));
138
150
  });
139
151
  }
140
152
  onDestroy() {
141
- this.storeUnsubscription(), [
142
- this.layoutOrientationControl,
143
- this.productCountControl,
144
- this.algorithmControl,
145
- this.localeControl,
146
- this.currencyControl,
147
- this.productLayoutControl,
148
- this.filtersControl,
149
- this.shuffleControl,
150
- this.pricePlacementControl
151
- ].forEach((e) => {
152
- var o;
153
- return (o = e == null ? void 0 : e.onDestroy) == null ? void 0 : o.call(e);
153
+ this.storeUnsubscription(), this._subControls.forEach((t) => {
154
+ var e;
155
+ return (e = t == null ? void 0 : t.onDestroy) == null ? void 0 : e.call(t);
154
156
  });
155
157
  }
156
158
  /**
@@ -158,19 +160,9 @@ class Q extends g {
158
160
  * Each sub-control manages its own form values and event listeners
159
161
  */
160
162
  _initializeSubControls() {
161
- [
162
- this.layoutOrientationControl,
163
- this.productCountControl,
164
- this.algorithmControl,
165
- this.localeControl,
166
- this.currencyControl,
167
- this.productLayoutControl,
168
- this.filtersControl,
169
- this.shuffleControl,
170
- this.pricePlacementControl
171
- ].forEach((e) => {
172
- var o;
173
- e && (e.api = this.api, e.currentNode = this.currentNode, (o = e.onRender) == null || o.call(e));
163
+ this._subControls.forEach((t) => {
164
+ var e;
165
+ t && (t.api = this.api, t.currentNode = this.currentNode, (e = t.onRender) == null || e.call(t));
174
166
  });
175
167
  }
176
168
  /**
@@ -186,9 +178,10 @@ class Q extends g {
186
178
  */
187
179
  _syncNodeConfigToStore() {
188
180
  var r;
189
- const t = h.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
181
+ const t = m.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
190
182
  this.store.patchCurrentBlockConfig({
191
183
  strategy: t.strategy,
184
+ strategyId: t.strategyId,
192
185
  language: t.language,
193
186
  size: t.size,
194
187
  productIds: t.productIds,
@@ -249,36 +242,36 @@ class Q extends g {
249
242
  _applySmartDefaults() {
250
243
  if (!this.currentNode || !this.api)
251
244
  return;
252
- const t = h.getConfig(this.currentNode), e = {};
253
- let o = null, r = null, n = null;
254
- if (t.currency.code === c.currency.code) {
245
+ const t = m.getConfig(this.currentNode), e = {};
246
+ let o = null, r = null, s = null;
247
+ if (t.currency.code === a.currency.code) {
255
248
  const { currencyList: i } = this.store;
256
249
  i.length > 0 && (i.some(
257
- (u) => u.value === `price.${c.currency.code}`
250
+ (u) => u.value === `price.${a.currency.code}`
258
251
  ) || (o = i[0].value.replace("price.", ""), e.currency = {
259
- ...c.currency,
252
+ ...a.currency,
260
253
  code: o,
261
254
  symbol: o
262
255
  }));
263
256
  }
264
- if (t.strategy === c.strategy) {
257
+ if (t.strategy === a.strategy) {
265
258
  const i = this.store.getActivePredictiveAlgorithms;
266
259
  i.length > 0 && (i.some(
267
- (u) => u.value === c.strategy
260
+ (u) => u.value === a.strategy
268
261
  ) || (r = i[0].value, e.strategy = r));
269
262
  }
270
- if (t.language === c.language) {
263
+ if (t.language === a.language) {
271
264
  const i = this.store.getLanguages;
272
265
  i.length > 0 && (i.some(
273
- (u) => u.value === c.language
274
- ) || (n = i[0].value, e.language = n));
266
+ (u) => u.value === a.language
267
+ ) || (s = i[0].value, e.language = s));
275
268
  }
276
- !o && !r && !n || (h.updateConfig(
269
+ !o && !r && !s || (m.updateConfig(
277
270
  this.api,
278
271
  this.currentNode,
279
272
  e,
280
273
  "Applied smart defaults"
281
- ), o && e.currency && L({
274
+ ), o && e.currency && D({
282
275
  currentNode: this.currentNode,
283
276
  documentModifier: this.api.getDocumentModifier(),
284
277
  currency: e.currency
@@ -288,24 +281,24 @@ class Q extends g {
288
281
  name: o,
289
282
  value: o,
290
283
  symbol: o,
291
- alignment: c.currency.alignment === "before" ? "0" : "1",
292
- decimalCount: c.currency.decimalCount.toString(),
293
- decimalSeparator: c.currency.decimalSeparator,
294
- thousandSeparator: c.currency.thousandSeparator
284
+ alignment: a.currency.alignment === "before" ? "0" : "1",
285
+ decimalCount: a.currency.decimalCount.toString(),
286
+ decimalSeparator: a.currency.decimalSeparator,
287
+ thousandSeparator: a.currency.thousandSeparator
295
288
  }
296
289
  } : {},
297
290
  ...r ? { strategy: r } : {},
298
- ...n ? { language: n } : {}
291
+ ...s ? { language: s } : {}
299
292
  }, { triggerRefetch: !1 }));
300
293
  }
301
294
  /**
302
295
  * Reads the recommendation-id attribute from the block element within the node
303
296
  */
304
297
  _getRecommendationIdFromNode(t) {
305
- const e = k(t);
298
+ const e = E(t);
306
299
  if (!e || !("getAttribute" in e))
307
300
  return null;
308
- const o = e.getAttribute(v);
301
+ const o = e.getAttribute(x);
309
302
  if (!o)
310
303
  return null;
311
304
  const r = parseInt(o);
@@ -328,64 +321,73 @@ class Q extends g {
328
321
  const { store: t } = this;
329
322
  let e = t.recommendationProducts, o = t.$state.configVersion, r = t.currentRecommendationId;
330
323
  this.storeUnsubscription = t.$subscribe(() => {
331
- const n = t.currentRecommendationId;
332
- if (n !== r) {
333
- r = n, e = t.recommendationProducts, o = t.$state.configVersion;
324
+ const s = t.currentRecommendationId;
325
+ if (s !== r) {
326
+ r = s, e = t.recommendationProducts, o = t.$state.configVersion;
334
327
  return;
335
328
  }
336
329
  const i = t.$state.configVersion;
337
330
  i !== o && (o = i, this._persistFiltersToNodeConfig(), this._debouncedFetchProducts());
338
- const a = t.recommendationProducts, u = a !== e, C = Array.isArray(a) && a.length > 0;
339
- u && C && (e = a, this._debouncedRegenerateWithProducts());
331
+ const l = t.recommendationProducts, u = l !== e, f = Array.isArray(l) && l.length > 0;
332
+ u && f && (e = l, this._debouncedRegenerateWithProducts());
340
333
  });
341
334
  }
342
335
  /**
343
- * Persists the current filter state from Pinia store to the Stripo node config.
344
- * This ensures filters survive template save/reload cycles.
336
+ * Persists the current filter + strategy state from Pinia store to the
337
+ * Stripo node config. This ensures they survive template save/reload cycles
338
+ * (strategyId must be in esd-ext-config before any save — SD-149105).
339
+ *
340
+ * `size` rides along because assigning a strategy overwrites it in the
341
+ * store only. Without this write, `_syncNodeConfigToStore` re-seeds the
342
+ * store from the stale node config on the next panel open, and a block
343
+ * whose saved HTML has the strategy's slot count gets rebuilt against the
344
+ * old one — resetting migrator-applied per-element styles.
345
345
  */
346
346
  _persistFiltersToNodeConfig() {
347
347
  if (!this.currentNode || !this.api)
348
348
  return;
349
- const { filters: t } = this.store.recommendationConfigs, e = h.getConfig(this.currentNode).filters;
350
- JSON.stringify(e) !== JSON.stringify(t) && h.updateConfig(
349
+ const { filters: t, strategyId: e, size: o } = this.store.recommendationConfigs, r = m.getConfig(this.currentNode);
350
+ JSON.stringify(r.filters) === JSON.stringify(t) && r.strategyId === e && r.size === o || m.updateConfig(
351
351
  this.api,
352
352
  this.currentNode,
353
- { filters: t },
353
+ { filters: t, strategyId: e, size: o },
354
354
  "Update recommendation filters"
355
355
  );
356
356
  }
357
357
  }
358
358
  export {
359
- tt as ALGORITHM_CONTROL_ID,
360
- N as AlgorithmControl,
361
- w as CONTROL_BLOCK_ID,
362
- ot as CURRENCY_CONTROL_ID,
363
- R as CurrencyControl,
364
- nt as FILTERS_CONTROL_ID,
365
- I as FiltersControl,
366
- st as LAYOUT_ORIENTATION_CONTROL_ID,
367
- lt as LOCALE_CONTROL_ID,
368
- _ as LayoutOrientationControl,
359
+ nt as ALGORITHM_CONTROL_ID,
360
+ _ as AlgorithmControl,
361
+ B as CONTROL_BLOCK_ID,
362
+ st as CURRENCY_CONTROL_ID,
363
+ I as CurrencyControl,
364
+ ct as FILTERS_CONTROL_ID,
365
+ R as FiltersControl,
366
+ ut as LAYOUT_ORIENTATION_CONTROL_ID,
367
+ mt as LOCALE_CONTROL_ID,
368
+ T as LayoutOrientationControl,
369
369
  b as LocaleControl,
370
- ut as PRICE_PLACEMENT_CONTROL_ID,
371
- ht as PRODUCT_COUNT_CONTROL_ID,
372
- Ct as PRODUCT_LAYOUT_CONTROL_ID,
373
- T as PricePlacementControl,
374
- O as ProductCountControl,
370
+ ft as PRICE_PLACEMENT_CONTROL_ID,
371
+ gt as PRODUCT_COUNT_CONTROL_ID,
372
+ yt as PRODUCT_LAYOUT_CONTROL_ID,
373
+ O as PricePlacementControl,
374
+ S as ProductCountControl,
375
375
  P as ProductLayoutControl,
376
- Q as RecommendationBlockControl,
377
- pt as SHUFFLE_CONTROL_ID,
378
- S as ShuffleControl,
379
- yt as adjustProductsToSize,
380
- Nt as formatProductPrice,
381
- k as getBlockElement,
382
- Rt as getCardComposition,
383
- It as getCurrentLayout,
384
- D as isPartnerManagedBlock,
385
- _t as regenerateMobileProductRows,
386
- A as regenerateProductRowsWithStyles,
387
- L as setCurrencyAttributes,
388
- bt as updatePricesInPlace,
389
- E as updateProductContentInPlace,
390
- Tt as updateSingleProductContent
376
+ et as RecommendationBlockControl,
377
+ _t as SHUFFLE_CONTROL_ID,
378
+ Rt as STRATEGY_CONTROL_ID,
379
+ L as ShuffleControl,
380
+ k as StrategyControl,
381
+ bt as adjustProductsToSize,
382
+ Ot as formatProductPrice,
383
+ E as getBlockElement,
384
+ St as getCardComposition,
385
+ Pt as getCurrentLayout,
386
+ v as isPartnerManagedBlock,
387
+ Lt as regenerateMobileProductRows,
388
+ w as regenerateProductRowsWithStyles,
389
+ D as setCurrencyAttributes,
390
+ kt as updatePricesInPlace,
391
+ A as updateProductContentInPlace,
392
+ Dt as updateSingleProductContent
391
393
  };
@@ -0,0 +1,141 @@
1
+ var g = Object.defineProperty;
2
+ var d = (r, s, t) => s in r ? g(r, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[s] = t;
3
+ var o = (r, s, t) => d(r, typeof s != "symbol" ? s + "" : s, t);
4
+ import { ACADEMY_LINKS as l } from "../../../../../enums/academy.js";
5
+ import { CommonControl as y } from "../../../common-control.js";
6
+ import { useRecommendationExtensionStore as h } from "../../store/recommendation.js";
7
+ const m = "recommendation-strategy-control";
8
+ class f extends y {
9
+ constructor() {
10
+ super(...arguments);
11
+ o(this, "store", h());
12
+ o(this, "storeUnsubscription", () => {
13
+ });
14
+ o(this, "listeners", []);
15
+ }
16
+ getId() {
17
+ return m;
18
+ }
19
+ getTemplate() {
20
+ const t = this.api.translate(
21
+ "You can assign a recommendation strategy by selecting an existing one or creating a new one to show specific content."
22
+ ), e = this.api.translate("For more information, you can"), a = this.api.translate("visit Academy");
23
+ return `
24
+ <div class="strategy-control-container">
25
+ <!-- A section heading, not a field label: the ue-label element renders
26
+ the panel's 13px field styling and the design asks for a 16px/600
27
+ heading. Kept outside a .container so the editor draws no rule. -->
28
+ <div class="strategy-control-header">
29
+ <h3 class="strategy-control-title">${this.api.translate("Recommendation Strategy")}</h3>
30
+ </div>
31
+ <div class="strategy-control-description-row">
32
+ <p class="strategy-control-description">
33
+ ${t}
34
+ ${e}
35
+ <a
36
+ href="${l.EMAIL_RECOMMENDATION_BLOCK}"
37
+ target="_blank"
38
+ rel="noopener noreferrer">${a}</a>.
39
+ </p>
40
+ </div>
41
+ <div data-strategy-empty-state>
42
+ ${this._GuTwoColumns([
43
+ this._GuButton({
44
+ name: "assignStrategy",
45
+ label: this.api.translate("Assign Strategy"),
46
+ id: "guido__btn-assign-strategy",
47
+ className: "strategy-control-cta accent"
48
+ }),
49
+ // Create round-trip ships in a later phase. Disabled, not dimmed:
50
+ // the design keeps it at full contrast.
51
+ this._GuButton({
52
+ name: "createStrategy",
53
+ label: this.api.translate("Create New Strategy"),
54
+ id: "guido__btn-create-strategy",
55
+ className: "strategy-control-cta primary",
56
+ disabled: !0
57
+ })
58
+ ], "strategy-control-cta-row")}
59
+ </div>
60
+ <div data-strategy-assigned-state style="display: none;">
61
+ <!-- The name band bleeds to the panel edges, so it is a sibling of the
62
+ actions row rather than sharing its 16px inset. -->
63
+ <div class="strategy-control-name-band">
64
+ <span class="strategy-control-name" data-strategy-name></span>
65
+ </div>
66
+ <div class="strategy-control-actions">
67
+ ${this._strategyAction({
68
+ id: "guido__btn-change-strategy",
69
+ label: this.api.translate("Change"),
70
+ modifier: "change"
71
+ })}
72
+ ${this._strategyAction({
73
+ id: "guido__btn-remove-strategy",
74
+ label: this.api.translate("Remove"),
75
+ modifier: "remove"
76
+ })}
77
+ </div>
78
+ </div>
79
+ </div>
80
+ `;
81
+ }
82
+ /**
83
+ * Change / Remove are icon+text actions in the design, not buttons — and
84
+ * `ue-button` cannot pair a caption with an arbitrary icon, so these are
85
+ * plain elements. Click wiring is by id, which works on any element.
86
+ *
87
+ * The icon is a CSS mask, not inline SVG: Stripo re-creates the control's
88
+ * markup through Angular, which rebuilds `<svg>` outside the SVG namespace
89
+ * (the view-box attribute comes back lowercased) and it never paints.
90
+ */
91
+ _strategyAction({ id: t, label: e, modifier: a }) {
92
+ return `
93
+ <button
94
+ id="${t}"
95
+ type="button"
96
+ class="strategy-control-action strategy-control-action--${a}">
97
+ <span>${e}</span>
98
+ </button>
99
+ `;
100
+ }
101
+ onRender() {
102
+ this._teardown(), this._setupButtonListeners(), this._renderState(), this.storeUnsubscription = this.store.$subscribe(() => {
103
+ this._renderState();
104
+ }), this.store.recommendationConfigs.strategyId && !this.store.strategies.length && this.store.fetchStrategies().catch(() => {
105
+ });
106
+ }
107
+ onDestroy() {
108
+ this._teardown();
109
+ }
110
+ _teardown() {
111
+ this.storeUnsubscription(), this.storeUnsubscription = () => {
112
+ }, this.listeners.forEach(({ element: t, handler: e }) => t.removeEventListener("click", e)), this.listeners = [];
113
+ }
114
+ _setupButtonListeners() {
115
+ const t = this.getContainer();
116
+ if (!t)
117
+ return;
118
+ const e = (a, i) => {
119
+ const n = t.querySelector(a);
120
+ n && (n.addEventListener("click", i), this.listeners.push({ element: n, handler: i }));
121
+ };
122
+ e("#guido__btn-assign-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-change-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-remove-strategy", () => this.store.removeStrategy());
123
+ }
124
+ /**
125
+ * Toggles empty vs assigned state from the store's current block config.
126
+ * Display name comes from the listing; falls back to the raw id while the
127
+ * listing loads (or when the strategy is no longer listed).
128
+ */
129
+ _renderState() {
130
+ var c;
131
+ const t = this.getContainer();
132
+ if (!t)
133
+ return;
134
+ const e = t.querySelector("[data-strategy-empty-state]"), a = t.querySelector("[data-strategy-assigned-state]"), i = t.querySelector("[data-strategy-name]"), { strategyId: n } = this.store.recommendationConfigs;
135
+ !e || !a || (e.style.display = n ? "none" : "", a.style.display = n ? "" : "none", i && n && (i.textContent = ((c = this.store.assignedStrategy) == null ? void 0 : c.name) ?? `#${n}`));
136
+ }
137
+ }
138
+ export {
139
+ m as STRATEGY_CONTROL_ID,
140
+ f as StrategyControl
141
+ };