@doswiftly/cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -0
- package/bin/doswiftly.js +2 -0
- package/dist/commands/auth-github.d.ts +6 -0
- package/dist/commands/auth-github.d.ts.map +1 -0
- package/dist/commands/auth-github.js +89 -0
- package/dist/commands/auth-github.js.map +1 -0
- package/dist/commands/auth-token.d.ts +12 -0
- package/dist/commands/auth-token.d.ts.map +1 -0
- package/dist/commands/auth-token.js +43 -0
- package/dist/commands/auth-token.js.map +1 -0
- package/dist/commands/auth.d.ts +22 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +348 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +234 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +104 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/deploy.d.ts +37 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +580 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +83 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +363 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/domain.d.ts +13 -0
- package/dist/commands/domain.d.ts.map +1 -0
- package/dist/commands/domain.js +128 -0
- package/dist/commands/domain.js.map +1 -0
- package/dist/commands/env.d.ts +25 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +228 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1028 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inspect.d.ts +12 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +162 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/migrate.d.ts +18 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +355 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/preview.d.ts +29 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +199 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/proxy.d.ts +9 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +37 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/sdk.d.ts +5 -0
- package/dist/commands/sdk.d.ts.map +1 -0
- package/dist/commands/sdk.js +82 -0
- package/dist/commands/sdk.js.map +1 -0
- package/dist/commands/template.d.ts +107 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +1309 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/types.d.ts +5 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +82 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +103 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/upgrade.d.ts +18 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +55 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/verify.d.ts +5 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +232 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +60 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/config/types.d.ts +173 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +48 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +416 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-url.d.ts +14 -0
- package/dist/lib/api-url.d.ts.map +1 -0
- package/dist/lib/api-url.js +24 -0
- package/dist/lib/api-url.js.map +1 -0
- package/dist/lib/api.d.ts +67 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +36 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.d.ts +39 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +195 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env-storage.d.ts +140 -0
- package/dist/lib/env-storage.d.ts.map +1 -0
- package/dist/lib/env-storage.js +464 -0
- package/dist/lib/env-storage.js.map +1 -0
- package/dist/lib/errors.d.ts +61 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +204 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/i18n.d.ts +99 -0
- package/dist/lib/i18n.d.ts.map +1 -0
- package/dist/lib/i18n.js +184 -0
- package/dist/lib/i18n.js.map +1 -0
- package/dist/lib/logger.d.ts +95 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +168 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/package-manager.d.ts +91 -0
- package/dist/lib/package-manager.d.ts.map +1 -0
- package/dist/lib/package-manager.js +205 -0
- package/dist/lib/package-manager.js.map +1 -0
- package/dist/lib/proxy-server.d.ts +24 -0
- package/dist/lib/proxy-server.d.ts.map +1 -0
- package/dist/lib/proxy-server.js +173 -0
- package/dist/lib/proxy-server.js.map +1 -0
- package/dist/lib/select-with-back.d.ts +34 -0
- package/dist/lib/select-with-back.d.ts.map +1 -0
- package/dist/lib/select-with-back.js +94 -0
- package/dist/lib/select-with-back.js.map +1 -0
- package/dist/lib/shared-api-client.d.ts +40 -0
- package/dist/lib/shared-api-client.d.ts.map +1 -0
- package/dist/lib/shared-api-client.js +92 -0
- package/dist/lib/shared-api-client.js.map +1 -0
- package/dist/lib/wizard-engine.d.ts +128 -0
- package/dist/lib/wizard-engine.d.ts.map +1 -0
- package/dist/lib/wizard-engine.js +168 -0
- package/dist/lib/wizard-engine.js.map +1 -0
- package/package.json +85 -0
- package/templates/storefront-minimal/.env.example +10 -0
- package/templates/storefront-minimal/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-minimal/app/globals.css +18 -0
- package/templates/storefront-minimal/app/layout.tsx +26 -0
- package/templates/storefront-minimal/app/page.tsx +93 -0
- package/templates/storefront-minimal/lib/graphql-client.ts +23 -0
- package/templates/storefront-minimal/next.config.ts +15 -0
- package/templates/storefront-minimal/open-next.config.ts +3 -0
- package/templates/storefront-minimal/package.json +30 -0
- package/templates/storefront-minimal/postcss.config.mjs +5 -0
- package/templates/storefront-minimal/tailwind.config.ts +14 -0
- package/templates/storefront-minimal/tsconfig.json +27 -0
- package/templates/storefront-minimal/wrangler.toml +9 -0
- package/templates/storefront-nextjs/.env.example +68 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-nextjs/.github/workflows/deploy.yml +25 -0
- package/templates/storefront-nextjs/.github/workflows/preview.yml +22 -0
- package/templates/storefront-nextjs/README.md +520 -0
- package/templates/storefront-nextjs/app/account/orders/page.tsx +216 -0
- package/templates/storefront-nextjs/app/account/page.tsx +167 -0
- package/templates/storefront-nextjs/app/auth/login/page.tsx +135 -0
- package/templates/storefront-nextjs/app/auth/register/page.tsx +228 -0
- package/templates/storefront-nextjs/app/cart/page.tsx +263 -0
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +200 -0
- package/templates/storefront-nextjs/app/categories/page.tsx +58 -0
- package/templates/storefront-nextjs/app/checkout/page.tsx +351 -0
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +158 -0
- package/templates/storefront-nextjs/app/collections/page.tsx +61 -0
- package/templates/storefront-nextjs/app/globals.css +98 -0
- package/templates/storefront-nextjs/app/layout.tsx +39 -0
- package/templates/storefront-nextjs/app/page.tsx +136 -0
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +119 -0
- package/templates/storefront-nextjs/app/products/page.tsx +107 -0
- package/templates/storefront-nextjs/app/search/page.tsx +127 -0
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +94 -0
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +77 -0
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +29 -0
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +217 -0
- package/templates/storefront-nextjs/components/commerce/pagination.tsx +62 -0
- package/templates/storefront-nextjs/components/commerce/product-actions.tsx +135 -0
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +109 -0
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +375 -0
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +178 -0
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +64 -0
- package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +210 -0
- package/templates/storefront-nextjs/components/layout/footer.tsx +107 -0
- package/templates/storefront-nextjs/components/layout/header.tsx +104 -0
- package/templates/storefront-nextjs/components/providers.tsx +62 -0
- package/templates/storefront-nextjs/lib/auth/routes.ts +52 -0
- package/templates/storefront-nextjs/lib/currency.tsx +140 -0
- package/templates/storefront-nextjs/lib/format.ts +159 -0
- package/templates/storefront-nextjs/lib/graphql-queries.ts +629 -0
- package/templates/storefront-nextjs/lib/hooks.ts +30 -0
- package/templates/storefront-nextjs/middleware.ts +80 -0
- package/templates/storefront-nextjs/next.config.ts +37 -0
- package/templates/storefront-nextjs/open-next.config.ts +3 -0
- package/templates/storefront-nextjs/package.dev.json +30 -0
- package/templates/storefront-nextjs/package.json +32 -0
- package/templates/storefront-nextjs/package.json.template +32 -0
- package/templates/storefront-nextjs/postcss.config.mjs +8 -0
- package/templates/storefront-nextjs/tailwind.config.ts +111 -0
- package/templates/storefront-nextjs/tsconfig.json +27 -0
- package/templates/storefront-nextjs/wrangler.toml +9 -0
- package/templates/storefront-nextjs-shadcn/.env.example +68 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +25 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +22 -0
- package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +282 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +96 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +190 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +263 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +135 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +142 -0
- package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +448 -0
- package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +307 -0
- package/templates/storefront-nextjs-shadcn/README.md +195 -0
- package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +245 -0
- package/templates/storefront-nextjs-shadcn/app/about/page.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +215 -0
- package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +484 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +128 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/tracking/page.tsx +206 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +80 -0
- package/templates/storefront-nextjs-shadcn/app/account/page.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +195 -0
- package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +87 -0
- package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +125 -0
- package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +131 -0
- package/templates/storefront-nextjs-shadcn/app/auth/login/page.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/app/auth/register/page.tsx +20 -0
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +323 -0
- package/templates/storefront-nextjs-shadcn/app/blog/page.tsx +159 -0
- package/templates/storefront-nextjs-shadcn/app/brands/[slug]/page.tsx +170 -0
- package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +73 -0
- package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +165 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +78 -0
- package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +75 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +1752 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/success/[orderId]/page.tsx +256 -0
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +74 -0
- package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +75 -0
- package/templates/storefront-nextjs-shadcn/app/contact/page.tsx +114 -0
- package/templates/storefront-nextjs-shadcn/app/error.tsx +90 -0
- package/templates/storefront-nextjs-shadcn/app/globals.css +125 -0
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +57 -0
- package/templates/storefront-nextjs-shadcn/app/not-found.tsx +68 -0
- package/templates/storefront-nextjs-shadcn/app/page.tsx +21 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +246 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +343 -0
- package/templates/storefront-nextjs-shadcn/app/products/page.tsx +25 -0
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +192 -0
- package/templates/storefront-nextjs-shadcn/app/returns/page.tsx +77 -0
- package/templates/storefront-nextjs-shadcn/app/robots.ts +53 -0
- package/templates/storefront-nextjs-shadcn/app/search/page.tsx +16 -0
- package/templates/storefront-nextjs-shadcn/app/search/search-client.tsx +47 -0
- package/templates/storefront-nextjs-shadcn/app/shipping/page.tsx +62 -0
- package/templates/storefront-nextjs-shadcn/app/sitemap.ts +144 -0
- package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +179 -0
- package/templates/storefront-nextjs-shadcn/codegen.ts +51 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +348 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +144 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +258 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +132 -0
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +188 -0
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +305 -0
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +240 -0
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +177 -0
- package/templates/storefront-nextjs-shadcn/components/blog/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/components/brand/brand-grid.tsx +64 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +48 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +112 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +84 -0
- package/templates/storefront-nextjs-shadcn/components/cart/index.ts +17 -0
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +121 -0
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +162 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/index.ts +25 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +187 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +160 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +154 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +225 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +62 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +158 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +174 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/variant-selector.tsx +210 -0
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +97 -0
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +187 -0
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +151 -0
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +166 -0
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +245 -0
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +246 -0
- package/templates/storefront-nextjs-shadcn/components/discount/index.ts +19 -0
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +113 -0
- package/templates/storefront-nextjs-shadcn/components/error/index.ts +7 -0
- package/templates/storefront-nextjs-shadcn/components/filters/attribute-filter.tsx +153 -0
- package/templates/storefront-nextjs-shadcn/components/filters/checkbox-group-filter.tsx +167 -0
- package/templates/storefront-nextjs-shadcn/components/filters/color-swatch-filter.tsx +176 -0
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +220 -0
- package/templates/storefront-nextjs-shadcn/components/filters/index.ts +36 -0
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +193 -0
- package/templates/storefront-nextjs-shadcn/components/filters/toggle-filter.tsx +132 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +321 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +309 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/index.ts +24 -0
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +72 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +85 -0
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +108 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +133 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +341 -0
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +128 -0
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +147 -0
- package/templates/storefront-nextjs-shadcn/components/layout/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +211 -0
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +95 -0
- package/templates/storefront-nextjs-shadcn/components/layout/theme-switcher.tsx +192 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/index.ts +11 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +93 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +177 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +250 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +217 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-badge.tsx +106 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +131 -0
- package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +196 -0
- package/templates/storefront-nextjs-shadcn/components/order/index.ts +11 -0
- package/templates/storefront-nextjs-shadcn/components/order/order-tracking.tsx +200 -0
- package/templates/storefront-nextjs-shadcn/components/order/shipment-card.tsx +407 -0
- package/templates/storefront-nextjs-shadcn/components/order/tracking-status.tsx +222 -0
- package/templates/storefront-nextjs-shadcn/components/order/tracking-timeline.tsx +205 -0
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +161 -0
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +250 -0
- package/templates/storefront-nextjs-shadcn/components/product/discount-badge.tsx +196 -0
- package/templates/storefront-nextjs-shadcn/components/product/index.ts +41 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +147 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +217 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-gallery.tsx +143 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +83 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +155 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +158 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-quantity-selector.tsx +111 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +238 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +169 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +220 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +338 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-summary.tsx +143 -0
- package/templates/storefront-nextjs-shadcn/components/product/sale-countdown.tsx +166 -0
- package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +213 -0
- package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +57 -0
- package/templates/storefront-nextjs-shadcn/components/product/stock-indicator.tsx +91 -0
- package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +103 -0
- package/templates/storefront-nextjs-shadcn/components/providers/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/providers/query-provider.tsx +260 -0
- package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +13 -0
- package/templates/storefront-nextjs-shadcn/components/returns/index.ts +26 -0
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +608 -0
- package/templates/storefront-nextjs-shadcn/components/returns/return-status-card.tsx +554 -0
- package/templates/storefront-nextjs-shadcn/components/search/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-suggestions.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/components/seo/index.ts +12 -0
- package/templates/storefront-nextjs-shadcn/components/seo/json-ld.tsx +56 -0
- package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +167 -0
- package/templates/storefront-nextjs-shadcn/components/shipping/index.ts +16 -0
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +337 -0
- package/templates/storefront-nextjs-shadcn/components/ui/accordion.tsx +153 -0
- package/templates/storefront-nextjs-shadcn/components/ui/alert.tsx +59 -0
- package/templates/storefront-nextjs-shadcn/components/ui/badge.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/components/ui/button.tsx +51 -0
- package/templates/storefront-nextjs-shadcn/components/ui/card.tsx +77 -0
- package/templates/storefront-nextjs-shadcn/components/ui/checkbox.tsx +30 -0
- package/templates/storefront-nextjs-shadcn/components/ui/dialog.tsx +137 -0
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +207 -0
- package/templates/storefront-nextjs-shadcn/components/ui/index.ts +67 -0
- package/templates/storefront-nextjs-shadcn/components/ui/input.tsx +65 -0
- package/templates/storefront-nextjs-shadcn/components/ui/label.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/components/ui/pagination.tsx +205 -0
- package/templates/storefront-nextjs-shadcn/components/ui/radio-group.tsx +44 -0
- package/templates/storefront-nextjs-shadcn/components/ui/select.tsx +160 -0
- package/templates/storefront-nextjs-shadcn/components/ui/separator.tsx +28 -0
- package/templates/storefront-nextjs-shadcn/components/ui/skeleton.tsx +20 -0
- package/templates/storefront-nextjs-shadcn/components/ui/spinner.tsx +82 -0
- package/templates/storefront-nextjs-shadcn/components/ui/tabs.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/components/ui/toast.tsx +96 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +148 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +47 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +165 -0
- package/templates/storefront-nextjs-shadcn/components.json +19 -0
- package/templates/storefront-nextjs-shadcn/generated/.gitkeep +2 -0
- package/templates/storefront-nextjs-shadcn/graphql/.gitkeep +31 -0
- package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +168 -0
- package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +160 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +310 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +286 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +110 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.test.ts +173 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.ts +298 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +220 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +57 -0
- package/templates/storefront-nextjs-shadcn/lib/config.ts +46 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +254 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/README.md +464 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +328 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +295 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +27 -0
- package/templates/storefront-nextjs-shadcn/lib/format.test.ts +397 -0
- package/templates/storefront-nextjs-shadcn/lib/format.ts +226 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +109 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +1183 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +267 -0
- package/templates/storefront-nextjs-shadcn/lib/hooks.ts +30 -0
- package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +89 -0
- package/templates/storefront-nextjs-shadcn/lib/utils.ts +6 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +47 -0
- package/templates/storefront-nextjs-shadcn/open-next.config.ts +3 -0
- package/templates/storefront-nextjs-shadcn/package.dev.json +30 -0
- package/templates/storefront-nextjs-shadcn/package.json +60 -0
- package/templates/storefront-nextjs-shadcn/package.json.template +46 -0
- package/templates/storefront-nextjs-shadcn/postcss.config.mjs +8 -0
- package/templates/storefront-nextjs-shadcn/proxy.ts +80 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/apple-pay.svg +8 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/bank-transfer.svg +10 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/blik.svg +6 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/cash-on-delivery.svg +11 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/google-pay.svg +11 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/mastercard.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/paypal.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/payu.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/przelewy24.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/stripe.svg +4 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/visa.svg +5 -0
- package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +66 -0
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +56 -0
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +184 -0
- package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +103 -0
- package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +291 -0
- package/templates/storefront-nextjs-shadcn/tailwind.config.ts +111 -0
- package/templates/storefront-nextjs-shadcn/tsconfig.json +27 -0
- package/templates/storefront-nextjs-shadcn/wrangler.toml +9 -0
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
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';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// TYPES
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalized response for paginated queries
|
|
14
|
+
* Converts GraphQL edges/nodes structure to flat arrays
|
|
15
|
+
*/
|
|
16
|
+
export interface NormalizedConnection<T> {
|
|
17
|
+
items: T[];
|
|
18
|
+
pageInfo: {
|
|
19
|
+
hasNextPage: boolean;
|
|
20
|
+
hasPreviousPage: boolean;
|
|
21
|
+
startCursor?: string | null;
|
|
22
|
+
endCursor?: string | null;
|
|
23
|
+
};
|
|
24
|
+
totalCount?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* GraphQL connection edge structure
|
|
29
|
+
*/
|
|
30
|
+
interface Edge<T> {
|
|
31
|
+
node: T;
|
|
32
|
+
cursor: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* GraphQL connection structure
|
|
37
|
+
*/
|
|
38
|
+
interface Connection<T> {
|
|
39
|
+
edges: Edge<T>[];
|
|
40
|
+
pageInfo: {
|
|
41
|
+
hasNextPage: boolean;
|
|
42
|
+
hasPreviousPage: boolean;
|
|
43
|
+
startCursor?: string | null;
|
|
44
|
+
endCursor?: string | null;
|
|
45
|
+
};
|
|
46
|
+
totalCount?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// UTILITY FUNCTIONS
|
|
51
|
+
// ============================================================================
|
|
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
|
+
/**
|
|
84
|
+
* Generic query hook with currency-aware caching
|
|
85
|
+
*
|
|
86
|
+
* Wraps React Query's useQuery with automatic currency injection
|
|
87
|
+
* 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
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const { data } = useGraphQLQuery(ProductDocument, { handle: 'my-product' });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function useGraphQLQuery<TResult, TVariables>(
|
|
100
|
+
document: TypedDocumentNode<TResult, TVariables>,
|
|
101
|
+
variables?: TVariables,
|
|
102
|
+
options?: Omit<UseQueryOptions<TResult>, 'queryKey' | 'queryFn'>
|
|
103
|
+
) {
|
|
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
|
+
|
|
116
|
+
return useQuery({
|
|
117
|
+
queryKey,
|
|
118
|
+
queryFn: () => client.request(document, variables as any),
|
|
119
|
+
...options,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 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
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const mutation = useGraphQLMutation(CartCreateDocument);
|
|
135
|
+
* await mutation.mutateAsync({ input: {...} });
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export function useGraphQLMutation<TResult, TVariables>(
|
|
139
|
+
document: TypedDocumentNode<TResult, TVariables>,
|
|
140
|
+
options?: UseMutationOptions<TResult, Error, TVariables>
|
|
141
|
+
) {
|
|
142
|
+
const client = getGraphQLClient();
|
|
143
|
+
|
|
144
|
+
return useMutation({
|
|
145
|
+
mutationFn: (variables: TVariables) => client.request(document, variables as any),
|
|
146
|
+
...options,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ============================================================================
|
|
151
|
+
// PRODUCT HOOKS
|
|
152
|
+
// ============================================================================
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 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
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```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
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export function useProduct(
|
|
184
|
+
handleOrId: string,
|
|
185
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
186
|
+
) {
|
|
187
|
+
const client = getGraphQLClient();
|
|
188
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
189
|
+
|
|
190
|
+
// Determine if it's an ID or handle
|
|
191
|
+
const isId = handleOrId.startsWith('gid://');
|
|
192
|
+
|
|
193
|
+
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);
|
|
201
|
+
},
|
|
202
|
+
...options,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
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
|
+
/**
|
|
237
|
+
* Map frontend sort values to GraphQL enum values
|
|
238
|
+
*/
|
|
239
|
+
function normalizeSortKey(sortKey?: string): { sortKey?: string; reverse?: boolean } {
|
|
240
|
+
if (!sortKey) return { sortKey: 'BEST_SELLING', reverse: false };
|
|
241
|
+
|
|
242
|
+
const sortMap: Record<string, { sortKey: string; reverse: boolean }> = {
|
|
243
|
+
'relevance': { sortKey: 'RELEVANCE', reverse: false },
|
|
244
|
+
'best-selling': { sortKey: 'BEST_SELLING', reverse: false },
|
|
245
|
+
'price-low-to-high': { sortKey: 'PRICE', reverse: false },
|
|
246
|
+
'price-high-to-low': { sortKey: 'PRICE', reverse: true },
|
|
247
|
+
'title-asc': { sortKey: 'TITLE', reverse: false },
|
|
248
|
+
'title-desc': { sortKey: 'TITLE', reverse: true },
|
|
249
|
+
'created-desc': { sortKey: 'CREATED', reverse: true },
|
|
250
|
+
'created-asc': { sortKey: 'CREATED', reverse: false },
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
return sortMap[sortKey] || { sortKey: 'BEST_SELLING', reverse: false };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function useProducts(
|
|
257
|
+
variables?: {
|
|
258
|
+
first?: number;
|
|
259
|
+
after?: string;
|
|
260
|
+
query?: string;
|
|
261
|
+
sortKey?: string;
|
|
262
|
+
reverse?: boolean;
|
|
263
|
+
},
|
|
264
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
265
|
+
) {
|
|
266
|
+
const client = getGraphQLClient();
|
|
267
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
268
|
+
|
|
269
|
+
return useQuery({
|
|
270
|
+
queryKey: ['Products', variables, currency],
|
|
271
|
+
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
|
+
const { sortKey: normalizedSortKey, reverse: normalizedReverse } = normalizeSortKey(variables?.sortKey);
|
|
277
|
+
|
|
278
|
+
const data = await client.request(ProductsDocument, {
|
|
279
|
+
first: variables?.first ?? 20,
|
|
280
|
+
after: variables?.after,
|
|
281
|
+
query: variables?.query,
|
|
282
|
+
sortKey: normalizedSortKey as any,
|
|
283
|
+
reverse: variables?.reverse !== undefined ? variables.reverse : normalizedReverse,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Normalize: edges/nodes → flat array
|
|
287
|
+
return {
|
|
288
|
+
products: data.products.edges.map((edge: any) => edge.node),
|
|
289
|
+
pageInfo: data.products.pageInfo,
|
|
290
|
+
totalCount: data.products.totalCount,
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
...options,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ============================================================================
|
|
298
|
+
// COLLECTION HOOKS
|
|
299
|
+
// ============================================================================
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 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
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```typescript
|
|
310
|
+
* const { data } = useCollection('featured-products');
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
export function useCollection(
|
|
314
|
+
handleOrId: string,
|
|
315
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
316
|
+
) {
|
|
317
|
+
const client = getGraphQLClient();
|
|
318
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
319
|
+
|
|
320
|
+
const isId = handleOrId.startsWith('gid://');
|
|
321
|
+
|
|
322
|
+
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);
|
|
333
|
+
},
|
|
334
|
+
...options,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Fetch collections with pagination and normalization
|
|
340
|
+
*
|
|
341
|
+
* @param variables - Query variables
|
|
342
|
+
* @param options - React Query options
|
|
343
|
+
* @returns Normalized collections response
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```typescript
|
|
347
|
+
* const { data } = useCollections({ first: 10 });
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
export function useCollections(
|
|
351
|
+
variables?: {
|
|
352
|
+
first?: number;
|
|
353
|
+
after?: string;
|
|
354
|
+
query?: string;
|
|
355
|
+
sortKey?: string;
|
|
356
|
+
reverse?: boolean;
|
|
357
|
+
},
|
|
358
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
359
|
+
) {
|
|
360
|
+
const client = getGraphQLClient();
|
|
361
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
362
|
+
|
|
363
|
+
return useQuery({
|
|
364
|
+
queryKey: ['Collections', variables, currency],
|
|
365
|
+
queryFn: async () => {
|
|
366
|
+
const { CollectionsDocument } = await import('@/generated/graphql');
|
|
367
|
+
|
|
368
|
+
const data = await client.request(CollectionsDocument, {
|
|
369
|
+
first: variables?.first ?? 20,
|
|
370
|
+
after: variables?.after,
|
|
371
|
+
query: variables?.query,
|
|
372
|
+
sortKey: variables?.sortKey as any,
|
|
373
|
+
reverse: variables?.reverse,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Normalize: edges/nodes → flat array
|
|
377
|
+
return {
|
|
378
|
+
collections: data.collections.edges.map((edge: any) => edge.node),
|
|
379
|
+
pageInfo: data.collections.pageInfo,
|
|
380
|
+
totalCount: data.collections.totalCount,
|
|
381
|
+
};
|
|
382
|
+
},
|
|
383
|
+
...options,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ============================================================================
|
|
388
|
+
// CATEGORY HOOKS
|
|
389
|
+
// ============================================================================
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* 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
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```typescript
|
|
404
|
+
* const { data } = useCategories();
|
|
405
|
+
* const categories = data?.categories ?? [];
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
export function useCategories(
|
|
409
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
410
|
+
) {
|
|
411
|
+
const client = getGraphQLClient();
|
|
412
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
413
|
+
|
|
414
|
+
return useQuery({
|
|
415
|
+
queryKey: ['Categories', currency],
|
|
416
|
+
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) => [
|
|
424
|
+
cat,
|
|
425
|
+
...(cat.children ? flattenCategories(cat.children) : [])
|
|
426
|
+
]);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const allCategories = flattenCategories(data.categories.roots || []);
|
|
430
|
+
|
|
431
|
+
return {
|
|
432
|
+
categories: allCategories,
|
|
433
|
+
roots: data.categories.roots,
|
|
434
|
+
totalCount: data.categories.totalCount,
|
|
435
|
+
};
|
|
436
|
+
},
|
|
437
|
+
...options,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ============================================================================
|
|
442
|
+
// CART HOOKS
|
|
443
|
+
// ============================================================================
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* 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
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```typescript
|
|
458
|
+
* const { data } = useCart(cartId);
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
export function useCart(
|
|
462
|
+
cartId: string | null,
|
|
463
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
464
|
+
) {
|
|
465
|
+
const client = getGraphQLClient();
|
|
466
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
467
|
+
|
|
468
|
+
return useQuery({
|
|
469
|
+
queryKey: ['Cart', cartId, currency],
|
|
470
|
+
queryFn: async () => {
|
|
471
|
+
if (!cartId) return null;
|
|
472
|
+
|
|
473
|
+
const { CartDocument } = await import('@/generated/graphql');
|
|
474
|
+
|
|
475
|
+
return client.request(CartDocument, { id: cartId });
|
|
476
|
+
},
|
|
477
|
+
enabled: Boolean(cartId),
|
|
478
|
+
...options,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* 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
|
+
*/
|
|
504
|
+
export function useCartCreate(
|
|
505
|
+
options?: UseMutationOptions<any, Error, any>
|
|
506
|
+
) {
|
|
507
|
+
const client = getGraphQLClient();
|
|
508
|
+
const queryClient = useQueryClient();
|
|
509
|
+
|
|
510
|
+
return useMutation({
|
|
511
|
+
mutationFn: async (variables: any) => {
|
|
512
|
+
const { CartCreateDocument } = await import('@/generated/graphql');
|
|
513
|
+
return client.request(CartCreateDocument, variables);
|
|
514
|
+
},
|
|
515
|
+
onSuccess: () => {
|
|
516
|
+
// Invalidate cart queries
|
|
517
|
+
queryClient.invalidateQueries({ queryKey: ['Cart'] });
|
|
518
|
+
},
|
|
519
|
+
...options,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* 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
|
+
*/
|
|
541
|
+
export function useCartLinesAdd(
|
|
542
|
+
options?: UseMutationOptions<any, Error, any>
|
|
543
|
+
) {
|
|
544
|
+
const client = getGraphQLClient();
|
|
545
|
+
const queryClient = useQueryClient();
|
|
546
|
+
|
|
547
|
+
return useMutation({
|
|
548
|
+
mutationFn: async (variables: any) => {
|
|
549
|
+
const { CartLinesAddDocument } = await import('@/generated/graphql');
|
|
550
|
+
return client.request(CartLinesAddDocument, variables);
|
|
551
|
+
},
|
|
552
|
+
onSuccess: () => {
|
|
553
|
+
// Invalidate cart queries
|
|
554
|
+
queryClient.invalidateQueries({ queryKey: ['Cart'] });
|
|
555
|
+
},
|
|
556
|
+
...options,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* 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
|
+
*/
|
|
578
|
+
export function useCartLinesUpdate(
|
|
579
|
+
options?: UseMutationOptions<any, Error, any>
|
|
580
|
+
) {
|
|
581
|
+
const client = getGraphQLClient();
|
|
582
|
+
const queryClient = useQueryClient();
|
|
583
|
+
|
|
584
|
+
return useMutation({
|
|
585
|
+
mutationFn: async (variables: any) => {
|
|
586
|
+
const { CartLinesUpdateDocument } = await import('@/generated/graphql');
|
|
587
|
+
return client.request(CartLinesUpdateDocument, variables);
|
|
588
|
+
},
|
|
589
|
+
onSuccess: () => {
|
|
590
|
+
// Invalidate cart queries
|
|
591
|
+
queryClient.invalidateQueries({ queryKey: ['Cart'] });
|
|
592
|
+
},
|
|
593
|
+
...options,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* 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
|
+
*/
|
|
615
|
+
export function useCartLinesRemove(
|
|
616
|
+
options?: UseMutationOptions<any, Error, any>
|
|
617
|
+
) {
|
|
618
|
+
const client = getGraphQLClient();
|
|
619
|
+
const queryClient = useQueryClient();
|
|
620
|
+
|
|
621
|
+
return useMutation({
|
|
622
|
+
mutationFn: async (variables: any) => {
|
|
623
|
+
const { CartLinesRemoveDocument } = await import('@/generated/graphql');
|
|
624
|
+
return client.request(CartLinesRemoveDocument, variables);
|
|
625
|
+
},
|
|
626
|
+
onSuccess: () => {
|
|
627
|
+
// Invalidate cart queries
|
|
628
|
+
queryClient.invalidateQueries({ queryKey: ['Cart'] });
|
|
629
|
+
},
|
|
630
|
+
...options,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* 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
|
+
*/
|
|
643
|
+
export function useCartDiscountCodesUpdate(
|
|
644
|
+
options?: UseMutationOptions<any, Error, any>
|
|
645
|
+
) {
|
|
646
|
+
const client = getGraphQLClient();
|
|
647
|
+
const queryClient = useQueryClient();
|
|
648
|
+
|
|
649
|
+
return useMutation({
|
|
650
|
+
mutationFn: async (variables: any) => {
|
|
651
|
+
const { CartDiscountCodesUpdateDocument } = await import('@/generated/graphql');
|
|
652
|
+
return client.request(CartDiscountCodesUpdateDocument, variables);
|
|
653
|
+
},
|
|
654
|
+
onSuccess: () => {
|
|
655
|
+
queryClient.invalidateQueries({ queryKey: ['Cart'] });
|
|
656
|
+
},
|
|
657
|
+
...options,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ============================================================================
|
|
662
|
+
// CHECKOUT HOOKS
|
|
663
|
+
// ============================================================================
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Fetch checkout by ID
|
|
667
|
+
*
|
|
668
|
+
* @param checkoutId - Checkout ID
|
|
669
|
+
* @param options - React Query options
|
|
670
|
+
* @returns Checkout query result
|
|
671
|
+
*/
|
|
672
|
+
export function useCheckout(
|
|
673
|
+
checkoutId: string | null,
|
|
674
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
675
|
+
) {
|
|
676
|
+
const client = getGraphQLClient();
|
|
677
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
678
|
+
|
|
679
|
+
return useQuery({
|
|
680
|
+
queryKey: ['Checkout', checkoutId, currency],
|
|
681
|
+
queryFn: async () => {
|
|
682
|
+
if (!checkoutId) return null;
|
|
683
|
+
|
|
684
|
+
const { CheckoutDocument } = await import('@/generated/graphql');
|
|
685
|
+
return client.request(CheckoutDocument, { id: checkoutId });
|
|
686
|
+
},
|
|
687
|
+
enabled: Boolean(checkoutId),
|
|
688
|
+
...options,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Create checkout from cart
|
|
694
|
+
*
|
|
695
|
+
* @param options - React Query mutation options
|
|
696
|
+
* @returns Checkout create mutation
|
|
697
|
+
*/
|
|
698
|
+
export function useCheckoutCreate(
|
|
699
|
+
options?: UseMutationOptions<any, Error, any>
|
|
700
|
+
) {
|
|
701
|
+
const client = getGraphQLClient();
|
|
702
|
+
const queryClient = useQueryClient();
|
|
703
|
+
|
|
704
|
+
return useMutation({
|
|
705
|
+
mutationFn: async (variables: any) => {
|
|
706
|
+
const { CheckoutCreateDocument } = await import('@/generated/graphql');
|
|
707
|
+
return client.request(CheckoutCreateDocument, variables);
|
|
708
|
+
},
|
|
709
|
+
onSuccess: () => {
|
|
710
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
711
|
+
},
|
|
712
|
+
...options,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Update checkout shipping address
|
|
718
|
+
*
|
|
719
|
+
* @param options - React Query mutation options
|
|
720
|
+
* @returns Checkout shipping address update mutation
|
|
721
|
+
*/
|
|
722
|
+
export function useCheckoutShippingAddressUpdate(
|
|
723
|
+
options?: UseMutationOptions<any, Error, any>
|
|
724
|
+
) {
|
|
725
|
+
const client = getGraphQLClient();
|
|
726
|
+
const queryClient = useQueryClient();
|
|
727
|
+
|
|
728
|
+
return useMutation({
|
|
729
|
+
mutationFn: async (variables: any) => {
|
|
730
|
+
const { CheckoutShippingAddressUpdateDocument } = await import('@/generated/graphql');
|
|
731
|
+
return client.request(CheckoutShippingAddressUpdateDocument, variables);
|
|
732
|
+
},
|
|
733
|
+
onSuccess: () => {
|
|
734
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
735
|
+
},
|
|
736
|
+
...options,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Update checkout billing address
|
|
742
|
+
*
|
|
743
|
+
* @param options - React Query mutation options
|
|
744
|
+
* @returns Checkout billing address update mutation
|
|
745
|
+
*/
|
|
746
|
+
export function useCheckoutBillingAddressUpdate(
|
|
747
|
+
options?: UseMutationOptions<any, Error, any>
|
|
748
|
+
) {
|
|
749
|
+
const client = getGraphQLClient();
|
|
750
|
+
const queryClient = useQueryClient();
|
|
751
|
+
|
|
752
|
+
return useMutation({
|
|
753
|
+
mutationFn: async (variables: any) => {
|
|
754
|
+
const { CheckoutBillingAddressUpdateDocument } = await import('@/generated/graphql');
|
|
755
|
+
return client.request(CheckoutBillingAddressUpdateDocument, variables);
|
|
756
|
+
},
|
|
757
|
+
onSuccess: () => {
|
|
758
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
759
|
+
},
|
|
760
|
+
...options,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Update checkout email
|
|
766
|
+
*
|
|
767
|
+
* @param options - React Query mutation options
|
|
768
|
+
* @returns Checkout email update mutation
|
|
769
|
+
*/
|
|
770
|
+
export function useCheckoutEmailUpdate(
|
|
771
|
+
options?: UseMutationOptions<any, Error, any>
|
|
772
|
+
) {
|
|
773
|
+
const client = getGraphQLClient();
|
|
774
|
+
const queryClient = useQueryClient();
|
|
775
|
+
|
|
776
|
+
return useMutation({
|
|
777
|
+
mutationFn: async (variables: any) => {
|
|
778
|
+
const { CheckoutEmailUpdateDocument } = await import('@/generated/graphql');
|
|
779
|
+
return client.request(CheckoutEmailUpdateDocument, variables);
|
|
780
|
+
},
|
|
781
|
+
onSuccess: () => {
|
|
782
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
783
|
+
},
|
|
784
|
+
...options,
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Update checkout shipping line (select shipping method)
|
|
790
|
+
*
|
|
791
|
+
* @param options - React Query mutation options
|
|
792
|
+
* @returns Checkout shipping line update mutation
|
|
793
|
+
*/
|
|
794
|
+
export function useCheckoutShippingLineUpdate(
|
|
795
|
+
options?: UseMutationOptions<any, Error, any>
|
|
796
|
+
) {
|
|
797
|
+
const client = getGraphQLClient();
|
|
798
|
+
const queryClient = useQueryClient();
|
|
799
|
+
|
|
800
|
+
return useMutation({
|
|
801
|
+
mutationFn: async (variables: any) => {
|
|
802
|
+
const { CheckoutShippingLineUpdateDocument } = await import('@/generated/graphql');
|
|
803
|
+
return client.request(CheckoutShippingLineUpdateDocument, variables);
|
|
804
|
+
},
|
|
805
|
+
onSuccess: () => {
|
|
806
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
807
|
+
},
|
|
808
|
+
...options,
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Apply discount code to checkout
|
|
814
|
+
*
|
|
815
|
+
* @param options - React Query mutation options
|
|
816
|
+
* @returns Checkout discount code apply mutation
|
|
817
|
+
*/
|
|
818
|
+
export function useCheckoutDiscountCodeApply(
|
|
819
|
+
options?: UseMutationOptions<any, Error, any>
|
|
820
|
+
) {
|
|
821
|
+
const client = getGraphQLClient();
|
|
822
|
+
const queryClient = useQueryClient();
|
|
823
|
+
|
|
824
|
+
return useMutation({
|
|
825
|
+
mutationFn: async (variables: any) => {
|
|
826
|
+
const { CheckoutDiscountCodeApplyDocument } = await import('@/generated/graphql');
|
|
827
|
+
return client.request(CheckoutDiscountCodeApplyDocument, variables);
|
|
828
|
+
},
|
|
829
|
+
onSuccess: () => {
|
|
830
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
831
|
+
},
|
|
832
|
+
...options,
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Remove discount code from checkout
|
|
838
|
+
*
|
|
839
|
+
* @param options - React Query mutation options
|
|
840
|
+
* @returns Checkout discount code remove mutation
|
|
841
|
+
*/
|
|
842
|
+
export function useCheckoutDiscountCodeRemove(
|
|
843
|
+
options?: UseMutationOptions<any, Error, any>
|
|
844
|
+
) {
|
|
845
|
+
const client = getGraphQLClient();
|
|
846
|
+
const queryClient = useQueryClient();
|
|
847
|
+
|
|
848
|
+
return useMutation({
|
|
849
|
+
mutationFn: async (variables: any) => {
|
|
850
|
+
const { CheckoutDiscountCodeRemoveDocument } = await import('@/generated/graphql');
|
|
851
|
+
return client.request(CheckoutDiscountCodeRemoveDocument, variables);
|
|
852
|
+
},
|
|
853
|
+
onSuccess: () => {
|
|
854
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
855
|
+
},
|
|
856
|
+
...options,
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Complete checkout (finalize order)
|
|
862
|
+
*
|
|
863
|
+
* @param options - React Query mutation options
|
|
864
|
+
* @returns Checkout complete mutation
|
|
865
|
+
*/
|
|
866
|
+
export function useCheckoutComplete(
|
|
867
|
+
options?: UseMutationOptions<any, Error, any>
|
|
868
|
+
) {
|
|
869
|
+
const client = getGraphQLClient();
|
|
870
|
+
const queryClient = useQueryClient();
|
|
871
|
+
|
|
872
|
+
return useMutation({
|
|
873
|
+
mutationFn: async (variables: any) => {
|
|
874
|
+
const { CheckoutCompleteDocument } = await import('@/generated/graphql');
|
|
875
|
+
return client.request(CheckoutCompleteDocument, variables);
|
|
876
|
+
},
|
|
877
|
+
onSuccess: () => {
|
|
878
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
879
|
+
queryClient.invalidateQueries({ queryKey: ['Cart'] });
|
|
880
|
+
},
|
|
881
|
+
...options,
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Apply gift card code to checkout
|
|
887
|
+
*
|
|
888
|
+
* @param options - React Query mutation options
|
|
889
|
+
* @returns Checkout gift card apply mutation
|
|
890
|
+
*/
|
|
891
|
+
export function useCheckoutGiftCardApply(
|
|
892
|
+
options?: UseMutationOptions<any, Error, any>
|
|
893
|
+
) {
|
|
894
|
+
const client = getGraphQLClient();
|
|
895
|
+
const queryClient = useQueryClient();
|
|
896
|
+
|
|
897
|
+
return useMutation({
|
|
898
|
+
mutationFn: async (variables: any) => {
|
|
899
|
+
const { CheckoutGiftCardApplyDocument } = await import('@/generated/graphql');
|
|
900
|
+
return client.request(CheckoutGiftCardApplyDocument, variables);
|
|
901
|
+
},
|
|
902
|
+
onSuccess: () => {
|
|
903
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
904
|
+
},
|
|
905
|
+
...options,
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Remove gift card from checkout
|
|
911
|
+
*
|
|
912
|
+
* @param options - React Query mutation options
|
|
913
|
+
* @returns Checkout gift card remove mutation
|
|
914
|
+
*/
|
|
915
|
+
export function useCheckoutGiftCardRemove(
|
|
916
|
+
options?: UseMutationOptions<any, Error, any>
|
|
917
|
+
) {
|
|
918
|
+
const client = getGraphQLClient();
|
|
919
|
+
const queryClient = useQueryClient();
|
|
920
|
+
|
|
921
|
+
return useMutation({
|
|
922
|
+
mutationFn: async (variables: any) => {
|
|
923
|
+
const { CheckoutGiftCardRemoveDocument } = await import('@/generated/graphql');
|
|
924
|
+
return client.request(CheckoutGiftCardRemoveDocument, variables);
|
|
925
|
+
},
|
|
926
|
+
onSuccess: () => {
|
|
927
|
+
queryClient.invalidateQueries({ queryKey: ['Checkout'] });
|
|
928
|
+
},
|
|
929
|
+
...options,
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// ============================================================================
|
|
934
|
+
// CUSTOMER HOOKS
|
|
935
|
+
// ============================================================================
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* 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
|
+
*/
|
|
964
|
+
export function useCustomerLogin(
|
|
965
|
+
options?: UseMutationOptions<any, Error, any>
|
|
966
|
+
) {
|
|
967
|
+
const client = getGraphQLClient();
|
|
968
|
+
|
|
969
|
+
return useMutation({
|
|
970
|
+
mutationFn: async (variables: any) => {
|
|
971
|
+
const { CustomerLoginDocument } = await import('@/generated/graphql');
|
|
972
|
+
return client.request(CustomerLoginDocument, variables);
|
|
973
|
+
},
|
|
974
|
+
...options,
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* 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
|
+
*/
|
|
996
|
+
export function useCustomerLogout(
|
|
997
|
+
options?: UseMutationOptions<any, Error, any>
|
|
998
|
+
) {
|
|
999
|
+
const client = getGraphQLClient();
|
|
1000
|
+
const queryClient = useQueryClient();
|
|
1001
|
+
|
|
1002
|
+
return useMutation({
|
|
1003
|
+
mutationFn: async (variables: any) => {
|
|
1004
|
+
const { CustomerLogoutDocument } = await import('@/generated/graphql');
|
|
1005
|
+
return client.request(CustomerLogoutDocument, variables);
|
|
1006
|
+
},
|
|
1007
|
+
onSuccess: () => {
|
|
1008
|
+
// Clear all queries on logout
|
|
1009
|
+
queryClient.clear();
|
|
1010
|
+
},
|
|
1011
|
+
...options,
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// ============================================================================
|
|
1016
|
+
// LOYALTY HOOKS
|
|
1017
|
+
// ============================================================================
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Fetch loyalty member data
|
|
1021
|
+
*
|
|
1022
|
+
* @param options - React Query options
|
|
1023
|
+
* @returns Loyalty member query result
|
|
1024
|
+
*/
|
|
1025
|
+
export function useLoyaltyMember(
|
|
1026
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
1027
|
+
) {
|
|
1028
|
+
const client = getGraphQLClient();
|
|
1029
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
1030
|
+
|
|
1031
|
+
return useQuery({
|
|
1032
|
+
queryKey: ['LoyaltyMember', currency],
|
|
1033
|
+
queryFn: async () => {
|
|
1034
|
+
const { LoyaltyMemberDocument } = await import('@/generated/graphql');
|
|
1035
|
+
return client.request(LoyaltyMemberDocument);
|
|
1036
|
+
},
|
|
1037
|
+
...options,
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Fetch loyalty rewards
|
|
1043
|
+
*
|
|
1044
|
+
* @param options - React Query options
|
|
1045
|
+
* @returns Loyalty rewards query result
|
|
1046
|
+
*/
|
|
1047
|
+
export function useLoyaltyRewards(
|
|
1048
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
1049
|
+
) {
|
|
1050
|
+
const client = getGraphQLClient();
|
|
1051
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
1052
|
+
|
|
1053
|
+
return useQuery({
|
|
1054
|
+
queryKey: ['LoyaltyRewards', currency],
|
|
1055
|
+
queryFn: async () => {
|
|
1056
|
+
const { LoyaltyRewardsDocument } = await import('@/generated/graphql');
|
|
1057
|
+
return client.request(LoyaltyRewardsDocument);
|
|
1058
|
+
},
|
|
1059
|
+
...options,
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* 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
|
+
*/
|
|
1070
|
+
export function useLoyaltyTransactions(
|
|
1071
|
+
variables?: { first?: number; after?: string },
|
|
1072
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
1073
|
+
) {
|
|
1074
|
+
const client = getGraphQLClient();
|
|
1075
|
+
const currency = useCurrencyStore((s: any) => s.currency);
|
|
1076
|
+
|
|
1077
|
+
return useQuery({
|
|
1078
|
+
queryKey: ['LoyaltyTransactions', variables, currency],
|
|
1079
|
+
queryFn: async () => {
|
|
1080
|
+
const { LoyaltyTransactionsDocument } = await import('@/generated/graphql');
|
|
1081
|
+
return client.request(LoyaltyTransactionsDocument, {
|
|
1082
|
+
first: variables?.first ?? 20,
|
|
1083
|
+
after: variables?.after,
|
|
1084
|
+
});
|
|
1085
|
+
},
|
|
1086
|
+
...options,
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Fetch loyalty settings
|
|
1092
|
+
*
|
|
1093
|
+
* @param options - React Query options
|
|
1094
|
+
* @returns Loyalty settings query result
|
|
1095
|
+
*/
|
|
1096
|
+
export function useLoyaltySettings(
|
|
1097
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
1098
|
+
) {
|
|
1099
|
+
const client = getGraphQLClient();
|
|
1100
|
+
|
|
1101
|
+
return useQuery({
|
|
1102
|
+
queryKey: ['LoyaltySettings'],
|
|
1103
|
+
queryFn: async () => {
|
|
1104
|
+
const { LoyaltySettingsDocument } = await import('@/generated/graphql');
|
|
1105
|
+
return client.request(LoyaltySettingsDocument);
|
|
1106
|
+
},
|
|
1107
|
+
...options,
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Fetch referral stats
|
|
1113
|
+
*
|
|
1114
|
+
* @param options - React Query options
|
|
1115
|
+
* @returns Referral stats query result
|
|
1116
|
+
*/
|
|
1117
|
+
export function useReferralStats(
|
|
1118
|
+
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
1119
|
+
) {
|
|
1120
|
+
const client = getGraphQLClient();
|
|
1121
|
+
|
|
1122
|
+
return useQuery({
|
|
1123
|
+
queryKey: ['ReferralStats'],
|
|
1124
|
+
queryFn: async () => {
|
|
1125
|
+
const { ReferralStatsDocument } = await import('@/generated/graphql');
|
|
1126
|
+
return client.request(ReferralStatsDocument);
|
|
1127
|
+
},
|
|
1128
|
+
...options,
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* Redeem loyalty reward mutation
|
|
1134
|
+
*
|
|
1135
|
+
* @param options - React Query mutation options
|
|
1136
|
+
* @returns Redeem reward mutation
|
|
1137
|
+
*/
|
|
1138
|
+
export function useRedeemLoyaltyReward(
|
|
1139
|
+
options?: UseMutationOptions<any, Error, any>
|
|
1140
|
+
) {
|
|
1141
|
+
const client = getGraphQLClient();
|
|
1142
|
+
const queryClient = useQueryClient();
|
|
1143
|
+
|
|
1144
|
+
return useMutation({
|
|
1145
|
+
mutationFn: async (variables: any) => {
|
|
1146
|
+
const { RedeemLoyaltyRewardDocument } = await import('@/generated/graphql');
|
|
1147
|
+
return client.request(RedeemLoyaltyRewardDocument, variables);
|
|
1148
|
+
},
|
|
1149
|
+
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'] });
|
|
1154
|
+
},
|
|
1155
|
+
...options,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// ============================================================================
|
|
1160
|
+
// CACHE UTILITIES
|
|
1161
|
+
// ============================================================================
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Hook to invalidate all queries when currency changes
|
|
1165
|
+
*
|
|
1166
|
+
* This is automatically handled by including currency in query keys,
|
|
1167
|
+
* 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
|
+
*/
|
|
1177
|
+
export function useInvalidateOnCurrencyChange() {
|
|
1178
|
+
const queryClient = useQueryClient();
|
|
1179
|
+
|
|
1180
|
+
return () => {
|
|
1181
|
+
queryClient.invalidateQueries();
|
|
1182
|
+
};
|
|
1183
|
+
}
|