@useinsider/guido 3.17.0-beta.668ca65 → 3.17.0-beta.6b602e7

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 (73) hide show
  1. package/README.md +5 -5
  2. package/dist/components/Guido.vue.js +7 -7
  3. package/dist/components/Guido.vue2.js +65 -61
  4. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +3 -3
  5. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +78 -52
  6. package/dist/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.js +18 -0
  7. package/dist/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue2.js +25 -0
  8. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue.js +17 -0
  9. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +57 -0
  10. package/dist/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue2.js +36 -35
  11. package/dist/composables/useCustomInterfaceAppearance.js +46 -42
  12. package/dist/composables/useLegacyStrategyConversion.js +52 -0
  13. package/dist/composables/useStripo.js +14 -14
  14. package/dist/config/i18n/en/labels.json.js +1 -1
  15. package/dist/config/migrator/index.js +9 -8
  16. package/dist/config/migrator/recommendation/compositionMapper.js +20 -20
  17. package/dist/config/migrator/recommendation/extractors.js +29 -24
  18. package/dist/config/migrator/recommendation/htmlBuilder.js +116 -130
  19. package/dist/config/migrator/recommendationMigrator.js +30 -25
  20. package/dist/config/migrator/recommendationTitleMigrator.js +119 -0
  21. package/dist/enums/extensions/recommendationBlock.js +2 -1
  22. package/dist/extensions/Blocks/Recommendation/block.js +12 -11
  23. package/dist/extensions/Blocks/Recommendation/constants/blockIds.js +1 -1
  24. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  25. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +24 -22
  26. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +35 -28
  27. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +329 -257
  28. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +211 -145
  29. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +66 -31
  30. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +1 -0
  31. package/dist/extensions/Blocks/Recommendation/controls/title/index.js +44 -0
  32. package/dist/extensions/Blocks/Recommendation/extension.js +31 -29
  33. package/dist/extensions/Blocks/Recommendation/services/configService.js +34 -16
  34. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +23 -4
  35. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +93 -42
  36. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +3 -3
  37. package/dist/extensions/Blocks/Recommendation/templates/titleRows.js +33 -0
  38. package/dist/extensions/Blocks/Recommendation/templates/utils.js +85 -107
  39. package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js +58 -0
  40. package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +22 -0
  41. package/dist/guido.css +1 -1
  42. package/dist/services/recommendationApi.js +61 -52
  43. package/dist/src/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.d.ts +2 -0
  44. package/dist/src/components/organisms/extensions/recommendation/RemoveStrategyModal.vue.d.ts +2 -0
  45. package/dist/src/composables/useLegacyStrategyConversion.d.ts +3 -0
  46. package/dist/src/config/migrator/recommendation/extractors.d.ts +8 -0
  47. package/dist/src/config/migrator/recommendationTitleMigrator.d.ts +5 -0
  48. package/dist/src/enums/extensions/recommendationBlock.d.ts +1 -0
  49. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +2 -1
  50. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +7 -0
  51. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +1 -0
  52. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  53. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +12 -0
  54. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +11 -0
  55. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  56. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +38 -8
  57. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +1 -0
  58. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +1 -0
  59. package/dist/src/extensions/Blocks/Recommendation/controls/title/index.d.ts +86 -0
  60. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +12 -1
  61. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +80 -1
  62. package/dist/src/extensions/Blocks/Recommendation/templates/titleRows.d.ts +13 -0
  63. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  64. package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.d.ts +39 -0
  65. package/dist/src/extensions/Blocks/Recommendation/utils/nodeConfigWriter.d.ts +31 -0
  66. package/dist/src/services/recommendationApi.d.ts +2 -1
  67. package/dist/src/utils/genericUtil.d.ts +5 -0
  68. package/dist/static/assets/icons/filled-arrow-rising.svg.js +5 -0
  69. package/dist/static/assets/icons/line-info.svg.js +5 -0
  70. package/dist/static/styles/components/recommendation-strategy.css.js +70 -5
  71. package/dist/static/styles/customEditorStyle.css.js +3 -1
  72. package/dist/utils/genericUtil.js +28 -27
  73. package/package.json +2 -2
@@ -1,59 +1,71 @@
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);
1
+ var m = Object.defineProperty;
2
+ var C = (g, l, t) => l in g ? m(g, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[l] = t;
3
+ var r = (g, l, t) => C(g, typeof l != "symbol" ? l + "" : l, t);
4
4
  import { useConfig as p } from "../../../../../composables/useConfig.js";
5
- import { CommonControl as y } from "../../../common-control.js";
5
+ import { ModificationDescription as y } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
+ import { CommonControl as N } from "../../../common-control.js";
6
7
  import { CURRENT_CONFIG_VERSION as _, 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 I } from "./algorithm.js";
10
- import { ALGORITHM_CONTROL_ID as it } from "./algorithm.js";
11
- import { CurrencyControl as R } from "./currency.js";
12
- import { CURRENCY_CONTROL_ID as at } from "./currency.js";
13
- import { FiltersControl as b } from "./filters.js";
14
- import { FILTERS_CONTROL_ID as lt } from "./filters.js";
15
- import { LayoutOrientationControl as T } from "./layoutOrientation.js";
16
- import { LAYOUT_ORIENTATION_CONTROL_ID as dt } from "./layoutOrientation.js";
17
- import { LocaleControl as S } from "./locale.js";
18
- import { LOCALE_CONTROL_ID as ht } from "./locale.js";
19
- import { PricePlacementControl as O } from "./pricePlacement.js";
20
- import { PRICE_PLACEMENT_CONTROL_ID as Ct } from "./pricePlacement.js";
21
- import { ProductCountControl as P } from "./productCount.js";
22
- import { PRODUCT_COUNT_CONTROL_ID as pt } from "./productCount.js";
23
- import { ProductLayoutControl as L } from "./productLayout.js";
24
- import { PRODUCT_LAYOUT_CONTROL_ID as _t } from "./productLayout.js";
25
- import { ShuffleControl as k } from "./shuffle.js";
26
- import { SHUFFLE_CONTROL_ID as It } from "./shuffle.js";
27
- import { StrategyControl as E } from "./strategy.js";
28
- import { STRATEGY_CONTROL_ID as bt } from "./strategy.js";
29
- import { setCurrencyAttributes as D, getBlockElement as B, isPartnerManagedBlock as w, updateProductContentInPlace as A, regenerateProductRowsWithStyles as v } from "./utils.js";
30
- import { adjustProductsToSize as St, formatProductPrice as Ot, getCardComposition as Pt, getCurrentLayout as Lt, regenerateMobileProductRows as kt, updatePricesInPlace as Et, updateSingleProductContent as Dt } from "./utils.js";
31
- import { useDebounceFn as h } from "../../../../../node_modules/@vueuse/shared/index.js";
32
- const x = "recommendation-id", F = "ui-elements-recommendation-block";
33
- class ot extends y {
8
+ import { RECOMMENDATION_ID_ATTR as I } from "../../constants/selectors.js";
9
+ import { RecommendationConfigService as c } from "../../services/configService.js";
10
+ import { useRecommendationExtensionStore as R } from "../../store/recommendation.js";
11
+ import { registerNodeConfigWriter as b } from "../../utils/nodeConfigWriter.js";
12
+ import { AlgorithmControl as T } from "./algorithm.js";
13
+ import { ALGORITHM_CONTROL_ID as ut } from "./algorithm.js";
14
+ import { CurrencyControl as P } from "./currency.js";
15
+ import { CURRENCY_CONTROL_ID as gt } from "./currency.js";
16
+ import { FiltersControl as S } from "./filters.js";
17
+ import { FILTERS_CONTROL_ID as ht } from "./filters.js";
18
+ import { LayoutOrientationControl as O } from "./layoutOrientation.js";
19
+ import { LAYOUT_ORIENTATION_CONTROL_ID as Ct } from "./layoutOrientation.js";
20
+ import { LocaleControl as L } from "./locale.js";
21
+ import { LOCALE_CONTROL_ID as yt } from "./locale.js";
22
+ import { PricePlacementControl as E } from "./pricePlacement.js";
23
+ import { PRICE_PLACEMENT_CONTROL_ID as _t } from "./pricePlacement.js";
24
+ import { ProductCountControl as k } from "./productCount.js";
25
+ import { PRODUCT_COUNT_CONTROL_ID as Rt } from "./productCount.js";
26
+ import { ProductLayoutControl as D } from "./productLayout.js";
27
+ import { PRODUCT_LAYOUT_CONTROL_ID as Tt } from "./productLayout.js";
28
+ import { ShuffleControl as F } from "./shuffle.js";
29
+ import { SHUFFLE_CONTROL_ID as St } from "./shuffle.js";
30
+ import { StrategyControl as w } from "./strategy.js";
31
+ import { STRATEGY_CONTROL_ID as Lt } from "./strategy.js";
32
+ import { setCurrencyAttributes as A, getBlockElement as B, isPartnerManagedBlock as v, updateProductContentInPlace as x, regenerateProductRowsWithStyles as U } from "./utils.js";
33
+ import { adjustProductsToSize as kt, formatProductPrice as Dt, getCardComposition as Ft, getCurrentLayout as wt, getMainContainerTbody as At, regenerateMobileProductRows as Bt, updatePricesInPlace as vt, updateSingleProductContent as xt } from "./utils.js";
34
+ import { useDebounceFn as f } from "../../../../../node_modules/@vueuse/shared/index.js";
35
+ const $ = "ui-elements-recommendation-block";
36
+ class at extends N {
34
37
  constructor() {
35
38
  super(...arguments);
36
- n(this, "store", N());
37
- n(this, "storeUnsubscription", () => {
39
+ r(this, "store", R());
40
+ r(this, "storeUnsubscription", () => {
38
41
  });
39
- n(this, "syncedBlockIds", /* @__PURE__ */ new Set());
42
+ // blockId -> fingerprint of the node config as it looked at the last sync.
43
+ r(this, "syncedConfigs", /* @__PURE__ */ new Map());
44
+ // A config change waiting to reach the node config. Deferred so it can ride the
45
+ // re-render's single apply() — one user action must cost one undo press.
46
+ r(this, "pendingNodeConfigWrite", null);
47
+ r(this, "pendingConfigFlushTimer", null);
40
48
  // Sub-control instances for lifecycle management
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);
51
- n(this, "rrsEnabled", !1);
49
+ r(this, "algorithmControl", null);
50
+ r(this, "localeControl", null);
51
+ r(this, "currencyControl", null);
52
+ r(this, "productCountControl", null);
53
+ r(this, "productLayoutControl", null);
54
+ r(this, "filtersControl", null);
55
+ r(this, "shuffleControl", null);
56
+ r(this, "pricePlacementControl", null);
57
+ r(this, "layoutOrientationControl", null);
58
+ r(this, "strategyControl", null);
59
+ r(this, "rrsEnabled", !1);
52
60
  /**
53
61
  * Debounced product fetch to prevent rapid API calls during config changes
54
62
  */
55
- n(this, "_debouncedFetchProducts", h(() => {
56
- this.store.fetchRecommendationProducts();
63
+ r(this, "_debouncedFetchProducts", f(async () => {
64
+ try {
65
+ await this.store.fetchRecommendationProducts();
66
+ } finally {
67
+ this._scheduleConfigFlush();
68
+ }
57
69
  }, 500));
58
70
  /**
59
71
  * Debounced content update when products arrive from API.
@@ -66,16 +78,20 @@ class ot extends y {
66
78
  * count. The store pads products to the configured size, so in-place only
67
79
  * fails when the size actually changed.
68
80
  */
69
- n(this, "_debouncedRegenerateWithProducts", h(() => {
81
+ r(this, "_debouncedRegenerateWithProducts", f(() => {
70
82
  const t = this.store.recommendationProducts;
71
- if (!this.currentNode || !this.api || w(this.currentNode))
83
+ if (!this.currentNode || !this.api)
84
+ return;
85
+ const e = this.api.getDocumentModifier(), o = this._stagePendingNodeConfig(e);
86
+ if (v(this.currentNode)) {
87
+ o && (e.apply(new y("Update recommendation configuration")), this.pendingNodeConfigWrite = null, this._clearConfigFlush());
72
88
  return;
73
- const e = this.api.getDocumentModifier();
74
- A({
89
+ }
90
+ x({
75
91
  currentNode: this.currentNode,
76
92
  documentModifier: e,
77
93
  products: t
78
- }) || v({
94
+ }) || U({
79
95
  currentNode: this.currentNode,
80
96
  documentModifier: e,
81
97
  products: t
@@ -83,7 +99,7 @@ class ot extends y {
83
99
  }, 100));
84
100
  }
85
101
  getId() {
86
- return F;
102
+ return $;
87
103
  }
88
104
  get _subControls() {
89
105
  return [
@@ -104,7 +120,7 @@ class ot extends y {
104
120
  return this.rrsEnabled = p().isFeatureEnabled("reusableRecommendationStrategy"), this._subControls.forEach((e) => {
105
121
  var o;
106
122
  return (o = e == null ? void 0 : e.onDestroy) == null ? void 0 : o.call(e);
107
- }), this.algorithmControl = new I(), this.localeControl = new S(), this.currencyControl = new R(), this.productCountControl = new P(), this.productLayoutControl = new L(), this.filtersControl = new b(), this.shuffleControl = new k(), this.pricePlacementControl = new O(), this.layoutOrientationControl = new T(), this.strategyControl = this.rrsEnabled ? new E() : null, this._subControls.forEach((e) => {
123
+ }), this.algorithmControl = new T(), this.localeControl = new L(), this.currencyControl = new P(), this.productCountControl = new k(), this.productLayoutControl = new D(), this.filtersControl = new S(), this.shuffleControl = new F(), this.pricePlacementControl = new E(), this.layoutOrientationControl = new O(), this.strategyControl = this.rrsEnabled ? new w() : null, this._subControls.forEach((e) => {
108
124
  e && (e.api = this.api);
109
125
  }), `
110
126
  <div class="recommendation-controls-container">
@@ -126,13 +142,14 @@ class ot extends y {
126
142
  const e = (i = this.getContainer()) == null ? void 0 : i.querySelector(".recommendation-controls-container");
127
143
  if (!e)
128
144
  return;
129
- const o = this._getRecommendationIdFromNode(t) === null ? null : m.getConfig(t), r = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= _);
130
- e.classList.toggle("strategy-block", r);
145
+ const o = this._getRecommendationIdFromNode(t) === null ? null : c.getConfig(t), n = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= _), s = this.rrsEnabled && !!o && !n;
146
+ e.classList.toggle("strategy-block", n), e.classList.toggle("legacy-strategy-block", s);
131
147
  }
132
148
  async onRender() {
133
149
  var o;
150
+ b(this.api);
134
151
  const t = this._getRecommendationIdFromNode(this.currentNode), e = t ?? this.store.currentRecommendationId;
135
- if (e !== null && this.store.setCurrentBlock(e), t !== null && !this.syncedBlockIds.has(t) && (this.syncedBlockIds.add(t), this._syncNodeConfigToStore()), this._listenStateUpdates(), this._showControlsForBlock(this.currentNode), e !== null && ((o = this.store.blockStates[e]) != null && o.isInitialized)) {
152
+ if (e !== null && this.store.setCurrentBlock(e), t !== null && this._syncNodeConfigIfChanged(t), this._listenStateUpdates(), this._showControlsForBlock(this.currentNode), e !== null && ((o = this.store.blockStates[e]) != null && o.isInitialized)) {
136
153
  this._initializeSubControls();
137
154
  return;
138
155
  }
@@ -142,15 +159,15 @@ class ot extends y {
142
159
  var o;
143
160
  super.onTemplateNodeUpdated(t);
144
161
  const e = this._getRecommendationIdFromNode(t);
145
- e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && !this.syncedBlockIds.has(e) && (this.syncedBlockIds.add(e), this._syncNodeConfigToStore()), this._showControlsForBlock(t), e !== null && !((o = this.store.blockStates[e]) != null && o.isInitialized) && this._fetchBlockData(e).then(() => {
162
+ e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && this._syncNodeConfigIfChanged(e), this._showControlsForBlock(t), e !== null && !((o = this.store.blockStates[e]) != null && o.isInitialized) && this._fetchBlockData(e).then(() => {
146
163
  this._initializeSubControls();
147
- }), this._subControls.forEach((r) => {
148
- var i;
149
- r != null && r.api && (r.currentNode = t, (i = r.onTemplateNodeUpdated) == null || i.call(r, t));
164
+ }), this._subControls.forEach((n) => {
165
+ var s;
166
+ n != null && n.api && (n.currentNode = t, (s = n.onTemplateNodeUpdated) == null || s.call(n, t));
150
167
  });
151
168
  }
152
169
  onDestroy() {
153
- this.storeUnsubscription(), this._subControls.forEach((t) => {
170
+ this.storeUnsubscription(), this._flushPendingNodeConfig(), this._subControls.forEach((t) => {
154
171
  var e;
155
172
  return (e = t == null ? void 0 : t.onDestroy) == null ? void 0 : e.call(t);
156
173
  });
@@ -175,12 +192,77 @@ class ot extends y {
175
192
  * Uses triggerRefetch: false because the fetch hasn't happened yet —
176
193
  * values are being prepared for the upcoming initial fetch.
177
194
  */
195
+ /**
196
+ * Re-syncs the store when the node config moved since we last read it.
197
+ *
198
+ * Undo/redo, the code editor and version-history restore rewrite the node
199
+ * config behind the store's back — Stripo exposes no undo signal, so a
200
+ * fingerprint diff on this hook is the only way to notice (SD-149105).
201
+ * An unchanged fingerprint means the move came from us, so runtime store
202
+ * edits are never clobbered (SD-141049).
203
+ */
204
+ _syncNodeConfigIfChanged(t) {
205
+ this.pendingNodeConfigWrite && this.pendingNodeConfigWrite.blockId !== t && this._flushPendingNodeConfig();
206
+ const e = JSON.stringify(c.getConfig(this.currentNode)), o = this.syncedConfigs.get(t);
207
+ this.syncedConfigs.set(t, e), o !== e && (this.pendingNodeConfigWrite || this._syncNodeConfigToStore());
208
+ }
209
+ _capturePendingNodeConfigWrite() {
210
+ const t = this.store.currentRecommendationId;
211
+ if (!this.currentNode || !this.api || t === null)
212
+ return;
213
+ const { filters: e, strategyId: o, size: n, nodeConfigVersion: s } = this.store.recommendationConfigs;
214
+ this.pendingNodeConfigWrite = {
215
+ blockId: t,
216
+ node: this.currentNode,
217
+ updates: { filters: e, strategyId: o, size: n, configVersion: s }
218
+ };
219
+ }
220
+ /**
221
+ * Accumulates the pending write into `documentModifier` without applying, so the
222
+ * caller's apply() commits config and re-render as ONE undo entry.
223
+ *
224
+ * Deliberately does not consume the pending record: the product helpers call
225
+ * apply() only when they actually changed something, so this batch may never be
226
+ * committed and the flush has to stay able to write it standalone.
227
+ * @param documentModifier - The batch to accumulate into
228
+ * @returns Whether anything was staged
229
+ */
230
+ _stagePendingNodeConfig(t) {
231
+ const e = this.pendingNodeConfigWrite;
232
+ return !e || !this.currentNode ? !1 : (c.stageConfig(t, this.currentNode, e.updates), !0);
233
+ }
234
+ /**
235
+ * Arms the guaranteed fallback write, once the product fetch has settled. The
236
+ * delay only has to outlast the re-render debounce that fetch triggers — firing
237
+ * earlier writes the config on its own and the undo entry splits in two again.
238
+ */
239
+ _scheduleConfigFlush() {
240
+ this._clearConfigFlush(), this.pendingConfigFlushTimer = setTimeout(() => {
241
+ this.pendingConfigFlushTimer = null, this._flushPendingNodeConfig();
242
+ }, 800);
243
+ }
244
+ _clearConfigFlush() {
245
+ this.pendingConfigFlushTimer !== null && (clearTimeout(this.pendingConfigFlushTimer), this.pendingConfigFlushTimer = null);
246
+ }
247
+ /**
248
+ * Writes the deferred change standalone when the batch it was staged into never
249
+ * committed. Always targets the LIVE node — the captured one is an immutable
250
+ * snapshot the intervening re-render staled, and saveConfig swallows that failure.
251
+ */
252
+ _flushPendingNodeConfig() {
253
+ const t = this.pendingNodeConfigWrite;
254
+ if (this.pendingNodeConfigWrite = null, this._clearConfigFlush(), !t || !this.api)
255
+ return;
256
+ const e = t.blockId === this.store.currentRecommendationId && this.currentNode ? this.currentNode : t.node, o = c.getConfig(e);
257
+ JSON.stringify(o.filters) === JSON.stringify(t.updates.filters) && o.strategyId === t.updates.strategyId && o.size === t.updates.size || c.updateConfig(this.api, e, t.updates, "Update recommendation filters");
258
+ }
178
259
  _syncNodeConfigToStore() {
179
- var r;
180
- const t = m.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
260
+ var n;
261
+ const t = c.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((n = this.store.blockStates[e]) == null ? void 0 : n.isInitialized);
181
262
  this.store.patchCurrentBlockConfig({
182
263
  strategy: t.strategy,
183
264
  strategyId: t.strategyId,
265
+ nodeConfigVersion: t.configVersion,
184
266
  language: t.language,
185
267
  size: t.size,
186
268
  productIds: t.productIds,
@@ -219,8 +301,8 @@ class ot extends y {
219
301
  t !== null && this.store.markBlockInitialized(t), (await Promise.allSettled([
220
302
  this.store.fetchRecommendationCreateData(),
221
303
  this.store.fetchRecommendationFilters()
222
- ])).forEach((o, r) => {
223
- o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][r]} failed`, o.reason);
304
+ ])).forEach((o, n) => {
305
+ o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][n]} failed`, o.reason);
224
306
  }), this._applySmartDefaults();
225
307
  try {
226
308
  await this.store.fetchRecommendationProducts();
@@ -241,36 +323,36 @@ class ot extends y {
241
323
  _applySmartDefaults() {
242
324
  if (!this.currentNode || !this.api)
243
325
  return;
244
- const t = m.getConfig(this.currentNode), e = {};
245
- let o = null, r = null, i = null;
326
+ const t = c.getConfig(this.currentNode), e = {};
327
+ let o = null, n = null, s = null;
246
328
  if (t.currency.code === a.currency.code) {
247
- const { currencyList: s } = this.store;
248
- s.length > 0 && (s.some(
249
- (u) => u.value === `price.${a.currency.code}`
250
- ) || (o = s[0].value.replace("price.", ""), e.currency = {
329
+ const { currencyList: i } = this.store;
330
+ i.length > 0 && (i.some(
331
+ (d) => d.value === `price.${a.currency.code}`
332
+ ) || (o = i[0].value.replace("price.", ""), e.currency = {
251
333
  ...a.currency,
252
334
  code: o,
253
335
  symbol: o
254
336
  }));
255
337
  }
256
338
  if (t.strategy === a.strategy) {
257
- const s = this.store.getActivePredictiveAlgorithms;
258
- s.length > 0 && (s.some(
259
- (u) => u.value === a.strategy
260
- ) || (r = s[0].value, e.strategy = r));
339
+ const i = this.store.getActivePredictiveAlgorithms;
340
+ i.length > 0 && (i.some(
341
+ (d) => d.value === a.strategy
342
+ ) || (n = i[0].value, e.strategy = n));
261
343
  }
262
344
  if (t.language === a.language) {
263
- const s = this.store.getLanguages;
264
- s.length > 0 && (s.some(
265
- (u) => u.value === a.language
266
- ) || (i = s[0].value, e.language = i));
345
+ const i = this.store.getLanguages;
346
+ i.length > 0 && (i.some(
347
+ (d) => d.value === a.language
348
+ ) || (s = i[0].value, e.language = s));
267
349
  }
268
- !o && !r && !i || (m.updateConfig(
350
+ !o && !n && !s || (c.updateConfig(
269
351
  this.api,
270
352
  this.currentNode,
271
353
  e,
272
354
  "Applied smart defaults"
273
- ), o && e.currency && D({
355
+ ), o && e.currency && A({
274
356
  currentNode: this.currentNode,
275
357
  documentModifier: this.api.getDocumentModifier(),
276
358
  currency: e.currency
@@ -286,8 +368,8 @@ class ot extends y {
286
368
  thousandSeparator: a.currency.thousandSeparator
287
369
  }
288
370
  } : {},
289
- ...r ? { strategy: r } : {},
290
- ...i ? { language: i } : {}
371
+ ...n ? { strategy: n } : {},
372
+ ...s ? { language: s } : {}
291
373
  }, { triggerRefetch: !1 }));
292
374
  }
293
375
  /**
@@ -297,11 +379,11 @@ class ot extends y {
297
379
  const e = B(t);
298
380
  if (!e || !("getAttribute" in e))
299
381
  return null;
300
- const o = e.getAttribute(x);
382
+ const o = e.getAttribute(I);
301
383
  if (!o)
302
384
  return null;
303
- const r = parseInt(o);
304
- return Number.isNaN(r) ? null : r;
385
+ const n = parseInt(o);
386
+ return Number.isNaN(n) ? null : n;
305
387
  }
306
388
  /**
307
389
  * Listen to store changes that require product refresh or regeneration.
@@ -318,70 +400,54 @@ class ot extends y {
318
400
  */
319
401
  _listenStateUpdates() {
320
402
  const { store: t } = this;
321
- let e = t.recommendationProducts, o = t.$state.configVersion, r = t.currentRecommendationId;
403
+ let e = t.recommendationProducts, o = t.$state.configVersion, n = t.currentRecommendationId;
322
404
  this.storeUnsubscription = t.$subscribe(() => {
323
- const i = t.currentRecommendationId;
324
- if (i !== r) {
325
- r = i, e = t.recommendationProducts, o = t.$state.configVersion;
405
+ const s = t.currentRecommendationId;
406
+ if (s !== n) {
407
+ n = s, e = t.recommendationProducts, o = t.$state.configVersion;
326
408
  return;
327
409
  }
328
- const s = t.$state.configVersion;
329
- s !== o && (o = s, this._persistFiltersToNodeConfig(), this._debouncedFetchProducts());
330
- const l = t.recommendationProducts, u = l !== e, f = Array.isArray(l) && l.length > 0;
331
- u && f && (e = l, this._debouncedRegenerateWithProducts());
410
+ const i = t.$state.configVersion;
411
+ i !== o && (o = i, this._capturePendingNodeConfigWrite(), this._debouncedFetchProducts());
412
+ const u = t.recommendationProducts, d = u !== e, h = Array.isArray(u) && u.length > 0;
413
+ d && h && (e = u, this._debouncedRegenerateWithProducts());
332
414
  });
333
415
  }
334
- /**
335
- * Persists filter + strategy state to the Stripo node config so it survives
336
- * save/reload (strategyId must reach esd-ext-config before any save).
337
- * `size` rides along: assigning a strategy writes it to the store only, and a
338
- * stale node config would rebuild the block against the old slot count.
339
- */
340
- _persistFiltersToNodeConfig() {
341
- if (!this.currentNode || !this.api)
342
- return;
343
- const { filters: t, strategyId: e, size: o } = this.store.recommendationConfigs, r = m.getConfig(this.currentNode);
344
- JSON.stringify(r.filters) === JSON.stringify(t) && r.strategyId === e && r.size === o || m.updateConfig(
345
- this.api,
346
- this.currentNode,
347
- { filters: t, strategyId: e, size: o },
348
- "Update recommendation filters"
349
- );
350
- }
351
416
  }
352
417
  export {
353
- it as ALGORITHM_CONTROL_ID,
354
- I as AlgorithmControl,
355
- F as CONTROL_BLOCK_ID,
356
- at as CURRENCY_CONTROL_ID,
357
- R as CurrencyControl,
358
- lt as FILTERS_CONTROL_ID,
359
- b as FiltersControl,
360
- dt as LAYOUT_ORIENTATION_CONTROL_ID,
361
- ht as LOCALE_CONTROL_ID,
362
- T as LayoutOrientationControl,
363
- S as LocaleControl,
364
- Ct as PRICE_PLACEMENT_CONTROL_ID,
365
- pt as PRODUCT_COUNT_CONTROL_ID,
366
- _t as PRODUCT_LAYOUT_CONTROL_ID,
367
- O as PricePlacementControl,
368
- P as ProductCountControl,
369
- L as ProductLayoutControl,
370
- ot as RecommendationBlockControl,
371
- It as SHUFFLE_CONTROL_ID,
372
- bt as STRATEGY_CONTROL_ID,
373
- k as ShuffleControl,
374
- E as StrategyControl,
375
- St as adjustProductsToSize,
376
- Ot as formatProductPrice,
418
+ ut as ALGORITHM_CONTROL_ID,
419
+ T as AlgorithmControl,
420
+ $ as CONTROL_BLOCK_ID,
421
+ gt as CURRENCY_CONTROL_ID,
422
+ P as CurrencyControl,
423
+ ht as FILTERS_CONTROL_ID,
424
+ S as FiltersControl,
425
+ Ct as LAYOUT_ORIENTATION_CONTROL_ID,
426
+ yt as LOCALE_CONTROL_ID,
427
+ O as LayoutOrientationControl,
428
+ L as LocaleControl,
429
+ _t as PRICE_PLACEMENT_CONTROL_ID,
430
+ Rt as PRODUCT_COUNT_CONTROL_ID,
431
+ Tt as PRODUCT_LAYOUT_CONTROL_ID,
432
+ E as PricePlacementControl,
433
+ k as ProductCountControl,
434
+ D as ProductLayoutControl,
435
+ at as RecommendationBlockControl,
436
+ St as SHUFFLE_CONTROL_ID,
437
+ Lt as STRATEGY_CONTROL_ID,
438
+ F as ShuffleControl,
439
+ w as StrategyControl,
440
+ kt as adjustProductsToSize,
441
+ Dt as formatProductPrice,
377
442
  B as getBlockElement,
378
- Pt as getCardComposition,
379
- Lt as getCurrentLayout,
380
- w as isPartnerManagedBlock,
381
- kt as regenerateMobileProductRows,
382
- v as regenerateProductRowsWithStyles,
383
- D as setCurrencyAttributes,
384
- Et as updatePricesInPlace,
385
- A as updateProductContentInPlace,
386
- Dt as updateSingleProductContent
443
+ Ft as getCardComposition,
444
+ wt as getCurrentLayout,
445
+ At as getMainContainerTbody,
446
+ v as isPartnerManagedBlock,
447
+ Bt as regenerateMobileProductRows,
448
+ U as regenerateProductRowsWithStyles,
449
+ A as setCurrencyAttributes,
450
+ vt as updatePricesInPlace,
451
+ x as updateProductContentInPlace,
452
+ xt as updateSingleProductContent
387
453
  };
@@ -1,26 +1,30 @@
1
- var d = Object.defineProperty;
2
- var y = (r, s, t) => s in r ? d(r, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[s] = t;
3
- var o = (r, s, t) => y(r, typeof s != "symbol" ? s + "" : s, t);
4
- import { ACADEMY_LINKS as m } from "../../../../../enums/academy.js";
5
- import { CommonControl as h } from "../../../common-control.js";
6
- import { useRecommendationExtensionStore as u } from "../../store/recommendation.js";
7
- const p = "recommendation-strategy-control";
8
- class f extends h {
1
+ var v = Object.defineProperty;
2
+ var _ = (o, a, t) => a in o ? v(o, a, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[a] = t;
3
+ var c = (o, a, t) => _(o, typeof a != "symbol" ? a + "" : a, t);
4
+ import { useLegacyStrategyConversion as S } from "../../../../../composables/useLegacyStrategyConversion.js";
5
+ import { ACADEMY_LINKS as b } from "../../../../../enums/academy.js";
6
+ import { CommonControl as f } from "../../../common-control.js";
7
+ import { useRecommendationExtensionStore as $ } from "../../store/recommendation.js";
8
+ const C = "recommendation-strategy-control";
9
+ class R extends f {
9
10
  constructor() {
10
11
  super(...arguments);
11
- o(this, "store", u());
12
- o(this, "storeUnsubscription", () => {
12
+ c(this, "store", $());
13
+ c(this, "legacyConversion", S());
14
+ c(this, "storeUnsubscription", () => {
13
15
  });
14
- o(this, "listeners", []);
15
- o(this, "lastPaintedState", null);
16
+ c(this, "listeners", []);
17
+ c(this, "lastPaintedState", null);
16
18
  }
17
19
  getId() {
18
- return p;
20
+ return C;
19
21
  }
20
22
  getTemplate() {
21
23
  const t = this.api.translate(
22
24
  "You can assign a recommendation strategy by selecting an existing one or creating a new one to show specific content."
23
- ), e = this.api.translate("For more information, you can"), a = this.api.translate("visit Academy");
25
+ ), e = this.api.translate("For more information, you can"), s = this.api.translate("visit Academy"), r = this.api.translate("Legacy Strategy"), n = this.api.translate(
26
+ "Note that this is an older version of the Recommendation Strategy. We recommend you update it to the new Recommendation Strategy for an improved experience."
27
+ );
24
28
  return `
25
29
  <div class="strategy-control-container">
26
30
  <div class="strategy-control-header">
@@ -31,9 +35,9 @@ class f extends h {
31
35
  ${t}
32
36
  ${e}
33
37
  <a
34
- href="${m.EMAIL_RECOMMENDATION_BLOCK}"
38
+ href="${b.EMAIL_RECOMMENDATION_BLOCK}"
35
39
  target="_blank"
36
- rel="noopener noreferrer">${a}</a>.
40
+ rel="noopener noreferrer">${s}</a>.
37
41
  </p>
38
42
  </div>
39
43
  <div data-strategy-empty-state>
@@ -53,6 +57,28 @@ class f extends h {
53
57
  })
54
58
  ], "strategy-control-cta-row")}
55
59
  </div>
60
+ <div data-strategy-legacy-state style="display: none;">
61
+ <div class="strategy-control-notice-row">
62
+ <div class="strategy-control-notice">
63
+ <p class="strategy-control-notice-text">${n}</p>
64
+ </div>
65
+ </div>
66
+ <div class="strategy-control-name-band">
67
+ <span class="strategy-control-name">${r}</span>
68
+ ${this._strategyAction({
69
+ id: "guido__btn-update-legacy-strategy",
70
+ label: this.api.translate("Update"),
71
+ modifier: "update"
72
+ })}
73
+ </div>
74
+ <div class="strategy-control-actions">
75
+ ${this._strategyAction({
76
+ id: "guido__btn-change-legacy-strategy",
77
+ label: this.api.translate("Change"),
78
+ modifier: "change"
79
+ })}
80
+ </div>
81
+ </div>
56
82
  <div data-strategy-assigned-state style="display: none;">
57
83
  <div class="strategy-control-name-band">
58
84
  <span class="strategy-control-name" data-strategy-name></span>
@@ -73,12 +99,12 @@ class f extends h {
73
99
  </div>
74
100
  `;
75
101
  }
76
- _strategyAction({ id: t, label: e, modifier: a }) {
102
+ _strategyAction({ id: t, label: e, modifier: s }) {
77
103
  return `
78
104
  <button
79
105
  id="${t}"
80
106
  type="button"
81
- class="strategy-control-action strategy-control-action--${a}">
107
+ class="strategy-control-action strategy-control-action--${s}">
82
108
  <span>${e}</span>
83
109
  </button>
84
110
  `;
@@ -100,25 +126,34 @@ class f extends h {
100
126
  const t = this.getContainer();
101
127
  if (!t)
102
128
  return;
103
- const e = (a, n) => {
104
- const i = t.querySelector(a);
105
- i && (i.addEventListener("click", n), this.listeners.push({ element: i, handler: n }));
129
+ const e = (s, r) => {
130
+ const n = t.querySelector(s);
131
+ n && (n.addEventListener("click", r), this.listeners.push({ element: n, handler: r }));
106
132
  };
107
- e("#guido__btn-assign-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-change-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-remove-strategy", () => this.store.removeStrategy());
133
+ e("#guido__btn-assign-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-change-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-change-legacy-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-remove-strategy", () => this.store.openRemoveStrategyModal()), e("#guido__btn-update-legacy-strategy", () => {
134
+ this.legacyConversion.convertLegacyStrategy();
135
+ });
108
136
  }
109
137
  _renderState() {
110
- var l;
111
- const { strategyId: t } = this.store.recommendationConfigs, e = t ? ((l = this.store.assignedStrategy) == null ? void 0 : l.name) ?? "" : "", a = `${t}\0${e}`;
112
- if (a === this.lastPaintedState)
138
+ var u;
139
+ const { strategyId: t } = this.store.recommendationConfigs, e = t ? ((u = this.store.assignedStrategy) == null ? void 0 : u.name) ?? "" : "", s = this.store.legacyConversionLoading, r = this.store.isLegacyStrategyBlock, n = this.store.legacyStrategyUpdatedModalStatus, h = `${t}\0${e}\0${s}\0${r}\0${n}`;
140
+ if (h === this.lastPaintedState)
141
+ return;
142
+ const g = this.getContainer();
143
+ if (!g)
113
144
  return;
114
- const n = this.getContainer();
115
- if (!n)
145
+ const d = g.querySelector("[data-strategy-empty-state]"), y = g.querySelector("[data-strategy-assigned-state]"), i = g.querySelector("[data-strategy-legacy-state]"), m = g.querySelector("[data-strategy-name]");
146
+ if (!d || !y || !i)
116
147
  return;
117
- const i = n.querySelector("[data-strategy-empty-state]"), c = n.querySelector("[data-strategy-assigned-state]"), g = n.querySelector("[data-strategy-name]");
118
- !i || !c || (i.style.display = t ? "none" : "", c.style.display = t ? "" : "none", g && t && (g.textContent = e || `#${t}`), this.lastPaintedState = a);
148
+ const p = n ? i : t ? y : r ? i : d;
149
+ [d, y, i].forEach((l) => {
150
+ l.style.display = l === p ? "" : "none";
151
+ }), i.querySelectorAll("button").forEach((l) => {
152
+ l.disabled = s;
153
+ }), i.classList.toggle("is-converting", s), m && t && (m.textContent = e || `#${t}`), this.lastPaintedState = h;
119
154
  }
120
155
  }
121
156
  export {
122
- p as STRATEGY_CONTROL_ID,
123
- f as StrategyControl
157
+ C as STRATEGY_CONTROL_ID,
158
+ R as StrategyControl
124
159
  };
@@ -438,6 +438,7 @@ export {
438
438
  C as getBlockElement,
439
439
  U as getCardComposition,
440
440
  P as getCurrentLayout,
441
+ oe as getMainContainerTbody,
441
442
  ee as isPartnerManagedBlock,
442
443
  Pe as regenerateMobileProductRows,
443
444
  Te as regenerateProductRowsWithStyles,