@useinsider/guido 3.12.0-beta.98baf2f → 3.12.0-beta.b73c720

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 (62) hide show
  1. package/dist/@types/config/schemas.js +13 -8
  2. package/dist/components/Guido.vue.js +6 -6
  3. package/dist/components/Guido.vue2.js +50 -49
  4. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.js +36 -0
  5. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue2.js +244 -0
  6. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.js +18 -0
  7. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue2.js +42 -0
  8. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.js +20 -0
  9. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue2.js +18 -0
  10. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.js +20 -0
  11. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue2.js +60 -0
  12. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.js +19 -0
  13. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue2.js +19 -0
  14. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.js +22 -0
  15. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue2.js +29 -0
  16. package/dist/composables/useRecommendation.js +82 -51
  17. package/dist/composables/useStrategyFilters.js +16 -0
  18. package/dist/composables/useStripoEventHandler.js +9 -12
  19. package/dist/config/migrator/recommendationMigrator.js +7 -4
  20. package/dist/enums/date.js +4 -3
  21. package/dist/enums/extensions/filteringV2.js +8 -0
  22. package/dist/enums/extensions/recommendationBlock.js +28 -22
  23. package/dist/enums/extensions/strategyDetail.js +148 -0
  24. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +12 -11
  25. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +123 -109
  26. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +146 -0
  27. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +1 -1
  28. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +256 -102
  29. package/dist/extensions/Blocks/Recommendation/utils/strategyHumanizer.js +97 -0
  30. package/dist/extensions/Blocks/Recommendation/utils/strategySummary.js +10 -0
  31. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +24 -12
  32. package/dist/guido.css +1 -1
  33. package/dist/services/recommendationApi.js +65 -27
  34. package/dist/services/stripoApi.js +27 -28
  35. package/dist/src/@types/config/schemas.d.ts +10 -0
  36. package/dist/src/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.d.ts +2 -0
  37. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.d.ts +43 -0
  38. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.d.ts +29 -0
  39. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.d.ts +40 -0
  40. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.d.ts +38 -0
  41. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.d.ts +51 -0
  42. package/dist/src/composables/useConfig.d.ts +2 -0
  43. package/dist/src/composables/useRecommendation.d.ts +2 -0
  44. package/dist/src/composables/useStrategyFilters.d.ts +24 -0
  45. package/dist/src/enums/extensions/filteringV2.d.ts +72 -0
  46. package/dist/src/enums/extensions/recommendationBlock.d.ts +10 -0
  47. package/dist/src/enums/extensions/strategyDetail.d.ts +90 -0
  48. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +3 -1
  49. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +61 -0
  50. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +606 -1
  51. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +7 -0
  52. package/dist/src/extensions/Blocks/Recommendation/utils/strategyHumanizer.d.ts +50 -0
  53. package/dist/src/extensions/Blocks/Recommendation/utils/strategySummary.d.ts +34 -0
  54. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +3 -1
  55. package/dist/src/services/recommendationApi.d.ts +7 -1
  56. package/dist/src/stores/config.d.ts +18 -0
  57. package/dist/src/utils/genericUtil.d.ts +9 -0
  58. package/dist/static/styles/base.css.js +15 -0
  59. package/dist/utils/dateUtil.js +23 -10
  60. package/dist/utils/genericUtil.js +10 -1
  61. package/dist/utils/templatePreparation.js +62 -58
  62. package/package.json +1 -1
@@ -54,6 +54,13 @@ export interface RecommendationNodeConfig {
54
54
  * @example 'mostPopular', 'complementaryItems', 'manualMerchandising'
55
55
  */
56
56
  strategy: string;
57
+ /**
58
+ * Reusable Recommendation Strategy (RRS) id from Smart Recommender.
59
+ * Empty string means no strategy is assigned.
60
+ * Only used when the `reusableRecommendationStrategy` feature is enabled;
61
+ * when set, it supersedes `strategy` for building the recommendation URL.
62
+ */
63
+ strategyId: string;
57
64
  /**
58
65
  * Product IDs for manual merchandising strategy
59
66
  * Only used when strategy === 'manualMerchandising'
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Renders one RRS filter condition as readable text for the detail drawer.
3
+ *
4
+ * Ported from design-system-organisms
5
+ * `src/organisms/OrgStrategyDetailModal/helpers/humanizer.helper.ts` (branch `develop`).
6
+ *
7
+ * Two deliberate deviations from the source:
8
+ *
9
+ * 1. `trans` is injected rather than read from a module-level `translations` object,
10
+ * so this stays pure and testable and picks up Guido's editor-config translations.
11
+ * 2. The source wraps every date in `getUnixTimestampFromDate(...)` before formatting.
12
+ * Guido's `formatShortDate` already accepts a raw date string via
13
+ * `convertToDateObject`, so that conversion hop is dropped rather than reimplemented.
14
+ *
15
+ * This humanizes the same JSON as `strategySummary.ts` but for a different surface —
16
+ * see the note in `@@/Enums/extensions/strategyDetail` on why the operator tables differ.
17
+ */
18
+ import type { RrsDetailCondition } from '@@/Types/recommendation';
19
+ /** Translation lookup, matching `useTranslations()`. */
20
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
21
+ export interface TimeUnitOption {
22
+ text: string;
23
+ value: string;
24
+ }
25
+ export declare const capitalizeFirstLetter: (text: string) => string;
26
+ export declare const getUserEventLabel: (userEvent: string, trans: TranslateFn) => string;
27
+ /** Describes a user-attribute or user-event condition, e.g. "Users' last viewed 3 items". */
28
+ export declare const getUserContextValue: (condition: RrsDetailCondition, trans: TranslateFn) => string;
29
+ /** Renders a `7d`-style duration as " in last 7 days", or '' when absent or malformed. */
30
+ export declare const getDurationSuffix: (duration: string | undefined, timeUnitOptions: TimeUnitOption[], trans: TranslateFn) => string;
31
+ export interface SuitableValueParams {
32
+ condition: RrsDetailCondition;
33
+ selectedOperator: string;
34
+ filterValue: string;
35
+ timeUnit: string;
36
+ inBetweenValues: string[];
37
+ timeUnitOptions: TimeUnitOption[];
38
+ isDateAttribute: () => boolean;
39
+ isMultiSelect: () => boolean;
40
+ isInOrNotInOperator: () => boolean;
41
+ trans: TranslateFn;
42
+ }
43
+ export declare const bracketJoin: (items: string[], and: string) => string;
44
+ /**
45
+ * A multi-select condition stores its values either as a JSON array or, on older
46
+ * strategies, as a bare comma-separated string. Both must humanize the same way.
47
+ */
48
+ export declare const parseMultiSelectValue: (value: string) => string[];
49
+ export declare function getSuitableValue(params: SuitableValueParams): string;
50
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Human-readable summary of a Reusable Recommendation Strategy (RRS).
3
+ *
4
+ * Ported from discovery-strategies-fe
5
+ * `src/components/atoms/GptAppBuilder/StrategyInfoBoxContent.composition.vue`.
6
+ *
7
+ * The source is a Vue SFC whose template is fourteen lines of plain `<div>`s over
8
+ * a `{ title, value }[]` computed. Only the computed carries meaning, so the port
9
+ * is a pure function instead: the panel renders it as an HTML string and the
10
+ * detail drawer renders it as components, both from this one implementation.
11
+ *
12
+ * Produces four rows — Algorithms and Method, Exclusions, Filters, Manual
13
+ * Merchandising — each truncated with "and N more." and falling back to
14
+ * `recommendation-strategy.none` when empty.
15
+ *
16
+ * `trans` is injected rather than pulled from `useTranslations()` so this stays a
17
+ * pure function: no composable lifetime to reason about, and tests need no config.
18
+ */
19
+ import type { RrsStrategy, RrsSummaryRow } from '@@/Types/recommendation';
20
+ /** Translation lookup, matching `useTranslations().trans`. */
21
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
22
+ /**
23
+ * `TOP_SELLERS` -> `recommendation-algorithm-names.top-sellers`.
24
+ * A pure string transform — Smart Recommender has no algorithm lookup table.
25
+ */
26
+ export declare const getAlgorithmTranslationKey: (algorithm: string) => string;
27
+ export declare const getMethodTranslationKey: (algorithm: string) => string;
28
+ /**
29
+ * Builds the four-row summary shown beside the strategy dropdown.
30
+ * @param strategy - A strategy row as returned by the RRS listing endpoint.
31
+ * @param trans - Translation lookup, e.g. `useTranslations().trans`.
32
+ */
33
+ export declare const buildStrategySummary: (strategy: RrsStrategy, trans: TranslateFn) => RrsSummaryRow[];
34
+ export {};
@@ -7,13 +7,15 @@ import type { Currency, Languages } from '@@/Types/recommendation';
7
7
  export interface ExtensionStoreSlice {
8
8
  languages: Languages;
9
9
  currencyList: Currency[];
10
+ /** Whether the `reusableRecommendationStrategy` feature is enabled. */
11
+ isRrsEnabled: boolean;
10
12
  }
11
13
  export interface RequiredField {
12
14
  key: string;
13
15
  canBeEmpty?: boolean;
14
16
  getValue: (config: PerBlockConfigs) => string;
15
17
  getAvailableOptions?: (store: ExtensionStoreSlice) => string[];
16
- condition?: (config: PerBlockConfigs) => boolean;
18
+ condition?: (config: PerBlockConfigs, store: ExtensionStoreSlice) => boolean;
17
19
  }
18
20
  export declare const REQUIRED_RECOMMENDATION_FIELDS: RequiredField[];
19
21
  export declare const RecommendationRequiredFieldsKey = "newsletter.recommendation-fill-required-fields";
@@ -1,6 +1,12 @@
1
- import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams } from '@@/Types/recommendation';
1
+ import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams, RrsCampaign, RrsStrategy, RrsStrategyUrl } from '@@/Types/recommendation';
2
+ /** Translation lookup, matching `useTranslations()`. */
3
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
2
4
  export declare const useRecommendationApi: () => {
3
5
  fetchRecommendationCreateData: () => Promise<InitialResponse>;
4
6
  fetchRecommendationFilters: () => Promise<FiltersResponse>;
5
7
  fetchRecommendationProducts: (strategy: string, params: RecommendationProductsParams) => Promise<RecommendationProduct[]>;
8
+ fetchRecommendationStrategies: () => Promise<RrsStrategy[]>;
9
+ fetchStrategyCampaigns: (strategyId: string, trans: TranslateFn) => Promise<RrsCampaign[]>;
10
+ fetchStrategyRecommendationUrl: (strategyId: string) => Promise<RrsStrategyUrl | null>;
6
11
  };
12
+ export {};
@@ -101,6 +101,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
101
101
  modulesDisabled: boolean;
102
102
  liquidSyntax: boolean;
103
103
  autosave: boolean;
104
+ reusableRecommendationStrategy: boolean;
104
105
  };
105
106
  blocks: {
106
107
  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")[];
@@ -238,6 +239,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
238
239
  modulesDisabled: boolean;
239
240
  liquidSyntax: boolean;
240
241
  autosave: boolean;
242
+ reusableRecommendationStrategy: boolean;
241
243
  };
242
244
  blocks: {
243
245
  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")[];
@@ -375,6 +377,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
375
377
  modulesDisabled: boolean;
376
378
  liquidSyntax: boolean;
377
379
  autosave: boolean;
380
+ reusableRecommendationStrategy: boolean;
378
381
  };
379
382
  blocks: {
380
383
  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")[];
@@ -512,6 +515,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
512
515
  modulesDisabled: boolean;
513
516
  liquidSyntax: boolean;
514
517
  autosave: boolean;
518
+ reusableRecommendationStrategy: boolean;
515
519
  };
516
520
  blocks: {
517
521
  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")[];
@@ -649,6 +653,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
649
653
  modulesDisabled: boolean;
650
654
  liquidSyntax: boolean;
651
655
  autosave: boolean;
656
+ reusableRecommendationStrategy: boolean;
652
657
  };
653
658
  blocks: {
654
659
  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")[];
@@ -786,6 +791,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
786
791
  modulesDisabled: boolean;
787
792
  liquidSyntax: boolean;
788
793
  autosave: boolean;
794
+ reusableRecommendationStrategy: boolean;
789
795
  };
790
796
  blocks: {
791
797
  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")[];
@@ -923,6 +929,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
923
929
  modulesDisabled: boolean;
924
930
  liquidSyntax: boolean;
925
931
  autosave: boolean;
932
+ reusableRecommendationStrategy: boolean;
926
933
  };
927
934
  blocks: {
928
935
  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")[];
@@ -1060,6 +1067,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1060
1067
  modulesDisabled: boolean;
1061
1068
  liquidSyntax: boolean;
1062
1069
  autosave: boolean;
1070
+ reusableRecommendationStrategy: boolean;
1063
1071
  };
1064
1072
  blocks: {
1065
1073
  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")[];
@@ -1197,6 +1205,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1197
1205
  modulesDisabled: boolean;
1198
1206
  liquidSyntax: boolean;
1199
1207
  autosave: boolean;
1208
+ reusableRecommendationStrategy: boolean;
1200
1209
  };
1201
1210
  blocks: {
1202
1211
  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")[];
@@ -1334,6 +1343,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1334
1343
  modulesDisabled: boolean;
1335
1344
  liquidSyntax: boolean;
1336
1345
  autosave: boolean;
1346
+ reusableRecommendationStrategy: boolean;
1337
1347
  };
1338
1348
  blocks: {
1339
1349
  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")[];
@@ -1471,6 +1481,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1471
1481
  modulesDisabled: boolean;
1472
1482
  liquidSyntax: boolean;
1473
1483
  autosave: boolean;
1484
+ reusableRecommendationStrategy: boolean;
1474
1485
  };
1475
1486
  blocks: {
1476
1487
  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")[];
@@ -1608,6 +1619,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1608
1619
  modulesDisabled: boolean;
1609
1620
  liquidSyntax: boolean;
1610
1621
  autosave: boolean;
1622
+ reusableRecommendationStrategy: boolean;
1611
1623
  };
1612
1624
  blocks: {
1613
1625
  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")[];
@@ -1745,6 +1757,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1745
1757
  modulesDisabled: boolean;
1746
1758
  liquidSyntax: boolean;
1747
1759
  autosave: boolean;
1760
+ reusableRecommendationStrategy: boolean;
1748
1761
  };
1749
1762
  blocks: {
1750
1763
  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")[];
@@ -1882,6 +1895,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1882
1895
  modulesDisabled: boolean;
1883
1896
  liquidSyntax: boolean;
1884
1897
  autosave: boolean;
1898
+ reusableRecommendationStrategy: boolean;
1885
1899
  };
1886
1900
  blocks: {
1887
1901
  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")[];
@@ -2019,6 +2033,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2019
2033
  modulesDisabled: boolean;
2020
2034
  liquidSyntax: boolean;
2021
2035
  autosave: boolean;
2036
+ reusableRecommendationStrategy: boolean;
2022
2037
  };
2023
2038
  blocks: {
2024
2039
  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")[];
@@ -2156,6 +2171,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2156
2171
  modulesDisabled: boolean;
2157
2172
  liquidSyntax: boolean;
2158
2173
  autosave: boolean;
2174
+ reusableRecommendationStrategy: boolean;
2159
2175
  };
2160
2176
  blocks: {
2161
2177
  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")[];
@@ -2293,6 +2309,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2293
2309
  modulesDisabled: boolean;
2294
2310
  liquidSyntax: boolean;
2295
2311
  autosave: boolean;
2312
+ reusableRecommendationStrategy: boolean;
2296
2313
  };
2297
2314
  blocks: {
2298
2315
  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")[];
@@ -2430,6 +2447,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2430
2447
  modulesDisabled: boolean;
2431
2448
  liquidSyntax: boolean;
2432
2449
  autosave: boolean;
2450
+ reusableRecommendationStrategy: boolean;
2433
2451
  };
2434
2452
  blocks: {
2435
2453
  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")[];
@@ -25,3 +25,12 @@ export declare const getCsrfToken: () => string;
25
25
  export declare const getUrlParams: () => URLSearchParams;
26
26
  export declare const getUrlParam: (paramName: string, defaultValue?: string) => string;
27
27
  export declare const replaceString: (string?: string, values?: Record<string, string | number>, isRemoveEmptyValues?: boolean) => string;
28
+ /**
29
+ * Parses a JSON-string field, yielding `fallback` on absent or malformed input.
30
+ *
31
+ * Smart Recommender returns several strategy fields (`filters`, `base_product`, ...)
32
+ * as embedded JSON strings, and every surface that reads them must degrade to a
33
+ * sensible empty value rather than break. Shared so a hardening change (rejecting
34
+ * non-object payloads, logging a malformed blob) lands on all of them at once.
35
+ */
36
+ export declare const parseJsonField: <T>(raw: string | undefined, fallback: T) => T;
@@ -10,6 +10,21 @@ ue-stripe-thumb,
10
10
  height: 100% !important;
11
11
  min-height: 0 !important;
12
12
  }
13
+
14
+ /*
15
+ * The Recommendation Strategy select and its details-drawer icon button share a
16
+ * single cell of Stripo's two-column panel grid, so they need their own row box.
17
+ */
18
+ .strategy-control__select-row {
19
+ display: flex;
20
+ align-items: center;
21
+ gap: 8px;
22
+ }
23
+
24
+ .strategy-control__select-row ue-select {
25
+ flex: 1;
26
+ min-width: 0;
27
+ }
13
28
  `;
14
29
  export {
15
30
  n as default
@@ -1,10 +1,19 @@
1
- import { DEFAULT_LOCALE as r, DEFAULT as o } from "../enums/date.js";
2
- const c = () => {
1
+ import { DEFAULT_LOCALE as r, DEFAULT as a, DEFAULT_TZ as c } from "../enums/date.js";
2
+ const m = (t) => t ? t instanceof Date ? t : typeof t == "string" ? t.length === 10 && /^\d{10}$/.test(t) ? new Date(Number(t) * 1e3) : new Date(t) : typeof t == "number" ? t.toString().length === 10 ? new Date(t * 1e3) : new Date(t) : /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date(), s = () => {
3
3
  const t = window.l10n;
4
- return (t == null ? void 0 : t.locale) === o ? r : (t == null ? void 0 : t.locale) || r;
5
- };
6
- new Intl.DateTimeFormat().resolvedOptions().timeZone;
7
- const i = (t) => {
4
+ return (t == null ? void 0 : t.locale) === a ? r : (t == null ? void 0 : t.locale) || r;
5
+ }, u = new Intl.DateTimeFormat().resolvedOptions().timeZone, f = () => {
6
+ const t = window.l10n;
7
+ return (t == null ? void 0 : t.timezone) || u || c;
8
+ }, D = (t, e = s(), n = f()) => {
9
+ const o = {
10
+ year: "numeric",
11
+ month: "2-digit",
12
+ day: "2-digit",
13
+ timeZone: n
14
+ }, i = m(t);
15
+ return new Intl.DateTimeFormat(e, o).format(i);
16
+ }, T = (t) => {
8
17
  if (!t)
9
18
  return "";
10
19
  const e = new Date(t);
@@ -16,9 +25,13 @@ const i = (t) => {
16
25
  second: "2-digit",
17
26
  hour12: !1
18
27
  }).format(e);
19
- }, m = (t) => t ? Math.floor(Date.now() / 1e3) >= t : !1;
28
+ }, h = (t) => t ? Math.floor(Date.now() / 1e3) >= t : !1;
20
29
  export {
21
- i as formatPatchDate,
22
- m as isAfterDate,
23
- c as locale
30
+ m as convertToDateObject,
31
+ T as formatPatchDate,
32
+ D as formatShortDate,
33
+ h as isAfterDate,
34
+ u as localTz,
35
+ s as locale,
36
+ f as timezone
24
37
  };
@@ -58,11 +58,20 @@ const o = (l) => {
58
58
  }, i = () => {
59
59
  var l;
60
60
  return ((l = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : l.getAttribute("content")) ?? "";
61
+ }, k = (l, s) => {
62
+ if (!l)
63
+ return s;
64
+ try {
65
+ return JSON.parse(l);
66
+ } catch {
67
+ return s;
68
+ }
61
69
  };
62
70
  export {
63
71
  v as buildMergeTagEntries,
64
72
  f as dynamicContentToMergeTags,
65
73
  b as flattenDynamicContentList,
66
74
  i as getCsrfToken,
67
- o as mergeTagToDynamicContent
75
+ o as mergeTagToDynamicContent,
76
+ k as parseJsonField
68
77
  };
@@ -1,59 +1,60 @@
1
- import { useActionsApi as P } from "../composables/useActionsApi.js";
2
- import { useHtmlCompiler as w } from "../composables/useHtmlCompiler.js";
3
- import { DEFAULT_CURRENCY as l, DEFAULT_NODE_CONFIG as i } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
4
- import { useRecommendationExtensionStore as b } from "../extensions/Blocks/Recommendation/store/recommendation.js";
5
- import { mapLegacyStrategy as E } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
6
- import { DATA_ATTRIBUTES as h } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
7
- import { parsePageList as H } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
8
- import { useDynamicContentStore as U } from "../stores/dynamic-content.js";
9
- import { useUnsubscribeStore as F } from "../stores/unsubscribe.js";
10
- function L(s) {
11
- const o = new DOMParser().parseFromString(s, "text/html").querySelectorAll(`[${h.PAGE_LIST}]`), t = [];
12
- return o.forEach((a) => {
13
- const n = a.getAttribute(h.PAGE_LIST);
14
- n && t.push(...H(n));
15
- }), [...new Set(t)];
1
+ import { useActionsApi as D } from "../composables/useActionsApi.js";
2
+ import { useHtmlCompiler as P } from "../composables/useHtmlCompiler.js";
3
+ import { DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as n } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
4
+ import { useRecommendationExtensionStore as A } from "../extensions/Blocks/Recommendation/store/recommendation.js";
5
+ import { mapLegacyStrategy as I } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
6
+ import { DATA_ATTRIBUTES as b } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
7
+ import { parsePageList as E } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
8
+ import { useDynamicContentStore as H } from "../stores/dynamic-content.js";
9
+ import { useUnsubscribeStore as U } from "../stores/unsubscribe.js";
10
+ function F(m) {
11
+ const o = new DOMParser().parseFromString(m, "text/html").querySelectorAll(`[${b.PAGE_LIST}]`), r = [];
12
+ return o.forEach((t) => {
13
+ const c = t.getAttribute(b.PAGE_LIST);
14
+ c && r.push(...E(c));
15
+ }), [...new Set(r)];
16
16
  }
17
- async function R(s) {
18
- const m = new DOMParser().parseFromString(s, "text/html").querySelectorAll(".recommendation-block-v2");
19
- if (m.length === 0)
17
+ async function L(m) {
18
+ const l = new DOMParser().parseFromString(m, "text/html").querySelectorAll(".recommendation-block-v2");
19
+ if (l.length === 0)
20
20
  return;
21
- const o = b();
22
- m.forEach((t) => {
23
- var c, p, g, f, r;
24
- const a = t.getAttribute("recommendation-id"), n = a ? Number(a) : NaN;
25
- if (!Number.isFinite(n))
21
+ const o = A(), r = /* @__PURE__ */ new Set();
22
+ l.forEach((t) => {
23
+ var g, f, y, a, S;
24
+ const c = t.getAttribute("recommendation-id"), p = c ? Number(c) : NaN;
25
+ if (!Number.isFinite(p))
26
26
  return;
27
- const d = t.getAttribute("esd-ext-config");
28
- if (!d)
27
+ const s = t.getAttribute("esd-ext-config");
28
+ if (!s)
29
29
  return;
30
30
  let e;
31
31
  try {
32
- e = JSON.parse(d);
32
+ e = JSON.parse(s);
33
33
  } catch {
34
34
  return;
35
35
  }
36
36
  if (!e || typeof e != "object" || Array.isArray(e))
37
37
  return;
38
- const y = {
38
+ const i = {
39
39
  // Normalize legacy strategy keys (e.g. `mostViewed` → `mostPopular`) so
40
40
  // already-saved blocks that never re-run the migrator don't emit a
41
41
  // strategy-less campaign URL. `||` also catches '' / undefined (SD-144882).
42
- strategy: E(e.strategy) || i.strategy,
43
- language: e.language ?? i.language,
44
- size: e.size ?? i.size,
42
+ strategy: I(e.strategy) || n.strategy,
43
+ strategyId: e.strategyId ?? n.strategyId,
44
+ language: e.language ?? n.language,
45
+ size: e.size ?? n.size,
45
46
  // Spread the default arrays so each block gets a fresh reference
46
47
  // instead of sharing the singleton in DEFAULT_NODE_CONFIG.
47
- productIds: e.productIds ?? [...i.productIds],
48
- filters: e.filters ?? [...i.filters],
49
- shuffleProducts: e.shuffleProducts ?? i.shuffleProducts,
50
- currencyCode: ((c = e.currency) == null ? void 0 : c.code) ?? l.code,
51
- currencyAlignment: ((p = e.currency) == null ? void 0 : p.alignment) ?? l.alignment,
52
- currencyDecimalCount: ((g = e.currency) == null ? void 0 : g.decimalCount) ?? l.decimalCount,
53
- currencyDecimalSeparator: ((f = e.currency) == null ? void 0 : f.decimalSeparator) ?? l.decimalSeparator,
54
- currencyThousandSeparator: ((r = e.currency) == null ? void 0 : r.thousandSeparator) ?? l.thousandSeparator
48
+ productIds: e.productIds ?? [...n.productIds],
49
+ filters: e.filters ?? [...n.filters],
50
+ shuffleProducts: e.shuffleProducts ?? n.shuffleProducts,
51
+ currencyCode: ((g = e.currency) == null ? void 0 : g.code) ?? d.code,
52
+ currencyAlignment: ((f = e.currency) == null ? void 0 : f.alignment) ?? d.alignment,
53
+ currencyDecimalCount: ((y = e.currency) == null ? void 0 : y.decimalCount) ?? d.decimalCount,
54
+ currencyDecimalSeparator: ((a = e.currency) == null ? void 0 : a.decimalSeparator) ?? d.decimalSeparator,
55
+ currencyThousandSeparator: ((S = e.currency) == null ? void 0 : S.thousandSeparator) ?? d.thousandSeparator
55
56
  };
56
- o.seedBlockUrlConfig(n, y);
57
+ o.seedBlockUrlConfig(p, i), r.add(i.strategyId);
57
58
  });
58
59
  try {
59
60
  await o.fetchRecommendationCreateData();
@@ -63,44 +64,47 @@ async function R(s) {
63
64
  t
64
65
  );
65
66
  }
67
+ r.delete(""), await Promise.all(
68
+ [...r].map((t) => o.fetchStrategyUrlTemplate(t))
69
+ );
66
70
  }
67
71
  const G = () => {
68
- const s = U(), u = F(), { getCompiledEmail: m, getTemplateData: o } = P(), { compileHtml: t, compileAmpHtml: a } = w();
72
+ const m = H(), u = U(), { getCompiledEmail: l, getTemplateData: o } = D(), { compileHtml: r, compileAmpHtml: t } = P();
69
73
  return {
70
74
  prepareTemplateDetails: async () => {
71
- const { html: d, ampHtml: e = "", ampErrors: y = [] } = await m({
75
+ const { html: p, ampHtml: s = "", ampErrors: e = [] } = await l({
72
76
  minimize: !0,
73
77
  resetDataSavedFlag: !1
74
- }), { html: c, css: p, syncModulesIds: g = [] } = await o();
75
- u.selectedUnsubscribePages.length && await u.fetchTemplates(), await R(c);
76
- const { compiledHtml: f, stats: r, appliedRules: A } = t(d), C = e && a(e).compiledHtml, T = s.getSelectedDynamicContentList, D = b(), S = L(c);
78
+ }), { html: i, css: g, syncModulesIds: f = [] } = await o();
79
+ u.selectedUnsubscribePages.length && await u.fetchTemplates(), await L(i);
80
+ const { compiledHtml: y, stats: a, appliedRules: S } = r(p), C = s && t(s).compiledHtml, T = m.getSelectedDynamicContentList, w = A(), h = F(i);
77
81
  return console.debug("HTML Compilation Stats:", {
78
- originalSize: r.originalSize,
79
- compiledSize: r.compiledSize,
80
- reduction: `${r.reductionPercentage.toFixed(2)}%`,
81
- appliedRules: A,
82
- executionTime: `${r.executionTime.toFixed(2)}ms`
82
+ originalSize: a.originalSize,
83
+ compiledSize: a.compiledSize,
84
+ reduction: `${a.reductionPercentage.toFixed(2)}%`,
85
+ appliedRules: S,
86
+ executionTime: `${a.executionTime.toFixed(2)}ms`
83
87
  }), {
84
88
  dynamicContentList: T,
85
- compiledHtml: f,
86
- rawHtml: c,
87
- css: p,
89
+ compiledHtml: y,
90
+ rawHtml: i,
91
+ css: g,
88
92
  ampHtml: C,
89
- ampErrors: y,
90
- modules: g.map(Number),
93
+ ampErrors: e,
94
+ modules: f.map(Number),
91
95
  recommendation: {
92
- campaignUrls: D.recommendationCampaignUrls,
96
+ campaignUrls: w.recommendationCampaignUrls,
93
97
  configs: {}
94
98
  },
95
99
  unsubscribe: {
96
- status: S.length > 0,
97
- config: S
100
+ status: h.length > 0,
101
+ config: h
98
102
  }
99
103
  };
100
104
  }
101
105
  };
102
106
  };
103
107
  export {
104
- L as getHtmlReferencedUnsubscribePages,
108
+ F as getHtmlReferencedUnsubscribePages,
105
109
  G as useTemplatePreparation
106
110
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.12.0-beta.98baf2f",
3
+ "version": "3.12.0-beta.b73c720",
4
4
  "description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
5
5
  "main": "./dist/guido.umd.cjs",
6
6
  "module": "./dist/library.js",