@doswiftly/cli 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/deploy.d.ts +20 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +219 -6
- package/dist/commands/deploy.js.map +1 -1
- package/package.json +4 -4
- package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-minimal/wrangler.toml +11 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs/wrangler.toml +11 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +29 -5
- package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/addresses/page.tsx +19 -15
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/error.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +39 -34
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/page.tsx +9 -7
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/page.tsx +13 -9
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +1 -2
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/settings/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +14 -12
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +14 -10
- package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/category-products-client.tsx +4 -2
- package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/error.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +228 -184
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
- package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
- package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
- package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
- package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/error.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +11 -8
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/products-client.tsx +14 -10
- package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +21 -25
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +6 -68
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +14 -12
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +28 -18
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +27 -22
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +48 -43
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +9 -7
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +18 -15
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +8 -7
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +21 -11
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +16 -13
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +12 -9
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +37 -12
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +20 -12
- package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +23 -24
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +4 -1
- package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +5 -3
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +8 -6
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +26 -13
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +25 -27
- package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
- package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +40 -7
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +56 -70
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
- package/templates/storefront-nextjs-shadcn/generated/graphql.ts +1159 -551
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +1 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +22 -249
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +3 -3
- package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
- package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
- package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +1 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +41 -8
- package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +20 -18
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +2 -1
- package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
- package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
- package/templates/storefront-nextjs-shadcn/package.json +3 -2
- package/templates/storefront-nextjs-shadcn/proxy.ts +115 -46
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -58
- package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
- /package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/loading.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
|
@@ -7,5 +7,6 @@ export type { LoginResult, LogoutResult, TokenRenewResult } from "./use-auth";
|
|
|
7
7
|
export { useAuthSync } from "./use-auth-sync";
|
|
8
8
|
export { useCartSync } from "./use-cart-sync";
|
|
9
9
|
export { useCartActions } from "./use-cart-actions";
|
|
10
|
+
export { useCartDI } from "./use-cart-di";
|
|
10
11
|
export { useFilterParams } from "./use-filter-params";
|
|
11
12
|
export type { UseFilterParamsReturn, UseFilterParamsOptions } from "./use-filter-params";
|
|
@@ -1,261 +1,54 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useRef } from 'react';
|
|
4
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
5
4
|
import { useCartStore, useCartStoreApi } from '@/stores/cart-store';
|
|
6
|
-
import { useCartCreate, useCartLinesAdd, useCartLinesUpdate, useCartLinesRemove } from '@/lib/graphql/hooks';
|
|
7
|
-
import { queryKeys } from '@/lib/graphql/query-keys';
|
|
8
|
-
import { toast } from 'sonner';
|
|
9
|
-
import { StorefrontError } from '@doswiftly/storefront-sdk';
|
|
10
5
|
|
|
11
6
|
// Debounce delay for quantity updates (prevents rate limiting)
|
|
12
7
|
const QUANTITY_UPDATE_DEBOUNCE_MS = 500;
|
|
13
8
|
|
|
14
9
|
/**
|
|
15
|
-
* Hook for cart mutations —
|
|
10
|
+
* Hook for cart mutations — delegates orchestration to SDK cart store.
|
|
16
11
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
12
|
+
* SDK handles: cart init, DI actions, error state, callbacks.
|
|
13
|
+
* Template handles: debounce, openCart UX.
|
|
19
14
|
*
|
|
20
15
|
* @example
|
|
21
16
|
* ```typescript
|
|
22
17
|
* const { addToCart, updateQuantity, removeFromCart } = useCartActions();
|
|
23
|
-
*
|
|
24
|
-
* await addToCart({
|
|
25
|
-
* variantId: 'variant-123',
|
|
26
|
-
* productId: 'product-456',
|
|
27
|
-
* productTitle: 'T-Shirt',
|
|
28
|
-
* variantTitle: 'Large / Blue',
|
|
29
|
-
* price: { amount: '29.99', currencyCode: 'USD' },
|
|
30
|
-
* quantity: 1
|
|
31
|
-
* });
|
|
32
|
-
*
|
|
33
|
-
* // updateQuantity and removeFromCart take lineId (not variantId)
|
|
34
|
-
* updateQuantity('line-abc', 3);
|
|
35
|
-
* removeFromCart('line-abc');
|
|
18
|
+
* await addToCart('variant-123', 1);
|
|
36
19
|
* ```
|
|
37
20
|
*/
|
|
38
21
|
export function useCartActions() {
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const {
|
|
42
|
-
setCartId,
|
|
43
|
-
clearCart,
|
|
44
|
-
openCart,
|
|
45
|
-
} = useCartStore();
|
|
22
|
+
const api = useCartStoreApi();
|
|
23
|
+
const isLoading = useCartStore((s) => s.isLoading);
|
|
46
24
|
|
|
47
|
-
//
|
|
48
|
-
const createCartMutation = useCartCreate();
|
|
49
|
-
const addLinesMutation = useCartLinesAdd();
|
|
50
|
-
const updateLinesMutation = useCartLinesUpdate();
|
|
51
|
-
const removeLinesMutation = useCartLinesRemove();
|
|
52
|
-
|
|
53
|
-
// Debounce refs for quantity updates (prevents ThrottlerException on rapid clicks)
|
|
25
|
+
// Debounce refs for quantity updates
|
|
54
26
|
const updateTimeoutRef = useRef<Map<string, NodeJS.Timeout>>(new Map());
|
|
55
|
-
const pendingUpdatesRef = useRef<Map<string, { lineId: string; quantity: number }>>(new Map());
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get or create cart ID.
|
|
59
|
-
* Reads cartId from store (fresh, not stale closure) or creates a new cart.
|
|
60
|
-
*/
|
|
61
|
-
const getOrCreateCartId = useCallback(async (forceNew: boolean = false): Promise<string> => {
|
|
62
|
-
const currentCartId = cartStoreApi.getState().cartId;
|
|
63
|
-
if (currentCartId && !forceNew) {
|
|
64
|
-
return currentCartId;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const result = await createCartMutation.mutateAsync({ input: {} });
|
|
69
|
-
|
|
70
|
-
if (result.cartCreate.cart) {
|
|
71
|
-
const newCartId = result.cartCreate.cart.id;
|
|
72
|
-
setCartId(newCartId);
|
|
73
|
-
return newCartId;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (result.cartCreate.userErrors?.length > 0) {
|
|
77
|
-
throw new Error(result.cartCreate.userErrors[0].message);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
throw new Error('Failed to create cart');
|
|
81
|
-
} catch (error: unknown) {
|
|
82
|
-
console.error('Cart creation failed:', error);
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
}, [cartStoreApi, setCartId, createCartMutation]);
|
|
86
27
|
|
|
87
28
|
/**
|
|
88
|
-
*
|
|
29
|
+
* Add item to cart by variant ID, delegates to SDK store.
|
|
89
30
|
*/
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
if (error instanceof Error) {
|
|
96
|
-
const msg = error.message.toLowerCase();
|
|
97
|
-
return msg.includes('cart not found') || msg.includes('cart does not exist');
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Add item to cart (server-only).
|
|
104
|
-
*
|
|
105
|
-
* Creates cart if needed. On "cart not found", clears cartId, creates new cart, retries once.
|
|
106
|
-
* React Query cache invalidation in hooks.ts updates all useCartSync consumers.
|
|
107
|
-
*/
|
|
108
|
-
const addToCart = useCallback(async (item: {
|
|
109
|
-
variantId: string;
|
|
110
|
-
productId: string;
|
|
111
|
-
productHandle?: string;
|
|
112
|
-
productTitle: string;
|
|
113
|
-
variantTitle: string;
|
|
114
|
-
price: { amount: string; currencyCode: string };
|
|
115
|
-
image?: { url: string; altText?: string | null } | null;
|
|
116
|
-
available?: boolean;
|
|
117
|
-
quantity?: number;
|
|
118
|
-
}, _options?: { _forceNewCart?: boolean }) => {
|
|
119
|
-
const forceNewCart = _options?._forceNewCart ?? false;
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
// Cancel in-flight cart queries to prevent stale data overwriting
|
|
123
|
-
await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
|
|
124
|
-
|
|
125
|
-
const cartId = await getOrCreateCartId(forceNewCart);
|
|
126
|
-
|
|
127
|
-
const result = await addLinesMutation.mutateAsync({
|
|
128
|
-
cartId,
|
|
129
|
-
lines: [{
|
|
130
|
-
merchandiseId: item.variantId,
|
|
131
|
-
quantity: item.quantity ?? 1,
|
|
132
|
-
}],
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
if (result.cartLinesAdd.userErrors?.length > 0) {
|
|
136
|
-
const errorMessage = result.cartLinesAdd.userErrors[0].message;
|
|
137
|
-
|
|
138
|
-
if (isCartNotFoundError({ message: errorMessage }) && !forceNewCart) {
|
|
139
|
-
console.warn('Cart expired, creating new cart and retrying...');
|
|
140
|
-
setCartId(null);
|
|
141
|
-
return addToCart(item, { _forceNewCart: true });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
throw new Error(errorMessage);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Open cart drawer to show the added item
|
|
148
|
-
openCart();
|
|
149
|
-
toast.success('Added to cart');
|
|
150
|
-
} catch (error: unknown) {
|
|
151
|
-
if (isCartNotFoundError(error) && !forceNewCart) {
|
|
152
|
-
console.warn('Cart expired (caught), creating new cart and retrying...');
|
|
153
|
-
setCartId(null);
|
|
154
|
-
return addToCart(item, { _forceNewCart: true });
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
console.error('Add to cart failed:', error);
|
|
158
|
-
const message = error instanceof Error ? error.message : 'Failed to add to cart';
|
|
159
|
-
toast.error(message);
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
}, [setCartId, openCart, getOrCreateCartId, addLinesMutation]);
|
|
31
|
+
const addToCart = useCallback(async (variantId: string, quantity = 1) => {
|
|
32
|
+
await api.getState().addToCart([{ merchandiseId: variantId, quantity }]);
|
|
33
|
+
api.getState().openCart();
|
|
34
|
+
}, [api]);
|
|
163
35
|
|
|
164
36
|
/**
|
|
165
|
-
* Execute the actual
|
|
37
|
+
* Execute the actual quantity update via SDK store.
|
|
166
38
|
*/
|
|
167
39
|
const executeQuantityUpdate = useCallback(async (lineId: string, quantity: number) => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
|
|
171
|
-
|
|
172
|
-
const currentCartId = cartStoreApi.getState().cartId;
|
|
173
|
-
if (!currentCartId) {
|
|
174
|
-
throw new Error('No cart found');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const result = await updateLinesMutation.mutateAsync({
|
|
178
|
-
cartId: currentCartId,
|
|
179
|
-
lines: [{
|
|
180
|
-
id: lineId,
|
|
181
|
-
quantity,
|
|
182
|
-
}],
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
if (result.cartLinesUpdate.userErrors?.length > 0) {
|
|
186
|
-
const errorMessage = result.cartLinesUpdate.userErrors[0].message;
|
|
187
|
-
|
|
188
|
-
if (isCartNotFoundError({ message: errorMessage })) {
|
|
189
|
-
console.warn('Cart expired during update, clearing cart');
|
|
190
|
-
clearCart();
|
|
191
|
-
toast.error('Your cart has expired. Please add items again.');
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
throw new Error(errorMessage);
|
|
196
|
-
}
|
|
197
|
-
} catch (error: unknown) {
|
|
198
|
-
if (isCartNotFoundError(error)) {
|
|
199
|
-
console.warn('Cart expired during update (caught), clearing cart');
|
|
200
|
-
clearCart();
|
|
201
|
-
toast.error('Your cart has expired. Please add items again.');
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
console.error('Update quantity failed:', error);
|
|
206
|
-
const message = error instanceof Error ? error.message : 'Failed to update quantity';
|
|
207
|
-
toast.error(message);
|
|
208
|
-
}
|
|
209
|
-
}, [cartStoreApi, queryClient, updateLinesMutation, clearCart]);
|
|
40
|
+
await api.getState().updateQuantity([{ id: lineId, quantity }]);
|
|
41
|
+
}, [api]);
|
|
210
42
|
|
|
211
43
|
/**
|
|
212
44
|
* Remove item from cart by line ID.
|
|
213
|
-
*
|
|
214
|
-
* Silently handles expired cart (just clears stale cartId).
|
|
215
45
|
*/
|
|
216
46
|
const removeFromCart = useCallback(async (lineId: string) => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!currentCartId) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const result = await removeLinesMutation.mutateAsync({
|
|
224
|
-
cartId: currentCartId,
|
|
225
|
-
lineIds: [lineId],
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
if (result.cartLinesRemove.userErrors?.length > 0) {
|
|
229
|
-
const errorMessage = result.cartLinesRemove.userErrors[0].message;
|
|
230
|
-
|
|
231
|
-
if (isCartNotFoundError({ message: errorMessage })) {
|
|
232
|
-
console.warn('Cart expired during remove, clearing stale cartId');
|
|
233
|
-
setCartId(null);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
throw new Error(errorMessage);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
toast.success('Removed from cart');
|
|
241
|
-
} catch (error: unknown) {
|
|
242
|
-
if (isCartNotFoundError(error)) {
|
|
243
|
-
console.warn('Cart expired during remove (caught), clearing stale cartId');
|
|
244
|
-
setCartId(null);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
console.error('Remove from cart failed:', error);
|
|
249
|
-
const message = error instanceof Error ? error.message : 'Failed to remove from cart';
|
|
250
|
-
toast.error(message);
|
|
251
|
-
throw error;
|
|
252
|
-
}
|
|
253
|
-
}, [cartStoreApi, setCartId, removeLinesMutation]);
|
|
47
|
+
await api.getState().removeFromCart([lineId]);
|
|
48
|
+
}, [api]);
|
|
254
49
|
|
|
255
50
|
/**
|
|
256
|
-
* Update item quantity (debounced
|
|
257
|
-
*
|
|
258
|
-
* Debounces GraphQL API calls to prevent ThrottlerException on rapid clicks.
|
|
51
|
+
* Update item quantity (debounced).
|
|
259
52
|
* If quantity <= 0, removes the item instead.
|
|
260
53
|
*/
|
|
261
54
|
const updateQuantity = useCallback((lineId: string, quantity: number) => {
|
|
@@ -264,23 +57,14 @@ export function useCartActions() {
|
|
|
264
57
|
return;
|
|
265
58
|
}
|
|
266
59
|
|
|
267
|
-
// Cancel any pending update for this line
|
|
268
60
|
const existingTimeout = updateTimeoutRef.current.get(lineId);
|
|
269
61
|
if (existingTimeout) {
|
|
270
62
|
clearTimeout(existingTimeout);
|
|
271
63
|
}
|
|
272
64
|
|
|
273
|
-
// Store the pending update
|
|
274
|
-
pendingUpdatesRef.current.set(lineId, { lineId, quantity });
|
|
275
|
-
|
|
276
|
-
// Schedule debounced API call
|
|
277
65
|
const timeout = setTimeout(() => {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
pendingUpdatesRef.current.delete(lineId);
|
|
281
|
-
updateTimeoutRef.current.delete(lineId);
|
|
282
|
-
executeQuantityUpdate(pending.lineId, pending.quantity);
|
|
283
|
-
}
|
|
66
|
+
updateTimeoutRef.current.delete(lineId);
|
|
67
|
+
executeQuantityUpdate(lineId, quantity);
|
|
284
68
|
}, QUANTITY_UPDATE_DEBOUNCE_MS);
|
|
285
69
|
|
|
286
70
|
updateTimeoutRef.current.set(lineId, timeout);
|
|
@@ -294,25 +78,14 @@ export function useCartActions() {
|
|
|
294
78
|
clearTimeout(timeout);
|
|
295
79
|
}
|
|
296
80
|
timeouts.clear();
|
|
297
|
-
pendingUpdatesRef.current.clear();
|
|
298
81
|
};
|
|
299
82
|
}, []);
|
|
300
83
|
|
|
301
|
-
/**
|
|
302
|
-
* Clear entire cart.
|
|
303
|
-
* Clears cartId in zustand persist → useCartSync returns empty.
|
|
304
|
-
*/
|
|
305
|
-
const clearEntireCart = useCallback(() => {
|
|
306
|
-
clearCart();
|
|
307
|
-
toast.success('Cart cleared');
|
|
308
|
-
}, [clearCart]);
|
|
309
|
-
|
|
310
84
|
return {
|
|
311
85
|
addToCart,
|
|
312
86
|
updateQuantity,
|
|
313
87
|
removeFromCart,
|
|
314
|
-
clearCart:
|
|
315
|
-
isLoading
|
|
316
|
-
updateLinesMutation.isPending || removeLinesMutation.isPending,
|
|
88
|
+
clearCart: useCallback(() => api.getState().clearCart(), [api]),
|
|
89
|
+
isLoading,
|
|
317
90
|
};
|
|
318
91
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import type { CartActions, CartData } from '@doswiftly/storefront-sdk/react';
|
|
5
|
+
import { assertNoUserErrors } from '@doswiftly/storefront-sdk';
|
|
6
|
+
import { useExecute } from '@/lib/graphql/client';
|
|
7
|
+
import type { CartQuery, CartCreateMutation, CartLinesAddMutation, CartLinesUpdateMutation, CartLinesRemoveMutation } from '@/generated/graphql';
|
|
8
|
+
import {
|
|
9
|
+
CartDocument,
|
|
10
|
+
CartCreateDocument,
|
|
11
|
+
CartLinesAddDocument,
|
|
12
|
+
CartLinesUpdateDocument,
|
|
13
|
+
CartLinesRemoveDocument,
|
|
14
|
+
} from '@/generated/graphql';
|
|
15
|
+
|
|
16
|
+
interface CartMutationResult {
|
|
17
|
+
cart: { id: string; totalQuantity: number } | null;
|
|
18
|
+
userErrors: Array<{ message: string; field?: string[]; code?: string }>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function extractCartData(result: CartMutationResult): CartData {
|
|
22
|
+
assertNoUserErrors(result);
|
|
23
|
+
return { id: result.cart!.id, totalQuantity: result.cart!.totalQuantity };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* CartActions DI implementation using template's GraphQL operations.
|
|
28
|
+
*
|
|
29
|
+
* Maps generated GraphQL documents to the SDK CartActions interface.
|
|
30
|
+
* Used by StoresProvider to wire createCartStore with template transport.
|
|
31
|
+
*/
|
|
32
|
+
export function useCartDI(): CartActions {
|
|
33
|
+
const execute = useExecute();
|
|
34
|
+
|
|
35
|
+
return useMemo(
|
|
36
|
+
(): CartActions => ({
|
|
37
|
+
fetchCart: async (cartId) => {
|
|
38
|
+
const data = await execute<CartQuery>(CartDocument.toString(), { id: cartId });
|
|
39
|
+
if (!data.cart) return null;
|
|
40
|
+
return { id: data.cart.id, totalQuantity: data.cart.totalQuantity };
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
createCart: async () => {
|
|
44
|
+
const data = await execute<CartCreateMutation>(CartCreateDocument.toString(), { input: {} });
|
|
45
|
+
assertNoUserErrors(data.cartCreate);
|
|
46
|
+
if (!data.cartCreate.cart) throw new Error('Failed to create cart');
|
|
47
|
+
return data.cartCreate.cart.id;
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
addLines: async (cartId, lines) => {
|
|
51
|
+
const data = await execute<CartLinesAddMutation>(CartLinesAddDocument.toString(), { cartId, lines });
|
|
52
|
+
return extractCartData(data.cartLinesAdd);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
updateLines: async (cartId, lines) => {
|
|
56
|
+
const data = await execute<CartLinesUpdateMutation>(CartLinesUpdateDocument.toString(), { cartId, lines });
|
|
57
|
+
return extractCartData(data.cartLinesUpdate);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
removeLines: async (cartId, lineIds) => {
|
|
61
|
+
const data = await execute<CartLinesRemoveMutation>(CartLinesRemoveDocument.toString(), { cartId, lineIds });
|
|
62
|
+
return extractCartData(data.cartLinesRemove);
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
[execute],
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -33,7 +33,7 @@ export interface CartItemData {
|
|
|
33
33
|
*/
|
|
34
34
|
export function useCartSync() {
|
|
35
35
|
const cartId = useCartStore((state) => state.cartId);
|
|
36
|
-
const
|
|
36
|
+
const clearCart = useCartStore((state) => state.clearCart);
|
|
37
37
|
const isHydrated = useHydrated();
|
|
38
38
|
|
|
39
39
|
const { data, isLoading, isSuccess, error, refetch } = useCart(cartId, {
|
|
@@ -51,9 +51,9 @@ export function useCartSync() {
|
|
|
51
51
|
// Auto-clear stale cartId
|
|
52
52
|
useEffect(() => {
|
|
53
53
|
if (isStaleCart) {
|
|
54
|
-
|
|
54
|
+
clearCart();
|
|
55
55
|
}
|
|
56
|
-
}, [isStaleCart,
|
|
56
|
+
}, [isStaleCart, clearCart]);
|
|
57
57
|
|
|
58
58
|
// Map GraphQL lines to display-friendly items
|
|
59
59
|
const items: CartItemData[] = (cart?.lines ?? []).map((line: CartLineFields) => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createNavigation } from 'next-intl/navigation';
|
|
2
|
+
import { defaultLocale, localePrefix } from './routing';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Navigation APIs for dynamic locales.
|
|
6
|
+
*
|
|
7
|
+
* No `locales` argument — any locale string accepted at runtime.
|
|
8
|
+
* Backend is SSOT for supportedLanguages.
|
|
9
|
+
* `defaultLocale` required for `as-needed` prefix mode (hides default in URL).
|
|
10
|
+
*/
|
|
11
|
+
export const { Link, redirect, usePathname, useRouter, getPathname } =
|
|
12
|
+
createNavigation({ defaultLocale, localePrefix });
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getRequestConfig } from 'next-intl/server';
|
|
2
|
+
import { defaultLocale } from './routing';
|
|
3
|
+
|
|
4
|
+
export default getRequestConfig(async ({ requestLocale }) => {
|
|
5
|
+
// requestLocale is set by next-intl middleware (proxy.ts) which validates
|
|
6
|
+
// against dynamic locales from backend. If somehow invalid, fall back.
|
|
7
|
+
const locale = (await requestLocale) || defaultLocale;
|
|
8
|
+
|
|
9
|
+
let messages;
|
|
10
|
+
try {
|
|
11
|
+
messages = (await import(`../messages/${locale}.json`)).default;
|
|
12
|
+
} catch {
|
|
13
|
+
messages = (await import(`../messages/${defaultLocale}.json`)).default;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return { locale, messages };
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* next-intl routing configuration (dynamic locales).
|
|
3
|
+
*
|
|
4
|
+
* - defaultLocale: from NEXT_PUBLIC_DEFAULT_LOCALE env var (set by CLI `doswiftly init`), fallback 'pl'.
|
|
5
|
+
* - localePrefix: 'as-needed' — default locale hidden in URL, others prefixed.
|
|
6
|
+
* - locales: NOT defined here — backend is SSOT for supportedLanguages.
|
|
7
|
+
* - Middleware (proxy.ts) fetches supportedLanguages dynamically per-request.
|
|
8
|
+
* - Navigation API (navigation.ts) accepts any locale string at runtime.
|
|
9
|
+
* - Layout validates against backend's shop.supportedLanguages.
|
|
10
|
+
*
|
|
11
|
+
* No `defineRouting` — we export primitives consumed by createNavigation and createMiddleware.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const defaultLocale =
|
|
15
|
+
(process.env.NEXT_PUBLIC_DEFAULT_LOCALE as string) || 'pl';
|
|
16
|
+
|
|
17
|
+
export const localePrefix = 'as-needed' as const;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useQuery, useMutation, useQueryClient, type UseQueryOptions, type UseMutationOptions } from '@tanstack/react-query';
|
|
4
4
|
import { useExecute } from './client';
|
|
5
|
-
import { useCurrencyStore, useAuthStore } from '@doswiftly/storefront-sdk/react';
|
|
5
|
+
import { useCurrencyStore, useAuthStore, useLanguageStore } from '@doswiftly/storefront-sdk/react';
|
|
6
6
|
import { normalizeConnection, type NormalizedConnection } from '@doswiftly/storefront-sdk';
|
|
7
7
|
import { queryKeys } from './query-keys';
|
|
8
8
|
import type { TypedDocumentString } from '@/generated/graphql';
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
CheckoutCompleteDocument,
|
|
35
35
|
CheckoutGiftCardApplyDocument,
|
|
36
36
|
CheckoutGiftCardRemoveDocument,
|
|
37
|
+
CheckoutGiftCardRecipientUpdateDocument,
|
|
37
38
|
CustomerDocument,
|
|
38
39
|
CustomerProfileDocument,
|
|
39
40
|
CustomerOrderDocument,
|
|
@@ -106,6 +107,8 @@ import type {
|
|
|
106
107
|
CheckoutGiftCardApplyMutationVariables,
|
|
107
108
|
CheckoutGiftCardRemoveMutation,
|
|
108
109
|
CheckoutGiftCardRemoveMutationVariables,
|
|
110
|
+
CheckoutGiftCardRecipientUpdateMutation,
|
|
111
|
+
CheckoutGiftCardRecipientUpdateMutationVariables,
|
|
109
112
|
CustomerQuery,
|
|
110
113
|
CustomerQueryVariables,
|
|
111
114
|
CustomerProfileQuery,
|
|
@@ -173,12 +176,14 @@ export function useGraphQLQuery<TResult, TVariables>(
|
|
|
173
176
|
) {
|
|
174
177
|
const execute = useExecute();
|
|
175
178
|
const currency = useCurrencyStore((s) => s.currency);
|
|
179
|
+
const language = useLanguageStore((s) => s.language);
|
|
176
180
|
const operationName = getOperationName(document as TypedDocumentString<unknown, unknown>);
|
|
177
|
-
const queryKey = [operationName, variables, currency];
|
|
181
|
+
const queryKey = [operationName, variables, currency, language];
|
|
178
182
|
|
|
179
183
|
return useQuery({
|
|
180
184
|
queryKey,
|
|
181
185
|
queryFn: () => execute<TResult>(document.toString(), variables as Record<string, unknown>),
|
|
186
|
+
enabled: !!language && (options?.enabled ?? true),
|
|
182
187
|
...options,
|
|
183
188
|
});
|
|
184
189
|
}
|
|
@@ -223,11 +228,12 @@ export function useProduct(
|
|
|
223
228
|
) {
|
|
224
229
|
const execute = useExecute();
|
|
225
230
|
const currency = useCurrencyStore((s) => s.currency);
|
|
231
|
+
const language = useLanguageStore((s) => s.language);
|
|
226
232
|
|
|
227
233
|
const isId = handleOrId.startsWith('gid://');
|
|
228
234
|
|
|
229
235
|
return useQuery({
|
|
230
|
-
queryKey: queryKeys.products.detail(handleOrId, currency),
|
|
236
|
+
queryKey: queryKeys.products.detail(handleOrId, currency, language),
|
|
231
237
|
queryFn: () => {
|
|
232
238
|
const variables: ProductQueryVariables = isId
|
|
233
239
|
? { id: handleOrId }
|
|
@@ -280,9 +286,10 @@ export function useProducts(
|
|
|
280
286
|
) {
|
|
281
287
|
const execute = useExecute();
|
|
282
288
|
const currency = useCurrencyStore((s) => s.currency);
|
|
289
|
+
const language = useLanguageStore((s) => s.language);
|
|
283
290
|
|
|
284
291
|
return useQuery({
|
|
285
|
-
queryKey: queryKeys.products.list(variables as Record<string, unknown>, currency),
|
|
292
|
+
queryKey: queryKeys.products.list(variables as Record<string, unknown>, currency, language),
|
|
286
293
|
queryFn: async () => {
|
|
287
294
|
const { sortKey: normalizedSortKey, reverse: normalizedReverse } = normalizeSortKey(variables?.sortKey);
|
|
288
295
|
|
|
@@ -325,11 +332,12 @@ export function useCollection(
|
|
|
325
332
|
) {
|
|
326
333
|
const execute = useExecute();
|
|
327
334
|
const currency = useCurrencyStore((s) => s.currency);
|
|
335
|
+
const language = useLanguageStore((s) => s.language);
|
|
328
336
|
|
|
329
337
|
const isId = handleOrId.startsWith('gid://');
|
|
330
338
|
|
|
331
339
|
return useQuery({
|
|
332
|
-
queryKey: queryKeys.collections.detail(handleOrId, currency),
|
|
340
|
+
queryKey: queryKeys.collections.detail(handleOrId, currency, language),
|
|
333
341
|
queryFn: () => {
|
|
334
342
|
const variables: CollectionQueryVariables = isId
|
|
335
343
|
? { id: handleOrId }
|
|
@@ -360,9 +368,10 @@ export function useCollections(
|
|
|
360
368
|
) {
|
|
361
369
|
const execute = useExecute();
|
|
362
370
|
const currency = useCurrencyStore((s) => s.currency);
|
|
371
|
+
const language = useLanguageStore((s) => s.language);
|
|
363
372
|
|
|
364
373
|
return useQuery({
|
|
365
|
-
queryKey: queryKeys.collections.list(variables as Record<string, unknown>, currency),
|
|
374
|
+
queryKey: queryKeys.collections.list(variables as Record<string, unknown>, currency, language),
|
|
366
375
|
queryFn: async () => {
|
|
367
376
|
const graphqlVariables: CollectionsQueryVariables = {
|
|
368
377
|
first: variables?.first ?? 20,
|
|
@@ -431,9 +440,10 @@ export function useCategories(
|
|
|
431
440
|
) {
|
|
432
441
|
const execute = useExecute();
|
|
433
442
|
const currency = useCurrencyStore((s) => s.currency);
|
|
443
|
+
const language = useLanguageStore((s) => s.language);
|
|
434
444
|
|
|
435
445
|
return useQuery({
|
|
436
|
-
queryKey: queryKeys.categories.list(currency),
|
|
446
|
+
queryKey: queryKeys.categories.list(currency, language),
|
|
437
447
|
queryFn: async () => {
|
|
438
448
|
const data = await execute<CategoriesQuery>(CategoriesDocument.toString());
|
|
439
449
|
|
|
@@ -478,9 +488,10 @@ export function useAvailableFilters(
|
|
|
478
488
|
) {
|
|
479
489
|
const execute = useExecute();
|
|
480
490
|
const currency = useCurrencyStore((s) => s.currency);
|
|
491
|
+
const language = useLanguageStore((s) => s.language);
|
|
481
492
|
|
|
482
493
|
return useQuery({
|
|
483
|
-
queryKey: queryKeys.filters.forContext(input as Record<string, unknown>, currency),
|
|
494
|
+
queryKey: queryKeys.filters.forContext(input as Record<string, unknown>, currency, language),
|
|
484
495
|
queryFn: () => {
|
|
485
496
|
const variables: AvailableFiltersQueryVariables = { input: input || null };
|
|
486
497
|
return execute<AvailableFiltersQuery>(AvailableFiltersDocument.toString(), variables);
|
|
@@ -879,6 +890,28 @@ export function useCheckoutGiftCardRemove(
|
|
|
879
890
|
});
|
|
880
891
|
}
|
|
881
892
|
|
|
893
|
+
/**
|
|
894
|
+
* Update gift card recipient data on checkout line item
|
|
895
|
+
*/
|
|
896
|
+
export function useCheckoutGiftCardRecipientUpdate(
|
|
897
|
+
options?: UseMutationOptions<CheckoutGiftCardRecipientUpdateMutation, Error, CheckoutGiftCardRecipientUpdateMutationVariables>
|
|
898
|
+
) {
|
|
899
|
+
const execute = useExecute();
|
|
900
|
+
const queryClient = useQueryClient();
|
|
901
|
+
|
|
902
|
+
return useMutation({
|
|
903
|
+
mutationFn: (variables: CheckoutGiftCardRecipientUpdateMutationVariables) =>
|
|
904
|
+
execute<CheckoutGiftCardRecipientUpdateMutation>(CheckoutGiftCardRecipientUpdateDocument.toString(), variables),
|
|
905
|
+
onMutate: async () => {
|
|
906
|
+
await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
|
|
907
|
+
},
|
|
908
|
+
onSettled: () => {
|
|
909
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
|
|
910
|
+
},
|
|
911
|
+
...options,
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
|
|
882
915
|
// ============================================================================
|
|
883
916
|
// CUSTOMER HOOKS
|
|
884
917
|
// ============================================================================
|