@behio/storefront-sdk 0.38.0 → 0.42.0
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.
- package/dist/{chunk-FOUBL2EQ.js → chunk-3TPJG2WV.js} +568 -147
- package/dist/{chunk-MF5LMHZ4.mjs → chunk-QOZYQMK2.mjs} +566 -145
- package/dist/{client-Cb_eHKm9.d.mts → client-B0vlHxB1.d.mts} +312 -6
- package/dist/{client-Cb_eHKm9.d.ts → client-B0vlHxB1.d.ts} +312 -6
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +142 -40
- package/dist/index.mjs +111 -9
- package/dist/next.d.mts +1 -1
- package/dist/next.d.ts +1 -1
- package/dist/next.js +2 -2
- package/dist/next.mjs +1 -1
- package/dist/react.d.mts +3022 -4
- package/dist/react.d.ts +3022 -4
- package/dist/react.js +2584 -477
- package/dist/react.mjs +2090 -86
- package/package.json +1 -1
- package/dist/chunk-CZRSJULD.js +0 -118
- package/dist/chunk-QUU76QUB.mjs +0 -118
package/dist/react.js
CHANGED
|
@@ -1,24 +1,1809 @@
|
|
|
1
|
-
"use
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/react/index.ts
|
|
22
|
+
var react_exports = {};
|
|
23
|
+
__export(react_exports, {
|
|
24
|
+
BehioAnalyticsTracker: () => BehioAnalyticsTracker,
|
|
25
|
+
BehioProvider: () => BehioProvider,
|
|
26
|
+
CurrencySwitcher: () => CurrencySwitcher,
|
|
27
|
+
StorefrontScripts: () => StorefrontScripts,
|
|
28
|
+
cookieStorage: () => cookieStorage,
|
|
29
|
+
createMemoryStorage: () => createMemoryStorage,
|
|
30
|
+
detectStorage: () => detectStorage,
|
|
31
|
+
formatPrice: () => formatPrice,
|
|
32
|
+
generateVisitorId: () => generateVisitorId,
|
|
33
|
+
getStoredVisitorId: () => getStoredVisitorId,
|
|
34
|
+
grantAnalyticsConsent: () => grantAnalyticsConsent,
|
|
35
|
+
localStorageAdapter: () => localStorageAdapter,
|
|
36
|
+
memoryStorage: () => memoryStorage,
|
|
37
|
+
revokeAnalyticsConsent: () => revokeAnalyticsConsent,
|
|
38
|
+
trackEcommerceEvent: () => trackEcommerceEvent,
|
|
39
|
+
useAddressAutocomplete: () => useAddressAutocomplete,
|
|
40
|
+
useAddresses: () => useAddresses,
|
|
41
|
+
useAnalyticsEvents: () => useAnalyticsEvents,
|
|
42
|
+
useAuth: () => useAuth,
|
|
43
|
+
useBehio: () => useBehio,
|
|
44
|
+
useBehioClient: () => useBehioClient,
|
|
45
|
+
useBundle: () => useBundle,
|
|
46
|
+
useBundles: () => useBundles,
|
|
47
|
+
useCart: () => useCart,
|
|
48
|
+
useCartCount: () => useCartCount,
|
|
49
|
+
useCategories: () => useCategories,
|
|
50
|
+
useCategory: () => useCategory,
|
|
51
|
+
useCertificateVerification: () => useCertificateVerification,
|
|
52
|
+
useCheckout: () => useCheckout,
|
|
53
|
+
useCookieConsent: () => useCookieConsent,
|
|
54
|
+
useCourse: () => useCourse,
|
|
55
|
+
useCourseCertificates: () => useCourseCertificates,
|
|
56
|
+
useCourses: () => useCourses,
|
|
57
|
+
useCrossSell: () => useCrossSell,
|
|
58
|
+
useCurrency: () => useCurrency,
|
|
59
|
+
useCustomer: () => useCustomer,
|
|
60
|
+
useFacets: () => useFacets,
|
|
61
|
+
useFeatured: () => useFeatured,
|
|
62
|
+
useFilters: () => useFilters,
|
|
63
|
+
useGiftCardBalance: () => useGiftCardBalance,
|
|
64
|
+
useIsInWishlist: () => useIsInWishlist,
|
|
65
|
+
useLabels: () => useLabels,
|
|
66
|
+
useLessonComments: () => useLessonComments,
|
|
67
|
+
useLessonNote: () => useLessonNote,
|
|
68
|
+
useLessonQuiz: () => useLessonQuiz,
|
|
69
|
+
useLessonTutor: () => useLessonTutor,
|
|
70
|
+
useLookupReturnableOrder: () => useLookupReturnableOrder,
|
|
71
|
+
useLoyalty: () => useLoyalty,
|
|
72
|
+
useMenu: () => useMenu,
|
|
73
|
+
useNewsletterSubscribe: () => useNewsletterSubscribe,
|
|
74
|
+
useNewsletterUnsubscribe: () => useNewsletterUnsubscribe,
|
|
75
|
+
useNotifyWhenAvailable: () => useNotifyWhenAvailable,
|
|
76
|
+
useOrder: () => useOrder,
|
|
77
|
+
useOrderAccess: () => useOrderAccess,
|
|
78
|
+
useOrders: () => useOrders,
|
|
79
|
+
usePage: () => usePage,
|
|
80
|
+
usePages: () => usePages,
|
|
81
|
+
usePaymentMethods: () => usePaymentMethods,
|
|
82
|
+
usePersonalOffers: () => usePersonalOffers,
|
|
83
|
+
usePickupPoints: () => usePickupPoints,
|
|
84
|
+
useProduct: () => useProduct,
|
|
85
|
+
useProductGroup: () => useProductGroup,
|
|
86
|
+
useProductPromotions: () => useProductPromotions,
|
|
87
|
+
useProductReviews: () => useProductReviews,
|
|
88
|
+
useProducts: () => useProducts,
|
|
89
|
+
useQuoteStatus: () => useQuoteStatus,
|
|
90
|
+
useReturnStatus: () => useReturnStatus,
|
|
91
|
+
useSearch: () => useSearch,
|
|
92
|
+
useShippingMethods: () => useShippingMethods,
|
|
93
|
+
useShippingQuote: () => useShippingQuote,
|
|
94
|
+
useShopInfo: () => useShopInfo,
|
|
95
|
+
useShopScripts: () => useShopScripts,
|
|
96
|
+
useShopSeo: () => useShopSeo,
|
|
97
|
+
useSubmitQuote: () => useSubmitQuote,
|
|
98
|
+
useSubmitReturn: () => useSubmitReturn,
|
|
99
|
+
useSubmitReview: () => useSubmitReview,
|
|
100
|
+
useSubscriptions: () => useSubscriptions,
|
|
101
|
+
useWishlist: () => useWishlist
|
|
102
|
+
});
|
|
103
|
+
module.exports = __toCommonJS(react_exports);
|
|
12
104
|
|
|
13
105
|
// src/react/provider.tsx
|
|
14
|
-
var
|
|
15
|
-
var
|
|
106
|
+
var import_react2 = require("react");
|
|
107
|
+
var import_react_query = require("@tanstack/react-query");
|
|
108
|
+
|
|
109
|
+
// src/types.ts
|
|
110
|
+
var BehioApiError = class _BehioApiError extends Error {
|
|
111
|
+
constructor(status, body, message) {
|
|
112
|
+
super(message || `API Error ${status}`);
|
|
113
|
+
this.name = "BehioApiError";
|
|
114
|
+
this.status = status;
|
|
115
|
+
this.body = body;
|
|
116
|
+
this.code = _BehioApiError.resolveCode(status, body);
|
|
117
|
+
this.isRetryable = status >= 500 || status === 429;
|
|
118
|
+
}
|
|
119
|
+
static resolveCode(status, body) {
|
|
120
|
+
if (status === 401) return "UNAUTHORIZED";
|
|
121
|
+
if (status === 403) return "FORBIDDEN";
|
|
122
|
+
if (status === 404) return "NOT_FOUND";
|
|
123
|
+
if (status === 409) return "EMAIL_ALREADY_EXISTS";
|
|
124
|
+
if (status === 429) return "RATE_LIMITED";
|
|
125
|
+
if (status >= 500) return "INTERNAL_ERROR";
|
|
126
|
+
const msg = (body?.message || "").toLowerCase();
|
|
127
|
+
if (msg.includes("invalid") && msg.includes("password"))
|
|
128
|
+
return "INVALID_CREDENTIALS";
|
|
129
|
+
if (msg.includes("invalid") && msg.includes("email"))
|
|
130
|
+
return "INVALID_CREDENTIALS";
|
|
131
|
+
if (msg.includes("cart") && msg.includes("empty")) return "CART_EMPTY";
|
|
132
|
+
if (msg.includes("product") && msg.includes("not found"))
|
|
133
|
+
return "PRODUCT_NOT_FOUND";
|
|
134
|
+
if (msg.includes("discount") && msg.includes("expired"))
|
|
135
|
+
return "DISCOUNT_EXPIRED";
|
|
136
|
+
if (msg.includes("discount") && msg.includes("invalid"))
|
|
137
|
+
return "INVALID_DISCOUNT";
|
|
138
|
+
if (msg.includes("token") && msg.includes("expired"))
|
|
139
|
+
return "TOKEN_EXPIRED";
|
|
140
|
+
if (msg.includes("cancel")) return "ORDER_NOT_CANCELLABLE";
|
|
141
|
+
if (status === 400) return "VALIDATION_ERROR";
|
|
142
|
+
return "UNKNOWN";
|
|
143
|
+
}
|
|
144
|
+
/** Check if this is a specific error type */
|
|
145
|
+
is(code) {
|
|
146
|
+
return this.code === code;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var BehioNetworkError = class extends Error {
|
|
150
|
+
constructor(message, isTimeout = false) {
|
|
151
|
+
super(message);
|
|
152
|
+
this.isRetryable = true;
|
|
153
|
+
this.name = "BehioNetworkError";
|
|
154
|
+
this.code = isTimeout ? "TIMEOUT" : "NETWORK_ERROR";
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
function ok(data) {
|
|
158
|
+
return { data, error: null };
|
|
159
|
+
}
|
|
160
|
+
function toSdkError(err) {
|
|
161
|
+
if (err instanceof BehioApiError) {
|
|
162
|
+
return {
|
|
163
|
+
code: err.code,
|
|
164
|
+
message: err.message,
|
|
165
|
+
status: err.status,
|
|
166
|
+
body: err.body,
|
|
167
|
+
isRetryable: err.isRetryable,
|
|
168
|
+
cause: err
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (err instanceof BehioNetworkError) {
|
|
172
|
+
return {
|
|
173
|
+
code: err.code,
|
|
174
|
+
message: err.message,
|
|
175
|
+
status: null,
|
|
176
|
+
isRetryable: err.isRetryable,
|
|
177
|
+
cause: err
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
code: "UNKNOWN",
|
|
182
|
+
message: err instanceof Error ? err.message : String(err),
|
|
183
|
+
status: null,
|
|
184
|
+
isRetryable: false,
|
|
185
|
+
cause: err
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/client.ts
|
|
190
|
+
var BehioStorefront = class {
|
|
191
|
+
constructor(config) {
|
|
192
|
+
/** Consent-gated persistent visitor id — set by the analytics tracker. */
|
|
193
|
+
this.analyticsVisitorId = null;
|
|
194
|
+
// Token refresh lock
|
|
195
|
+
this.isRefreshing = false;
|
|
196
|
+
this.refreshPromise = null;
|
|
197
|
+
// Event emitter
|
|
198
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
199
|
+
// Interceptors
|
|
200
|
+
this.requestInterceptors = [];
|
|
201
|
+
this.responseInterceptors = [];
|
|
202
|
+
// Rate limit tracking
|
|
203
|
+
this.rateLimitRemaining = null;
|
|
204
|
+
this.rateLimitReset = null;
|
|
205
|
+
this.baseUrl = (config.baseUrl || "https://be.behio.com").replace(
|
|
206
|
+
/\/$/,
|
|
207
|
+
""
|
|
208
|
+
);
|
|
209
|
+
this.apiKey = config.apiKey;
|
|
210
|
+
this.shopDomain = config.shopDomain;
|
|
211
|
+
this.defaultLocale = config.locale;
|
|
212
|
+
this.defaultCurrency = config.currency;
|
|
213
|
+
this.fetchFn = config.fetch || globalThis.fetch.bind(globalThis);
|
|
214
|
+
this.timeout = config.timeout ?? 3e4;
|
|
215
|
+
this.retries = config.retries ?? 1;
|
|
216
|
+
this.retryDelay = config.retryDelay ?? 1e3;
|
|
217
|
+
this.catalog = new CatalogModule(this);
|
|
218
|
+
this.auth = new AuthModule(this);
|
|
219
|
+
this.cart = new CartModule(this);
|
|
220
|
+
this.checkout = new CheckoutModule(this);
|
|
221
|
+
this.orders = new OrdersModule(this);
|
|
222
|
+
this.customer = new CustomerModule(this);
|
|
223
|
+
this.pages = new PagesModule(this);
|
|
224
|
+
this.wishlist = new WishlistModule(this);
|
|
225
|
+
this.reviews = new ReviewsModule(this);
|
|
226
|
+
this.returns = new ReturnsModule(this);
|
|
227
|
+
this.consent = new ConsentModule(this);
|
|
228
|
+
this.quotes = new QuotesModule(this);
|
|
229
|
+
this.addresses = new AddressModule(this);
|
|
230
|
+
this.shipping = new ShippingModule(this);
|
|
231
|
+
this.newsletter = new NewsletterModule(this);
|
|
232
|
+
this.subscriptions = new SubscriptionsModule(this);
|
|
233
|
+
this.certificates = new CourseCertificatesModule(this);
|
|
234
|
+
}
|
|
235
|
+
// --- Public methods ---
|
|
236
|
+
/**
|
|
237
|
+
* Called by the analytics tracker when the visitor grants (id) or revokes
|
|
238
|
+
* (null) analytics consent. When set, requests carry the X-Behio-Vid header
|
|
239
|
+
* so the backend can attribute orders to the visitor journey.
|
|
240
|
+
*/
|
|
241
|
+
setAnalyticsVisitorId(id) {
|
|
242
|
+
this.analyticsVisitorId = id;
|
|
243
|
+
}
|
|
244
|
+
/** The consent-gated visitor id, if analytics consent was granted. */
|
|
245
|
+
getAnalyticsVisitorId() {
|
|
246
|
+
return this.analyticsVisitorId;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Fire-and-forget Behio Analytics ingest. Uses a bare keepalive fetch (not
|
|
250
|
+
* the interceptor pipeline) so flushes on pagehide still land, and swallows
|
|
251
|
+
* every error — analytics must never break the shop. The client sends no
|
|
252
|
+
* identity; the visitor hash is computed server-side from a daily salt.
|
|
253
|
+
*/
|
|
254
|
+
async sendAnalyticsEvents(input) {
|
|
255
|
+
try {
|
|
256
|
+
const headers = {
|
|
257
|
+
"X-Api-Key": this.apiKey,
|
|
258
|
+
"Content-Type": "application/json"
|
|
259
|
+
};
|
|
260
|
+
if (this.shopDomain) headers["X-Shop-Domain"] = this.shopDomain;
|
|
261
|
+
await fetch(`${this.baseUrl}/storefront/v1/analytics/events`, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
keepalive: true,
|
|
264
|
+
headers,
|
|
265
|
+
body: JSON.stringify(input)
|
|
266
|
+
});
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Personal behavioral offers for this visitor (consent-gated id). Returns
|
|
272
|
+
* only the visitor's own offers; requires the analytics visitor id.
|
|
273
|
+
*/
|
|
274
|
+
async getPersonalOffers(visitorId) {
|
|
275
|
+
return this.request("GET", "/offers", { query: { visitorId } });
|
|
276
|
+
}
|
|
277
|
+
/** Email-gate completion: trade an e-mail for the personal discount code. */
|
|
278
|
+
async claimOfferByEmail(offerId, input) {
|
|
279
|
+
return this.request("POST", `/offers/${offerId}/claim-email`, {
|
|
280
|
+
body: input
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
/** Get basic shop info */
|
|
284
|
+
async getShopInfo() {
|
|
285
|
+
return this.request("GET", "/shop");
|
|
286
|
+
}
|
|
287
|
+
/** Get SEO metadata for the shop homepage in the given locale (defaults to shop default). */
|
|
288
|
+
async getShopSeo(locale) {
|
|
289
|
+
return this.request("GET", "/shop/seo", {
|
|
290
|
+
query: locale ? { locale } : void 0
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get the merchant-defined scripts (analytics, pixels, verification, custom
|
|
295
|
+
* head/body markup) to inject into the storefront. Returns only enabled
|
|
296
|
+
* entries. Render typed entries into their snippet and RAW verbatim; gate
|
|
297
|
+
* any entry with `consentRequired` behind the visitor's analytics consent.
|
|
298
|
+
*/
|
|
299
|
+
async getShopScripts() {
|
|
300
|
+
return this.request("GET", "/shop/scripts");
|
|
301
|
+
}
|
|
302
|
+
/** Set auth tokens (e.g. from localStorage) */
|
|
303
|
+
setTokens(tokens) {
|
|
304
|
+
this.accessToken = tokens.accessToken;
|
|
305
|
+
this.refreshToken = tokens.refreshToken;
|
|
306
|
+
}
|
|
307
|
+
/** Clear auth tokens */
|
|
308
|
+
clearTokens() {
|
|
309
|
+
this.accessToken = void 0;
|
|
310
|
+
this.refreshToken = void 0;
|
|
311
|
+
}
|
|
312
|
+
/** Get current access token */
|
|
313
|
+
getAccessToken() {
|
|
314
|
+
return this.accessToken;
|
|
315
|
+
}
|
|
316
|
+
/** Get current refresh token */
|
|
317
|
+
getRefreshToken() {
|
|
318
|
+
return this.refreshToken;
|
|
319
|
+
}
|
|
320
|
+
/** Set cart session token (e.g. from cookie) */
|
|
321
|
+
setCartSession(token) {
|
|
322
|
+
this.cartSession = token;
|
|
323
|
+
}
|
|
324
|
+
/** Get cart session token */
|
|
325
|
+
getCartSession() {
|
|
326
|
+
return this.cartSession;
|
|
327
|
+
}
|
|
328
|
+
/** Clear cart session */
|
|
329
|
+
clearCartSession() {
|
|
330
|
+
this.cartSession = void 0;
|
|
331
|
+
}
|
|
332
|
+
// --- Default currency / locale (runtime-switchable) ---
|
|
333
|
+
/**
|
|
334
|
+
* Set the default currency sent on every catalog request (unless a per-call
|
|
335
|
+
* `currency` overrides it). Read live at request time, so changing it takes
|
|
336
|
+
* effect immediately without rebuilding the client. Pass undefined to clear
|
|
337
|
+
* (falls back to the shop's default currency server-side).
|
|
338
|
+
*/
|
|
339
|
+
setCurrency(currency) {
|
|
340
|
+
this.defaultCurrency = currency || void 0;
|
|
341
|
+
}
|
|
342
|
+
/** Get the current default currency, if any. */
|
|
343
|
+
getCurrency() {
|
|
344
|
+
return this.defaultCurrency;
|
|
345
|
+
}
|
|
346
|
+
/** Set the default locale sent on every catalog request (per-call wins). */
|
|
347
|
+
setLocale(locale) {
|
|
348
|
+
this.defaultLocale = locale || void 0;
|
|
349
|
+
}
|
|
350
|
+
/** Get the current default locale, if any. */
|
|
351
|
+
getLocale() {
|
|
352
|
+
return this.defaultLocale;
|
|
353
|
+
}
|
|
354
|
+
// --- Event emitter ---
|
|
355
|
+
/** Subscribe to SDK events. Returns an unsubscribe function. */
|
|
356
|
+
on(event, handler) {
|
|
357
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
358
|
+
this.listeners.get(event).add(handler);
|
|
359
|
+
return () => {
|
|
360
|
+
this.listeners.get(event)?.delete(handler);
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/** @internal Emit an event (fire-and-forget, handler errors are swallowed) */
|
|
364
|
+
emit(event, data) {
|
|
365
|
+
this.listeners.get(event)?.forEach((fn) => {
|
|
366
|
+
try {
|
|
367
|
+
fn(data);
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
// --- Interceptors ---
|
|
373
|
+
/** Add a request interceptor. Returns an unsubscribe function. */
|
|
374
|
+
addRequestInterceptor(fn) {
|
|
375
|
+
this.requestInterceptors.push(fn);
|
|
376
|
+
return () => {
|
|
377
|
+
this.requestInterceptors = this.requestInterceptors.filter(
|
|
378
|
+
(f) => f !== fn
|
|
379
|
+
);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/** Add a response interceptor. Returns an unsubscribe function. */
|
|
383
|
+
addResponseInterceptor(fn) {
|
|
384
|
+
this.responseInterceptors.push(fn);
|
|
385
|
+
return () => {
|
|
386
|
+
this.responseInterceptors = this.responseInterceptors.filter(
|
|
387
|
+
(f) => f !== fn
|
|
388
|
+
);
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
// --- Rate limit ---
|
|
392
|
+
/** Get current rate limit info from latest response headers */
|
|
393
|
+
getRateLimitInfo() {
|
|
394
|
+
return { remaining: this.rateLimitRemaining, reset: this.rateLimitReset };
|
|
395
|
+
}
|
|
396
|
+
// --- Token refresh ---
|
|
397
|
+
async handleTokenRefresh() {
|
|
398
|
+
if (this.isRefreshing) {
|
|
399
|
+
if (this.refreshPromise) await this.refreshPromise;
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
this.isRefreshing = true;
|
|
403
|
+
this.refreshPromise = (async () => {
|
|
404
|
+
try {
|
|
405
|
+
const res = await this.auth.refresh();
|
|
406
|
+
if (res.error) {
|
|
407
|
+
this.clearTokens();
|
|
408
|
+
this.emit("auth:token-refresh-failed");
|
|
409
|
+
throw new BehioApiError(401, null, "Token refresh failed");
|
|
410
|
+
}
|
|
411
|
+
this.emit("auth:token-refresh");
|
|
412
|
+
} catch (err) {
|
|
413
|
+
this.clearTokens();
|
|
414
|
+
this.emit("auth:token-refresh-failed");
|
|
415
|
+
if (err instanceof BehioApiError) throw err;
|
|
416
|
+
throw new BehioApiError(401, null, "Token refresh failed");
|
|
417
|
+
} finally {
|
|
418
|
+
this.isRefreshing = false;
|
|
419
|
+
this.refreshPromise = null;
|
|
420
|
+
}
|
|
421
|
+
})();
|
|
422
|
+
return this.refreshPromise;
|
|
423
|
+
}
|
|
424
|
+
// --- Public request wrapper (SdkResult) ---
|
|
425
|
+
/**
|
|
426
|
+
* Every public module method funnels through here. Internally calls
|
|
427
|
+
* `rawRequest` (which throws on failure) and maps thrown errors to
|
|
428
|
+
* `SdkError` so the public surface can return `SdkResult<T>`.
|
|
429
|
+
*
|
|
430
|
+
* @internal — don't call from outside the SDK; use the typed module
|
|
431
|
+
* methods (behio.catalog.*, behio.cart.*, …) instead.
|
|
432
|
+
*/
|
|
433
|
+
async request(method, path, options) {
|
|
434
|
+
try {
|
|
435
|
+
const data = await this.rawRequest(method, path, options);
|
|
436
|
+
return ok(data);
|
|
437
|
+
} catch (err) {
|
|
438
|
+
return { data: null, error: toSdkError(err) };
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Binary GET (PDF downloads). Same auth headers as `request()`, but the
|
|
443
|
+
* response is returned as a Blob instead of parsed JSON.
|
|
444
|
+
*
|
|
445
|
+
* @internal — use the typed module methods (behio.certificates.downloadPdf).
|
|
446
|
+
*/
|
|
447
|
+
async requestBlob(path) {
|
|
448
|
+
try {
|
|
449
|
+
const headers = { "X-Api-Key": this.apiKey };
|
|
450
|
+
if (this.accessToken)
|
|
451
|
+
headers.Authorization = `Bearer ${this.accessToken}`;
|
|
452
|
+
const res = await this.fetchFn(`${this.baseUrl}/storefront/v1${path}`, {
|
|
453
|
+
headers
|
|
454
|
+
});
|
|
455
|
+
if (!res.ok) {
|
|
456
|
+
let body = null;
|
|
457
|
+
try {
|
|
458
|
+
body = await res.json();
|
|
459
|
+
} catch {
|
|
460
|
+
}
|
|
461
|
+
throw new BehioApiError(res.status, body);
|
|
462
|
+
}
|
|
463
|
+
return ok(await res.blob());
|
|
464
|
+
} catch (err) {
|
|
465
|
+
return { data: null, error: toSdkError(err) };
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Throws on failure (API error / network / timeout). Kept private so
|
|
470
|
+
* internal auth refresh recursion keeps its existing control flow —
|
|
471
|
+
* public callers must go through `request()` which returns Result.
|
|
472
|
+
*/
|
|
473
|
+
async rawRequest(method, path, options) {
|
|
474
|
+
const params = new URLSearchParams();
|
|
475
|
+
if (options?.query) {
|
|
476
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
477
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
478
|
+
if (Array.isArray(value)) {
|
|
479
|
+
for (const v of value) {
|
|
480
|
+
if (v !== void 0 && v !== null && v !== "") {
|
|
481
|
+
params.append(key, String(v));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
params.set(key, String(value));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (this.defaultLocale && !params.has("locale")) {
|
|
490
|
+
params.set("locale", this.defaultLocale);
|
|
491
|
+
}
|
|
492
|
+
if (this.defaultCurrency && !params.has("currency")) {
|
|
493
|
+
params.set("currency", this.defaultCurrency);
|
|
494
|
+
}
|
|
495
|
+
const qs = params.toString();
|
|
496
|
+
const url = `${this.baseUrl}/storefront/v1${path}${qs ? `?${qs}` : ""}`;
|
|
497
|
+
const headers = {
|
|
498
|
+
"X-Api-Key": this.apiKey,
|
|
499
|
+
"Content-Type": "application/json"
|
|
500
|
+
};
|
|
501
|
+
if (this.shopDomain) {
|
|
502
|
+
headers["X-Shop-Domain"] = this.shopDomain;
|
|
503
|
+
}
|
|
504
|
+
if (this.accessToken && options?.auth !== false) {
|
|
505
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
506
|
+
}
|
|
507
|
+
if (this.cartSession) {
|
|
508
|
+
headers["X-Cart-Session"] = this.cartSession;
|
|
509
|
+
}
|
|
510
|
+
if (this.analyticsVisitorId) {
|
|
511
|
+
headers["X-Behio-Vid"] = this.analyticsVisitorId;
|
|
512
|
+
}
|
|
513
|
+
if (options?.headers) {
|
|
514
|
+
Object.assign(headers, options.headers);
|
|
515
|
+
}
|
|
516
|
+
const bodyStr = options?.body ? JSON.stringify(options.body) : void 0;
|
|
517
|
+
let interceptedConfig = {
|
|
518
|
+
url,
|
|
519
|
+
method,
|
|
520
|
+
headers,
|
|
521
|
+
body: bodyStr
|
|
522
|
+
};
|
|
523
|
+
for (const interceptor of this.requestInterceptors) {
|
|
524
|
+
interceptedConfig = await interceptor(interceptedConfig);
|
|
525
|
+
}
|
|
526
|
+
this.emit("request", { method, path });
|
|
527
|
+
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
528
|
+
const controller = new AbortController();
|
|
529
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
530
|
+
if (options?.signal) {
|
|
531
|
+
if (options.signal.aborted) {
|
|
532
|
+
clearTimeout(timeoutId);
|
|
533
|
+
throw new BehioNetworkError("Request aborted", false);
|
|
534
|
+
}
|
|
535
|
+
options.signal.addEventListener("abort", () => controller.abort(), {
|
|
536
|
+
once: true
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
let res;
|
|
540
|
+
try {
|
|
541
|
+
res = await this.fetchFn(interceptedConfig.url, {
|
|
542
|
+
method: interceptedConfig.method,
|
|
543
|
+
headers: interceptedConfig.headers,
|
|
544
|
+
body: interceptedConfig.body,
|
|
545
|
+
signal: controller.signal
|
|
546
|
+
});
|
|
547
|
+
} catch (err) {
|
|
548
|
+
clearTimeout(timeoutId);
|
|
549
|
+
const isAbort = err instanceof DOMException && err.name === "AbortError";
|
|
550
|
+
const networkErr = new BehioNetworkError(
|
|
551
|
+
isAbort ? "Request timed out" : err.message || "Network error",
|
|
552
|
+
isAbort
|
|
553
|
+
);
|
|
554
|
+
this.emit("error", networkErr);
|
|
555
|
+
if (attempt < this.retries) {
|
|
556
|
+
await new Promise(
|
|
557
|
+
(r) => setTimeout(r, this.retryDelay * (attempt + 1))
|
|
558
|
+
);
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
throw networkErr;
|
|
562
|
+
} finally {
|
|
563
|
+
clearTimeout(timeoutId);
|
|
564
|
+
}
|
|
565
|
+
const remaining = res.headers.get("X-RateLimit-Remaining");
|
|
566
|
+
const reset = res.headers.get("X-RateLimit-Reset");
|
|
567
|
+
if (remaining) this.rateLimitRemaining = parseInt(remaining, 10);
|
|
568
|
+
if (reset) this.rateLimitReset = parseInt(reset, 10);
|
|
569
|
+
if (this.rateLimitRemaining !== null && this.rateLimitRemaining <= 5) {
|
|
570
|
+
this.emit("rate-limit-warning", {
|
|
571
|
+
remaining: this.rateLimitRemaining,
|
|
572
|
+
reset: this.rateLimitReset
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
this.emit("response", { method, path, status: res.status });
|
|
576
|
+
if (!res.ok) {
|
|
577
|
+
const body = await res.json().catch(() => null);
|
|
578
|
+
const apiError = new BehioApiError(
|
|
579
|
+
res.status,
|
|
580
|
+
body,
|
|
581
|
+
body?.message || `API Error ${res.status}`
|
|
582
|
+
);
|
|
583
|
+
if (res.status === 401 && this.refreshToken && options?.auth !== false && !options?._isRetryAfterRefresh) {
|
|
584
|
+
try {
|
|
585
|
+
await this.handleTokenRefresh();
|
|
586
|
+
return this.rawRequest(method, path, {
|
|
587
|
+
...options,
|
|
588
|
+
_isRetryAfterRefresh: true
|
|
589
|
+
});
|
|
590
|
+
} catch {
|
|
591
|
+
this.emit("error", apiError);
|
|
592
|
+
throw apiError;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
this.emit("error", apiError);
|
|
596
|
+
if (attempt < this.retries && apiError.isRetryable) {
|
|
597
|
+
await new Promise(
|
|
598
|
+
(r) => setTimeout(r, this.retryDelay * (attempt + 1))
|
|
599
|
+
);
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
throw apiError;
|
|
603
|
+
}
|
|
604
|
+
const responseData = res.status === 204 ? null : await res.json();
|
|
605
|
+
for (const interceptor of this.responseInterceptors) {
|
|
606
|
+
try {
|
|
607
|
+
await interceptor({
|
|
608
|
+
status: res.status,
|
|
609
|
+
data: responseData,
|
|
610
|
+
headers: res.headers
|
|
611
|
+
});
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return responseData;
|
|
616
|
+
}
|
|
617
|
+
throw new BehioNetworkError("Request failed after retries");
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
var CatalogModule = class {
|
|
621
|
+
constructor(client) {
|
|
622
|
+
this.client = client;
|
|
623
|
+
}
|
|
624
|
+
/** List products with filtering, pagination, search */
|
|
625
|
+
async getProducts(query) {
|
|
626
|
+
const q = {};
|
|
627
|
+
if (query) {
|
|
628
|
+
if (query.page) q.page = query.page;
|
|
629
|
+
if (query.limit) q.limit = query.limit;
|
|
630
|
+
if (query.category) q.category = query.category;
|
|
631
|
+
if (query.label) q.label = query.label;
|
|
632
|
+
if (query.priceMin) q.priceMin = query.priceMin;
|
|
633
|
+
if (query.priceMax) q.priceMax = query.priceMax;
|
|
634
|
+
if (query.currency) q.currency = query.currency;
|
|
635
|
+
if (query.locale) q.locale = query.locale;
|
|
636
|
+
if (query.sort) q.sort = query.sort;
|
|
637
|
+
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
638
|
+
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
639
|
+
if (query.search) q.search = query.search;
|
|
640
|
+
if (query.customFields)
|
|
641
|
+
q.customFields = JSON.stringify(query.customFields);
|
|
642
|
+
if (query.facets) q.facets = JSON.stringify(query.facets);
|
|
643
|
+
if (query.ids && query.ids.length > 0) q.ids = query.ids;
|
|
644
|
+
if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
|
|
645
|
+
if (query.labels && query.labels.length > 0) q.labels = query.labels;
|
|
646
|
+
if (query.categories && query.categories.length > 0)
|
|
647
|
+
q.categories = query.categories;
|
|
648
|
+
if (query.excludeIds && query.excludeIds.length > 0)
|
|
649
|
+
q.excludeIds = query.excludeIds;
|
|
650
|
+
if (query.excludeCategories && query.excludeCategories.length > 0)
|
|
651
|
+
q.excludeCategories = query.excludeCategories;
|
|
652
|
+
if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
|
|
653
|
+
if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
|
|
654
|
+
if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
|
|
655
|
+
}
|
|
656
|
+
return this.client.request(
|
|
657
|
+
"GET",
|
|
658
|
+
"/catalog/products",
|
|
659
|
+
{ query: q }
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
/** Get product detail by slug */
|
|
663
|
+
async getProduct(slug, options) {
|
|
664
|
+
return this.client.request(
|
|
665
|
+
"GET",
|
|
666
|
+
`/catalog/products/${slug}`,
|
|
667
|
+
{
|
|
668
|
+
query: { locale: options?.locale, currency: options?.currency }
|
|
669
|
+
}
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
/** Get category tree */
|
|
673
|
+
async getCategories(locale) {
|
|
674
|
+
const res = await this.client.request("GET", "/catalog/categories", { query: { locale } });
|
|
675
|
+
if (res.error) return res;
|
|
676
|
+
return ok({ categories: res.data.categories || res.data.items || [] });
|
|
677
|
+
}
|
|
678
|
+
/** Get category detail by slug */
|
|
679
|
+
async getCategory(slug, locale) {
|
|
680
|
+
return this.client.request(
|
|
681
|
+
"GET",
|
|
682
|
+
`/catalog/categories/${slug}`,
|
|
683
|
+
{ query: { locale } }
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
/** Get products in a category */
|
|
687
|
+
async getCategoryProducts(slug, query) {
|
|
688
|
+
const q = {};
|
|
689
|
+
if (query) {
|
|
690
|
+
if (query.page) q.page = query.page;
|
|
691
|
+
if (query.limit) q.limit = query.limit;
|
|
692
|
+
if (query.sort) q.sort = query.sort;
|
|
693
|
+
if (query.locale) q.locale = query.locale;
|
|
694
|
+
if (query.currency) q.currency = query.currency;
|
|
695
|
+
}
|
|
696
|
+
return this.client.request(
|
|
697
|
+
"GET",
|
|
698
|
+
`/catalog/categories/${slug}/products`,
|
|
699
|
+
{ query: q }
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Resolved navigation menu by handle (e.g. "main", "footer"), built in the
|
|
704
|
+
* admin Navigace. Item labels resolve in `locale`; typed refs come back as
|
|
705
|
+
* {type, slug} for link building. Returns an SdkResult error (404) when the
|
|
706
|
+
* handle is unknown or inactive — callers fall back to their own source.
|
|
707
|
+
*/
|
|
708
|
+
async getMenu(handle, options) {
|
|
709
|
+
return this.client.request(
|
|
710
|
+
"GET",
|
|
711
|
+
`/catalog/menu/${encodeURIComponent(handle)}`,
|
|
712
|
+
{
|
|
713
|
+
query: { locale: options?.locale }
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
/** Get all labels */
|
|
718
|
+
async getLabels(locale) {
|
|
719
|
+
const res = await this.client.request("GET", "/catalog/labels", { query: { locale } });
|
|
720
|
+
if (res.error) return res;
|
|
721
|
+
return ok({ labels: res.data.labels || res.data.items || [] });
|
|
722
|
+
}
|
|
723
|
+
/** Get featured products */
|
|
724
|
+
async getFeatured(options) {
|
|
725
|
+
return this.client.request(
|
|
726
|
+
"GET",
|
|
727
|
+
"/catalog/featured",
|
|
728
|
+
{
|
|
729
|
+
query: { locale: options?.locale, currency: options?.currency }
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
/** Get available filter fields for dynamic filter UI */
|
|
734
|
+
async getFilters() {
|
|
735
|
+
return this.client.request(
|
|
736
|
+
"GET",
|
|
737
|
+
"/catalog/filters"
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Facet groups + selection-aware counts for the current filter set (custom
|
|
742
|
+
* fields, labels, price, availability, rating, subcategories). Pass the SAME
|
|
743
|
+
* query you pass to `getProducts` (category, search, price, inStock, ratingMin,
|
|
744
|
+
* customFields, facets slugs, labels): counts for each facet are computed with
|
|
745
|
+
* that facet excluded, and values that drop to 0 are still returned (render
|
|
746
|
+
* them disabled). Use this to build an Alza-style filter sidebar.
|
|
747
|
+
*/
|
|
748
|
+
async getFacets(query) {
|
|
749
|
+
const q = {};
|
|
750
|
+
if (query) {
|
|
751
|
+
if (query.category) q.category = query.category;
|
|
752
|
+
if (query.label) q.label = query.label;
|
|
753
|
+
if (query.priceMin) q.priceMin = query.priceMin;
|
|
754
|
+
if (query.priceMax) q.priceMax = query.priceMax;
|
|
755
|
+
if (query.currency) q.currency = query.currency;
|
|
756
|
+
if (query.locale) q.locale = query.locale;
|
|
757
|
+
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
758
|
+
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
759
|
+
if (query.search) q.search = query.search;
|
|
760
|
+
if (query.customFields)
|
|
761
|
+
q.customFields = JSON.stringify(query.customFields);
|
|
762
|
+
if (query.facets) q.facets = JSON.stringify(query.facets);
|
|
763
|
+
if (query.labels && query.labels.length > 0) q.labels = query.labels;
|
|
764
|
+
if (query.categories && query.categories.length > 0)
|
|
765
|
+
q.categories = query.categories;
|
|
766
|
+
if (query.excludeCategories && query.excludeCategories.length > 0)
|
|
767
|
+
q.excludeCategories = query.excludeCategories;
|
|
768
|
+
}
|
|
769
|
+
return this.client.request("GET", "/catalog/facets", {
|
|
770
|
+
query: q
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
/** Search products */
|
|
774
|
+
async search(query, options) {
|
|
775
|
+
return this.getProducts({ search: query, ...options });
|
|
776
|
+
}
|
|
777
|
+
/** List all active bundles */
|
|
778
|
+
async getBundles() {
|
|
779
|
+
return this.client.request("GET", "/catalog/bundles");
|
|
780
|
+
}
|
|
781
|
+
/** Get a single bundle by slug */
|
|
782
|
+
async getBundle(slug) {
|
|
783
|
+
return this.client.request("GET", `/catalog/bundles/${slug}`);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* One product group ("collection") by slug with its products as standard
|
|
787
|
+
* list items. Groups are curated in the admin; use this to render curated
|
|
788
|
+
* product bands. Returns an SdkResult error (404) when the group is
|
|
789
|
+
* missing or inactive — callers should render nothing in that case.
|
|
790
|
+
*/
|
|
791
|
+
async getProductGroup(slug, options) {
|
|
792
|
+
return this.client.request(
|
|
793
|
+
"GET",
|
|
794
|
+
`/catalog/product-groups/${encodeURIComponent(slug)}`,
|
|
795
|
+
{
|
|
796
|
+
query: { locale: options?.locale, currency: options?.currency }
|
|
797
|
+
}
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Cross-sell / related / upsell products for a product. Returns three
|
|
802
|
+
* separate lists: `related` (podobné produkty), `upsell` (dražší
|
|
803
|
+
* alternativy) and `crossSell` (doporučené k nákupu). Items are localized
|
|
804
|
+
* and priced in the requested currency, ready to render with the same card
|
|
805
|
+
* component as `getFeatured` / `getProductGroup`.
|
|
806
|
+
*/
|
|
807
|
+
async getCrossSell(productSlug, options) {
|
|
808
|
+
return this.client.request(
|
|
809
|
+
"GET",
|
|
810
|
+
`/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
|
|
811
|
+
{ query: { locale: options?.locale, currency: options?.currency } }
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
/** Active promotions applicable to a product (with countdown end time) */
|
|
815
|
+
/** Back-in-stock notification subscription for a sold-out product. */
|
|
816
|
+
async notifyWhenAvailable(productId, email) {
|
|
817
|
+
return this.client.request(
|
|
818
|
+
"POST",
|
|
819
|
+
`/catalog/products/${productId}/notify-when-available`,
|
|
820
|
+
{ body: { email } }
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
async getProductPromotions(productSlug) {
|
|
824
|
+
return this.client.request(
|
|
825
|
+
"GET",
|
|
826
|
+
`/catalog/products/${productSlug}/promotions`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
/** Check a gift card code — returns validity and remaining balance */
|
|
830
|
+
async checkGiftCard(code) {
|
|
831
|
+
return this.client.request(
|
|
832
|
+
"GET",
|
|
833
|
+
`/catalog/gift-cards/${encodeURIComponent(code)}/check`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Buy a gift card (GAP-39): creates a cart-independent order for the chosen
|
|
838
|
+
* amount. Redirect the customer to `paymentRedirectUrl` when present; the
|
|
839
|
+
* code is generated and emailed to the recipient once the order is paid.
|
|
840
|
+
*/
|
|
841
|
+
async purchaseGiftCard(input) {
|
|
842
|
+
return this.client.request(
|
|
843
|
+
"POST",
|
|
844
|
+
"/gift-cards/purchase",
|
|
845
|
+
{ body: input }
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
/** List configured payment methods (filtered by currency). */
|
|
849
|
+
async listPaymentMethods(opts) {
|
|
850
|
+
const query = {};
|
|
851
|
+
if (opts?.currency) query.currency = opts.currency;
|
|
852
|
+
return this.client.request(
|
|
853
|
+
"GET",
|
|
854
|
+
"/catalog/payment-methods",
|
|
855
|
+
{ query }
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
var AuthModule = class {
|
|
860
|
+
constructor(client) {
|
|
861
|
+
this.client = client;
|
|
862
|
+
}
|
|
863
|
+
/** Register a new customer */
|
|
864
|
+
async register(input) {
|
|
865
|
+
const res = await this.client.request(
|
|
866
|
+
"POST",
|
|
867
|
+
"/auth/register",
|
|
868
|
+
{
|
|
869
|
+
body: input,
|
|
870
|
+
auth: false
|
|
871
|
+
}
|
|
872
|
+
);
|
|
873
|
+
if (res.error) return res;
|
|
874
|
+
if (res.data.accessToken && res.data.refreshToken) {
|
|
875
|
+
this.client.setTokens({
|
|
876
|
+
accessToken: res.data.accessToken,
|
|
877
|
+
refreshToken: res.data.refreshToken
|
|
878
|
+
});
|
|
879
|
+
this.client.emit("auth:login", { email: input.email });
|
|
880
|
+
}
|
|
881
|
+
return res;
|
|
882
|
+
}
|
|
883
|
+
/** Login with email and password */
|
|
884
|
+
async login(input) {
|
|
885
|
+
const res = await this.client.request("POST", "/auth/login", {
|
|
886
|
+
body: input,
|
|
887
|
+
auth: false
|
|
888
|
+
});
|
|
889
|
+
if (res.error) return res;
|
|
890
|
+
this.client.setTokens(res.data);
|
|
891
|
+
this.client.emit("auth:login", { email: input.email });
|
|
892
|
+
return res;
|
|
893
|
+
}
|
|
894
|
+
/** Refresh access token using refresh token */
|
|
895
|
+
async refresh(refreshToken) {
|
|
896
|
+
const token = refreshToken || this.client.getRefreshToken();
|
|
897
|
+
if (!token) {
|
|
898
|
+
return {
|
|
899
|
+
data: null,
|
|
900
|
+
error: {
|
|
901
|
+
code: "UNAUTHORIZED",
|
|
902
|
+
message: "No refresh token available",
|
|
903
|
+
status: null,
|
|
904
|
+
isRetryable: false
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
const res = await this.client.request("POST", "/auth/refresh", {
|
|
909
|
+
body: { refreshToken: token },
|
|
910
|
+
auth: false
|
|
911
|
+
});
|
|
912
|
+
if (res.error) return res;
|
|
913
|
+
this.client.setTokens(res.data);
|
|
914
|
+
return res;
|
|
915
|
+
}
|
|
916
|
+
/** Logout (invalidate refresh token) */
|
|
917
|
+
async logout(refreshToken) {
|
|
918
|
+
const token = refreshToken || this.client.getRefreshToken();
|
|
919
|
+
const res = await this.client.request(
|
|
920
|
+
"POST",
|
|
921
|
+
"/auth/logout",
|
|
922
|
+
{
|
|
923
|
+
body: { refreshToken: token }
|
|
924
|
+
}
|
|
925
|
+
);
|
|
926
|
+
this.client.clearTokens();
|
|
927
|
+
this.client.emit("auth:logout");
|
|
928
|
+
return res;
|
|
929
|
+
}
|
|
930
|
+
/** Request password reset email */
|
|
931
|
+
async forgotPassword(email) {
|
|
932
|
+
return this.client.request(
|
|
933
|
+
"POST",
|
|
934
|
+
"/auth/forgot-password",
|
|
935
|
+
{
|
|
936
|
+
body: { email },
|
|
937
|
+
auth: false
|
|
938
|
+
}
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
/** Reset password with token */
|
|
942
|
+
async resetPassword(token, newPassword) {
|
|
943
|
+
return this.client.request(
|
|
944
|
+
"POST",
|
|
945
|
+
"/auth/reset-password",
|
|
946
|
+
{
|
|
947
|
+
body: { token, newPassword },
|
|
948
|
+
auth: false
|
|
949
|
+
}
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
/** Verify email with token */
|
|
953
|
+
async verifyEmail(token) {
|
|
954
|
+
return this.client.request("POST", "/auth/verify-email", {
|
|
955
|
+
body: { token },
|
|
956
|
+
auth: false
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
/** Check if user is logged in (has access token) */
|
|
960
|
+
isLoggedIn() {
|
|
961
|
+
return !!this.client.getAccessToken();
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
var CartModule = class {
|
|
965
|
+
constructor(client) {
|
|
966
|
+
this.client = client;
|
|
967
|
+
}
|
|
968
|
+
/** Get current cart */
|
|
969
|
+
async get() {
|
|
970
|
+
return this.client.request("GET", "/cart");
|
|
971
|
+
}
|
|
972
|
+
/** Add item to cart */
|
|
973
|
+
async addItem(input) {
|
|
974
|
+
const res = await this.client.request(
|
|
975
|
+
"POST",
|
|
976
|
+
"/cart/items",
|
|
977
|
+
{
|
|
978
|
+
body: { productId: input.productId, quantity: input.quantity }
|
|
979
|
+
}
|
|
980
|
+
);
|
|
981
|
+
if (res.error) return res;
|
|
982
|
+
if (res.data.newSessionToken) {
|
|
983
|
+
this.client.setCartSession(res.data.newSessionToken);
|
|
984
|
+
}
|
|
985
|
+
this.client.emit("cart:updated", res.data);
|
|
986
|
+
return res;
|
|
987
|
+
}
|
|
988
|
+
/** Update item quantity */
|
|
989
|
+
async updateQuantity(itemId, quantity) {
|
|
990
|
+
const res = await this.client.request(
|
|
991
|
+
"PATCH",
|
|
992
|
+
`/cart/items/${itemId}`,
|
|
993
|
+
{
|
|
994
|
+
body: { quantity }
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
if (res.error) return res;
|
|
998
|
+
this.client.emit("cart:updated", res.data);
|
|
999
|
+
return res;
|
|
1000
|
+
}
|
|
1001
|
+
/** Remove item from cart */
|
|
1002
|
+
async removeItem(itemId) {
|
|
1003
|
+
const res = await this.client.request(
|
|
1004
|
+
"DELETE",
|
|
1005
|
+
`/cart/items/${itemId}`
|
|
1006
|
+
);
|
|
1007
|
+
if (res.error) return res;
|
|
1008
|
+
this.client.emit("cart:updated", res.data);
|
|
1009
|
+
return res;
|
|
1010
|
+
}
|
|
1011
|
+
/** Clear entire cart */
|
|
1012
|
+
async clear() {
|
|
1013
|
+
const res = await this.client.request("DELETE", "/cart");
|
|
1014
|
+
if (res.error) return res;
|
|
1015
|
+
this.client.emit("cart:cleared");
|
|
1016
|
+
return res;
|
|
1017
|
+
}
|
|
1018
|
+
/** Apply a gift card code to the cart. Balance is deducted at checkout. */
|
|
1019
|
+
async applyGiftCard(code) {
|
|
1020
|
+
const res = await this.client.request("POST", "/cart/gift-card", {
|
|
1021
|
+
body: { code }
|
|
1022
|
+
});
|
|
1023
|
+
if (res.error) return res;
|
|
1024
|
+
this.client.emit("cart:updated", res.data);
|
|
1025
|
+
return res;
|
|
1026
|
+
}
|
|
1027
|
+
/** Remove a specific applied gift card from the cart by its code. */
|
|
1028
|
+
async removeGiftCard(code) {
|
|
1029
|
+
const res = await this.client.request(
|
|
1030
|
+
"DELETE",
|
|
1031
|
+
`/cart/gift-card/${encodeURIComponent(code)}`
|
|
1032
|
+
);
|
|
1033
|
+
if (res.error) return res;
|
|
1034
|
+
this.client.emit("cart:updated", res.data);
|
|
1035
|
+
return res;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Add a bundle to the cart. Price is snapshotted at the bundle's current
|
|
1039
|
+
* price. Pass either the bundle id or its slug — slug is more ergonomic
|
|
1040
|
+
* for static storefront wiring (`behio.cart.addBundle({slug: "morning-set"})`).
|
|
1041
|
+
*
|
|
1042
|
+
* Respects the bundle's `minQuantity`, `maxQuantity`, and `stockLimit`:
|
|
1043
|
+
* the request rejects with HTTP 400 if the resulting cart line would
|
|
1044
|
+
* violate any of them. The returned error includes the relevant field
|
|
1045
|
+
* (`minQuantity`, `maxQuantity`, or `remaining`) so the storefront can
|
|
1046
|
+
* surface a meaningful message.
|
|
1047
|
+
*
|
|
1048
|
+
* @param identifier Either `{id: bundleId}` or `{slug: bundleSlug}`. As a
|
|
1049
|
+
* convenience, passing a plain string is treated as the
|
|
1050
|
+
* bundle id for backwards compatibility.
|
|
1051
|
+
* @param quantity How many bundles to add (defaults to 1). Capped by
|
|
1052
|
+
* the bundle's `maxQuantity` if set.
|
|
1053
|
+
*/
|
|
1054
|
+
async addBundle(identifier, quantity = 1) {
|
|
1055
|
+
const body = {
|
|
1056
|
+
quantity
|
|
1057
|
+
};
|
|
1058
|
+
if (typeof identifier === "string") {
|
|
1059
|
+
body.bundleId = identifier;
|
|
1060
|
+
} else if ("id" in identifier) {
|
|
1061
|
+
body.bundleId = identifier.id;
|
|
1062
|
+
} else {
|
|
1063
|
+
body.bundleSlug = identifier.slug;
|
|
1064
|
+
}
|
|
1065
|
+
const res = await this.client.request("POST", "/cart/bundles", {
|
|
1066
|
+
body
|
|
1067
|
+
});
|
|
1068
|
+
if (res.error) return res;
|
|
1069
|
+
this.client.emit("cart:updated", res.data);
|
|
1070
|
+
return res;
|
|
1071
|
+
}
|
|
1072
|
+
/** Update quantity of a bundle already in the cart */
|
|
1073
|
+
async updateBundleQuantity(bundleId, quantity) {
|
|
1074
|
+
const res = await this.client.request(
|
|
1075
|
+
"PATCH",
|
|
1076
|
+
`/cart/bundles/${bundleId}`,
|
|
1077
|
+
{
|
|
1078
|
+
body: { quantity }
|
|
1079
|
+
}
|
|
1080
|
+
);
|
|
1081
|
+
if (res.error) return res;
|
|
1082
|
+
this.client.emit("cart:updated", res.data);
|
|
1083
|
+
return res;
|
|
1084
|
+
}
|
|
1085
|
+
/** Remove a bundle from the cart */
|
|
1086
|
+
async removeBundle(bundleId) {
|
|
1087
|
+
const res = await this.client.request(
|
|
1088
|
+
"DELETE",
|
|
1089
|
+
`/cart/bundles/${bundleId}`
|
|
1090
|
+
);
|
|
1091
|
+
if (res.error) return res;
|
|
1092
|
+
this.client.emit("cart:updated", res.data);
|
|
1093
|
+
return res;
|
|
1094
|
+
}
|
|
1095
|
+
/** Merge anonymous cart into authenticated customer cart */
|
|
1096
|
+
async merge() {
|
|
1097
|
+
const res = await this.client.request("POST", "/cart/merge");
|
|
1098
|
+
if (res.error) return res;
|
|
1099
|
+
this.client.emit("cart:updated", res.data);
|
|
1100
|
+
return res;
|
|
1101
|
+
}
|
|
1102
|
+
/** Apply discount code */
|
|
1103
|
+
async applyDiscount(code) {
|
|
1104
|
+
const res = await this.client.request("POST", "/cart/discount", {
|
|
1105
|
+
body: { code }
|
|
1106
|
+
});
|
|
1107
|
+
if (res.error) return res;
|
|
1108
|
+
this.client.emit("cart:updated", res.data);
|
|
1109
|
+
return res;
|
|
1110
|
+
}
|
|
1111
|
+
/** Remove discount code */
|
|
1112
|
+
async removeDiscount() {
|
|
1113
|
+
const res = await this.client.request("DELETE", "/cart/discount");
|
|
1114
|
+
if (res.error) return res;
|
|
1115
|
+
this.client.emit("cart:updated", res.data);
|
|
1116
|
+
return res;
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
var CheckoutModule = class {
|
|
1120
|
+
constructor(client) {
|
|
1121
|
+
this.client = client;
|
|
1122
|
+
}
|
|
1123
|
+
/** Create order from cart */
|
|
1124
|
+
async createOrder(input) {
|
|
1125
|
+
const res = await this.client.request("POST", "/checkout", {
|
|
1126
|
+
body: input
|
|
1127
|
+
});
|
|
1128
|
+
if (res.error) return res;
|
|
1129
|
+
this.client.clearCartSession();
|
|
1130
|
+
this.client.emit("order:created", res.data);
|
|
1131
|
+
this.client.emit("cart:cleared");
|
|
1132
|
+
return res;
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
var OrdersModule = class {
|
|
1136
|
+
constructor(client) {
|
|
1137
|
+
this.client = client;
|
|
1138
|
+
}
|
|
1139
|
+
/** List customer orders (requires auth) */
|
|
1140
|
+
async list(options) {
|
|
1141
|
+
return this.client.request(
|
|
1142
|
+
"GET",
|
|
1143
|
+
"/orders",
|
|
1144
|
+
{
|
|
1145
|
+
query: { page: options?.page, limit: options?.limit }
|
|
1146
|
+
}
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
/** Get order detail (requires auth) */
|
|
1150
|
+
async get(orderNumber) {
|
|
1151
|
+
return this.client.request("GET", `/orders/${orderNumber}`);
|
|
1152
|
+
}
|
|
1153
|
+
/** Cancel a PENDING order (requires auth) */
|
|
1154
|
+
async cancel(orderNumber) {
|
|
1155
|
+
return this.client.request(
|
|
1156
|
+
"POST",
|
|
1157
|
+
`/orders/${orderNumber}/cancel`
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Track an order by tracking token (no login, only API key). Returns a
|
|
1162
|
+
* PII-minimized view: order status + items + masked email + destination
|
|
1163
|
+
* city, never full address / phone / billing — the token travels in URLs
|
|
1164
|
+
* and e-mails so it must not expose full personal data.
|
|
1165
|
+
*/
|
|
1166
|
+
async track(trackingToken) {
|
|
1167
|
+
return this.client.request(
|
|
1168
|
+
"GET",
|
|
1169
|
+
`/orders/track/${trackingToken}`,
|
|
1170
|
+
{ auth: false }
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Step 1 of guest order-access: request a 6-digit code e-mailed to the
|
|
1175
|
+
* address on the order. The response is always generic (success) regardless
|
|
1176
|
+
* of whether the order number + e-mail match, so order numbers can't be
|
|
1177
|
+
* enumerated. No login, only API key.
|
|
1178
|
+
*/
|
|
1179
|
+
async requestAccessCode(orderNumber, email) {
|
|
1180
|
+
return this.client.request(
|
|
1181
|
+
"POST",
|
|
1182
|
+
"/orders/access/request",
|
|
1183
|
+
{
|
|
1184
|
+
auth: false,
|
|
1185
|
+
body: { orderNumber, email }
|
|
1186
|
+
}
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Step 2 of guest order-access: verify the e-mailed code. On success returns
|
|
1191
|
+
* the FULL order detail plus a short-lived `accessToken` you can pass to
|
|
1192
|
+
* {@link getByAccessToken} to re-fetch the detail without re-entering the
|
|
1193
|
+
* code. No login, only API key.
|
|
1194
|
+
*/
|
|
1195
|
+
async verifyAccessCode(orderNumber, email, code) {
|
|
1196
|
+
return this.client.request(
|
|
1197
|
+
"POST",
|
|
1198
|
+
"/orders/access/verify",
|
|
1199
|
+
{
|
|
1200
|
+
auth: false,
|
|
1201
|
+
body: { orderNumber, email, code }
|
|
1202
|
+
}
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Re-fetch a guest order's full detail using the `accessToken` returned by
|
|
1207
|
+
* {@link verifyAccessCode}. The token is scoped to that single order and
|
|
1208
|
+
* expires after 30 minutes.
|
|
1209
|
+
*/
|
|
1210
|
+
async getByAccessToken(accessToken) {
|
|
1211
|
+
return this.client.request("GET", "/orders/access/detail", {
|
|
1212
|
+
auth: false,
|
|
1213
|
+
headers: { "X-Order-Access": accessToken }
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Guest digital download (GAP-07): mint a short-lived signed URL for a
|
|
1218
|
+
* download grant on a guest-accessed order, using the order-access token from
|
|
1219
|
+
* {@link verifyAccessCode}. Scoped to that one order.
|
|
1220
|
+
*/
|
|
1221
|
+
async getAccessDownloadUrl(accessToken, downloadId) {
|
|
1222
|
+
return this.client.request(
|
|
1223
|
+
"POST",
|
|
1224
|
+
`/orders/access/downloads/${downloadId}/url`,
|
|
1225
|
+
{
|
|
1226
|
+
auth: false,
|
|
1227
|
+
headers: { "X-Order-Access": accessToken }
|
|
1228
|
+
}
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
var CustomerModule = class {
|
|
1233
|
+
constructor(client) {
|
|
1234
|
+
this.client = client;
|
|
1235
|
+
}
|
|
1236
|
+
/** Get customer profile */
|
|
1237
|
+
async getProfile() {
|
|
1238
|
+
return this.client.request("GET", "/customer/profile");
|
|
1239
|
+
}
|
|
1240
|
+
/** Update customer profile */
|
|
1241
|
+
async updateProfile(data) {
|
|
1242
|
+
return this.client.request("PATCH", "/customer/profile", {
|
|
1243
|
+
body: data
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
/** Change password */
|
|
1247
|
+
async changePassword(currentPassword, newPassword) {
|
|
1248
|
+
return this.client.request("PUT", "/customer/password", {
|
|
1249
|
+
body: { currentPassword, newPassword }
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
/** List addresses */
|
|
1253
|
+
async getAddresses() {
|
|
1254
|
+
return this.client.request(
|
|
1255
|
+
"GET",
|
|
1256
|
+
"/customer/addresses"
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
/** Create address */
|
|
1260
|
+
async createAddress(address) {
|
|
1261
|
+
return this.client.request("POST", "/customer/addresses", {
|
|
1262
|
+
body: address
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
/** Update address */
|
|
1266
|
+
async updateAddress(addressId, data) {
|
|
1267
|
+
return this.client.request(
|
|
1268
|
+
"PATCH",
|
|
1269
|
+
`/customer/addresses/${addressId}`,
|
|
1270
|
+
{ body: data }
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
/** Delete address */
|
|
1274
|
+
async deleteAddress(addressId) {
|
|
1275
|
+
return this.client.request(
|
|
1276
|
+
"DELETE",
|
|
1277
|
+
`/customer/addresses/${addressId}`
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Loyalty program summary for the logged-in customer (points balance +
|
|
1282
|
+
* value, current tier, next-tier progress, point ratio, referral code,
|
|
1283
|
+
* recent transactions). Requires an authenticated customer session.
|
|
1284
|
+
*/
|
|
1285
|
+
async getLoyalty() {
|
|
1286
|
+
return this.client.request("GET", "/customer/loyalty");
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Digital product delivery (GAP-07): list the logged-in customer's download
|
|
1290
|
+
* grants across all their orders (file name, product, remaining downloads,
|
|
1291
|
+
* expiry). Requires an authenticated customer session.
|
|
1292
|
+
*/
|
|
1293
|
+
async getDownloads() {
|
|
1294
|
+
return this.client.request(
|
|
1295
|
+
"GET",
|
|
1296
|
+
"/customer/downloads"
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Mint a short-lived signed URL for one download grant. Counts against the
|
|
1301
|
+
* grant's download budget and enforces the max-download + expiry limits
|
|
1302
|
+
* server-side. Requires an authenticated customer session.
|
|
1303
|
+
*/
|
|
1304
|
+
async getDownloadUrl(downloadId) {
|
|
1305
|
+
return this.client.request(
|
|
1306
|
+
"POST",
|
|
1307
|
+
`/customer/downloads/${downloadId}/url`
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Online courses (LMS): list the logged-in customer's enrolled courses
|
|
1312
|
+
* with progress. Enrollment is created automatically when an order with a
|
|
1313
|
+
* course product is paid. Requires an authenticated customer session.
|
|
1314
|
+
*/
|
|
1315
|
+
async getCourses() {
|
|
1316
|
+
return this.client.request(
|
|
1317
|
+
"GET",
|
|
1318
|
+
"/customer/courses"
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Course player payload: modules and lessons in order, with per-lesson
|
|
1323
|
+
* drip-unlock state. Locked lessons never contain content — the server
|
|
1324
|
+
* withholds `videoUrl`/`content`/`attachments` until `unlockAt`.
|
|
1325
|
+
*/
|
|
1326
|
+
async getCourse(courseId) {
|
|
1327
|
+
return this.client.request(
|
|
1328
|
+
"GET",
|
|
1329
|
+
`/customer/courses/${courseId}`
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
/** Mark an unlocked lesson as completed (idempotent). */
|
|
1333
|
+
async completeLesson(courseId, lessonId) {
|
|
1334
|
+
return this.client.request(
|
|
1335
|
+
"POST",
|
|
1336
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/complete`
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Quiz for an unlocked lesson. Correct answers are never included — scoring
|
|
1341
|
+
* happens server-side in `submitLessonQuiz`. Locked lessons return 403.
|
|
1342
|
+
*/
|
|
1343
|
+
async getLessonQuiz(courseId, lessonId) {
|
|
1344
|
+
return this.client.request(
|
|
1345
|
+
"GET",
|
|
1346
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/quiz`
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Submit quiz answers: returns the score, reveals correct answers and, when
|
|
1351
|
+
* the score reaches `passPercent` (70 %), marks the lesson completed
|
|
1352
|
+
* automatically.
|
|
1353
|
+
*/
|
|
1354
|
+
async submitLessonQuiz(courseId, lessonId, answers) {
|
|
1355
|
+
return this.client.request(
|
|
1356
|
+
"POST",
|
|
1357
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/quiz/submit`,
|
|
1358
|
+
{ body: { answers } }
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Completion certificates of the logged-in customer. Each carries a public
|
|
1363
|
+
* verification code for sharing (LinkedIn, CV).
|
|
1364
|
+
*/
|
|
1365
|
+
async getCourseCertificates() {
|
|
1366
|
+
return this.client.request(
|
|
1367
|
+
"GET",
|
|
1368
|
+
"/customer/courses/certificates"
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* AI tutor "Ask about this lesson": the student's private thread for one
|
|
1373
|
+
* lesson (oldest first). `enabled: false` = the merchant turned the tutor
|
|
1374
|
+
* off — hide the widget. Locked lessons return 403.
|
|
1375
|
+
*/
|
|
1376
|
+
async getLessonTutorThread(courseId, lessonId) {
|
|
1377
|
+
return this.client.request(
|
|
1378
|
+
"GET",
|
|
1379
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/tutor`
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Ask the AI tutor a question about the lesson. The answer sticks to the
|
|
1384
|
+
* lesson topic and comes back in the language of the question (Czech by
|
|
1385
|
+
* default). Rate limited (20/min); 403 on locked lessons or when the
|
|
1386
|
+
* merchant disabled the tutor.
|
|
1387
|
+
*/
|
|
1388
|
+
async askLessonTutor(courseId, lessonId, question) {
|
|
1389
|
+
return this.client.request(
|
|
1390
|
+
"POST",
|
|
1391
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/tutor`,
|
|
1392
|
+
{ body: { question } }
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Lesson discussion: paginated top-level comments (newest first) with one
|
|
1397
|
+
* level of replies. Only enrolled customers with the lesson unlocked; 403
|
|
1398
|
+
* when the merchant disabled the discussion.
|
|
1399
|
+
*/
|
|
1400
|
+
async getLessonComments(courseId, lessonId, page = 1) {
|
|
1401
|
+
return this.client.request(
|
|
1402
|
+
"GET",
|
|
1403
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/comments`,
|
|
1404
|
+
{ query: { page: String(page) } }
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Post a comment under the lesson, or a reply when `parentId` points to a
|
|
1409
|
+
* top-level comment (replies go one level deep only). Max 5000 characters.
|
|
1410
|
+
*/
|
|
1411
|
+
async postLessonComment(courseId, lessonId, body, parentId) {
|
|
1412
|
+
return this.client.request(
|
|
1413
|
+
"POST",
|
|
1414
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/comments`,
|
|
1415
|
+
{ body: { body, ...parentId ? { parentId } : {} } }
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Delete the customer's OWN comment (including its replies). Someone
|
|
1420
|
+
* else's comment returns 404.
|
|
1421
|
+
*/
|
|
1422
|
+
async deleteLessonComment(courseId, lessonId, commentId) {
|
|
1423
|
+
return this.client.request(
|
|
1424
|
+
"DELETE",
|
|
1425
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/comments/${commentId}`
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* The student's private note for one lesson. `body` is an empty string
|
|
1430
|
+
* when no note exists yet. Visible only to the logged-in student.
|
|
1431
|
+
*/
|
|
1432
|
+
async getLessonNote(courseId, lessonId) {
|
|
1433
|
+
return this.client.request(
|
|
1434
|
+
"GET",
|
|
1435
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/note`
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Save (upsert) the student's private lesson note. Autosave-friendly; an
|
|
1440
|
+
* empty string clears the note. Max 20 000 characters.
|
|
1441
|
+
*/
|
|
1442
|
+
async saveLessonNote(courseId, lessonId, body) {
|
|
1443
|
+
return this.client.request(
|
|
1444
|
+
"PUT",
|
|
1445
|
+
`/customer/courses/${courseId}/lessons/${lessonId}/note`,
|
|
1446
|
+
{ body: { body } }
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
var CourseCertificatesModule = class {
|
|
1451
|
+
constructor(client) {
|
|
1452
|
+
this.client = client;
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Publicly verify a certificate code (no customer login needed) — build a
|
|
1456
|
+
* `/certifikat/{code}` page with this. Unknown codes return a 404 error.
|
|
1457
|
+
*/
|
|
1458
|
+
async verify(code) {
|
|
1459
|
+
return this.client.request(
|
|
1460
|
+
"GET",
|
|
1461
|
+
`/course-certificates/${encodeURIComponent(code)}`
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Download the certificate as a branded PDF (A5 landscape). Returns a Blob;
|
|
1466
|
+
* trigger a browser download via `URL.createObjectURL(blob)`.
|
|
1467
|
+
*/
|
|
1468
|
+
async downloadPdf(code) {
|
|
1469
|
+
return this.client.requestBlob(
|
|
1470
|
+
`/course-certificates/${encodeURIComponent(code)}/pdf`
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
var PagesModule = class {
|
|
1475
|
+
constructor(client) {
|
|
1476
|
+
this.client = client;
|
|
1477
|
+
}
|
|
1478
|
+
/** List CMS pages */
|
|
1479
|
+
async list(locale) {
|
|
1480
|
+
return this.client.request("GET", "/pages", {
|
|
1481
|
+
query: { locale }
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
/** Get page by slug */
|
|
1485
|
+
async get(slug, locale) {
|
|
1486
|
+
return this.client.request("GET", `/pages/${slug}`, {
|
|
1487
|
+
query: { locale }
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
var WishlistModule = class {
|
|
1492
|
+
constructor(client) {
|
|
1493
|
+
this.client = client;
|
|
1494
|
+
}
|
|
1495
|
+
async get() {
|
|
1496
|
+
return this.client.request(
|
|
1497
|
+
"GET",
|
|
1498
|
+
"/customer/wishlist"
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1501
|
+
async add(productId) {
|
|
1502
|
+
return this.client.request(
|
|
1503
|
+
"POST",
|
|
1504
|
+
"/customer/wishlist",
|
|
1505
|
+
{ body: { productId } }
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
async remove(productId) {
|
|
1509
|
+
return this.client.request(
|
|
1510
|
+
"DELETE",
|
|
1511
|
+
`/customer/wishlist/${productId}`
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
async isInWishlist(productId) {
|
|
1515
|
+
return this.client.request(
|
|
1516
|
+
"GET",
|
|
1517
|
+
`/customer/wishlist/${productId}/check`
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
var SubscriptionsModule = class {
|
|
1522
|
+
constructor(client) {
|
|
1523
|
+
this.client = client;
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* List the logged-in customer's recurring-order subscriptions (products,
|
|
1527
|
+
* cadence, next order date, status). Requires an authenticated customer
|
|
1528
|
+
* session. Subscriptions are created by the merchant in v1.
|
|
1529
|
+
*/
|
|
1530
|
+
async list() {
|
|
1531
|
+
return this.client.request(
|
|
1532
|
+
"GET",
|
|
1533
|
+
"/customer/subscriptions"
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
/** Pause an active subscription (no orders are generated while paused). */
|
|
1537
|
+
async pause(subscriptionId) {
|
|
1538
|
+
return this.client.request(
|
|
1539
|
+
"POST",
|
|
1540
|
+
`/customer/subscriptions/${subscriptionId}/pause`
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
/** Resume a paused subscription (re-schedules the next order). */
|
|
1544
|
+
async resume(subscriptionId) {
|
|
1545
|
+
return this.client.request(
|
|
1546
|
+
"POST",
|
|
1547
|
+
`/customer/subscriptions/${subscriptionId}/resume`
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
/** Cancel a subscription permanently (no more orders). */
|
|
1551
|
+
async cancel(subscriptionId) {
|
|
1552
|
+
return this.client.request(
|
|
1553
|
+
"POST",
|
|
1554
|
+
`/customer/subscriptions/${subscriptionId}/cancel`
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
var ReviewsModule = class {
|
|
1559
|
+
constructor(client) {
|
|
1560
|
+
this.client = client;
|
|
1561
|
+
}
|
|
1562
|
+
async getProductReviews(productId, page = 1, limit = 20) {
|
|
1563
|
+
return this.client.request(
|
|
1564
|
+
"GET",
|
|
1565
|
+
`/catalog/products/${productId}/reviews`,
|
|
1566
|
+
{
|
|
1567
|
+
query: { page: String(page), limit: String(limit) }
|
|
1568
|
+
}
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
async submit(input) {
|
|
1572
|
+
return this.client.request("POST", "/catalog/reviews", {
|
|
1573
|
+
body: input
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
async voteHelpful(reviewId, helpful) {
|
|
1577
|
+
return this.client.request(
|
|
1578
|
+
"POST",
|
|
1579
|
+
`/catalog/reviews/${reviewId}/vote`,
|
|
1580
|
+
{ body: { helpful } }
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
var ReturnsModule = class {
|
|
1585
|
+
constructor(client) {
|
|
1586
|
+
this.client = client;
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Guest order lookup for the EU withdrawal form: order number + the email
|
|
1590
|
+
* used on the order resolve to the order id and per-item returnable
|
|
1591
|
+
* quantities. POST so the email never appears in a URL.
|
|
1592
|
+
*/
|
|
1593
|
+
async lookupOrder(orderNumber, email) {
|
|
1594
|
+
return this.client.request(
|
|
1595
|
+
"POST",
|
|
1596
|
+
"/returns/lookup-order",
|
|
1597
|
+
{
|
|
1598
|
+
body: { orderNumber, email }
|
|
1599
|
+
}
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
async submit(input) {
|
|
1603
|
+
return this.client.request("POST", "/returns", {
|
|
1604
|
+
body: input
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
/** Email is the ownership gate; POST so it never lands in a URL / log. */
|
|
1608
|
+
async getStatus(returnId, email) {
|
|
1609
|
+
return this.client.request(
|
|
1610
|
+
"POST",
|
|
1611
|
+
`/returns/${returnId}/status`,
|
|
1612
|
+
{ body: { email } }
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
var ConsentModule = class {
|
|
1617
|
+
constructor(client) {
|
|
1618
|
+
this.client = client;
|
|
1619
|
+
}
|
|
1620
|
+
async record(input) {
|
|
1621
|
+
return this.client.request("POST", "/consent", {
|
|
1622
|
+
body: input,
|
|
1623
|
+
auth: false
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
async get(visitorId) {
|
|
1627
|
+
const result = await this.client.request("GET", `/consent/${visitorId}/status`, { auth: false });
|
|
1628
|
+
if (result.error && result.error.status === 404) {
|
|
1629
|
+
const legacy = await this.client.request(
|
|
1630
|
+
"GET",
|
|
1631
|
+
`/consent/${visitorId}`,
|
|
1632
|
+
{ auth: false }
|
|
1633
|
+
);
|
|
1634
|
+
if (legacy.error && legacy.error.status === 404)
|
|
1635
|
+
return { data: null, error: null };
|
|
1636
|
+
return legacy;
|
|
1637
|
+
}
|
|
1638
|
+
if (result.error) return { data: null, error: result.error };
|
|
1639
|
+
return {
|
|
1640
|
+
data: result.data?.consented ? result.data.consent : null,
|
|
1641
|
+
error: null
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
async revoke(visitorId) {
|
|
1645
|
+
return this.client.request(
|
|
1646
|
+
"DELETE",
|
|
1647
|
+
`/consent/${visitorId}`,
|
|
1648
|
+
{ auth: false }
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
var QuotesModule = class {
|
|
1653
|
+
constructor(client) {
|
|
1654
|
+
this.client = client;
|
|
1655
|
+
}
|
|
1656
|
+
async submit(input) {
|
|
1657
|
+
return this.client.request("POST", "/catalog/quote-request", {
|
|
1658
|
+
body: input
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
async accept(quoteId, email) {
|
|
1662
|
+
return this.client.request(
|
|
1663
|
+
"POST",
|
|
1664
|
+
`/quotes/${quoteId}/accept`,
|
|
1665
|
+
{ body: { email } }
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
/** Email is the ownership gate — quotes carry contact PII and negotiated
|
|
1669
|
+
* prices, so the id alone is never enough. POST keeps it out of URLs. */
|
|
1670
|
+
async getStatus(quoteId, email) {
|
|
1671
|
+
return this.client.request(
|
|
1672
|
+
"POST",
|
|
1673
|
+
`/quotes/${quoteId}/status`,
|
|
1674
|
+
{ body: { email } }
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* The logged-in customer's own quote requests ("Moje poptávky", GAP-18).
|
|
1679
|
+
* Requires an authenticated session; ownership is the auth token (customer id
|
|
1680
|
+
* + verified email), never a payload. Newest first.
|
|
1681
|
+
*/
|
|
1682
|
+
async listMine() {
|
|
1683
|
+
return this.client.request(
|
|
1684
|
+
"GET",
|
|
1685
|
+
"/customer/quotes"
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
var AddressModule = class {
|
|
1690
|
+
constructor(client) {
|
|
1691
|
+
this.client = client;
|
|
1692
|
+
}
|
|
1693
|
+
/** Search for address suggestions (debounce on your side, or use the React hook) */
|
|
1694
|
+
async autocomplete(query, country) {
|
|
1695
|
+
if (!query || query.length < 2) return ok({ suggestions: [] });
|
|
1696
|
+
return this.client.request(
|
|
1697
|
+
"GET",
|
|
1698
|
+
"/addresses/autocomplete",
|
|
1699
|
+
{
|
|
1700
|
+
query: { q: query, country }
|
|
1701
|
+
}
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
/** Get full structured address from a suggestion's placeId */
|
|
1705
|
+
async getDetail(placeId) {
|
|
1706
|
+
return this.client.request(
|
|
1707
|
+
"GET",
|
|
1708
|
+
"/addresses/place-detail",
|
|
1709
|
+
{
|
|
1710
|
+
query: { placeId }
|
|
1711
|
+
}
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
var ShippingModule = class {
|
|
1716
|
+
constructor(client) {
|
|
1717
|
+
this.client = client;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Return the configured shipping methods that pass the current
|
|
1721
|
+
* currency + country filter. Fixed-price methods come back with
|
|
1722
|
+
* their `pricing[]` row resolved; live-quote methods come back with
|
|
1723
|
+
* `price` 0 here — call `quote()` to get the real live price.
|
|
1724
|
+
*/
|
|
1725
|
+
async listMethods(opts) {
|
|
1726
|
+
const query = {};
|
|
1727
|
+
if (opts?.currency) query.currency = opts.currency;
|
|
1728
|
+
if (opts?.country) query.country = opts.country;
|
|
1729
|
+
if (opts?.cartTotal != null) query.cartTotal = String(opts.cartTotal);
|
|
1730
|
+
if (opts?.cartWeightKg != null)
|
|
1731
|
+
query.cartWeightKg = String(opts.cartWeightKg);
|
|
1732
|
+
return this.client.request(
|
|
1733
|
+
"GET",
|
|
1734
|
+
"/catalog/shipping-methods",
|
|
1735
|
+
{ query }
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Quote shipping for a destination address + cart contents. Each
|
|
1740
|
+
* configured method is evaluated:
|
|
1741
|
+
* - `priceStrategy="fixed"` → resolved from the merchant's per-currency
|
|
1742
|
+
* `pricing[]` rows + free-shipping threshold check.
|
|
1743
|
+
* - `priceStrategy="live_quote"` → dispatched to the upstream
|
|
1744
|
+
* meta-provider (Zaslat, future Shippo / Sendcloud / …) and run
|
|
1745
|
+
* through the merchant's markup/rounding rules.
|
|
1746
|
+
*
|
|
1747
|
+
* Filter `available: true` for the checkout picker; `available: false`
|
|
1748
|
+
* rows carry a `reason` (`"no_rate_returned"`, `"live_quote_not_implemented"`,
|
|
1749
|
+
* …) you can log but should not display.
|
|
1750
|
+
*/
|
|
1751
|
+
async quote(input) {
|
|
1752
|
+
return this.client.request(
|
|
1753
|
+
"POST",
|
|
1754
|
+
"/catalog/shipping/quote",
|
|
1755
|
+
{ body: input }
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* List pickup points (parcel shops / lockers) for a method that has
|
|
1760
|
+
* `supportsPickupPoints`. Filter with `query` (name / city / zip) for a
|
|
1761
|
+
* "find your branch" box. Send the chosen point's `externalId` back as
|
|
1762
|
+
* `checkout.pickupPointId`.
|
|
1763
|
+
*/
|
|
1764
|
+
async getPickupPoints(input) {
|
|
1765
|
+
const query = { methodId: input.methodId };
|
|
1766
|
+
if (input.query) query.query = input.query;
|
|
1767
|
+
if (input.country) query.country = input.country;
|
|
1768
|
+
if (input.limit != null) query.limit = String(input.limit);
|
|
1769
|
+
return this.client.request(
|
|
1770
|
+
"GET",
|
|
1771
|
+
"/catalog/shipping/pickup-points",
|
|
1772
|
+
{ query }
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
var NewsletterModule = class {
|
|
1777
|
+
constructor(client) {
|
|
1778
|
+
this.client = client;
|
|
1779
|
+
}
|
|
1780
|
+
async subscribe(input) {
|
|
1781
|
+
return this.client.request(
|
|
1782
|
+
"POST",
|
|
1783
|
+
"/newsletter/subscribe",
|
|
1784
|
+
{
|
|
1785
|
+
body: input,
|
|
1786
|
+
auth: false
|
|
1787
|
+
}
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
async unsubscribe(email) {
|
|
1791
|
+
return this.client.request(
|
|
1792
|
+
"POST",
|
|
1793
|
+
"/newsletter/unsubscribe",
|
|
1794
|
+
{
|
|
1795
|
+
body: { email },
|
|
1796
|
+
auth: false
|
|
1797
|
+
}
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
16
1801
|
|
|
17
1802
|
// src/react/context.ts
|
|
18
|
-
|
|
19
|
-
var BehioContext =
|
|
1803
|
+
var import_react = require("react");
|
|
1804
|
+
var BehioContext = (0, import_react.createContext)(null);
|
|
20
1805
|
function useBehio() {
|
|
21
|
-
const ctx =
|
|
1806
|
+
const ctx = (0, import_react.useContext)(BehioContext);
|
|
22
1807
|
if (!ctx) {
|
|
23
1808
|
throw new Error("useBehio must be used within a <BehioProvider>");
|
|
24
1809
|
}
|
|
@@ -49,20 +1834,20 @@ var localStorageAdapter = {
|
|
|
49
1834
|
get(key) {
|
|
50
1835
|
try {
|
|
51
1836
|
return window.localStorage.getItem(key);
|
|
52
|
-
} catch
|
|
1837
|
+
} catch {
|
|
53
1838
|
return null;
|
|
54
1839
|
}
|
|
55
1840
|
},
|
|
56
1841
|
set(key, value) {
|
|
57
1842
|
try {
|
|
58
1843
|
window.localStorage.setItem(key, value);
|
|
59
|
-
} catch
|
|
1844
|
+
} catch {
|
|
60
1845
|
}
|
|
61
1846
|
},
|
|
62
1847
|
remove(key) {
|
|
63
1848
|
try {
|
|
64
1849
|
window.localStorage.removeItem(key);
|
|
65
|
-
} catch
|
|
1850
|
+
} catch {
|
|
66
1851
|
}
|
|
67
1852
|
}
|
|
68
1853
|
};
|
|
@@ -70,7 +1855,7 @@ function createMemoryStorage() {
|
|
|
70
1855
|
const store = /* @__PURE__ */ new Map();
|
|
71
1856
|
return {
|
|
72
1857
|
get(key) {
|
|
73
|
-
return
|
|
1858
|
+
return store.get(key) ?? null;
|
|
74
1859
|
},
|
|
75
1860
|
set(key, value) {
|
|
76
1861
|
store.set(key, value);
|
|
@@ -111,7 +1896,7 @@ var STORAGE_KEYS = {
|
|
|
111
1896
|
};
|
|
112
1897
|
|
|
113
1898
|
// src/react/provider.tsx
|
|
114
|
-
var
|
|
1899
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
115
1900
|
function BehioProvider({
|
|
116
1901
|
apiKey,
|
|
117
1902
|
baseUrl,
|
|
@@ -127,7 +1912,7 @@ function BehioProvider({
|
|
|
127
1912
|
queryClient: externalQueryClient,
|
|
128
1913
|
children
|
|
129
1914
|
}) {
|
|
130
|
-
const storageAdapter =
|
|
1915
|
+
const storageAdapter = (0, import_react2.useMemo)(() => resolveStorage(storageOption), [storageOption]);
|
|
131
1916
|
const resolveInitialCurrency = () => {
|
|
132
1917
|
if (persistCurrency) {
|
|
133
1918
|
const stored = cookieStorage.get(currencyCookieName);
|
|
@@ -135,10 +1920,10 @@ function BehioProvider({
|
|
|
135
1920
|
}
|
|
136
1921
|
return currency;
|
|
137
1922
|
};
|
|
138
|
-
const [activeCurrency, setActiveCurrency] =
|
|
139
|
-
const clientRef =
|
|
1923
|
+
const [activeCurrency, setActiveCurrency] = (0, import_react2.useState)(resolveInitialCurrency);
|
|
1924
|
+
const clientRef = (0, import_react2.useRef)(null);
|
|
140
1925
|
if (!clientRef.current) {
|
|
141
|
-
clientRef.current = new
|
|
1926
|
+
clientRef.current = new BehioStorefront({
|
|
142
1927
|
apiKey,
|
|
143
1928
|
baseUrl,
|
|
144
1929
|
...shopDomain ? { shopDomain } : {},
|
|
@@ -149,7 +1934,7 @@ function BehioProvider({
|
|
|
149
1934
|
});
|
|
150
1935
|
}
|
|
151
1936
|
const client = clientRef.current;
|
|
152
|
-
|
|
1937
|
+
(0, import_react2.useEffect)(() => {
|
|
153
1938
|
const accessToken = storageAdapter.get(STORAGE_KEYS.ACCESS_TOKEN);
|
|
154
1939
|
const refreshToken = storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
155
1940
|
if (accessToken && refreshToken) {
|
|
@@ -160,7 +1945,7 @@ function BehioProvider({
|
|
|
160
1945
|
client.setCartSession(cartSession);
|
|
161
1946
|
}
|
|
162
1947
|
}, [client, storageAdapter]);
|
|
163
|
-
|
|
1948
|
+
(0, import_react2.useEffect)(() => {
|
|
164
1949
|
if (!persistCurrency) return;
|
|
165
1950
|
const stored = cookieStorage.get(currencyCookieName);
|
|
166
1951
|
if (stored && stored !== activeCurrency) {
|
|
@@ -168,17 +1953,17 @@ function BehioProvider({
|
|
|
168
1953
|
client.setCurrency(stored);
|
|
169
1954
|
}
|
|
170
1955
|
}, []);
|
|
171
|
-
|
|
1956
|
+
(0, import_react2.useEffect)(() => {
|
|
172
1957
|
if (currency !== void 0 && currency !== activeCurrency) {
|
|
173
1958
|
setActiveCurrency(currency);
|
|
174
1959
|
client.setCurrency(currency);
|
|
175
1960
|
}
|
|
176
1961
|
}, [currency]);
|
|
177
|
-
const queryClientRef =
|
|
178
|
-
const qc =
|
|
1962
|
+
const queryClientRef = (0, import_react2.useRef)(null);
|
|
1963
|
+
const qc = (0, import_react2.useMemo)(() => {
|
|
179
1964
|
if (externalQueryClient) return externalQueryClient;
|
|
180
1965
|
if (!queryClientRef.current) {
|
|
181
|
-
queryClientRef.current = new
|
|
1966
|
+
queryClientRef.current = new import_react_query.QueryClient({
|
|
182
1967
|
defaultOptions: {
|
|
183
1968
|
queries: {
|
|
184
1969
|
staleTime: 6e4,
|
|
@@ -189,7 +1974,7 @@ function BehioProvider({
|
|
|
189
1974
|
}
|
|
190
1975
|
return queryClientRef.current;
|
|
191
1976
|
}, [externalQueryClient]);
|
|
192
|
-
const setCurrency =
|
|
1977
|
+
const setCurrency = (0, import_react2.useCallback)(
|
|
193
1978
|
(next) => {
|
|
194
1979
|
setActiveCurrency(next);
|
|
195
1980
|
client.setCurrency(next);
|
|
@@ -197,12 +1982,12 @@ function BehioProvider({
|
|
|
197
1982
|
if (next) cookieStorage.set(currencyCookieName, next);
|
|
198
1983
|
else cookieStorage.remove(currencyCookieName);
|
|
199
1984
|
}
|
|
200
|
-
|
|
1985
|
+
onCurrencyChange?.(next);
|
|
201
1986
|
void qc.invalidateQueries({ queryKey: ["behio"] });
|
|
202
1987
|
},
|
|
203
1988
|
[client, qc, persistCurrency, currencyCookieName, onCurrencyChange]
|
|
204
1989
|
);
|
|
205
|
-
const ctxValue =
|
|
1990
|
+
const ctxValue = (0, import_react2.useMemo)(
|
|
206
1991
|
() => ({
|
|
207
1992
|
client,
|
|
208
1993
|
storage: storageAdapter,
|
|
@@ -213,12 +1998,12 @@ function BehioProvider({
|
|
|
213
1998
|
}),
|
|
214
1999
|
[client, storageAdapter, activeCurrency, setCurrency, defaultCurrency, currencies]
|
|
215
2000
|
);
|
|
216
|
-
return /* @__PURE__ */
|
|
2001
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query.QueryClientProvider, { client: qc, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BehioContext.Provider, { value: ctxValue, children }) });
|
|
217
2002
|
}
|
|
218
2003
|
|
|
219
2004
|
// src/react/hooks/use-products.ts
|
|
220
|
-
|
|
221
|
-
|
|
2005
|
+
var import_react3 = require("react");
|
|
2006
|
+
var import_react_query2 = require("@tanstack/react-query");
|
|
222
2007
|
|
|
223
2008
|
// src/react/utils/unwrap.ts
|
|
224
2009
|
var UnwrappedError = class extends Error {
|
|
@@ -237,10 +2022,10 @@ async function unwrap(p) {
|
|
|
237
2022
|
// src/react/hooks/use-products.ts
|
|
238
2023
|
function useProducts(query) {
|
|
239
2024
|
const { client } = useBehio();
|
|
240
|
-
const { initialData, enabled, page: initialPage, limit, ...filters } =
|
|
241
|
-
const [page, setPage] =
|
|
242
|
-
const serializedFilters =
|
|
243
|
-
const infinite =
|
|
2025
|
+
const { initialData, enabled, page: initialPage, limit, ...filters } = query ?? {};
|
|
2026
|
+
const [page, setPage] = (0, import_react3.useState)(initialPage ?? 1);
|
|
2027
|
+
const serializedFilters = (0, import_react3.useMemo)(() => JSON.stringify({ ...filters, limit }), [filters, limit]);
|
|
2028
|
+
const infinite = (0, import_react_query2.useInfiniteQuery)({
|
|
244
2029
|
queryKey: ["behio", "products", serializedFilters, page],
|
|
245
2030
|
queryFn: ({ pageParam }) => unwrap(client.catalog.getProducts({ ...filters, limit, page: pageParam })),
|
|
246
2031
|
initialPageParam: page,
|
|
@@ -249,19 +2034,19 @@ function useProducts(query) {
|
|
|
249
2034
|
initialData: initialData ? { pages: [initialData], pageParams: [initialData.page] } : void 0,
|
|
250
2035
|
enabled: enabled !== false
|
|
251
2036
|
});
|
|
252
|
-
const items =
|
|
253
|
-
() =>
|
|
2037
|
+
const items = (0, import_react3.useMemo)(
|
|
2038
|
+
() => infinite.data?.pages.flatMap((p) => p.items) ?? [],
|
|
254
2039
|
[infinite.data]
|
|
255
2040
|
);
|
|
256
|
-
const lastPage =
|
|
257
|
-
const firstPage =
|
|
258
|
-
const loadMore =
|
|
2041
|
+
const lastPage = infinite.data?.pages[infinite.data.pages.length - 1];
|
|
2042
|
+
const firstPage = infinite.data?.pages[0];
|
|
2043
|
+
const loadMore = (0, import_react3.useCallback)(() => {
|
|
259
2044
|
if (infinite.hasNextPage && !infinite.isFetchingNextPage) {
|
|
260
2045
|
return infinite.fetchNextPage();
|
|
261
2046
|
}
|
|
262
2047
|
return Promise.resolve();
|
|
263
2048
|
}, [infinite]);
|
|
264
|
-
const goToPage =
|
|
2049
|
+
const goToPage = (0, import_react3.useCallback)((newPage) => {
|
|
265
2050
|
setPage(newPage);
|
|
266
2051
|
}, []);
|
|
267
2052
|
return {
|
|
@@ -271,10 +2056,10 @@ function useProducts(query) {
|
|
|
271
2056
|
/** Raw React Query data with all pages */
|
|
272
2057
|
data: infinite.data,
|
|
273
2058
|
/** Last loaded page meta */
|
|
274
|
-
total:
|
|
275
|
-
totalPages:
|
|
276
|
-
currentPage:
|
|
277
|
-
limit:
|
|
2059
|
+
total: lastPage?.total ?? 0,
|
|
2060
|
+
totalPages: lastPage?.totalPages ?? 0,
|
|
2061
|
+
currentPage: lastPage?.page ?? page,
|
|
2062
|
+
limit: lastPage?.limit ?? limit ?? 20,
|
|
278
2063
|
// --- Pagination controls ---
|
|
279
2064
|
/** Current page (manual pagination) */
|
|
280
2065
|
page,
|
|
@@ -285,8 +2070,8 @@ function useProducts(query) {
|
|
|
285
2070
|
/** Fetch previous page */
|
|
286
2071
|
loadPrevious: infinite.fetchPreviousPage,
|
|
287
2072
|
/** Can load more? */
|
|
288
|
-
hasMore:
|
|
289
|
-
hasPrevious:
|
|
2073
|
+
hasMore: infinite.hasNextPage ?? false,
|
|
2074
|
+
hasPrevious: infinite.hasPreviousPage ?? false,
|
|
290
2075
|
// --- States ---
|
|
291
2076
|
isLoading: infinite.isLoading,
|
|
292
2077
|
isFetching: infinite.isFetching,
|
|
@@ -300,117 +2085,117 @@ function useProducts(query) {
|
|
|
300
2085
|
}
|
|
301
2086
|
|
|
302
2087
|
// src/react/hooks/use-product.ts
|
|
303
|
-
|
|
2088
|
+
var import_react_query3 = require("@tanstack/react-query");
|
|
304
2089
|
function useProduct(slug, options) {
|
|
305
2090
|
const { client } = useBehio();
|
|
306
|
-
return
|
|
2091
|
+
return (0, import_react_query3.useQuery)({
|
|
307
2092
|
// currency + locale belong in the key so switching either refetches the
|
|
308
2093
|
// price/translation instead of serving a stale cache hit.
|
|
309
|
-
queryKey: ["behio", "product", slug,
|
|
2094
|
+
queryKey: ["behio", "product", slug, options?.locale, options?.currency],
|
|
310
2095
|
queryFn: () => unwrap(client.catalog.getProduct(slug, {
|
|
311
|
-
locale:
|
|
312
|
-
currency:
|
|
2096
|
+
locale: options?.locale,
|
|
2097
|
+
currency: options?.currency
|
|
313
2098
|
})),
|
|
314
|
-
initialData:
|
|
315
|
-
enabled:
|
|
2099
|
+
initialData: options?.initialData,
|
|
2100
|
+
enabled: options?.enabled !== false && !!slug
|
|
316
2101
|
});
|
|
317
2102
|
}
|
|
318
2103
|
|
|
319
2104
|
// src/react/hooks/use-categories.ts
|
|
320
|
-
|
|
2105
|
+
var import_react_query4 = require("@tanstack/react-query");
|
|
321
2106
|
function useCategories(locale, options) {
|
|
322
2107
|
const { client } = useBehio();
|
|
323
|
-
return
|
|
2108
|
+
return (0, import_react_query4.useQuery)({
|
|
324
2109
|
queryKey: ["behio", "categories", locale],
|
|
325
2110
|
queryFn: async () => {
|
|
326
2111
|
const result = await unwrap(client.catalog.getCategories(locale));
|
|
327
2112
|
return result.categories;
|
|
328
2113
|
},
|
|
329
|
-
enabled:
|
|
2114
|
+
enabled: options?.enabled !== false
|
|
330
2115
|
});
|
|
331
2116
|
}
|
|
332
2117
|
function useCategory(slug, options) {
|
|
333
2118
|
const { client } = useBehio();
|
|
334
|
-
return
|
|
335
|
-
queryKey: ["behio", "category", slug,
|
|
336
|
-
queryFn: () => unwrap(client.catalog.getCategory(slug,
|
|
337
|
-
enabled:
|
|
2119
|
+
return (0, import_react_query4.useQuery)({
|
|
2120
|
+
queryKey: ["behio", "category", slug, options?.locale],
|
|
2121
|
+
queryFn: () => unwrap(client.catalog.getCategory(slug, options?.locale)),
|
|
2122
|
+
enabled: options?.enabled !== false && !!slug
|
|
338
2123
|
});
|
|
339
2124
|
}
|
|
340
2125
|
|
|
341
2126
|
// src/react/hooks/use-menu.ts
|
|
342
|
-
|
|
2127
|
+
var import_react_query5 = require("@tanstack/react-query");
|
|
343
2128
|
function useMenu(handle, options) {
|
|
344
2129
|
const { client } = useBehio();
|
|
345
|
-
return
|
|
346
|
-
queryKey: ["behio", "menu", handle,
|
|
347
|
-
queryFn: () => unwrap(client.catalog.getMenu(handle, { locale:
|
|
348
|
-
enabled:
|
|
2130
|
+
return (0, import_react_query5.useQuery)({
|
|
2131
|
+
queryKey: ["behio", "menu", handle, options?.locale],
|
|
2132
|
+
queryFn: () => unwrap(client.catalog.getMenu(handle, { locale: options?.locale })),
|
|
2133
|
+
enabled: options?.enabled !== false && !!handle
|
|
349
2134
|
});
|
|
350
2135
|
}
|
|
351
2136
|
|
|
352
2137
|
// src/react/hooks/use-labels.ts
|
|
353
|
-
|
|
2138
|
+
var import_react_query6 = require("@tanstack/react-query");
|
|
354
2139
|
function useLabels(locale, options) {
|
|
355
2140
|
const { client } = useBehio();
|
|
356
|
-
return
|
|
2141
|
+
return (0, import_react_query6.useQuery)({
|
|
357
2142
|
queryKey: ["behio", "labels", locale],
|
|
358
2143
|
queryFn: async () => {
|
|
359
2144
|
const result = await unwrap(client.catalog.getLabels(locale));
|
|
360
2145
|
return result.labels;
|
|
361
2146
|
},
|
|
362
|
-
enabled:
|
|
2147
|
+
enabled: options?.enabled !== false
|
|
363
2148
|
});
|
|
364
2149
|
}
|
|
365
2150
|
|
|
366
2151
|
// src/react/hooks/use-featured.ts
|
|
367
|
-
|
|
2152
|
+
var import_react_query7 = require("@tanstack/react-query");
|
|
368
2153
|
function useFeatured(options) {
|
|
369
2154
|
const { client } = useBehio();
|
|
370
|
-
return
|
|
371
|
-
queryKey: ["behio", "featured",
|
|
2155
|
+
return (0, import_react_query7.useQuery)({
|
|
2156
|
+
queryKey: ["behio", "featured", options?.locale, options?.currency],
|
|
372
2157
|
queryFn: () => unwrap(client.catalog.getFeatured({
|
|
373
|
-
locale:
|
|
374
|
-
currency:
|
|
2158
|
+
locale: options?.locale,
|
|
2159
|
+
currency: options?.currency
|
|
375
2160
|
})),
|
|
376
|
-
initialData:
|
|
377
|
-
enabled:
|
|
2161
|
+
initialData: options?.initialData,
|
|
2162
|
+
enabled: options?.enabled !== false
|
|
378
2163
|
});
|
|
379
2164
|
}
|
|
380
2165
|
|
|
381
2166
|
// src/react/hooks/use-filters.ts
|
|
382
|
-
|
|
2167
|
+
var import_react_query8 = require("@tanstack/react-query");
|
|
383
2168
|
function useFilters(options) {
|
|
384
2169
|
const { client } = useBehio();
|
|
385
|
-
return
|
|
2170
|
+
return (0, import_react_query8.useQuery)({
|
|
386
2171
|
queryKey: ["behio", "filters"],
|
|
387
2172
|
queryFn: async () => {
|
|
388
2173
|
const result = await unwrap(client.catalog.getFilters());
|
|
389
2174
|
return result.filters;
|
|
390
2175
|
},
|
|
391
|
-
enabled:
|
|
2176
|
+
enabled: options?.enabled !== false
|
|
392
2177
|
});
|
|
393
2178
|
}
|
|
394
2179
|
|
|
395
2180
|
// src/react/hooks/use-facets.ts
|
|
396
|
-
|
|
2181
|
+
var import_react_query9 = require("@tanstack/react-query");
|
|
397
2182
|
function useFacets(query, options) {
|
|
398
2183
|
const { client } = useBehio();
|
|
399
|
-
return
|
|
400
|
-
queryKey: ["behio", "facets",
|
|
2184
|
+
return (0, import_react_query9.useQuery)({
|
|
2185
|
+
queryKey: ["behio", "facets", query ?? {}],
|
|
401
2186
|
queryFn: async () => unwrap(client.catalog.getFacets(query)),
|
|
402
|
-
enabled:
|
|
2187
|
+
enabled: options?.enabled !== false
|
|
403
2188
|
});
|
|
404
2189
|
}
|
|
405
2190
|
|
|
406
2191
|
// src/react/hooks/use-search.ts
|
|
407
|
-
|
|
408
|
-
|
|
2192
|
+
var import_react4 = require("react");
|
|
2193
|
+
var import_react_query10 = require("@tanstack/react-query");
|
|
409
2194
|
function useSearch(query, options) {
|
|
410
2195
|
const { client } = useBehio();
|
|
411
|
-
const debounceMs =
|
|
412
|
-
const [debouncedQuery, setDebouncedQuery] =
|
|
413
|
-
|
|
2196
|
+
const debounceMs = options?.debounceMs ?? 300;
|
|
2197
|
+
const [debouncedQuery, setDebouncedQuery] = (0, import_react4.useState)(query);
|
|
2198
|
+
(0, import_react4.useEffect)(() => {
|
|
414
2199
|
if (debounceMs <= 0) {
|
|
415
2200
|
setDebouncedQuery(query);
|
|
416
2201
|
return;
|
|
@@ -418,36 +2203,36 @@ function useSearch(query, options) {
|
|
|
418
2203
|
const timer = setTimeout(() => setDebouncedQuery(query), debounceMs);
|
|
419
2204
|
return () => clearTimeout(timer);
|
|
420
2205
|
}, [query, debounceMs]);
|
|
421
|
-
return
|
|
422
|
-
queryKey: ["behio", "search", debouncedQuery,
|
|
2206
|
+
return (0, import_react_query10.useQuery)({
|
|
2207
|
+
queryKey: ["behio", "search", debouncedQuery, options?.page, options?.limit],
|
|
423
2208
|
queryFn: () => unwrap(client.catalog.search(debouncedQuery, {
|
|
424
|
-
page:
|
|
425
|
-
limit:
|
|
2209
|
+
page: options?.page,
|
|
2210
|
+
limit: options?.limit
|
|
426
2211
|
})),
|
|
427
|
-
enabled:
|
|
2212
|
+
enabled: options?.enabled !== false && debouncedQuery.length > 0
|
|
428
2213
|
});
|
|
429
2214
|
}
|
|
430
2215
|
|
|
431
2216
|
// src/react/hooks/use-cart.ts
|
|
432
|
-
|
|
433
|
-
|
|
2217
|
+
var import_react5 = require("react");
|
|
2218
|
+
var import_react_query11 = require("@tanstack/react-query");
|
|
434
2219
|
var CART_KEY = ["behio", "cart"];
|
|
435
2220
|
function useCart(options) {
|
|
436
2221
|
const { client, storage } = useBehio();
|
|
437
|
-
const queryClient =
|
|
2222
|
+
const queryClient = (0, import_react_query11.useQueryClient)();
|
|
438
2223
|
const {
|
|
439
2224
|
data: cart,
|
|
440
2225
|
isLoading,
|
|
441
2226
|
error
|
|
442
|
-
} =
|
|
2227
|
+
} = (0, import_react_query11.useQuery)({
|
|
443
2228
|
queryKey: [...CART_KEY],
|
|
444
2229
|
queryFn: () => unwrap(client.cart.get()),
|
|
445
2230
|
// Only fetch if we have a cart session or are logged in
|
|
446
|
-
enabled:
|
|
2231
|
+
enabled: options?.enabled !== false && (!!client.getCartSession() || !!client.getAccessToken())
|
|
447
2232
|
});
|
|
448
|
-
const addMutation =
|
|
2233
|
+
const addMutation = (0, import_react_query11.useMutation)({
|
|
449
2234
|
mutationFn: async ({ productId, quantity }) => {
|
|
450
|
-
return unwrap(client.cart.addItem({ productId, quantity:
|
|
2235
|
+
return unwrap(client.cart.addItem({ productId, quantity: quantity ?? 1 }));
|
|
451
2236
|
},
|
|
452
2237
|
onSuccess: (result) => {
|
|
453
2238
|
if (result.newSessionToken) {
|
|
@@ -456,7 +2241,7 @@ function useCart(options) {
|
|
|
456
2241
|
queryClient.setQueryData([...CART_KEY], result);
|
|
457
2242
|
}
|
|
458
2243
|
});
|
|
459
|
-
const updateMutation =
|
|
2244
|
+
const updateMutation = (0, import_react_query11.useMutation)({
|
|
460
2245
|
mutationFn: async ({ itemId, quantity }) => {
|
|
461
2246
|
return unwrap(client.cart.updateQuantity(itemId, quantity));
|
|
462
2247
|
},
|
|
@@ -476,7 +2261,7 @@ function useCart(options) {
|
|
|
476
2261
|
return { previous };
|
|
477
2262
|
},
|
|
478
2263
|
onError: (_err, _vars, context) => {
|
|
479
|
-
if (
|
|
2264
|
+
if (context?.previous) {
|
|
480
2265
|
queryClient.setQueryData([...CART_KEY], context.previous);
|
|
481
2266
|
}
|
|
482
2267
|
},
|
|
@@ -484,7 +2269,7 @@ function useCart(options) {
|
|
|
484
2269
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
485
2270
|
}
|
|
486
2271
|
});
|
|
487
|
-
const removeMutation =
|
|
2272
|
+
const removeMutation = (0, import_react_query11.useMutation)({
|
|
488
2273
|
mutationFn: async (itemId) => {
|
|
489
2274
|
return unwrap(client.cart.removeItem(itemId));
|
|
490
2275
|
},
|
|
@@ -502,7 +2287,7 @@ function useCart(options) {
|
|
|
502
2287
|
return { previous };
|
|
503
2288
|
},
|
|
504
2289
|
onError: (_err, _vars, context) => {
|
|
505
|
-
if (
|
|
2290
|
+
if (context?.previous) {
|
|
506
2291
|
queryClient.setQueryData([...CART_KEY], context.previous);
|
|
507
2292
|
}
|
|
508
2293
|
},
|
|
@@ -510,7 +2295,7 @@ function useCart(options) {
|
|
|
510
2295
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
511
2296
|
}
|
|
512
2297
|
});
|
|
513
|
-
const clearMutation =
|
|
2298
|
+
const clearMutation = (0, import_react_query11.useMutation)({
|
|
514
2299
|
mutationFn: () => unwrap(client.cart.clear()),
|
|
515
2300
|
onSuccess: () => {
|
|
516
2301
|
queryClient.setQueryData([...CART_KEY], null);
|
|
@@ -518,19 +2303,19 @@ function useCart(options) {
|
|
|
518
2303
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
519
2304
|
}
|
|
520
2305
|
});
|
|
521
|
-
const applyDiscountMutation =
|
|
2306
|
+
const applyDiscountMutation = (0, import_react_query11.useMutation)({
|
|
522
2307
|
mutationFn: (code) => unwrap(client.cart.applyDiscount(code)),
|
|
523
2308
|
onSuccess: (result) => {
|
|
524
2309
|
queryClient.setQueryData([...CART_KEY], result);
|
|
525
2310
|
}
|
|
526
2311
|
});
|
|
527
|
-
const removeDiscountMutation =
|
|
2312
|
+
const removeDiscountMutation = (0, import_react_query11.useMutation)({
|
|
528
2313
|
mutationFn: () => unwrap(client.cart.removeDiscount()),
|
|
529
2314
|
onSuccess: (result) => {
|
|
530
2315
|
queryClient.setQueryData([...CART_KEY], result);
|
|
531
2316
|
}
|
|
532
2317
|
});
|
|
533
|
-
const mergeMutation =
|
|
2318
|
+
const mergeMutation = (0, import_react_query11.useMutation)({
|
|
534
2319
|
mutationFn: () => unwrap(client.cart.merge()),
|
|
535
2320
|
onSuccess: (result) => {
|
|
536
2321
|
queryClient.setQueryData([...CART_KEY], result);
|
|
@@ -538,36 +2323,36 @@ function useCart(options) {
|
|
|
538
2323
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
539
2324
|
}
|
|
540
2325
|
});
|
|
541
|
-
const addItem =
|
|
2326
|
+
const addItem = (0, import_react5.useCallback)(
|
|
542
2327
|
(productId, quantity) => addMutation.mutateAsync({ productId, quantity }),
|
|
543
2328
|
[addMutation]
|
|
544
2329
|
);
|
|
545
|
-
const updateQuantity =
|
|
2330
|
+
const updateQuantity = (0, import_react5.useCallback)(
|
|
546
2331
|
(itemId, quantity) => updateMutation.mutateAsync({ itemId, quantity }),
|
|
547
2332
|
[updateMutation]
|
|
548
2333
|
);
|
|
549
|
-
const removeItem =
|
|
2334
|
+
const removeItem = (0, import_react5.useCallback)(
|
|
550
2335
|
(itemId) => removeMutation.mutateAsync(itemId),
|
|
551
2336
|
[removeMutation]
|
|
552
2337
|
);
|
|
553
|
-
const clear =
|
|
2338
|
+
const clear = (0, import_react5.useCallback)(
|
|
554
2339
|
() => clearMutation.mutateAsync(),
|
|
555
2340
|
[clearMutation]
|
|
556
2341
|
);
|
|
557
|
-
const applyDiscount =
|
|
2342
|
+
const applyDiscount = (0, import_react5.useCallback)(
|
|
558
2343
|
(code) => applyDiscountMutation.mutateAsync(code),
|
|
559
2344
|
[applyDiscountMutation]
|
|
560
2345
|
);
|
|
561
|
-
const removeDiscount =
|
|
2346
|
+
const removeDiscount = (0, import_react5.useCallback)(
|
|
562
2347
|
() => removeDiscountMutation.mutateAsync(),
|
|
563
2348
|
[removeDiscountMutation]
|
|
564
2349
|
);
|
|
565
|
-
const merge =
|
|
2350
|
+
const merge = (0, import_react5.useCallback)(
|
|
566
2351
|
() => mergeMutation.mutateAsync(),
|
|
567
2352
|
[mergeMutation]
|
|
568
2353
|
);
|
|
569
2354
|
return {
|
|
570
|
-
cart:
|
|
2355
|
+
cart: cart ?? null,
|
|
571
2356
|
isLoading,
|
|
572
2357
|
error,
|
|
573
2358
|
// Actions
|
|
@@ -579,7 +2364,7 @@ function useCart(options) {
|
|
|
579
2364
|
removeDiscount,
|
|
580
2365
|
merge,
|
|
581
2366
|
// Computed
|
|
582
|
-
itemCount:
|
|
2367
|
+
itemCount: cart?.itemCount ?? 0,
|
|
583
2368
|
isEmpty: !cart || cart.items.length === 0,
|
|
584
2369
|
// Mutation states
|
|
585
2370
|
isAdding: addMutation.isPending,
|
|
@@ -589,39 +2374,39 @@ function useCart(options) {
|
|
|
589
2374
|
}
|
|
590
2375
|
|
|
591
2376
|
// src/react/hooks/use-cart-count.ts
|
|
592
|
-
|
|
2377
|
+
var import_react_query12 = require("@tanstack/react-query");
|
|
593
2378
|
var CART_KEY2 = ["behio", "cart"];
|
|
594
2379
|
function useCartCount(options) {
|
|
595
2380
|
const { client } = useBehio();
|
|
596
|
-
const queryClient =
|
|
2381
|
+
const queryClient = (0, import_react_query12.useQueryClient)();
|
|
597
2382
|
const cachedCart = queryClient.getQueryData([...CART_KEY2]);
|
|
598
|
-
const { data } =
|
|
2383
|
+
const { data } = (0, import_react_query12.useQuery)({
|
|
599
2384
|
queryKey: [...CART_KEY2],
|
|
600
2385
|
queryFn: () => unwrap(client.cart.get()),
|
|
601
|
-
enabled:
|
|
2386
|
+
enabled: options?.enabled !== false && !cachedCart && (!!client.getCartSession() || !!client.getAccessToken())
|
|
602
2387
|
});
|
|
603
|
-
const cart =
|
|
604
|
-
return
|
|
2388
|
+
const cart = cachedCart ?? data;
|
|
2389
|
+
return cart?.itemCount ?? 0;
|
|
605
2390
|
}
|
|
606
2391
|
|
|
607
2392
|
// src/react/hooks/use-auth.ts
|
|
608
|
-
|
|
609
|
-
|
|
2393
|
+
var import_react6 = require("react");
|
|
2394
|
+
var import_react_query13 = require("@tanstack/react-query");
|
|
610
2395
|
var CUSTOMER_KEY = ["behio", "customer"];
|
|
611
2396
|
var CART_KEY3 = ["behio", "cart"];
|
|
612
2397
|
function useAuth() {
|
|
613
2398
|
const { client, storage } = useBehio();
|
|
614
|
-
const queryClient =
|
|
2399
|
+
const queryClient = (0, import_react_query13.useQueryClient)();
|
|
615
2400
|
const isLoggedIn = !!client.getAccessToken();
|
|
616
2401
|
const {
|
|
617
2402
|
data: customer,
|
|
618
2403
|
isLoading
|
|
619
|
-
} =
|
|
2404
|
+
} = (0, import_react_query13.useQuery)({
|
|
620
2405
|
queryKey: [...CUSTOMER_KEY],
|
|
621
2406
|
queryFn: () => unwrap(client.customer.getProfile()),
|
|
622
2407
|
enabled: isLoggedIn
|
|
623
2408
|
});
|
|
624
|
-
const persistTokens =
|
|
2409
|
+
const persistTokens = (0, import_react6.useCallback)(
|
|
625
2410
|
(tokens) => {
|
|
626
2411
|
client.setTokens(tokens);
|
|
627
2412
|
storage.set(STORAGE_KEYS.ACCESS_TOKEN, tokens.accessToken);
|
|
@@ -629,7 +2414,7 @@ function useAuth() {
|
|
|
629
2414
|
},
|
|
630
2415
|
[client, storage]
|
|
631
2416
|
);
|
|
632
|
-
const clearAuth =
|
|
2417
|
+
const clearAuth = (0, import_react6.useCallback)(() => {
|
|
633
2418
|
client.clearTokens();
|
|
634
2419
|
storage.remove(STORAGE_KEYS.ACCESS_TOKEN);
|
|
635
2420
|
storage.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
@@ -637,19 +2422,19 @@ function useAuth() {
|
|
|
637
2422
|
queryClient.invalidateQueries({ queryKey: ["behio", "orders"] });
|
|
638
2423
|
queryClient.invalidateQueries({ queryKey: ["behio", "addresses"] });
|
|
639
2424
|
}, [client, storage, queryClient]);
|
|
640
|
-
const postAuth =
|
|
2425
|
+
const postAuth = (0, import_react6.useCallback)(async () => {
|
|
641
2426
|
await queryClient.invalidateQueries({ queryKey: [...CUSTOMER_KEY] });
|
|
642
2427
|
if (client.getCartSession()) {
|
|
643
2428
|
try {
|
|
644
2429
|
await unwrap(client.cart.merge());
|
|
645
2430
|
client.clearCartSession();
|
|
646
2431
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
647
|
-
} catch
|
|
2432
|
+
} catch {
|
|
648
2433
|
}
|
|
649
2434
|
}
|
|
650
2435
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY3] });
|
|
651
2436
|
}, [client, storage, queryClient]);
|
|
652
|
-
const loginMutation =
|
|
2437
|
+
const loginMutation = (0, import_react_query13.useMutation)({
|
|
653
2438
|
mutationFn: async ({ email, password }) => {
|
|
654
2439
|
return unwrap(client.auth.login({ email, password }));
|
|
655
2440
|
},
|
|
@@ -658,7 +2443,7 @@ function useAuth() {
|
|
|
658
2443
|
await postAuth();
|
|
659
2444
|
}
|
|
660
2445
|
});
|
|
661
|
-
const registerMutation =
|
|
2446
|
+
const registerMutation = (0, import_react_query13.useMutation)({
|
|
662
2447
|
mutationFn: async (input) => {
|
|
663
2448
|
return unwrap(client.auth.register(input));
|
|
664
2449
|
},
|
|
@@ -669,48 +2454,48 @@ function useAuth() {
|
|
|
669
2454
|
}
|
|
670
2455
|
}
|
|
671
2456
|
});
|
|
672
|
-
const logoutMutation =
|
|
2457
|
+
const logoutMutation = (0, import_react_query13.useMutation)({
|
|
673
2458
|
mutationFn: () => unwrap(client.auth.logout()),
|
|
674
2459
|
onSettled: () => {
|
|
675
2460
|
clearAuth();
|
|
676
2461
|
}
|
|
677
2462
|
});
|
|
678
|
-
const forgotPasswordMutation =
|
|
2463
|
+
const forgotPasswordMutation = (0, import_react_query13.useMutation)({
|
|
679
2464
|
mutationFn: (email) => unwrap(client.auth.forgotPassword(email))
|
|
680
2465
|
});
|
|
681
|
-
const resetPasswordMutation =
|
|
2466
|
+
const resetPasswordMutation = (0, import_react_query13.useMutation)({
|
|
682
2467
|
mutationFn: ({ token, newPassword }) => unwrap(client.auth.resetPassword(token, newPassword))
|
|
683
2468
|
});
|
|
684
|
-
const verifyEmailMutation =
|
|
2469
|
+
const verifyEmailMutation = (0, import_react_query13.useMutation)({
|
|
685
2470
|
mutationFn: (token) => unwrap(client.auth.verifyEmail(token))
|
|
686
2471
|
});
|
|
687
|
-
const login =
|
|
2472
|
+
const login = (0, import_react6.useCallback)(
|
|
688
2473
|
(email, password) => loginMutation.mutateAsync({ email, password }).then(() => void 0),
|
|
689
2474
|
[loginMutation]
|
|
690
2475
|
);
|
|
691
|
-
const register =
|
|
2476
|
+
const register = (0, import_react6.useCallback)(
|
|
692
2477
|
(input) => registerMutation.mutateAsync(input).then(() => void 0),
|
|
693
2478
|
[registerMutation]
|
|
694
2479
|
);
|
|
695
|
-
const logout =
|
|
2480
|
+
const logout = (0, import_react6.useCallback)(
|
|
696
2481
|
() => logoutMutation.mutateAsync().then(() => void 0),
|
|
697
2482
|
[logoutMutation]
|
|
698
2483
|
);
|
|
699
|
-
const forgotPassword =
|
|
2484
|
+
const forgotPassword = (0, import_react6.useCallback)(
|
|
700
2485
|
(email) => forgotPasswordMutation.mutateAsync(email).then(() => void 0),
|
|
701
2486
|
[forgotPasswordMutation]
|
|
702
2487
|
);
|
|
703
|
-
const resetPassword =
|
|
2488
|
+
const resetPassword = (0, import_react6.useCallback)(
|
|
704
2489
|
(token, newPassword) => resetPasswordMutation.mutateAsync({ token, newPassword }).then(() => void 0),
|
|
705
2490
|
[resetPasswordMutation]
|
|
706
2491
|
);
|
|
707
|
-
const verifyEmail =
|
|
2492
|
+
const verifyEmail = (0, import_react6.useCallback)(
|
|
708
2493
|
(token) => verifyEmailMutation.mutateAsync(token).then(() => void 0),
|
|
709
2494
|
[verifyEmailMutation]
|
|
710
2495
|
);
|
|
711
2496
|
return {
|
|
712
2497
|
isLoggedIn,
|
|
713
|
-
customer:
|
|
2498
|
+
customer: customer ?? null,
|
|
714
2499
|
isLoading,
|
|
715
2500
|
// Actions
|
|
716
2501
|
login,
|
|
@@ -728,33 +2513,33 @@ function useAuth() {
|
|
|
728
2513
|
}
|
|
729
2514
|
|
|
730
2515
|
// src/react/hooks/use-customer.ts
|
|
731
|
-
|
|
732
|
-
|
|
2516
|
+
var import_react7 = require("react");
|
|
2517
|
+
var import_react_query14 = require("@tanstack/react-query");
|
|
733
2518
|
var CUSTOMER_KEY2 = ["behio", "customer"];
|
|
734
2519
|
function useCustomer(options) {
|
|
735
2520
|
const { client } = useBehio();
|
|
736
|
-
const queryClient =
|
|
2521
|
+
const queryClient = (0, import_react_query14.useQueryClient)();
|
|
737
2522
|
const {
|
|
738
2523
|
data,
|
|
739
2524
|
isLoading,
|
|
740
2525
|
error
|
|
741
|
-
} =
|
|
2526
|
+
} = (0, import_react_query14.useQuery)({
|
|
742
2527
|
queryKey: [...CUSTOMER_KEY2],
|
|
743
2528
|
queryFn: () => unwrap(client.customer.getProfile()),
|
|
744
|
-
enabled:
|
|
2529
|
+
enabled: options?.enabled !== false && !!client.getAccessToken()
|
|
745
2530
|
});
|
|
746
|
-
const updateMutation =
|
|
2531
|
+
const updateMutation = (0, import_react_query14.useMutation)({
|
|
747
2532
|
mutationFn: (data2) => unwrap(client.customer.updateProfile(data2)),
|
|
748
2533
|
onSuccess: (updated) => {
|
|
749
2534
|
queryClient.setQueryData([...CUSTOMER_KEY2], updated);
|
|
750
2535
|
}
|
|
751
2536
|
});
|
|
752
|
-
const updateProfile =
|
|
2537
|
+
const updateProfile = (0, import_react7.useCallback)(
|
|
753
2538
|
(profileData) => updateMutation.mutateAsync(profileData),
|
|
754
2539
|
[updateMutation]
|
|
755
2540
|
);
|
|
756
2541
|
return {
|
|
757
|
-
data:
|
|
2542
|
+
data: data ?? null,
|
|
758
2543
|
isLoading,
|
|
759
2544
|
error,
|
|
760
2545
|
updateProfile,
|
|
@@ -763,56 +2548,56 @@ function useCustomer(options) {
|
|
|
763
2548
|
}
|
|
764
2549
|
|
|
765
2550
|
// src/react/hooks/use-addresses.ts
|
|
766
|
-
|
|
767
|
-
|
|
2551
|
+
var import_react8 = require("react");
|
|
2552
|
+
var import_react_query15 = require("@tanstack/react-query");
|
|
768
2553
|
var ADDRESSES_KEY = ["behio", "addresses"];
|
|
769
2554
|
function useAddresses(options) {
|
|
770
2555
|
const { client } = useBehio();
|
|
771
|
-
const queryClient =
|
|
2556
|
+
const queryClient = (0, import_react_query15.useQueryClient)();
|
|
772
2557
|
const {
|
|
773
2558
|
data,
|
|
774
2559
|
isLoading,
|
|
775
2560
|
error
|
|
776
|
-
} =
|
|
2561
|
+
} = (0, import_react_query15.useQuery)({
|
|
777
2562
|
queryKey: [...ADDRESSES_KEY],
|
|
778
2563
|
queryFn: async () => {
|
|
779
2564
|
const result = await unwrap(client.customer.getAddresses());
|
|
780
2565
|
return result.items;
|
|
781
2566
|
},
|
|
782
|
-
enabled:
|
|
2567
|
+
enabled: options?.enabled !== false && !!client.getAccessToken()
|
|
783
2568
|
});
|
|
784
|
-
const createMutation =
|
|
2569
|
+
const createMutation = (0, import_react_query15.useMutation)({
|
|
785
2570
|
mutationFn: (address) => unwrap(client.customer.createAddress(address)),
|
|
786
2571
|
onSuccess: () => {
|
|
787
2572
|
queryClient.invalidateQueries({ queryKey: [...ADDRESSES_KEY] });
|
|
788
2573
|
}
|
|
789
2574
|
});
|
|
790
|
-
const updateMutation =
|
|
2575
|
+
const updateMutation = (0, import_react_query15.useMutation)({
|
|
791
2576
|
mutationFn: ({ addressId, data: data2 }) => unwrap(client.customer.updateAddress(addressId, data2)),
|
|
792
2577
|
onSuccess: () => {
|
|
793
2578
|
queryClient.invalidateQueries({ queryKey: [...ADDRESSES_KEY] });
|
|
794
2579
|
}
|
|
795
2580
|
});
|
|
796
|
-
const deleteMutation =
|
|
2581
|
+
const deleteMutation = (0, import_react_query15.useMutation)({
|
|
797
2582
|
mutationFn: (addressId) => unwrap(client.customer.deleteAddress(addressId)),
|
|
798
2583
|
onSuccess: () => {
|
|
799
2584
|
queryClient.invalidateQueries({ queryKey: [...ADDRESSES_KEY] });
|
|
800
2585
|
}
|
|
801
2586
|
});
|
|
802
|
-
const createAddress =
|
|
2587
|
+
const createAddress = (0, import_react8.useCallback)(
|
|
803
2588
|
(address) => createMutation.mutateAsync(address),
|
|
804
2589
|
[createMutation]
|
|
805
2590
|
);
|
|
806
|
-
const updateAddress =
|
|
2591
|
+
const updateAddress = (0, import_react8.useCallback)(
|
|
807
2592
|
(addressId, addressData) => updateMutation.mutateAsync({ addressId, data: addressData }),
|
|
808
2593
|
[updateMutation]
|
|
809
2594
|
);
|
|
810
|
-
const deleteAddress =
|
|
2595
|
+
const deleteAddress = (0, import_react8.useCallback)(
|
|
811
2596
|
(addressId) => deleteMutation.mutateAsync(addressId),
|
|
812
2597
|
[deleteMutation]
|
|
813
2598
|
);
|
|
814
2599
|
return {
|
|
815
|
-
addresses:
|
|
2600
|
+
addresses: data ?? [],
|
|
816
2601
|
isLoading,
|
|
817
2602
|
error,
|
|
818
2603
|
createAddress,
|
|
@@ -824,18 +2609,18 @@ function useAddresses(options) {
|
|
|
824
2609
|
}
|
|
825
2610
|
|
|
826
2611
|
// src/react/hooks/use-address-autocomplete.ts
|
|
827
|
-
|
|
828
|
-
|
|
2612
|
+
var import_react9 = require("react");
|
|
2613
|
+
var import_react_query16 = require("@tanstack/react-query");
|
|
829
2614
|
var AC_KEY = ["behio", "address-autocomplete"];
|
|
830
2615
|
function useAddressAutocomplete(options) {
|
|
831
2616
|
const { country, debounce = 300, minChars = 3, enabled = true } = options;
|
|
832
2617
|
const { client } = useBehio();
|
|
833
|
-
const [query, setQuery] =
|
|
834
|
-
const [debouncedQuery, setDebouncedQuery] =
|
|
835
|
-
const [selected, setSelected] =
|
|
836
|
-
const [isSelecting, setIsSelecting] =
|
|
837
|
-
const timerRef =
|
|
838
|
-
|
|
2618
|
+
const [query, setQuery] = (0, import_react9.useState)("");
|
|
2619
|
+
const [debouncedQuery, setDebouncedQuery] = (0, import_react9.useState)("");
|
|
2620
|
+
const [selected, setSelected] = (0, import_react9.useState)(null);
|
|
2621
|
+
const [isSelecting, setIsSelecting] = (0, import_react9.useState)(false);
|
|
2622
|
+
const timerRef = (0, import_react9.useRef)(void 0);
|
|
2623
|
+
(0, import_react9.useEffect)(() => {
|
|
839
2624
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
840
2625
|
if (query.length < minChars) {
|
|
841
2626
|
setDebouncedQuery("");
|
|
@@ -848,14 +2633,14 @@ function useAddressAutocomplete(options) {
|
|
|
848
2633
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
849
2634
|
};
|
|
850
2635
|
}, [query, debounce, minChars]);
|
|
851
|
-
const { data, isLoading } =
|
|
2636
|
+
const { data, isLoading } = (0, import_react_query16.useQuery)({
|
|
852
2637
|
queryKey: [...AC_KEY, debouncedQuery, country],
|
|
853
2638
|
queryFn: () => unwrap(client.addresses.autocomplete(debouncedQuery, country)),
|
|
854
2639
|
enabled: enabled && debouncedQuery.length >= minChars,
|
|
855
2640
|
staleTime: 6e4
|
|
856
2641
|
// Cache suggestions for 1 min
|
|
857
2642
|
});
|
|
858
|
-
const select =
|
|
2643
|
+
const select = (0, import_react9.useCallback)(
|
|
859
2644
|
async (placeId) => {
|
|
860
2645
|
setIsSelecting(true);
|
|
861
2646
|
try {
|
|
@@ -870,7 +2655,7 @@ function useAddressAutocomplete(options) {
|
|
|
870
2655
|
},
|
|
871
2656
|
[client]
|
|
872
2657
|
);
|
|
873
|
-
const clear =
|
|
2658
|
+
const clear = (0, import_react9.useCallback)(() => {
|
|
874
2659
|
setQuery("");
|
|
875
2660
|
setDebouncedQuery("");
|
|
876
2661
|
setSelected(null);
|
|
@@ -878,7 +2663,7 @@ function useAddressAutocomplete(options) {
|
|
|
878
2663
|
return {
|
|
879
2664
|
query,
|
|
880
2665
|
setQuery,
|
|
881
|
-
suggestions:
|
|
2666
|
+
suggestions: data?.suggestions ?? [],
|
|
882
2667
|
isLoading: isLoading && debouncedQuery.length >= minChars,
|
|
883
2668
|
select,
|
|
884
2669
|
selected,
|
|
@@ -888,39 +2673,39 @@ function useAddressAutocomplete(options) {
|
|
|
888
2673
|
}
|
|
889
2674
|
|
|
890
2675
|
// src/react/hooks/use-loyalty.ts
|
|
891
|
-
|
|
2676
|
+
var import_react_query17 = require("@tanstack/react-query");
|
|
892
2677
|
var LOYALTY_KEY = ["behio", "loyalty"];
|
|
893
2678
|
function useLoyalty(options) {
|
|
894
2679
|
const { client } = useBehio();
|
|
895
|
-
const { data, isLoading, error, refetch } =
|
|
2680
|
+
const { data, isLoading, error, refetch } = (0, import_react_query17.useQuery)({
|
|
896
2681
|
queryKey: [...LOYALTY_KEY],
|
|
897
2682
|
queryFn: () => unwrap(client.customer.getLoyalty()),
|
|
898
|
-
enabled:
|
|
2683
|
+
enabled: options?.enabled !== false && !!client.getAccessToken()
|
|
899
2684
|
});
|
|
900
2685
|
return { loyalty: data, isLoading, error, refetch };
|
|
901
2686
|
}
|
|
902
2687
|
|
|
903
2688
|
// src/react/hooks/use-courses.ts
|
|
904
|
-
|
|
2689
|
+
var import_react_query18 = require("@tanstack/react-query");
|
|
905
2690
|
var COURSES_KEY = ["behio", "courses"];
|
|
906
2691
|
function useCourses(options) {
|
|
907
2692
|
const { client } = useBehio();
|
|
908
|
-
const { data, isLoading, error, refetch } =
|
|
2693
|
+
const { data, isLoading, error, refetch } = (0, import_react_query18.useQuery)({
|
|
909
2694
|
queryKey: [...COURSES_KEY],
|
|
910
2695
|
queryFn: () => unwrap(client.customer.getCourses()),
|
|
911
|
-
enabled:
|
|
2696
|
+
enabled: options?.enabled !== false && !!client.getAccessToken()
|
|
912
2697
|
});
|
|
913
|
-
return { courses:
|
|
2698
|
+
return { courses: data?.items ?? [], isLoading, error, refetch };
|
|
914
2699
|
}
|
|
915
2700
|
function useCourse(courseId, options) {
|
|
916
2701
|
const { client } = useBehio();
|
|
917
|
-
const queryClient =
|
|
918
|
-
const { data, isLoading, error, refetch } =
|
|
2702
|
+
const queryClient = (0, import_react_query18.useQueryClient)();
|
|
2703
|
+
const { data, isLoading, error, refetch } = (0, import_react_query18.useQuery)({
|
|
919
2704
|
queryKey: [...COURSES_KEY, courseId],
|
|
920
2705
|
queryFn: () => unwrap(client.customer.getCourse(courseId)),
|
|
921
|
-
enabled:
|
|
2706
|
+
enabled: options?.enabled !== false && !!courseId && !!client.getAccessToken()
|
|
922
2707
|
});
|
|
923
|
-
const complete =
|
|
2708
|
+
const complete = (0, import_react_query18.useMutation)({
|
|
924
2709
|
mutationFn: ({ lessonId }) => unwrap(client.customer.completeLesson(courseId, lessonId)),
|
|
925
2710
|
onSuccess: () => {
|
|
926
2711
|
void queryClient.invalidateQueries({ queryKey: [...COURSES_KEY] });
|
|
@@ -935,33 +2720,238 @@ function useCourse(courseId, options) {
|
|
|
935
2720
|
isCompleting: complete.isPending
|
|
936
2721
|
};
|
|
937
2722
|
}
|
|
2723
|
+
function useLessonQuiz(courseId, lessonId, options) {
|
|
2724
|
+
const { client } = useBehio();
|
|
2725
|
+
const queryClient = (0, import_react_query18.useQueryClient)();
|
|
2726
|
+
const { data, isLoading, error, refetch } = (0, import_react_query18.useQuery)({
|
|
2727
|
+
queryKey: [...COURSES_KEY, courseId, "quiz", lessonId],
|
|
2728
|
+
queryFn: () => unwrap(
|
|
2729
|
+
client.customer.getLessonQuiz(courseId, lessonId)
|
|
2730
|
+
),
|
|
2731
|
+
enabled: options?.enabled !== false && !!courseId && !!lessonId && !!client.getAccessToken()
|
|
2732
|
+
});
|
|
2733
|
+
const submit = (0, import_react_query18.useMutation)(
|
|
2734
|
+
{
|
|
2735
|
+
mutationFn: ({ answers }) => unwrap(
|
|
2736
|
+
client.customer.submitLessonQuiz(
|
|
2737
|
+
courseId,
|
|
2738
|
+
lessonId,
|
|
2739
|
+
answers
|
|
2740
|
+
)
|
|
2741
|
+
),
|
|
2742
|
+
onSuccess: (result) => {
|
|
2743
|
+
if (result.lessonCompleted) {
|
|
2744
|
+
void queryClient.invalidateQueries({ queryKey: [...COURSES_KEY] });
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
);
|
|
2749
|
+
return {
|
|
2750
|
+
quiz: data,
|
|
2751
|
+
isLoading,
|
|
2752
|
+
error,
|
|
2753
|
+
refetch,
|
|
2754
|
+
submitQuiz: (answers) => submit.mutateAsync({ answers }),
|
|
2755
|
+
isSubmitting: submit.isPending,
|
|
2756
|
+
result: submit.data ?? null
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
function useCourseCertificates(options) {
|
|
2760
|
+
const { client } = useBehio();
|
|
2761
|
+
const { data, isLoading, error, refetch } = (0, import_react_query18.useQuery)({
|
|
2762
|
+
queryKey: [...COURSES_KEY, "certificates"],
|
|
2763
|
+
queryFn: () => unwrap(client.customer.getCourseCertificates()),
|
|
2764
|
+
enabled: options?.enabled !== false && !!client.getAccessToken()
|
|
2765
|
+
});
|
|
2766
|
+
return { certificates: data?.items ?? [], isLoading, error, refetch };
|
|
2767
|
+
}
|
|
2768
|
+
function useCertificateVerification(code, options) {
|
|
2769
|
+
const { client } = useBehio();
|
|
2770
|
+
const { data, isLoading, error, refetch } = (0, import_react_query18.useQuery)(
|
|
2771
|
+
{
|
|
2772
|
+
queryKey: ["behio", "certificate-verification", code],
|
|
2773
|
+
queryFn: () => unwrap(client.certificates.verify(code)),
|
|
2774
|
+
enabled: options?.enabled !== false && !!code,
|
|
2775
|
+
retry: false
|
|
2776
|
+
}
|
|
2777
|
+
);
|
|
2778
|
+
return { verification: data ?? null, isLoading, error, refetch };
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
// src/react/hooks/use-lesson-tutor.ts
|
|
2782
|
+
var import_react_query19 = require("@tanstack/react-query");
|
|
2783
|
+
var TUTOR_KEY = ["behio", "course-tutor"];
|
|
2784
|
+
function useLessonTutor(courseId, lessonId, options) {
|
|
2785
|
+
const { client } = useBehio();
|
|
2786
|
+
const queryClient = (0, import_react_query19.useQueryClient)();
|
|
2787
|
+
const { data, isLoading, error, refetch } = (0, import_react_query19.useQuery)({
|
|
2788
|
+
queryKey: [...TUTOR_KEY, courseId, lessonId],
|
|
2789
|
+
queryFn: () => unwrap(
|
|
2790
|
+
client.customer.getLessonTutorThread(
|
|
2791
|
+
courseId,
|
|
2792
|
+
lessonId
|
|
2793
|
+
)
|
|
2794
|
+
),
|
|
2795
|
+
enabled: options?.enabled !== false && !!courseId && !!lessonId && !!client.getAccessToken()
|
|
2796
|
+
});
|
|
2797
|
+
const ask = (0, import_react_query19.useMutation)({
|
|
2798
|
+
mutationFn: ({ question }) => unwrap(
|
|
2799
|
+
client.customer.askLessonTutor(
|
|
2800
|
+
courseId,
|
|
2801
|
+
lessonId,
|
|
2802
|
+
question
|
|
2803
|
+
)
|
|
2804
|
+
),
|
|
2805
|
+
onSuccess: (message) => {
|
|
2806
|
+
queryClient.setQueryData(
|
|
2807
|
+
[...TUTOR_KEY, courseId, lessonId],
|
|
2808
|
+
(prev) => prev ? { ...prev, items: [...prev.items, message] } : prev
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
2812
|
+
return {
|
|
2813
|
+
messages: data?.items ?? [],
|
|
2814
|
+
/** False when the merchant disabled the AI tutor — hide the widget. */
|
|
2815
|
+
enabled: data?.enabled ?? true,
|
|
2816
|
+
isLoading,
|
|
2817
|
+
error,
|
|
2818
|
+
refetch,
|
|
2819
|
+
ask: (question) => ask.mutateAsync({ question }),
|
|
2820
|
+
isAsking: ask.isPending,
|
|
2821
|
+
askError: ask.error
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
938
2824
|
|
|
939
|
-
// src/react/hooks/use-
|
|
2825
|
+
// src/react/hooks/use-lesson-comments.ts
|
|
2826
|
+
var import_react_query20 = require("@tanstack/react-query");
|
|
2827
|
+
var COMMENTS_KEY = ["behio", "course-comments"];
|
|
2828
|
+
function useLessonComments(courseId, lessonId, options) {
|
|
2829
|
+
const { client } = useBehio();
|
|
2830
|
+
const queryClient = (0, import_react_query20.useQueryClient)();
|
|
2831
|
+
const page = options?.page ?? 1;
|
|
2832
|
+
const { data, isLoading, error, refetch } = (0, import_react_query20.useQuery)({
|
|
2833
|
+
queryKey: [...COMMENTS_KEY, courseId, lessonId, page],
|
|
2834
|
+
queryFn: () => unwrap(
|
|
2835
|
+
client.customer.getLessonComments(
|
|
2836
|
+
courseId,
|
|
2837
|
+
lessonId,
|
|
2838
|
+
page
|
|
2839
|
+
)
|
|
2840
|
+
),
|
|
2841
|
+
enabled: options?.enabled !== false && !!courseId && !!lessonId && !!client.getAccessToken()
|
|
2842
|
+
});
|
|
2843
|
+
const invalidate = () => queryClient.invalidateQueries({
|
|
2844
|
+
queryKey: [...COMMENTS_KEY, courseId, lessonId]
|
|
2845
|
+
});
|
|
2846
|
+
const post = (0, import_react_query20.useMutation)({
|
|
2847
|
+
mutationFn: ({ body, parentId }) => unwrap(
|
|
2848
|
+
client.customer.postLessonComment(
|
|
2849
|
+
courseId,
|
|
2850
|
+
lessonId,
|
|
2851
|
+
body,
|
|
2852
|
+
parentId
|
|
2853
|
+
)
|
|
2854
|
+
),
|
|
2855
|
+
onSuccess: () => {
|
|
2856
|
+
void invalidate();
|
|
2857
|
+
}
|
|
2858
|
+
});
|
|
2859
|
+
const remove = (0, import_react_query20.useMutation)({
|
|
2860
|
+
mutationFn: ({ commentId }) => unwrap(
|
|
2861
|
+
client.customer.deleteLessonComment(
|
|
2862
|
+
courseId,
|
|
2863
|
+
lessonId,
|
|
2864
|
+
commentId
|
|
2865
|
+
)
|
|
2866
|
+
),
|
|
2867
|
+
onSuccess: () => {
|
|
2868
|
+
void invalidate();
|
|
2869
|
+
}
|
|
2870
|
+
});
|
|
2871
|
+
return {
|
|
2872
|
+
comments: data?.items ?? [],
|
|
2873
|
+
totalCount: data?.totalCount ?? 0,
|
|
2874
|
+
page: data?.page ?? page,
|
|
2875
|
+
pageSize: data?.pageSize ?? 20,
|
|
2876
|
+
/** False when the merchant disabled the discussion — hide the widget. */
|
|
2877
|
+
enabled: data?.enabled ?? true,
|
|
2878
|
+
isLoading,
|
|
2879
|
+
error,
|
|
2880
|
+
refetch,
|
|
2881
|
+
postComment: (body, parentId) => post.mutateAsync({ body, parentId }),
|
|
2882
|
+
isPosting: post.isPending,
|
|
2883
|
+
deleteComment: (commentId) => remove.mutateAsync({ commentId }),
|
|
2884
|
+
isDeleting: remove.isPending
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
// src/react/hooks/use-lesson-note.ts
|
|
2889
|
+
var import_react_query21 = require("@tanstack/react-query");
|
|
2890
|
+
var NOTE_KEY = ["behio", "course-note"];
|
|
2891
|
+
function useLessonNote(courseId, lessonId, options) {
|
|
2892
|
+
const { client } = useBehio();
|
|
2893
|
+
const queryClient = (0, import_react_query21.useQueryClient)();
|
|
2894
|
+
const { data, isLoading, error, refetch } = (0, import_react_query21.useQuery)({
|
|
2895
|
+
queryKey: [...NOTE_KEY, courseId, lessonId],
|
|
2896
|
+
queryFn: () => unwrap(
|
|
2897
|
+
client.customer.getLessonNote(courseId, lessonId)
|
|
2898
|
+
),
|
|
2899
|
+
enabled: options?.enabled !== false && !!courseId && !!lessonId && !!client.getAccessToken()
|
|
2900
|
+
});
|
|
2901
|
+
const save = (0, import_react_query21.useMutation)({
|
|
2902
|
+
mutationFn: ({ body }) => unwrap(
|
|
2903
|
+
client.customer.saveLessonNote(
|
|
2904
|
+
courseId,
|
|
2905
|
+
lessonId,
|
|
2906
|
+
body
|
|
2907
|
+
)
|
|
2908
|
+
),
|
|
2909
|
+
onSuccess: (note) => {
|
|
2910
|
+
queryClient.setQueryData(
|
|
2911
|
+
[...NOTE_KEY, courseId, lessonId],
|
|
2912
|
+
note
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
});
|
|
2916
|
+
return {
|
|
2917
|
+
note: data ?? null,
|
|
2918
|
+
body: data?.body ?? "",
|
|
2919
|
+
updatedAt: data?.updatedAt ?? null,
|
|
2920
|
+
isLoading,
|
|
2921
|
+
error,
|
|
2922
|
+
refetch,
|
|
2923
|
+
save: (body) => save.mutateAsync({ body }),
|
|
2924
|
+
isSaving: save.isPending,
|
|
2925
|
+
saveError: save.error
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
940
2928
|
|
|
2929
|
+
// src/react/hooks/use-subscriptions.ts
|
|
2930
|
+
var import_react_query22 = require("@tanstack/react-query");
|
|
941
2931
|
var SUBSCRIPTIONS_KEY = ["behio", "subscriptions"];
|
|
942
2932
|
function useSubscriptions(options) {
|
|
943
2933
|
const { client } = useBehio();
|
|
944
|
-
const qc =
|
|
945
|
-
const query =
|
|
2934
|
+
const qc = (0, import_react_query22.useQueryClient)();
|
|
2935
|
+
const query = (0, import_react_query22.useQuery)({
|
|
946
2936
|
queryKey: [...SUBSCRIPTIONS_KEY],
|
|
947
2937
|
queryFn: () => unwrap(client.subscriptions.list()),
|
|
948
|
-
enabled:
|
|
2938
|
+
enabled: options?.enabled !== false && !!client.getAccessToken()
|
|
949
2939
|
});
|
|
950
2940
|
const invalidate = () => qc.invalidateQueries({ queryKey: [...SUBSCRIPTIONS_KEY] });
|
|
951
|
-
const pauseMutation =
|
|
2941
|
+
const pauseMutation = (0, import_react_query22.useMutation)({
|
|
952
2942
|
mutationFn: (subscriptionId) => unwrap(client.subscriptions.pause(subscriptionId)),
|
|
953
2943
|
onSuccess: invalidate
|
|
954
2944
|
});
|
|
955
|
-
const resumeMutation =
|
|
2945
|
+
const resumeMutation = (0, import_react_query22.useMutation)({
|
|
956
2946
|
mutationFn: (subscriptionId) => unwrap(client.subscriptions.resume(subscriptionId)),
|
|
957
2947
|
onSuccess: invalidate
|
|
958
2948
|
});
|
|
959
|
-
const cancelMutation =
|
|
2949
|
+
const cancelMutation = (0, import_react_query22.useMutation)({
|
|
960
2950
|
mutationFn: (subscriptionId) => unwrap(client.subscriptions.cancel(subscriptionId)),
|
|
961
2951
|
onSuccess: invalidate
|
|
962
2952
|
});
|
|
963
2953
|
return {
|
|
964
|
-
subscriptions:
|
|
2954
|
+
subscriptions: query.data?.items ?? [],
|
|
965
2955
|
isLoading: query.isLoading,
|
|
966
2956
|
error: query.error,
|
|
967
2957
|
refetch: query.refetch,
|
|
@@ -975,12 +2965,12 @@ function useSubscriptions(options) {
|
|
|
975
2965
|
}
|
|
976
2966
|
|
|
977
2967
|
// src/react/hooks/use-pickup-points.ts
|
|
978
|
-
|
|
2968
|
+
var import_react_query23 = require("@tanstack/react-query");
|
|
979
2969
|
function usePickupPoints(input) {
|
|
980
2970
|
const { client } = useBehio();
|
|
981
2971
|
const { methodId, query, country, limit, enabled } = input;
|
|
982
|
-
const { data, isLoading, error, refetch } =
|
|
983
|
-
queryKey: ["behio", "pickup-points", methodId,
|
|
2972
|
+
const { data, isLoading, error, refetch } = (0, import_react_query23.useQuery)({
|
|
2973
|
+
queryKey: ["behio", "pickup-points", methodId, query ?? "", country ?? "", limit ?? 30],
|
|
984
2974
|
queryFn: () => unwrap(
|
|
985
2975
|
client.shipping.getPickupPoints({
|
|
986
2976
|
methodId,
|
|
@@ -991,44 +2981,44 @@ function usePickupPoints(input) {
|
|
|
991
2981
|
),
|
|
992
2982
|
enabled: enabled !== false && !!methodId
|
|
993
2983
|
});
|
|
994
|
-
return { pickupPoints:
|
|
2984
|
+
return { pickupPoints: data?.items ?? [], isLoading, error, refetch };
|
|
995
2985
|
}
|
|
996
2986
|
|
|
997
2987
|
// src/react/hooks/use-shipping-methods.ts
|
|
998
|
-
|
|
2988
|
+
var import_react_query24 = require("@tanstack/react-query");
|
|
999
2989
|
function useShippingMethods(options) {
|
|
1000
2990
|
const { client, currency: activeCurrency } = useBehio();
|
|
1001
|
-
const currency =
|
|
1002
|
-
const { country, cartTotal, cartWeightKg } =
|
|
1003
|
-
const { data, isLoading, error, refetch } =
|
|
2991
|
+
const currency = options?.currency ?? activeCurrency;
|
|
2992
|
+
const { country, cartTotal, cartWeightKg } = options ?? {};
|
|
2993
|
+
const { data, isLoading, error, refetch } = (0, import_react_query24.useQuery)({
|
|
1004
2994
|
queryKey: [
|
|
1005
2995
|
"behio",
|
|
1006
2996
|
"shipping-methods",
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
2997
|
+
currency ?? "",
|
|
2998
|
+
country ?? "",
|
|
2999
|
+
cartTotal ?? "",
|
|
3000
|
+
cartWeightKg ?? ""
|
|
1011
3001
|
],
|
|
1012
3002
|
queryFn: () => unwrap(client.shipping.listMethods({ currency, country, cartTotal, cartWeightKg })),
|
|
1013
|
-
enabled:
|
|
3003
|
+
enabled: options?.enabled !== false
|
|
1014
3004
|
});
|
|
1015
|
-
return { methods:
|
|
3005
|
+
return { methods: data?.items ?? [], isLoading, error, refetch };
|
|
1016
3006
|
}
|
|
1017
3007
|
|
|
1018
3008
|
// src/react/hooks/use-shipping-quote.ts
|
|
1019
|
-
|
|
3009
|
+
var import_react_query25 = require("@tanstack/react-query");
|
|
1020
3010
|
function useShippingQuote(options) {
|
|
1021
3011
|
const { client, currency: activeCurrency } = useBehio();
|
|
1022
|
-
const { destinationAddress, items, cartTotal, enabled } =
|
|
1023
|
-
const currency =
|
|
1024
|
-
const { data, isLoading, error, refetch } =
|
|
3012
|
+
const { destinationAddress, items, cartTotal, enabled } = options ?? {};
|
|
3013
|
+
const currency = options?.currency ?? activeCurrency;
|
|
3014
|
+
const { data, isLoading, error, refetch } = (0, import_react_query25.useQuery)({
|
|
1025
3015
|
queryKey: [
|
|
1026
3016
|
"behio",
|
|
1027
3017
|
"shipping-quote",
|
|
1028
|
-
JSON.stringify(
|
|
1029
|
-
JSON.stringify(
|
|
1030
|
-
|
|
1031
|
-
|
|
3018
|
+
JSON.stringify(destinationAddress ?? null),
|
|
3019
|
+
JSON.stringify(items ?? null),
|
|
3020
|
+
cartTotal ?? "",
|
|
3021
|
+
currency ?? ""
|
|
1032
3022
|
],
|
|
1033
3023
|
queryFn: () => unwrap(
|
|
1034
3024
|
client.shipping.quote({
|
|
@@ -1038,33 +3028,33 @@ function useShippingQuote(options) {
|
|
|
1038
3028
|
currency
|
|
1039
3029
|
})
|
|
1040
3030
|
),
|
|
1041
|
-
enabled: enabled !== false && !!
|
|
3031
|
+
enabled: enabled !== false && !!destinationAddress?.country
|
|
1042
3032
|
});
|
|
1043
|
-
return { quotes:
|
|
3033
|
+
return { quotes: data?.items ?? [], isLoading, error, refetch };
|
|
1044
3034
|
}
|
|
1045
3035
|
|
|
1046
3036
|
// src/react/hooks/use-payment-methods.ts
|
|
1047
|
-
|
|
3037
|
+
var import_react_query26 = require("@tanstack/react-query");
|
|
1048
3038
|
function usePaymentMethods(options) {
|
|
1049
3039
|
const { client, currency: activeCurrency } = useBehio();
|
|
1050
|
-
const currency =
|
|
1051
|
-
const { data, isLoading, error, refetch } =
|
|
1052
|
-
queryKey: ["behio", "payment-methods",
|
|
3040
|
+
const currency = options?.currency ?? activeCurrency;
|
|
3041
|
+
const { data, isLoading, error, refetch } = (0, import_react_query26.useQuery)({
|
|
3042
|
+
queryKey: ["behio", "payment-methods", currency ?? ""],
|
|
1053
3043
|
queryFn: () => unwrap(client.catalog.listPaymentMethods({ currency })),
|
|
1054
|
-
enabled:
|
|
3044
|
+
enabled: options?.enabled !== false
|
|
1055
3045
|
});
|
|
1056
|
-
return { methods:
|
|
3046
|
+
return { methods: data?.items ?? [], isLoading, error, refetch };
|
|
1057
3047
|
}
|
|
1058
3048
|
|
|
1059
3049
|
// src/react/hooks/use-personal-offers.ts
|
|
1060
|
-
|
|
1061
|
-
|
|
3050
|
+
var import_react10 = require("react");
|
|
3051
|
+
var import_react_query27 = require("@tanstack/react-query");
|
|
1062
3052
|
function usePersonalOffers(options) {
|
|
1063
3053
|
const { client } = useBehio();
|
|
1064
|
-
const queryClient =
|
|
1065
|
-
const [polledVid, setPolledVid] =
|
|
1066
|
-
const explicitVid =
|
|
1067
|
-
|
|
3054
|
+
const queryClient = (0, import_react_query27.useQueryClient)();
|
|
3055
|
+
const [polledVid, setPolledVid] = (0, import_react10.useState)(null);
|
|
3056
|
+
const explicitVid = options?.visitorId;
|
|
3057
|
+
(0, import_react10.useEffect)(() => {
|
|
1068
3058
|
if (explicitVid) return;
|
|
1069
3059
|
const read = () => {
|
|
1070
3060
|
const vid = client.getAnalyticsVisitorId();
|
|
@@ -1077,14 +3067,14 @@ function usePersonalOffers(options) {
|
|
|
1077
3067
|
}, 3e3);
|
|
1078
3068
|
return () => clearInterval(timer);
|
|
1079
3069
|
}, [client, explicitVid]);
|
|
1080
|
-
const visitorId =
|
|
1081
|
-
const queryKey = ["behio", "personal-offers",
|
|
1082
|
-
const { data, isLoading, error, refetch } =
|
|
3070
|
+
const visitorId = explicitVid ?? polledVid;
|
|
3071
|
+
const queryKey = ["behio", "personal-offers", visitorId ?? ""];
|
|
3072
|
+
const { data, isLoading, error, refetch } = (0, import_react_query27.useQuery)({
|
|
1083
3073
|
queryKey,
|
|
1084
3074
|
queryFn: () => unwrap(client.getPersonalOffers(visitorId)),
|
|
1085
|
-
enabled:
|
|
3075
|
+
enabled: options?.enabled !== false && !!visitorId
|
|
1086
3076
|
});
|
|
1087
|
-
const claimMutation =
|
|
3077
|
+
const claimMutation = (0, import_react_query27.useMutation)({
|
|
1088
3078
|
mutationFn: ({ offerId, email }) => unwrap(client.claimOfferByEmail(offerId, { visitorId, email })),
|
|
1089
3079
|
onSuccess: (result, { offerId }) => {
|
|
1090
3080
|
queryClient.setQueryData(
|
|
@@ -1097,14 +3087,14 @@ function usePersonalOffers(options) {
|
|
|
1097
3087
|
);
|
|
1098
3088
|
}
|
|
1099
3089
|
});
|
|
1100
|
-
const claimByEmail =
|
|
3090
|
+
const claimByEmail = (0, import_react10.useCallback)(
|
|
1101
3091
|
(offerId, email) => claimMutation.mutateAsync({ offerId, email }),
|
|
1102
3092
|
[claimMutation]
|
|
1103
3093
|
);
|
|
1104
3094
|
return {
|
|
1105
|
-
offers:
|
|
3095
|
+
offers: data?.items ?? [],
|
|
1106
3096
|
/** Resolved consent-gated visitor id (null before consent). */
|
|
1107
|
-
visitorId:
|
|
3097
|
+
visitorId: visitorId ?? null,
|
|
1108
3098
|
isLoading,
|
|
1109
3099
|
error,
|
|
1110
3100
|
refetch,
|
|
@@ -1116,22 +3106,22 @@ function usePersonalOffers(options) {
|
|
|
1116
3106
|
}
|
|
1117
3107
|
|
|
1118
3108
|
// src/react/hooks/use-analytics-events.ts
|
|
1119
|
-
|
|
3109
|
+
var import_react11 = require("react");
|
|
1120
3110
|
function useAnalyticsEvents() {
|
|
1121
3111
|
const { client } = useBehio();
|
|
1122
|
-
const track =
|
|
3112
|
+
const track = (0, import_react11.useCallback)(
|
|
1123
3113
|
(events, opts) => {
|
|
1124
3114
|
const list = Array.isArray(events) ? events : [events];
|
|
1125
3115
|
if (list.length === 0) return Promise.resolve();
|
|
1126
3116
|
return client.sendAnalyticsEvents({
|
|
1127
|
-
sessionId:
|
|
1128
|
-
visitorId:
|
|
3117
|
+
sessionId: opts?.sessionId,
|
|
3118
|
+
visitorId: client.getAnalyticsVisitorId() ?? void 0,
|
|
1129
3119
|
events: list.map((e) => ({ ts: Date.now(), ...e }))
|
|
1130
3120
|
});
|
|
1131
3121
|
},
|
|
1132
3122
|
[client]
|
|
1133
3123
|
);
|
|
1134
|
-
const trackEvent =
|
|
3124
|
+
const trackEvent = (0, import_react11.useCallback)(
|
|
1135
3125
|
(name, props) => track({ type: "custom", name, props }),
|
|
1136
3126
|
[track]
|
|
1137
3127
|
);
|
|
@@ -1146,28 +3136,28 @@ function useAnalyticsEvents() {
|
|
|
1146
3136
|
}
|
|
1147
3137
|
|
|
1148
3138
|
// src/react/hooks/use-newsletter.ts
|
|
1149
|
-
|
|
3139
|
+
var import_react_query28 = require("@tanstack/react-query");
|
|
1150
3140
|
function useNewsletterSubscribe() {
|
|
1151
3141
|
const { client } = useBehio();
|
|
1152
|
-
return
|
|
3142
|
+
return (0, import_react_query28.useMutation)({
|
|
1153
3143
|
mutationFn: (input) => unwrap(client.newsletter.subscribe(input))
|
|
1154
3144
|
});
|
|
1155
3145
|
}
|
|
1156
3146
|
function useNewsletterUnsubscribe() {
|
|
1157
3147
|
const { client } = useBehio();
|
|
1158
|
-
return
|
|
3148
|
+
return (0, import_react_query28.useMutation)({
|
|
1159
3149
|
mutationFn: (email) => unwrap(client.newsletter.unsubscribe(email))
|
|
1160
3150
|
});
|
|
1161
3151
|
}
|
|
1162
3152
|
|
|
1163
3153
|
// src/react/hooks/use-orders.ts
|
|
1164
|
-
|
|
1165
|
-
|
|
3154
|
+
var import_react12 = require("react");
|
|
3155
|
+
var import_react_query29 = require("@tanstack/react-query");
|
|
1166
3156
|
function useOrders(options) {
|
|
1167
3157
|
const { client } = useBehio();
|
|
1168
|
-
const { page: initialPage, limit, enabled } =
|
|
1169
|
-
const [page, setPage] =
|
|
1170
|
-
const infinite =
|
|
3158
|
+
const { page: initialPage, limit, enabled } = options ?? {};
|
|
3159
|
+
const [page, setPage] = (0, import_react12.useState)(initialPage ?? 1);
|
|
3160
|
+
const infinite = (0, import_react_query29.useInfiniteQuery)({
|
|
1171
3161
|
queryKey: ["behio", "orders", limit, page],
|
|
1172
3162
|
queryFn: ({ pageParam }) => unwrap(client.orders.list({ limit, page: pageParam })),
|
|
1173
3163
|
initialPageParam: page,
|
|
@@ -1175,33 +3165,33 @@ function useOrders(options) {
|
|
|
1175
3165
|
getPreviousPageParam: (firstPage) => firstPage.page > 1 ? firstPage.page - 1 : void 0,
|
|
1176
3166
|
enabled: enabled !== false && !!client.getAccessToken()
|
|
1177
3167
|
});
|
|
1178
|
-
const items =
|
|
1179
|
-
() =>
|
|
3168
|
+
const items = (0, import_react12.useMemo)(
|
|
3169
|
+
() => infinite.data?.pages.flatMap((p) => p.items) ?? [],
|
|
1180
3170
|
[infinite.data]
|
|
1181
3171
|
);
|
|
1182
|
-
const lastPage =
|
|
1183
|
-
const loadMore =
|
|
3172
|
+
const lastPage = infinite.data?.pages[infinite.data.pages.length - 1];
|
|
3173
|
+
const loadMore = (0, import_react12.useCallback)(() => {
|
|
1184
3174
|
if (infinite.hasNextPage && !infinite.isFetchingNextPage) {
|
|
1185
3175
|
return infinite.fetchNextPage();
|
|
1186
3176
|
}
|
|
1187
3177
|
return Promise.resolve();
|
|
1188
3178
|
}, [infinite]);
|
|
1189
|
-
const goToPage =
|
|
3179
|
+
const goToPage = (0, import_react12.useCallback)((newPage) => {
|
|
1190
3180
|
setPage(newPage);
|
|
1191
3181
|
}, []);
|
|
1192
3182
|
return {
|
|
1193
3183
|
items,
|
|
1194
3184
|
data: infinite.data,
|
|
1195
|
-
total:
|
|
1196
|
-
totalPages:
|
|
1197
|
-
currentPage:
|
|
1198
|
-
limit:
|
|
3185
|
+
total: lastPage?.total ?? 0,
|
|
3186
|
+
totalPages: lastPage?.totalPages ?? 0,
|
|
3187
|
+
currentPage: lastPage?.page ?? page,
|
|
3188
|
+
limit: lastPage?.limit ?? limit ?? 20,
|
|
1199
3189
|
page,
|
|
1200
3190
|
setPage: goToPage,
|
|
1201
3191
|
loadMore,
|
|
1202
3192
|
loadPrevious: infinite.fetchPreviousPage,
|
|
1203
|
-
hasMore:
|
|
1204
|
-
hasPrevious:
|
|
3193
|
+
hasMore: infinite.hasNextPage ?? false,
|
|
3194
|
+
hasPrevious: infinite.hasPreviousPage ?? false,
|
|
1205
3195
|
isLoading: infinite.isLoading,
|
|
1206
3196
|
isFetching: infinite.isFetching,
|
|
1207
3197
|
isLoadingMore: infinite.isFetchingNextPage,
|
|
@@ -1212,33 +3202,33 @@ function useOrders(options) {
|
|
|
1212
3202
|
}
|
|
1213
3203
|
|
|
1214
3204
|
// src/react/hooks/use-order.ts
|
|
1215
|
-
|
|
1216
|
-
|
|
3205
|
+
var import_react13 = require("react");
|
|
3206
|
+
var import_react_query30 = require("@tanstack/react-query");
|
|
1217
3207
|
function useOrder(orderNumber, options) {
|
|
1218
3208
|
const { client } = useBehio();
|
|
1219
|
-
const queryClient =
|
|
3209
|
+
const queryClient = (0, import_react_query30.useQueryClient)();
|
|
1220
3210
|
const {
|
|
1221
3211
|
data,
|
|
1222
3212
|
isLoading,
|
|
1223
3213
|
error
|
|
1224
|
-
} =
|
|
3214
|
+
} = (0, import_react_query30.useQuery)({
|
|
1225
3215
|
queryKey: ["behio", "order", orderNumber],
|
|
1226
3216
|
queryFn: () => unwrap(client.orders.get(orderNumber)),
|
|
1227
|
-
enabled:
|
|
3217
|
+
enabled: options?.enabled !== false && !!orderNumber && !!client.getAccessToken()
|
|
1228
3218
|
});
|
|
1229
|
-
const cancelMutation =
|
|
3219
|
+
const cancelMutation = (0, import_react_query30.useMutation)({
|
|
1230
3220
|
mutationFn: () => unwrap(client.orders.cancel(orderNumber)),
|
|
1231
3221
|
onSuccess: (updated) => {
|
|
1232
3222
|
queryClient.setQueryData(["behio", "order", orderNumber], updated);
|
|
1233
3223
|
queryClient.invalidateQueries({ queryKey: ["behio", "orders"] });
|
|
1234
3224
|
}
|
|
1235
3225
|
});
|
|
1236
|
-
const cancel =
|
|
3226
|
+
const cancel = (0, import_react13.useCallback)(
|
|
1237
3227
|
() => cancelMutation.mutateAsync(),
|
|
1238
3228
|
[cancelMutation]
|
|
1239
3229
|
);
|
|
1240
3230
|
return {
|
|
1241
|
-
data:
|
|
3231
|
+
data: data ?? null,
|
|
1242
3232
|
isLoading,
|
|
1243
3233
|
error,
|
|
1244
3234
|
cancel,
|
|
@@ -1247,22 +3237,22 @@ function useOrder(orderNumber, options) {
|
|
|
1247
3237
|
}
|
|
1248
3238
|
|
|
1249
3239
|
// src/react/hooks/use-order-access.ts
|
|
1250
|
-
|
|
1251
|
-
|
|
3240
|
+
var import_react14 = require("react");
|
|
3241
|
+
var import_react_query31 = require("@tanstack/react-query");
|
|
1252
3242
|
function useOrderAccess() {
|
|
1253
3243
|
const { client } = useBehio();
|
|
1254
|
-
const [orderNumber, setOrderNumber] =
|
|
1255
|
-
const [email, setEmail] =
|
|
1256
|
-
const [order, setOrder] =
|
|
1257
|
-
const [accessToken, setAccessToken] =
|
|
1258
|
-
const requestMutation =
|
|
3244
|
+
const [orderNumber, setOrderNumber] = (0, import_react14.useState)(null);
|
|
3245
|
+
const [email, setEmail] = (0, import_react14.useState)(null);
|
|
3246
|
+
const [order, setOrder] = (0, import_react14.useState)(null);
|
|
3247
|
+
const [accessToken, setAccessToken] = (0, import_react14.useState)(null);
|
|
3248
|
+
const requestMutation = (0, import_react_query31.useMutation)({
|
|
1259
3249
|
mutationFn: (input) => unwrap(client.orders.requestAccessCode(input.orderNumber, input.email)),
|
|
1260
3250
|
onSuccess: (_data, input) => {
|
|
1261
3251
|
setOrderNumber(input.orderNumber);
|
|
1262
3252
|
setEmail(input.email);
|
|
1263
3253
|
}
|
|
1264
3254
|
});
|
|
1265
|
-
const verifyMutation =
|
|
3255
|
+
const verifyMutation = (0, import_react_query31.useMutation)({
|
|
1266
3256
|
mutationFn: (code) => {
|
|
1267
3257
|
if (!orderNumber || !email) {
|
|
1268
3258
|
throw new Error("Request a code before verifying");
|
|
@@ -1274,12 +3264,12 @@ function useOrderAccess() {
|
|
|
1274
3264
|
setAccessToken(result.accessToken);
|
|
1275
3265
|
}
|
|
1276
3266
|
});
|
|
1277
|
-
const requestCode =
|
|
3267
|
+
const requestCode = (0, import_react14.useCallback)(
|
|
1278
3268
|
(on, em) => requestMutation.mutateAsync({ orderNumber: on, email: em }),
|
|
1279
3269
|
[requestMutation]
|
|
1280
3270
|
);
|
|
1281
|
-
const verifyCode =
|
|
1282
|
-
const reset =
|
|
3271
|
+
const verifyCode = (0, import_react14.useCallback)((code) => verifyMutation.mutateAsync(code), [verifyMutation]);
|
|
3272
|
+
const reset = (0, import_react14.useCallback)(() => {
|
|
1283
3273
|
setOrderNumber(null);
|
|
1284
3274
|
setEmail(null);
|
|
1285
3275
|
setOrder(null);
|
|
@@ -1307,13 +3297,13 @@ function useOrderAccess() {
|
|
|
1307
3297
|
}
|
|
1308
3298
|
|
|
1309
3299
|
// src/react/hooks/use-checkout.ts
|
|
1310
|
-
|
|
1311
|
-
|
|
3300
|
+
var import_react15 = require("react");
|
|
3301
|
+
var import_react_query32 = require("@tanstack/react-query");
|
|
1312
3302
|
function useCheckout() {
|
|
1313
3303
|
const { client, storage } = useBehio();
|
|
1314
|
-
const queryClient =
|
|
1315
|
-
const [order, setOrder] =
|
|
1316
|
-
const mutation =
|
|
3304
|
+
const queryClient = (0, import_react_query32.useQueryClient)();
|
|
3305
|
+
const [order, setOrder] = (0, import_react15.useState)(null);
|
|
3306
|
+
const mutation = (0, import_react_query32.useMutation)({
|
|
1317
3307
|
mutationFn: (input) => unwrap(client.checkout.createOrder(input)),
|
|
1318
3308
|
onSuccess: (result) => {
|
|
1319
3309
|
setOrder(result);
|
|
@@ -1322,11 +3312,11 @@ function useCheckout() {
|
|
|
1322
3312
|
queryClient.invalidateQueries({ queryKey: ["behio", "orders"] });
|
|
1323
3313
|
}
|
|
1324
3314
|
});
|
|
1325
|
-
const createOrder =
|
|
3315
|
+
const createOrder = (0, import_react15.useCallback)(
|
|
1326
3316
|
(input) => mutation.mutateAsync(input),
|
|
1327
3317
|
[mutation]
|
|
1328
3318
|
);
|
|
1329
|
-
const reset =
|
|
3319
|
+
const reset = (0, import_react15.useCallback)(() => {
|
|
1330
3320
|
setOrder(null);
|
|
1331
3321
|
mutation.reset();
|
|
1332
3322
|
}, [mutation]);
|
|
@@ -1340,56 +3330,56 @@ function useCheckout() {
|
|
|
1340
3330
|
}
|
|
1341
3331
|
|
|
1342
3332
|
// src/react/hooks/use-pages.ts
|
|
1343
|
-
|
|
3333
|
+
var import_react_query33 = require("@tanstack/react-query");
|
|
1344
3334
|
function usePages(locale, options) {
|
|
1345
3335
|
const { client } = useBehio();
|
|
1346
|
-
return
|
|
3336
|
+
return (0, import_react_query33.useQuery)({
|
|
1347
3337
|
queryKey: ["behio", "pages", locale],
|
|
1348
3338
|
queryFn: async () => {
|
|
1349
3339
|
const result = await unwrap(client.pages.list(locale));
|
|
1350
3340
|
return result.pages;
|
|
1351
3341
|
},
|
|
1352
|
-
enabled:
|
|
3342
|
+
enabled: options?.enabled !== false
|
|
1353
3343
|
});
|
|
1354
3344
|
}
|
|
1355
3345
|
function usePage(slug, locale, options) {
|
|
1356
3346
|
const { client } = useBehio();
|
|
1357
|
-
return
|
|
3347
|
+
return (0, import_react_query33.useQuery)({
|
|
1358
3348
|
queryKey: ["behio", "page", slug, locale],
|
|
1359
3349
|
queryFn: () => unwrap(client.pages.get(slug, locale)),
|
|
1360
|
-
enabled:
|
|
3350
|
+
enabled: options?.enabled !== false && !!slug
|
|
1361
3351
|
});
|
|
1362
3352
|
}
|
|
1363
3353
|
|
|
1364
3354
|
// src/react/hooks/use-shop-info.ts
|
|
1365
|
-
|
|
3355
|
+
var import_react_query34 = require("@tanstack/react-query");
|
|
1366
3356
|
function useShopInfo(options) {
|
|
1367
3357
|
const { client } = useBehio();
|
|
1368
|
-
return
|
|
3358
|
+
return (0, import_react_query34.useQuery)({
|
|
1369
3359
|
queryKey: ["behio", "shop-info"],
|
|
1370
3360
|
queryFn: () => unwrap(client.getShopInfo()),
|
|
1371
|
-
enabled:
|
|
3361
|
+
enabled: options?.enabled !== false
|
|
1372
3362
|
});
|
|
1373
3363
|
}
|
|
1374
3364
|
|
|
1375
3365
|
// src/react/hooks/use-shop-scripts.ts
|
|
1376
|
-
|
|
3366
|
+
var import_react_query35 = require("@tanstack/react-query");
|
|
1377
3367
|
function useShopScripts(options) {
|
|
1378
3368
|
const { client } = useBehio();
|
|
1379
|
-
return
|
|
3369
|
+
return (0, import_react_query35.useQuery)({
|
|
1380
3370
|
queryKey: ["behio", "shop-scripts"],
|
|
1381
3371
|
queryFn: () => unwrap(client.getShopScripts()),
|
|
1382
|
-
enabled:
|
|
3372
|
+
enabled: options?.enabled !== false
|
|
1383
3373
|
});
|
|
1384
3374
|
}
|
|
1385
3375
|
|
|
1386
3376
|
// src/react/hooks/use-shop-seo.ts
|
|
1387
|
-
|
|
3377
|
+
var import_react_query36 = require("@tanstack/react-query");
|
|
1388
3378
|
function useShopSeo(options) {
|
|
1389
3379
|
const { client } = useBehio();
|
|
1390
|
-
const { locale, initialData, enabled = true } =
|
|
1391
|
-
return
|
|
1392
|
-
queryKey: ["behio", "shop-seo",
|
|
3380
|
+
const { locale, initialData, enabled = true } = options ?? {};
|
|
3381
|
+
return (0, import_react_query36.useQuery)({
|
|
3382
|
+
queryKey: ["behio", "shop-seo", locale ?? "_default"],
|
|
1393
3383
|
queryFn: () => unwrap(client.getShopSeo(locale)),
|
|
1394
3384
|
initialData,
|
|
1395
3385
|
enabled
|
|
@@ -1407,11 +3397,11 @@ function dedupe(values) {
|
|
|
1407
3397
|
function useCurrency() {
|
|
1408
3398
|
const { currency, setCurrency, configuredDefaultCurrency, configuredCurrencies } = useBehio();
|
|
1409
3399
|
const { data: shop, isLoading } = useShopInfo();
|
|
1410
|
-
const defaultCurrency =
|
|
1411
|
-
const currencies = configuredCurrencies && configuredCurrencies.length > 0 ? dedupe(configuredCurrencies) : dedupe([defaultCurrency, ...
|
|
3400
|
+
const defaultCurrency = configuredDefaultCurrency ?? shop?.defaultCurrency;
|
|
3401
|
+
const currencies = configuredCurrencies && configuredCurrencies.length > 0 ? dedupe(configuredCurrencies) : dedupe([defaultCurrency, ...shop?.supportedCurrencies ?? []]);
|
|
1412
3402
|
return {
|
|
1413
3403
|
currency,
|
|
1414
|
-
effectiveCurrency:
|
|
3404
|
+
effectiveCurrency: currency ?? defaultCurrency,
|
|
1415
3405
|
setCurrency,
|
|
1416
3406
|
currencies,
|
|
1417
3407
|
defaultCurrency,
|
|
@@ -1420,7 +3410,7 @@ function useCurrency() {
|
|
|
1420
3410
|
}
|
|
1421
3411
|
|
|
1422
3412
|
// src/react/components/currency-switcher.tsx
|
|
1423
|
-
|
|
3413
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1424
3414
|
function CurrencySwitcher({
|
|
1425
3415
|
className,
|
|
1426
3416
|
labels,
|
|
@@ -1431,40 +3421,40 @@ function CurrencySwitcher({
|
|
|
1431
3421
|
const { currencies, effectiveCurrency, setCurrency } = useCurrency();
|
|
1432
3422
|
if (currencies.length === 0) return null;
|
|
1433
3423
|
if (currencies.length <= 1 && !showWhenSingle) return null;
|
|
1434
|
-
const label = (code) =>
|
|
3424
|
+
const label = (code) => labels?.[code] ?? code;
|
|
1435
3425
|
if (children) {
|
|
1436
|
-
return /* @__PURE__ */
|
|
3426
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: children({ currencies, value: effectiveCurrency, setCurrency, label }) });
|
|
1437
3427
|
}
|
|
1438
|
-
return /* @__PURE__ */
|
|
3428
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1439
3429
|
"select",
|
|
1440
3430
|
{
|
|
1441
3431
|
className,
|
|
1442
3432
|
"aria-label": ariaLabel,
|
|
1443
|
-
value:
|
|
3433
|
+
value: effectiveCurrency ?? "",
|
|
1444
3434
|
onChange: (e) => setCurrency(e.target.value),
|
|
1445
|
-
children: currencies.map((code) => /* @__PURE__ */
|
|
3435
|
+
children: currencies.map((code) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: code, children: label(code) }, code))
|
|
1446
3436
|
}
|
|
1447
3437
|
);
|
|
1448
3438
|
}
|
|
1449
3439
|
|
|
1450
3440
|
// src/react/components/storefront-scripts.tsx
|
|
1451
|
-
|
|
3441
|
+
var import_react16 = require("react");
|
|
1452
3442
|
|
|
1453
3443
|
// src/react/hooks/use-consent.ts
|
|
1454
|
-
|
|
3444
|
+
var import_react_query37 = require("@tanstack/react-query");
|
|
1455
3445
|
function useCookieConsent(visitorId) {
|
|
1456
3446
|
const { client } = useBehio();
|
|
1457
|
-
const qc =
|
|
1458
|
-
const query =
|
|
3447
|
+
const qc = (0, import_react_query37.useQueryClient)();
|
|
3448
|
+
const query = (0, import_react_query37.useQuery)({
|
|
1459
3449
|
queryKey: ["behio", "consent", visitorId],
|
|
1460
3450
|
queryFn: () => unwrap(client.consent.get(visitorId)),
|
|
1461
3451
|
enabled: Boolean(visitorId)
|
|
1462
3452
|
});
|
|
1463
|
-
const recordMutation =
|
|
3453
|
+
const recordMutation = (0, import_react_query37.useMutation)({
|
|
1464
3454
|
mutationFn: (input) => unwrap(client.consent.record(input)),
|
|
1465
3455
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["behio", "consent"] })
|
|
1466
3456
|
});
|
|
1467
|
-
const revokeMutation =
|
|
3457
|
+
const revokeMutation = (0, import_react_query37.useMutation)({
|
|
1468
3458
|
mutationFn: () => unwrap(client.consent.revoke(visitorId)),
|
|
1469
3459
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["behio", "consent"] })
|
|
1470
3460
|
});
|
|
@@ -1538,26 +3528,26 @@ function injectHtml(target, html) {
|
|
|
1538
3528
|
}
|
|
1539
3529
|
function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
|
|
1540
3530
|
const { data } = useShopScripts();
|
|
1541
|
-
const [visitorId, setVisitorId] =
|
|
1542
|
-
|
|
3531
|
+
const [visitorId, setVisitorId] = (0, import_react16.useState)(visitorIdProp ?? "");
|
|
3532
|
+
(0, import_react16.useEffect)(() => {
|
|
1543
3533
|
if (visitorIdProp) return;
|
|
1544
3534
|
try {
|
|
1545
3535
|
const v = localStorage.getItem(VISITOR_KEY);
|
|
1546
3536
|
if (v) setVisitorId(v);
|
|
1547
|
-
} catch
|
|
3537
|
+
} catch {
|
|
1548
3538
|
}
|
|
1549
3539
|
}, [visitorIdProp]);
|
|
1550
3540
|
const { data: consent } = useCookieConsent(visitorId || void 0);
|
|
1551
|
-
const analyticsOk = Boolean(
|
|
1552
|
-
const scripts =
|
|
1553
|
-
() => (
|
|
3541
|
+
const analyticsOk = Boolean(consent?.analytics);
|
|
3542
|
+
const scripts = (0, import_react16.useMemo)(
|
|
3543
|
+
() => (data?.scripts ?? []).filter((s) => !s.consentRequired || analyticsOk),
|
|
1554
3544
|
[data, analyticsOk]
|
|
1555
3545
|
);
|
|
1556
|
-
const signature =
|
|
3546
|
+
const signature = (0, import_react16.useMemo)(
|
|
1557
3547
|
() => JSON.stringify(scripts.map((s) => [s.id, s.type, s.placement, s.value])),
|
|
1558
3548
|
[scripts]
|
|
1559
3549
|
);
|
|
1560
|
-
|
|
3550
|
+
(0, import_react16.useEffect)(() => {
|
|
1561
3551
|
if (typeof document === "undefined") return;
|
|
1562
3552
|
const added = [];
|
|
1563
3553
|
for (const s of scripts) {
|
|
@@ -1574,7 +3564,7 @@ function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
|
|
|
1574
3564
|
}
|
|
1575
3565
|
|
|
1576
3566
|
// src/react/components/behio-analytics.tsx
|
|
1577
|
-
|
|
3567
|
+
var import_react17 = require("react");
|
|
1578
3568
|
|
|
1579
3569
|
// src/react/hooks/use-behio-client.ts
|
|
1580
3570
|
function useBehioClient() {
|
|
@@ -1584,7 +3574,7 @@ function useBehioClient() {
|
|
|
1584
3574
|
// src/react/components/behio-analytics.tsx
|
|
1585
3575
|
function BehioAnalyticsTracker() {
|
|
1586
3576
|
const client = useBehioClient();
|
|
1587
|
-
|
|
3577
|
+
(0, import_react17.useEffect)(() => {
|
|
1588
3578
|
if (typeof window === "undefined") return;
|
|
1589
3579
|
const w = window;
|
|
1590
3580
|
if (w.__behioAnalytics) return;
|
|
@@ -1608,17 +3598,17 @@ function initTracker(client) {
|
|
|
1608
3598
|
let dwellAccumMs = 0;
|
|
1609
3599
|
const refreshIdentity = async () => {
|
|
1610
3600
|
try {
|
|
1611
|
-
const stored =
|
|
3601
|
+
const stored = localStorage.getItem(VISITOR_KEY2) ?? void 0;
|
|
1612
3602
|
if (!stored) {
|
|
1613
3603
|
visitorId = void 0;
|
|
1614
3604
|
client.setAnalyticsVisitorId(null);
|
|
1615
3605
|
return;
|
|
1616
3606
|
}
|
|
1617
3607
|
const { data } = await client.consent.get(stored);
|
|
1618
|
-
const granted = Boolean(
|
|
3608
|
+
const granted = Boolean(data?.analytics);
|
|
1619
3609
|
visitorId = granted ? stored : void 0;
|
|
1620
3610
|
client.setAnalyticsVisitorId(granted ? stored : null);
|
|
1621
|
-
} catch
|
|
3611
|
+
} catch {
|
|
1622
3612
|
visitorId = void 0;
|
|
1623
3613
|
}
|
|
1624
3614
|
};
|
|
@@ -1699,17 +3689,17 @@ function initTracker(client) {
|
|
|
1699
3689
|
};
|
|
1700
3690
|
const onClick = (ev) => {
|
|
1701
3691
|
const target = ev.target;
|
|
1702
|
-
const el =
|
|
3692
|
+
const el = target?.closest?.("a,button,[role=button],[data-behio-event]");
|
|
1703
3693
|
if (!el) return;
|
|
1704
|
-
const explicit =
|
|
1705
|
-
const text = (
|
|
1706
|
-
const hrefRaw =
|
|
3694
|
+
const explicit = el.getAttribute("data-behio-event") ?? void 0;
|
|
3695
|
+
const text = (el.textContent ?? "").trim().replace(/\s+/g, " ").slice(0, 80) || void 0;
|
|
3696
|
+
const hrefRaw = el.getAttribute("href") ?? void 0;
|
|
1707
3697
|
let href;
|
|
1708
3698
|
if (hrefRaw) {
|
|
1709
3699
|
try {
|
|
1710
3700
|
const u = new URL(hrefRaw, window.location.origin);
|
|
1711
3701
|
href = u.origin === window.location.origin ? u.pathname : u.hostname;
|
|
1712
|
-
} catch
|
|
3702
|
+
} catch {
|
|
1713
3703
|
href = void 0;
|
|
1714
3704
|
}
|
|
1715
3705
|
}
|
|
@@ -1743,7 +3733,7 @@ function initTracker(client) {
|
|
|
1743
3733
|
if (event === "purchase") return;
|
|
1744
3734
|
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
1745
3735
|
const props = {
|
|
1746
|
-
...
|
|
3736
|
+
...payload.props ?? {},
|
|
1747
3737
|
...payload.search_term != null ? { query: payload.search_term } : {},
|
|
1748
3738
|
...payload.shipping_tier != null ? { shippingTier: payload.shipping_tier } : {},
|
|
1749
3739
|
...payload.payment_type != null ? { paymentType: payload.payment_type } : {},
|
|
@@ -1753,7 +3743,7 @@ function initTracker(client) {
|
|
|
1753
3743
|
items: items.length,
|
|
1754
3744
|
// First item id = the product (view_item/add_to_cart are
|
|
1755
3745
|
// single-product in practice) - Behavioral Offers count on it.
|
|
1756
|
-
itemId:
|
|
3746
|
+
itemId: items[0]?.item_id
|
|
1757
3747
|
} : {}
|
|
1758
3748
|
};
|
|
1759
3749
|
enqueue({
|
|
@@ -1799,7 +3789,7 @@ function getSessionId() {
|
|
|
1799
3789
|
const sid = `s-${Math.random().toString(36).slice(2, 12)}${Date.now().toString(36)}`;
|
|
1800
3790
|
sessionStorage.setItem("behio.sid", sid);
|
|
1801
3791
|
return sid;
|
|
1802
|
-
} catch
|
|
3792
|
+
} catch {
|
|
1803
3793
|
return `s-${Math.random().toString(36).slice(2, 12)}`;
|
|
1804
3794
|
}
|
|
1805
3795
|
}
|
|
@@ -1807,115 +3797,115 @@ function utmFromSearch(search) {
|
|
|
1807
3797
|
try {
|
|
1808
3798
|
const p = new URLSearchParams(search);
|
|
1809
3799
|
return {
|
|
1810
|
-
utmSource:
|
|
1811
|
-
utmMedium:
|
|
1812
|
-
utmCampaign:
|
|
3800
|
+
utmSource: p.get("utm_source") ?? void 0,
|
|
3801
|
+
utmMedium: p.get("utm_medium") ?? void 0,
|
|
3802
|
+
utmCampaign: p.get("utm_campaign") ?? void 0,
|
|
1813
3803
|
// Extended attribution: paid-search term/content + click ids. The
|
|
1814
3804
|
// backend folds these into event props (raw columns unchanged).
|
|
1815
|
-
utmTerm:
|
|
1816
|
-
utmContent:
|
|
1817
|
-
gclid:
|
|
1818
|
-
fbclid:
|
|
3805
|
+
utmTerm: p.get("utm_term") ?? void 0,
|
|
3806
|
+
utmContent: p.get("utm_content") ?? void 0,
|
|
3807
|
+
gclid: p.get("gclid") ?? void 0,
|
|
3808
|
+
fbclid: p.get("fbclid") ?? void 0
|
|
1819
3809
|
};
|
|
1820
|
-
} catch
|
|
3810
|
+
} catch {
|
|
1821
3811
|
return {};
|
|
1822
3812
|
}
|
|
1823
3813
|
}
|
|
1824
3814
|
|
|
1825
3815
|
// src/react/hooks/use-bundles.ts
|
|
1826
|
-
|
|
3816
|
+
var import_react_query38 = require("@tanstack/react-query");
|
|
1827
3817
|
function useBundles(options) {
|
|
1828
3818
|
const { client } = useBehio();
|
|
1829
|
-
return
|
|
3819
|
+
return (0, import_react_query38.useQuery)({
|
|
1830
3820
|
queryKey: ["behio", "bundles"],
|
|
1831
3821
|
queryFn: () => unwrap(client.catalog.getBundles()),
|
|
1832
|
-
enabled:
|
|
1833
|
-
initialData:
|
|
3822
|
+
enabled: options?.enabled ?? true,
|
|
3823
|
+
initialData: options?.initialData
|
|
1834
3824
|
});
|
|
1835
3825
|
}
|
|
1836
3826
|
function useBundle(slug, options) {
|
|
1837
3827
|
const { client } = useBehio();
|
|
1838
|
-
return
|
|
3828
|
+
return (0, import_react_query38.useQuery)({
|
|
1839
3829
|
queryKey: ["behio", "bundle", slug],
|
|
1840
3830
|
queryFn: () => unwrap(client.catalog.getBundle(slug)),
|
|
1841
|
-
enabled: Boolean(slug) && (
|
|
1842
|
-
initialData:
|
|
3831
|
+
enabled: Boolean(slug) && (options?.enabled ?? true),
|
|
3832
|
+
initialData: options?.initialData
|
|
1843
3833
|
});
|
|
1844
3834
|
}
|
|
1845
3835
|
|
|
1846
3836
|
// src/react/hooks/use-product-group.ts
|
|
1847
|
-
|
|
3837
|
+
var import_react_query39 = require("@tanstack/react-query");
|
|
1848
3838
|
function useProductGroup(slug, options) {
|
|
1849
3839
|
const { client } = useBehio();
|
|
1850
|
-
return
|
|
1851
|
-
queryKey: ["behio", "product-group", slug,
|
|
3840
|
+
return (0, import_react_query39.useQuery)({
|
|
3841
|
+
queryKey: ["behio", "product-group", slug, options?.locale, options?.currency],
|
|
1852
3842
|
queryFn: () => unwrap(
|
|
1853
3843
|
client.catalog.getProductGroup(slug, {
|
|
1854
|
-
locale:
|
|
1855
|
-
currency:
|
|
3844
|
+
locale: options?.locale,
|
|
3845
|
+
currency: options?.currency
|
|
1856
3846
|
})
|
|
1857
3847
|
),
|
|
1858
|
-
enabled: Boolean(slug) && (
|
|
1859
|
-
initialData:
|
|
3848
|
+
enabled: Boolean(slug) && (options?.enabled ?? true),
|
|
3849
|
+
initialData: options?.initialData
|
|
1860
3850
|
});
|
|
1861
3851
|
}
|
|
1862
3852
|
|
|
1863
3853
|
// src/react/hooks/use-cross-sell.ts
|
|
1864
|
-
|
|
3854
|
+
var import_react_query40 = require("@tanstack/react-query");
|
|
1865
3855
|
function useCrossSell(productSlug, options) {
|
|
1866
3856
|
const { client } = useBehio();
|
|
1867
|
-
return
|
|
1868
|
-
queryKey: ["behio", "cross-sell", productSlug,
|
|
3857
|
+
return (0, import_react_query40.useQuery)({
|
|
3858
|
+
queryKey: ["behio", "cross-sell", productSlug, options?.locale, options?.currency],
|
|
1869
3859
|
queryFn: () => unwrap(
|
|
1870
3860
|
client.catalog.getCrossSell(productSlug, {
|
|
1871
|
-
locale:
|
|
1872
|
-
currency:
|
|
3861
|
+
locale: options?.locale,
|
|
3862
|
+
currency: options?.currency
|
|
1873
3863
|
})
|
|
1874
3864
|
),
|
|
1875
|
-
enabled: Boolean(productSlug) && (
|
|
1876
|
-
initialData:
|
|
3865
|
+
enabled: Boolean(productSlug) && (options?.enabled ?? true),
|
|
3866
|
+
initialData: options?.initialData
|
|
1877
3867
|
});
|
|
1878
3868
|
}
|
|
1879
3869
|
|
|
1880
3870
|
// src/react/hooks/use-product-promotions.ts
|
|
1881
|
-
|
|
3871
|
+
var import_react_query41 = require("@tanstack/react-query");
|
|
1882
3872
|
function useProductPromotions(productSlug, options) {
|
|
1883
3873
|
const { client } = useBehio();
|
|
1884
|
-
return
|
|
3874
|
+
return (0, import_react_query41.useQuery)({
|
|
1885
3875
|
queryKey: ["behio", "product-promotions", productSlug],
|
|
1886
3876
|
queryFn: () => unwrap(client.catalog.getProductPromotions(productSlug)),
|
|
1887
|
-
enabled: Boolean(productSlug) && (
|
|
1888
|
-
refetchInterval:
|
|
3877
|
+
enabled: Boolean(productSlug) && (options?.enabled ?? true),
|
|
3878
|
+
refetchInterval: options?.refetchIntervalMs
|
|
1889
3879
|
});
|
|
1890
3880
|
}
|
|
1891
3881
|
|
|
1892
3882
|
// src/react/hooks/use-gift-card.ts
|
|
1893
|
-
|
|
3883
|
+
var import_react_query42 = require("@tanstack/react-query");
|
|
1894
3884
|
function useGiftCardBalance(code, options) {
|
|
1895
3885
|
const { client } = useBehio();
|
|
1896
|
-
const trimmed =
|
|
1897
|
-
return
|
|
3886
|
+
const trimmed = code?.trim();
|
|
3887
|
+
return (0, import_react_query42.useQuery)({
|
|
1898
3888
|
queryKey: ["behio", "gift-card-balance", trimmed],
|
|
1899
3889
|
queryFn: () => unwrap(client.catalog.checkGiftCard(trimmed)),
|
|
1900
|
-
enabled: Boolean(trimmed && trimmed.length >= 6) && (
|
|
3890
|
+
enabled: Boolean(trimmed && trimmed.length >= 6) && (options?.enabled ?? true)
|
|
1901
3891
|
});
|
|
1902
3892
|
}
|
|
1903
3893
|
|
|
1904
3894
|
// src/react/hooks/use-wishlist.ts
|
|
1905
|
-
|
|
3895
|
+
var import_react_query43 = require("@tanstack/react-query");
|
|
1906
3896
|
function useWishlist(options) {
|
|
1907
3897
|
const { client } = useBehio();
|
|
1908
|
-
const qc =
|
|
1909
|
-
const query =
|
|
3898
|
+
const qc = (0, import_react_query43.useQueryClient)();
|
|
3899
|
+
const query = (0, import_react_query43.useQuery)({
|
|
1910
3900
|
queryKey: ["behio", "wishlist"],
|
|
1911
3901
|
queryFn: () => unwrap(client.wishlist.get()),
|
|
1912
|
-
enabled:
|
|
3902
|
+
enabled: options?.enabled ?? true
|
|
1913
3903
|
});
|
|
1914
|
-
const addMutation =
|
|
3904
|
+
const addMutation = (0, import_react_query43.useMutation)({
|
|
1915
3905
|
mutationFn: (productId) => unwrap(client.wishlist.add(productId)),
|
|
1916
3906
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["behio", "wishlist"] })
|
|
1917
3907
|
});
|
|
1918
|
-
const removeMutation =
|
|
3908
|
+
const removeMutation = (0, import_react_query43.useMutation)({
|
|
1919
3909
|
mutationFn: (productId) => unwrap(client.wishlist.remove(productId)),
|
|
1920
3910
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["behio", "wishlist"] })
|
|
1921
3911
|
});
|
|
@@ -1929,7 +3919,7 @@ function useWishlist(options) {
|
|
|
1929
3919
|
}
|
|
1930
3920
|
function useIsInWishlist(productId) {
|
|
1931
3921
|
const { client } = useBehio();
|
|
1932
|
-
return
|
|
3922
|
+
return (0, import_react_query43.useQuery)({
|
|
1933
3923
|
queryKey: ["behio", "wishlist-check", productId],
|
|
1934
3924
|
queryFn: () => unwrap(client.wishlist.isInWishlist(productId)),
|
|
1935
3925
|
enabled: Boolean(productId)
|
|
@@ -1937,41 +3927,41 @@ function useIsInWishlist(productId) {
|
|
|
1937
3927
|
}
|
|
1938
3928
|
|
|
1939
3929
|
// src/react/hooks/use-reviews.ts
|
|
1940
|
-
|
|
3930
|
+
var import_react_query44 = require("@tanstack/react-query");
|
|
1941
3931
|
function useProductReviews(productId, options) {
|
|
1942
3932
|
const { client } = useBehio();
|
|
1943
|
-
return
|
|
1944
|
-
queryKey: ["behio", "reviews", productId,
|
|
1945
|
-
queryFn: () => unwrap(client.reviews.getProductReviews(productId,
|
|
1946
|
-
enabled: Boolean(productId) && (
|
|
3933
|
+
return (0, import_react_query44.useQuery)({
|
|
3934
|
+
queryKey: ["behio", "reviews", productId, options?.page ?? 1],
|
|
3935
|
+
queryFn: () => unwrap(client.reviews.getProductReviews(productId, options?.page, options?.limit)),
|
|
3936
|
+
enabled: Boolean(productId) && (options?.enabled ?? true)
|
|
1947
3937
|
});
|
|
1948
3938
|
}
|
|
1949
3939
|
function useSubmitReview() {
|
|
1950
3940
|
const { client } = useBehio();
|
|
1951
|
-
const qc =
|
|
1952
|
-
return
|
|
3941
|
+
const qc = (0, import_react_query44.useQueryClient)();
|
|
3942
|
+
return (0, import_react_query44.useMutation)({
|
|
1953
3943
|
mutationFn: (input) => unwrap(client.reviews.submit(input)),
|
|
1954
3944
|
onSuccess: (_, input) => qc.invalidateQueries({ queryKey: ["behio", "reviews", input.productId] })
|
|
1955
3945
|
});
|
|
1956
3946
|
}
|
|
1957
3947
|
|
|
1958
3948
|
// src/react/hooks/use-returns.ts
|
|
1959
|
-
|
|
3949
|
+
var import_react_query45 = require("@tanstack/react-query");
|
|
1960
3950
|
function useLookupReturnableOrder() {
|
|
1961
3951
|
const { client } = useBehio();
|
|
1962
|
-
return
|
|
3952
|
+
return (0, import_react_query45.useMutation)({
|
|
1963
3953
|
mutationFn: ({ orderNumber, email }) => unwrap(client.returns.lookupOrder(orderNumber, email))
|
|
1964
3954
|
});
|
|
1965
3955
|
}
|
|
1966
3956
|
function useSubmitReturn() {
|
|
1967
3957
|
const { client } = useBehio();
|
|
1968
|
-
return
|
|
3958
|
+
return (0, import_react_query45.useMutation)({
|
|
1969
3959
|
mutationFn: (input) => unwrap(client.returns.submit(input))
|
|
1970
3960
|
});
|
|
1971
3961
|
}
|
|
1972
3962
|
function useReturnStatus(returnId, email) {
|
|
1973
3963
|
const { client } = useBehio();
|
|
1974
|
-
return
|
|
3964
|
+
return (0, import_react_query45.useQuery)({
|
|
1975
3965
|
queryKey: ["behio", "return-status", returnId],
|
|
1976
3966
|
queryFn: () => unwrap(client.returns.getStatus(returnId, email)),
|
|
1977
3967
|
enabled: Boolean(returnId && email)
|
|
@@ -1979,16 +3969,16 @@ function useReturnStatus(returnId, email) {
|
|
|
1979
3969
|
}
|
|
1980
3970
|
|
|
1981
3971
|
// src/react/hooks/use-quotes.ts
|
|
1982
|
-
|
|
3972
|
+
var import_react_query46 = require("@tanstack/react-query");
|
|
1983
3973
|
function useSubmitQuote() {
|
|
1984
3974
|
const { client } = useBehio();
|
|
1985
|
-
return
|
|
3975
|
+
return (0, import_react_query46.useMutation)({
|
|
1986
3976
|
mutationFn: (input) => unwrap(client.quotes.submit(input))
|
|
1987
3977
|
});
|
|
1988
3978
|
}
|
|
1989
3979
|
function useQuoteStatus(quoteId, email) {
|
|
1990
3980
|
const { client } = useBehio();
|
|
1991
|
-
return
|
|
3981
|
+
return (0, import_react_query46.useQuery)({
|
|
1992
3982
|
queryKey: ["behio", "quote-status", quoteId],
|
|
1993
3983
|
queryFn: () => unwrap(client.quotes.getStatus(quoteId, email)),
|
|
1994
3984
|
enabled: Boolean(quoteId && email)
|
|
@@ -1996,84 +3986,201 @@ function useQuoteStatus(quoteId, email) {
|
|
|
1996
3986
|
}
|
|
1997
3987
|
|
|
1998
3988
|
// src/react/hooks/use-back-in-stock.ts
|
|
1999
|
-
|
|
3989
|
+
var import_react_query47 = require("@tanstack/react-query");
|
|
2000
3990
|
function useNotifyWhenAvailable() {
|
|
2001
3991
|
const { client } = useBehio();
|
|
2002
|
-
return
|
|
3992
|
+
return (0, import_react_query47.useMutation)({
|
|
2003
3993
|
mutationFn: ({ productId, email }) => unwrap(client.catalog.notifyWhenAvailable(productId, email))
|
|
2004
3994
|
});
|
|
2005
3995
|
}
|
|
2006
3996
|
|
|
3997
|
+
// src/react/utils/format-price.ts
|
|
3998
|
+
function formatPrice(amount, currency, locale) {
|
|
3999
|
+
const resolvedLocale = locale ?? "cs";
|
|
4000
|
+
try {
|
|
4001
|
+
return new Intl.NumberFormat(resolvedLocale, {
|
|
4002
|
+
style: "currency",
|
|
4003
|
+
currency,
|
|
4004
|
+
minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
|
|
4005
|
+
maximumFractionDigits: 2
|
|
4006
|
+
}).format(amount);
|
|
4007
|
+
} catch {
|
|
4008
|
+
return `${amount} ${currency}`;
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
2007
4011
|
|
|
4012
|
+
// src/analytics.ts
|
|
4013
|
+
var GA4_NAME_MAP = {
|
|
4014
|
+
newsletter_signup: "generate_lead"
|
|
4015
|
+
};
|
|
4016
|
+
function trackEcommerceEvent(event, payload) {
|
|
4017
|
+
if (typeof window === "undefined") return;
|
|
4018
|
+
const w = window;
|
|
4019
|
+
try {
|
|
4020
|
+
w.__behioEcommerceSink?.(event, payload);
|
|
4021
|
+
} catch {
|
|
4022
|
+
}
|
|
4023
|
+
const gaName = GA4_NAME_MAP[event] ?? event;
|
|
4024
|
+
try {
|
|
4025
|
+
if (typeof w.gtag === "function") {
|
|
4026
|
+
w.gtag("event", gaName, payload);
|
|
4027
|
+
return;
|
|
4028
|
+
}
|
|
4029
|
+
if (Array.isArray(w.dataLayer)) {
|
|
4030
|
+
w.dataLayer.push({ ecommerce: null });
|
|
4031
|
+
w.dataLayer.push({ event: gaName, ecommerce: payload });
|
|
4032
|
+
}
|
|
4033
|
+
} catch {
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
2008
4036
|
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
4037
|
+
// src/consent-visitor.ts
|
|
4038
|
+
var VISITOR_KEY3 = "behio_visitor_id";
|
|
4039
|
+
var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
4040
|
+
function getStoredVisitorId() {
|
|
4041
|
+
if (typeof window === "undefined") return null;
|
|
4042
|
+
try {
|
|
4043
|
+
const ls = localStorage.getItem(VISITOR_KEY3);
|
|
4044
|
+
if (ls) return ls;
|
|
4045
|
+
} catch {
|
|
4046
|
+
}
|
|
4047
|
+
return readCookie(VISITOR_KEY3);
|
|
4048
|
+
}
|
|
4049
|
+
function generateVisitorId() {
|
|
4050
|
+
const bytes = new Uint8Array(18);
|
|
4051
|
+
try {
|
|
4052
|
+
globalThis.crypto?.getRandomValues?.(bytes);
|
|
4053
|
+
} catch {
|
|
4054
|
+
}
|
|
4055
|
+
let filled = false;
|
|
4056
|
+
for (const b of bytes) if (b !== 0) filled = true;
|
|
4057
|
+
if (!filled) for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
4058
|
+
let bin = "";
|
|
4059
|
+
for (const b of bytes) bin += String.fromCharCode(b);
|
|
4060
|
+
const b64 = typeof btoa === "function" ? btoa(bin) : Buffer.from(bytes).toString("base64");
|
|
4061
|
+
return `v${b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")}`.slice(0, 40);
|
|
4062
|
+
}
|
|
4063
|
+
function writeVisitorId(id) {
|
|
4064
|
+
if (typeof window === "undefined") return;
|
|
4065
|
+
try {
|
|
4066
|
+
localStorage.setItem(VISITOR_KEY3, id);
|
|
4067
|
+
} catch {
|
|
4068
|
+
}
|
|
4069
|
+
try {
|
|
4070
|
+
document.cookie = `${VISITOR_KEY3}=${encodeURIComponent(id)}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
|
|
4071
|
+
} catch {
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
function readCookie(name) {
|
|
4075
|
+
if (typeof document === "undefined") return null;
|
|
4076
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
|
|
4077
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
4078
|
+
}
|
|
4079
|
+
function emitConsentChanged() {
|
|
4080
|
+
if (typeof window === "undefined") return;
|
|
4081
|
+
try {
|
|
4082
|
+
window.dispatchEvent(new Event("behio:consent-changed"));
|
|
4083
|
+
} catch {
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
async function grantAnalyticsConsent(client, categories) {
|
|
4087
|
+
const id = getStoredVisitorId() ?? generateVisitorId();
|
|
4088
|
+
writeVisitorId(id);
|
|
4089
|
+
client.setAnalyticsVisitorId(id);
|
|
4090
|
+
const res = await client.consent.record({
|
|
4091
|
+
visitorId: id,
|
|
4092
|
+
analytics: true,
|
|
4093
|
+
marketing: categories?.marketing ?? false,
|
|
4094
|
+
preferences: categories?.preferences ?? false
|
|
4095
|
+
});
|
|
4096
|
+
emitConsentChanged();
|
|
4097
|
+
return res;
|
|
4098
|
+
}
|
|
4099
|
+
async function revokeAnalyticsConsent(client) {
|
|
4100
|
+
const id = getStoredVisitorId();
|
|
4101
|
+
client.setAnalyticsVisitorId(null);
|
|
4102
|
+
emitConsentChanged();
|
|
4103
|
+
if (!id) return { data: { success: true }, error: null };
|
|
4104
|
+
return client.consent.revoke(id);
|
|
4105
|
+
}
|
|
4106
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4107
|
+
0 && (module.exports = {
|
|
4108
|
+
BehioAnalyticsTracker,
|
|
4109
|
+
BehioProvider,
|
|
4110
|
+
CurrencySwitcher,
|
|
4111
|
+
StorefrontScripts,
|
|
4112
|
+
cookieStorage,
|
|
4113
|
+
createMemoryStorage,
|
|
4114
|
+
detectStorage,
|
|
4115
|
+
formatPrice,
|
|
4116
|
+
generateVisitorId,
|
|
4117
|
+
getStoredVisitorId,
|
|
4118
|
+
grantAnalyticsConsent,
|
|
4119
|
+
localStorageAdapter,
|
|
4120
|
+
memoryStorage,
|
|
4121
|
+
revokeAnalyticsConsent,
|
|
4122
|
+
trackEcommerceEvent,
|
|
4123
|
+
useAddressAutocomplete,
|
|
4124
|
+
useAddresses,
|
|
4125
|
+
useAnalyticsEvents,
|
|
4126
|
+
useAuth,
|
|
4127
|
+
useBehio,
|
|
4128
|
+
useBehioClient,
|
|
4129
|
+
useBundle,
|
|
4130
|
+
useBundles,
|
|
4131
|
+
useCart,
|
|
4132
|
+
useCartCount,
|
|
4133
|
+
useCategories,
|
|
4134
|
+
useCategory,
|
|
4135
|
+
useCertificateVerification,
|
|
4136
|
+
useCheckout,
|
|
4137
|
+
useCookieConsent,
|
|
4138
|
+
useCourse,
|
|
4139
|
+
useCourseCertificates,
|
|
4140
|
+
useCourses,
|
|
4141
|
+
useCrossSell,
|
|
4142
|
+
useCurrency,
|
|
4143
|
+
useCustomer,
|
|
4144
|
+
useFacets,
|
|
4145
|
+
useFeatured,
|
|
4146
|
+
useFilters,
|
|
4147
|
+
useGiftCardBalance,
|
|
4148
|
+
useIsInWishlist,
|
|
4149
|
+
useLabels,
|
|
4150
|
+
useLessonComments,
|
|
4151
|
+
useLessonNote,
|
|
4152
|
+
useLessonQuiz,
|
|
4153
|
+
useLessonTutor,
|
|
4154
|
+
useLookupReturnableOrder,
|
|
4155
|
+
useLoyalty,
|
|
4156
|
+
useMenu,
|
|
4157
|
+
useNewsletterSubscribe,
|
|
4158
|
+
useNewsletterUnsubscribe,
|
|
4159
|
+
useNotifyWhenAvailable,
|
|
4160
|
+
useOrder,
|
|
4161
|
+
useOrderAccess,
|
|
4162
|
+
useOrders,
|
|
4163
|
+
usePage,
|
|
4164
|
+
usePages,
|
|
4165
|
+
usePaymentMethods,
|
|
4166
|
+
usePersonalOffers,
|
|
4167
|
+
usePickupPoints,
|
|
4168
|
+
useProduct,
|
|
4169
|
+
useProductGroup,
|
|
4170
|
+
useProductPromotions,
|
|
4171
|
+
useProductReviews,
|
|
4172
|
+
useProducts,
|
|
4173
|
+
useQuoteStatus,
|
|
4174
|
+
useReturnStatus,
|
|
4175
|
+
useSearch,
|
|
4176
|
+
useShippingMethods,
|
|
4177
|
+
useShippingQuote,
|
|
4178
|
+
useShopInfo,
|
|
4179
|
+
useShopScripts,
|
|
4180
|
+
useShopSeo,
|
|
4181
|
+
useSubmitQuote,
|
|
4182
|
+
useSubmitReturn,
|
|
4183
|
+
useSubmitReview,
|
|
4184
|
+
useSubscriptions,
|
|
4185
|
+
useWishlist
|
|
4186
|
+
});
|