@useinsider/guido 3.18.2-beta.fc246a7 → 3.18.2-beta.fc87f65
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 -57
- package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +23 -37
- 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/components/organisms/save-as-template/SaveAsTemplateDrawer.vue2.js +34 -34
- package/dist/composables/useCustomInterfaceAppearance.js +56 -46
- package/dist/composables/useHtmlValidator.js +87 -82
- package/dist/composables/useHttp.js +24 -22
- package/dist/composables/useRecommendation.js +40 -35
- package/dist/composables/useStripo.js +30 -30
- package/dist/config/migrator/itemsBlockMigrator.js +167 -257
- package/dist/enums/extensions/recommendationBlock.js +38 -31
- package/dist/extensions/Blocks/Items/controls/name/trimming.js +17 -18
- package/dist/extensions/Blocks/Items/controls/settingsControl.js +105 -114
- package/dist/extensions/Blocks/Items/enums/productEnums.js +9 -14
- package/dist/extensions/Blocks/Items/layouts/horizontal.html.js +9 -11
- package/dist/extensions/Blocks/Items/template.js +204 -219
- package/dist/extensions/Blocks/Recommendation/block.js +43 -39
- 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 +194 -91
- 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/services/templateLibraryApi.js +17 -14
- 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/Items/enums/productEnums.d.ts +0 -3
- package/dist/src/extensions/Blocks/Items/template.d.ts +0 -19
- 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 +79 -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/src/utils/genericUtil.d.ts +6 -0
- package/dist/src/utils/liquidDynamicContent.d.ts +20 -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/utils/genericUtil.js +63 -54
- package/dist/utils/liquidDynamicContent.js +15 -0
- package/dist/utils/pairProductVariables.js +71 -71
- package/dist/utils/templatePreparation.js +61 -60
- package/package.json +5 -5
- package/dist/extensions/Blocks/Items/constants/cardLayout.js +0 -20
- package/dist/src/extensions/Blocks/Items/constants/cardLayout.d.ts +0 -44
|
@@ -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 N, PriceAttributes as U } from "../../../../enums/extensions/recommendationBlock.js";
|
|
2
|
+
import { useRecommendationApi as A } 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 x } from "../constants/layout.js";
|
|
7
|
+
import { DEFAULT_NODE_CONFIG as B, EXCLUDED_ALGORITHM_IDS as M } from "../constants/defaultConfig.js";
|
|
8
|
+
import { getDefaultProducts as 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 T } from "../utils/productIds.js";
|
|
12
|
+
import { isFilterValid as z } from "../validation/filterSchema.js";
|
|
13
|
+
import { getInvalidFields as V, isConfigValid as j } from "../validation/requiredFields.js";
|
|
14
|
+
const f = A();
|
|
15
|
+
let S = null, p = null, y = null, I = "", R = null, b = 0;
|
|
16
|
+
const g = /* @__PURE__ */ new Map();
|
|
17
|
+
let l = null;
|
|
18
|
+
function G() {
|
|
18
19
|
return {
|
|
19
|
-
cardsInRow:
|
|
20
|
+
cardsInRow: x,
|
|
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
|
+
}), it = 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,8 +146,8 @@ 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((n) => !
|
|
149
|
+
const e = v(), r = [];
|
|
150
|
+
return t.activePredictiveAlgorithms.filter((n) => !M.includes(n)).forEach((n) => {
|
|
143
151
|
r.push(...e.filter((s) => s.id === n));
|
|
144
152
|
}), r.map((n) => ({
|
|
145
153
|
text: n.name,
|
|
@@ -157,7 +165,7 @@ const 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}` : U.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
|
|
161
169
|
text: t.displayName,
|
|
162
170
|
value: e,
|
|
163
171
|
type: t.attributeType
|
|
@@ -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
|
/**
|
|
@@ -201,12 +209,16 @@ const z = () => ({
|
|
|
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
|
};
|
|
@@ -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, n = {
|
|
243
255
|
name: e.currencyCode,
|
|
244
256
|
value: e.currencyCode,
|
|
245
257
|
symbol: e.currencySymbol,
|
|
@@ -247,7 +259,7 @@ 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], i = s ?
|
|
262
|
+
}, s = !this.blockStates[t], i = s ? P() : this.blockStates[t];
|
|
251
263
|
i.recommendationConfigs = {
|
|
252
264
|
...i.recommendationConfigs,
|
|
253
265
|
strategy: e.strategy,
|
|
@@ -286,7 +298,7 @@ const z = () => ({
|
|
|
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, [n] =
|
|
301
|
+
const [r] = this.getFilterList, [n] = N(r == null ? void 0 : r.type);
|
|
290
302
|
return {
|
|
291
303
|
type: "standardFilter",
|
|
292
304
|
attribute: (r == null ? void 0 : r.value) ?? "",
|
|
@@ -346,29 +358,97 @@ 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
|
+
invalidateStrategies() {
|
|
387
|
+
this.strategiesFetched = !1, this.strategiesSettled = !1, b += 1;
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* Opens a Smart Recommender strategy page in a new tab and refreshes the list every time
|
|
391
|
+
* the user comes back. `visibilitychange` only: a `focus` listener would also fire when
|
|
392
|
+
* focus merely returns from the Stripo canvas iframe to the panel, refetching and
|
|
393
|
+
* regenerating the canvas while the user never left the editor (SD-149916).
|
|
394
|
+
*
|
|
395
|
+
* Deliberately NOT one-shot: the strategy tab stays open, so a user can create a second
|
|
396
|
+
* strategy in it and come back again — a listener that disarmed on the first return would
|
|
397
|
+
* leave that one missing from the list. It disarms on the first return AFTER that tab is
|
|
398
|
+
* closed instead, so an ordinary alt-tab later in the session costs nothing. Armed only
|
|
399
|
+
* once the user actually opens a strategy page, so anyone who never does pays nothing.
|
|
400
|
+
*/
|
|
401
|
+
_armStrategyPageReturn(t) {
|
|
402
|
+
const e = window.open(t, "_blank");
|
|
403
|
+
if (!e)
|
|
404
|
+
return;
|
|
405
|
+
e.opener = null, l == null || l();
|
|
406
|
+
let r = !1;
|
|
407
|
+
const n = () => {
|
|
408
|
+
document.hidden || r || (e != null && e.closed && (l == null || l()), r = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
|
|
409
|
+
}).finally(() => {
|
|
410
|
+
r = !1;
|
|
411
|
+
}));
|
|
412
|
+
};
|
|
413
|
+
document.addEventListener("visibilitychange", n), l = () => {
|
|
414
|
+
document.removeEventListener("visibilitychange", n), l = null;
|
|
415
|
+
};
|
|
416
|
+
},
|
|
417
|
+
/**
|
|
418
|
+
* Reloads the products of the block the user is on, so an edit made in the strategy tab
|
|
419
|
+
* lands on the canvas instead of waiting for the assign drawer to be reopened.
|
|
420
|
+
*
|
|
421
|
+
* Only the selected block: the canvas regeneration is driven off the active block's
|
|
422
|
+
* product array, so refreshing an unselected block would fetch without redrawing it.
|
|
423
|
+
*/
|
|
424
|
+
async _refreshAssignedStrategyProducts() {
|
|
425
|
+
var e;
|
|
426
|
+
const t = this.currentRecommendationId;
|
|
427
|
+
t === null || !((e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId) || await this.fetchRecommendationProducts();
|
|
428
|
+
},
|
|
429
|
+
/** Lets the editor tear the return listeners down when it unmounts. */
|
|
430
|
+
disposeStrategyPageReturn() {
|
|
431
|
+
l == null || l();
|
|
432
|
+
},
|
|
433
|
+
/**
|
|
434
|
+
* Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
|
|
435
|
+
* new strategy is offered; the user picks it themselves.
|
|
436
|
+
*/
|
|
437
|
+
openStrategyCreation() {
|
|
438
|
+
this._armStrategyPageReturn(_.RECOMMENDATION_STRATEGY_CREATE_PATH);
|
|
439
|
+
},
|
|
440
|
+
/**
|
|
441
|
+
* Opens the strategy's edit page. Every return refreshes the list so the block reflects the
|
|
442
|
+
* edited configuration.
|
|
443
|
+
*/
|
|
444
|
+
openStrategyEdit(t) {
|
|
445
|
+
this._armStrategyPageReturn(D(t));
|
|
446
|
+
},
|
|
367
447
|
async resolveStrategyUrlTemplate(t, e) {
|
|
368
448
|
const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
|
|
369
449
|
if (s)
|
|
370
450
|
return s;
|
|
371
|
-
const { full_path: i = "" } = await
|
|
451
|
+
const { full_path: i = "" } = await f.generateStrategyEndpoint(t, e) ?? {};
|
|
372
452
|
return n && i && (this.strategyUrlTemplates = {
|
|
373
453
|
...this.strategyUrlTemplates,
|
|
374
454
|
[n]: i
|
|
@@ -381,23 +461,27 @@ const z = () => ({
|
|
|
381
461
|
});
|
|
382
462
|
},
|
|
383
463
|
removeStrategy() {
|
|
384
|
-
this.patchCurrentBlockConfig({
|
|
464
|
+
this.patchCurrentBlockConfig({
|
|
465
|
+
strategyId: "",
|
|
466
|
+
strategy: "",
|
|
467
|
+
size: B.size
|
|
468
|
+
});
|
|
385
469
|
},
|
|
386
470
|
// ====================================================================
|
|
387
471
|
// Shared Data Fetching (fetched once, used by all blocks)
|
|
388
472
|
// ====================================================================
|
|
389
473
|
async fetchRecommendationCreateData() {
|
|
390
474
|
if (!this.activePredictiveAlgorithms.length) {
|
|
391
|
-
if (
|
|
392
|
-
await
|
|
475
|
+
if (S) {
|
|
476
|
+
await S;
|
|
393
477
|
return;
|
|
394
478
|
}
|
|
395
|
-
|
|
479
|
+
S = (async () => {
|
|
396
480
|
const {
|
|
397
481
|
activePredictiveAlgorithms: t,
|
|
398
482
|
languages: e,
|
|
399
483
|
currencies: r
|
|
400
|
-
} = await
|
|
484
|
+
} = await f.fetchRecommendationCreateData();
|
|
401
485
|
if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
|
|
402
486
|
const n = this.blockStates[this.currentRecommendationId];
|
|
403
487
|
n.filterStatus = !!n.recommendationConfigs.filters.length;
|
|
@@ -405,26 +489,26 @@ const z = () => ({
|
|
|
405
489
|
this.currencyList = r;
|
|
406
490
|
})();
|
|
407
491
|
try {
|
|
408
|
-
await
|
|
492
|
+
await S;
|
|
409
493
|
} finally {
|
|
410
|
-
|
|
494
|
+
S = null;
|
|
411
495
|
}
|
|
412
496
|
}
|
|
413
497
|
},
|
|
414
498
|
async fetchRecommendationFilters() {
|
|
415
499
|
if (!Object.keys(this.filterList).length) {
|
|
416
|
-
if (
|
|
417
|
-
await
|
|
500
|
+
if (p) {
|
|
501
|
+
await p;
|
|
418
502
|
return;
|
|
419
503
|
}
|
|
420
|
-
|
|
421
|
-
const t = await
|
|
504
|
+
p = (async () => {
|
|
505
|
+
const t = await f.fetchRecommendationFilters();
|
|
422
506
|
this.filterList = t;
|
|
423
507
|
})();
|
|
424
508
|
try {
|
|
425
|
-
await
|
|
509
|
+
await p;
|
|
426
510
|
} finally {
|
|
427
|
-
|
|
511
|
+
p = null;
|
|
428
512
|
}
|
|
429
513
|
}
|
|
430
514
|
},
|
|
@@ -453,7 +537,7 @@ const z = () => ({
|
|
|
453
537
|
const n = [...e.recommendationConfigs.filters];
|
|
454
538
|
n[r] = {
|
|
455
539
|
...t,
|
|
456
|
-
isValid:
|
|
540
|
+
isValid: z(t)
|
|
457
541
|
}, e.recommendationConfigs.filters = n;
|
|
458
542
|
}
|
|
459
543
|
},
|
|
@@ -467,10 +551,10 @@ const z = () => ({
|
|
|
467
551
|
let i = 1;
|
|
468
552
|
n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
|
|
469
553
|
} else {
|
|
470
|
-
const i = [...new Set(n.map((
|
|
471
|
-
n = n.map((
|
|
472
|
-
...
|
|
473
|
-
filterGroup: o.get(
|
|
554
|
+
const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, u) => a - u), o = new Map(i.map((a, u) => [a, u + 1]));
|
|
555
|
+
n = n.map((a) => ({
|
|
556
|
+
...a,
|
|
557
|
+
filterGroup: o.get(a.filterGroup) ?? a.filterGroup
|
|
474
558
|
}));
|
|
475
559
|
}
|
|
476
560
|
e.recommendationConfigs.filters = n;
|
|
@@ -491,7 +575,7 @@ const z = () => ({
|
|
|
491
575
|
}), e.recommendationConfigs.filters = r;
|
|
492
576
|
},
|
|
493
577
|
generateFilterQuery() {
|
|
494
|
-
return
|
|
578
|
+
return w(this.recommendationConfigs.filters);
|
|
495
579
|
},
|
|
496
580
|
/**
|
|
497
581
|
* Validation-only check invoked at save-CTA time. Defined as an action
|
|
@@ -499,33 +583,52 @@ const z = () => ({
|
|
|
499
583
|
* every block's recommendationConfigs across user edits.
|
|
500
584
|
*/
|
|
501
585
|
hasInvalidBlock() {
|
|
502
|
-
return Object.values(this.blockStates).some((t) => !
|
|
586
|
+
return Object.values(this.blockStates).some((t) => !j(t.recommendationConfigs, this));
|
|
587
|
+
},
|
|
588
|
+
/**
|
|
589
|
+
* Which required fields are missing across the design's blocks, so the save
|
|
590
|
+
* toaster can name the cause instead of always showing the generic message.
|
|
591
|
+
*/
|
|
592
|
+
invalidBlockFields() {
|
|
593
|
+
return Object.values(this.blockStates).flatMap((t) => V(t.recommendationConfigs, this));
|
|
503
594
|
},
|
|
504
595
|
// ====================================================================
|
|
505
596
|
// Per-Block Product Fetching
|
|
506
597
|
// ====================================================================
|
|
507
598
|
async fetchRecommendationProducts() {
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
599
|
+
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
600
|
+
return;
|
|
601
|
+
const t = [
|
|
602
|
+
this.currentRecommendationId,
|
|
603
|
+
this.configVersion,
|
|
604
|
+
b
|
|
605
|
+
].join(":");
|
|
606
|
+
if (y && I === t) {
|
|
607
|
+
await y;
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
I = t, y = this._doFetchProducts();
|
|
611
|
+
try {
|
|
612
|
+
await y;
|
|
613
|
+
} finally {
|
|
614
|
+
I === t && (y = null, I = "");
|
|
519
615
|
}
|
|
520
616
|
},
|
|
521
617
|
async _doFetchProducts() {
|
|
522
|
-
var
|
|
523
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n =
|
|
618
|
+
var h;
|
|
619
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = k();
|
|
524
620
|
if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
|
|
525
621
|
await this._doFetchStrategyProducts(t);
|
|
526
622
|
return;
|
|
527
623
|
}
|
|
528
|
-
const s =
|
|
624
|
+
const s = (g.get(t) ?? 0) + 1;
|
|
625
|
+
g.set(t, s);
|
|
626
|
+
const i = parseInt(r.size) || 6;
|
|
627
|
+
if (!r.strategy) {
|
|
628
|
+
this._setBlockProducts(t, [], i);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
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
632
|
locale: r.language,
|
|
530
633
|
currency: r.currencySettings.value,
|
|
531
634
|
partnerName: n.partnerName,
|
|
@@ -533,21 +636,21 @@ const z = () => ({
|
|
|
533
636
|
details: !0,
|
|
534
637
|
campaignId: n.variationId
|
|
535
638
|
};
|
|
536
|
-
r.strategy === "manualMerchandising" ?
|
|
537
|
-
|
|
538
|
-
|
|
639
|
+
r.strategy === "manualMerchandising" ? c.productId = T(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), a && (c.filter = a), r.shuffleProducts && (c.shuffle = !0), Object.assign(
|
|
640
|
+
c,
|
|
641
|
+
O(n.partnerName, r.strategy)
|
|
539
642
|
);
|
|
540
|
-
let
|
|
643
|
+
let m;
|
|
541
644
|
try {
|
|
542
|
-
|
|
645
|
+
m = await f.fetchRecommendationProducts(u, c);
|
|
543
646
|
} catch {
|
|
544
|
-
|
|
647
|
+
m = [];
|
|
545
648
|
}
|
|
546
|
-
this._setBlockProducts(t,
|
|
649
|
+
g.get(t) === s && this._setBlockProducts(t, m, i);
|
|
547
650
|
},
|
|
548
651
|
async _doFetchStrategyProducts(t) {
|
|
549
|
-
const e = (
|
|
550
|
-
|
|
652
|
+
const e = (g.get(t) ?? 0) + 1;
|
|
653
|
+
g.set(t, e);
|
|
551
654
|
const { recommendationConfigs: r } = this.blockStates[t];
|
|
552
655
|
let n = [], s = parseInt(r.size) || 6;
|
|
553
656
|
try {
|
|
@@ -557,25 +660,25 @@ const z = () => ({
|
|
|
557
660
|
);
|
|
558
661
|
if (i) {
|
|
559
662
|
s = parseInt(String(i.size ?? "")) || s;
|
|
560
|
-
const o = r.currencySettings.value,
|
|
561
|
-
|
|
663
|
+
const o = r.currencySettings.value, u = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), c = u.indexOf("?"), m = c === -1 ? u : u.slice(0, c), h = (c === -1 ? "" : u.slice(c + 1)).split("&").filter((C) => C && !C.startsWith("user_id=")), d = h.length ? `${m}?${h.join("&")}` : m;
|
|
664
|
+
d && (n = await f.fetchRecommendationProductsByUrl(d));
|
|
562
665
|
}
|
|
563
666
|
} catch {
|
|
564
667
|
n = [];
|
|
565
668
|
}
|
|
566
|
-
|
|
669
|
+
g.get(t) === e && this._setBlockProducts(t, n, s);
|
|
567
670
|
},
|
|
568
671
|
_setBlockProducts(t, e, r) {
|
|
569
672
|
if (!this.blockStates[t])
|
|
570
673
|
return;
|
|
571
|
-
const n = e.length > 0 ? e :
|
|
674
|
+
const n = e.length > 0 ? e : F(r);
|
|
572
675
|
this.blockStates[t].recommendationProducts = [
|
|
573
676
|
...n.slice(0, r),
|
|
574
|
-
...
|
|
677
|
+
...F(Math.max(0, r - n.length))
|
|
575
678
|
];
|
|
576
679
|
}
|
|
577
680
|
}
|
|
578
681
|
});
|
|
579
682
|
export {
|
|
580
|
-
|
|
683
|
+
it as useRecommendationExtensionStore
|
|
581
684
|
};
|
|
@@ -1,22 +1,32 @@
|
|
|
1
|
-
import { ModificationDescription as
|
|
2
|
-
import { RECOMMENDATION_ID_ATTR as
|
|
3
|
-
import { isPartnerManagedBlock as
|
|
4
|
-
import { RecommendationConfigService as
|
|
5
|
-
let
|
|
6
|
-
function
|
|
7
|
-
|
|
1
|
+
import { ModificationDescription as g } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
|
|
2
|
+
import { RECOMMENDATION_ID_ATTR as s } from "../constants/selectors.js";
|
|
3
|
+
import { isPartnerManagedBlock as p, updateProductContentInPlace as C, regenerateProductRowsWithStyles as N } from "../controls/main/utils.js";
|
|
4
|
+
import { RecommendationConfigService as m } from "../services/configService.js";
|
|
5
|
+
let o = null;
|
|
6
|
+
function D(n) {
|
|
7
|
+
o = n;
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
9
|
+
function P(n, r, c, l) {
|
|
10
|
+
const i = o == null ? void 0 : o.getDocumentRootHtmlNode();
|
|
11
|
+
if (!o || !i || !("querySelector" in i))
|
|
12
12
|
return !1;
|
|
13
|
-
const
|
|
14
|
-
if (!
|
|
13
|
+
const e = i.querySelector(`[${s}="${n}"]`);
|
|
14
|
+
if (!e)
|
|
15
15
|
return !1;
|
|
16
|
-
const o = e.
|
|
17
|
-
|
|
16
|
+
const t = o.getDocumentModifier(), a = m.getConfig(e), d = Object.keys(r).some((u) => JSON.stringify(a[u]) !== JSON.stringify(r[u]));
|
|
17
|
+
if (m.stageConfig(t, e, r), p(e))
|
|
18
|
+
return t.apply(new g(l)), !0;
|
|
19
|
+
let f = !1;
|
|
20
|
+
return C({
|
|
21
|
+
currentNode: e,
|
|
22
|
+
documentModifier: t,
|
|
23
|
+
products: c,
|
|
24
|
+
onApplied: () => {
|
|
25
|
+
f = !0;
|
|
26
|
+
}
|
|
27
|
+
}) || (f = N({ currentNode: e, documentModifier: t, products: c })), !f && d && t.apply(new g(l)), !0;
|
|
18
28
|
}
|
|
19
29
|
export {
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
P as commitBlockChangeOutsidePanel,
|
|
31
|
+
D as registerNodeConfigWriter
|
|
22
32
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useRecommendationExtensionStore as s } from "../store/recommendation.js";
|
|
2
|
+
import { commitBlockChangeOutsidePanel as d } from "./nodeConfigWriter.js";
|
|
3
|
+
async function u(e, n) {
|
|
4
|
+
const t = s(), o = t.blockStates[e];
|
|
5
|
+
if (!o)
|
|
6
|
+
return !1;
|
|
7
|
+
const { strategyId: a, strategy: c, size: i } = o.recommendationConfigs;
|
|
8
|
+
if (t.currentRecommendationId === e)
|
|
9
|
+
try {
|
|
10
|
+
await t.fetchRecommendationProducts();
|
|
11
|
+
} catch (m) {
|
|
12
|
+
console.error("[recommendation] product refetch after a strategy change failed", m);
|
|
13
|
+
}
|
|
14
|
+
const r = d(
|
|
15
|
+
e,
|
|
16
|
+
{ strategyId: a, strategy: c, size: i },
|
|
17
|
+
o.recommendationProducts,
|
|
18
|
+
n
|
|
19
|
+
);
|
|
20
|
+
return r || console.error("[recommendation] strategy change did not reach a node", { blockId: e }), r;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
u as commitStrategyChange
|
|
24
|
+
};
|
|
@@ -1,34 +1,43 @@
|
|
|
1
|
-
const
|
|
1
|
+
const o = "strategy", u = [
|
|
2
2
|
{
|
|
3
3
|
key: "locale",
|
|
4
4
|
getValue: (e) => e.language,
|
|
5
5
|
getAvailableOptions: (e) => Object.keys(e.languages)
|
|
6
6
|
},
|
|
7
|
+
{
|
|
8
|
+
// A block feeds from either an assigned strategy (RRS) or an algorithm (legacy).
|
|
9
|
+
// Only a fresh RRS block has neither, and its feed URL would otherwise be built
|
|
10
|
+
// from an empty algorithm path.
|
|
11
|
+
key: o,
|
|
12
|
+
getValue: (e) => e.strategyId || e.strategy
|
|
13
|
+
},
|
|
7
14
|
{
|
|
8
15
|
key: "currency",
|
|
9
16
|
getValue: (e) => e.currencySettings.value,
|
|
10
17
|
getAvailableOptions: (e) => e.currencyList.map((n) => n.text),
|
|
11
18
|
canBeEmpty: !0
|
|
12
19
|
}
|
|
13
|
-
],
|
|
14
|
-
function c(e, n, i =
|
|
20
|
+
], l = "newsletter.recommendation-fill-required-fields", g = "newsletter.recommendation-assign-strategy";
|
|
21
|
+
function c(e, n, i = u) {
|
|
15
22
|
return i.filter((t) => {
|
|
16
|
-
var
|
|
23
|
+
var s;
|
|
17
24
|
if (t.condition && !t.condition(e))
|
|
18
25
|
return !1;
|
|
19
|
-
const
|
|
20
|
-
if (t.canBeEmpty && (
|
|
26
|
+
const a = (s = t.getAvailableOptions) == null ? void 0 : s.call(t, n);
|
|
27
|
+
if (t.canBeEmpty && (a == null ? void 0 : a.length) === 0)
|
|
21
28
|
return !1;
|
|
22
|
-
const
|
|
23
|
-
return
|
|
29
|
+
const r = t.getValue(e);
|
|
30
|
+
return r ? a !== void 0 && !a.includes(r) : !0;
|
|
24
31
|
}).map((t) => t.key);
|
|
25
32
|
}
|
|
26
|
-
function
|
|
33
|
+
function y(e, n) {
|
|
27
34
|
return c(e, n).length === 0;
|
|
28
35
|
}
|
|
29
36
|
export {
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
u as REQUIRED_RECOMMENDATION_FIELDS,
|
|
38
|
+
g as RecommendationAssignStrategyKey,
|
|
39
|
+
l as RecommendationRequiredFieldsKey,
|
|
40
|
+
o as STRATEGY_FIELD_KEY,
|
|
32
41
|
c as getInvalidFields,
|
|
33
|
-
|
|
42
|
+
y as isConfigValid
|
|
34
43
|
};
|