@doswiftly/cli 0.1.18 → 0.1.19

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.
Files changed (210) hide show
  1. package/README.md +23 -323
  2. package/dist/commands/check.js +1 -1
  3. package/dist/commands/check.js.map +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +39 -20
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/doctor.js +3 -3
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/init.js +4 -4
  10. package/dist/commands/sdk.js +5 -5
  11. package/dist/commands/sdk.js.map +1 -1
  12. package/dist/commands/template.js +4 -4
  13. package/dist/commands/template.js.map +1 -1
  14. package/dist/commands/types.js +5 -5
  15. package/dist/commands/types.js.map +1 -1
  16. package/dist/commands/verify.js +2 -2
  17. package/dist/commands/verify.js.map +1 -1
  18. package/dist/lib/package-manager.d.ts +1 -1
  19. package/dist/lib/package-manager.js +1 -1
  20. package/package.json +1 -1
  21. package/templates/storefront-nextjs/README.md +16 -12
  22. package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
  23. package/templates/storefront-nextjs/app/account/page.tsx +2 -2
  24. package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
  25. package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
  26. package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
  27. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
  28. package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
  29. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
  30. package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
  31. package/templates/storefront-nextjs/app/page.tsx +1 -1
  32. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
  33. package/templates/storefront-nextjs/app/products/page.tsx +2 -2
  34. package/templates/storefront-nextjs/app/search/page.tsx +1 -1
  35. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
  36. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
  37. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
  38. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
  39. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
  40. package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
  41. package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
  42. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
  43. package/templates/storefront-nextjs/components/providers.tsx +1 -1
  44. package/templates/storefront-nextjs/lib/currency.tsx +3 -3
  45. package/templates/storefront-nextjs/lib/format.ts +1 -1
  46. package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
  47. package/templates/storefront-nextjs/package.dev.json +1 -1
  48. package/templates/storefront-nextjs/package.json +1 -1
  49. package/templates/storefront-nextjs/package.json.template +1 -1
  50. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
  51. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
  52. package/templates/storefront-nextjs-shadcn/CLAUDE.md +148 -35
  53. package/templates/storefront-nextjs-shadcn/README.md +29 -162
  54. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +98 -91
  55. package/templates/storefront-nextjs-shadcn/app/account/error.tsx +43 -0
  56. package/templates/storefront-nextjs-shadcn/app/account/loading.tsx +19 -0
  57. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +53 -162
  58. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/loading.tsx +60 -0
  59. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +36 -47
  60. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +46 -29
  61. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +8 -5
  62. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +108 -71
  63. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
  64. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
  65. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +10 -5
  66. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/loading.tsx +17 -0
  67. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +43 -2
  68. package/templates/storefront-nextjs-shadcn/app/blog/loading.tsx +19 -0
  69. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +2 -1
  70. package/templates/storefront-nextjs-shadcn/app/cart/loading.tsx +26 -0
  71. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +6 -3
  72. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/category-products-client.tsx +56 -0
  73. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/loading.tsx +32 -0
  74. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +76 -59
  75. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +8 -4
  76. package/templates/storefront-nextjs-shadcn/app/checkout/error.tsx +43 -0
  77. package/templates/storefront-nextjs-shadcn/app/checkout/loading.tsx +31 -0
  78. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +116 -79
  79. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/loading.tsx +19 -0
  80. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +1 -1
  81. package/templates/storefront-nextjs-shadcn/app/collections/loading.tsx +18 -0
  82. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +7 -4
  83. package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
  84. package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
  85. package/templates/storefront-nextjs-shadcn/app/layout.tsx +46 -11
  86. package/templates/storefront-nextjs-shadcn/app/products/[slug]/error.tsx +43 -0
  87. package/templates/storefront-nextjs-shadcn/app/products/[slug]/loading.tsx +29 -0
  88. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +6 -6
  89. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +15 -61
  90. package/templates/storefront-nextjs-shadcn/app/products/loading.tsx +32 -0
  91. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +405 -151
  92. package/templates/storefront-nextjs-shadcn/app/search/loading.tsx +18 -0
  93. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +8 -5
  94. package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
  95. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
  96. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +3 -1
  97. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +26 -24
  98. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
  99. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +9 -9
  100. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +11 -37
  101. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +37 -23
  102. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +4 -3
  103. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +8 -5
  104. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +1 -1
  105. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
  106. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +1 -1
  107. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +22 -7
  108. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +2 -2
  109. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +1 -1
  110. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +2 -2
  111. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
  112. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +1 -1
  113. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +3 -3
  114. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
  115. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +2 -2
  116. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +2 -1
  117. package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
  118. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +2 -12
  119. package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
  120. package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
  121. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +4 -4
  122. package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
  123. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +2 -2
  124. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +33 -23
  125. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
  126. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
  127. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +10 -19
  128. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
  129. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
  130. package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
  131. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +3 -1
  132. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +69 -0
  133. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +84 -0
  134. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +138 -0
  135. package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
  136. package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
  137. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +3 -31
  138. package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
  139. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +176 -123
  140. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
  141. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +2 -2
  142. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
  143. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
  144. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +19 -7
  145. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
  146. package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
  147. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
  148. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +1 -7
  149. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
  150. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
  151. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
  152. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +30 -0
  153. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
  154. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
  155. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +3 -2
  156. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
  157. package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
  158. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
  159. package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
  160. package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
  161. package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
  162. package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
  163. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +3 -1
  164. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +12779 -0
  165. package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
  166. package/templates/storefront-nextjs-shadcn/hooks/index.ts +2 -0
  167. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
  168. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
  169. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +51 -19
  170. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +13 -9
  171. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
  172. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
  173. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +32 -0
  174. package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
  175. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +687 -632
  176. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +86 -0
  177. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +131 -182
  178. package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
  179. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
  180. package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
  181. package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
  182. package/templates/storefront-nextjs-shadcn/package.json +12 -13
  183. package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
  184. package/templates/storefront-nextjs-shadcn/proxy.ts +3 -4
  185. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +41 -39
  186. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
  187. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
  188. package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
  189. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
  190. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
  191. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
  192. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
  193. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
  194. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
  195. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
  196. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
  197. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
  198. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
  199. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
  200. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
  201. package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
  202. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
  203. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
  204. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
  205. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
  206. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
  207. package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
  208. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
  209. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
  210. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
@@ -1,134 +1,191 @@
1
1
  'use client';
2
2
 
3
3
  import { useQuery, useMutation, useQueryClient, type UseQueryOptions, type UseMutationOptions } from '@tanstack/react-query';
4
- import { getGraphQLClient } from './client';
5
- import { useCurrencyStore } from '@/stores/currency-store';
6
- import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
4
+ import { useExecute } from './client';
5
+ import { useCurrencyStore, useAuthStore } from '@doswiftly/storefront-sdk/react';
6
+ import { normalizeConnection, type NormalizedConnection } from '@doswiftly/storefront-sdk';
7
+ import { queryKeys } from './query-keys';
8
+ import type { TypedDocumentString } from '@/generated/graphql';
9
+ import type { NormalizedProductsResult, NormalizedCollectionsResult, NormalizedCategoriesResult } from './types';
10
+
11
+ // Static imports — Documents
12
+ import {
13
+ ProductDocument,
14
+ ProductsDocument,
15
+ CollectionDocument,
16
+ CollectionsDocument,
17
+ CategoriesDocument,
18
+ CategoryDocument,
19
+ AvailableFiltersDocument,
20
+ CartDocument,
21
+ CartCreateDocument,
22
+ CartLinesAddDocument,
23
+ CartLinesUpdateDocument,
24
+ CartLinesRemoveDocument,
25
+ CartDiscountCodesUpdateDocument,
26
+ CheckoutDocument,
27
+ CheckoutCreateDocument,
28
+ CheckoutShippingAddressUpdateDocument,
29
+ CheckoutBillingAddressUpdateDocument,
30
+ CheckoutEmailUpdateDocument,
31
+ CheckoutShippingLineUpdateDocument,
32
+ CheckoutDiscountCodeApplyDocument,
33
+ CheckoutDiscountCodeRemoveDocument,
34
+ CheckoutCompleteDocument,
35
+ CheckoutGiftCardApplyDocument,
36
+ CheckoutGiftCardRemoveDocument,
37
+ CustomerDocument,
38
+ CustomerProfileDocument,
39
+ CustomerOrderDocument,
40
+ CustomerUpdateDocument,
41
+ CustomerAddressCreateDocument,
42
+ CustomerAddressUpdateDocument,
43
+ CustomerAddressDeleteDocument,
44
+ CustomerDefaultAddressUpdateDocument,
45
+ CustomerLoginDocument,
46
+ CustomerLogoutDocument,
47
+ LoyaltyMemberDocument,
48
+ LoyaltyRewardsDocument,
49
+ LoyaltyTransactionsDocument,
50
+ LoyaltySettingsDocument,
51
+ ReferralStatsDocument,
52
+ RedeemLoyaltyRewardDocument,
53
+ } from '@/generated/graphql';
54
+
55
+ // Static imports — Types
56
+ import type {
57
+ ProductQuery,
58
+ ProductQueryVariables,
59
+ ProductsQuery,
60
+ ProductsQueryVariables,
61
+ ProductSortKeys,
62
+ CollectionQuery,
63
+ CollectionQueryVariables,
64
+ CollectionsQuery,
65
+ CollectionsQueryVariables,
66
+ CollectionSortKeys,
67
+ CategoriesQuery,
68
+ CategoryQuery,
69
+ CategoryQueryVariables,
70
+ Category,
71
+ ProductFilterInput,
72
+ AvailableFiltersQuery,
73
+ AvailableFiltersQueryVariables,
74
+ AvailableFiltersInput,
75
+ CartQuery,
76
+ CartQueryVariables,
77
+ CartCreateMutation,
78
+ CartCreateMutationVariables,
79
+ CartLinesAddMutation,
80
+ CartLinesAddMutationVariables,
81
+ CartLinesUpdateMutation,
82
+ CartLinesUpdateMutationVariables,
83
+ CartLinesRemoveMutation,
84
+ CartLinesRemoveMutationVariables,
85
+ CartDiscountCodesUpdateMutation,
86
+ CartDiscountCodesUpdateMutationVariables,
87
+ CheckoutQuery,
88
+ CheckoutQueryVariables,
89
+ CheckoutCreateMutation,
90
+ CheckoutCreateMutationVariables,
91
+ CheckoutShippingAddressUpdateMutation,
92
+ CheckoutShippingAddressUpdateMutationVariables,
93
+ CheckoutBillingAddressUpdateMutation,
94
+ CheckoutBillingAddressUpdateMutationVariables,
95
+ CheckoutEmailUpdateMutation,
96
+ CheckoutEmailUpdateMutationVariables,
97
+ CheckoutShippingLineUpdateMutation,
98
+ CheckoutShippingLineUpdateMutationVariables,
99
+ CheckoutDiscountCodeApplyMutation,
100
+ CheckoutDiscountCodeApplyMutationVariables,
101
+ CheckoutDiscountCodeRemoveMutation,
102
+ CheckoutDiscountCodeRemoveMutationVariables,
103
+ CheckoutCompleteMutation,
104
+ CheckoutCompleteMutationVariables,
105
+ CheckoutGiftCardApplyMutation,
106
+ CheckoutGiftCardApplyMutationVariables,
107
+ CheckoutGiftCardRemoveMutation,
108
+ CheckoutGiftCardRemoveMutationVariables,
109
+ CustomerQuery,
110
+ CustomerQueryVariables,
111
+ CustomerProfileQuery,
112
+ CustomerProfileQueryVariables,
113
+ CustomerOrderQuery,
114
+ CustomerOrderQueryVariables,
115
+ CustomerUpdateMutation,
116
+ CustomerUpdateMutationVariables,
117
+ CustomerUpdateInput,
118
+ CustomerAddressCreateMutation,
119
+ CustomerAddressCreateMutationVariables,
120
+ CustomerAddressUpdateMutation,
121
+ CustomerAddressUpdateMutationVariables,
122
+ CustomerAddressDeleteMutation,
123
+ CustomerAddressDeleteMutationVariables,
124
+ CustomerDefaultAddressUpdateMutation,
125
+ CustomerDefaultAddressUpdateMutationVariables,
126
+ CustomerLoginMutation,
127
+ CustomerLoginMutationVariables,
128
+ CustomerLogoutMutation,
129
+ CustomerLogoutMutationVariables,
130
+ LoyaltyMemberQuery,
131
+ LoyaltyRewardsQuery,
132
+ LoyaltyTransactionsQuery,
133
+ LoyaltyTransactionsQueryVariables,
134
+ LoyaltySettingsQuery,
135
+ ReferralStatsQuery,
136
+ RedeemLoyaltyRewardMutation,
137
+ RedeemLoyaltyRewardMutationVariables,
138
+ } from '@/generated/graphql';
139
+
140
+ // normalizeConnection and NormalizedConnection imported from SDK (see top of file)
7
141
 
8
142
  // ============================================================================
9
- // TYPES
143
+ // GENERIC QUERY HOOK
10
144
  // ============================================================================
11
145
 
12
- /**
13
- * Normalized response for paginated queries
14
- * Converts GraphQL edges/nodes structure to flat arrays
15
- */
16
- export interface NormalizedConnection<T> {
17
- items: T[];
18
- pageInfo: {
19
- hasNextPage: boolean;
20
- hasPreviousPage: boolean;
21
- startCursor?: string | null;
22
- endCursor?: string | null;
23
- };
24
- totalCount?: number;
25
- }
26
-
27
- /**
28
- * GraphQL connection edge structure
29
- */
30
- interface Edge<T> {
31
- node: T;
32
- cursor: string;
33
- }
34
-
35
- /**
36
- * GraphQL connection structure
37
- */
38
- interface Connection<T> {
39
- edges: Edge<T>[];
40
- pageInfo: {
41
- hasNextPage: boolean;
42
- hasPreviousPage: boolean;
43
- startCursor?: string | null;
44
- endCursor?: string | null;
45
- };
46
- totalCount?: number;
47
- }
48
-
49
- // ============================================================================
50
- // UTILITY FUNCTIONS
51
- // ============================================================================
146
+ /** Cache operation names to avoid regex on every render */
147
+ const operationNameCache = new Map<TypedDocumentString<unknown, unknown>, string>();
52
148
 
53
- /**
54
- * Normalize GraphQL connection to flat array
55
- *
56
- * Converts edges/nodes structure to a simple array of items
57
- * for easier consumption in components.
58
- *
59
- * @param connection - GraphQL connection object
60
- * @returns Normalized response with flat items array
61
- *
62
- * @example
63
- * ```typescript
64
- * const data = { products: { edges: [...], pageInfo: {...} } };
65
- * const normalized = normalizeConnection(data.products);
66
- * // normalized.items is now a flat array
67
- * ```
68
- */
69
- export function normalizeConnection<T>(
70
- connection: Connection<T>
71
- ): NormalizedConnection<T> {
72
- return {
73
- items: connection.edges.map((edge) => edge.node),
74
- pageInfo: connection.pageInfo,
75
- totalCount: connection.totalCount,
76
- };
149
+ function getOperationName(document: TypedDocumentString<unknown, unknown>): string {
150
+ let name = operationNameCache.get(document);
151
+ if (!name) {
152
+ name = document.toString().match(/^(?:query|mutation|subscription)\s+(\w+)/)?.[1] || 'GraphQLQuery';
153
+ operationNameCache.set(document, name);
154
+ }
155
+ return name;
77
156
  }
78
157
 
79
- // ============================================================================
80
- // GENERIC QUERY HOOK
81
- // ============================================================================
82
-
83
158
  /**
84
159
  * Generic query hook with currency-aware caching
85
- *
160
+ *
86
161
  * Wraps React Query's useQuery with automatic currency injection
87
162
  * into query keys for proper cache invalidation when currency changes.
88
- *
89
- * @param document - TypedDocumentNode from codegen
90
- * @param variables - Query variables
91
- * @param options - React Query options
92
- * @returns React Query result
93
- *
163
+ *
94
164
  * @example
95
165
  * ```typescript
96
166
  * const { data } = useGraphQLQuery(ProductDocument, { handle: 'my-product' });
97
167
  * ```
98
168
  */
99
169
  export function useGraphQLQuery<TResult, TVariables>(
100
- document: TypedDocumentNode<TResult, TVariables>,
170
+ document: TypedDocumentString<TResult, TVariables>,
101
171
  variables?: TVariables,
102
172
  options?: Omit<UseQueryOptions<TResult>, 'queryKey' | 'queryFn'>
103
173
  ) {
104
- const client = getGraphQLClient();
105
- const currency = useCurrencyStore((s: any) => s.currency);
106
-
107
- // Generate query key from document name and variables
108
- // Include currency to invalidate cache when currency changes
109
- const queryKey = [
110
- // Extract operation name from document
111
- (document as any).definitions?.[0]?.name?.value || 'GraphQLQuery',
112
- variables,
113
- currency,
114
- ];
115
-
174
+ const execute = useExecute();
175
+ const currency = useCurrencyStore((s) => s.currency);
176
+ const operationName = getOperationName(document as TypedDocumentString<unknown, unknown>);
177
+ const queryKey = [operationName, variables, currency];
178
+
116
179
  return useQuery({
117
180
  queryKey,
118
- queryFn: () => client.request(document, variables as any),
181
+ queryFn: () => execute<TResult>(document.toString(), variables as Record<string, unknown>),
119
182
  ...options,
120
183
  });
121
184
  }
122
185
 
123
186
  /**
124
187
  * Generic mutation hook
125
- *
126
- * Wraps React Query's useMutation for GraphQL mutations.
127
- *
128
- * @param document - TypedDocumentNode from codegen
129
- * @param options - React Query mutation options
130
- * @returns React Query mutation result
131
- *
188
+ *
132
189
  * @example
133
190
  * ```typescript
134
191
  * const mutation = useGraphQLMutation(CartCreateDocument);
@@ -136,13 +193,13 @@ export function useGraphQLQuery<TResult, TVariables>(
136
193
  * ```
137
194
  */
138
195
  export function useGraphQLMutation<TResult, TVariables>(
139
- document: TypedDocumentNode<TResult, TVariables>,
196
+ document: TypedDocumentString<TResult, TVariables>,
140
197
  options?: UseMutationOptions<TResult, Error, TVariables>
141
198
  ) {
142
- const client = getGraphQLClient();
143
-
199
+ const execute = useExecute();
200
+
144
201
  return useMutation({
145
- mutationFn: (variables: TVariables) => client.request(document, variables as any),
202
+ mutationFn: (variables: TVariables) => execute<TResult>(document.toString(), variables as Record<string, unknown>),
146
203
  ...options,
147
204
  });
148
205
  }
@@ -153,106 +210,63 @@ export function useGraphQLMutation<TResult, TVariables>(
153
210
 
154
211
  /**
155
212
  * Fetch single product by handle or ID
156
- *
157
- * Automatically includes current currency in request headers
158
- * and query key for proper cache invalidation.
159
- *
160
- * @param handleOrId - Product handle (string) or ID (gid://)
161
- * @param options - React Query options
162
- * @returns Product query result
163
- *
213
+ *
164
214
  * @example
165
215
  * ```typescript
166
- * // In a Client Component
167
- * 'use client';
168
- *
169
- * export function ProductPrice({ handle }) {
170
- * const { data, isLoading } = useProduct(handle);
171
- *
172
- * if (isLoading) return <div>Loading...</div>;
173
- *
174
- * return (
175
- * <div suppressHydrationWarning>
176
- * {data.product.priceRange.minVariantPrice.amount}{' '}
177
- * {data.product.priceRange.minVariantPrice.currencyCode}
178
- * </div>
179
- * );
180
- * }
216
+ * const { data, isLoading } = useProduct('my-product-handle');
217
+ * const product = data?.product;
181
218
  * ```
182
219
  */
183
220
  export function useProduct(
184
221
  handleOrId: string,
185
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
222
+ options?: Omit<UseQueryOptions<ProductQuery>, 'queryKey' | 'queryFn'>
186
223
  ) {
187
- const client = getGraphQLClient();
188
- const currency = useCurrencyStore((s: any) => s.currency);
189
-
190
- // Determine if it's an ID or handle
224
+ const execute = useExecute();
225
+ const currency = useCurrencyStore((s) => s.currency);
226
+
191
227
  const isId = handleOrId.startsWith('gid://');
192
-
228
+
193
229
  return useQuery({
194
- queryKey: ['Product', handleOrId, currency],
195
- queryFn: async () => {
196
- // Dynamic import to avoid circular dependencies
197
- const { ProductDocument } = await import('@/generated/graphql');
198
-
199
- const variables = isId ? { id: handleOrId } : { handle: handleOrId };
200
- return client.request(ProductDocument, variables as any);
230
+ queryKey: queryKeys.products.detail(handleOrId, currency),
231
+ queryFn: () => {
232
+ const variables: ProductQueryVariables = isId
233
+ ? { id: handleOrId }
234
+ : { handle: handleOrId };
235
+ return execute<ProductQuery>(ProductDocument.toString(), variables);
201
236
  },
202
237
  ...options,
203
238
  });
204
239
  }
205
240
 
206
- /**
207
- * Fetch products with pagination and normalization
208
- *
209
- * Automatically normalizes GraphQL edges/nodes structure to flat arrays.
210
- * Includes currency in query key for cache invalidation.
211
- *
212
- * @param variables - Query variables (first, after, query, sortKey, reverse)
213
- * @param options - React Query options
214
- * @returns Normalized products response
215
- *
216
- * @example
217
- * ```typescript
218
- * // In a Client Component
219
- * 'use client';
220
- *
221
- * export function ProductList() {
222
- * const { data, isLoading } = useProducts({ first: 20 });
223
- *
224
- * if (isLoading) return <div>Loading...</div>;
225
- *
226
- * return (
227
- * <div>
228
- * {data.products.map(product => (
229
- * <ProductCard key={product.id} product={product} />
230
- * ))}
231
- * </div>
232
- * );
233
- * }
234
- * ```
235
- */
236
241
  /**
237
242
  * Map frontend sort values to GraphQL enum values
238
243
  */
239
- function normalizeSortKey(sortKey?: string): { sortKey?: string; reverse?: boolean } {
244
+ function normalizeSortKey(sortKey?: string): { sortKey: ProductSortKeys; reverse: boolean } {
240
245
  if (!sortKey) return { sortKey: 'BEST_SELLING', reverse: false };
241
-
242
- const sortMap: Record<string, { sortKey: string; reverse: boolean }> = {
246
+
247
+ const sortMap: Record<string, { sortKey: ProductSortKeys; reverse: boolean }> = {
243
248
  'relevance': { sortKey: 'RELEVANCE', reverse: false },
244
249
  'best-selling': { sortKey: 'BEST_SELLING', reverse: false },
245
250
  'price-low-to-high': { sortKey: 'PRICE', reverse: false },
246
251
  'price-high-to-low': { sortKey: 'PRICE', reverse: true },
247
252
  'title-asc': { sortKey: 'TITLE', reverse: false },
248
253
  'title-desc': { sortKey: 'TITLE', reverse: true },
249
- 'created-desc': { sortKey: 'CREATED', reverse: true },
250
- 'created-asc': { sortKey: 'CREATED', reverse: false },
254
+ 'created-desc': { sortKey: 'CREATED_AT', reverse: true },
255
+ 'created-asc': { sortKey: 'CREATED_AT', reverse: false },
251
256
  };
252
-
257
+
253
258
  return sortMap[sortKey] || { sortKey: 'BEST_SELLING', reverse: false };
254
259
  }
255
260
 
261
+ /**
262
+ * Fetch products with pagination and normalization
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const { data, isLoading } = useProducts({ first: 20 });
267
+ * data?.products.map(product => ...)
268
+ * ```
269
+ */
256
270
  export function useProducts(
257
271
  variables?: {
258
272
  first?: number;
@@ -260,32 +274,31 @@ export function useProducts(
260
274
  query?: string;
261
275
  sortKey?: string;
262
276
  reverse?: boolean;
277
+ filters?: ProductFilterInput;
263
278
  },
264
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
279
+ options?: Omit<UseQueryOptions<NormalizedProductsResult>, 'queryKey' | 'queryFn'>
265
280
  ) {
266
- const client = getGraphQLClient();
267
- const currency = useCurrencyStore((s: any) => s.currency);
268
-
281
+ const execute = useExecute();
282
+ const currency = useCurrencyStore((s) => s.currency);
283
+
269
284
  return useQuery({
270
- queryKey: ['Products', variables, currency],
285
+ queryKey: queryKeys.products.list(variables as Record<string, unknown>, currency),
271
286
  queryFn: async () => {
272
- // Dynamic import to avoid circular dependencies
273
- const { ProductsDocument } = await import('@/generated/graphql');
274
-
275
- // Normalize sort key to GraphQL enum format
276
287
  const { sortKey: normalizedSortKey, reverse: normalizedReverse } = normalizeSortKey(variables?.sortKey);
277
-
278
- const data = await client.request(ProductsDocument, {
288
+
289
+ const graphqlVariables: ProductsQueryVariables = {
279
290
  first: variables?.first ?? 20,
280
291
  after: variables?.after,
281
292
  query: variables?.query,
282
- sortKey: normalizedSortKey as any,
293
+ sortKey: normalizedSortKey,
283
294
  reverse: variables?.reverse !== undefined ? variables.reverse : normalizedReverse,
284
- });
285
-
286
- // Normalize: edges/nodes → flat array
295
+ filters: variables?.filters,
296
+ };
297
+
298
+ const data = await execute<ProductsQuery>(ProductsDocument.toString(), graphqlVariables);
299
+
287
300
  return {
288
- products: data.products.edges.map((edge: any) => edge.node),
301
+ products: data.products.edges.map((edge) => edge.node),
289
302
  pageInfo: data.products.pageInfo,
290
303
  totalCount: data.products.totalCount,
291
304
  };
@@ -300,11 +313,7 @@ export function useProducts(
300
313
 
301
314
  /**
302
315
  * Fetch single collection by handle or ID
303
- *
304
- * @param handleOrId - Collection handle or ID
305
- * @param options - React Query options
306
- * @returns Collection query result
307
- *
316
+ *
308
317
  * @example
309
318
  * ```typescript
310
319
  * const { data } = useCollection('featured-products');
@@ -312,24 +321,20 @@ export function useProducts(
312
321
  */
313
322
  export function useCollection(
314
323
  handleOrId: string,
315
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
324
+ options?: Omit<UseQueryOptions<CollectionQuery>, 'queryKey' | 'queryFn'>
316
325
  ) {
317
- const client = getGraphQLClient();
318
- const currency = useCurrencyStore((s: any) => s.currency);
319
-
326
+ const execute = useExecute();
327
+ const currency = useCurrencyStore((s) => s.currency);
328
+
320
329
  const isId = handleOrId.startsWith('gid://');
321
-
330
+
322
331
  return useQuery({
323
- queryKey: ['Collection', handleOrId, currency],
324
- queryFn: async () => {
325
- const { CollectionDocument } = await import('@/generated/graphql');
326
-
327
- // Type-safe conditional variables
328
- const variables = isId
329
- ? { id: handleOrId } as { id: string }
330
- : { handle: handleOrId } as { handle: string };
331
-
332
- return client.request(CollectionDocument, variables as any);
332
+ queryKey: queryKeys.collections.detail(handleOrId, currency),
333
+ queryFn: () => {
334
+ const variables: CollectionQueryVariables = isId
335
+ ? { id: handleOrId }
336
+ : { handle: handleOrId };
337
+ return execute<CollectionQuery>(CollectionDocument.toString(), variables);
333
338
  },
334
339
  ...options,
335
340
  });
@@ -337,11 +342,7 @@ export function useCollection(
337
342
 
338
343
  /**
339
344
  * Fetch collections with pagination and normalization
340
- *
341
- * @param variables - Query variables
342
- * @param options - React Query options
343
- * @returns Normalized collections response
344
- *
345
+ *
345
346
  * @example
346
347
  * ```typescript
347
348
  * const { data } = useCollections({ first: 10 });
@@ -352,30 +353,29 @@ export function useCollections(
352
353
  first?: number;
353
354
  after?: string;
354
355
  query?: string;
355
- sortKey?: string;
356
+ sortKey?: CollectionSortKeys;
356
357
  reverse?: boolean;
357
358
  },
358
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
359
+ options?: Omit<UseQueryOptions<NormalizedCollectionsResult>, 'queryKey' | 'queryFn'>
359
360
  ) {
360
- const client = getGraphQLClient();
361
- const currency = useCurrencyStore((s: any) => s.currency);
362
-
361
+ const execute = useExecute();
362
+ const currency = useCurrencyStore((s) => s.currency);
363
+
363
364
  return useQuery({
364
- queryKey: ['Collections', variables, currency],
365
+ queryKey: queryKeys.collections.list(variables as Record<string, unknown>, currency),
365
366
  queryFn: async () => {
366
- const { CollectionsDocument } = await import('@/generated/graphql');
367
-
368
- const data = await client.request(CollectionsDocument, {
367
+ const graphqlVariables: CollectionsQueryVariables = {
369
368
  first: variables?.first ?? 20,
370
369
  after: variables?.after,
371
370
  query: variables?.query,
372
- sortKey: variables?.sortKey as any,
371
+ sortKey: variables?.sortKey,
373
372
  reverse: variables?.reverse,
374
- });
375
-
376
- // Normalize: edges/nodes flat array
373
+ };
374
+
375
+ const data = await execute<CollectionsQuery>(CollectionsDocument.toString(), graphqlVariables);
376
+
377
377
  return {
378
- collections: data.collections.edges.map((edge: any) => edge.node),
378
+ collections: data.collections.edges.map((edge) => edge.node),
379
379
  pageInfo: data.collections.pageInfo,
380
380
  totalCount: data.collections.totalCount,
381
381
  };
@@ -388,17 +388,38 @@ export function useCollections(
388
388
  // CATEGORY HOOKS
389
389
  // ============================================================================
390
390
 
391
+ /**
392
+ * Fetch single category by slug or ID
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * const { data } = useCategory('gift-cards');
397
+ * const category = data?.category;
398
+ * ```
399
+ */
400
+ export function useCategory(
401
+ slugOrId: string,
402
+ options?: Omit<UseQueryOptions<CategoryQuery>, 'queryKey' | 'queryFn'>
403
+ ) {
404
+ const execute = useExecute();
405
+
406
+ const isId = slugOrId.startsWith('gid://');
407
+
408
+ return useQuery({
409
+ queryKey: queryKeys.categories.detail(slugOrId),
410
+ queryFn: () => {
411
+ const variables: CategoryQueryVariables = isId
412
+ ? { id: slugOrId }
413
+ : { slug: slugOrId };
414
+ return execute<CategoryQuery>(CategoryDocument.toString(), variables);
415
+ },
416
+ ...options,
417
+ });
418
+ }
419
+
391
420
  /**
392
421
  * Fetch categories with hierarchical structure
393
- *
394
- * Categories are hierarchical (tree structure) and used for catalog organization.
395
- * Use for filters, navigation, and SEO.
396
- *
397
- * Returns flat array of all categories (roots + children) for easy filtering.
398
- *
399
- * @param options - React Query options
400
- * @returns Categories response with flat array
401
- *
422
+ *
402
423
  * @example
403
424
  * ```typescript
404
425
  * const { data } = useCategories();
@@ -406,28 +427,25 @@ export function useCollections(
406
427
  * ```
407
428
  */
408
429
  export function useCategories(
409
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
430
+ options?: Omit<UseQueryOptions<NormalizedCategoriesResult>, 'queryKey' | 'queryFn'>
410
431
  ) {
411
- const client = getGraphQLClient();
412
- const currency = useCurrencyStore((s: any) => s.currency);
413
-
432
+ const execute = useExecute();
433
+ const currency = useCurrencyStore((s) => s.currency);
434
+
414
435
  return useQuery({
415
- queryKey: ['Categories', currency],
436
+ queryKey: queryKeys.categories.list(currency),
416
437
  queryFn: async () => {
417
- const { CategoriesDocument } = await import('@/generated/graphql');
418
-
419
- const data = await client.request(CategoriesDocument);
420
-
421
- // Flatten hierarchical structure to array for filters
422
- const flattenCategories = (cats: any[]): any[] => {
423
- return cats.flatMap((cat: any) => [
438
+ const data = await execute<CategoriesQuery>(CategoriesDocument.toString());
439
+
440
+ const flattenCategories = (cats: Category[]): Category[] => {
441
+ return cats.flatMap((cat) => [
424
442
  cat,
425
- ...(cat.children ? flattenCategories(cat.children) : [])
443
+ ...(cat.children ? flattenCategories(cat.children as Category[]) : [])
426
444
  ]);
427
445
  };
428
-
429
- const allCategories = flattenCategories(data.categories.roots || []);
430
-
446
+
447
+ const allCategories = flattenCategories(data.categories.roots as Category[]);
448
+
431
449
  return {
432
450
  categories: allCategories,
433
451
  roots: data.categories.roots,
@@ -438,21 +456,47 @@ export function useCategories(
438
456
  });
439
457
  }
440
458
 
459
+ // ============================================================================
460
+ // AVAILABLE FILTERS HOOK
461
+ // ============================================================================
462
+
463
+ /**
464
+ * Fetch available filters for product listing (dynamic attributes, price range, categories).
465
+ * Context-aware: pass categoryId, collectionId, or searchQuery to scope filters.
466
+ *
467
+ * @example
468
+ * ```typescript
469
+ * const { data } = useAvailableFilters({ categoryId: 'some-id' });
470
+ * data?.availableFilters.attributes // dynamic attribute filters with counts
471
+ * data?.availableFilters.priceRange // { min, max } in context
472
+ * data?.availableFilters.categories // category filters with counts
473
+ * ```
474
+ */
475
+ export function useAvailableFilters(
476
+ input?: AvailableFiltersInput,
477
+ options?: Omit<UseQueryOptions<AvailableFiltersQuery>, 'queryKey' | 'queryFn'>
478
+ ) {
479
+ const execute = useExecute();
480
+ const currency = useCurrencyStore((s) => s.currency);
481
+
482
+ return useQuery({
483
+ queryKey: queryKeys.filters.forContext(input as Record<string, unknown>, currency),
484
+ queryFn: () => {
485
+ const variables: AvailableFiltersQueryVariables = { input: input || null };
486
+ return execute<AvailableFiltersQuery>(AvailableFiltersDocument.toString(), variables);
487
+ },
488
+ staleTime: 60_000, // Filters change infrequently, cache for 1 min
489
+ ...options,
490
+ });
491
+ }
492
+
441
493
  // ============================================================================
442
494
  // CART HOOKS
443
495
  // ============================================================================
444
496
 
445
497
  /**
446
498
  * Fetch cart by ID
447
- *
448
- * Includes currency in query key to refetch when currency changes.
449
- * Cart prices are locked for 24 hours, but currency header is still
450
- * included for consistency.
451
- *
452
- * @param cartId - Cart ID
453
- * @param options - React Query options
454
- * @returns Cart query result
455
- *
499
+ *
456
500
  * @example
457
501
  * ```typescript
458
502
  * const { data } = useCart(cartId);
@@ -460,19 +504,17 @@ export function useCategories(
460
504
  */
461
505
  export function useCart(
462
506
  cartId: string | null,
463
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
507
+ options?: Omit<UseQueryOptions<CartQuery | null>, 'queryKey' | 'queryFn'>
464
508
  ) {
465
- const client = getGraphQLClient();
466
- const currency = useCurrencyStore((s: any) => s.currency);
467
-
509
+ const execute = useExecute();
510
+ const currency = useCurrencyStore((s) => s.currency);
511
+
468
512
  return useQuery({
469
- queryKey: ['Cart', cartId, currency],
470
- queryFn: async () => {
513
+ queryKey: queryKeys.cart.detail(cartId, currency),
514
+ queryFn: () => {
471
515
  if (!cartId) return null;
472
-
473
- const { CartDocument } = await import('@/generated/graphql');
474
-
475
- return client.request(CartDocument, { id: cartId });
516
+ const variables: CartQueryVariables = { id: cartId };
517
+ return execute<CartQuery>(CartDocument.toString(), variables);
476
518
  },
477
519
  enabled: Boolean(cartId),
478
520
  ...options,
@@ -481,40 +523,22 @@ export function useCart(
481
523
 
482
524
  /**
483
525
  * Create a new cart
484
- *
485
- * @param options - React Query mutation options
486
- * @returns Cart create mutation
487
- *
488
- * @example
489
- * ```typescript
490
- * const createCart = useCartCreate();
491
- *
492
- * const handleCreate = async () => {
493
- * const { cartCreate } = await createCart.mutateAsync({
494
- * input: { lines: [...] }
495
- * });
496
- *
497
- * if (cartCreate.cart) {
498
- * // Store cart ID
499
- * localStorage.setItem('cartId', cartCreate.cart.id);
500
- * }
501
- * };
502
- * ```
503
526
  */
504
527
  export function useCartCreate(
505
- options?: UseMutationOptions<any, Error, any>
528
+ options?: UseMutationOptions<CartCreateMutation, Error, CartCreateMutationVariables>
506
529
  ) {
507
- const client = getGraphQLClient();
530
+ const execute = useExecute();
508
531
  const queryClient = useQueryClient();
509
-
532
+
510
533
  return useMutation({
511
- mutationFn: async (variables: any) => {
512
- const { CartCreateDocument } = await import('@/generated/graphql');
513
- return client.request(CartCreateDocument, variables);
534
+ mutationFn: (variables: CartCreateMutationVariables) =>
535
+ execute<CartCreateMutation>(CartCreateDocument.toString(), variables),
536
+ onMutate: async () => {
537
+ // Cancel in-flight cart queries to prevent stale data overwriting mutation result
538
+ await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
514
539
  },
515
- onSuccess: () => {
516
- // Invalidate cart queries
517
- queryClient.invalidateQueries({ queryKey: ['Cart'] });
540
+ onSettled: () => {
541
+ queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
518
542
  },
519
543
  ...options,
520
544
  });
@@ -522,36 +546,21 @@ export function useCartCreate(
522
546
 
523
547
  /**
524
548
  * Add lines to cart
525
- *
526
- * @param options - React Query mutation options
527
- * @returns Cart lines add mutation
528
- *
529
- * @example
530
- * ```typescript
531
- * const addLines = useCartLinesAdd();
532
- *
533
- * const handleAddToCart = async () => {
534
- * await addLines.mutateAsync({
535
- * cartId: 'cart-123',
536
- * lines: [{ merchandiseId: 'variant-456', quantity: 1 }]
537
- * });
538
- * };
539
- * ```
540
549
  */
541
550
  export function useCartLinesAdd(
542
- options?: UseMutationOptions<any, Error, any>
551
+ options?: UseMutationOptions<CartLinesAddMutation, Error, CartLinesAddMutationVariables>
543
552
  ) {
544
- const client = getGraphQLClient();
553
+ const execute = useExecute();
545
554
  const queryClient = useQueryClient();
546
-
555
+
547
556
  return useMutation({
548
- mutationFn: async (variables: any) => {
549
- const { CartLinesAddDocument } = await import('@/generated/graphql');
550
- return client.request(CartLinesAddDocument, variables);
557
+ mutationFn: (variables: CartLinesAddMutationVariables) =>
558
+ execute<CartLinesAddMutation>(CartLinesAddDocument.toString(), variables),
559
+ onMutate: async () => {
560
+ await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
551
561
  },
552
- onSuccess: () => {
553
- // Invalidate cart queries
554
- queryClient.invalidateQueries({ queryKey: ['Cart'] });
562
+ onSettled: () => {
563
+ queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
555
564
  },
556
565
  ...options,
557
566
  });
@@ -559,36 +568,21 @@ export function useCartLinesAdd(
559
568
 
560
569
  /**
561
570
  * Update cart lines
562
- *
563
- * @param options - React Query mutation options
564
- * @returns Cart lines update mutation
565
- *
566
- * @example
567
- * ```typescript
568
- * const updateLines = useCartLinesUpdate();
569
- *
570
- * const handleUpdateQuantity = async (lineId: string, quantity: number) => {
571
- * await updateLines.mutateAsync({
572
- * cartId: 'cart-123',
573
- * lines: [{ id: lineId, quantity }]
574
- * });
575
- * };
576
- * ```
577
571
  */
578
572
  export function useCartLinesUpdate(
579
- options?: UseMutationOptions<any, Error, any>
573
+ options?: UseMutationOptions<CartLinesUpdateMutation, Error, CartLinesUpdateMutationVariables>
580
574
  ) {
581
- const client = getGraphQLClient();
575
+ const execute = useExecute();
582
576
  const queryClient = useQueryClient();
583
-
577
+
584
578
  return useMutation({
585
- mutationFn: async (variables: any) => {
586
- const { CartLinesUpdateDocument } = await import('@/generated/graphql');
587
- return client.request(CartLinesUpdateDocument, variables);
579
+ mutationFn: (variables: CartLinesUpdateMutationVariables) =>
580
+ execute<CartLinesUpdateMutation>(CartLinesUpdateDocument.toString(), variables),
581
+ onMutate: async () => {
582
+ await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
588
583
  },
589
- onSuccess: () => {
590
- // Invalidate cart queries
591
- queryClient.invalidateQueries({ queryKey: ['Cart'] });
584
+ onSettled: () => {
585
+ queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
592
586
  },
593
587
  ...options,
594
588
  });
@@ -596,36 +590,21 @@ export function useCartLinesUpdate(
596
590
 
597
591
  /**
598
592
  * Remove lines from cart
599
- *
600
- * @param options - React Query mutation options
601
- * @returns Cart lines remove mutation
602
- *
603
- * @example
604
- * ```typescript
605
- * const removeLines = useCartLinesRemove();
606
- *
607
- * const handleRemove = async (lineId: string) => {
608
- * await removeLines.mutateAsync({
609
- * cartId: 'cart-123',
610
- * lineIds: [lineId]
611
- * });
612
- * };
613
- * ```
614
593
  */
615
594
  export function useCartLinesRemove(
616
- options?: UseMutationOptions<any, Error, any>
595
+ options?: UseMutationOptions<CartLinesRemoveMutation, Error, CartLinesRemoveMutationVariables>
617
596
  ) {
618
- const client = getGraphQLClient();
597
+ const execute = useExecute();
619
598
  const queryClient = useQueryClient();
620
-
599
+
621
600
  return useMutation({
622
- mutationFn: async (variables: any) => {
623
- const { CartLinesRemoveDocument } = await import('@/generated/graphql');
624
- return client.request(CartLinesRemoveDocument, variables);
601
+ mutationFn: (variables: CartLinesRemoveMutationVariables) =>
602
+ execute<CartLinesRemoveMutation>(CartLinesRemoveDocument.toString(), variables),
603
+ onMutate: async () => {
604
+ await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
625
605
  },
626
- onSuccess: () => {
627
- // Invalidate cart queries
628
- queryClient.invalidateQueries({ queryKey: ['Cart'] });
606
+ onSettled: () => {
607
+ queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
629
608
  },
630
609
  ...options,
631
610
  });
@@ -633,26 +612,21 @@ export function useCartLinesRemove(
633
612
 
634
613
  /**
635
614
  * Update discount codes on cart
636
- *
637
- * Invalidates cart query on success so all consumers (useCartSync)
638
- * automatically receive updated discount data.
639
- *
640
- * @param options - React Query mutation options
641
- * @returns Cart discount codes update mutation
642
615
  */
643
616
  export function useCartDiscountCodesUpdate(
644
- options?: UseMutationOptions<any, Error, any>
617
+ options?: UseMutationOptions<CartDiscountCodesUpdateMutation, Error, CartDiscountCodesUpdateMutationVariables>
645
618
  ) {
646
- const client = getGraphQLClient();
619
+ const execute = useExecute();
647
620
  const queryClient = useQueryClient();
648
621
 
649
622
  return useMutation({
650
- mutationFn: async (variables: any) => {
651
- const { CartDiscountCodesUpdateDocument } = await import('@/generated/graphql');
652
- return client.request(CartDiscountCodesUpdateDocument, variables);
623
+ mutationFn: (variables: CartDiscountCodesUpdateMutationVariables) =>
624
+ execute<CartDiscountCodesUpdateMutation>(CartDiscountCodesUpdateDocument.toString(), variables),
625
+ onMutate: async () => {
626
+ await queryClient.cancelQueries({ queryKey: queryKeys.cart.all() });
653
627
  },
654
- onSuccess: () => {
655
- queryClient.invalidateQueries({ queryKey: ['Cart'] });
628
+ onSettled: () => {
629
+ queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
656
630
  },
657
631
  ...options,
658
632
  });
@@ -664,25 +638,20 @@ export function useCartDiscountCodesUpdate(
664
638
 
665
639
  /**
666
640
  * Fetch checkout by ID
667
- *
668
- * @param checkoutId - Checkout ID
669
- * @param options - React Query options
670
- * @returns Checkout query result
671
641
  */
672
642
  export function useCheckout(
673
643
  checkoutId: string | null,
674
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
644
+ options?: Omit<UseQueryOptions<CheckoutQuery | null>, 'queryKey' | 'queryFn'>
675
645
  ) {
676
- const client = getGraphQLClient();
677
- const currency = useCurrencyStore((s: any) => s.currency);
646
+ const execute = useExecute();
647
+ const currency = useCurrencyStore((s) => s.currency);
678
648
 
679
649
  return useQuery({
680
- queryKey: ['Checkout', checkoutId, currency],
681
- queryFn: async () => {
650
+ queryKey: queryKeys.checkout.detail(checkoutId, currency),
651
+ queryFn: () => {
682
652
  if (!checkoutId) return null;
683
-
684
- const { CheckoutDocument } = await import('@/generated/graphql');
685
- return client.request(CheckoutDocument, { id: checkoutId });
653
+ const variables: CheckoutQueryVariables = { id: checkoutId };
654
+ return execute<CheckoutQuery>(CheckoutDocument.toString(), variables);
686
655
  },
687
656
  enabled: Boolean(checkoutId),
688
657
  ...options,
@@ -691,23 +660,21 @@ export function useCheckout(
691
660
 
692
661
  /**
693
662
  * Create checkout from cart
694
- *
695
- * @param options - React Query mutation options
696
- * @returns Checkout create mutation
697
663
  */
698
664
  export function useCheckoutCreate(
699
- options?: UseMutationOptions<any, Error, any>
665
+ options?: UseMutationOptions<CheckoutCreateMutation, Error, CheckoutCreateMutationVariables>
700
666
  ) {
701
- const client = getGraphQLClient();
667
+ const execute = useExecute();
702
668
  const queryClient = useQueryClient();
703
669
 
704
670
  return useMutation({
705
- mutationFn: async (variables: any) => {
706
- const { CheckoutCreateDocument } = await import('@/generated/graphql');
707
- return client.request(CheckoutCreateDocument, variables);
671
+ mutationFn: (variables: CheckoutCreateMutationVariables) =>
672
+ execute<CheckoutCreateMutation>(CheckoutCreateDocument.toString(), variables),
673
+ onMutate: async () => {
674
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
708
675
  },
709
- onSuccess: () => {
710
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
676
+ onSettled: () => {
677
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
711
678
  },
712
679
  ...options,
713
680
  });
@@ -715,23 +682,21 @@ export function useCheckoutCreate(
715
682
 
716
683
  /**
717
684
  * Update checkout shipping address
718
- *
719
- * @param options - React Query mutation options
720
- * @returns Checkout shipping address update mutation
721
685
  */
722
686
  export function useCheckoutShippingAddressUpdate(
723
- options?: UseMutationOptions<any, Error, any>
687
+ options?: UseMutationOptions<CheckoutShippingAddressUpdateMutation, Error, CheckoutShippingAddressUpdateMutationVariables>
724
688
  ) {
725
- const client = getGraphQLClient();
689
+ const execute = useExecute();
726
690
  const queryClient = useQueryClient();
727
691
 
728
692
  return useMutation({
729
- mutationFn: async (variables: any) => {
730
- const { CheckoutShippingAddressUpdateDocument } = await import('@/generated/graphql');
731
- return client.request(CheckoutShippingAddressUpdateDocument, variables);
693
+ mutationFn: (variables: CheckoutShippingAddressUpdateMutationVariables) =>
694
+ execute<CheckoutShippingAddressUpdateMutation>(CheckoutShippingAddressUpdateDocument.toString(), variables),
695
+ onMutate: async () => {
696
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
732
697
  },
733
- onSuccess: () => {
734
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
698
+ onSettled: () => {
699
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
735
700
  },
736
701
  ...options,
737
702
  });
@@ -739,23 +704,21 @@ export function useCheckoutShippingAddressUpdate(
739
704
 
740
705
  /**
741
706
  * Update checkout billing address
742
- *
743
- * @param options - React Query mutation options
744
- * @returns Checkout billing address update mutation
745
707
  */
746
708
  export function useCheckoutBillingAddressUpdate(
747
- options?: UseMutationOptions<any, Error, any>
709
+ options?: UseMutationOptions<CheckoutBillingAddressUpdateMutation, Error, CheckoutBillingAddressUpdateMutationVariables>
748
710
  ) {
749
- const client = getGraphQLClient();
711
+ const execute = useExecute();
750
712
  const queryClient = useQueryClient();
751
713
 
752
714
  return useMutation({
753
- mutationFn: async (variables: any) => {
754
- const { CheckoutBillingAddressUpdateDocument } = await import('@/generated/graphql');
755
- return client.request(CheckoutBillingAddressUpdateDocument, variables);
715
+ mutationFn: (variables: CheckoutBillingAddressUpdateMutationVariables) =>
716
+ execute<CheckoutBillingAddressUpdateMutation>(CheckoutBillingAddressUpdateDocument.toString(), variables),
717
+ onMutate: async () => {
718
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
756
719
  },
757
- onSuccess: () => {
758
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
720
+ onSettled: () => {
721
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
759
722
  },
760
723
  ...options,
761
724
  });
@@ -763,23 +726,21 @@ export function useCheckoutBillingAddressUpdate(
763
726
 
764
727
  /**
765
728
  * Update checkout email
766
- *
767
- * @param options - React Query mutation options
768
- * @returns Checkout email update mutation
769
729
  */
770
730
  export function useCheckoutEmailUpdate(
771
- options?: UseMutationOptions<any, Error, any>
731
+ options?: UseMutationOptions<CheckoutEmailUpdateMutation, Error, CheckoutEmailUpdateMutationVariables>
772
732
  ) {
773
- const client = getGraphQLClient();
733
+ const execute = useExecute();
774
734
  const queryClient = useQueryClient();
775
735
 
776
736
  return useMutation({
777
- mutationFn: async (variables: any) => {
778
- const { CheckoutEmailUpdateDocument } = await import('@/generated/graphql');
779
- return client.request(CheckoutEmailUpdateDocument, variables);
737
+ mutationFn: (variables: CheckoutEmailUpdateMutationVariables) =>
738
+ execute<CheckoutEmailUpdateMutation>(CheckoutEmailUpdateDocument.toString(), variables),
739
+ onMutate: async () => {
740
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
780
741
  },
781
- onSuccess: () => {
782
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
742
+ onSettled: () => {
743
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
783
744
  },
784
745
  ...options,
785
746
  });
@@ -787,23 +748,21 @@ export function useCheckoutEmailUpdate(
787
748
 
788
749
  /**
789
750
  * Update checkout shipping line (select shipping method)
790
- *
791
- * @param options - React Query mutation options
792
- * @returns Checkout shipping line update mutation
793
751
  */
794
752
  export function useCheckoutShippingLineUpdate(
795
- options?: UseMutationOptions<any, Error, any>
753
+ options?: UseMutationOptions<CheckoutShippingLineUpdateMutation, Error, CheckoutShippingLineUpdateMutationVariables>
796
754
  ) {
797
- const client = getGraphQLClient();
755
+ const execute = useExecute();
798
756
  const queryClient = useQueryClient();
799
757
 
800
758
  return useMutation({
801
- mutationFn: async (variables: any) => {
802
- const { CheckoutShippingLineUpdateDocument } = await import('@/generated/graphql');
803
- return client.request(CheckoutShippingLineUpdateDocument, variables);
759
+ mutationFn: (variables: CheckoutShippingLineUpdateMutationVariables) =>
760
+ execute<CheckoutShippingLineUpdateMutation>(CheckoutShippingLineUpdateDocument.toString(), variables),
761
+ onMutate: async () => {
762
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
804
763
  },
805
- onSuccess: () => {
806
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
764
+ onSettled: () => {
765
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
807
766
  },
808
767
  ...options,
809
768
  });
@@ -811,23 +770,21 @@ export function useCheckoutShippingLineUpdate(
811
770
 
812
771
  /**
813
772
  * Apply discount code to checkout
814
- *
815
- * @param options - React Query mutation options
816
- * @returns Checkout discount code apply mutation
817
773
  */
818
774
  export function useCheckoutDiscountCodeApply(
819
- options?: UseMutationOptions<any, Error, any>
775
+ options?: UseMutationOptions<CheckoutDiscountCodeApplyMutation, Error, CheckoutDiscountCodeApplyMutationVariables>
820
776
  ) {
821
- const client = getGraphQLClient();
777
+ const execute = useExecute();
822
778
  const queryClient = useQueryClient();
823
779
 
824
780
  return useMutation({
825
- mutationFn: async (variables: any) => {
826
- const { CheckoutDiscountCodeApplyDocument } = await import('@/generated/graphql');
827
- return client.request(CheckoutDiscountCodeApplyDocument, variables);
781
+ mutationFn: (variables: CheckoutDiscountCodeApplyMutationVariables) =>
782
+ execute<CheckoutDiscountCodeApplyMutation>(CheckoutDiscountCodeApplyDocument.toString(), variables),
783
+ onMutate: async () => {
784
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
828
785
  },
829
- onSuccess: () => {
830
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
786
+ onSettled: () => {
787
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
831
788
  },
832
789
  ...options,
833
790
  });
@@ -835,23 +792,21 @@ export function useCheckoutDiscountCodeApply(
835
792
 
836
793
  /**
837
794
  * Remove discount code from checkout
838
- *
839
- * @param options - React Query mutation options
840
- * @returns Checkout discount code remove mutation
841
795
  */
842
796
  export function useCheckoutDiscountCodeRemove(
843
- options?: UseMutationOptions<any, Error, any>
797
+ options?: UseMutationOptions<CheckoutDiscountCodeRemoveMutation, Error, CheckoutDiscountCodeRemoveMutationVariables>
844
798
  ) {
845
- const client = getGraphQLClient();
799
+ const execute = useExecute();
846
800
  const queryClient = useQueryClient();
847
801
 
848
802
  return useMutation({
849
- mutationFn: async (variables: any) => {
850
- const { CheckoutDiscountCodeRemoveDocument } = await import('@/generated/graphql');
851
- return client.request(CheckoutDiscountCodeRemoveDocument, variables);
803
+ mutationFn: (variables: CheckoutDiscountCodeRemoveMutationVariables) =>
804
+ execute<CheckoutDiscountCodeRemoveMutation>(CheckoutDiscountCodeRemoveDocument.toString(), variables),
805
+ onMutate: async () => {
806
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
852
807
  },
853
- onSuccess: () => {
854
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
808
+ onSettled: () => {
809
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
855
810
  },
856
811
  ...options,
857
812
  });
@@ -859,24 +814,22 @@ export function useCheckoutDiscountCodeRemove(
859
814
 
860
815
  /**
861
816
  * Complete checkout (finalize order)
862
- *
863
- * @param options - React Query mutation options
864
- * @returns Checkout complete mutation
865
817
  */
866
818
  export function useCheckoutComplete(
867
- options?: UseMutationOptions<any, Error, any>
819
+ options?: UseMutationOptions<CheckoutCompleteMutation, Error, CheckoutCompleteMutationVariables>
868
820
  ) {
869
- const client = getGraphQLClient();
821
+ const execute = useExecute();
870
822
  const queryClient = useQueryClient();
871
823
 
872
824
  return useMutation({
873
- mutationFn: async (variables: any) => {
874
- const { CheckoutCompleteDocument } = await import('@/generated/graphql');
875
- return client.request(CheckoutCompleteDocument, variables);
825
+ mutationFn: (variables: CheckoutCompleteMutationVariables) =>
826
+ execute<CheckoutCompleteMutation>(CheckoutCompleteDocument.toString(), variables),
827
+ onMutate: async () => {
828
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
876
829
  },
877
- onSuccess: () => {
878
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
879
- queryClient.invalidateQueries({ queryKey: ['Cart'] });
830
+ onSettled: () => {
831
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
832
+ queryClient.invalidateQueries({ queryKey: queryKeys.cart.all() });
880
833
  },
881
834
  ...options,
882
835
  });
@@ -884,23 +837,21 @@ export function useCheckoutComplete(
884
837
 
885
838
  /**
886
839
  * Apply gift card code to checkout
887
- *
888
- * @param options - React Query mutation options
889
- * @returns Checkout gift card apply mutation
890
840
  */
891
841
  export function useCheckoutGiftCardApply(
892
- options?: UseMutationOptions<any, Error, any>
842
+ options?: UseMutationOptions<CheckoutGiftCardApplyMutation, Error, CheckoutGiftCardApplyMutationVariables>
893
843
  ) {
894
- const client = getGraphQLClient();
844
+ const execute = useExecute();
895
845
  const queryClient = useQueryClient();
896
846
 
897
847
  return useMutation({
898
- mutationFn: async (variables: any) => {
899
- const { CheckoutGiftCardApplyDocument } = await import('@/generated/graphql');
900
- return client.request(CheckoutGiftCardApplyDocument, variables);
848
+ mutationFn: (variables: CheckoutGiftCardApplyMutationVariables) =>
849
+ execute<CheckoutGiftCardApplyMutation>(CheckoutGiftCardApplyDocument.toString(), variables),
850
+ onMutate: async () => {
851
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
901
852
  },
902
- onSuccess: () => {
903
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
853
+ onSettled: () => {
854
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
904
855
  },
905
856
  ...options,
906
857
  });
@@ -908,23 +859,21 @@ export function useCheckoutGiftCardApply(
908
859
 
909
860
  /**
910
861
  * Remove gift card from checkout
911
- *
912
- * @param options - React Query mutation options
913
- * @returns Checkout gift card remove mutation
914
862
  */
915
863
  export function useCheckoutGiftCardRemove(
916
- options?: UseMutationOptions<any, Error, any>
864
+ options?: UseMutationOptions<CheckoutGiftCardRemoveMutation, Error, CheckoutGiftCardRemoveMutationVariables>
917
865
  ) {
918
- const client = getGraphQLClient();
866
+ const execute = useExecute();
919
867
  const queryClient = useQueryClient();
920
868
 
921
869
  return useMutation({
922
- mutationFn: async (variables: any) => {
923
- const { CheckoutGiftCardRemoveDocument } = await import('@/generated/graphql');
924
- return client.request(CheckoutGiftCardRemoveDocument, variables);
870
+ mutationFn: (variables: CheckoutGiftCardRemoveMutationVariables) =>
871
+ execute<CheckoutGiftCardRemoveMutation>(CheckoutGiftCardRemoveDocument.toString(), variables),
872
+ onMutate: async () => {
873
+ await queryClient.cancelQueries({ queryKey: queryKeys.checkout.all() });
925
874
  },
926
- onSuccess: () => {
927
- queryClient.invalidateQueries({ queryKey: ['Checkout'] });
875
+ onSettled: () => {
876
+ queryClient.invalidateQueries({ queryKey: queryKeys.checkout.all() });
928
877
  },
929
878
  ...options,
930
879
  });
@@ -934,78 +883,217 @@ export function useCheckoutGiftCardRemove(
934
883
  // CUSTOMER HOOKS
935
884
  // ============================================================================
936
885
 
886
+ /**
887
+ * Fetch current customer data (profile, addresses, orders).
888
+ * Automatically reads accessToken from auth store.
889
+ * Query is disabled until accessToken is available (after Zustand persist rehydration).
890
+ */
891
+ export function useCustomer(
892
+ options?: Omit<UseQueryOptions<CustomerQuery>, 'queryKey' | 'queryFn' | 'enabled'>
893
+ ) {
894
+ const execute = useExecute();
895
+ const accessToken = useAuthStore((s) => s.accessToken);
896
+
897
+ return useQuery({
898
+ queryKey: queryKeys.customer.detail(accessToken || ''),
899
+ queryFn: () =>
900
+ execute<CustomerQuery>(CustomerDocument.toString(), {
901
+ customerAccessToken: accessToken!,
902
+ } as CustomerQueryVariables),
903
+ enabled: !!accessToken,
904
+ ...options,
905
+ });
906
+ }
907
+
908
+ /**
909
+ * Lightweight customer profile query (no orders, no addresses list).
910
+ * Use for settings/profile pages that only need basic customer info.
911
+ */
912
+ export function useCustomerProfile(
913
+ options?: Omit<UseQueryOptions<CustomerProfileQuery>, 'queryKey' | 'queryFn' | 'enabled'>
914
+ ) {
915
+ const execute = useExecute();
916
+ const accessToken = useAuthStore((s) => s.accessToken);
917
+
918
+ return useQuery({
919
+ queryKey: queryKeys.customer.detail(accessToken || ''),
920
+ queryFn: () =>
921
+ execute<CustomerProfileQuery>(CustomerProfileDocument.toString(), {
922
+ customerAccessToken: accessToken!,
923
+ } as CustomerProfileQueryVariables),
924
+ enabled: !!accessToken,
925
+ ...options,
926
+ });
927
+ }
928
+
929
+ /**
930
+ * Fetch single order by ID for authenticated customer.
931
+ * More efficient than useCustomer() — fetches only the requested order.
932
+ */
933
+ export function useCustomerOrder(
934
+ orderId: string,
935
+ options?: Omit<UseQueryOptions<CustomerOrderQuery>, 'queryKey' | 'queryFn' | 'enabled'>
936
+ ) {
937
+ const execute = useExecute();
938
+ const accessToken = useAuthStore((s) => s.accessToken);
939
+ const currency = useCurrencyStore((s) => s.currency);
940
+
941
+ return useQuery({
942
+ queryKey: queryKeys.customer.order(orderId, currency),
943
+ queryFn: () =>
944
+ execute<CustomerOrderQuery>(CustomerOrderDocument.toString(), {
945
+ orderId,
946
+ customerAccessToken: accessToken!,
947
+ } as CustomerOrderQueryVariables),
948
+ enabled: !!accessToken && !!orderId,
949
+ ...options,
950
+ });
951
+ }
952
+
953
+ /**
954
+ * Update customer profile (firstName, lastName, phone).
955
+ * Automatically invalidates all customer queries on settle.
956
+ */
957
+ export function useCustomerUpdate(
958
+ options?: UseMutationOptions<CustomerUpdateMutation, Error, CustomerUpdateInput>
959
+ ) {
960
+ const execute = useExecute();
961
+ const { accessToken } = useAuthStore();
962
+ const queryClient = useQueryClient();
963
+
964
+ return useMutation({
965
+ mutationFn: (customer: CustomerUpdateInput) =>
966
+ execute<CustomerUpdateMutation>(CustomerUpdateDocument.toString(), {
967
+ customer,
968
+ customerAccessToken: accessToken!,
969
+ } as CustomerUpdateMutationVariables),
970
+ onSettled: () => {
971
+ queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
972
+ },
973
+ ...options,
974
+ });
975
+ }
976
+
977
+ /**
978
+ * Create a new customer address.
979
+ */
980
+ export function useCustomerAddressCreate(
981
+ options?: UseMutationOptions<CustomerAddressCreateMutation, Error, { address: Record<string, unknown> }>
982
+ ) {
983
+ const execute = useExecute();
984
+ const { accessToken } = useAuthStore();
985
+ const queryClient = useQueryClient();
986
+
987
+ return useMutation({
988
+ mutationFn: ({ address }: { address: Record<string, unknown> }) =>
989
+ execute<CustomerAddressCreateMutation>(
990
+ CustomerAddressCreateDocument.toString(),
991
+ { address, customerAccessToken: accessToken! } as CustomerAddressCreateMutationVariables,
992
+ ),
993
+ onSettled: () => {
994
+ queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
995
+ },
996
+ ...options,
997
+ });
998
+ }
999
+
1000
+ /**
1001
+ * Update an existing customer address.
1002
+ */
1003
+ export function useCustomerAddressUpdate(
1004
+ options?: UseMutationOptions<CustomerAddressUpdateMutation, Error, { id: string; address: Record<string, unknown> }>
1005
+ ) {
1006
+ const execute = useExecute();
1007
+ const { accessToken } = useAuthStore();
1008
+ const queryClient = useQueryClient();
1009
+
1010
+ return useMutation({
1011
+ mutationFn: ({ id, address }: { id: string; address: Record<string, unknown> }) =>
1012
+ execute<CustomerAddressUpdateMutation>(
1013
+ CustomerAddressUpdateDocument.toString(),
1014
+ { id, address, customerAccessToken: accessToken! } as CustomerAddressUpdateMutationVariables,
1015
+ ),
1016
+ onSettled: () => {
1017
+ queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
1018
+ },
1019
+ ...options,
1020
+ });
1021
+ }
1022
+
1023
+ /**
1024
+ * Delete a customer address.
1025
+ */
1026
+ export function useCustomerAddressDelete(
1027
+ options?: UseMutationOptions<CustomerAddressDeleteMutation, Error, string>
1028
+ ) {
1029
+ const execute = useExecute();
1030
+ const { accessToken } = useAuthStore();
1031
+ const queryClient = useQueryClient();
1032
+
1033
+ return useMutation({
1034
+ mutationFn: (id: string) =>
1035
+ execute<CustomerAddressDeleteMutation>(
1036
+ CustomerAddressDeleteDocument.toString(),
1037
+ { id, customerAccessToken: accessToken! } as CustomerAddressDeleteMutationVariables,
1038
+ ),
1039
+ onSettled: () => {
1040
+ queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
1041
+ },
1042
+ ...options,
1043
+ });
1044
+ }
1045
+
1046
+ /**
1047
+ * Set default customer address.
1048
+ */
1049
+ export function useCustomerDefaultAddressUpdate(
1050
+ options?: UseMutationOptions<CustomerDefaultAddressUpdateMutation, Error, string>
1051
+ ) {
1052
+ const execute = useExecute();
1053
+ const { accessToken } = useAuthStore();
1054
+ const queryClient = useQueryClient();
1055
+
1056
+ return useMutation({
1057
+ mutationFn: (addressId: string) =>
1058
+ execute<CustomerDefaultAddressUpdateMutation>(
1059
+ CustomerDefaultAddressUpdateDocument.toString(),
1060
+ { addressId, customerAccessToken: accessToken! } as CustomerDefaultAddressUpdateMutationVariables,
1061
+ ),
1062
+ onSettled: () => {
1063
+ queryClient.invalidateQueries({ queryKey: queryKeys.customer.all() });
1064
+ },
1065
+ ...options,
1066
+ });
1067
+ }
1068
+
937
1069
  /**
938
1070
  * Customer login mutation
939
- *
940
- * @param options - React Query mutation options
941
- * @returns Customer login mutation
942
- *
943
- * @example
944
- * ```typescript
945
- * const login = useCustomerLogin();
946
- *
947
- * const handleLogin = async (email: string, password: string) => {
948
- * const { customerAccessTokenCreate } = await login.mutateAsync({
949
- * input: { email, password }
950
- * });
951
- *
952
- * if (customerAccessTokenCreate.customerAccessToken) {
953
- * // Store token in httpOnly cookie via API route
954
- * await fetch('/api/auth/set-token', {
955
- * method: 'POST',
956
- * body: JSON.stringify({
957
- * token: customerAccessTokenCreate.customerAccessToken.accessToken
958
- * })
959
- * });
960
- * }
961
- * };
962
- * ```
963
1071
  */
964
1072
  export function useCustomerLogin(
965
- options?: UseMutationOptions<any, Error, any>
1073
+ options?: UseMutationOptions<CustomerLoginMutation, Error, CustomerLoginMutationVariables>
966
1074
  ) {
967
- const client = getGraphQLClient();
968
-
1075
+ const execute = useExecute();
1076
+
969
1077
  return useMutation({
970
- mutationFn: async (variables: any) => {
971
- const { CustomerLoginDocument } = await import('@/generated/graphql');
972
- return client.request(CustomerLoginDocument, variables);
973
- },
1078
+ mutationFn: (variables: CustomerLoginMutationVariables) =>
1079
+ execute<CustomerLoginMutation>(CustomerLoginDocument.toString(), variables),
974
1080
  ...options,
975
1081
  });
976
1082
  }
977
1083
 
978
1084
  /**
979
1085
  * Customer logout mutation
980
- *
981
- * @param options - React Query mutation options
982
- * @returns Customer logout mutation
983
- *
984
- * @example
985
- * ```typescript
986
- * const logout = useCustomerLogout();
987
- *
988
- * const handleLogout = async () => {
989
- * await logout.mutateAsync({ customerAccessToken: token });
990
- *
991
- * // Clear cookie via API route
992
- * await fetch('/api/auth/clear-token', { method: 'POST' });
993
- * };
994
- * ```
995
1086
  */
996
1087
  export function useCustomerLogout(
997
- options?: UseMutationOptions<any, Error, any>
1088
+ options?: UseMutationOptions<CustomerLogoutMutation, Error, CustomerLogoutMutationVariables>
998
1089
  ) {
999
- const client = getGraphQLClient();
1090
+ const execute = useExecute();
1000
1091
  const queryClient = useQueryClient();
1001
-
1092
+
1002
1093
  return useMutation({
1003
- mutationFn: async (variables: any) => {
1004
- const { CustomerLogoutDocument } = await import('@/generated/graphql');
1005
- return client.request(CustomerLogoutDocument, variables);
1006
- },
1094
+ mutationFn: (variables: CustomerLogoutMutationVariables) =>
1095
+ execute<CustomerLogoutMutation>(CustomerLogoutDocument.toString(), variables),
1007
1096
  onSuccess: () => {
1008
- // Clear all queries on logout
1009
1097
  queryClient.clear();
1010
1098
  },
1011
1099
  ...options,
@@ -1018,139 +1106,114 @@ export function useCustomerLogout(
1018
1106
 
1019
1107
  /**
1020
1108
  * Fetch loyalty member data
1021
- *
1022
- * @param options - React Query options
1023
- * @returns Loyalty member query result
1024
1109
  */
1025
1110
  export function useLoyaltyMember(
1026
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
1111
+ options?: Omit<UseQueryOptions<LoyaltyMemberQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1027
1112
  ) {
1028
- const client = getGraphQLClient();
1029
- const currency = useCurrencyStore((s: any) => s.currency);
1113
+ const execute = useExecute();
1114
+ const accessToken = useAuthStore((s) => s.accessToken);
1115
+ const currency = useCurrencyStore((s) => s.currency);
1030
1116
 
1031
1117
  return useQuery({
1032
- queryKey: ['LoyaltyMember', currency],
1033
- queryFn: async () => {
1034
- const { LoyaltyMemberDocument } = await import('@/generated/graphql');
1035
- return client.request(LoyaltyMemberDocument);
1036
- },
1118
+ queryKey: queryKeys.loyalty.member(currency),
1119
+ queryFn: () => execute<LoyaltyMemberQuery>(LoyaltyMemberDocument.toString()),
1120
+ enabled: !!accessToken,
1037
1121
  ...options,
1038
1122
  });
1039
1123
  }
1040
1124
 
1041
1125
  /**
1042
1126
  * Fetch loyalty rewards
1043
- *
1044
- * @param options - React Query options
1045
- * @returns Loyalty rewards query result
1046
1127
  */
1047
1128
  export function useLoyaltyRewards(
1048
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
1129
+ options?: Omit<UseQueryOptions<LoyaltyRewardsQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1049
1130
  ) {
1050
- const client = getGraphQLClient();
1051
- const currency = useCurrencyStore((s: any) => s.currency);
1131
+ const execute = useExecute();
1132
+ const accessToken = useAuthStore((s) => s.accessToken);
1133
+ const currency = useCurrencyStore((s) => s.currency);
1052
1134
 
1053
1135
  return useQuery({
1054
- queryKey: ['LoyaltyRewards', currency],
1055
- queryFn: async () => {
1056
- const { LoyaltyRewardsDocument } = await import('@/generated/graphql');
1057
- return client.request(LoyaltyRewardsDocument);
1058
- },
1136
+ queryKey: queryKeys.loyalty.rewards(currency),
1137
+ queryFn: () => execute<LoyaltyRewardsQuery>(LoyaltyRewardsDocument.toString()),
1138
+ enabled: !!accessToken,
1059
1139
  ...options,
1060
1140
  });
1061
1141
  }
1062
1142
 
1063
1143
  /**
1064
1144
  * Fetch loyalty transactions with pagination
1065
- *
1066
- * @param variables - Query variables (first, after)
1067
- * @param options - React Query options
1068
- * @returns Loyalty transactions query result
1069
1145
  */
1070
1146
  export function useLoyaltyTransactions(
1071
1147
  variables?: { first?: number; after?: string },
1072
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
1148
+ options?: Omit<UseQueryOptions<LoyaltyTransactionsQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1073
1149
  ) {
1074
- const client = getGraphQLClient();
1075
- const currency = useCurrencyStore((s: any) => s.currency);
1150
+ const execute = useExecute();
1151
+ const accessToken = useAuthStore((s) => s.accessToken);
1152
+ const currency = useCurrencyStore((s) => s.currency);
1076
1153
 
1077
1154
  return useQuery({
1078
- queryKey: ['LoyaltyTransactions', variables, currency],
1079
- queryFn: async () => {
1080
- const { LoyaltyTransactionsDocument } = await import('@/generated/graphql');
1081
- return client.request(LoyaltyTransactionsDocument, {
1155
+ queryKey: queryKeys.loyalty.transactions(variables as Record<string, unknown>, currency),
1156
+ queryFn: () => {
1157
+ const graphqlVariables: LoyaltyTransactionsQueryVariables = {
1082
1158
  first: variables?.first ?? 20,
1083
1159
  after: variables?.after,
1084
- });
1160
+ };
1161
+ return execute<LoyaltyTransactionsQuery>(LoyaltyTransactionsDocument.toString(), graphqlVariables);
1085
1162
  },
1163
+ enabled: !!accessToken,
1086
1164
  ...options,
1087
1165
  });
1088
1166
  }
1089
1167
 
1090
1168
  /**
1091
1169
  * Fetch loyalty settings
1092
- *
1093
- * @param options - React Query options
1094
- * @returns Loyalty settings query result
1095
1170
  */
1096
1171
  export function useLoyaltySettings(
1097
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
1172
+ options?: Omit<UseQueryOptions<LoyaltySettingsQuery>, 'queryKey' | 'queryFn'>
1098
1173
  ) {
1099
- const client = getGraphQLClient();
1174
+ const execute = useExecute();
1100
1175
 
1101
1176
  return useQuery({
1102
- queryKey: ['LoyaltySettings'],
1103
- queryFn: async () => {
1104
- const { LoyaltySettingsDocument } = await import('@/generated/graphql');
1105
- return client.request(LoyaltySettingsDocument);
1106
- },
1177
+ queryKey: queryKeys.loyalty.settings(),
1178
+ queryFn: () => execute<LoyaltySettingsQuery>(LoyaltySettingsDocument.toString()),
1107
1179
  ...options,
1108
1180
  });
1109
1181
  }
1110
1182
 
1111
1183
  /**
1112
1184
  * Fetch referral stats
1113
- *
1114
- * @param options - React Query options
1115
- * @returns Referral stats query result
1116
1185
  */
1117
1186
  export function useReferralStats(
1118
- options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
1187
+ options?: Omit<UseQueryOptions<ReferralStatsQuery>, 'queryKey' | 'queryFn' | 'enabled'>
1119
1188
  ) {
1120
- const client = getGraphQLClient();
1189
+ const execute = useExecute();
1190
+ const accessToken = useAuthStore((s) => s.accessToken);
1121
1191
 
1122
1192
  return useQuery({
1123
- queryKey: ['ReferralStats'],
1124
- queryFn: async () => {
1125
- const { ReferralStatsDocument } = await import('@/generated/graphql');
1126
- return client.request(ReferralStatsDocument);
1127
- },
1193
+ queryKey: queryKeys.loyalty.referralStats(),
1194
+ queryFn: () => execute<ReferralStatsQuery>(ReferralStatsDocument.toString()),
1195
+ enabled: !!accessToken,
1128
1196
  ...options,
1129
1197
  });
1130
1198
  }
1131
1199
 
1132
1200
  /**
1133
1201
  * Redeem loyalty reward mutation
1134
- *
1135
- * @param options - React Query mutation options
1136
- * @returns Redeem reward mutation
1137
1202
  */
1138
1203
  export function useRedeemLoyaltyReward(
1139
- options?: UseMutationOptions<any, Error, any>
1204
+ options?: UseMutationOptions<RedeemLoyaltyRewardMutation, Error, RedeemLoyaltyRewardMutationVariables>
1140
1205
  ) {
1141
- const client = getGraphQLClient();
1206
+ const execute = useExecute();
1142
1207
  const queryClient = useQueryClient();
1208
+ const currency = useCurrencyStore((s) => s.currency);
1143
1209
 
1144
1210
  return useMutation({
1145
- mutationFn: async (variables: any) => {
1146
- const { RedeemLoyaltyRewardDocument } = await import('@/generated/graphql');
1147
- return client.request(RedeemLoyaltyRewardDocument, variables);
1148
- },
1211
+ mutationFn: (variables: RedeemLoyaltyRewardMutationVariables) =>
1212
+ execute<RedeemLoyaltyRewardMutation>(RedeemLoyaltyRewardDocument.toString(), variables),
1149
1213
  onSuccess: () => {
1150
- // Invalidate loyalty queries to refresh points and rewards
1151
- queryClient.invalidateQueries({ queryKey: ['LoyaltyMember'] });
1152
- queryClient.invalidateQueries({ queryKey: ['LoyaltyRewards'] });
1153
- queryClient.invalidateQueries({ queryKey: ['LoyaltyTransactions'] });
1214
+ queryClient.invalidateQueries({ queryKey: queryKeys.loyalty.member(currency) });
1215
+ queryClient.invalidateQueries({ queryKey: queryKeys.loyalty.rewards(currency) });
1216
+ queryClient.invalidateQueries({ queryKey: queryKeys.loyalty.transactions() });
1154
1217
  },
1155
1218
  ...options,
1156
1219
  });
@@ -1162,21 +1225,13 @@ export function useRedeemLoyaltyReward(
1162
1225
 
1163
1226
  /**
1164
1227
  * Hook to invalidate all queries when currency changes
1165
- *
1228
+ *
1166
1229
  * This is automatically handled by including currency in query keys,
1167
1230
  * but this utility can be used for manual invalidation if needed.
1168
- *
1169
- * @example
1170
- * ```typescript
1171
- * const invalidateOnCurrencyChange = useInvalidateOnCurrencyChange();
1172
- *
1173
- * // Manually trigger invalidation
1174
- * invalidateOnCurrencyChange();
1175
- * ```
1176
1231
  */
1177
1232
  export function useInvalidateOnCurrencyChange() {
1178
1233
  const queryClient = useQueryClient();
1179
-
1234
+
1180
1235
  return () => {
1181
1236
  queryClient.invalidateQueries();
1182
1237
  };