@useinsider/guido 3.18.2-beta.9be830c → 3.18.2-beta.a5a0a11

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 (92) hide show
  1. package/dist/@types/config/schemas.js +13 -15
  2. package/dist/components/Guido.vue.js +7 -7
  3. package/dist/components/Guido.vue2.js +30 -30
  4. package/dist/components/organisms/AutoSaveController.vue2.js +8 -10
  5. package/dist/components/organisms/base/Toaster.vue2.js +13 -12
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +13 -11
  7. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +74 -57
  8. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +23 -37
  9. package/dist/components/organisms/header/AutoSaveToggle.vue.js +22 -0
  10. package/dist/components/organisms/header/AutoSaveToggle.vue2.js +19 -0
  11. package/dist/components/organisms/header/EditorActions.vue.js +13 -13
  12. package/dist/components/organisms/header/EditorActions.vue2.js +52 -36
  13. package/dist/components/organisms/header/HeaderWrapper.vue.js +6 -6
  14. package/dist/components/organisms/header/HeaderWrapper.vue2.js +17 -9
  15. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  16. package/dist/components/organisms/header/LeftSlot.vue2.js +23 -28
  17. package/dist/components/organisms/header/MiddleSlot.vue.js +4 -4
  18. package/dist/components/organisms/header/MiddleSlot.vue2.js +7 -8
  19. package/dist/components/organisms/header/RightSlot.vue.js +8 -9
  20. package/dist/components/organisms/header/RightSlot.vue2.js +18 -13
  21. package/dist/components/organisms/onboarding/OnboardingWrapper.vue.js +12 -12
  22. package/dist/components/organisms/onboarding/OnboardingWrapper.vue2.js +38 -54
  23. package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.js +21 -0
  24. package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue2.js +47 -0
  25. package/dist/composables/useCustomInterfaceAppearance.js +52 -45
  26. package/dist/composables/useHtmlValidator.js +87 -82
  27. package/dist/composables/useRecommendation.js +40 -35
  28. package/dist/composables/useStripo.js +30 -30
  29. package/dist/composables/useToaster.js +14 -20
  30. package/dist/config/i18n/en/labels.json.js +4 -36
  31. package/dist/config/i18n/en/tooltips.json.js +1 -2
  32. package/dist/enums/extensions/recommendationBlock.js +38 -31
  33. package/dist/extensions/Blocks/Recommendation/block.js +61 -42
  34. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +21 -20
  35. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +40 -35
  36. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +84 -52
  37. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +74 -74
  38. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +260 -140
  39. package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +26 -16
  40. package/dist/extensions/Blocks/Recommendation/utils/strategyCommit.js +24 -0
  41. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +21 -12
  42. package/dist/guido.css +1 -1
  43. package/dist/node_modules/@vueuse/shared/index.js +28 -40
  44. package/dist/sprite.svg +0 -6
  45. package/dist/src/@types/config/schemas.d.ts +0 -4
  46. package/dist/src/components/organisms/AutoSaveController.vue.d.ts +1 -3
  47. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +3 -1
  48. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +3 -1
  49. package/dist/src/composables/useActionsApi.d.ts +2 -2
  50. package/dist/src/composables/useConfig.d.ts +0 -2
  51. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  52. package/dist/src/extensions/Blocks/Recommendation/block.d.ts +10 -0
  53. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +1 -0
  54. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  55. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +2 -0
  56. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +9 -0
  57. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +7 -2
  58. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +86 -0
  59. package/dist/src/extensions/Blocks/Recommendation/utils/strategyCommit.d.ts +14 -0
  60. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +4 -0
  61. package/dist/src/stores/config.d.ts +0 -18
  62. package/dist/src/stores/editor.d.ts +0 -107
  63. package/dist/src/stores/onboarding.d.ts +21 -195
  64. package/dist/src/stores/version-history.d.ts +1 -1
  65. package/dist/src/utils/timeUtil.d.ts +0 -10
  66. package/dist/static/styles/components/recommendation-strategy.css.js +34 -4
  67. package/dist/static/styles/components/skeleton.css.js +23 -0
  68. package/dist/static/styles/variables.css.js +0 -2
  69. package/dist/stores/editor.js +4 -13
  70. package/dist/stores/onboarding.js +50 -66
  71. package/dist/utils/templatePreparation.js +55 -55
  72. package/dist/utils/timeUtil.js +17 -7
  73. package/package.json +5 -5
  74. package/dist/components/organisms/header/AutoSaveStatus.vue.js +0 -17
  75. package/dist/components/organisms/header/AutoSaveStatus.vue2.js +0 -27
  76. package/dist/components/organisms/header/EditorActionsMenu.vue.js +0 -20
  77. package/dist/components/organisms/header/EditorActionsMenu.vue2.js +0 -48
  78. package/dist/components/organisms/header/accessibility/AccessibilityToolbar.vue.js +0 -18
  79. package/dist/components/organisms/header/accessibility/AccessibilityToolbar.vue2.js +0 -37
  80. package/dist/components/organisms/header/accessibility/ColorVisionSelect.vue.js +0 -24
  81. package/dist/components/organisms/header/accessibility/ColorVisionSelect.vue2.js +0 -48
  82. package/dist/components/organisms/onboarding/EditorTabBarOnboarding.vue.js +0 -19
  83. package/dist/components/organisms/onboarding/EditorTabBarOnboarding.vue2.js +0 -82
  84. package/dist/composables/useAccessibilityApi.js +0 -35
  85. package/dist/node_modules/@vueuse/core/index.js +0 -102
  86. package/dist/src/components/organisms/header/accessibility/AccessibilityToolbar.vue.d.ts +0 -2
  87. package/dist/src/components/organisms/header/accessibility/ColorVisionSelect.vue.d.ts +0 -2
  88. package/dist/src/components/organisms/onboarding/EditorTabBarOnboarding.vue.d.ts +0 -2
  89. package/dist/src/composables/useAccessibilityApi.d.ts +0 -15
  90. package/dist/static/styles/components/accessibility.css.js +0 -282
  91. /package/dist/src/components/organisms/header/{AutoSaveStatus.vue.d.ts → AutoSaveToggle.vue.d.ts} +0 -0
  92. /package/dist/src/components/organisms/{header/EditorActionsMenu.vue.d.ts → onboarding/RecommendationStrategyOnboarding.vue.d.ts} +0 -0
@@ -1,22 +1,26 @@
1
- import { getRecommendationFeedSourceMaps as b, getOperatorOptions as G, PriceAttributes as v } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as D } from "../../../../services/recommendationApi.js";
3
- import { useConfigStore as F } from "../../../../stores/config.js";
4
- import { defineStore as P } from "pinia";
1
+ import { useToaster as D } from "../../../../composables/useToaster.js";
2
+ import { useTranslations as _ } from "../../../../composables/useTranslations.js";
3
+ import { getRecommendationFeedSourceMaps as v, strategyEditPath as A, URLS as E, getOperatorOptions as N, PriceAttributes as U } from "../../../../enums/extensions/recommendationBlock.js";
4
+ import { ToasterTypeOptions as L } from "../../../../enums/toaster.js";
5
+ import { useRecommendationApi as T } from "../../../../services/recommendationApi.js";
6
+ import { useConfigStore as k } from "../../../../stores/config.js";
7
+ import { defineStore as B } from "pinia";
5
8
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as _, DEFAULT_CARDS_IN_ROW as U } from "../constants/layout.js";
7
- import { DEFAULT_NODE_CONFIG as N, EXCLUDED_ALGORITHM_IDS as L } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as R } from "../templates/utils.js";
9
- import { generateCompleteFilterQuery as k } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as x } from "../utils/partnerCustomizations.js";
11
- import { sanitizeProductIds as A } from "../utils/productIds.js";
12
- import { isFilterValid as B } from "../validation/filterSchema.js";
13
- import { isConfigValid as M } from "../validation/requiredFields.js";
14
- const u = D();
15
- let f = null, g = null, h = null, S = null;
16
- const p = /* @__PURE__ */ new Map();
17
- function w() {
9
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as O, DEFAULT_CARDS_IN_ROW as x } from "../constants/layout.js";
10
+ import { DEFAULT_NODE_CONFIG as M, EXCLUDED_ALGORITHM_IDS as z } from "../constants/defaultConfig.js";
11
+ import { getDefaultProducts as F } from "../templates/utils.js";
12
+ import { generateCompleteFilterQuery as w } from "../utils/filterUtil.js";
13
+ import { getPartnerRecommendationParams as V } from "../utils/partnerCustomizations.js";
14
+ import { sanitizeProductIds as j } from "../utils/productIds.js";
15
+ import { isFilterValid as $ } from "../validation/filterSchema.js";
16
+ import { getInvalidFields as q, isConfigValid as K } from "../validation/requiredFields.js";
17
+ const f = T();
18
+ let S = null, p = null, y = null, I = "", R = null, b = 0;
19
+ const g = /* @__PURE__ */ new Map();
20
+ let l = null;
21
+ function G() {
18
22
  return {
19
- cardsInRow: U,
23
+ cardsInRow: x,
20
24
  currencySettings: {
21
25
  name: "USD",
22
26
  value: "USD",
@@ -30,13 +34,17 @@ function w() {
30
34
  productIds: [],
31
35
  id: 1,
32
36
  language: "en_US",
33
- mobileCardsInRow: _,
37
+ mobileCardsInRow: O,
34
38
  mobileLayoutEnabled: !1,
35
39
  orientation: "grid",
36
40
  recommendedProducts: [],
37
41
  sendProductRequestFlag: !1,
38
42
  shuffleProducts: !1,
39
- strategy: "mostPopular",
43
+ // With RRS on, a block that has not been synced yet must not claim an algorithm:
44
+ // that default is what reached buildCampaignUrl and baked a most-popular feed URL
45
+ // for a block the user never configured (SD-149916). Legacy blocks still get their real
46
+ // algorithm from the node-config sync or the save-time seed.
47
+ strategy: k().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
40
48
  strategyId: "",
41
49
  textTrimming: !0,
42
50
  unresponsive: !1,
@@ -46,9 +54,9 @@ function w() {
46
54
  priceHideIfSameAsDiscounted: !1
47
55
  };
48
56
  }
49
- function C() {
57
+ function P() {
50
58
  return {
51
- recommendationConfigs: w(),
59
+ recommendationConfigs: G(),
52
60
  recommendationProducts: [],
53
61
  filterStatus: !1,
54
62
  filterSelectionDrawerStatus: !1,
@@ -60,7 +68,7 @@ function C() {
60
68
  syncedConfigFingerprint: null
61
69
  };
62
70
  }
63
- const z = () => ({
71
+ const H = () => ({
64
72
  recommendationCampaignUrls: {},
65
73
  activePredictiveAlgorithms: [],
66
74
  languages: {},
@@ -69,11 +77,14 @@ const z = () => ({
69
77
  strategies: [],
70
78
  strategiesLoading: !1,
71
79
  strategyUrlTemplates: {},
80
+ strategiesFetched: !1,
81
+ strategiesSettled: !1,
72
82
  blockStates: {},
73
83
  currentRecommendationId: null,
84
+ strategyPanelOpen: !1,
74
85
  configVersion: 0
75
- }), Y = P("guidoRecommendationExtension", {
76
- state: () => z(),
86
+ }), mt = B("guidoRecommendationExtension", {
87
+ state: () => H(),
77
88
  getters: {
78
89
  // ====================================================================
79
90
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -83,7 +94,7 @@ const z = () => ({
83
94
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
84
95
  */
85
96
  recommendationConfigs(t) {
86
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : w();
97
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : G();
87
98
  },
88
99
  /**
89
100
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -138,12 +149,12 @@ const z = () => ({
138
149
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
139
150
  },
140
151
  getActivePredictiveAlgorithms: (t) => {
141
- const e = b(), r = [];
142
- return t.activePredictiveAlgorithms.filter((n) => !L.includes(n)).forEach((n) => {
143
- r.push(...e.filter((s) => s.id === n));
144
- }), r.map((n) => ({
145
- text: n.name,
146
- value: n.key
152
+ const e = v(), r = [];
153
+ return t.activePredictiveAlgorithms.filter((i) => !z.includes(i)).forEach((i) => {
154
+ r.push(...e.filter((s) => s.id === i));
155
+ }), r.map((i) => ({
156
+ text: i.name,
157
+ value: i.key
147
158
  }));
148
159
  },
149
160
  getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
@@ -157,7 +168,7 @@ const z = () => ({
157
168
  getFilterList() {
158
169
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
159
170
  let e;
160
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : v.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
171
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : U.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
161
172
  text: t.displayName,
162
173
  value: e,
163
174
  type: t.attributeType
@@ -179,7 +190,7 @@ const z = () => ({
179
190
  setCurrentBlock(t) {
180
191
  this.blockStates[t] || (this.blockStates = {
181
192
  ...this.blockStates,
182
- [t]: C()
193
+ [t]: P()
183
194
  }), this.currentRecommendationId = t;
184
195
  },
185
196
  /**
@@ -189,32 +200,36 @@ const z = () => ({
189
200
  removeBlockState(t) {
190
201
  const e = t.toString();
191
202
  if (this.recommendationCampaignUrls[e]) {
192
- const n = { ...this.recommendationCampaignUrls };
193
- delete n[e], this.recommendationCampaignUrls = n;
203
+ const i = { ...this.recommendationCampaignUrls };
204
+ delete i[e], this.recommendationCampaignUrls = i;
194
205
  }
195
206
  const r = { ...this.blockStates };
196
207
  if (delete r[t], this.blockStates = r, this.currentRecommendationId === t) {
197
- const n = Object.keys(this.blockStates).map(Number);
198
- this.currentRecommendationId = n.length > 0 ? n[0] : null;
208
+ const i = Object.keys(this.blockStates).map(Number);
209
+ this.currentRecommendationId = i.length > 0 ? i[0] : null;
199
210
  }
200
211
  },
201
212
  /**
202
213
  * Deep-clones a block's state and mirrors its campaign URL under a new id.
203
214
  * Invoked from `onCreated` when a duplicate is detected (SD-142352).
215
+ *
216
+ * The clone keeps the source's data but drops its "already fetched / already
217
+ * synced" claims for strategy blocks, so it fetches again like a fresh drop
218
+ * (SD-149916).
204
219
  */
205
220
  cloneBlockState(t, e) {
206
221
  const r = this.blockStates[t];
207
222
  if (r) {
208
223
  const o = structuredClone(r);
209
- o.recommendationConfigs.id = e, this.blockStates = {
224
+ o.recommendationConfigs.id = e, o.recommendationConfigs.strategyId && k().isFeatureEnabled("reusableRecommendationStrategy") && (o.isInitialized = !1, o.syncedConfigFingerprint = null), this.blockStates = {
210
225
  ...this.blockStates,
211
226
  [e]: o
212
227
  };
213
228
  }
214
- const n = t.toString(), s = e.toString(), i = this.recommendationCampaignUrls[n];
215
- i && (this.recommendationCampaignUrls = {
229
+ const i = t.toString(), s = e.toString(), n = this.recommendationCampaignUrls[i];
230
+ n && (this.recommendationCampaignUrls = {
216
231
  ...this.recommendationCampaignUrls,
217
- [s]: i
232
+ [s]: n
218
233
  });
219
234
  },
220
235
  /**
@@ -239,7 +254,7 @@ const z = () => ({
239
254
  * `isInitialized`) alone.
240
255
  */
241
256
  seedBlockUrlConfig(t, e) {
242
- const r = (o, c) => o === "." || o === "," || o === " " ? o : c, n = {
257
+ const r = (o, a) => o === "." || o === "," || o === " " ? o : a, i = {
243
258
  name: e.currencyCode,
244
259
  value: e.currencyCode,
245
260
  symbol: e.currencySymbol,
@@ -247,9 +262,9 @@ const z = () => ({
247
262
  decimalCount: String(e.currencyDecimalCount),
248
263
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
249
264
  thousandSeparator: r(e.currencyThousandSeparator, ".")
250
- }, s = !this.blockStates[t], i = s ? C() : this.blockStates[t];
251
- i.recommendationConfigs = {
252
- ...i.recommendationConfigs,
265
+ }, s = !this.blockStates[t], n = s ? P() : this.blockStates[t];
266
+ n.recommendationConfigs = {
267
+ ...n.recommendationConfigs,
253
268
  strategy: e.strategy,
254
269
  strategyId: e.strategyId,
255
270
  language: e.language,
@@ -257,10 +272,10 @@ const z = () => ({
257
272
  productIds: e.productIds,
258
273
  filters: e.filters,
259
274
  shuffleProducts: e.shuffleProducts,
260
- currencySettings: n
275
+ currencySettings: i
261
276
  }, s && (this.blockStates = {
262
277
  ...this.blockStates,
263
- [t]: i
278
+ [t]: n
264
279
  });
265
280
  },
266
281
  /**
@@ -279,18 +294,18 @@ const z = () => ({
279
294
  ...t.currencySettings || {}
280
295
  }
281
296
  };
282
- const { triggerRefetch: n = !0 } = e;
283
- n && this.configVersion++;
297
+ const { triggerRefetch: i = !0 } = e;
298
+ i && this.configVersion++;
284
299
  },
285
300
  /**
286
301
  * Creates a filter with the first available attribute and operator pre-selected.
287
302
  */
288
303
  createDefaultFilter(t, e) {
289
- const [r] = this.getFilterList, [n] = G(r == null ? void 0 : r.type);
304
+ const [r] = this.getFilterList, [i] = N(r == null ? void 0 : r.type);
290
305
  return {
291
306
  type: "standardFilter",
292
307
  attribute: (r == null ? void 0 : r.value) ?? "",
293
- operator: (n == null ? void 0 : n.value) ?? "",
308
+ operator: (i == null ? void 0 : i.value) ?? "",
294
309
  innerGroupOperator: "*",
295
310
  outerGroupOperator: "*",
296
311
  filterNumber: e,
@@ -346,33 +361,115 @@ const z = () => ({
346
361
  this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !1);
347
362
  },
348
363
  async fetchStrategies() {
349
- if (S) {
350
- await S;
364
+ for (; R; )
365
+ try {
366
+ await R;
367
+ } catch {
368
+ }
369
+ if (this.strategiesFetched)
351
370
  return;
352
- }
353
- S = (async () => {
371
+ const t = b, e = (async () => {
354
372
  this.strategiesLoading = !0;
355
373
  try {
356
- this.strategies = await u.fetchRecommendationStrategies();
374
+ const r = await f.fetchRecommendationStrategies();
375
+ if (t !== b)
376
+ return;
377
+ this.strategies = r, this.strategiesFetched = !0;
357
378
  } finally {
358
- this.strategiesLoading = !1;
379
+ this.strategiesLoading = !1, t === b && (this.strategiesSettled = !0);
359
380
  }
360
381
  })();
382
+ R = e;
361
383
  try {
362
- await S;
384
+ await e;
363
385
  } finally {
364
- S = null;
386
+ R = null;
365
387
  }
366
388
  },
389
+ /**
390
+ * Clears what a block inherited from the id it arrived holding, so the panel fetches for it.
391
+ * A re-dropped saved module can keep the source's id — and its "already fetched" entry —
392
+ * while the strategy's product count has moved on (SD-149916).
393
+ */
394
+ resetArrivedBlock(t) {
395
+ const e = this.blockStates[t];
396
+ e != null && e.recommendationConfigs.strategyId && (e.isInitialized = !1, e.syncedConfigFingerprint = null, this.invalidateStrategies(), this.fetchStrategies().catch(() => {
397
+ }));
398
+ },
399
+ invalidateStrategies() {
400
+ this.strategiesFetched = !1, this.strategiesSettled = !1, b += 1;
401
+ },
402
+ /**
403
+ * Opens a Smart Recommender strategy page in a new tab and refreshes the list every time
404
+ * the user comes back. `visibilitychange` only: a `focus` listener would also fire when
405
+ * focus merely returns from the Stripo canvas iframe to the panel, refetching and
406
+ * regenerating the canvas while the user never left the editor (SD-149916).
407
+ *
408
+ * Deliberately NOT one-shot: the strategy tab stays open, so a user can create a second
409
+ * strategy in it and come back again — a listener that disarmed on the first return would
410
+ * leave that one missing from the list. It disarms on the first return AFTER that tab is
411
+ * closed instead, so an ordinary alt-tab later in the session costs nothing. Armed only
412
+ * once the user actually opens a strategy page, so anyone who never does pays nothing.
413
+ * @returns whether a tab actually opened, so callers can skip work that assumes one did
414
+ */
415
+ _armStrategyPageReturn(t) {
416
+ const e = window.open(t, "_blank");
417
+ if (!e)
418
+ return D().showToaster({
419
+ type: L.Error,
420
+ message: _()("discovery-strategy.error-description")
421
+ }), !1;
422
+ e.opener = null, l == null || l();
423
+ let r = !1;
424
+ const i = () => {
425
+ document.hidden || r || (e != null && e.closed && (l == null || l()), r = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
426
+ }).finally(() => {
427
+ r = !1;
428
+ }));
429
+ };
430
+ return document.addEventListener("visibilitychange", i), l = () => {
431
+ document.removeEventListener("visibilitychange", i), l = null;
432
+ }, !0;
433
+ },
434
+ /**
435
+ * Reloads the products of the block the user is on, so an edit made in the strategy tab
436
+ * lands on the canvas instead of waiting for the assign drawer to be reopened.
437
+ *
438
+ * Only the selected block: the canvas regeneration is driven off the active block's
439
+ * product array, so refreshing an unselected block would fetch without redrawing it.
440
+ */
441
+ async _refreshAssignedStrategyProducts() {
442
+ var e;
443
+ const t = this.currentRecommendationId;
444
+ t === null || !((e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId) || await this.fetchRecommendationProducts();
445
+ },
446
+ /** Lets the editor tear the return listeners down when it unmounts. */
447
+ disposeStrategyPageReturn() {
448
+ l == null || l();
449
+ },
450
+ /**
451
+ * Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
452
+ * new strategy is offered; the user picks it themselves.
453
+ */
454
+ openStrategyCreation() {
455
+ return this._armStrategyPageReturn(E.RECOMMENDATION_STRATEGY_CREATE_PATH);
456
+ },
457
+ /**
458
+ * Opens the strategy's edit page. Every return refreshes the list so the block reflects the
459
+ * edited configuration.
460
+ */
461
+ openStrategyEdit(t) {
462
+ return this._armStrategyPageReturn(A(t));
463
+ },
367
464
  async resolveStrategyUrlTemplate(t, e) {
368
- const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
465
+ const r = typeof t.updated_at == "string" ? t.updated_at : "", i = r ? `${t.strategy_id}:${r}:${e}` : "", s = i ? this.strategyUrlTemplates[i] : void 0;
369
466
  if (s)
370
467
  return s;
371
- const { full_path: i = "" } = await u.generateStrategyEndpoint(t, e) ?? {};
372
- return n && i && (this.strategyUrlTemplates = {
468
+ const { full_path: n = "" } = await f.generateStrategyEndpoint(t, e) ?? {};
469
+ return i && n && (this.strategyUrlTemplates = {
373
470
  ...this.strategyUrlTemplates,
374
- [n]: i
375
- }), i;
471
+ [i]: n
472
+ }), n;
376
473
  },
377
474
  assignStrategy(t) {
378
475
  this.patchCurrentBlockConfig({
@@ -381,50 +478,54 @@ const z = () => ({
381
478
  });
382
479
  },
383
480
  removeStrategy() {
384
- this.patchCurrentBlockConfig({ strategyId: "", size: N.size });
481
+ this.patchCurrentBlockConfig({
482
+ strategyId: "",
483
+ strategy: "",
484
+ size: M.size
485
+ });
385
486
  },
386
487
  // ====================================================================
387
488
  // Shared Data Fetching (fetched once, used by all blocks)
388
489
  // ====================================================================
389
490
  async fetchRecommendationCreateData() {
390
491
  if (!this.activePredictiveAlgorithms.length) {
391
- if (f) {
392
- await f;
492
+ if (S) {
493
+ await S;
393
494
  return;
394
495
  }
395
- f = (async () => {
496
+ S = (async () => {
396
497
  const {
397
498
  activePredictiveAlgorithms: t,
398
499
  languages: e,
399
500
  currencies: r
400
- } = await u.fetchRecommendationCreateData();
501
+ } = await f.fetchRecommendationCreateData();
401
502
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
402
- const n = this.blockStates[this.currentRecommendationId];
403
- n.filterStatus = !!n.recommendationConfigs.filters.length;
503
+ const i = this.blockStates[this.currentRecommendationId];
504
+ i.filterStatus = !!i.recommendationConfigs.filters.length;
404
505
  }
405
506
  this.currencyList = r;
406
507
  })();
407
508
  try {
408
- await f;
509
+ await S;
409
510
  } finally {
410
- f = null;
511
+ S = null;
411
512
  }
412
513
  }
413
514
  },
414
515
  async fetchRecommendationFilters() {
415
516
  if (!Object.keys(this.filterList).length) {
416
- if (g) {
417
- await g;
517
+ if (p) {
518
+ await p;
418
519
  return;
419
520
  }
420
- g = (async () => {
421
- const t = await u.fetchRecommendationFilters();
521
+ p = (async () => {
522
+ const t = await f.fetchRecommendationFilters();
422
523
  this.filterList = t;
423
524
  })();
424
525
  try {
425
- await g;
526
+ await p;
426
527
  } finally {
427
- g = null;
528
+ p = null;
428
529
  }
429
530
  }
430
531
  },
@@ -439,41 +540,41 @@ const z = () => ({
439
540
  deleteFilterGroup(t) {
440
541
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
441
542
  return;
442
- const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((i) => i.filterGroup !== t), n = [...new Set(r.map((i) => i.filterGroup))].sort((i, o) => i - o), s = new Map(n.map((i, o) => [i, o + 1]));
443
- e.recommendationConfigs.filters = r.map((i) => ({
444
- ...i,
445
- filterGroup: s.get(i.filterGroup) ?? i.filterGroup
543
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((n) => n.filterGroup !== t), i = [...new Set(r.map((n) => n.filterGroup))].sort((n, o) => n - o), s = new Map(i.map((n, o) => [n, o + 1]));
544
+ e.recommendationConfigs.filters = r.map((n) => ({
545
+ ...n,
546
+ filterGroup: s.get(n.filterGroup) ?? n.filterGroup
446
547
  }));
447
548
  },
448
549
  updateFilter(t) {
449
550
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
450
551
  return;
451
- const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
552
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((i) => i.filterNumber === t.filterNumber && i.filterGroup === t.filterGroup);
452
553
  if (r !== -1) {
453
- const n = [...e.recommendationConfigs.filters];
454
- n[r] = {
554
+ const i = [...e.recommendationConfigs.filters];
555
+ i[r] = {
455
556
  ...t,
456
- isValid: B(t)
457
- }, e.recommendationConfigs.filters = n;
557
+ isValid: $(t)
558
+ }, e.recommendationConfigs.filters = i;
458
559
  }
459
560
  },
460
561
  deleteFilter(t) {
461
562
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
462
563
  return;
463
- const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
564
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((i) => i.filterNumber === t.filterNumber && i.filterGroup === t.filterGroup);
464
565
  if (r !== -1) {
465
- let n = [...e.recommendationConfigs.filters];
466
- if (n.splice(r, 1), n.some((i) => i.filterGroup === t.filterGroup)) {
467
- let i = 1;
468
- n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
566
+ let i = [...e.recommendationConfigs.filters];
567
+ if (i.splice(r, 1), i.some((n) => n.filterGroup === t.filterGroup)) {
568
+ let n = 1;
569
+ i = i.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: n++ } : o);
469
570
  } else {
470
- const i = [...new Set(n.map((c) => c.filterGroup))].sort((c, a) => c - a), o = new Map(i.map((c, a) => [c, a + 1]));
471
- n = n.map((c) => ({
472
- ...c,
473
- filterGroup: o.get(c.filterGroup) ?? c.filterGroup
571
+ const n = [...new Set(i.map((a) => a.filterGroup))].sort((a, m) => a - m), o = new Map(n.map((a, m) => [a, m + 1]));
572
+ i = i.map((a) => ({
573
+ ...a,
574
+ filterGroup: o.get(a.filterGroup) ?? a.filterGroup
474
575
  }));
475
576
  }
476
- e.recommendationConfigs.filters = n;
577
+ e.recommendationConfigs.filters = i;
477
578
  }
478
579
  },
479
580
  addFilter(t) {
@@ -481,8 +582,8 @@ const z = () => ({
481
582
  return;
482
583
  const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], s = r.filter(
483
584
  (o) => o.filterGroup === t.filterGroup
484
- ).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
485
- i !== -1 ? r.splice(i + 1, 0, {
585
+ ).length + 1, n = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
586
+ n !== -1 ? r.splice(n + 1, 0, {
486
587
  ...t,
487
588
  filterNumber: s
488
589
  }) : r.push({
@@ -491,7 +592,7 @@ const z = () => ({
491
592
  }), e.recommendationConfigs.filters = r;
492
593
  },
493
594
  generateFilterQuery() {
494
- return k(this.recommendationConfigs.filters);
595
+ return w(this.recommendationConfigs.filters);
495
596
  },
496
597
  /**
497
598
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -499,83 +600,102 @@ const z = () => ({
499
600
  * every block's recommendationConfigs across user edits.
500
601
  */
501
602
  hasInvalidBlock() {
502
- return Object.values(this.blockStates).some((t) => !M(t.recommendationConfigs, this));
603
+ return Object.values(this.blockStates).some((t) => !K(t.recommendationConfigs, this));
604
+ },
605
+ /**
606
+ * Which required fields are missing across the design's blocks, so the save
607
+ * toaster can name the cause instead of always showing the generic message.
608
+ */
609
+ invalidBlockFields() {
610
+ return Object.values(this.blockStates).flatMap((t) => q(t.recommendationConfigs, this));
503
611
  },
504
612
  // ====================================================================
505
613
  // Per-Block Product Fetching
506
614
  // ====================================================================
507
615
  async fetchRecommendationProducts() {
508
- if (!(this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])) {
509
- if (h) {
510
- await h;
511
- return;
512
- }
513
- h = this._doFetchProducts();
514
- try {
515
- await h;
516
- } finally {
517
- h = null;
518
- }
616
+ if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
617
+ return;
618
+ const t = [
619
+ this.currentRecommendationId,
620
+ this.configVersion,
621
+ b
622
+ ].join(":");
623
+ if (y && I === t) {
624
+ await y;
625
+ return;
626
+ }
627
+ I = t, y = this._doFetchProducts();
628
+ try {
629
+ await y;
630
+ } finally {
631
+ I === t && (y = null, I = "");
519
632
  }
520
633
  },
521
634
  async _doFetchProducts() {
522
- var d;
523
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = F();
524
- if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
635
+ var h;
636
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, i = k();
637
+ if (r.strategyId && i.isFeatureEnabled("reusableRecommendationStrategy")) {
525
638
  await this._doFetchStrategyProducts(t);
526
639
  return;
527
640
  }
528
- const s = r.filters.filter((m) => m.isValid), i = k(s), o = ((d = b().find((m) => m.key === r.strategy)) == null ? void 0 : d.path) || "", c = parseInt(r.size) || 6, a = {
641
+ const s = (g.get(t) ?? 0) + 1;
642
+ g.set(t, s);
643
+ const n = parseInt(r.size) || 6;
644
+ if (!r.strategy) {
645
+ this._setBlockProducts(t, [], n);
646
+ return;
647
+ }
648
+ const o = r.filters.filter((d) => d.isValid), a = w(o), m = ((h = v().find((d) => d.key === r.strategy)) == null ? void 0 : h.path) || "", c = {
529
649
  locale: r.language,
530
650
  currency: r.currencySettings.value,
531
- partnerName: n.partnerName,
651
+ partnerName: i.partnerName,
532
652
  size: r.size,
533
653
  details: !0,
534
- campaignId: n.variationId
654
+ campaignId: i.variationId
535
655
  };
536
- r.strategy === "manualMerchandising" ? a.productId = A(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), i && (a.filter = i), r.shuffleProducts && (a.shuffle = !0), Object.assign(
537
- a,
538
- x(n.partnerName, r.strategy)
656
+ r.strategy === "manualMerchandising" ? c.productId = j(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), a && (c.filter = a), r.shuffleProducts && (c.shuffle = !0), Object.assign(
657
+ c,
658
+ V(i.partnerName, r.strategy)
539
659
  );
540
- let l;
660
+ let u;
541
661
  try {
542
- l = await u.fetchRecommendationProducts(o, a);
662
+ u = await f.fetchRecommendationProducts(m, c);
543
663
  } catch {
544
- l = [];
664
+ u = [];
545
665
  }
546
- this._setBlockProducts(t, l, c);
666
+ g.get(t) === s && this._setBlockProducts(t, u, n);
547
667
  },
548
668
  async _doFetchStrategyProducts(t) {
549
- const e = (p.get(t) ?? 0) + 1;
550
- p.set(t, e);
669
+ const e = (g.get(t) ?? 0) + 1;
670
+ g.set(t, e);
551
671
  const { recommendationConfigs: r } = this.blockStates[t];
552
- let n = [], s = parseInt(r.size) || 6;
672
+ let i = [], s = parseInt(r.size) || 6;
553
673
  try {
554
674
  await this.fetchStrategies();
555
- const i = this.strategies.find(
675
+ const n = this.strategies.find(
556
676
  (o) => String(o.strategy_id) === r.strategyId
557
677
  );
558
- if (i) {
559
- s = parseInt(String(i.size ?? "")) || s;
560
- const o = r.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), l = a.indexOf("?"), d = l === -1 ? a : a.slice(0, l), m = (l === -1 ? "" : a.slice(l + 1)).split("&").filter((I) => I && !I.startsWith("user_id=")), y = m.length ? `${d}?${m.join("&")}` : d;
561
- y && (n = await u.fetchRecommendationProductsByUrl(y));
678
+ if (n) {
679
+ s = parseInt(String(n.size ?? "")) || s;
680
+ const o = r.currencySettings.value, m = (await this.resolveStrategyUrlTemplate(n, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), c = m.indexOf("?"), u = c === -1 ? m : m.slice(0, c), h = (c === -1 ? "" : m.slice(c + 1)).split("&").filter((C) => C && !C.startsWith("user_id=")), d = h.length ? `${u}?${h.join("&")}` : u;
681
+ d && (i = await f.fetchRecommendationProductsByUrl(d));
562
682
  }
563
683
  } catch {
564
- n = [];
684
+ i = [];
565
685
  }
566
- p.get(t) === e && this._setBlockProducts(t, n, s);
686
+ g.get(t) === e && this._setBlockProducts(t, i, s);
567
687
  },
568
688
  _setBlockProducts(t, e, r) {
569
689
  if (!this.blockStates[t])
570
690
  return;
571
- const n = e.length > 0 ? e : R(r);
691
+ const i = e.length > 0 ? e : F(r);
572
692
  this.blockStates[t].recommendationProducts = [
573
- ...n.slice(0, r),
574
- ...R(Math.max(0, r - n.length))
693
+ ...i.slice(0, r),
694
+ ...F(Math.max(0, r - i.length))
575
695
  ];
576
696
  }
577
697
  }
578
698
  });
579
699
  export {
580
- Y as useRecommendationExtensionStore
700
+ mt as useRecommendationExtensionStore
581
701
  };