@behio/storefront-sdk 2.8.0 → 2.10.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/README.md +33 -0
- package/dist/{chunk-JCHJ2HW4.js → chunk-AOUXSMSR.js} +22 -2
- package/dist/{chunk-WQVG4HW7.mjs → chunk-AUH7KNL6.mjs} +22 -2
- package/dist/{client-t58lBodJ.d.mts → client-GId6Qj64.d.mts} +85 -2
- package/dist/{client-t58lBodJ.d.ts → client-GId6Qj64.d.ts} +85 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +83 -14
- package/dist/index.mjs +81 -12
- 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 +92 -1
- package/dist/react.d.ts +92 -1
- package/dist/react.js +83 -14
- package/dist/react.mjs +96 -27
- package/package.json +1 -1
package/dist/react.d.ts
CHANGED
|
@@ -228,6 +228,8 @@ interface CheckoutSettings {
|
|
|
228
228
|
type PriceDisplay = "INCL_VAT" | "EXCL_VAT" | "CUSTOMER_CHOICE";
|
|
229
229
|
type CurrencyDisplayFormat = "SYMBOL_BEFORE" | "SYMBOL_AFTER" | "CODE";
|
|
230
230
|
interface ShopInfo {
|
|
231
|
+
/** Merchant default catalog order, normalized to the public sort values. */
|
|
232
|
+
defaultProductSort?: ProductSortValue;
|
|
231
233
|
currencyDisplayFormat?: CurrencyDisplayFormat;
|
|
232
234
|
account?: {
|
|
233
235
|
passwordMinLength: number;
|
|
@@ -561,6 +563,8 @@ interface ProductLabel {
|
|
|
561
563
|
slug: string;
|
|
562
564
|
name: string;
|
|
563
565
|
color?: string;
|
|
566
|
+
/** Merchant icon name. Render a trusted local icon, with a safe fallback. */
|
|
567
|
+
iconName?: string | null;
|
|
564
568
|
}
|
|
565
569
|
/**
|
|
566
570
|
* Resolved availability status of a product. Either a merchant preset
|
|
@@ -1128,6 +1132,7 @@ interface FacetLabel {
|
|
|
1128
1132
|
slug: string;
|
|
1129
1133
|
name: string;
|
|
1130
1134
|
color?: string | null;
|
|
1135
|
+
iconName?: string | null;
|
|
1131
1136
|
count: number;
|
|
1132
1137
|
}
|
|
1133
1138
|
interface FacetCategory {
|
|
@@ -2542,6 +2547,7 @@ interface PickupPointsInput {
|
|
|
2542
2547
|
}
|
|
2543
2548
|
/** Public terms of the eshop's loyalty program. */
|
|
2544
2549
|
interface LoyaltyProgram {
|
|
2550
|
+
enrollmentType?: "AUTO_ON_REGISTER" | "OPT_IN" | "INVITE_ONLY";
|
|
2545
2551
|
name: string;
|
|
2546
2552
|
pointName: string;
|
|
2547
2553
|
pointNamePlural: string;
|
|
@@ -2556,6 +2562,8 @@ interface LoyaltyProgram {
|
|
|
2556
2562
|
}
|
|
2557
2563
|
interface LoyaltyBalance {
|
|
2558
2564
|
currentPoints: number;
|
|
2565
|
+
/** Revoked points to offset before new rewards become spendable. Absent on older servers. */
|
|
2566
|
+
pointsDebt?: number;
|
|
2559
2567
|
/** `currentPoints` valued in the program currency. */
|
|
2560
2568
|
currentPointsValue: number;
|
|
2561
2569
|
lifetimePointsEarned: number;
|
|
@@ -2587,10 +2595,13 @@ interface LoyaltyNextTier {
|
|
|
2587
2595
|
progressPercent: number;
|
|
2588
2596
|
}
|
|
2589
2597
|
interface LoyaltyTransaction {
|
|
2598
|
+
/** Stable identifier on servers supporting cursor history. */
|
|
2599
|
+
id?: string;
|
|
2590
2600
|
type: string;
|
|
2591
2601
|
/** Positive = earned, negative = redeemed / expired. */
|
|
2592
2602
|
points: number;
|
|
2593
2603
|
balanceAfter: number;
|
|
2604
|
+
debtAfter?: number;
|
|
2594
2605
|
eventType: string | null;
|
|
2595
2606
|
description: string | null;
|
|
2596
2607
|
createdAt: number;
|
|
@@ -2602,6 +2613,7 @@ interface LoyaltyTransaction {
|
|
|
2602
2613
|
* "join and earn" CTA).
|
|
2603
2614
|
*/
|
|
2604
2615
|
interface LoyaltySummary {
|
|
2616
|
+
canJoin?: boolean;
|
|
2605
2617
|
hasProgram: boolean;
|
|
2606
2618
|
enrolled: boolean;
|
|
2607
2619
|
program: LoyaltyProgram | null;
|
|
@@ -2610,6 +2622,18 @@ interface LoyaltySummary {
|
|
|
2610
2622
|
nextTier: LoyaltyNextTier | null;
|
|
2611
2623
|
referralCode: string | null;
|
|
2612
2624
|
transactions: LoyaltyTransaction[];
|
|
2625
|
+
/** Follow using getLoyalty({transactionsCursor}); null is the last page. */
|
|
2626
|
+
transactionsNextCursor?: string | null;
|
|
2627
|
+
canApplyReferral?: boolean;
|
|
2628
|
+
}
|
|
2629
|
+
interface LoyaltyReferralResult {
|
|
2630
|
+
alreadyApplied: boolean;
|
|
2631
|
+
recipientPoints: number;
|
|
2632
|
+
}
|
|
2633
|
+
interface LoyaltyQuery {
|
|
2634
|
+
transactionsCursor?: string;
|
|
2635
|
+
/** 1 to 50, default 20. */
|
|
2636
|
+
transactionsLimit?: number;
|
|
2613
2637
|
}
|
|
2614
2638
|
interface CrossSellItem {
|
|
2615
2639
|
productId: string;
|
|
@@ -2938,6 +2962,55 @@ interface CartLinkClaimed extends Cart {
|
|
|
2938
2962
|
/** Code carried by the link, including a refused code. Null means no code was offered. */
|
|
2939
2963
|
linkDiscountCode: string | null;
|
|
2940
2964
|
}
|
|
2965
|
+
/** Earned merchant badges for the current authenticated customer. */
|
|
2966
|
+
interface CustomerBadge {
|
|
2967
|
+
id: string;
|
|
2968
|
+
name: string;
|
|
2969
|
+
slug: string;
|
|
2970
|
+
description: string | null;
|
|
2971
|
+
iconUrl: string | null;
|
|
2972
|
+
type: "MILESTONE" | "CHALLENGE" | "STREAK" | "ACHIEVEMENT" | "CUSTOM";
|
|
2973
|
+
earnedAt: number;
|
|
2974
|
+
}
|
|
2975
|
+
interface CustomerChallenge {
|
|
2976
|
+
id: string;
|
|
2977
|
+
name: string;
|
|
2978
|
+
slug: string;
|
|
2979
|
+
description: string | null;
|
|
2980
|
+
targetType: string;
|
|
2981
|
+
targetValue: number;
|
|
2982
|
+
/** TOTAL_SPEND uses the shop's default currency and stored reference FX rates. */
|
|
2983
|
+
currency: string;
|
|
2984
|
+
startsAt: number;
|
|
2985
|
+
endsAt: number;
|
|
2986
|
+
rewardPoints: number | null;
|
|
2987
|
+
rewardBadgeName: string | null;
|
|
2988
|
+
active: boolean;
|
|
2989
|
+
joined: boolean;
|
|
2990
|
+
currentProgress: number;
|
|
2991
|
+
completedAt: number | null;
|
|
2992
|
+
canJoin: boolean;
|
|
2993
|
+
rewardMembershipRequired: boolean;
|
|
2994
|
+
}
|
|
2995
|
+
interface CustomerGamificationQuery {
|
|
2996
|
+
badgesPage?: number;
|
|
2997
|
+
challengesPage?: number;
|
|
2998
|
+
/** 1..50, default 20. */
|
|
2999
|
+
limit?: number;
|
|
3000
|
+
}
|
|
3001
|
+
interface CustomerGamification {
|
|
3002
|
+
badges: CustomerBadge[];
|
|
3003
|
+
badgesTotal: number;
|
|
3004
|
+
badgesPage: number;
|
|
3005
|
+
challenges: CustomerChallenge[];
|
|
3006
|
+
challengesTotal: number;
|
|
3007
|
+
challengesPage: number;
|
|
3008
|
+
limit: number;
|
|
3009
|
+
}
|
|
3010
|
+
interface GamificationRefreshResult {
|
|
3011
|
+
awarded: number;
|
|
3012
|
+
completed: number;
|
|
3013
|
+
}
|
|
2941
3014
|
|
|
2942
3015
|
declare class BehioStorefront {
|
|
2943
3016
|
private baseUrl;
|
|
@@ -3561,7 +3634,17 @@ declare class CustomerModule {
|
|
|
3561
3634
|
* value, current tier, next-tier progress, point ratio, referral code,
|
|
3562
3635
|
* recent transactions). Requires an authenticated customer session.
|
|
3563
3636
|
*/
|
|
3564
|
-
getLoyalty(): Promise<SdkResult<LoyaltySummary>>;
|
|
3637
|
+
getLoyalty(query?: LoyaltyQuery): Promise<SdkResult<LoyaltySummary>>;
|
|
3638
|
+
/** Apply an invitation to the caller's active loyalty membership, once. */
|
|
3639
|
+
applyReferral(code: string): Promise<SdkResult<LoyaltyReferralResult>>;
|
|
3640
|
+
/** Explicit enrollment; invite-only programs require the shop administrator. */
|
|
3641
|
+
joinLoyalty(): Promise<SdkResult<LoyaltySummary>>;
|
|
3642
|
+
/** Authenticated account rewards. Both collections have independent pages. */
|
|
3643
|
+
getGamification(query?: CustomerGamificationQuery): Promise<SdkResult<CustomerGamification>>;
|
|
3644
|
+
/** Join an active challenge; a repeated call preserves the original joinedAt. */
|
|
3645
|
+
joinChallenge(challengeId: string): Promise<SdkResult<GamificationRefreshResult>>;
|
|
3646
|
+
/** Reconcile persisted qualifying activity. Never supplies progress or points. */
|
|
3647
|
+
refreshGamification(): Promise<SdkResult<GamificationRefreshResult>>;
|
|
3565
3648
|
/**
|
|
3566
3649
|
* Digital product delivery (GAP-07): list the logged-in customer's download
|
|
3567
3650
|
* grants across all their orders (file name, product, remaining downloads,
|
|
@@ -4070,6 +4153,13 @@ declare function useCart(options?: UseCartOptions): {
|
|
|
4070
4153
|
updateQuantity: (itemId: string, quantity: number) => Promise<Cart>;
|
|
4071
4154
|
removeItem: (itemId: string) => Promise<Cart>;
|
|
4072
4155
|
clear: () => Promise<void>;
|
|
4156
|
+
addBundle: (bundle: string | {
|
|
4157
|
+
id: string;
|
|
4158
|
+
} | {
|
|
4159
|
+
slug: string;
|
|
4160
|
+
}, quantity?: number) => Promise<Cart>;
|
|
4161
|
+
updateBundleQuantity: (lineId: string, quantity: number) => Promise<Cart>;
|
|
4162
|
+
removeBundle: (lineId: string) => Promise<Cart>;
|
|
4073
4163
|
applyDiscount: (code: string) => Promise<Cart>;
|
|
4074
4164
|
removeDiscount: () => Promise<Cart>;
|
|
4075
4165
|
merge: () => Promise<Cart>;
|
|
@@ -4078,6 +4168,7 @@ declare function useCart(options?: UseCartOptions): {
|
|
|
4078
4168
|
isAdding: boolean;
|
|
4079
4169
|
isUpdating: boolean;
|
|
4080
4170
|
isRemoving: boolean;
|
|
4171
|
+
isUpdatingBundle: boolean;
|
|
4081
4172
|
};
|
|
4082
4173
|
|
|
4083
4174
|
interface UseCartCountOptions {
|
package/dist/react.js
CHANGED
|
@@ -1655,8 +1655,28 @@ var CustomerModule = class {
|
|
|
1655
1655
|
* value, current tier, next-tier progress, point ratio, referral code,
|
|
1656
1656
|
* recent transactions). Requires an authenticated customer session.
|
|
1657
1657
|
*/
|
|
1658
|
-
async getLoyalty() {
|
|
1659
|
-
return this.client.request("GET", "/customer/loyalty");
|
|
1658
|
+
async getLoyalty(query = {}) {
|
|
1659
|
+
return this.client.request("GET", "/customer/loyalty", { query: { ...query } });
|
|
1660
|
+
}
|
|
1661
|
+
/** Apply an invitation to the caller's active loyalty membership, once. */
|
|
1662
|
+
async applyReferral(code) {
|
|
1663
|
+
return this.client.request("POST", "/customer/loyalty/referral", { body: { code } });
|
|
1664
|
+
}
|
|
1665
|
+
/** Explicit enrollment; invite-only programs require the shop administrator. */
|
|
1666
|
+
async joinLoyalty() {
|
|
1667
|
+
return this.client.request("POST", "/customer/loyalty/join", { body: { consent: true } });
|
|
1668
|
+
}
|
|
1669
|
+
/** Authenticated account rewards. Both collections have independent pages. */
|
|
1670
|
+
async getGamification(query = {}) {
|
|
1671
|
+
return this.client.request("GET", "/customer/gamification", { query: { ...query } });
|
|
1672
|
+
}
|
|
1673
|
+
/** Join an active challenge; a repeated call preserves the original joinedAt. */
|
|
1674
|
+
async joinChallenge(challengeId) {
|
|
1675
|
+
return this.client.request("POST", `/customer/gamification/challenges/${encodeURIComponent(challengeId)}/join`);
|
|
1676
|
+
}
|
|
1677
|
+
/** Reconcile persisted qualifying activity. Never supplies progress or points. */
|
|
1678
|
+
async refreshGamification() {
|
|
1679
|
+
return this.client.request("POST", "/customer/gamification/refresh");
|
|
1660
1680
|
}
|
|
1661
1681
|
/**
|
|
1662
1682
|
* Digital product delivery (GAP-07): list the logged-in customer's download
|
|
@@ -2779,6 +2799,16 @@ function useCart(options) {
|
|
|
2779
2799
|
queryClient.setQueryData([...CART_KEY], result);
|
|
2780
2800
|
void refreshBundleQueries(queryClient);
|
|
2781
2801
|
};
|
|
2802
|
+
(0, import_react5.useEffect)(
|
|
2803
|
+
() => client.on("cart:updated", (data) => {
|
|
2804
|
+
if (!data || typeof data !== "object" || !("items" in data)) return;
|
|
2805
|
+
const session = client.getCartSession();
|
|
2806
|
+
if (session) storage.set(STORAGE_KEYS.CART_SESSION, session);
|
|
2807
|
+
void acceptCart(data);
|
|
2808
|
+
}),
|
|
2809
|
+
// acceptCart only closes over queryClient, which is stable per provider.
|
|
2810
|
+
[client, queryClient, storage]
|
|
2811
|
+
);
|
|
2782
2812
|
const addMutation = (0, import_react_query12.useMutation)({
|
|
2783
2813
|
mutationFn: async ({ productId, quantity }) => {
|
|
2784
2814
|
return unwrap(client.cart.addItem({ productId, quantity: quantity ?? 1 }));
|
|
@@ -2811,6 +2841,26 @@ function useCart(options) {
|
|
|
2811
2841
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
2812
2842
|
}
|
|
2813
2843
|
});
|
|
2844
|
+
const acceptBundleCart = async (result) => {
|
|
2845
|
+
const session = result.newSessionToken ?? result.sessionToken;
|
|
2846
|
+
if (session) storage.set(STORAGE_KEYS.CART_SESSION, session);
|
|
2847
|
+
await acceptCart(result);
|
|
2848
|
+
};
|
|
2849
|
+
const addBundleMutation = (0, import_react_query12.useMutation)({
|
|
2850
|
+
mutationFn: async ({ bundle, quantity }) => unwrap(client.cart.addBundle(bundle, quantity ?? 1)),
|
|
2851
|
+
onMutate: cancelCartRead,
|
|
2852
|
+
onSuccess: acceptBundleCart
|
|
2853
|
+
});
|
|
2854
|
+
const updateBundleMutation = (0, import_react_query12.useMutation)({
|
|
2855
|
+
mutationFn: async ({ lineId, quantity }) => unwrap(client.cart.updateBundleQuantity(lineId, quantity)),
|
|
2856
|
+
onMutate: cancelCartRead,
|
|
2857
|
+
onSuccess: acceptBundleCart
|
|
2858
|
+
});
|
|
2859
|
+
const removeBundleMutation = (0, import_react_query12.useMutation)({
|
|
2860
|
+
mutationFn: async (lineId) => unwrap(client.cart.removeBundle(lineId)),
|
|
2861
|
+
onMutate: cancelCartRead,
|
|
2862
|
+
onSuccess: acceptBundleCart
|
|
2863
|
+
});
|
|
2814
2864
|
const clearMutation = (0, import_react_query12.useMutation)({
|
|
2815
2865
|
mutationFn: () => unwrap(client.cart.clear()),
|
|
2816
2866
|
onMutate: cancelCartRead,
|
|
@@ -2859,6 +2909,18 @@ function useCart(options) {
|
|
|
2859
2909
|
() => clearMutation.mutateAsync(),
|
|
2860
2910
|
[clearMutation]
|
|
2861
2911
|
);
|
|
2912
|
+
const addBundle = (0, import_react5.useCallback)(
|
|
2913
|
+
(bundle, quantity) => addBundleMutation.mutateAsync({ bundle, quantity }),
|
|
2914
|
+
[addBundleMutation]
|
|
2915
|
+
);
|
|
2916
|
+
const updateBundleQuantity = (0, import_react5.useCallback)(
|
|
2917
|
+
(lineId, quantity) => updateBundleMutation.mutateAsync({ lineId, quantity }),
|
|
2918
|
+
[updateBundleMutation]
|
|
2919
|
+
);
|
|
2920
|
+
const removeBundle = (0, import_react5.useCallback)(
|
|
2921
|
+
(lineId) => removeBundleMutation.mutateAsync(lineId),
|
|
2922
|
+
[removeBundleMutation]
|
|
2923
|
+
);
|
|
2862
2924
|
const applyDiscount = (0, import_react5.useCallback)(
|
|
2863
2925
|
(code) => applyDiscountMutation.mutateAsync(code),
|
|
2864
2926
|
[applyDiscountMutation]
|
|
@@ -2880,6 +2942,9 @@ function useCart(options) {
|
|
|
2880
2942
|
updateQuantity,
|
|
2881
2943
|
removeItem,
|
|
2882
2944
|
clear,
|
|
2945
|
+
addBundle,
|
|
2946
|
+
updateBundleQuantity,
|
|
2947
|
+
removeBundle,
|
|
2883
2948
|
applyDiscount,
|
|
2884
2949
|
removeDiscount,
|
|
2885
2950
|
merge,
|
|
@@ -2889,7 +2954,8 @@ function useCart(options) {
|
|
|
2889
2954
|
// Mutation states
|
|
2890
2955
|
isAdding: addMutation.isPending,
|
|
2891
2956
|
isUpdating: updateMutation.isPending,
|
|
2892
|
-
isRemoving: removeMutation.isPending
|
|
2957
|
+
isRemoving: removeMutation.isPending,
|
|
2958
|
+
isUpdatingBundle: addBundleMutation.isPending || updateBundleMutation.isPending || removeBundleMutation.isPending
|
|
2893
2959
|
};
|
|
2894
2960
|
}
|
|
2895
2961
|
|
|
@@ -4019,16 +4085,19 @@ function errorParams(err) {
|
|
|
4019
4085
|
const params = body?.params;
|
|
4020
4086
|
return params && typeof params === "object" && !Array.isArray(params) ? params : {};
|
|
4021
4087
|
}
|
|
4022
|
-
var FORM_FIELD_ERROR_CODES =
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4088
|
+
var FORM_FIELD_ERROR_CODES = {
|
|
4089
|
+
required: true,
|
|
4090
|
+
invalid: true,
|
|
4091
|
+
tooShort: true,
|
|
4092
|
+
tooLong: true,
|
|
4093
|
+
min: true,
|
|
4094
|
+
max: true,
|
|
4095
|
+
notOption: true,
|
|
4096
|
+
pattern: true,
|
|
4097
|
+
dateNotFuture: true,
|
|
4098
|
+
dateNotPast: true,
|
|
4099
|
+
tooManyFiles: true
|
|
4100
|
+
};
|
|
4032
4101
|
function formFieldErrors(err) {
|
|
4033
4102
|
if (errorCode(err) !== "be.forms.validationFailed") return [];
|
|
4034
4103
|
const raw = errorParams(err).errors;
|
|
@@ -4040,7 +4109,7 @@ function formFieldErrors(err) {
|
|
|
4040
4109
|
if (idx <= 0) continue;
|
|
4041
4110
|
const key = entry.slice(0, idx);
|
|
4042
4111
|
const code = entry.slice(idx + 1);
|
|
4043
|
-
if (!
|
|
4112
|
+
if (!Object.prototype.hasOwnProperty.call(FORM_FIELD_ERROR_CODES, code)) continue;
|
|
4044
4113
|
out.push({ key, code });
|
|
4045
4114
|
}
|
|
4046
4115
|
return out;
|
package/dist/react.mjs
CHANGED
|
@@ -1545,8 +1545,28 @@ var CustomerModule = class {
|
|
|
1545
1545
|
* value, current tier, next-tier progress, point ratio, referral code,
|
|
1546
1546
|
* recent transactions). Requires an authenticated customer session.
|
|
1547
1547
|
*/
|
|
1548
|
-
async getLoyalty() {
|
|
1549
|
-
return this.client.request("GET", "/customer/loyalty");
|
|
1548
|
+
async getLoyalty(query = {}) {
|
|
1549
|
+
return this.client.request("GET", "/customer/loyalty", { query: { ...query } });
|
|
1550
|
+
}
|
|
1551
|
+
/** Apply an invitation to the caller's active loyalty membership, once. */
|
|
1552
|
+
async applyReferral(code) {
|
|
1553
|
+
return this.client.request("POST", "/customer/loyalty/referral", { body: { code } });
|
|
1554
|
+
}
|
|
1555
|
+
/** Explicit enrollment; invite-only programs require the shop administrator. */
|
|
1556
|
+
async joinLoyalty() {
|
|
1557
|
+
return this.client.request("POST", "/customer/loyalty/join", { body: { consent: true } });
|
|
1558
|
+
}
|
|
1559
|
+
/** Authenticated account rewards. Both collections have independent pages. */
|
|
1560
|
+
async getGamification(query = {}) {
|
|
1561
|
+
return this.client.request("GET", "/customer/gamification", { query: { ...query } });
|
|
1562
|
+
}
|
|
1563
|
+
/** Join an active challenge; a repeated call preserves the original joinedAt. */
|
|
1564
|
+
async joinChallenge(challengeId) {
|
|
1565
|
+
return this.client.request("POST", `/customer/gamification/challenges/${encodeURIComponent(challengeId)}/join`);
|
|
1566
|
+
}
|
|
1567
|
+
/** Reconcile persisted qualifying activity. Never supplies progress or points. */
|
|
1568
|
+
async refreshGamification() {
|
|
1569
|
+
return this.client.request("POST", "/customer/gamification/refresh");
|
|
1550
1570
|
}
|
|
1551
1571
|
/**
|
|
1552
1572
|
* Digital product delivery (GAP-07): list the logged-in customer's download
|
|
@@ -2638,7 +2658,7 @@ function useSearch(query, options) {
|
|
|
2638
2658
|
}
|
|
2639
2659
|
|
|
2640
2660
|
// src/react/hooks/use-cart.ts
|
|
2641
|
-
import { useCallback as useCallback3 } from "react";
|
|
2661
|
+
import { useCallback as useCallback3, useEffect as useEffect3 } from "react";
|
|
2642
2662
|
import { useQuery as useQuery10, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2643
2663
|
|
|
2644
2664
|
// src/react/utils/refresh-bundle-queries.ts
|
|
@@ -2669,6 +2689,16 @@ function useCart(options) {
|
|
|
2669
2689
|
queryClient.setQueryData([...CART_KEY], result);
|
|
2670
2690
|
void refreshBundleQueries(queryClient);
|
|
2671
2691
|
};
|
|
2692
|
+
useEffect3(
|
|
2693
|
+
() => client.on("cart:updated", (data) => {
|
|
2694
|
+
if (!data || typeof data !== "object" || !("items" in data)) return;
|
|
2695
|
+
const session = client.getCartSession();
|
|
2696
|
+
if (session) storage.set(STORAGE_KEYS.CART_SESSION, session);
|
|
2697
|
+
void acceptCart(data);
|
|
2698
|
+
}),
|
|
2699
|
+
// acceptCart only closes over queryClient, which is stable per provider.
|
|
2700
|
+
[client, queryClient, storage]
|
|
2701
|
+
);
|
|
2672
2702
|
const addMutation = useMutation({
|
|
2673
2703
|
mutationFn: async ({ productId, quantity }) => {
|
|
2674
2704
|
return unwrap(client.cart.addItem({ productId, quantity: quantity ?? 1 }));
|
|
@@ -2701,6 +2731,26 @@ function useCart(options) {
|
|
|
2701
2731
|
queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
|
|
2702
2732
|
}
|
|
2703
2733
|
});
|
|
2734
|
+
const acceptBundleCart = async (result) => {
|
|
2735
|
+
const session = result.newSessionToken ?? result.sessionToken;
|
|
2736
|
+
if (session) storage.set(STORAGE_KEYS.CART_SESSION, session);
|
|
2737
|
+
await acceptCart(result);
|
|
2738
|
+
};
|
|
2739
|
+
const addBundleMutation = useMutation({
|
|
2740
|
+
mutationFn: async ({ bundle, quantity }) => unwrap(client.cart.addBundle(bundle, quantity ?? 1)),
|
|
2741
|
+
onMutate: cancelCartRead,
|
|
2742
|
+
onSuccess: acceptBundleCart
|
|
2743
|
+
});
|
|
2744
|
+
const updateBundleMutation = useMutation({
|
|
2745
|
+
mutationFn: async ({ lineId, quantity }) => unwrap(client.cart.updateBundleQuantity(lineId, quantity)),
|
|
2746
|
+
onMutate: cancelCartRead,
|
|
2747
|
+
onSuccess: acceptBundleCart
|
|
2748
|
+
});
|
|
2749
|
+
const removeBundleMutation = useMutation({
|
|
2750
|
+
mutationFn: async (lineId) => unwrap(client.cart.removeBundle(lineId)),
|
|
2751
|
+
onMutate: cancelCartRead,
|
|
2752
|
+
onSuccess: acceptBundleCart
|
|
2753
|
+
});
|
|
2704
2754
|
const clearMutation = useMutation({
|
|
2705
2755
|
mutationFn: () => unwrap(client.cart.clear()),
|
|
2706
2756
|
onMutate: cancelCartRead,
|
|
@@ -2749,6 +2799,18 @@ function useCart(options) {
|
|
|
2749
2799
|
() => clearMutation.mutateAsync(),
|
|
2750
2800
|
[clearMutation]
|
|
2751
2801
|
);
|
|
2802
|
+
const addBundle = useCallback3(
|
|
2803
|
+
(bundle, quantity) => addBundleMutation.mutateAsync({ bundle, quantity }),
|
|
2804
|
+
[addBundleMutation]
|
|
2805
|
+
);
|
|
2806
|
+
const updateBundleQuantity = useCallback3(
|
|
2807
|
+
(lineId, quantity) => updateBundleMutation.mutateAsync({ lineId, quantity }),
|
|
2808
|
+
[updateBundleMutation]
|
|
2809
|
+
);
|
|
2810
|
+
const removeBundle = useCallback3(
|
|
2811
|
+
(lineId) => removeBundleMutation.mutateAsync(lineId),
|
|
2812
|
+
[removeBundleMutation]
|
|
2813
|
+
);
|
|
2752
2814
|
const applyDiscount = useCallback3(
|
|
2753
2815
|
(code) => applyDiscountMutation.mutateAsync(code),
|
|
2754
2816
|
[applyDiscountMutation]
|
|
@@ -2770,6 +2832,9 @@ function useCart(options) {
|
|
|
2770
2832
|
updateQuantity,
|
|
2771
2833
|
removeItem,
|
|
2772
2834
|
clear,
|
|
2835
|
+
addBundle,
|
|
2836
|
+
updateBundleQuantity,
|
|
2837
|
+
removeBundle,
|
|
2773
2838
|
applyDiscount,
|
|
2774
2839
|
removeDiscount,
|
|
2775
2840
|
merge,
|
|
@@ -2779,7 +2844,8 @@ function useCart(options) {
|
|
|
2779
2844
|
// Mutation states
|
|
2780
2845
|
isAdding: addMutation.isPending,
|
|
2781
2846
|
isUpdating: updateMutation.isPending,
|
|
2782
|
-
isRemoving: removeMutation.isPending
|
|
2847
|
+
isRemoving: removeMutation.isPending,
|
|
2848
|
+
isUpdatingBundle: addBundleMutation.isPending || updateBundleMutation.isPending || removeBundleMutation.isPending
|
|
2783
2849
|
};
|
|
2784
2850
|
}
|
|
2785
2851
|
|
|
@@ -3044,7 +3110,7 @@ function useAddresses(options) {
|
|
|
3044
3110
|
}
|
|
3045
3111
|
|
|
3046
3112
|
// src/react/hooks/use-address-autocomplete.ts
|
|
3047
|
-
import { useState as useState4, useEffect as
|
|
3113
|
+
import { useState as useState4, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback7 } from "react";
|
|
3048
3114
|
import { useQuery as useQuery15 } from "@tanstack/react-query";
|
|
3049
3115
|
var AC_KEY = ["behio", "address-autocomplete"];
|
|
3050
3116
|
function useAddressAutocomplete(options) {
|
|
@@ -3055,7 +3121,7 @@ function useAddressAutocomplete(options) {
|
|
|
3055
3121
|
const [selected, setSelected] = useState4(null);
|
|
3056
3122
|
const [isSelecting, setIsSelecting] = useState4(false);
|
|
3057
3123
|
const timerRef = useRef2(void 0);
|
|
3058
|
-
|
|
3124
|
+
useEffect4(() => {
|
|
3059
3125
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3060
3126
|
if (query.length < minChars) {
|
|
3061
3127
|
setDebouncedQuery("");
|
|
@@ -3483,14 +3549,14 @@ function usePaymentMethods(options) {
|
|
|
3483
3549
|
}
|
|
3484
3550
|
|
|
3485
3551
|
// src/react/hooks/use-personal-offers.ts
|
|
3486
|
-
import { useCallback as useCallback8, useEffect as
|
|
3552
|
+
import { useCallback as useCallback8, useEffect as useEffect5, useState as useState5 } from "react";
|
|
3487
3553
|
import { useMutation as useMutation10, useQuery as useQuery26, useQueryClient as useQueryClient12 } from "@tanstack/react-query";
|
|
3488
3554
|
function usePersonalOffers(options) {
|
|
3489
3555
|
const { client } = useBehio();
|
|
3490
3556
|
const queryClient = useQueryClient12();
|
|
3491
3557
|
const [polledVid, setPolledVid] = useState5(null);
|
|
3492
3558
|
const explicitVid = options?.visitorId;
|
|
3493
|
-
|
|
3559
|
+
useEffect5(() => {
|
|
3494
3560
|
if (explicitVid) return;
|
|
3495
3561
|
const read = () => {
|
|
3496
3562
|
const vid = client.getAnalyticsVisitorId();
|
|
@@ -3542,7 +3608,7 @@ function usePersonalOffers(options) {
|
|
|
3542
3608
|
}
|
|
3543
3609
|
|
|
3544
3610
|
// src/react/hooks/use-visitor-messages.ts
|
|
3545
|
-
import { useCallback as useCallback9, useEffect as
|
|
3611
|
+
import { useCallback as useCallback9, useEffect as useEffect6, useRef as useRef3, useState as useState6 } from "react";
|
|
3546
3612
|
import { useQuery as useQuery27, useQueryClient as useQueryClient13 } from "@tanstack/react-query";
|
|
3547
3613
|
function useVisitorMessages(options) {
|
|
3548
3614
|
const { client } = useBehio();
|
|
@@ -3550,7 +3616,7 @@ function useVisitorMessages(options) {
|
|
|
3550
3616
|
const [polledVid, setPolledVid] = useState6(null);
|
|
3551
3617
|
const seenRef = useRef3(/* @__PURE__ */ new Set());
|
|
3552
3618
|
const explicitVid = options?.visitorId;
|
|
3553
|
-
|
|
3619
|
+
useEffect6(() => {
|
|
3554
3620
|
if (explicitVid) return;
|
|
3555
3621
|
const read = () => {
|
|
3556
3622
|
const vid = client.getAnalyticsVisitorId();
|
|
@@ -3587,7 +3653,7 @@ function useVisitorMessages(options) {
|
|
|
3587
3653
|
);
|
|
3588
3654
|
const onMessage = options?.onMessage;
|
|
3589
3655
|
const dispatchDom = options?.dispatchDomEvents !== false;
|
|
3590
|
-
|
|
3656
|
+
useEffect6(() => {
|
|
3591
3657
|
for (const message of data?.items ?? []) {
|
|
3592
3658
|
if (seenRef.current.has(message.id)) continue;
|
|
3593
3659
|
seenRef.current.add(message.id);
|
|
@@ -3909,16 +3975,19 @@ function errorParams(err) {
|
|
|
3909
3975
|
const params = body?.params;
|
|
3910
3976
|
return params && typeof params === "object" && !Array.isArray(params) ? params : {};
|
|
3911
3977
|
}
|
|
3912
|
-
var FORM_FIELD_ERROR_CODES =
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3978
|
+
var FORM_FIELD_ERROR_CODES = {
|
|
3979
|
+
required: true,
|
|
3980
|
+
invalid: true,
|
|
3981
|
+
tooShort: true,
|
|
3982
|
+
tooLong: true,
|
|
3983
|
+
min: true,
|
|
3984
|
+
max: true,
|
|
3985
|
+
notOption: true,
|
|
3986
|
+
pattern: true,
|
|
3987
|
+
dateNotFuture: true,
|
|
3988
|
+
dateNotPast: true,
|
|
3989
|
+
tooManyFiles: true
|
|
3990
|
+
};
|
|
3922
3991
|
function formFieldErrors(err) {
|
|
3923
3992
|
if (errorCode(err) !== "be.forms.validationFailed") return [];
|
|
3924
3993
|
const raw = errorParams(err).errors;
|
|
@@ -3930,7 +3999,7 @@ function formFieldErrors(err) {
|
|
|
3930
3999
|
if (idx <= 0) continue;
|
|
3931
4000
|
const key = entry.slice(0, idx);
|
|
3932
4001
|
const code = entry.slice(idx + 1);
|
|
3933
|
-
if (!
|
|
4002
|
+
if (!Object.prototype.hasOwnProperty.call(FORM_FIELD_ERROR_CODES, code)) continue;
|
|
3934
4003
|
out.push({ key, code });
|
|
3935
4004
|
}
|
|
3936
4005
|
return out;
|
|
@@ -4070,7 +4139,7 @@ function CurrencySwitcher({
|
|
|
4070
4139
|
}
|
|
4071
4140
|
|
|
4072
4141
|
// src/react/components/storefront-scripts.tsx
|
|
4073
|
-
import { useEffect as
|
|
4142
|
+
import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef4, useState as useState10 } from "react";
|
|
4074
4143
|
|
|
4075
4144
|
// src/react/hooks/use-consent.ts
|
|
4076
4145
|
import { useQuery as useQuery35, useMutation as useMutation16, useQueryClient as useQueryClient16 } from "@tanstack/react-query";
|
|
@@ -4162,7 +4231,7 @@ function injectHtml(target, html, start = false) {
|
|
|
4162
4231
|
function StorefrontScripts({ visitorId: visitorIdProp, initialScripts } = {}) {
|
|
4163
4232
|
const { data } = useShopScripts({ enabled: initialScripts === void 0, initialData: initialScripts ? { scripts: initialScripts } : void 0 });
|
|
4164
4233
|
const [visitorId, setVisitorId] = useState10(visitorIdProp ?? "");
|
|
4165
|
-
|
|
4234
|
+
useEffect7(() => {
|
|
4166
4235
|
if (visitorIdProp) return;
|
|
4167
4236
|
try {
|
|
4168
4237
|
const v = localStorage.getItem(VISITOR_KEY);
|
|
@@ -4182,7 +4251,7 @@ function StorefrontScripts({ visitorId: visitorIdProp, initialScripts } = {}) {
|
|
|
4182
4251
|
[scripts]
|
|
4183
4252
|
);
|
|
4184
4253
|
const activeConsented = useRef4(/* @__PURE__ */ new Set());
|
|
4185
|
-
|
|
4254
|
+
useEffect7(() => {
|
|
4186
4255
|
if (typeof document === "undefined") return;
|
|
4187
4256
|
const nextConsented = new Set(scripts.filter((s) => s.consentRequired).map((s) => s.id));
|
|
4188
4257
|
const revoked = [...activeConsented.current].some((id) => !nextConsented.has(id));
|
|
@@ -4206,7 +4275,7 @@ function StorefrontScripts({ visitorId: visitorIdProp, initialScripts } = {}) {
|
|
|
4206
4275
|
}
|
|
4207
4276
|
|
|
4208
4277
|
// src/react/components/behio-analytics.tsx
|
|
4209
|
-
import { useEffect as
|
|
4278
|
+
import { useEffect as useEffect8 } from "react";
|
|
4210
4279
|
|
|
4211
4280
|
// src/react/hooks/use-behio-client.ts
|
|
4212
4281
|
function useBehioClient() {
|
|
@@ -4216,7 +4285,7 @@ function useBehioClient() {
|
|
|
4216
4285
|
// src/react/components/behio-analytics.tsx
|
|
4217
4286
|
function BehioAnalyticsTracker() {
|
|
4218
4287
|
const client = useBehioClient();
|
|
4219
|
-
|
|
4288
|
+
useEffect8(() => {
|
|
4220
4289
|
if (typeof window === "undefined") return;
|
|
4221
4290
|
const w = window;
|
|
4222
4291
|
if (w.__behioAnalytics) return;
|