@useinsider/guido 3.18.2-beta.eedafd2 → 3.18.2-beta.f42547e

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 (80) hide show
  1. package/README.md +0 -1
  2. package/dist/@types/config/schemas.js +26 -28
  3. package/dist/components/Guido.vue.js +6 -6
  4. package/dist/components/Guido.vue2.js +70 -72
  5. package/dist/components/organisms/AutoSaveController.vue2.js +8 -9
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +13 -11
  7. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +70 -51
  8. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +23 -37
  9. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  10. package/dist/components/organisms/header/EditorActions.vue2.js +56 -86
  11. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  12. package/dist/components/organisms/header/LeftSlot.vue2.js +12 -16
  13. package/dist/components/organisms/onboarding/OnboardingWrapper.vue2.js +20 -18
  14. package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.js +21 -0
  15. package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue2.js +47 -0
  16. package/dist/components/wrappers/WpDrawer.vue.js +7 -9
  17. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  18. package/dist/composables/useCustomInterfaceAppearance.js +56 -46
  19. package/dist/composables/useHtmlCompiler.js +26 -37
  20. package/dist/composables/useHtmlValidator.js +84 -81
  21. package/dist/composables/useRecommendation.js +40 -35
  22. package/dist/composables/useSave.js +45 -90
  23. package/dist/composables/useStripo.js +30 -30
  24. package/dist/composables/validators/useCouponBlockValidator.js +21 -24
  25. package/dist/composables/validators/useUnsubscribeBlockValidator.js +27 -34
  26. package/dist/enums/extensions/recommendationBlock.js +38 -31
  27. package/dist/extensions/Blocks/Recommendation/block.js +43 -39
  28. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +21 -20
  29. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +40 -35
  30. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +84 -52
  31. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +74 -74
  32. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +181 -89
  33. package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +26 -16
  34. package/dist/extensions/Blocks/Recommendation/utils/strategyCommit.js +24 -0
  35. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +21 -12
  36. package/dist/guido.css +1 -1
  37. package/dist/src/@types/config/schemas.d.ts +0 -4
  38. package/dist/src/components/Guido.vue.d.ts +1 -17
  39. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -17
  40. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -17
  41. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -17
  42. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +3 -4
  43. package/dist/src/composables/useConfig.d.ts +0 -2
  44. package/dist/src/composables/useHtmlValidator.d.ts +0 -21
  45. package/dist/src/composables/useSave.d.ts +1 -6
  46. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +0 -3
  47. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +0 -10
  48. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  49. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +1 -0
  50. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  51. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +2 -0
  52. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +9 -0
  53. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +7 -2
  54. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +73 -3
  55. package/dist/src/extensions/Blocks/Recommendation/utils/strategyCommit.d.ts +14 -0
  56. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +4 -0
  57. package/dist/src/stores/config.d.ts +0 -30
  58. package/dist/src/stores/onboarding.d.ts +377 -0
  59. package/dist/src/utils/templatePreparation.d.ts +0 -30
  60. package/dist/static/styles/components/recommendation-strategy.css.js +34 -4
  61. package/dist/static/styles/components/skeleton.css.js +23 -0
  62. package/dist/stores/config.js +22 -39
  63. package/dist/stores/onboarding.js +41 -36
  64. package/dist/utils/genericUtil.js +61 -64
  65. package/dist/utils/templatePreparation.js +49 -52
  66. package/package.json +1 -1
  67. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +0 -20
  68. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +0 -48
  69. package/dist/composables/useSaveValidation.js +0 -279
  70. package/dist/composables/useSaveValidationRollout.js +0 -9
  71. package/dist/enums/save-validation.js +0 -4
  72. package/dist/src/@types/save-validation.d.ts +0 -19
  73. package/dist/src/composables/useSaveValidation.d.ts +0 -6
  74. package/dist/src/composables/useSaveValidationRollout.d.ts +0 -3
  75. package/dist/src/enums/save-validation.d.ts +0 -27
  76. package/dist/src/stores/save-validation.d.ts +0 -22
  77. package/dist/src/utils/liquidDynamicContent.d.ts +0 -20
  78. package/dist/stores/save-validation.js +0 -30
  79. package/dist/utils/liquidDynamicContent.js +0 -15
  80. /package/dist/src/components/organisms/{validation/ValidationErrorDrawer.vue.d.ts → onboarding/RecommendationStrategyOnboarding.vue.d.ts} +0 -0
@@ -1,22 +1,23 @@
1
- import { getRecommendationFeedSourceMaps as I, getOperatorOptions as G, PriceAttributes as D } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as F } from "../../../../services/recommendationApi.js";
1
+ import { getRecommendationFeedSourceMaps as C, strategyEditPath as D, URLS as _, getOperatorOptions as N, PriceAttributes as U } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as A } from "../../../../services/recommendationApi.js";
3
3
  import { useConfigStore as P } from "../../../../stores/config.js";
4
- import { defineStore as _ } from "pinia";
4
+ import { defineStore as L } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as U, DEFAULT_CARDS_IN_ROW as N } from "../constants/layout.js";
7
- import { DEFAULT_NODE_CONFIG as L, EXCLUDED_ALGORITHM_IDS as x } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as k } from "../templates/utils.js";
9
- import { generateCompleteFilterQuery as R } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as B } from "../utils/partnerCustomizations.js";
11
- import { sanitizeProductIds as A } from "../utils/productIds.js";
12
- import { isFilterValid as M } from "../validation/filterSchema.js";
13
- import { isConfigValid as C } from "../validation/requiredFields.js";
14
- const u = F();
15
- let f = null, g = null, h = null, S = null;
16
- const p = /* @__PURE__ */ new Map();
17
- function v() {
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as E, DEFAULT_CARDS_IN_ROW as x } from "../constants/layout.js";
7
+ import { DEFAULT_NODE_CONFIG as B, EXCLUDED_ALGORITHM_IDS as M } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as v } from "../templates/utils.js";
9
+ import { generateCompleteFilterQuery as F } from "../utils/filterUtil.js";
10
+ import { getPartnerRecommendationParams as O } from "../utils/partnerCustomizations.js";
11
+ import { sanitizeProductIds as T } from "../utils/productIds.js";
12
+ import { isFilterValid as z } from "../validation/filterSchema.js";
13
+ import { getInvalidFields as V, isConfigValid as j } from "../validation/requiredFields.js";
14
+ const f = A();
15
+ let S = null, p = null, y = null, I = "", R = null, b = 0;
16
+ const h = /* @__PURE__ */ new Map();
17
+ let l = null;
18
+ function G() {
18
19
  return {
19
- cardsInRow: N,
20
+ cardsInRow: x,
20
21
  currencySettings: {
21
22
  name: "USD",
22
23
  value: "USD",
@@ -30,13 +31,17 @@ function v() {
30
31
  productIds: [],
31
32
  id: 1,
32
33
  language: "en_US",
33
- mobileCardsInRow: U,
34
+ mobileCardsInRow: E,
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,
@@ -48,7 +53,7 @@ function v() {
48
53
  }
49
54
  function w() {
50
55
  return {
51
- recommendationConfigs: v(),
56
+ recommendationConfigs: G(),
52
57
  recommendationProducts: [],
53
58
  filterStatus: !1,
54
59
  filterSelectionDrawerStatus: !1,
@@ -60,7 +65,7 @@ function w() {
60
65
  syncedConfigFingerprint: null
61
66
  };
62
67
  }
63
- const O = () => ({
68
+ const $ = () => ({
64
69
  recommendationCampaignUrls: {},
65
70
  activePredictiveAlgorithms: [],
66
71
  languages: {},
@@ -69,11 +74,14 @@ const O = () => ({
69
74
  strategies: [],
70
75
  strategiesLoading: !1,
71
76
  strategyUrlTemplates: {},
77
+ strategiesFetched: !1,
78
+ strategiesSettled: !1,
72
79
  blockStates: {},
73
80
  currentRecommendationId: null,
81
+ strategyPanelOpen: !1,
74
82
  configVersion: 0
75
- }), Y = _("guidoRecommendationExtension", {
76
- state: () => O(),
83
+ }), it = L("guidoRecommendationExtension", {
84
+ state: () => $(),
77
85
  getters: {
78
86
  // ====================================================================
79
87
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -83,7 +91,7 @@ const O = () => ({
83
91
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
84
92
  */
85
93
  recommendationConfigs(t) {
86
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : v();
94
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : G();
87
95
  },
88
96
  /**
89
97
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -138,8 +146,8 @@ const O = () => ({
138
146
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
139
147
  },
140
148
  getActivePredictiveAlgorithms: (t) => {
141
- const e = I(), r = [];
142
- return t.activePredictiveAlgorithms.filter((n) => !x.includes(n)).forEach((n) => {
149
+ const e = C(), r = [];
150
+ return t.activePredictiveAlgorithms.filter((n) => !M.includes(n)).forEach((n) => {
143
151
  r.push(...e.filter((s) => s.id === n));
144
152
  }), r.map((n) => ({
145
153
  text: n.name,
@@ -157,7 +165,7 @@ const O = () => ({
157
165
  getFilterList() {
158
166
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
159
167
  let e;
160
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : D.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
168
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : U.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
161
169
  text: t.displayName,
162
170
  value: e,
163
171
  type: t.attributeType
@@ -239,7 +247,7 @@ const O = () => ({
239
247
  * `isInitialized`) alone.
240
248
  */
241
249
  seedBlockUrlConfig(t, e) {
242
- const r = (o, c) => o === "." || o === "," || o === " " ? o : c, n = {
250
+ const r = (o, a) => o === "." || o === "," || o === " " ? o : a, n = {
243
251
  name: e.currencyCode,
244
252
  value: e.currencyCode,
245
253
  symbol: e.currencySymbol,
@@ -286,7 +294,7 @@ const O = () => ({
286
294
  * Creates a filter with the first available attribute and operator pre-selected.
287
295
  */
288
296
  createDefaultFilter(t, e) {
289
- const [r] = this.getFilterList, [n] = G(r == null ? void 0 : r.type);
297
+ const [r] = this.getFilterList, [n] = N(r == null ? void 0 : r.type);
290
298
  return {
291
299
  type: "standardFilter",
292
300
  attribute: (r == null ? void 0 : r.value) ?? "",
@@ -346,29 +354,97 @@ const O = () => ({
346
354
  this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !1);
347
355
  },
348
356
  async fetchStrategies() {
349
- if (S) {
350
- await S;
357
+ for (; R; )
358
+ try {
359
+ await R;
360
+ } catch {
361
+ }
362
+ if (this.strategiesFetched)
351
363
  return;
352
- }
353
- S = (async () => {
364
+ const t = b, e = (async () => {
354
365
  this.strategiesLoading = !0;
355
366
  try {
356
- this.strategies = await u.fetchRecommendationStrategies();
367
+ const r = await f.fetchRecommendationStrategies();
368
+ if (t !== b)
369
+ return;
370
+ this.strategies = r, this.strategiesFetched = !0;
357
371
  } finally {
358
- this.strategiesLoading = !1;
372
+ this.strategiesLoading = !1, t === b && (this.strategiesSettled = !0);
359
373
  }
360
374
  })();
375
+ R = e;
361
376
  try {
362
- await S;
377
+ await e;
363
378
  } finally {
364
- S = null;
379
+ R = null;
365
380
  }
366
381
  },
382
+ invalidateStrategies() {
383
+ this.strategiesFetched = !1, this.strategiesSettled = !1, b += 1;
384
+ },
385
+ /**
386
+ * Opens a Smart Recommender strategy page in a new tab and refreshes the list every time
387
+ * the user comes back. `visibilitychange` only: a `focus` listener would also fire when
388
+ * focus merely returns from the Stripo canvas iframe to the panel, refetching and
389
+ * regenerating the canvas while the user never left the editor (SD-149916).
390
+ *
391
+ * Deliberately NOT one-shot: the strategy tab stays open, so a user can create a second
392
+ * strategy in it and come back again — a listener that disarmed on the first return would
393
+ * leave that one missing from the list. It disarms on the first return AFTER that tab is
394
+ * closed instead, so an ordinary alt-tab later in the session costs nothing. Armed only
395
+ * once the user actually opens a strategy page, so anyone who never does pays nothing.
396
+ */
397
+ _armStrategyPageReturn(t) {
398
+ const e = window.open(t, "_blank");
399
+ if (!e)
400
+ return;
401
+ e.opener = null, l == null || l();
402
+ let r = !1;
403
+ const n = () => {
404
+ document.hidden || r || (e != null && e.closed && (l == null || l()), r = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
405
+ }).finally(() => {
406
+ r = !1;
407
+ }));
408
+ };
409
+ document.addEventListener("visibilitychange", n), l = () => {
410
+ document.removeEventListener("visibilitychange", n), l = null;
411
+ };
412
+ },
413
+ /**
414
+ * Reloads the products of the block the user is on, so an edit made in the strategy tab
415
+ * lands on the canvas instead of waiting for the assign drawer to be reopened.
416
+ *
417
+ * Only the selected block: the canvas regeneration is driven off the active block's
418
+ * product array, so refreshing an unselected block would fetch without redrawing it.
419
+ */
420
+ async _refreshAssignedStrategyProducts() {
421
+ var e;
422
+ const t = this.currentRecommendationId;
423
+ t === null || !((e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId) || await this.fetchRecommendationProducts();
424
+ },
425
+ /** Lets the editor tear the return listeners down when it unmounts. */
426
+ disposeStrategyPageReturn() {
427
+ l == null || l();
428
+ },
429
+ /**
430
+ * Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
431
+ * new strategy is offered; the user picks it themselves.
432
+ */
433
+ openStrategyCreation() {
434
+ this._armStrategyPageReturn(_.RECOMMENDATION_STRATEGY_CREATE_PATH);
435
+ },
436
+ /**
437
+ * Opens the strategy's edit page. Every return refreshes the list so the block reflects the
438
+ * edited configuration.
439
+ */
440
+ openStrategyEdit(t) {
441
+ this._armStrategyPageReturn(D(t));
442
+ },
367
443
  async resolveStrategyUrlTemplate(t, e) {
368
444
  const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
369
445
  if (s)
370
446
  return s;
371
- const { full_path: i = "" } = await u.generateStrategyEndpoint(t, e) ?? {};
447
+ const { full_path: i = "" } = await f.generateStrategyEndpoint(t, e) ?? {};
372
448
  return n && i && (this.strategyUrlTemplates = {
373
449
  ...this.strategyUrlTemplates,
374
450
  [n]: i
@@ -381,23 +457,27 @@ const O = () => ({
381
457
  });
382
458
  },
383
459
  removeStrategy() {
384
- this.patchCurrentBlockConfig({ strategyId: "", size: L.size });
460
+ this.patchCurrentBlockConfig({
461
+ strategyId: "",
462
+ strategy: "",
463
+ size: B.size
464
+ });
385
465
  },
386
466
  // ====================================================================
387
467
  // Shared Data Fetching (fetched once, used by all blocks)
388
468
  // ====================================================================
389
469
  async fetchRecommendationCreateData() {
390
470
  if (!this.activePredictiveAlgorithms.length) {
391
- if (f) {
392
- await f;
471
+ if (S) {
472
+ await S;
393
473
  return;
394
474
  }
395
- f = (async () => {
475
+ S = (async () => {
396
476
  const {
397
477
  activePredictiveAlgorithms: t,
398
478
  languages: e,
399
479
  currencies: r
400
- } = await u.fetchRecommendationCreateData();
480
+ } = await f.fetchRecommendationCreateData();
401
481
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
402
482
  const n = this.blockStates[this.currentRecommendationId];
403
483
  n.filterStatus = !!n.recommendationConfigs.filters.length;
@@ -405,26 +485,26 @@ const O = () => ({
405
485
  this.currencyList = r;
406
486
  })();
407
487
  try {
408
- await f;
488
+ await S;
409
489
  } finally {
410
- f = null;
490
+ S = null;
411
491
  }
412
492
  }
413
493
  },
414
494
  async fetchRecommendationFilters() {
415
495
  if (!Object.keys(this.filterList).length) {
416
- if (g) {
417
- await g;
496
+ if (p) {
497
+ await p;
418
498
  return;
419
499
  }
420
- g = (async () => {
421
- const t = await u.fetchRecommendationFilters();
500
+ p = (async () => {
501
+ const t = await f.fetchRecommendationFilters();
422
502
  this.filterList = t;
423
503
  })();
424
504
  try {
425
- await g;
505
+ await p;
426
506
  } finally {
427
- g = null;
507
+ p = null;
428
508
  }
429
509
  }
430
510
  },
@@ -453,7 +533,7 @@ const O = () => ({
453
533
  const n = [...e.recommendationConfigs.filters];
454
534
  n[r] = {
455
535
  ...t,
456
- isValid: M(t)
536
+ isValid: z(t)
457
537
  }, e.recommendationConfigs.filters = n;
458
538
  }
459
539
  },
@@ -467,10 +547,10 @@ const O = () => ({
467
547
  let i = 1;
468
548
  n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
469
549
  } 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
550
+ const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, u) => a - u), o = new Map(i.map((a, u) => [a, u + 1]));
551
+ n = n.map((a) => ({
552
+ ...a,
553
+ filterGroup: o.get(a.filterGroup) ?? a.filterGroup
474
554
  }));
475
555
  }
476
556
  e.recommendationConfigs.filters = n;
@@ -491,7 +571,7 @@ const O = () => ({
491
571
  }), e.recommendationConfigs.filters = r;
492
572
  },
493
573
  generateFilterQuery() {
494
- return R(this.recommendationConfigs.filters);
574
+ return F(this.recommendationConfigs.filters);
495
575
  },
496
576
  /**
497
577
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -499,40 +579,52 @@ const O = () => ({
499
579
  * every block's recommendationConfigs across user edits.
500
580
  */
501
581
  hasInvalidBlock() {
502
- return Object.values(this.blockStates).some((t) => !C(t.recommendationConfigs, this));
582
+ return Object.values(this.blockStates).some((t) => !j(t.recommendationConfigs, this));
503
583
  },
504
584
  /**
505
- * Per-instance counterpart to `hasInvalidBlock`: the ids of every block
506
- * that fails required-field validation, one drawer row each (SD-147430).
585
+ * Which required fields are missing across the design's blocks, so the save
586
+ * toaster can name the cause instead of always showing the generic message.
507
587
  */
508
- invalidBlockIds() {
509
- return Object.entries(this.blockStates).filter(([, t]) => !C(t.recommendationConfigs, this)).map(([t]) => t);
588
+ invalidBlockFields() {
589
+ return Object.values(this.blockStates).flatMap((t) => V(t.recommendationConfigs, this));
510
590
  },
511
591
  // ====================================================================
512
592
  // Per-Block Product Fetching
513
593
  // ====================================================================
514
594
  async fetchRecommendationProducts() {
515
- if (!(this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])) {
516
- if (h) {
517
- await h;
518
- return;
519
- }
520
- h = this._doFetchProducts();
521
- try {
522
- await h;
523
- } finally {
524
- h = null;
525
- }
595
+ if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
596
+ return;
597
+ const t = [
598
+ this.currentRecommendationId,
599
+ this.configVersion,
600
+ b
601
+ ].join(":");
602
+ if (y && I === t) {
603
+ await y;
604
+ return;
605
+ }
606
+ I = t, y = this._doFetchProducts();
607
+ try {
608
+ await y;
609
+ } finally {
610
+ I === t && (y = null, I = "");
526
611
  }
527
612
  },
528
613
  async _doFetchProducts() {
529
- var d;
614
+ var g;
530
615
  const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = P();
531
616
  if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
532
617
  await this._doFetchStrategyProducts(t);
533
618
  return;
534
619
  }
535
- const s = r.filters.filter((m) => m.isValid), i = R(s), o = ((d = I().find((m) => m.key === r.strategy)) == null ? void 0 : d.path) || "", c = parseInt(r.size) || 6, a = {
620
+ const s = (h.get(t) ?? 0) + 1;
621
+ h.set(t, s);
622
+ const i = parseInt(r.size) || 6;
623
+ if (!r.strategy) {
624
+ this._setBlockProducts(t, [], i);
625
+ return;
626
+ }
627
+ const o = r.filters.filter((d) => d.isValid), a = F(o), u = ((g = C().find((d) => d.key === r.strategy)) == null ? void 0 : g.path) || "", c = {
536
628
  locale: r.language,
537
629
  currency: r.currencySettings.value,
538
630
  partnerName: n.partnerName,
@@ -540,21 +632,21 @@ const O = () => ({
540
632
  details: !0,
541
633
  campaignId: n.variationId
542
634
  };
543
- 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(
544
- a,
545
- B(n.partnerName, r.strategy)
635
+ r.strategy === "manualMerchandising" ? c.productId = T(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(
636
+ c,
637
+ O(n.partnerName, r.strategy)
546
638
  );
547
- let l;
639
+ let m;
548
640
  try {
549
- l = await u.fetchRecommendationProducts(o, a);
641
+ m = await f.fetchRecommendationProducts(u, c);
550
642
  } catch {
551
- l = [];
643
+ m = [];
552
644
  }
553
- this._setBlockProducts(t, l, c);
645
+ h.get(t) === s && this._setBlockProducts(t, m, i);
554
646
  },
555
647
  async _doFetchStrategyProducts(t) {
556
- const e = (p.get(t) ?? 0) + 1;
557
- p.set(t, e);
648
+ const e = (h.get(t) ?? 0) + 1;
649
+ h.set(t, e);
558
650
  const { recommendationConfigs: r } = this.blockStates[t];
559
651
  let n = [], s = parseInt(r.size) || 6;
560
652
  try {
@@ -564,25 +656,25 @@ const O = () => ({
564
656
  );
565
657
  if (i) {
566
658
  s = parseInt(String(i.size ?? "")) || s;
567
- 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((b) => b && !b.startsWith("user_id=")), y = m.length ? `${d}?${m.join("&")}` : d;
568
- y && (n = await u.fetchRecommendationProductsByUrl(y));
659
+ const o = r.currencySettings.value, u = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), c = u.indexOf("?"), m = c === -1 ? u : u.slice(0, c), g = (c === -1 ? "" : u.slice(c + 1)).split("&").filter((k) => k && !k.startsWith("user_id=")), d = g.length ? `${m}?${g.join("&")}` : m;
660
+ d && (n = await f.fetchRecommendationProductsByUrl(d));
569
661
  }
570
662
  } catch {
571
663
  n = [];
572
664
  }
573
- p.get(t) === e && this._setBlockProducts(t, n, s);
665
+ h.get(t) === e && this._setBlockProducts(t, n, s);
574
666
  },
575
667
  _setBlockProducts(t, e, r) {
576
668
  if (!this.blockStates[t])
577
669
  return;
578
- const n = e.length > 0 ? e : k(r);
670
+ const n = e.length > 0 ? e : v(r);
579
671
  this.blockStates[t].recommendationProducts = [
580
672
  ...n.slice(0, r),
581
- ...k(Math.max(0, r - n.length))
673
+ ...v(Math.max(0, r - n.length))
582
674
  ];
583
675
  }
584
676
  }
585
677
  });
586
678
  export {
587
- Y as useRecommendationExtensionStore
679
+ it as useRecommendationExtensionStore
588
680
  };
@@ -1,22 +1,32 @@
1
- import { ModificationDescription as f } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
- import { RECOMMENDATION_ID_ATTR as m } from "../constants/selectors.js";
3
- import { isPartnerManagedBlock as a, updateProductContentInPlace as d, regenerateProductRowsWithStyles as l } from "../controls/main/utils.js";
4
- import { RecommendationConfigService as s } from "../services/configService.js";
5
- let e = null;
6
- function N(r) {
7
- e = r;
1
+ import { ModificationDescription as g } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
+ import { RECOMMENDATION_ID_ATTR as s } from "../constants/selectors.js";
3
+ import { isPartnerManagedBlock as p, updateProductContentInPlace as C, regenerateProductRowsWithStyles as N } from "../controls/main/utils.js";
4
+ import { RecommendationConfigService as m } from "../services/configService.js";
5
+ let o = null;
6
+ function D(n) {
7
+ o = n;
8
8
  }
9
- function D(r, c, i, u) {
10
- const n = e == null ? void 0 : e.getDocumentRootHtmlNode();
11
- if (!e || !n || !("querySelector" in n))
9
+ function P(n, r, c, l) {
10
+ const i = o == null ? void 0 : o.getDocumentRootHtmlNode();
11
+ if (!o || !i || !("querySelector" in i))
12
12
  return !1;
13
- const t = n.querySelector(`[${m}="${r}"]`);
14
- if (!t)
13
+ const e = i.querySelector(`[${s}="${n}"]`);
14
+ if (!e)
15
15
  return !1;
16
- const o = e.getDocumentModifier();
17
- return s.stageConfig(o, t, c), a(t) ? (o.apply(new f(u)), !0) : (d({ currentNode: t, documentModifier: o, products: i }) || l({ currentNode: t, documentModifier: o, products: i }), !0);
16
+ const t = o.getDocumentModifier(), a = m.getConfig(e), d = Object.keys(r).some((u) => JSON.stringify(a[u]) !== JSON.stringify(r[u]));
17
+ if (m.stageConfig(t, e, r), p(e))
18
+ return t.apply(new g(l)), !0;
19
+ let f = !1;
20
+ return C({
21
+ currentNode: e,
22
+ documentModifier: t,
23
+ products: c,
24
+ onApplied: () => {
25
+ f = !0;
26
+ }
27
+ }) || (f = N({ currentNode: e, documentModifier: t, products: c })), !f && d && t.apply(new g(l)), !0;
18
28
  }
19
29
  export {
20
- D as commitBlockChangeOutsidePanel,
21
- N as registerNodeConfigWriter
30
+ P as commitBlockChangeOutsidePanel,
31
+ D as registerNodeConfigWriter
22
32
  };
@@ -0,0 +1,24 @@
1
+ import { useRecommendationExtensionStore as s } from "../store/recommendation.js";
2
+ import { commitBlockChangeOutsidePanel as d } from "./nodeConfigWriter.js";
3
+ async function u(e, n) {
4
+ const t = s(), o = t.blockStates[e];
5
+ if (!o)
6
+ return !1;
7
+ const { strategyId: a, strategy: c, size: i } = o.recommendationConfigs;
8
+ if (t.currentRecommendationId === e)
9
+ try {
10
+ await t.fetchRecommendationProducts();
11
+ } catch (m) {
12
+ console.error("[recommendation] product refetch after a strategy change failed", m);
13
+ }
14
+ const r = d(
15
+ e,
16
+ { strategyId: a, strategy: c, size: i },
17
+ o.recommendationProducts,
18
+ n
19
+ );
20
+ return r || console.error("[recommendation] strategy change did not reach a node", { blockId: e }), r;
21
+ }
22
+ export {
23
+ u as commitStrategyChange
24
+ };
@@ -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-b8f24f34] .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-f938f128]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.validation-error-list[data-v-30d2e1f9]{gap:16px}.validation-error-row[data-v-30d2e1f9]{gap:8px;padding:24px;border:1px solid #E1E1E1;border-radius:8px}.validation-error-row__icon[data-v-30d2e1f9]{flex-shrink:0;font-size:24px;line-height:0}.validation-error-row__text[data-v-30d2e1f9]{font-size:13px;line-height:16px;color:#292929;word-break:break-word}.guido-editor__wrapper[data-v-584fdf72]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-584fdf72]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-584fdf72]{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-521cf6b3]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-521cf6b3] .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}
@@ -395,8 +395,6 @@ export declare const FeaturesSchema: v.ObjectSchema<{
395
395
  readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
396
396
  /** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
397
397
  readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
398
- /** Allow persisting a template that still has validation errors (drawer "Save and Continue" soft-save). Host sets false for transactional + unsubscribe. */
399
- readonly allowSaveWithErrors: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
400
398
  readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
401
399
  }, undefined>;
402
400
  /**
@@ -797,8 +795,6 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
797
795
  readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
798
796
  /** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
799
797
  readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
800
- /** Allow persisting a template that still has validation errors (drawer "Save and Continue" soft-save). Host sets false for transactional + unsubscribe. */
801
- readonly allowSaveWithErrors: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
802
798
  readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
803
799
  }, undefined>, {}>;
804
800
  /** Block configuration */