@useinsider/guido 3.18.1-beta.d0bc5f3 → 3.18.1-beta.d82285c
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/composables/useHtmlValidator.js +84 -79
- package/dist/composables/useRecommendation.js +21 -18
- package/dist/extensions/Blocks/Recommendation/block.js +43 -39
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +57 -41
- package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +21 -12
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +5 -0
- package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +4 -0
- package/dist/utils/templatePreparation.js +50 -50
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useConfig as V } from "./useConfig.js";
|
|
2
2
|
import { getIgnoredRecommendationBlockIds as M } from "../config/compiler/utils/recommendationIgnoreUtils.js";
|
|
3
3
|
import { TemplateTypes as W } from "../enums/defaults.js";
|
|
4
|
-
import { DISPLAY_CONDITIONS_REGEX as G, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as H, CampaignCouldNotBeSavedKey as
|
|
4
|
+
import { DISPLAY_CONDITIONS_REGEX as G, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as H, CampaignCouldNotBeSavedKey as K, CanNotMakeAnyChangesForRunningKey as P, DATA_ATTRIBUTE_REGEX as X, DYNAMIC_CONTENT_TAG_REGEX as Y, ALLOWED_DYNAMIC_SYSTEM_TOKENS as $, VALID_DYNAMIC_VARIABLE_REGEX as U } from "../enums/html-validator.js";
|
|
5
5
|
import { ToasterTypeOptions as c } from "../enums/toaster.js";
|
|
6
6
|
import { BLOCK_ROOT_SELECTOR as j } from "../extensions/Blocks/Checkbox/constants/selectors.js";
|
|
7
7
|
import { itemsBlockDynamicVariables as q } from "../extensions/Blocks/Items/enums/productEnums.js";
|
|
@@ -9,58 +9,58 @@ import { BLOCK_ROOT_SELECTOR as z } from "../extensions/Blocks/RadioButton/const
|
|
|
9
9
|
import { RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES as Z } from "../extensions/Blocks/Recommendation/constants/selectors.js";
|
|
10
10
|
import { useRecommendationExtensionStore as J } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
11
11
|
import { filterToProductAttrValue as Q } from "../extensions/Blocks/Recommendation/utils/compositionKeys.js";
|
|
12
|
-
import {
|
|
13
|
-
import { useRecommendationStore as
|
|
14
|
-
import { base64EncodeWithSpecialChars as
|
|
15
|
-
import { computed as
|
|
16
|
-
import { useHttp as
|
|
17
|
-
import { useToaster as
|
|
18
|
-
import { useTranslations as
|
|
19
|
-
const
|
|
20
|
-
function
|
|
21
|
-
return [...s.matchAll(
|
|
12
|
+
import { STRATEGY_FIELD_KEY as ee, RecommendationAssignStrategyKey as te, RecommendationRequiredFieldsKey as ne } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
|
|
13
|
+
import { useRecommendationStore as se } from "../stores/recommendation.js";
|
|
14
|
+
import { base64EncodeWithSpecialChars as re } from "../utils/base64.js";
|
|
15
|
+
import { computed as oe } from "vue";
|
|
16
|
+
import { useHttp as ie } from "./useHttp.js";
|
|
17
|
+
import { useToaster as ae } from "./useToaster.js";
|
|
18
|
+
import { useTranslations as ce } from "./useTranslations.js";
|
|
19
|
+
const le = /recommendation-id="(\d+)"/g;
|
|
20
|
+
function me(s) {
|
|
21
|
+
return [...s.matchAll(le)].map((m) => m[1]);
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
return
|
|
23
|
+
function ue(s, m) {
|
|
24
|
+
return m.some((f) => s.startsWith(`${f}_`));
|
|
25
25
|
}
|
|
26
|
-
const
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
return
|
|
26
|
+
const de = /^(\d+)_\d+_(.+)$/;
|
|
27
|
+
function fe(s) {
|
|
28
|
+
const m = s.match(de);
|
|
29
|
+
return m ? { recoId: m[1], attribute: m[2] } : null;
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
const
|
|
31
|
+
function ge(s) {
|
|
32
|
+
const m = new Set(Z);
|
|
33
33
|
return Object.values(s).forEach((f) => {
|
|
34
|
-
|
|
35
|
-
}),
|
|
34
|
+
m.add(Q(f).toLowerCase());
|
|
35
|
+
}), m;
|
|
36
36
|
}
|
|
37
|
-
function
|
|
37
|
+
function pe(s, m, f, o, d) {
|
|
38
38
|
const u = s.match(/{{([a-zA-Z0-9_.\s]*)}}/gm);
|
|
39
39
|
if (!u)
|
|
40
40
|
return [];
|
|
41
|
-
const
|
|
41
|
+
const p = new Set(m.map((C) => C.toLowerCase())), E = [];
|
|
42
42
|
return u.forEach((C) => {
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
43
|
+
const g = C.slice(2, -2).trim().toLowerCase();
|
|
44
|
+
if (g !== "" && p.has(g))
|
|
45
45
|
return;
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
if (!d || d.has(
|
|
46
|
+
const y = fe(g);
|
|
47
|
+
if (y && o.has(y.recoId)) {
|
|
48
|
+
if (!d || d.has(y.attribute))
|
|
49
49
|
return;
|
|
50
|
-
E.push(
|
|
50
|
+
E.push(g);
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
ue(g, f) || g.includes(".") || E.push(g);
|
|
54
54
|
}), E;
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
return (s.replace(
|
|
58
|
-
const d = o.slice(2, -2), u = d.indexOf("|"),
|
|
59
|
-
return
|
|
56
|
+
function Ee(s) {
|
|
57
|
+
return (s.replace(X, "$1").match(Y) ?? []).filter((o) => {
|
|
58
|
+
const d = o.slice(2, -2), u = d.indexOf("|"), p = (u === -1 ? d : d.slice(0, u)).trim();
|
|
59
|
+
return $.includes(p) ? !1 : U.test(p) ? u !== -1 && d.slice(u + 1).trim() === "" : !0;
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
const
|
|
63
|
-
const { showToaster: s } =
|
|
62
|
+
const ke = () => {
|
|
63
|
+
const { showToaster: s } = ae(), { post: m } = ie(), { config: f } = V(), o = ce(), d = se(), u = J(), p = oe(
|
|
64
64
|
() => {
|
|
65
65
|
var e, t;
|
|
66
66
|
return ((t = (e = f.value) == null ? void 0 : e.partner) == null ? void 0 : t.messageType) === W.transactional;
|
|
@@ -73,27 +73,27 @@ const De = () => {
|
|
|
73
73
|
} catch {
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
|
-
return
|
|
76
|
+
return ge(u.filterList);
|
|
77
77
|
}, C = async (e) => {
|
|
78
|
-
const n = await
|
|
78
|
+
const n = await m(
|
|
79
79
|
"/newsletter/template-library/check-template-html-body",
|
|
80
|
-
{ html:
|
|
80
|
+
{ html: re(e) }
|
|
81
81
|
), { status: r, message: a } = n.data;
|
|
82
82
|
return r || s({
|
|
83
83
|
type: c.Error,
|
|
84
84
|
message: r === void 0 ? a : o("newsletter.invalid-url-link-for-toaster")
|
|
85
|
-
}), o(
|
|
85
|
+
}), o(K), a === o(P) && s({
|
|
86
86
|
type: c.Error,
|
|
87
87
|
message: o("newsletter.already-in-progress")
|
|
88
88
|
}), r;
|
|
89
|
-
},
|
|
89
|
+
}, g = (e) => !["if", "endif", "else", "elif", "now"].includes(e.toLowerCase()), y = (e) => ["if", "endif"].includes(e.toLowerCase()), I = (e, t) => {
|
|
90
90
|
const n = e.match(/({%(.*?)%})/g);
|
|
91
91
|
let r = !0;
|
|
92
|
-
return n !== null && !
|
|
92
|
+
return n !== null && !p.value && n.forEach((a) => {
|
|
93
93
|
const i = a.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
|
|
94
94
|
if (i && i.length > 0) {
|
|
95
|
-
const [
|
|
96
|
-
|
|
95
|
+
const [l] = i;
|
|
96
|
+
g(l) && !t.includes(l) && (s({
|
|
97
97
|
type: c.Warning,
|
|
98
98
|
message: o("custom-fields.invalid-custom-fields")
|
|
99
99
|
}), r = !1);
|
|
@@ -101,11 +101,11 @@ const De = () => {
|
|
|
101
101
|
}), r;
|
|
102
102
|
}, R = async (e, t, n) => {
|
|
103
103
|
const r = n ? await C(e) : !0;
|
|
104
|
-
return
|
|
105
|
-
},
|
|
104
|
+
return I(e, t) && r;
|
|
105
|
+
}, A = (e) => e.length > 0 ? !0 : (s({
|
|
106
106
|
type: c.Warning,
|
|
107
107
|
message: o("newsletter.html-content-is-empty")
|
|
108
|
-
}), !1),
|
|
108
|
+
}), !1), _ = (e) => {
|
|
109
109
|
const t = (e.match(/{/gm) || []).length, n = (e.match(/}/gm) || []).length;
|
|
110
110
|
return t > n && s({
|
|
111
111
|
type: c.Warning,
|
|
@@ -114,26 +114,26 @@ const De = () => {
|
|
|
114
114
|
type: c.Warning,
|
|
115
115
|
message: o("custom-fields.missing-opening-braces")
|
|
116
116
|
}), t === n;
|
|
117
|
-
},
|
|
118
|
-
const t =
|
|
117
|
+
}, b = (e) => {
|
|
118
|
+
const t = Ee(e).length === 0;
|
|
119
119
|
return t || s({
|
|
120
120
|
type: c.Warning,
|
|
121
121
|
message: o("custom-fields.invalid-custom-fields")
|
|
122
122
|
}), t;
|
|
123
123
|
}, O = (e, t, n, r) => {
|
|
124
|
-
if (
|
|
124
|
+
if (p.value)
|
|
125
125
|
return !0;
|
|
126
|
-
const a =
|
|
126
|
+
const a = pe(
|
|
127
127
|
e,
|
|
128
128
|
t,
|
|
129
|
-
|
|
129
|
+
me(e),
|
|
130
130
|
n,
|
|
131
131
|
r
|
|
132
132
|
);
|
|
133
133
|
if (a.length > 0) {
|
|
134
134
|
const i = `
|
|
135
135
|
<ul>
|
|
136
|
-
${a.map((
|
|
136
|
+
${a.map((l) => `<li>${l}</li>`).join("")}
|
|
137
137
|
</ul>
|
|
138
138
|
`;
|
|
139
139
|
return s({
|
|
@@ -146,22 +146,22 @@ const De = () => {
|
|
|
146
146
|
const t = e.match(/{%(.*?)%}/g), n = [];
|
|
147
147
|
let r = !0;
|
|
148
148
|
if (t && t.forEach((a) => {
|
|
149
|
-
const i = a.match(G),
|
|
150
|
-
(!i || a !== v) && !
|
|
149
|
+
const i = a.match(G), l = a.match(H), v = (i == null ? void 0 : i.join("")) || "";
|
|
150
|
+
(!i || a !== v) && !l && (s({
|
|
151
151
|
type: c.Error,
|
|
152
152
|
message: o("newsletter.display-conditions-invalid-syntax")
|
|
153
|
-
}), r = !1), i && i.forEach((
|
|
154
|
-
|
|
153
|
+
}), r = !1), i && i.forEach((h) => {
|
|
154
|
+
h.trim() === "=" && (s({
|
|
155
155
|
type: c.Error,
|
|
156
156
|
message: o("custom-conditions.wrong-equality-operators")
|
|
157
157
|
}), r = !1);
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
const S = h.match(/^[a-zA-Z]*$/g);
|
|
159
|
+
S && S.forEach((T) => {
|
|
160
|
+
y(T) && n.push(T);
|
|
161
161
|
});
|
|
162
162
|
});
|
|
163
163
|
}), n.length) {
|
|
164
|
-
const a = n.filter((
|
|
164
|
+
const a = n.filter((l) => l === "if"), i = n.filter((l) => l === "endif");
|
|
165
165
|
a.length !== i.length && (s({
|
|
166
166
|
type: c.Error,
|
|
167
167
|
message: o("custom-conditions.missing-if-endif-tag")
|
|
@@ -177,23 +177,28 @@ const De = () => {
|
|
|
177
177
|
}, N = (e) => (e.match(/({%(.*?)%})/g) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, r) => n + r, 0) > 0 ? (s({
|
|
178
178
|
type: c.Warning,
|
|
179
179
|
message: o("custom-conditions.no-braces-inside-if-tag")
|
|
180
|
-
}), !1) : !0, B = () =>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
}), !1) : !0, B = () => {
|
|
181
|
+
if (!u.hasInvalidBlock())
|
|
182
|
+
return !0;
|
|
183
|
+
const e = u.invalidBlockFields().includes(ee);
|
|
184
|
+
return s({
|
|
185
|
+
type: e ? c.Warning : c.Error,
|
|
186
|
+
message: o(e ? te : ne)
|
|
187
|
+
}), !1;
|
|
188
|
+
}, x = () => d.recommendationConfigs && Object.values(d.recommendationConfigs).find((t) => t.filters.find((n) => n.value === "")) !== void 0 ? (s({
|
|
184
189
|
type: c.Error,
|
|
185
190
|
message: o("newsletter.fill-all-necessary-fields")
|
|
186
|
-
}), !1) : !0,
|
|
191
|
+
}), !1) : !0, F = (e) => {
|
|
187
192
|
const t = /src="[^"]*\.(svg|pst)"/gm;
|
|
188
193
|
return e.match(t) === null ? !0 : (s({
|
|
189
194
|
type: c.Error,
|
|
190
195
|
message: o("newsletter.invalid-image-type")
|
|
191
196
|
}), !1);
|
|
192
|
-
},
|
|
197
|
+
}, D = (e) => {
|
|
193
198
|
const r = new DOMParser().parseFromString(e, "text/html").querySelectorAll(j);
|
|
194
199
|
return Array.from(r).find((i) => {
|
|
195
|
-
var
|
|
196
|
-
return !((
|
|
200
|
+
var l;
|
|
201
|
+
return !((l = i.id) != null && l.trim());
|
|
197
202
|
}) ? (s({
|
|
198
203
|
type: c.Error,
|
|
199
204
|
message: o("unsubscribe-templates.select-checkbox-groups")
|
|
@@ -201,26 +206,26 @@ const De = () => {
|
|
|
201
206
|
}, k = (e) => {
|
|
202
207
|
const r = new DOMParser().parseFromString(e, "text/html").querySelectorAll(z);
|
|
203
208
|
return Array.from(r).find((i) => {
|
|
204
|
-
var
|
|
205
|
-
return !((
|
|
209
|
+
var l;
|
|
210
|
+
return !((l = i.id) != null && l.trim());
|
|
206
211
|
}) ? (s({
|
|
207
212
|
type: c.Error,
|
|
208
213
|
message: o("unsubscribe-templates.select-radio-button-groups")
|
|
209
214
|
}), !1) : !0;
|
|
210
215
|
};
|
|
211
216
|
return { validateHtml: async (e, t, n = !1) => {
|
|
212
|
-
var v,
|
|
217
|
+
var v, h;
|
|
213
218
|
const r = [
|
|
214
|
-
...t.map((
|
|
219
|
+
...t.map((S) => S.value),
|
|
215
220
|
...q,
|
|
216
|
-
...((
|
|
221
|
+
...((h = (v = f.value) == null ? void 0 : v.template) == null ? void 0 : h.customFieldAttributes) ?? []
|
|
217
222
|
], a = M(e), i = await E(a);
|
|
218
|
-
return await R(e, r, n) &&
|
|
223
|
+
return await R(e, r, n) && A(e) && _(e) && b(e) && O(e, r, a, i) && w(e) && L(e) && N(e) && B() && x() && F(e) && D(e) && k(e);
|
|
219
224
|
} };
|
|
220
225
|
};
|
|
221
226
|
export {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
ge as buildPartnerAttributeNameSet,
|
|
228
|
+
pe as findInvalidCustomFields,
|
|
229
|
+
fe as parseRecommendationVariable,
|
|
230
|
+
ke as useHtmlValidator
|
|
226
231
|
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { getRecommendationFeedSourceMaps as y, URLS as I } from "../enums/extensions/recommendationBlock.js";
|
|
2
2
|
import { MinDeviceViewport as C, DefaultPadding as R } from "../enums/recommendation.js";
|
|
3
|
-
import { useRecommendationExtensionStore as
|
|
3
|
+
import { useRecommendationExtensionStore as f } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
4
4
|
import { generateCompleteFilterQuery as b } from "../extensions/Blocks/Recommendation/utils/filterUtil.js";
|
|
5
5
|
import { getPartnerRecommendationParams as h } from "../extensions/Blocks/Recommendation/utils/partnerCustomizations.js";
|
|
6
6
|
import { sanitizeProductIds as x } from "../extensions/Blocks/Recommendation/utils/productIds.js";
|
|
7
7
|
import { useConfigStore as P } from "../stores/config.js";
|
|
8
8
|
const B = () => ({
|
|
9
9
|
calculateCardWidth: ({
|
|
10
|
-
mobileLeftPadding:
|
|
10
|
+
mobileLeftPadding: a,
|
|
11
11
|
mobileRightPadding: c,
|
|
12
|
-
cardsInRow:
|
|
12
|
+
cardsInRow: i,
|
|
13
13
|
unresponsive: n
|
|
14
14
|
}) => {
|
|
15
|
-
const o = n ?
|
|
15
|
+
const o = n ? i : 1, e = a + c + (o - 1) * R;
|
|
16
16
|
return (C - e) / o;
|
|
17
17
|
},
|
|
18
|
-
getRecommendationCampaignData: (
|
|
19
|
-
const c =
|
|
18
|
+
getRecommendationCampaignData: (a) => {
|
|
19
|
+
const c = f(), i = Number(a), n = c.blockStates[i];
|
|
20
20
|
if (!n)
|
|
21
21
|
return {
|
|
22
22
|
textTrimming: !1,
|
|
@@ -44,17 +44,17 @@ const B = () => ({
|
|
|
44
44
|
discountAfterTextValue: ""
|
|
45
45
|
};
|
|
46
46
|
},
|
|
47
|
-
buildCampaignUrl: (
|
|
48
|
-
var
|
|
49
|
-
const
|
|
47
|
+
buildCampaignUrl: (a, c) => {
|
|
48
|
+
var l;
|
|
49
|
+
const i = f(), n = P(), o = Number(a);
|
|
50
50
|
let e;
|
|
51
51
|
if (c)
|
|
52
52
|
e = c;
|
|
53
53
|
else {
|
|
54
|
-
const
|
|
55
|
-
if (!
|
|
54
|
+
const s = i.blockStates[o];
|
|
55
|
+
if (!s)
|
|
56
56
|
return "";
|
|
57
|
-
const { recommendationConfigs: r } =
|
|
57
|
+
const { recommendationConfigs: r } = s;
|
|
58
58
|
e = {
|
|
59
59
|
strategy: r.strategy,
|
|
60
60
|
strategyId: r.strategyId,
|
|
@@ -67,17 +67,20 @@ const B = () => ({
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
if (e.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy"))
|
|
70
|
-
return
|
|
70
|
+
return i.recommendationCampaignUrls[a] = {
|
|
71
71
|
strategyId: e.strategyId,
|
|
72
72
|
locale: e.language,
|
|
73
73
|
currency: e.currencyCode
|
|
74
74
|
}, "";
|
|
75
|
-
const
|
|
75
|
+
const d = ((l = y().find((s) => s.key === e.strategy)) == null ? void 0 : l.path) || "";
|
|
76
|
+
if (!d)
|
|
77
|
+
return delete i.recommendationCampaignUrls[a], "";
|
|
78
|
+
const t = new URLSearchParams();
|
|
76
79
|
t.set("locale", e.language), t.set("currency", e.currencyCode), t.set("partnerName", n.partnerName), t.set("size", e.size), t.set("details", "true"), t.set("campaignId", n.variationId), e.strategy === "manualMerchandising" ? t.set("productId", x(e.productIds)) : e.strategy === "similarViewed" && t.set("productId", "{itemId}"), e.strategy === "userBased" && t.set("userId", "{user_id}");
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const p = decodeURIComponent(t.toString()),
|
|
80
|
-
return
|
|
80
|
+
const g = e.filters.filter((s) => s.isValid), u = b(g);
|
|
81
|
+
u && t.set("filter", u), e.shuffleProducts && t.set("shuffle", "true"), Object.entries(h(n.partnerName, e.strategy)).forEach(([s, r]) => t.set(s, r));
|
|
82
|
+
const p = decodeURIComponent(t.toString()), m = `${I.RECOMMENDATION_API_URL}/v2/${d}?${p}`;
|
|
83
|
+
return i.recommendationCampaignUrls[a] = m, m;
|
|
81
84
|
}
|
|
82
85
|
});
|
|
83
86
|
export {
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { useConfig as
|
|
5
|
-
import { BlockId as
|
|
6
|
-
import { getMigrationBannerHtml as
|
|
7
|
-
import { Block as
|
|
8
|
-
import { CURRENT_CONFIG_VERSION as
|
|
1
|
+
var D = Object.defineProperty;
|
|
2
|
+
var y = (l, c, t) => c in l ? D(l, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[c] = t;
|
|
3
|
+
var f = (l, c, t) => y(l, typeof c != "symbol" ? c + "" : c, t);
|
|
4
|
+
import { useConfig as C } from "../../../composables/useConfig.js";
|
|
5
|
+
import { BlockId as b } from "../../../enums/block.js";
|
|
6
|
+
import { getMigrationBannerHtml as A } from "../../../utils/migrationBannerHtml.js";
|
|
7
|
+
import { Block as E, BlockCompositionType as S, ModificationDescription as g } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
|
|
8
|
+
import { CURRENT_CONFIG_VERSION as N, PRE_RRS_CONFIG_VERSION as O } from "./constants/defaultConfig.js";
|
|
9
9
|
import { RECOMMENDATION_ID_ATTR as m } from "./constants/selectors.js";
|
|
10
|
-
import { regenerateMobileProductRows as
|
|
11
|
-
import { ensureMobileCssRulesExist as
|
|
10
|
+
import { regenerateMobileProductRows as M } from "./controls/main/utils.js";
|
|
11
|
+
import { ensureMobileCssRulesExist as _, setMobileLayoutOptOut as I, hasMobileLayoutOptOut as w } from "./controls/mobileLayout/cssRules.js";
|
|
12
12
|
import { RecommendationConfigService as s } from "./services/configService.js";
|
|
13
13
|
import { useRecommendationExtensionStore as d } from "./store/recommendation.js";
|
|
14
|
-
import { getDefaultTemplate as
|
|
15
|
-
import { useRecommendationBlockWarning as
|
|
16
|
-
const
|
|
17
|
-
let
|
|
18
|
-
class
|
|
14
|
+
import { getDefaultTemplate as x } from "./templates/grid/template.js";
|
|
15
|
+
import { useRecommendationBlockWarning as H } from "./useRecommendationBlockWarning.js";
|
|
16
|
+
const k = b.Recommendation, u = "recommendation-block-v2", R = "hide-price";
|
|
17
|
+
let B = !1;
|
|
18
|
+
class j extends E {
|
|
19
19
|
constructor() {
|
|
20
20
|
super();
|
|
21
21
|
/**
|
|
22
22
|
* Stores the ID generated in getTemplate() so onCreated() can reuse it.
|
|
23
23
|
* This avoids generating a new (different) ID in onCreated().
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
f(this, "_pendingBlockId", null);
|
|
26
26
|
}
|
|
27
27
|
getId() {
|
|
28
|
-
return
|
|
28
|
+
return k;
|
|
29
29
|
}
|
|
30
30
|
getIcon() {
|
|
31
31
|
return "recommendation-icon";
|
|
32
32
|
}
|
|
33
33
|
getBlockCompositionType() {
|
|
34
|
-
return
|
|
34
|
+
return S.CONTAINER;
|
|
35
35
|
}
|
|
36
36
|
getName() {
|
|
37
37
|
return this.api.translate("Recommendation Block");
|
|
@@ -42,8 +42,8 @@ class W extends b {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
getSettingsPanelTitleHtml() {
|
|
45
|
-
return
|
|
46
|
-
|
|
45
|
+
return A(
|
|
46
|
+
k,
|
|
47
47
|
this.api.translate("Recommendation Block"),
|
|
48
48
|
this.api.translate("This block is switched from the Old Version to the New Version. We recommend you check the Recommendation block and test your message to ensure it works properly.")
|
|
49
49
|
);
|
|
@@ -66,7 +66,7 @@ class W extends b {
|
|
|
66
66
|
*/
|
|
67
67
|
getTemplate() {
|
|
68
68
|
const t = this._generateNextId();
|
|
69
|
-
return this._pendingBlockId = t,
|
|
69
|
+
return this._pendingBlockId = t, x(t);
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Called when a new block is dropped into the template
|
|
@@ -89,23 +89,27 @@ class W extends b {
|
|
|
89
89
|
}
|
|
90
90
|
const i = this._pendingBlockId ?? this._generateNextId();
|
|
91
91
|
this._pendingBlockId = null, this._assignRecommendationId(t, i);
|
|
92
|
-
const { config:
|
|
92
|
+
const n = C().isFeatureEnabled("reusableRecommendationStrategy"), o = n && !s.hasConfig(t), { config: r, wasFreshDrop: a } = s.initializeConfig(
|
|
93
93
|
this.api,
|
|
94
94
|
t,
|
|
95
95
|
{
|
|
96
96
|
recommendationId: i,
|
|
97
|
-
configVersion:
|
|
97
|
+
configVersion: n ? N : O,
|
|
98
|
+
// An RRS block picks a strategy, not an algorithm, and its algorithm control is
|
|
99
|
+
// hidden. Keeping the `mostPopular` default would bake a most-popular feed URL
|
|
100
|
+
// at save for a block the user never configured.
|
|
101
|
+
...o ? { strategy: "" } : {}
|
|
98
102
|
}
|
|
99
|
-
),
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
103
|
+
), p = d();
|
|
104
|
+
if (p.setCurrentBlock(i), this._stampHidePrice(t, r.priceHideIfSameAsDiscounted), a) {
|
|
105
|
+
_(this.api);
|
|
106
|
+
const h = this._getBlockElement(t);
|
|
107
|
+
h && (I(this.api, h, !0), M({
|
|
104
108
|
currentNode: t,
|
|
105
109
|
documentModifier: this.api.getDocumentModifier()
|
|
106
110
|
}));
|
|
107
111
|
}
|
|
108
|
-
|
|
112
|
+
p.patchCurrentBlockConfig({ language: r.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
|
|
109
113
|
}
|
|
110
114
|
/**
|
|
111
115
|
* Called when the document changes or template is loaded
|
|
@@ -127,11 +131,11 @@ class W extends b {
|
|
|
127
131
|
}
|
|
128
132
|
this._healLingeringDuplicate(t), s.needsMigration(t) && this._migrateFromLegacy(t), this._healHidePriceAttr(t);
|
|
129
133
|
try {
|
|
130
|
-
|
|
134
|
+
B || (_(this.api), B = !0);
|
|
131
135
|
const e = s.getConfig(t), i = this._getBlockElement(t);
|
|
132
136
|
if (i) {
|
|
133
137
|
const n = !e.mobileLayoutEnabled;
|
|
134
|
-
|
|
138
|
+
w(i) !== n && I(this.api, i, n);
|
|
135
139
|
}
|
|
136
140
|
} catch {
|
|
137
141
|
}
|
|
@@ -161,7 +165,7 @@ class W extends b {
|
|
|
161
165
|
*/
|
|
162
166
|
_warnIfMultipleBlocks() {
|
|
163
167
|
try {
|
|
164
|
-
|
|
168
|
+
H().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
|
|
165
169
|
} catch {
|
|
166
170
|
}
|
|
167
171
|
}
|
|
@@ -211,9 +215,9 @@ class W extends b {
|
|
|
211
215
|
if (!o)
|
|
212
216
|
return;
|
|
213
217
|
let r = -1;
|
|
214
|
-
for (let
|
|
215
|
-
if (n[
|
|
216
|
-
r =
|
|
218
|
+
for (let a = 0; a < n.length; a++)
|
|
219
|
+
if (n[a] === o) {
|
|
220
|
+
r = a;
|
|
217
221
|
break;
|
|
218
222
|
}
|
|
219
223
|
if (r <= 0)
|
|
@@ -293,7 +297,7 @@ class W extends b {
|
|
|
293
297
|
*/
|
|
294
298
|
_stampHidePrice(t, e) {
|
|
295
299
|
const i = this._getBlockElement(t);
|
|
296
|
-
i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(
|
|
300
|
+
i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(R, String(e)).apply(new g("Stamp hide-price"));
|
|
297
301
|
}
|
|
298
302
|
/**
|
|
299
303
|
* Stamps `hide-price` from config only when the block lacks the attribute —
|
|
@@ -301,7 +305,7 @@ class W extends b {
|
|
|
301
305
|
*/
|
|
302
306
|
_healHidePriceAttr(t) {
|
|
303
307
|
const e = this._getBlockElement(t);
|
|
304
|
-
if (!e || !("getAttribute" in e) || e.getAttribute(
|
|
308
|
+
if (!e || !("getAttribute" in e) || e.getAttribute(R) !== null)
|
|
305
309
|
return;
|
|
306
310
|
const { priceHideIfSameAsDiscounted: i } = s.getConfig(t);
|
|
307
311
|
this._stampHidePrice(t, i);
|
|
@@ -338,6 +342,6 @@ class W extends b {
|
|
|
338
342
|
}
|
|
339
343
|
}
|
|
340
344
|
export {
|
|
341
|
-
|
|
342
|
-
|
|
345
|
+
k as BLOCK_ID,
|
|
346
|
+
j as RecommendationBlock
|
|
343
347
|
};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { getRecommendationFeedSourceMaps as k, strategyEditPath as G, URLS as
|
|
2
|
-
import { useRecommendationApi as
|
|
3
|
-
import { useConfigStore as
|
|
4
|
-
import { defineStore as
|
|
1
|
+
import { getRecommendationFeedSourceMaps as k, strategyEditPath as G, URLS as D, getOperatorOptions as _, PriceAttributes as L } from "../../../../enums/extensions/recommendationBlock.js";
|
|
2
|
+
import { useRecommendationApi as E } from "../../../../services/recommendationApi.js";
|
|
3
|
+
import { useConfigStore as P } from "../../../../stores/config.js";
|
|
4
|
+
import { defineStore as N } from "pinia";
|
|
5
5
|
import "../constants/selectors.js";
|
|
6
|
-
import { DEFAULT_MOBILE_CARDS_IN_ROW as
|
|
7
|
-
import { DEFAULT_NODE_CONFIG as x, EXCLUDED_ALGORITHM_IDS as
|
|
6
|
+
import { DEFAULT_MOBILE_CARDS_IN_ROW as U, DEFAULT_CARDS_IN_ROW as A } from "../constants/layout.js";
|
|
7
|
+
import { DEFAULT_NODE_CONFIG as x, EXCLUDED_ALGORITHM_IDS as B } from "../constants/defaultConfig.js";
|
|
8
8
|
import { getDefaultProducts as C } from "../templates/utils.js";
|
|
9
9
|
import { generateCompleteFilterQuery as v } from "../utils/filterUtil.js";
|
|
10
|
-
import { getPartnerRecommendationParams as
|
|
10
|
+
import { getPartnerRecommendationParams as M } from "../utils/partnerCustomizations.js";
|
|
11
11
|
import { sanitizeProductIds as O } from "../utils/productIds.js";
|
|
12
12
|
import { isFilterValid as T } from "../validation/filterSchema.js";
|
|
13
|
-
import {
|
|
14
|
-
const d =
|
|
13
|
+
import { getInvalidFields as z, isConfigValid as V } from "../validation/requiredFields.js";
|
|
14
|
+
const d = E();
|
|
15
15
|
let h = null, g = null, S = null, p = null, y = 0;
|
|
16
|
-
const
|
|
17
|
-
let
|
|
18
|
-
function
|
|
16
|
+
const b = /* @__PURE__ */ new Map();
|
|
17
|
+
let u = null;
|
|
18
|
+
function F() {
|
|
19
19
|
return {
|
|
20
|
-
cardsInRow:
|
|
20
|
+
cardsInRow: A,
|
|
21
21
|
currencySettings: {
|
|
22
22
|
name: "USD",
|
|
23
23
|
value: "USD",
|
|
@@ -31,13 +31,17 @@ function P() {
|
|
|
31
31
|
productIds: [],
|
|
32
32
|
id: 1,
|
|
33
33
|
language: "en_US",
|
|
34
|
-
mobileCardsInRow:
|
|
34
|
+
mobileCardsInRow: U,
|
|
35
35
|
mobileLayoutEnabled: !1,
|
|
36
36
|
orientation: "grid",
|
|
37
37
|
recommendedProducts: [],
|
|
38
38
|
sendProductRequestFlag: !1,
|
|
39
39
|
shuffleProducts: !1,
|
|
40
|
-
|
|
40
|
+
// With RRS on, a block that has not been synced yet must not claim an algorithm:
|
|
41
|
+
// that default is what reached buildCampaignUrl and baked a most-popular feed URL
|
|
42
|
+
// for a block the user never configured (SD-149916). Legacy blocks still get their real
|
|
43
|
+
// algorithm from the node-config sync or the save-time seed.
|
|
44
|
+
strategy: P().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
|
|
41
45
|
strategyId: "",
|
|
42
46
|
textTrimming: !0,
|
|
43
47
|
unresponsive: !1,
|
|
@@ -49,7 +53,7 @@ function P() {
|
|
|
49
53
|
}
|
|
50
54
|
function w() {
|
|
51
55
|
return {
|
|
52
|
-
recommendationConfigs:
|
|
56
|
+
recommendationConfigs: F(),
|
|
53
57
|
recommendationProducts: [],
|
|
54
58
|
filterStatus: !1,
|
|
55
59
|
filterSelectionDrawerStatus: !1,
|
|
@@ -60,7 +64,7 @@ function w() {
|
|
|
60
64
|
filterSnapshot: null
|
|
61
65
|
};
|
|
62
66
|
}
|
|
63
|
-
const
|
|
67
|
+
const j = () => ({
|
|
64
68
|
recommendationCampaignUrls: {},
|
|
65
69
|
activePredictiveAlgorithms: [],
|
|
66
70
|
languages: {},
|
|
@@ -73,8 +77,8 @@ const V = () => ({
|
|
|
73
77
|
blockStates: {},
|
|
74
78
|
currentRecommendationId: null,
|
|
75
79
|
configVersion: 0
|
|
76
|
-
}),
|
|
77
|
-
state: () =>
|
|
80
|
+
}), nt = N("guidoRecommendationExtension", {
|
|
81
|
+
state: () => j(),
|
|
78
82
|
getters: {
|
|
79
83
|
// ====================================================================
|
|
80
84
|
// Proxy Getters — Backward Compatible Access to Current Block State
|
|
@@ -84,7 +88,7 @@ const V = () => ({
|
|
|
84
88
|
* This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
|
|
85
89
|
*/
|
|
86
90
|
recommendationConfigs(t) {
|
|
87
|
-
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs :
|
|
91
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : F();
|
|
88
92
|
},
|
|
89
93
|
/**
|
|
90
94
|
* Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
|
|
@@ -140,7 +144,7 @@ const V = () => ({
|
|
|
140
144
|
},
|
|
141
145
|
getActivePredictiveAlgorithms: (t) => {
|
|
142
146
|
const e = k(), r = [];
|
|
143
|
-
return t.activePredictiveAlgorithms.filter((n) => !
|
|
147
|
+
return t.activePredictiveAlgorithms.filter((n) => !B.includes(n)).forEach((n) => {
|
|
144
148
|
r.push(...e.filter((s) => s.id === n));
|
|
145
149
|
}), r.map((n) => ({
|
|
146
150
|
text: n.name,
|
|
@@ -158,7 +162,7 @@ const V = () => ({
|
|
|
158
162
|
getFilterList() {
|
|
159
163
|
return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
|
|
160
164
|
let e;
|
|
161
|
-
return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` :
|
|
165
|
+
return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : L.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
|
|
162
166
|
text: t.displayName,
|
|
163
167
|
value: e,
|
|
164
168
|
type: t.attributeType
|
|
@@ -287,7 +291,7 @@ const V = () => ({
|
|
|
287
291
|
* Creates a filter with the first available attribute and operator pre-selected.
|
|
288
292
|
*/
|
|
289
293
|
createDefaultFilter(t, e) {
|
|
290
|
-
const [r] = this.getFilterList, [n] =
|
|
294
|
+
const [r] = this.getFilterList, [n] = _(r == null ? void 0 : r.type);
|
|
291
295
|
return {
|
|
292
296
|
type: "standardFilter",
|
|
293
297
|
attribute: (r == null ? void 0 : r.value) ?? "",
|
|
@@ -381,7 +385,7 @@ const V = () => ({
|
|
|
381
385
|
* page, so anyone who never does pays nothing.
|
|
382
386
|
*/
|
|
383
387
|
_armStrategyPageReturn(t) {
|
|
384
|
-
window.open(t, "_blank", "noopener"),
|
|
388
|
+
window.open(t, "_blank", "noopener"), u == null || u();
|
|
385
389
|
let e = !1;
|
|
386
390
|
const r = () => {
|
|
387
391
|
document.hidden || e || (e = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
|
|
@@ -389,8 +393,8 @@ const V = () => ({
|
|
|
389
393
|
e = !1;
|
|
390
394
|
}));
|
|
391
395
|
};
|
|
392
|
-
document.addEventListener("visibilitychange", r), window.addEventListener("focus", r),
|
|
393
|
-
document.removeEventListener("visibilitychange", r), window.removeEventListener("focus", r),
|
|
396
|
+
document.addEventListener("visibilitychange", r), window.addEventListener("focus", r), u = () => {
|
|
397
|
+
document.removeEventListener("visibilitychange", r), window.removeEventListener("focus", r), u = null;
|
|
394
398
|
};
|
|
395
399
|
},
|
|
396
400
|
/**
|
|
@@ -407,14 +411,14 @@ const V = () => ({
|
|
|
407
411
|
},
|
|
408
412
|
/** Lets the editor tear the return listeners down when it unmounts. */
|
|
409
413
|
disposeStrategyPageReturn() {
|
|
410
|
-
|
|
414
|
+
u == null || u();
|
|
411
415
|
},
|
|
412
416
|
/**
|
|
413
417
|
* Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
|
|
414
418
|
* new strategy is offered; the user picks it themselves.
|
|
415
419
|
*/
|
|
416
420
|
openStrategyCreation() {
|
|
417
|
-
this._armStrategyPageReturn(
|
|
421
|
+
this._armStrategyPageReturn(D.RECOMMENDATION_STRATEGY_CREATE_PATH);
|
|
418
422
|
},
|
|
419
423
|
/**
|
|
420
424
|
* Opens the strategy's edit page. Every return refreshes the list so the block reflects the
|
|
@@ -558,7 +562,14 @@ const V = () => ({
|
|
|
558
562
|
* every block's recommendationConfigs across user edits.
|
|
559
563
|
*/
|
|
560
564
|
hasInvalidBlock() {
|
|
561
|
-
return Object.values(this.blockStates).some((t) => !
|
|
565
|
+
return Object.values(this.blockStates).some((t) => !V(t.recommendationConfigs, this));
|
|
566
|
+
},
|
|
567
|
+
/**
|
|
568
|
+
* Which required fields are missing across the design's blocks, so the save
|
|
569
|
+
* toaster can name the cause instead of always showing the generic message.
|
|
570
|
+
*/
|
|
571
|
+
invalidBlockFields() {
|
|
572
|
+
return Object.values(this.blockStates).flatMap((t) => z(t.recommendationConfigs, this));
|
|
562
573
|
},
|
|
563
574
|
// ====================================================================
|
|
564
575
|
// Per-Block Product Fetching
|
|
@@ -579,12 +590,17 @@ const V = () => ({
|
|
|
579
590
|
},
|
|
580
591
|
async _doFetchProducts() {
|
|
581
592
|
var f;
|
|
582
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n =
|
|
593
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = P();
|
|
583
594
|
if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
|
|
584
595
|
await this._doFetchStrategyProducts(t);
|
|
585
596
|
return;
|
|
586
597
|
}
|
|
587
|
-
const s =
|
|
598
|
+
const s = parseInt(r.size) || 6;
|
|
599
|
+
if (!r.strategy) {
|
|
600
|
+
this._setBlockProducts(t, [], s);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const i = r.filters.filter((m) => m.isValid), o = v(i), c = ((f = k().find((m) => m.key === r.strategy)) == null ? void 0 : f.path) || "", a = {
|
|
588
604
|
locale: r.language,
|
|
589
605
|
currency: r.currencySettings.value,
|
|
590
606
|
partnerName: n.partnerName,
|
|
@@ -592,21 +608,21 @@ const V = () => ({
|
|
|
592
608
|
details: !0,
|
|
593
609
|
campaignId: n.variationId
|
|
594
610
|
};
|
|
595
|
-
r.strategy === "manualMerchandising" ? a.productId = O(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"),
|
|
611
|
+
r.strategy === "manualMerchandising" ? a.productId = O(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), o && (a.filter = o), r.shuffleProducts && (a.shuffle = !0), Object.assign(
|
|
596
612
|
a,
|
|
597
|
-
|
|
613
|
+
M(n.partnerName, r.strategy)
|
|
598
614
|
);
|
|
599
615
|
let l;
|
|
600
616
|
try {
|
|
601
|
-
l = await d.fetchRecommendationProducts(
|
|
617
|
+
l = await d.fetchRecommendationProducts(c, a);
|
|
602
618
|
} catch {
|
|
603
619
|
l = [];
|
|
604
620
|
}
|
|
605
|
-
this._setBlockProducts(t, l,
|
|
621
|
+
this._setBlockProducts(t, l, s);
|
|
606
622
|
},
|
|
607
623
|
async _doFetchStrategyProducts(t) {
|
|
608
|
-
const e = (
|
|
609
|
-
|
|
624
|
+
const e = (b.get(t) ?? 0) + 1;
|
|
625
|
+
b.set(t, e);
|
|
610
626
|
const { recommendationConfigs: r } = this.blockStates[t];
|
|
611
627
|
let n = [], s = parseInt(r.size) || 6;
|
|
612
628
|
try {
|
|
@@ -616,13 +632,13 @@ const V = () => ({
|
|
|
616
632
|
);
|
|
617
633
|
if (i) {
|
|
618
634
|
s = parseInt(String(i.size ?? "")) || s;
|
|
619
|
-
const o = r.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), l = a.indexOf("?"), f = l === -1 ? a : a.slice(0, l),
|
|
620
|
-
|
|
635
|
+
const o = r.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), l = a.indexOf("?"), f = l === -1 ? a : a.slice(0, l), m = (l === -1 ? "" : a.slice(l + 1)).split("&").filter((R) => R && !R.startsWith("user_id=")), I = m.length ? `${f}?${m.join("&")}` : f;
|
|
636
|
+
I && (n = await d.fetchRecommendationProductsByUrl(I));
|
|
621
637
|
}
|
|
622
638
|
} catch {
|
|
623
639
|
n = [];
|
|
624
640
|
}
|
|
625
|
-
|
|
641
|
+
b.get(t) === e && this._setBlockProducts(t, n, s);
|
|
626
642
|
},
|
|
627
643
|
_setBlockProducts(t, e, r) {
|
|
628
644
|
if (!this.blockStates[t])
|
|
@@ -636,5 +652,5 @@ const V = () => ({
|
|
|
636
652
|
}
|
|
637
653
|
});
|
|
638
654
|
export {
|
|
639
|
-
|
|
655
|
+
nt as useRecommendationExtensionStore
|
|
640
656
|
};
|
|
@@ -1,34 +1,43 @@
|
|
|
1
|
-
const
|
|
1
|
+
const o = "strategy", u = [
|
|
2
2
|
{
|
|
3
3
|
key: "locale",
|
|
4
4
|
getValue: (e) => e.language,
|
|
5
5
|
getAvailableOptions: (e) => Object.keys(e.languages)
|
|
6
6
|
},
|
|
7
|
+
{
|
|
8
|
+
// A block feeds from either an assigned strategy (RRS) or an algorithm (legacy).
|
|
9
|
+
// Only a fresh RRS block has neither, and its feed URL would otherwise be built
|
|
10
|
+
// from an empty algorithm path.
|
|
11
|
+
key: o,
|
|
12
|
+
getValue: (e) => e.strategyId || e.strategy
|
|
13
|
+
},
|
|
7
14
|
{
|
|
8
15
|
key: "currency",
|
|
9
16
|
getValue: (e) => e.currencySettings.value,
|
|
10
17
|
getAvailableOptions: (e) => e.currencyList.map((n) => n.text),
|
|
11
18
|
canBeEmpty: !0
|
|
12
19
|
}
|
|
13
|
-
],
|
|
14
|
-
function c(e, n, i =
|
|
20
|
+
], l = "newsletter.recommendation-fill-required-fields", g = "newsletter.recommendation-assign-strategy";
|
|
21
|
+
function c(e, n, i = u) {
|
|
15
22
|
return i.filter((t) => {
|
|
16
|
-
var
|
|
23
|
+
var s;
|
|
17
24
|
if (t.condition && !t.condition(e))
|
|
18
25
|
return !1;
|
|
19
|
-
const
|
|
20
|
-
if (t.canBeEmpty && (
|
|
26
|
+
const a = (s = t.getAvailableOptions) == null ? void 0 : s.call(t, n);
|
|
27
|
+
if (t.canBeEmpty && (a == null ? void 0 : a.length) === 0)
|
|
21
28
|
return !1;
|
|
22
|
-
const
|
|
23
|
-
return
|
|
29
|
+
const r = t.getValue(e);
|
|
30
|
+
return r ? a !== void 0 && !a.includes(r) : !0;
|
|
24
31
|
}).map((t) => t.key);
|
|
25
32
|
}
|
|
26
|
-
function
|
|
33
|
+
function y(e, n) {
|
|
27
34
|
return c(e, n).length === 0;
|
|
28
35
|
}
|
|
29
36
|
export {
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
u as REQUIRED_RECOMMENDATION_FIELDS,
|
|
38
|
+
g as RecommendationAssignStrategyKey,
|
|
39
|
+
l as RecommendationRequiredFieldsKey,
|
|
40
|
+
o as STRATEGY_FIELD_KEY,
|
|
32
41
|
c as getInvalidFields,
|
|
33
|
-
|
|
42
|
+
y as isConfigValid
|
|
34
43
|
};
|
|
@@ -494,6 +494,11 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
|
|
|
494
494
|
* every block's recommendationConfigs across user edits.
|
|
495
495
|
*/
|
|
496
496
|
hasInvalidBlock(): boolean;
|
|
497
|
+
/**
|
|
498
|
+
* Which required fields are missing across the design's blocks, so the save
|
|
499
|
+
* toaster can name the cause instead of always showing the generic message.
|
|
500
|
+
*/
|
|
501
|
+
invalidBlockFields(): string[];
|
|
497
502
|
fetchRecommendationProducts(): Promise<void>;
|
|
498
503
|
_doFetchProducts(): Promise<void>;
|
|
499
504
|
_doFetchStrategyProducts(blockId: number): Promise<void>;
|
|
@@ -15,7 +15,11 @@ export interface RequiredField {
|
|
|
15
15
|
getAvailableOptions?: (store: ExtensionStoreSlice) => string[];
|
|
16
16
|
condition?: (config: PerBlockConfigs) => boolean;
|
|
17
17
|
}
|
|
18
|
+
/** Field key of the strategy/algorithm descriptor, used to pick the toaster message. */
|
|
19
|
+
export declare const STRATEGY_FIELD_KEY = "strategy";
|
|
18
20
|
export declare const REQUIRED_RECOMMENDATION_FIELDS: RequiredField[];
|
|
19
21
|
export declare const RecommendationRequiredFieldsKey = "newsletter.recommendation-fill-required-fields";
|
|
22
|
+
/** Shown instead of the generic message when the only thing missing is the strategy. */
|
|
23
|
+
export declare const RecommendationAssignStrategyKey = "newsletter.recommendation-assign-strategy";
|
|
20
24
|
export declare function getInvalidFields(config: PerBlockConfigs, store: ExtensionStoreSlice, fields?: RequiredField[]): string[];
|
|
21
25
|
export declare function isConfigValid(config: PerBlockConfigs, store: ExtensionStoreSlice): boolean;
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { useActionsApi as
|
|
2
|
-
import { useConfig as
|
|
3
|
-
import { useHtmlCompiler as
|
|
4
|
-
import { DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as a } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
|
|
5
|
-
import { useRecommendationExtensionStore as
|
|
6
|
-
import { mapLegacyStrategy as
|
|
1
|
+
import { useActionsApi as R } from "../composables/useActionsApi.js";
|
|
2
|
+
import { useConfig as k } from "../composables/useConfig.js";
|
|
3
|
+
import { useHtmlCompiler as N } from "../composables/useHtmlCompiler.js";
|
|
4
|
+
import { CURRENT_CONFIG_VERSION as U, DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as a } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
|
|
5
|
+
import { useRecommendationExtensionStore as T } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
6
|
+
import { mapLegacyStrategy as x } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
|
|
7
7
|
import { DATA_ATTRIBUTES as A } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
|
|
8
|
-
import { parsePageList as
|
|
9
|
-
import { useDynamicContentStore as
|
|
10
|
-
import { useEditorStore as
|
|
11
|
-
import { useUnsubscribeStore as
|
|
8
|
+
import { parsePageList as L } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
|
|
9
|
+
import { useDynamicContentStore as z } from "../stores/dynamic-content.js";
|
|
10
|
+
import { useEditorStore as _ } from "../stores/editor.js";
|
|
11
|
+
import { useUnsubscribeStore as O } from "../stores/unsubscribe.js";
|
|
12
12
|
import { assignTrackingIds as v } from "./linkTrackingIds.js";
|
|
13
|
-
function
|
|
13
|
+
function B(m) {
|
|
14
14
|
const i = new DOMParser().parseFromString(m, "text/html").querySelectorAll(`[${A.PAGE_LIST}]`), t = [];
|
|
15
15
|
return i.forEach((s) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const r = s.getAttribute(A.PAGE_LIST);
|
|
17
|
+
r && t.push(...L(r));
|
|
18
18
|
}), [...new Set(t)];
|
|
19
19
|
}
|
|
20
|
-
async function
|
|
20
|
+
async function G(m) {
|
|
21
21
|
const n = new DOMParser().parseFromString(m, "text/html").querySelectorAll(".recommendation-block-v2");
|
|
22
22
|
if (n.length === 0)
|
|
23
23
|
return;
|
|
24
|
-
const i =
|
|
24
|
+
const i = T();
|
|
25
25
|
n.forEach((t) => {
|
|
26
|
-
var
|
|
27
|
-
const s = t.getAttribute("recommendation-id"),
|
|
28
|
-
if (!Number.isFinite(
|
|
26
|
+
var l, g, f, c, y;
|
|
27
|
+
const s = t.getAttribute("recommendation-id"), r = s ? Number(s) : NaN;
|
|
28
|
+
if (!Number.isFinite(r))
|
|
29
29
|
return;
|
|
30
30
|
const p = t.getAttribute("esd-ext-config");
|
|
31
31
|
if (!p)
|
|
@@ -38,11 +38,11 @@ async function M(m) {
|
|
|
38
38
|
}
|
|
39
39
|
if (!e || typeof e != "object" || Array.isArray(e))
|
|
40
40
|
return;
|
|
41
|
-
const
|
|
41
|
+
const b = !!e.strategyId || (e.configVersion ?? 0) >= U, o = {
|
|
42
42
|
// Normalize legacy strategy keys (e.g. `mostViewed` → `mostPopular`) so
|
|
43
43
|
// already-saved blocks that never re-run the migrator don't emit a
|
|
44
44
|
// strategy-less campaign URL. `||` also catches '' / undefined (SD-144882).
|
|
45
|
-
strategy:
|
|
45
|
+
strategy: x(e.strategy) || (b ? "" : a.strategy),
|
|
46
46
|
strategyId: e.strategyId ?? a.strategyId,
|
|
47
47
|
language: e.language ?? a.language,
|
|
48
48
|
size: e.size ?? a.size,
|
|
@@ -51,13 +51,13 @@ async function M(m) {
|
|
|
51
51
|
productIds: e.productIds ?? [...a.productIds],
|
|
52
52
|
filters: e.filters ?? [...a.filters],
|
|
53
53
|
shuffleProducts: e.shuffleProducts ?? a.shuffleProducts,
|
|
54
|
-
currencyCode: ((
|
|
55
|
-
currencyAlignment: ((
|
|
56
|
-
currencyDecimalCount: ((
|
|
57
|
-
currencyDecimalSeparator: ((
|
|
58
|
-
currencyThousandSeparator: ((
|
|
54
|
+
currencyCode: ((l = e.currency) == null ? void 0 : l.code) ?? d.code,
|
|
55
|
+
currencyAlignment: ((g = e.currency) == null ? void 0 : g.alignment) ?? d.alignment,
|
|
56
|
+
currencyDecimalCount: ((f = e.currency) == null ? void 0 : f.decimalCount) ?? d.decimalCount,
|
|
57
|
+
currencyDecimalSeparator: ((c = e.currency) == null ? void 0 : c.decimalSeparator) ?? d.decimalSeparator,
|
|
58
|
+
currencyThousandSeparator: ((y = e.currency) == null ? void 0 : y.thousandSeparator) ?? d.thousandSeparator
|
|
59
59
|
};
|
|
60
|
-
i.seedBlockUrlConfig(
|
|
60
|
+
i.seedBlockUrlConfig(r, o);
|
|
61
61
|
});
|
|
62
62
|
try {
|
|
63
63
|
await i.fetchRecommendationCreateData();
|
|
@@ -68,51 +68,51 @@ async function M(m) {
|
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
const
|
|
72
|
-
const m =
|
|
71
|
+
const ee = () => {
|
|
72
|
+
const m = z(), u = _(), n = O(), { getCompiledEmail: i, getTemplateData: t, updateHtmlAndCss: s } = R(), { compileHtml: r, compileAmpHtml: p } = N(), { isFeatureEnabled: e } = k();
|
|
73
73
|
return {
|
|
74
74
|
prepareTemplateDetails: async () => {
|
|
75
|
-
let { html:
|
|
75
|
+
let { html: o, css: l, syncModulesIds: g = [] } = await t();
|
|
76
76
|
if (e("linkTrackingIds"))
|
|
77
77
|
try {
|
|
78
|
-
const { html:
|
|
79
|
-
u.trackIdSessionFloor = H,
|
|
80
|
-
} catch (
|
|
81
|
-
console.error("Tracking-id step failed, saving anyway:",
|
|
78
|
+
const { html: h, changed: P, nextSeq: H } = v(o, u.trackIdSessionFloor);
|
|
79
|
+
u.trackIdSessionFloor = H, P && (o = h, await s(h, l));
|
|
80
|
+
} catch (h) {
|
|
81
|
+
console.error("Tracking-id step failed, saving anyway:", h);
|
|
82
82
|
}
|
|
83
|
-
const { html: f, ampHtml: c = "", ampErrors:
|
|
83
|
+
const { html: f, ampHtml: c = "", ampErrors: y = [] } = await i({
|
|
84
84
|
minimize: !0,
|
|
85
85
|
resetDataSavedFlag: !1
|
|
86
86
|
});
|
|
87
|
-
n.selectedUnsubscribePages.length && await n.fetchTemplates(), await
|
|
88
|
-
const { compiledHtml: I, stats:
|
|
87
|
+
n.selectedUnsubscribePages.length && await n.fetchTemplates(), await G(o);
|
|
88
|
+
const { compiledHtml: I, stats: S, appliedRules: E } = r(f), w = c && p(c).compiledHtml, D = m.getSelectedDynamicContentList, F = T(), C = B(o);
|
|
89
89
|
return console.debug("HTML Compilation Stats:", {
|
|
90
|
-
originalSize:
|
|
91
|
-
compiledSize:
|
|
92
|
-
reduction: `${
|
|
93
|
-
appliedRules:
|
|
94
|
-
executionTime: `${
|
|
90
|
+
originalSize: S.originalSize,
|
|
91
|
+
compiledSize: S.compiledSize,
|
|
92
|
+
reduction: `${S.reductionPercentage.toFixed(2)}%`,
|
|
93
|
+
appliedRules: E,
|
|
94
|
+
executionTime: `${S.executionTime.toFixed(2)}ms`
|
|
95
95
|
}), {
|
|
96
|
-
dynamicContentList:
|
|
96
|
+
dynamicContentList: D,
|
|
97
97
|
compiledHtml: I,
|
|
98
|
-
rawHtml:
|
|
98
|
+
rawHtml: o,
|
|
99
99
|
css: l,
|
|
100
|
-
ampHtml:
|
|
101
|
-
ampErrors:
|
|
100
|
+
ampHtml: w,
|
|
101
|
+
ampErrors: y,
|
|
102
102
|
modules: g.map(Number),
|
|
103
103
|
recommendation: {
|
|
104
|
-
campaignUrls:
|
|
104
|
+
campaignUrls: F.recommendationCampaignUrls,
|
|
105
105
|
configs: {}
|
|
106
106
|
},
|
|
107
107
|
unsubscribe: {
|
|
108
|
-
status:
|
|
109
|
-
config:
|
|
108
|
+
status: C.length > 0,
|
|
109
|
+
config: C
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
};
|
|
115
115
|
export {
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
B as getHtmlReferencedUnsubscribePages,
|
|
117
|
+
ee as useTemplatePreparation
|
|
118
118
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useinsider/guido",
|
|
3
|
-
"version": "3.18.1-beta.
|
|
3
|
+
"version": "3.18.1-beta.d82285c",
|
|
4
4
|
"description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
|
|
5
5
|
"main": "./dist/guido.umd.cjs",
|
|
6
6
|
"module": "./dist/library.js",
|