@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.mjs
CHANGED
|
@@ -150,6 +150,7 @@ var BehioStorefront = class {
|
|
|
150
150
|
this.shopDomain = config.shopDomain;
|
|
151
151
|
this.defaultLocale = config.locale;
|
|
152
152
|
this.defaultCurrency = config.currency;
|
|
153
|
+
this.setCountry(config.country);
|
|
153
154
|
this.fetchFn = config.fetch || globalThis.fetch.bind(globalThis);
|
|
154
155
|
this.timeout = config.timeout ?? 3e4;
|
|
155
156
|
this.retries = config.retries ?? 1;
|
|
@@ -311,6 +312,14 @@ var BehioStorefront = class {
|
|
|
311
312
|
getCurrency() {
|
|
312
313
|
return this.defaultCurrency;
|
|
313
314
|
}
|
|
315
|
+
/** Set the catalog's delivery country. Use cart.setDestination separately for the cart. */
|
|
316
|
+
setCountry(country) {
|
|
317
|
+
const normalized = country?.trim().toUpperCase();
|
|
318
|
+
this.defaultCountry = normalized && /^[A-Z]{2}$/.test(normalized) ? normalized : void 0;
|
|
319
|
+
}
|
|
320
|
+
getCountry() {
|
|
321
|
+
return this.defaultCountry;
|
|
322
|
+
}
|
|
314
323
|
/** Set the default locale sent on every catalog request (per-call wins). */
|
|
315
324
|
setLocale(locale) {
|
|
316
325
|
this.defaultLocale = locale || void 0;
|
|
@@ -466,12 +475,16 @@ var BehioStorefront = class {
|
|
|
466
475
|
if (this.defaultCurrency && !params.has("currency")) {
|
|
467
476
|
params.set("currency", this.defaultCurrency);
|
|
468
477
|
}
|
|
478
|
+
if (this.defaultCountry && path.startsWith("/catalog/") && !params.has("country")) {
|
|
479
|
+
params.set("country", this.defaultCountry);
|
|
480
|
+
}
|
|
469
481
|
const qs = params.toString();
|
|
470
482
|
const url = `${this.baseUrl}/storefront/v1${path}${qs ? `?${qs}` : ""}`;
|
|
471
483
|
const headers = {
|
|
472
484
|
"X-Api-Key": this.apiKey,
|
|
473
485
|
"Content-Type": "application/json"
|
|
474
486
|
};
|
|
487
|
+
if (path === "/cart" || path.startsWith("/cart/")) headers["X-Behio-Cart-Contract"] = "2";
|
|
475
488
|
if (this.shopDomain) {
|
|
476
489
|
headers["X-Shop-Domain"] = this.shopDomain;
|
|
477
490
|
}
|
|
@@ -598,42 +611,46 @@ var BehioStorefront = class {
|
|
|
598
611
|
throw new BehioNetworkError("Request failed after retries");
|
|
599
612
|
}
|
|
600
613
|
};
|
|
614
|
+
function catalogProductsQuery(query) {
|
|
615
|
+
const q = {};
|
|
616
|
+
if (query) {
|
|
617
|
+
if (query.page !== void 0) q.page = query.page;
|
|
618
|
+
if (query.limit !== void 0) q.limit = query.limit;
|
|
619
|
+
if (query.category) q.category = query.category;
|
|
620
|
+
if (query.label) q.label = query.label;
|
|
621
|
+
if (query.priceMin !== void 0) q.priceMin = query.priceMin;
|
|
622
|
+
if (query.priceMax !== void 0) q.priceMax = query.priceMax;
|
|
623
|
+
if (query.currency) q.currency = query.currency;
|
|
624
|
+
if (query.country) q.country = query.country;
|
|
625
|
+
if (query.locale) q.locale = query.locale;
|
|
626
|
+
if (query.sort) q.sort = query.sort;
|
|
627
|
+
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
628
|
+
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
629
|
+
if (query.search) q.search = query.search;
|
|
630
|
+
if (query.parameters) q.parameters = JSON.stringify(query.parameters);
|
|
631
|
+
if (query.facets) q.facets = JSON.stringify(query.facets);
|
|
632
|
+
if (query.ids && query.ids.length > 0) q.ids = query.ids;
|
|
633
|
+
if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
|
|
634
|
+
if (query.labels && query.labels.length > 0) q.labels = query.labels;
|
|
635
|
+
if (query.categories && query.categories.length > 0)
|
|
636
|
+
q.categories = query.categories;
|
|
637
|
+
if (query.excludeIds && query.excludeIds.length > 0)
|
|
638
|
+
q.excludeIds = query.excludeIds;
|
|
639
|
+
if (query.excludeCategories && query.excludeCategories.length > 0)
|
|
640
|
+
q.excludeCategories = query.excludeCategories;
|
|
641
|
+
if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
|
|
642
|
+
if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
|
|
643
|
+
if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
|
|
644
|
+
}
|
|
645
|
+
return q;
|
|
646
|
+
}
|
|
601
647
|
var CatalogModule = class {
|
|
602
648
|
constructor(client) {
|
|
603
649
|
this.client = client;
|
|
604
650
|
}
|
|
605
651
|
/** List products with filtering, pagination, search */
|
|
606
652
|
async getProducts(query) {
|
|
607
|
-
const q =
|
|
608
|
-
if (query) {
|
|
609
|
-
if (query.page) q.page = query.page;
|
|
610
|
-
if (query.limit) q.limit = query.limit;
|
|
611
|
-
if (query.category) q.category = query.category;
|
|
612
|
-
if (query.label) q.label = query.label;
|
|
613
|
-
if (query.priceMin) q.priceMin = query.priceMin;
|
|
614
|
-
if (query.priceMax) q.priceMax = query.priceMax;
|
|
615
|
-
if (query.currency) q.currency = query.currency;
|
|
616
|
-
if (query.country) q.country = query.country;
|
|
617
|
-
if (query.locale) q.locale = query.locale;
|
|
618
|
-
if (query.sort) q.sort = query.sort;
|
|
619
|
-
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
620
|
-
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
621
|
-
if (query.search) q.search = query.search;
|
|
622
|
-
if (query.parameters) q.parameters = JSON.stringify(query.parameters);
|
|
623
|
-
if (query.facets) q.facets = JSON.stringify(query.facets);
|
|
624
|
-
if (query.ids && query.ids.length > 0) q.ids = query.ids;
|
|
625
|
-
if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
|
|
626
|
-
if (query.labels && query.labels.length > 0) q.labels = query.labels;
|
|
627
|
-
if (query.categories && query.categories.length > 0)
|
|
628
|
-
q.categories = query.categories;
|
|
629
|
-
if (query.excludeIds && query.excludeIds.length > 0)
|
|
630
|
-
q.excludeIds = query.excludeIds;
|
|
631
|
-
if (query.excludeCategories && query.excludeCategories.length > 0)
|
|
632
|
-
q.excludeCategories = query.excludeCategories;
|
|
633
|
-
if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
|
|
634
|
-
if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
|
|
635
|
-
if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
|
|
636
|
-
}
|
|
653
|
+
const q = catalogProductsQuery(query);
|
|
637
654
|
return this.client.request(
|
|
638
655
|
"GET",
|
|
639
656
|
"/catalog/products",
|
|
@@ -675,14 +692,7 @@ var CatalogModule = class {
|
|
|
675
692
|
}
|
|
676
693
|
/** Get products in a category */
|
|
677
694
|
async getCategoryProducts(slug, query) {
|
|
678
|
-
const q =
|
|
679
|
-
if (query) {
|
|
680
|
-
if (query.page) q.page = query.page;
|
|
681
|
-
if (query.limit) q.limit = query.limit;
|
|
682
|
-
if (query.sort) q.sort = query.sort;
|
|
683
|
-
if (query.locale) q.locale = query.locale;
|
|
684
|
-
if (query.currency) q.currency = query.currency;
|
|
685
|
-
}
|
|
695
|
+
const q = catalogProductsQuery(query);
|
|
686
696
|
return this.client.request(
|
|
687
697
|
"GET",
|
|
688
698
|
`/catalog/categories/${slug}/products`,
|
|
@@ -716,7 +726,7 @@ var CatalogModule = class {
|
|
|
716
726
|
"GET",
|
|
717
727
|
"/catalog/featured",
|
|
718
728
|
{
|
|
719
|
-
query: { locale: options?.locale, currency: options?.currency }
|
|
729
|
+
query: { locale: options?.locale, currency: options?.currency, country: options?.country }
|
|
720
730
|
}
|
|
721
731
|
);
|
|
722
732
|
}
|
|
@@ -774,9 +784,10 @@ var CatalogModule = class {
|
|
|
774
784
|
if (query) {
|
|
775
785
|
if (query.category) q.category = query.category;
|
|
776
786
|
if (query.label) q.label = query.label;
|
|
777
|
-
if (query.priceMin) q.priceMin = query.priceMin;
|
|
778
|
-
if (query.priceMax) q.priceMax = query.priceMax;
|
|
787
|
+
if (query.priceMin !== void 0) q.priceMin = query.priceMin;
|
|
788
|
+
if (query.priceMax !== void 0) q.priceMax = query.priceMax;
|
|
779
789
|
if (query.currency) q.currency = query.currency;
|
|
790
|
+
if (query.country) q.country = query.country;
|
|
780
791
|
if (query.locale) q.locale = query.locale;
|
|
781
792
|
if (query.inStock !== void 0) q.inStock = query.inStock;
|
|
782
793
|
if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
|
|
@@ -798,12 +809,12 @@ var CatalogModule = class {
|
|
|
798
809
|
return this.getProducts({ search: query, ...options });
|
|
799
810
|
}
|
|
800
811
|
/** List all active bundles */
|
|
801
|
-
async getBundles() {
|
|
802
|
-
return this.client.request("GET", "/catalog/bundles");
|
|
812
|
+
async getBundles(options) {
|
|
813
|
+
return this.client.request("GET", "/catalog/bundles", { query: { ...options } });
|
|
803
814
|
}
|
|
804
815
|
/** Get a single bundle by slug */
|
|
805
|
-
async getBundle(slug) {
|
|
806
|
-
return this.client.request("GET", `/catalog/bundles/${slug}
|
|
816
|
+
async getBundle(slug, options) {
|
|
817
|
+
return this.client.request("GET", `/catalog/bundles/${encodeURIComponent(slug)}`, { query: { ...options } });
|
|
807
818
|
}
|
|
808
819
|
/**
|
|
809
820
|
* One product group ("collection") by slug with its products as standard
|
|
@@ -816,7 +827,7 @@ var CatalogModule = class {
|
|
|
816
827
|
"GET",
|
|
817
828
|
`/catalog/product-groups/${encodeURIComponent(slug)}`,
|
|
818
829
|
{
|
|
819
|
-
query: { locale: options?.locale, currency: options?.currency }
|
|
830
|
+
query: { locale: options?.locale, currency: options?.currency, country: options?.country }
|
|
820
831
|
}
|
|
821
832
|
);
|
|
822
833
|
}
|
|
@@ -831,7 +842,7 @@ var CatalogModule = class {
|
|
|
831
842
|
return this.client.request(
|
|
832
843
|
"GET",
|
|
833
844
|
`/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
|
|
834
|
-
{ query: { locale: options?.locale, currency: options?.currency } }
|
|
845
|
+
{ query: { locale: options?.locale, currency: options?.currency, country: options?.country } }
|
|
835
846
|
);
|
|
836
847
|
}
|
|
837
848
|
/** Active promotions applicable to a product (with countdown end time) */
|
|
@@ -1039,6 +1050,7 @@ var CartModule = class {
|
|
|
1039
1050
|
body: { currency }
|
|
1040
1051
|
});
|
|
1041
1052
|
if (res.error) return res;
|
|
1053
|
+
if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
|
|
1042
1054
|
this.client.emit("cart:updated", res.data);
|
|
1043
1055
|
return res;
|
|
1044
1056
|
}
|
|
@@ -1059,6 +1071,7 @@ var CartModule = class {
|
|
|
1059
1071
|
body: input
|
|
1060
1072
|
});
|
|
1061
1073
|
if (res.error) return res;
|
|
1074
|
+
if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
|
|
1062
1075
|
this.client.emit("cart:updated", res.data);
|
|
1063
1076
|
return res;
|
|
1064
1077
|
}
|
|
@@ -1143,14 +1156,15 @@ var CartModule = class {
|
|
|
1143
1156
|
body
|
|
1144
1157
|
});
|
|
1145
1158
|
if (res.error) return res;
|
|
1159
|
+
if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
|
|
1146
1160
|
this.client.emit("cart:updated", res.data);
|
|
1147
1161
|
return res;
|
|
1148
1162
|
}
|
|
1149
|
-
/** Update
|
|
1150
|
-
async updateBundleQuantity(
|
|
1163
|
+
/** Update by CartBundleLine.id; legacy catalog bundle IDs are accepted by the API */
|
|
1164
|
+
async updateBundleQuantity(lineId, quantity) {
|
|
1151
1165
|
const res = await this.client.request(
|
|
1152
1166
|
"PATCH",
|
|
1153
|
-
`/cart/bundles/${
|
|
1167
|
+
`/cart/bundles/${lineId}`,
|
|
1154
1168
|
{
|
|
1155
1169
|
body: { quantity }
|
|
1156
1170
|
}
|
|
@@ -1159,11 +1173,11 @@ var CartModule = class {
|
|
|
1159
1173
|
this.client.emit("cart:updated", res.data);
|
|
1160
1174
|
return res;
|
|
1161
1175
|
}
|
|
1162
|
-
/** Remove
|
|
1163
|
-
async removeBundle(
|
|
1176
|
+
/** Remove by CartBundleLine.id; scoped to the current visitor cart */
|
|
1177
|
+
async removeBundle(lineId) {
|
|
1164
1178
|
const res = await this.client.request(
|
|
1165
1179
|
"DELETE",
|
|
1166
|
-
`/cart/bundles/${
|
|
1180
|
+
`/cart/bundles/${lineId}`
|
|
1167
1181
|
);
|
|
1168
1182
|
if (res.error) return res;
|
|
1169
1183
|
this.client.emit("cart:updated", res.data);
|
|
@@ -1197,6 +1211,12 @@ var CheckoutModule = class {
|
|
|
1197
1211
|
constructor(client) {
|
|
1198
1212
|
this.client = client;
|
|
1199
1213
|
}
|
|
1214
|
+
/** Recalculate the current cart including shipping, fees, benefits and rounding.
|
|
1215
|
+
* No order, stock claim, payment or gift-card redemption is created. Required
|
|
1216
|
+
* legal consents are enforced only on createOrder, not while quoting. */
|
|
1217
|
+
async preview(input) {
|
|
1218
|
+
return this.client.request("POST", "/checkout/preview", { body: input });
|
|
1219
|
+
}
|
|
1200
1220
|
/** Create order from cart */
|
|
1201
1221
|
async createOrder(input) {
|
|
1202
1222
|
const res = await this.client.request("POST", "/checkout", {
|
|
@@ -2091,6 +2111,7 @@ function BehioProvider({
|
|
|
2091
2111
|
});
|
|
2092
2112
|
}
|
|
2093
2113
|
const client = clientRef.current;
|
|
2114
|
+
const [sessionRestored, setSessionRestored] = useState(false);
|
|
2094
2115
|
useEffect(() => {
|
|
2095
2116
|
const accessToken = storageAdapter.get(STORAGE_KEYS.ACCESS_TOKEN);
|
|
2096
2117
|
const refreshToken = storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
@@ -2101,6 +2122,7 @@ function BehioProvider({
|
|
|
2101
2122
|
if (cartSession) {
|
|
2102
2123
|
client.setCartSession(cartSession);
|
|
2103
2124
|
}
|
|
2125
|
+
setSessionRestored(true);
|
|
2104
2126
|
}, [client, storageAdapter]);
|
|
2105
2127
|
useEffect(() => {
|
|
2106
2128
|
if (!persistCurrency) return;
|
|
@@ -2155,12 +2177,13 @@ function BehioProvider({
|
|
|
2155
2177
|
() => ({
|
|
2156
2178
|
client,
|
|
2157
2179
|
storage: storageAdapter,
|
|
2180
|
+
sessionRestored,
|
|
2158
2181
|
currency: activeCurrency,
|
|
2159
2182
|
setCurrency,
|
|
2160
2183
|
configuredDefaultCurrency: defaultCurrency,
|
|
2161
2184
|
configuredCurrencies: currencies
|
|
2162
2185
|
}),
|
|
2163
|
-
[client, storageAdapter, activeCurrency, setCurrency, defaultCurrency, currencies]
|
|
2186
|
+
[client, storageAdapter, sessionRestored, activeCurrency, setCurrency, defaultCurrency, currencies]
|
|
2164
2187
|
);
|
|
2165
2188
|
return /* @__PURE__ */ jsx(QueryClientProvider, { client: qc, children: /* @__PURE__ */ jsx(BehioContext.Provider, { value: ctxValue, children }) });
|
|
2166
2189
|
}
|
|
@@ -2410,6 +2433,15 @@ function useSearch(query, options) {
|
|
|
2410
2433
|
// src/react/hooks/use-cart.ts
|
|
2411
2434
|
import { useCallback as useCallback3 } from "react";
|
|
2412
2435
|
import { useQuery as useQuery10, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2436
|
+
|
|
2437
|
+
// src/react/utils/refresh-bundle-queries.ts
|
|
2438
|
+
async function refreshBundleQueries(queryClient) {
|
|
2439
|
+
const keys = [["behio", "bundle"], ["behio", "bundles"]];
|
|
2440
|
+
await Promise.all(keys.map((queryKey) => queryClient.cancelQueries({ queryKey })));
|
|
2441
|
+
await Promise.all(keys.map((queryKey) => queryClient.invalidateQueries({ queryKey })));
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// src/react/hooks/use-cart.ts
|
|
2413
2445
|
var CART_KEY = ["behio", "cart"];
|
|
2414
2446
|
function useCart(options) {
|
|
2415
2447
|
const { client, storage } = useBehio();
|
|
@@ -2424,41 +2456,30 @@ function useCart(options) {
|
|
|
2424
2456
|
// Only fetch if we have a cart session or are logged in
|
|
2425
2457
|
enabled: options?.enabled !== false && (!!client.getCartSession() || !!client.getAccessToken())
|
|
2426
2458
|
});
|
|
2459
|
+
const cancelCartRead = () => queryClient.cancelQueries({ queryKey: [...CART_KEY] });
|
|
2460
|
+
const acceptCart = async (result) => {
|
|
2461
|
+
await cancelCartRead();
|
|
2462
|
+
queryClient.setQueryData([...CART_KEY], result);
|
|
2463
|
+
void refreshBundleQueries(queryClient);
|
|
2464
|
+
};
|
|
2427
2465
|
const addMutation = useMutation({
|
|
2428
2466
|
mutationFn: async ({ productId, quantity }) => {
|
|
2429
2467
|
return unwrap(client.cart.addItem({ productId, quantity: quantity ?? 1 }));
|
|
2430
2468
|
},
|
|
2431
|
-
|
|
2469
|
+
onMutate: cancelCartRead,
|
|
2470
|
+
onSuccess: async (result) => {
|
|
2432
2471
|
if (result.newSessionToken) {
|
|
2433
2472
|
storage.set(STORAGE_KEYS.CART_SESSION, result.newSessionToken);
|
|
2434
2473
|
}
|
|
2435
|
-
|
|
2474
|
+
await acceptCart(result);
|
|
2436
2475
|
}
|
|
2437
2476
|
});
|
|
2438
2477
|
const updateMutation = useMutation({
|
|
2439
2478
|
mutationFn: async ({ itemId, quantity }) => {
|
|
2440
2479
|
return unwrap(client.cart.updateQuantity(itemId, quantity));
|
|
2441
2480
|
},
|
|
2442
|
-
onMutate:
|
|
2443
|
-
|
|
2444
|
-
const previous = queryClient.getQueryData([...CART_KEY]);
|
|
2445
|
-
if (previous) {
|
|
2446
|
-
const updatedItems = previous.items.map(
|
|
2447
|
-
(item) => item.id === itemId ? { ...item, quantity, totalPrice: item.unitPrice * quantity } : item
|
|
2448
|
-
);
|
|
2449
|
-
queryClient.setQueryData([...CART_KEY], {
|
|
2450
|
-
...previous,
|
|
2451
|
-
items: updatedItems,
|
|
2452
|
-
itemCount: updatedItems.reduce((sum, i) => sum + i.quantity, 0)
|
|
2453
|
-
});
|
|
2454
|
-
}
|
|
2455
|
-
return { previous };
|
|
2456
|
-
},
|
|
2457
|
-
onError: (_err, _vars, context) => {
|
|
2458
|
-
if (context?.previous) {
|
|
2459
|
-
queryClient.setQueryData([...CART_KEY], context.previous);
|
|
2460
|
-
}
|
|
2461
|
-
},
|
|
2481
|
+
onMutate: cancelCartRead,
|
|
2482
|
+
onSuccess: acceptCart,
|
|
2462
2483
|
onSettled: () => {
|
|
2463
2484
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
2464
2485
|
}
|
|
@@ -2467,54 +2488,42 @@ function useCart(options) {
|
|
|
2467
2488
|
mutationFn: async (itemId) => {
|
|
2468
2489
|
return unwrap(client.cart.removeItem(itemId));
|
|
2469
2490
|
},
|
|
2470
|
-
onMutate:
|
|
2471
|
-
|
|
2472
|
-
const previous = queryClient.getQueryData([...CART_KEY]);
|
|
2473
|
-
if (previous) {
|
|
2474
|
-
const updatedItems = previous.items.filter((item) => item.id !== itemId);
|
|
2475
|
-
queryClient.setQueryData([...CART_KEY], {
|
|
2476
|
-
...previous,
|
|
2477
|
-
items: updatedItems,
|
|
2478
|
-
itemCount: updatedItems.reduce((sum, i) => sum + i.quantity, 0)
|
|
2479
|
-
});
|
|
2480
|
-
}
|
|
2481
|
-
return { previous };
|
|
2482
|
-
},
|
|
2483
|
-
onError: (_err, _vars, context) => {
|
|
2484
|
-
if (context?.previous) {
|
|
2485
|
-
queryClient.setQueryData([...CART_KEY], context.previous);
|
|
2486
|
-
}
|
|
2487
|
-
},
|
|
2491
|
+
onMutate: cancelCartRead,
|
|
2492
|
+
onSuccess: acceptCart,
|
|
2488
2493
|
onSettled: () => {
|
|
2489
2494
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
2490
2495
|
}
|
|
2491
2496
|
});
|
|
2492
2497
|
const clearMutation = useMutation({
|
|
2493
2498
|
mutationFn: () => unwrap(client.cart.clear()),
|
|
2494
|
-
|
|
2499
|
+
onMutate: cancelCartRead,
|
|
2500
|
+
onSuccess: async () => {
|
|
2501
|
+
await cancelCartRead();
|
|
2495
2502
|
queryClient.setQueryData([...CART_KEY], null);
|
|
2496
2503
|
client.clearCartSession();
|
|
2497
2504
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
2505
|
+
void refreshBundleQueries(queryClient);
|
|
2498
2506
|
}
|
|
2499
2507
|
});
|
|
2500
2508
|
const applyDiscountMutation = useMutation({
|
|
2501
2509
|
mutationFn: (code) => unwrap(client.cart.applyDiscount(code)),
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
}
|
|
2510
|
+
onMutate: cancelCartRead,
|
|
2511
|
+
onSuccess: acceptCart
|
|
2505
2512
|
});
|
|
2506
2513
|
const removeDiscountMutation = useMutation({
|
|
2507
2514
|
mutationFn: () => unwrap(client.cart.removeDiscount()),
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
}
|
|
2515
|
+
onMutate: cancelCartRead,
|
|
2516
|
+
onSuccess: acceptCart
|
|
2511
2517
|
});
|
|
2512
2518
|
const mergeMutation = useMutation({
|
|
2513
2519
|
mutationFn: () => unwrap(client.cart.merge()),
|
|
2514
|
-
|
|
2520
|
+
onMutate: cancelCartRead,
|
|
2521
|
+
onSuccess: async (result) => {
|
|
2522
|
+
await cancelCartRead();
|
|
2515
2523
|
queryClient.setQueryData([...CART_KEY], result);
|
|
2516
2524
|
client.clearCartSession();
|
|
2517
2525
|
storage.remove(STORAGE_KEYS.CART_SESSION);
|
|
2526
|
+
void refreshBundleQueries(queryClient);
|
|
2518
2527
|
}
|
|
2519
2528
|
});
|
|
2520
2529
|
const addItem = useCallback3(
|
|
@@ -2559,7 +2568,7 @@ function useCart(options) {
|
|
|
2559
2568
|
merge,
|
|
2560
2569
|
// Computed
|
|
2561
2570
|
itemCount: cart?.itemCount ?? 0,
|
|
2562
|
-
isEmpty: !cart || cart.items.length === 0,
|
|
2571
|
+
isEmpty: !cart || cart.items.length === 0 && (cart.bundleLines?.length ?? 0) === 0,
|
|
2563
2572
|
// Mutation states
|
|
2564
2573
|
isAdding: addMutation.isPending,
|
|
2565
2574
|
isUpdating: updateMutation.isPending,
|
|
@@ -2615,6 +2624,7 @@ function useAuth() {
|
|
|
2615
2624
|
queryClient.setQueryData([...CUSTOMER_KEY], null);
|
|
2616
2625
|
queryClient.invalidateQueries({ queryKey: ["behio", "orders"] });
|
|
2617
2626
|
queryClient.invalidateQueries({ queryKey: ["behio", "addresses"] });
|
|
2627
|
+
void refreshBundleQueries(queryClient);
|
|
2618
2628
|
}, [client, storage, queryClient]);
|
|
2619
2629
|
const postAuth = useCallback4(async () => {
|
|
2620
2630
|
await queryClient.invalidateQueries({ queryKey: [...CUSTOMER_KEY] });
|
|
@@ -2627,6 +2637,7 @@ function useAuth() {
|
|
|
2627
2637
|
}
|
|
2628
2638
|
}
|
|
2629
2639
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY3] });
|
|
2640
|
+
void refreshBundleQueries(queryClient);
|
|
2630
2641
|
}, [client, storage, queryClient]);
|
|
2631
2642
|
const loginMutation = useMutation2({
|
|
2632
2643
|
mutationFn: async ({ email, password }) => {
|
|
@@ -3590,6 +3601,11 @@ function useCheckout() {
|
|
|
3590
3601
|
reset
|
|
3591
3602
|
};
|
|
3592
3603
|
}
|
|
3604
|
+
function useCheckoutPreview() {
|
|
3605
|
+
const { client } = useBehio();
|
|
3606
|
+
const mutation = useMutation14({ mutationFn: (input) => unwrap(client.checkout.preview(input)) });
|
|
3607
|
+
return { preview: mutation.mutateAsync, data: mutation.data, isPreviewing: mutation.isPending, error: mutation.error, reset: mutation.reset };
|
|
3608
|
+
}
|
|
3593
3609
|
|
|
3594
3610
|
// src/react/hooks/use-pages.ts
|
|
3595
3611
|
import { useQuery as useQuery29 } from "@tanstack/react-query";
|
|
@@ -3754,7 +3770,8 @@ function useShopScripts(options) {
|
|
|
3754
3770
|
return useQuery33({
|
|
3755
3771
|
queryKey: ["behio", "shop-scripts"],
|
|
3756
3772
|
queryFn: () => unwrap(client.getShopScripts()),
|
|
3757
|
-
enabled: options?.enabled !== false
|
|
3773
|
+
enabled: options?.enabled !== false,
|
|
3774
|
+
initialData: options?.initialData
|
|
3758
3775
|
});
|
|
3759
3776
|
}
|
|
3760
3777
|
|
|
@@ -3823,7 +3840,7 @@ function CurrencySwitcher({
|
|
|
3823
3840
|
}
|
|
3824
3841
|
|
|
3825
3842
|
// src/react/components/storefront-scripts.tsx
|
|
3826
|
-
import { useEffect as useEffect6, useMemo as useMemo5, useState as useState10 } from "react";
|
|
3843
|
+
import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef4, useState as useState10 } from "react";
|
|
3827
3844
|
|
|
3828
3845
|
// src/react/hooks/use-consent.ts
|
|
3829
3846
|
import { useQuery as useQuery35, useMutation as useMutation16, useQueryClient as useQueryClient16 } from "@tanstack/react-query";
|
|
@@ -3888,31 +3905,32 @@ function partsFor(s) {
|
|
|
3888
3905
|
return [{ target: "head", html: `<meta name="google-site-verification" content="${id}" />` }];
|
|
3889
3906
|
case "RAW":
|
|
3890
3907
|
default:
|
|
3891
|
-
return [{ target: s.placement === "HEAD" ? "head" : "body", html: s.value }];
|
|
3908
|
+
return [{ target: s.placement === "HEAD" ? "head" : "body", start: s.placement === "BODY_START", html: s.value }];
|
|
3892
3909
|
}
|
|
3893
3910
|
}
|
|
3894
|
-
function injectHtml(target, html) {
|
|
3911
|
+
function injectHtml(target, html, start = false) {
|
|
3895
3912
|
const tpl = document.createElement("template");
|
|
3896
3913
|
tpl.innerHTML = html;
|
|
3897
3914
|
const added = [];
|
|
3915
|
+
const anchor = start ? target.firstChild : null;
|
|
3898
3916
|
Array.from(tpl.content.childNodes).forEach((node) => {
|
|
3899
3917
|
if (node.nodeName === "SCRIPT") {
|
|
3900
3918
|
const old = node;
|
|
3901
3919
|
const script = document.createElement("script");
|
|
3902
3920
|
for (const attr of Array.from(old.attributes)) script.setAttribute(attr.name, attr.value);
|
|
3903
3921
|
script.text = old.text;
|
|
3904
|
-
target.
|
|
3922
|
+
target.insertBefore(script, anchor);
|
|
3905
3923
|
added.push(script);
|
|
3906
3924
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
3907
3925
|
const clone = node.cloneNode(true);
|
|
3908
|
-
target.
|
|
3926
|
+
target.insertBefore(clone, anchor);
|
|
3909
3927
|
added.push(clone);
|
|
3910
3928
|
}
|
|
3911
3929
|
});
|
|
3912
3930
|
return added;
|
|
3913
3931
|
}
|
|
3914
|
-
function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
|
|
3915
|
-
const { data } = useShopScripts();
|
|
3932
|
+
function StorefrontScripts({ visitorId: visitorIdProp, initialScripts } = {}) {
|
|
3933
|
+
const { data } = useShopScripts({ enabled: initialScripts === void 0, initialData: initialScripts ? { scripts: initialScripts } : void 0 });
|
|
3916
3934
|
const [visitorId, setVisitorId] = useState10(visitorIdProp ?? "");
|
|
3917
3935
|
useEffect6(() => {
|
|
3918
3936
|
if (visitorIdProp) return;
|
|
@@ -3924,21 +3942,30 @@ function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
|
|
|
3924
3942
|
}, [visitorIdProp]);
|
|
3925
3943
|
const { data: consent } = useCookieConsent(visitorId || void 0);
|
|
3926
3944
|
const analyticsOk = Boolean(consent?.analytics);
|
|
3945
|
+
const marketingOk = Boolean(consent?.marketing);
|
|
3927
3946
|
const scripts = useMemo5(
|
|
3928
|
-
() => (data?.scripts ?? []).filter((s) => !s.consentRequired || analyticsOk),
|
|
3929
|
-
[data, analyticsOk]
|
|
3947
|
+
() => (initialScripts ?? data?.scripts ?? []).filter((s) => !s.consentRequired || (s.type === "META_PIXEL" ? marketingOk : analyticsOk)),
|
|
3948
|
+
[initialScripts, data, analyticsOk, marketingOk]
|
|
3930
3949
|
);
|
|
3931
3950
|
const signature = useMemo5(
|
|
3932
3951
|
() => JSON.stringify(scripts.map((s) => [s.id, s.type, s.placement, s.value])),
|
|
3933
3952
|
[scripts]
|
|
3934
3953
|
);
|
|
3954
|
+
const activeConsented = useRef4(/* @__PURE__ */ new Set());
|
|
3935
3955
|
useEffect6(() => {
|
|
3936
3956
|
if (typeof document === "undefined") return;
|
|
3957
|
+
const nextConsented = new Set(scripts.filter((s) => s.consentRequired).map((s) => s.id));
|
|
3958
|
+
const revoked = [...activeConsented.current].some((id) => !nextConsented.has(id));
|
|
3959
|
+
activeConsented.current = nextConsented;
|
|
3960
|
+
if (revoked) {
|
|
3961
|
+
window.location.reload();
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3937
3964
|
const added = [];
|
|
3938
3965
|
for (const s of scripts) {
|
|
3939
3966
|
for (const part of partsFor(s)) {
|
|
3940
3967
|
const target = part.target === "head" ? document.head : document.body;
|
|
3941
|
-
added.push(...injectHtml(target, part.html));
|
|
3968
|
+
added.push(...injectHtml(target, part.html, part.start));
|
|
3942
3969
|
}
|
|
3943
3970
|
}
|
|
3944
3971
|
return () => {
|
|
@@ -4200,20 +4227,22 @@ function utmFromSearch(search) {
|
|
|
4200
4227
|
// src/react/hooks/use-bundles.ts
|
|
4201
4228
|
import { useQuery as useQuery36 } from "@tanstack/react-query";
|
|
4202
4229
|
function useBundles(options) {
|
|
4203
|
-
const { client } = useBehio();
|
|
4230
|
+
const { client, sessionRestored } = useBehio();
|
|
4231
|
+
const query = { locale: options?.locale ?? client.getLocale(), currency: options?.currency ?? client.getCurrency(), country: options?.country ?? client.getCountry() };
|
|
4204
4232
|
return useQuery36({
|
|
4205
|
-
queryKey: ["behio", "bundles"],
|
|
4206
|
-
queryFn: () => unwrap(client.catalog.getBundles()),
|
|
4207
|
-
enabled: options?.enabled ?? true,
|
|
4233
|
+
queryKey: ["behio", "bundles", query],
|
|
4234
|
+
queryFn: () => unwrap(client.catalog.getBundles(query)),
|
|
4235
|
+
enabled: sessionRestored && (options?.enabled ?? true),
|
|
4208
4236
|
initialData: options?.initialData
|
|
4209
4237
|
});
|
|
4210
4238
|
}
|
|
4211
4239
|
function useBundle(slug, options) {
|
|
4212
|
-
const { client } = useBehio();
|
|
4240
|
+
const { client, sessionRestored } = useBehio();
|
|
4241
|
+
const query = { locale: options?.locale ?? client.getLocale(), currency: options?.currency ?? client.getCurrency(), country: options?.country ?? client.getCountry() };
|
|
4213
4242
|
return useQuery36({
|
|
4214
|
-
queryKey: ["behio", "bundle", slug],
|
|
4215
|
-
queryFn: () => unwrap(client.catalog.getBundle(slug)),
|
|
4216
|
-
enabled: Boolean(slug) && (options?.enabled ?? true),
|
|
4243
|
+
queryKey: ["behio", "bundle", slug, query],
|
|
4244
|
+
queryFn: () => unwrap(client.catalog.getBundle(slug, query)),
|
|
4245
|
+
enabled: sessionRestored && Boolean(slug) && (options?.enabled ?? true),
|
|
4217
4246
|
initialData: options?.initialData
|
|
4218
4247
|
});
|
|
4219
4248
|
}
|
|
@@ -4316,8 +4345,10 @@ import { useQuery as useQuery42, useMutation as useMutation18, useQueryClient as
|
|
|
4316
4345
|
function useProductReviews(productId, options) {
|
|
4317
4346
|
const { client } = useBehio();
|
|
4318
4347
|
return useQuery42({
|
|
4319
|
-
queryKey: ["behio", "reviews", productId, options?.page ?? 1],
|
|
4348
|
+
queryKey: ["behio", "reviews", productId, options?.page ?? 1, options?.limit ?? 20],
|
|
4320
4349
|
queryFn: () => unwrap(client.reviews.getProductReviews(productId, options?.page, options?.limit)),
|
|
4350
|
+
initialData: options?.initialData,
|
|
4351
|
+
staleTime: 3e4,
|
|
4321
4352
|
enabled: Boolean(productId) && (options?.enabled ?? true)
|
|
4322
4353
|
});
|
|
4323
4354
|
}
|
|
@@ -4380,15 +4411,23 @@ function useNotifyWhenAvailable() {
|
|
|
4380
4411
|
}
|
|
4381
4412
|
|
|
4382
4413
|
// src/react/utils/format-price.ts
|
|
4383
|
-
function formatPrice(amount, currency, locale) {
|
|
4414
|
+
function formatPrice(amount, currency, locale, display) {
|
|
4384
4415
|
const resolvedLocale = locale ?? "cs";
|
|
4385
4416
|
try {
|
|
4386
|
-
|
|
4417
|
+
const formatter = new Intl.NumberFormat(resolvedLocale, {
|
|
4387
4418
|
style: "currency",
|
|
4388
4419
|
currency,
|
|
4420
|
+
currencyDisplay: display === "CODE" ? "code" : "symbol",
|
|
4389
4421
|
minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
|
|
4390
4422
|
maximumFractionDigits: 2
|
|
4391
|
-
})
|
|
4423
|
+
});
|
|
4424
|
+
if (display === "SYMBOL_BEFORE" || display === "SYMBOL_AFTER") {
|
|
4425
|
+
const parts = formatter.formatToParts(amount);
|
|
4426
|
+
const symbol = parts.find((p) => p.type === "currency")?.value ?? currency;
|
|
4427
|
+
const value = parts.filter((p) => p.type !== "currency").map((p) => p.value).join("").trim();
|
|
4428
|
+
return display === "SYMBOL_BEFORE" ? `${symbol}\xA0${value}` : `${value}\xA0${symbol}`;
|
|
4429
|
+
}
|
|
4430
|
+
return formatter.format(amount);
|
|
4392
4431
|
} catch {
|
|
4393
4432
|
return `${amount} ${currency}`;
|
|
4394
4433
|
}
|
|
@@ -4521,6 +4560,7 @@ export {
|
|
|
4521
4560
|
useCategory,
|
|
4522
4561
|
useCertificateVerification,
|
|
4523
4562
|
useCheckout,
|
|
4563
|
+
useCheckoutPreview,
|
|
4524
4564
|
useCookieConsent,
|
|
4525
4565
|
useCourse,
|
|
4526
4566
|
useCourseCertificates,
|