Implement useCart/useAddItem

This commit is contained in:
Michael Bromley 2021-01-25 13:41:14 +01:00
parent 2d2861b944
commit 0f7c151805
6 changed files with 110 additions and 52 deletions

View File

@ -1,4 +1,3 @@
import type { GetLoggedInCustomerQuery } from '../../../schema'
import type { CustomersHandlers } from '..'
export const getLoggedInCustomerQuery = /* GraphQL */ `
@ -22,7 +21,7 @@ export const getLoggedInCustomerQuery = /* GraphQL */ `
}
`
export type Customer = NonNullable<GetLoggedInCustomerQuery['customer']>
export type Customer = NonNullable<any>
const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({
req,

View File

@ -9,7 +9,7 @@ const API_URL = process.env.VENDURE_SHOP_API_URL
if (!API_URL) {
throw new Error(
`The environment variable BIGCOMMERCE_STOREFRONT_API_URL is missing and it's required to access your store`
`The environment variable VENDURE_SHOP_API_URL is missing and it's required to access your store`
)
}

View File

@ -1,25 +1,43 @@
import { useCallback } from 'react'
import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import { HookFetcher } from '@commerce/utils/types'
import fetchGraphqlApi from '@framework/api/utils/fetch-graphql-api'
import useCartAddItem from '@commerce/cart/use-add-item'
import type { ItemBody, AddItemBody } from '../api/cart'
import useCart, { Cart } from './use-cart'
import useCart from './use-cart'
import { useCallback } from 'react'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'POST',
export const addItemToOrderMutation = /* GraphQL */ `
mutation addItemToOrder($variantId: ID!, $quantity: Int!) {
addItemToOrder(productVariantId: $variantId, quantity: $quantity) {
... on Order {
id
code
totalQuantity
total
totalWithTax
lines {
id
productVariant {
featuredAsset {
id
preview
}
}
}
}
}
}
`
export type AddItemInput = ItemBody
export type AddItemInput = { productId?: number; variantId: number; quantity?: number; };
export const fetcher: HookFetcher<Cart, AddItemBody> = (
export const fetcher: HookFetcher<Cart, AddItemInput> = (
options,
{ item },
{ variantId, quantity },
fetch
) => {
if (
item.quantity &&
(!Number.isInteger(item.quantity) || item.quantity! < 1)
quantity &&
(!Number.isInteger(quantity) || quantity! < 1)
) {
throw new CommerceError({
message: 'The item quantity has to be a valid integer greater than 0',
@ -27,20 +45,23 @@ export const fetcher: HookFetcher<Cart, AddItemBody> = (
}
return fetch({
...defaultOpts,
...options,
body: { item },
query: addItemToOrderMutation,
variables: { variantId, quantity: quantity || 1 },
}).then(res => {
console.log({ res });
return res;
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useAddItem = () => {
const { mutate } = useCart()
const fn = useCartAddItem(defaultOpts, customFetcher)
const fn = useCartAddItem({}, customFetcher)
return useCallback(
async function addItem(input: AddItemInput) {
const data = await fn({ item: input })
const data = await fn({ quantity: input.quantity, variantId: input.variantId })
await mutate(data, false)
return data
},

View File

@ -1,46 +1,83 @@
import { normalizeCart } from '../lib/normalize'
import type { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data'
import useResponse from '@commerce/utils/use-response'
import fetchGraphqlApi from '@framework/api/utils/fetch-graphql-api'
import { HookFetcher } from '@commerce/utils/types'
import useData, { SwrOptions } from '@commerce/utils/use-data'
import useCommerceCart, { CartInput } from '@commerce/cart/use-cart'
import type { Cart as BigCommerceCart } from '../api/cart'
import useResponse from '@commerce/utils/use-response'
import useAction from '@commerce/utils/use-action'
import { useCallback } from 'react'
import { normalizeCart } from '../../bigcommerce/lib/normalize'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'GET',
export const getCartQuery = /* GraphQL */ `
query activeOrder {
activeOrder {
id
code
totalQuantity
subTotal
subTotalWithTax
total
totalWithTax
currencyCode
lines {
id
quantity
featuredAsset {
id
preview
}
productVariant {
name
product {
slug
}
productId
}
}
}
}
`
type UseCartResponse = BigCommerceCart & Cart
export const fetcher: HookFetcher<UseCartResponse | null, CartInput> = (
export const fetcher: HookFetcher<any | null> = (
options,
{ cartId },
input,
fetch
) => {
return cartId ? fetch({ ...defaultOpts, ...options }) : null
return fetch({ ...options, query: getCartQuery })
}
export function extendHook(
customFetcher: typeof fetcher,
swrOptions?: SwrOptions<UseCartResponse | null, CartInput>
swrOptions?: SwrOptions<any | null>
) {
const useCart = () => {
const response = useCommerceCart(defaultOpts, [], customFetcher, {
revalidateOnFocus: false,
...swrOptions,
})
const response = useData({}, [], customFetcher, swrOptions)
const res = useResponse(response, {
normalizer: normalizeCart,
normalizer: (data => {
const order = data?.activeOrder;
console.log({ order });
return (order ? {
id: order.id,
currency: { code: order.currencyCode },
subTotal: order.subTotalWithTax / 100,
total: order.totalWithTax / 100,
items: order.lines?.map(l => ({
name: l.productVariant.name,
quantity: l.quantity,
url: l.productVariant.product.slug,
variantId: l.productVariant.id,
productId: l.productVariant.productId,
images: [{ url: l.featuredAsset?.preview }]
}))
} : null)
}),
descriptors: {
isEmpty: {
get() {
return Object.values(response.data?.line_items ?? {}).every(
(items) => !items.length
)
},
enumerable: true,
},
return response.data?.activeOrder?.totalQuantity === 0
},
enumerable: true
}
}
})
return res

View File

@ -15,8 +15,9 @@ export const fetcher: HookFetcher<Customer | null> = async (
_,
fetch
) => {
const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options })
return data?.customer ?? null
// const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options })
// return data?.customer ?? null
return null;
}
export function extendHook(

View File

@ -26,13 +26,13 @@ async function getError(res: Response) {
export const vendureConfig: CommerceConfig = {
locale: 'en-us',
cartCookie: 'bc_cartId',
async fetcher({ url, method = 'GET', variables, body: bodyObj }) {
const hasBody = Boolean(variables || bodyObj)
async fetcher({ url, method = 'POST', variables, query, body: bodyObj }) {
const hasBody = Boolean(variables || query)
const body = hasBody
? JSON.stringify(variables ? { variables } : bodyObj)
? JSON.stringify({ query, variables })
: undefined
const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined
const res = await fetch(url!, { method, body, headers })
const res = await fetch('http://localhost:3001/shop-api'!, { method, body, headers, credentials: 'include' })
if (res.ok) {
const { data } = await res.json()