@useinsider/guido 3.19.0-beta.f02eaa1 → 3.19.0-beta.f0d5980
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/@types/config/schemas.js +104 -91
- package/dist/components/Guido.vue.js +8 -8
- package/dist/components/Guido.vue2.js +86 -81
- 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/components/wrappers/WpModal.vue.js +1 -1
- package/dist/components/wrappers/WpModal.vue2.js +4 -3
- package/dist/composables/useActionsApi.js +52 -47
- package/dist/composables/useAuthError.js +52 -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 +64 -54
- package/dist/composables/useLegacyStrategyConversion.js +23 -19
- package/dist/composables/useRecommendation.js +40 -35
- package/dist/composables/useStripo.js +43 -39
- package/dist/composables/useToaster.js +18 -22
- package/dist/composables/useTranslations.js +3 -2
- package/dist/composables/useVersionHistoryApi.js +33 -24
- package/dist/composables/validators/useLiquidValidator.js +15 -15
- package/dist/config/migrator/index.js +14 -13
- package/dist/config/migrator/recommendationPartnerMigrator.js +50 -0
- package/dist/enums/extensions/recommendationBlock.js +43 -35
- package/dist/extensions/Blocks/Recommendation/block.js +65 -45
- package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +21 -20
- package/dist/extensions/Blocks/Recommendation/controls/main/index.js +70 -58
- package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +133 -79
- package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +96 -97
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +212 -94
- 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/partnerScope.js +25 -0
- 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/@types/config/schemas.d.ts +16 -0
- package/dist/src/components/organisms/base/AuthErrorModal.vue.d.ts +2 -0
- package/dist/src/components/wrappers/WpModal.vue.d.ts +5 -1
- package/dist/src/composables/useAuthError.d.ts +12 -0
- package/dist/src/composables/useConfig.d.ts +2 -0
- package/dist/src/composables/useHttp.d.ts +2 -0
- package/dist/src/config/migrator/index.d.ts +2 -1
- package/dist/src/config/migrator/recommendationPartnerMigrator.d.ts +19 -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/types/nodeConfig.d.ts +6 -0
- package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.d.ts +1 -1
- package/dist/src/extensions/Blocks/Recommendation/utils/partnerScope.d.ts +33 -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/config.d.ts +20 -0
- package/dist/src/utils/stripoEditorApi.d.ts +4 -0
- package/dist/static/styles/components/recommendation-strategy.css.js +43 -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 {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useToaster as _ } from "../../../../composables/useToaster.js";
|
|
2
|
+
import { useTranslations as L } from "../../../../composables/useTranslations.js";
|
|
3
|
+
import { getRecommendationFeedSourceMaps as F, strategyEditPath as U, strategyCreatePath as N, getOperatorOptions as M, PriceAttributes as E } from "../../../../enums/extensions/recommendationBlock.js";
|
|
4
|
+
import { ToasterTypeOptions as B } from "../../../../enums/toaster.js";
|
|
5
|
+
import { useRecommendationApi as z } from "../../../../services/recommendationApi.js";
|
|
6
|
+
import { useConfigStore as R } from "../../../../stores/config.js";
|
|
7
|
+
import { defineStore as A } 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 x } from "../constants/layout.js";
|
|
10
|
+
import { CURRENT_CONFIG_VERSION as C, DEFAULT_NODE_CONFIG as T, 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 = z();
|
|
19
|
+
let S = null, p = null, y = null, I = "", k = null, b = 0;
|
|
20
|
+
const g = /* @__PURE__ */ new Map();
|
|
21
|
+
let l = null;
|
|
22
|
+
function D() {
|
|
19
23
|
return {
|
|
20
|
-
cardsInRow:
|
|
24
|
+
cardsInRow: x,
|
|
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: R().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: D(),
|
|
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 = A("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 : D();
|
|
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
|
|
122
|
+
return R().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}` : E.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 && R().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] = M(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 (; k; )
|
|
384
|
+
try {
|
|
385
|
+
await k;
|
|
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
|
+
k = e;
|
|
380
402
|
try {
|
|
381
|
-
await
|
|
403
|
+
await e;
|
|
382
404
|
} finally {
|
|
383
|
-
|
|
405
|
+
k = 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 _().showToaster({
|
|
438
|
+
type: B.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(N());
|
|
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)
|
|
@@ -406,6 +503,7 @@ const E = () => ({
|
|
|
406
503
|
this.blockStates[r].legacyConversionLoading = !0;
|
|
407
504
|
try {
|
|
408
505
|
const n = await m.migrateLegacyStrategy(t, e);
|
|
506
|
+
this.invalidateStrategies();
|
|
409
507
|
try {
|
|
410
508
|
await this.fetchStrategies();
|
|
411
509
|
} catch (o) {
|
|
@@ -415,7 +513,7 @@ const E = () => ({
|
|
|
415
513
|
return i && (i.recommendationConfigs = {
|
|
416
514
|
...i.recommendationConfigs,
|
|
417
515
|
strategyId: n,
|
|
418
|
-
nodeConfigVersion:
|
|
516
|
+
nodeConfigVersion: C,
|
|
419
517
|
...(s == null ? void 0 : s.size) !== void 0 ? { size: String(s.size) } : {}
|
|
420
518
|
}), this.currentRecommendationId === r && this.configVersion++, n;
|
|
421
519
|
} finally {
|
|
@@ -434,8 +532,9 @@ const E = () => ({
|
|
|
434
532
|
removeStrategy() {
|
|
435
533
|
this.patchCurrentBlockConfig({
|
|
436
534
|
strategyId: "",
|
|
437
|
-
|
|
438
|
-
|
|
535
|
+
strategy: "",
|
|
536
|
+
size: T.size,
|
|
537
|
+
nodeConfigVersion: C
|
|
439
538
|
});
|
|
440
539
|
},
|
|
441
540
|
// ====================================================================
|
|
@@ -443,11 +542,11 @@ const E = () => ({
|
|
|
443
542
|
// ====================================================================
|
|
444
543
|
async fetchRecommendationCreateData() {
|
|
445
544
|
if (!this.activePredictiveAlgorithms.length) {
|
|
446
|
-
if (
|
|
447
|
-
await
|
|
545
|
+
if (S) {
|
|
546
|
+
await S;
|
|
448
547
|
return;
|
|
449
548
|
}
|
|
450
|
-
|
|
549
|
+
S = (async () => {
|
|
451
550
|
const {
|
|
452
551
|
activePredictiveAlgorithms: t,
|
|
453
552
|
languages: e,
|
|
@@ -460,26 +559,26 @@ const E = () => ({
|
|
|
460
559
|
this.currencyList = r;
|
|
461
560
|
})();
|
|
462
561
|
try {
|
|
463
|
-
await
|
|
562
|
+
await S;
|
|
464
563
|
} finally {
|
|
465
|
-
|
|
564
|
+
S = null;
|
|
466
565
|
}
|
|
467
566
|
}
|
|
468
567
|
},
|
|
469
568
|
async fetchRecommendationFilters() {
|
|
470
569
|
if (!Object.keys(this.filterList).length) {
|
|
471
|
-
if (
|
|
472
|
-
await
|
|
570
|
+
if (p) {
|
|
571
|
+
await p;
|
|
473
572
|
return;
|
|
474
573
|
}
|
|
475
|
-
|
|
574
|
+
p = (async () => {
|
|
476
575
|
const t = await m.fetchRecommendationFilters();
|
|
477
576
|
this.filterList = t;
|
|
478
577
|
})();
|
|
479
578
|
try {
|
|
480
|
-
await
|
|
579
|
+
await p;
|
|
481
580
|
} finally {
|
|
482
|
-
|
|
581
|
+
p = null;
|
|
483
582
|
}
|
|
484
583
|
}
|
|
485
584
|
},
|
|
@@ -508,7 +607,7 @@ const E = () => ({
|
|
|
508
607
|
const n = [...e.recommendationConfigs.filters];
|
|
509
608
|
n[r] = {
|
|
510
609
|
...t,
|
|
511
|
-
isValid:
|
|
610
|
+
isValid: K(t)
|
|
512
611
|
}, e.recommendationConfigs.filters = n;
|
|
513
612
|
}
|
|
514
613
|
},
|
|
@@ -522,10 +621,10 @@ const E = () => ({
|
|
|
522
621
|
let i = 1;
|
|
523
622
|
n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
|
|
524
623
|
} else {
|
|
525
|
-
const i = [...new Set(n.map((
|
|
526
|
-
n = n.map((
|
|
527
|
-
...
|
|
528
|
-
filterGroup: o.get(
|
|
624
|
+
const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, d) => a - d), o = new Map(i.map((a, d) => [a, d + 1]));
|
|
625
|
+
n = n.map((a) => ({
|
|
626
|
+
...a,
|
|
627
|
+
filterGroup: o.get(a.filterGroup) ?? a.filterGroup
|
|
529
628
|
}));
|
|
530
629
|
}
|
|
531
630
|
e.recommendationConfigs.filters = n;
|
|
@@ -546,7 +645,7 @@ const E = () => ({
|
|
|
546
645
|
}), e.recommendationConfigs.filters = r;
|
|
547
646
|
},
|
|
548
647
|
generateFilterQuery() {
|
|
549
|
-
return
|
|
648
|
+
return P(this.recommendationConfigs.filters);
|
|
550
649
|
},
|
|
551
650
|
/**
|
|
552
651
|
* Validation-only check invoked at save-CTA time. Defined as an action
|
|
@@ -554,33 +653,52 @@ const E = () => ({
|
|
|
554
653
|
* every block's recommendationConfigs across user edits.
|
|
555
654
|
*/
|
|
556
655
|
hasInvalidBlock() {
|
|
557
|
-
return Object.values(this.blockStates).some((t) => !
|
|
656
|
+
return Object.values(this.blockStates).some((t) => !J(t.recommendationConfigs, this));
|
|
657
|
+
},
|
|
658
|
+
/**
|
|
659
|
+
* Which required fields are missing across the design's blocks, so the save
|
|
660
|
+
* toaster can name the cause instead of always showing the generic message.
|
|
661
|
+
*/
|
|
662
|
+
invalidBlockFields() {
|
|
663
|
+
return Object.values(this.blockStates).flatMap((t) => H(t.recommendationConfigs, this));
|
|
558
664
|
},
|
|
559
665
|
// ====================================================================
|
|
560
666
|
// Per-Block Product Fetching
|
|
561
667
|
// ====================================================================
|
|
562
668
|
async fetchRecommendationProducts() {
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
669
|
+
if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
|
|
670
|
+
return;
|
|
671
|
+
const t = [
|
|
672
|
+
this.currentRecommendationId,
|
|
673
|
+
this.configVersion,
|
|
674
|
+
b
|
|
675
|
+
].join(":");
|
|
676
|
+
if (y && I === t) {
|
|
677
|
+
await y;
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
I = t, y = this._doFetchProducts();
|
|
681
|
+
try {
|
|
682
|
+
await y;
|
|
683
|
+
} finally {
|
|
684
|
+
I === t && (y = null, I = "");
|
|
574
685
|
}
|
|
575
686
|
},
|
|
576
687
|
async _doFetchProducts() {
|
|
577
|
-
var
|
|
578
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n =
|
|
688
|
+
var h;
|
|
689
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = R();
|
|
579
690
|
if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
|
|
580
691
|
await this._doFetchStrategyProducts(t);
|
|
581
692
|
return;
|
|
582
693
|
}
|
|
583
|
-
const s =
|
|
694
|
+
const s = (g.get(t) ?? 0) + 1;
|
|
695
|
+
g.set(t, s);
|
|
696
|
+
const i = parseInt(r.size) || 6;
|
|
697
|
+
if (!r.strategy) {
|
|
698
|
+
this._setBlockProducts(t, [], i);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
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
702
|
locale: r.language,
|
|
585
703
|
currency: r.currencySettings.value,
|
|
586
704
|
partnerName: n.partnerName,
|
|
@@ -588,17 +706,17 @@ const E = () => ({
|
|
|
588
706
|
details: !0,
|
|
589
707
|
campaignId: n.variationId
|
|
590
708
|
};
|
|
591
|
-
r.strategy === "manualMerchandising" ?
|
|
592
|
-
|
|
593
|
-
|
|
709
|
+
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(
|
|
710
|
+
c,
|
|
711
|
+
$(n.partnerName, r.strategy)
|
|
594
712
|
);
|
|
595
|
-
let
|
|
713
|
+
let u;
|
|
596
714
|
try {
|
|
597
|
-
|
|
715
|
+
u = await m.fetchRecommendationProducts(d, c);
|
|
598
716
|
} catch {
|
|
599
|
-
|
|
717
|
+
u = [];
|
|
600
718
|
}
|
|
601
|
-
this._setBlockProducts(t,
|
|
719
|
+
g.get(t) === s && this._setBlockProducts(t, u, i);
|
|
602
720
|
},
|
|
603
721
|
/**
|
|
604
722
|
* Block-scoped counterpart of `fetchRecommendationProducts`, which only ever serves the
|
|
@@ -610,8 +728,8 @@ const E = () => ({
|
|
|
610
728
|
(e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId && await this._doFetchStrategyProducts(t);
|
|
611
729
|
},
|
|
612
730
|
async _doFetchStrategyProducts(t) {
|
|
613
|
-
const e = (
|
|
614
|
-
|
|
731
|
+
const e = (g.get(t) ?? 0) + 1;
|
|
732
|
+
g.set(t, e);
|
|
615
733
|
const { recommendationConfigs: r } = this.blockStates[t];
|
|
616
734
|
let n = [], s = parseInt(r.size) || 6;
|
|
617
735
|
try {
|
|
@@ -621,25 +739,25 @@ const E = () => ({
|
|
|
621
739
|
);
|
|
622
740
|
if (i) {
|
|
623
741
|
s = parseInt(String(i.size ?? "")) || s;
|
|
624
|
-
const o = r.currencySettings.value,
|
|
625
|
-
|
|
742
|
+
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;
|
|
743
|
+
f && (n = await m.fetchRecommendationProductsByUrl(f));
|
|
626
744
|
}
|
|
627
745
|
} catch {
|
|
628
746
|
n = [];
|
|
629
747
|
}
|
|
630
|
-
|
|
748
|
+
g.get(t) === e && this._setBlockProducts(t, n, s);
|
|
631
749
|
},
|
|
632
750
|
_setBlockProducts(t, e, r) {
|
|
633
751
|
if (!this.blockStates[t])
|
|
634
752
|
return;
|
|
635
|
-
const n = e.length > 0 ? e :
|
|
753
|
+
const n = e.length > 0 ? e : w(r);
|
|
636
754
|
this.blockStates[t].recommendationProducts = [
|
|
637
755
|
...n.slice(0, r),
|
|
638
|
-
...
|
|
756
|
+
...w(Math.max(0, r - n.length))
|
|
639
757
|
];
|
|
640
758
|
}
|
|
641
759
|
}
|
|
642
760
|
});
|
|
643
761
|
export {
|
|
644
|
-
|
|
762
|
+
ft as useRecommendationExtensionStore
|
|
645
763
|
};
|
|
@@ -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,25 @@
|
|
|
1
|
+
import { ATTR_CUSTOM_PREFIX as f } from "../constants/selectors.js";
|
|
2
|
+
function o(t) {
|
|
3
|
+
if (typeof t == "string")
|
|
4
|
+
return t.trim() !== "";
|
|
5
|
+
if (t && typeof t == "object" && "strategyId" in t) {
|
|
6
|
+
const { strategyId: r } = t;
|
|
7
|
+
return (typeof r == "string" || typeof r == "number") && String(r).trim() !== "";
|
|
8
|
+
}
|
|
9
|
+
return !1;
|
|
10
|
+
}
|
|
11
|
+
function u({ config: t, blockId: r, partnerName: e, campaignUrls: n, rrsEnabled: i }) {
|
|
12
|
+
return e ? t.partnerName ? t.partnerName !== e : !i || !t.strategyId || !n ? !1 : !o(n[String(r)]) : !1;
|
|
13
|
+
}
|
|
14
|
+
function a({ config: t, campaignUrls: r, rrsEnabled: e }) {
|
|
15
|
+
return !t.strategyId || e && !!r;
|
|
16
|
+
}
|
|
17
|
+
function g(t) {
|
|
18
|
+
return t.filter((r) => !r.startsWith(f));
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
a as canJudgeUnstampedBlock,
|
|
22
|
+
u as carriesForeignPartnerConfig,
|
|
23
|
+
o as isDefinedCampaignUrl,
|
|
24
|
+
g as withoutCustomAttributes
|
|
25
|
+
};
|
|
@@ -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
|
+
};
|