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