@useinsider/guido 3.2.1-beta.d58ee72 → 3.3.0-beta.1778fd7

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 (158) hide show
  1. package/README.md +117 -1
  2. package/dist/@types/config/schemas.js +153 -95
  3. package/dist/components/Guido.vue.js +4 -4
  4. package/dist/components/Guido.vue2.js +106 -90
  5. package/dist/components/organisms/AutoSaveController.vue.js +17 -0
  6. package/dist/components/organisms/AutoSaveController.vue2.js +13 -0
  7. package/dist/components/organisms/extensions/recommendation/FilterItem.vue.js +9 -11
  8. package/dist/components/organisms/extensions/recommendation/FilterItem.vue2.js +70 -35
  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/MiddleSlot.vue.js +7 -7
  12. package/dist/components/organisms/header/RightSlot.vue.js +8 -8
  13. package/dist/components/organisms/header/RightSlot.vue2.js +9 -8
  14. package/dist/components/organisms/onboarding/AMPOnboarding.vue2.js +51 -31
  15. package/dist/components/organisms/onboarding/GenericOnboarding.vue.js +1 -1
  16. package/dist/components/organisms/onboarding/GenericOnboarding.vue2.js +23 -22
  17. package/dist/components/organisms/onboarding/ItemsOnboarding.vue.js +1 -1
  18. package/dist/components/organisms/onboarding/ItemsOnboarding.vue2.js +37 -39
  19. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.js +3 -3
  20. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue2.js +30 -41
  21. package/dist/components/organisms/onboarding/VersionHistoryOnboarding.vue2.js +15 -14
  22. package/dist/composables/useAutoSave.js +71 -0
  23. package/dist/composables/useCortexBlueprintBridge.js +66 -0
  24. package/dist/composables/useEmailTemplateApplier.js +41 -0
  25. package/dist/composables/useFullStoryBridge.js +14 -0
  26. package/dist/composables/useGuidoStateBridge.js +48 -0
  27. package/dist/composables/useHtmlValidator.js +41 -36
  28. package/dist/composables/useRecommendation.js +46 -26
  29. package/dist/composables/useRibbonOffset.js +21 -0
  30. package/dist/composables/useSave.js +19 -16
  31. package/dist/composables/useStripo.js +66 -62
  32. package/dist/composables/validators/useCouponBlockValidator.js +24 -0
  33. package/dist/config/compiler/recommendationCompilerRules.js +25 -25
  34. package/dist/config/compiler/unsubscribeCompilerRules.js +40 -37
  35. package/dist/config/compiler/utils/recommendationCompilerUtils.js +107 -71
  36. package/dist/config/migrator/index.js +20 -9
  37. package/dist/config/migrator/radioButtonMigrator.js +92 -47
  38. package/dist/config/migrator/recommendation/compositionMapper.js +98 -0
  39. package/dist/config/migrator/recommendation/extractors.js +27 -0
  40. package/dist/config/migrator/recommendation/htmlBuilder.js +496 -0
  41. package/dist/config/migrator/recommendation/parseLegacyConfig.js +33 -0
  42. package/dist/config/migrator/recommendation/settingsMapper.js +70 -0
  43. package/dist/config/migrator/recommendation/themeMapper.js +93 -0
  44. package/dist/config/migrator/recommendationMigrator.js +74 -290
  45. package/dist/enums/extensions/recommendationBlock.js +103 -47
  46. package/dist/enums/onboarding.js +7 -2
  47. package/dist/enums/unsubscribe.js +39 -31
  48. package/dist/extensions/Blocks/Checkbox/control.js +23 -23
  49. package/dist/extensions/Blocks/RadioButton/control.js +15 -15
  50. package/dist/extensions/Blocks/RadioButton/template.js +7 -7
  51. package/dist/extensions/Blocks/Recommendation/block.js +60 -50
  52. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  53. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +5 -5
  54. package/dist/extensions/Blocks/Recommendation/controls/customAttribute/index.js +21 -18
  55. package/dist/extensions/Blocks/Recommendation/controls/customAttribute/textTrim.js +99 -0
  56. package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +27 -26
  57. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +3 -1
  58. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +228 -181
  59. package/dist/extensions/Blocks/Recommendation/controls/name/textTrim.js +27 -57
  60. package/dist/extensions/Blocks/Recommendation/controls/shared/textTrimCssRules.js +14 -0
  61. package/dist/extensions/Blocks/Recommendation/services/configService.js +76 -33
  62. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +18 -17
  63. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +144 -91
  64. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +19 -10
  65. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +8 -8
  66. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +25 -15
  67. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +11 -11
  68. package/dist/extensions/Blocks/Recommendation/templates/utils.js +1 -1
  69. package/dist/extensions/Blocks/Recommendation/utils/filterUtil.js +31 -15
  70. package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMap.js +21 -0
  71. package/dist/extensions/Blocks/Recommendation/utils/preserveTextStyles.js +13 -22
  72. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +33 -0
  73. package/dist/guido.css +1 -1
  74. package/dist/node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js +393 -264
  75. package/dist/node_modules/valibot/dist/index.js +450 -235
  76. package/dist/package.json.js +1 -1
  77. package/dist/services/templateLibraryApi.js +5 -4
  78. package/dist/services/unsubscribeApi.js +6 -6
  79. package/dist/src/@types/config/defaults.d.ts +5 -1
  80. package/dist/src/@types/config/index.d.ts +3 -3
  81. package/dist/src/@types/config/schemas.d.ts +217 -0
  82. package/dist/src/@types/config/types.d.ts +9 -1
  83. package/dist/src/components/Guido.vue.d.ts +1 -1
  84. package/dist/src/components/organisms/AutoSaveController.vue.d.ts +2 -0
  85. package/dist/src/components/organisms/header/AutoSaveToggle.vue.d.ts +2 -0
  86. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  87. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -1
  88. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -1
  89. package/dist/src/components/wrappers/WpModal.vue.d.ts +1 -1
  90. package/dist/src/composables/useAutoSave.d.ts +3 -0
  91. package/dist/src/composables/useConfig.d.ts +58 -0
  92. package/dist/src/composables/useCortexBlueprintBridge.d.ts +25 -0
  93. package/dist/src/composables/useEmailTemplateApplier.d.ts +21 -0
  94. package/dist/src/composables/useFullStoryBridge.d.ts +11 -0
  95. package/dist/src/composables/useGuidoStateBridge.d.ts +22 -0
  96. package/dist/src/composables/useRecommendation.d.ts +10 -1
  97. package/dist/src/composables/useRecommendation.test.d.ts +1 -0
  98. package/dist/src/composables/useRibbonOffset.d.ts +4 -0
  99. package/dist/src/composables/useSave.d.ts +1 -1
  100. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
  101. package/dist/src/config/migrator/index.d.ts +2 -1
  102. package/dist/src/config/migrator/recommendation/compositionMapper.d.ts +2 -0
  103. package/dist/src/config/migrator/recommendation/compositionMapper.test.d.ts +1 -0
  104. package/dist/src/config/migrator/recommendation/extractors.d.ts +7 -0
  105. package/dist/src/config/migrator/recommendation/extractors.test.d.ts +1 -0
  106. package/dist/src/config/migrator/recommendation/htmlBuilder.d.ts +11 -0
  107. package/dist/src/config/migrator/recommendation/parseLegacyConfig.d.ts +15 -0
  108. package/dist/src/config/migrator/recommendation/parseLegacyConfig.test.d.ts +1 -0
  109. package/dist/src/config/migrator/recommendation/settingsMapper.d.ts +7 -0
  110. package/dist/src/config/migrator/recommendation/settingsMapper.test.d.ts +1 -0
  111. package/dist/src/config/migrator/recommendation/themeMapper.d.ts +5 -0
  112. package/dist/src/config/migrator/recommendation/themeMapper.test.d.ts +1 -0
  113. package/dist/src/config/migrator/recommendation/types.d.ts +205 -0
  114. package/dist/src/config/migrator/recommendationMigrator.d.ts +13 -1
  115. package/dist/src/config/migrator/recommendationMigrator.test.d.ts +1 -0
  116. package/dist/src/enums/extensions/recommendationBlock.d.ts +6 -1
  117. package/dist/src/enums/onboarding.d.ts +6 -0
  118. package/dist/src/enums/unsubscribe.d.ts +13 -3
  119. package/dist/src/extensions/Blocks/RadioButton/template.d.ts +1 -1
  120. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +1 -0
  121. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/index.d.ts +3 -0
  122. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/textTrim.d.ts +35 -0
  123. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.test.d.ts +1 -0
  124. package/dist/src/extensions/Blocks/Recommendation/controls/name/textTrim.d.ts +3 -20
  125. package/dist/src/extensions/Blocks/Recommendation/controls/shared/textTrimCssRules.d.ts +29 -0
  126. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +21 -3
  127. package/dist/src/extensions/Blocks/Recommendation/services/configService.test.d.ts +1 -0
  128. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +41 -1
  129. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +1 -1
  130. package/dist/src/extensions/Blocks/Recommendation/utils/filterUtil.d.ts +2 -0
  131. package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.d.ts +21 -0
  132. package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.test.d.ts +1 -0
  133. package/dist/src/extensions/Blocks/Recommendation/utils/preserveTextStyles.d.ts +0 -3
  134. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +21 -0
  135. package/dist/src/library.d.ts +1 -1
  136. package/dist/src/stores/autosave.d.ts +12 -0
  137. package/dist/src/stores/config.d.ts +522 -0
  138. package/dist/src/stores/editor.d.ts +23 -0
  139. package/dist/src/stores/guido-email-editor.d.ts +41 -0
  140. package/dist/src/stores/onboarding.d.ts +4 -0
  141. package/dist/src/utils/htmlEscape.d.ts +5 -0
  142. package/dist/src/utils/htmlEscape.test.d.ts +1 -0
  143. package/dist/src/utils/timeUtil.d.ts +8 -0
  144. package/dist/static/styles/components/button.css.js +16 -9
  145. package/dist/static/styles/components/loader.css.js +4 -0
  146. package/dist/static/styles/components/narrow-panel.css.js +52 -0
  147. package/dist/stores/autosave.js +17 -0
  148. package/dist/stores/editor.js +3 -1
  149. package/dist/stores/guido-email-editor.js +20 -0
  150. package/dist/stores/onboarding.js +4 -0
  151. package/dist/utils/htmlEscape.js +13 -0
  152. package/dist/utils/pairProductVariables.js +89 -88
  153. package/dist/utils/templatePreparation.js +74 -27
  154. package/dist/utils/timeUtil.js +19 -0
  155. package/package.json +7 -3
  156. package/dist/enums/displayConditions.js +0 -80
  157. package/dist/extensions/Blocks/Recommendation/templates/grid/migration.js +0 -251
  158. package/dist/src/enums/displayConditions.d.ts +0 -2
@@ -1,15 +1,16 @@
1
- import { RecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as k } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as y } from "../../../../services/recommendationApi.js";
3
- import { useConfigStore as C } from "../../../../stores/config.js";
4
- import { defineStore as G } from "pinia";
1
+ import { getRecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as y } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as C } from "../../../../services/recommendationApi.js";
3
+ import { useConfigStore as G } from "../../../../stores/config.js";
4
+ import { defineStore as P } from "pinia";
5
5
  import { DEFAULT_CARDS_IN_ROW as F } from "../constants/layout.js";
6
- import { EXCLUDED_ALGORITHM_IDS as w } from "../constants/defaultConfig.js";
6
+ import { EXCLUDED_ALGORITHM_IDS as D } from "../constants/defaultConfig.js";
7
7
  import { getDefaultProducts as S } from "../templates/utils.js";
8
8
  import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
9
- import { isFilterValid as D } from "../validation/filterSchema.js";
10
- const h = y();
11
- let u = null, m = null, d = null;
12
- function I() {
9
+ import { isFilterValid as w } from "../validation/filterSchema.js";
10
+ import { isConfigValid as v } from "../validation/requiredFields.js";
11
+ const h = C();
12
+ let m = null, u = null, d = null;
13
+ function k() {
13
14
  return {
14
15
  cardsInRow: F,
15
16
  currencySettings: {
@@ -37,9 +38,9 @@ function I() {
37
38
  customAttributes: []
38
39
  };
39
40
  }
40
- function P() {
41
+ function I() {
41
42
  return {
42
- recommendationConfigs: I(),
43
+ recommendationConfigs: k(),
43
44
  recommendationProducts: [],
44
45
  filterStatus: !1,
45
46
  filterSelectionDrawerStatus: !1,
@@ -48,7 +49,7 @@ function P() {
48
49
  filterSnapshot: null
49
50
  };
50
51
  }
51
- const v = () => ({
52
+ const N = () => ({
52
53
  recommendationCampaignUrls: {},
53
54
  activePredictiveAlgorithms: [],
54
55
  languages: {},
@@ -57,8 +58,8 @@ const v = () => ({
57
58
  blockStates: {},
58
59
  currentRecommendationId: null,
59
60
  configVersion: 0
60
- }), E = G("guidoRecommendationExtension", {
61
- state: () => v(),
61
+ }), M = P("guidoRecommendationExtension", {
62
+ state: () => N(),
62
63
  getters: {
63
64
  // ====================================================================
64
65
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -68,7 +69,7 @@ const v = () => ({
68
69
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
69
70
  */
70
71
  recommendationConfigs(t) {
71
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : I();
72
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : k();
72
73
  },
73
74
  /**
74
75
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -102,46 +103,45 @@ const v = () => ({
102
103
  },
103
104
  hasValidFilters() {
104
105
  const { filters: t } = this.recommendationConfigs;
105
- return t.length ? t.every((r) => r.isValid) : !1;
106
+ return t.length ? t.every((e) => e.isValid) : !1;
106
107
  },
107
108
  getFilterGroupCount() {
108
109
  const { filters: t } = this.recommendationConfigs;
109
- return t.length ? new Set(t.map((r) => r.filterGroup)).size : 0;
110
+ return t.length ? new Set(t.map((e) => e.filterGroup)).size : 0;
110
111
  },
111
112
  getUniqueFilterGroups() {
112
113
  const { filters: t } = this.recommendationConfigs;
113
- return [...new Set(t.map((r) => r.filterGroup))].sort((r, e) => r - e);
114
+ return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
114
115
  },
115
116
  getActivePredictiveAlgorithms: (t) => {
116
- const r = [];
117
- return t.activePredictiveAlgorithms.filter((e) => !w.includes(e)).forEach((e) => {
118
- r.push(...g.filter((n) => n.id === e));
119
- }), r.map((e) => ({
120
- text: e.name,
121
- value: e.key
117
+ const e = g(), r = [];
118
+ return t.activePredictiveAlgorithms.filter((n) => !D.includes(n)).forEach((n) => {
119
+ r.push(...e.filter((c) => c.id === n));
120
+ }), r.map((n) => ({
121
+ text: n.name,
122
+ value: n.key
122
123
  }));
123
124
  },
124
- getLanguages: (t) => Object.entries(t.languages).map(([r, e]) => ({
125
- text: e,
126
- value: r
125
+ getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
126
+ text: r,
127
+ value: e
127
128
  })),
128
- getCurrencySymbolList: (t) => t.currencyList.map((r) => ({
129
- text: r.text,
130
- value: r.text
129
+ getCurrencySymbolList: (t) => t.currencyList.map((e) => ({
130
+ text: e.text,
131
+ value: e.text
131
132
  })),
132
133
  getFilterList() {
133
134
  return Object.values(this.filterList).map((t) => {
134
- const r = t.type === "defaultAttribute", e = k.includes(t.attributeName);
135
- let n = r ? t.attributeName : `product_attributes.${t.attributeName}`;
136
- return n = e ? `${n}.${this.recommendationConfigs.currencySettings.value}` : n, {
135
+ let e;
136
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : y.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
137
137
  text: t.displayName,
138
- value: n,
138
+ value: e,
139
139
  type: t.attributeType
140
140
  };
141
141
  });
142
142
  },
143
143
  getSelectedFilterGroup() {
144
- return (t) => [...this.recommendationConfigs.filters].filter((r) => r.filterGroup === t);
144
+ return (t) => [...this.recommendationConfigs.filters].filter((e) => e.filterGroup === t);
145
145
  }
146
146
  },
147
147
  actions: {
@@ -155,7 +155,7 @@ const v = () => ({
155
155
  setCurrentBlock(t) {
156
156
  this.blockStates[t] || (this.blockStates = {
157
157
  ...this.blockStates,
158
- [t]: P()
158
+ [t]: I()
159
159
  }), this.currentRecommendationId = t;
160
160
  },
161
161
  /**
@@ -163,10 +163,15 @@ const v = () => ({
163
163
  * Resets currentRecommendationId if it was the deleted block.
164
164
  */
165
165
  removeBlockState(t) {
166
+ const e = t.toString();
167
+ if (this.recommendationCampaignUrls[e]) {
168
+ const n = { ...this.recommendationCampaignUrls };
169
+ delete n[e], this.recommendationCampaignUrls = n;
170
+ }
166
171
  const r = { ...this.blockStates };
167
172
  if (delete r[t], this.blockStates = r, this.currentRecommendationId === t) {
168
- const e = Object.keys(this.blockStates).map(Number);
169
- this.currentRecommendationId = e.length > 0 ? e[0] : null;
173
+ const n = Object.keys(this.blockStates).map(Number);
174
+ this.currentRecommendationId = n.length > 0 ? n[0] : null;
170
175
  }
171
176
  },
172
177
  /**
@@ -176,37 +181,75 @@ const v = () => ({
176
181
  markBlockInitialized(t) {
177
182
  this.blockStates[t] && (this.blockStates[t].isInitialized = !0);
178
183
  },
184
+ /**
185
+ * Seeds the URL-relevant fields of a block from a persisted node config.
186
+ *
187
+ * Used at save-time to ensure the campaign URL is built from the
188
+ * persisted truth (the `esd-ext-config` blob in the raw HTML) even when
189
+ * the user never selected the block in this editor session — without
190
+ * this seed, `_syncNodeConfigToStore` would never have run for that
191
+ * block and the store would hold default values (USD/en_US/mostPopular)
192
+ * instead of the real config.
193
+ *
194
+ * Creates the block entry if missing; otherwise patches only the URL-
195
+ * relevant subset and leaves runtime fields (e.g., `recommendedProducts`,
196
+ * `isInitialized`) alone.
197
+ */
198
+ seedBlockUrlConfig(t, e) {
199
+ const r = (o, s) => o === "." || o === "," || o === " " ? o : s, n = {
200
+ name: e.currencyCode,
201
+ value: e.currencyCode,
202
+ symbol: e.currencyCode,
203
+ alignment: e.currencyAlignment === "before" ? "0" : "1",
204
+ decimalCount: String(e.currencyDecimalCount),
205
+ decimalSeparator: r(e.currencyDecimalSeparator, ","),
206
+ thousandSeparator: r(e.currencyThousandSeparator, ".")
207
+ }, c = !this.blockStates[t], i = c ? I() : this.blockStates[t];
208
+ i.recommendationConfigs = {
209
+ ...i.recommendationConfigs,
210
+ strategy: e.strategy,
211
+ language: e.language,
212
+ size: e.size,
213
+ productIds: e.productIds,
214
+ filters: e.filters,
215
+ shuffleProducts: e.shuffleProducts,
216
+ currencySettings: n
217
+ }, c && (this.blockStates = {
218
+ ...this.blockStates,
219
+ [t]: i
220
+ });
221
+ },
179
222
  /**
180
223
  * Patches the current block's recommendationConfigs.
181
224
  * Replaces `store.$patch({ recommendationConfigs: { ... } })` pattern.
182
225
  */
183
- patchCurrentBlockConfig(t, r = {}) {
226
+ patchCurrentBlockConfig(t, e = {}) {
184
227
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
185
228
  return;
186
- const e = this.blockStates[this.currentRecommendationId];
187
- e.recommendationConfigs = {
188
- ...e.recommendationConfigs,
229
+ const r = this.blockStates[this.currentRecommendationId];
230
+ r.recommendationConfigs = {
231
+ ...r.recommendationConfigs,
189
232
  ...t,
190
233
  currencySettings: {
191
- ...e.recommendationConfigs.currencySettings,
234
+ ...r.recommendationConfigs.currencySettings,
192
235
  ...t.currencySettings || {}
193
236
  }
194
237
  };
195
- const { triggerRefetch: n = !0 } = r;
238
+ const { triggerRefetch: n = !0 } = e;
196
239
  n && this.configVersion++;
197
240
  },
198
241
  /**
199
242
  * Creates a filter with the first available attribute and operator pre-selected.
200
243
  */
201
- createDefaultFilter(t, r) {
202
- const [e] = this.getFilterList, [n] = R(e == null ? void 0 : e.type);
244
+ createDefaultFilter(t, e) {
245
+ const [r] = this.getFilterList, [n] = R(r == null ? void 0 : r.type);
203
246
  return {
204
247
  type: "standardFilter",
205
- attribute: (e == null ? void 0 : e.value) ?? "",
248
+ attribute: (r == null ? void 0 : r.value) ?? "",
206
249
  operator: (n == null ? void 0 : n.value) ?? "",
207
250
  innerGroupOperator: "*",
208
251
  outerGroupOperator: "*",
209
- filterNumber: r,
252
+ filterNumber: e,
210
253
  filterGroup: t,
211
254
  isValid: !1,
212
255
  value: ""
@@ -251,43 +294,43 @@ const v = () => ({
251
294
  // ====================================================================
252
295
  async fetchRecommendationCreateData() {
253
296
  if (!this.activePredictiveAlgorithms.length) {
254
- if (u) {
255
- await u;
297
+ if (m) {
298
+ await m;
256
299
  return;
257
300
  }
258
- u = (async () => {
301
+ m = (async () => {
259
302
  const {
260
303
  activePredictiveAlgorithms: t,
261
- languages: r,
262
- currencies: e
304
+ languages: e,
305
+ currencies: r
263
306
  } = await h.fetchRecommendationCreateData();
264
- if (this.activePredictiveAlgorithms = t, this.languages = r, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
307
+ if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
265
308
  const n = this.blockStates[this.currentRecommendationId];
266
309
  n.filterStatus = !!n.recommendationConfigs.filters.length;
267
310
  }
268
- this.currencyList = e;
311
+ this.currencyList = r;
269
312
  })();
270
313
  try {
271
- await u;
314
+ await m;
272
315
  } finally {
273
- u = null;
316
+ m = null;
274
317
  }
275
318
  }
276
319
  },
277
320
  async fetchRecommendationFilters() {
278
321
  if (!Object.keys(this.filterList).length) {
279
- if (m) {
280
- await m;
322
+ if (u) {
323
+ await u;
281
324
  return;
282
325
  }
283
- m = (async () => {
326
+ u = (async () => {
284
327
  const t = await h.fetchRecommendationFilters();
285
328
  this.filterList = t;
286
329
  })();
287
330
  try {
288
- await m;
331
+ await u;
289
332
  } finally {
290
- m = null;
333
+ u = null;
291
334
  }
292
335
  }
293
336
  },
@@ -302,8 +345,8 @@ const v = () => ({
302
345
  deleteFilterGroup(t) {
303
346
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
304
347
  return;
305
- const r = this.blockStates[this.currentRecommendationId], e = r.recommendationConfigs.filters.filter((i) => i.filterGroup !== t), n = [...new Set(e.map((i) => i.filterGroup))].sort((i, o) => i - o), c = new Map(n.map((i, o) => [i, o + 1]));
306
- r.recommendationConfigs.filters = e.map((i) => ({
348
+ 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), c = new Map(n.map((i, o) => [i, o + 1]));
349
+ e.recommendationConfigs.filters = r.map((i) => ({
307
350
  ...i,
308
351
  filterGroup: c.get(i.filterGroup) ?? i.filterGroup
309
352
  }));
@@ -311,51 +354,59 @@ const v = () => ({
311
354
  updateFilter(t) {
312
355
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
313
356
  return;
314
- const r = this.blockStates[this.currentRecommendationId], e = r.recommendationConfigs.filters.findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
315
- if (e !== -1) {
316
- const n = [...r.recommendationConfigs.filters];
317
- n[e] = {
357
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
358
+ if (r !== -1) {
359
+ const n = [...e.recommendationConfigs.filters];
360
+ n[r] = {
318
361
  ...t,
319
- isValid: D(t)
320
- }, r.recommendationConfigs.filters = n;
362
+ isValid: w(t)
363
+ }, e.recommendationConfigs.filters = n;
321
364
  }
322
365
  },
323
366
  deleteFilter(t) {
324
367
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
325
368
  return;
326
- const r = this.blockStates[this.currentRecommendationId], e = [...r.recommendationConfigs.filters].findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
327
- if (e !== -1) {
328
- let n = [...r.recommendationConfigs.filters];
329
- if (n.splice(e, 1), n.some((i) => i.filterGroup === t.filterGroup)) {
369
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
370
+ if (r !== -1) {
371
+ let n = [...e.recommendationConfigs.filters];
372
+ if (n.splice(r, 1), n.some((i) => i.filterGroup === t.filterGroup)) {
330
373
  let i = 1;
331
374
  n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
332
375
  } else {
333
- const i = [...new Set(n.map((s) => s.filterGroup))].sort((s, l) => s - l), o = new Map(i.map((s, l) => [s, l + 1]));
376
+ const i = [...new Set(n.map((s) => s.filterGroup))].sort((s, a) => s - a), o = new Map(i.map((s, a) => [s, a + 1]));
334
377
  n = n.map((s) => ({
335
378
  ...s,
336
379
  filterGroup: o.get(s.filterGroup) ?? s.filterGroup
337
380
  }));
338
381
  }
339
- r.recommendationConfigs.filters = n;
382
+ e.recommendationConfigs.filters = n;
340
383
  }
341
384
  },
342
385
  addFilter(t) {
343
386
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
344
387
  return;
345
- const r = this.blockStates[this.currentRecommendationId], e = [...r.recommendationConfigs.filters], c = e.filter(
388
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], c = r.filter(
346
389
  (o) => o.filterGroup === t.filterGroup
347
- ).length + 1, i = e.findLastIndex((o) => o.filterGroup === t.filterGroup);
348
- i !== -1 ? e.splice(i + 1, 0, {
390
+ ).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
391
+ i !== -1 ? r.splice(i + 1, 0, {
349
392
  ...t,
350
393
  filterNumber: c
351
- }) : e.push({
394
+ }) : r.push({
352
395
  ...t,
353
396
  filterNumber: c
354
- }), r.recommendationConfigs.filters = e;
397
+ }), e.recommendationConfigs.filters = r;
355
398
  },
356
399
  generateFilterQuery() {
357
400
  return b(this.recommendationConfigs.filters);
358
401
  },
402
+ /**
403
+ * Validation-only check invoked at save-CTA time. Defined as an action
404
+ * (not a getter) so reading it does not register reactive tracking on
405
+ * every block's recommendationConfigs across user edits.
406
+ */
407
+ hasInvalidBlock() {
408
+ return Object.values(this.blockStates).some((t) => !v(t.recommendationConfigs, this));
409
+ },
359
410
  // ====================================================================
360
411
  // Per-Block Product Fetching
361
412
  // ====================================================================
@@ -375,29 +426,31 @@ const v = () => ({
375
426
  },
376
427
  async _doFetchProducts() {
377
428
  var p;
378
- const t = this.currentRecommendationId, r = this.blockStates[t], { recommendationConfigs: e } = r, n = e.filters.filter((a) => a.isValid), c = b(n), i = ((p = g.find((a) => a.key === e.strategy)) == null ? void 0 : p.path) || "", o = C(), s = {
379
- locale: e.language,
380
- currency: e.currencySettings.value,
429
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = g().find((l) => l.key === r.strategy)) == null ? void 0 : p.path) || "", o = G(), s = parseInt(r.size) || 6, a = {
430
+ locale: r.language,
431
+ currency: r.currencySettings.value,
381
432
  partnerName: o.partnerName,
382
- size: e.size,
433
+ size: r.size,
383
434
  details: !0,
384
435
  campaignId: o.variationId
385
436
  };
386
- e.strategy === "manualMerchandising" ? s.productId = e.productIds.join(",") : e.strategy === "similarViewed" && (s.productId = "{itemId}"), e.strategy === "userBased" && (s.userId = "{user_id}"), c && (s.filter = c), e.shuffleProducts && (s.shuffle = !0);
387
- const l = parseInt(e.size) || 6;
437
+ r.strategy === "manualMerchandising" ? a.productId = r.productIds.slice(0, s).join(",") : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), c && (a.filter = c), r.shuffleProducts && (a.shuffle = !0);
388
438
  let f;
389
439
  try {
390
- f = await h.fetchRecommendationProducts(i, s);
440
+ f = await h.fetchRecommendationProducts(i, a);
391
441
  } catch {
392
442
  f = [];
393
443
  }
394
444
  if (this.blockStates[t]) {
395
- const a = f.length > 0 ? f : S(l);
396
- this.blockStates[t].recommendationProducts = a.length < l ? [...a, ...S(l - a.length)] : a;
445
+ const l = f.length > 0 ? f : S(s);
446
+ l.length < s ? this.blockStates[t].recommendationProducts = [
447
+ ...l,
448
+ ...S(s - l.length)
449
+ ] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
397
450
  }
398
451
  }
399
452
  }
400
453
  });
401
454
  export {
402
- E as useRecommendationExtensionStore
455
+ M as useRecommendationExtensionStore
403
456
  };
@@ -1,5 +1,5 @@
1
1
  import { RecommendationBlockId as s } from "../../constants/blockIds.js";
2
- import { ATTR_PRODUCT_ATTR as g, ATTR_PRODUCT_BUTTON as u, ATTR_PRODUCT_OMNIBUS_DISCOUNT as m, ATTR_PRODUCT_OMNIBUS_PRICE as h, ATTR_PRODUCT_OLD_PRICE as y, ATTR_PRODUCT_PRICE as T, ATTR_PRODUCT_NAME as f, ATTR_PRODUCT_IMAGE as x } from "../../constants/selectors.js";
2
+ import { ATTR_PRODUCT_ATTR as b, ATTR_PRODUCT_BUTTON as u, ATTR_PRODUCT_OMNIBUS_DISCOUNT as m, ATTR_PRODUCT_OMNIBUS_PRICE as h, ATTR_PRODUCT_OLD_PRICE as y, ATTR_PRODUCT_PRICE as T, ATTR_PRODUCT_NAME as f, ATTR_PRODUCT_IMAGE as x } from "../../constants/selectors.js";
3
3
  import { useRecommendationExtensionStore as _ } from "../../store/recommendation.js";
4
4
  import { formatPrice as $ } from "../../utils/priceFormatter.js";
5
5
  import { sanitizeImageUrl as C, CUSTOM_CELL_HTML as R } from "../utils.js";
@@ -15,7 +15,7 @@ function p() {
15
15
  thousandSeparator: e.thousandSeparator
16
16
  };
17
17
  }
18
- function r(t, e = "price") {
18
+ function i(t, e = "price") {
19
19
  const o = p(), n = t[e], d = (n == null ? void 0 : n[o.code]) ?? Object.values(n ?? {})[0] ?? 0;
20
20
  return $({
21
21
  price: d,
@@ -92,7 +92,7 @@ const I = {
92
92
  align="center"
93
93
  esd-extension-block-id="${s.PRICE}">
94
94
  <p contenteditable="false" style="font-size: 16px; color: #333333;">
95
- <strong>${r(t, "price")}</strong>
95
+ <strong>${i(t, "price")}</strong>
96
96
  </p>
97
97
  </td>
98
98
  </tr>
@@ -116,7 +116,7 @@ const I = {
116
116
  align="center"
117
117
  esd-extension-block-id="${s.OLD_PRICE}">
118
118
  <p contenteditable="false" style="font-size: 14px; color: #999999;">
119
- <strong>${r(t, "original_price")}</strong>
119
+ <strong>${i(t, "original_price")}</strong>
120
120
  </p>
121
121
  </td>
122
122
  </tr>
@@ -143,7 +143,7 @@ const I = {
143
143
  esd-extension-block-id="${s.OMNIBUS_PRICE}">
144
144
  <p contenteditable="false" style="font-size: 12px; color: #666666;">
145
145
  <span class="omnibus-text-before">Lowest 30-day price: </span>
146
- <span class="omnibus-price-value">${r(t, "original_price")}</span>
146
+ <span class="omnibus-price-value">${i(t, "original_price")}</span>
147
147
  <span class="omnibus-text-after"></span>
148
148
  </p>
149
149
  </td>
@@ -153,8 +153,8 @@ const I = {
153
153
  </td>
154
154
  `,
155
155
  [m]: (t) => {
156
- var c, i;
157
- const e = p(), o = ((c = t.original_price) == null ? void 0 : c[e.code]) ?? Object.values(t.original_price ?? {})[0] ?? 0, n = ((i = t.price) == null ? void 0 : i[e.code]) ?? Object.values(t.price ?? {})[0] ?? 0, d = o > 0 ? Math.round((o - n) / o * 100) : 0, b = d > 0 ? `-${d}%` : "0%";
156
+ var r, c;
157
+ const e = p(), o = ((r = t.original_price) == null ? void 0 : r[e.code]) ?? Object.values(t.original_price ?? {})[0] ?? 0, n = ((c = t.price) == null ? void 0 : c[e.code]) ?? Object.values(t.price ?? {})[0] ?? 0, d = o > 0 ? Math.round((o - n) / o * 100) : 0, g = d > 0 ? `-${d}%` : "0%";
158
158
  return `
159
159
  <td class="${l}" style="padding: ${a}; height: 100%;" valign="top">
160
160
  <table
@@ -174,7 +174,7 @@ const I = {
174
174
  esd-extension-block-id="${s.OMNIBUS_DISCOUNT}">
175
175
  <p contenteditable="false" style="font-size: 12px; color: #666666;">
176
176
  <span class="omnibus-text-before"></span>
177
- <span class="omnibus-discount-value">${b}</span>
177
+ <span class="omnibus-discount-value">${g}</span>
178
178
  <span class="omnibus-text-after"></span>
179
179
  </p>
180
180
  </td>
@@ -210,7 +210,16 @@ const I = {
210
210
  href="#"
211
211
  class="es-button buy-button"
212
212
  target="_blank"
213
- style="color: rgb(56, 118, 29); background: rgb(217, 234, 211);">
213
+ style="
214
+ color: rgb(56, 118, 29);
215
+ background: rgb(217, 234, 211);
216
+ font-family: arial, 'helvetica neue', helvetica, sans-serif;
217
+ font-size: 16px;
218
+ font-weight: normal;
219
+ line-height: 120%;
220
+ mso-border-alt: 10px solid rgb(217, 234, 211);
221
+ mso-padding-alt: 0;
222
+ ">
214
223
  Buy
215
224
  </a>
216
225
  </span>
@@ -240,7 +249,7 @@ const I = {
240
249
  <tbody>
241
250
  <tr valign="top">
242
251
  <td
243
- ${g}="${t}"
252
+ ${b}="${t}"
244
253
  class="esd-block-text product-custom-attribute es-p0t es-p0b es-p15l es-p15r"
245
254
  align="center"
246
255
  esd-extension-block-id="${s.CUSTOM_ATTRIBUTE}">
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_PRODUCTS_PER_ROW as L } from "../../constants/layout.js";
2
- import { DEFAULT_CARD_COMPOSITION as E, spacer as I, getDefaultProducts as _, createBlockTemplate as S, DEFAULTS as b, buildElementRenderer as A, DEFAULT_CARD_VISIBILITY as f } from "../utils.js";
3
- import { gridElementRenderer as w, ATTRIBUTE_CELL_CLASS as D, DEFAULT_CELL_PADDING as C } from "./elementRenderer.js";
2
+ import { getDefaultProducts as E, createBlockTemplate as I, DEFAULTS as _, DEFAULT_CARD_COMPOSITION as S, spacer as b, buildElementRenderer as A, DEFAULT_CARD_VISIBILITY as f } from "../utils.js";
3
+ import { ATTRIBUTE_CELL_CLASS as w, DEFAULT_CELL_PADDING as D, gridElementRenderer as C } from "./elementRenderer.js";
4
4
  const O = `
5
5
  <tr class="recommendation-product-row">
6
6
  <td>
@@ -27,8 +27,8 @@ const O = `
27
27
  {-{-CELLS-}-}
28
28
  </tr>
29
29
  `;
30
- function P(t, e, o, r = E, n = {}) {
31
- const c = (100 / e).toFixed(2), i = e - t.length, l = `<td class="${D}" style="padding: ${C};" width="${c}%"></td>`, d = i > 0 ? l.repeat(i) : "", a = A(o, r, n);
30
+ function P(t, e, o, r = S, n = {}) {
31
+ const c = (100 / e).toFixed(2), i = e - t.length, l = `<td class="${w}" style="padding: ${D};" width="${c}%"></td>`, d = i > 0 ? l.repeat(i) : "", a = A(o, r, n);
32
32
  return r.filter((s) => a[s]).map((s) => {
33
33
  const T = f[s] ?? !0, u = T ? "" : 'style="display: none;"', R = t.map((m) => a[s](m).replace("<td", `<td width="${c}%"`)).join("");
34
34
  return g.replace("{-{-ATTR_TYPE-}-}", s).replace("{-{-VISIBILITY-}-}", T ? "1" : "0").replace("{-{-DISPLAY_STYLE-}-}", u).replace("{-{-CELLS-}-}", R + d);
@@ -46,15 +46,15 @@ function U(t, e, o, r, n = {}) {
46
46
  r,
47
47
  n
48
48
  ), p = O.replace("{-{-ATTRIBUTE_ROWS-}-}", a);
49
- return d > 0 ? I + p : p;
49
+ return d > 0 ? b + p : p;
50
50
  }).join("");
51
51
  }
52
52
  function h(t, e, o, r = {}) {
53
- return U(t, e, w, o, r);
53
+ return U(t, e, C, o, r);
54
54
  }
55
55
  function F(t) {
56
- const e = t ? `ins-recommendation-v3-block-${t}` : void 0, o = S("grid", e), r = _(), n = h(r, L);
57
- return o.replace("{-{-TITLE-}-}", b.TITLE).replace("{-{-PRODUCT_ROWS-}-}", n).replace("{-{-MOBILE_PRODUCT_ROWS-}-}", "");
56
+ const e = t ? `ins-recommendation-v3-block-${t}` : void 0, o = I("grid", e), r = E(), n = h(r, L);
57
+ return o.replace("{-{-TITLE-}-}", _.TITLE).replace("{-{-PRODUCT_ROWS-}-}", n).replace("{-{-MOBILE_PRODUCT_ROWS-}-}", "");
58
58
  }
59
59
  export {
60
60
  F as getDefaultTemplate,