@useinsider/guido 3.12.0-beta.841bb6a → 3.12.0-beta.86e34da

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 (90) hide show
  1. package/dist/@types/config/schemas.js +9 -4
  2. package/dist/components/Guido.vue.js +6 -6
  3. package/dist/components/Guido.vue2.js +50 -49
  4. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +1 -1
  5. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +8 -8
  6. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  7. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +10 -10
  8. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.js +38 -0
  9. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue2.js +271 -0
  10. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.js +18 -0
  11. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue2.js +42 -0
  12. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.js +20 -0
  13. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue2.js +18 -0
  14. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.js +20 -0
  15. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue2.js +60 -0
  16. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.js +19 -0
  17. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue2.js +19 -0
  18. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.js +22 -0
  19. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue2.js +29 -0
  20. package/dist/composables/usePreviewInteractionGuard.js +36 -11
  21. package/dist/composables/useRecommendation.js +56 -40
  22. package/dist/composables/useRecommendationPreview.js +61 -60
  23. package/dist/composables/useStrategyFilters.js +16 -0
  24. package/dist/config/migrator/recommendationMigrator.js +7 -4
  25. package/dist/enums/date.js +4 -3
  26. package/dist/enums/extensions/filteringV2.js +1024 -0
  27. package/dist/enums/extensions/recommendationBlock.js +45 -24
  28. package/dist/enums/extensions/strategyDetail.js +148 -0
  29. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  30. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +12 -11
  31. package/dist/extensions/Blocks/Recommendation/controls/blockBackground/index.js +10 -0
  32. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +141 -123
  33. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +285 -0
  34. package/dist/extensions/Blocks/Recommendation/extension.js +23 -21
  35. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +16 -2
  36. package/dist/extensions/Blocks/Recommendation/recommendation.css.js +257 -0
  37. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +41 -40
  38. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +460 -113
  39. package/dist/extensions/Blocks/Recommendation/utils/strategyHumanizer.js +97 -0
  40. package/dist/extensions/Blocks/Recommendation/utils/strategyNavigation.js +8 -0
  41. package/dist/extensions/Blocks/Recommendation/utils/strategySummary.js +95 -0
  42. package/dist/extensions/Blocks/Recommendation/utils/strategyUrl.js +43 -0
  43. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +24 -10
  44. package/dist/extensions/Blocks/Unsubscribe/utils/constants.js +3 -2
  45. package/dist/extensions/Blocks/common-control.js +2 -1
  46. package/dist/extensions/Blocks/controlFactories.js +125 -75
  47. package/dist/guido.css +1 -1
  48. package/dist/services/recommendationApi.js +120 -32
  49. package/dist/services/stripoApi.js +18 -18
  50. package/dist/src/@types/config/schemas.d.ts +10 -0
  51. package/dist/src/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.d.ts +2 -0
  52. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.d.ts +43 -0
  53. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.d.ts +29 -0
  54. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.d.ts +40 -0
  55. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.d.ts +38 -0
  56. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.d.ts +51 -0
  57. package/dist/src/composables/useConfig.d.ts +2 -0
  58. package/dist/src/composables/usePreviewInteractionGuard.d.ts +1 -1
  59. package/dist/src/composables/useRecommendation.d.ts +2 -0
  60. package/dist/src/composables/useStrategyFilters.d.ts +24 -0
  61. package/dist/src/enums/extensions/filteringV2.d.ts +72 -0
  62. package/dist/src/enums/extensions/recommendationBlock.d.ts +33 -0
  63. package/dist/src/enums/extensions/strategyDetail.d.ts +90 -0
  64. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +1 -0
  65. package/dist/src/extensions/Blocks/Recommendation/controls/blockBackground/index.d.ts +14 -0
  66. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  67. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +10 -4
  68. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +75 -0
  69. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +903 -1
  70. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +7 -0
  71. package/dist/src/extensions/Blocks/Recommendation/utils/strategyHumanizer.d.ts +50 -0
  72. package/dist/src/extensions/Blocks/Recommendation/utils/strategyNavigation.d.ts +10 -0
  73. package/dist/src/extensions/Blocks/Recommendation/utils/strategySummary.d.ts +34 -0
  74. package/dist/src/extensions/Blocks/Recommendation/utils/strategyUrl.d.ts +51 -0
  75. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +8 -1
  76. package/dist/src/extensions/Blocks/Unsubscribe/utils/constants.d.ts +1 -0
  77. package/dist/src/extensions/Blocks/common-control.d.ts +2 -1
  78. package/dist/src/extensions/Blocks/controlFactories.d.ts +36 -0
  79. package/dist/src/mock/api/recommendation-strategies.d.ts +2 -0
  80. package/dist/src/services/recommendationApi.d.ts +9 -1
  81. package/dist/src/stores/config.d.ts +18 -0
  82. package/dist/src/utils/genericUtil.d.ts +9 -0
  83. package/dist/src/utils/urlSchemes.d.ts +6 -0
  84. package/dist/static/styles/components/base-input.css.js +6 -7
  85. package/dist/utils/dateUtil.js +23 -10
  86. package/dist/utils/genericUtil.js +10 -1
  87. package/dist/utils/pairProductVariables.js +43 -44
  88. package/dist/utils/templatePreparation.js +70 -57
  89. package/dist/utils/urlSchemes.js +4 -0
  90. package/package.json +1 -1
@@ -0,0 +1,285 @@
1
+ var $ = Object.defineProperty;
2
+ var O = (g, h, t) => h in g ? $(g, h, { enumerable: !0, configurable: !0, writable: !0, value: t }) : g[h] = t;
3
+ var u = (g, h, t) => O(g, typeof h != "symbol" ? h + "" : h, t);
4
+ import { useTranslations as f } from "../../../../../composables/useTranslations.js";
5
+ import { STRATEGY_PAGES as R } from "../../../../../enums/extensions/recommendationBlock.js";
6
+ import { formatShortDate as I } from "../../../../../utils/dateUtil.js";
7
+ import { UEAttr as b } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
8
+ import { CommonControl as L } from "../../../common-control.js";
9
+ import { useRecommendationExtensionStore as A } from "../../store/recommendation.js";
10
+ import { navigateToStrategyPage as w } from "../../utils/strategyNavigation.js";
11
+ import { buildStrategySummary as H } from "../../utils/strategySummary.js";
12
+ const D = "recommendation-strategy-control", T = {
13
+ CREATE_BUTTON: "strategyCreate",
14
+ DETAILS_BUTTON: "strategyDetails"
15
+ }, E = "guido__btn-strategy-create", C = "guido__btn-strategy-details", s = {
16
+ ROOT: "data-strategy-control",
17
+ TRIGGER: "data-strategy-trigger",
18
+ VALUE: "data-strategy-value",
19
+ MENU: "data-strategy-menu",
20
+ SEARCH: "data-strategy-search",
21
+ LIST: "data-strategy-list",
22
+ OPTION_ID: "data-strategy-id",
23
+ CARD: "data-strategy-card",
24
+ VIEW_MORE: "data-strategy-view-more"
25
+ }, _ = 8, M = 150, N = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zm0-2a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm-1-7a1 1 0 1 1 2 0v4a1 1 0 1 1-2 0v-4zm1-5a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path></svg>', G = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 15.999a1.042 1.042 0 0 1-.784-.305l-5.91-5.91a1.045 1.045 0 1 1 1.478-1.478L12 13.522l5.216-5.216a1.045 1.045 0 1 1 1.478 1.478l-5.91 5.91a1.042 1.042 0 0 1-.784.305z"></path></svg>', o = (g) => g.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
26
+ class j extends L {
27
+ constructor() {
28
+ super(...arguments);
29
+ u(this, "store", A());
30
+ /**
31
+ * InOne's dictionary, not Stripo's. Every dotted key here
32
+ * (`discovery-strategy.*`, `gpt-app-builder.*`) lives in the host panel's
33
+ * translations, which `this.api.translate` does not read — that one is only
34
+ * for the plain-English control labels the rest of this panel uses.
35
+ */
36
+ u(this, "trans", f());
37
+ u(this, "events", null);
38
+ u(this, "unsubscribeStore", null);
39
+ u(this, "searchTerm", "");
40
+ u(this, "isOpen", !1);
41
+ u(this, "cardHideTimer", null);
42
+ }
43
+ getId() {
44
+ return D;
45
+ }
46
+ getTemplate() {
47
+ return `
48
+ <div class="strategy-control" ${s.ROOT}>
49
+ ${this._GuOneColumn([
50
+ this._GuLabel({ text: this.api.translate("Recommendation Strategy") }),
51
+ // The menu is absolutely positioned against THIS wrapper. Without
52
+ // it the menu is a grid item of the column container and its
53
+ // static position resolves to the container's top, so it opens
54
+ // over the label instead of under the trigger.
55
+ `<div class="strategy-control__field">
56
+ <div class="strategy-control__row">
57
+ <button type="button" class="strategy-select" ${s.TRIGGER}>
58
+ <span class="strategy-select__value" ${s.VALUE}></span>
59
+ <span class="strategy-select__caret">${G}</span>
60
+ </button>
61
+ ${this._GuIconButton({
62
+ name: T.DETAILS_BUTTON,
63
+ icon: "strategy-settings-icon",
64
+ className: "strategy-control__details",
65
+ id: C
66
+ })}
67
+ </div>
68
+ <div class="strategy-menu" ${s.MENU} hidden>
69
+ <div class="strategy-menu__search">
70
+ <input type="text" class="strategy-menu__search-input" ${s.SEARCH}
71
+ placeholder="${o(this.trans("discovery-strategy.search"))}">
72
+ </div>
73
+ <ul class="strategy-menu__list" ${s.LIST}></ul>
74
+ </div>
75
+ </div>`,
76
+ `<div class="strategy-control__create-row">
77
+ ${this._GuButton({
78
+ name: T.CREATE_BUTTON,
79
+ label: this.api.translate("Create New Strategy"),
80
+ id: E
81
+ })}
82
+ </div>`
83
+ ])}
84
+ <div class="strategy-card" ${s.CARD} hidden></div>
85
+ </div>
86
+ `;
87
+ }
88
+ onRender() {
89
+ this._setupEventListeners(), this._subscribeToStoreChanges(), this._render();
90
+ }
91
+ onDestroy() {
92
+ var t, e;
93
+ this._cancelHideCard(), (t = this.events) == null || t.abort(), this.events = null, (e = this.unsubscribeStore) == null || e.call(this), this.unsubscribeStore = null, this.isOpen = !1, this.searchTerm = "", super.onDestroy();
94
+ }
95
+ _root() {
96
+ var t;
97
+ return ((t = this.getContainer()) == null ? void 0 : t.querySelector(`[${s.ROOT}]`)) ?? null;
98
+ }
99
+ /**
100
+ * All listeners are delegated onto nodes that survive `innerHTML` rebuilds and
101
+ * are torn down together by the AbortController — the option rows themselves
102
+ * are replaced on every render and must never own a listener.
103
+ */
104
+ _setupEventListeners() {
105
+ var d, c, m, p, y, v;
106
+ (d = this.events) == null || d.abort(), this.events = new AbortController();
107
+ const { signal: t } = this.events, e = this._root();
108
+ if (!e)
109
+ return;
110
+ (c = e.querySelector(`[${s.TRIGGER}]`)) == null || c.addEventListener("click", () => this._toggleMenu(), { signal: t }), (m = e.querySelector(`#${C}`)) == null || m.addEventListener("click", () => this.store.openStrategyDrawer(), { signal: t }), (p = e.querySelector(`#${E}`)) == null || p.addEventListener("click", () => w(R.CREATE), { signal: t }), (y = e.querySelector(`[${s.SEARCH}]`)) == null || y.addEventListener("input", (l) => {
111
+ this.searchTerm = l.target.value, this._renderOptions();
112
+ }, { signal: t });
113
+ const r = e.querySelector(`[${s.LIST}]`);
114
+ r == null || r.addEventListener("click", (l) => {
115
+ const n = l.target.closest(`[${s.OPTION_ID}]`), a = n == null ? void 0 : n.getAttribute(s.OPTION_ID);
116
+ a && (this._closeMenu(), this.store.selectStrategy(a));
117
+ }, { signal: t }), r == null || r.addEventListener("mouseenter", (l) => {
118
+ var a, S;
119
+ const n = (S = (a = l.target) == null ? void 0 : a.closest) == null ? void 0 : S.call(a, `[${s.OPTION_ID}]`);
120
+ n && this._showCard(n);
121
+ }, { capture: !0, signal: t }), r == null || r.addEventListener("mouseleave", () => this._scheduleHideCard(), { signal: t });
122
+ const i = e.querySelector(`[${s.CARD}]`);
123
+ i == null || i.addEventListener("mouseenter", () => this._cancelHideCard(), { signal: t }), i == null || i.addEventListener("mouseleave", () => this._hideCard(), { signal: t }), i == null || i.addEventListener("click", (l) => {
124
+ const n = l.target.closest(`[${s.VIEW_MORE}]`), a = n == null ? void 0 : n.getAttribute(s.VIEW_MORE);
125
+ a && (this._closeMenu(), a !== this.store.recommendationConfigs.strategyId && this.store.selectStrategy(a), this.store.openStrategyDrawer());
126
+ }, { signal: t }), (v = this.getContainer()) == null || v.addEventListener("mousedown", (l) => {
127
+ var n, a;
128
+ this.isOpen && !((a = (n = l.target).closest) != null && a.call(n, `[${s.ROOT}]`)) && this._closeMenu();
129
+ }, { signal: t });
130
+ }
131
+ /**
132
+ * Keeps the trigger label and the gear's enabled state in step with the store.
133
+ *
134
+ * Both depend on data that arrives after the first render: the strategy listing
135
+ * is fetched asynchronously, and `strategyId` changes when the user picks,
136
+ * duplicates, or switches blocks.
137
+ */
138
+ _subscribeToStoreChanges() {
139
+ var e;
140
+ (e = this.unsubscribeStore) == null || e.call(this);
141
+ let t = this._stateKey();
142
+ this.unsubscribeStore = this.store.$subscribe(() => {
143
+ const r = this._stateKey();
144
+ r !== t && (t = r, this._render());
145
+ });
146
+ }
147
+ /**
148
+ * Fingerprint of everything the rendered markup depends on.
149
+ *
150
+ * Ids rather than a count: a refetch that renames a strategy or swaps one for
151
+ * another of the same length would otherwise leave the trigger and the card
152
+ * showing stale copy until some unrelated store change happened to repaint.
153
+ */
154
+ _stateKey() {
155
+ return [
156
+ this.store.recommendationConfigs.strategyId,
157
+ this.store.strategiesLoading,
158
+ this.store.strategies.map((t) => `${t.strategy_id}:${t.name}`).join(",")
159
+ ].join("|");
160
+ }
161
+ _render() {
162
+ this._renderTrigger(), this._renderOptions(), this.api.setUIEAttribute(
163
+ T.DETAILS_BUTTON,
164
+ b.BUTTON.disabled,
165
+ this.store.getSelectedStrategy ? "false" : "true"
166
+ );
167
+ }
168
+ _renderTrigger() {
169
+ var r;
170
+ const t = (r = this._root()) == null ? void 0 : r.querySelector(`[${s.VALUE}]`);
171
+ if (!t)
172
+ return;
173
+ const e = this.store.getSelectedStrategy;
174
+ t.textContent = (e == null ? void 0 : e.name) ?? this.trans("smart-recommender.select-recommendation-strategy"), t.classList.toggle("strategy-select__value--placeholder", !e), t.classList.toggle("strategy-select__value--missing", this.store.hasMissingStrategy);
175
+ }
176
+ _renderOptions() {
177
+ var i;
178
+ const t = (i = this._root()) == null ? void 0 : i.querySelector(`[${s.LIST}]`);
179
+ if (!t)
180
+ return;
181
+ if (this._hideCard(), this.store.strategiesLoading) {
182
+ t.innerHTML = `<li class="strategy-menu__empty">${o(
183
+ this.trans("newsletter.loading")
184
+ )}</li>`;
185
+ return;
186
+ }
187
+ const e = this._filteredStrategies();
188
+ if (!e.length) {
189
+ t.innerHTML = `<li class="strategy-menu__empty">${o(
190
+ this.trans("discovery-strategy.no-data-title-text")
191
+ )}</li>`;
192
+ return;
193
+ }
194
+ const r = this.store.recommendationConfigs.strategyId;
195
+ t.innerHTML = e.map((d) => {
196
+ const c = String(d.strategy_id);
197
+ return `<li class="strategy-menu__option${c === r ? " strategy-menu__option--selected" : ""}"
198
+ ${s.OPTION_ID}="${o(c)}">${o(d.name)}</li>`;
199
+ }).join("");
200
+ }
201
+ /**
202
+ * Client-side filtering: the listing is fetched whole and cached, so narrowing
203
+ * it here avoids a request per keystroke.
204
+ */
205
+ _filteredStrategies() {
206
+ const t = this.searchTerm.trim().toLowerCase();
207
+ return t ? this.store.strategies.filter((e) => e.name.toLowerCase().includes(t)) : this.store.strategies;
208
+ }
209
+ _toggleMenu() {
210
+ var e, r, i;
211
+ if (this.isOpen) {
212
+ this._closeMenu();
213
+ return;
214
+ }
215
+ this.isOpen = !0;
216
+ const t = this._root();
217
+ (e = t == null ? void 0 : t.querySelector(`[${s.MENU}]`)) == null || e.removeAttribute("hidden"), (r = t == null ? void 0 : t.querySelector(`[${s.TRIGGER}]`)) == null || r.classList.add("strategy-select--open"), this._renderOptions(), (i = t == null ? void 0 : t.querySelector(`[${s.SEARCH}]`)) == null || i.focus();
218
+ }
219
+ _closeMenu() {
220
+ var r, i;
221
+ this.isOpen = !1, this.searchTerm = "", this._hideCard();
222
+ const t = this._root(), e = t == null ? void 0 : t.querySelector(`[${s.SEARCH}]`);
223
+ e && (e.value = ""), (r = t == null ? void 0 : t.querySelector(`[${s.MENU}]`)) == null || r.setAttribute("hidden", ""), (i = t == null ? void 0 : t.querySelector(`[${s.TRIGGER}]`)) == null || i.classList.remove("strategy-select--open");
224
+ }
225
+ /**
226
+ * The hover card is `position: fixed`, so it can sit beside the panel instead of
227
+ * being clipped by it. Placed once per hover — the list cannot scroll under the
228
+ * pointer without the pointer leaving, so there is nothing to keep in sync.
229
+ */
230
+ _showCard(t) {
231
+ var y;
232
+ const e = (y = this._root()) == null ? void 0 : y.querySelector(`[${s.CARD}]`), r = t.getAttribute(s.OPTION_ID), i = this.store.strategies.find((v) => String(v.strategy_id) === r);
233
+ if (!e || !i)
234
+ return;
235
+ this._cancelHideCard(), e.innerHTML = this._buildCardHtml(i), e.removeAttribute("hidden");
236
+ const d = t.getBoundingClientRect(), c = e.getBoundingClientRect(), m = d.left - c.width - _ >= _ ? d.left - c.width - _ : d.right + _, p = window.innerHeight - c.height - _;
237
+ e.style.left = `${Math.max(_, m)}px`, e.style.top = `${Math.min(Math.max(_, d.top), Math.max(_, p))}px`;
238
+ }
239
+ /** Grace period for the pointer to cross the gap between option and card. */
240
+ _scheduleHideCard() {
241
+ this._cancelHideCard(), this.cardHideTimer = setTimeout(() => this._hideCard(), M);
242
+ }
243
+ _cancelHideCard() {
244
+ this.cardHideTimer && (clearTimeout(this.cardHideTimer), this.cardHideTimer = null);
245
+ }
246
+ _hideCard() {
247
+ var t, e;
248
+ this._cancelHideCard(), (e = (t = this._root()) == null ? void 0 : t.querySelector(`[${s.CARD}]`)) == null || e.setAttribute("hidden", "");
249
+ }
250
+ /**
251
+ * Mirrors the design system's `InInfoBox`: coloured top rule, heading +
252
+ * "last updated" details, content, then a divided footer holding the View
253
+ * More text button. The component itself cannot render here, so the markup
254
+ * and the CSS reproduce its structure and tokens rather than approximating
255
+ * them — the values in `recommendation.css` are read from its stylesheet.
256
+ */
257
+ _buildCardHtml(t) {
258
+ const { trans: e } = this, r = H(t, e).map((i) => `
259
+ <div class="strategy-card__row">
260
+ <p class="strategy-card__row-title">${o(i.title)}</p>
261
+ <p class="strategy-card__row-value">${o(i.value)}</p>
262
+ </div>
263
+ `).join("");
264
+ return `
265
+ <div class="strategy-card__body">
266
+ <p class="strategy-card__title">${o(t.name)}</p>
267
+ <p class="strategy-card__updated">${o(e("gpt-app-builder.strategy-last-updated", {
268
+ date: I(t.updated_at)
269
+ }))}</p>
270
+ <div class="strategy-card__rows">${r}</div>
271
+ </div>
272
+ <div class="strategy-card__footer">
273
+ <button type="button" class="strategy-card__more"
274
+ ${s.VIEW_MORE}="${o(String(t.strategy_id))}">
275
+ ${N}
276
+ <span>${o(e("gpt-app-builder.view-more"))}</span>
277
+ </button>
278
+ </div>
279
+ `;
280
+ }
281
+ }
282
+ export {
283
+ D as STRATEGY_CONTROL_ID,
284
+ j as StrategyControl
285
+ };
@@ -1,4 +1,4 @@
1
- import { ExtensionBuilder as r } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
1
+ import { ExtensionBuilder as t } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
2
  import { RecommendationBlock as m } from "./block.js";
3
3
  import n from "./canvasPreview.css.js";
4
4
  import { RecommendationBlockControl as i } from "./controls/main/index.js";
@@ -6,42 +6,44 @@ import "./constants/selectors.js";
6
6
  import "./store/recommendation.js";
7
7
  import "./utils/captureStyleTemplates.js";
8
8
  import { NameControls as e } from "./controls/name/index.js";
9
- import { PriceControls as s } from "./controls/price/index.js";
10
- import { OldPriceControls as l } from "./controls/oldPrice/index.js";
9
+ import { PriceControls as l } from "./controls/price/index.js";
10
+ import { OldPriceControls as s } from "./controls/oldPrice/index.js";
11
11
  import { OmnibusPriceControls as p } from "./controls/omnibusPrice/index.js";
12
12
  import { OmnibusDiscountControls as a } from "./controls/omnibusDiscount/index.js";
13
13
  import { ButtonControls as c } from "./controls/button/index.js";
14
- import { ImageControls as f } from "./controls/image/index.js";
15
- import { CustomAttributeControls as C } from "./controls/customAttribute/index.js";
14
+ import { ImageControls as C } from "./controls/image/index.js";
15
+ import { CustomAttributeControls as f } from "./controls/customAttribute/index.js";
16
16
  import { SpacingControl as d } from "./controls/spacing/index.js";
17
- import { CardBackgroundColorControl as u } from "./controls/cardBackground/index.js";
18
- import { RecommendationCardCompositionControl as g } from "./controls/cardComposition/index.js";
19
- import { SyncInfoMessageControl as y } from "./controls/syncInfoMessage.js";
17
+ import { BlockBackgroundColorControl as u } from "./controls/blockBackground/index.js";
18
+ import { CardBackgroundColorControl as g } from "./controls/cardBackground/index.js";
19
+ import { RecommendationCardCompositionControl as y } from "./controls/cardComposition/index.js";
20
+ import { SyncInfoMessageControl as B } from "./controls/syncInfoMessage.js";
20
21
  import { RecommendationIconsRegistry as P } from "./iconsRegistry.js";
21
22
  import R from "./recommendation.css.js";
22
23
  import { SettingsPanel as S } from "./settingsPanel.js";
23
- const w = [
24
+ const k = [
24
25
  e,
25
- s,
26
26
  l,
27
+ s,
27
28
  p,
28
29
  a,
29
30
  c,
30
- f,
31
- C
32
- ], B = [
31
+ C,
32
+ f
33
+ ], w = [
33
34
  i,
34
35
  u,
35
- d,
36
36
  g,
37
- y
37
+ d,
38
+ y,
39
+ B
38
40
  ], b = [
39
- ...B,
40
- ...w.flatMap((o) => Object.values(o))
41
- ], U = b.reduce(
42
- (o, t) => o.addControl(t),
43
- new r().addBlock(m).withSettingsPanelRegistry(S)
41
+ ...w,
42
+ ...k.flatMap((o) => Object.values(o))
43
+ ], W = b.reduce(
44
+ (o, r) => o.addControl(r),
45
+ new t().addBlock(m).withSettingsPanelRegistry(S)
44
46
  ).addStyles(R).withPreviewStyles(n).withIconsRegistry(P).build();
45
47
  export {
46
- U as default
48
+ W as default
47
49
  };
@@ -1,6 +1,6 @@
1
1
  import r from "../../../static/assets/info.svg.js";
2
2
  import { IconsRegistry as C } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
3
- class h extends C {
3
+ class l extends C {
4
4
  registerIconsSvg(t) {
5
5
  t["recommendation-icon"] = `
6
6
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
@@ -74,9 +74,23 @@ class h extends C {
74
74
  <path d="M17 2V18C17 18.5523 16.5523 19 16 19H10V1H16C16.5523 1 17 1.44772 17 2Z" stroke="currentColor"
75
75
  stroke-width="2" fill="none"/>
76
76
  </svg>
77
+ `, t["strategy-settings-icon"] = `
78
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"
79
+ fill="currentColor">
80
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M14 4a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v.757c0
81
+ .891-1.077 1.337-1.707.708l-.536-.536a1 1 0 0 0-1.414 0L4.93 6.343a1 1 0 0 0 0
82
+ 1.414l.535.536c.63.63.184 1.707-.707 1.707H4a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h.757c.891 0
83
+ 1.337 1.077.708 1.707l-.536.536a1 1 0 0 0 0 1.414l1.414 1.414a1 1 0 0 0 1.414
84
+ 0l.536-.535c.63-.63 1.707-.184 1.707.707V20a1 1 0 0 0 1 1h2a1 1 0 0 0
85
+ 1-1v-.757c0-.891 1.077-1.337 1.707-.707l.536.535a1 1 0 0 0 1.414 0l1.414-1.414a1 1 0 0 0
86
+ 0-1.414l-.536-.536c-.63-.63-.183-1.707.708-1.707H20a1 1 0 0 0 1-1v-2a1 1 0 0
87
+ 0-1-1h-.757c-.891 0-1.337-1.077-.707-1.707l.535-.536a1 1 0 0 0
88
+ 0-1.414L17.657 4.93a1 1 0 0 0-1.414 0l-.536.536c-.63.63-1.707.183-1.707-.708V4zm3 8a5 5 0
89
+ 1 1-10 0 5 5 0 0 1 10 0zm-5 2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path>
90
+ </svg>
77
91
  `, t["migration-info-icon"] = r;
78
92
  }
79
93
  }
80
94
  export {
81
- h as RecommendationIconsRegistry
95
+ l as RecommendationIconsRegistry
82
96
  };
@@ -289,6 +289,263 @@ ue-orderable.orderable-disabled .droppable-icon {
289
289
  font-weight: 400;
290
290
  line-height: 16px;
291
291
  }
292
+
293
+ /*
294
+ * Recommendation Strategy picker (controls/main/strategy.ts).
295
+ *
296
+ * Lives inside the settings panel, so it scrolls with it and needs no positioning
297
+ * code. Only the hover card escapes the panel, via \`position: fixed\`.
298
+ *
299
+ * The values below are the design system's own, read from its stylesheets
300
+ * (\`combobox.css\` for the trigger, \`InInfoBox\`'s CSS for the card). The \`In*\`
301
+ * components themselves cannot render in this shadow root — they are styled from
302
+ * the host document, and copying their stylesheets across drags normalize.css and
303
+ * a global \`*\`/\`svg\` reset into Stripo's panel.
304
+ */
305
+
306
+ /*
307
+ * Anchors the dropdown. Without a positioned wrapper the menu is a grid item of
308
+ * the column container, and its absolute static position resolves to that
309
+ * container's top — so it opens over the label instead of under the trigger.
310
+ */
311
+ .strategy-control__field {
312
+ position: relative;
313
+ }
314
+
315
+ .strategy-control__row {
316
+ display: flex;
317
+ align-items: center;
318
+ gap: 8px;
319
+ }
320
+
321
+ /*
322
+ * Same tokens as \`ue-select .button\` in components/combobox.css, so the strategy
323
+ * trigger is indistinguishable from Recommendation Locale / Currency beside it.
324
+ * It cannot simply reuse that rule: the selector is scoped to \`ue-select\`, and a
325
+ * Stripo select cannot render this control's per-option hover card.
326
+ */
327
+ .strategy-select {
328
+ display: flex;
329
+ flex: 1;
330
+ align-items: center;
331
+ justify-content: space-between;
332
+ gap: 8px;
333
+ min-width: 0;
334
+ min-height: 40px;
335
+ padding: 8px;
336
+ overflow: hidden;
337
+ background-color: var(--guido-color-gray-0);
338
+ border: 1px solid var(--guido-color-gray-300);
339
+ border-radius: 4px;
340
+ box-shadow: none;
341
+ cursor: pointer;
342
+ font-size: 13px;
343
+ color: var(--guido-color-gray-800);
344
+ text-align: left;
345
+ }
346
+
347
+ .strategy-select:hover {
348
+ border-color: var(--guido-color-primary-500);
349
+ }
350
+
351
+ /* Matches the focus ring Stripo paints on an open select. */
352
+ .strategy-select--open {
353
+ border-color: var(--guido-color-primary-500);
354
+ box-shadow: 0 0 0 3px var(--guido-color-primary-200);
355
+ }
356
+
357
+ .strategy-select__value {
358
+ overflow: hidden;
359
+ text-overflow: ellipsis;
360
+ white-space: nowrap;
361
+ }
362
+
363
+ .strategy-select__value--placeholder {
364
+ color: var(--guido-color-gray-700);
365
+ }
366
+
367
+ .strategy-select__value--missing {
368
+ color: var(--guido-color-danger-500);
369
+ }
370
+
371
+ .strategy-select__caret {
372
+ display: flex;
373
+ flex: none;
374
+ color: var(--guido-color-gray-800);
375
+ }
376
+
377
+ /* The gear button sits beside the trigger and must not stretch with it. */
378
+ .strategy-control__details {
379
+ flex: none;
380
+ }
381
+
382
+ .strategy-menu {
383
+ position: absolute;
384
+ z-index: 5;
385
+ top: 100%;
386
+ right: 0;
387
+ left: 0;
388
+ margin-top: 4px;
389
+ overflow: hidden;
390
+ background: var(--guido-color-white);
391
+ border: 1px solid var(--guido-color-gray-300);
392
+ border-radius: 4px;
393
+ box-shadow: var(--guido-box-shadow);
394
+ }
395
+
396
+ .strategy-menu[hidden] {
397
+ display: none;
398
+ }
399
+
400
+ .strategy-menu__search {
401
+ padding: 8px;
402
+ border-bottom: 1px solid var(--guido-color-gray-300);
403
+ }
404
+
405
+ .strategy-menu__search-input {
406
+ box-sizing: border-box;
407
+ width: 100%;
408
+ height: 32px;
409
+ padding: 4px 8px;
410
+ border: 1px solid var(--guido-color-gray-300);
411
+ border-radius: 4px;
412
+ font: inherit;
413
+ }
414
+
415
+ .strategy-menu__list {
416
+ max-height: 240px;
417
+ margin: 0;
418
+ padding: 0;
419
+ overflow-y: auto;
420
+ list-style: none;
421
+ }
422
+
423
+ .strategy-menu__option {
424
+ overflow: hidden;
425
+ padding: 8px 12px;
426
+ cursor: pointer;
427
+ font-size: 13px;
428
+ text-overflow: ellipsis;
429
+ white-space: nowrap;
430
+ }
431
+
432
+ .strategy-menu__option:hover {
433
+ background: var(--guido-color-gray-100);
434
+ }
435
+
436
+ .strategy-menu__option--selected {
437
+ color: var(--guido-color-primary-500);
438
+ background: var(--guido-color-primary-0);
439
+ }
440
+
441
+ .strategy-menu__empty {
442
+ padding: 12px;
443
+ color: var(--guido-color-gray-600);
444
+ font-size: 13px;
445
+ text-align: center;
446
+ }
447
+
448
+ .strategy-control__create-row {
449
+ margin-top: 8px;
450
+ }
451
+
452
+ /*
453
+ * \`_GuButton\` renders <ue-button> > .control-shadow-wrapper > button.button, and
454
+ * every level is shrink-to-fit. Stretching only the innermost one leaves the
455
+ * button its intrinsic width, which is why the design's full-width row needs all
456
+ * three.
457
+ */
458
+ .strategy-control__create-row ue-button,
459
+ .strategy-control__create-row .control-shadow-wrapper,
460
+ .strategy-control__create-row .button {
461
+ display: block;
462
+ width: 100%;
463
+ }
464
+
465
+ /*
466
+ * Hover card. Reproduces the design system's \`InInfoBox\` — same 240px medium
467
+ * width, 4px information-blue top rule, 16px body, and a divided footer for the
468
+ * View More button.
469
+ *
470
+ * \`position: fixed\` so the card sits beside the narrow panel instead of being
471
+ * clipped by it.
472
+ */
473
+ .strategy-card {
474
+ position: fixed;
475
+ z-index: 10;
476
+ width: 240px;
477
+ background: var(--guido-color-white);
478
+ border-top: 4px solid var(--guido-color-border-onpage-message-info);
479
+ border-radius: 4px;
480
+ box-shadow: 0 4px 12px rgba(57, 57, 57, 0.15);
481
+ }
482
+
483
+ .strategy-card[hidden] {
484
+ display: none;
485
+ }
486
+
487
+ .strategy-card__body {
488
+ padding: 16px;
489
+ }
490
+
491
+ .strategy-card__title {
492
+ margin: 0;
493
+ color: var(--guido-color-gray-800);
494
+ font-size: 16px;
495
+ font-weight: 600;
496
+ line-height: 24px;
497
+ word-break: break-word;
498
+ }
499
+
500
+ .strategy-card__updated {
501
+ margin: 4px 0 0;
502
+ color: var(--guido-color-gray-600);
503
+ font-size: 13px;
504
+ font-weight: 400;
505
+ line-height: 16px;
506
+ }
507
+
508
+ .strategy-card__rows {
509
+ margin-top: 16px;
510
+ }
511
+
512
+ .strategy-card__row + .strategy-card__row {
513
+ margin-top: 12px;
514
+ }
515
+
516
+ .strategy-card__row-title {
517
+ margin: 0;
518
+ color: var(--guido-color-gray-600);
519
+ font-size: 12px;
520
+ line-height: 16px;
521
+ }
522
+
523
+ .strategy-card__row-value {
524
+ margin: 0;
525
+ color: var(--guido-color-gray-800);
526
+ font-size: 13px;
527
+ line-height: 18px;
528
+ word-break: break-word;
529
+ }
530
+
531
+ .strategy-card__footer {
532
+ padding: 8px 16px;
533
+ border-top: 1px solid var(--guido-color-gray-300);
534
+ }
535
+
536
+ .strategy-card__more {
537
+ display: flex;
538
+ align-items: center;
539
+ gap: 4px;
540
+ padding: 0;
541
+ color: var(--guido-color-primary-500);
542
+ background: none;
543
+ border: 0;
544
+ cursor: pointer;
545
+ font-size: 13px;
546
+ font-weight: 600;
547
+ line-height: 16px;
548
+ }
292
549
  `;
293
550
  export {
294
551
  n as default