@useinsider/guido 3.3.0-beta.d5c796a → 3.3.0-beta.ddabd14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Guido.vue.js +1 -1
- package/dist/components/Guido.vue2.js +103 -91
- package/dist/components/organisms/extensions/recommendation/FilterItem.vue.js +9 -11
- package/dist/components/organisms/extensions/recommendation/FilterItem.vue2.js +70 -35
- package/dist/components/organisms/header/MiddleSlot.vue.js +1 -1
- package/dist/components/organisms/header/MiddleSlot.vue2.js +7 -8
- package/dist/composables/useCortexBlueprintBridge.js +36 -61
- package/dist/composables/useHtmlValidator.js +41 -36
- package/dist/composables/useRecommendation.js +2 -2
- package/dist/composables/useStripo.js +66 -62
- package/dist/config/migrator/index.js +21 -10
- package/dist/config/migrator/radioButtonMigrator.js +73 -48
- package/dist/enums/extensions/recommendationBlock.js +101 -46
- package/dist/enums/unsubscribe.js +25 -24
- package/dist/extensions/Blocks/Checkbox/control.js +23 -23
- package/dist/extensions/Blocks/RadioButton/control.js +15 -15
- package/dist/extensions/Blocks/RadioButton/template.js +6 -6
- package/dist/extensions/Blocks/Recommendation/block.js +43 -36
- package/dist/extensions/Blocks/Recommendation/services/configService.js +33 -26
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +35 -26
- package/dist/extensions/Blocks/Recommendation/utils/filterUtil.js +25 -12
- package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +33 -0
- package/dist/guido.css +1 -1
- package/dist/services/unsubscribeApi.js +6 -6
- package/dist/src/composables/useCortexBlueprintBridge.d.ts +2 -5
- package/dist/src/enums/extensions/recommendationBlock.d.ts +6 -1
- package/dist/src/enums/unsubscribe.d.ts +8 -3
- package/dist/src/extensions/Blocks/RadioButton/template.d.ts +1 -1
- package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +11 -3
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +7 -1
- package/dist/src/extensions/Blocks/Recommendation/utils/filterUtil.d.ts +2 -0
- package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +21 -0
- package/dist/utils/templatePreparation.js +57 -50
- package/package.json +1 -1
- package/dist/components/organisms/header/AiStatusPill.vue.js +0 -18
- package/dist/components/organisms/header/AiStatusPill.vue2.js +0 -13
- package/dist/src/components/organisms/header/AiStatusPill.vue.d.ts +0 -2
- package/dist/src/stores/ai-status.d.ts +0 -41
- package/dist/stores/ai-status.js +0 -25
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ModificationDescription as
|
|
2
|
-
import { CURRENT_CONFIG_VERSION as l, DEFAULT_NODE_CONFIG as
|
|
1
|
+
import { ModificationDescription as C } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
|
|
2
|
+
import { CURRENT_CONFIG_VERSION as l, DEFAULT_NODE_CONFIG as c } from "../constants/defaultConfig.js";
|
|
3
3
|
import { setCurrencyAttributes as D } from "../controls/main/utils.js";
|
|
4
4
|
import { hasMinimalConfig as A } from "../types/nodeConfig.js";
|
|
5
5
|
function S(e) {
|
|
@@ -8,7 +8,7 @@ function S(e) {
|
|
|
8
8
|
function N(e) {
|
|
9
9
|
return e === "." || e === "," || e === " " || e === "";
|
|
10
10
|
}
|
|
11
|
-
class
|
|
11
|
+
class M {
|
|
12
12
|
// ========================================================================
|
|
13
13
|
// Read Operations
|
|
14
14
|
// ========================================================================
|
|
@@ -83,7 +83,7 @@ class V {
|
|
|
83
83
|
* @returns The new complete configuration
|
|
84
84
|
*/
|
|
85
85
|
static updateConfig(i, t, o, n) {
|
|
86
|
-
const
|
|
86
|
+
const r = this.getConfig(t), u = this.deepMerge(r, o);
|
|
87
87
|
return this.saveConfig(i, t, u, n), u;
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
@@ -91,21 +91,28 @@ class V {
|
|
|
91
91
|
*
|
|
92
92
|
* Called when a block is first created (dropped into template).
|
|
93
93
|
* Can optionally merge in partial config from migration.
|
|
94
|
+
*
|
|
95
|
+
* The `wasFreshDrop` flag distinguishes a brand-new drop (no prior config)
|
|
96
|
+
* from a clone (Stripo replays the source's setNodeConfig payload before
|
|
97
|
+
* onCreated fires). Callers use this to skip side-effects already inherited
|
|
98
|
+
* from the source.
|
|
94
99
|
* @example
|
|
95
100
|
* // In Block.onCreated lifecycle
|
|
96
|
-
* RecommendationConfigService.initializeConfig(this.api, node);
|
|
101
|
+
* const { config, wasFreshDrop } = RecommendationConfigService.initializeConfig(this.api, node);
|
|
97
102
|
* @param api - Stripo extension API with document modifier
|
|
98
103
|
* @param node - The immutable HTML node to initialize
|
|
99
104
|
* @param partialConfig - Optional partial config to merge with defaults
|
|
100
|
-
* @returns The initialized configuration
|
|
105
|
+
* @returns The initialized configuration and whether the node was a fresh drop
|
|
101
106
|
*/
|
|
102
107
|
static initializeConfig(i, t, o) {
|
|
108
|
+
if (this.hasConfig(t))
|
|
109
|
+
return { config: o ? this.updateConfig(i, t, o, "Initialize recommendation block") : this.getConfig(t), wasFreshDrop: !1 };
|
|
103
110
|
const n = o ? this.mergeWithDefaults(o) : this.cloneDefaults();
|
|
104
111
|
return this.saveConfig(i, t, n, "Initialize recommendation block"), D({
|
|
105
112
|
currentNode: t,
|
|
106
113
|
documentModifier: i.getDocumentModifier(),
|
|
107
114
|
currency: n.currency
|
|
108
|
-
}), n;
|
|
115
|
+
}), { config: n, wasFreshDrop: !0 };
|
|
109
116
|
}
|
|
110
117
|
/**
|
|
111
118
|
* Save complete configuration to a node
|
|
@@ -118,9 +125,9 @@ class V {
|
|
|
118
125
|
*/
|
|
119
126
|
static saveConfig(i, t, o, n) {
|
|
120
127
|
try {
|
|
121
|
-
i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new
|
|
122
|
-
} catch (
|
|
123
|
-
console.warn("[RecommendationConfigService] Failed to save config:",
|
|
128
|
+
i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new C(n));
|
|
129
|
+
} catch (r) {
|
|
130
|
+
console.warn("[RecommendationConfigService] Failed to save config:", r);
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
// ========================================================================
|
|
@@ -157,29 +164,29 @@ class V {
|
|
|
157
164
|
s && typeof s == "object" && (Object.assign(o, s), o.configVersion = l);
|
|
158
165
|
} catch {
|
|
159
166
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
167
|
+
const r = t.getAttribute("data-layout");
|
|
168
|
+
r === "list" || r === "horizontal" ? o.layout = "list" : (r === "grid" || r === "vertical") && (o.layout = "grid");
|
|
162
169
|
const u = t.getAttribute("data-card-composition");
|
|
163
170
|
u && (o.composition = u.split(",").filter(Boolean));
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
171
|
+
const b = t.getAttribute("data-column-spacing");
|
|
172
|
+
b && (o.columnSpacing = parseInt(b) || c.columnSpacing);
|
|
173
|
+
const p = t.getAttribute("data-row-spacing");
|
|
174
|
+
if (p && (o.rowSpacing = parseInt(p) || c.rowSpacing), !o.currency) {
|
|
168
175
|
const s = t.getAttribute("currency"), y = t.getAttribute("currency-symbol"), d = t.getAttribute("currency-alignment"), f = t.getAttribute("currency-thousand-separator"), g = t.getAttribute("currency-decimal-separator"), m = t.getAttribute("currency-decimal-count");
|
|
169
176
|
if (s || y || d || f || g || m) {
|
|
170
|
-
const a =
|
|
177
|
+
const a = c.currency, h = m ? parseInt(m) : NaN;
|
|
171
178
|
o.currency = {
|
|
172
179
|
code: s ?? a.code,
|
|
173
180
|
symbol: y ?? a.symbol,
|
|
174
181
|
alignment: d === "0" ? "before" : "after",
|
|
175
|
-
decimalCount: Number.isFinite(
|
|
182
|
+
decimalCount: Number.isFinite(h) ? h : a.decimalCount,
|
|
176
183
|
decimalSeparator: S(g) ? g : a.decimalSeparator,
|
|
177
184
|
thousandSeparator: N(f) ? f : a.thousandSeparator
|
|
178
185
|
};
|
|
179
186
|
}
|
|
180
187
|
}
|
|
181
188
|
}
|
|
182
|
-
return this.initializeConfig(i, t, o);
|
|
189
|
+
return this.initializeConfig(i, t, o).config;
|
|
183
190
|
}
|
|
184
191
|
/**
|
|
185
192
|
* Check if configuration needs migration
|
|
@@ -197,12 +204,12 @@ class V {
|
|
|
197
204
|
*/
|
|
198
205
|
static cloneDefaults() {
|
|
199
206
|
return {
|
|
200
|
-
...
|
|
201
|
-
currency: { ...
|
|
202
|
-
omnibusPrice: { ...
|
|
203
|
-
omnibusDiscount: { ...
|
|
204
|
-
composition: [...
|
|
205
|
-
visibility: { ...
|
|
207
|
+
...c,
|
|
208
|
+
currency: { ...c.currency },
|
|
209
|
+
omnibusPrice: { ...c.omnibusPrice },
|
|
210
|
+
omnibusDiscount: { ...c.omnibusDiscount },
|
|
211
|
+
composition: [...c.composition],
|
|
212
|
+
visibility: { ...c.visibility },
|
|
206
213
|
filters: [],
|
|
207
214
|
productIds: [],
|
|
208
215
|
recommendationId: 0
|
|
@@ -272,5 +279,5 @@ class V {
|
|
|
272
279
|
}
|
|
273
280
|
}
|
|
274
281
|
export {
|
|
275
|
-
|
|
282
|
+
M as RecommendationConfigService
|
|
276
283
|
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getRecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as y } from "../../../../enums/extensions/recommendationBlock.js";
|
|
2
2
|
import { useRecommendationApi as C } from "../../../../services/recommendationApi.js";
|
|
3
3
|
import { useConfigStore as G } from "../../../../stores/config.js";
|
|
4
4
|
import { defineStore as P } from "pinia";
|
|
5
5
|
import { DEFAULT_CARDS_IN_ROW as F } from "../constants/layout.js";
|
|
6
6
|
import { EXCLUDED_ALGORITHM_IDS as D } from "../constants/defaultConfig.js";
|
|
7
|
-
import { getDefaultProducts as
|
|
7
|
+
import { getDefaultProducts as S } from "../templates/utils.js";
|
|
8
8
|
import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
|
|
9
9
|
import { isFilterValid as w } from "../validation/filterSchema.js";
|
|
10
|
+
import { isConfigValid as v } from "../validation/requiredFields.js";
|
|
10
11
|
const h = C();
|
|
11
|
-
let
|
|
12
|
+
let m = null, u = null, d = null;
|
|
12
13
|
function k() {
|
|
13
14
|
return {
|
|
14
15
|
cardsInRow: F,
|
|
@@ -48,7 +49,7 @@ function I() {
|
|
|
48
49
|
filterSnapshot: null
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
|
-
const
|
|
52
|
+
const N = () => ({
|
|
52
53
|
recommendationCampaignUrls: {},
|
|
53
54
|
activePredictiveAlgorithms: [],
|
|
54
55
|
languages: {},
|
|
@@ -57,8 +58,8 @@ const v = () => ({
|
|
|
57
58
|
blockStates: {},
|
|
58
59
|
currentRecommendationId: null,
|
|
59
60
|
configVersion: 0
|
|
60
|
-
}),
|
|
61
|
-
state: () =>
|
|
61
|
+
}), M = P("guidoRecommendationExtension", {
|
|
62
|
+
state: () => N(),
|
|
62
63
|
getters: {
|
|
63
64
|
// ====================================================================
|
|
64
65
|
// Proxy Getters — Backward Compatible Access to Current Block State
|
|
@@ -113,12 +114,12 @@ const v = () => ({
|
|
|
113
114
|
return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
|
|
114
115
|
},
|
|
115
116
|
getActivePredictiveAlgorithms: (t) => {
|
|
116
|
-
const e = [];
|
|
117
|
-
return t.activePredictiveAlgorithms.filter((
|
|
118
|
-
|
|
119
|
-
}),
|
|
120
|
-
text:
|
|
121
|
-
value:
|
|
117
|
+
const e = g(), r = [];
|
|
118
|
+
return t.activePredictiveAlgorithms.filter((n) => !D.includes(n)).forEach((n) => {
|
|
119
|
+
r.push(...e.filter((c) => c.id === n));
|
|
120
|
+
}), r.map((n) => ({
|
|
121
|
+
text: n.name,
|
|
122
|
+
value: n.key
|
|
122
123
|
}));
|
|
123
124
|
},
|
|
124
125
|
getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
|
|
@@ -293,11 +294,11 @@ const v = () => ({
|
|
|
293
294
|
// ====================================================================
|
|
294
295
|
async fetchRecommendationCreateData() {
|
|
295
296
|
if (!this.activePredictiveAlgorithms.length) {
|
|
296
|
-
if (
|
|
297
|
-
await
|
|
297
|
+
if (m) {
|
|
298
|
+
await m;
|
|
298
299
|
return;
|
|
299
300
|
}
|
|
300
|
-
|
|
301
|
+
m = (async () => {
|
|
301
302
|
const {
|
|
302
303
|
activePredictiveAlgorithms: t,
|
|
303
304
|
languages: e,
|
|
@@ -310,26 +311,26 @@ const v = () => ({
|
|
|
310
311
|
this.currencyList = r;
|
|
311
312
|
})();
|
|
312
313
|
try {
|
|
313
|
-
await
|
|
314
|
+
await m;
|
|
314
315
|
} finally {
|
|
315
|
-
|
|
316
|
+
m = null;
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
},
|
|
319
320
|
async fetchRecommendationFilters() {
|
|
320
321
|
if (!Object.keys(this.filterList).length) {
|
|
321
|
-
if (
|
|
322
|
-
await
|
|
322
|
+
if (u) {
|
|
323
|
+
await u;
|
|
323
324
|
return;
|
|
324
325
|
}
|
|
325
|
-
|
|
326
|
+
u = (async () => {
|
|
326
327
|
const t = await h.fetchRecommendationFilters();
|
|
327
328
|
this.filterList = t;
|
|
328
329
|
})();
|
|
329
330
|
try {
|
|
330
|
-
await
|
|
331
|
+
await u;
|
|
331
332
|
} finally {
|
|
332
|
-
|
|
333
|
+
u = null;
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
},
|
|
@@ -398,6 +399,14 @@ const v = () => ({
|
|
|
398
399
|
generateFilterQuery() {
|
|
399
400
|
return b(this.recommendationConfigs.filters);
|
|
400
401
|
},
|
|
402
|
+
/**
|
|
403
|
+
* Validation-only check invoked at save-CTA time. Defined as an action
|
|
404
|
+
* (not a getter) so reading it does not register reactive tracking on
|
|
405
|
+
* every block's recommendationConfigs across user edits.
|
|
406
|
+
*/
|
|
407
|
+
hasInvalidBlock() {
|
|
408
|
+
return Object.values(this.blockStates).some((t) => !v(t.recommendationConfigs, this));
|
|
409
|
+
},
|
|
401
410
|
// ====================================================================
|
|
402
411
|
// Per-Block Product Fetching
|
|
403
412
|
// ====================================================================
|
|
@@ -417,7 +426,7 @@ const v = () => ({
|
|
|
417
426
|
},
|
|
418
427
|
async _doFetchProducts() {
|
|
419
428
|
var p;
|
|
420
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p =
|
|
429
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = g().find((l) => l.key === r.strategy)) == null ? void 0 : p.path) || "", o = G(), s = parseInt(r.size) || 6, a = {
|
|
421
430
|
locale: r.language,
|
|
422
431
|
currency: r.currencySettings.value,
|
|
423
432
|
partnerName: o.partnerName,
|
|
@@ -433,15 +442,15 @@ const v = () => ({
|
|
|
433
442
|
f = [];
|
|
434
443
|
}
|
|
435
444
|
if (this.blockStates[t]) {
|
|
436
|
-
const l = f.length > 0 ? f :
|
|
445
|
+
const l = f.length > 0 ? f : S(s);
|
|
437
446
|
l.length < s ? this.blockStates[t].recommendationProducts = [
|
|
438
447
|
...l,
|
|
439
|
-
...
|
|
448
|
+
...S(s - l.length)
|
|
440
449
|
] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
|
|
441
450
|
}
|
|
442
451
|
}
|
|
443
452
|
}
|
|
444
453
|
});
|
|
445
454
|
export {
|
|
446
|
-
|
|
455
|
+
M as useRecommendationExtensionStore
|
|
447
456
|
};
|
|
@@ -1,31 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
import { OP_ANY_OF as l } from "../../../../enums/extensions/recommendationBlock.js";
|
|
2
|
+
function m(t) {
|
|
3
|
+
try {
|
|
4
|
+
return decodeURIComponent(t);
|
|
5
|
+
} catch {
|
|
6
|
+
return t;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function f(t) {
|
|
10
|
+
return t.split(",").filter(Boolean).map(m);
|
|
11
|
+
}
|
|
12
|
+
function y(t) {
|
|
2
13
|
if (t.length === 0)
|
|
3
14
|
return "";
|
|
4
15
|
const o = t.sort((r, e) => r.filterNumber - e.filterNumber), u = o.map((r) => {
|
|
5
|
-
const e = r.operator ===
|
|
16
|
+
const e = r.operator === l, a = e ? "=" : r.operator, c = e ? f(r.value).join(l) : r.value;
|
|
6
17
|
return `[${r.attribute}][${a}][${c}]`;
|
|
7
|
-
}), [
|
|
8
|
-
let n =
|
|
18
|
+
}), [i, ...p] = u;
|
|
19
|
+
let n = i;
|
|
9
20
|
for (let r = 0; r < p.length; r++) {
|
|
10
21
|
const e = o[r].innerGroupOperator;
|
|
11
22
|
n += `${e}${p[r]}`;
|
|
12
23
|
}
|
|
13
24
|
return `(${n})`;
|
|
14
25
|
}
|
|
15
|
-
function
|
|
26
|
+
function Q(t) {
|
|
16
27
|
if (!t || t.length === 0)
|
|
17
28
|
return "";
|
|
18
|
-
const o = t.reduce((r, e) => (r[e.filterGroup] || (r[e.filterGroup] = []), r[e.filterGroup].push(e), r), {}), u = Object.keys(o).map(Number).sort((r, e) => r - e),
|
|
29
|
+
const o = t.reduce((r, e) => (r[e.filterGroup] || (r[e.filterGroup] = []), r[e.filterGroup].push(e), r), {}), u = Object.keys(o).map(Number).sort((r, e) => r - e), i = u.map((r) => {
|
|
19
30
|
const e = o[r];
|
|
20
|
-
return
|
|
21
|
-
}), [p, ...n] =
|
|
22
|
-
let
|
|
31
|
+
return y(e);
|
|
32
|
+
}), [p, ...n] = i;
|
|
33
|
+
let s = p;
|
|
23
34
|
for (let r = 0; r < n.length; r++) {
|
|
24
35
|
const e = u[r + 1], c = o[e][0].outerGroupOperator;
|
|
25
|
-
|
|
36
|
+
s += `${c}${n[r]}`;
|
|
26
37
|
}
|
|
27
|
-
return
|
|
38
|
+
return s.trim();
|
|
28
39
|
}
|
|
29
40
|
export {
|
|
30
|
-
|
|
41
|
+
Q as generateCompleteFilterQuery,
|
|
42
|
+
f as parseTagList,
|
|
43
|
+
m as safeDecodeURIComponent
|
|
31
44
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const o = [
|
|
2
|
+
{
|
|
3
|
+
key: "locale",
|
|
4
|
+
getValue: (e) => e.language,
|
|
5
|
+
getAvailableOptions: (e) => Object.keys(e.languages)
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
key: "currency",
|
|
9
|
+
getValue: (e) => e.currencySettings.value,
|
|
10
|
+
getAvailableOptions: (e) => e.currencyList.map((n) => n.text)
|
|
11
|
+
}
|
|
12
|
+
], l = "newsletter.recommendation-fill-required-fields";
|
|
13
|
+
function u(e, n) {
|
|
14
|
+
return o.filter((t) => {
|
|
15
|
+
var a;
|
|
16
|
+
if (t.condition && !t.condition(e))
|
|
17
|
+
return !1;
|
|
18
|
+
const i = t.getValue(e);
|
|
19
|
+
if (!i)
|
|
20
|
+
return !0;
|
|
21
|
+
const r = (a = t.getAvailableOptions) == null ? void 0 : a.call(t, n);
|
|
22
|
+
return r !== void 0 && !r.includes(i);
|
|
23
|
+
}).map((t) => t.key);
|
|
24
|
+
}
|
|
25
|
+
function s(e, n) {
|
|
26
|
+
return u(e, n).length === 0;
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
o as REQUIRED_RECOMMENDATION_FIELDS,
|
|
30
|
+
l as RecommendationRequiredFieldsKey,
|
|
31
|
+
u as getInvalidFields,
|
|
32
|
+
s as isConfigValid
|
|
33
|
+
};
|
package/dist/guido.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.gap-16[data-v-
|
|
1
|
+
.gap-16[data-v-8053a037],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-cd76c125] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-4e2a4adb]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-fcccebf6]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-fcccebf6]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-fcccebf6]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useHttp as t } from "../composables/useHttp.js";
|
|
2
|
-
import { useToaster as
|
|
2
|
+
import { useToaster as a } from "../composables/useToaster.js";
|
|
3
3
|
const u = () => {
|
|
4
|
-
const { get:
|
|
4
|
+
const { get: e } = t(), { handleError: s } = a();
|
|
5
5
|
return { getUnsubscribePages: async () => {
|
|
6
6
|
try {
|
|
7
|
-
const { data:
|
|
8
|
-
return
|
|
9
|
-
} catch (
|
|
10
|
-
return s(
|
|
7
|
+
const { data: r } = await e("/unsubscribe-pages/get-all");
|
|
8
|
+
return Array.isArray(r) ? r : [];
|
|
9
|
+
} catch (r) {
|
|
10
|
+
return s(r, "Failed to fetch unsubscribe pages"), [];
|
|
11
11
|
}
|
|
12
12
|
} };
|
|
13
13
|
};
|
|
@@ -6,16 +6,13 @@
|
|
|
6
6
|
* that also share that pinia — reads the same store at runtime via
|
|
7
7
|
* `getActivePinia()._s.get('chat')`. No build-time dep on cortex-fe.
|
|
8
8
|
*
|
|
9
|
-
* The bridge handles
|
|
9
|
+
* The bridge handles two segment shapes:
|
|
10
10
|
*
|
|
11
11
|
* - `blueprint` segments with `blueprintType === 'email_template'` are
|
|
12
12
|
* applied to the Stripo editor (immediate on first sighting, debounced on
|
|
13
13
|
* update, identical-html no-ops).
|
|
14
|
-
* - `tool` segments toggle the AI status pill via `useAiStatusStore`. The
|
|
15
|
-
* pill goes up when `isRunning: true` and clears on `isRunning: false`,
|
|
16
|
-
* `error: true`, or any `error` segment / message-level `isError` flag.
|
|
17
14
|
* - `error` segments (and message-level `isError` / `isCancelled`) trigger
|
|
18
|
-
* a localized toaster
|
|
15
|
+
* a localized toaster.
|
|
19
16
|
*
|
|
20
17
|
* In dev (`import.meta.env.DEV`) every observed segment + message-level flag
|
|
21
18
|
* change is also `console.debug`-logged with the prefix `[guido:cortex]` so a
|
|
@@ -6,11 +6,16 @@ export declare const URLS: {
|
|
|
6
6
|
export declare const QUERY_PARAMS: {
|
|
7
7
|
CLIENT_ID: string;
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Get recommendation feed source maps lazily so translated names resolve at access time.
|
|
11
|
+
* Must be called within a Vue component context or after Pinia is initialized.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getRecommendationFeedSourceMaps: () => RecommendationFeedItem[];
|
|
10
14
|
export declare const PriceAttributes: string[];
|
|
11
15
|
export declare const currencyLocationMaps: TextValueObject[];
|
|
12
16
|
export declare const currencyOperators: TextValueObject[];
|
|
13
17
|
export declare const currencyDecimalCounts: TextValueObject[];
|
|
18
|
+
export declare const OP_ANY_OF = "||";
|
|
14
19
|
export declare const operatorOptionsForStrings: TextValueObject[];
|
|
15
20
|
export declare const operatorOptionsForArrayOfStrings: TextValueObject[];
|
|
16
21
|
export declare const operatorOptionsForNumbers: TextValueObject[];
|
|
@@ -18,9 +18,14 @@ export declare const PRODUCT_TYPE_URL_SEGMENTS: {
|
|
|
18
18
|
readonly 97: "email";
|
|
19
19
|
};
|
|
20
20
|
export declare const INSIDER_ID = "iid";
|
|
21
|
-
export declare const
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export declare const DEFAULT_UNSUBSCRIBE_GROUP_SEND_GRID_ID = "G";
|
|
22
|
+
/**
|
|
23
|
+
* Get the default unsubscribe group lazily so the translated name resolves at access time.
|
|
24
|
+
* Must be called within a Vue component context or after Pinia is initialized.
|
|
25
|
+
*/
|
|
26
|
+
export declare const getDefaultUnsubscribeGroup: () => {
|
|
27
|
+
name: string;
|
|
28
|
+
sendGridId: string;
|
|
24
29
|
};
|
|
25
30
|
export declare const UNSUBSCRIBE_PAGES_LINK = "/email/unsubscribe-pages";
|
|
26
31
|
export declare const PAGE_TYPES: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const migrationTemplate = "\n <td\n align=\"left\"\n esd-extension-block-id=\"radio-button-block\"\n esd-handler-name=\"esd-extension-RadioButtonBlock\"\n class=\"\n radio-button\n radio-button-v2\n esd-block-ra\n esd-radio-button-block\n esd-extension-block\n es-p10t\n es-p10b\n es-p30r\n es-p30l\"\n >\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td align=\"left\" width=\"70%\" style=\"vertical-align: top;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n {-{-TITLE-}-}\n </tr>\n <tr>\n {-{-DESCRIPTION-}-}\n </tr>\n </tbody>\n </table>\n </td>\n <td align=\"right\" width=\"30%\" style=\"vertical-align: middle;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioYes\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-YES-}-}\n </tr>\n </table>\n </td>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioNo\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-NO-}-}\n </tr>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n";
|
|
1
|
+
declare const migrationTemplate = "\n <td\n align=\"left\"\n esd-extension-block-id=\"radio-button-block\"\n esd-handler-name=\"esd-extension-RadioButtonBlock\"\n class=\"\n radio-button-block\n radio-button-v2\n esd-block-ra\n esd-radio-button-block\n esd-extension-block\n es-p10t\n es-p10b\n es-p30r\n es-p30l\"\n >\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td align=\"left\" width=\"70%\" style=\"vertical-align: top;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n {-{-TITLE-}-}\n </tr>\n <tr>\n {-{-DESCRIPTION-}-}\n </tr>\n </tbody>\n </table>\n </td>\n <td align=\"right\" width=\"30%\" style=\"vertical-align: middle;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioYes\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-YES-}-}\n </tr>\n </table>\n </td>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioNo\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-NO-}-}\n </tr>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n";
|
|
2
2
|
/**
|
|
3
3
|
* @returns The template for the default checkbox block
|
|
4
4
|
*/
|
|
@@ -95,15 +95,23 @@ export declare class RecommendationConfigService {
|
|
|
95
95
|
*
|
|
96
96
|
* Called when a block is first created (dropped into template).
|
|
97
97
|
* Can optionally merge in partial config from migration.
|
|
98
|
+
*
|
|
99
|
+
* The `wasFreshDrop` flag distinguishes a brand-new drop (no prior config)
|
|
100
|
+
* from a clone (Stripo replays the source's setNodeConfig payload before
|
|
101
|
+
* onCreated fires). Callers use this to skip side-effects already inherited
|
|
102
|
+
* from the source.
|
|
98
103
|
* @example
|
|
99
104
|
* // In Block.onCreated lifecycle
|
|
100
|
-
* RecommendationConfigService.initializeConfig(this.api, node);
|
|
105
|
+
* const { config, wasFreshDrop } = RecommendationConfigService.initializeConfig(this.api, node);
|
|
101
106
|
* @param api - Stripo extension API with document modifier
|
|
102
107
|
* @param node - The immutable HTML node to initialize
|
|
103
108
|
* @param partialConfig - Optional partial config to merge with defaults
|
|
104
|
-
* @returns The initialized configuration
|
|
109
|
+
* @returns The initialized configuration and whether the node was a fresh drop
|
|
105
110
|
*/
|
|
106
|
-
static initializeConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, partialConfig?: PartialNodeConfig):
|
|
111
|
+
static initializeConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, partialConfig?: PartialNodeConfig): {
|
|
112
|
+
config: RecommendationNodeConfig;
|
|
113
|
+
wasFreshDrop: boolean;
|
|
114
|
+
};
|
|
107
115
|
/**
|
|
108
116
|
* Save complete configuration to a node
|
|
109
117
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Orientation, Languages, Currency, NumericSeparator, FiltersResponse, Filter, RecommendationProduct } from '@@/Types/recommendation';
|
|
2
|
-
interface PerBlockConfigs {
|
|
2
|
+
export interface PerBlockConfigs {
|
|
3
3
|
cardsInRow: number;
|
|
4
4
|
currencySettings: {
|
|
5
5
|
name: string;
|
|
@@ -272,6 +272,12 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
|
|
|
272
272
|
deleteFilter(filter: Filter): void;
|
|
273
273
|
addFilter(filter: Filter): void;
|
|
274
274
|
generateFilterQuery(): string;
|
|
275
|
+
/**
|
|
276
|
+
* Validation-only check invoked at save-CTA time. Defined as an action
|
|
277
|
+
* (not a getter) so reading it does not register reactive tracking on
|
|
278
|
+
* every block's recommendationConfigs across user edits.
|
|
279
|
+
*/
|
|
280
|
+
hasInvalidBlock(): boolean;
|
|
275
281
|
fetchRecommendationProducts(): Promise<void>;
|
|
276
282
|
_doFetchProducts(): Promise<void>;
|
|
277
283
|
}>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Filter } from '@@/Types/recommendation';
|
|
2
|
+
export declare function safeDecodeURIComponent(value: string): string;
|
|
3
|
+
export declare function parseTagList(value: string): string[];
|
|
2
4
|
/**
|
|
3
5
|
* Generates the complete query with outer group operators
|
|
4
6
|
* @param filters Array of Filter objects
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PerBlockConfigs } from '../store/recommendation';
|
|
2
|
+
import type { Currency, Languages } from '@@/Types/recommendation';
|
|
3
|
+
/**
|
|
4
|
+
* Structural slice of the recommendation extension store that descriptors may read.
|
|
5
|
+
* Add new fields here when a future descriptor needs them.
|
|
6
|
+
*/
|
|
7
|
+
export interface ExtensionStoreSlice {
|
|
8
|
+
languages: Languages;
|
|
9
|
+
currencyList: Currency[];
|
|
10
|
+
}
|
|
11
|
+
interface RequiredField {
|
|
12
|
+
key: string;
|
|
13
|
+
getValue: (config: PerBlockConfigs) => string;
|
|
14
|
+
getAvailableOptions?: (store: ExtensionStoreSlice) => string[];
|
|
15
|
+
condition?: (config: PerBlockConfigs) => boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const REQUIRED_RECOMMENDATION_FIELDS: RequiredField[];
|
|
18
|
+
export declare const RecommendationRequiredFieldsKey = "newsletter.recommendation-fill-required-fields";
|
|
19
|
+
export declare function getInvalidFields(config: PerBlockConfigs, store: ExtensionStoreSlice): string[];
|
|
20
|
+
export declare function isConfigValid(config: PerBlockConfigs, store: ExtensionStoreSlice): boolean;
|
|
21
|
+
export {};
|