@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.
- package/README.md +1 -0
- package/dist/@types/config/schemas.js +28 -26
- package/dist/components/Guido.vue.js +8 -8
- package/dist/components/Guido.vue2.js +53 -51
- 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 +86 -56
- 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/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/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 +43 -36
- 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 +30 -0
- package/dist/src/stores/save-validation.d.ts +22 -0
- package/dist/src/utils/liquidDynamicContent.d.ts +20 -0
- package/dist/src/utils/templatePreparation.d.ts +30 -0
- package/dist/stores/config.js +39 -22
- package/dist/stores/save-validation.js +30 -0
- package/dist/utils/genericUtil.js +64 -61
- package/dist/utils/liquidDynamicContent.js +15 -0
- package/dist/utils/templatePreparation.js +28 -25
- package/package.json +1 -1
|
@@ -395,6 +395,8 @@ export declare const FeaturesSchema: v.ObjectSchema<{
|
|
|
395
395
|
readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
396
396
|
/** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
|
|
397
397
|
readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
398
|
+
/** Allow persisting a template that still has validation errors (drawer "Save and Continue" soft-save). Host sets false for transactional + unsubscribe. */
|
|
399
|
+
readonly allowSaveWithErrors: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
|
|
398
400
|
readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
399
401
|
}, undefined>;
|
|
400
402
|
/**
|
|
@@ -795,6 +797,8 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
|
|
|
795
797
|
readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
796
798
|
/** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
|
|
797
799
|
readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
800
|
+
/** Allow persisting a template that still has validation errors (drawer "Save and Continue" soft-save). Host sets false for transactional + unsubscribe. */
|
|
801
|
+
readonly allowSaveWithErrors: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
|
|
798
802
|
readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
799
803
|
}, undefined>, {}>;
|
|
800
804
|
/** 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;
|
|
@@ -99,6 +99,7 @@ export declare const useConfig: () => {
|
|
|
99
99
|
autosave: boolean;
|
|
100
100
|
allowlistEnabled: boolean;
|
|
101
101
|
linkTrackingIds: boolean;
|
|
102
|
+
allowSaveWithErrors: boolean;
|
|
102
103
|
reusableRecommendationStrategy: boolean;
|
|
103
104
|
};
|
|
104
105
|
blocks: {
|
|
@@ -234,6 +235,7 @@ export declare const useConfig: () => {
|
|
|
234
235
|
autosave: boolean;
|
|
235
236
|
allowlistEnabled: boolean;
|
|
236
237
|
linkTrackingIds: boolean;
|
|
238
|
+
allowSaveWithErrors: boolean;
|
|
237
239
|
reusableRecommendationStrategy: boolean;
|
|
238
240
|
} | null>;
|
|
239
241
|
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
|
+
}
|
|
@@ -462,6 +462,11 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
|
|
|
462
462
|
* every block's recommendationConfigs across user edits.
|
|
463
463
|
*/
|
|
464
464
|
hasInvalidBlock(): boolean;
|
|
465
|
+
/**
|
|
466
|
+
* Per-instance counterpart to `hasInvalidBlock`: the ids of every block
|
|
467
|
+
* that fails required-field validation, one drawer row each (SD-147430).
|
|
468
|
+
*/
|
|
469
|
+
invalidBlockIds(): string[];
|
|
465
470
|
fetchRecommendationProducts(): Promise<void>;
|
|
466
471
|
_doFetchProducts(): Promise<void>;
|
|
467
472
|
_doFetchStrategyProducts(blockId: number): Promise<void>;
|
|
@@ -104,6 +104,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
104
104
|
autosave: boolean;
|
|
105
105
|
allowlistEnabled: boolean;
|
|
106
106
|
linkTrackingIds: boolean;
|
|
107
|
+
allowSaveWithErrors: boolean;
|
|
107
108
|
reusableRecommendationStrategy: boolean;
|
|
108
109
|
};
|
|
109
110
|
blocks: {
|
|
@@ -245,6 +246,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
245
246
|
autosave: boolean;
|
|
246
247
|
allowlistEnabled: boolean;
|
|
247
248
|
linkTrackingIds: boolean;
|
|
249
|
+
allowSaveWithErrors: boolean;
|
|
248
250
|
reusableRecommendationStrategy: boolean;
|
|
249
251
|
};
|
|
250
252
|
blocks: {
|
|
@@ -386,6 +388,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
386
388
|
autosave: boolean;
|
|
387
389
|
allowlistEnabled: boolean;
|
|
388
390
|
linkTrackingIds: boolean;
|
|
391
|
+
allowSaveWithErrors: boolean;
|
|
389
392
|
reusableRecommendationStrategy: boolean;
|
|
390
393
|
};
|
|
391
394
|
blocks: {
|
|
@@ -527,6 +530,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
527
530
|
autosave: boolean;
|
|
528
531
|
allowlistEnabled: boolean;
|
|
529
532
|
linkTrackingIds: boolean;
|
|
533
|
+
allowSaveWithErrors: boolean;
|
|
530
534
|
reusableRecommendationStrategy: boolean;
|
|
531
535
|
};
|
|
532
536
|
blocks: {
|
|
@@ -668,6 +672,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
668
672
|
autosave: boolean;
|
|
669
673
|
allowlistEnabled: boolean;
|
|
670
674
|
linkTrackingIds: boolean;
|
|
675
|
+
allowSaveWithErrors: boolean;
|
|
671
676
|
reusableRecommendationStrategy: boolean;
|
|
672
677
|
};
|
|
673
678
|
blocks: {
|
|
@@ -809,6 +814,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
809
814
|
autosave: boolean;
|
|
810
815
|
allowlistEnabled: boolean;
|
|
811
816
|
linkTrackingIds: boolean;
|
|
817
|
+
allowSaveWithErrors: boolean;
|
|
812
818
|
reusableRecommendationStrategy: boolean;
|
|
813
819
|
};
|
|
814
820
|
blocks: {
|
|
@@ -950,6 +956,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
950
956
|
autosave: boolean;
|
|
951
957
|
allowlistEnabled: boolean;
|
|
952
958
|
linkTrackingIds: boolean;
|
|
959
|
+
allowSaveWithErrors: boolean;
|
|
953
960
|
reusableRecommendationStrategy: boolean;
|
|
954
961
|
};
|
|
955
962
|
blocks: {
|
|
@@ -1091,6 +1098,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1091
1098
|
autosave: boolean;
|
|
1092
1099
|
allowlistEnabled: boolean;
|
|
1093
1100
|
linkTrackingIds: boolean;
|
|
1101
|
+
allowSaveWithErrors: boolean;
|
|
1094
1102
|
reusableRecommendationStrategy: boolean;
|
|
1095
1103
|
};
|
|
1096
1104
|
blocks: {
|
|
@@ -1232,6 +1240,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1232
1240
|
autosave: boolean;
|
|
1233
1241
|
allowlistEnabled: boolean;
|
|
1234
1242
|
linkTrackingIds: boolean;
|
|
1243
|
+
allowSaveWithErrors: boolean;
|
|
1235
1244
|
reusableRecommendationStrategy: boolean;
|
|
1236
1245
|
};
|
|
1237
1246
|
blocks: {
|
|
@@ -1373,6 +1382,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1373
1382
|
autosave: boolean;
|
|
1374
1383
|
allowlistEnabled: boolean;
|
|
1375
1384
|
linkTrackingIds: boolean;
|
|
1385
|
+
allowSaveWithErrors: boolean;
|
|
1376
1386
|
reusableRecommendationStrategy: boolean;
|
|
1377
1387
|
};
|
|
1378
1388
|
blocks: {
|
|
@@ -1514,6 +1524,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1514
1524
|
autosave: boolean;
|
|
1515
1525
|
allowlistEnabled: boolean;
|
|
1516
1526
|
linkTrackingIds: boolean;
|
|
1527
|
+
allowSaveWithErrors: boolean;
|
|
1517
1528
|
reusableRecommendationStrategy: boolean;
|
|
1518
1529
|
};
|
|
1519
1530
|
blocks: {
|
|
@@ -1655,6 +1666,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1655
1666
|
autosave: boolean;
|
|
1656
1667
|
allowlistEnabled: boolean;
|
|
1657
1668
|
linkTrackingIds: boolean;
|
|
1669
|
+
allowSaveWithErrors: boolean;
|
|
1658
1670
|
reusableRecommendationStrategy: boolean;
|
|
1659
1671
|
};
|
|
1660
1672
|
blocks: {
|
|
@@ -1796,6 +1808,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1796
1808
|
autosave: boolean;
|
|
1797
1809
|
allowlistEnabled: boolean;
|
|
1798
1810
|
linkTrackingIds: boolean;
|
|
1811
|
+
allowSaveWithErrors: boolean;
|
|
1799
1812
|
reusableRecommendationStrategy: boolean;
|
|
1800
1813
|
};
|
|
1801
1814
|
blocks: {
|
|
@@ -1937,6 +1950,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
1937
1950
|
autosave: boolean;
|
|
1938
1951
|
allowlistEnabled: boolean;
|
|
1939
1952
|
linkTrackingIds: boolean;
|
|
1953
|
+
allowSaveWithErrors: boolean;
|
|
1940
1954
|
reusableRecommendationStrategy: boolean;
|
|
1941
1955
|
};
|
|
1942
1956
|
blocks: {
|
|
@@ -2078,6 +2092,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
2078
2092
|
autosave: boolean;
|
|
2079
2093
|
allowlistEnabled: boolean;
|
|
2080
2094
|
linkTrackingIds: boolean;
|
|
2095
|
+
allowSaveWithErrors: boolean;
|
|
2081
2096
|
reusableRecommendationStrategy: boolean;
|
|
2082
2097
|
};
|
|
2083
2098
|
blocks: {
|
|
@@ -2121,6 +2136,18 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
2121
2136
|
};
|
|
2122
2137
|
} | null;
|
|
2123
2138
|
} & import("pinia").PiniaCustomStateProperties<ConfigStoreState>) => number;
|
|
2139
|
+
/**
|
|
2140
|
+
* Transactional campaigns keep the hard save block. Reactive getter, so
|
|
2141
|
+
* consumers stay safe in setup where a raw config read snapshots null (SD-147945).
|
|
2142
|
+
*/
|
|
2143
|
+
isTransactional(): boolean;
|
|
2144
|
+
/**
|
|
2145
|
+
* Soft-save policy: may the editor persist a template with validation errors?
|
|
2146
|
+
* Default true (host sets features.allowSaveWithErrors=false for transactional +
|
|
2147
|
+
* unsub). Reactive getter defaulting true — reading via isFeatureEnabled would
|
|
2148
|
+
* floor to false pre-init and momentarily hide the soft-save option (SD-147430).
|
|
2149
|
+
*/
|
|
2150
|
+
allowSaveWithErrors(): boolean;
|
|
2124
2151
|
/**
|
|
2125
2152
|
* Get the username
|
|
2126
2153
|
*/
|
|
@@ -2219,6 +2246,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
2219
2246
|
autosave: boolean;
|
|
2220
2247
|
allowlistEnabled: boolean;
|
|
2221
2248
|
linkTrackingIds: boolean;
|
|
2249
|
+
allowSaveWithErrors: boolean;
|
|
2222
2250
|
reusableRecommendationStrategy: boolean;
|
|
2223
2251
|
};
|
|
2224
2252
|
blocks: {
|
|
@@ -2360,6 +2388,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
2360
2388
|
autosave: boolean;
|
|
2361
2389
|
allowlistEnabled: boolean;
|
|
2362
2390
|
linkTrackingIds: boolean;
|
|
2391
|
+
allowSaveWithErrors: boolean;
|
|
2363
2392
|
reusableRecommendationStrategy: boolean;
|
|
2364
2393
|
};
|
|
2365
2394
|
blocks: {
|
|
@@ -2503,6 +2532,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
|
|
|
2503
2532
|
autosave: boolean;
|
|
2504
2533
|
allowlistEnabled: boolean;
|
|
2505
2534
|
linkTrackingIds: boolean;
|
|
2535
|
+
allowSaveWithErrors: boolean;
|
|
2506
2536
|
reusableRecommendationStrategy: boolean;
|
|
2507
2537
|
};
|
|
2508
2538
|
blocks: {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SaveValidationError, SaveValidationIntent } from '@@/Types/save-validation';
|
|
2
|
+
interface SaveValidationState {
|
|
3
|
+
errors: SaveValidationError[];
|
|
4
|
+
isDrawerOpen: boolean;
|
|
5
|
+
intent: SaveValidationIntent | null;
|
|
6
|
+
}
|
|
7
|
+
export declare const useSaveValidationStore: import("pinia").StoreDefinition<"guidoSaveValidation", SaveValidationState, {
|
|
8
|
+
errorCount: (state: {
|
|
9
|
+
errors: {
|
|
10
|
+
rule: import("../enums/save-validation").SaveValidationRule;
|
|
11
|
+
message: string;
|
|
12
|
+
detail?: string | undefined;
|
|
13
|
+
type: import("../enums/toaster").ToasterTypeOptions;
|
|
14
|
+
}[];
|
|
15
|
+
isDrawerOpen: boolean;
|
|
16
|
+
intent: SaveValidationIntent | null;
|
|
17
|
+
} & import("pinia").PiniaCustomStateProperties<SaveValidationState>) => number;
|
|
18
|
+
}, {
|
|
19
|
+
open(errors: SaveValidationError[], intent: SaveValidationIntent): void;
|
|
20
|
+
close(): void;
|
|
21
|
+
}>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BaseDynamicContent } from '@@/Types/generic';
|
|
2
|
+
export type DynamicContentFormat = NonNullable<BaseDynamicContent['format']>;
|
|
3
|
+
/** Format key the dynamic-content picker sends for a custom date/time format. */
|
|
4
|
+
export declare const TIME_FORMAT_KEY = "time-format";
|
|
5
|
+
/** Wraps a Liquid filter argument in whichever quote style the value itself does not use. */
|
|
6
|
+
export declare const quoteArgument: (value: string) => string | null;
|
|
7
|
+
/**
|
|
8
|
+
* `{ key: 'time-format', value: '%Y-%m-%d' }` -> `date: "%Y-%m-%d"`.
|
|
9
|
+
*
|
|
10
|
+
* Only a custom date/time strftime pattern has a Liquid equivalent. Anything else returns null so
|
|
11
|
+
* the caller emits a plain tag rather than one that silently lost — or literally printed — its
|
|
12
|
+
* format.
|
|
13
|
+
* @example formatToLiquidFilter({ key: 'time-format', value: '%b %-d, %Y' }) -> date: "%b %-d, %Y"
|
|
14
|
+
*/
|
|
15
|
+
export declare const formatToLiquidFilter: (format: DynamicContentFormat) => string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Inverse of `formatToLiquidFilter` — reads the picker format back out of one filter segment, so a
|
|
18
|
+
* reopened tag restores its selection in the host's format dropdown.
|
|
19
|
+
*/
|
|
20
|
+
export declare const liquidFilterToFormat: (segment: string) => DynamicContentFormat | null;
|
|
@@ -15,6 +15,36 @@ import type { SavedTemplateDetails } from '@@/Types/stripo';
|
|
|
15
15
|
* same source), so it is reliably present regardless of how the block was added.
|
|
16
16
|
*/
|
|
17
17
|
export declare function getHtmlReferencedUnsubscribePages(html: string): number[];
|
|
18
|
+
/**
|
|
19
|
+
* Hydrates the recommendation extension store from the persisted `esd-ext-config`
|
|
20
|
+
* JSON blob on each `.recommendation-block-v2` element in the raw editor HTML.
|
|
21
|
+
*
|
|
22
|
+
* Why this runs before `compileHtml`:
|
|
23
|
+
* The HTML compiler's `prepareRecommendationBlocks` rule asks
|
|
24
|
+
* `useRecommendation.buildCampaignUrl(id)` to assemble each block's API URL
|
|
25
|
+
* from `blockStates[id].recommendationConfigs`. That Pinia entry is normally
|
|
26
|
+
* populated by `RecommendationBlockControl._syncNodeConfigToStore`, which
|
|
27
|
+
* only fires when the user *selects* the block. Saving without ever
|
|
28
|
+
* selecting a migrated block would otherwise build the URL from empty/
|
|
29
|
+
* default state and the campaign URL would not be saved.
|
|
30
|
+
*
|
|
31
|
+
* Why we read raw HTML, not compiled HTML:
|
|
32
|
+
* Stripo's `compileEmail()` strips editor-only attributes (`esd-*`) from the
|
|
33
|
+
* compiled output, so `esd-ext-config` is gone by the time the compiler
|
|
34
|
+
* pipeline sees the HTML. The raw HTML from `getTemplateData()` preserves
|
|
35
|
+
* the attribute as the migrator wrote it.
|
|
36
|
+
*
|
|
37
|
+
* Reference data pre-load:
|
|
38
|
+
* When the template has recommendation blocks, this also pre-loads the
|
|
39
|
+
* shared reference data (languages / currency list) so the save-time
|
|
40
|
+
* required-fields validator can check each block's locale and currency
|
|
41
|
+
* against the partner's real options. Best-effort — on a fetch failure the
|
|
42
|
+
* validator degrades to its empty-list guard (`requiredFields.ts`) instead
|
|
43
|
+
* of blocking the save or stranding the Save button.
|
|
44
|
+
*/
|
|
45
|
+
declare function hydrateRecommendationStoreFromRawHtml(rawHtml: string): Promise<void>;
|
|
18
46
|
export declare const useTemplatePreparation: () => {
|
|
19
47
|
prepareTemplateDetails: () => Promise<Omit<SavedTemplateDetails, "metadata">>;
|
|
48
|
+
hydrateRecommendationStore: typeof hydrateRecommendationStoreFromRawHtml;
|
|
20
49
|
};
|
|
50
|
+
export {};
|