@useinsider/guido 3.19.0-beta.f02eaa1 → 3.19.0-beta.f66e9de
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 +8 -8
- package/dist/components/Guido.vue2.js +44 -43
- package/dist/components/organisms/base/AuthErrorModal.vue.js +18 -0
- package/dist/components/organisms/base/AuthErrorModal.vue2.js +42 -0
- package/dist/components/organisms/base/Toaster.vue2.js +13 -12
- package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +9 -7
- package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +69 -52
- package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +28 -44
- package/dist/components/organisms/header/EditorActions.vue.js +4 -4
- package/dist/components/organisms/header/EditorActions.vue2.js +23 -25
- package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.js +3 -3
- package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue2.js +21 -20
- package/dist/composables/useActionsApi.js +52 -47
- package/dist/composables/useAuthError.js +54 -0
- package/dist/composables/useCodeEditorApi.js +23 -9
- package/dist/composables/useCustomInterfaceAppearance.js +61 -51
- package/dist/composables/useHtmlValidator.js +87 -82
- package/dist/composables/useHttp.js +54 -44
- package/dist/composables/useRecommendation.js +40 -35
- package/dist/composables/useStripo.js +30 -30
- package/dist/composables/useToaster.js +14 -20
- package/dist/composables/useTranslations.js +3 -2
- package/dist/composables/useVersionHistoryApi.js +33 -24
- package/dist/enums/extensions/recommendationBlock.js +32 -25
- 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 +63 -58
- package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +109 -78
- package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +96 -97
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +209 -92
- package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js +1 -1
- 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/base/AuthErrorModal.vue.d.ts +2 -0
- package/dist/src/components/wrappers/WpModal.vue.d.ts +1 -1
- package/dist/src/composables/useAuthError.d.ts +13 -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 +8 -0
- package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +7 -6
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +92 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.d.ts +1 -1
- 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/utils/stripoEditorApi.d.ts +4 -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/unsubscribe.js +3 -3
- package/dist/utils/stripoEditorApi.js +11 -0
- package/dist/utils/templatePreparation.js +55 -55
- package/package.json +2 -1
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useToaster as D } from "../../../../composables/useToaster.js";
|
|
2
|
+
import { useTranslations as L } from "../../../../composables/useTranslations.js";
|
|
3
|
+
import { getRecommendationFeedSourceMaps as F, strategyEditPath as U, URLS as E, getOperatorOptions as N, PriceAttributes as M } from "../../../../enums/extensions/recommendationBlock.js";
|
|
4
|
+
import { ToasterTypeOptions as A } from "../../../../enums/toaster.js";
|
|
5
|
+
import { useRecommendationApi as T } from "../../../../services/recommendationApi.js";
|
|
3
6
|
import { useConfigStore as k } from "../../../../stores/config.js";
|
|
4
|
-
import { defineStore as
|
|
7
|
+
import { defineStore as B } from "pinia";
|
|
5
8
|
import "../constants/selectors.js";
|
|
6
|
-
import { DEFAULT_MOBILE_CARDS_IN_ROW as
|
|
7
|
-
import { CURRENT_CONFIG_VERSION as
|
|
8
|
-
import { getDefaultProducts as
|
|
9
|
-
import { generateCompleteFilterQuery as
|
|
10
|
-
import { isLegacyStrategyConfig as
|
|
11
|
-
import { getPartnerRecommendationParams as
|
|
12
|
-
import { sanitizeProductIds as
|
|
13
|
-
import { isFilterValid as
|
|
14
|
-
import { isConfigValid as
|
|
15
|
-
const m =
|
|
16
|
-
let
|
|
17
|
-
const
|
|
18
|
-
|
|
9
|
+
import { DEFAULT_MOBILE_CARDS_IN_ROW as O, DEFAULT_CARDS_IN_ROW as z } from "../constants/layout.js";
|
|
10
|
+
import { CURRENT_CONFIG_VERSION as C, DEFAULT_NODE_CONFIG as x, EXCLUDED_ALGORITHM_IDS as V } from "../constants/defaultConfig.js";
|
|
11
|
+
import { getDefaultProducts as w } from "../templates/utils.js";
|
|
12
|
+
import { generateCompleteFilterQuery as P } from "../utils/filterUtil.js";
|
|
13
|
+
import { isLegacyStrategyConfig as j } from "../utils/legacyStrategyMigration.js";
|
|
14
|
+
import { getPartnerRecommendationParams as $ } from "../utils/partnerCustomizations.js";
|
|
15
|
+
import { sanitizeProductIds as q } from "../utils/productIds.js";
|
|
16
|
+
import { isFilterValid as K } from "../validation/filterSchema.js";
|
|
17
|
+
import { getInvalidFields as H, isConfigValid as J } from "../validation/requiredFields.js";
|
|
18
|
+
const m = T();
|
|
19
|
+
let S = null, p = null, y = null, I = "", R = null, b = 0;
|
|
20
|
+
const g = /* @__PURE__ */ new Map();
|
|
21
|
+
let l = null;
|
|
22
|
+
function _() {
|
|
19
23
|
return {
|
|
20
|
-
cardsInRow:
|
|
24
|
+
cardsInRow: z,
|
|
21
25
|
currencySettings: {
|
|
22
26
|
name: "USD",
|
|
23
27
|
value: "USD",
|
|
@@ -31,15 +35,19 @@ function G() {
|
|
|
31
35
|
productIds: [],
|
|
32
36
|
id: 1,
|
|
33
37
|
language: "en_US",
|
|
34
|
-
mobileCardsInRow:
|
|
38
|
+
mobileCardsInRow: O,
|
|
35
39
|
mobileLayoutEnabled: !1,
|
|
36
40
|
orientation: "grid",
|
|
37
41
|
recommendedProducts: [],
|
|
38
42
|
sendProductRequestFlag: !1,
|
|
39
43
|
shuffleProducts: !1,
|
|
40
|
-
|
|
44
|
+
// With RRS on, a block that has not been synced yet must not claim an algorithm:
|
|
45
|
+
// that default is what reached buildCampaignUrl and baked a most-popular feed URL
|
|
46
|
+
// for a block the user never configured (SD-149916). Legacy blocks still get their real
|
|
47
|
+
// algorithm from the node-config sync or the save-time seed.
|
|
48
|
+
strategy: k().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
|
|
41
49
|
strategyId: "",
|
|
42
|
-
nodeConfigVersion:
|
|
50
|
+
nodeConfigVersion: C,
|
|
43
51
|
textTrimming: !0,
|
|
44
52
|
unresponsive: !1,
|
|
45
53
|
size: "6",
|
|
@@ -48,9 +56,9 @@ function G() {
|
|
|
48
56
|
priceHideIfSameAsDiscounted: !1
|
|
49
57
|
};
|
|
50
58
|
}
|
|
51
|
-
function
|
|
59
|
+
function G() {
|
|
52
60
|
return {
|
|
53
|
-
recommendationConfigs:
|
|
61
|
+
recommendationConfigs: _(),
|
|
54
62
|
recommendationProducts: [],
|
|
55
63
|
filterStatus: !1,
|
|
56
64
|
filterSelectionDrawerStatus: !1,
|
|
@@ -64,7 +72,7 @@ function w() {
|
|
|
64
72
|
legacyConversionLoading: !1
|
|
65
73
|
};
|
|
66
74
|
}
|
|
67
|
-
const
|
|
75
|
+
const Q = () => ({
|
|
68
76
|
recommendationCampaignUrls: {},
|
|
69
77
|
activePredictiveAlgorithms: [],
|
|
70
78
|
languages: {},
|
|
@@ -73,11 +81,14 @@ const E = () => ({
|
|
|
73
81
|
strategies: [],
|
|
74
82
|
strategiesLoading: !1,
|
|
75
83
|
strategyUrlTemplates: {},
|
|
84
|
+
strategiesFetched: !1,
|
|
85
|
+
strategiesSettled: !1,
|
|
76
86
|
blockStates: {},
|
|
77
87
|
currentRecommendationId: null,
|
|
88
|
+
strategyPanelOpen: !1,
|
|
78
89
|
configVersion: 0
|
|
79
|
-
}),
|
|
80
|
-
state: () =>
|
|
90
|
+
}), ft = B("guidoRecommendationExtension", {
|
|
91
|
+
state: () => Q(),
|
|
81
92
|
getters: {
|
|
82
93
|
// ====================================================================
|
|
83
94
|
// Proxy Getters — Backward Compatible Access to Current Block State
|
|
@@ -87,7 +98,7 @@ const E = () => ({
|
|
|
87
98
|
* This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
|
|
88
99
|
*/
|
|
89
100
|
recommendationConfigs(t) {
|
|
90
|
-
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs :
|
|
101
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : _();
|
|
91
102
|
},
|
|
92
103
|
/**
|
|
93
104
|
* Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
|
|
@@ -108,10 +119,10 @@ const E = () => ({
|
|
|
108
119
|
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].removeStrategyModalStatus : !1;
|
|
109
120
|
},
|
|
110
121
|
isLegacyStrategyBlock() {
|
|
111
|
-
return k().isFeatureEnabled("reusableRecommendationStrategy") &&
|
|
122
|
+
return k().isFeatureEnabled("reusableRecommendationStrategy") && j(this.recommendationConfigs);
|
|
112
123
|
},
|
|
113
124
|
legacyConversionLoading(t) {
|
|
114
|
-
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].legacyConversionLoading : !1;
|
|
125
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].legacyConversionLoading ?? !1 : !1;
|
|
115
126
|
},
|
|
116
127
|
legacyStrategyUpdatedModalStatus(t) {
|
|
117
128
|
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].legacyStrategyUpdatedModalStatus : !1;
|
|
@@ -151,8 +162,8 @@ const E = () => ({
|
|
|
151
162
|
return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
|
|
152
163
|
},
|
|
153
164
|
getActivePredictiveAlgorithms: (t) => {
|
|
154
|
-
const e =
|
|
155
|
-
return t.activePredictiveAlgorithms.filter((n) => !
|
|
165
|
+
const e = F(), r = [];
|
|
166
|
+
return t.activePredictiveAlgorithms.filter((n) => !V.includes(n)).forEach((n) => {
|
|
156
167
|
r.push(...e.filter((s) => s.id === n));
|
|
157
168
|
}), r.map((n) => ({
|
|
158
169
|
text: n.name,
|
|
@@ -170,7 +181,7 @@ const E = () => ({
|
|
|
170
181
|
getFilterList() {
|
|
171
182
|
return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
|
|
172
183
|
let e;
|
|
173
|
-
return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` :
|
|
184
|
+
return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : M.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
|
|
174
185
|
text: t.displayName,
|
|
175
186
|
value: e,
|
|
176
187
|
type: t.attributeType
|
|
@@ -192,7 +203,7 @@ const E = () => ({
|
|
|
192
203
|
setCurrentBlock(t) {
|
|
193
204
|
this.blockStates[t] || (this.blockStates = {
|
|
194
205
|
...this.blockStates,
|
|
195
|
-
[t]:
|
|
206
|
+
[t]: G()
|
|
196
207
|
}), this.currentRecommendationId = t;
|
|
197
208
|
},
|
|
198
209
|
/**
|
|
@@ -214,12 +225,16 @@ const E = () => ({
|
|
|
214
225
|
/**
|
|
215
226
|
* Deep-clones a block's state and mirrors its campaign URL under a new id.
|
|
216
227
|
* Invoked from `onCreated` when a duplicate is detected (SD-142352).
|
|
228
|
+
*
|
|
229
|
+
* The clone keeps the source's data but drops its "already fetched / already
|
|
230
|
+
* synced" claims for strategy blocks, so it fetches again like a fresh drop
|
|
231
|
+
* (SD-149916).
|
|
217
232
|
*/
|
|
218
233
|
cloneBlockState(t, e) {
|
|
219
234
|
const r = this.blockStates[t];
|
|
220
235
|
if (r) {
|
|
221
236
|
const o = structuredClone(r);
|
|
222
|
-
o.recommendationConfigs.id = e, this.blockStates = {
|
|
237
|
+
o.recommendationConfigs.id = e, o.recommendationConfigs.strategyId && k().isFeatureEnabled("reusableRecommendationStrategy") && (o.isInitialized = !1, o.syncedConfigFingerprint = null), this.blockStates = {
|
|
223
238
|
...this.blockStates,
|
|
224
239
|
[e]: o
|
|
225
240
|
};
|
|
@@ -252,7 +267,7 @@ const E = () => ({
|
|
|
252
267
|
* `isInitialized`) alone.
|
|
253
268
|
*/
|
|
254
269
|
seedBlockUrlConfig(t, e) {
|
|
255
|
-
const r = (o,
|
|
270
|
+
const r = (o, a) => o === "." || o === "," || o === " " ? o : a, n = {
|
|
256
271
|
name: e.currencyCode,
|
|
257
272
|
value: e.currencyCode,
|
|
258
273
|
symbol: e.currencySymbol,
|
|
@@ -260,7 +275,7 @@ const E = () => ({
|
|
|
260
275
|
decimalCount: String(e.currencyDecimalCount),
|
|
261
276
|
decimalSeparator: r(e.currencyDecimalSeparator, ","),
|
|
262
277
|
thousandSeparator: r(e.currencyThousandSeparator, ".")
|
|
263
|
-
}, s = !this.blockStates[t], i = s ?
|
|
278
|
+
}, s = !this.blockStates[t], i = s ? G() : this.blockStates[t];
|
|
264
279
|
i.recommendationConfigs = {
|
|
265
280
|
...i.recommendationConfigs,
|
|
266
281
|
strategy: e.strategy,
|
|
@@ -299,7 +314,7 @@ const E = () => ({
|
|
|
299
314
|
* Creates a filter with the first available attribute and operator pre-selected.
|
|
300
315
|
*/
|
|
301
316
|
createDefaultFilter(t, e) {
|
|
302
|
-
const [r] = this.getFilterList, [n] =
|
|
317
|
+
const [r] = this.getFilterList, [n] = N(r == null ? void 0 : r.type);
|
|
303
318
|
return {
|
|
304
319
|
type: "standardFilter",
|
|
305
320
|
attribute: (r == null ? void 0 : r.value) ?? "",
|
|
@@ -365,24 +380,106 @@ const E = () => ({
|
|
|
365
380
|
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !1);
|
|
366
381
|
},
|
|
367
382
|
async fetchStrategies() {
|
|
368
|
-
|
|
369
|
-
|
|
383
|
+
for (; R; )
|
|
384
|
+
try {
|
|
385
|
+
await R;
|
|
386
|
+
} catch {
|
|
387
|
+
}
|
|
388
|
+
if (this.strategiesFetched)
|
|
370
389
|
return;
|
|
371
|
-
|
|
372
|
-
S = (async () => {
|
|
390
|
+
const t = b, e = (async () => {
|
|
373
391
|
this.strategiesLoading = !0;
|
|
374
392
|
try {
|
|
375
|
-
|
|
393
|
+
const r = await m.fetchRecommendationStrategies();
|
|
394
|
+
if (t !== b)
|
|
395
|
+
return;
|
|
396
|
+
this.strategies = r, this.strategiesFetched = !0;
|
|
376
397
|
} finally {
|
|
377
|
-
this.strategiesLoading = !1;
|
|
398
|
+
this.strategiesLoading = !1, t === b && (this.strategiesSettled = !0);
|
|
378
399
|
}
|
|
379
400
|
})();
|
|
401
|
+
R = e;
|
|
380
402
|
try {
|
|
381
|
-
await
|
|
403
|
+
await e;
|
|
382
404
|
} finally {
|
|
383
|
-
|
|
405
|
+
R = null;
|
|
384
406
|
}
|
|
385
407
|
},
|
|
408
|
+
/**
|
|
409
|
+
* Clears what a block inherited from the id it arrived holding, so the panel fetches for it.
|
|
410
|
+
* A re-dropped saved module can keep the source's id — and its "already fetched" entry —
|
|
411
|
+
* while the strategy's product count has moved on (SD-149916).
|
|
412
|
+
*/
|
|
413
|
+
resetArrivedBlock(t) {
|
|
414
|
+
const e = this.blockStates[t];
|
|
415
|
+
e != null && e.recommendationConfigs.strategyId && (e.isInitialized = !1, e.syncedConfigFingerprint = null, this.invalidateStrategies(), this.fetchStrategies().catch(() => {
|
|
416
|
+
}));
|
|
417
|
+
},
|
|
418
|
+
invalidateStrategies() {
|
|
419
|
+
this.strategiesFetched = !1, this.strategiesSettled = !1, b += 1;
|
|
420
|
+
},
|
|
421
|
+
/**
|
|
422
|
+
* Opens a Smart Recommender strategy page in a new tab and refreshes the list every time
|
|
423
|
+
* the user comes back. `visibilitychange` only: a `focus` listener would also fire when
|
|
424
|
+
* focus merely returns from the Stripo canvas iframe to the panel, refetching and
|
|
425
|
+
* regenerating the canvas while the user never left the editor (SD-149916).
|
|
426
|
+
*
|
|
427
|
+
* Deliberately NOT one-shot: the strategy tab stays open, so a user can create a second
|
|
428
|
+
* strategy in it and come back again — a listener that disarmed on the first return would
|
|
429
|
+
* leave that one missing from the list. It disarms on the first return AFTER that tab is
|
|
430
|
+
* closed instead, so an ordinary alt-tab later in the session costs nothing. Armed only
|
|
431
|
+
* once the user actually opens a strategy page, so anyone who never does pays nothing.
|
|
432
|
+
* @returns whether a tab actually opened, so callers can skip work that assumes one did
|
|
433
|
+
*/
|
|
434
|
+
_armStrategyPageReturn(t) {
|
|
435
|
+
const e = window.open(t, "_blank");
|
|
436
|
+
if (!e)
|
|
437
|
+
return D().showToaster({
|
|
438
|
+
type: A.Error,
|
|
439
|
+
message: L()("discovery-strategy.error-description")
|
|
440
|
+
}), !1;
|
|
441
|
+
e.opener = null, l == null || l();
|
|
442
|
+
let r = !1;
|
|
443
|
+
const n = () => {
|
|
444
|
+
document.hidden || r || (e != null && e.closed && (l == null || l()), r = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
|
|
445
|
+
}).finally(() => {
|
|
446
|
+
r = !1;
|
|
447
|
+
}));
|
|
448
|
+
};
|
|
449
|
+
return document.addEventListener("visibilitychange", n), l = () => {
|
|
450
|
+
document.removeEventListener("visibilitychange", n), l = null;
|
|
451
|
+
}, !0;
|
|
452
|
+
},
|
|
453
|
+
/**
|
|
454
|
+
* Reloads the products of the block the user is on, so an edit made in the strategy tab
|
|
455
|
+
* lands on the canvas instead of waiting for the assign drawer to be reopened.
|
|
456
|
+
*
|
|
457
|
+
* Only the selected block: the canvas regeneration is driven off the active block's
|
|
458
|
+
* product array, so refreshing an unselected block would fetch without redrawing it.
|
|
459
|
+
*/
|
|
460
|
+
async _refreshAssignedStrategyProducts() {
|
|
461
|
+
var e;
|
|
462
|
+
const t = this.currentRecommendationId;
|
|
463
|
+
t === null || !((e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId) || await this.fetchRecommendationProducts();
|
|
464
|
+
},
|
|
465
|
+
/** Lets the editor tear the return listeners down when it unmounts. */
|
|
466
|
+
disposeStrategyPageReturn() {
|
|
467
|
+
l == null || l();
|
|
468
|
+
},
|
|
469
|
+
/**
|
|
470
|
+
* Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
|
|
471
|
+
* new strategy is offered; the user picks it themselves.
|
|
472
|
+
*/
|
|
473
|
+
openStrategyCreation() {
|
|
474
|
+
return this._armStrategyPageReturn(E.RECOMMENDATION_STRATEGY_CREATE_PATH);
|
|
475
|
+
},
|
|
476
|
+
/**
|
|
477
|
+
* Opens the strategy's edit page. Every return refreshes the list so the block reflects the
|
|
478
|
+
* edited configuration.
|
|
479
|
+
*/
|
|
480
|
+
openStrategyEdit(t) {
|
|
481
|
+
return this._armStrategyPageReturn(U(t));
|
|
482
|
+
},
|
|
386
483
|
async resolveStrategyUrlTemplate(t, e) {
|
|
387
484
|
const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
|
|
388
485
|
if (s)
|
|
@@ -415,7 +512,7 @@ const E = () => ({
|
|
|
415
512
|
return i && (i.recommendationConfigs = {
|
|
416
513
|
...i.recommendationConfigs,
|
|
417
514
|
strategyId: n,
|
|
418
|
-
nodeConfigVersion:
|
|
515
|
+
nodeConfigVersion: C,
|
|
419
516
|
...(s == null ? void 0 : s.size) !== void 0 ? { size: String(s.size) } : {}
|
|
420
517
|
}), this.currentRecommendationId === r && this.configVersion++, n;
|
|
421
518
|
} finally {
|
|
@@ -434,8 +531,9 @@ const E = () => ({
|
|
|
434
531
|
removeStrategy() {
|
|
435
532
|
this.patchCurrentBlockConfig({
|
|
436
533
|
strategyId: "",
|
|
437
|
-
|
|
438
|
-
|
|
534
|
+
strategy: "",
|
|
535
|
+
size: x.size,
|
|
536
|
+
nodeConfigVersion: C
|
|
439
537
|
});
|
|
440
538
|
},
|
|
441
539
|
// ====================================================================
|
|
@@ -443,11 +541,11 @@ const E = () => ({
|
|
|
443
541
|
// ====================================================================
|
|
444
542
|
async fetchRecommendationCreateData() {
|
|
445
543
|
if (!this.activePredictiveAlgorithms.length) {
|
|
446
|
-
if (
|
|
447
|
-
await
|
|
544
|
+
if (S) {
|
|
545
|
+
await S;
|
|
448
546
|
return;
|
|
449
547
|
}
|
|
450
|
-
|
|
548
|
+
S = (async () => {
|
|
451
549
|
const {
|
|
452
550
|
activePredictiveAlgorithms: t,
|
|
453
551
|
languages: e,
|
|
@@ -460,26 +558,26 @@ const E = () => ({
|
|
|
460
558
|
this.currencyList = r;
|
|
461
559
|
})();
|
|
462
560
|
try {
|
|
463
|
-
await
|
|
561
|
+
await S;
|
|
464
562
|
} finally {
|
|
465
|
-
|
|
563
|
+
S = null;
|
|
466
564
|
}
|
|
467
565
|
}
|
|
468
566
|
},
|
|
469
567
|
async fetchRecommendationFilters() {
|
|
470
568
|
if (!Object.keys(this.filterList).length) {
|
|
471
|
-
if (
|
|
472
|
-
await
|
|
569
|
+
if (p) {
|
|
570
|
+
await p;
|
|
473
571
|
return;
|
|
474
572
|
}
|
|
475
|
-
|
|
573
|
+
p = (async () => {
|
|
476
574
|
const t = await m.fetchRecommendationFilters();
|
|
477
575
|
this.filterList = t;
|
|
478
576
|
})();
|
|
479
577
|
try {
|
|
480
|
-
await
|
|
578
|
+
await p;
|
|
481
579
|
} finally {
|
|
482
|
-
|
|
580
|
+
p = null;
|
|
483
581
|
}
|
|
484
582
|
}
|
|
485
583
|
},
|
|
@@ -508,7 +606,7 @@ const E = () => ({
|
|
|
508
606
|
const n = [...e.recommendationConfigs.filters];
|
|
509
607
|
n[r] = {
|
|
510
608
|
...t,
|
|
511
|
-
isValid:
|
|
609
|
+
isValid: K(t)
|
|
512
610
|
}, e.recommendationConfigs.filters = n;
|
|
513
611
|
}
|
|
514
612
|
},
|
|
@@ -522,10 +620,10 @@ const E = () => ({
|
|
|
522
620
|
let i = 1;
|
|
523
621
|
n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
|
|
524
622
|
} else {
|
|
525
|
-
const i = [...new Set(n.map((
|
|
526
|
-
n = n.map((
|
|
527
|
-
...
|
|
528
|
-
filterGroup: o.get(
|
|
623
|
+
const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, d) => a - d), o = new Map(i.map((a, d) => [a, d + 1]));
|
|
624
|
+
n = n.map((a) => ({
|
|
625
|
+
...a,
|
|
626
|
+
filterGroup: o.get(a.filterGroup) ?? a.filterGroup
|
|
529
627
|
}));
|
|
530
628
|
}
|
|
531
629
|
e.recommendationConfigs.filters = n;
|
|
@@ -546,7 +644,7 @@ const E = () => ({
|
|
|
546
644
|
}), e.recommendationConfigs.filters = r;
|
|
547
645
|
},
|
|
548
646
|
generateFilterQuery() {
|
|
549
|
-
return
|
|
647
|
+
return P(this.recommendationConfigs.filters);
|
|
550
648
|
},
|
|
551
649
|
/**
|
|
552
650
|
* Validation-only check invoked at save-CTA time. Defined as an action
|
|
@@ -554,33 +652,52 @@ const E = () => ({
|
|
|
554
652
|
* every block's recommendationConfigs across user edits.
|
|
555
653
|
*/
|
|
556
654
|
hasInvalidBlock() {
|
|
557
|
-
return Object.values(this.blockStates).some((t) => !
|
|
655
|
+
return Object.values(this.blockStates).some((t) => !J(t.recommendationConfigs, this));
|
|
656
|
+
},
|
|
657
|
+
/**
|
|
658
|
+
* Which required fields are missing across the design's blocks, so the save
|
|
659
|
+
* toaster can name the cause instead of always showing the generic message.
|
|
660
|
+
*/
|
|
661
|
+
invalidBlockFields() {
|
|
662
|
+
return Object.values(this.blockStates).flatMap((t) => H(t.recommendationConfigs, this));
|
|
558
663
|
},
|
|
559
664
|
// ====================================================================
|
|
560
665
|
// Per-Block Product Fetching
|
|
561
666
|
// ====================================================================
|
|
562
667
|
async fetchRecommendationProducts() {
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
668
|
+
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
669
|
+
return;
|
|
670
|
+
const t = [
|
|
671
|
+
this.currentRecommendationId,
|
|
672
|
+
this.configVersion,
|
|
673
|
+
b
|
|
674
|
+
].join(":");
|
|
675
|
+
if (y && I === t) {
|
|
676
|
+
await y;
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
I = t, y = this._doFetchProducts();
|
|
680
|
+
try {
|
|
681
|
+
await y;
|
|
682
|
+
} finally {
|
|
683
|
+
I === t && (y = null, I = "");
|
|
574
684
|
}
|
|
575
685
|
},
|
|
576
686
|
async _doFetchProducts() {
|
|
577
|
-
var
|
|
687
|
+
var h;
|
|
578
688
|
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = k();
|
|
579
689
|
if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
|
|
580
690
|
await this._doFetchStrategyProducts(t);
|
|
581
691
|
return;
|
|
582
692
|
}
|
|
583
|
-
const s =
|
|
693
|
+
const s = (g.get(t) ?? 0) + 1;
|
|
694
|
+
g.set(t, s);
|
|
695
|
+
const i = parseInt(r.size) || 6;
|
|
696
|
+
if (!r.strategy) {
|
|
697
|
+
this._setBlockProducts(t, [], i);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const o = r.filters.filter((f) => f.isValid), a = P(o), d = ((h = F().find((f) => f.key === r.strategy)) == null ? void 0 : h.path) || "", c = {
|
|
584
701
|
locale: r.language,
|
|
585
702
|
currency: r.currencySettings.value,
|
|
586
703
|
partnerName: n.partnerName,
|
|
@@ -588,17 +705,17 @@ const E = () => ({
|
|
|
588
705
|
details: !0,
|
|
589
706
|
campaignId: n.variationId
|
|
590
707
|
};
|
|
591
|
-
r.strategy === "manualMerchandising" ?
|
|
592
|
-
|
|
593
|
-
|
|
708
|
+
r.strategy === "manualMerchandising" ? c.productId = q(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(
|
|
709
|
+
c,
|
|
710
|
+
$(n.partnerName, r.strategy)
|
|
594
711
|
);
|
|
595
|
-
let
|
|
712
|
+
let u;
|
|
596
713
|
try {
|
|
597
|
-
|
|
714
|
+
u = await m.fetchRecommendationProducts(d, c);
|
|
598
715
|
} catch {
|
|
599
|
-
|
|
716
|
+
u = [];
|
|
600
717
|
}
|
|
601
|
-
this._setBlockProducts(t,
|
|
718
|
+
g.get(t) === s && this._setBlockProducts(t, u, i);
|
|
602
719
|
},
|
|
603
720
|
/**
|
|
604
721
|
* Block-scoped counterpart of `fetchRecommendationProducts`, which only ever serves the
|
|
@@ -610,8 +727,8 @@ const E = () => ({
|
|
|
610
727
|
(e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId && await this._doFetchStrategyProducts(t);
|
|
611
728
|
},
|
|
612
729
|
async _doFetchStrategyProducts(t) {
|
|
613
|
-
const e = (
|
|
614
|
-
|
|
730
|
+
const e = (g.get(t) ?? 0) + 1;
|
|
731
|
+
g.set(t, e);
|
|
615
732
|
const { recommendationConfigs: r } = this.blockStates[t];
|
|
616
733
|
let n = [], s = parseInt(r.size) || 6;
|
|
617
734
|
try {
|
|
@@ -621,25 +738,25 @@ const E = () => ({
|
|
|
621
738
|
);
|
|
622
739
|
if (i) {
|
|
623
740
|
s = parseInt(String(i.size ?? "")) || s;
|
|
624
|
-
const o = r.currencySettings.value,
|
|
625
|
-
|
|
741
|
+
const o = r.currencySettings.value, d = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), c = d.indexOf("?"), u = c === -1 ? d : d.slice(0, c), h = (c === -1 ? "" : d.slice(c + 1)).split("&").filter((v) => v && !v.startsWith("user_id=")), f = h.length ? `${u}?${h.join("&")}` : u;
|
|
742
|
+
f && (n = await m.fetchRecommendationProductsByUrl(f));
|
|
626
743
|
}
|
|
627
744
|
} catch {
|
|
628
745
|
n = [];
|
|
629
746
|
}
|
|
630
|
-
|
|
747
|
+
g.get(t) === e && this._setBlockProducts(t, n, s);
|
|
631
748
|
},
|
|
632
749
|
_setBlockProducts(t, e, r) {
|
|
633
750
|
if (!this.blockStates[t])
|
|
634
751
|
return;
|
|
635
|
-
const n = e.length > 0 ? e :
|
|
752
|
+
const n = e.length > 0 ? e : w(r);
|
|
636
753
|
this.blockStates[t].recommendationProducts = [
|
|
637
754
|
...n.slice(0, r),
|
|
638
|
-
...
|
|
755
|
+
...w(Math.max(0, r - n.length))
|
|
639
756
|
];
|
|
640
757
|
}
|
|
641
758
|
}
|
|
642
759
|
});
|
|
643
760
|
export {
|
|
644
|
-
|
|
761
|
+
ft as useRecommendationExtensionStore
|
|
645
762
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../constants/selectors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { RRS_CONFIG_VERSION as m } from "../constants/defaultConfig.js";
|
|
3
3
|
import { sanitizeProductIds as p } from "./productIds.js";
|
|
4
4
|
const d = "manualMerchandising";
|
|
5
5
|
function N({ strategyId: e, nodeConfigVersion: t }) {
|
|
@@ -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 d } from "../store/recommendation.js";
|
|
2
|
+
import { commitBlockChangeOutsidePanel as f } from "./nodeConfigWriter.js";
|
|
3
|
+
async function h(e, r) {
|
|
4
|
+
const t = d(), o = t.blockStates[e];
|
|
5
|
+
if (!o)
|
|
6
|
+
return !1;
|
|
7
|
+
const { strategyId: a, strategy: c, size: i, nodeConfigVersion: s } = 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 n = f(
|
|
15
|
+
e,
|
|
16
|
+
{ strategyId: a, strategy: c, size: i, configVersion: s },
|
|
17
|
+
o.recommendationProducts,
|
|
18
|
+
r
|
|
19
|
+
);
|
|
20
|
+
return n || console.error("[recommendation] strategy change did not reach a node", { blockId: e }), n;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
h as commitStrategyChange
|
|
24
|
+
};
|