@useinsider/guido 3.19.0 → 3.20.0-beta.7b31fec
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -3
- package/dist/@types/config/schemas.js +59 -51
- package/dist/components/Guido.vue.js +4 -4
- package/dist/components/Guido.vue2.js +70 -68
- package/dist/components/organisms/base/Toaster.vue2.js +13 -12
- package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +13 -11
- package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +78 -57
- package/dist/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.js +18 -0
- package/dist/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue2.js +25 -0
- package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +28 -42
- package/dist/components/organisms/onboarding/OnboardingWrapper.vue2.js +28 -24
- package/dist/components/organisms/onboarding/RecommendationLegacyStrategyOnboarding.vue.js +21 -0
- package/dist/components/organisms/onboarding/RecommendationLegacyStrategyOnboarding.vue2.js +46 -0
- package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.js +21 -0
- package/dist/components/organisms/onboarding/RecommendationStrategyOnboarding.vue2.js +47 -0
- package/dist/composables/useCustomInterfaceAppearance.js +65 -51
- package/dist/composables/useHtmlValidator.js +87 -82
- package/dist/composables/useLegacyStrategyConversion.js +61 -0
- package/dist/composables/useRecommendation.js +40 -35
- package/dist/composables/useStripo.js +30 -30
- package/dist/composables/useToaster.js +14 -20
- package/dist/enums/extensions/recommendationBlock.js +49 -33
- 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 +100 -94
- package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +130 -55
- package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +74 -74
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +273 -92
- package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js +42 -0
- 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/recommendationApi.js +46 -37
- package/dist/src/@types/config/schemas.d.ts +16 -0
- package/dist/src/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.d.ts +2 -0
- package/dist/src/components/organisms/onboarding/RecommendationLegacyStrategyOnboarding.vue.d.ts +2 -0
- package/dist/src/components/organisms/onboarding/RecommendationStrategyOnboarding.vue.d.ts +2 -0
- package/dist/src/composables/useConfig.d.ts +4 -0
- package/dist/src/composables/useLegacyStrategyConversion.d.ts +3 -0
- package/dist/src/enums/extensions/recommendationBlock.d.ts +7 -1
- 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 +10 -0
- package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +7 -2
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +162 -1
- package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.d.ts +32 -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/services/recommendationApi.d.ts +2 -1
- package/dist/src/stores/config.d.ts +322 -0
- package/dist/src/stores/onboarding.d.ts +808 -10
- package/dist/static/assets/icons/filled-arrow-rising.svg.js +5 -0
- package/dist/static/assets/icons/line-info.svg.js +5 -0
- package/dist/static/styles/components/recommendation-strategy.css.js +104 -9
- package/dist/static/styles/components/skeleton.css.js +23 -0
- package/dist/stores/config.js +74 -60
- package/dist/stores/onboarding.js +46 -36
- package/dist/utils/templatePreparation.js +55 -55
- package/package.json +1 -1
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
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";
|
|
6
|
+
import { useConfigStore as k } from "../../../../stores/config.js";
|
|
7
|
+
import { defineStore as B } from "pinia";
|
|
5
8
|
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 {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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 _() {
|
|
18
23
|
return {
|
|
19
|
-
cardsInRow:
|
|
24
|
+
cardsInRow: z,
|
|
20
25
|
currencySettings: {
|
|
21
26
|
name: "USD",
|
|
22
27
|
value: "USD",
|
|
@@ -30,14 +35,19 @@ function w() {
|
|
|
30
35
|
productIds: [],
|
|
31
36
|
id: 1,
|
|
32
37
|
language: "en_US",
|
|
33
|
-
mobileCardsInRow:
|
|
38
|
+
mobileCardsInRow: O,
|
|
34
39
|
mobileLayoutEnabled: !1,
|
|
35
40
|
orientation: "grid",
|
|
36
41
|
recommendedProducts: [],
|
|
37
42
|
sendProductRequestFlag: !1,
|
|
38
43
|
shuffleProducts: !1,
|
|
39
|
-
|
|
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",
|
|
40
49
|
strategyId: "",
|
|
50
|
+
nodeConfigVersion: C,
|
|
41
51
|
textTrimming: !0,
|
|
42
52
|
unresponsive: !1,
|
|
43
53
|
size: "6",
|
|
@@ -46,21 +56,23 @@ function w() {
|
|
|
46
56
|
priceHideIfSameAsDiscounted: !1
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
|
-
function
|
|
59
|
+
function G() {
|
|
50
60
|
return {
|
|
51
|
-
recommendationConfigs:
|
|
61
|
+
recommendationConfigs: _(),
|
|
52
62
|
recommendationProducts: [],
|
|
53
63
|
filterStatus: !1,
|
|
54
64
|
filterSelectionDrawerStatus: !1,
|
|
55
65
|
assignStrategyDrawerStatus: !1,
|
|
56
66
|
removeStrategyModalStatus: !1,
|
|
67
|
+
legacyStrategyUpdatedModalStatus: !1,
|
|
57
68
|
filterGroup: 1,
|
|
58
69
|
isInitialized: !1,
|
|
59
70
|
filterSnapshot: null,
|
|
60
|
-
syncedConfigFingerprint: null
|
|
71
|
+
syncedConfigFingerprint: null,
|
|
72
|
+
legacyConversionLoading: !1
|
|
61
73
|
};
|
|
62
74
|
}
|
|
63
|
-
const
|
|
75
|
+
const Q = () => ({
|
|
64
76
|
recommendationCampaignUrls: {},
|
|
65
77
|
activePredictiveAlgorithms: [],
|
|
66
78
|
languages: {},
|
|
@@ -69,11 +81,14 @@ const z = () => ({
|
|
|
69
81
|
strategies: [],
|
|
70
82
|
strategiesLoading: !1,
|
|
71
83
|
strategyUrlTemplates: {},
|
|
84
|
+
strategiesFetched: !1,
|
|
85
|
+
strategiesSettled: !1,
|
|
72
86
|
blockStates: {},
|
|
73
87
|
currentRecommendationId: null,
|
|
88
|
+
strategyPanelOpen: !1,
|
|
74
89
|
configVersion: 0
|
|
75
|
-
}),
|
|
76
|
-
state: () =>
|
|
90
|
+
}), ft = B("guidoRecommendationExtension", {
|
|
91
|
+
state: () => Q(),
|
|
77
92
|
getters: {
|
|
78
93
|
// ====================================================================
|
|
79
94
|
// Proxy Getters — Backward Compatible Access to Current Block State
|
|
@@ -83,7 +98,7 @@ const z = () => ({
|
|
|
83
98
|
* This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
|
|
84
99
|
*/
|
|
85
100
|
recommendationConfigs(t) {
|
|
86
|
-
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 : _();
|
|
87
102
|
},
|
|
88
103
|
/**
|
|
89
104
|
* Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
|
|
@@ -103,6 +118,15 @@ const z = () => ({
|
|
|
103
118
|
removeStrategyModalStatus(t) {
|
|
104
119
|
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].removeStrategyModalStatus : !1;
|
|
105
120
|
},
|
|
121
|
+
isLegacyStrategyBlock() {
|
|
122
|
+
return k().isFeatureEnabled("reusableRecommendationStrategy") && j(this.recommendationConfigs);
|
|
123
|
+
},
|
|
124
|
+
legacyConversionLoading(t) {
|
|
125
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].legacyConversionLoading ?? !1 : !1;
|
|
126
|
+
},
|
|
127
|
+
legacyStrategyUpdatedModalStatus(t) {
|
|
128
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].legacyStrategyUpdatedModalStatus : !1;
|
|
129
|
+
},
|
|
106
130
|
assignedStrategy(t) {
|
|
107
131
|
const { strategyId: e } = this.recommendationConfigs;
|
|
108
132
|
return e ? t.strategies.find((r) => String(r.strategy_id) === e) ?? null : null;
|
|
@@ -138,8 +162,8 @@ const z = () => ({
|
|
|
138
162
|
return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
|
|
139
163
|
},
|
|
140
164
|
getActivePredictiveAlgorithms: (t) => {
|
|
141
|
-
const e =
|
|
142
|
-
return t.activePredictiveAlgorithms.filter((n) => !
|
|
165
|
+
const e = F(), r = [];
|
|
166
|
+
return t.activePredictiveAlgorithms.filter((n) => !V.includes(n)).forEach((n) => {
|
|
143
167
|
r.push(...e.filter((s) => s.id === n));
|
|
144
168
|
}), r.map((n) => ({
|
|
145
169
|
text: n.name,
|
|
@@ -157,7 +181,7 @@ const z = () => ({
|
|
|
157
181
|
getFilterList() {
|
|
158
182
|
return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
|
|
159
183
|
let e;
|
|
160
|
-
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, {
|
|
161
185
|
text: t.displayName,
|
|
162
186
|
value: e,
|
|
163
187
|
type: t.attributeType
|
|
@@ -179,7 +203,7 @@ const z = () => ({
|
|
|
179
203
|
setCurrentBlock(t) {
|
|
180
204
|
this.blockStates[t] || (this.blockStates = {
|
|
181
205
|
...this.blockStates,
|
|
182
|
-
[t]:
|
|
206
|
+
[t]: G()
|
|
183
207
|
}), this.currentRecommendationId = t;
|
|
184
208
|
},
|
|
185
209
|
/**
|
|
@@ -201,12 +225,16 @@ const z = () => ({
|
|
|
201
225
|
/**
|
|
202
226
|
* Deep-clones a block's state and mirrors its campaign URL under a new id.
|
|
203
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).
|
|
204
232
|
*/
|
|
205
233
|
cloneBlockState(t, e) {
|
|
206
234
|
const r = this.blockStates[t];
|
|
207
235
|
if (r) {
|
|
208
236
|
const o = structuredClone(r);
|
|
209
|
-
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 = {
|
|
210
238
|
...this.blockStates,
|
|
211
239
|
[e]: o
|
|
212
240
|
};
|
|
@@ -239,7 +267,7 @@ const z = () => ({
|
|
|
239
267
|
* `isInitialized`) alone.
|
|
240
268
|
*/
|
|
241
269
|
seedBlockUrlConfig(t, e) {
|
|
242
|
-
const r = (o,
|
|
270
|
+
const r = (o, a) => o === "." || o === "," || o === " " ? o : a, n = {
|
|
243
271
|
name: e.currencyCode,
|
|
244
272
|
value: e.currencyCode,
|
|
245
273
|
symbol: e.currencySymbol,
|
|
@@ -247,7 +275,7 @@ const z = () => ({
|
|
|
247
275
|
decimalCount: String(e.currencyDecimalCount),
|
|
248
276
|
decimalSeparator: r(e.currencyDecimalSeparator, ","),
|
|
249
277
|
thousandSeparator: r(e.currencyThousandSeparator, ".")
|
|
250
|
-
}, s = !this.blockStates[t], i = s ?
|
|
278
|
+
}, s = !this.blockStates[t], i = s ? G() : this.blockStates[t];
|
|
251
279
|
i.recommendationConfigs = {
|
|
252
280
|
...i.recommendationConfigs,
|
|
253
281
|
strategy: e.strategy,
|
|
@@ -286,7 +314,7 @@ const z = () => ({
|
|
|
286
314
|
* Creates a filter with the first available attribute and operator pre-selected.
|
|
287
315
|
*/
|
|
288
316
|
createDefaultFilter(t, e) {
|
|
289
|
-
const [r] = this.getFilterList, [n] =
|
|
317
|
+
const [r] = this.getFilterList, [n] = N(r == null ? void 0 : r.type);
|
|
290
318
|
return {
|
|
291
319
|
type: "standardFilter",
|
|
292
320
|
attribute: (r == null ? void 0 : r.value) ?? "",
|
|
@@ -339,6 +367,12 @@ const z = () => ({
|
|
|
339
367
|
closeAssignStrategyDrawer() {
|
|
340
368
|
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !1);
|
|
341
369
|
},
|
|
370
|
+
openLegacyStrategyUpdatedModal() {
|
|
371
|
+
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].legacyStrategyUpdatedModalStatus = !0);
|
|
372
|
+
},
|
|
373
|
+
closeLegacyStrategyUpdatedModal() {
|
|
374
|
+
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].legacyStrategyUpdatedModalStatus = !1);
|
|
375
|
+
},
|
|
342
376
|
openRemoveStrategyModal() {
|
|
343
377
|
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !0);
|
|
344
378
|
},
|
|
@@ -346,29 +380,111 @@ const z = () => ({
|
|
|
346
380
|
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !1);
|
|
347
381
|
},
|
|
348
382
|
async fetchStrategies() {
|
|
349
|
-
|
|
350
|
-
|
|
383
|
+
for (; R; )
|
|
384
|
+
try {
|
|
385
|
+
await R;
|
|
386
|
+
} catch {
|
|
387
|
+
}
|
|
388
|
+
if (this.strategiesFetched)
|
|
351
389
|
return;
|
|
352
|
-
|
|
353
|
-
S = (async () => {
|
|
390
|
+
const t = b, e = (async () => {
|
|
354
391
|
this.strategiesLoading = !0;
|
|
355
392
|
try {
|
|
356
|
-
|
|
393
|
+
const r = await m.fetchRecommendationStrategies();
|
|
394
|
+
if (t !== b)
|
|
395
|
+
return;
|
|
396
|
+
this.strategies = r, this.strategiesFetched = !0;
|
|
357
397
|
} finally {
|
|
358
|
-
this.strategiesLoading = !1;
|
|
398
|
+
this.strategiesLoading = !1, t === b && (this.strategiesSettled = !0);
|
|
359
399
|
}
|
|
360
400
|
})();
|
|
401
|
+
R = e;
|
|
361
402
|
try {
|
|
362
|
-
await
|
|
403
|
+
await e;
|
|
363
404
|
} finally {
|
|
364
|
-
|
|
405
|
+
R = null;
|
|
365
406
|
}
|
|
366
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
|
+
},
|
|
367
483
|
async resolveStrategyUrlTemplate(t, e) {
|
|
368
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;
|
|
369
485
|
if (s)
|
|
370
486
|
return s;
|
|
371
|
-
const { full_path: i = "" } = await
|
|
487
|
+
const { full_path: i = "" } = await m.generateStrategyEndpoint(t, e) ?? {};
|
|
372
488
|
return n && i && (this.strategyUrlTemplates = {
|
|
373
489
|
...this.strategyUrlTemplates,
|
|
374
490
|
[n]: i
|
|
@@ -380,24 +496,61 @@ const z = () => ({
|
|
|
380
496
|
...t.size !== void 0 ? { size: String(t.size) } : {}
|
|
381
497
|
});
|
|
382
498
|
},
|
|
499
|
+
async convertLegacyStrategy(t, e = { scope: "campaign" }) {
|
|
500
|
+
const r = this.currentRecommendationId;
|
|
501
|
+
if (r === null || !this.blockStates[r])
|
|
502
|
+
throw new Error("[Guido] convertLegacyStrategy called without an active block");
|
|
503
|
+
this.blockStates[r].legacyConversionLoading = !0;
|
|
504
|
+
try {
|
|
505
|
+
const n = await m.migrateLegacyStrategy(t, e);
|
|
506
|
+
try {
|
|
507
|
+
await this.fetchStrategies();
|
|
508
|
+
} catch (o) {
|
|
509
|
+
console.error("[Guido] strategy listing refresh after conversion failed", o);
|
|
510
|
+
}
|
|
511
|
+
const s = this.strategies.find((o) => String(o.strategy_id) === n), i = this.blockStates[r];
|
|
512
|
+
return i && (i.recommendationConfigs = {
|
|
513
|
+
...i.recommendationConfigs,
|
|
514
|
+
strategyId: n,
|
|
515
|
+
nodeConfigVersion: C,
|
|
516
|
+
...(s == null ? void 0 : s.size) !== void 0 ? { size: String(s.size) } : {}
|
|
517
|
+
}), this.currentRecommendationId === r && this.configVersion++, n;
|
|
518
|
+
} finally {
|
|
519
|
+
this.blockStates[r] && (this.blockStates[r].legacyConversionLoading = !1);
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
/**
|
|
523
|
+
* Undoes the config half of a conversion whose document write never landed, so the
|
|
524
|
+
* panel and the drawer fall back to "Legacy Strategy" instead of showing an assignment
|
|
525
|
+
* the document never received.
|
|
526
|
+
*/
|
|
527
|
+
revertLegacyConversion(t, e) {
|
|
528
|
+
const r = this.blockStates[t];
|
|
529
|
+
r && (r.recommendationConfigs = { ...r.recommendationConfigs, ...e }, this.currentRecommendationId === t && this.configVersion++);
|
|
530
|
+
},
|
|
383
531
|
removeStrategy() {
|
|
384
|
-
this.patchCurrentBlockConfig({
|
|
532
|
+
this.patchCurrentBlockConfig({
|
|
533
|
+
strategyId: "",
|
|
534
|
+
strategy: "",
|
|
535
|
+
size: x.size,
|
|
536
|
+
nodeConfigVersion: C
|
|
537
|
+
});
|
|
385
538
|
},
|
|
386
539
|
// ====================================================================
|
|
387
540
|
// Shared Data Fetching (fetched once, used by all blocks)
|
|
388
541
|
// ====================================================================
|
|
389
542
|
async fetchRecommendationCreateData() {
|
|
390
543
|
if (!this.activePredictiveAlgorithms.length) {
|
|
391
|
-
if (
|
|
392
|
-
await
|
|
544
|
+
if (S) {
|
|
545
|
+
await S;
|
|
393
546
|
return;
|
|
394
547
|
}
|
|
395
|
-
|
|
548
|
+
S = (async () => {
|
|
396
549
|
const {
|
|
397
550
|
activePredictiveAlgorithms: t,
|
|
398
551
|
languages: e,
|
|
399
552
|
currencies: r
|
|
400
|
-
} = await
|
|
553
|
+
} = await m.fetchRecommendationCreateData();
|
|
401
554
|
if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
|
|
402
555
|
const n = this.blockStates[this.currentRecommendationId];
|
|
403
556
|
n.filterStatus = !!n.recommendationConfigs.filters.length;
|
|
@@ -405,26 +558,26 @@ const z = () => ({
|
|
|
405
558
|
this.currencyList = r;
|
|
406
559
|
})();
|
|
407
560
|
try {
|
|
408
|
-
await
|
|
561
|
+
await S;
|
|
409
562
|
} finally {
|
|
410
|
-
|
|
563
|
+
S = null;
|
|
411
564
|
}
|
|
412
565
|
}
|
|
413
566
|
},
|
|
414
567
|
async fetchRecommendationFilters() {
|
|
415
568
|
if (!Object.keys(this.filterList).length) {
|
|
416
|
-
if (
|
|
417
|
-
await
|
|
569
|
+
if (p) {
|
|
570
|
+
await p;
|
|
418
571
|
return;
|
|
419
572
|
}
|
|
420
|
-
|
|
421
|
-
const t = await
|
|
573
|
+
p = (async () => {
|
|
574
|
+
const t = await m.fetchRecommendationFilters();
|
|
422
575
|
this.filterList = t;
|
|
423
576
|
})();
|
|
424
577
|
try {
|
|
425
|
-
await
|
|
578
|
+
await p;
|
|
426
579
|
} finally {
|
|
427
|
-
|
|
580
|
+
p = null;
|
|
428
581
|
}
|
|
429
582
|
}
|
|
430
583
|
},
|
|
@@ -453,7 +606,7 @@ const z = () => ({
|
|
|
453
606
|
const n = [...e.recommendationConfigs.filters];
|
|
454
607
|
n[r] = {
|
|
455
608
|
...t,
|
|
456
|
-
isValid:
|
|
609
|
+
isValid: K(t)
|
|
457
610
|
}, e.recommendationConfigs.filters = n;
|
|
458
611
|
}
|
|
459
612
|
},
|
|
@@ -467,10 +620,10 @@ const z = () => ({
|
|
|
467
620
|
let i = 1;
|
|
468
621
|
n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
|
|
469
622
|
} else {
|
|
470
|
-
const i = [...new Set(n.map((
|
|
471
|
-
n = n.map((
|
|
472
|
-
...
|
|
473
|
-
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
|
|
474
627
|
}));
|
|
475
628
|
}
|
|
476
629
|
e.recommendationConfigs.filters = n;
|
|
@@ -491,7 +644,7 @@ const z = () => ({
|
|
|
491
644
|
}), e.recommendationConfigs.filters = r;
|
|
492
645
|
},
|
|
493
646
|
generateFilterQuery() {
|
|
494
|
-
return
|
|
647
|
+
return P(this.recommendationConfigs.filters);
|
|
495
648
|
},
|
|
496
649
|
/**
|
|
497
650
|
* Validation-only check invoked at save-CTA time. Defined as an action
|
|
@@ -499,33 +652,52 @@ const z = () => ({
|
|
|
499
652
|
* every block's recommendationConfigs across user edits.
|
|
500
653
|
*/
|
|
501
654
|
hasInvalidBlock() {
|
|
502
|
-
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));
|
|
503
663
|
},
|
|
504
664
|
// ====================================================================
|
|
505
665
|
// Per-Block Product Fetching
|
|
506
666
|
// ====================================================================
|
|
507
667
|
async fetchRecommendationProducts() {
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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 = "");
|
|
519
684
|
}
|
|
520
685
|
},
|
|
521
686
|
async _doFetchProducts() {
|
|
522
|
-
var
|
|
523
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n =
|
|
687
|
+
var h;
|
|
688
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = k();
|
|
524
689
|
if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
|
|
525
690
|
await this._doFetchStrategyProducts(t);
|
|
526
691
|
return;
|
|
527
692
|
}
|
|
528
|
-
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 = {
|
|
529
701
|
locale: r.language,
|
|
530
702
|
currency: r.currencySettings.value,
|
|
531
703
|
partnerName: n.partnerName,
|
|
@@ -533,21 +705,30 @@ const z = () => ({
|
|
|
533
705
|
details: !0,
|
|
534
706
|
campaignId: n.variationId
|
|
535
707
|
};
|
|
536
|
-
r.strategy === "manualMerchandising" ?
|
|
537
|
-
|
|
538
|
-
|
|
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)
|
|
539
711
|
);
|
|
540
|
-
let
|
|
712
|
+
let u;
|
|
541
713
|
try {
|
|
542
|
-
|
|
714
|
+
u = await m.fetchRecommendationProducts(d, c);
|
|
543
715
|
} catch {
|
|
544
|
-
|
|
716
|
+
u = [];
|
|
545
717
|
}
|
|
546
|
-
this._setBlockProducts(t,
|
|
718
|
+
g.get(t) === s && this._setBlockProducts(t, u, i);
|
|
719
|
+
},
|
|
720
|
+
/**
|
|
721
|
+
* Block-scoped counterpart of `fetchRecommendationProducts`, which only ever serves the
|
|
722
|
+
* selected block. The legacy conversion must refresh the products of the block that
|
|
723
|
+
* started it even after the user selected another one.
|
|
724
|
+
*/
|
|
725
|
+
async fetchStrategyProductsForBlock(t) {
|
|
726
|
+
var e;
|
|
727
|
+
(e = this.blockStates[t]) != null && e.recommendationConfigs.strategyId && await this._doFetchStrategyProducts(t);
|
|
547
728
|
},
|
|
548
729
|
async _doFetchStrategyProducts(t) {
|
|
549
|
-
const e = (
|
|
550
|
-
|
|
730
|
+
const e = (g.get(t) ?? 0) + 1;
|
|
731
|
+
g.set(t, e);
|
|
551
732
|
const { recommendationConfigs: r } = this.blockStates[t];
|
|
552
733
|
let n = [], s = parseInt(r.size) || 6;
|
|
553
734
|
try {
|
|
@@ -557,25 +738,25 @@ const z = () => ({
|
|
|
557
738
|
);
|
|
558
739
|
if (i) {
|
|
559
740
|
s = parseInt(String(i.size ?? "")) || s;
|
|
560
|
-
const o = r.currencySettings.value,
|
|
561
|
-
|
|
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));
|
|
562
743
|
}
|
|
563
744
|
} catch {
|
|
564
745
|
n = [];
|
|
565
746
|
}
|
|
566
|
-
|
|
747
|
+
g.get(t) === e && this._setBlockProducts(t, n, s);
|
|
567
748
|
},
|
|
568
749
|
_setBlockProducts(t, e, r) {
|
|
569
750
|
if (!this.blockStates[t])
|
|
570
751
|
return;
|
|
571
|
-
const n = e.length > 0 ? e :
|
|
752
|
+
const n = e.length > 0 ? e : w(r);
|
|
572
753
|
this.blockStates[t].recommendationProducts = [
|
|
573
754
|
...n.slice(0, r),
|
|
574
|
-
...
|
|
755
|
+
...w(Math.max(0, r - n.length))
|
|
575
756
|
];
|
|
576
757
|
}
|
|
577
758
|
}
|
|
578
759
|
});
|
|
579
760
|
export {
|
|
580
|
-
|
|
761
|
+
ft as useRecommendationExtensionStore
|
|
581
762
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import "../constants/selectors.js";
|
|
2
|
+
import { RRS_CONFIG_VERSION as m } from "../constants/defaultConfig.js";
|
|
3
|
+
import { sanitizeProductIds as p } from "./productIds.js";
|
|
4
|
+
const d = "manualMerchandising";
|
|
5
|
+
function N({ strategyId: e, nodeConfigVersion: t }) {
|
|
6
|
+
return !e && t < m;
|
|
7
|
+
}
|
|
8
|
+
const o = (e) => {
|
|
9
|
+
const t = String(e ?? "").trim();
|
|
10
|
+
return /^[1-9]\d*$/.test(t) ? t : null;
|
|
11
|
+
};
|
|
12
|
+
function f({ campaignId: e, templateLibraryId: t }) {
|
|
13
|
+
if (o(e))
|
|
14
|
+
return { scope: "campaign" };
|
|
15
|
+
const n = o(t);
|
|
16
|
+
return n ? { scope: "template", templateId: n } : { scope: "campaign" };
|
|
17
|
+
}
|
|
18
|
+
function y({
|
|
19
|
+
blockId: e,
|
|
20
|
+
url: t,
|
|
21
|
+
campaignId: n,
|
|
22
|
+
variationId: a,
|
|
23
|
+
strategy: c,
|
|
24
|
+
productIds: s,
|
|
25
|
+
target: r
|
|
26
|
+
}) {
|
|
27
|
+
const i = c === d ? p(s).split(",").filter(Boolean) : [];
|
|
28
|
+
return {
|
|
29
|
+
...r.scope === "template" ? { templateId: Number.parseInt(r.templateId) } : {
|
|
30
|
+
campaignId: Number.parseInt(n) || 0,
|
|
31
|
+
variationId: Number.parseInt(a) || 0
|
|
32
|
+
},
|
|
33
|
+
blockId: String(e),
|
|
34
|
+
url: t,
|
|
35
|
+
...i.length ? { productIds: i } : {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
y as buildLegacyMigrationPayload,
|
|
40
|
+
N as isLegacyStrategyConfig,
|
|
41
|
+
f as resolveMigrationTarget
|
|
42
|
+
};
|