@useinsider/guido 3.2.0-beta.4655fd0 → 3.2.0-beta.488f8eb
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.
- package/README.md +117 -1
- package/dist/@types/config/schemas.js +153 -95
- package/dist/components/Guido.vue.js +4 -4
- package/dist/components/Guido.vue2.js +90 -88
- package/dist/components/organisms/AutoSaveController.vue.js +17 -0
- package/dist/components/organisms/AutoSaveController.vue2.js +13 -0
- package/dist/components/organisms/header/AutoSaveToggle.vue.js +22 -0
- package/dist/components/organisms/header/AutoSaveToggle.vue2.js +19 -0
- package/dist/components/organisms/header/RightSlot.vue.js +8 -8
- package/dist/components/organisms/header/RightSlot.vue2.js +9 -8
- package/dist/components/organisms/onboarding/AMPOnboarding.vue2.js +51 -31
- package/dist/components/organisms/onboarding/GenericOnboarding.vue.js +1 -1
- package/dist/components/organisms/onboarding/GenericOnboarding.vue2.js +23 -22
- package/dist/components/organisms/onboarding/ItemsOnboarding.vue.js +1 -1
- package/dist/components/organisms/onboarding/ItemsOnboarding.vue2.js +37 -39
- package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.js +3 -3
- package/dist/components/organisms/onboarding/TextBlockOnboarding.vue2.js +30 -41
- package/dist/components/organisms/onboarding/VersionHistoryOnboarding.vue2.js +15 -14
- package/dist/composables/useAutoSave.js +71 -0
- package/dist/composables/useHtmlValidator.js +41 -36
- package/dist/composables/useRecommendation.js +46 -26
- package/dist/composables/useRibbonOffset.js +21 -0
- package/dist/composables/useSave.js +19 -16
- package/dist/composables/useStripo.js +14 -16
- package/dist/composables/validators/useCouponBlockValidator.js +24 -0
- package/dist/config/compiler/recommendationCompilerRules.js +25 -25
- package/dist/config/compiler/unsubscribeCompilerRules.js +40 -37
- package/dist/config/compiler/utils/recommendationCompilerUtils.js +104 -71
- package/dist/config/migrator/index.js +9 -9
- package/dist/config/migrator/recommendation/compositionMapper.js +98 -0
- package/dist/config/migrator/recommendation/extractors.js +27 -0
- package/dist/config/migrator/recommendation/htmlBuilder.js +496 -0
- package/dist/config/migrator/recommendation/parseLegacyConfig.js +33 -0
- package/dist/config/migrator/recommendation/settingsMapper.js +70 -0
- package/dist/config/migrator/recommendation/themeMapper.js +93 -0
- package/dist/config/migrator/recommendationMigrator.js +74 -290
- package/dist/enums/extensions/recommendationBlock.js +2 -1
- package/dist/enums/onboarding.js +7 -2
- package/dist/enums/unsubscribe.js +34 -27
- package/dist/extensions/Blocks/Recommendation/block.js +35 -32
- package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +5 -5
- package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +25 -24
- package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +228 -181
- package/dist/extensions/Blocks/Recommendation/services/configService.js +65 -29
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +130 -81
- package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +19 -10
- package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +8 -8
- package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +25 -15
- package/dist/extensions/Blocks/Recommendation/templates/utils.js +1 -1
- package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMap.js +21 -0
- package/dist/extensions/Blocks/Recommendation/utils/preserveTextStyles.js +13 -22
- package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +33 -0
- package/dist/guido.css +1 -1
- package/dist/node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js +258 -235
- package/dist/node_modules/valibot/dist/index.js +450 -235
- package/dist/package.json.js +1 -1
- package/dist/services/templateLibraryApi.js +5 -4
- package/dist/src/@types/config/defaults.d.ts +5 -1
- package/dist/src/@types/config/index.d.ts +3 -3
- package/dist/src/@types/config/schemas.d.ts +217 -0
- package/dist/src/@types/config/types.d.ts +9 -1
- package/dist/src/components/Guido.vue.d.ts +1 -1
- package/dist/src/components/organisms/AutoSaveController.vue.d.ts +2 -0
- package/dist/src/components/organisms/header/AutoSaveToggle.vue.d.ts +2 -0
- package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
- package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -1
- package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -1
- package/dist/src/components/wrappers/WpModal.vue.d.ts +1 -1
- package/dist/src/composables/useAutoSave.d.ts +3 -0
- package/dist/src/composables/useConfig.d.ts +58 -0
- package/dist/src/composables/useRecommendation.d.ts +10 -1
- package/dist/src/composables/useRecommendation.test.d.ts +1 -0
- package/dist/src/composables/useRibbonOffset.d.ts +4 -0
- package/dist/src/composables/useSave.d.ts +1 -1
- package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
- package/dist/src/config/migrator/index.d.ts +2 -1
- package/dist/src/config/migrator/recommendation/compositionMapper.d.ts +2 -0
- package/dist/src/config/migrator/recommendation/compositionMapper.test.d.ts +1 -0
- package/dist/src/config/migrator/recommendation/extractors.d.ts +7 -0
- package/dist/src/config/migrator/recommendation/extractors.test.d.ts +1 -0
- package/dist/src/config/migrator/recommendation/htmlBuilder.d.ts +11 -0
- package/dist/src/config/migrator/recommendation/parseLegacyConfig.d.ts +15 -0
- package/dist/src/config/migrator/recommendation/parseLegacyConfig.test.d.ts +1 -0
- package/dist/src/config/migrator/recommendation/settingsMapper.d.ts +7 -0
- package/dist/src/config/migrator/recommendation/settingsMapper.test.d.ts +1 -0
- package/dist/src/config/migrator/recommendation/themeMapper.d.ts +5 -0
- package/dist/src/config/migrator/recommendation/themeMapper.test.d.ts +1 -0
- package/dist/src/config/migrator/recommendation/types.d.ts +205 -0
- package/dist/src/config/migrator/recommendationMigrator.d.ts +13 -1
- package/dist/src/config/migrator/recommendationMigrator.test.d.ts +1 -0
- package/dist/src/enums/onboarding.d.ts +6 -0
- package/dist/src/enums/unsubscribe.d.ts +5 -0
- package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.test.d.ts +1 -0
- package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +10 -0
- package/dist/src/extensions/Blocks/Recommendation/services/configService.test.d.ts +1 -0
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +41 -1
- package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +1 -1
- package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.d.ts +21 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.test.d.ts +1 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/preserveTextStyles.d.ts +0 -3
- package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +21 -0
- package/dist/src/library.d.ts +1 -1
- package/dist/src/stores/autosave.d.ts +12 -0
- package/dist/src/stores/config.d.ts +522 -0
- package/dist/src/stores/editor.d.ts +23 -0
- package/dist/src/stores/onboarding.d.ts +4 -0
- package/dist/src/utils/htmlEscape.d.ts +5 -0
- package/dist/src/utils/htmlEscape.test.d.ts +1 -0
- package/dist/src/utils/timeUtil.d.ts +8 -0
- package/dist/stores/autosave.js +17 -0
- package/dist/stores/editor.js +3 -1
- package/dist/stores/onboarding.js +4 -0
- package/dist/utils/htmlEscape.js +13 -0
- package/dist/utils/pairProductVariables.js +89 -88
- package/dist/utils/templatePreparation.js +72 -32
- package/dist/utils/timeUtil.js +19 -0
- package/package.json +7 -3
- package/dist/enums/displayConditions.js +0 -80
- package/dist/extensions/Blocks/Recommendation/templates/grid/migration.js +0 -251
- package/dist/src/enums/displayConditions.d.ts +0 -2
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { ModificationDescription as
|
|
2
|
-
import { CURRENT_CONFIG_VERSION as
|
|
3
|
-
import { setCurrencyAttributes as
|
|
4
|
-
import { hasMinimalConfig as
|
|
5
|
-
|
|
1
|
+
import { ModificationDescription as h } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
|
|
2
|
+
import { CURRENT_CONFIG_VERSION as l, DEFAULT_NODE_CONFIG as r } from "../constants/defaultConfig.js";
|
|
3
|
+
import { setCurrencyAttributes as D } from "../controls/main/utils.js";
|
|
4
|
+
import { hasMinimalConfig as A } from "../types/nodeConfig.js";
|
|
5
|
+
function S(e) {
|
|
6
|
+
return e === "." || e === "," || e === " ";
|
|
7
|
+
}
|
|
8
|
+
function N(e) {
|
|
9
|
+
return e === "." || e === "," || e === " " || e === "";
|
|
10
|
+
}
|
|
11
|
+
class V {
|
|
6
12
|
// ========================================================================
|
|
7
13
|
// Read Operations
|
|
8
14
|
// ========================================================================
|
|
@@ -42,7 +48,7 @@ class C {
|
|
|
42
48
|
return !1;
|
|
43
49
|
try {
|
|
44
50
|
const t = i.getNodeConfig();
|
|
45
|
-
return
|
|
51
|
+
return A(t);
|
|
46
52
|
} catch {
|
|
47
53
|
return !1;
|
|
48
54
|
}
|
|
@@ -77,8 +83,8 @@ class C {
|
|
|
77
83
|
* @returns The new complete configuration
|
|
78
84
|
*/
|
|
79
85
|
static updateConfig(i, t, o, n) {
|
|
80
|
-
const
|
|
81
|
-
return this.saveConfig(i, t,
|
|
86
|
+
const c = this.getConfig(t), u = this.deepMerge(c, o);
|
|
87
|
+
return this.saveConfig(i, t, u, n), u;
|
|
82
88
|
}
|
|
83
89
|
/**
|
|
84
90
|
* Initialize configuration for a new block
|
|
@@ -95,7 +101,7 @@ class C {
|
|
|
95
101
|
*/
|
|
96
102
|
static initializeConfig(i, t, o) {
|
|
97
103
|
const n = o ? this.mergeWithDefaults(o) : this.cloneDefaults();
|
|
98
|
-
return this.saveConfig(i, t, n, "Initialize recommendation block"),
|
|
104
|
+
return this.saveConfig(i, t, n, "Initialize recommendation block"), D({
|
|
99
105
|
currentNode: t,
|
|
100
106
|
documentModifier: i.getDocumentModifier(),
|
|
101
107
|
currency: n.currency
|
|
@@ -112,9 +118,9 @@ class C {
|
|
|
112
118
|
*/
|
|
113
119
|
static saveConfig(i, t, o, n) {
|
|
114
120
|
try {
|
|
115
|
-
i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new
|
|
116
|
-
} catch (
|
|
117
|
-
console.warn("[RecommendationConfigService] Failed to save config:",
|
|
121
|
+
i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new h(n));
|
|
122
|
+
} catch (c) {
|
|
123
|
+
console.warn("[RecommendationConfigService] Failed to save config:", c);
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
// ========================================================================
|
|
@@ -125,23 +131,53 @@ class C {
|
|
|
125
131
|
*
|
|
126
132
|
* Reads existing data-attributes and creates a proper node config.
|
|
127
133
|
* Used when loading templates created before node config was implemented.
|
|
134
|
+
*
|
|
135
|
+
* Sources, in priority order (later overrides earlier):
|
|
136
|
+
* 1. `esd-ext-config` JSON blob — emitted by the recommendation migrator and
|
|
137
|
+
* by hand-authored new templates. Carries the full RecommendationNodeConfig
|
|
138
|
+
* (strategy, language, currency, filters, productIds, etc.) which the
|
|
139
|
+
* discrete data-* attrs below cannot capture.
|
|
140
|
+
* 2. Discrete `data-*` attributes — runtime source of truth for
|
|
141
|
+
* layout/composition/spacing; controls keep these in sync as the user
|
|
142
|
+
* edits, so we let them override the JSON blob if they disagree.
|
|
143
|
+
* 3. `currency-*` attributes — durable currency source on the block element.
|
|
128
144
|
* @param api - Stripo extension API with document modifier
|
|
129
145
|
* @param node - The block node to migrate
|
|
130
146
|
* @returns The migrated configuration
|
|
131
147
|
*/
|
|
132
148
|
static migrateFromDataAttributes(i, t) {
|
|
133
149
|
const o = {
|
|
134
|
-
configVersion:
|
|
150
|
+
configVersion: l
|
|
135
151
|
};
|
|
136
152
|
if ("getAttribute" in t && typeof t.getAttribute == "function") {
|
|
137
|
-
const n = t.getAttribute("
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
c
|
|
153
|
+
const n = t.getAttribute("esd-ext-config");
|
|
154
|
+
if (n)
|
|
155
|
+
try {
|
|
156
|
+
const s = JSON.parse(n);
|
|
157
|
+
s && typeof s == "object" && (Object.assign(o, s), o.configVersion = l);
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
const c = t.getAttribute("data-layout");
|
|
161
|
+
c === "list" || c === "horizontal" ? o.layout = "list" : (c === "grid" || c === "vertical") && (o.layout = "grid");
|
|
162
|
+
const u = t.getAttribute("data-card-composition");
|
|
163
|
+
u && (o.composition = u.split(",").filter(Boolean));
|
|
164
|
+
const p = t.getAttribute("data-column-spacing");
|
|
165
|
+
p && (o.columnSpacing = parseInt(p) || r.columnSpacing);
|
|
166
|
+
const b = t.getAttribute("data-row-spacing");
|
|
167
|
+
if (b && (o.rowSpacing = parseInt(b) || r.rowSpacing), !o.currency) {
|
|
168
|
+
const s = t.getAttribute("currency"), y = t.getAttribute("currency-symbol"), d = t.getAttribute("currency-alignment"), f = t.getAttribute("currency-thousand-separator"), g = t.getAttribute("currency-decimal-separator"), m = t.getAttribute("currency-decimal-count");
|
|
169
|
+
if (s || y || d || f || g || m) {
|
|
170
|
+
const a = r.currency, C = m ? parseInt(m) : NaN;
|
|
171
|
+
o.currency = {
|
|
172
|
+
code: s ?? a.code,
|
|
173
|
+
symbol: y ?? a.symbol,
|
|
174
|
+
alignment: d === "0" ? "before" : "after",
|
|
175
|
+
decimalCount: Number.isFinite(C) ? C : a.decimalCount,
|
|
176
|
+
decimalSeparator: S(g) ? g : a.decimalSeparator,
|
|
177
|
+
thousandSeparator: N(f) ? f : a.thousandSeparator
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
145
181
|
}
|
|
146
182
|
return this.initializeConfig(i, t, o);
|
|
147
183
|
}
|
|
@@ -151,7 +187,7 @@ class C {
|
|
|
151
187
|
* @returns True if migration is needed
|
|
152
188
|
*/
|
|
153
189
|
static needsMigration(i) {
|
|
154
|
-
return i ? this.hasConfig(i) ? this.getConfigVersion(i) <
|
|
190
|
+
return i ? this.hasConfig(i) ? this.getConfigVersion(i) < l : !0 : !1;
|
|
155
191
|
}
|
|
156
192
|
// ========================================================================
|
|
157
193
|
// Internal Helpers
|
|
@@ -161,12 +197,12 @@ class C {
|
|
|
161
197
|
*/
|
|
162
198
|
static cloneDefaults() {
|
|
163
199
|
return {
|
|
164
|
-
...
|
|
165
|
-
currency: { ...
|
|
166
|
-
omnibusPrice: { ...
|
|
167
|
-
omnibusDiscount: { ...
|
|
168
|
-
composition: [...
|
|
169
|
-
visibility: { ...
|
|
200
|
+
...r,
|
|
201
|
+
currency: { ...r.currency },
|
|
202
|
+
omnibusPrice: { ...r.omnibusPrice },
|
|
203
|
+
omnibusDiscount: { ...r.omnibusDiscount },
|
|
204
|
+
composition: [...r.composition],
|
|
205
|
+
visibility: { ...r.visibility },
|
|
170
206
|
filters: [],
|
|
171
207
|
productIds: [],
|
|
172
208
|
recommendationId: 0
|
|
@@ -236,5 +272,5 @@ class C {
|
|
|
236
272
|
}
|
|
237
273
|
}
|
|
238
274
|
export {
|
|
239
|
-
|
|
275
|
+
V as RecommendationConfigService
|
|
240
276
|
};
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { RecommendationFeedSourceMaps as
|
|
1
|
+
import { RecommendationFeedSourceMaps as S, getOperatorOptions as R, PriceAttributes as y } from "../../../../enums/extensions/recommendationBlock.js";
|
|
2
2
|
import { useRecommendationApi as C } from "../../../../services/recommendationApi.js";
|
|
3
|
-
import { useConfigStore as
|
|
4
|
-
import { defineStore as
|
|
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
|
|
7
|
-
import { getDefaultProducts as
|
|
6
|
+
import { EXCLUDED_ALGORITHM_IDS as D } from "../constants/defaultConfig.js";
|
|
7
|
+
import { getDefaultProducts as g } from "../templates/utils.js";
|
|
8
8
|
import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
|
|
9
|
-
import { isFilterValid as
|
|
9
|
+
import { isFilterValid as w } from "../validation/filterSchema.js";
|
|
10
|
+
import { isConfigValid as v } from "../validation/requiredFields.js";
|
|
10
11
|
const h = C();
|
|
11
12
|
let m = null, u = null, d = null;
|
|
12
|
-
function
|
|
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
|
|
41
|
+
function I() {
|
|
41
42
|
return {
|
|
42
|
-
recommendationConfigs:
|
|
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
|
|
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
|
-
}),
|
|
61
|
-
state: () =>
|
|
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 :
|
|
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,45 +103,45 @@ const v = () => ({
|
|
|
102
103
|
},
|
|
103
104
|
hasValidFilters() {
|
|
104
105
|
const { filters: t } = this.recommendationConfigs;
|
|
105
|
-
return t.length ? t.every((
|
|
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((
|
|
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((
|
|
114
|
+
return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
|
|
114
115
|
},
|
|
115
116
|
getActivePredictiveAlgorithms: (t) => {
|
|
116
|
-
const
|
|
117
|
-
return t.activePredictiveAlgorithms.filter((
|
|
118
|
-
|
|
119
|
-
}),
|
|
120
|
-
text:
|
|
121
|
-
value:
|
|
117
|
+
const e = [];
|
|
118
|
+
return t.activePredictiveAlgorithms.filter((r) => !D.includes(r)).forEach((r) => {
|
|
119
|
+
e.push(...S.filter((n) => n.id === r));
|
|
120
|
+
}), e.map((r) => ({
|
|
121
|
+
text: r.name,
|
|
122
|
+
value: r.key
|
|
122
123
|
}));
|
|
123
124
|
},
|
|
124
|
-
getLanguages: (t) => Object.entries(t.languages).map(([
|
|
125
|
-
text:
|
|
126
|
-
value:
|
|
125
|
+
getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
|
|
126
|
+
text: r,
|
|
127
|
+
value: e
|
|
127
128
|
})),
|
|
128
|
-
getCurrencySymbolList: (t) => t.currencyList.map((
|
|
129
|
-
text:
|
|
130
|
-
value:
|
|
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
|
-
let
|
|
135
|
-
return t.type === "productAttribute" ?
|
|
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, {
|
|
136
137
|
text: t.displayName,
|
|
137
|
-
value:
|
|
138
|
+
value: e,
|
|
138
139
|
type: t.attributeType
|
|
139
140
|
};
|
|
140
141
|
});
|
|
141
142
|
},
|
|
142
143
|
getSelectedFilterGroup() {
|
|
143
|
-
return (t) => [...this.recommendationConfigs.filters].filter((
|
|
144
|
+
return (t) => [...this.recommendationConfigs.filters].filter((e) => e.filterGroup === t);
|
|
144
145
|
}
|
|
145
146
|
},
|
|
146
147
|
actions: {
|
|
@@ -154,7 +155,7 @@ const v = () => ({
|
|
|
154
155
|
setCurrentBlock(t) {
|
|
155
156
|
this.blockStates[t] || (this.blockStates = {
|
|
156
157
|
...this.blockStates,
|
|
157
|
-
[t]:
|
|
158
|
+
[t]: I()
|
|
158
159
|
}), this.currentRecommendationId = t;
|
|
159
160
|
},
|
|
160
161
|
/**
|
|
@@ -162,13 +163,13 @@ const v = () => ({
|
|
|
162
163
|
* Resets currentRecommendationId if it was the deleted block.
|
|
163
164
|
*/
|
|
164
165
|
removeBlockState(t) {
|
|
165
|
-
const
|
|
166
|
-
if (this.recommendationCampaignUrls[
|
|
166
|
+
const e = t.toString();
|
|
167
|
+
if (this.recommendationCampaignUrls[e]) {
|
|
167
168
|
const n = { ...this.recommendationCampaignUrls };
|
|
168
|
-
delete n[
|
|
169
|
+
delete n[e], this.recommendationCampaignUrls = n;
|
|
169
170
|
}
|
|
170
|
-
const
|
|
171
|
-
if (delete
|
|
171
|
+
const r = { ...this.blockStates };
|
|
172
|
+
if (delete r[t], this.blockStates = r, this.currentRecommendationId === t) {
|
|
172
173
|
const n = Object.keys(this.blockStates).map(Number);
|
|
173
174
|
this.currentRecommendationId = n.length > 0 ? n[0] : null;
|
|
174
175
|
}
|
|
@@ -180,37 +181,75 @@ const v = () => ({
|
|
|
180
181
|
markBlockInitialized(t) {
|
|
181
182
|
this.blockStates[t] && (this.blockStates[t].isInitialized = !0);
|
|
182
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
|
+
},
|
|
183
222
|
/**
|
|
184
223
|
* Patches the current block's recommendationConfigs.
|
|
185
224
|
* Replaces `store.$patch({ recommendationConfigs: { ... } })` pattern.
|
|
186
225
|
*/
|
|
187
|
-
patchCurrentBlockConfig(t,
|
|
226
|
+
patchCurrentBlockConfig(t, e = {}) {
|
|
188
227
|
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
189
228
|
return;
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
...
|
|
229
|
+
const r = this.blockStates[this.currentRecommendationId];
|
|
230
|
+
r.recommendationConfigs = {
|
|
231
|
+
...r.recommendationConfigs,
|
|
193
232
|
...t,
|
|
194
233
|
currencySettings: {
|
|
195
|
-
...
|
|
234
|
+
...r.recommendationConfigs.currencySettings,
|
|
196
235
|
...t.currencySettings || {}
|
|
197
236
|
}
|
|
198
237
|
};
|
|
199
|
-
const { triggerRefetch: n = !0 } =
|
|
238
|
+
const { triggerRefetch: n = !0 } = e;
|
|
200
239
|
n && this.configVersion++;
|
|
201
240
|
},
|
|
202
241
|
/**
|
|
203
242
|
* Creates a filter with the first available attribute and operator pre-selected.
|
|
204
243
|
*/
|
|
205
|
-
createDefaultFilter(t,
|
|
206
|
-
const [
|
|
244
|
+
createDefaultFilter(t, e) {
|
|
245
|
+
const [r] = this.getFilterList, [n] = R(r == null ? void 0 : r.type);
|
|
207
246
|
return {
|
|
208
247
|
type: "standardFilter",
|
|
209
|
-
attribute: (
|
|
248
|
+
attribute: (r == null ? void 0 : r.value) ?? "",
|
|
210
249
|
operator: (n == null ? void 0 : n.value) ?? "",
|
|
211
250
|
innerGroupOperator: "*",
|
|
212
251
|
outerGroupOperator: "*",
|
|
213
|
-
filterNumber:
|
|
252
|
+
filterNumber: e,
|
|
214
253
|
filterGroup: t,
|
|
215
254
|
isValid: !1,
|
|
216
255
|
value: ""
|
|
@@ -262,14 +301,14 @@ const v = () => ({
|
|
|
262
301
|
m = (async () => {
|
|
263
302
|
const {
|
|
264
303
|
activePredictiveAlgorithms: t,
|
|
265
|
-
languages:
|
|
266
|
-
currencies:
|
|
304
|
+
languages: e,
|
|
305
|
+
currencies: r
|
|
267
306
|
} = await h.fetchRecommendationCreateData();
|
|
268
|
-
if (this.activePredictiveAlgorithms = t, this.languages =
|
|
307
|
+
if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
|
|
269
308
|
const n = this.blockStates[this.currentRecommendationId];
|
|
270
309
|
n.filterStatus = !!n.recommendationConfigs.filters.length;
|
|
271
310
|
}
|
|
272
|
-
this.currencyList =
|
|
311
|
+
this.currencyList = r;
|
|
273
312
|
})();
|
|
274
313
|
try {
|
|
275
314
|
await m;
|
|
@@ -306,8 +345,8 @@ const v = () => ({
|
|
|
306
345
|
deleteFilterGroup(t) {
|
|
307
346
|
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
308
347
|
return;
|
|
309
|
-
const
|
|
310
|
-
|
|
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) => ({
|
|
311
350
|
...i,
|
|
312
351
|
filterGroup: c.get(i.filterGroup) ?? i.filterGroup
|
|
313
352
|
}));
|
|
@@ -315,51 +354,59 @@ const v = () => ({
|
|
|
315
354
|
updateFilter(t) {
|
|
316
355
|
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
317
356
|
return;
|
|
318
|
-
const
|
|
319
|
-
if (
|
|
320
|
-
const n = [...
|
|
321
|
-
n[
|
|
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] = {
|
|
322
361
|
...t,
|
|
323
|
-
isValid:
|
|
324
|
-
},
|
|
362
|
+
isValid: w(t)
|
|
363
|
+
}, e.recommendationConfigs.filters = n;
|
|
325
364
|
}
|
|
326
365
|
},
|
|
327
366
|
deleteFilter(t) {
|
|
328
367
|
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
329
368
|
return;
|
|
330
|
-
const
|
|
331
|
-
if (
|
|
332
|
-
let n = [...
|
|
333
|
-
if (n.splice(
|
|
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)) {
|
|
334
373
|
let i = 1;
|
|
335
374
|
n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
|
|
336
375
|
} else {
|
|
337
|
-
const i = [...new Set(n.map((s) => s.filterGroup))].sort((s,
|
|
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]));
|
|
338
377
|
n = n.map((s) => ({
|
|
339
378
|
...s,
|
|
340
379
|
filterGroup: o.get(s.filterGroup) ?? s.filterGroup
|
|
341
380
|
}));
|
|
342
381
|
}
|
|
343
|
-
|
|
382
|
+
e.recommendationConfigs.filters = n;
|
|
344
383
|
}
|
|
345
384
|
},
|
|
346
385
|
addFilter(t) {
|
|
347
386
|
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
348
387
|
return;
|
|
349
|
-
const
|
|
388
|
+
const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], c = r.filter(
|
|
350
389
|
(o) => o.filterGroup === t.filterGroup
|
|
351
|
-
).length + 1, i =
|
|
352
|
-
i !== -1 ?
|
|
390
|
+
).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
|
|
391
|
+
i !== -1 ? r.splice(i + 1, 0, {
|
|
353
392
|
...t,
|
|
354
393
|
filterNumber: c
|
|
355
|
-
}) :
|
|
394
|
+
}) : r.push({
|
|
356
395
|
...t,
|
|
357
396
|
filterNumber: c
|
|
358
|
-
}),
|
|
397
|
+
}), e.recommendationConfigs.filters = r;
|
|
359
398
|
},
|
|
360
399
|
generateFilterQuery() {
|
|
361
400
|
return b(this.recommendationConfigs.filters);
|
|
362
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
|
+
},
|
|
363
410
|
// ====================================================================
|
|
364
411
|
// Per-Block Product Fetching
|
|
365
412
|
// ====================================================================
|
|
@@ -379,29 +426,31 @@ const v = () => ({
|
|
|
379
426
|
},
|
|
380
427
|
async _doFetchProducts() {
|
|
381
428
|
var p;
|
|
382
|
-
const t = this.currentRecommendationId,
|
|
383
|
-
locale:
|
|
384
|
-
currency:
|
|
429
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = S.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,
|
|
385
432
|
partnerName: o.partnerName,
|
|
386
|
-
size:
|
|
433
|
+
size: r.size,
|
|
387
434
|
details: !0,
|
|
388
435
|
campaignId: o.variationId
|
|
389
436
|
};
|
|
390
|
-
|
|
391
|
-
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);
|
|
392
438
|
let f;
|
|
393
439
|
try {
|
|
394
|
-
f = await h.fetchRecommendationProducts(i,
|
|
440
|
+
f = await h.fetchRecommendationProducts(i, a);
|
|
395
441
|
} catch {
|
|
396
442
|
f = [];
|
|
397
443
|
}
|
|
398
444
|
if (this.blockStates[t]) {
|
|
399
|
-
const
|
|
400
|
-
this.blockStates[t].recommendationProducts =
|
|
445
|
+
const l = f.length > 0 ? f : g(s);
|
|
446
|
+
l.length < s ? this.blockStates[t].recommendationProducts = [
|
|
447
|
+
...l,
|
|
448
|
+
...g(s - l.length)
|
|
449
|
+
] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
|
|
401
450
|
}
|
|
402
451
|
}
|
|
403
452
|
}
|
|
404
453
|
});
|
|
405
454
|
export {
|
|
406
|
-
|
|
455
|
+
M as useRecommendationExtensionStore
|
|
407
456
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RecommendationBlockId as s } from "../../constants/blockIds.js";
|
|
2
|
-
import { ATTR_PRODUCT_ATTR as
|
|
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
|
|
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>${
|
|
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>${
|
|
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">${
|
|
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
|
|
157
|
-
const e = p(), o = ((
|
|
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">${
|
|
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="
|
|
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
|
-
${
|
|
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}">
|