@useinsider/guido 3.17.0-beta.3ff6ea0 → 3.17.0-beta.6255bfc

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 (76) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +12 -6
  3. package/dist/components/Guido.vue.js +2 -2
  4. package/dist/components/Guido.vue2.js +31 -30
  5. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +18 -0
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +66 -0
  7. package/dist/composables/useCustomInterfaceAppearance.js +52 -38
  8. package/dist/composables/useRecommendation.js +52 -44
  9. package/dist/composables/useStripo.js +14 -14
  10. package/dist/config/migrator/index.js +8 -9
  11. package/dist/config/migrator/recommendation/compositionMapper.js +20 -20
  12. package/dist/config/migrator/recommendation/extractors.js +24 -29
  13. package/dist/config/migrator/recommendation/htmlBuilder.js +130 -116
  14. package/dist/config/migrator/recommendation/settingsMapper.js +19 -17
  15. package/dist/config/migrator/recommendationMigrator.js +15 -16
  16. package/dist/enums/academy.js +2 -1
  17. package/dist/enums/date.js +4 -3
  18. package/dist/enums/extensions/recommendationBlock.js +4 -1
  19. package/dist/extensions/Blocks/Recommendation/block.js +26 -21
  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 +26 -25
  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 +178 -165
  26. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +142 -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/services/configService.js +35 -35
  30. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +4 -23
  31. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +251 -110
  32. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +3 -3
  33. package/dist/extensions/Blocks/Recommendation/templates/utils.js +107 -85
  34. package/dist/extensions/Blocks/common-control.js +22 -19
  35. package/dist/guido.css +1 -1
  36. package/dist/services/recommendationApi.js +75 -30
  37. package/dist/src/@types/config/schemas.d.ts +12 -0
  38. package/dist/src/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.d.ts +3 -0
  39. package/dist/src/components/wrappers/WpModal.vue.d.ts +1 -1
  40. package/dist/src/composables/useConfig.d.ts +2 -0
  41. package/dist/src/composables/useRecommendation.d.ts +1 -0
  42. package/dist/src/config/migrator/recommendation/extractors.d.ts +0 -8
  43. package/dist/src/enums/academy.d.ts +1 -0
  44. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  45. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +1 -2
  46. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +0 -7
  47. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +7 -7
  48. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +2 -2
  49. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +0 -10
  50. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +0 -10
  51. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +0 -1
  52. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +15 -3
  53. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +30 -0
  54. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +0 -1
  55. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +206 -10
  56. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  57. package/dist/src/extensions/Blocks/common-control.d.ts +4 -1
  58. package/dist/src/mock/api/recommendation-strategies.d.ts +63 -0
  59. package/dist/src/services/recommendationApi.d.ts +4 -1
  60. package/dist/src/stores/config.d.ts +18 -0
  61. package/dist/src/utils/strategyInfoBox.d.ts +22 -0
  62. package/dist/static/assets/icons/line-change.svg.js +5 -0
  63. package/dist/static/assets/icons/line-delete.svg.js +5 -0
  64. package/dist/static/assets/icons/line-plus.svg.js +5 -0
  65. package/dist/static/styles/components/recommendation-strategy.css.js +188 -0
  66. package/dist/static/styles/customEditorStyle.css.js +1 -3
  67. package/dist/utils/dateUtil.js +29 -10
  68. package/dist/utils/strategyInfoBox.js +37 -0
  69. package/dist/utils/templatePreparation.js +37 -34
  70. package/package.json +2 -1
  71. package/dist/config/migrator/recommendationTitleMigrator.js +0 -119
  72. package/dist/extensions/Blocks/Recommendation/controls/title/index.js +0 -44
  73. package/dist/extensions/Blocks/Recommendation/templates/titleRows.js +0 -33
  74. package/dist/src/config/migrator/recommendationTitleMigrator.d.ts +0 -5
  75. package/dist/src/extensions/Blocks/Recommendation/controls/title/index.d.ts +0 -86
  76. package/dist/src/extensions/Blocks/Recommendation/templates/titleRows.d.ts +0 -13
@@ -1,21 +1,22 @@
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";
3
- import { useConfigStore as G } from "../../../../stores/config.js";
4
- import { defineStore as P } from "pinia";
1
+ import { useConfig as C } from "../../../../composables/useConfig.js";
2
+ import { getRecommendationFeedSourceMaps as y, getOperatorOptions as w, PriceAttributes as G } from "../../../../enums/extensions/recommendationBlock.js";
3
+ import { useRecommendationApi as D } from "../../../../services/recommendationApi.js";
4
+ import { useConfigStore as P } from "../../../../stores/config.js";
5
+ import { defineStore as F } from "pinia";
5
6
  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";
9
- import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as v } from "../utils/partnerCustomizations.js";
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;
16
- function k() {
7
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as v, DEFAULT_CARDS_IN_ROW as _ } from "../constants/layout.js";
8
+ import { DEFAULT_NODE_CONFIG as U, EXCLUDED_ALGORITHM_IDS as N } from "../constants/defaultConfig.js";
9
+ import { getDefaultProducts as b } from "../templates/utils.js";
10
+ import { generateCompleteFilterQuery as I } from "../utils/filterUtil.js";
11
+ import { getPartnerRecommendationParams as L } from "../utils/partnerCustomizations.js";
12
+ import { sanitizeProductIds as x } from "../utils/productIds.js";
13
+ import { isFilterValid as A } from "../validation/filterSchema.js";
14
+ import { isConfigValid as B } from "../validation/requiredFields.js";
15
+ const d = D();
16
+ let f = null, g = null, h = null, S = null;
17
+ function R() {
17
18
  return {
18
- cardsInRow: D,
19
+ cardsInRow: _,
19
20
  currencySettings: {
20
21
  name: "USD",
21
22
  value: "USD",
@@ -29,13 +30,14 @@ function k() {
29
30
  productIds: [],
30
31
  id: 1,
31
32
  language: "en_US",
32
- mobileCardsInRow: F,
33
+ mobileCardsInRow: v,
33
34
  mobileLayoutEnabled: !1,
34
35
  orientation: "grid",
35
36
  recommendedProducts: [],
36
37
  sendProductRequestFlag: !1,
37
38
  shuffleProducts: !1,
38
39
  strategy: "mostPopular",
40
+ strategyId: "",
39
41
  textTrimming: !0,
40
42
  unresponsive: !1,
41
43
  size: "6",
@@ -44,28 +46,32 @@ function k() {
44
46
  priceHideIfSameAsDiscounted: !1
45
47
  };
46
48
  }
47
- function I() {
49
+ function k() {
48
50
  return {
49
- recommendationConfigs: k(),
51
+ recommendationConfigs: R(),
50
52
  recommendationProducts: [],
51
53
  filterStatus: !1,
52
54
  filterSelectionDrawerStatus: !1,
55
+ assignStrategyDrawerStatus: !1,
53
56
  filterGroup: 1,
54
57
  isInitialized: !1,
55
58
  filterSnapshot: null
56
59
  };
57
60
  }
58
- const U = () => ({
61
+ const z = () => ({
59
62
  recommendationCampaignUrls: {},
60
63
  activePredictiveAlgorithms: [],
61
64
  languages: {},
62
65
  currencyList: [],
63
66
  filterList: {},
67
+ strategies: [],
68
+ strategiesLoading: !1,
69
+ strategyUrlTemplates: {},
64
70
  blockStates: {},
65
71
  currentRecommendationId: null,
66
72
  configVersion: 0
67
- }), H = P("guidoRecommendationExtension", {
68
- state: () => U(),
73
+ }), Y = F("guidoRecommendationExtension", {
74
+ state: () => z(),
69
75
  getters: {
70
76
  // ====================================================================
71
77
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -75,7 +81,7 @@ const U = () => ({
75
81
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
76
82
  */
77
83
  recommendationConfigs(t) {
78
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : k();
84
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : R();
79
85
  },
80
86
  /**
81
87
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -89,6 +95,20 @@ const U = () => ({
89
95
  filterSelectionDrawerStatus(t) {
90
96
  return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].filterSelectionDrawerStatus : !1;
91
97
  },
98
+ /**
99
+ * Proxy getter: delegates to blockStates[currentRecommendationId].assignStrategyDrawerStatus
100
+ */
101
+ assignStrategyDrawerStatus(t) {
102
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].assignStrategyDrawerStatus : !1;
103
+ },
104
+ /**
105
+ * The current block's assigned strategy record from the listing,
106
+ * or null when nothing is assigned / the strategy is no longer listed.
107
+ */
108
+ assignedStrategy(t) {
109
+ const { strategyId: e } = this.recommendationConfigs;
110
+ return e ? t.strategies.find((r) => String(r.strategy_id) === e) ?? null : null;
111
+ },
92
112
  /**
93
113
  * Proxy getter: delegates to blockStates[currentRecommendationId].filterStatus
94
114
  */
@@ -120,12 +140,12 @@ const U = () => ({
120
140
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
121
141
  },
122
142
  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));
126
- }), r.map((n) => ({
127
- text: n.name,
128
- value: n.key
143
+ const e = y(), r = [];
144
+ return t.activePredictiveAlgorithms.filter((i) => !N.includes(i)).forEach((i) => {
145
+ r.push(...e.filter((s) => s.id === i));
146
+ }), r.map((i) => ({
147
+ text: i.name,
148
+ value: i.key
129
149
  }));
130
150
  },
131
151
  getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
@@ -139,7 +159,7 @@ const U = () => ({
139
159
  getFilterList() {
140
160
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
141
161
  let e;
142
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : C.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
162
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : G.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
143
163
  text: t.displayName,
144
164
  value: e,
145
165
  type: t.attributeType
@@ -161,7 +181,7 @@ const U = () => ({
161
181
  setCurrentBlock(t) {
162
182
  this.blockStates[t] || (this.blockStates = {
163
183
  ...this.blockStates,
164
- [t]: I()
184
+ [t]: k()
165
185
  }), this.currentRecommendationId = t;
166
186
  },
167
187
  /**
@@ -171,13 +191,13 @@ const U = () => ({
171
191
  removeBlockState(t) {
172
192
  const e = t.toString();
173
193
  if (this.recommendationCampaignUrls[e]) {
174
- const n = { ...this.recommendationCampaignUrls };
175
- delete n[e], this.recommendationCampaignUrls = n;
194
+ const i = { ...this.recommendationCampaignUrls };
195
+ delete i[e], this.recommendationCampaignUrls = i;
176
196
  }
177
197
  const r = { ...this.blockStates };
178
198
  if (delete r[t], this.blockStates = r, this.currentRecommendationId === t) {
179
- const n = Object.keys(this.blockStates).map(Number);
180
- this.currentRecommendationId = n.length > 0 ? n[0] : null;
199
+ const i = Object.keys(this.blockStates).map(Number);
200
+ this.currentRecommendationId = i.length > 0 ? i[0] : null;
181
201
  }
182
202
  },
183
203
  /**
@@ -193,10 +213,10 @@ const U = () => ({
193
213
  [e]: o
194
214
  };
195
215
  }
196
- const n = t.toString(), c = e.toString(), i = this.recommendationCampaignUrls[n];
197
- i && (this.recommendationCampaignUrls = {
216
+ const i = t.toString(), s = e.toString(), n = this.recommendationCampaignUrls[i];
217
+ n && (this.recommendationCampaignUrls = {
198
218
  ...this.recommendationCampaignUrls,
199
- [c]: i
219
+ [s]: n
200
220
  });
201
221
  },
202
222
  /**
@@ -221,7 +241,7 @@ const U = () => ({
221
241
  * `isInitialized`) alone.
222
242
  */
223
243
  seedBlockUrlConfig(t, e) {
224
- const r = (o, s) => o === "." || o === "," || o === " " ? o : s, n = {
244
+ const r = (o, a) => o === "." || o === "," || o === " " ? o : a, i = {
225
245
  name: e.currencyCode,
226
246
  value: e.currencyCode,
227
247
  symbol: e.currencyCode,
@@ -229,19 +249,20 @@ const U = () => ({
229
249
  decimalCount: String(e.currencyDecimalCount),
230
250
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
231
251
  thousandSeparator: r(e.currencyThousandSeparator, ".")
232
- }, c = !this.blockStates[t], i = c ? I() : this.blockStates[t];
233
- i.recommendationConfigs = {
234
- ...i.recommendationConfigs,
252
+ }, s = !this.blockStates[t], n = s ? k() : this.blockStates[t];
253
+ n.recommendationConfigs = {
254
+ ...n.recommendationConfigs,
235
255
  strategy: e.strategy,
256
+ strategyId: e.strategyId,
236
257
  language: e.language,
237
258
  size: e.size,
238
259
  productIds: e.productIds,
239
260
  filters: e.filters,
240
261
  shuffleProducts: e.shuffleProducts,
241
- currencySettings: n
242
- }, c && (this.blockStates = {
262
+ currencySettings: i
263
+ }, s && (this.blockStates = {
243
264
  ...this.blockStates,
244
- [t]: i
265
+ [t]: n
245
266
  });
246
267
  },
247
268
  /**
@@ -260,18 +281,18 @@ const U = () => ({
260
281
  ...t.currencySettings || {}
261
282
  }
262
283
  };
263
- const { triggerRefetch: n = !0 } = e;
264
- n && this.configVersion++;
284
+ const { triggerRefetch: i = !0 } = e;
285
+ i && this.configVersion++;
265
286
  },
266
287
  /**
267
288
  * Creates a filter with the first available attribute and operator pre-selected.
268
289
  */
269
290
  createDefaultFilter(t, e) {
270
- const [r] = this.getFilterList, [n] = R(r == null ? void 0 : r.type);
291
+ const [r] = this.getFilterList, [i] = w(r == null ? void 0 : r.type);
271
292
  return {
272
293
  type: "standardFilter",
273
294
  attribute: (r == null ? void 0 : r.value) ?? "",
274
- operator: (n == null ? void 0 : n.value) ?? "",
295
+ operator: (i == null ? void 0 : i.value) ?? "",
275
296
  innerGroupOperator: "*",
276
297
  outerGroupOperator: "*",
277
298
  filterNumber: e,
@@ -315,47 +336,129 @@ const U = () => ({
315
336
  t.filterSnapshot !== null && (t.recommendationConfigs.filters = t.filterSnapshot, t.filterSnapshot = null), t.filterSelectionDrawerStatus = !1;
316
337
  },
317
338
  // ====================================================================
339
+ // Reusable Recommendation Strategy (SD-149105)
340
+ // ====================================================================
341
+ /**
342
+ * Opens the Assign Strategy drawer for the current block.
343
+ * Selection state lives in the drawer component until Assign is
344
+ * pressed, so closing without assigning needs no snapshot/revert.
345
+ */
346
+ openAssignStrategyDrawer() {
347
+ this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !0);
348
+ },
349
+ closeAssignStrategyDrawer() {
350
+ this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !1);
351
+ },
352
+ /**
353
+ * Fetches the active + EMAIL strategy listing. Always fetches fresh
354
+ * (dead-strategy detection and post-create refresh need freshness);
355
+ * only concurrent in-flight calls share one request.
356
+ */
357
+ async fetchStrategies() {
358
+ if (S) {
359
+ await S;
360
+ return;
361
+ }
362
+ S = (async () => {
363
+ this.strategiesLoading = !0;
364
+ try {
365
+ this.strategies = await d.fetchRecommendationStrategies();
366
+ } finally {
367
+ this.strategiesLoading = !1;
368
+ }
369
+ })();
370
+ try {
371
+ await S;
372
+ } finally {
373
+ S = null;
374
+ }
375
+ },
376
+ /**
377
+ * Resolves a strategy's recommendation endpoint template via
378
+ * endpoint-generate, cached per strategy id + revision + currency.
379
+ * Failures throw and are NOT cached, so the next attempt retries.
380
+ *
381
+ * `full_path` bakes in the algorithm path and `size=` as they were when
382
+ * generated, so the revision keys the cache — and a record with no
383
+ * revision is not cached at all, since its key could never change.
384
+ * Currency joins the key because it is sent in the request body, so two
385
+ * currencies can resolve to different paths for the same revision.
386
+ */
387
+ async resolveStrategyUrlTemplate(t, e) {
388
+ const r = typeof t.updated_at == "string" ? t.updated_at : "", i = r ? `${t.strategy_id}:${r}:${e}` : "", s = i ? this.strategyUrlTemplates[i] : void 0;
389
+ if (s)
390
+ return s;
391
+ const { full_path: n } = await d.generateStrategyEndpoint(t, e);
392
+ return i && (this.strategyUrlTemplates = {
393
+ ...this.strategyUrlTemplates,
394
+ [i]: n
395
+ }), n;
396
+ },
397
+ /**
398
+ * Assigns a strategy to the current block. The config write is
399
+ * synchronous (before any await) so a block switch cannot misdirect
400
+ * it; the configVersion bump persists it to node config and triggers
401
+ * the product refetch via the coordinator control's subscription.
402
+ */
403
+ assignStrategy(t) {
404
+ this.patchCurrentBlockConfig({
405
+ strategyId: String(t.strategy_id),
406
+ ...t.size !== void 0 ? { size: String(t.size) } : {}
407
+ });
408
+ },
409
+ /**
410
+ * Clears the current block's strategy; it falls back to the per-block
411
+ * algorithm settings.
412
+ *
413
+ * `size` goes back to the default rather than staying on the removed
414
+ * strategy's count: with Number of Products hidden in RRS mode, a block
415
+ * left on that count would have no UI to correct it.
416
+ */
417
+ removeStrategy() {
418
+ this.patchCurrentBlockConfig({ strategyId: "", size: U.size });
419
+ },
420
+ // ====================================================================
318
421
  // Shared Data Fetching (fetched once, used by all blocks)
319
422
  // ====================================================================
320
423
  async fetchRecommendationCreateData() {
321
424
  if (!this.activePredictiveAlgorithms.length) {
322
- if (m) {
323
- await m;
425
+ if (f) {
426
+ await f;
324
427
  return;
325
428
  }
326
- m = (async () => {
429
+ f = (async () => {
327
430
  const {
328
431
  activePredictiveAlgorithms: t,
329
432
  languages: e,
330
433
  currencies: r
331
- } = await h.fetchRecommendationCreateData();
434
+ } = await d.fetchRecommendationCreateData();
332
435
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
333
- const n = this.blockStates[this.currentRecommendationId];
334
- n.filterStatus = !!n.recommendationConfigs.filters.length;
436
+ const i = this.blockStates[this.currentRecommendationId];
437
+ i.filterStatus = !!i.recommendationConfigs.filters.length;
335
438
  }
336
439
  this.currencyList = r;
337
440
  })();
338
441
  try {
339
- await m;
442
+ await f;
340
443
  } finally {
341
- m = null;
444
+ f = null;
342
445
  }
343
446
  }
344
447
  },
345
448
  async fetchRecommendationFilters() {
346
449
  if (!Object.keys(this.filterList).length) {
347
- if (u) {
348
- await u;
450
+ if (g) {
451
+ await g;
349
452
  return;
350
453
  }
351
- u = (async () => {
352
- const t = await h.fetchRecommendationFilters();
454
+ g = (async () => {
455
+ const t = await d.fetchRecommendationFilters();
353
456
  this.filterList = t;
354
457
  })();
355
458
  try {
356
- await u;
459
+ await g;
357
460
  } finally {
358
- u = null;
461
+ g = null;
359
462
  }
360
463
  }
361
464
  },
@@ -370,59 +473,59 @@ const U = () => ({
370
473
  deleteFilterGroup(t) {
371
474
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
372
475
  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]));
374
- e.recommendationConfigs.filters = r.map((i) => ({
375
- ...i,
376
- filterGroup: c.get(i.filterGroup) ?? i.filterGroup
476
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((n) => n.filterGroup !== t), i = [...new Set(r.map((n) => n.filterGroup))].sort((n, o) => n - o), s = new Map(i.map((n, o) => [n, o + 1]));
477
+ e.recommendationConfigs.filters = r.map((n) => ({
478
+ ...n,
479
+ filterGroup: s.get(n.filterGroup) ?? n.filterGroup
377
480
  }));
378
481
  },
379
482
  updateFilter(t) {
380
483
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
381
484
  return;
382
- const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
485
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((i) => i.filterNumber === t.filterNumber && i.filterGroup === t.filterGroup);
383
486
  if (r !== -1) {
384
- const n = [...e.recommendationConfigs.filters];
385
- n[r] = {
487
+ const i = [...e.recommendationConfigs.filters];
488
+ i[r] = {
386
489
  ...t,
387
- isValid: x(t)
388
- }, e.recommendationConfigs.filters = n;
490
+ isValid: A(t)
491
+ }, e.recommendationConfigs.filters = i;
389
492
  }
390
493
  },
391
494
  deleteFilter(t) {
392
495
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
393
496
  return;
394
- const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
497
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((i) => i.filterNumber === t.filterNumber && i.filterGroup === t.filterGroup);
395
498
  if (r !== -1) {
396
- let n = [...e.recommendationConfigs.filters];
397
- if (n.splice(r, 1), n.some((i) => i.filterGroup === t.filterGroup)) {
398
- let i = 1;
399
- n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
499
+ let i = [...e.recommendationConfigs.filters];
500
+ if (i.splice(r, 1), i.some((n) => n.filterGroup === t.filterGroup)) {
501
+ let n = 1;
502
+ i = i.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: n++ } : o);
400
503
  } 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
504
+ const n = [...new Set(i.map((a) => a.filterGroup))].sort((a, c) => a - c), o = new Map(n.map((a, c) => [a, c + 1]));
505
+ i = i.map((a) => ({
506
+ ...a,
507
+ filterGroup: o.get(a.filterGroup) ?? a.filterGroup
405
508
  }));
406
509
  }
407
- e.recommendationConfigs.filters = n;
510
+ e.recommendationConfigs.filters = i;
408
511
  }
409
512
  },
410
513
  addFilter(t) {
411
514
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
412
515
  return;
413
- const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], c = r.filter(
516
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], s = r.filter(
414
517
  (o) => o.filterGroup === t.filterGroup
415
- ).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
416
- i !== -1 ? r.splice(i + 1, 0, {
518
+ ).length + 1, n = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
519
+ n !== -1 ? r.splice(n + 1, 0, {
417
520
  ...t,
418
- filterNumber: c
521
+ filterNumber: s
419
522
  }) : r.push({
420
523
  ...t,
421
- filterNumber: c
524
+ filterNumber: s
422
525
  }), e.recommendationConfigs.filters = r;
423
526
  },
424
527
  generateFilterQuery() {
425
- return b(this.recommendationConfigs.filters);
528
+ return I(this.recommendationConfigs.filters);
426
529
  },
427
530
  /**
428
531
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -430,55 +533,93 @@ const U = () => ({
430
533
  * every block's recommendationConfigs across user edits.
431
534
  */
432
535
  hasInvalidBlock() {
433
- return Object.values(this.blockStates).some((t) => !L(t.recommendationConfigs, this));
536
+ return Object.values(this.blockStates).some((t) => !B(t.recommendationConfigs, this));
434
537
  },
435
538
  // ====================================================================
436
539
  // Per-Block Product Fetching
437
540
  // ====================================================================
438
541
  async fetchRecommendationProducts() {
439
542
  if (!(this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])) {
440
- if (d) {
441
- await d;
543
+ if (h) {
544
+ await h;
442
545
  return;
443
546
  }
444
- d = this._doFetchProducts();
547
+ h = this._doFetchProducts();
445
548
  try {
446
- await d;
549
+ await h;
447
550
  } finally {
448
- d = null;
551
+ h = null;
449
552
  }
450
553
  }
451
554
  },
452
555
  async _doFetchProducts() {
453
556
  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 = {
557
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, i = P(), { isFeatureEnabled: s } = C();
558
+ if (s("reusableRecommendationStrategy") && r.strategyId) {
559
+ await this._doFetchStrategyProducts(t);
560
+ return;
561
+ }
562
+ const n = r.filters.filter((u) => u.isValid), o = I(n), a = ((p = y().find((u) => u.key === r.strategy)) == null ? void 0 : p.path) || "", c = parseInt(r.size) || 6, l = {
455
563
  locale: r.language,
456
564
  currency: r.currencySettings.value,
457
- partnerName: o.partnerName,
565
+ partnerName: i.partnerName,
458
566
  size: r.size,
459
567
  details: !0,
460
- campaignId: o.variationId
568
+ campaignId: i.variationId
461
569
  };
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)
570
+ r.strategy === "manualMerchandising" ? l.productId = x(r.productIds) : r.strategy === "similarViewed" && (l.productId = "{itemId}"), r.strategy === "userBased" && (l.userId = "{user_id}"), o && (l.filter = o), r.shuffleProducts && (l.shuffle = !0), Object.assign(
571
+ l,
572
+ L(i.partnerName, r.strategy)
465
573
  );
466
- let f;
574
+ let m;
467
575
  try {
468
- f = await h.fetchRecommendationProducts(i, a);
576
+ m = await d.fetchRecommendationProducts(a, l);
469
577
  } catch {
470
- f = [];
578
+ m = [];
471
579
  }
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;
580
+ this._setBlockProducts(t, m, c);
581
+ },
582
+ /**
583
+ * Fetches products for a strategy-backed block: listing lookup →
584
+ * endpoint-generate template → fill {locale}/{currency}, drop
585
+ * {user_id} (resolved at send time) → fetch. Any failure (dead
586
+ * strategy, resolution error) falls back to placeholder products.
587
+ */
588
+ async _doFetchStrategyProducts(t) {
589
+ const { recommendationConfigs: e } = this.blockStates[t];
590
+ let r = [], i = parseInt(e.size) || 6;
591
+ try {
592
+ this.strategies.length || await this.fetchStrategies();
593
+ const s = this.strategies.find(
594
+ (n) => String(n.strategy_id) === e.strategyId
595
+ );
596
+ if (s) {
597
+ i = parseInt(String(s.size ?? "")) || i;
598
+ const n = e.currencySettings.value, a = (await this.resolveStrategyUrlTemplate(s, n)).replaceAll("{locale}", e.language).replaceAll("{currency}", n), c = a.indexOf("?"), l = c === -1 ? a : a.slice(0, c), m = (c === -1 ? "" : a.slice(c + 1)).split("&").filter((u) => u && !u.startsWith("user_id=")), p = m.length ? `${l}?${m.join("&")}` : l;
599
+ r = await d.fetchRecommendationProductsByUrl(p);
600
+ }
601
+ } catch {
602
+ r = [];
478
603
  }
604
+ this._setBlockProducts(t, r, i);
605
+ },
606
+ /**
607
+ * Writes products to a block's state, padded/clipped to exactly `size`:
608
+ * in-place content updates compare the DOM element count against
609
+ * products.length, and a longer list makes the renderer extend its slot
610
+ * grid, which resets migrator-applied styles.
611
+ */
612
+ _setBlockProducts(t, e, r) {
613
+ if (!this.blockStates[t])
614
+ return;
615
+ const i = e.length > 0 ? e : b(r);
616
+ this.blockStates[t].recommendationProducts = [
617
+ ...i.slice(0, r),
618
+ ...b(Math.max(0, r - i.length))
619
+ ];
479
620
  }
480
621
  }
481
622
  });
482
623
  export {
483
- H as useRecommendationExtensionStore
624
+ Y as useRecommendationExtensionStore
484
625
  };
@@ -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,