@behio/storefront-sdk 1.19.0 → 2.0.1
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/README.md +214 -19
- package/dist/{chunk-W2P2D3EZ.mjs → chunk-SCFR6JH7.mjs} +73 -53
- package/dist/{chunk-GAYYRRUL.js → chunk-TDIAWIQR.js} +97 -77
- package/dist/{client-DhW5x2Zr.d.mts → client-C4dnHtID.d.mts} +197 -26
- package/dist/{client-DhW5x2Zr.d.ts → client-C4dnHtID.d.ts} +197 -26
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +84 -23
- package/dist/index.mjs +68 -7
- package/dist/next.d.mts +3 -1
- package/dist/next.d.ts +3 -1
- package/dist/next.js +9 -5
- package/dist/next.mjs +5 -1
- package/dist/react.d.mts +218 -32
- package/dist/react.d.ts +218 -32
- package/dist/react.js +166 -125
- package/dist/react.mjs +166 -126
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -53,6 +53,7 @@ __export(react_exports, {
|
|
|
53
53
|
useCategory: () => useCategory,
|
|
54
54
|
useCertificateVerification: () => useCertificateVerification,
|
|
55
55
|
useCheckout: () => useCheckout,
|
|
56
|
+
useCheckoutPreview: () => useCheckoutPreview,
|
|
56
57
|
useCookieConsent: () => useCookieConsent,
|
|
57
58
|
useCourse: () => useCourse,
|
|
58
59
|
useCourseCertificates: () => useCourseCertificates,
|
|
@@ -259,6 +260,7 @@ var BehioStorefront = class {
|
|
|
259
260
|
this.shopDomain = config.shopDomain;
|
|
260
261
|
this.defaultLocale = config.locale;
|
|
261
262
|
this.defaultCurrency = config.currency;
|
|
263
|
+
this.setCountry(config.country);
|
|
262
264
|
this.fetchFn = config.fetch || globalThis.fetch.bind(globalThis);
|
|
263
265
|
this.timeout = config.timeout ?? 3e4;
|
|
264
266
|
this.retries = config.retries ?? 1;
|
|
@@ -420,6 +422,14 @@ var BehioStorefront = class {
|
|
|
420
422
|
getCurrency() {
|
|
421
423
|
return this.defaultCurrency;
|
|
422
424
|
}
|
|
425
|
+
/** Set the catalog's delivery country. Use cart.setDestination separately for the cart. */
|
|
426
|
+
setCountry(country) {
|
|
427
|
+
const normalized = country?.trim().toUpperCase();
|
|
428
|
+
this.defaultCountry = normalized && /^[A-Z]{2}$/.test(normalized) ? normalized : void 0;
|
|
429
|
+
}
|
|
430
|
+
getCountry() {
|
|
431
|
+
return this.defaultCountry;
|
|
432
|
+
}
|
|
423
433
|
/** Set the default locale sent on every catalog request (per-call wins). */
|
|
424
434
|
setLocale(locale) {
|
|
425
435
|
this.defaultLocale = locale || void 0;
|
|
@@ -575,12 +585,16 @@ var BehioStorefront = class {
|
|
|
575
585
|
if (this.defaultCurrency && !params.has("currency")) {
|
|
576
586
|
params.set("currency", this.defaultCurrency);
|
|
577
587
|
}
|
|
588
|
+
if (this.defaultCountry && path.startsWith("/catalog/") && !params.has("country")) {
|
|
589
|
+
params.set("country", this.defaultCountry);
|
|
590
|
+
}
|
|
578
591
|
const qs = params.toString();
|
|
579
592
|
const url = `${this.baseUrl}/storefront/v1${path}${qs ? `?${qs}` : ""}`;
|
|
580
593
|
const headers = {
|
|
581
594
|
"X-Api-Key": this.apiKey,
|
|
582
595
|
"Content-Type": "application/json"
|
|
583
596
|
};
|
|
597
|
+
if (path === "/cart" || path.startsWith("/cart/")) headers["X-Behio-Cart-Contract"] = "2";
|
|
584
598
|
if (this.shopDomain) {
|
|
585
599
|
headers["X-Shop-Domain"] = this.shopDomain;
|
|
586
600
|
}
|
|
@@ -707,42 +721,46 @@ var BehioStorefront = class {
|
|
|
707
721
|
throw new BehioNetworkError("Request failed after retries");
|
|
708
722
|
}
|
|
709
723
|
};
|
|
724
|
+
function catalogProductsQuery(query) {
|
|
725
|
+
const q = {};
|
|
726
|
+
if (query) {
|
|
727
|
+
if (query.page !== void 0) q.page = query.page;
|
|
728
|
+
if (query.limit !== void 0) q.limit = query.limit;
|
|
729
|
+
if (query.category) q.category = query.category;
|
|
730
|
+
if (query.label) q.label = query.label;
|
|
731
|
+
if (query.priceMin !== void 0) q.priceMin = query.priceMin;
|
|
732
|
+
if (query.priceMax !== void 0) q.priceMax = query.priceMax;
|
|
733
|
+
if (query.currency) q.currency = query.currency;
|
|
734
|
+
if (query.country) q.country = query.country;
|
|
735
|
+
if (query.locale) q.locale = query.locale;
|
|
736
|
+
if (query.sort) q.sort = query.sort;
|
|
737
|
+
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
738
|
+
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
739
|
+
if (query.search) q.search = query.search;
|
|
740
|
+
if (query.parameters) q.parameters = JSON.stringify(query.parameters);
|
|
741
|
+
if (query.facets) q.facets = JSON.stringify(query.facets);
|
|
742
|
+
if (query.ids && query.ids.length > 0) q.ids = query.ids;
|
|
743
|
+
if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
|
|
744
|
+
if (query.labels && query.labels.length > 0) q.labels = query.labels;
|
|
745
|
+
if (query.categories && query.categories.length > 0)
|
|
746
|
+
q.categories = query.categories;
|
|
747
|
+
if (query.excludeIds && query.excludeIds.length > 0)
|
|
748
|
+
q.excludeIds = query.excludeIds;
|
|
749
|
+
if (query.excludeCategories && query.excludeCategories.length > 0)
|
|
750
|
+
q.excludeCategories = query.excludeCategories;
|
|
751
|
+
if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
|
|
752
|
+
if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
|
|
753
|
+
if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
|
|
754
|
+
}
|
|
755
|
+
return q;
|
|
756
|
+
}
|
|
710
757
|
var CatalogModule = class {
|
|
711
758
|
constructor(client) {
|
|
712
759
|
this.client = client;
|
|
713
760
|
}
|
|
714
761
|
/** List products with filtering, pagination, search */
|
|
715
762
|
async getProducts(query) {
|
|
716
|
-
const q =
|
|
717
|
-
if (query) {
|
|
718
|
-
if (query.page) q.page = query.page;
|
|
719
|
-
if (query.limit) q.limit = query.limit;
|
|
720
|
-
if (query.category) q.category = query.category;
|
|
721
|
-
if (query.label) q.label = query.label;
|
|
722
|
-
if (query.priceMin) q.priceMin = query.priceMin;
|
|
723
|
-
if (query.priceMax) q.priceMax = query.priceMax;
|
|
724
|
-
if (query.currency) q.currency = query.currency;
|
|
725
|
-
if (query.country) q.country = query.country;
|
|
726
|
-
if (query.locale) q.locale = query.locale;
|
|
727
|
-
if (query.sort) q.sort = query.sort;
|
|
728
|
-
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
729
|
-
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
730
|
-
if (query.search) q.search = query.search;
|
|
731
|
-
if (query.parameters) q.parameters = JSON.stringify(query.parameters);
|
|
732
|
-
if (query.facets) q.facets = JSON.stringify(query.facets);
|
|
733
|
-
if (query.ids && query.ids.length > 0) q.ids = query.ids;
|
|
734
|
-
if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
|
|
735
|
-
if (query.labels && query.labels.length > 0) q.labels = query.labels;
|
|
736
|
-
if (query.categories && query.categories.length > 0)
|
|
737
|
-
q.categories = query.categories;
|
|
738
|
-
if (query.excludeIds && query.excludeIds.length > 0)
|
|
739
|
-
q.excludeIds = query.excludeIds;
|
|
740
|
-
if (query.excludeCategories && query.excludeCategories.length > 0)
|
|
741
|
-
q.excludeCategories = query.excludeCategories;
|
|
742
|
-
if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
|
|
743
|
-
if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
|
|
744
|
-
if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
|
|
745
|
-
}
|
|
763
|
+
const q = catalogProductsQuery(query);
|
|
746
764
|
return this.client.request(
|
|
747
765
|
"GET",
|
|
748
766
|
"/catalog/products",
|
|
@@ -784,14 +802,7 @@ var CatalogModule = class {
|
|
|
784
802
|
}
|
|
785
803
|
/** Get products in a category */
|
|
786
804
|
async getCategoryProducts(slug, query) {
|
|
787
|
-
const q =
|
|
788
|
-
if (query) {
|
|
789
|
-
if (query.page) q.page = query.page;
|
|
790
|
-
if (query.limit) q.limit = query.limit;
|
|
791
|
-
if (query.sort) q.sort = query.sort;
|
|
792
|
-
if (query.locale) q.locale = query.locale;
|
|
793
|
-
if (query.currency) q.currency = query.currency;
|
|
794
|
-
}
|
|
805
|
+
const q = catalogProductsQuery(query);
|
|
795
806
|
return this.client.request(
|
|
796
807
|
"GET",
|
|
797
808
|
`/catalog/categories/${slug}/products`,
|
|
@@ -825,7 +836,7 @@ var CatalogModule = class {
|
|
|
825
836
|
"GET",
|
|
826
837
|
"/catalog/featured",
|
|
827
838
|
{
|
|
828
|
-
query: { locale: options?.locale, currency: options?.currency }
|
|
839
|
+
query: { locale: options?.locale, currency: options?.currency, country: options?.country }
|
|
829
840
|
}
|
|
830
841
|
);
|
|
831
842
|
}
|
|
@@ -883,9 +894,10 @@ var CatalogModule = class {
|
|
|
883
894
|
if (query) {
|
|
884
895
|
if (query.category) q.category = query.category;
|
|
885
896
|
if (query.label) q.label = query.label;
|
|
886
|
-
if (query.priceMin) q.priceMin = query.priceMin;
|
|
887
|
-
if (query.priceMax) q.priceMax = query.priceMax;
|
|
897
|
+
if (query.priceMin !== void 0) q.priceMin = query.priceMin;
|
|
898
|
+
if (query.priceMax !== void 0) q.priceMax = query.priceMax;
|
|
888
899
|
if (query.currency) q.currency = query.currency;
|
|
900
|
+
if (query.country) q.country = query.country;
|
|
889
901
|
if (query.locale) q.locale = query.locale;
|
|
890
902
|
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
891
903
|
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
@@ -907,12 +919,12 @@ var CatalogModule = class {
|
|
|
907
919
|
return this.getProducts({ search: query, ...options });
|
|
908
920
|
}
|
|
909
921
|
/** List all active bundles */
|
|
910
|
-
async getBundles() {
|
|
911
|
-
return this.client.request("GET", "/catalog/bundles");
|
|
922
|
+
async getBundles(options) {
|
|
923
|
+
return this.client.request("GET", "/catalog/bundles", { query: { ...options } });
|
|
912
924
|
}
|
|
913
925
|
/** Get a single bundle by slug */
|
|
914
|
-
async getBundle(slug) {
|
|
915
|
-
return this.client.request("GET", `/catalog/bundles/${slug}
|
|
926
|
+
async getBundle(slug, options) {
|
|
927
|
+
return this.client.request("GET", `/catalog/bundles/${encodeURIComponent(slug)}`, { query: { ...options } });
|
|
916
928
|
}
|
|
917
929
|
/**
|
|
918
930
|
* One product group ("collection") by slug with its products as standard
|
|
@@ -925,7 +937,7 @@ var CatalogModule = class {
|
|
|
925
937
|
"GET",
|
|
926
938
|
`/catalog/product-groups/${encodeURIComponent(slug)}`,
|
|
927
939
|
{
|
|
928
|
-
query: { locale: options?.locale, currency: options?.currency }
|
|
940
|
+
query: { locale: options?.locale, currency: options?.currency, country: options?.country }
|
|
929
941
|
}
|
|
930
942
|
);
|
|
931
943
|
}
|
|
@@ -940,7 +952,7 @@ var CatalogModule = class {
|
|
|
940
952
|
return this.client.request(
|
|
941
953
|
"GET",
|
|
942
954
|
`/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
|
|
943
|
-
{ query: { locale: options?.locale, currency: options?.currency } }
|
|
955
|
+
{ query: { locale: options?.locale, currency: options?.currency, country: options?.country } }
|
|
944
956
|
);
|
|
945
957
|
}
|
|
946
958
|
/** Active promotions applicable to a product (with countdown end time) */
|
|
@@ -1148,6 +1160,7 @@ var CartModule = class {
|
|
|
1148
1160
|
body: { currency }
|
|
1149
1161
|
});
|
|
1150
1162
|
if (res.error) return res;
|
|
1163
|
+
if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
|
|
1151
1164
|
this.client.emit("cart:updated", res.data);
|
|
1152
1165
|
return res;
|
|
1153
1166
|
}
|
|
@@ -1168,6 +1181,7 @@ var CartModule = class {
|
|
|
1168
1181
|
body: input
|
|
1169
1182
|
});
|
|
1170
1183
|
if (res.error) return res;
|
|
1184
|
+
if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
|
|
1171
1185
|
this.client.emit("cart:updated", res.data);
|
|
1172
1186
|
return res;
|
|
1173
1187
|
}
|
|
@@ -1252,14 +1266,15 @@ var CartModule = class {
|
|
|
1252
1266
|
body
|
|
1253
1267
|
});
|
|
1254
1268
|
if (res.error) return res;
|
|
1269
|
+
if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
|
|
1255
1270
|
this.client.emit("cart:updated", res.data);
|
|
1256
1271
|
return res;
|
|
1257
1272
|
}
|
|
1258
|
-
/** Update
|
|
1259
|
-
async updateBundleQuantity(
|
|
1273
|
+
/** Update by CartBundleLine.id; legacy catalog bundle IDs are accepted by the API */
|
|
1274
|
+
async updateBundleQuantity(lineId, quantity) {
|
|
1260
1275
|
const res = await this.client.request(
|
|
1261
1276
|
"PATCH",
|
|
1262
|
-
`/cart/bundles/${
|
|
1277
|
+
`/cart/bundles/${lineId}`,
|
|
1263
1278
|
{
|
|
1264
1279
|
body: { quantity }
|
|
1265
1280
|
}
|
|
@@ -1268,11 +1283,11 @@ var CartModule = class {
|
|
|
1268
1283
|
this.client.emit("cart:updated", res.data);
|
|
1269
1284
|
return res;
|
|
1270
1285
|
}
|
|
1271
|
-
/** Remove
|
|
1272
|
-
async removeBundle(
|
|
1286
|
+
/** Remove by CartBundleLine.id; scoped to the current visitor cart */
|
|
1287
|
+
async removeBundle(lineId) {
|
|
1273
1288
|
const res = await this.client.request(
|
|
1274
1289
|
"DELETE",
|
|
1275
|
-
`/cart/bundles/${
|
|
1290
|
+
`/cart/bundles/${lineId}`
|
|
1276
1291
|
);
|
|
1277
1292
|
if (res.error) return res;
|
|
1278
1293
|
this.client.emit("cart:updated", res.data);
|
|
@@ -1306,6 +1321,12 @@ var CheckoutModule = class {
|
|
|
1306
1321
|
constructor(client) {
|
|
1307
1322
|
this.client = client;
|
|
1308
1323
|
}
|
|
1324
|
+
/** Recalculate the current cart including shipping, fees, benefits and rounding.
|
|
1325
|
+
* No order, stock claim, payment or gift-card redemption is created. Required
|
|
1326
|
+
* legal consents are enforced only on createOrder, not while quoting. */
|
|
1327
|
+
async preview(input) {
|
|
1328
|
+
return this.client.request("POST", "/checkout/preview", { body: input });
|
|
1329
|
+
}
|
|
1309
1330
|
/** Create order from cart */
|
|
1310
1331
|
async createOrder(input) {
|
|
1311
1332
|
const res = await this.client.request("POST", "/checkout", {
|
|
@@ -2200,6 +2221,7 @@ function BehioProvider({
|
|
|
2200
2221
|
});
|
|
2201
2222
|
}
|
|
2202
2223
|
const client = clientRef.current;
|
|
2224
|
+
const [sessionRestored, setSessionRestored] = (0, import_react2.useState)(false);
|
|
2203
2225
|
(0, import_react2.useEffect)(() => {
|
|
2204
2226
|
const accessToken = storageAdapter.get(STORAGE_KEYS.ACCESS_TOKEN);
|
|
2205
2227
|
const refreshToken = storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
@@ -2210,6 +2232,7 @@ function BehioProvider({
|
|
|
2210
2232
|
if (cartSession) {
|
|
2211
2233
|
client.setCartSession(cartSession);
|
|
2212
2234
|
}
|
|
2235
|
+
setSessionRestored(true);
|
|
2213
2236
|
}, [client, storageAdapter]);
|
|
2214
2237
|
(0, import_react2.useEffect)(() => {
|
|
2215
2238
|
if (!persistCurrency) return;
|
|
@@ -2264,12 +2287,13 @@ function BehioProvider({
|
|
|
2264
2287
|
() => ({
|
|
2265
2288
|
client,
|
|
2266
2289
|
storage: storageAdapter,
|
|
2290
|
+
sessionRestored,
|
|
2267
2291
|
currency: activeCurrency,
|
|
2268
2292
|
setCurrency,
|
|
2269
2293
|
configuredDefaultCurrency: defaultCurrency,
|
|
2270
2294
|
configuredCurrencies: currencies
|
|
2271
2295
|
}),
|
|
2272
|
-
[client, storageAdapter, activeCurrency, setCurrency, defaultCurrency, currencies]
|
|
2296
|
+
[client, storageAdapter, sessionRestored, activeCurrency, setCurrency, defaultCurrency, currencies]
|
|
2273
2297
|
);
|
|
2274
2298
|
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 }) });
|
|
2275
2299
|
}
|
|
@@ -2519,6 +2543,15 @@ function useSearch(query, options) {
|
|
|
2519
2543
|
// src/react/hooks/use-cart.ts
|
|
2520
2544
|
var import_react5 = require("react");
|
|
2521
2545
|
var import_react_query12 = require("@tanstack/react-query");
|
|
2546
|
+
|
|
2547
|
+
// src/react/utils/refresh-bundle-queries.ts
|
|
2548
|
+
async function refreshBundleQueries(queryClient) {
|
|
2549
|
+
const keys = [["behio", "bundle"], ["behio", "bundles"]];
|
|
2550
|
+
await Promise.all(keys.map((queryKey) => queryClient.cancelQueries({ queryKey })));
|
|
2551
|
+
await Promise.all(keys.map((queryKey) => queryClient.invalidateQueries({ queryKey })));
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// src/react/hooks/use-cart.ts
|
|
2522
2555
|
var CART_KEY = ["behio", "cart"];
|
|
2523
2556
|
function useCart(options) {
|
|
2524
2557
|
const { client, storage } = useBehio();
|
|
@@ -2533,41 +2566,30 @@ function useCart(options) {
|
|
|
2533
2566
|
// Only fetch if we have a cart session or are logged in
|
|
2534
2567
|
enabled: options?.enabled !== false && (!!client.getCartSession() || !!client.getAccessToken())
|
|
2535
2568
|
});
|
|
2569
|
+
const cancelCartRead = () => queryClient.cancelQueries({ queryKey: [...CART_KEY] });
|
|
2570
|
+
const acceptCart = async (result) => {
|
|
2571
|
+
await cancelCartRead();
|
|
2572
|
+
queryClient.setQueryData([...CART_KEY], result);
|
|
2573
|
+
void refreshBundleQueries(queryClient);
|
|
2574
|
+
};
|
|
2536
2575
|
const addMutation = (0, import_react_query12.useMutation)({
|
|
2537
2576
|
mutationFn: async ({ productId, quantity }) => {
|
|
2538
2577
|
return unwrap(client.cart.addItem({ productId, quantity: quantity ?? 1 }));
|
|
2539
2578
|
},
|
|
2540
|
-
|
|
2579
|
+
onMutate: cancelCartRead,
|
|
2580
|
+
onSuccess: async (result) => {
|
|
2541
2581
|
if (result.newSessionToken) {
|
|
2542
2582
|
storage.set(STORAGE_KEYS.CART_SESSION, result.newSessionToken);
|
|
2543
2583
|
}
|
|
2544
|
-
|
|
2584
|
+
await acceptCart(result);
|
|
2545
2585
|
}
|
|
2546
2586
|
});
|
|
2547
2587
|
const updateMutation = (0, import_react_query12.useMutation)({
|
|
2548
2588
|
mutationFn: async ({ itemId, quantity }) => {
|
|
2549
2589
|
return unwrap(client.cart.updateQuantity(itemId, quantity));
|
|
2550
2590
|
},
|
|
2551
|
-
onMutate:
|
|
2552
|
-
|
|
2553
|
-
const previous = queryClient.getQueryData([...CART_KEY]);
|
|
2554
|
-
if (previous) {
|
|
2555
|
-
const updatedItems = previous.items.map(
|
|
2556
|
-
(item) => item.id === itemId ? { ...item, quantity, totalPrice: item.unitPrice * quantity } : item
|
|
2557
|
-
);
|
|
2558
|
-
queryClient.setQueryData([...CART_KEY], {
|
|
2559
|
-
...previous,
|
|
2560
|
-
items: updatedItems,
|
|
2561
|
-
itemCount: updatedItems.reduce((sum, i) => sum + i.quantity, 0)
|
|
2562
|
-
});
|
|
2563
|
-
}
|
|
2564
|
-
return { previous };
|
|
2565
|
-
},
|
|
2566
|
-
onError: (_err, _vars, context) => {
|
|
2567
|
-
if (context?.previous) {
|
|
2568
|
-
queryClient.setQueryData([...CART_KEY], context.previous);
|
|
2569
|
-
}
|
|
2570
|
-
},
|
|
2591
|
+
onMutate: cancelCartRead,
|
|
2592
|
+
onSuccess: acceptCart,
|
|
2571
2593
|
onSettled: () => {
|
|
2572
2594
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
2573
2595
|
}
|
|
@@ -2576,54 +2598,42 @@ function useCart(options) {
|
|
|
2576
2598
|
mutationFn: async (itemId) => {
|
|
2577
2599
|
return unwrap(client.cart.removeItem(itemId));
|
|
2578
2600
|
},
|
|
2579
|
-
onMutate:
|
|
2580
|
-
|
|
2581
|
-
const previous = queryClient.getQueryData([...CART_KEY]);
|
|
2582
|
-
if (previous) {
|
|
2583
|
-
const updatedItems = previous.items.filter((item) => item.id !== itemId);
|
|
2584
|
-
queryClient.setQueryData([...CART_KEY], {
|
|
2585
|
-
...previous,
|
|
2586
|
-
items: updatedItems,
|
|
2587
|
-
itemCount: updatedItems.reduce((sum, i) => sum + i.quantity, 0)
|
|
2588
|
-
});
|
|
2589
|
-
}
|
|
2590
|
-
return { previous };
|
|
2591
|
-
},
|
|
2592
|
-
onError: (_err, _vars, context) => {
|
|
2593
|
-
if (context?.previous) {
|
|
2594
|
-
queryClient.setQueryData([...CART_KEY], context.previous);
|
|
2595
|
-
}
|
|
2596
|
-
},
|
|
2601
|
+
onMutate: cancelCartRead,
|
|
2602
|
+
onSuccess: acceptCart,
|
|
2597
2603
|
onSettled: () => {
|
|
2598
2604
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
2599
2605
|
}
|
|
2600
2606
|
});
|
|
2601
2607
|
const clearMutation = (0, import_react_query12.useMutation)({
|
|
2602
2608
|
mutationFn: () => unwrap(client.cart.clear()),
|
|
2603
|
-
|
|
2609
|
+
onMutate: cancelCartRead,
|
|
2610
|
+
onSuccess: async () => {
|
|
2611
|
+
await cancelCartRead();
|
|
2604
2612
|
queryClient.setQueryData([...CART_KEY], null);
|
|
2605
2613
|
client.clearCartSession();
|
|
2606
2614
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
2615
|
+
void refreshBundleQueries(queryClient);
|
|
2607
2616
|
}
|
|
2608
2617
|
});
|
|
2609
2618
|
const applyDiscountMutation = (0, import_react_query12.useMutation)({
|
|
2610
2619
|
mutationFn: (code) => unwrap(client.cart.applyDiscount(code)),
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
}
|
|
2620
|
+
onMutate: cancelCartRead,
|
|
2621
|
+
onSuccess: acceptCart
|
|
2614
2622
|
});
|
|
2615
2623
|
const removeDiscountMutation = (0, import_react_query12.useMutation)({
|
|
2616
2624
|
mutationFn: () => unwrap(client.cart.removeDiscount()),
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
}
|
|
2625
|
+
onMutate: cancelCartRead,
|
|
2626
|
+
onSuccess: acceptCart
|
|
2620
2627
|
});
|
|
2621
2628
|
const mergeMutation = (0, import_react_query12.useMutation)({
|
|
2622
2629
|
mutationFn: () => unwrap(client.cart.merge()),
|
|
2623
|
-
|
|
2630
|
+
onMutate: cancelCartRead,
|
|
2631
|
+
onSuccess: async (result) => {
|
|
2632
|
+
await cancelCartRead();
|
|
2624
2633
|
queryClient.setQueryData([...CART_KEY], result);
|
|
2625
2634
|
client.clearCartSession();
|
|
2626
2635
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
2636
|
+
void refreshBundleQueries(queryClient);
|
|
2627
2637
|
}
|
|
2628
2638
|
});
|
|
2629
2639
|
const addItem = (0, import_react5.useCallback)(
|
|
@@ -2668,7 +2678,7 @@ function useCart(options) {
|
|
|
2668
2678
|
merge,
|
|
2669
2679
|
// Computed
|
|
2670
2680
|
itemCount: cart?.itemCount ?? 0,
|
|
2671
|
-
isEmpty: !cart || cart.items.length === 0,
|
|
2681
|
+
isEmpty: !cart || cart.items.length === 0 && (cart.bundleLines?.length ?? 0) === 0,
|
|
2672
2682
|
// Mutation states
|
|
2673
2683
|
isAdding: addMutation.isPending,
|
|
2674
2684
|
isUpdating: updateMutation.isPending,
|
|
@@ -2724,6 +2734,7 @@ function useAuth() {
|
|
|
2724
2734
|
queryClient.setQueryData([...CUSTOMER_KEY], null);
|
|
2725
2735
|
queryClient.invalidateQueries({ queryKey: ["behio", "orders"] });
|
|
2726
2736
|
queryClient.invalidateQueries({ queryKey: ["behio", "addresses"] });
|
|
2737
|
+
void refreshBundleQueries(queryClient);
|
|
2727
2738
|
}, [client, storage, queryClient]);
|
|
2728
2739
|
const postAuth = (0, import_react6.useCallback)(async () => {
|
|
2729
2740
|
await queryClient.invalidateQueries({ queryKey: [...CUSTOMER_KEY] });
|
|
@@ -2736,6 +2747,7 @@ function useAuth() {
|
|
|
2736
2747
|
}
|
|
2737
2748
|
}
|
|
2738
2749
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY3] });
|
|
2750
|
+
void refreshBundleQueries(queryClient);
|
|
2739
2751
|
}, [client, storage, queryClient]);
|
|
2740
2752
|
const loginMutation = (0, import_react_query14.useMutation)({
|
|
2741
2753
|
mutationFn: async ({ email, password }) => {
|
|
@@ -3699,6 +3711,11 @@ function useCheckout() {
|
|
|
3699
3711
|
reset
|
|
3700
3712
|
};
|
|
3701
3713
|
}
|
|
3714
|
+
function useCheckoutPreview() {
|
|
3715
|
+
const { client } = useBehio();
|
|
3716
|
+
const mutation = (0, import_react_query34.useMutation)({ mutationFn: (input) => unwrap(client.checkout.preview(input)) });
|
|
3717
|
+
return { preview: mutation.mutateAsync, data: mutation.data, isPreviewing: mutation.isPending, error: mutation.error, reset: mutation.reset };
|
|
3718
|
+
}
|
|
3702
3719
|
|
|
3703
3720
|
// src/react/hooks/use-pages.ts
|
|
3704
3721
|
var import_react_query35 = require("@tanstack/react-query");
|
|
@@ -3863,7 +3880,8 @@ function useShopScripts(options) {
|
|
|
3863
3880
|
return (0, import_react_query39.useQuery)({
|
|
3864
3881
|
queryKey: ["behio", "shop-scripts"],
|
|
3865
3882
|
queryFn: () => unwrap(client.getShopScripts()),
|
|
3866
|
-
enabled: options?.enabled !== false
|
|
3883
|
+
enabled: options?.enabled !== false,
|
|
3884
|
+
initialData: options?.initialData
|
|
3867
3885
|
});
|
|
3868
3886
|
}
|
|
3869
3887
|
|
|
@@ -3997,31 +4015,32 @@ function partsFor(s) {
|
|
|
3997
4015
|
return [{ target: "head", html: `<meta name="google-site-verification" content="${id}" />` }];
|
|
3998
4016
|
case "RAW":
|
|
3999
4017
|
default:
|
|
4000
|
-
return [{ target: s.placement === "HEAD" ? "head" : "body", html: s.value }];
|
|
4018
|
+
return [{ target: s.placement === "HEAD" ? "head" : "body", start: s.placement === "BODY_START", html: s.value }];
|
|
4001
4019
|
}
|
|
4002
4020
|
}
|
|
4003
|
-
function injectHtml(target, html) {
|
|
4021
|
+
function injectHtml(target, html, start = false) {
|
|
4004
4022
|
const tpl = document.createElement("template");
|
|
4005
4023
|
tpl.innerHTML = html;
|
|
4006
4024
|
const added = [];
|
|
4025
|
+
const anchor = start ? target.firstChild : null;
|
|
4007
4026
|
Array.from(tpl.content.childNodes).forEach((node) => {
|
|
4008
4027
|
if (node.nodeName === "SCRIPT") {
|
|
4009
4028
|
const old = node;
|
|
4010
4029
|
const script = document.createElement("script");
|
|
4011
4030
|
for (const attr of Array.from(old.attributes)) script.setAttribute(attr.name, attr.value);
|
|
4012
4031
|
script.text = old.text;
|
|
4013
|
-
target.
|
|
4032
|
+
target.insertBefore(script, anchor);
|
|
4014
4033
|
added.push(script);
|
|
4015
4034
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
4016
4035
|
const clone = node.cloneNode(true);
|
|
4017
|
-
target.
|
|
4036
|
+
target.insertBefore(clone, anchor);
|
|
4018
4037
|
added.push(clone);
|
|
4019
4038
|
}
|
|
4020
4039
|
});
|
|
4021
4040
|
return added;
|
|
4022
4041
|
}
|
|
4023
|
-
function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
|
|
4024
|
-
const { data } = useShopScripts();
|
|
4042
|
+
function StorefrontScripts({ visitorId: visitorIdProp, initialScripts } = {}) {
|
|
4043
|
+
const { data } = useShopScripts({ enabled: initialScripts === void 0, initialData: initialScripts ? { scripts: initialScripts } : void 0 });
|
|
4025
4044
|
const [visitorId, setVisitorId] = (0, import_react18.useState)(visitorIdProp ?? "");
|
|
4026
4045
|
(0, import_react18.useEffect)(() => {
|
|
4027
4046
|
if (visitorIdProp) return;
|
|
@@ -4033,21 +4052,30 @@ function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
|
|
|
4033
4052
|
}, [visitorIdProp]);
|
|
4034
4053
|
const { data: consent } = useCookieConsent(visitorId || void 0);
|
|
4035
4054
|
const analyticsOk = Boolean(consent?.analytics);
|
|
4055
|
+
const marketingOk = Boolean(consent?.marketing);
|
|
4036
4056
|
const scripts = (0, import_react18.useMemo)(
|
|
4037
|
-
() => (data?.scripts ?? []).filter((s) => !s.consentRequired || analyticsOk),
|
|
4038
|
-
[data, analyticsOk]
|
|
4057
|
+
() => (initialScripts ?? data?.scripts ?? []).filter((s) => !s.consentRequired || (s.type === "META_PIXEL" ? marketingOk : analyticsOk)),
|
|
4058
|
+
[initialScripts, data, analyticsOk, marketingOk]
|
|
4039
4059
|
);
|
|
4040
4060
|
const signature = (0, import_react18.useMemo)(
|
|
4041
4061
|
() => JSON.stringify(scripts.map((s) => [s.id, s.type, s.placement, s.value])),
|
|
4042
4062
|
[scripts]
|
|
4043
4063
|
);
|
|
4064
|
+
const activeConsented = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
|
|
4044
4065
|
(0, import_react18.useEffect)(() => {
|
|
4045
4066
|
if (typeof document === "undefined") return;
|
|
4067
|
+
const nextConsented = new Set(scripts.filter((s) => s.consentRequired).map((s) => s.id));
|
|
4068
|
+
const revoked = [...activeConsented.current].some((id) => !nextConsented.has(id));
|
|
4069
|
+
activeConsented.current = nextConsented;
|
|
4070
|
+
if (revoked) {
|
|
4071
|
+
window.location.reload();
|
|
4072
|
+
return;
|
|
4073
|
+
}
|
|
4046
4074
|
const added = [];
|
|
4047
4075
|
for (const s of scripts) {
|
|
4048
4076
|
for (const part of partsFor(s)) {
|
|
4049
4077
|
const target = part.target === "head" ? document.head : document.body;
|
|
4050
|
-
added.push(...injectHtml(target, part.html));
|
|
4078
|
+
added.push(...injectHtml(target, part.html, part.start));
|
|
4051
4079
|
}
|
|
4052
4080
|
}
|
|
4053
4081
|
return () => {
|
|
@@ -4309,20 +4337,22 @@ function utmFromSearch(search) {
|
|
|
4309
4337
|
// src/react/hooks/use-bundles.ts
|
|
4310
4338
|
var import_react_query42 = require("@tanstack/react-query");
|
|
4311
4339
|
function useBundles(options) {
|
|
4312
|
-
const { client } = useBehio();
|
|
4340
|
+
const { client, sessionRestored } = useBehio();
|
|
4341
|
+
const query = { locale: options?.locale ?? client.getLocale(), currency: options?.currency ?? client.getCurrency(), country: options?.country ?? client.getCountry() };
|
|
4313
4342
|
return (0, import_react_query42.useQuery)({
|
|
4314
|
-
queryKey: ["behio", "bundles"],
|
|
4315
|
-
queryFn: () => unwrap(client.catalog.getBundles()),
|
|
4316
|
-
enabled: options?.enabled ?? true,
|
|
4343
|
+
queryKey: ["behio", "bundles", query],
|
|
4344
|
+
queryFn: () => unwrap(client.catalog.getBundles(query)),
|
|
4345
|
+
enabled: sessionRestored && (options?.enabled ?? true),
|
|
4317
4346
|
initialData: options?.initialData
|
|
4318
4347
|
});
|
|
4319
4348
|
}
|
|
4320
4349
|
function useBundle(slug, options) {
|
|
4321
|
-
const { client } = useBehio();
|
|
4350
|
+
const { client, sessionRestored } = useBehio();
|
|
4351
|
+
const query = { locale: options?.locale ?? client.getLocale(), currency: options?.currency ?? client.getCurrency(), country: options?.country ?? client.getCountry() };
|
|
4322
4352
|
return (0, import_react_query42.useQuery)({
|
|
4323
|
-
queryKey: ["behio", "bundle", slug],
|
|
4324
|
-
queryFn: () => unwrap(client.catalog.getBundle(slug)),
|
|
4325
|
-
enabled: Boolean(slug) && (options?.enabled ?? true),
|
|
4353
|
+
queryKey: ["behio", "bundle", slug, query],
|
|
4354
|
+
queryFn: () => unwrap(client.catalog.getBundle(slug, query)),
|
|
4355
|
+
enabled: sessionRestored && Boolean(slug) && (options?.enabled ?? true),
|
|
4326
4356
|
initialData: options?.initialData
|
|
4327
4357
|
});
|
|
4328
4358
|
}
|
|
@@ -4425,8 +4455,10 @@ var import_react_query48 = require("@tanstack/react-query");
|
|
|
4425
4455
|
function useProductReviews(productId, options) {
|
|
4426
4456
|
const { client } = useBehio();
|
|
4427
4457
|
return (0, import_react_query48.useQuery)({
|
|
4428
|
-
queryKey: ["behio", "reviews", productId, options?.page ?? 1],
|
|
4458
|
+
queryKey: ["behio", "reviews", productId, options?.page ?? 1, options?.limit ?? 20],
|
|
4429
4459
|
queryFn: () => unwrap(client.reviews.getProductReviews(productId, options?.page, options?.limit)),
|
|
4460
|
+
initialData: options?.initialData,
|
|
4461
|
+
staleTime: 3e4,
|
|
4430
4462
|
enabled: Boolean(productId) && (options?.enabled ?? true)
|
|
4431
4463
|
});
|
|
4432
4464
|
}
|
|
@@ -4489,15 +4521,23 @@ function useNotifyWhenAvailable() {
|
|
|
4489
4521
|
}
|
|
4490
4522
|
|
|
4491
4523
|
// src/react/utils/format-price.ts
|
|
4492
|
-
function formatPrice(amount, currency, locale) {
|
|
4524
|
+
function formatPrice(amount, currency, locale, display) {
|
|
4493
4525
|
const resolvedLocale = locale ?? "cs";
|
|
4494
4526
|
try {
|
|
4495
|
-
|
|
4527
|
+
const formatter = new Intl.NumberFormat(resolvedLocale, {
|
|
4496
4528
|
style: "currency",
|
|
4497
4529
|
currency,
|
|
4530
|
+
currencyDisplay: display === "CODE" ? "code" : "symbol",
|
|
4498
4531
|
minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
|
|
4499
4532
|
maximumFractionDigits: 2
|
|
4500
|
-
})
|
|
4533
|
+
});
|
|
4534
|
+
if (display === "SYMBOL_BEFORE" || display === "SYMBOL_AFTER") {
|
|
4535
|
+
const parts = formatter.formatToParts(amount);
|
|
4536
|
+
const symbol = parts.find((p) => p.type === "currency")?.value ?? currency;
|
|
4537
|
+
const value = parts.filter((p) => p.type !== "currency").map((p) => p.value).join("").trim();
|
|
4538
|
+
return display === "SYMBOL_BEFORE" ? `${symbol}\xA0${value}` : `${value}\xA0${symbol}`;
|
|
4539
|
+
}
|
|
4540
|
+
return formatter.format(amount);
|
|
4501
4541
|
} catch {
|
|
4502
4542
|
return `${amount} ${currency}`;
|
|
4503
4543
|
}
|
|
@@ -4631,6 +4671,7 @@ async function revokeAnalyticsConsent(client) {
|
|
|
4631
4671
|
useCategory,
|
|
4632
4672
|
useCertificateVerification,
|
|
4633
4673
|
useCheckout,
|
|
4674
|
+
useCheckoutPreview,
|
|
4634
4675
|
useCookieConsent,
|
|
4635
4676
|
useCourse,
|
|
4636
4677
|
useCourseCertificates,
|