@useinsider/guido 3.18.2-beta.8ef3138 → 3.18.2-beta.eedafd2

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.
Files changed (50) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +28 -26
  3. package/dist/components/Guido.vue.js +8 -8
  4. package/dist/components/Guido.vue2.js +53 -51
  5. package/dist/components/organisms/AutoSaveController.vue2.js +9 -8
  6. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  7. package/dist/components/organisms/header/EditorActions.vue2.js +86 -56
  8. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  9. package/dist/components/organisms/header/LeftSlot.vue2.js +16 -12
  10. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +20 -0
  11. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +48 -0
  12. package/dist/components/wrappers/WpDrawer.vue.js +9 -7
  13. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  14. package/dist/composables/useHtmlCompiler.js +37 -26
  15. package/dist/composables/useHtmlValidator.js +2 -0
  16. package/dist/composables/useSave.js +90 -45
  17. package/dist/composables/useSaveValidation.js +279 -0
  18. package/dist/composables/useSaveValidationRollout.js +9 -0
  19. package/dist/composables/validators/useCouponBlockValidator.js +24 -21
  20. package/dist/composables/validators/useUnsubscribeBlockValidator.js +34 -27
  21. package/dist/enums/save-validation.js +4 -0
  22. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +43 -36
  23. package/dist/guido.css +1 -1
  24. package/dist/src/@types/config/schemas.d.ts +4 -0
  25. package/dist/src/@types/save-validation.d.ts +19 -0
  26. package/dist/src/components/Guido.vue.d.ts +17 -1
  27. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +17 -1
  28. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +17 -1
  29. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +17 -1
  30. package/dist/src/components/organisms/validation/ValidationErrorDrawer.vue.d.ts +2 -0
  31. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +4 -3
  32. package/dist/src/composables/useConfig.d.ts +2 -0
  33. package/dist/src/composables/useHtmlValidator.d.ts +21 -0
  34. package/dist/src/composables/useSave.d.ts +6 -1
  35. package/dist/src/composables/useSaveValidation.d.ts +6 -0
  36. package/dist/src/composables/useSaveValidationRollout.d.ts +3 -0
  37. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
  38. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +10 -0
  39. package/dist/src/enums/save-validation.d.ts +27 -0
  40. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +5 -0
  41. package/dist/src/stores/config.d.ts +30 -0
  42. package/dist/src/stores/save-validation.d.ts +22 -0
  43. package/dist/src/utils/liquidDynamicContent.d.ts +20 -0
  44. package/dist/src/utils/templatePreparation.d.ts +30 -0
  45. package/dist/stores/config.js +39 -22
  46. package/dist/stores/save-validation.js +30 -0
  47. package/dist/utils/genericUtil.js +64 -61
  48. package/dist/utils/liquidDynamicContent.js +15 -0
  49. package/dist/utils/templatePreparation.js +28 -25
  50. package/package.json +1 -1
@@ -0,0 +1,279 @@
1
+ import { getIgnoredRecommendationBlockIds as B } from "../config/compiler/utils/recommendationIgnoreUtils.js";
2
+ import { CanNotMakeAnyChangesForRunningKey as k, DISPLAY_CONDITIONS_REGEX as Y, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as x } from "../enums/html-validator.js";
3
+ import { SaveValidationRule as o } from "../enums/save-validation.js";
4
+ import { ToasterTypeOptions as r } from "../enums/toaster.js";
5
+ import { BLOCK_ROOT_SELECTOR as U } from "../extensions/Blocks/Checkbox/constants/selectors.js";
6
+ import { itemsBlockDynamicVariables as M } from "../extensions/Blocks/Items/enums/productEnums.js";
7
+ import { BLOCK_ROOT_SELECTOR as P } from "../extensions/Blocks/RadioButton/constants/selectors.js";
8
+ import { useRecommendationExtensionStore as W } from "../extensions/Blocks/Recommendation/store/recommendation.js";
9
+ import { RecommendationRequiredFieldsKey as G } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
10
+ import { useConfigStore as q } from "../stores/config.js";
11
+ import { useDynamicContentStore as H } from "../stores/dynamic-content.js";
12
+ import { useRecommendationStore as V } from "../stores/recommendation.js";
13
+ import { base64EncodeWithSpecialChars as T } from "../utils/base64.js";
14
+ import { useTemplatePreparation as X } from "../utils/templatePreparation.js";
15
+ import { useActionsApi as j } from "./useActionsApi.js";
16
+ import { useConfig as z } from "./useConfig.js";
17
+ import { useHtmlCompiler as $ } from "./useHtmlCompiler.js";
18
+ import { buildPartnerAttributeNameSet as Q, getInvalidDynamicContentTags as Z, getRecommendationBlockIds as J, findInvalidCustomFields as tt } from "./useHtmlValidator.js";
19
+ import { useHttp as et } from "./useHttp.js";
20
+ import { useTranslations as nt } from "./useTranslations.js";
21
+ import { findInvalidCouponBlocks as it, COUPON_TAG_MODIFIED_KEY as ot } from "./validators/useCouponBlockValidator.js";
22
+ import { findDuplicateUnsubPageTypes as rt, countUnsubBlocksWithoutPage as st, EMPTY_BLOCK_KEY as at } from "./validators/useUnsubscribeBlockValidator.js";
23
+ const ct = "newsletter.html-content-is-empty", lt = "custom-fields.missing-closing-braces", ut = "custom-fields.missing-opening-braces", I = "custom-fields.invalid-custom-fields", mt = "newsletter.display-conditions-invalid-syntax", dt = "custom-conditions.wrong-equality-operators", pt = "custom-conditions.missing-if-endif-tag", gt = "custom-conditions.no-space-after-braces", ft = "custom-conditions.no-braces-inside-if-tag", yt = "newsletter.fill-all-necessary-fields", Et = "newsletter.invalid-image-type", It = "unsubscribe-templates.select-checkbox-groups", St = "unsubscribe-templates.select-radio-button-groups", Ct = "newsletter.invalid-url-link-for-toaster", F = "journey-builder.liquid-validation-failed", _t = "/newsletter/template-library/check-template-html-body", ht = /src="[^"]*\.(svg|pst)"/gm, R = /({%(.*?)%})/g, kt = ["if", "endif", "else", "elif", "now"], Tt = (e) => !kt.includes(e.toLowerCase()), Ft = (e) => ["if", "endif"].includes(e.toLowerCase());
24
+ function Ot(e) {
25
+ return it(e).map((i) => ({
26
+ rule: o.CouponTagModified,
27
+ type: r.Warning,
28
+ key: ot,
29
+ detail: i
30
+ }));
31
+ }
32
+ function Rt(e) {
33
+ return rt(e).map((i) => ({
34
+ rule: o.DuplicateUnsubBlock,
35
+ type: r.Error,
36
+ key: i.messageKey
37
+ }));
38
+ }
39
+ function Lt(e) {
40
+ return Array.from({ length: st(e) }, () => ({
41
+ rule: o.UnsubBlockMissingTemplate,
42
+ type: r.Warning,
43
+ key: at
44
+ }));
45
+ }
46
+ function Nt(e, i) {
47
+ const n = e.match(R);
48
+ if (!n)
49
+ return [];
50
+ const a = [];
51
+ return n.forEach((u) => {
52
+ const s = u.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
53
+ if (s && s.length > 0) {
54
+ const [m] = s;
55
+ Tt(m) && !i.includes(m) && a.push({
56
+ rule: o.CustomFieldScript,
57
+ type: r.Warning,
58
+ key: I,
59
+ detail: m
60
+ });
61
+ }
62
+ }), a;
63
+ }
64
+ function bt(e) {
65
+ return e.length > 0 ? [] : [{ rule: o.EmptyHtml, type: r.Warning, key: ct }];
66
+ }
67
+ function At(e) {
68
+ const i = (e.match(/{/gm) || []).length, n = (e.match(/}/gm) || []).length;
69
+ return i > n ? [{
70
+ rule: o.MismatchedBraces,
71
+ type: r.Warning,
72
+ key: lt
73
+ }] : i < n ? [{
74
+ rule: o.MismatchedBraces,
75
+ type: r.Warning,
76
+ key: ut
77
+ }] : [];
78
+ }
79
+ function Dt(e) {
80
+ return Z(e).map((i) => ({
81
+ rule: o.InvalidDynamicTag,
82
+ type: r.Warning,
83
+ key: I,
84
+ detail: i
85
+ }));
86
+ }
87
+ function vt(e, i, n, a) {
88
+ const u = J(e);
89
+ return tt(e, i, u, n, a).map((s) => ({
90
+ rule: o.InvalidCustomField,
91
+ type: r.Error,
92
+ key: I,
93
+ detail: s
94
+ }));
95
+ }
96
+ function Kt(e) {
97
+ const i = e.match(/{%(.*?)%}/g), n = [], a = [];
98
+ if (i && i.forEach((u) => {
99
+ const s = u.match(Y), m = u.match(x), y = (s == null ? void 0 : s.join("")) || "";
100
+ (!s || u !== y) && !m && a.push({
101
+ rule: o.ConditionsSyntax,
102
+ type: r.Error,
103
+ key: mt,
104
+ detail: u
105
+ }), s && s.forEach((g) => {
106
+ g.trim() === "=" && a.push({
107
+ rule: o.WrongEqualityOperator,
108
+ type: r.Error,
109
+ key: dt
110
+ }), (g.match(/^[a-zA-Z]*$/g) ?? []).forEach((f) => {
111
+ Ft(f) && n.push(f);
112
+ });
113
+ });
114
+ }), n.length) {
115
+ const u = n.filter((m) => m === "if").length, s = n.filter((m) => m === "endif").length;
116
+ u !== s && a.push({
117
+ rule: o.MissingIfEndif,
118
+ type: r.Error,
119
+ key: pt
120
+ });
121
+ }
122
+ return a;
123
+ }
124
+ function wt(e) {
125
+ const i = (e.match(/{% /gm) || []).length, n = (e.match(/ %}/gm) || []).length;
126
+ return i === n ? [] : [{
127
+ rule: o.ConditionTagSpacing,
128
+ type: r.Warning,
129
+ key: gt
130
+ }];
131
+ }
132
+ function Bt(e) {
133
+ return (e.match(R) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, a) => n + a, 0) === 0 ? [] : [{
134
+ rule: o.BracesInsideIfTag,
135
+ type: r.Warning,
136
+ key: ft
137
+ }];
138
+ }
139
+ function Yt(e) {
140
+ return (e.match(ht) ?? []).map((i) => ({
141
+ rule: o.InvalidImageType,
142
+ type: r.Error,
143
+ key: Et,
144
+ detail: i
145
+ }));
146
+ }
147
+ function O(e, i, n, a) {
148
+ const u = new DOMParser().parseFromString(e, "text/html");
149
+ return Array.from(u.querySelectorAll(i)).filter((s) => {
150
+ var m;
151
+ return !((m = s.id) != null && m.trim());
152
+ }).map(() => ({ rule: n, type: r.Error, key: a }));
153
+ }
154
+ const ae = () => {
155
+ const { config: e, isFeatureEnabled: i } = z(), n = nt(), { post: a } = et(), { getCompiledEmail: u, getTemplateData: s } = j(), { compileHtml: m } = $(), { hydrateRecommendationStore: y } = X(), g = H(), f = V(), E = W(), L = q(), N = (t) => ({
156
+ rule: t.rule,
157
+ type: t.type,
158
+ detail: t.detail,
159
+ message: "key" in t ? n(t.key) : t.message
160
+ }), b = async (t) => {
161
+ if (t.size === 0)
162
+ return /* @__PURE__ */ new Set();
163
+ try {
164
+ await E.fetchRecommendationFilters();
165
+ } catch {
166
+ return null;
167
+ }
168
+ return Q(E.filterList);
169
+ }, A = async (t) => {
170
+ try {
171
+ const l = await a(_t, { html: T(t) }), { status: c, message: d } = l.data, p = [];
172
+ return c || p.push({
173
+ rule: o.InvalidLinks,
174
+ type: r.Error,
175
+ // status===undefined means a bare message instead of a boolean; surface it,
176
+ // but fall back to the canned key when it's missing so a malformed body
177
+ // never renders a literal 'undefined' row (SD-147430).
178
+ ...c === void 0 && d ? { message: d } : { key: Ct }
179
+ }), d === n(k) && p.push({
180
+ rule: o.LinkCheckInProgress,
181
+ type: r.Error,
182
+ key: k
183
+ }), p;
184
+ } catch (l) {
185
+ return console.warn("Link-check collector failed; skipping the link rule.", l), [];
186
+ }
187
+ }, D = async (t) => {
188
+ try {
189
+ const l = await a("/newsletter/contents/validate-syntax", [{
190
+ identifier: "default",
191
+ syntax: "liquid",
192
+ contents: {
193
+ subject: null,
194
+ preheader: null,
195
+ html: T(t),
196
+ ampHtml: null
197
+ }
198
+ }]);
199
+ if (!Array.isArray(l.data)) {
200
+ const d = l.data;
201
+ return [{
202
+ rule: o.LiquidSyntax,
203
+ type: r.Warning,
204
+ message: d.message ?? n(F)
205
+ }];
206
+ }
207
+ const [c] = l.data;
208
+ return Object.values((c == null ? void 0 : c.errors) ?? {}).map((d) => ({
209
+ rule: o.LiquidSyntax,
210
+ type: r.Warning,
211
+ message: d.replace(/^line \d+:\s*/, "")
212
+ }));
213
+ } catch {
214
+ return [{
215
+ rule: o.LiquidSyntax,
216
+ type: r.Error,
217
+ message: n(F)
218
+ }];
219
+ }
220
+ }, v = () => {
221
+ const t = E.invalidBlockIds().map((c) => ({
222
+ rule: o.RecommendationRequiredFields,
223
+ type: r.Error,
224
+ key: G,
225
+ detail: c
226
+ })), l = (f.recommendationConfigs ?? []).filter((c) => c.filters.some((d) => d.value === "")).map(() => ({
227
+ rule: o.RecommendationFilters,
228
+ type: r.Error,
229
+ key: yt
230
+ }));
231
+ return [...t, ...l];
232
+ }, K = async (t, l) => {
233
+ var _, h;
234
+ const c = [
235
+ ...l.map((w) => w.value),
236
+ ...M,
237
+ ...((h = (_ = e.value) == null ? void 0 : _.template) == null ? void 0 : h.customFieldAttributes) ?? []
238
+ ], d = B(t), p = await b(d), { isTransactional: C } = L;
239
+ return [
240
+ ...await A(t),
241
+ ...C ? [] : Nt(t, c),
242
+ ...bt(t),
243
+ ...At(t),
244
+ ...Dt(t),
245
+ ...C ? [] : vt(t, c, d, p),
246
+ ...Kt(t),
247
+ ...wt(t),
248
+ ...Bt(t),
249
+ ...v(),
250
+ ...Yt(t),
251
+ ...O(
252
+ t,
253
+ U,
254
+ o.CheckboxUnsubGroup,
255
+ It
256
+ ),
257
+ ...O(
258
+ t,
259
+ P,
260
+ o.RadioButtonUnsubGroup,
261
+ St
262
+ )
263
+ ];
264
+ }, S = async (t, l) => [
265
+ ...Ot(t),
266
+ ...Rt(t),
267
+ ...Lt(t),
268
+ ...i("liquidSyntax") ? await D(t) : await K(t, l)
269
+ ].map(N);
270
+ return { collectSaveValidationErrors: S, validateCurrentTemplate: async () => {
271
+ const { html: t } = await s();
272
+ await y(t);
273
+ const { html: l } = await u({ minimize: !0, resetDataSavedFlag: !1 }), { compiledHtml: c } = m(l);
274
+ return S(c, g.getSelectedDynamicContentList);
275
+ } };
276
+ };
277
+ export {
278
+ ae as useSaveValidation
279
+ };
@@ -0,0 +1,9 @@
1
+ import { useConfigStore as t } from "../stores/config.js";
2
+ import { computed as o } from "vue";
3
+ const n = ["newsletteruatcamp", "newsletterqacamp"], c = () => {
4
+ const e = t();
5
+ return { isEnabled: o(() => n.includes(e.partnerName)) };
6
+ };
7
+ export {
8
+ c as useSaveValidationRollout
9
+ };
@@ -1,24 +1,27 @@
1
- import { ToasterTypeOptions as c } from "../../enums/toaster.js";
2
- import { COUPON_PLACEHOLDER_DEFAULT as i, COUPON_PLACEHOLDER_LIQUID as l } from "../../extensions/Blocks/CouponBlock/template.js";
3
- import { useToaster as m } from "../useToaster.js";
4
- import { useTranslations as p } from "../useTranslations.js";
5
- const u = /* @__PURE__ */ new Set([
6
- i,
7
- l
8
- ]), A = () => {
9
- const { showToaster: t } = m(), r = p();
10
- return { validateCouponBlockTags: (e) => {
11
- const n = new DOMParser().parseFromString(e, "text/html");
12
- return Array.from(n.querySelectorAll(".coupon-block")).find((s) => {
13
- var o;
14
- const a = ((o = s.textContent) == null ? void 0 : o.trim()) ?? "";
15
- return !u.has(a);
16
- }) ? (t({
17
- type: c.Warning,
18
- message: r("newsletter.coupon-tag-modified")
19
- }), !1) : !0;
20
- } };
1
+ import { ToasterTypeOptions as n } from "../../enums/toaster.js";
2
+ import { COUPON_PLACEHOLDER_DEFAULT as s, COUPON_PLACEHOLDER_LIQUID as a } from "../../extensions/Blocks/CouponBlock/template.js";
3
+ import { useToaster as i } from "../useToaster.js";
4
+ import { useTranslations as l } from "../useTranslations.js";
5
+ const c = /* @__PURE__ */ new Set([
6
+ s,
7
+ a
8
+ ]), p = "newsletter.coupon-tag-modified";
9
+ function u(r) {
10
+ const e = new DOMParser().parseFromString(r, "text/html");
11
+ return Array.from(e.querySelectorAll(".coupon-block")).map((o) => {
12
+ var t;
13
+ return ((t = o.textContent) == null ? void 0 : t.trim()) ?? "";
14
+ }).filter((o) => !c.has(o));
15
+ }
16
+ const T = () => {
17
+ const { showToaster: r } = i(), e = l();
18
+ return { validateCouponBlockTags: (t) => u(t).length > 0 ? (r({
19
+ type: n.Warning,
20
+ message: e(p)
21
+ }), !1) : !0 };
21
22
  };
22
23
  export {
23
- A as useCouponBlockValidator
24
+ p as COUPON_TAG_MODIFIED_KEY,
25
+ u as findInvalidCouponBlocks,
26
+ T as useCouponBlockValidator
24
27
  };
@@ -1,8 +1,8 @@
1
- import { ToasterTypeOptions as i } from "../../enums/toaster.js";
1
+ import { ToasterTypeOptions as c } from "../../enums/toaster.js";
2
2
  import { PAGE_TYPES as u } from "../../enums/unsubscribe.js";
3
- import { UNSUBSCRIBE_BLOCK_SELECTOR as l, DATA_ATTRIBUTES as a } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
4
- import { useToaster as E } from "../useToaster.js";
5
- import { useTranslations as m } from "../useTranslations.js";
3
+ import { UNSUBSCRIBE_BLOCK_SELECTOR as i, DATA_ATTRIBUTES as a } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
4
+ import { useToaster as p } from "../useToaster.js";
5
+ import { useTranslations as l } from "../useTranslations.js";
6
6
  const T = [
7
7
  {
8
8
  pageType: u.GLOBAL_UNSUBSCRIBE,
@@ -12,30 +12,37 @@ const T = [
12
12
  pageType: u.SUBSCRIPTION_PREFERENCE_CENTER,
13
13
  messageKey: "unsubscription-preference.duplicate-pref-center"
14
14
  }
15
- ], g = "unsubscription-preference.duplicate-both", b = "unsubscription-preference.select-page-to-continue", P = () => {
16
- const { showToaster: c } = E(), p = m();
17
- return { validateUnsubscribeBlockUniqueness: (n) => {
18
- const o = new DOMParser().parseFromString(n, "text/html"), r = /* @__PURE__ */ new Map();
19
- o.querySelectorAll(l).forEach((e) => {
20
- const s = Number(e.getAttribute(a.PAGE_TYPE));
21
- r.set(s, (r.get(s) ?? 0) + 1);
22
- });
23
- const t = T.filter((e) => (r.get(e.pageType) ?? 0) > 1).map((e) => e.messageKey);
24
- return t.length ? (c({
25
- type: i.Error,
26
- message: p(t.length > 1 ? g : t[0])
15
+ ], g = "unsubscription-preference.duplicate-both", E = "unsubscription-preference.select-page-to-continue";
16
+ function m(r) {
17
+ const s = new DOMParser().parseFromString(r, "text/html"), t = /* @__PURE__ */ new Map();
18
+ return s.querySelectorAll(i).forEach((n) => {
19
+ const e = Number(n.getAttribute(a.PAGE_TYPE));
20
+ t.set(e, (t.get(e) ?? 0) + 1);
21
+ }), T.filter((n) => (t.get(n.pageType) ?? 0) > 1);
22
+ }
23
+ function b(r) {
24
+ const s = new DOMParser().parseFromString(r, "text/html");
25
+ return Array.from(s.querySelectorAll(i)).filter((t) => {
26
+ const n = t.getAttribute(a.PAGE_TYPE), e = t.getAttribute(a.PAGE_LIST);
27
+ return !n || !e;
28
+ }).length;
29
+ }
30
+ const y = () => {
31
+ const { showToaster: r } = p(), s = l();
32
+ return { validateUnsubscribeBlockUniqueness: (e) => {
33
+ const o = m(e);
34
+ return o.length ? (r({
35
+ type: c.Error,
36
+ message: s(o.length > 1 ? g : o[0].messageKey)
27
37
  }), !1) : !0;
28
- }, validateUnsubscribeBlockHasTemplate: (n) => {
29
- const o = new DOMParser().parseFromString(n, "text/html");
30
- return Array.from(o.querySelectorAll(l)).some((t) => {
31
- const e = t.getAttribute(a.PAGE_TYPE), s = t.getAttribute(a.PAGE_LIST);
32
- return !e || !s;
33
- }) ? (c({
34
- type: i.Warning,
35
- message: p(b)
36
- }), !1) : !0;
37
- } };
38
+ }, validateUnsubscribeBlockHasTemplate: (e) => b(e) === 0 ? !0 : (r({
39
+ type: c.Warning,
40
+ message: s(E)
41
+ }), !1) };
38
42
  };
39
43
  export {
40
- P as useUnsubscribeBlockValidator
44
+ E as EMPTY_BLOCK_KEY,
45
+ b as countUnsubBlocksWithoutPage,
46
+ m as findDuplicateUnsubPageTypes,
47
+ y as useUnsubscribeBlockValidator
41
48
  };
@@ -0,0 +1,4 @@
1
+ var n = /* @__PURE__ */ ((s) => (s.CouponTagModified = "coupon-tag-modified", s.DuplicateUnsubBlock = "duplicate-unsub-block", s.UnsubBlockMissingTemplate = "unsub-block-missing-template", s.LiquidSyntax = "liquid-syntax", s.InvalidLinks = "invalid-links", s.LinkCheckInProgress = "link-check-in-progress", s.CustomFieldScript = "custom-field-script", s.EmptyHtml = "empty-html", s.MismatchedBraces = "mismatched-braces", s.InvalidDynamicTag = "invalid-dynamic-tag", s.InvalidCustomField = "invalid-custom-field", s.ConditionsSyntax = "conditions-syntax", s.WrongEqualityOperator = "wrong-equality-operator", s.MissingIfEndif = "missing-if-endif", s.ConditionTagSpacing = "condition-tag-spacing", s.BracesInsideIfTag = "braces-inside-if-tag", s.RecommendationRequiredFields = "recommendation-required-fields", s.RecommendationFilters = "recommendation-filters", s.InvalidImageType = "invalid-image-type", s.CheckboxUnsubGroup = "checkbox-unsub-group", s.RadioButtonUnsubGroup = "radio-button-unsub-group", s))(n || {});
2
+ export {
3
+ n as SaveValidationRule
4
+ };
@@ -1,22 +1,22 @@
1
- import { getRecommendationFeedSourceMaps as b, getOperatorOptions as G, PriceAttributes as v } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as D } from "../../../../services/recommendationApi.js";
3
- import { useConfigStore as F } from "../../../../stores/config.js";
4
- import { defineStore as P } from "pinia";
1
+ import { getRecommendationFeedSourceMaps as I, getOperatorOptions as G, PriceAttributes as D } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as F } from "../../../../services/recommendationApi.js";
3
+ import { useConfigStore as P } from "../../../../stores/config.js";
4
+ import { defineStore as _ } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as _, DEFAULT_CARDS_IN_ROW as U } from "../constants/layout.js";
7
- import { DEFAULT_NODE_CONFIG as N, EXCLUDED_ALGORITHM_IDS as L } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as R } from "../templates/utils.js";
9
- import { generateCompleteFilterQuery as k } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as x } from "../utils/partnerCustomizations.js";
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as U, DEFAULT_CARDS_IN_ROW as N } from "../constants/layout.js";
7
+ import { DEFAULT_NODE_CONFIG as L, EXCLUDED_ALGORITHM_IDS as x } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as k } from "../templates/utils.js";
9
+ import { generateCompleteFilterQuery as R } from "../utils/filterUtil.js";
10
+ import { getPartnerRecommendationParams as B } from "../utils/partnerCustomizations.js";
11
11
  import { sanitizeProductIds as A } from "../utils/productIds.js";
12
- import { isFilterValid as B } from "../validation/filterSchema.js";
13
- import { isConfigValid as M } from "../validation/requiredFields.js";
14
- const u = D();
12
+ import { isFilterValid as M } from "../validation/filterSchema.js";
13
+ import { isConfigValid as C } from "../validation/requiredFields.js";
14
+ const u = F();
15
15
  let f = null, g = null, h = null, S = null;
16
16
  const p = /* @__PURE__ */ new Map();
17
- function w() {
17
+ function v() {
18
18
  return {
19
- cardsInRow: U,
19
+ cardsInRow: N,
20
20
  currencySettings: {
21
21
  name: "USD",
22
22
  value: "USD",
@@ -30,7 +30,7 @@ function w() {
30
30
  productIds: [],
31
31
  id: 1,
32
32
  language: "en_US",
33
- mobileCardsInRow: _,
33
+ mobileCardsInRow: U,
34
34
  mobileLayoutEnabled: !1,
35
35
  orientation: "grid",
36
36
  recommendedProducts: [],
@@ -46,9 +46,9 @@ function w() {
46
46
  priceHideIfSameAsDiscounted: !1
47
47
  };
48
48
  }
49
- function C() {
49
+ function w() {
50
50
  return {
51
- recommendationConfigs: w(),
51
+ recommendationConfigs: v(),
52
52
  recommendationProducts: [],
53
53
  filterStatus: !1,
54
54
  filterSelectionDrawerStatus: !1,
@@ -60,7 +60,7 @@ function C() {
60
60
  syncedConfigFingerprint: null
61
61
  };
62
62
  }
63
- const z = () => ({
63
+ const O = () => ({
64
64
  recommendationCampaignUrls: {},
65
65
  activePredictiveAlgorithms: [],
66
66
  languages: {},
@@ -72,8 +72,8 @@ const z = () => ({
72
72
  blockStates: {},
73
73
  currentRecommendationId: null,
74
74
  configVersion: 0
75
- }), Y = P("guidoRecommendationExtension", {
76
- state: () => z(),
75
+ }), Y = _("guidoRecommendationExtension", {
76
+ state: () => O(),
77
77
  getters: {
78
78
  // ====================================================================
79
79
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -83,7 +83,7 @@ const z = () => ({
83
83
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
84
84
  */
85
85
  recommendationConfigs(t) {
86
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : w();
86
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : v();
87
87
  },
88
88
  /**
89
89
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -138,8 +138,8 @@ const z = () => ({
138
138
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
139
139
  },
140
140
  getActivePredictiveAlgorithms: (t) => {
141
- const e = b(), r = [];
142
- return t.activePredictiveAlgorithms.filter((n) => !L.includes(n)).forEach((n) => {
141
+ const e = I(), r = [];
142
+ return t.activePredictiveAlgorithms.filter((n) => !x.includes(n)).forEach((n) => {
143
143
  r.push(...e.filter((s) => s.id === n));
144
144
  }), r.map((n) => ({
145
145
  text: n.name,
@@ -157,7 +157,7 @@ const z = () => ({
157
157
  getFilterList() {
158
158
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
159
159
  let e;
160
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : v.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
160
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : D.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
161
161
  text: t.displayName,
162
162
  value: e,
163
163
  type: t.attributeType
@@ -179,7 +179,7 @@ const z = () => ({
179
179
  setCurrentBlock(t) {
180
180
  this.blockStates[t] || (this.blockStates = {
181
181
  ...this.blockStates,
182
- [t]: C()
182
+ [t]: w()
183
183
  }), this.currentRecommendationId = t;
184
184
  },
185
185
  /**
@@ -247,7 +247,7 @@ const z = () => ({
247
247
  decimalCount: String(e.currencyDecimalCount),
248
248
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
249
249
  thousandSeparator: r(e.currencyThousandSeparator, ".")
250
- }, s = !this.blockStates[t], i = s ? C() : this.blockStates[t];
250
+ }, s = !this.blockStates[t], i = s ? w() : this.blockStates[t];
251
251
  i.recommendationConfigs = {
252
252
  ...i.recommendationConfigs,
253
253
  strategy: e.strategy,
@@ -381,7 +381,7 @@ const z = () => ({
381
381
  });
382
382
  },
383
383
  removeStrategy() {
384
- this.patchCurrentBlockConfig({ strategyId: "", size: N.size });
384
+ this.patchCurrentBlockConfig({ strategyId: "", size: L.size });
385
385
  },
386
386
  // ====================================================================
387
387
  // Shared Data Fetching (fetched once, used by all blocks)
@@ -453,7 +453,7 @@ const z = () => ({
453
453
  const n = [...e.recommendationConfigs.filters];
454
454
  n[r] = {
455
455
  ...t,
456
- isValid: B(t)
456
+ isValid: M(t)
457
457
  }, e.recommendationConfigs.filters = n;
458
458
  }
459
459
  },
@@ -491,7 +491,7 @@ const z = () => ({
491
491
  }), e.recommendationConfigs.filters = r;
492
492
  },
493
493
  generateFilterQuery() {
494
- return k(this.recommendationConfigs.filters);
494
+ return R(this.recommendationConfigs.filters);
495
495
  },
496
496
  /**
497
497
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -499,7 +499,14 @@ const z = () => ({
499
499
  * every block's recommendationConfigs across user edits.
500
500
  */
501
501
  hasInvalidBlock() {
502
- return Object.values(this.blockStates).some((t) => !M(t.recommendationConfigs, this));
502
+ return Object.values(this.blockStates).some((t) => !C(t.recommendationConfigs, this));
503
+ },
504
+ /**
505
+ * Per-instance counterpart to `hasInvalidBlock`: the ids of every block
506
+ * that fails required-field validation, one drawer row each (SD-147430).
507
+ */
508
+ invalidBlockIds() {
509
+ return Object.entries(this.blockStates).filter(([, t]) => !C(t.recommendationConfigs, this)).map(([t]) => t);
503
510
  },
504
511
  // ====================================================================
505
512
  // Per-Block Product Fetching
@@ -520,12 +527,12 @@ const z = () => ({
520
527
  },
521
528
  async _doFetchProducts() {
522
529
  var d;
523
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = F();
530
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = P();
524
531
  if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
525
532
  await this._doFetchStrategyProducts(t);
526
533
  return;
527
534
  }
528
- const s = r.filters.filter((m) => m.isValid), i = k(s), o = ((d = b().find((m) => m.key === r.strategy)) == null ? void 0 : d.path) || "", c = parseInt(r.size) || 6, a = {
535
+ const s = r.filters.filter((m) => m.isValid), i = R(s), o = ((d = I().find((m) => m.key === r.strategy)) == null ? void 0 : d.path) || "", c = parseInt(r.size) || 6, a = {
529
536
  locale: r.language,
530
537
  currency: r.currencySettings.value,
531
538
  partnerName: n.partnerName,
@@ -535,7 +542,7 @@ const z = () => ({
535
542
  };
536
543
  r.strategy === "manualMerchandising" ? a.productId = A(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), i && (a.filter = i), r.shuffleProducts && (a.shuffle = !0), Object.assign(
537
544
  a,
538
- x(n.partnerName, r.strategy)
545
+ B(n.partnerName, r.strategy)
539
546
  );
540
547
  let l;
541
548
  try {
@@ -557,7 +564,7 @@ const z = () => ({
557
564
  );
558
565
  if (i) {
559
566
  s = parseInt(String(i.size ?? "")) || s;
560
- const o = r.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), l = a.indexOf("?"), d = l === -1 ? a : a.slice(0, l), m = (l === -1 ? "" : a.slice(l + 1)).split("&").filter((I) => I && !I.startsWith("user_id=")), y = m.length ? `${d}?${m.join("&")}` : d;
567
+ const o = r.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), l = a.indexOf("?"), d = l === -1 ? a : a.slice(0, l), m = (l === -1 ? "" : a.slice(l + 1)).split("&").filter((b) => b && !b.startsWith("user_id=")), y = m.length ? `${d}?${m.join("&")}` : d;
561
568
  y && (n = await u.fetchRecommendationProductsByUrl(y));
562
569
  }
563
570
  } catch {
@@ -568,10 +575,10 @@ const z = () => ({
568
575
  _setBlockProducts(t, e, r) {
569
576
  if (!this.blockStates[t])
570
577
  return;
571
- const n = e.length > 0 ? e : R(r);
578
+ const n = e.length > 0 ? e : k(r);
572
579
  this.blockStates[t].recommendationProducts = [
573
580
  ...n.slice(0, r),
574
- ...R(Math.max(0, r - n.length))
581
+ ...k(Math.max(0, r - n.length))
575
582
  ];
576
583
  }
577
584
  }
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .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-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__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-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-050dfa52]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-050dfa52]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-050dfa52]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .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-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{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-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}
1
+ @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-b8f24f34] .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-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__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-f938f128]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.validation-error-list[data-v-30d2e1f9]{gap:16px}.validation-error-row[data-v-30d2e1f9]{gap:8px;padding:24px;border:1px solid #E1E1E1;border-radius:8px}.validation-error-row__icon[data-v-30d2e1f9]{flex-shrink:0;font-size:24px;line-height:0}.validation-error-row__text[data-v-30d2e1f9]{font-size:13px;line-height:16px;color:#292929;word-break:break-word}.guido-editor__wrapper[data-v-584fdf72]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-584fdf72]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-584fdf72]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .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-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{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-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}