Updated Saleor Provider (#356)
* Initial work, copied from the Shopify provider * Added basis setup and type generation for the products queries * refactor: adjust the types * task: relax the Node.js constraint * fix: page/product properties * disable unknown fields * mention Saleor in the README * setup debugging for Next.js * Check nextjs-commerce bug if no images are added for a product * fix: client/server pecularities for env visibility Must prefix with `NEXT_PUBLIC_` so that the API URL is visible on the client * re: make search work with Saleor API (WIP) * task: update deps * task: move to Webpack 5.x * saleor: initial cart integration * update deps * saleor: shall the cart appear! * task: remove deprecated packages * saleor: adding/removing from the cart * saleor: preliminary signup process * saleor: fix the prices in the cart * update deps * update deps * Added the options for a variant to the product page * Mapped options to variants * Mapped options to variants * saleor: refine the auth process * saleor: remove unused code * saleor: handle customer find via refresh temporary solution * saleor: update deps * saleor: fix the session handling * saleor: fix the variants * saleor: simplify the naming for GraphQL statements * saleor: fix the type for collection * saleor: arrange the error codes * saleor: integrate collections * saleor: fix product sorting * saleor: set cookie location * saleor: update the schema * saleor: attach checkout to customer * saleor: fix the checkout flow * saleor: unify GraphQL naming approach * task: update deps * Add the env variables for saleor to the template * task: prettier * saleor: stub API for build/typescript compilation thanks @cond0r * task: temporarily disable for the `build` * saleor: refactor GraphQL queries * saleor: adjust the config * task: update dependencies * revert: Next.js to `10.0.9` * saleor: fix the checkout fetch query * task: update dependencies * saleor: adapt for displaying featured products * saleor: update the provider structure * saleor: make the home page representable * feature/cart: display the variant name (cond) Co-authored-by: Patryk Zawadzki <patrys@room-303.com> Co-authored-by: royderks <10717410+royderks@users.noreply.github.com>
This commit is contained in:
		@@ -13,3 +13,6 @@ NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
 | 
			
		||||
 | 
			
		||||
NEXT_PUBLIC_SWELL_STORE_ID=
 | 
			
		||||
NEXT_PUBLIC_SWELL_PUBLIC_KEY=
 | 
			
		||||
 | 
			
		||||
NEXT_PUBLIC_SALEOR_API_URL=
 | 
			
		||||
NEXT_PUBLIC_SALEOR_CHANNEL=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.prettierrc
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.prettierrc
									
									
									
									
									
								
							@@ -2,5 +2,13 @@
 | 
			
		||||
  "semi": false,
 | 
			
		||||
  "singleQuote": true,
 | 
			
		||||
  "tabWidth": 2,
 | 
			
		||||
  "useTabs": false
 | 
			
		||||
  "useTabs": false,
 | 
			
		||||
  "overrides": [
 | 
			
		||||
    {
 | 
			
		||||
      "files": ["framework/saleor/**/*"],
 | 
			
		||||
      "options": {
 | 
			
		||||
        "printWidth": 120
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
 | 
			
		||||
- Swell Demo: https://swell.vercel.store/
 | 
			
		||||
- BigCommerce Demo: https://bigcommerce.vercel.store/
 | 
			
		||||
- Vendure Demo: https://vendure.vercel.store
 | 
			
		||||
- Saleor Demo: https://saleor.vercel.store/
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
@@ -26,7 +27,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
 | 
			
		||||
 | 
			
		||||
## Integrations
 | 
			
		||||
 | 
			
		||||
Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan to support all major ecommerce backends.
 | 
			
		||||
Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify and Saleor. We plan to support all major ecommerce backends.
 | 
			
		||||
 | 
			
		||||
## Considerations
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -127,4 +127,3 @@ a {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								codegen.bigcommerce.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								codegen.bigcommerce.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
{
 | 
			
		||||
  "schema": {
 | 
			
		||||
    "https://buybutton.store/graphql": {
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer xzy"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "documents": [
 | 
			
		||||
    {
 | 
			
		||||
      "./framework/bigcommerce/api/**/*.ts": {
 | 
			
		||||
        "noRequire": true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "generates": {
 | 
			
		||||
    "./framework/bigcommerce/schema.d.ts": {
 | 
			
		||||
      "plugins": ["typescript", "typescript-operations"]
 | 
			
		||||
    },
 | 
			
		||||
    "./framework/bigcommerce/schema.graphql": {
 | 
			
		||||
      "plugins": ["schema-ast"]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "hooks": {
 | 
			
		||||
    "afterAllFileWrite": ["prettier --write"]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								codegen.json
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								codegen.json
									
									
									
									
									
								
							@@ -1,23 +1,29 @@
 | 
			
		||||
{
 | 
			
		||||
  "schema": {
 | 
			
		||||
    "https://buybutton.store/graphql": {
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer xzy"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    "https://master.staging.saleor.cloud/graphql/": {}
 | 
			
		||||
  },
 | 
			
		||||
  "documents": [
 | 
			
		||||
    {
 | 
			
		||||
      "./framework/bigcommerce/api/**/*.ts": {
 | 
			
		||||
      "./framework/saleor/utils/queries/get-all-products-query.ts": {
 | 
			
		||||
        "noRequire": true
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "./framework/saleor/utils/queries/get-all-products-paths-query.ts": {
 | 
			
		||||
        "noRequire": true
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "./framework/saleor/utils/queries/get-products.ts": {
 | 
			
		||||
        "noRequire": true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "generates": {
 | 
			
		||||
    "./framework/bigcommerce/schema.d.ts": {
 | 
			
		||||
    "./framework/saleor/schema.d.ts": {
 | 
			
		||||
      "plugins": ["typescript", "typescript-operations"]
 | 
			
		||||
    },
 | 
			
		||||
    "./framework/bigcommerce/schema.graphql": {
 | 
			
		||||
    "./framework/saleor/schema.graphql": {
 | 
			
		||||
      "plugins": ["schema-ast"]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "features": {
 | 
			
		||||
    "wishlist": true
 | 
			
		||||
    "wishlist": false,
 | 
			
		||||
    "customCheckout": false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -108,10 +108,14 @@ const CartItem = ({
 | 
			
		||||
      <div className="flex-1 flex flex-col text-base">
 | 
			
		||||
        <Link href={`/product/${item.path}`}>
 | 
			
		||||
          <span
 | 
			
		||||
            className="font-bold text-lg cursor-pointer leading-6"
 | 
			
		||||
            onClick={() => closeSidebarIfPresent()}
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              className="font-bold text-lg cursor-pointer leading-6"
 | 
			
		||||
            >
 | 
			
		||||
              {item.name}
 | 
			
		||||
            </div>
 | 
			
		||||
            {item.variant ? <span> {item.variant.name}</span> : ""}
 | 
			
		||||
          </span>
 | 
			
		||||
        </Link>
 | 
			
		||||
        {options && options.length > 0 ? (
 | 
			
		||||
 
 | 
			
		||||
@@ -49,13 +49,8 @@ const Layout: FC<Props> = ({
 | 
			
		||||
  children,
 | 
			
		||||
  pageProps: { categories = [], ...pageProps },
 | 
			
		||||
}) => {
 | 
			
		||||
  const {
 | 
			
		||||
    displaySidebar,
 | 
			
		||||
    displayModal,
 | 
			
		||||
    closeSidebar,
 | 
			
		||||
    closeModal,
 | 
			
		||||
    modalView,
 | 
			
		||||
  } = useUI()
 | 
			
		||||
  const { displaySidebar, displayModal, closeSidebar, closeModal, modalView } =
 | 
			
		||||
    useUI()
 | 
			
		||||
  const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
 | 
			
		||||
  const { locale = 'en-US' } = useRouter()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ interface Props {
 | 
			
		||||
  className?: string
 | 
			
		||||
  product: Product
 | 
			
		||||
  variant?: 'slim' | 'simple'
 | 
			
		||||
  imgProps?: Omit<ImageProps, 'src'>
 | 
			
		||||
  imgProps?: Omit<any, 'src'>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const placeholderImg = '/product-img-placeholder.svg'
 | 
			
		||||
 
 | 
			
		||||
@@ -32,10 +32,11 @@ const ProductView: FC<Props> = ({ product }) => {
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // Selects the default option
 | 
			
		||||
    product.variants[0].options?.forEach((v) => {
 | 
			
		||||
    const options = product.variants[0].options || []
 | 
			
		||||
    options.forEach((v) => {
 | 
			
		||||
      setChoices((choices) => ({
 | 
			
		||||
        ...choices,
 | 
			
		||||
        [v.displayName.toLowerCase()]: v.values[0].label.toLowerCase(),
 | 
			
		||||
        [v.displayName.toLowerCase()]: v.values[0]?.label.toLowerCase(),
 | 
			
		||||
      }))
 | 
			
		||||
    })
 | 
			
		||||
  }, [])
 | 
			
		||||
@@ -126,7 +127,8 @@ const ProductView: FC<Props> = ({ product }) => {
 | 
			
		||||
                          setChoices((choices) => {
 | 
			
		||||
                            return {
 | 
			
		||||
                              ...choices,
 | 
			
		||||
                              [opt.displayName.toLowerCase()]: v.label.toLowerCase(),
 | 
			
		||||
                              [opt.displayName.toLowerCase()]:
 | 
			
		||||
                                v.label.toLowerCase(),
 | 
			
		||||
                            }
 | 
			
		||||
                          })
 | 
			
		||||
                        }}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,8 @@ const Container: FC<Props> = ({ children, className, el = 'div', clean }) => {
 | 
			
		||||
    'mx-auto max-w-8xl px-6': !clean,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  let Component: React.ComponentType<
 | 
			
		||||
    React.HTMLAttributes<HTMLDivElement>
 | 
			
		||||
  > = el as any
 | 
			
		||||
  let Component: React.ComponentType<React.HTMLAttributes<HTMLDivElement>> =
 | 
			
		||||
    el as any
 | 
			
		||||
 | 
			
		||||
  return <Component className={rootClassName}>{children}</Component>
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ const fs = require('fs')
 | 
			
		||||
const merge = require('deepmerge')
 | 
			
		||||
const prettier = require('prettier')
 | 
			
		||||
 | 
			
		||||
const PROVIDERS = ['bigcommerce', 'shopify', 'swell', 'vendure']
 | 
			
		||||
const PROVIDERS = ['bigcommerce', 'shopify', 'swell', 'vendure', 'saleor']
 | 
			
		||||
 | 
			
		||||
function getProviderName() {
 | 
			
		||||
  return (
 | 
			
		||||
@@ -18,6 +18,8 @@ function getProviderName() {
 | 
			
		||||
      ? 'shopify'
 | 
			
		||||
      : process.env.NEXT_PUBLIC_SWELL_STORE_ID
 | 
			
		||||
      ? 'swell'
 | 
			
		||||
      : process.env.NEXT_PUBLIC_SALEOR_API_URL
 | 
			
		||||
      ? 'saleor'
 | 
			
		||||
      : null)
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
A commerce provider is a headless e-commerce platform that integrates with the [Commerce Framework](./README.md). Right now we have the following providers:
 | 
			
		||||
 | 
			
		||||
- BigCommerce ([framework/bigcommerce](../bigcommerce))
 | 
			
		||||
- Saleor ([framework/saleor](../saleor))
 | 
			
		||||
- Shopify ([framework/shopify](../shopify))
 | 
			
		||||
 | 
			
		||||
Adding a commerce provider means adding a new folder in `framework` with a folder structure like the next one:
 | 
			
		||||
@@ -156,7 +157,9 @@ export const handler: SWRHook<
 | 
			
		||||
    const data = cartId ? await fetch(options) : null
 | 
			
		||||
    return data && normalizeCart(data)
 | 
			
		||||
  },
 | 
			
		||||
  useHook: ({ useData }) => (input) => {
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ useData }) =>
 | 
			
		||||
    (input) => {
 | 
			
		||||
      const response = useData({
 | 
			
		||||
        swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
 | 
			
		||||
      })
 | 
			
		||||
@@ -217,7 +220,9 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = {
 | 
			
		||||
 | 
			
		||||
    return normalizeCart(data)
 | 
			
		||||
  },
 | 
			
		||||
  useHook: ({ fetch }) => () => {
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ fetch }) =>
 | 
			
		||||
    () => {
 | 
			
		||||
      const { mutate } = useCart()
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
 
 | 
			
		||||
@@ -11,10 +11,8 @@ type InferValue<Prop extends PropertyKey, Desc> = Desc extends {
 | 
			
		||||
  ? Record<Prop, T>
 | 
			
		||||
  : never
 | 
			
		||||
 | 
			
		||||
type DefineProperty<
 | 
			
		||||
  Prop extends PropertyKey,
 | 
			
		||||
  Desc extends PropertyDescriptor
 | 
			
		||||
> = Desc extends { writable: any; set(val: any): any }
 | 
			
		||||
type DefineProperty<Prop extends PropertyKey, Desc extends PropertyDescriptor> =
 | 
			
		||||
  Desc extends { writable: any; set(val: any): any }
 | 
			
		||||
    ? never
 | 
			
		||||
    : Desc extends { writable: any; get(): any }
 | 
			
		||||
    ? never
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								framework/saleor/.env.template
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								framework/saleor/.env.template
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
COMMERCE_PROVIDER=saleor
 | 
			
		||||
 | 
			
		||||
NEXT_SALEOR_API_URL=
 | 
			
		||||
NEXT_SALEOR_CHANNEL=
 | 
			
		||||
							
								
								
									
										19
									
								
								framework/saleor/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								framework/saleor/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
## Saleor Provider
 | 
			
		||||
 | 
			
		||||
**Demo:** TBD
 | 
			
		||||
 | 
			
		||||
Before getting starter, a [Saleor](https://saleor.io/) account and store is required before using the provider.
 | 
			
		||||
 | 
			
		||||
Next, copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git):
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cp framework/saleor/.env.template .env.local
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then, set the environment variables in `.env.local` to match the ones from your store.
 | 
			
		||||
 | 
			
		||||
## Contribute
 | 
			
		||||
 | 
			
		||||
Our commitment to Open Source can be found [here](https://vercel.com/oss).
 | 
			
		||||
 | 
			
		||||
If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues).
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/cart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/cart.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/catalog/products.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/catalog/products.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/checkout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/checkout.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/customers/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/customers/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/customers/login.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/customers/login.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/customers/logout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/customers/logout.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/customers/signup.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/customers/signup.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/cart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/cart.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/catalog/products.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/catalog/products.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										57
									
								
								framework/saleor/api/endpoints/checkout/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								framework/saleor/api/endpoints/checkout/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
import { CommerceAPI, GetAPISchema, createEndpoint } from '@commerce/api'
 | 
			
		||||
import checkoutEndpoint from '@commerce/api/endpoints/checkout'
 | 
			
		||||
import { CheckoutSchema } from '@commerce/types/checkout'
 | 
			
		||||
 | 
			
		||||
export type CheckoutAPI = GetAPISchema<CommerceAPI, CheckoutSchema>
 | 
			
		||||
 | 
			
		||||
export type CheckoutEndpoint = CheckoutAPI['endpoint']
 | 
			
		||||
 | 
			
		||||
const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
 | 
			
		||||
  req,
 | 
			
		||||
  res,
 | 
			
		||||
  config,
 | 
			
		||||
}) => {
 | 
			
		||||
  try {
 | 
			
		||||
    const html = `
 | 
			
		||||
      <!DOCTYPE html>
 | 
			
		||||
        <html lang="en">
 | 
			
		||||
        <head>
 | 
			
		||||
          <meta charset="UTF-8">
 | 
			
		||||
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
          <title>Checkout</title>
 | 
			
		||||
        </head>
 | 
			
		||||
        <body>
 | 
			
		||||
          <div style='margin: 10rem auto; text-align: center; font-family: SansSerif, "Segoe UI", Helvetica; color: #888;'>
 | 
			
		||||
             <svg xmlns="http://www.w3.org/2000/svg" style='height: 60px; width: 60px;' fill="none" viewBox="0 0 24 24" stroke="currentColor">
 | 
			
		||||
               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
 | 
			
		||||
             </svg>
 | 
			
		||||
             <h1>Checkout not yet implemented :(</h1>
 | 
			
		||||
             <p>
 | 
			
		||||
             See <a href='https://github.com/vercel/commerce/issues/64' target='_blank'>#64</a>
 | 
			
		||||
             </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </body>
 | 
			
		||||
      </html>
 | 
			
		||||
    `
 | 
			
		||||
 | 
			
		||||
    res.status(200)
 | 
			
		||||
    res.setHeader('Content-Type', 'text/html')
 | 
			
		||||
    res.write(html)
 | 
			
		||||
    res.end()
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error(error)
 | 
			
		||||
 | 
			
		||||
    const message = 'An unexpected error ocurred'
 | 
			
		||||
 | 
			
		||||
    res.status(500).json({ data: null, errors: [{ message }] })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const handlers: CheckoutEndpoint['handlers'] = { checkout }
 | 
			
		||||
 | 
			
		||||
const checkoutApi = createEndpoint<CheckoutAPI>({
 | 
			
		||||
  handler: checkoutEndpoint,
 | 
			
		||||
  handlers,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export default checkoutApi
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/customer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/customer.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/login.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/login.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/logout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/logout.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/signup.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/signup.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/endpoints/wishlist.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/endpoints/wishlist.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function (_commerce: any) {}
 | 
			
		||||
							
								
								
									
										49
									
								
								framework/saleor/api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								framework/saleor/api/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
import type { CommerceAPIConfig } from '@commerce/api'
 | 
			
		||||
 | 
			
		||||
import * as Const from '../const'
 | 
			
		||||
 | 
			
		||||
if (!Const.API_URL) {
 | 
			
		||||
  throw new Error(`The environment variable NEXT_SALEOR_API_URL is missing and it's required to access your store`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!Const.API_CHANNEL) {
 | 
			
		||||
  throw new Error(`The environment variable NEXT_SALEOR_CHANNEL is missing and it's required to access your store`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
 | 
			
		||||
 | 
			
		||||
export interface SaleorConfig extends CommerceAPIConfig {
 | 
			
		||||
  storeChannel: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const config: SaleorConfig = {
 | 
			
		||||
  locale: 'en-US',
 | 
			
		||||
  commerceUrl: Const.API_URL,
 | 
			
		||||
  apiToken: Const.SALEOR_TOKEN,
 | 
			
		||||
  cartCookie: Const.CHECKOUT_ID_COOKIE,
 | 
			
		||||
  cartCookieMaxAge: 60 * 60 * 24 * 30,
 | 
			
		||||
  fetch: fetchGraphqlApi,
 | 
			
		||||
  customerCookie: '',
 | 
			
		||||
  storeChannel: Const.API_CHANNEL,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  CommerceAPI,
 | 
			
		||||
  getCommerceApi as commerceApi,
 | 
			
		||||
} from '@commerce/api'
 | 
			
		||||
 | 
			
		||||
import * as operations from './operations'
 | 
			
		||||
 | 
			
		||||
export interface ShopifyConfig extends CommerceAPIConfig {}
 | 
			
		||||
 | 
			
		||||
export const provider = { config, operations }
 | 
			
		||||
 | 
			
		||||
export type Provider = typeof provider
 | 
			
		||||
 | 
			
		||||
export type SaleorAPI<P extends Provider = Provider> = CommerceAPI<P>
 | 
			
		||||
 | 
			
		||||
export function getCommerceApi<P extends Provider>(
 | 
			
		||||
  customProvider: P = provider as any
 | 
			
		||||
): SaleorAPI<P> {
 | 
			
		||||
  return commerceApi(customProvider)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								framework/saleor/api/operations/get-all-pages.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								framework/saleor/api/operations/get-all-pages.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
 | 
			
		||||
import { QueryPagesArgs, PageCountableEdge } from '../../schema'
 | 
			
		||||
import type { SaleorConfig, Provider } from '..'
 | 
			
		||||
import * as Query from '../../utils/queries'
 | 
			
		||||
 | 
			
		||||
export type Page = any
 | 
			
		||||
 | 
			
		||||
 export type GetAllPagesResult<
 | 
			
		||||
   T extends { pages: any[] } = { pages: Page[] }
 | 
			
		||||
 > = T
 | 
			
		||||
 | 
			
		||||
export default function getAllPagesOperation({
 | 
			
		||||
  commerce,
 | 
			
		||||
}: OperationContext<Provider>) {
 | 
			
		||||
 | 
			
		||||
  async function getAllPages({
 | 
			
		||||
    query = Query.PageMany,
 | 
			
		||||
    config,
 | 
			
		||||
    variables,
 | 
			
		||||
  }: {
 | 
			
		||||
    url?: string
 | 
			
		||||
    config?: Partial<SaleorConfig>
 | 
			
		||||
    variables?: QueryPagesArgs
 | 
			
		||||
    preview?: boolean
 | 
			
		||||
    query?: string
 | 
			
		||||
  } = {}): Promise<GetAllPagesResult> {
 | 
			
		||||
    const { fetch, locale, locales = ['en-US'] } = commerce.getConfig(config)
 | 
			
		||||
 | 
			
		||||
    const { data } = await fetch(query, { variables },
 | 
			
		||||
      {
 | 
			
		||||
        ...(locale && {
 | 
			
		||||
          headers: {
 | 
			
		||||
            'Accept-Language': locale,
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const pages = data.pages?.edges?.map(({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
 | 
			
		||||
      ...node,
 | 
			
		||||
      url: `/${locale}/${slug}`,
 | 
			
		||||
      name,
 | 
			
		||||
    }))
 | 
			
		||||
  
 | 
			
		||||
    return { pages }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return getAllPages
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								framework/saleor/api/operations/get-all-product-paths.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								framework/saleor/api/operations/get-all-product-paths.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
import {
 | 
			
		||||
  GetAllProductPathsQuery,
 | 
			
		||||
  GetAllProductPathsQueryVariables,
 | 
			
		||||
  ProductCountableEdge,
 | 
			
		||||
} from '../../schema'
 | 
			
		||||
import type { ShopifyConfig, Provider, SaleorConfig } from '..'
 | 
			
		||||
 | 
			
		||||
import { getAllProductsPathsQuery } from '../../utils/queries'
 | 
			
		||||
import fetchAllProducts from '../utils/fetch-all-products'
 | 
			
		||||
 | 
			
		||||
export type GetAllProductPathsResult = {
 | 
			
		||||
  products: Array<{ path: string }>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function getAllProductPathsOperation({
 | 
			
		||||
  commerce,
 | 
			
		||||
}: OperationContext<Provider>) {
 | 
			
		||||
 | 
			
		||||
  async function getAllProductPaths({
 | 
			
		||||
    query,
 | 
			
		||||
    config,
 | 
			
		||||
    variables,
 | 
			
		||||
  }: {
 | 
			
		||||
    query?: string
 | 
			
		||||
    config?: SaleorConfig
 | 
			
		||||
    variables?: any 
 | 
			
		||||
  } = {}): Promise<GetAllProductPathsResult> {
 | 
			
		||||
    config = commerce.getConfig(config)
 | 
			
		||||
 | 
			
		||||
    const products = await fetchAllProducts({
 | 
			
		||||
      config,
 | 
			
		||||
      query: getAllProductsPathsQuery,
 | 
			
		||||
      variables,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      products: products?.map(({ node: { slug } }: ProductCountableEdge) => ({
 | 
			
		||||
        path: `/${slug}`,
 | 
			
		||||
      })),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return getAllProductPaths
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								framework/saleor/api/operations/get-all-products.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								framework/saleor/api/operations/get-all-products.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
import { Product } from '@commerce/types/product'
 | 
			
		||||
 | 
			
		||||
import { ProductCountableEdge } from '../../schema'
 | 
			
		||||
import type { Provider, SaleorConfig } from '..'
 | 
			
		||||
import { normalizeProduct } from '../../utils'
 | 
			
		||||
 | 
			
		||||
import * as Query from '../../utils/queries'
 | 
			
		||||
import { GraphQLFetcherResult } from '@commerce/api'
 | 
			
		||||
 | 
			
		||||
type ReturnType = {
 | 
			
		||||
  products: Product[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function getAllProductsOperation({
 | 
			
		||||
  commerce,
 | 
			
		||||
}: OperationContext<Provider>) {
 | 
			
		||||
  async function getAllProducts({
 | 
			
		||||
    query = Query.ProductMany,
 | 
			
		||||
    variables,
 | 
			
		||||
    config,
 | 
			
		||||
    featured,
 | 
			
		||||
  }: {
 | 
			
		||||
    query?: string
 | 
			
		||||
    variables?: any 
 | 
			
		||||
    config?: Partial<SaleorConfig>
 | 
			
		||||
    preview?: boolean
 | 
			
		||||
    featured?: boolean
 | 
			
		||||
  } = {}): Promise<ReturnType> {
 | 
			
		||||
    const { fetch, locale } = commerce.getConfig(config)
 | 
			
		||||
 | 
			
		||||
    if (featured) {
 | 
			
		||||
      variables = { ...variables, categoryId: 'Q29sbGVjdGlvbjo0' };
 | 
			
		||||
      query = Query.CollectionOne
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const { data }: GraphQLFetcherResult = await fetch(
 | 
			
		||||
      query,
 | 
			
		||||
      { variables },
 | 
			
		||||
      {
 | 
			
		||||
        ...(locale && {
 | 
			
		||||
          headers: {
 | 
			
		||||
            'Accept-Language': locale,
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if (featured) {
 | 
			
		||||
      const products = data.collection.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        products,
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      const products = data.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        products,
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return getAllProducts
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								framework/saleor/api/operations/get-page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								framework/saleor/api/operations/get-page.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
import type { Provider, SaleorConfig } from '..'
 | 
			
		||||
import { QueryPageArgs } from '../../schema'
 | 
			
		||||
 | 
			
		||||
import * as Query from '../../utils/queries'
 | 
			
		||||
 | 
			
		||||
export type Page = any
 | 
			
		||||
 | 
			
		||||
 export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
 | 
			
		||||
 | 
			
		||||
export default function getPageOperation({
 | 
			
		||||
  commerce,
 | 
			
		||||
}: OperationContext<Provider>) {
 | 
			
		||||
 | 
			
		||||
  async function getPage({
 | 
			
		||||
    query = Query.PageOne,
 | 
			
		||||
    variables,
 | 
			
		||||
    config,
 | 
			
		||||
  }: {
 | 
			
		||||
    query?: string
 | 
			
		||||
    variables: QueryPageArgs, 
 | 
			
		||||
    config?: Partial<SaleorConfig>
 | 
			
		||||
    preview?: boolean
 | 
			
		||||
  }): Promise<GetPageResult> {
 | 
			
		||||
    const { fetch, locale = 'en-US' } = commerce.getConfig(config)
 | 
			
		||||
 | 
			
		||||
    const {
 | 
			
		||||
      data: { page },
 | 
			
		||||
    } = await fetch(query, { variables },
 | 
			
		||||
      {
 | 
			
		||||
        ...(locale && {
 | 
			
		||||
          headers: {
 | 
			
		||||
            'Accept-Language': locale,
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      page: page
 | 
			
		||||
        ? {
 | 
			
		||||
            ...page,
 | 
			
		||||
            name: page.title,
 | 
			
		||||
            url: `/${locale}/${page.slug}`,
 | 
			
		||||
          }
 | 
			
		||||
        : null,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return getPage
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								framework/saleor/api/operations/get-product.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								framework/saleor/api/operations/get-product.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
import { normalizeProduct, } from '../../utils'
 | 
			
		||||
import type { Provider, SaleorConfig } from '..'
 | 
			
		||||
 | 
			
		||||
import * as Query from '../../utils/queries'
 | 
			
		||||
 | 
			
		||||
type Variables = {
 | 
			
		||||
  slug: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReturnType = {
 | 
			
		||||
  product: any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function getProductOperation({
 | 
			
		||||
  commerce,
 | 
			
		||||
}: OperationContext<Provider>) {
 | 
			
		||||
  async function getProduct({
 | 
			
		||||
    query = Query.ProductOneBySlug,
 | 
			
		||||
    variables,
 | 
			
		||||
    config: cfg,
 | 
			
		||||
  }: {
 | 
			
		||||
    query?: string
 | 
			
		||||
    variables: Variables 
 | 
			
		||||
    config?: Partial<SaleorConfig>
 | 
			
		||||
    preview?: boolean
 | 
			
		||||
  }): Promise<ReturnType> {
 | 
			
		||||
    const { fetch, locale } = commerce.getConfig(cfg)
 | 
			
		||||
 | 
			
		||||
    const { data } = await fetch(query, { variables },
 | 
			
		||||
      {
 | 
			
		||||
        ...(locale && {
 | 
			
		||||
          headers: {
 | 
			
		||||
            'Accept-Language': locale,
 | 
			
		||||
          },
 | 
			
		||||
        }),
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      product: data?.product ? normalizeProduct(data.product) : null,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return getProduct
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								framework/saleor/api/operations/get-site-info.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								framework/saleor/api/operations/get-site-info.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
import { Category } from '@commerce/types/site'
 | 
			
		||||
import type { SaleorConfig, Provider } from '..'
 | 
			
		||||
 | 
			
		||||
import { getCategories, getVendors } from '../../utils'
 | 
			
		||||
 | 
			
		||||
interface GetSiteInfoResult {
 | 
			
		||||
  categories: Category[]
 | 
			
		||||
  brands: any[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function getSiteInfoOperation({ commerce }: OperationContext<Provider>) {
 | 
			
		||||
  async function getSiteInfo({
 | 
			
		||||
    query,
 | 
			
		||||
    config,
 | 
			
		||||
    variables,
 | 
			
		||||
  }: {
 | 
			
		||||
    query?: string
 | 
			
		||||
    config?: Partial<SaleorConfig>
 | 
			
		||||
    preview?: boolean
 | 
			
		||||
    variables?: any 
 | 
			
		||||
  } = {}): Promise<GetSiteInfoResult> {
 | 
			
		||||
    const cfg = commerce.getConfig(config)
 | 
			
		||||
 | 
			
		||||
    const categories = await getCategories(cfg)
 | 
			
		||||
    const brands = await getVendors(cfg)
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      categories,
 | 
			
		||||
      brands,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return getSiteInfo
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								framework/saleor/api/operations/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								framework/saleor/api/operations/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
export { default as getAllPages } from './get-all-pages'
 | 
			
		||||
export { default as getPage } from './get-page'
 | 
			
		||||
export { default as getAllProducts } from './get-all-products'
 | 
			
		||||
export { default as getAllProductPaths } from './get-all-product-paths'
 | 
			
		||||
export { default as getProduct } from './get-product'
 | 
			
		||||
export { default as getSiteInfo } from './get-site-info'
 | 
			
		||||
export { default as login } from './login'
 | 
			
		||||
							
								
								
									
										42
									
								
								framework/saleor/api/operations/login.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								framework/saleor/api/operations/login.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
import type { ServerResponse } from 'http'
 | 
			
		||||
import type { OperationContext } from '@commerce/api/operations'
 | 
			
		||||
import type { Provider, SaleorConfig } from '..'
 | 
			
		||||
import {
 | 
			
		||||
  throwUserErrors,
 | 
			
		||||
} from '../../utils'
 | 
			
		||||
 | 
			
		||||
import * as Mutation from '../../utils/mutations'
 | 
			
		||||
 | 
			
		||||
export default function loginOperation({
 | 
			
		||||
  commerce,
 | 
			
		||||
}: OperationContext<Provider>) {
 | 
			
		||||
  async function login({
 | 
			
		||||
    query = Mutation.SessionCreate,
 | 
			
		||||
    variables,
 | 
			
		||||
    config,
 | 
			
		||||
  }: {
 | 
			
		||||
    query?: string
 | 
			
		||||
    variables: any 
 | 
			
		||||
    res: ServerResponse
 | 
			
		||||
    config?: SaleorConfig
 | 
			
		||||
  }): Promise<any> {
 | 
			
		||||
    config = commerce.getConfig(config)
 | 
			
		||||
 | 
			
		||||
    const { data: { customerAccessTokenCreate } } = await config.fetch(query, { variables })
 | 
			
		||||
 | 
			
		||||
    throwUserErrors(customerAccessTokenCreate?.customerUserErrors)
 | 
			
		||||
 | 
			
		||||
    const customerAccessToken = customerAccessTokenCreate?.customerAccessToken
 | 
			
		||||
    const accessToken = customerAccessToken?.accessToken
 | 
			
		||||
 | 
			
		||||
    // if (accessToken) {
 | 
			
		||||
    //   setCustomerToken(accessToken)
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      result: customerAccessToken?.accessToken,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return login
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								framework/saleor/api/utils/fetch-all-products.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								framework/saleor/api/utils/fetch-all-products.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
import { ProductCountableEdge } from '../../schema'
 | 
			
		||||
import { SaleorConfig } from '..'
 | 
			
		||||
 | 
			
		||||
const fetchAllProducts = async ({
 | 
			
		||||
  config,
 | 
			
		||||
  query,
 | 
			
		||||
  variables,
 | 
			
		||||
  acc = [],
 | 
			
		||||
  cursor,
 | 
			
		||||
}: {
 | 
			
		||||
  config: SaleorConfig
 | 
			
		||||
  query: string
 | 
			
		||||
  acc?: ProductCountableEdge[]
 | 
			
		||||
  variables?: any
 | 
			
		||||
  cursor?: string
 | 
			
		||||
}): Promise<ProductCountableEdge[]> => {
 | 
			
		||||
  const { data } = await config.fetch(query, {
 | 
			
		||||
    variables: { ...variables, cursor },
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const edges: ProductCountableEdge[] = data.products?.edges ?? []
 | 
			
		||||
  const hasNextPage = data.products?.pageInfo?.hasNextPage
 | 
			
		||||
  acc = acc.concat(edges)
 | 
			
		||||
 | 
			
		||||
  if (hasNextPage) {
 | 
			
		||||
    const cursor = edges.pop()?.cursor
 | 
			
		||||
    if (cursor) {
 | 
			
		||||
      return fetchAllProducts({
 | 
			
		||||
        config,
 | 
			
		||||
        query,
 | 
			
		||||
        variables,
 | 
			
		||||
        acc,
 | 
			
		||||
        cursor,
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return acc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default fetchAllProducts
 | 
			
		||||
							
								
								
									
										37
									
								
								framework/saleor/api/utils/fetch-graphql-api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								framework/saleor/api/utils/fetch-graphql-api.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
import type { GraphQLFetcher } from '@commerce/api'
 | 
			
		||||
import fetch from './fetch'
 | 
			
		||||
 | 
			
		||||
import { API_URL } from '../../const'
 | 
			
		||||
import { getError } from '../../utils/handle-fetch-response'
 | 
			
		||||
import { getCommerceApi } from '..'
 | 
			
		||||
import { getToken } from '@framework/utils'
 | 
			
		||||
 | 
			
		||||
const fetchGraphqlApi: GraphQLFetcher = async (query: string, { variables } = {}, fetchOptions) => {
 | 
			
		||||
  const config = getCommerceApi().getConfig()
 | 
			
		||||
  const token = getToken()
 | 
			
		||||
 | 
			
		||||
  const res = await fetch(API_URL!, {
 | 
			
		||||
    ...fetchOptions,
 | 
			
		||||
    method: 'POST',
 | 
			
		||||
    headers: {
 | 
			
		||||
      ...(token && {
 | 
			
		||||
        Authorization: `Bearer ${token}`,
 | 
			
		||||
      }),
 | 
			
		||||
      ...fetchOptions?.headers,
 | 
			
		||||
      'Content-Type': 'application/json',
 | 
			
		||||
    },
 | 
			
		||||
    body: JSON.stringify({
 | 
			
		||||
      query,
 | 
			
		||||
      variables,
 | 
			
		||||
    }),
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const { data, errors, status } = await res.json()
 | 
			
		||||
 | 
			
		||||
  if (errors) {
 | 
			
		||||
    throw getError(errors, status)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return { data, res }
 | 
			
		||||
}
 | 
			
		||||
export default fetchGraphqlApi
 | 
			
		||||
							
								
								
									
										2
									
								
								framework/saleor/api/utils/fetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								framework/saleor/api/utils/fetch.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
import zeitFetch from '@vercel/fetch'
 | 
			
		||||
export default zeitFetch()
 | 
			
		||||
							
								
								
									
										22
									
								
								framework/saleor/api/utils/is-allowed-method.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								framework/saleor/api/utils/is-allowed-method.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
import type { NextApiRequest, NextApiResponse } from 'next'
 | 
			
		||||
 | 
			
		||||
export default function isAllowedMethod(req: NextApiRequest, res: NextApiResponse, allowedMethods: string[]) {
 | 
			
		||||
  const methods = allowedMethods.includes('OPTIONS') ? allowedMethods : [...allowedMethods, 'OPTIONS']
 | 
			
		||||
 | 
			
		||||
  if (!req.method || !methods.includes(req.method)) {
 | 
			
		||||
    res.status(405)
 | 
			
		||||
    res.setHeader('Allow', methods.join(', '))
 | 
			
		||||
    res.end()
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (req.method === 'OPTIONS') {
 | 
			
		||||
    res.status(200)
 | 
			
		||||
    res.setHeader('Allow', methods.join(', '))
 | 
			
		||||
    res.setHeader('Content-Length', '0')
 | 
			
		||||
    res.end()
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/api/wishlist.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/api/wishlist.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export default function () {}
 | 
			
		||||
							
								
								
									
										63
									
								
								framework/saleor/auth/use-login.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								framework/saleor/auth/use-login.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
import { useCallback } from 'react'
 | 
			
		||||
 | 
			
		||||
import type { MutationHook } from '@commerce/utils/types'
 | 
			
		||||
import { CommerceError } from '@commerce/utils/errors'
 | 
			
		||||
import useCustomer from '../customer/use-customer'
 | 
			
		||||
import * as mutation from '../utils/mutations'
 | 
			
		||||
import { Mutation, MutationTokenCreateArgs } from '../schema'
 | 
			
		||||
import useLogin, { UseLogin } from '@commerce/auth/use-login'
 | 
			
		||||
import { setCSRFToken, setToken, throwUserErrors, checkoutAttach, getCheckoutId } from '../utils'
 | 
			
		||||
import { LoginHook } from '@commerce/types/login'
 | 
			
		||||
 | 
			
		||||
export default useLogin as UseLogin<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler: MutationHook<LoginHook> = {
 | 
			
		||||
  fetchOptions: {
 | 
			
		||||
    query: mutation.SessionCreate,
 | 
			
		||||
  },
 | 
			
		||||
  async fetcher({ input: { email, password }, options, fetch }) {
 | 
			
		||||
    if (!(email && password)) {
 | 
			
		||||
      throw new CommerceError({
 | 
			
		||||
        message: 'A first name, last name, email and password are required to login',
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { tokenCreate } = await fetch<Mutation, MutationTokenCreateArgs>({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: { email, password },
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    throwUserErrors(tokenCreate?.errors)
 | 
			
		||||
 | 
			
		||||
    const { token, csrfToken } = tokenCreate!
 | 
			
		||||
 | 
			
		||||
    if (token && csrfToken) {
 | 
			
		||||
      setToken(token)
 | 
			
		||||
      setCSRFToken(csrfToken)
 | 
			
		||||
 | 
			
		||||
      const { checkoutId } = getCheckoutId()
 | 
			
		||||
      checkoutAttach(fetch, {
 | 
			
		||||
        variables: { checkoutId },
 | 
			
		||||
        headers: {
 | 
			
		||||
          Authorization: `JWT ${token}`,
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ fetch }) =>
 | 
			
		||||
    () => {
 | 
			
		||||
      const { revalidate } = useCustomer()
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
        async function login(input) {
 | 
			
		||||
          const data = await fetch({ input })
 | 
			
		||||
          await revalidate()
 | 
			
		||||
          return data
 | 
			
		||||
        },
 | 
			
		||||
        [fetch, revalidate]
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								framework/saleor/auth/use-logout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								framework/saleor/auth/use-logout.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
import { useCallback } from 'react'
 | 
			
		||||
import type { MutationHook } from '@commerce/utils/types'
 | 
			
		||||
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
 | 
			
		||||
import useCustomer from '../customer/use-customer'
 | 
			
		||||
import * as mutation from '../utils/mutations'
 | 
			
		||||
import { setCSRFToken, setToken, setCheckoutToken } from '../utils/customer-token'
 | 
			
		||||
import { LogoutHook } from '@commerce/types/logout'
 | 
			
		||||
 | 
			
		||||
export default useLogout as UseLogout<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler: MutationHook<LogoutHook> = {
 | 
			
		||||
  fetchOptions: {
 | 
			
		||||
    query: mutation.SessionDestroy,
 | 
			
		||||
  },
 | 
			
		||||
  async fetcher({ options, fetch }) {
 | 
			
		||||
    await fetch({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: {},
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    setToken()
 | 
			
		||||
    setCSRFToken()
 | 
			
		||||
    setCheckoutToken()
 | 
			
		||||
 | 
			
		||||
    return null
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ fetch }) =>
 | 
			
		||||
    () => {
 | 
			
		||||
      const { mutate } = useCustomer()
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
        async function logout() {
 | 
			
		||||
          const data = await fetch()
 | 
			
		||||
          await mutate(null, false)
 | 
			
		||||
          return data
 | 
			
		||||
        },
 | 
			
		||||
        [fetch, mutate]
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								framework/saleor/auth/use-signup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								framework/saleor/auth/use-signup.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
import { useCallback } from 'react'
 | 
			
		||||
import type { MutationHook } from '@commerce/utils/types'
 | 
			
		||||
import { CommerceError } from '@commerce/utils/errors'
 | 
			
		||||
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
 | 
			
		||||
import useCustomer from '../customer/use-customer'
 | 
			
		||||
import { AccountRegisterInput, Mutation, MutationAccountRegisterArgs } from '../schema'
 | 
			
		||||
 | 
			
		||||
import * as mutation from '../utils/mutations'
 | 
			
		||||
import { handleAutomaticLogin, throwUserErrors } from '../utils'
 | 
			
		||||
import { SignupHook } from '@commerce/types/signup'
 | 
			
		||||
 | 
			
		||||
export default useSignup as UseSignup<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler: MutationHook<SignupHook> = {
 | 
			
		||||
  fetchOptions: {
 | 
			
		||||
    query: mutation.AccountCreate,
 | 
			
		||||
  },
 | 
			
		||||
  async fetcher({ input: { email, password }, options, fetch }) {
 | 
			
		||||
    if (!(email && password)) {
 | 
			
		||||
      throw new CommerceError({
 | 
			
		||||
        message: 'A first name, last name, email and password are required to signup',
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { customerCreate } = await fetch<Mutation, MutationAccountRegisterArgs>({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: {
 | 
			
		||||
        input: {
 | 
			
		||||
          email,
 | 
			
		||||
          password,
 | 
			
		||||
          redirectUrl: 'https://localhost.com',
 | 
			
		||||
          channel: 'default-channel'
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    throwUserErrors(customerCreate?.errors)
 | 
			
		||||
    await handleAutomaticLogin(fetch, { email, password })
 | 
			
		||||
 | 
			
		||||
    return null
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ fetch }) =>
 | 
			
		||||
    () => {
 | 
			
		||||
      const { revalidate } = useCustomer()
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
        async function signup(input) {
 | 
			
		||||
          const data = await fetch({ input })
 | 
			
		||||
          await revalidate()
 | 
			
		||||
          return data
 | 
			
		||||
        },
 | 
			
		||||
        [fetch, revalidate]
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								framework/saleor/cart/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								framework/saleor/cart/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
export { default as useCart } from './use-cart'
 | 
			
		||||
export { default as useAddItem } from './use-add-item'
 | 
			
		||||
export { default as useUpdateItem } from './use-update-item'
 | 
			
		||||
export { default as useRemoveItem } from './use-remove-item'
 | 
			
		||||
							
								
								
									
										54
									
								
								framework/saleor/cart/use-add-item.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								framework/saleor/cart/use-add-item.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
import { useCallback } from 'react'
 | 
			
		||||
import type { MutationHook } from '@commerce/utils/types'
 | 
			
		||||
import { CommerceError } from '@commerce/utils/errors'
 | 
			
		||||
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
 | 
			
		||||
import useCart from './use-cart'
 | 
			
		||||
 | 
			
		||||
import * as mutation from '../utils/mutations'
 | 
			
		||||
 | 
			
		||||
import { getCheckoutId, checkoutToCart } from '../utils'
 | 
			
		||||
 | 
			
		||||
import { Mutation, MutationCheckoutLinesAddArgs } from '../schema'
 | 
			
		||||
import { AddItemHook } from '@commerce/types/cart'
 | 
			
		||||
 | 
			
		||||
export default useAddItem as UseAddItem<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler: MutationHook<AddItemHook> = {
 | 
			
		||||
  fetchOptions: { query: mutation.CheckoutLineAdd },
 | 
			
		||||
  async fetcher({ input: item, options, fetch }) {
 | 
			
		||||
    if (item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1)) {
 | 
			
		||||
      throw new CommerceError({
 | 
			
		||||
        message: 'The item quantity has to be a valid integer greater than 0',
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { checkoutLinesAdd } = await fetch<Mutation, MutationCheckoutLinesAddArgs>({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: {
 | 
			
		||||
        checkoutId: getCheckoutId().checkoutId,
 | 
			
		||||
        lineItems: [
 | 
			
		||||
          {
 | 
			
		||||
            variantId: item.variantId,
 | 
			
		||||
            quantity: item.quantity ?? 1,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return checkoutToCart(checkoutLinesAdd)
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ fetch }) =>
 | 
			
		||||
    () => {
 | 
			
		||||
      const { mutate } = useCart()
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
        async function addItem(input) {
 | 
			
		||||
          const data = await fetch({ input })
 | 
			
		||||
          await mutate(data, false)
 | 
			
		||||
          return data
 | 
			
		||||
        },
 | 
			
		||||
        [fetch, mutate]
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								framework/saleor/cart/use-cart.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								framework/saleor/cart/use-cart.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
import { useMemo } from 'react'
 | 
			
		||||
import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
 | 
			
		||||
 | 
			
		||||
import { SWRHook } from '@commerce/utils/types'
 | 
			
		||||
import { checkoutCreate, checkoutToCart, getCheckoutId } from '../utils'
 | 
			
		||||
import * as query from '../utils/queries'
 | 
			
		||||
import { GetCartHook } from '@commerce/types/cart'
 | 
			
		||||
 | 
			
		||||
export default useCommerceCart as UseCart<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler: SWRHook<GetCartHook> = {
 | 
			
		||||
  fetchOptions: {
 | 
			
		||||
    query: query.CheckoutOne,
 | 
			
		||||
  },
 | 
			
		||||
  async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
 | 
			
		||||
    let checkout
 | 
			
		||||
 | 
			
		||||
    if (checkoutId) {
 | 
			
		||||
      const checkoutId = getCheckoutId().checkoutToken
 | 
			
		||||
      const data = await fetch({
 | 
			
		||||
        ...options,
 | 
			
		||||
        variables: { checkoutId },
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      checkout = data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (checkout?.completedAt || !checkoutId) {
 | 
			
		||||
      checkout = await checkoutCreate(fetch)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return checkoutToCart(checkout)
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ useData }) =>
 | 
			
		||||
    (input) => {
 | 
			
		||||
      const response = useData({
 | 
			
		||||
        swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
 | 
			
		||||
      })
 | 
			
		||||
      return useMemo(
 | 
			
		||||
        () =>
 | 
			
		||||
          Object.create(response, {
 | 
			
		||||
            isEmpty: {
 | 
			
		||||
              get() {
 | 
			
		||||
                return (response.data?.lineItems.length ?? 0) <= 0
 | 
			
		||||
              },
 | 
			
		||||
              enumerable: true,
 | 
			
		||||
            },
 | 
			
		||||
          }),
 | 
			
		||||
        [response]
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								framework/saleor/cart/use-remove-item.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								framework/saleor/cart/use-remove-item.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import { useCallback } from 'react'
 | 
			
		||||
import type { MutationHookContext, HookFetcherContext, MutationHook } from '@commerce/utils/types'
 | 
			
		||||
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
 | 
			
		||||
import useCart from './use-cart'
 | 
			
		||||
import * as mutation from '../utils/mutations'
 | 
			
		||||
import { getCheckoutId, checkoutToCart } from '../utils'
 | 
			
		||||
import { Mutation, MutationCheckoutLineDeleteArgs } from '../schema'
 | 
			
		||||
import { LineItem, RemoveItemHook } from '../types/cart'
 | 
			
		||||
 | 
			
		||||
export default useRemoveItem as UseRemoveItem<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler = {
 | 
			
		||||
  fetchOptions: { query: mutation.CheckoutLineDelete },
 | 
			
		||||
  async fetcher({ input: { itemId }, options, fetch }: HookFetcherContext<RemoveItemHook>) {
 | 
			
		||||
    const data = await fetch<Mutation, MutationCheckoutLineDeleteArgs>({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: {
 | 
			
		||||
        checkoutId: getCheckoutId().checkoutId,
 | 
			
		||||
        lineId: itemId,
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    return checkoutToCart(data.checkoutLineDelete)
 | 
			
		||||
  },
 | 
			
		||||
  useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => <
 | 
			
		||||
    T extends LineItem | undefined = undefined
 | 
			
		||||
  > () => {
 | 
			
		||||
      const { mutate } = useCart()
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
        async function removeItem(input) {
 | 
			
		||||
          const data = await fetch({ input: { itemId: input.id } })
 | 
			
		||||
          await mutate(data, false)
 | 
			
		||||
 | 
			
		||||
          return data
 | 
			
		||||
        },
 | 
			
		||||
        [fetch, mutate]
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								framework/saleor/cart/use-update-item.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								framework/saleor/cart/use-update-item.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
import { useCallback } from 'react'
 | 
			
		||||
import debounce from 'lodash.debounce'
 | 
			
		||||
import type { HookFetcherContext, MutationHookContext } from '@commerce/utils/types'
 | 
			
		||||
import { ValidationError } from '@commerce/utils/errors'
 | 
			
		||||
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
 | 
			
		||||
 | 
			
		||||
import useCart from './use-cart'
 | 
			
		||||
import { handler as removeItemHandler } from './use-remove-item'
 | 
			
		||||
import type { LineItem } from '../types'
 | 
			
		||||
import { checkoutToCart } from '../utils'
 | 
			
		||||
import { getCheckoutId } from '../utils'
 | 
			
		||||
import { Mutation, MutationCheckoutLinesUpdateArgs } from '../schema'
 | 
			
		||||
 | 
			
		||||
import * as mutation from '../utils/mutations'
 | 
			
		||||
 | 
			
		||||
import type { UpdateItemHook } from '../types/cart'
 | 
			
		||||
 | 
			
		||||
export type UpdateItemActionInput<T = any> = T extends LineItem
 | 
			
		||||
  ? Partial<UpdateItemHook['actionInput']>
 | 
			
		||||
  : UpdateItemHook['actionInput']
 | 
			
		||||
 | 
			
		||||
export default useUpdateItem as UseUpdateItem<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler = {
 | 
			
		||||
  fetchOptions: { query: mutation.CheckoutLineUpdate },
 | 
			
		||||
  async fetcher({ 
 | 
			
		||||
    input: { itemId, item }, 
 | 
			
		||||
    options, 
 | 
			
		||||
    fetch 
 | 
			
		||||
  }: HookFetcherContext<UpdateItemHook>) {
 | 
			
		||||
    if (Number.isInteger(item.quantity)) {
 | 
			
		||||
      // Also allow the update hook to remove an item if the quantity is lower than 1
 | 
			
		||||
      if (item.quantity! < 1) {
 | 
			
		||||
        return removeItemHandler.fetcher({
 | 
			
		||||
          options: removeItemHandler.fetchOptions,
 | 
			
		||||
          input: { itemId },
 | 
			
		||||
          fetch,
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    } else if (item.quantity) {
 | 
			
		||||
      throw new ValidationError({
 | 
			
		||||
        message: 'The item quantity has to be a valid integer',
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const checkoutId = getCheckoutId().checkoutId
 | 
			
		||||
    const { checkoutLinesUpdate } = await fetch<Mutation, MutationCheckoutLinesUpdateArgs>({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: {
 | 
			
		||||
        checkoutId,
 | 
			
		||||
        lineItems: [
 | 
			
		||||
          {
 | 
			
		||||
            variantId: item.variantId,
 | 
			
		||||
            quantity: item.quantity,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return checkoutToCart(checkoutLinesUpdate)
 | 
			
		||||
  },
 | 
			
		||||
  useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => 
 | 
			
		||||
    <T extends LineItem | undefined = undefined>(
 | 
			
		||||
      ctx: {
 | 
			
		||||
        item?: T
 | 
			
		||||
        wait?: number
 | 
			
		||||
      } = {}
 | 
			
		||||
    ) => {
 | 
			
		||||
      const { item } = ctx
 | 
			
		||||
      const { mutate } = useCart() as any
 | 
			
		||||
 | 
			
		||||
      return useCallback(
 | 
			
		||||
        debounce(async (input: UpdateItemActionInput<T>) => {
 | 
			
		||||
          const itemId = input.id ?? item?.id
 | 
			
		||||
          const productId = input.productId ?? item?.productId
 | 
			
		||||
          const variantId = input.productId ?? item?.variantId
 | 
			
		||||
          if (!itemId || !productId || !variantId) {
 | 
			
		||||
            throw new ValidationError({
 | 
			
		||||
              message: 'Invalid input used for this operation',
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          const data = await fetch({
 | 
			
		||||
            input: {
 | 
			
		||||
              item: {
 | 
			
		||||
                productId,
 | 
			
		||||
                variantId,
 | 
			
		||||
                quantity: input.quantity,
 | 
			
		||||
              },
 | 
			
		||||
              itemId,
 | 
			
		||||
            },
 | 
			
		||||
          })
 | 
			
		||||
          await mutate(data, false)
 | 
			
		||||
          return data
 | 
			
		||||
        }, ctx.wait ?? 500),
 | 
			
		||||
        [fetch, mutate]
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								framework/saleor/commerce.config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								framework/saleor/commerce.config.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "provider": "saleor",
 | 
			
		||||
  "features": {
 | 
			
		||||
    "wishlist": false,
 | 
			
		||||
    "customCheckout": true
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								framework/saleor/const.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								framework/saleor/const.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
export const API_URL = process.env.NEXT_PUBLIC_SALEOR_API_URL
 | 
			
		||||
export const API_CHANNEL = process.env.NEXT_PUBLIC_SALEOR_CHANNEL
 | 
			
		||||
export const CHECKOUT_ID_COOKIE = 'saleor.CheckoutID'
 | 
			
		||||
export const SALEOR_TOKEN = 'saleor.Token'
 | 
			
		||||
export const SALEOR_CRSF_TOKEN = 'saleor.CSRFToken'
 | 
			
		||||
							
								
								
									
										1
									
								
								framework/saleor/customer/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								framework/saleor/customer/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
export { default as useCustomer } from './use-customer'
 | 
			
		||||
							
								
								
									
										30
									
								
								framework/saleor/customer/use-customer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								framework/saleor/customer/use-customer.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
 | 
			
		||||
import { CustomerHook } from '@commerce/types/customer'
 | 
			
		||||
import { SWRHook } from '@commerce/utils/types'
 | 
			
		||||
 | 
			
		||||
import * as query from '../utils/queries'
 | 
			
		||||
 | 
			
		||||
export default useCustomer as UseCustomer<typeof handler>
 | 
			
		||||
 | 
			
		||||
export const handler: SWRHook<CustomerHook> = {
 | 
			
		||||
  fetchOptions: {
 | 
			
		||||
    query: query.CustomerCurrent,
 | 
			
		||||
  },
 | 
			
		||||
  async fetcher({ options, fetch }) {
 | 
			
		||||
    const data = await fetch<any | null>({
 | 
			
		||||
      ...options,
 | 
			
		||||
      variables: {},
 | 
			
		||||
    })
 | 
			
		||||
    return data.me ?? null
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ useData }) =>
 | 
			
		||||
    (input) => {
 | 
			
		||||
      return useData({
 | 
			
		||||
        swrOptions: {
 | 
			
		||||
          revalidateOnFocus: false,
 | 
			
		||||
          ...input?.swrOptions,
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								framework/saleor/fetcher.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								framework/saleor/fetcher.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import { Fetcher } from '@commerce/utils/types'
 | 
			
		||||
import { API_URL } from './const'
 | 
			
		||||
import { getToken, handleFetchResponse } from './utils'
 | 
			
		||||
 | 
			
		||||
const fetcher: Fetcher = async ({ url = API_URL, method = 'POST', variables, query }) => {
 | 
			
		||||
  const token = getToken()
 | 
			
		||||
 | 
			
		||||
  return handleFetchResponse(
 | 
			
		||||
    await fetch(url!, {
 | 
			
		||||
      method,
 | 
			
		||||
      body: JSON.stringify({ query, variables }),
 | 
			
		||||
      headers: {
 | 
			
		||||
        Authorization: `JWT ${token}`,
 | 
			
		||||
        'Content-Type': 'application/json',
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default fetcher
 | 
			
		||||
							
								
								
									
										32
									
								
								framework/saleor/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								framework/saleor/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
import * as React from 'react'
 | 
			
		||||
import { ReactNode } from 'react'
 | 
			
		||||
 | 
			
		||||
import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce } from '@commerce'
 | 
			
		||||
 | 
			
		||||
import { saleorProvider, SaleorProvider } from './provider'
 | 
			
		||||
import * as Const from './const'
 | 
			
		||||
 | 
			
		||||
export { saleorProvider }
 | 
			
		||||
export type { SaleorProvider }
 | 
			
		||||
 | 
			
		||||
export const saleorConfig: CommerceConfig = {
 | 
			
		||||
  locale: 'en-us',
 | 
			
		||||
  cartCookie: Const.CHECKOUT_ID_COOKIE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SaleorConfig = Partial<CommerceConfig>
 | 
			
		||||
 | 
			
		||||
export type SaleorProps = {
 | 
			
		||||
  children?: ReactNode
 | 
			
		||||
  locale: string
 | 
			
		||||
} & SaleorConfig
 | 
			
		||||
 | 
			
		||||
export function CommerceProvider({ children, ...config }: SaleorProps) {
 | 
			
		||||
  return (
 | 
			
		||||
    <CoreCommerceProvider provider={saleorProvider} config={{ ...saleorConfig, ...config }}>
 | 
			
		||||
      {children}
 | 
			
		||||
    </CoreCommerceProvider>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useCommerce = () => useCoreCommerce()
 | 
			
		||||
							
								
								
									
										8
									
								
								framework/saleor/next.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								framework/saleor/next.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
const commerce = require('./commerce.config.json')
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  commerce,
 | 
			
		||||
  images: {
 | 
			
		||||
    domains: [process.env.COMMERCE_IMAGE_HOST],
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								framework/saleor/product/use-price.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								framework/saleor/product/use-price.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
export * from '@commerce/product/use-price'
 | 
			
		||||
export { default } from '@commerce/product/use-price'
 | 
			
		||||
							
								
								
									
										74
									
								
								framework/saleor/product/use-search.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								framework/saleor/product/use-search.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
import { SWRHook } from '@commerce/utils/types'
 | 
			
		||||
import { Product } from '@commerce/types/product'
 | 
			
		||||
import useSearch, { UseSearch } from '@commerce/product/use-search'
 | 
			
		||||
 | 
			
		||||
import { ProductCountableEdge } from '../schema'
 | 
			
		||||
import { getSearchVariables, normalizeProduct } from '../utils'
 | 
			
		||||
 | 
			
		||||
import * as query from '../utils/queries'
 | 
			
		||||
import { SearchProductsHook } from '@commerce/types/product'
 | 
			
		||||
 | 
			
		||||
export default useSearch as UseSearch<typeof handler>
 | 
			
		||||
 | 
			
		||||
export type SearchProductsInput = {
 | 
			
		||||
  search?: string
 | 
			
		||||
  categoryId?: string | number
 | 
			
		||||
  brandId?: string | number
 | 
			
		||||
  sort?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SearchProductsData = {
 | 
			
		||||
  products: Product[]
 | 
			
		||||
  found: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const handler: SWRHook<SearchProductsHook> = {
 | 
			
		||||
  fetchOptions: {
 | 
			
		||||
    query: query.ProductMany,
 | 
			
		||||
  },
 | 
			
		||||
  async fetcher({ input, options, fetch }) {
 | 
			
		||||
    const { categoryId, brandId } = input
 | 
			
		||||
 | 
			
		||||
    const data = await fetch({
 | 
			
		||||
      query: categoryId ? query.CollectionOne : options.query,
 | 
			
		||||
      method: options?.method,
 | 
			
		||||
      variables: getSearchVariables(input),
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    let edges
 | 
			
		||||
 | 
			
		||||
    if (categoryId) {
 | 
			
		||||
      edges = data.collection?.products?.edges ?? []
 | 
			
		||||
      // FIXME @zaiste, no `vendor` in Saleor
 | 
			
		||||
      // if (brandId) {
 | 
			
		||||
      //   edges = edges.filter(
 | 
			
		||||
      //     ({ node: { vendor } }: ProductCountableEdge) =>
 | 
			
		||||
      //       vendor.replace(/\s+/g, '-').toLowerCase() === brandId
 | 
			
		||||
      //   )
 | 
			
		||||
      // }
 | 
			
		||||
    } else {
 | 
			
		||||
      edges = data.products?.edges ?? []
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      products: edges.map(({ node }: ProductCountableEdge) => normalizeProduct(node)),
 | 
			
		||||
      found: !!edges.length,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  useHook:
 | 
			
		||||
    ({ useData }) =>
 | 
			
		||||
    (input = {}) => {
 | 
			
		||||
      return useData({
 | 
			
		||||
        input: [
 | 
			
		||||
          ['search', input.search],
 | 
			
		||||
          ['categoryId', input.categoryId],
 | 
			
		||||
          ['brandId', input.brandId],
 | 
			
		||||
          ['sort', input.sort],
 | 
			
		||||
        ],
 | 
			
		||||
        swrOptions: {
 | 
			
		||||
          revalidateOnFocus: false,
 | 
			
		||||
          ...input.swrOptions,
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								framework/saleor/provider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								framework/saleor/provider.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import { handler as useCart } from './cart/use-cart'
 | 
			
		||||
import { handler as useAddItem } from './cart/use-add-item'
 | 
			
		||||
import { handler as useUpdateItem } from './cart/use-update-item'
 | 
			
		||||
import { handler as useRemoveItem } from './cart/use-remove-item'
 | 
			
		||||
 | 
			
		||||
import { handler as useCustomer } from './customer/use-customer'
 | 
			
		||||
import { handler as useSearch } from './product/use-search'
 | 
			
		||||
 | 
			
		||||
import { handler as useLogin } from './auth/use-login'
 | 
			
		||||
import { handler as useLogout } from './auth/use-logout'
 | 
			
		||||
import { handler as useSignup } from './auth/use-signup'
 | 
			
		||||
 | 
			
		||||
import fetcher from './fetcher'
 | 
			
		||||
 | 
			
		||||
export const saleorProvider = {
 | 
			
		||||
  locale: 'en-us',
 | 
			
		||||
  cartCookie: '',
 | 
			
		||||
  cartCookieToken: '',
 | 
			
		||||
  fetcher,
 | 
			
		||||
  cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
 | 
			
		||||
  customer: { useCustomer },
 | 
			
		||||
  products: { useSearch },
 | 
			
		||||
  auth: { useLogin, useLogout, useSignup },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SaleorProvider = typeof saleorProvider
 | 
			
		||||
							
								
								
									
										11488
									
								
								framework/saleor/schema.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11488
									
								
								framework/saleor/schema.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										18973
									
								
								framework/saleor/schema.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18973
									
								
								framework/saleor/schema.graphql
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										43
									
								
								framework/saleor/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								framework/saleor/types.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
import type { Cart as CoreCart } from '@commerce/types'
 | 
			
		||||
import { CheckoutLine } from './schema'
 | 
			
		||||
 | 
			
		||||
export type SaleorCheckout = {
 | 
			
		||||
  id: string
 | 
			
		||||
  webUrl: string
 | 
			
		||||
  lineItems: CheckoutLine[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type Cart = CoreCart.Cart & {
 | 
			
		||||
  lineItems: LineItem[]
 | 
			
		||||
}
 | 
			
		||||
export interface LineItem extends CoreCart.LineItem {
 | 
			
		||||
  options?: any[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Cart mutations
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export type OptionSelections = {
 | 
			
		||||
  option_id: number
 | 
			
		||||
  option_value: number | string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CartItemBody = CoreCart.CartItemBody & {
 | 
			
		||||
  productId: string // The product id is always required for BC
 | 
			
		||||
  optionSelections?: OptionSelections
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// export type GetCartHandlerBody = CoreCart.GetCartHandlerBody
 | 
			
		||||
 | 
			
		||||
// export type AddCartItemBody = Core.AddCartItemBody<CartItemBody>
 | 
			
		||||
 | 
			
		||||
// export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody>
 | 
			
		||||
 | 
			
		||||
// export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody>
 | 
			
		||||
 | 
			
		||||
// export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody>
 | 
			
		||||
 | 
			
		||||
// export type RemoveCartItemBody = Core.RemoveCartItemBody
 | 
			
		||||
 | 
			
		||||
// export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody
 | 
			
		||||
							
								
								
									
										32
									
								
								framework/saleor/types/cart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								framework/saleor/types/cart.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
import * as Core from '@commerce/types/cart'
 | 
			
		||||
 | 
			
		||||
export * from '@commerce/types/cart'
 | 
			
		||||
 | 
			
		||||
export type SaleorCart = {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extend core cart types
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export type Cart = Core.Cart & {
 | 
			
		||||
  lineItems: Core.LineItem[]
 | 
			
		||||
  url?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CartTypes = Core.CartTypes
 | 
			
		||||
 | 
			
		||||
export type CartHooks = Core.CartHooks<CartTypes>
 | 
			
		||||
 | 
			
		||||
export type GetCartHook = CartHooks['getCart']
 | 
			
		||||
export type AddItemHook = CartHooks['addItem']
 | 
			
		||||
export type UpdateItemHook = CartHooks['updateItem']
 | 
			
		||||
export type RemoveItemHook = CartHooks['removeItem']
 | 
			
		||||
 | 
			
		||||
export type CartSchema = Core.CartSchema<CartTypes>
 | 
			
		||||
 | 
			
		||||
export type CartHandlers = Core.CartHandlers<CartTypes>
 | 
			
		||||
 | 
			
		||||
export type GetCartHandler = CartHandlers['getCart']
 | 
			
		||||
export type AddItemHandler = CartHandlers['addItem']
 | 
			
		||||
export type UpdateItemHandler = CartHandlers['updateItem']
 | 
			
		||||
export type RemoveItemHandler = CartHandlers['removeItem']
 | 
			
		||||
							
								
								
									
										12
									
								
								framework/saleor/utils/checkout-attach.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								framework/saleor/utils/checkout-attach.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
import * as mutation from './mutations'
 | 
			
		||||
import { CheckoutCustomerAttach } from '../schema'
 | 
			
		||||
 | 
			
		||||
export const checkoutAttach = async (fetch: any, { variables, headers }: any): Promise<CheckoutCustomerAttach> => {
 | 
			
		||||
  const data = await fetch({
 | 
			
		||||
    query: mutation.CheckoutAttach,
 | 
			
		||||
    variables,
 | 
			
		||||
    headers,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return data
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								framework/saleor/utils/checkout-create.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								framework/saleor/utils/checkout-create.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import Cookies from 'js-cookie'
 | 
			
		||||
 | 
			
		||||
import * as mutation from './mutations'
 | 
			
		||||
import { CheckoutCreate } from '../schema'
 | 
			
		||||
import { CHECKOUT_ID_COOKIE } from '@framework/const'
 | 
			
		||||
 | 
			
		||||
export const checkoutCreate = async (fetch: any): Promise<CheckoutCreate> => {
 | 
			
		||||
  const data = await fetch({ query: mutation.CheckoutCreate })
 | 
			
		||||
  const checkout = data.checkoutCreate?.checkout
 | 
			
		||||
  const checkoutId = checkout?.id
 | 
			
		||||
  const checkoutToken = checkout?.token
 | 
			
		||||
 | 
			
		||||
  const value = `${checkoutId}:${checkoutToken}`
 | 
			
		||||
 | 
			
		||||
  if (checkoutId) {
 | 
			
		||||
    const options = {
 | 
			
		||||
      expires: 60 * 60 * 24 * 30,
 | 
			
		||||
    }
 | 
			
		||||
    Cookies.set(CHECKOUT_ID_COOKIE, value, options)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return checkout
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default checkoutCreate
 | 
			
		||||
							
								
								
									
										35
									
								
								framework/saleor/utils/checkout-to-cart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								framework/saleor/utils/checkout-to-cart.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import { Cart } from '../types'
 | 
			
		||||
import { CommerceError } from '@commerce/utils/errors'
 | 
			
		||||
 | 
			
		||||
import { CheckoutLinesAdd, CheckoutLinesUpdate, CheckoutCreate, CheckoutError, Checkout, Maybe, CheckoutLineDelete } from '../schema'
 | 
			
		||||
 | 
			
		||||
import { normalizeCart } from './normalize'
 | 
			
		||||
import throwUserErrors from './throw-user-errors'
 | 
			
		||||
 | 
			
		||||
export type CheckoutQuery = {
 | 
			
		||||
  checkout: Checkout
 | 
			
		||||
  errors?: Array<CheckoutError>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type CheckoutPayload = CheckoutLinesAdd | CheckoutLinesUpdate | CheckoutCreate | CheckoutQuery | CheckoutLineDelete
 | 
			
		||||
 | 
			
		||||
const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
 | 
			
		||||
  if (!checkoutPayload) {
 | 
			
		||||
    throw new CommerceError({
 | 
			
		||||
      message: 'Missing checkout payload from response',
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const checkout = checkoutPayload?.checkout
 | 
			
		||||
  throwUserErrors(checkoutPayload?.errors)
 | 
			
		||||
 | 
			
		||||
  if (!checkout) {
 | 
			
		||||
    throw new CommerceError({
 | 
			
		||||
      message: 'Missing checkout object from response',
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return normalizeCart(checkout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default checkoutToCart
 | 
			
		||||
							
								
								
									
										25
									
								
								framework/saleor/utils/customer-token.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								framework/saleor/utils/customer-token.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import Cookies, { CookieAttributes } from 'js-cookie'
 | 
			
		||||
import * as Const from '../const'
 | 
			
		||||
 | 
			
		||||
export const getToken = () => Cookies.get(Const.SALEOR_TOKEN)
 | 
			
		||||
export const setToken = (token?: string, options?: CookieAttributes) => {
 | 
			
		||||
  setCookie(Const.SALEOR_TOKEN, token, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getCSRFToken = () => Cookies.get(Const.SALEOR_CRSF_TOKEN)
 | 
			
		||||
export const setCSRFToken = (token?: string, options?: CookieAttributes) => {
 | 
			
		||||
  setCookie(Const.SALEOR_CRSF_TOKEN, token, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getCheckoutToken = () => Cookies.get(Const.CHECKOUT_ID_COOKIE)
 | 
			
		||||
export const setCheckoutToken = (token?: string, options?: CookieAttributes) => {
 | 
			
		||||
  setCookie(Const.CHECKOUT_ID_COOKIE, token, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const setCookie = (name: string, token?: string, options?: CookieAttributes) => {
 | 
			
		||||
  if (!token) {
 | 
			
		||||
    Cookies.remove(name)
 | 
			
		||||
  } else {
 | 
			
		||||
    Cookies.set(name, token, options ?? { expires: 60 * 60 * 24 * 30 })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								framework/saleor/utils/fragments/checkout-details.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								framework/saleor/utils/fragments/checkout-details.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
export const CheckoutDetails = /* GraphQL */ `
 | 
			
		||||
  fragment CheckoutDetails on Checkout {
 | 
			
		||||
    id
 | 
			
		||||
    token
 | 
			
		||||
    created
 | 
			
		||||
    totalPrice {
 | 
			
		||||
      currency
 | 
			
		||||
      gross {
 | 
			
		||||
        amount
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    subtotalPrice {
 | 
			
		||||
      currency
 | 
			
		||||
      gross {
 | 
			
		||||
        amount
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lines {
 | 
			
		||||
      id
 | 
			
		||||
      variant {
 | 
			
		||||
        id
 | 
			
		||||
        name
 | 
			
		||||
        sku
 | 
			
		||||
        product {
 | 
			
		||||
          name
 | 
			
		||||
          slug
 | 
			
		||||
        }
 | 
			
		||||
        media {
 | 
			
		||||
          url
 | 
			
		||||
        }
 | 
			
		||||
        pricing {
 | 
			
		||||
          price {
 | 
			
		||||
            gross {
 | 
			
		||||
              amount
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      quantity
 | 
			
		||||
      totalPrice {
 | 
			
		||||
        currency
 | 
			
		||||
        gross {
 | 
			
		||||
          amount
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										2
									
								
								framework/saleor/utils/fragments/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								framework/saleor/utils/fragments/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
export { ProductConnection } from './product'
 | 
			
		||||
export { CheckoutDetails } from './checkout-details'
 | 
			
		||||
							
								
								
									
										29
									
								
								framework/saleor/utils/fragments/product.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								framework/saleor/utils/fragments/product.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
export const ProductConnection = /* GraphQL */ `
 | 
			
		||||
  fragment ProductConnection on ProductCountableConnection {
 | 
			
		||||
    pageInfo {
 | 
			
		||||
      hasNextPage
 | 
			
		||||
      hasPreviousPage
 | 
			
		||||
    }
 | 
			
		||||
    edges {
 | 
			
		||||
      node {
 | 
			
		||||
        id
 | 
			
		||||
        name
 | 
			
		||||
        description
 | 
			
		||||
        slug
 | 
			
		||||
        pricing {
 | 
			
		||||
          priceRange {
 | 
			
		||||
            start {
 | 
			
		||||
              net {
 | 
			
		||||
                amount
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        media {
 | 
			
		||||
          url
 | 
			
		||||
          alt
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										23
									
								
								framework/saleor/utils/get-categories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								framework/saleor/utils/get-categories.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import { Category } from '@commerce/types/site'
 | 
			
		||||
import { SaleorConfig } from '../api'
 | 
			
		||||
import { CollectionCountableEdge } from '../schema'
 | 
			
		||||
import * as query from './queries'
 | 
			
		||||
 | 
			
		||||
const getCategories = async (config: SaleorConfig): Promise<Category[]> => {
 | 
			
		||||
  const { data } = await config.fetch(query.CollectionMany, {
 | 
			
		||||
    variables: {
 | 
			
		||||
      first: 100,
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    data.collections?.edges?.map(({ node: { id, name, slug } }: CollectionCountableEdge) => ({
 | 
			
		||||
      id,
 | 
			
		||||
      name,
 | 
			
		||||
      slug,
 | 
			
		||||
      path: `/${slug}`,
 | 
			
		||||
    })) ?? []
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default getCategories
 | 
			
		||||
							
								
								
									
										9
									
								
								framework/saleor/utils/get-checkout-id.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								framework/saleor/utils/get-checkout-id.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import Cookies from 'js-cookie'
 | 
			
		||||
import { CHECKOUT_ID_COOKIE } from '../const'
 | 
			
		||||
 | 
			
		||||
const getCheckoutId = (id?: string) => {
 | 
			
		||||
  const r = Cookies.get(CHECKOUT_ID_COOKIE)?.split(':') || []
 | 
			
		||||
  return { checkoutId: r[0], checkoutToken: r[1] }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default getCheckoutId
 | 
			
		||||
							
								
								
									
										18
									
								
								framework/saleor/utils/get-search-variables.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								framework/saleor/utils/get-search-variables.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
import { getSortVariables } from './get-sort-variables'
 | 
			
		||||
import type { SearchProductsInput } from '../product/use-search'
 | 
			
		||||
 | 
			
		||||
export const getSearchVariables = ({ brandId, search, categoryId, sort }: SearchProductsInput) => {
 | 
			
		||||
  const sortBy = {
 | 
			
		||||
    field: 'NAME',
 | 
			
		||||
    direction: 'ASC',
 | 
			
		||||
    ...getSortVariables(sort, !!categoryId),
 | 
			
		||||
    channel: 'default-channel',
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
    categoryId,
 | 
			
		||||
    filter: { search },
 | 
			
		||||
    sortBy,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default getSearchVariables
 | 
			
		||||
							
								
								
									
										30
									
								
								framework/saleor/utils/get-sort-variables.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								framework/saleor/utils/get-sort-variables.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
export const getSortVariables = (sort?: string, isCategory: boolean = false) => {
 | 
			
		||||
  let output = {}
 | 
			
		||||
  switch (sort) {
 | 
			
		||||
    case 'price-asc':
 | 
			
		||||
      output = {
 | 
			
		||||
        field: 'PRICE',
 | 
			
		||||
        direction: 'ASC',
 | 
			
		||||
      }
 | 
			
		||||
      break
 | 
			
		||||
    case 'price-desc':
 | 
			
		||||
      output = {
 | 
			
		||||
        field: 'PRICE',
 | 
			
		||||
        direction: 'DESC',
 | 
			
		||||
      }
 | 
			
		||||
      break
 | 
			
		||||
    case 'trending-desc':
 | 
			
		||||
      output = {
 | 
			
		||||
        field: 'RANK',
 | 
			
		||||
        direction: 'DESC',
 | 
			
		||||
      }
 | 
			
		||||
      break
 | 
			
		||||
    case 'latest-desc':
 | 
			
		||||
      output = {
 | 
			
		||||
        field: 'DATE',
 | 
			
		||||
        direction: 'DESC',
 | 
			
		||||
      }
 | 
			
		||||
      break
 | 
			
		||||
  }
 | 
			
		||||
  return output
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								framework/saleor/utils/get-vendors.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								framework/saleor/utils/get-vendors.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
import { SaleorConfig } from '../api'
 | 
			
		||||
 | 
			
		||||
export type Brand = {
 | 
			
		||||
  entityId: string
 | 
			
		||||
  name: string
 | 
			
		||||
  path: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type BrandEdge = {
 | 
			
		||||
  node: Brand
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type Brands = BrandEdge[]
 | 
			
		||||
 | 
			
		||||
// TODO: Find a way to get vendors from meta
 | 
			
		||||
const getVendors = async (config: SaleorConfig): Promise<BrandEdge[]> => {
 | 
			
		||||
  // const vendors = await fetchAllProducts({
 | 
			
		||||
  //   config,
 | 
			
		||||
  //   query: getAllProductVendors,
 | 
			
		||||
  //   variables: {
 | 
			
		||||
  //     first: 100,
 | 
			
		||||
  //   },
 | 
			
		||||
  // })
 | 
			
		||||
 | 
			
		||||
  // let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor)
 | 
			
		||||
 | 
			
		||||
  // return [...new Set(vendorsStrings)].map((v) => {
 | 
			
		||||
  //   const id = v.replace(/\s+/g, '-').toLowerCase()
 | 
			
		||||
  //   return {
 | 
			
		||||
  //     node: {
 | 
			
		||||
  //       entityId: id,
 | 
			
		||||
  //       name: v,
 | 
			
		||||
  //       path: `brands/${id}`,
 | 
			
		||||
  //     },
 | 
			
		||||
  //   }
 | 
			
		||||
  // })
 | 
			
		||||
 | 
			
		||||
  return []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default getVendors
 | 
			
		||||
							
								
								
									
										27
									
								
								framework/saleor/utils/handle-fetch-response.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								framework/saleor/utils/handle-fetch-response.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import { FetcherError } from '@commerce/utils/errors'
 | 
			
		||||
 | 
			
		||||
export function getError(errors: any[], status: number) {
 | 
			
		||||
  errors = errors ?? [{ message: 'Failed to fetch Saleor API' }]
 | 
			
		||||
  return new FetcherError({ errors, status })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getAsyncError(res: Response) {
 | 
			
		||||
  const data = await res.json()
 | 
			
		||||
  return getError(data.errors, res.status)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const handleFetchResponse = async (res: Response) => {
 | 
			
		||||
  if (res.ok) {
 | 
			
		||||
    const { data, errors } = await res.json()
 | 
			
		||||
 | 
			
		||||
    if (errors && errors.length) {
 | 
			
		||||
      throw getError(errors, res.status)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return data
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw await getAsyncError(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default handleFetchResponse
 | 
			
		||||
							
								
								
									
										35
									
								
								framework/saleor/utils/handle-login.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								framework/saleor/utils/handle-login.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import { FetcherOptions } from '@commerce/utils/types'
 | 
			
		||||
import { CreateToken, Mutation, MutationTokenCreateArgs } from '../schema'
 | 
			
		||||
import { setToken, setCSRFToken } from './customer-token'
 | 
			
		||||
import * as mutation from './mutations'
 | 
			
		||||
import throwUserErrors from './throw-user-errors'
 | 
			
		||||
 | 
			
		||||
const handleLogin = (data: CreateToken) => {
 | 
			
		||||
  throwUserErrors(data?.errors)
 | 
			
		||||
 | 
			
		||||
  const token = data?.token
 | 
			
		||||
 | 
			
		||||
  if (token) {
 | 
			
		||||
    setToken(token)
 | 
			
		||||
    setCSRFToken(token)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return token
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const handleAutomaticLogin = async (
 | 
			
		||||
  fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
 | 
			
		||||
  input: MutationTokenCreateArgs
 | 
			
		||||
) => {
 | 
			
		||||
  try {
 | 
			
		||||
    const { tokenCreate } = await fetch<Mutation, MutationTokenCreateArgs>({
 | 
			
		||||
      query: mutation.SessionCreate,
 | 
			
		||||
      variables: { ...input },
 | 
			
		||||
    })
 | 
			
		||||
    handleLogin(tokenCreate!)
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    //
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default handleLogin
 | 
			
		||||
							
								
								
									
										19
									
								
								framework/saleor/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								framework/saleor/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
export { getSortVariables } from './get-sort-variables'
 | 
			
		||||
 | 
			
		||||
export { default as handleFetchResponse } from './handle-fetch-response'
 | 
			
		||||
export { default as getSearchVariables } from './get-search-variables'
 | 
			
		||||
export { default as getVendors } from './get-vendors'
 | 
			
		||||
export { default as getCategories } from './get-categories'
 | 
			
		||||
export { default as getCheckoutId } from './get-checkout-id'
 | 
			
		||||
 | 
			
		||||
export { default as checkoutCreate } from './checkout-create'
 | 
			
		||||
export { checkoutAttach } from './checkout-attach'
 | 
			
		||||
 | 
			
		||||
export { default as checkoutToCart } from './checkout-to-cart'
 | 
			
		||||
export { default as handleLogin, handleAutomaticLogin } from './handle-login'
 | 
			
		||||
export { default as throwUserErrors } from './throw-user-errors'
 | 
			
		||||
 | 
			
		||||
export * from './queries'
 | 
			
		||||
export * from './mutations'
 | 
			
		||||
export * from './normalize'
 | 
			
		||||
export * from './customer-token'
 | 
			
		||||
							
								
								
									
										15
									
								
								framework/saleor/utils/mutations/account-create.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								framework/saleor/utils/mutations/account-create.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
export const AccountCreate = /* GraphQL */ `
 | 
			
		||||
  mutation AccountCreate($input: AccountRegisterInput!) {
 | 
			
		||||
    accountRegister(input: $input) {
 | 
			
		||||
      errors {
 | 
			
		||||
        code
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
      user {
 | 
			
		||||
        email
 | 
			
		||||
        isActive
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										12
									
								
								framework/saleor/utils/mutations/checkout-attach.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								framework/saleor/utils/mutations/checkout-attach.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
export const CheckoutAttach = /* GraphQl */ `
 | 
			
		||||
  mutation CheckoutAttach($checkoutId: ID!) {
 | 
			
		||||
    checkoutCustomerAttach(checkoutId: $checkoutId) {
 | 
			
		||||
      errors {
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
      checkout {
 | 
			
		||||
        id
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										17
									
								
								framework/saleor/utils/mutations/checkout-create.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								framework/saleor/utils/mutations/checkout-create.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import * as fragment from '../fragments'
 | 
			
		||||
 | 
			
		||||
export const CheckoutCreate = /* GraphQL */ `
 | 
			
		||||
  mutation CheckoutCreate {
 | 
			
		||||
    checkoutCreate(input: { email: "customer@example.com", lines: [], channel: "default-channel" }) {
 | 
			
		||||
      errors {
 | 
			
		||||
        code
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
      checkout {
 | 
			
		||||
        ...CheckoutDetails
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ${fragment.CheckoutDetails}
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										17
									
								
								framework/saleor/utils/mutations/checkout-line-add.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								framework/saleor/utils/mutations/checkout-line-add.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import * as fragment from '../fragments'
 | 
			
		||||
 | 
			
		||||
export const CheckoutLineAdd = /* GraphQL */ `
 | 
			
		||||
  mutation CheckoutLineAdd($checkoutId: ID!, $lineItems: [CheckoutLineInput!]!) {
 | 
			
		||||
    checkoutLinesAdd(checkoutId: $checkoutId, lines: $lineItems) {
 | 
			
		||||
      errors {
 | 
			
		||||
        code
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
      checkout {
 | 
			
		||||
        ...CheckoutDetails
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ${fragment.CheckoutDetails}
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										17
									
								
								framework/saleor/utils/mutations/checkout-line-remove.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								framework/saleor/utils/mutations/checkout-line-remove.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import * as fragment from '../fragments'
 | 
			
		||||
 | 
			
		||||
export const CheckoutLineDelete = /* GraphQL */ `
 | 
			
		||||
  mutation CheckoutLineDelete($checkoutId: ID!, $lineId: ID!) {
 | 
			
		||||
    checkoutLineDelete(checkoutId: $checkoutId, lineId: $lineId) {
 | 
			
		||||
      errors {
 | 
			
		||||
        code
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
      checkout {
 | 
			
		||||
        ...CheckoutDetails
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ${fragment.CheckoutDetails}
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										17
									
								
								framework/saleor/utils/mutations/checkout-line-update.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								framework/saleor/utils/mutations/checkout-line-update.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import * as fragment from '../fragments'
 | 
			
		||||
 | 
			
		||||
export const CheckoutLineUpdate = /* GraphQL */ `
 | 
			
		||||
  mutation CheckoutLineUpdate($checkoutId: ID!, $lineItems: [CheckoutLineInput!]!) {
 | 
			
		||||
    checkoutLinesUpdate(checkoutId: $checkoutId, lines: $lineItems) {
 | 
			
		||||
      errors {
 | 
			
		||||
        code
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
      checkout {
 | 
			
		||||
        ...CheckoutDetails
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ${fragment.CheckoutDetails}
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										8
									
								
								framework/saleor/utils/mutations/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								framework/saleor/utils/mutations/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
export { AccountCreate } from './account-create'
 | 
			
		||||
export { CheckoutCreate } from './checkout-create'
 | 
			
		||||
export { CheckoutLineAdd } from './checkout-line-add'
 | 
			
		||||
export { CheckoutLineUpdate } from './checkout-line-update'
 | 
			
		||||
export { CheckoutLineDelete } from './checkout-line-remove'
 | 
			
		||||
export { SessionCreate } from './session-create'
 | 
			
		||||
export { SessionDestroy } from './session-destroy'
 | 
			
		||||
export { CheckoutAttach } from './checkout-attach'
 | 
			
		||||
							
								
								
									
										14
									
								
								framework/saleor/utils/mutations/session-create.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								framework/saleor/utils/mutations/session-create.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
export const SessionCreate = /* GraphQL */ `
 | 
			
		||||
  mutation SessionCreate($email: String!, $password: String!) {
 | 
			
		||||
    tokenCreate(email: $email, password: $password) {
 | 
			
		||||
      token
 | 
			
		||||
      refreshToken
 | 
			
		||||
      csrfToken
 | 
			
		||||
      errors {
 | 
			
		||||
        code
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										10
									
								
								framework/saleor/utils/mutations/session-destroy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								framework/saleor/utils/mutations/session-destroy.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
export const SessionDestroy = /* GraphQL */ `
 | 
			
		||||
  mutation SessionDestroy {
 | 
			
		||||
    tokensDeactivateAll {
 | 
			
		||||
      errors {
 | 
			
		||||
        field
 | 
			
		||||
        message
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										133
									
								
								framework/saleor/utils/normalize.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								framework/saleor/utils/normalize.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
import { Product } from '@commerce/types/product'
 | 
			
		||||
 | 
			
		||||
import { Product as SaleorProduct, Checkout, CheckoutLine, Money, ProductVariant } from '../schema'
 | 
			
		||||
 | 
			
		||||
import type { Cart, LineItem } from '../types'
 | 
			
		||||
 | 
			
		||||
// TODO: Check nextjs-commerce bug if no images are added for a product
 | 
			
		||||
const placeholderImg = '/product-img-placeholder.svg'
 | 
			
		||||
 | 
			
		||||
const money = ({ amount, currency }: Money) => {
 | 
			
		||||
  return {
 | 
			
		||||
    value: +amount,
 | 
			
		||||
    currencyCode: currency || 'USD',
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const normalizeProductOptions = (options: ProductVariant[]) => {
 | 
			
		||||
  return options
 | 
			
		||||
    ?.map((option) => option?.attributes)
 | 
			
		||||
    .flat(1)
 | 
			
		||||
    .reduce<any>((acc, x) => {
 | 
			
		||||
      if (acc.find(({ displayName }: any) => displayName === x.attribute.name)) {
 | 
			
		||||
        return acc.map((opt: any) => {
 | 
			
		||||
          return opt.displayName === x.attribute.name
 | 
			
		||||
            ? {
 | 
			
		||||
                ...opt,
 | 
			
		||||
                values: [
 | 
			
		||||
                  ...opt.values,
 | 
			
		||||
                  ...x.values.map((value: any) => ({
 | 
			
		||||
                    label: value?.name,
 | 
			
		||||
                  })),
 | 
			
		||||
                ],
 | 
			
		||||
              }
 | 
			
		||||
            : opt
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return acc.concat({
 | 
			
		||||
        __typename: 'MultipleChoiceOption',
 | 
			
		||||
        displayName: x.attribute.name,
 | 
			
		||||
        variant: 'size',
 | 
			
		||||
        values: x.values.map((value: any) => ({
 | 
			
		||||
          label: value?.name,
 | 
			
		||||
        })),
 | 
			
		||||
      })
 | 
			
		||||
    }, [])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const normalizeProductVariants = (variants: ProductVariant[]) => {
 | 
			
		||||
  return variants?.map((variant) => {
 | 
			
		||||
    const { id, sku, name, pricing } = variant
 | 
			
		||||
    const price = pricing?.price?.net && money(pricing.price.net)?.value
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      id,
 | 
			
		||||
      name,
 | 
			
		||||
      sku: sku ?? id,
 | 
			
		||||
      price,
 | 
			
		||||
      listPrice: price,
 | 
			
		||||
      requiresShipping: true,
 | 
			
		||||
      options: normalizeProductOptions([variant]),
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function normalizeProduct(productNode: SaleorProduct): Product {
 | 
			
		||||
  const { id, name, media = [], variants, description, slug, pricing, ...rest } = productNode
 | 
			
		||||
 | 
			
		||||
  const product = {
 | 
			
		||||
    id,
 | 
			
		||||
    name,
 | 
			
		||||
    vendor: '',
 | 
			
		||||
    description: description ? JSON.parse(description)?.blocks[0]?.data.text : '',
 | 
			
		||||
    path: `/${slug}`,
 | 
			
		||||
    slug: slug?.replace(/^\/+|\/+$/g, ''),
 | 
			
		||||
    price: (pricing?.priceRange?.start?.net && money(pricing.priceRange.start.net)) || {
 | 
			
		||||
      value: 0,
 | 
			
		||||
      currencyCode: 'USD',
 | 
			
		||||
    },
 | 
			
		||||
    // TODO: Check nextjs-commerce bug if no images are added for a product
 | 
			
		||||
    images: media?.length ? media : [{ url: placeholderImg }],
 | 
			
		||||
    variants: variants && variants.length > 0 ? normalizeProductVariants(variants as ProductVariant[]) : [],
 | 
			
		||||
    options: variants && variants.length > 0 ? normalizeProductOptions(variants as ProductVariant[]) : [],
 | 
			
		||||
    ...rest,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return product as Product
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function normalizeCart(checkout: Checkout): Cart {
 | 
			
		||||
  const lines = checkout.lines as CheckoutLine[]
 | 
			
		||||
  const lineItems: LineItem[] = lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    id: checkout.id,
 | 
			
		||||
    customerId: '',
 | 
			
		||||
    email: '',
 | 
			
		||||
    createdAt: checkout.created,
 | 
			
		||||
    currency: {
 | 
			
		||||
      code: checkout.totalPrice?.currency!,
 | 
			
		||||
    },
 | 
			
		||||
    taxesIncluded: false,
 | 
			
		||||
    lineItems,
 | 
			
		||||
    lineItemsSubtotalPrice: checkout.subtotalPrice?.gross?.amount!,
 | 
			
		||||
    subtotalPrice: checkout.subtotalPrice?.gross?.amount!,
 | 
			
		||||
    totalPrice: checkout.totalPrice?.gross.amount!,
 | 
			
		||||
    discounts: [],
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function normalizeLineItem({ id, variant, quantity }: CheckoutLine): LineItem {
 | 
			
		||||
  return {
 | 
			
		||||
    id,
 | 
			
		||||
    variantId: String(variant?.id),
 | 
			
		||||
    productId: String(variant?.id),
 | 
			
		||||
    name: `${variant.product.name}`,
 | 
			
		||||
    quantity,
 | 
			
		||||
    variant: {
 | 
			
		||||
      id: String(variant?.id),
 | 
			
		||||
      sku: variant?.sku ?? '',
 | 
			
		||||
      name: variant?.name!,
 | 
			
		||||
      image: {
 | 
			
		||||
        url: variant?.media![0] ? variant?.media![0].url : placeholderImg,
 | 
			
		||||
      },
 | 
			
		||||
      requiresShipping: false,
 | 
			
		||||
      price: variant?.pricing?.price?.gross.amount!,
 | 
			
		||||
      listPrice: 0,
 | 
			
		||||
    },
 | 
			
		||||
    path: String(variant?.product?.slug),
 | 
			
		||||
    discounts: [],
 | 
			
		||||
    options: [],
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								framework/saleor/utils/queries/checkout-one.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								framework/saleor/utils/queries/checkout-one.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
import * as fragment from '../fragments'
 | 
			
		||||
 | 
			
		||||
export const CheckoutOne = /* GraphQL */ `
 | 
			
		||||
  query CheckoutOne($checkoutId: UUID!) {
 | 
			
		||||
    checkout(token: $checkoutId) {
 | 
			
		||||
      ... on Checkout {
 | 
			
		||||
        ...CheckoutDetails
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ${fragment.CheckoutDetails}
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										13
									
								
								framework/saleor/utils/queries/collection-many.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								framework/saleor/utils/queries/collection-many.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
export const CollectionMany = /* GraphQL */ `
 | 
			
		||||
  query CollectionMany($first: Int!, $channel: String = "default-channel") {
 | 
			
		||||
    collections(first: $first, channel: $channel) {
 | 
			
		||||
      edges {
 | 
			
		||||
        node {
 | 
			
		||||
          id
 | 
			
		||||
          name
 | 
			
		||||
          slug
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										13
									
								
								framework/saleor/utils/queries/collection-one.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								framework/saleor/utils/queries/collection-one.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import * as fragment from '../fragments'
 | 
			
		||||
 | 
			
		||||
export const CollectionOne = /* GraphQL */ `
 | 
			
		||||
  query getProductsFromCollection($categoryId: ID!, $first: Int = 100, $channel: String = "default-channel") {
 | 
			
		||||
    collection(id: $categoryId, channel: $channel) {
 | 
			
		||||
      id
 | 
			
		||||
      products(first: $first) {
 | 
			
		||||
        ...ProductConnection
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ${fragment.ProductConnection}
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										11
									
								
								framework/saleor/utils/queries/customer-current.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								framework/saleor/utils/queries/customer-current.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
export const CustomerCurrent = /* GraphQL */ `
 | 
			
		||||
  query CustomerCurrent {
 | 
			
		||||
    me {
 | 
			
		||||
      id
 | 
			
		||||
      email
 | 
			
		||||
      firstName
 | 
			
		||||
      lastName
 | 
			
		||||
      dateJoined
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
							
								
								
									
										7
									
								
								framework/saleor/utils/queries/customer-one.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								framework/saleor/utils/queries/customer-one.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
export const CustomerOne = /* GraphQL */ `
 | 
			
		||||
  query CustomerOne($customerAccessToken: String!) {
 | 
			
		||||
    customer(customerAccessToken: $customerAccessToken) {
 | 
			
		||||
      id
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
export const getAllProductVendors = /* GraphQL */ `
 | 
			
		||||
  query getAllProductVendors($first: Int = 250, $cursor: String) {
 | 
			
		||||
    products(first: $first, after: $cursor) {
 | 
			
		||||
      pageInfo {
 | 
			
		||||
        hasNextPage
 | 
			
		||||
        hasPreviousPage
 | 
			
		||||
      }
 | 
			
		||||
      edges {
 | 
			
		||||
        node {
 | 
			
		||||
          vendor
 | 
			
		||||
        }
 | 
			
		||||
        cursor
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
export const getAllProductsPathsQuery = /* GraphQL */ `
 | 
			
		||||
  query getAllProductPaths($first: Int = 100, $cursor: String, $channel: String = "default-channel") {
 | 
			
		||||
    products(first: $first, after: $cursor, channel: $channel) {
 | 
			
		||||
      pageInfo {
 | 
			
		||||
        hasNextPage
 | 
			
		||||
        hasPreviousPage
 | 
			
		||||
      }
 | 
			
		||||
      edges {
 | 
			
		||||
        node {
 | 
			
		||||
          slug
 | 
			
		||||
        }
 | 
			
		||||
        cursor
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user