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