4
0
forked from crowetic/commerce
Jakub Neander 3b2bf654fe
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>
2021-06-10 01:46:28 -05:00
..
2021-06-10 01:46:28 -05:00
2021-06-10 01:46:28 -05:00
2021-06-10 01:46:28 -05:00

Commerce Framework

The commerce framework ships multiple hooks and a Node.js API, both using an underlying headless e-commerce platform, which we call commerce providers.

The core features are:

  • Code splitted hooks for data fetching using SWR, and to handle common user actions
  • A Node.js API for initial data population, static generation of content and for creating the API endpoints that connect to the hooks, if required.

👩‍🔬 If you would like to contribute a new provider, check the docs for Adding a new Commerce Provider.

🚧 The core commerce framework is under active development, new features and updates will be continuously added over time. Breaking changes are expected while we finish the API.

Commerce Hooks

A commerce hook is a React hook that's connected to a commerce provider. They focus on user actions and data fetching of data that wasn't statically generated.

Data fetching hooks use SWR underneath and you're welcome to use any of its return values and options. For example, using the useCustomer hook:

const { data, isLoading, error } = useCustomer({
  swrOptions: {
    revalidateOnFocus: true,
  },
})

CommerceProvider

This component adds the provider config and handlers to the context of your React tree for it's children. You can optionally pass the locale to it:

import { CommerceProvider } from '@framework'

const App = ({ locale = 'en-US', children }) => {
  return <CommerceProvider locale={locale}>{children}</CommerceProvider>
}

Authentication Hooks

useSignup

Returns a signup function that can be used to sign up the current visitor:

import useSignup from '@framework/auth/use-signup'

const SignupView = () => {
  const signup = useSignup()

  const handleSignup = async () => {
    await signup({
      email,
      firstName,
      lastName,
      password,
    })
  }

  return <form onSubmit={handleSignup}>{children}</form>
}

useLogin

Returns a login function that can be used to sign in the current visitor into an existing customer:

import useLogin from '@framework/auth/use-login'

const LoginView = () => {
  const login = useLogin()
  const handleLogin = async () => {
    await login({
      email,
      password,
    })
  }

  return <form onSubmit={handleLogin}>{children}</form>
}

useLogout

Returns a logout function that signs out the current customer when called.

import useLogout from '@framework/auth/use-logout'

const LogoutButton = () => {
  const logout = useLogout()
  return (
    <button type="button" onClick={() => logout()}>
      Logout
    </button>
  )
}

Customer Hooks

useCustomer

Fetches and returns the data of the signed in customer:

import useCustomer from '@framework/customer/use-customer'

const Profile = () => {
  const { data, isLoading, error } = useCustomer()

  if (isLoading) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>
  if (!data) return null

  return <div>Hello, {data.firstName}</div>
}

Product Hooks

usePrice

Helper hook to format price according to the commerce locale and currency code. It also handles discounts:

import useCart from '@framework/cart/use-cart'
import usePrice from '@framework/product/use-price'

// ...
const { data } = useCart()
const { price, discount, basePrice } = usePrice(
  data && {
    amount: data.subtotalPrice,
    currencyCode: data.currency.code,
    // If `baseAmount` is used, a discount will be calculated
    // baseAmount: number,
  }
)
// ...

useSearch

Fetches and returns the products that match a set of filters:

import useSearch from '@framework/product/use-search'

const SearchPage = ({ searchString, category, brand, sortStr }) => {
  const { data } = useSearch({
    search: searchString || '',
    categoryId: category?.entityId,
    brandId: brand?.entityId,
    sort: sortStr,
  })

  return (
    <Grid layout="normal">
      {data.products.map((product) => (
        <ProductCard key={product.path} product={product} />
      ))}
    </Grid>
  )
}

Cart Hooks

useCart

Fetches and returns the data of the current cart:

import useCart from '@framework/cart/use-cart'

const CartTotal = () => {
  const { data, isLoading, isEmpty, error } = useCart()

  if (isLoading) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>
  if (isEmpty) return <p>The cart is empty</p>

  return <p>The cart total is {data.totalPrice}</p>
}

useAddItem

Returns a function that adds a new item to the cart when called, if this is the first item it will create the cart:

import { useAddItem } from '@framework/cart'

const AddToCartButton = ({ productId, variantId }) => {
  const addItem = useAddItem()

  const addToCart = async () => {
    await addItem({
      productId,
      variantId,
    })
  }

  return <button onClick={addToCart}>Add To Cart</button>
}

useUpdateItem

Returns a function that updates a current item in the cart when called, usually the quantity.

import { useUpdateItem } from '@framework/cart'

const CartItemQuantity = ({ item }) => {
  const [quantity, setQuantity] = useState(item.quantity)
  const updateItem = useUpdateItem({ item })

  const updateQuantity = async (e) => {
    const val = e.target.value

    setQuantity(val)
    await updateItem({ quantity: val })
  }

  return (
    <input
      type="number"
      max={99}
      min={0}
      value={quantity}
      onChange={updateQuantity}
    />
  )
}

If the quantity is lower than 1 the item will be removed from the cart.

useRemoveItem

Returns a function that removes an item in the cart when called:

import { useRemoveItem } from '@framework/cart'

const RemoveButton = ({ item }) => {
  const removeItem = useRemoveItem()
  const handleRemove = async () => {
    await removeItem(item)
  }

  return <button onClick={handleRemove}>Remove</button>
}

Wishlist Hooks

Wishlist hooks work just like cart hooks. Feel free to check how those work first.

The example below shows how to use the useWishlist, useAddItem and useRemoveItem hooks:

import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist'

const WishlistButton = ({ productId, variant }) => {
  const addItem = useAddItem()
  const removeItem = useRemoveItem()
  const { data, isLoading, isEmpty, error } = useWishlist()

  if (isLoading) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>
  if (isEmpty) return <p>The wihslist is empty</p>

  const { data: customer } = useCustomer()
  const itemInWishlist = data?.items?.find(
    (item) => item.product_id === productId && item.variant_id === variant.id
  )

  const handleWishlistChange = async (e) => {
    e.preventDefault()
    if (!customer) return

    if (itemInWishlist) {
      await removeItem({ id: itemInWishlist.id })
    } else {
      await addItem({
        productId,
        variantId: variant.id,
      })
    }
  }

  return (
    <button onClick={handleWishlistChange}>
      <Heart fill={itemInWishlist ? 'var(--pink)' : 'none'} />
    </button>
  )
}

Commerce API

While commerce hooks focus on client side data fetching and interactions, the commerce API focuses on static content generation for pages and API endpoints in a Node.js context.

The commerce API is currently going through a refactor in https://github.com/vercel/commerce/pull/252 - We'll update the docs once the API is released.

More

Feel free to read through the source for more usage, and check the commerce vercel demo and commerce repo for usage examples: (demo.vercel.store) (repo)