@useinsider/guido 3.19.0-beta.3906ea8 → 3.19.0-beta.3abc7c5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/@types/config/schemas.js +33 -31
- package/dist/components/Guido.vue.js +8 -8
- package/dist/components/Guido.vue2.js +50 -48
- package/dist/components/organisms/AutoSaveController.vue2.js +9 -8
- package/dist/components/organisms/header/EditorActions.vue.js +2 -2
- package/dist/components/organisms/header/EditorActions.vue2.js +85 -57
- package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
- package/dist/components/organisms/header/LeftSlot.vue2.js +16 -12
- package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +20 -0
- package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +48 -0
- package/dist/components/wrappers/WpDrawer.vue.js +9 -7
- package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
- package/dist/composables/useActionsApi.js +52 -47
- package/dist/composables/useCodeEditorApi.js +23 -9
- package/dist/composables/useHtmlCompiler.js +37 -26
- package/dist/composables/useHtmlValidator.js +2 -0
- package/dist/composables/useSave.js +90 -45
- package/dist/composables/useSaveValidation.js +279 -0
- package/dist/composables/useSaveValidationRollout.js +9 -0
- package/dist/composables/useTranslations.js +3 -2
- package/dist/composables/useVersionHistoryApi.js +33 -24
- package/dist/composables/validators/useCouponBlockValidator.js +24 -21
- package/dist/composables/validators/useUnsubscribeBlockValidator.js +34 -27
- package/dist/enums/save-validation.js +4 -0
- package/dist/extensions/Blocks/Recommendation/store/recommendation.js +54 -47
- package/dist/guido.css +1 -1
- package/dist/src/@types/config/schemas.d.ts +4 -0
- package/dist/src/@types/save-validation.d.ts +19 -0
- package/dist/src/components/Guido.vue.d.ts +17 -1
- package/dist/src/components/organisms/header/EditorActions.vue.d.ts +17 -1
- package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +17 -1
- package/dist/src/components/organisms/header/RightSlot.vue.d.ts +17 -1
- package/dist/src/components/organisms/validation/ValidationErrorDrawer.vue.d.ts +2 -0
- package/dist/src/components/wrappers/WpDrawer.vue.d.ts +4 -3
- package/dist/src/composables/useConfig.d.ts +2 -0
- package/dist/src/composables/useHtmlValidator.d.ts +21 -0
- package/dist/src/composables/useSave.d.ts +6 -1
- package/dist/src/composables/useSaveValidation.d.ts +6 -0
- package/dist/src/composables/useSaveValidationRollout.d.ts +3 -0
- package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
- package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +10 -0
- package/dist/src/enums/save-validation.d.ts +27 -0
- package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +5 -0
- package/dist/src/stores/config.d.ts +32 -0
- package/dist/src/stores/save-validation.d.ts +22 -0
- package/dist/src/utils/stripoEditorApi.d.ts +4 -0
- package/dist/src/utils/templatePreparation.d.ts +30 -0
- package/dist/stores/config.js +84 -67
- package/dist/stores/save-validation.js +30 -0
- package/dist/stores/unsubscribe.js +3 -3
- package/dist/utils/stripoEditorApi.js +11 -0
- package/dist/utils/templatePreparation.js +39 -36
- package/package.json +3 -2
|
@@ -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,27 +1,27 @@
|
|
|
1
|
-
import { useToaster as
|
|
2
|
-
import { useTranslations as
|
|
3
|
-
import { getRecommendationFeedSourceMaps as F, strategyEditPath as
|
|
4
|
-
import { ToasterTypeOptions as
|
|
5
|
-
import { useRecommendationApi as
|
|
6
|
-
import { useConfigStore as
|
|
7
|
-
import { defineStore as
|
|
1
|
+
import { useToaster as L } from "../../../../composables/useToaster.js";
|
|
2
|
+
import { useTranslations as U } from "../../../../composables/useTranslations.js";
|
|
3
|
+
import { getRecommendationFeedSourceMaps as F, strategyEditPath as E, URLS as N, getOperatorOptions as M, PriceAttributes as A } from "../../../../enums/extensions/recommendationBlock.js";
|
|
4
|
+
import { ToasterTypeOptions as B } from "../../../../enums/toaster.js";
|
|
5
|
+
import { useRecommendationApi as O } from "../../../../services/recommendationApi.js";
|
|
6
|
+
import { useConfigStore as R } from "../../../../stores/config.js";
|
|
7
|
+
import { defineStore as T } from "pinia";
|
|
8
8
|
import "../constants/selectors.js";
|
|
9
|
-
import { DEFAULT_MOBILE_CARDS_IN_ROW as
|
|
10
|
-
import { CURRENT_CONFIG_VERSION as C, DEFAULT_NODE_CONFIG as
|
|
9
|
+
import { DEFAULT_MOBILE_CARDS_IN_ROW as z, DEFAULT_CARDS_IN_ROW as x } from "../constants/layout.js";
|
|
10
|
+
import { CURRENT_CONFIG_VERSION as C, DEFAULT_NODE_CONFIG as V, EXCLUDED_ALGORITHM_IDS as j } from "../constants/defaultConfig.js";
|
|
11
11
|
import { getDefaultProducts as w } from "../templates/utils.js";
|
|
12
12
|
import { generateCompleteFilterQuery as P } from "../utils/filterUtil.js";
|
|
13
|
-
import { isLegacyStrategyConfig as
|
|
14
|
-
import { getPartnerRecommendationParams as
|
|
15
|
-
import { sanitizeProductIds as
|
|
16
|
-
import { isFilterValid as
|
|
17
|
-
import { getInvalidFields as
|
|
18
|
-
const m =
|
|
19
|
-
let S = null, p = null, y = null, I = "",
|
|
13
|
+
import { isLegacyStrategyConfig as $ } from "../utils/legacyStrategyMigration.js";
|
|
14
|
+
import { getPartnerRecommendationParams as q } from "../utils/partnerCustomizations.js";
|
|
15
|
+
import { sanitizeProductIds as K } from "../utils/productIds.js";
|
|
16
|
+
import { isFilterValid as H } from "../validation/filterSchema.js";
|
|
17
|
+
import { getInvalidFields as J, isConfigValid as G } from "../validation/requiredFields.js";
|
|
18
|
+
const m = O();
|
|
19
|
+
let S = null, p = null, y = null, I = "", k = null, b = 0;
|
|
20
20
|
const g = /* @__PURE__ */ new Map();
|
|
21
21
|
let l = null;
|
|
22
|
-
function
|
|
22
|
+
function D() {
|
|
23
23
|
return {
|
|
24
|
-
cardsInRow:
|
|
24
|
+
cardsInRow: x,
|
|
25
25
|
currencySettings: {
|
|
26
26
|
name: "USD",
|
|
27
27
|
value: "USD",
|
|
@@ -35,7 +35,7 @@ function _() {
|
|
|
35
35
|
productIds: [],
|
|
36
36
|
id: 1,
|
|
37
37
|
language: "en_US",
|
|
38
|
-
mobileCardsInRow:
|
|
38
|
+
mobileCardsInRow: z,
|
|
39
39
|
mobileLayoutEnabled: !1,
|
|
40
40
|
orientation: "grid",
|
|
41
41
|
recommendedProducts: [],
|
|
@@ -45,7 +45,7 @@ function _() {
|
|
|
45
45
|
// that default is what reached buildCampaignUrl and baked a most-popular feed URL
|
|
46
46
|
// for a block the user never configured (SD-149916). Legacy blocks still get their real
|
|
47
47
|
// algorithm from the node-config sync or the save-time seed.
|
|
48
|
-
strategy:
|
|
48
|
+
strategy: R().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
|
|
49
49
|
strategyId: "",
|
|
50
50
|
nodeConfigVersion: C,
|
|
51
51
|
textTrimming: !0,
|
|
@@ -56,9 +56,9 @@ function _() {
|
|
|
56
56
|
priceHideIfSameAsDiscounted: !1
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function _() {
|
|
60
60
|
return {
|
|
61
|
-
recommendationConfigs:
|
|
61
|
+
recommendationConfigs: D(),
|
|
62
62
|
recommendationProducts: [],
|
|
63
63
|
filterStatus: !1,
|
|
64
64
|
filterSelectionDrawerStatus: !1,
|
|
@@ -87,7 +87,7 @@ const Q = () => ({
|
|
|
87
87
|
currentRecommendationId: null,
|
|
88
88
|
strategyPanelOpen: !1,
|
|
89
89
|
configVersion: 0
|
|
90
|
-
}), ft =
|
|
90
|
+
}), ft = T("guidoRecommendationExtension", {
|
|
91
91
|
state: () => Q(),
|
|
92
92
|
getters: {
|
|
93
93
|
// ====================================================================
|
|
@@ -98,7 +98,7 @@ const Q = () => ({
|
|
|
98
98
|
* This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
|
|
99
99
|
*/
|
|
100
100
|
recommendationConfigs(t) {
|
|
101
|
-
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs :
|
|
101
|
+
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : D();
|
|
102
102
|
},
|
|
103
103
|
/**
|
|
104
104
|
* Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
|
|
@@ -119,7 +119,7 @@ const Q = () => ({
|
|
|
119
119
|
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].removeStrategyModalStatus : !1;
|
|
120
120
|
},
|
|
121
121
|
isLegacyStrategyBlock() {
|
|
122
|
-
return
|
|
122
|
+
return R().isFeatureEnabled("reusableRecommendationStrategy") && $(this.recommendationConfigs);
|
|
123
123
|
},
|
|
124
124
|
legacyConversionLoading(t) {
|
|
125
125
|
return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].legacyConversionLoading ?? !1 : !1;
|
|
@@ -163,7 +163,7 @@ const Q = () => ({
|
|
|
163
163
|
},
|
|
164
164
|
getActivePredictiveAlgorithms: (t) => {
|
|
165
165
|
const e = F(), r = [];
|
|
166
|
-
return t.activePredictiveAlgorithms.filter((n) => !
|
|
166
|
+
return t.activePredictiveAlgorithms.filter((n) => !j.includes(n)).forEach((n) => {
|
|
167
167
|
r.push(...e.filter((s) => s.id === n));
|
|
168
168
|
}), r.map((n) => ({
|
|
169
169
|
text: n.name,
|
|
@@ -181,7 +181,7 @@ const Q = () => ({
|
|
|
181
181
|
getFilterList() {
|
|
182
182
|
return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
|
|
183
183
|
let e;
|
|
184
|
-
return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` :
|
|
184
|
+
return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : A.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
|
|
185
185
|
text: t.displayName,
|
|
186
186
|
value: e,
|
|
187
187
|
type: t.attributeType
|
|
@@ -203,7 +203,7 @@ const Q = () => ({
|
|
|
203
203
|
setCurrentBlock(t) {
|
|
204
204
|
this.blockStates[t] || (this.blockStates = {
|
|
205
205
|
...this.blockStates,
|
|
206
|
-
[t]:
|
|
206
|
+
[t]: _()
|
|
207
207
|
}), this.currentRecommendationId = t;
|
|
208
208
|
},
|
|
209
209
|
/**
|
|
@@ -234,7 +234,7 @@ const Q = () => ({
|
|
|
234
234
|
const r = this.blockStates[t];
|
|
235
235
|
if (r) {
|
|
236
236
|
const o = structuredClone(r);
|
|
237
|
-
o.recommendationConfigs.id = e, o.recommendationConfigs.strategyId &&
|
|
237
|
+
o.recommendationConfigs.id = e, o.recommendationConfigs.strategyId && R().isFeatureEnabled("reusableRecommendationStrategy") && (o.isInitialized = !1, o.syncedConfigFingerprint = null), this.blockStates = {
|
|
238
238
|
...this.blockStates,
|
|
239
239
|
[e]: o
|
|
240
240
|
};
|
|
@@ -275,7 +275,7 @@ const Q = () => ({
|
|
|
275
275
|
decimalCount: String(e.currencyDecimalCount),
|
|
276
276
|
decimalSeparator: r(e.currencyDecimalSeparator, ","),
|
|
277
277
|
thousandSeparator: r(e.currencyThousandSeparator, ".")
|
|
278
|
-
}, s = !this.blockStates[t], i = s ?
|
|
278
|
+
}, s = !this.blockStates[t], i = s ? _() : this.blockStates[t];
|
|
279
279
|
i.recommendationConfigs = {
|
|
280
280
|
...i.recommendationConfigs,
|
|
281
281
|
strategy: e.strategy,
|
|
@@ -314,7 +314,7 @@ const Q = () => ({
|
|
|
314
314
|
* Creates a filter with the first available attribute and operator pre-selected.
|
|
315
315
|
*/
|
|
316
316
|
createDefaultFilter(t, e) {
|
|
317
|
-
const [r] = this.getFilterList, [n] =
|
|
317
|
+
const [r] = this.getFilterList, [n] = M(r == null ? void 0 : r.type);
|
|
318
318
|
return {
|
|
319
319
|
type: "standardFilter",
|
|
320
320
|
attribute: (r == null ? void 0 : r.value) ?? "",
|
|
@@ -380,9 +380,9 @@ const Q = () => ({
|
|
|
380
380
|
this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].removeStrategyModalStatus = !1);
|
|
381
381
|
},
|
|
382
382
|
async fetchStrategies() {
|
|
383
|
-
for (;
|
|
383
|
+
for (; k; )
|
|
384
384
|
try {
|
|
385
|
-
await
|
|
385
|
+
await k;
|
|
386
386
|
} catch {
|
|
387
387
|
}
|
|
388
388
|
if (this.strategiesFetched)
|
|
@@ -398,11 +398,11 @@ const Q = () => ({
|
|
|
398
398
|
this.strategiesLoading = !1, t === b && (this.strategiesSettled = !0);
|
|
399
399
|
}
|
|
400
400
|
})();
|
|
401
|
-
|
|
401
|
+
k = e;
|
|
402
402
|
try {
|
|
403
403
|
await e;
|
|
404
404
|
} finally {
|
|
405
|
-
|
|
405
|
+
k = null;
|
|
406
406
|
}
|
|
407
407
|
},
|
|
408
408
|
/**
|
|
@@ -434,9 +434,9 @@ const Q = () => ({
|
|
|
434
434
|
_armStrategyPageReturn(t) {
|
|
435
435
|
const e = window.open(t, "_blank");
|
|
436
436
|
if (!e)
|
|
437
|
-
return
|
|
438
|
-
type:
|
|
439
|
-
message:
|
|
437
|
+
return L().showToaster({
|
|
438
|
+
type: B.Error,
|
|
439
|
+
message: U()("discovery-strategy.error-description")
|
|
440
440
|
}), !1;
|
|
441
441
|
e.opener = null, l == null || l();
|
|
442
442
|
let r = !1;
|
|
@@ -471,14 +471,14 @@ const Q = () => ({
|
|
|
471
471
|
* new strategy is offered; the user picks it themselves.
|
|
472
472
|
*/
|
|
473
473
|
openStrategyCreation() {
|
|
474
|
-
return this._armStrategyPageReturn(
|
|
474
|
+
return this._armStrategyPageReturn(N.RECOMMENDATION_STRATEGY_CREATE_PATH);
|
|
475
475
|
},
|
|
476
476
|
/**
|
|
477
477
|
* Opens the strategy's edit page. Every return refreshes the list so the block reflects the
|
|
478
478
|
* edited configuration.
|
|
479
479
|
*/
|
|
480
480
|
openStrategyEdit(t) {
|
|
481
|
-
return this._armStrategyPageReturn(
|
|
481
|
+
return this._armStrategyPageReturn(E(t));
|
|
482
482
|
},
|
|
483
483
|
async resolveStrategyUrlTemplate(t, e) {
|
|
484
484
|
const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
|
|
@@ -532,7 +532,7 @@ const Q = () => ({
|
|
|
532
532
|
this.patchCurrentBlockConfig({
|
|
533
533
|
strategyId: "",
|
|
534
534
|
strategy: "",
|
|
535
|
-
size:
|
|
535
|
+
size: V.size,
|
|
536
536
|
nodeConfigVersion: C
|
|
537
537
|
});
|
|
538
538
|
},
|
|
@@ -606,7 +606,7 @@ const Q = () => ({
|
|
|
606
606
|
const n = [...e.recommendationConfigs.filters];
|
|
607
607
|
n[r] = {
|
|
608
608
|
...t,
|
|
609
|
-
isValid:
|
|
609
|
+
isValid: H(t)
|
|
610
610
|
}, e.recommendationConfigs.filters = n;
|
|
611
611
|
}
|
|
612
612
|
},
|
|
@@ -652,14 +652,21 @@ const Q = () => ({
|
|
|
652
652
|
* every block's recommendationConfigs across user edits.
|
|
653
653
|
*/
|
|
654
654
|
hasInvalidBlock() {
|
|
655
|
-
return Object.values(this.blockStates).some((t) => !
|
|
655
|
+
return Object.values(this.blockStates).some((t) => !G(t.recommendationConfigs, this));
|
|
656
|
+
},
|
|
657
|
+
/**
|
|
658
|
+
* Per-instance counterpart to `hasInvalidBlock`: the ids of every block
|
|
659
|
+
* that fails required-field validation, one drawer row each (SD-147430).
|
|
660
|
+
*/
|
|
661
|
+
invalidBlockIds() {
|
|
662
|
+
return Object.entries(this.blockStates).filter(([, t]) => !G(t.recommendationConfigs, this)).map(([t]) => t);
|
|
656
663
|
},
|
|
657
664
|
/**
|
|
658
665
|
* Which required fields are missing across the design's blocks, so the save
|
|
659
666
|
* toaster can name the cause instead of always showing the generic message.
|
|
660
667
|
*/
|
|
661
668
|
invalidBlockFields() {
|
|
662
|
-
return Object.values(this.blockStates).flatMap((t) =>
|
|
669
|
+
return Object.values(this.blockStates).flatMap((t) => J(t.recommendationConfigs, this));
|
|
663
670
|
},
|
|
664
671
|
// ====================================================================
|
|
665
672
|
// Per-Block Product Fetching
|
|
@@ -685,7 +692,7 @@ const Q = () => ({
|
|
|
685
692
|
},
|
|
686
693
|
async _doFetchProducts() {
|
|
687
694
|
var h;
|
|
688
|
-
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n =
|
|
695
|
+
const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = R();
|
|
689
696
|
if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
|
|
690
697
|
await this._doFetchStrategyProducts(t);
|
|
691
698
|
return;
|
|
@@ -705,9 +712,9 @@ const Q = () => ({
|
|
|
705
712
|
details: !0,
|
|
706
713
|
campaignId: n.variationId
|
|
707
714
|
};
|
|
708
|
-
r.strategy === "manualMerchandising" ? c.productId =
|
|
715
|
+
r.strategy === "manualMerchandising" ? c.productId = K(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), a && (c.filter = a), r.shuffleProducts && (c.shuffle = !0), Object.assign(
|
|
709
716
|
c,
|
|
710
|
-
|
|
717
|
+
q(n.partnerName, r.strategy)
|
|
711
718
|
);
|
|
712
719
|
let u;
|
|
713
720
|
try {
|
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-
|
|
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-5e7f10cc]{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-74f39e54]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-74f39e54]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-74f39e54]{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-19d8cb42] .vueperslides__bullets,[data-v-ec2027ff] .vueperslides__bullets,[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-6ce48dfd]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-6ce48dfd] .in-skeleton{width:100%!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-6ce48dfd] .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}.legacy-strategy-updated-modal[data-v-da2225a1]{position:relative;z-index:145}
|
|
@@ -403,6 +403,8 @@ export declare const FeaturesSchema: v.ObjectSchema<{
|
|
|
403
403
|
readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
404
404
|
/** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
|
|
405
405
|
readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
406
|
+
/** Allow persisting a template that still has validation errors (drawer "Save and Continue" soft-save). Host sets false for transactional + unsubscribe. */
|
|
407
|
+
readonly allowSaveWithErrors: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
|
|
406
408
|
readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
407
409
|
}, undefined>;
|
|
408
410
|
/**
|
|
@@ -811,6 +813,8 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
|
|
|
811
813
|
readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
812
814
|
/** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
|
|
813
815
|
readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
816
|
+
/** Allow persisting a template that still has validation errors (drawer "Save and Continue" soft-save). Host sets false for transactional + unsubscribe. */
|
|
817
|
+
readonly allowSaveWithErrors: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
|
|
814
818
|
readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
815
819
|
}, undefined>, {}>;
|
|
816
820
|
/** Block configuration */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SaveValidationRule } from '@@/Enums/save-validation';
|
|
2
|
+
import type { ToasterTypeOptions } from '@@/Enums/toaster';
|
|
3
|
+
/** Which action triggered the validation run. Drives telemetry only in v1. */
|
|
4
|
+
export type SaveValidationIntent = 'save' | 'back' | 'test';
|
|
5
|
+
/**
|
|
6
|
+
* A single validation failure instance, one per drawer row. Names avoid the
|
|
7
|
+
* public `ValidationError` (config validation) collision — NOT exported from
|
|
8
|
+
* library.ts.
|
|
9
|
+
*/
|
|
10
|
+
export interface SaveValidationError {
|
|
11
|
+
/** Stable discriminator for tests and de-duplication. */
|
|
12
|
+
rule: SaveValidationRule;
|
|
13
|
+
/** Final translated string — trans() is applied at collect time. */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Raw user content (field name / tag / src) — not i18n. */
|
|
16
|
+
detail?: string;
|
|
17
|
+
/** Mirrors the legacy toaster severity. Kept for the discriminator/tests; the drawer renders a uniform icon and never reads it. */
|
|
18
|
+
type: ToasterTypeOptions;
|
|
19
|
+
}
|
|
@@ -15,7 +15,23 @@ declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_T
|
|
|
15
15
|
close: () => void;
|
|
16
16
|
};
|
|
17
17
|
hasChanges: import("vue").ComputedRef<boolean>;
|
|
18
|
-
saveSilent: () => Promise<
|
|
18
|
+
saveSilent: () => Promise<{
|
|
19
|
+
dynamicContentList: import("@@/Types/generic").DynamicContent[];
|
|
20
|
+
compiledHtml: string;
|
|
21
|
+
rawHtml: string;
|
|
22
|
+
css: string;
|
|
23
|
+
ampHtml: string;
|
|
24
|
+
ampErrors: string[];
|
|
25
|
+
modules: number[];
|
|
26
|
+
recommendation: {
|
|
27
|
+
campaignUrls: Record<string, import("../@types/recommendation").RecommendationCampaignUrlValue>;
|
|
28
|
+
configs: Record<string, string>;
|
|
29
|
+
};
|
|
30
|
+
unsubscribe: {
|
|
31
|
+
status: boolean;
|
|
32
|
+
config: number[];
|
|
33
|
+
};
|
|
34
|
+
} | undefined> | undefined;
|
|
19
35
|
setLoading: (value: boolean) => void;
|
|
20
36
|
}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {
|
|
21
37
|
"dynamic-content:open": (detail: {
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
2
|
-
handleSave: (isSilent: boolean) => Promise<
|
|
2
|
+
handleSave: (isSilent: boolean) => Promise<{
|
|
3
|
+
dynamicContentList: import("../../../@types/generic.js").DynamicContent[];
|
|
4
|
+
compiledHtml: string;
|
|
5
|
+
rawHtml: string;
|
|
6
|
+
css: string;
|
|
7
|
+
ampHtml: string;
|
|
8
|
+
ampErrors: string[];
|
|
9
|
+
modules: number[];
|
|
10
|
+
recommendation: {
|
|
11
|
+
campaignUrls: Record<string, import("../../../@types/recommendation.js").RecommendationCampaignUrlValue>;
|
|
12
|
+
configs: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
unsubscribe: {
|
|
15
|
+
status: boolean;
|
|
16
|
+
config: number[];
|
|
17
|
+
};
|
|
18
|
+
} | undefined> | undefined;
|
|
3
19
|
}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
4
20
|
export default _default;
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
2
|
-
handleSave: (isSilent: boolean) => Promise<
|
|
2
|
+
handleSave: (isSilent: boolean) => Promise<{
|
|
3
|
+
dynamicContentList: import("../../../@types/generic.js").DynamicContent[];
|
|
4
|
+
compiledHtml: string;
|
|
5
|
+
rawHtml: string;
|
|
6
|
+
css: string;
|
|
7
|
+
ampHtml: string;
|
|
8
|
+
ampErrors: string[];
|
|
9
|
+
modules: number[];
|
|
10
|
+
recommendation: {
|
|
11
|
+
campaignUrls: Record<string, import("../../../@types/recommendation.js").RecommendationCampaignUrlValue>;
|
|
12
|
+
configs: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
unsubscribe: {
|
|
15
|
+
status: boolean;
|
|
16
|
+
config: number[];
|
|
17
|
+
};
|
|
18
|
+
} | undefined> | undefined;
|
|
3
19
|
}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
4
20
|
export default _default;
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
2
|
-
handleSave: (isSilent: boolean) => Promise<
|
|
2
|
+
handleSave: (isSilent: boolean) => Promise<{
|
|
3
|
+
dynamicContentList: import("../../../@types/generic.js").DynamicContent[];
|
|
4
|
+
compiledHtml: string;
|
|
5
|
+
rawHtml: string;
|
|
6
|
+
css: string;
|
|
7
|
+
ampHtml: string;
|
|
8
|
+
ampErrors: string[];
|
|
9
|
+
modules: number[];
|
|
10
|
+
recommendation: {
|
|
11
|
+
campaignUrls: Record<string, import("../../../@types/recommendation.js").RecommendationCampaignUrlValue>;
|
|
12
|
+
configs: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
unsubscribe: {
|
|
15
|
+
status: boolean;
|
|
16
|
+
config: number[];
|
|
17
|
+
};
|
|
18
|
+
} | undefined> | undefined;
|
|
3
19
|
}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
4
20
|
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
2
|
+
export default _default;
|
|
@@ -10,11 +10,11 @@ interface Props {
|
|
|
10
10
|
status: boolean;
|
|
11
11
|
titleText: string;
|
|
12
12
|
}
|
|
13
|
-
declare var
|
|
13
|
+
declare var __VLS_13: {}, __VLS_15: {};
|
|
14
14
|
type __VLS_Slots = {} & {
|
|
15
|
-
headerBottomSlot?: (props: typeof
|
|
15
|
+
headerBottomSlot?: (props: typeof __VLS_13) => any;
|
|
16
16
|
} & {
|
|
17
|
-
default?: (props: typeof
|
|
17
|
+
default?: (props: typeof __VLS_15) => any;
|
|
18
18
|
};
|
|
19
19
|
declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption<Props>, {
|
|
20
20
|
className: string;
|
|
@@ -26,6 +26,7 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<
|
|
|
26
26
|
cancelOrBackButtonEvent: () => void;
|
|
27
27
|
onCloseEvent: () => void;
|
|
28
28
|
primaryButtonEvent: () => void;
|
|
29
|
+
secondaryButtonEvent: () => void;
|
|
29
30
|
}, string, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<Props>, {
|
|
30
31
|
className: string;
|
|
31
32
|
size: string;
|
|
@@ -101,6 +101,7 @@ export declare const useConfig: () => {
|
|
|
101
101
|
autosave: boolean;
|
|
102
102
|
allowlistEnabled: boolean;
|
|
103
103
|
linkTrackingIds: boolean;
|
|
104
|
+
allowSaveWithErrors: boolean;
|
|
104
105
|
reusableRecommendationStrategy: boolean;
|
|
105
106
|
};
|
|
106
107
|
blocks: {
|
|
@@ -238,6 +239,7 @@ export declare const useConfig: () => {
|
|
|
238
239
|
autosave: boolean;
|
|
239
240
|
allowlistEnabled: boolean;
|
|
240
241
|
linkTrackingIds: boolean;
|
|
242
|
+
allowSaveWithErrors: boolean;
|
|
241
243
|
reusableRecommendationStrategy: boolean;
|
|
242
244
|
} | null>;
|
|
243
245
|
blocks: import("vue").ComputedRef<{
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { DynamicContent } from '@@/Types/generic';
|
|
2
2
|
import type { FiltersResponse } from '@@/Types/recommendation';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts recommendation block IDs directly from the HTML by parsing
|
|
5
|
+
* the recommendation-id attribute on block elements.
|
|
6
|
+
* This avoids depending on store state which may not be populated
|
|
7
|
+
* when the settings panel hasn't been opened yet (e.g., saved templates).
|
|
8
|
+
*/
|
|
9
|
+
export declare function getRecommendationBlockIds(html: string): string[];
|
|
3
10
|
/**
|
|
4
11
|
* Parses a `{{recoId_index_attributeName}}` variable (already stripped of the
|
|
5
12
|
* braces/whitespace) into its recommendation id and attribute name. Returns
|
|
@@ -27,6 +34,20 @@ export declare function buildPartnerAttributeNameSet(filterList: FiltersResponse
|
|
|
27
34
|
* before, so flagging it would false-positive on merge tags like `{{user.name}}`.
|
|
28
35
|
*/
|
|
29
36
|
export declare function findInvalidCustomFields(html: string, validCustomFields: string[], recommendationBlockIds: string[], ignoredIds: Set<string>, ignoredAttributeNames: Set<string> | null): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Returns the invalid `{{ ... }}` dynamic-content tags found in the HTML.
|
|
39
|
+
*
|
|
40
|
+
* A tag's variable name is the part before the first `|` (everything after it is the fallback).
|
|
41
|
+
* Surrounding whitespace is always allowed (trimmed). A tag is valid only when the variable name
|
|
42
|
+
* is an allowed system token, or it is a clean identifier (`[A-Za-z0-9_.]`) and — when a `|` is
|
|
43
|
+
* present — the fallback after it is not empty. Otherwise the tag is invalid (e.g.
|
|
44
|
+
* `{{Api Response: OA1 Main Item Url}}` — spaces/`:` — or `{{bounce|}}` — empty fallback).
|
|
45
|
+
*
|
|
46
|
+
* `data-*` block-config attribute values are excluded from the scan: blocks such as Items/cart
|
|
47
|
+
* store product merge tags (`data-product_button_link="{{Abandoned Cart Item (1) Url}}"`) that the
|
|
48
|
+
* backend resolves through its own pipeline, so they must not be treated as user-authored tags.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getInvalidDynamicContentTags(html: string): string[];
|
|
30
51
|
export declare const useHtmlValidator: () => {
|
|
31
52
|
validateHtml: (html: string, customFields: DynamicContent[], isOnSaveValidation?: boolean) => Promise<boolean>;
|
|
32
53
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { SavedTemplateDetails } from '@@/Types/stripo';
|
|
2
|
+
type SaveDetails = Omit<SavedTemplateDetails, 'metadata' | 'silent'>;
|
|
3
|
+
type SaveResult = SaveDetails | undefined;
|
|
2
4
|
export declare const useSave: () => {
|
|
3
|
-
save: (suppressEmit?: boolean, silent?: boolean) => Promise<
|
|
5
|
+
save: (suppressEmit?: boolean, silent?: boolean) => Promise<SaveResult>;
|
|
6
|
+
saveWithErrors: () => Promise<SaveResult>;
|
|
7
|
+
saveRawOnly: () => Promise<SaveResult>;
|
|
4
8
|
};
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DynamicContent } from '@@/Types/generic';
|
|
2
|
+
import type { SaveValidationError } from '@@/Types/save-validation';
|
|
3
|
+
export declare const useSaveValidation: () => {
|
|
4
|
+
collectSaveValidationErrors: (compiledHtml: string, dynamicContentList: DynamicContent[]) => Promise<SaveValidationError[]>;
|
|
5
|
+
validateCurrentTemplate: () => Promise<SaveValidationError[]>;
|
|
6
|
+
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare const COUPON_TAG_MODIFIED_KEY = "newsletter.coupon-tag-modified";
|
|
2
|
+
/** Pure core: the tag text of every coupon block whose code was modified. */
|
|
3
|
+
export declare function findInvalidCouponBlocks(html: string): string[];
|
|
1
4
|
export declare const useCouponBlockValidator: () => {
|
|
2
5
|
validateCouponBlockTags: (html: string) => boolean;
|
|
3
6
|
};
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
declare const SINGLETON_BLOCK_RULES: {
|
|
2
|
+
pageType: number;
|
|
3
|
+
messageKey: string;
|
|
4
|
+
}[];
|
|
5
|
+
export declare const EMPTY_BLOCK_KEY = "unsubscription-preference.select-page-to-continue";
|
|
6
|
+
/** Pure core: the singleton rules whose page type appears more than once. */
|
|
7
|
+
export declare function findDuplicateUnsubPageTypes(html: string): typeof SINGLETON_BLOCK_RULES;
|
|
8
|
+
/** Pure core: count of unsubscribe blocks with no page type or page list chosen. */
|
|
9
|
+
export declare function countUnsubBlocksWithoutPage(html: string): number;
|
|
1
10
|
export declare const useUnsubscribeBlockValidator: () => {
|
|
2
11
|
validateUnsubscribeBlockUniqueness: (html: string) => boolean;
|
|
3
12
|
validateUnsubscribeBlockHasTemplate: (html: string) => boolean;
|
|
4
13
|
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable discriminators for each save-validation rule site — one per rule that
|
|
3
|
+
* can produce a drawer row, kept in sync with the collector + legacy validator chains.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum SaveValidationRule {
|
|
6
|
+
CouponTagModified = "coupon-tag-modified",
|
|
7
|
+
DuplicateUnsubBlock = "duplicate-unsub-block",
|
|
8
|
+
UnsubBlockMissingTemplate = "unsub-block-missing-template",
|
|
9
|
+
LiquidSyntax = "liquid-syntax",
|
|
10
|
+
InvalidLinks = "invalid-links",
|
|
11
|
+
LinkCheckInProgress = "link-check-in-progress",
|
|
12
|
+
CustomFieldScript = "custom-field-script",
|
|
13
|
+
EmptyHtml = "empty-html",
|
|
14
|
+
MismatchedBraces = "mismatched-braces",
|
|
15
|
+
InvalidDynamicTag = "invalid-dynamic-tag",
|
|
16
|
+
InvalidCustomField = "invalid-custom-field",
|
|
17
|
+
ConditionsSyntax = "conditions-syntax",
|
|
18
|
+
WrongEqualityOperator = "wrong-equality-operator",
|
|
19
|
+
MissingIfEndif = "missing-if-endif",
|
|
20
|
+
ConditionTagSpacing = "condition-tag-spacing",
|
|
21
|
+
BracesInsideIfTag = "braces-inside-if-tag",
|
|
22
|
+
RecommendationRequiredFields = "recommendation-required-fields",
|
|
23
|
+
RecommendationFilters = "recommendation-filters",
|
|
24
|
+
InvalidImageType = "invalid-image-type",
|
|
25
|
+
CheckboxUnsubGroup = "checkbox-unsub-group",
|
|
26
|
+
RadioButtonUnsubGroup = "radio-button-unsub-group"
|
|
27
|
+
}
|
|
@@ -612,6 +612,11 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
|
|
|
612
612
|
* every block's recommendationConfigs across user edits.
|
|
613
613
|
*/
|
|
614
614
|
hasInvalidBlock(): boolean;
|
|
615
|
+
/**
|
|
616
|
+
* Per-instance counterpart to `hasInvalidBlock`: the ids of every block
|
|
617
|
+
* that fails required-field validation, one drawer row each (SD-147430).
|
|
618
|
+
*/
|
|
619
|
+
invalidBlockIds(): string[];
|
|
615
620
|
/**
|
|
616
621
|
* Which required fields are missing across the design's blocks, so the save
|
|
617
622
|
* toaster can name the cause instead of always showing the generic message.
|