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

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 (88) hide show
  1. package/README.md +6 -5
  2. package/dist/@types/config/schemas.js +7 -6
  3. package/dist/components/Guido.vue.js +6 -6
  4. package/dist/components/Guido.vue2.js +96 -94
  5. package/dist/components/organisms/AutoSaveController.vue2.js +8 -9
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +21 -0
  7. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +106 -0
  8. package/dist/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue.js +19 -0
  9. package/dist/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue2.js +63 -0
  10. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  11. package/dist/components/organisms/header/EditorActions.vue2.js +43 -74
  12. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  13. package/dist/components/organisms/header/LeftSlot.vue2.js +23 -46
  14. package/dist/components/wrappers/WpDrawer.vue.js +7 -9
  15. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  16. package/dist/composables/useCustomInterfaceAppearance.js +52 -38
  17. package/dist/composables/useHtmlCompiler.js +26 -37
  18. package/dist/composables/useHtmlValidator.js +0 -2
  19. package/dist/composables/useRecommendation.js +41 -34
  20. package/dist/composables/useSave.js +41 -84
  21. package/dist/composables/validators/useCouponBlockValidator.js +21 -24
  22. package/dist/composables/validators/useUnsubscribeBlockValidator.js +27 -34
  23. package/dist/config/migrator/recommendation/settingsMapper.js +17 -17
  24. package/dist/config/migrator/recommendationMigrator.js +5 -4
  25. package/dist/enums/academy.js +2 -1
  26. package/dist/enums/date.js +5 -3
  27. package/dist/enums/extensions/recommendationBlock.js +3 -1
  28. package/dist/extensions/Blocks/Recommendation/block.js +26 -21
  29. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +34 -34
  30. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +96 -95
  31. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +238 -185
  32. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +124 -0
  33. package/dist/extensions/Blocks/Recommendation/services/configService.js +54 -36
  34. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +165 -86
  35. package/dist/extensions/Blocks/common-control.js +22 -19
  36. package/dist/guido.css +1 -1
  37. package/dist/services/recommendationApi.js +69 -30
  38. package/dist/src/@types/config/schemas.d.ts +2 -0
  39. package/dist/src/components/Guido.vue.d.ts +1 -17
  40. package/dist/src/components/organisms/{validation/ValidationErrorDrawer.vue.d.ts → extensions/recommendation/AssignStrategyDrawer.vue.d.ts} +1 -0
  41. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue.d.ts +15 -0
  42. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -17
  43. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -17
  44. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -17
  45. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +3 -4
  46. package/dist/src/composables/useConfig.d.ts +2 -0
  47. package/dist/src/composables/useHtmlValidator.d.ts +0 -21
  48. package/dist/src/composables/useRecommendation.d.ts +1 -0
  49. package/dist/src/composables/useSave.d.ts +1 -6
  50. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +0 -3
  51. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +0 -10
  52. package/dist/src/enums/academy.d.ts +1 -0
  53. package/dist/src/enums/date.d.ts +1 -0
  54. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  55. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +2 -6
  56. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  57. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +44 -8
  58. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +16 -0
  59. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +12 -1
  60. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +150 -15
  61. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +1 -0
  62. package/dist/src/extensions/Blocks/common-control.d.ts +4 -1
  63. package/dist/src/mock/api/recommendation-strategies.d.ts +2 -0
  64. package/dist/src/services/recommendationApi.d.ts +4 -1
  65. package/dist/src/stores/config.d.ts +18 -5
  66. package/dist/src/stores/save-as-template.d.ts +4 -3
  67. package/dist/src/utils/genericUtil.d.ts +5 -0
  68. package/dist/src/utils/templatePreparation.d.ts +0 -30
  69. package/dist/static/assets/icons/line-change.svg.js +5 -0
  70. package/dist/static/assets/icons/line-delete.svg.js +5 -0
  71. package/dist/static/assets/icons/line-plus.svg.js +5 -0
  72. package/dist/static/styles/components/recommendation-strategy.css.js +155 -0
  73. package/dist/stores/config.js +6 -13
  74. package/dist/utils/dateUtil.js +23 -10
  75. package/dist/utils/genericUtil.js +28 -27
  76. package/dist/utils/templatePreparation.js +48 -50
  77. package/package.json +3 -2
  78. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +0 -20
  79. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +0 -48
  80. package/dist/composables/useSaveValidation.js +0 -279
  81. package/dist/composables/useSaveValidationRollout.js +0 -9
  82. package/dist/enums/save-validation.js +0 -4
  83. package/dist/src/@types/save-validation.d.ts +0 -19
  84. package/dist/src/composables/useSaveValidation.d.ts +0 -6
  85. package/dist/src/composables/useSaveValidationRollout.d.ts +0 -3
  86. package/dist/src/enums/save-validation.d.ts +0 -27
  87. package/dist/src/stores/save-validation.d.ts +0 -22
  88. package/dist/stores/save-validation.js +0 -30
@@ -1,53 +1,64 @@
1
- var f = Object.defineProperty;
2
- var p = (d, l, t) => l in d ? f(d, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : d[l] = t;
3
- var s = (d, l, t) => p(d, typeof l != "symbol" ? l + "" : l, t);
4
- import { CommonControl as g } from "../../../common-control.js";
5
- import { DEFAULT_NODE_CONFIG as c } from "../../constants/defaultConfig.js";
6
- import { RecommendationConfigService as h } from "../../services/configService.js";
7
- import { useRecommendationExtensionStore as y } from "../../store/recommendation.js";
8
- import { AlgorithmControl as N } from "./algorithm.js";
9
- import { ALGORITHM_CONTROL_ID as tt } from "./algorithm.js";
10
- import { CurrencyControl as R } from "./currency.js";
11
- import { CURRENCY_CONTROL_ID as ot } from "./currency.js";
12
- import { FiltersControl as I } from "./filters.js";
13
- import { FILTERS_CONTROL_ID as nt } from "./filters.js";
14
- import { LayoutOrientationControl as _ } from "./layoutOrientation.js";
15
- import { LAYOUT_ORIENTATION_CONTROL_ID as st } from "./layoutOrientation.js";
16
- import { LocaleControl as b } from "./locale.js";
17
- import { LOCALE_CONTROL_ID as lt } from "./locale.js";
18
- import { PricePlacementControl as T } from "./pricePlacement.js";
19
- import { PRICE_PLACEMENT_CONTROL_ID as ut } from "./pricePlacement.js";
20
- import { ProductCountControl as O } from "./productCount.js";
21
- import { PRODUCT_COUNT_CONTROL_ID as ht } from "./productCount.js";
22
- import { ProductLayoutControl as P } from "./productLayout.js";
23
- import { PRODUCT_LAYOUT_CONTROL_ID as Ct } from "./productLayout.js";
24
- import { ShuffleControl as S } from "./shuffle.js";
25
- import { SHUFFLE_CONTROL_ID as pt } from "./shuffle.js";
26
- import { setCurrencyAttributes as L, getBlockElement as k, isPartnerManagedBlock as D, updateProductContentInPlace as E, regenerateProductRowsWithStyles as A } from "./utils.js";
27
- import { adjustProductsToSize as yt, formatProductPrice as Nt, getCardComposition as Rt, getCurrentLayout as It, regenerateMobileProductRows as _t, updatePricesInPlace as bt, updateSingleProductContent as Tt } from "./utils.js";
28
- import { useDebounceFn as m } from "../../../../../node_modules/@vueuse/shared/index.js";
29
- const v = "recommendation-id", w = "ui-elements-recommendation-block";
30
- class Q extends g {
1
+ var m = Object.defineProperty;
2
+ var C = (h, l, t) => l in h ? m(h, l, { enumerable: !0, configurable: !0, writable: !0, value: t }) : h[l] = t;
3
+ var r = (h, l, t) => C(h, typeof l != "symbol" ? l + "" : l, t);
4
+ import { useConfig as p } from "../../../../../composables/useConfig.js";
5
+ import { ModificationDescription as y } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
+ import { CommonControl as N } from "../../../common-control.js";
7
+ import { CURRENT_CONFIG_VERSION as _, DEFAULT_NODE_CONFIG as a } from "../../constants/defaultConfig.js";
8
+ import { RecommendationConfigService as c } from "../../services/configService.js";
9
+ import { useRecommendationExtensionStore as I } from "../../store/recommendation.js";
10
+ import { AlgorithmControl as R } from "./algorithm.js";
11
+ import { ALGORITHM_CONTROL_ID as at } from "./algorithm.js";
12
+ import { CurrencyControl as b } from "./currency.js";
13
+ import { CURRENCY_CONTROL_ID as ct } from "./currency.js";
14
+ import { FiltersControl as T } from "./filters.js";
15
+ import { FILTERS_CONTROL_ID as dt } from "./filters.js";
16
+ import { LayoutOrientationControl as P } from "./layoutOrientation.js";
17
+ import { LAYOUT_ORIENTATION_CONTROL_ID as ft } from "./layoutOrientation.js";
18
+ import { LocaleControl as S } from "./locale.js";
19
+ import { LOCALE_CONTROL_ID as mt } from "./locale.js";
20
+ import { PricePlacementControl as O } from "./pricePlacement.js";
21
+ import { PRICE_PLACEMENT_CONTROL_ID as pt } from "./pricePlacement.js";
22
+ import { ProductCountControl as L } from "./productCount.js";
23
+ import { PRODUCT_COUNT_CONTROL_ID as Nt } from "./productCount.js";
24
+ import { ProductLayoutControl as E } from "./productLayout.js";
25
+ import { PRODUCT_LAYOUT_CONTROL_ID as It } from "./productLayout.js";
26
+ import { ShuffleControl as k } from "./shuffle.js";
27
+ import { SHUFFLE_CONTROL_ID as bt } from "./shuffle.js";
28
+ import { StrategyControl as D } from "./strategy.js";
29
+ import { STRATEGY_CONTROL_ID as Pt } from "./strategy.js";
30
+ import { setCurrencyAttributes as F, getBlockElement as w, isPartnerManagedBlock as A, updateProductContentInPlace as v, regenerateProductRowsWithStyles as x } from "./utils.js";
31
+ import { adjustProductsToSize as Ot, formatProductPrice as Lt, getCardComposition as Et, getCurrentLayout as kt, regenerateMobileProductRows as Dt, updatePricesInPlace as Ft, updateSingleProductContent as wt } from "./utils.js";
32
+ import { useDebounceFn as f } from "../../../../../node_modules/@vueuse/shared/index.js";
33
+ const B = "recommendation-id", U = "ui-elements-recommendation-block";
34
+ class rt extends N {
31
35
  constructor() {
32
36
  super(...arguments);
33
- s(this, "store", y());
34
- s(this, "storeUnsubscription", () => {
37
+ r(this, "store", I());
38
+ r(this, "storeUnsubscription", () => {
35
39
  });
36
- s(this, "syncedBlockIds", /* @__PURE__ */ new Set());
40
+ // blockId -> fingerprint of the node config as it looked at the last sync.
41
+ r(this, "syncedConfigs", /* @__PURE__ */ new Map());
42
+ // A config change waiting to reach the node config. Deferred so it can ride the
43
+ // re-render's single apply() — one user action must cost one undo press.
44
+ r(this, "pendingNodeConfigWrite", null);
45
+ r(this, "pendingConfigFlushTimer", null);
37
46
  // Sub-control instances for lifecycle management
38
- s(this, "algorithmControl", null);
39
- s(this, "localeControl", null);
40
- s(this, "currencyControl", null);
41
- s(this, "productCountControl", null);
42
- s(this, "productLayoutControl", null);
43
- s(this, "filtersControl", null);
44
- s(this, "shuffleControl", null);
45
- s(this, "pricePlacementControl", null);
46
- s(this, "layoutOrientationControl", null);
47
+ r(this, "algorithmControl", null);
48
+ r(this, "localeControl", null);
49
+ r(this, "currencyControl", null);
50
+ r(this, "productCountControl", null);
51
+ r(this, "productLayoutControl", null);
52
+ r(this, "filtersControl", null);
53
+ r(this, "shuffleControl", null);
54
+ r(this, "pricePlacementControl", null);
55
+ r(this, "layoutOrientationControl", null);
56
+ r(this, "strategyControl", null);
57
+ r(this, "rrsEnabled", !1);
47
58
  /**
48
59
  * Debounced product fetch to prevent rapid API calls during config changes
49
60
  */
50
- s(this, "_debouncedFetchProducts", m(() => {
61
+ r(this, "_debouncedFetchProducts", f(() => {
51
62
  this.store.fetchRecommendationProducts();
52
63
  }, 500));
53
64
  /**
@@ -61,16 +72,20 @@ class Q extends g {
61
72
  * count. The store pads products to the configured size, so in-place only
62
73
  * fails when the size actually changed.
63
74
  */
64
- s(this, "_debouncedRegenerateWithProducts", m(() => {
75
+ r(this, "_debouncedRegenerateWithProducts", f(() => {
65
76
  const t = this.store.recommendationProducts;
66
- if (!this.currentNode || !this.api || D(this.currentNode))
77
+ if (!this.currentNode || !this.api)
67
78
  return;
68
- const e = this.api.getDocumentModifier();
69
- E({
79
+ const e = this.api.getDocumentModifier(), o = this._stagePendingNodeConfig(e);
80
+ if (A(this.currentNode)) {
81
+ o && (e.apply(new y("Update recommendation configuration")), this.pendingNodeConfigWrite = null, this._clearConfigFlush());
82
+ return;
83
+ }
84
+ v({
70
85
  currentNode: this.currentNode,
71
86
  documentModifier: e,
72
87
  products: t
73
- }) || A({
88
+ }) || x({
74
89
  currentNode: this.currentNode,
75
90
  documentModifier: e,
76
91
  products: t
@@ -78,23 +93,32 @@ class Q extends g {
78
93
  }, 100));
79
94
  }
80
95
  getId() {
81
- return w;
96
+ return U;
82
97
  }
83
- getTemplate() {
84
- return this.algorithmControl = new N(), this.localeControl = new b(), this.currencyControl = new R(), this.productCountControl = new O(), this.productLayoutControl = new P(), this.filtersControl = new I(), this.shuffleControl = new S(), this.pricePlacementControl = new T(), this.layoutOrientationControl = new _(), [
98
+ get _subControls() {
99
+ return [
100
+ this.layoutOrientationControl,
101
+ this.productCountControl,
85
102
  this.algorithmControl,
86
103
  this.localeControl,
87
104
  this.currencyControl,
88
- this.productCountControl,
89
105
  this.productLayoutControl,
90
106
  this.filtersControl,
91
107
  this.shuffleControl,
92
108
  this.pricePlacementControl,
93
- this.layoutOrientationControl
94
- ].forEach((e) => {
95
- e.api = this.api;
109
+ this.strategyControl
110
+ ];
111
+ }
112
+ getTemplate() {
113
+ var t;
114
+ return this.rrsEnabled = p().isFeatureEnabled("reusableRecommendationStrategy"), this._subControls.forEach((e) => {
115
+ var o;
116
+ return (o = e == null ? void 0 : e.onDestroy) == null ? void 0 : o.call(e);
117
+ }), this.algorithmControl = new R(), this.localeControl = new S(), this.currencyControl = new b(), this.productCountControl = new L(), this.productLayoutControl = new E(), this.filtersControl = new T(), this.shuffleControl = new k(), this.pricePlacementControl = new O(), this.layoutOrientationControl = new P(), this.strategyControl = this.rrsEnabled ? new D() : null, this._subControls.forEach((e) => {
118
+ e && (e.api = this.api);
96
119
  }), `
97
120
  <div class="recommendation-controls-container">
121
+ ${((t = this.strategyControl) == null ? void 0 : t.getTemplate()) ?? ""}
98
122
  ${this.layoutOrientationControl.getTemplate()}
99
123
  ${this.productCountControl.getTemplate()}
100
124
  ${this.productLayoutControl.getTemplate()}
@@ -107,50 +131,38 @@ class Q extends g {
107
131
  </div>
108
132
  `;
109
133
  }
134
+ _showControlsForBlock(t) {
135
+ var i;
136
+ const e = (i = this.getContainer()) == null ? void 0 : i.querySelector(".recommendation-controls-container");
137
+ if (!e)
138
+ return;
139
+ const o = this._getRecommendationIdFromNode(t) === null ? null : c.getConfig(t), n = this.rrsEnabled && !!o && (!!(o != null && o.strategyId) || ((o == null ? void 0 : o.configVersion) ?? 0) >= _);
140
+ e.classList.toggle("strategy-block", n);
141
+ }
110
142
  async onRender() {
111
143
  var o;
112
144
  const t = this._getRecommendationIdFromNode(this.currentNode), e = t ?? this.store.currentRecommendationId;
113
- if (e !== null && this.store.setCurrentBlock(e), t !== null && !this.syncedBlockIds.has(t) && (this.syncedBlockIds.add(t), this._syncNodeConfigToStore()), this._listenStateUpdates(), e !== null && ((o = this.store.blockStates[e]) != null && o.isInitialized)) {
145
+ if (e !== null && this.store.setCurrentBlock(e), t !== null && this._syncNodeConfigIfChanged(t), this._listenStateUpdates(), this._showControlsForBlock(this.currentNode), e !== null && ((o = this.store.blockStates[e]) != null && o.isInitialized)) {
114
146
  this._initializeSubControls();
115
147
  return;
116
148
  }
117
149
  await this._fetchBlockData(e), this._initializeSubControls();
118
150
  }
119
151
  onTemplateNodeUpdated(t) {
120
- var r;
152
+ var o;
121
153
  super.onTemplateNodeUpdated(t);
122
154
  const e = this._getRecommendationIdFromNode(t);
123
- e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && !this.syncedBlockIds.has(e) && (this.syncedBlockIds.add(e), this._syncNodeConfigToStore()), e !== null && !((r = this.store.blockStates[e]) != null && r.isInitialized) && this._fetchBlockData(e).then(() => {
155
+ e !== null && e !== this.store.currentRecommendationId && this.store.setCurrentBlock(e), e !== null && this._syncNodeConfigIfChanged(e), this._showControlsForBlock(t), e !== null && !((o = this.store.blockStates[e]) != null && o.isInitialized) && this._fetchBlockData(e).then(() => {
124
156
  this._initializeSubControls();
125
- }), [
126
- this.layoutOrientationControl,
127
- this.productCountControl,
128
- this.algorithmControl,
129
- this.localeControl,
130
- this.currencyControl,
131
- this.productLayoutControl,
132
- this.filtersControl,
133
- this.shuffleControl,
134
- this.pricePlacementControl
135
- ].forEach((n) => {
157
+ }), this._subControls.forEach((n) => {
136
158
  var i;
137
159
  n != null && n.api && (n.currentNode = t, (i = n.onTemplateNodeUpdated) == null || i.call(n, t));
138
160
  });
139
161
  }
140
162
  onDestroy() {
141
- this.storeUnsubscription(), [
142
- this.layoutOrientationControl,
143
- this.productCountControl,
144
- this.algorithmControl,
145
- this.localeControl,
146
- this.currencyControl,
147
- this.productLayoutControl,
148
- this.filtersControl,
149
- this.shuffleControl,
150
- this.pricePlacementControl
151
- ].forEach((e) => {
152
- var o;
153
- return (o = e == null ? void 0 : e.onDestroy) == null ? void 0 : o.call(e);
163
+ this.storeUnsubscription(), this._flushPendingNodeConfig(), this._subControls.forEach((t) => {
164
+ var e;
165
+ return (e = t == null ? void 0 : t.onDestroy) == null ? void 0 : e.call(t);
154
166
  });
155
167
  }
156
168
  /**
@@ -158,25 +170,14 @@ class Q extends g {
158
170
  * Each sub-control manages its own form values and event listeners
159
171
  */
160
172
  _initializeSubControls() {
161
- [
162
- this.layoutOrientationControl,
163
- this.productCountControl,
164
- this.algorithmControl,
165
- this.localeControl,
166
- this.currencyControl,
167
- this.productLayoutControl,
168
- this.filtersControl,
169
- this.shuffleControl,
170
- this.pricePlacementControl
171
- ].forEach((e) => {
172
- var o;
173
- e && (e.api = this.api, e.currentNode = this.currentNode, (o = e.onRender) == null || o.call(e));
173
+ this._subControls.forEach((t) => {
174
+ var e;
175
+ t && (t.api = this.api, t.currentNode = this.currentNode, (e = t.onRender) == null || e.call(t));
174
176
  });
175
177
  }
176
178
  /**
177
179
  * Syncs persisted node config into the Pinia store's block state.
178
180
  *
179
- * setCurrentBlock() creates a default entry (USD, en_US, mostPopular).
180
181
  * For saved templates, the real config lives in the node (e.g., TRY, tr_TR).
181
182
  * This method reads it and patches the store so fetchRecommendationProducts()
182
183
  * uses the correct values.
@@ -184,11 +185,76 @@ class Q extends g {
184
185
  * Uses triggerRefetch: false because the fetch hasn't happened yet —
185
186
  * values are being prepared for the upcoming initial fetch.
186
187
  */
188
+ /**
189
+ * Re-syncs the store when the node config moved since we last read it.
190
+ *
191
+ * Undo/redo, the code editor and version-history restore rewrite the node
192
+ * config behind the store's back — Stripo exposes no undo signal, so a
193
+ * fingerprint diff on this hook is the only way to notice (SD-149105).
194
+ * An unchanged fingerprint means the move came from us, so runtime store
195
+ * edits are never clobbered (SD-141049).
196
+ */
197
+ _syncNodeConfigIfChanged(t) {
198
+ this.pendingNodeConfigWrite && this.pendingNodeConfigWrite.blockId !== t && this._flushPendingNodeConfig();
199
+ const e = JSON.stringify(c.getConfig(this.currentNode)), o = this.syncedConfigs.get(t);
200
+ this.syncedConfigs.set(t, e), o !== e && (this.pendingNodeConfigWrite || this._syncNodeConfigToStore());
201
+ }
202
+ _capturePendingNodeConfigWrite() {
203
+ const t = this.store.currentRecommendationId;
204
+ if (!this.currentNode || !this.api || t === null)
205
+ return;
206
+ const { filters: e, strategyId: o, size: n } = this.store.recommendationConfigs;
207
+ this.pendingNodeConfigWrite = {
208
+ blockId: t,
209
+ node: this.currentNode,
210
+ updates: { filters: e, strategyId: o, size: n }
211
+ };
212
+ }
213
+ /**
214
+ * Accumulates the pending write into `documentModifier` without applying, so the
215
+ * caller's apply() commits config and re-render as ONE undo entry.
216
+ *
217
+ * Deliberately does not consume the pending record: the product helpers call
218
+ * apply() only when they actually changed something, so this batch may never be
219
+ * committed and the flush has to stay able to write it standalone.
220
+ * @param documentModifier - The batch to accumulate into
221
+ * @returns Whether anything was staged
222
+ */
223
+ _stagePendingNodeConfig(t) {
224
+ const e = this.pendingNodeConfigWrite;
225
+ return !e || !this.currentNode ? !1 : (c.stageConfig(t, this.currentNode, e.updates), !0);
226
+ }
227
+ /**
228
+ * Arms the guaranteed fallback write. The delay has to outlast the product fetch
229
+ * and the re-render debounce it triggers — firing earlier writes the config on
230
+ * its own and the undo entry splits in two again.
231
+ */
232
+ _scheduleConfigFlush() {
233
+ this._clearConfigFlush(), this.pendingConfigFlushTimer = setTimeout(() => {
234
+ this.pendingConfigFlushTimer = null, this._flushPendingNodeConfig();
235
+ }, 800);
236
+ }
237
+ _clearConfigFlush() {
238
+ this.pendingConfigFlushTimer !== null && (clearTimeout(this.pendingConfigFlushTimer), this.pendingConfigFlushTimer = null);
239
+ }
240
+ /**
241
+ * Writes the deferred change standalone when the batch it was staged into never
242
+ * committed. Always targets the LIVE node — the captured one is an immutable
243
+ * snapshot the intervening re-render staled, and saveConfig swallows that failure.
244
+ */
245
+ _flushPendingNodeConfig() {
246
+ const t = this.pendingNodeConfigWrite;
247
+ if (this.pendingNodeConfigWrite = null, this._clearConfigFlush(), !t || !this.api)
248
+ return;
249
+ const e = t.blockId === this.store.currentRecommendationId && this.currentNode ? this.currentNode : t.node, o = c.getConfig(e);
250
+ JSON.stringify(o.filters) === JSON.stringify(t.updates.filters) && o.strategyId === t.updates.strategyId && o.size === t.updates.size || c.updateConfig(this.api, e, t.updates, "Update recommendation filters");
251
+ }
187
252
  _syncNodeConfigToStore() {
188
- var r;
189
- const t = h.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((r = this.store.blockStates[e]) == null ? void 0 : r.isInitialized);
253
+ var n;
254
+ const t = c.getConfig(this.currentNode), e = this.store.currentRecommendationId, o = e !== null && ((n = this.store.blockStates[e]) == null ? void 0 : n.isInitialized);
190
255
  this.store.patchCurrentBlockConfig({
191
256
  strategy: t.strategy,
257
+ strategyId: t.strategyId,
192
258
  language: t.language,
193
259
  size: t.size,
194
260
  productIds: t.productIds,
@@ -227,8 +293,8 @@ class Q extends g {
227
293
  t !== null && this.store.markBlockInitialized(t), (await Promise.allSettled([
228
294
  this.store.fetchRecommendationCreateData(),
229
295
  this.store.fetchRecommendationFilters()
230
- ])).forEach((o, r) => {
231
- o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][r]} failed`, o.reason);
296
+ ])).forEach((o, n) => {
297
+ o.status === "rejected" && console.warn(`Recommendation block: ${["fetchRecommendationCreateData", "fetchRecommendationFilters"][n]} failed`, o.reason);
232
298
  }), this._applySmartDefaults();
233
299
  try {
234
300
  await this.store.fetchRecommendationProducts();
@@ -249,36 +315,36 @@ class Q extends g {
249
315
  _applySmartDefaults() {
250
316
  if (!this.currentNode || !this.api)
251
317
  return;
252
- const t = h.getConfig(this.currentNode), e = {};
253
- let o = null, r = null, n = null;
254
- if (t.currency.code === c.currency.code) {
255
- const { currencyList: i } = this.store;
256
- i.length > 0 && (i.some(
257
- (u) => u.value === `price.${c.currency.code}`
258
- ) || (o = i[0].value.replace("price.", ""), e.currency = {
259
- ...c.currency,
318
+ const t = c.getConfig(this.currentNode), e = {};
319
+ let o = null, n = null, i = null;
320
+ if (t.currency.code === a.currency.code) {
321
+ const { currencyList: s } = this.store;
322
+ s.length > 0 && (s.some(
323
+ (d) => d.value === `price.${a.currency.code}`
324
+ ) || (o = s[0].value.replace("price.", ""), e.currency = {
325
+ ...a.currency,
260
326
  code: o,
261
327
  symbol: o
262
328
  }));
263
329
  }
264
- if (t.strategy === c.strategy) {
265
- const i = this.store.getActivePredictiveAlgorithms;
266
- i.length > 0 && (i.some(
267
- (u) => u.value === c.strategy
268
- ) || (r = i[0].value, e.strategy = r));
330
+ if (t.strategy === a.strategy) {
331
+ const s = this.store.getActivePredictiveAlgorithms;
332
+ s.length > 0 && (s.some(
333
+ (d) => d.value === a.strategy
334
+ ) || (n = s[0].value, e.strategy = n));
269
335
  }
270
- if (t.language === c.language) {
271
- const i = this.store.getLanguages;
272
- i.length > 0 && (i.some(
273
- (u) => u.value === c.language
274
- ) || (n = i[0].value, e.language = n));
336
+ if (t.language === a.language) {
337
+ const s = this.store.getLanguages;
338
+ s.length > 0 && (s.some(
339
+ (d) => d.value === a.language
340
+ ) || (i = s[0].value, e.language = i));
275
341
  }
276
- !o && !r && !n || (h.updateConfig(
342
+ !o && !n && !i || (c.updateConfig(
277
343
  this.api,
278
344
  this.currentNode,
279
345
  e,
280
346
  "Applied smart defaults"
281
- ), o && e.currency && L({
347
+ ), o && e.currency && F({
282
348
  currentNode: this.currentNode,
283
349
  documentModifier: this.api.getDocumentModifier(),
284
350
  currency: e.currency
@@ -288,28 +354,28 @@ class Q extends g {
288
354
  name: o,
289
355
  value: o,
290
356
  symbol: o,
291
- alignment: c.currency.alignment === "before" ? "0" : "1",
292
- decimalCount: c.currency.decimalCount.toString(),
293
- decimalSeparator: c.currency.decimalSeparator,
294
- thousandSeparator: c.currency.thousandSeparator
357
+ alignment: a.currency.alignment === "before" ? "0" : "1",
358
+ decimalCount: a.currency.decimalCount.toString(),
359
+ decimalSeparator: a.currency.decimalSeparator,
360
+ thousandSeparator: a.currency.thousandSeparator
295
361
  }
296
362
  } : {},
297
- ...r ? { strategy: r } : {},
298
- ...n ? { language: n } : {}
363
+ ...n ? { strategy: n } : {},
364
+ ...i ? { language: i } : {}
299
365
  }, { triggerRefetch: !1 }));
300
366
  }
301
367
  /**
302
368
  * Reads the recommendation-id attribute from the block element within the node
303
369
  */
304
370
  _getRecommendationIdFromNode(t) {
305
- const e = k(t);
371
+ const e = w(t);
306
372
  if (!e || !("getAttribute" in e))
307
373
  return null;
308
- const o = e.getAttribute(v);
374
+ const o = e.getAttribute(B);
309
375
  if (!o)
310
376
  return null;
311
- const r = parseInt(o);
312
- return Number.isNaN(r) ? null : r;
377
+ const n = parseInt(o);
378
+ return Number.isNaN(n) ? null : n;
313
379
  }
314
380
  /**
315
381
  * Listen to store changes that require product refresh or regeneration.
@@ -326,66 +392,53 @@ class Q extends g {
326
392
  */
327
393
  _listenStateUpdates() {
328
394
  const { store: t } = this;
329
- let e = t.recommendationProducts, o = t.$state.configVersion, r = t.currentRecommendationId;
395
+ let e = t.recommendationProducts, o = t.$state.configVersion, n = t.currentRecommendationId;
330
396
  this.storeUnsubscription = t.$subscribe(() => {
331
- const n = t.currentRecommendationId;
332
- if (n !== r) {
333
- r = n, e = t.recommendationProducts, o = t.$state.configVersion;
397
+ const i = t.currentRecommendationId;
398
+ if (i !== n) {
399
+ n = i, e = t.recommendationProducts, o = t.$state.configVersion;
334
400
  return;
335
401
  }
336
- const i = t.$state.configVersion;
337
- i !== o && (o = i, this._persistFiltersToNodeConfig(), this._debouncedFetchProducts());
338
- const a = t.recommendationProducts, u = a !== e, C = Array.isArray(a) && a.length > 0;
339
- u && C && (e = a, this._debouncedRegenerateWithProducts());
402
+ const s = t.$state.configVersion;
403
+ s !== o && (o = s, this._capturePendingNodeConfigWrite(), this._scheduleConfigFlush(), this._debouncedFetchProducts());
404
+ const u = t.recommendationProducts, d = u !== e, g = Array.isArray(u) && u.length > 0;
405
+ d && g && (e = u, this._debouncedRegenerateWithProducts());
340
406
  });
341
407
  }
342
- /**
343
- * Persists the current filter state from Pinia store to the Stripo node config.
344
- * This ensures filters survive template save/reload cycles.
345
- */
346
- _persistFiltersToNodeConfig() {
347
- if (!this.currentNode || !this.api)
348
- return;
349
- const { filters: t } = this.store.recommendationConfigs, e = h.getConfig(this.currentNode).filters;
350
- JSON.stringify(e) !== JSON.stringify(t) && h.updateConfig(
351
- this.api,
352
- this.currentNode,
353
- { filters: t },
354
- "Update recommendation filters"
355
- );
356
- }
357
408
  }
358
409
  export {
359
- tt as ALGORITHM_CONTROL_ID,
360
- N as AlgorithmControl,
361
- w as CONTROL_BLOCK_ID,
362
- ot as CURRENCY_CONTROL_ID,
363
- R as CurrencyControl,
364
- nt as FILTERS_CONTROL_ID,
365
- I as FiltersControl,
366
- st as LAYOUT_ORIENTATION_CONTROL_ID,
367
- lt as LOCALE_CONTROL_ID,
368
- _ as LayoutOrientationControl,
369
- b as LocaleControl,
370
- ut as PRICE_PLACEMENT_CONTROL_ID,
371
- ht as PRODUCT_COUNT_CONTROL_ID,
372
- Ct as PRODUCT_LAYOUT_CONTROL_ID,
373
- T as PricePlacementControl,
374
- O as ProductCountControl,
375
- P as ProductLayoutControl,
376
- Q as RecommendationBlockControl,
377
- pt as SHUFFLE_CONTROL_ID,
378
- S as ShuffleControl,
379
- yt as adjustProductsToSize,
380
- Nt as formatProductPrice,
381
- k as getBlockElement,
382
- Rt as getCardComposition,
383
- It as getCurrentLayout,
384
- D as isPartnerManagedBlock,
385
- _t as regenerateMobileProductRows,
386
- A as regenerateProductRowsWithStyles,
387
- L as setCurrencyAttributes,
388
- bt as updatePricesInPlace,
389
- E as updateProductContentInPlace,
390
- Tt as updateSingleProductContent
410
+ at as ALGORITHM_CONTROL_ID,
411
+ R as AlgorithmControl,
412
+ U as CONTROL_BLOCK_ID,
413
+ ct as CURRENCY_CONTROL_ID,
414
+ b as CurrencyControl,
415
+ dt as FILTERS_CONTROL_ID,
416
+ T as FiltersControl,
417
+ ft as LAYOUT_ORIENTATION_CONTROL_ID,
418
+ mt as LOCALE_CONTROL_ID,
419
+ P as LayoutOrientationControl,
420
+ S as LocaleControl,
421
+ pt as PRICE_PLACEMENT_CONTROL_ID,
422
+ Nt as PRODUCT_COUNT_CONTROL_ID,
423
+ It as PRODUCT_LAYOUT_CONTROL_ID,
424
+ O as PricePlacementControl,
425
+ L as ProductCountControl,
426
+ E as ProductLayoutControl,
427
+ rt as RecommendationBlockControl,
428
+ bt as SHUFFLE_CONTROL_ID,
429
+ Pt as STRATEGY_CONTROL_ID,
430
+ k as ShuffleControl,
431
+ D as StrategyControl,
432
+ Ot as adjustProductsToSize,
433
+ Lt as formatProductPrice,
434
+ w as getBlockElement,
435
+ Et as getCardComposition,
436
+ kt as getCurrentLayout,
437
+ A as isPartnerManagedBlock,
438
+ Dt as regenerateMobileProductRows,
439
+ x as regenerateProductRowsWithStyles,
440
+ F as setCurrencyAttributes,
441
+ Ft as updatePricesInPlace,
442
+ v as updateProductContentInPlace,
443
+ wt as updateSingleProductContent
391
444
  };