@useinsider/guido 3.18.1-beta.280b4e1 → 3.18.1-beta.2e195ed

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 (36) hide show
  1. package/dist/components/Guido.vue.js +3 -3
  2. package/dist/components/Guido.vue2.js +30 -30
  3. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +13 -11
  4. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +65 -53
  5. package/dist/components/organisms/onboarding/OnboardingWrapper.vue2.js +20 -18
  6. package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.js +21 -0
  7. package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue2.js +47 -0
  8. package/dist/composables/useCustomInterfaceAppearance.js +56 -48
  9. package/dist/composables/useHtmlValidator.js +84 -79
  10. package/dist/composables/useRecommendation.js +21 -18
  11. package/dist/composables/useStripo.js +30 -30
  12. package/dist/enums/extensions/recommendationBlock.js +38 -31
  13. package/dist/extensions/Blocks/Recommendation/block.js +43 -39
  14. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +62 -57
  15. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +84 -52
  16. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +165 -78
  17. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +21 -12
  18. package/dist/guido.css +1 -1
  19. package/dist/library.js +13 -15
  20. package/dist/src/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.d.ts +2 -0
  21. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  22. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +2 -0
  23. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +9 -0
  24. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +74 -0
  25. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +4 -0
  26. package/dist/src/library.d.ts +0 -2
  27. package/dist/src/public/trackingIds.d.ts +2 -6
  28. package/dist/src/stores/guido-email-editor.d.ts +27 -27
  29. package/dist/src/stores/onboarding.d.ts +377 -0
  30. package/dist/static/styles/components/recommendation-strategy.css.js +51 -4
  31. package/dist/stores/guido-email-editor.js +4 -0
  32. package/dist/stores/onboarding.js +41 -36
  33. package/dist/utils/templatePreparation.js +50 -50
  34. package/package.json +1 -5
  35. package/dist/public/editorState.js +0 -4
  36. package/dist/src/public/editorState.d.ts +0 -14
@@ -1,37 +1,37 @@
1
- var R = Object.defineProperty;
2
- var B = (a, c, t) => c in a ? R(a, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[c] = t;
3
- var p = (a, c, t) => B(a, typeof c != "symbol" ? c + "" : c, t);
4
- import { useConfig as D } from "../../../composables/useConfig.js";
5
- import { BlockId as y } from "../../../enums/block.js";
6
- import { getMigrationBannerHtml as C } from "../../../utils/migrationBannerHtml.js";
7
- import { Block as b, BlockCompositionType as A, ModificationDescription as g } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
8
- import { CURRENT_CONFIG_VERSION as E, PRE_RRS_CONFIG_VERSION as S } from "./constants/defaultConfig.js";
1
+ var D = Object.defineProperty;
2
+ var y = (l, c, t) => c in l ? D(l, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[c] = t;
3
+ var f = (l, c, t) => y(l, typeof c != "symbol" ? c + "" : c, t);
4
+ import { useConfig as C } from "../../../composables/useConfig.js";
5
+ import { BlockId as b } from "../../../enums/block.js";
6
+ import { getMigrationBannerHtml as A } from "../../../utils/migrationBannerHtml.js";
7
+ import { Block as E, BlockCompositionType as S, ModificationDescription as g } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
8
+ import { CURRENT_CONFIG_VERSION as N, PRE_RRS_CONFIG_VERSION as O } from "./constants/defaultConfig.js";
9
9
  import { RECOMMENDATION_ID_ATTR as m } from "./constants/selectors.js";
10
- import { regenerateMobileProductRows as N } from "./controls/main/utils.js";
11
- import { ensureMobileCssRulesExist as h, setMobileLayoutOptOut as f, hasMobileLayoutOptOut as O } from "./controls/mobileLayout/cssRules.js";
10
+ import { regenerateMobileProductRows as M } from "./controls/main/utils.js";
11
+ import { ensureMobileCssRulesExist as _, setMobileLayoutOptOut as I, hasMobileLayoutOptOut as w } from "./controls/mobileLayout/cssRules.js";
12
12
  import { RecommendationConfigService as s } from "./services/configService.js";
13
13
  import { useRecommendationExtensionStore as d } from "./store/recommendation.js";
14
- import { getDefaultTemplate as M } from "./templates/grid/template.js";
15
- import { useRecommendationBlockWarning as w } from "./useRecommendationBlockWarning.js";
16
- const _ = y.Recommendation, u = "recommendation-block-v2", I = "hide-price";
17
- let k = !1;
18
- class W extends b {
14
+ import { getDefaultTemplate as x } from "./templates/grid/template.js";
15
+ import { useRecommendationBlockWarning as H } from "./useRecommendationBlockWarning.js";
16
+ const k = b.Recommendation, u = "recommendation-block-v2", R = "hide-price";
17
+ let B = !1;
18
+ class j extends E {
19
19
  constructor() {
20
20
  super();
21
21
  /**
22
22
  * Stores the ID generated in getTemplate() so onCreated() can reuse it.
23
23
  * This avoids generating a new (different) ID in onCreated().
24
24
  */
25
- p(this, "_pendingBlockId", null);
25
+ f(this, "_pendingBlockId", null);
26
26
  }
27
27
  getId() {
28
- return _;
28
+ return k;
29
29
  }
30
30
  getIcon() {
31
31
  return "recommendation-icon";
32
32
  }
33
33
  getBlockCompositionType() {
34
- return A.CONTAINER;
34
+ return S.CONTAINER;
35
35
  }
36
36
  getName() {
37
37
  return this.api.translate("Recommendation Block");
@@ -42,8 +42,8 @@ class W extends b {
42
42
  );
43
43
  }
44
44
  getSettingsPanelTitleHtml() {
45
- return C(
46
- _,
45
+ return A(
46
+ k,
47
47
  this.api.translate("Recommendation Block"),
48
48
  this.api.translate("This block is switched from the Old Version to the New Version. We recommend you check the Recommendation block and test your message to ensure it works properly.")
49
49
  );
@@ -66,7 +66,7 @@ class W extends b {
66
66
  */
67
67
  getTemplate() {
68
68
  const t = this._generateNextId();
69
- return this._pendingBlockId = t, M(t);
69
+ return this._pendingBlockId = t, x(t);
70
70
  }
71
71
  /**
72
72
  * Called when a new block is dropped into the template
@@ -89,23 +89,27 @@ class W extends b {
89
89
  }
90
90
  const i = this._pendingBlockId ?? this._generateNextId();
91
91
  this._pendingBlockId = null, this._assignRecommendationId(t, i);
92
- const { config: n, wasFreshDrop: o } = s.initializeConfig(
92
+ const n = C().isFeatureEnabled("reusableRecommendationStrategy"), o = n && !s.hasConfig(t), { config: r, wasFreshDrop: a } = s.initializeConfig(
93
93
  this.api,
94
94
  t,
95
95
  {
96
96
  recommendationId: i,
97
- configVersion: D().isFeatureEnabled("reusableRecommendationStrategy") ? E : S
97
+ configVersion: n ? N : O,
98
+ // An RRS block picks a strategy, not an algorithm, and its algorithm control is
99
+ // hidden. Keeping the `mostPopular` default would bake a most-popular feed URL
100
+ // at save for a block the user never configured.
101
+ ...o ? { strategy: "" } : {}
98
102
  }
99
- ), r = d();
100
- if (r.setCurrentBlock(i), this._stampHidePrice(t, n.priceHideIfSameAsDiscounted), o) {
101
- h(this.api);
102
- const l = this._getBlockElement(t);
103
- l && (f(this.api, l, !0), N({
103
+ ), p = d();
104
+ if (p.setCurrentBlock(i), this._stampHidePrice(t, r.priceHideIfSameAsDiscounted), a) {
105
+ _(this.api);
106
+ const h = this._getBlockElement(t);
107
+ h && (I(this.api, h, !0), M({
104
108
  currentNode: t,
105
109
  documentModifier: this.api.getDocumentModifier()
106
110
  }));
107
111
  }
108
- r.patchCurrentBlockConfig({ language: n.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
112
+ p.patchCurrentBlockConfig({ language: r.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
109
113
  }
110
114
  /**
111
115
  * Called when the document changes or template is loaded
@@ -127,11 +131,11 @@ class W extends b {
127
131
  }
128
132
  this._healLingeringDuplicate(t), s.needsMigration(t) && this._migrateFromLegacy(t), this._healHidePriceAttr(t);
129
133
  try {
130
- k || (h(this.api), k = !0);
134
+ B || (_(this.api), B = !0);
131
135
  const e = s.getConfig(t), i = this._getBlockElement(t);
132
136
  if (i) {
133
137
  const n = !e.mobileLayoutEnabled;
134
- O(i) !== n && f(this.api, i, n);
138
+ w(i) !== n && I(this.api, i, n);
135
139
  }
136
140
  } catch {
137
141
  }
@@ -161,7 +165,7 @@ class W extends b {
161
165
  */
162
166
  _warnIfMultipleBlocks() {
163
167
  try {
164
- w().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
168
+ H().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
165
169
  } catch {
166
170
  }
167
171
  }
@@ -211,9 +215,9 @@ class W extends b {
211
215
  if (!o)
212
216
  return;
213
217
  let r = -1;
214
- for (let l = 0; l < n.length; l++)
215
- if (n[l] === o) {
216
- r = l;
218
+ for (let a = 0; a < n.length; a++)
219
+ if (n[a] === o) {
220
+ r = a;
217
221
  break;
218
222
  }
219
223
  if (r <= 0)
@@ -293,7 +297,7 @@ class W extends b {
293
297
  */
294
298
  _stampHidePrice(t, e) {
295
299
  const i = this._getBlockElement(t);
296
- i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(I, String(e)).apply(new g("Stamp hide-price"));
300
+ i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(R, String(e)).apply(new g("Stamp hide-price"));
297
301
  }
298
302
  /**
299
303
  * Stamps `hide-price` from config only when the block lacks the attribute —
@@ -301,7 +305,7 @@ class W extends b {
301
305
  */
302
306
  _healHidePriceAttr(t) {
303
307
  const e = this._getBlockElement(t);
304
- if (!e || !("getAttribute" in e) || e.getAttribute(I) !== null)
308
+ if (!e || !("getAttribute" in e) || e.getAttribute(R) !== null)
305
309
  return;
306
310
  const { priceHideIfSameAsDiscounted: i } = s.getConfig(t);
307
311
  this._stampHidePrice(t, i);
@@ -338,6 +342,6 @@ class W extends b {
338
342
  }
339
343
  }
340
344
  export {
341
- _ as BLOCK_ID,
342
- W as RecommendationBlock
345
+ k as BLOCK_ID,
346
+ j as RecommendationBlock
343
347
  };
@@ -1,20 +1,20 @@
1
1
  var m = Object.defineProperty;
2
- var C = (h, l, t) => l in h ? m(h, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : h[l] = t;
3
- var n = (h, l, t) => C(h, typeof l != "symbol" ? l + "" : l, t);
2
+ var C = (g, c, t) => c in g ? m(g, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[c] = t;
3
+ var r = (g, c, t) => C(g, typeof c != "symbol" ? c + "" : c, t);
4
4
  import { useConfig as p } from "../../../../../composables/useConfig.js";
5
5
  import { ModificationDescription as y } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
6
  import { CommonControl as N } from "../../../common-control.js";
7
7
  import { CURRENT_CONFIG_VERSION as _, DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
8
8
  import { RECOMMENDATION_ID_ATTR as I } from "../../constants/selectors.js";
9
- import { RecommendationConfigService as c } from "../../services/configService.js";
9
+ import { RecommendationConfigService as l } from "../../services/configService.js";
10
10
  import { useRecommendationExtensionStore as R } from "../../store/recommendation.js";
11
11
  import { registerNodeConfigWriter as b } from "../../utils/nodeConfigWriter.js";
12
12
  import { AlgorithmControl as T } from "./algorithm.js";
13
13
  import { ALGORITHM_CONTROL_ID as ut } from "./algorithm.js";
14
14
  import { CurrencyControl as P } from "./currency.js";
15
- import { CURRENCY_CONTROL_ID as ht } from "./currency.js";
15
+ import { CURRENCY_CONTROL_ID as gt } from "./currency.js";
16
16
  import { FiltersControl as S } from "./filters.js";
17
- import { FILTERS_CONTROL_ID as gt } from "./filters.js";
17
+ import { FILTERS_CONTROL_ID as ft } from "./filters.js";
18
18
  import { LayoutOrientationControl as O } from "./layoutOrientation.js";
19
19
  import { LAYOUT_ORIENTATION_CONTROL_ID as Ct } from "./layoutOrientation.js";
20
20
  import { LocaleControl as L } from "./locale.js";
@@ -31,36 +31,36 @@ import { StrategyControl as w } from "./strategy.js";
31
31
  import { STRATEGY_CONTROL_ID as Lt } from "./strategy.js";
32
32
  import { setCurrencyAttributes as A, getBlockElement as v, isPartnerManagedBlock as x, updateProductContentInPlace as B, regenerateProductRowsWithStyles as U } from "./utils.js";
33
33
  import { adjustProductsToSize as kt, formatProductPrice as Dt, getCardComposition as Ft, getCurrentLayout as wt, getMainContainerTbody as At, regenerateMobileProductRows as vt, updatePricesInPlace as xt, updateSingleProductContent as Bt } from "./utils.js";
34
- import { useDebounceFn as f } from "../../../../../node_modules/@vueuse/shared/index.js";
35
- const $ = "ui-elements-recommendation-block";
34
+ import { useDebounceFn as h } from "../../../../../node_modules/@vueuse/shared/index.js";
35
+ const W = "ui-elements-recommendation-block";
36
36
  class at extends N {
37
37
  constructor() {
38
38
  super(...arguments);
39
- n(this, "store", R());
40
- n(this, "storeUnsubscription", () => {
39
+ r(this, "store", R());
40
+ r(this, "storeUnsubscription", () => {
41
41
  });
42
42
  // blockId -> fingerprint of the node config as it looked at the last sync.
43
- n(this, "syncedConfigs", /* @__PURE__ */ new Map());
43
+ r(this, "syncedConfigs", /* @__PURE__ */ new Map());
44
44
  // A config change waiting to reach the node config. Deferred so it can ride the
45
45
  // re-render's single apply() — one user action must cost one undo press.
46
- n(this, "pendingNodeConfigWrite", null);
47
- n(this, "pendingConfigFlushTimer", null);
46
+ r(this, "pendingNodeConfigWrite", null);
47
+ r(this, "pendingConfigFlushTimer", null);
48
48
  // Sub-control instances for lifecycle management
49
- n(this, "algorithmControl", null);
50
- n(this, "localeControl", null);
51
- n(this, "currencyControl", null);
52
- n(this, "productCountControl", null);
53
- n(this, "productLayoutControl", null);
54
- n(this, "filtersControl", null);
55
- n(this, "shuffleControl", null);
56
- n(this, "pricePlacementControl", null);
57
- n(this, "layoutOrientationControl", null);
58
- n(this, "strategyControl", null);
59
- 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);
60
60
  /**
61
61
  * Debounced product fetch to prevent rapid API calls during config changes
62
62
  */
63
- n(this, "_debouncedFetchProducts", f(async () => {
63
+ r(this, "_debouncedFetchProducts", h(async () => {
64
64
  try {
65
65
  await this.store.fetchRecommendationProducts();
66
66
  } finally {
@@ -78,7 +78,7 @@ class at extends N {
78
78
  * count. The store pads products to the configured size, so in-place only
79
79
  * fails when the size actually changed.
80
80
  */
81
- n(this, "_debouncedRegenerateWithProducts", f(() => {
81
+ r(this, "_debouncedRegenerateWithProducts", h(() => {
82
82
  const t = this.store.recommendationProducts;
83
83
  if (!this.currentNode || !this.api)
84
84
  return;
@@ -99,7 +99,7 @@ class at extends N {
99
99
  }, 100));
100
100
  }
101
101
  getId() {
102
- return $;
102
+ return W;
103
103
  }
104
104
  get _subControls() {
105
105
  return [
@@ -142,14 +142,14 @@ class at extends N {
142
142
  const e = (i = this.getContainer()) == null ? void 0 : i.querySelector(".recommendation-controls-container");
143
143
  if (!e)
144
144
  return;
145
- const o = this._getRecommendationIdFromNode(t) === null ? null : c.getConfig(t), r = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= _);
146
- e.classList.toggle("strategy-block", r);
145
+ const o = this._getRecommendationIdFromNode(t) === null ? null : l.getConfig(t), n = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= _);
146
+ e.classList.toggle("strategy-block", n);
147
147
  }
148
148
  async onRender() {
149
149
  var o;
150
150
  b(this.api);
151
151
  const t = this._getRecommendationIdFromNode(this.currentNode), e = t ?? this.store.currentRecommendationId;
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)) {
152
+ if (e !== null && this.store.setCurrentBlock(e), t !== null && this._syncNodeConfigIfChanged(t), this._listenStateUpdates(), this._showControlsForBlock(this.currentNode), t !== null && this.strategyControl && (this.strategyControl.api = this.api, this.strategyControl.currentNode = this.currentNode, this.strategyControl.onRender()), e !== null && ((o = this.store.blockStates[e]) != null && o.isInitialized)) {
153
153
  this._initializeSubControls();
154
154
  return;
155
155
  }
@@ -161,9 +161,9 @@ class at extends N {
161
161
  const e = this._getRecommendationIdFromNode(t);
162
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(() => {
163
163
  this._initializeSubControls();
164
- }), this._subControls.forEach((r) => {
164
+ }), this._subControls.forEach((n) => {
165
165
  var i;
166
- r != null && r.api && (r.currentNode = t, (i = r.onTemplateNodeUpdated) == null || i.call(r, t));
166
+ n != null && n.api && (n.currentNode = t, (i = n.onTemplateNodeUpdated) == null || i.call(n, t));
167
167
  });
168
168
  }
169
169
  onDestroy() {
@@ -203,18 +203,23 @@ class at extends N {
203
203
  */
204
204
  _syncNodeConfigIfChanged(t) {
205
205
  this.pendingNodeConfigWrite && this.pendingNodeConfigWrite.blockId !== t && this._flushPendingNodeConfig();
206
- const e = JSON.stringify(c.getConfig(this.currentNode)), o = this.syncedConfigs.get(t);
206
+ const e = JSON.stringify(l.getConfig(this.currentNode)), o = this.syncedConfigs.get(t);
207
207
  this.syncedConfigs.set(t, e), o !== e && (this.pendingNodeConfigWrite || this._syncNodeConfigToStore());
208
208
  }
209
+ /** Whether the deferred write would move the node's strategyId — see the caller (SD-149916). */
210
+ _isPendingStrategyIdChange() {
211
+ const t = this.pendingNodeConfigWrite;
212
+ return t ? l.getConfig(this.currentNode).strategyId !== t.updates.strategyId : !1;
213
+ }
209
214
  _capturePendingNodeConfigWrite() {
210
215
  const t = this.store.currentRecommendationId;
211
216
  if (!this.currentNode || !this.api || t === null)
212
217
  return;
213
- const { filters: e, strategyId: o, size: r } = this.store.recommendationConfigs;
218
+ const { filters: e, strategyId: o, size: n } = this.store.recommendationConfigs;
214
219
  this.pendingNodeConfigWrite = {
215
220
  blockId: t,
216
221
  node: this.currentNode,
217
- updates: { filters: e, strategyId: o, size: r }
222
+ updates: { filters: e, strategyId: o, size: n }
218
223
  };
219
224
  }
220
225
  /**
@@ -229,7 +234,7 @@ class at extends N {
229
234
  */
230
235
  _stagePendingNodeConfig(t) {
231
236
  const e = this.pendingNodeConfigWrite;
232
- return !e || !this.currentNode ? !1 : (c.stageConfig(t, this.currentNode, e.updates), !0);
237
+ return !e || !this.currentNode ? !1 : (l.stageConfig(t, this.currentNode, e.updates), !0);
233
238
  }
234
239
  /**
235
240
  * Arms the guaranteed fallback write, once the product fetch has settled. The
@@ -253,12 +258,12 @@ class at extends N {
253
258
  const t = this.pendingNodeConfigWrite;
254
259
  if (this.pendingNodeConfigWrite = null, this._clearConfigFlush(), !t || !this.api)
255
260
  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");
261
+ const e = t.blockId === this.store.currentRecommendationId && this.currentNode ? this.currentNode : t.node, o = l.getConfig(e);
262
+ JSON.stringify(o.filters) === JSON.stringify(t.updates.filters) && o.strategyId === t.updates.strategyId && o.size === t.updates.size || l.updateConfig(this.api, e, t.updates, "Update recommendation filters");
258
263
  }
259
264
  _syncNodeConfigToStore() {
260
- var r;
261
- const t = c.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
265
+ var n;
266
+ const t = l.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((n = this.store.blockStates[e]) == null ? void 0 : n.isInitialized);
262
267
  this.store.patchCurrentBlockConfig({
263
268
  strategy: t.strategy,
264
269
  strategyId: t.strategyId,
@@ -300,8 +305,8 @@ class at extends N {
300
305
  t !== null && this.store.markBlockInitialized(t), (await Promise.allSettled([
301
306
  this.store.fetchRecommendationCreateData(),
302
307
  this.store.fetchRecommendationFilters()
303
- ])).forEach((o, r) => {
304
- o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][r]} failed`, o.reason);
308
+ ])).forEach((o, n) => {
309
+ o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][n]} failed`, o.reason);
305
310
  }), this._applySmartDefaults();
306
311
  try {
307
312
  await this.store.fetchRecommendationProducts();
@@ -322,8 +327,8 @@ class at extends N {
322
327
  _applySmartDefaults() {
323
328
  if (!this.currentNode || !this.api)
324
329
  return;
325
- const t = c.getConfig(this.currentNode), e = {};
326
- let o = null, r = null, i = null;
330
+ const t = l.getConfig(this.currentNode), e = {};
331
+ let o = null, n = null, i = null;
327
332
  if (t.currency.code === a.currency.code) {
328
333
  const { currencyList: s } = this.store;
329
334
  s.length > 0 && (s.some(
@@ -338,7 +343,7 @@ class at extends N {
338
343
  const s = this.store.getActivePredictiveAlgorithms;
339
344
  s.length > 0 && (s.some(
340
345
  (d) => d.value === a.strategy
341
- ) || (r = s[0].value, e.strategy = r));
346
+ ) || (n = s[0].value, e.strategy = n));
342
347
  }
343
348
  if (t.language === a.language) {
344
349
  const s = this.store.getLanguages;
@@ -346,7 +351,7 @@ class at extends N {
346
351
  (d) => d.value === a.language
347
352
  ) || (i = s[0].value, e.language = i));
348
353
  }
349
- !o && !r && !i || (c.updateConfig(
354
+ !o && !n && !i || (l.updateConfig(
350
355
  this.api,
351
356
  this.currentNode,
352
357
  e,
@@ -367,7 +372,7 @@ class at extends N {
367
372
  thousandSeparator: a.currency.thousandSeparator
368
373
  }
369
374
  } : {},
370
- ...r ? { strategy: r } : {},
375
+ ...n ? { strategy: n } : {},
371
376
  ...i ? { language: i } : {}
372
377
  }, { triggerRefetch: !1 }));
373
378
  }
@@ -381,8 +386,8 @@ class at extends N {
381
386
  const o = e.getAttribute(I);
382
387
  if (!o)
383
388
  return null;
384
- const r = parseInt(o);
385
- return Number.isNaN(r) ? null : r;
389
+ const n = parseInt(o);
390
+ return Number.isNaN(n) ? null : n;
386
391
  }
387
392
  /**
388
393
  * Listen to store changes that require product refresh or regeneration.
@@ -399,27 +404,27 @@ class at extends N {
399
404
  */
400
405
  _listenStateUpdates() {
401
406
  const { store: t } = this;
402
- let e = t.recommendationProducts, o = t.$state.configVersion, r = t.currentRecommendationId;
407
+ let e = t.recommendationProducts, o = t.$state.configVersion, n = t.currentRecommendationId;
403
408
  this.storeUnsubscription = t.$subscribe(() => {
404
409
  const i = t.currentRecommendationId;
405
- if (i !== r) {
406
- r = i, e = t.recommendationProducts, o = t.$state.configVersion;
410
+ if (i !== n) {
411
+ n = i, e = t.recommendationProducts, o = t.$state.configVersion;
407
412
  return;
408
413
  }
409
414
  const s = t.$state.configVersion;
410
- s !== o && (o = s, this._capturePendingNodeConfigWrite(), this._debouncedFetchProducts());
411
- const u = t.recommendationProducts, d = u !== e, g = Array.isArray(u) && u.length > 0;
412
- d && g && (e = u, this._debouncedRegenerateWithProducts());
415
+ s !== o && (o = s, this._capturePendingNodeConfigWrite(), this._isPendingStrategyIdChange() && this._flushPendingNodeConfig(), this._debouncedFetchProducts());
416
+ const u = t.recommendationProducts, d = u !== e, f = Array.isArray(u) && u.length > 0;
417
+ d && f && (e = u, this._debouncedRegenerateWithProducts());
413
418
  });
414
419
  }
415
420
  }
416
421
  export {
417
422
  ut as ALGORITHM_CONTROL_ID,
418
423
  T as AlgorithmControl,
419
- $ as CONTROL_BLOCK_ID,
420
- ht as CURRENCY_CONTROL_ID,
424
+ W as CONTROL_BLOCK_ID,
425
+ gt as CURRENCY_CONTROL_ID,
421
426
  P as CurrencyControl,
422
- gt as FILTERS_CONTROL_ID,
427
+ ft as FILTERS_CONTROL_ID,
423
428
  S as FiltersControl,
424
429
  Ct as LAYOUT_ORIENTATION_CONTROL_ID,
425
430
  yt as LOCALE_CONTROL_ID,
@@ -1,21 +1,25 @@
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 u = Object.defineProperty;
2
+ var S = (o, n, t) => n in o ? u(o, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[n] = t;
3
+ var r = (o, n, t) => S(o, typeof n != "symbol" ? n + "" : n, t);
4
+ import { ACADEMY_LINKS as _ } from "../../../../../enums/academy.js";
5
+ import { useOnboardingStore as f } from "../../../../../stores/onboarding.js";
6
+ import { CommonControl as v } from "../../../common-control.js";
7
+ import { CURRENT_CONFIG_VERSION as b } from "../../constants/defaultConfig.js";
8
+ import { RecommendationConfigService as h } from "../../services/configService.js";
9
+ import { useRecommendationExtensionStore as C } from "../../store/recommendation.js";
10
+ const k = "recommendation-strategy-control";
11
+ class I extends v {
9
12
  constructor() {
10
13
  super(...arguments);
11
- o(this, "store", u());
12
- o(this, "storeUnsubscription", () => {
14
+ r(this, "store", C());
15
+ r(this, "onboardingStore", f());
16
+ r(this, "storeUnsubscription", () => {
13
17
  });
14
- o(this, "listeners", []);
15
- o(this, "lastPaintedState", null);
18
+ r(this, "listeners", []);
19
+ r(this, "lastPaintedState", null);
16
20
  }
17
21
  getId() {
18
- return p;
22
+ return k;
19
23
  }
20
24
  getTemplate() {
21
25
  const t = this.api.translate(
@@ -23,21 +27,34 @@ class f extends h {
23
27
  ), e = this.api.translate("For more information, you can"), a = this.api.translate("visit Academy");
24
28
  return `
25
29
  <div class="strategy-control-container">
26
- <div class="strategy-control-header">
27
- <h3 class="strategy-control-title">${this.api.translate("Recommendation Strategy")}</h3>
28
- </div>
29
- <div class="strategy-control-description-row">
30
- <p class="strategy-control-description">
31
- ${t}
32
- ${e}
33
- <a
34
- href="${m.EMAIL_RECOMMENDATION_BLOCK}"
35
- target="_blank"
36
- rel="noopener noreferrer">${a}</a>.
37
- </p>
30
+ <div data-strategy-loading-state>
31
+ <div class="strategy-control-header">
32
+ <span class="guido-skeleton guido-skeleton--strategy-title"></span>
33
+ </div>
34
+ <div class="strategy-control-description-row">
35
+ <span class="guido-skeleton guido-skeleton--strategy-description"></span>
36
+ </div>
37
+ <div class="strategy-control-skeleton-row">
38
+ <span class="guido-skeleton guido-skeleton--strategy-cta"></span>
39
+ <span class="guido-skeleton guido-skeleton--strategy-cta"></span>
40
+ </div>
38
41
  </div>
39
- <div data-strategy-empty-state>
40
- ${this._GuTwoColumns([
42
+ <div data-strategy-content style="display: none;">
43
+ <div class="strategy-control-header">
44
+ <h3 class="strategy-control-title">${this.api.translate("Recommendation Strategy")}</h3>
45
+ </div>
46
+ <div class="strategy-control-description-row">
47
+ <p class="strategy-control-description">
48
+ ${t}
49
+ ${e}
50
+ <a
51
+ href="${_.EMAIL_RECOMMENDATION_BLOCK}"
52
+ target="_blank"
53
+ rel="noopener noreferrer">${a}</a>.
54
+ </p>
55
+ </div>
56
+ <div data-strategy-empty-state style="display: none;">
57
+ ${this._GuTwoColumns([
41
58
  this._GuButton({
42
59
  name: "assignStrategy",
43
60
  label: this.api.translate("Assign Strategy"),
@@ -48,26 +65,28 @@ class f extends h {
48
65
  name: "createStrategy",
49
66
  label: this.api.translate("Create New Strategy"),
50
67
  id: "guido__btn-create-strategy",
51
- className: "strategy-control-cta primary",
52
- disabled: !0
68
+ className: "strategy-control-cta primary"
53
69
  })
54
70
  ], "strategy-control-cta-row")}
55
- </div>
56
- <div data-strategy-assigned-state style="display: none;">
57
- <div class="strategy-control-name-band">
58
- <span class="strategy-control-name" data-strategy-name></span>
59
71
  </div>
60
- <div class="strategy-control-actions">
61
- ${this._strategyAction({
72
+ <div data-strategy-assigned-state style="display: none;">
73
+ <div class="strategy-control-name-band">
74
+ <span class="strategy-control-name" data-strategy-name></span>
75
+ <span
76
+ class="guido-skeleton guido-skeleton--strategy-name" data-strategy-name-skeleton></span>
77
+ </div>
78
+ <div class="strategy-control-actions">
79
+ ${this._strategyAction({
62
80
  id: "guido__btn-change-strategy",
63
81
  label: this.api.translate("Change"),
64
82
  modifier: "change"
65
83
  })}
66
- ${this._strategyAction({
84
+ ${this._strategyAction({
67
85
  id: "guido__btn-remove-strategy",
68
86
  label: this.api.translate("Remove"),
69
87
  modifier: "remove"
70
88
  })}
89
+ </div>
71
90
  </div>
72
91
  </div>
73
92
  </div>
@@ -84,41 +103,54 @@ class f extends h {
84
103
  `;
85
104
  }
86
105
  onRender() {
87
- this._teardown(), this._setupButtonListeners(), this._renderState(), this.storeUnsubscription = this.store.$subscribe(() => {
106
+ this._teardown(), this.store.strategyPanelOpen = !0, this._setupButtonListeners(), this._renderState(), this.storeUnsubscription = this.store.$subscribe(() => {
88
107
  this._renderState();
89
- }), this.store.recommendationConfigs.strategyId && !this.store.strategies.length && this.store.fetchStrategies().catch(() => {
108
+ }), this.store.recommendationConfigs.strategyId && !this.store.strategiesFetched && this.store.fetchStrategies().catch(() => {
90
109
  });
91
110
  }
111
+ /**
112
+ * Whether the document itself says this block has no strategy.
113
+ *
114
+ * Read the node, not the store: when the main control cannot resolve the block id off the
115
+ * node it never syncs the persisted config, so the store still holds defaults (empty
116
+ * strategyId) for an assigned block.
117
+ */
118
+ _isNodeUnassigned() {
119
+ if (!h.hasConfig(this.currentNode))
120
+ return !1;
121
+ const t = h.getConfig(this.currentNode);
122
+ return !t.strategyId && (t.configVersion ?? 0) >= b;
123
+ }
92
124
  onDestroy() {
93
125
  this._teardown();
94
126
  }
95
127
  _teardown() {
96
- this.storeUnsubscription(), this.storeUnsubscription = () => {
128
+ this.store.strategyPanelOpen = !1, this.storeUnsubscription(), this.storeUnsubscription = () => {
97
129
  }, this.lastPaintedState = null, this.listeners.forEach(({ element: t, handler: e }) => t.removeEventListener("click", e)), this.listeners = [];
98
130
  }
99
131
  _setupButtonListeners() {
100
132
  const t = this.getContainer();
101
133
  if (!t)
102
134
  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 }));
135
+ const e = (a, i) => {
136
+ const s = t.querySelector(a);
137
+ s && (s.addEventListener("click", i), this.listeners.push({ element: s, handler: i }));
106
138
  };
107
- e("#guido__btn-assign-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-change-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-remove-strategy", () => this.store.openRemoveStrategyModal());
139
+ e("#guido__btn-assign-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-create-strategy", () => this.store.openStrategyCreation()), e("#guido__btn-change-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-remove-strategy", () => this.store.openRemoveStrategyModal());
108
140
  }
109
141
  _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)
142
+ var p;
143
+ const { strategyId: t } = this.store.recommendationConfigs, e = t ? ((p = this.store.assignedStrategy) == null ? void 0 : p.name) ?? "" : "", a = !!t && !e && (this.store.strategiesLoading || !this.store.strategiesSettled), i = `${t}\0${e}\0${a}`;
144
+ if (i === this.lastPaintedState)
113
145
  return;
114
- const n = this.getContainer();
115
- if (!n)
146
+ const s = this.getContainer();
147
+ if (!s)
116
148
  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);
149
+ const c = s.querySelector("[data-strategy-empty-state]"), g = s.querySelector("[data-strategy-assigned-state]"), l = s.querySelector("[data-strategy-loading-state]"), y = s.querySelector("[data-strategy-content]"), d = s.querySelector("[data-strategy-name]"), m = s.querySelector("[data-strategy-name-skeleton]");
150
+ !c || !g || (l && (l.style.display = "none"), y && (y.style.display = ""), c.style.display = t ? "none" : "", g.style.display = t ? "" : "none", !t && this._isNodeUnassigned() && this.onboardingStore.startOnboarding("recommendationStrategyOnboarding"), d && (d.style.display = a ? "none" : "", t && !a && (d.textContent = e || `#${t}`)), m && (m.style.display = a ? "block" : "none"), this.lastPaintedState = i);
119
151
  }
120
152
  }
121
153
  export {
122
- p as STRATEGY_CONTROL_ID,
123
- f as StrategyControl
154
+ k as STRATEGY_CONTROL_ID,
155
+ I as StrategyControl
124
156
  };