@useinsider/guido 3.17.0-beta.dc7f4d2 → 3.17.0-beta.e6704da

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 (75) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +12 -6
  3. package/dist/components/Guido.vue.js +5 -5
  4. package/dist/components/Guido.vue2.js +77 -77
  5. package/dist/components/organisms/AutoSaveController.vue2.js +8 -9
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +18 -0
  7. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +70 -0
  8. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue.js +20 -0
  9. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue2.js +55 -0
  10. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  11. package/dist/components/organisms/header/EditorActions.vue2.js +43 -74
  12. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  13. package/dist/components/organisms/header/LeftSlot.vue2.js +23 -46
  14. package/dist/components/wrappers/WpDrawer.vue.js +7 -9
  15. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  16. package/dist/composables/useCustomInterfaceAppearance.js +19 -16
  17. package/dist/composables/useHtmlCompiler.js +26 -37
  18. package/dist/composables/useHtmlValidator.js +0 -2
  19. package/dist/composables/useRecommendation.js +41 -34
  20. package/dist/composables/useSave.js +41 -84
  21. package/dist/composables/validators/useCouponBlockValidator.js +21 -24
  22. package/dist/composables/validators/useUnsubscribeBlockValidator.js +27 -34
  23. package/dist/config/migrator/recommendationMigrator.js +6 -4
  24. package/dist/enums/academy.js +2 -1
  25. package/dist/enums/date.js +4 -3
  26. package/dist/enums/extensions/recommendationBlock.js +25 -23
  27. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +13 -11
  28. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +96 -95
  29. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +156 -154
  30. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +141 -0
  31. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +223 -82
  32. package/dist/extensions/Blocks/common-control.js +22 -19
  33. package/dist/guido.css +1 -1
  34. package/dist/services/recommendationApi.js +78 -29
  35. package/dist/src/@types/config/schemas.d.ts +12 -0
  36. package/dist/src/components/Guido.vue.d.ts +1 -17
  37. package/dist/src/components/organisms/{validation/ValidationErrorDrawer.vue.d.ts → extensions/recommendation/AssignStrategyDrawer.vue.d.ts} +1 -0
  38. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBox.vue.d.ts +24 -0
  39. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -17
  40. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -17
  41. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -17
  42. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +3 -4
  43. package/dist/src/composables/useConfig.d.ts +2 -0
  44. package/dist/src/composables/useHtmlValidator.d.ts +0 -21
  45. package/dist/src/composables/useRecommendation.d.ts +1 -0
  46. package/dist/src/composables/useSave.d.ts +1 -6
  47. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +0 -3
  48. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +0 -10
  49. package/dist/src/enums/academy.d.ts +1 -0
  50. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  51. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +14 -3
  52. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +29 -0
  53. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +214 -15
  54. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  55. package/dist/src/extensions/Blocks/common-control.d.ts +4 -1
  56. package/dist/src/mock/api/recommendation-strategies.d.ts +43 -0
  57. package/dist/src/services/recommendationApi.d.ts +4 -1
  58. package/dist/src/stores/config.d.ts +18 -5
  59. package/dist/src/utils/templatePreparation.d.ts +0 -30
  60. package/dist/static/styles/components/recommendation-strategy.css.js +171 -0
  61. package/dist/stores/config.js +6 -13
  62. package/dist/utils/dateUtil.js +23 -10
  63. package/dist/utils/templatePreparation.js +50 -50
  64. package/package.json +2 -1
  65. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +0 -20
  66. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +0 -48
  67. package/dist/composables/useSaveValidation.js +0 -279
  68. package/dist/composables/useSaveValidationRollout.js +0 -9
  69. package/dist/enums/save-validation.js +0 -4
  70. package/dist/src/@types/save-validation.d.ts +0 -19
  71. package/dist/src/composables/useSaveValidation.d.ts +0 -6
  72. package/dist/src/composables/useSaveValidationRollout.d.ts +0 -3
  73. package/dist/src/enums/save-validation.d.ts +0 -27
  74. package/dist/src/stores/save-validation.d.ts +0 -22
  75. package/dist/stores/save-validation.js +0 -30
@@ -1,52 +1,101 @@
1
- import { useHttp as d } from "../composables/useHttp.js";
2
- import { QUERY_PARAMS as l, URLS as u } from "../enums/extensions/recommendationBlock.js";
3
- const R = (o) => o.reduce(
4
- (a, t, s) => (a[s] = {
5
- attributeName: t.attributeName,
6
- attributeJs: t.attributeJs,
7
- attributeType: t.attributeType,
8
- type: t.type,
9
- displayName: t.displayName,
10
- isFilterable: t.isFilterable
11
- }, a),
1
+ import { useHttp as p } from "../composables/useHttp.js";
2
+ import { URLS as m, RRS_ACTIVE_EMAIL_FILTER as l, QUERY_PARAMS as h } from "../enums/extensions/recommendationBlock.js";
3
+ const R = 100, y = (s) => s.reduce(
4
+ (i, a, d) => (i[d] = {
5
+ attributeName: a.attributeName,
6
+ attributeJs: a.attributeJs,
7
+ attributeType: a.attributeType,
8
+ type: a.type,
9
+ displayName: a.displayName,
10
+ isFilterable: a.isFilterable
11
+ }, i),
12
12
  {}
13
- ), f = () => {
14
- const { get: o } = d(), { get: a } = d({ headers: {} }), t = "6KcLM9TwheVB1mgK";
13
+ ), w = () => {
14
+ const { get: s, post: i } = p(), { get: a } = p({ headers: {} }), d = "6KcLM9TwheVB1mgK";
15
15
  return {
16
16
  fetchRecommendationCreateData: async () => {
17
17
  try {
18
- return (await o("/newsletter/recommendations/create-data")).data;
18
+ return (await s("/newsletter/recommendations/create-data")).data;
19
19
  } catch (e) {
20
20
  throw console.error("fetchUserModalState error:", e), e;
21
21
  }
22
22
  },
23
23
  fetchRecommendationFilters: async () => {
24
24
  try {
25
- const { data: e } = await o(
26
- u.PRODUCT_ATTRIBUTES_PATH
27
- ), n = Array.isArray(e) ? e : (e == null ? void 0 : e.data) ?? [];
28
- return R(n);
25
+ const { data: e } = await s(
26
+ m.PRODUCT_ATTRIBUTES_PATH
27
+ ), t = Array.isArray(e) ? e : (e == null ? void 0 : e.data) ?? [];
28
+ return y(t);
29
29
  } catch (e) {
30
30
  throw console.error("fetchRecommendationFilters error:", e), e;
31
31
  }
32
32
  },
33
- fetchRecommendationProducts: async (e, n) => {
34
- var i;
33
+ fetchRecommendationProducts: async (e, t) => {
34
+ var r;
35
35
  try {
36
- const r = new URLSearchParams(Object.entries(n));
37
- r.set(l.CLIENT_ID, t);
38
- const m = decodeURIComponent(r.toString());
39
- console.debug("🏁 Recommendation API Query:", m);
40
- const c = await a(
41
- `${u.RECOMMENDATION_API_URL}/v2/${e}?${m}`
36
+ const o = new URLSearchParams(Object.entries(t));
37
+ o.set(h.CLIENT_ID, d);
38
+ const c = decodeURIComponent(o.toString());
39
+ console.debug("🏁 Recommendation API Query:", c);
40
+ const n = await a(
41
+ `${m.RECOMMENDATION_API_URL}/v2/${e}?${c}`
42
42
  );
43
- return ((i = c == null ? void 0 : c.data) == null ? void 0 : i.data) ?? [];
43
+ return ((r = n == null ? void 0 : n.data) == null ? void 0 : r.data) ?? [];
44
+ } catch (o) {
45
+ throw console.error("fetchRecommendationProducts error:", o), o;
46
+ }
47
+ },
48
+ fetchRecommendationProductsByUrl: async (e) => {
49
+ var t;
50
+ try {
51
+ const r = new RegExp(`[?&]${h.CLIENT_ID}=`).test(e), o = e.includes("?") ? "&" : "?", c = r ? e : `${e}${o}${h.CLIENT_ID}=${d}`, n = await a(
52
+ c
53
+ );
54
+ return ((t = n == null ? void 0 : n.data) == null ? void 0 : t.data) ?? [];
44
55
  } catch (r) {
45
- throw console.error("fetchRecommendationProducts error:", r), r;
56
+ throw console.error("fetchRecommendationProductsByUrl error:", r), r;
57
+ }
58
+ },
59
+ fetchRecommendationStrategies: async () => {
60
+ try {
61
+ const e = /* @__PURE__ */ new Map();
62
+ let t = 1;
63
+ for (; ; ) {
64
+ const { data: r } = await s(
65
+ `${m.RECOMMENDATION_STRATEGIES_PATH}?filters=${encodeURIComponent(l)}&per_page=${R}&page=${t}`
66
+ ), o = Array.isArray(r) ? r : (r == null ? void 0 : r.data) ?? [], c = e.size;
67
+ if (o.forEach((n) => e.set(n.strategy_id, n)), o.length < R || e.size === c)
68
+ break;
69
+ t += 1;
70
+ }
71
+ return [...e.values()];
72
+ } catch (e) {
73
+ throw console.error("fetchRecommendationStrategies error:", e), e;
74
+ }
75
+ },
76
+ generateStrategyEndpoint: async (e) => {
77
+ try {
78
+ const { data: t } = await i(
79
+ `${m.RECOMMENDATION_STRATEGIES_PATH}/endpoint-generate`,
80
+ {
81
+ // Email designs have no campaign yet; backend only needs it
82
+ // for previewer flows.
83
+ campaignId: 0,
84
+ pageTypes: [],
85
+ isPreviewer: !1,
86
+ strategy: e,
87
+ userId: ""
88
+ }
89
+ );
90
+ if (!t || typeof t != "object" || !("full_path" in t || "data" in t))
91
+ throw new Error("Unexpected endpoint-generate response shape");
92
+ return "full_path" in t ? t : t.data;
93
+ } catch (t) {
94
+ throw console.error("generateStrategyEndpoint error:", t), t;
46
95
  }
47
96
  }
48
97
  };
49
98
  };
50
99
  export {
51
- f as useRecommendationApi
100
+ w as useRecommendationApi
52
101
  };
@@ -395,6 +395,12 @@ 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
+ /**
399
+ * Assign a Reusable Recommendation Strategy (RRS) to recommendation blocks instead of
400
+ * per-block algorithm settings. Hides Algorithm, Filters, Shuffle and Number of Products
401
+ * (they live on the strategy) and adds the Assign Strategy flow.
402
+ */
403
+ readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
398
404
  }, undefined>;
399
405
  /**
400
406
  * Default block types available in Stripo
@@ -794,6 +800,12 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
794
800
  readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
795
801
  /** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
796
802
  readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
803
+ /**
804
+ * Assign a Reusable Recommendation Strategy (RRS) to recommendation blocks instead of
805
+ * per-block algorithm settings. Hides Algorithm, Filters, Shuffle and Number of Products
806
+ * (they live on the strategy) and adds the Assign Strategy flow.
807
+ */
808
+ readonly reusableRecommendationStrategy: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
797
809
  }, undefined>, {}>;
798
810
  /** Block configuration */
799
811
  readonly blocks: v.OptionalSchema<v.ObjectSchema<{
@@ -15,23 +15,7 @@ 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<{
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, string>;
28
- configs: Record<string, string>;
29
- };
30
- unsubscribe: {
31
- status: boolean;
32
- config: number[];
33
- };
34
- } | undefined> | undefined;
18
+ saveSilent: () => Promise<Omit<SavedTemplateDetails, "metadata" | "silent"> | undefined> | undefined;
35
19
  setLoading: (value: boolean) => void;
36
20
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {
37
21
  "dynamic-content:open": (detail: {
@@ -1,2 +1,3 @@
1
+ import '@useinsider/design-system-organisms/style';
1
2
  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
3
  export default _default;
@@ -0,0 +1,24 @@
1
+ import type { RrsStrategy } from '@@/Types/recommendation';
2
+ /**
3
+ * Hover preview for a row of the Assign Strategy dropdown: the four facts that
4
+ * decide whether a strategy is the right one, without leaving the list.
5
+ *
6
+ * Rendered as the design system info box's `slot` rather than through its
7
+ * `descriptionText`, which is injected with innerHTML — every value here is
8
+ * partner data (strategy name, merchandising ids), so it goes through the
9
+ * template instead.
10
+ */
11
+ type __VLS_Props = {
12
+ strategy: RrsStrategy;
13
+ };
14
+ declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__VLS_Props>, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<__VLS_TypePropsToOption<__VLS_Props>>>, {}>;
15
+ export default _default;
16
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
17
+ type __VLS_TypePropsToOption<T> = {
18
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
19
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
20
+ } : {
21
+ type: import('vue').PropType<T[K]>;
22
+ required: true;
23
+ };
24
+ };
@@ -1,20 +1,4 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
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, string>;
12
- configs: Record<string, string>;
13
- };
14
- unsubscribe: {
15
- status: boolean;
16
- config: number[];
17
- };
18
- } | undefined> | undefined;
2
+ handleSave: (isSilent: boolean) => Promise<Omit<import("../../../@types/stripo.js").SavedTemplateDetails, "metadata" | "silent"> | undefined> | undefined;
19
3
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
20
4
  export default _default;
@@ -1,20 +1,4 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
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, string>;
12
- configs: Record<string, string>;
13
- };
14
- unsubscribe: {
15
- status: boolean;
16
- config: number[];
17
- };
18
- } | undefined> | undefined;
2
+ handleSave: (isSilent: boolean) => Promise<Omit<import("../../../@types/stripo.js").SavedTemplateDetails, "metadata" | "silent"> | undefined> | undefined;
19
3
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
20
4
  export default _default;
@@ -1,20 +1,4 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
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, string>;
12
- configs: Record<string, string>;
13
- };
14
- unsubscribe: {
15
- status: boolean;
16
- config: number[];
17
- };
18
- } | undefined> | undefined;
2
+ handleSave: (isSilent: boolean) => Promise<Omit<import("../../../@types/stripo.js").SavedTemplateDetails, "metadata" | "silent"> | undefined> | undefined;
19
3
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
20
4
  export default _default;
@@ -10,11 +10,11 @@ interface Props {
10
10
  status: boolean;
11
11
  titleText: string;
12
12
  }
13
- declare var __VLS_13: {}, __VLS_15: {};
13
+ declare var __VLS_12: {}, __VLS_14: {};
14
14
  type __VLS_Slots = {} & {
15
- headerBottomSlot?: (props: typeof __VLS_13) => any;
15
+ headerBottomSlot?: (props: typeof __VLS_12) => any;
16
16
  } & {
17
- default?: (props: typeof __VLS_15) => any;
17
+ default?: (props: typeof __VLS_14) => any;
18
18
  };
19
19
  declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption<Props>, {
20
20
  className: string;
@@ -26,7 +26,6 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_WithDefaults<
26
26
  cancelOrBackButtonEvent: () => void;
27
27
  onCloseEvent: () => void;
28
28
  primaryButtonEvent: () => void;
29
- secondaryButtonEvent: () => void;
30
29
  }, string, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<Props>, {
31
30
  className: string;
32
31
  size: string;
@@ -99,6 +99,7 @@ export declare const useConfig: () => {
99
99
  autosave: boolean;
100
100
  allowlistEnabled: boolean;
101
101
  linkTrackingIds: boolean;
102
+ reusableRecommendationStrategy: boolean;
102
103
  };
103
104
  blocks: {
104
105
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -233,6 +234,7 @@ export declare const useConfig: () => {
233
234
  autosave: boolean;
234
235
  allowlistEnabled: boolean;
235
236
  linkTrackingIds: boolean;
237
+ reusableRecommendationStrategy: boolean;
236
238
  } | null>;
237
239
  blocks: import("vue").ComputedRef<{
238
240
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1,12 +1,5 @@
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[];
10
3
  /**
11
4
  * Parses a `{{recoId_index_attributeName}}` variable (already stripped of the
12
5
  * braces/whitespace) into its recommendation id and attribute name. Returns
@@ -34,20 +27,6 @@ export declare function buildPartnerAttributeNameSet(filterList: FiltersResponse
34
27
  * before, so flagging it would false-positive on merge tags like `{{user.name}}`.
35
28
  */
36
29
  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[];
51
30
  export declare const useHtmlValidator: () => {
52
31
  validateHtml: (html: string, customFields: DynamicContent[], isOnSaveValidation?: boolean) => Promise<boolean>;
53
32
  };
@@ -18,6 +18,7 @@ export declare const useRecommendation: () => {
18
18
  getRecommendationCampaignData: (id: string) => CampaignData;
19
19
  buildCampaignUrl: (campaignId: string, overrides?: {
20
20
  strategy: string;
21
+ strategyId: string;
21
22
  language: string;
22
23
  currencyCode: string;
23
24
  size: string;
@@ -1,9 +1,4 @@
1
1
  import type { SavedTemplateDetails } from '@@/Types/stripo';
2
- type SaveDetails = Omit<SavedTemplateDetails, 'metadata' | 'silent'>;
3
- type SaveResult = SaveDetails | undefined;
4
2
  export declare const useSave: () => {
5
- save: (suppressEmit?: boolean, silent?: boolean) => Promise<SaveResult>;
6
- saveWithErrors: () => Promise<SaveResult>;
7
- saveRawOnly: () => Promise<SaveResult>;
3
+ save: (suppressEmit?: boolean, silent?: boolean) => Promise<Omit<SavedTemplateDetails, "metadata" | "silent"> | undefined>;
8
4
  };
9
- export {};
@@ -1,6 +1,3 @@
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[];
4
1
  export declare const useCouponBlockValidator: () => {
5
2
  validateCouponBlockTags: (html: string) => boolean;
6
3
  };
@@ -1,14 +1,4 @@
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;
10
1
  export declare const useUnsubscribeBlockValidator: () => {
11
2
  validateUnsubscribeBlockUniqueness: (html: string) => boolean;
12
3
  validateUnsubscribeBlockHasTemplate: (html: string) => boolean;
13
4
  };
14
- export {};
@@ -8,5 +8,6 @@ export declare const ACADEMY_LINKS: {
8
8
  readonly EMAIL_EDITOR: "https://academy.insiderone.com/docs/email-drag-and-drop-editor";
9
9
  readonly GLOBAL_UNSUBSCRIBE: "https://academy.insiderone.com/docs/global-unsubscribe-preference-center";
10
10
  readonly AMP_FOR_EMAILS: "https://academy.insiderone.com/docs/amp-for-emails";
11
+ readonly EMAIL_RECOMMENDATION_BLOCK: "https://academy.insiderone.com/docs/new-editor-email-recommendation-block";
11
12
  };
12
13
  export type AcademyLinkKey = keyof typeof ACADEMY_LINKS;
@@ -3,7 +3,9 @@ import type { RecommendationFeedItem } from '@@/Types/recommendation';
3
3
  export declare const URLS: {
4
4
  RECOMMENDATION_API_URL: string;
5
5
  PRODUCT_ATTRIBUTES_PATH: string;
6
+ RECOMMENDATION_STRATEGIES_PATH: string;
6
7
  };
8
+ export declare const RRS_ACTIVE_EMAIL_FILTER = "[sr_strategies.status][=][active]*[sr_strategies.suitable_channels][~][EMAIL]";
7
9
  export declare const QUERY_PARAMS: {
8
10
  CLIENT_ID: string;
9
11
  };
@@ -9,8 +9,9 @@ import { PricePlacementControl, PRICE_PLACEMENT_CONTROL_ID } from './pricePlacem
9
9
  import { ProductCountControl, PRODUCT_COUNT_CONTROL_ID } from './productCount';
10
10
  import { ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID } from './productLayout';
11
11
  import { ShuffleControl, SHUFFLE_CONTROL_ID } from './shuffle';
12
+ import { StrategyControl, STRATEGY_CONTROL_ID } from './strategy';
12
13
  export declare const CONTROL_BLOCK_ID = "ui-elements-recommendation-block";
13
- export { AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductCountControl, PRODUCT_COUNT_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, LayoutOrientationControl, LAYOUT_ORIENTATION_CONTROL_ID, PricePlacementControl, PRICE_PLACEMENT_CONTROL_ID, };
14
+ export { AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductCountControl, PRODUCT_COUNT_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, LayoutOrientationControl, LAYOUT_ORIENTATION_CONTROL_ID, PricePlacementControl, PRICE_PLACEMENT_CONTROL_ID, StrategyControl, STRATEGY_CONTROL_ID, };
14
15
  export * from './utils';
15
16
  /**
16
17
  * Main recommendation block control that composes all sub-controls
@@ -29,7 +30,10 @@ export declare class RecommendationBlockControl extends CommonControl {
29
30
  private shuffleControl;
30
31
  private pricePlacementControl;
31
32
  private layoutOrientationControl;
33
+ private strategyControl;
32
34
  getId(): string;
35
+ /** Every sub-control the coordinator owns, in panel order. */
36
+ private get _subControls();
33
37
  getTemplate(): string;
34
38
  onRender(): Promise<void>;
35
39
  onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
@@ -107,8 +111,15 @@ export declare class RecommendationBlockControl extends CommonControl {
107
111
  */
108
112
  private _listenStateUpdates;
109
113
  /**
110
- * Persists the current filter state from Pinia store to the Stripo node config.
111
- * This ensures filters survive template save/reload cycles.
114
+ * Persists the current filter + strategy state from Pinia store to the
115
+ * Stripo node config. This ensures they survive template save/reload cycles
116
+ * (strategyId must be in esd-ext-config before any save — SD-149105).
117
+ *
118
+ * `size` rides along because assigning a strategy overwrites it in the
119
+ * store only. Without this write, `_syncNodeConfigToStore` re-seeds the
120
+ * store from the stale node config on the next panel open, and a block
121
+ * whose saved HTML has the strategy's slot count gets rebuilt against the
122
+ * old one — resetting migrator-applied per-element styles.
112
123
  */
113
124
  private _persistFiltersToNodeConfig;
114
125
  }
@@ -0,0 +1,29 @@
1
+ import { CommonControl } from '../../../common-control';
2
+ export declare const STRATEGY_CONTROL_ID = "recommendation-strategy-control";
3
+ export declare class StrategyControl extends CommonControl {
4
+ private store;
5
+ private storeUnsubscription;
6
+ private listeners;
7
+ getId(): string;
8
+ getTemplate(): string;
9
+ /**
10
+ * Change / Remove are icon+text actions in the design, not buttons — and
11
+ * `ue-button` cannot pair a caption with an arbitrary icon, so these are
12
+ * plain elements. Click wiring is by id, which works on any element.
13
+ *
14
+ * The icon is a CSS mask, not inline SVG: Stripo re-creates the control's
15
+ * markup through Angular, which rebuilds `<svg>` outside the SVG namespace
16
+ * (the view-box attribute comes back lowercased) and it never paints.
17
+ */
18
+ private _strategyAction;
19
+ onRender(): void;
20
+ onDestroy(): void;
21
+ private _teardown;
22
+ private _setupButtonListeners;
23
+ /**
24
+ * Toggles empty vs assigned state from the store's current block config.
25
+ * Display name comes from the listing; falls back to the raw id while the
26
+ * listing loads (or when the strategy is no longer listed).
27
+ */
28
+ private _renderState;
29
+ }