@useinsider/guido 3.18.1-beta.dea994d → 3.18.1-beta.e489d08

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 (34) 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 +46 -41
  9. package/dist/composables/useHtmlValidator.js +84 -79
  10. package/dist/composables/usePreviewMode.js +11 -12
  11. package/dist/composables/useRecommendation.js +21 -18
  12. package/dist/composables/useStripo.js +7 -7
  13. package/dist/config/compiler/htmlCompilerRules.js +6 -14
  14. package/dist/enums/extensions/recommendationBlock.js +38 -31
  15. package/dist/extensions/Blocks/Recommendation/block.js +43 -39
  16. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +62 -57
  17. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +80 -52
  18. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +150 -73
  19. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +21 -12
  20. package/dist/guido.css +1 -1
  21. package/dist/src/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.d.ts +2 -0
  22. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  23. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +2 -0
  24. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +9 -0
  25. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +61 -0
  26. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +4 -0
  27. package/dist/src/stores/onboarding.d.ts +377 -0
  28. package/dist/static/styles/components/recommendation-strategy.css.js +51 -4
  29. package/dist/static/styles/customEditorStyle.css.js +0 -14
  30. package/dist/stores/onboarding.js +41 -36
  31. package/dist/utils/templatePreparation.js +50 -50
  32. package/package.json +1 -1
  33. package/dist/src/utils/appendMobileHiddenColumnCss.d.ts +0 -2
  34. package/dist/utils/appendMobileHiddenColumnCss.js +0 -11
@@ -1,22 +1,23 @@
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";
1
+ import { getRecommendationFeedSourceMaps as k, strategyEditPath as G, URLS as D, getOperatorOptions as _, PriceAttributes as L } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as E } from "../../../../services/recommendationApi.js";
3
3
  import { useConfigStore as P } from "../../../../stores/config.js";
4
- import { defineStore as F } from "pinia";
4
+ import { defineStore as N } from "pinia";
5
5
  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, h = null, g = null, S = null;
16
- const p = /* @__PURE__ */ new Map();
17
- function w() {
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as U, DEFAULT_CARDS_IN_ROW as A } from "../constants/layout.js";
7
+ import { DEFAULT_NODE_CONFIG as x, EXCLUDED_ALGORITHM_IDS as B } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as C } from "../templates/utils.js";
9
+ import { generateCompleteFilterQuery as v } from "../utils/filterUtil.js";
10
+ import { getPartnerRecommendationParams as M } from "../utils/partnerCustomizations.js";
11
+ import { sanitizeProductIds as O } from "../utils/productIds.js";
12
+ import { isFilterValid as T } from "../validation/filterSchema.js";
13
+ import { getInvalidFields as z, isConfigValid as V } from "../validation/requiredFields.js";
14
+ const d = E();
15
+ let h = null, g = null, S = null, p = null, y = 0;
16
+ const b = /* @__PURE__ */ new Map();
17
+ let u = null;
18
+ function F() {
18
19
  return {
19
- cardsInRow: U,
20
+ cardsInRow: A,
20
21
  currencySettings: {
21
22
  name: "USD",
22
23
  value: "USD",
@@ -30,13 +31,17 @@ function w() {
30
31
  productIds: [],
31
32
  id: 1,
32
33
  language: "en_US",
33
- mobileCardsInRow: _,
34
+ mobileCardsInRow: U,
34
35
  mobileLayoutEnabled: !1,
35
36
  orientation: "grid",
36
37
  recommendedProducts: [],
37
38
  sendProductRequestFlag: !1,
38
39
  shuffleProducts: !1,
39
- strategy: "mostPopular",
40
+ // With RRS on, a block that has not been synced yet must not claim an algorithm:
41
+ // that default is what reached buildCampaignUrl and baked a most-popular feed URL
42
+ // for a block the user never configured (SD-149916). Legacy blocks still get their real
43
+ // algorithm from the node-config sync or the save-time seed.
44
+ strategy: P().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
40
45
  strategyId: "",
41
46
  textTrimming: !0,
42
47
  unresponsive: !1,
@@ -46,9 +51,9 @@ function w() {
46
51
  priceHideIfSameAsDiscounted: !1
47
52
  };
48
53
  }
49
- function C() {
54
+ function w() {
50
55
  return {
51
- recommendationConfigs: w(),
56
+ recommendationConfigs: F(),
52
57
  recommendationProducts: [],
53
58
  filterStatus: !1,
54
59
  filterSelectionDrawerStatus: !1,
@@ -59,7 +64,7 @@ function C() {
59
64
  filterSnapshot: null
60
65
  };
61
66
  }
62
- const z = () => ({
67
+ const j = () => ({
63
68
  recommendationCampaignUrls: {},
64
69
  activePredictiveAlgorithms: [],
65
70
  languages: {},
@@ -68,11 +73,13 @@ const z = () => ({
68
73
  strategies: [],
69
74
  strategiesLoading: !1,
70
75
  strategyUrlTemplates: {},
76
+ strategiesFetched: !1,
71
77
  blockStates: {},
72
78
  currentRecommendationId: null,
79
+ strategyPanelOpen: !1,
73
80
  configVersion: 0
74
- }), Y = F("guidoRecommendationExtension", {
75
- state: () => z(),
81
+ }), nt = N("guidoRecommendationExtension", {
82
+ state: () => j(),
76
83
  getters: {
77
84
  // ====================================================================
78
85
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -82,7 +89,7 @@ const z = () => ({
82
89
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
83
90
  */
84
91
  recommendationConfigs(t) {
85
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : w();
92
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : F();
86
93
  },
87
94
  /**
88
95
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -137,8 +144,8 @@ const z = () => ({
137
144
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
138
145
  },
139
146
  getActivePredictiveAlgorithms: (t) => {
140
- const e = b(), r = [];
141
- return t.activePredictiveAlgorithms.filter((n) => !L.includes(n)).forEach((n) => {
147
+ const e = k(), r = [];
148
+ return t.activePredictiveAlgorithms.filter((n) => !B.includes(n)).forEach((n) => {
142
149
  r.push(...e.filter((s) => s.id === n));
143
150
  }), r.map((n) => ({
144
151
  text: n.name,
@@ -156,7 +163,7 @@ const z = () => ({
156
163
  getFilterList() {
157
164
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
158
165
  let e;
159
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : v.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
166
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : L.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
160
167
  text: t.displayName,
161
168
  value: e,
162
169
  type: t.attributeType
@@ -178,7 +185,7 @@ const z = () => ({
178
185
  setCurrentBlock(t) {
179
186
  this.blockStates[t] || (this.blockStates = {
180
187
  ...this.blockStates,
181
- [t]: C()
188
+ [t]: w()
182
189
  }), this.currentRecommendationId = t;
183
190
  },
184
191
  /**
@@ -246,7 +253,7 @@ const z = () => ({
246
253
  decimalCount: String(e.currencyDecimalCount),
247
254
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
248
255
  thousandSeparator: r(e.currencyThousandSeparator, ".")
249
- }, s = !this.blockStates[t], i = s ? C() : this.blockStates[t];
256
+ }, s = !this.blockStates[t], i = s ? w() : this.blockStates[t];
250
257
  i.recommendationConfigs = {
251
258
  ...i.recommendationConfigs,
252
259
  strategy: e.strategy,
@@ -285,7 +292,7 @@ const z = () => ({
285
292
  * Creates a filter with the first available attribute and operator pre-selected.
286
293
  */
287
294
  createDefaultFilter(t, e) {
288
- const [r] = this.getFilterList, [n] = G(r == null ? void 0 : r.type);
295
+ const [r] = this.getFilterList, [n] = _(r == null ? void 0 : r.type);
289
296
  return {
290
297
  type: "standardFilter",
291
298
  attribute: (r == null ? void 0 : r.value) ?? "",
@@ -345,29 +352,87 @@ const z = () => ({
345
352
  this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !1);
346
353
  },
347
354
  async fetchStrategies() {
348
- if (S) {
349
- await S;
355
+ if (p && await p, this.strategiesFetched)
350
356
  return;
351
- }
352
- S = (async () => {
357
+ const t = y;
358
+ p = (async () => {
353
359
  this.strategiesLoading = !0;
354
360
  try {
355
- this.strategies = await u.fetchRecommendationStrategies();
361
+ const e = await d.fetchRecommendationStrategies();
362
+ if (t !== y)
363
+ return;
364
+ this.strategies = e, this.strategiesFetched = !0;
356
365
  } finally {
357
366
  this.strategiesLoading = !1;
358
367
  }
359
368
  })();
360
369
  try {
361
- await S;
370
+ await p;
362
371
  } finally {
363
- S = null;
372
+ p = null;
364
373
  }
365
374
  },
375
+ invalidateStrategies() {
376
+ this.strategiesFetched = !1, y += 1;
377
+ },
378
+ /**
379
+ * Opens a Smart Recommender strategy page in a new tab and refreshes the list every time
380
+ * the user comes back. `visibilitychange` covers the tab case; `focus` covers the page
381
+ * landing in a separate window, where this document never goes hidden.
382
+ *
383
+ * Deliberately NOT one-shot: the strategy tab stays open, so a user can create a second
384
+ * strategy in it and come back again — a listener that disarmed on the first return would
385
+ * leave that one missing from the list. Armed only once the user actually opens a strategy
386
+ * page, so anyone who never does pays nothing.
387
+ */
388
+ _armStrategyPageReturn(t) {
389
+ window.open(t, "_blank", "noopener"), u == null || u();
390
+ let e = !1;
391
+ const r = () => {
392
+ document.hidden || e || (e = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
393
+ }).finally(() => {
394
+ e = !1;
395
+ }));
396
+ };
397
+ document.addEventListener("visibilitychange", r), window.addEventListener("focus", r), u = () => {
398
+ document.removeEventListener("visibilitychange", r), window.removeEventListener("focus", r), u = null;
399
+ };
400
+ },
401
+ /**
402
+ * Reloads the products of the block the user is on, so an edit made in the strategy tab
403
+ * lands on the canvas instead of waiting for the assign drawer to be reopened.
404
+ *
405
+ * Only the selected block: the canvas regeneration is driven off the active block's
406
+ * product array, so refreshing an unselected block would fetch without redrawing it.
407
+ */
408
+ async _refreshAssignedStrategyProducts() {
409
+ var e;
410
+ const t = this.currentRecommendationId;
411
+ t === null || !((e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId) || await this.fetchRecommendationProducts();
412
+ },
413
+ /** Lets the editor tear the return listeners down when it unmounts. */
414
+ disposeStrategyPageReturn() {
415
+ u == null || u();
416
+ },
417
+ /**
418
+ * Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
419
+ * new strategy is offered; the user picks it themselves.
420
+ */
421
+ openStrategyCreation() {
422
+ this._armStrategyPageReturn(D.RECOMMENDATION_STRATEGY_CREATE_PATH);
423
+ },
424
+ /**
425
+ * Opens the strategy's edit page. Every return refreshes the list so the block reflects the
426
+ * edited configuration.
427
+ */
428
+ openStrategyEdit(t) {
429
+ this._armStrategyPageReturn(G(t));
430
+ },
366
431
  async resolveStrategyUrlTemplate(t, e) {
367
432
  const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
368
433
  if (s)
369
434
  return s;
370
- const { full_path: i = "" } = await u.generateStrategyEndpoint(t, e) ?? {};
435
+ const { full_path: i = "" } = await d.generateStrategyEndpoint(t, e) ?? {};
371
436
  return n && i && (this.strategyUrlTemplates = {
372
437
  ...this.strategyUrlTemplates,
373
438
  [n]: i
@@ -380,23 +445,23 @@ const z = () => ({
380
445
  });
381
446
  },
382
447
  removeStrategy() {
383
- this.patchCurrentBlockConfig({ strategyId: "", size: N.size });
448
+ this.patchCurrentBlockConfig({ strategyId: "", size: x.size });
384
449
  },
385
450
  // ====================================================================
386
451
  // Shared Data Fetching (fetched once, used by all blocks)
387
452
  // ====================================================================
388
453
  async fetchRecommendationCreateData() {
389
454
  if (!this.activePredictiveAlgorithms.length) {
390
- if (f) {
391
- await f;
455
+ if (h) {
456
+ await h;
392
457
  return;
393
458
  }
394
- f = (async () => {
459
+ h = (async () => {
395
460
  const {
396
461
  activePredictiveAlgorithms: t,
397
462
  languages: e,
398
463
  currencies: r
399
- } = await u.fetchRecommendationCreateData();
464
+ } = await d.fetchRecommendationCreateData();
400
465
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
401
466
  const n = this.blockStates[this.currentRecommendationId];
402
467
  n.filterStatus = !!n.recommendationConfigs.filters.length;
@@ -404,26 +469,26 @@ const z = () => ({
404
469
  this.currencyList = r;
405
470
  })();
406
471
  try {
407
- await f;
472
+ await h;
408
473
  } finally {
409
- f = null;
474
+ h = null;
410
475
  }
411
476
  }
412
477
  },
413
478
  async fetchRecommendationFilters() {
414
479
  if (!Object.keys(this.filterList).length) {
415
- if (h) {
416
- await h;
480
+ if (g) {
481
+ await g;
417
482
  return;
418
483
  }
419
- h = (async () => {
420
- const t = await u.fetchRecommendationFilters();
484
+ g = (async () => {
485
+ const t = await d.fetchRecommendationFilters();
421
486
  this.filterList = t;
422
487
  })();
423
488
  try {
424
- await h;
489
+ await g;
425
490
  } finally {
426
- h = null;
491
+ g = null;
427
492
  }
428
493
  }
429
494
  },
@@ -452,7 +517,7 @@ const z = () => ({
452
517
  const n = [...e.recommendationConfigs.filters];
453
518
  n[r] = {
454
519
  ...t,
455
- isValid: B(t)
520
+ isValid: T(t)
456
521
  }, e.recommendationConfigs.filters = n;
457
522
  }
458
523
  },
@@ -490,7 +555,7 @@ const z = () => ({
490
555
  }), e.recommendationConfigs.filters = r;
491
556
  },
492
557
  generateFilterQuery() {
493
- return k(this.recommendationConfigs.filters);
558
+ return v(this.recommendationConfigs.filters);
494
559
  },
495
560
  /**
496
561
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -498,33 +563,45 @@ const z = () => ({
498
563
  * every block's recommendationConfigs across user edits.
499
564
  */
500
565
  hasInvalidBlock() {
501
- return Object.values(this.blockStates).some((t) => !M(t.recommendationConfigs, this));
566
+ return Object.values(this.blockStates).some((t) => !V(t.recommendationConfigs, this));
567
+ },
568
+ /**
569
+ * Which required fields are missing across the design's blocks, so the save
570
+ * toaster can name the cause instead of always showing the generic message.
571
+ */
572
+ invalidBlockFields() {
573
+ return Object.values(this.blockStates).flatMap((t) => z(t.recommendationConfigs, this));
502
574
  },
503
575
  // ====================================================================
504
576
  // Per-Block Product Fetching
505
577
  // ====================================================================
506
578
  async fetchRecommendationProducts() {
507
579
  if (!(this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])) {
508
- if (g) {
509
- await g;
580
+ if (S) {
581
+ await S;
510
582
  return;
511
583
  }
512
- g = this._doFetchProducts();
584
+ S = this._doFetchProducts();
513
585
  try {
514
- await g;
586
+ await S;
515
587
  } finally {
516
- g = null;
588
+ S = null;
517
589
  }
518
590
  }
519
591
  },
520
592
  async _doFetchProducts() {
521
- var d;
593
+ var f;
522
594
  const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = P();
523
595
  if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
524
596
  await this._doFetchStrategyProducts(t);
525
597
  return;
526
598
  }
527
- 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 = {
599
+ const s = parseInt(r.size) || 6;
600
+ if (!r.strategy) {
601
+ this._setBlockProducts(t, [], s);
602
+ return;
603
+ }
604
+ const i = r.filters.filter((m) => m.isValid), o = v(i), c = ((f = k().find((m) => m.key === r.strategy)) == null ? void 0 : f.path) || "", a = {
528
605
  locale: r.language,
529
606
  currency: r.currencySettings.value,
530
607
  partnerName: n.partnerName,
@@ -532,21 +609,21 @@ const z = () => ({
532
609
  details: !0,
533
610
  campaignId: n.variationId
534
611
  };
535
- 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(
612
+ r.strategy === "manualMerchandising" ? a.productId = O(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), o && (a.filter = o), r.shuffleProducts && (a.shuffle = !0), Object.assign(
536
613
  a,
537
- x(n.partnerName, r.strategy)
614
+ M(n.partnerName, r.strategy)
538
615
  );
539
616
  let l;
540
617
  try {
541
- l = await u.fetchRecommendationProducts(o, a);
618
+ l = await d.fetchRecommendationProducts(c, a);
542
619
  } catch {
543
620
  l = [];
544
621
  }
545
- this._setBlockProducts(t, l, c);
622
+ this._setBlockProducts(t, l, s);
546
623
  },
547
624
  async _doFetchStrategyProducts(t) {
548
- const e = (p.get(t) ?? 0) + 1;
549
- p.set(t, e);
625
+ const e = (b.get(t) ?? 0) + 1;
626
+ b.set(t, e);
550
627
  const { recommendationConfigs: r } = this.blockStates[t];
551
628
  let n = [], s = parseInt(r.size) || 6;
552
629
  try {
@@ -556,25 +633,25 @@ const z = () => ({
556
633
  );
557
634
  if (i) {
558
635
  s = parseInt(String(i.size ?? "")) || s;
559
- 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;
560
- y && (n = await u.fetchRecommendationProductsByUrl(y));
636
+ const o = r.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), l = a.indexOf("?"), f = l === -1 ? a : a.slice(0, l), m = (l === -1 ? "" : a.slice(l + 1)).split("&").filter((R) => R && !R.startsWith("user_id=")), I = m.length ? `${f}?${m.join("&")}` : f;
637
+ I && (n = await d.fetchRecommendationProductsByUrl(I));
561
638
  }
562
639
  } catch {
563
640
  n = [];
564
641
  }
565
- p.get(t) === e && this._setBlockProducts(t, n, s);
642
+ b.get(t) === e && this._setBlockProducts(t, n, s);
566
643
  },
567
644
  _setBlockProducts(t, e, r) {
568
645
  if (!this.blockStates[t])
569
646
  return;
570
- const n = e.length > 0 ? e : R(r);
647
+ const n = e.length > 0 ? e : C(r);
571
648
  this.blockStates[t].recommendationProducts = [
572
649
  ...n.slice(0, r),
573
- ...R(Math.max(0, r - n.length))
650
+ ...C(Math.max(0, r - n.length))
574
651
  ];
575
652
  }
576
653
  }
577
654
  });
578
655
  export {
579
- Y as useRecommendationExtensionStore
656
+ nt as useRecommendationExtensionStore
580
657
  };
@@ -1,34 +1,43 @@
1
- const l = [
1
+ const o = "strategy", u = [
2
2
  {
3
3
  key: "locale",
4
4
  getValue: (e) => e.language,
5
5
  getAvailableOptions: (e) => Object.keys(e.languages)
6
6
  },
7
+ {
8
+ // A block feeds from either an assigned strategy (RRS) or an algorithm (legacy).
9
+ // Only a fresh RRS block has neither, and its feed URL would otherwise be built
10
+ // from an empty algorithm path.
11
+ key: o,
12
+ getValue: (e) => e.strategyId || e.strategy
13
+ },
7
14
  {
8
15
  key: "currency",
9
16
  getValue: (e) => e.currencySettings.value,
10
17
  getAvailableOptions: (e) => e.currencyList.map((n) => n.text),
11
18
  canBeEmpty: !0
12
19
  }
13
- ], o = "newsletter.recommendation-fill-required-fields";
14
- function c(e, n, i = l) {
20
+ ], l = "newsletter.recommendation-fill-required-fields", g = "newsletter.recommendation-assign-strategy";
21
+ function c(e, n, i = u) {
15
22
  return i.filter((t) => {
16
- var u;
23
+ var s;
17
24
  if (t.condition && !t.condition(e))
18
25
  return !1;
19
- const r = (u = t.getAvailableOptions) == null ? void 0 : u.call(t, n);
20
- if (t.canBeEmpty && (r == null ? void 0 : r.length) === 0)
26
+ const a = (s = t.getAvailableOptions) == null ? void 0 : s.call(t, n);
27
+ if (t.canBeEmpty && (a == null ? void 0 : a.length) === 0)
21
28
  return !1;
22
- const a = t.getValue(e);
23
- return a ? r !== void 0 && !r.includes(a) : !0;
29
+ const r = t.getValue(e);
30
+ return r ? a !== void 0 && !a.includes(r) : !0;
24
31
  }).map((t) => t.key);
25
32
  }
26
- function s(e, n) {
33
+ function y(e, n) {
27
34
  return c(e, n).length === 0;
28
35
  }
29
36
  export {
30
- l as REQUIRED_RECOMMENDATION_FIELDS,
31
- o as RecommendationRequiredFieldsKey,
37
+ u as REQUIRED_RECOMMENDATION_FIELDS,
38
+ g as RecommendationAssignStrategyKey,
39
+ l as RecommendationRequiredFieldsKey,
40
+ o as STRATEGY_FIELD_KEY,
32
41
  c as getInvalidFields,
33
- s as isConfigValid
42
+ y as isConfigValid
34
43
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-050dfa52]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-050dfa52]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-050dfa52]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}
1
+ @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-cc5ee925]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-cc5ee925]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-cc5ee925]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-ec2027ff] .vueperslides__bullets,[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-b447f626]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-b447f626] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
2
+ export default _default;
@@ -5,7 +5,9 @@ export declare const URLS: {
5
5
  PRODUCT_ATTRIBUTES_PATH: string;
6
6
  RECOMMENDATION_STRATEGIES_PATH: string;
7
7
  RECOMMENDATION_STRATEGIES_PANEL_PATH: string;
8
+ RECOMMENDATION_STRATEGY_CREATE_PATH: string;
8
9
  };
10
+ export declare const strategyEditPath: (strategyId: string | number) => string;
9
11
  export declare const QUERY_PARAMS: {
10
12
  CLIENT_ID: string;
11
13
  };
@@ -66,6 +66,8 @@ export declare class RecommendationBlockControl extends CommonControl {
66
66
  * edits are never clobbered (SD-141049).
67
67
  */
68
68
  private _syncNodeConfigIfChanged;
69
+ /** Whether the deferred write would move the node's strategyId — see the caller (SD-149916). */
70
+ private _isPendingStrategyIdChange;
69
71
  private _capturePendingNodeConfigWrite;
70
72
  /**
71
73
  * Accumulates the pending write into `documentModifier` without applying, so the
@@ -2,6 +2,7 @@ import { CommonControl } from '../../../common-control';
2
2
  export declare const STRATEGY_CONTROL_ID = "recommendation-strategy-control";
3
3
  export declare class StrategyControl extends CommonControl {
4
4
  private store;
5
+ private onboardingStore;
5
6
  private storeUnsubscription;
6
7
  private listeners;
7
8
  private lastPaintedState;
@@ -9,6 +10,14 @@ export declare class StrategyControl extends CommonControl {
9
10
  getTemplate(): string;
10
11
  private _strategyAction;
11
12
  onRender(): void;
13
+ /**
14
+ * Whether the document itself says this block has no strategy.
15
+ *
16
+ * Read the node, not the store: when the main control cannot resolve the block id off the
17
+ * node it never syncs the persisted config, so the store still holds defaults (empty
18
+ * strategyId) for an assigned block.
19
+ */
20
+ private _isNodeUnassigned;
12
21
  onDestroy(): void;
13
22
  private _teardown;
14
23
  private _setupButtonListeners;