@useinsider/guido 3.18.2-beta.d3f87c7 → 3.18.2-beta.dfce34a

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.
@@ -15,7 +15,7 @@ var a = function() {
15
15
  n,
16
16
  !1,
17
17
  null,
18
- "521cf6b3"
18
+ "184392c2"
19
19
  );
20
20
  const y = i.exports;
21
21
  export {
@@ -1,47 +1,47 @@
1
- import { defineComponent as A, defineAsyncComponent as S, ref as T, computed as g, watch as h } from "vue";
1
+ import { defineComponent as A, defineAsyncComponent as S, ref as v, computed as l, watch as h } from "vue";
2
2
  import "@useinsider/design-system-organisms/style";
3
3
  import { useToaster as C } from "../../../../composables/useToaster.js";
4
4
  import { useTranslations as b } from "../../../../composables/useTranslations.js";
5
5
  import { DOTTED_DATE_LOCALE as B } from "../../../../enums/date.js";
6
6
  import { URLS as k } from "../../../../enums/extensions/recommendationBlock.js";
7
- import { ToasterTypeOptions as d } from "../../../../enums/toaster.js";
7
+ import { ToasterTypeOptions as g } from "../../../../enums/toaster.js";
8
8
  import { useRecommendationExtensionStore as R } from "../../../../extensions/Blocks/Recommendation/store/recommendation.js";
9
- import { commitStrategyChange as _ } from "../../../../extensions/Blocks/Recommendation/utils/strategyCommit.js";
9
+ import { commitStrategyChange as T } from "../../../../extensions/Blocks/Recommendation/utils/strategyCommit.js";
10
10
  import N from "./StrategyInfoBoxContent.vue.js";
11
11
  import { formatShortDate as P } from "../../../../utils/dateUtil.js";
12
- import { asText as v } from "../../../../utils/genericUtil.js";
12
+ import { asText as _ } from "../../../../utils/genericUtil.js";
13
13
  import { escapeHtml as U } from "../../../../utils/htmlEscape.js";
14
14
  import { InSkeleton as L } from "@useinsider/design-system-vue";
15
15
  const rt = /* @__PURE__ */ A({
16
16
  __name: "AssignStrategyDrawer",
17
17
  setup(M) {
18
18
  const m = () => import("@useinsider/design-system-organisms"), u = (t, r) => ({
19
- render: (a) => a(L, { props: { sizing: { width: t, height: r } } })
19
+ render: (s) => s(L, { props: { sizing: { width: t, height: r } } })
20
20
  }), w = S({
21
21
  loader: async () => (await m()).OrgStrategyDetailModal,
22
22
  loadingComponent: u(480, 240),
23
23
  delay: 0
24
24
  }), D = S({
25
25
  loader: async () => (await m()).OrgStrategyDropdown
26
- }), n = "guido-edit-strategy-", o = b(), e = R(), { showToaster: i } = C(), s = T(null), c = T(!1), I = g(() => e.assignStrategyDrawerStatus ? o("email-editor.assign-strategy-description", { url: k.RECOMMENDATION_STRATEGIES_PANEL_PATH }) : "");
26
+ }), i = "guido-edit-strategy-", o = b(), e = R(), { showToaster: n } = C(), a = v(null), c = v(!1), E = l(() => e.assignStrategyDrawerStatus ? o("email-editor.assign-strategy-description", { url: k.RECOMMENDATION_STRATEGIES_PANEL_PATH }) : "");
27
27
  h(() => e.assignStrategyDrawerStatus, (t) => {
28
28
  if (!t)
29
29
  return;
30
30
  const { strategyId: r } = e.recommendationConfigs;
31
- s.value = r || null, c.value = !1, e.fetchStrategies().catch(() => {
32
- i({
33
- type: d.Error,
31
+ a.value = r || null, c.value = !1, e.invalidateStrategies(), e.fetchStrategies().catch(() => {
32
+ n({
33
+ type: g.Error,
34
34
  message: o("discovery-strategy.error-description")
35
35
  });
36
36
  });
37
37
  });
38
38
  const y = (t) => {
39
- const r = v(t.updated_at || t.created_at);
39
+ const r = _(t.updated_at || t.created_at);
40
40
  if (!r)
41
41
  return "";
42
- const a = P(r, B);
43
- return o("recommendation-strategy.last-updated-on", { value: a });
44
- }, x = g(() => e.strategies.map((t) => ({
42
+ const s = P(r, B);
43
+ return o("recommendation-strategy.last-updated-on", { value: s });
44
+ }, I = l(() => e.strategies.map((t) => ({
45
45
  text: t.name,
46
46
  value: t.strategy_id,
47
47
  infoBoxProps: {
@@ -49,7 +49,7 @@ const rt = /* @__PURE__ */ A({
49
49
  theme: "light",
50
50
  variant: "information",
51
51
  className: "assign-strategy-info-box",
52
- titleText: U(v(t.name)),
52
+ titleText: U(_(t.name)),
53
53
  descriptionStatus: !0,
54
54
  descriptionText: y(t),
55
55
  slot: N,
@@ -57,7 +57,7 @@ const rt = /* @__PURE__ */ A({
57
57
  // The info box drops the option's value/text, so the id is the only channel
58
58
  // that can carry the strategy back to the click handler.
59
59
  buttonOptions: [{
60
- id: `${n}${t.strategy_id}`,
60
+ id: `${i}${t.strategy_id}`,
61
61
  text: o("eureka.edit-strategy"),
62
62
  type: "text",
63
63
  // Resolved from Guido's own public/sprite.svg by InIcons; an unknown name would
@@ -65,19 +65,24 @@ const rt = /* @__PURE__ */ A({
65
65
  icon: "line-open-website"
66
66
  }]
67
67
  }
68
- }))), E = (t) => {
68
+ }))), x = (t) => {
69
69
  var f;
70
- const r = (f = t == null ? void 0 : t.id) != null && f.startsWith(n) ? t.id.slice(n.length) : "";
70
+ const r = (f = t == null ? void 0 : t.id) != null && f.startsWith(i) ? t.id.slice(i.length) : "";
71
71
  if (!r)
72
72
  return;
73
- const a = e.currentRecommendationId;
74
- String(s.value ?? "") === r && (s.value = null);
75
- const l = e.recommendationConfigs.strategyId === r;
76
- l && e.removeStrategy(), e.openStrategyEdit(r), l && a !== null && _(a, "Removed recommendation strategy");
77
- }, p = g(() => {
78
- const t = e.strategies.find((r) => String(r.strategy_id) === String(s.value ?? ""));
73
+ const s = e.currentRecommendationId, d = e.recommendationConfigs.strategyId === r;
74
+ if (!e.openStrategyEdit(r)) {
75
+ n({
76
+ type: g.Error,
77
+ message: o("discovery-strategy.error-description")
78
+ });
79
+ return;
80
+ }
81
+ String(a.value ?? "") === r && (a.value = null), d && (e.removeStrategy(), s !== null && T(s, "Removed recommendation strategy"));
82
+ }, p = l(() => {
83
+ const t = e.strategies.find((r) => String(r.strategy_id) === String(a.value ?? ""));
79
84
  return t ? { ...t, fallback_config: null } : null;
80
- }), O = g(() => ({
85
+ }), O = l(() => ({
81
86
  primaryButton: {
82
87
  styling: "solid",
83
88
  type: "primary",
@@ -89,7 +94,7 @@ const rt = /* @__PURE__ */ A({
89
94
  labelText: o("products.cancel")
90
95
  }
91
96
  }));
92
- return { __sfc: !0, loadOrganisms: m, skeleton: u, OrgStrategyDetailModal: w, OrgStrategyDropdown: D, EDIT_BUTTON_ID_PREFIX: n, trans: o, store: e, showToaster: i, selectedStrategyId: s, showSelectionError: c, drawerDescription: I, lastUpdatedText: y, strategyOptions: x, onInfoBoxButtonClick: E, selectedStrategy: p, footerButtonGroupOptions: O, closeDrawer: () => {
97
+ return { __sfc: !0, loadOrganisms: m, skeleton: u, OrgStrategyDetailModal: w, OrgStrategyDropdown: D, EDIT_BUTTON_ID_PREFIX: i, trans: o, store: e, showToaster: n, selectedStrategyId: a, showSelectionError: c, drawerDescription: E, lastUpdatedText: y, strategyOptions: I, onInfoBoxButtonClick: x, selectedStrategy: p, footerButtonGroupOptions: O, closeDrawer: () => {
93
98
  e.closeAssignStrategyDrawer();
94
99
  }, assignStrategy: async () => {
95
100
  const t = p.value;
@@ -100,23 +105,23 @@ const rt = /* @__PURE__ */ A({
100
105
  let r = "";
101
106
  try {
102
107
  r = await e.resolveStrategyUrlTemplate(t, e.recommendationConfigs.currencySettings.value);
103
- } catch (l) {
104
- console.error("[AssignStrategyDrawer] resolveStrategyUrlTemplate failed", l);
108
+ } catch (d) {
109
+ console.error("[AssignStrategyDrawer] resolveStrategyUrlTemplate failed", d);
105
110
  }
106
111
  if (!r) {
107
- i({
108
- type: d.Error,
112
+ n({
113
+ type: g.Error,
109
114
  message: o("email-editor.strategy-assign-failed", { strategyName: t.name })
110
115
  });
111
116
  return;
112
117
  }
113
- const a = e.currentRecommendationId;
114
- e.assignStrategy(t), i({
115
- type: d.Success,
118
+ const s = e.currentRecommendationId;
119
+ e.assignStrategy(t), n({
120
+ type: g.Success,
116
121
  message: o("email-editor.strategy-assigned-success", { strategyName: t.name })
117
- }), e.closeAssignStrategyDrawer(), a !== null && await _(a, "Assigned recommendation strategy");
122
+ }), e.closeAssignStrategyDrawer(), s !== null && await T(s, "Assigned recommendation strategy");
118
123
  }, onSelect: (t) => {
119
- s.value = t.value, c.value = !1;
124
+ a.value = t.value, c.value = !1;
120
125
  } };
121
126
  }
122
127
  });
@@ -1,20 +1,20 @@
1
- var D = Object.defineProperty;
2
- var y = (l, c, t) => c in l ? D(l, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[c] = t;
3
- var f = (l, c, t) => y(l, typeof c != "symbol" ? c + "" : c, t);
4
- import { useConfig as C } from "../../../composables/useConfig.js";
5
- import { BlockId as b } from "../../../enums/block.js";
6
- import { getMigrationBannerHtml as A } from "../../../utils/migrationBannerHtml.js";
7
- import { Block as E, BlockCompositionType as S, ModificationDescription as g } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
1
+ var y = Object.defineProperty;
2
+ var A = (l, c, t) => c in l ? y(l, c, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[c] = t;
3
+ var g = (l, c, t) => A(l, typeof c != "symbol" ? c + "" : c, t);
4
+ import { useConfig as h } from "../../../composables/useConfig.js";
5
+ import { BlockId as D } from "../../../enums/block.js";
6
+ import { getMigrationBannerHtml as C } from "../../../utils/migrationBannerHtml.js";
7
+ import { Block as E, BlockCompositionType as S, ModificationDescription as p } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
8
8
  import { CURRENT_CONFIG_VERSION as N, PRE_RRS_CONFIG_VERSION as O } from "./constants/defaultConfig.js";
9
9
  import { RECOMMENDATION_ID_ATTR as m } from "./constants/selectors.js";
10
10
  import { regenerateMobileProductRows as M } from "./controls/main/utils.js";
11
- import { ensureMobileCssRulesExist as _, setMobileLayoutOptOut as I, hasMobileLayoutOptOut as w } from "./controls/mobileLayout/cssRules.js";
11
+ import { ensureMobileCssRulesExist as I, setMobileLayoutOptOut as R, hasMobileLayoutOptOut as w } from "./controls/mobileLayout/cssRules.js";
12
12
  import { RecommendationConfigService as s } from "./services/configService.js";
13
13
  import { useRecommendationExtensionStore as d } from "./store/recommendation.js";
14
- import { getDefaultTemplate as x } from "./templates/grid/template.js";
15
- import { useRecommendationBlockWarning as H } from "./useRecommendationBlockWarning.js";
16
- const k = b.Recommendation, u = "recommendation-block-v2", R = "hide-price";
17
- let B = !1;
14
+ import { getDefaultTemplate as T } from "./templates/grid/template.js";
15
+ import { useRecommendationBlockWarning as x } from "./useRecommendationBlockWarning.js";
16
+ const k = D.Recommendation, u = "recommendation-block-v2", B = "hide-price";
17
+ let b = !1;
18
18
  class j extends E {
19
19
  constructor() {
20
20
  super();
@@ -22,7 +22,9 @@ class j extends E {
22
22
  * Stores the ID generated in getTemplate() so onCreated() can reuse it.
23
23
  * This avoids generating a new (different) ID in onCreated().
24
24
  */
25
- f(this, "_pendingBlockId", null);
25
+ g(this, "_pendingBlockId", null);
26
+ /** Ids already reset under their new name, so the deferred pass skips their stale snapshot. */
27
+ g(this, "_resetSourceIds", /* @__PURE__ */ new Set());
26
28
  }
27
29
  getId() {
28
30
  return k;
@@ -42,7 +44,7 @@ class j extends E {
42
44
  );
43
45
  }
44
46
  getSettingsPanelTitleHtml() {
45
- return A(
47
+ return C(
46
48
  k,
47
49
  this.api.translate("Recommendation Block"),
48
50
  this.api.translate("This block is switched from the Old Version to the New Version. We recommend you check the Recommendation block and test your message to ensure it works properly.")
@@ -66,7 +68,7 @@ class j extends E {
66
68
  */
67
69
  getTemplate() {
68
70
  const t = this._generateNextId();
69
- return this._pendingBlockId = t, x(t);
71
+ return this._pendingBlockId = t, T(t);
70
72
  }
71
73
  /**
72
74
  * Called when a new block is dropped into the template
@@ -79,17 +81,17 @@ class j extends E {
79
81
  const e = this._getRecommendationId(t);
80
82
  if (e !== null && e > 0) {
81
83
  if (this._detectDuplicate(e)) {
82
- this._handleDuplicate(t, e);
84
+ this._handleDuplicate(t, e), this._scheduleArrivalReset(t);
83
85
  return;
84
86
  }
85
87
  this._pendingBlockId === null && setTimeout(() => {
86
88
  this._getBlockElement(t) && this._detectDuplicate(e) && this._handleDuplicate(t, e);
87
- }, 0);
89
+ }, 0), this._scheduleArrivalReset(t);
88
90
  return;
89
91
  }
90
92
  const i = this._pendingBlockId ?? this._generateNextId();
91
93
  this._pendingBlockId = null, this._assignRecommendationId(t, i);
92
- const n = C().isFeatureEnabled("reusableRecommendationStrategy"), o = n && !s.hasConfig(t), { config: r, wasFreshDrop: a } = s.initializeConfig(
94
+ const n = h().isFeatureEnabled("reusableRecommendationStrategy"), o = n && !s.hasConfig(t), { config: r, wasFreshDrop: a } = s.initializeConfig(
93
95
  this.api,
94
96
  t,
95
97
  {
@@ -100,16 +102,16 @@ class j extends E {
100
102
  // at save for a block the user never configured.
101
103
  ...o ? { strategy: "" } : {}
102
104
  }
103
- ), p = d();
104
- if (p.setCurrentBlock(i), this._stampHidePrice(t, r.priceHideIfSameAsDiscounted), a) {
105
- _(this.api);
106
- const h = this._getBlockElement(t);
107
- h && (I(this.api, h, !0), M({
105
+ ), f = d();
106
+ if (f.setCurrentBlock(i), this._stampHidePrice(t, r.priceHideIfSameAsDiscounted), a) {
107
+ I(this.api);
108
+ const _ = this._getBlockElement(t);
109
+ _ && (R(this.api, _, !0), M({
108
110
  currentNode: t,
109
111
  documentModifier: this.api.getDocumentModifier()
110
112
  }));
111
113
  }
112
- p.patchCurrentBlockConfig({ language: r.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
114
+ f.patchCurrentBlockConfig({ language: r.language }, { triggerRefetch: !1 }), this._warnIfMultipleBlocks();
113
115
  }
114
116
  /**
115
117
  * Called when the document changes or template is loaded
@@ -131,11 +133,11 @@ class j extends E {
131
133
  }
132
134
  this._healLingeringDuplicate(t), s.needsMigration(t) && this._migrateFromLegacy(t), this._healHidePriceAttr(t);
133
135
  try {
134
- B || (_(this.api), B = !0);
136
+ b || (I(this.api), b = !0);
135
137
  const e = s.getConfig(t), i = this._getBlockElement(t);
136
138
  if (i) {
137
139
  const n = !e.mobileLayoutEnabled;
138
- w(i) !== n && I(this.api, i, n);
140
+ w(i) !== n && R(this.api, i, n);
139
141
  }
140
142
  } catch {
141
143
  }
@@ -165,7 +167,7 @@ class j extends E {
165
167
  */
166
168
  _warnIfMultipleBlocks() {
167
169
  try {
168
- H().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
170
+ x().warnIfMultipleBlocks(this.api.getDocumentRootHtmlNode());
169
171
  } catch {
170
172
  }
171
173
  }
@@ -187,6 +189,19 @@ class j extends E {
187
189
  }
188
190
  return t + 1;
189
191
  }
192
+ /**
193
+ * Drops the "already fetched" claims of a block that arrived holding a baked id
194
+ * (saved module, paste, duplicate), so the settings panel reads its strategy again.
195
+ *
196
+ * Deferred a tick: the collision handlers rename a sibling first, and either sibling can be
197
+ * the renamed one depending on where the module was dropped (SD-149916).
198
+ */
199
+ _scheduleArrivalReset(t) {
200
+ h().isFeatureEnabled("reusableRecommendationStrategy") && setTimeout(() => {
201
+ const e = this._getRecommendationId(t);
202
+ e !== null && e > 0 && !this._resetSourceIds.delete(e) && d().resetArrivedBlock(e);
203
+ }, 0);
204
+ }
190
205
  /**
191
206
  * Heals a duplicate that slipped past the create-time `_detectDuplicate`
192
207
  * scan (or that was persisted by an older template before SD-142352).
@@ -241,7 +256,7 @@ class j extends E {
241
256
  /** Assigns a fresh id to a duplicated block and syncs DOM, node config and store. */
242
257
  _handleDuplicate(t, e) {
243
258
  const i = this._reassignDuplicateId(t, e), n = d();
244
- n.cloneBlockState(e, i), n.setCurrentBlock(i), this._warnIfMultipleBlocks();
259
+ n.cloneBlockState(e, i), n.setCurrentBlock(i), h().isFeatureEnabled("reusableRecommendationStrategy") && (this._resetSourceIds.add(e), n.resetArrivedBlock(i)), this._warnIfMultipleBlocks();
245
260
  }
246
261
  /**
247
262
  * Core id-reassignment for a duplicated block: rewrites the DOM
@@ -274,7 +289,7 @@ class j extends E {
274
289
  if (!("getAttribute" in t))
275
290
  return;
276
291
  const n = `ins-recommendation-v3-block-${e}`, o = `ins-recommendation-v3-block-${i}`, r = this.api.getDocumentModifier();
277
- r.modifyHtml(t).removeClass(n).setClass(o), r.apply(new g(
292
+ r.modifyHtml(t).removeClass(n).setClass(o), r.apply(new p(
278
293
  `Reassign recommendation instance class ${n} -> ${o}`
279
294
  ));
280
295
  }
@@ -289,7 +304,7 @@ class j extends E {
289
304
  if (!i)
290
305
  return;
291
306
  const n = this.api.getDocumentModifier();
292
- n.modifyHtml(i).setAttribute(m, e.toString()), n.apply(new g(`Assign recommendation ID ${e}`));
307
+ n.modifyHtml(i).setAttribute(m, e.toString()), n.apply(new p(`Assign recommendation ID ${e}`));
293
308
  }
294
309
  /**
295
310
  * Stamps the `hide-price` attribute (read by email-service at send time) on
@@ -297,7 +312,7 @@ class j extends E {
297
312
  */
298
313
  _stampHidePrice(t, e) {
299
314
  const i = this._getBlockElement(t);
300
- i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(R, String(e)).apply(new g("Stamp hide-price"));
315
+ i && this.api.getDocumentModifier().modifyHtml(i).setAttribute(B, String(e)).apply(new p("Stamp hide-price"));
301
316
  }
302
317
  /**
303
318
  * Stamps `hide-price` from config only when the block lacks the attribute —
@@ -305,7 +320,7 @@ class j extends E {
305
320
  */
306
321
  _healHidePriceAttr(t) {
307
322
  const e = this._getBlockElement(t);
308
- if (!e || !("getAttribute" in e) || e.getAttribute(R) !== null)
323
+ if (!e || !("getAttribute" in e) || e.getAttribute(B) !== null)
309
324
  return;
310
325
  const { priceHideIfSameAsDiscounted: i } = s.getConfig(t);
311
326
  this._stampHidePrice(t, i);
@@ -1,23 +1,23 @@
1
- import { getRecommendationFeedSourceMaps as C, strategyEditPath as D, URLS as _, getOperatorOptions as N, PriceAttributes as U } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as A } from "../../../../services/recommendationApi.js";
3
- import { useConfigStore as P } from "../../../../stores/config.js";
4
- import { defineStore as L } from "pinia";
1
+ import { getRecommendationFeedSourceMaps as v, strategyEditPath as D, URLS as _, getOperatorOptions as A, PriceAttributes as N } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as U } from "../../../../services/recommendationApi.js";
3
+ import { useConfigStore as k } from "../../../../stores/config.js";
4
+ import { defineStore as E } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as E, DEFAULT_CARDS_IN_ROW as x } from "../constants/layout.js";
7
- import { DEFAULT_NODE_CONFIG as B, EXCLUDED_ALGORITHM_IDS as M } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as v } from "../templates/utils.js";
9
- import { generateCompleteFilterQuery as F } from "../utils/filterUtil.js";
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as L, DEFAULT_CARDS_IN_ROW as B } from "../constants/layout.js";
7
+ import { DEFAULT_NODE_CONFIG as x, EXCLUDED_ALGORITHM_IDS as M } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as F } from "../templates/utils.js";
9
+ import { generateCompleteFilterQuery as w } from "../utils/filterUtil.js";
10
10
  import { getPartnerRecommendationParams as O } from "../utils/partnerCustomizations.js";
11
- import { sanitizeProductIds as T } from "../utils/productIds.js";
12
- import { isFilterValid as z } from "../validation/filterSchema.js";
11
+ import { sanitizeProductIds as z } from "../utils/productIds.js";
12
+ import { isFilterValid as T } from "../validation/filterSchema.js";
13
13
  import { getInvalidFields as V, isConfigValid as j } from "../validation/requiredFields.js";
14
- const f = A();
14
+ const f = U();
15
15
  let S = null, p = null, y = null, I = "", R = null, b = 0;
16
- const h = /* @__PURE__ */ new Map();
16
+ const g = /* @__PURE__ */ new Map();
17
17
  let l = null;
18
18
  function G() {
19
19
  return {
20
- cardsInRow: x,
20
+ cardsInRow: B,
21
21
  currencySettings: {
22
22
  name: "USD",
23
23
  value: "USD",
@@ -31,7 +31,7 @@ function G() {
31
31
  productIds: [],
32
32
  id: 1,
33
33
  language: "en_US",
34
- mobileCardsInRow: E,
34
+ mobileCardsInRow: L,
35
35
  mobileLayoutEnabled: !1,
36
36
  orientation: "grid",
37
37
  recommendedProducts: [],
@@ -41,7 +41,7 @@ function G() {
41
41
  // that default is what reached buildCampaignUrl and baked a most-popular feed URL
42
42
  // for a block the user never configured (SD-149916). Legacy blocks still get their real
43
43
  // algorithm from the node-config sync or the save-time seed.
44
- strategy: P().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
44
+ strategy: k().isFeatureEnabled("reusableRecommendationStrategy") ? "" : "mostPopular",
45
45
  strategyId: "",
46
46
  textTrimming: !0,
47
47
  unresponsive: !1,
@@ -51,7 +51,7 @@ function G() {
51
51
  priceHideIfSameAsDiscounted: !1
52
52
  };
53
53
  }
54
- function w() {
54
+ function P() {
55
55
  return {
56
56
  recommendationConfigs: G(),
57
57
  recommendationProducts: [],
@@ -80,7 +80,7 @@ const $ = () => ({
80
80
  currentRecommendationId: null,
81
81
  strategyPanelOpen: !1,
82
82
  configVersion: 0
83
- }), it = L("guidoRecommendationExtension", {
83
+ }), nt = E("guidoRecommendationExtension", {
84
84
  state: () => $(),
85
85
  getters: {
86
86
  // ====================================================================
@@ -146,12 +146,12 @@ const $ = () => ({
146
146
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
147
147
  },
148
148
  getActivePredictiveAlgorithms: (t) => {
149
- const e = C(), r = [];
150
- return t.activePredictiveAlgorithms.filter((n) => !M.includes(n)).forEach((n) => {
151
- r.push(...e.filter((s) => s.id === n));
152
- }), r.map((n) => ({
153
- text: n.name,
154
- value: n.key
149
+ const e = v(), r = [];
150
+ return t.activePredictiveAlgorithms.filter((i) => !M.includes(i)).forEach((i) => {
151
+ r.push(...e.filter((s) => s.id === i));
152
+ }), r.map((i) => ({
153
+ text: i.name,
154
+ value: i.key
155
155
  }));
156
156
  },
157
157
  getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
@@ -165,7 +165,7 @@ const $ = () => ({
165
165
  getFilterList() {
166
166
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
167
167
  let e;
168
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : U.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
168
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : N.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
169
169
  text: t.displayName,
170
170
  value: e,
171
171
  type: t.attributeType
@@ -187,7 +187,7 @@ const $ = () => ({
187
187
  setCurrentBlock(t) {
188
188
  this.blockStates[t] || (this.blockStates = {
189
189
  ...this.blockStates,
190
- [t]: w()
190
+ [t]: P()
191
191
  }), this.currentRecommendationId = t;
192
192
  },
193
193
  /**
@@ -197,32 +197,36 @@ const $ = () => ({
197
197
  removeBlockState(t) {
198
198
  const e = t.toString();
199
199
  if (this.recommendationCampaignUrls[e]) {
200
- const n = { ...this.recommendationCampaignUrls };
201
- delete n[e], this.recommendationCampaignUrls = n;
200
+ const i = { ...this.recommendationCampaignUrls };
201
+ delete i[e], this.recommendationCampaignUrls = i;
202
202
  }
203
203
  const r = { ...this.blockStates };
204
204
  if (delete r[t], this.blockStates = r, this.currentRecommendationId === t) {
205
- const n = Object.keys(this.blockStates).map(Number);
206
- this.currentRecommendationId = n.length > 0 ? n[0] : null;
205
+ const i = Object.keys(this.blockStates).map(Number);
206
+ this.currentRecommendationId = i.length > 0 ? i[0] : null;
207
207
  }
208
208
  },
209
209
  /**
210
210
  * Deep-clones a block's state and mirrors its campaign URL under a new id.
211
211
  * Invoked from `onCreated` when a duplicate is detected (SD-142352).
212
+ *
213
+ * The clone keeps the source's data but drops its "already fetched / already
214
+ * synced" claims for strategy blocks, so it fetches again like a fresh drop
215
+ * (SD-149916).
212
216
  */
213
217
  cloneBlockState(t, e) {
214
218
  const r = this.blockStates[t];
215
219
  if (r) {
216
220
  const o = structuredClone(r);
217
- o.recommendationConfigs.id = e, this.blockStates = {
221
+ o.recommendationConfigs.id = e, o.recommendationConfigs.strategyId && k().isFeatureEnabled("reusableRecommendationStrategy") && (o.isInitialized = !1, o.syncedConfigFingerprint = null), this.blockStates = {
218
222
  ...this.blockStates,
219
223
  [e]: o
220
224
  };
221
225
  }
222
- const n = t.toString(), s = e.toString(), i = this.recommendationCampaignUrls[n];
223
- i && (this.recommendationCampaignUrls = {
226
+ const i = t.toString(), s = e.toString(), n = this.recommendationCampaignUrls[i];
227
+ n && (this.recommendationCampaignUrls = {
224
228
  ...this.recommendationCampaignUrls,
225
- [s]: i
229
+ [s]: n
226
230
  });
227
231
  },
228
232
  /**
@@ -247,7 +251,7 @@ const $ = () => ({
247
251
  * `isInitialized`) alone.
248
252
  */
249
253
  seedBlockUrlConfig(t, e) {
250
- const r = (o, a) => o === "." || o === "," || o === " " ? o : a, n = {
254
+ const r = (o, a) => o === "." || o === "," || o === " " ? o : a, i = {
251
255
  name: e.currencyCode,
252
256
  value: e.currencyCode,
253
257
  symbol: e.currencySymbol,
@@ -255,9 +259,9 @@ const $ = () => ({
255
259
  decimalCount: String(e.currencyDecimalCount),
256
260
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
257
261
  thousandSeparator: r(e.currencyThousandSeparator, ".")
258
- }, s = !this.blockStates[t], i = s ? w() : this.blockStates[t];
259
- i.recommendationConfigs = {
260
- ...i.recommendationConfigs,
262
+ }, s = !this.blockStates[t], n = s ? P() : this.blockStates[t];
263
+ n.recommendationConfigs = {
264
+ ...n.recommendationConfigs,
261
265
  strategy: e.strategy,
262
266
  strategyId: e.strategyId,
263
267
  language: e.language,
@@ -265,10 +269,10 @@ const $ = () => ({
265
269
  productIds: e.productIds,
266
270
  filters: e.filters,
267
271
  shuffleProducts: e.shuffleProducts,
268
- currencySettings: n
272
+ currencySettings: i
269
273
  }, s && (this.blockStates = {
270
274
  ...this.blockStates,
271
- [t]: i
275
+ [t]: n
272
276
  });
273
277
  },
274
278
  /**
@@ -287,18 +291,18 @@ const $ = () => ({
287
291
  ...t.currencySettings || {}
288
292
  }
289
293
  };
290
- const { triggerRefetch: n = !0 } = e;
291
- n && this.configVersion++;
294
+ const { triggerRefetch: i = !0 } = e;
295
+ i && this.configVersion++;
292
296
  },
293
297
  /**
294
298
  * Creates a filter with the first available attribute and operator pre-selected.
295
299
  */
296
300
  createDefaultFilter(t, e) {
297
- const [r] = this.getFilterList, [n] = N(r == null ? void 0 : r.type);
301
+ const [r] = this.getFilterList, [i] = A(r == null ? void 0 : r.type);
298
302
  return {
299
303
  type: "standardFilter",
300
304
  attribute: (r == null ? void 0 : r.value) ?? "",
301
- operator: (n == null ? void 0 : n.value) ?? "",
305
+ operator: (i == null ? void 0 : i.value) ?? "",
302
306
  innerGroupOperator: "*",
303
307
  outerGroupOperator: "*",
304
308
  filterNumber: e,
@@ -379,6 +383,16 @@ const $ = () => ({
379
383
  R = null;
380
384
  }
381
385
  },
386
+ /**
387
+ * Clears what a block inherited from the id it arrived holding, so the panel fetches for it.
388
+ * A re-dropped saved module can keep the source's id — and its "already fetched" entry —
389
+ * while the strategy's product count has moved on (SD-149916).
390
+ */
391
+ resetArrivedBlock(t) {
392
+ const e = this.blockStates[t];
393
+ e != null && e.recommendationConfigs.strategyId && (e.isInitialized = !1, e.syncedConfigFingerprint = null, this.invalidateStrategies(), this.fetchStrategies().catch(() => {
394
+ }));
395
+ },
382
396
  invalidateStrategies() {
383
397
  this.strategiesFetched = !1, this.strategiesSettled = !1, b += 1;
384
398
  },
@@ -393,22 +407,23 @@ const $ = () => ({
393
407
  * leave that one missing from the list. It disarms on the first return AFTER that tab is
394
408
  * closed instead, so an ordinary alt-tab later in the session costs nothing. Armed only
395
409
  * once the user actually opens a strategy page, so anyone who never does pays nothing.
410
+ * @returns whether a tab actually opened, so callers can skip work that assumes one did
396
411
  */
397
412
  _armStrategyPageReturn(t) {
398
413
  const e = window.open(t, "_blank");
399
414
  if (!e)
400
- return;
415
+ return !1;
401
416
  e.opener = null, l == null || l();
402
417
  let r = !1;
403
- const n = () => {
418
+ const i = () => {
404
419
  document.hidden || r || (e != null && e.closed && (l == null || l()), r = !0, this.invalidateStrategies(), this.fetchStrategies().then(() => this._refreshAssignedStrategyProducts()).catch(() => {
405
420
  }).finally(() => {
406
421
  r = !1;
407
422
  }));
408
423
  };
409
- document.addEventListener("visibilitychange", n), l = () => {
410
- document.removeEventListener("visibilitychange", n), l = null;
411
- };
424
+ return document.addEventListener("visibilitychange", i), l = () => {
425
+ document.removeEventListener("visibilitychange", i), l = null;
426
+ }, !0;
412
427
  },
413
428
  /**
414
429
  * Reloads the products of the block the user is on, so an edit made in the strategy tab
@@ -431,24 +446,24 @@ const $ = () => ({
431
446
  * new strategy is offered; the user picks it themselves.
432
447
  */
433
448
  openStrategyCreation() {
434
- this._armStrategyPageReturn(_.RECOMMENDATION_STRATEGY_CREATE_PATH);
449
+ return this._armStrategyPageReturn(_.RECOMMENDATION_STRATEGY_CREATE_PATH);
435
450
  },
436
451
  /**
437
452
  * Opens the strategy's edit page. Every return refreshes the list so the block reflects the
438
453
  * edited configuration.
439
454
  */
440
455
  openStrategyEdit(t) {
441
- this._armStrategyPageReturn(D(t));
456
+ return this._armStrategyPageReturn(D(t));
442
457
  },
443
458
  async resolveStrategyUrlTemplate(t, e) {
444
- const r = typeof t.updated_at == "string" ? t.updated_at : "", n = r ? `${t.strategy_id}:${r}:${e}` : "", s = n ? this.strategyUrlTemplates[n] : void 0;
459
+ const r = typeof t.updated_at == "string" ? t.updated_at : "", i = r ? `${t.strategy_id}:${r}:${e}` : "", s = i ? this.strategyUrlTemplates[i] : void 0;
445
460
  if (s)
446
461
  return s;
447
- const { full_path: i = "" } = await f.generateStrategyEndpoint(t, e) ?? {};
448
- return n && i && (this.strategyUrlTemplates = {
462
+ const { full_path: n = "" } = await f.generateStrategyEndpoint(t, e) ?? {};
463
+ return i && n && (this.strategyUrlTemplates = {
449
464
  ...this.strategyUrlTemplates,
450
- [n]: i
451
- }), i;
465
+ [i]: n
466
+ }), n;
452
467
  },
453
468
  assignStrategy(t) {
454
469
  this.patchCurrentBlockConfig({
@@ -460,7 +475,7 @@ const $ = () => ({
460
475
  this.patchCurrentBlockConfig({
461
476
  strategyId: "",
462
477
  strategy: "",
463
- size: B.size
478
+ size: x.size
464
479
  });
465
480
  },
466
481
  // ====================================================================
@@ -479,8 +494,8 @@ const $ = () => ({
479
494
  currencies: r
480
495
  } = await f.fetchRecommendationCreateData();
481
496
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
482
- const n = this.blockStates[this.currentRecommendationId];
483
- n.filterStatus = !!n.recommendationConfigs.filters.length;
497
+ const i = this.blockStates[this.currentRecommendationId];
498
+ i.filterStatus = !!i.recommendationConfigs.filters.length;
484
499
  }
485
500
  this.currencyList = r;
486
501
  })();
@@ -519,41 +534,41 @@ const $ = () => ({
519
534
  deleteFilterGroup(t) {
520
535
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
521
536
  return;
522
- 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]));
523
- e.recommendationConfigs.filters = r.map((i) => ({
524
- ...i,
525
- filterGroup: s.get(i.filterGroup) ?? i.filterGroup
537
+ 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]));
538
+ e.recommendationConfigs.filters = r.map((n) => ({
539
+ ...n,
540
+ filterGroup: s.get(n.filterGroup) ?? n.filterGroup
526
541
  }));
527
542
  },
528
543
  updateFilter(t) {
529
544
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
530
545
  return;
531
- const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
546
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.findIndex((i) => i.filterNumber === t.filterNumber && i.filterGroup === t.filterGroup);
532
547
  if (r !== -1) {
533
- const n = [...e.recommendationConfigs.filters];
534
- n[r] = {
548
+ const i = [...e.recommendationConfigs.filters];
549
+ i[r] = {
535
550
  ...t,
536
- isValid: z(t)
537
- }, e.recommendationConfigs.filters = n;
551
+ isValid: T(t)
552
+ }, e.recommendationConfigs.filters = i;
538
553
  }
539
554
  },
540
555
  deleteFilter(t) {
541
556
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
542
557
  return;
543
- const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((n) => n.filterNumber === t.filterNumber && n.filterGroup === t.filterGroup);
558
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters].findIndex((i) => i.filterNumber === t.filterNumber && i.filterGroup === t.filterGroup);
544
559
  if (r !== -1) {
545
- let n = [...e.recommendationConfigs.filters];
546
- if (n.splice(r, 1), n.some((i) => i.filterGroup === t.filterGroup)) {
547
- let i = 1;
548
- n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
560
+ let i = [...e.recommendationConfigs.filters];
561
+ if (i.splice(r, 1), i.some((n) => n.filterGroup === t.filterGroup)) {
562
+ let n = 1;
563
+ i = i.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: n++ } : o);
549
564
  } else {
550
- const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, u) => a - u), o = new Map(i.map((a, u) => [a, u + 1]));
551
- n = n.map((a) => ({
565
+ const n = [...new Set(i.map((a) => a.filterGroup))].sort((a, u) => a - u), o = new Map(n.map((a, u) => [a, u + 1]));
566
+ i = i.map((a) => ({
552
567
  ...a,
553
568
  filterGroup: o.get(a.filterGroup) ?? a.filterGroup
554
569
  }));
555
570
  }
556
- e.recommendationConfigs.filters = n;
571
+ e.recommendationConfigs.filters = i;
557
572
  }
558
573
  },
559
574
  addFilter(t) {
@@ -561,8 +576,8 @@ const $ = () => ({
561
576
  return;
562
577
  const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], s = r.filter(
563
578
  (o) => o.filterGroup === t.filterGroup
564
- ).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
565
- i !== -1 ? r.splice(i + 1, 0, {
579
+ ).length + 1, n = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
580
+ n !== -1 ? r.splice(n + 1, 0, {
566
581
  ...t,
567
582
  filterNumber: s
568
583
  }) : r.push({
@@ -571,7 +586,7 @@ const $ = () => ({
571
586
  }), e.recommendationConfigs.filters = r;
572
587
  },
573
588
  generateFilterQuery() {
574
- return F(this.recommendationConfigs.filters);
589
+ return w(this.recommendationConfigs.filters);
575
590
  },
576
591
  /**
577
592
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -611,30 +626,30 @@ const $ = () => ({
611
626
  }
612
627
  },
613
628
  async _doFetchProducts() {
614
- var g;
615
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = P();
616
- if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
629
+ var h;
630
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, i = k();
631
+ if (r.strategyId && i.isFeatureEnabled("reusableRecommendationStrategy")) {
617
632
  await this._doFetchStrategyProducts(t);
618
633
  return;
619
634
  }
620
- const s = (h.get(t) ?? 0) + 1;
621
- h.set(t, s);
622
- const i = parseInt(r.size) || 6;
635
+ const s = (g.get(t) ?? 0) + 1;
636
+ g.set(t, s);
637
+ const n = parseInt(r.size) || 6;
623
638
  if (!r.strategy) {
624
- this._setBlockProducts(t, [], i);
639
+ this._setBlockProducts(t, [], n);
625
640
  return;
626
641
  }
627
- const o = r.filters.filter((d) => d.isValid), a = F(o), u = ((g = C().find((d) => d.key === r.strategy)) == null ? void 0 : g.path) || "", c = {
642
+ const o = r.filters.filter((d) => d.isValid), a = w(o), u = ((h = v().find((d) => d.key === r.strategy)) == null ? void 0 : h.path) || "", c = {
628
643
  locale: r.language,
629
644
  currency: r.currencySettings.value,
630
- partnerName: n.partnerName,
645
+ partnerName: i.partnerName,
631
646
  size: r.size,
632
647
  details: !0,
633
- campaignId: n.variationId
648
+ campaignId: i.variationId
634
649
  };
635
- r.strategy === "manualMerchandising" ? c.productId = T(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), a && (c.filter = a), r.shuffleProducts && (c.shuffle = !0), Object.assign(
650
+ r.strategy === "manualMerchandising" ? c.productId = z(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), a && (c.filter = a), r.shuffleProducts && (c.shuffle = !0), Object.assign(
636
651
  c,
637
- O(n.partnerName, r.strategy)
652
+ O(i.partnerName, r.strategy)
638
653
  );
639
654
  let m;
640
655
  try {
@@ -642,39 +657,39 @@ const $ = () => ({
642
657
  } catch {
643
658
  m = [];
644
659
  }
645
- h.get(t) === s && this._setBlockProducts(t, m, i);
660
+ g.get(t) === s && this._setBlockProducts(t, m, n);
646
661
  },
647
662
  async _doFetchStrategyProducts(t) {
648
- const e = (h.get(t) ?? 0) + 1;
649
- h.set(t, e);
663
+ const e = (g.get(t) ?? 0) + 1;
664
+ g.set(t, e);
650
665
  const { recommendationConfigs: r } = this.blockStates[t];
651
- let n = [], s = parseInt(r.size) || 6;
666
+ let i = [], s = parseInt(r.size) || 6;
652
667
  try {
653
668
  await this.fetchStrategies();
654
- const i = this.strategies.find(
669
+ const n = this.strategies.find(
655
670
  (o) => String(o.strategy_id) === r.strategyId
656
671
  );
657
- if (i) {
658
- s = parseInt(String(i.size ?? "")) || s;
659
- const o = r.currencySettings.value, u = (await this.resolveStrategyUrlTemplate(i, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), c = u.indexOf("?"), m = c === -1 ? u : u.slice(0, c), g = (c === -1 ? "" : u.slice(c + 1)).split("&").filter((k) => k && !k.startsWith("user_id=")), d = g.length ? `${m}?${g.join("&")}` : m;
660
- d && (n = await f.fetchRecommendationProductsByUrl(d));
672
+ if (n) {
673
+ s = parseInt(String(n.size ?? "")) || s;
674
+ const o = r.currencySettings.value, u = (await this.resolveStrategyUrlTemplate(n, o)).replaceAll("{locale}", r.language).replaceAll("{currency}", o), c = u.indexOf("?"), m = c === -1 ? u : u.slice(0, c), h = (c === -1 ? "" : u.slice(c + 1)).split("&").filter((C) => C && !C.startsWith("user_id=")), d = h.length ? `${m}?${h.join("&")}` : m;
675
+ d && (i = await f.fetchRecommendationProductsByUrl(d));
661
676
  }
662
677
  } catch {
663
- n = [];
678
+ i = [];
664
679
  }
665
- h.get(t) === e && this._setBlockProducts(t, n, s);
680
+ g.get(t) === e && this._setBlockProducts(t, i, s);
666
681
  },
667
682
  _setBlockProducts(t, e, r) {
668
683
  if (!this.blockStates[t])
669
684
  return;
670
- const n = e.length > 0 ? e : v(r);
685
+ const i = e.length > 0 ? e : F(r);
671
686
  this.blockStates[t].recommendationProducts = [
672
- ...n.slice(0, r),
673
- ...v(Math.max(0, r - n.length))
687
+ ...i.slice(0, r),
688
+ ...F(Math.max(0, r - i.length))
674
689
  ];
675
690
  }
676
691
  }
677
692
  });
678
693
  export {
679
- it as useRecommendationExtensionStore
694
+ nt as useRecommendationExtensionStore
680
695
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-cc5ee925]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-cc5ee925]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-cc5ee925]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-ec2027ff] .vueperslides__bullets,[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-521cf6b3]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-521cf6b3] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}
1
+ @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-cc5ee925]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-cc5ee925]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-cc5ee925]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-ec2027ff] .vueperslides__bullets,[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-184392c2]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-184392c2] .in-skeleton{width:100%!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-184392c2] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}
@@ -15,6 +15,8 @@ export declare class RecommendationBlock extends Block {
15
15
  * This avoids generating a new (different) ID in onCreated().
16
16
  */
17
17
  private _pendingBlockId;
18
+ /** Ids already reset under their new name, so the deferred pass skips their stale snapshot. */
19
+ private _resetSourceIds;
18
20
  constructor();
19
21
  getId(): string;
20
22
  getIcon(): string;
@@ -73,6 +75,14 @@ export declare class RecommendationBlock extends Block {
73
75
  * in the document and finding the maximum existing ID + 1.
74
76
  */
75
77
  private _generateNextId;
78
+ /**
79
+ * Drops the "already fetched" claims of a block that arrived holding a baked id
80
+ * (saved module, paste, duplicate), so the settings panel reads its strategy again.
81
+ *
82
+ * Deferred a tick: the collision handlers rename a sibling first, and either sibling can be
83
+ * the renamed one depending on where the module was dropped (SD-149916).
84
+ */
85
+ private _scheduleArrivalReset;
76
86
  /**
77
87
  * Heals a duplicate that slipped past the create-time `_detectDuplicate`
78
88
  * scan (or that was persisted by an older template before SD-142352).
@@ -426,6 +426,10 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
426
426
  /**
427
427
  * Deep-clones a block's state and mirrors its campaign URL under a new id.
428
428
  * Invoked from `onCreated` when a duplicate is detected (SD-142352).
429
+ *
430
+ * The clone keeps the source's data but drops its "already fetched / already
431
+ * synced" claims for strategy blocks, so it fetches again like a fresh drop
432
+ * (SD-149916).
429
433
  */
430
434
  cloneBlockState(sourceId: number, newId: number): void;
431
435
  /**
@@ -481,6 +485,12 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
481
485
  openRemoveStrategyModal(): void;
482
486
  closeRemoveStrategyModal(): void;
483
487
  fetchStrategies(): Promise<void>;
488
+ /**
489
+ * Clears what a block inherited from the id it arrived holding, so the panel fetches for it.
490
+ * A re-dropped saved module can keep the source's id — and its "already fetched" entry —
491
+ * while the strategy's product count has moved on (SD-149916).
492
+ */
493
+ resetArrivedBlock(id: number): void;
484
494
  invalidateStrategies(): void;
485
495
  /**
486
496
  * Opens a Smart Recommender strategy page in a new tab and refreshes the list every time
@@ -493,8 +503,9 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
493
503
  * leave that one missing from the list. It disarms on the first return AFTER that tab is
494
504
  * closed instead, so an ordinary alt-tab later in the session costs nothing. Armed only
495
505
  * once the user actually opens a strategy page, so anyone who never does pays nothing.
506
+ * @returns whether a tab actually opened, so callers can skip work that assumes one did
496
507
  */
497
- _armStrategyPageReturn(url: string): void;
508
+ _armStrategyPageReturn(url: string): boolean;
498
509
  /**
499
510
  * Reloads the products of the block the user is on, so an edit made in the strategy tab
500
511
  * lands on the canvas instead of waiting for the assign drawer to be reopened.
@@ -509,12 +520,12 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
509
520
  * Opens Smart Recommender's strategy creation page. Every return refreshes the list so the
510
521
  * new strategy is offered; the user picks it themselves.
511
522
  */
512
- openStrategyCreation(): void;
523
+ openStrategyCreation(): boolean;
513
524
  /**
514
525
  * Opens the strategy's edit page. Every return refreshes the list so the block reflects the
515
526
  * edited configuration.
516
527
  */
517
- openStrategyEdit(strategyId: string | number): void;
528
+ openStrategyEdit(strategyId: string | number): boolean;
518
529
  resolveStrategyUrlTemplate(strategy: RrsStrategy, currency: string): Promise<string>;
519
530
  assignStrategy(strategy: RrsStrategy): void;
520
531
  removeStrategy(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.18.2-beta.d3f87c7",
3
+ "version": "3.18.2-beta.dfce34a",
4
4
  "description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
5
5
  "main": "./dist/guido.umd.cjs",
6
6
  "module": "./dist/library.js",