@useinsider/guido 3.17.0-beta.db80c84 → 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 (70) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +12 -6
  3. package/dist/components/Guido.vue.js +6 -6
  4. package/dist/components/Guido.vue2.js +63 -61
  5. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +18 -0
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +70 -0
  7. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue.js +20 -0
  8. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue2.js +55 -0
  9. package/dist/composables/useCustomInterfaceAppearance.js +19 -16
  10. package/dist/composables/useRecommendation.js +41 -34
  11. package/dist/composables/useStripo.js +14 -14
  12. package/dist/config/migrator/index.js +8 -9
  13. package/dist/config/migrator/recommendation/compositionMapper.js +20 -20
  14. package/dist/config/migrator/recommendation/extractors.js +24 -29
  15. package/dist/config/migrator/recommendation/htmlBuilder.js +130 -116
  16. package/dist/config/migrator/recommendationMigrator.js +15 -16
  17. package/dist/enums/academy.js +2 -1
  18. package/dist/enums/date.js +4 -3
  19. package/dist/enums/extensions/recommendationBlock.js +25 -23
  20. package/dist/extensions/Blocks/Recommendation/constants/blockIds.js +1 -1
  21. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  22. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +21 -20
  23. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +28 -34
  24. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +256 -323
  25. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +158 -157
  26. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +141 -0
  27. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +0 -1
  28. package/dist/extensions/Blocks/Recommendation/extension.js +29 -31
  29. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +4 -23
  30. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +216 -68
  31. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +3 -3
  32. package/dist/extensions/Blocks/Recommendation/templates/utils.js +107 -85
  33. package/dist/extensions/Blocks/common-control.js +22 -19
  34. package/dist/guido.css +1 -1
  35. package/dist/services/recommendationApi.js +78 -29
  36. package/dist/src/@types/config/schemas.d.ts +12 -0
  37. package/dist/src/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.d.ts +3 -0
  38. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBox.vue.d.ts +24 -0
  39. package/dist/src/composables/useConfig.d.ts +2 -0
  40. package/dist/src/composables/useRecommendation.d.ts +1 -0
  41. package/dist/src/config/migrator/recommendation/extractors.d.ts +0 -8
  42. package/dist/src/enums/academy.d.ts +1 -0
  43. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  44. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +1 -2
  45. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +0 -7
  46. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +0 -1
  47. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  48. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +0 -10
  49. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +0 -10
  50. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +0 -1
  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/controls/main/utils.d.ts +0 -1
  54. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +214 -10
  55. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  56. package/dist/src/extensions/Blocks/common-control.d.ts +4 -1
  57. package/dist/src/mock/api/recommendation-strategies.d.ts +43 -0
  58. package/dist/src/services/recommendationApi.d.ts +4 -1
  59. package/dist/src/stores/config.d.ts +18 -0
  60. package/dist/static/styles/components/recommendation-strategy.css.js +171 -0
  61. package/dist/static/styles/customEditorStyle.css.js +1 -3
  62. package/dist/utils/dateUtil.js +23 -10
  63. package/dist/utils/templatePreparation.js +37 -34
  64. package/package.json +2 -1
  65. package/dist/config/migrator/recommendationTitleMigrator.js +0 -119
  66. package/dist/extensions/Blocks/Recommendation/controls/title/index.js +0 -44
  67. package/dist/extensions/Blocks/Recommendation/templates/titleRows.js +0 -33
  68. package/dist/src/config/migrator/recommendationTitleMigrator.d.ts +0 -5
  69. package/dist/src/extensions/Blocks/Recommendation/controls/title/index.d.ts +0 -86
  70. package/dist/src/extensions/Blocks/Recommendation/templates/titleRows.d.ts +0 -13
@@ -5,24 +5,23 @@ import { RecommendationBlockControl as i } from "./controls/main/index.js";
5
5
  import "./constants/selectors.js";
6
6
  import "./store/recommendation.js";
7
7
  import "./utils/captureStyleTemplates.js";
8
- import { TitleControls as e } from "./controls/title/index.js";
9
- import { NameControls as l } from "./controls/name/index.js";
10
- import { PriceControls as s } from "./controls/price/index.js";
11
- import { OldPriceControls as p } from "./controls/oldPrice/index.js";
12
- import { OmnibusPriceControls as a } from "./controls/omnibusPrice/index.js";
13
- import { OmnibusDiscountControls as c } from "./controls/omnibusDiscount/index.js";
14
- import { ButtonControls as C } from "./controls/button/index.js";
15
- import { ImageControls as f } from "./controls/image/index.js";
16
- import { CustomAttributeControls as d } from "./controls/customAttribute/index.js";
17
- import { SpacingControl as u } from "./controls/spacing/index.js";
18
- import { BlockBackgroundColorControl as g } from "./controls/blockBackground/index.js";
19
- import { CardBackgroundColorControl as y } from "./controls/cardBackground/index.js";
20
- import { RecommendationCardCompositionControl as B } from "./controls/cardComposition/index.js";
21
- import { SyncInfoMessageControl as P } from "./controls/syncInfoMessage.js";
22
- import { RecommendationIconsRegistry as R } from "./iconsRegistry.js";
23
- import S from "./recommendation.css.js";
24
- import { SettingsPanel as k } from "./settingsPanel.js";
25
- const w = [
8
+ import { NameControls as e } from "./controls/name/index.js";
9
+ import { PriceControls as l } from "./controls/price/index.js";
10
+ import { OldPriceControls as s } from "./controls/oldPrice/index.js";
11
+ import { OmnibusPriceControls as p } from "./controls/omnibusPrice/index.js";
12
+ import { OmnibusDiscountControls as a } from "./controls/omnibusDiscount/index.js";
13
+ import { ButtonControls as c } from "./controls/button/index.js";
14
+ import { ImageControls as C } from "./controls/image/index.js";
15
+ import { CustomAttributeControls as f } from "./controls/customAttribute/index.js";
16
+ import { SpacingControl as d } from "./controls/spacing/index.js";
17
+ import { BlockBackgroundColorControl as u } from "./controls/blockBackground/index.js";
18
+ import { CardBackgroundColorControl as g } from "./controls/cardBackground/index.js";
19
+ import { RecommendationCardCompositionControl as y } from "./controls/cardComposition/index.js";
20
+ import { SyncInfoMessageControl as B } from "./controls/syncInfoMessage.js";
21
+ import { RecommendationIconsRegistry as P } from "./iconsRegistry.js";
22
+ import R from "./recommendation.css.js";
23
+ import { SettingsPanel as S } from "./settingsPanel.js";
24
+ const k = [
26
25
  e,
27
26
  l,
28
27
  s,
@@ -30,22 +29,21 @@ const w = [
30
29
  a,
31
30
  c,
32
31
  C,
33
- f,
34
- d
35
- ], b = [
32
+ f
33
+ ], w = [
36
34
  i,
35
+ u,
37
36
  g,
37
+ d,
38
38
  y,
39
- u,
40
- B,
41
- P
42
- ], v = [
43
- ...b,
44
- ...w.flatMap((o) => Object.values(o))
45
- ], Y = v.reduce(
39
+ B
40
+ ], b = [
41
+ ...w,
42
+ ...k.flatMap((o) => Object.values(o))
43
+ ], W = b.reduce(
46
44
  (o, r) => o.addControl(r),
47
- new t().addBlock(m).withSettingsPanelRegistry(k)
48
- ).addStyles(S).withPreviewStyles(n).withIconsRegistry(R).build();
45
+ new t().addBlock(m).withSettingsPanelRegistry(S)
46
+ ).addStyles(R).withPreviewStyles(n).withIconsRegistry(P).build();
49
47
  export {
50
- Y as default
48
+ W as default
51
49
  };
@@ -3,10 +3,9 @@ import { BLOCK_ID as R } from "./block.js";
3
3
  import { RecommendationBlockId as S } from "./constants/blockIds.js";
4
4
  import { RecommendationControlId as T } from "./constants/controlIds.js";
5
5
  import "./constants/selectors.js";
6
- import { CONTROL_BLOCK_ID as L } from "./controls/main/index.js";
6
+ import { CONTROL_BLOCK_ID as U } from "./controls/main/index.js";
7
7
  import "./store/recommendation.js";
8
8
  import "./utils/captureStyleTemplates.js";
9
- import "./controls/title/index.js";
10
9
  import "./controls/name/index.js";
11
10
  import "./controls/price/index.js";
12
11
  import "./controls/oldPrice/index.js";
@@ -16,7 +15,7 @@ import "./controls/button/index.js";
16
15
  import "./controls/image/index.js";
17
16
  import { SPACING_CONTROL_ID as A } from "./controls/spacing/index.js";
18
17
  import "./controls/blockBackground/index.js";
19
- import { CARD_BACKGROUND_COLOR_CONTROL_ID as U } from "./controls/cardBackground/index.js";
18
+ import { CARD_BACKGROUND_COLOR_CONTROL_ID as L } from "./controls/cardBackground/index.js";
20
19
  import { COMPOSITION_CONTROL_BLOCK_ID as D } from "./controls/cardComposition/index.js";
21
20
  import { SYNC_INFO_MESSAGE_CONTROL_ID as N } from "./controls/syncInfoMessage.js";
22
21
  class s extends E {
@@ -25,7 +24,7 @@ class s extends E {
25
24
  new _(
26
25
  I.SETTINGS,
27
26
  [
28
- L,
27
+ U,
29
28
  C.EXTERNAL_INDENTS
30
29
  ]
31
30
  ),
@@ -33,7 +32,7 @@ class s extends E {
33
32
  I.STYLES,
34
33
  [
35
34
  T.BLOCK_BACKGROUND,
36
- U,
35
+ L,
37
36
  A
38
37
  ]
39
38
  ),
@@ -43,24 +42,6 @@ class s extends E {
43
42
  D
44
43
  ]
45
44
  ).withLabel(this.api.translate("Card Composition"))
46
- ], O[S.TITLE] = [
47
- new _(
48
- I.SETTINGS,
49
- [
50
- T.TITLE_STYLE,
51
- T.TITLE_ALIGN,
52
- T.TITLE_PADDINGS
53
- ]
54
- ),
55
- new _(
56
- I.STYLES,
57
- [
58
- T.TITLE_BACKGROUND,
59
- T.TITLE_FONT_FAMILY,
60
- T.TITLE_SIZE,
61
- T.TITLE_COLOR
62
- ]
63
- )
64
45
  ], O[S.NAME] = [
65
46
  new _(
66
47
  I.SETTINGS,
@@ -1,21 +1,21 @@
1
- import { getRecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as C } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as y } from "../../../../services/recommendationApi.js";
1
+ import { getRecommendationFeedSourceMaps as p, getOperatorOptions as R, PriceAttributes as C } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as w } from "../../../../services/recommendationApi.js";
3
3
  import { useConfigStore as G } from "../../../../stores/config.js";
4
4
  import { defineStore as P } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as F, DEFAULT_CARDS_IN_ROW as D } from "../constants/layout.js";
7
- import { EXCLUDED_ALGORITHM_IDS as w } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as S } from "../templates/utils.js";
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as D, DEFAULT_CARDS_IN_ROW as F } from "../constants/layout.js";
7
+ import { DEFAULT_NODE_CONFIG as v, EXCLUDED_ALGORITHM_IDS as _ } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as y } from "../templates/utils.js";
9
9
  import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as v } from "../utils/partnerCustomizations.js";
10
+ import { getPartnerRecommendationParams as U } from "../utils/partnerCustomizations.js";
11
11
  import { sanitizeProductIds as N } from "../utils/productIds.js";
12
- import { isFilterValid as x } from "../validation/filterSchema.js";
13
- import { isConfigValid as L } from "../validation/requiredFields.js";
14
- const h = y();
15
- let m = null, u = null, d = null;
12
+ import { isFilterValid as L } from "../validation/filterSchema.js";
13
+ import { isConfigValid as x } from "../validation/requiredFields.js";
14
+ const u = w();
15
+ let d = null, f = null, h = null, g = null;
16
16
  function k() {
17
17
  return {
18
- cardsInRow: D,
18
+ cardsInRow: F,
19
19
  currencySettings: {
20
20
  name: "USD",
21
21
  value: "USD",
@@ -29,13 +29,14 @@ function k() {
29
29
  productIds: [],
30
30
  id: 1,
31
31
  language: "en_US",
32
- mobileCardsInRow: F,
32
+ mobileCardsInRow: D,
33
33
  mobileLayoutEnabled: !1,
34
34
  orientation: "grid",
35
35
  recommendedProducts: [],
36
36
  sendProductRequestFlag: !1,
37
37
  shuffleProducts: !1,
38
38
  strategy: "mostPopular",
39
+ strategyId: "",
39
40
  textTrimming: !0,
40
41
  unresponsive: !1,
41
42
  size: "6",
@@ -50,22 +51,26 @@ function I() {
50
51
  recommendationProducts: [],
51
52
  filterStatus: !1,
52
53
  filterSelectionDrawerStatus: !1,
54
+ assignStrategyDrawerStatus: !1,
53
55
  filterGroup: 1,
54
56
  isInitialized: !1,
55
57
  filterSnapshot: null
56
58
  };
57
59
  }
58
- const U = () => ({
60
+ const A = () => ({
59
61
  recommendationCampaignUrls: {},
60
62
  activePredictiveAlgorithms: [],
61
63
  languages: {},
62
64
  currencyList: [],
63
65
  filterList: {},
66
+ strategies: [],
67
+ strategiesLoading: !1,
68
+ strategyUrlTemplates: {},
64
69
  blockStates: {},
65
70
  currentRecommendationId: null,
66
71
  configVersion: 0
67
- }), H = P("guidoRecommendationExtension", {
68
- state: () => U(),
72
+ }), J = P("guidoRecommendationExtension", {
73
+ state: () => A(),
69
74
  getters: {
70
75
  // ====================================================================
71
76
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -89,6 +94,20 @@ const U = () => ({
89
94
  filterSelectionDrawerStatus(t) {
90
95
  return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].filterSelectionDrawerStatus : !1;
91
96
  },
97
+ /**
98
+ * Proxy getter: delegates to blockStates[currentRecommendationId].assignStrategyDrawerStatus
99
+ */
100
+ assignStrategyDrawerStatus(t) {
101
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].assignStrategyDrawerStatus : !1;
102
+ },
103
+ /**
104
+ * The current block's assigned strategy record from the listing,
105
+ * or null when nothing is assigned / the strategy is no longer listed.
106
+ */
107
+ assignedStrategy(t) {
108
+ const { strategyId: e } = this.recommendationConfigs;
109
+ return e ? t.strategies.find((r) => String(r.strategy_id) === e) ?? null : null;
110
+ },
92
111
  /**
93
112
  * Proxy getter: delegates to blockStates[currentRecommendationId].filterStatus
94
113
  */
@@ -120,9 +139,9 @@ const U = () => ({
120
139
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
121
140
  },
122
141
  getActivePredictiveAlgorithms: (t) => {
123
- const e = g(), r = [];
124
- return t.activePredictiveAlgorithms.filter((n) => !w.includes(n)).forEach((n) => {
125
- r.push(...e.filter((c) => c.id === n));
142
+ const e = p(), r = [];
143
+ return t.activePredictiveAlgorithms.filter((n) => !_.includes(n)).forEach((n) => {
144
+ r.push(...e.filter((s) => s.id === n));
126
145
  }), r.map((n) => ({
127
146
  text: n.name,
128
147
  value: n.key
@@ -193,10 +212,10 @@ const U = () => ({
193
212
  [e]: o
194
213
  };
195
214
  }
196
- const n = t.toString(), c = e.toString(), i = this.recommendationCampaignUrls[n];
215
+ const n = t.toString(), s = e.toString(), i = this.recommendationCampaignUrls[n];
197
216
  i && (this.recommendationCampaignUrls = {
198
217
  ...this.recommendationCampaignUrls,
199
- [c]: i
218
+ [s]: i
200
219
  });
201
220
  },
202
221
  /**
@@ -221,7 +240,7 @@ const U = () => ({
221
240
  * `isInitialized`) alone.
222
241
  */
223
242
  seedBlockUrlConfig(t, e) {
224
- const r = (o, s) => o === "." || o === "," || o === " " ? o : s, n = {
243
+ const r = (o, a) => o === "." || o === "," || o === " " ? o : a, n = {
225
244
  name: e.currencyCode,
226
245
  value: e.currencyCode,
227
246
  symbol: e.currencyCode,
@@ -229,17 +248,18 @@ const U = () => ({
229
248
  decimalCount: String(e.currencyDecimalCount),
230
249
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
231
250
  thousandSeparator: r(e.currencyThousandSeparator, ".")
232
- }, c = !this.blockStates[t], i = c ? I() : this.blockStates[t];
251
+ }, s = !this.blockStates[t], i = s ? I() : this.blockStates[t];
233
252
  i.recommendationConfigs = {
234
253
  ...i.recommendationConfigs,
235
254
  strategy: e.strategy,
255
+ strategyId: e.strategyId,
236
256
  language: e.language,
237
257
  size: e.size,
238
258
  productIds: e.productIds,
239
259
  filters: e.filters,
240
260
  shuffleProducts: e.shuffleProducts,
241
261
  currencySettings: n
242
- }, c && (this.blockStates = {
262
+ }, s && (this.blockStates = {
243
263
  ...this.blockStates,
244
264
  [t]: i
245
265
  });
@@ -315,20 +335,107 @@ const U = () => ({
315
335
  t.filterSnapshot !== null && (t.recommendationConfigs.filters = t.filterSnapshot, t.filterSnapshot = null), t.filterSelectionDrawerStatus = !1;
316
336
  },
317
337
  // ====================================================================
338
+ // Reusable Recommendation Strategy (SD-149105)
339
+ // ====================================================================
340
+ /**
341
+ * Opens the Assign Strategy drawer for the current block.
342
+ * Selection state lives in the drawer component until Assign is
343
+ * pressed, so closing without assigning needs no snapshot/revert.
344
+ */
345
+ openAssignStrategyDrawer() {
346
+ this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !0);
347
+ },
348
+ closeAssignStrategyDrawer() {
349
+ this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !1);
350
+ },
351
+ /**
352
+ * Fetches the active + EMAIL strategy listing. Always fetches fresh
353
+ * (dead-strategy detection and post-create refresh need freshness);
354
+ * only concurrent in-flight calls share one request.
355
+ */
356
+ async fetchStrategies() {
357
+ if (g) {
358
+ await g;
359
+ return;
360
+ }
361
+ g = (async () => {
362
+ this.strategiesLoading = !0;
363
+ try {
364
+ this.strategies = await u.fetchRecommendationStrategies();
365
+ } finally {
366
+ this.strategiesLoading = !1;
367
+ }
368
+ })();
369
+ try {
370
+ await g;
371
+ } finally {
372
+ g = null;
373
+ }
374
+ },
375
+ /**
376
+ * Resolves a strategy's recommendation endpoint template via
377
+ * endpoint-generate, cached per strategy id + revision. Failures throw
378
+ * and are NOT cached, so the next attempt retries.
379
+ *
380
+ * The revision is part of the key because `full_path` bakes in the
381
+ * algorithm path and `size=` as they were when it was generated, while
382
+ * the listing is deliberately never cached — editing a strategy in the
383
+ * panel and re-assigning it here would otherwise call a stale endpoint.
384
+ *
385
+ * A record with no revision is not cached at all: a `<id>:` key can
386
+ * never change, so it would serve the stale template forever — exactly
387
+ * what the revision is here to prevent.
388
+ */
389
+ async resolveStrategyUrlTemplate(t) {
390
+ const e = typeof t.updated_at == "string" ? t.updated_at : "", r = e ? `${t.strategy_id}:${e}` : "", n = r ? this.strategyUrlTemplates[r] : void 0;
391
+ if (n)
392
+ return n;
393
+ const { full_path: s } = await u.generateStrategyEndpoint(t);
394
+ return r && (this.strategyUrlTemplates = {
395
+ ...this.strategyUrlTemplates,
396
+ [r]: s
397
+ }), s;
398
+ },
399
+ /**
400
+ * Assigns a strategy to the current block. The config write is
401
+ * synchronous (before any await) so a block switch cannot misdirect
402
+ * it; the configVersion bump persists it to node config and triggers
403
+ * the product refetch via the coordinator control's subscription.
404
+ */
405
+ assignStrategy(t) {
406
+ this.patchCurrentBlockConfig({
407
+ strategyId: String(t.strategy_id),
408
+ ...t.size !== void 0 ? { size: String(t.size) } : {}
409
+ });
410
+ },
411
+ /**
412
+ * Clears the current block's strategy; it falls back to the per-block
413
+ * algorithm settings.
414
+ *
415
+ * `size` goes back to the default rather than staying on the removed
416
+ * strategy's count: the Number of Products control is hidden in RRS
417
+ * mode (D2), so a block left on the strategy's count would have no UI
418
+ * to correct it. The pre-assign count is not restored — that would
419
+ * need a persisted snapshot field; tracked for a later phase.
420
+ */
421
+ removeStrategy() {
422
+ this.patchCurrentBlockConfig({ strategyId: "", size: v.size });
423
+ },
424
+ // ====================================================================
318
425
  // Shared Data Fetching (fetched once, used by all blocks)
319
426
  // ====================================================================
320
427
  async fetchRecommendationCreateData() {
321
428
  if (!this.activePredictiveAlgorithms.length) {
322
- if (m) {
323
- await m;
429
+ if (d) {
430
+ await d;
324
431
  return;
325
432
  }
326
- m = (async () => {
433
+ d = (async () => {
327
434
  const {
328
435
  activePredictiveAlgorithms: t,
329
436
  languages: e,
330
437
  currencies: r
331
- } = await h.fetchRecommendationCreateData();
438
+ } = await u.fetchRecommendationCreateData();
332
439
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
333
440
  const n = this.blockStates[this.currentRecommendationId];
334
441
  n.filterStatus = !!n.recommendationConfigs.filters.length;
@@ -336,26 +443,26 @@ const U = () => ({
336
443
  this.currencyList = r;
337
444
  })();
338
445
  try {
339
- await m;
446
+ await d;
340
447
  } finally {
341
- m = null;
448
+ d = null;
342
449
  }
343
450
  }
344
451
  },
345
452
  async fetchRecommendationFilters() {
346
453
  if (!Object.keys(this.filterList).length) {
347
- if (u) {
348
- await u;
454
+ if (f) {
455
+ await f;
349
456
  return;
350
457
  }
351
- u = (async () => {
352
- const t = await h.fetchRecommendationFilters();
458
+ f = (async () => {
459
+ const t = await u.fetchRecommendationFilters();
353
460
  this.filterList = t;
354
461
  })();
355
462
  try {
356
- await u;
463
+ await f;
357
464
  } finally {
358
- u = null;
465
+ f = null;
359
466
  }
360
467
  }
361
468
  },
@@ -370,10 +477,10 @@ const U = () => ({
370
477
  deleteFilterGroup(t) {
371
478
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
372
479
  return;
373
- const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((i) => i.filterGroup !== t), n = [...new Set(r.map((i) => i.filterGroup))].sort((i, o) => i - o), c = new Map(n.map((i, o) => [i, o + 1]));
480
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((i) => i.filterGroup !== t), n = [...new Set(r.map((i) => i.filterGroup))].sort((i, o) => i - o), s = new Map(n.map((i, o) => [i, o + 1]));
374
481
  e.recommendationConfigs.filters = r.map((i) => ({
375
482
  ...i,
376
- filterGroup: c.get(i.filterGroup) ?? i.filterGroup
483
+ filterGroup: s.get(i.filterGroup) ?? i.filterGroup
377
484
  }));
378
485
  },
379
486
  updateFilter(t) {
@@ -384,7 +491,7 @@ const U = () => ({
384
491
  const n = [...e.recommendationConfigs.filters];
385
492
  n[r] = {
386
493
  ...t,
387
- isValid: x(t)
494
+ isValid: L(t)
388
495
  }, e.recommendationConfigs.filters = n;
389
496
  }
390
497
  },
@@ -398,10 +505,10 @@ const U = () => ({
398
505
  let i = 1;
399
506
  n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
400
507
  } else {
401
- const i = [...new Set(n.map((s) => s.filterGroup))].sort((s, a) => s - a), o = new Map(i.map((s, a) => [s, a + 1]));
402
- n = n.map((s) => ({
403
- ...s,
404
- filterGroup: o.get(s.filterGroup) ?? s.filterGroup
508
+ const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, c) => a - c), o = new Map(i.map((a, c) => [a, c + 1]));
509
+ n = n.map((a) => ({
510
+ ...a,
511
+ filterGroup: o.get(a.filterGroup) ?? a.filterGroup
405
512
  }));
406
513
  }
407
514
  e.recommendationConfigs.filters = n;
@@ -410,15 +517,15 @@ const U = () => ({
410
517
  addFilter(t) {
411
518
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
412
519
  return;
413
- const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], c = r.filter(
520
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], s = r.filter(
414
521
  (o) => o.filterGroup === t.filterGroup
415
522
  ).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
416
523
  i !== -1 ? r.splice(i + 1, 0, {
417
524
  ...t,
418
- filterNumber: c
525
+ filterNumber: s
419
526
  }) : r.push({
420
527
  ...t,
421
- filterNumber: c
528
+ filterNumber: s
422
529
  }), e.recommendationConfigs.filters = r;
423
530
  },
424
531
  generateFilterQuery() {
@@ -430,55 +537,96 @@ const U = () => ({
430
537
  * every block's recommendationConfigs across user edits.
431
538
  */
432
539
  hasInvalidBlock() {
433
- return Object.values(this.blockStates).some((t) => !L(t.recommendationConfigs, this));
540
+ return Object.values(this.blockStates).some((t) => !x(t.recommendationConfigs, this));
434
541
  },
435
542
  // ====================================================================
436
543
  // Per-Block Product Fetching
437
544
  // ====================================================================
438
545
  async fetchRecommendationProducts() {
439
546
  if (!(this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])) {
440
- if (d) {
441
- await d;
547
+ if (h) {
548
+ await h;
442
549
  return;
443
550
  }
444
- d = this._doFetchProducts();
551
+ h = this._doFetchProducts();
445
552
  try {
446
- await d;
553
+ await h;
447
554
  } finally {
448
- d = null;
555
+ h = null;
449
556
  }
450
557
  }
451
558
  },
452
559
  async _doFetchProducts() {
453
- var p;
454
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = g().find((l) => l.key === r.strategy)) == null ? void 0 : p.path) || "", o = G(), s = parseInt(r.size) || 6, a = {
560
+ var S;
561
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = G();
562
+ if (n.isFeatureEnabled("reusableRecommendationStrategy") && r.strategyId) {
563
+ await this._doFetchStrategyProducts(t);
564
+ return;
565
+ }
566
+ const s = r.filters.filter((m) => m.isValid), i = b(s), o = ((S = p().find((m) => m.key === r.strategy)) == null ? void 0 : S.path) || "", a = parseInt(r.size) || 6, c = {
455
567
  locale: r.language,
456
568
  currency: r.currencySettings.value,
457
- partnerName: o.partnerName,
569
+ partnerName: n.partnerName,
458
570
  size: r.size,
459
571
  details: !0,
460
- campaignId: o.variationId
572
+ campaignId: n.variationId
461
573
  };
462
- r.strategy === "manualMerchandising" ? a.productId = N(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), c && (a.filter = c), r.shuffleProducts && (a.shuffle = !0), Object.assign(
463
- a,
464
- v(o.partnerName, r.strategy)
574
+ r.strategy === "manualMerchandising" ? c.productId = N(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), i && (c.filter = i), r.shuffleProducts && (c.shuffle = !0), Object.assign(
575
+ c,
576
+ U(n.partnerName, r.strategy)
465
577
  );
466
- let f;
578
+ let l;
467
579
  try {
468
- f = await h.fetchRecommendationProducts(i, a);
580
+ l = await u.fetchRecommendationProducts(o, c);
469
581
  } catch {
470
- f = [];
582
+ l = [];
471
583
  }
472
- if (this.blockStates[t]) {
473
- const l = f.length > 0 ? f : S(s);
474
- l.length < s ? this.blockStates[t].recommendationProducts = [
475
- ...l,
476
- ...S(s - l.length)
477
- ] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
584
+ this._setBlockProducts(t, l, a);
585
+ },
586
+ /**
587
+ * Fetches products for a strategy-backed block: listing lookup →
588
+ * endpoint-generate template → fill {locale}/{currency}, drop
589
+ * {user_id} (resolved at send time) → fetch. Any failure (dead
590
+ * strategy, resolution error) falls back to placeholder products.
591
+ */
592
+ async _doFetchStrategyProducts(t) {
593
+ const { recommendationConfigs: e } = this.blockStates[t];
594
+ let r = [], n = parseInt(e.size) || 6;
595
+ try {
596
+ this.strategies.length || await this.fetchStrategies();
597
+ const s = this.strategies.find(
598
+ (i) => String(i.strategy_id) === e.strategyId
599
+ );
600
+ if (s) {
601
+ n = parseInt(String(s.size ?? "")) || n;
602
+ const o = (await this.resolveStrategyUrlTemplate(s)).replaceAll("{locale}", e.language).replaceAll("{currency}", e.currencySettings.value), a = o.indexOf("?"), c = a === -1 ? o : o.slice(0, a), l = (a === -1 ? "" : o.slice(a + 1)).split("&").filter((m) => m && !m.startsWith("user_id=")), S = l.length ? `${c}?${l.join("&")}` : c;
603
+ r = await u.fetchRecommendationProductsByUrl(S);
604
+ }
605
+ } catch {
606
+ r = [];
478
607
  }
608
+ this._setBlockProducts(t, r, n);
609
+ },
610
+ /**
611
+ * Writes products to a block's state, padded/clipped to `size` so
612
+ * in-place content updates (which compare DOM element count vs
613
+ * products.length) succeed:
614
+ * - API returned nothing → fill entirely with defaults
615
+ * - fewer than requested → pad with dummies
616
+ * - more than requested → clip, preventing the renderer from extending
617
+ * its slot grid (which would reset migrator-applied styles).
618
+ */
619
+ _setBlockProducts(t, e, r) {
620
+ if (!this.blockStates[t])
621
+ return;
622
+ const n = e.length > 0 ? e : y(r);
623
+ n.length < r ? this.blockStates[t].recommendationProducts = [
624
+ ...n,
625
+ ...y(r - n.length)
626
+ ] : n.length > r ? this.blockStates[t].recommendationProducts = n.slice(0, r) : this.blockStates[t].recommendationProducts = n;
479
627
  }
480
628
  }
481
629
  });
482
630
  export {
483
- H as useRecommendationExtensionStore
631
+ J as useRecommendationExtensionStore
484
632
  };
@@ -1,6 +1,6 @@
1
1
  import { ATTR_PRODUCT_PRICE as f, ATTR_PRODUCT_OLD_PRICE as O } from "../../constants/selectors.js";
2
2
  import { DEFAULT_PRODUCTS_PER_ROW as w } from "../../constants/layout.js";
3
- import { createBlockTemplate as A, getDefaultProducts as g, DEFAULTS as U, buildSpacer as h, DEFAULT_CARD_COMPOSITION as Y, buildElementRenderer as B, resolveInlinePriceOrder as F, buildCellOptions as I, DEFAULT_CARD_VISIBILITY as W } from "../utils.js";
3
+ import { getDefaultProducts as A, DEFAULTS as g, createBlockTemplate as U, buildSpacer as h, DEFAULT_CARD_COMPOSITION as Y, buildElementRenderer as B, resolveInlinePriceOrder as F, buildCellOptions as I, DEFAULT_CARD_VISIBILITY as W } from "../utils.js";
4
4
  import { DEFAULT_CELL_PADDING as k, renderInlineGridPriceCell as H, buildFillerCell as x, gridElementRenderer as y } from "./elementRenderer.js";
5
5
  const G = `
6
6
  <tr class="recommendation-product-row">
@@ -69,8 +69,8 @@ function M(t, r, o, e = {}, c = {}) {
69
69
  );
70
70
  }
71
71
  function Q(t) {
72
- const r = t ? `ins-recommendation-v3-block-${t}` : void 0, o = A("grid", r), e = g(), c = M(e, w);
73
- return o.replace("{-{-TITLE-}-}", U.TITLE).replace("{-{-PRODUCT_ROWS-}-}", c).replace("{-{-MOBILE_PRODUCT_ROWS-}-}", "");
72
+ const r = t ? `ins-recommendation-v3-block-${t}` : void 0, o = U("grid", r), e = A(), c = M(e, w);
73
+ return o.replace("{-{-TITLE-}-}", g.TITLE).replace("{-{-PRODUCT_ROWS-}-}", c).replace("{-{-MOBILE_PRODUCT_ROWS-}-}", "");
74
74
  }
75
75
  export {
76
76
  Q as getDefaultTemplate,