mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
add to cart c:
This commit is contained in:
@@ -5,11 +5,17 @@ import createApiHandler, {
|
||||
} from './utils/create-api-handler'
|
||||
import { BigcommerceApiError } from './utils/errors'
|
||||
|
||||
type Cart = any
|
||||
export type Item = {
|
||||
productId: number
|
||||
variantId: number
|
||||
quantity?: number
|
||||
}
|
||||
|
||||
export type Cart = any
|
||||
|
||||
const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
|
||||
|
||||
const cartApi: BigcommerceApiHandler = async (req, res, config) => {
|
||||
const cartApi: BigcommerceApiHandler<Cart> = async (req, res, config) => {
|
||||
if (!isAllowedMethod(req, res, METHODS)) return
|
||||
|
||||
const { cookies } = req
|
||||
@@ -27,7 +33,7 @@ const cartApi: BigcommerceApiHandler = async (req, res, config) => {
|
||||
} catch (error) {
|
||||
if (error instanceof BigcommerceApiError && error.status === 404) {
|
||||
// Remove the cookie if it exists but the cart wasn't found
|
||||
res.setHeader('Set-Cookie', getCartCookie(name))
|
||||
res.setHeader('Set-Cookie', getCartCookie(config.cartCookie))
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
@@ -38,10 +44,11 @@ const cartApi: BigcommerceApiHandler = async (req, res, config) => {
|
||||
|
||||
// Create or add a product to the cart
|
||||
if (req.method === 'POST') {
|
||||
const { product } = req.body
|
||||
const item: Item | undefined = req.body?.item
|
||||
|
||||
if (!product) {
|
||||
if (!item) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Missing product' }],
|
||||
})
|
||||
}
|
||||
@@ -49,28 +56,32 @@ const cartApi: BigcommerceApiHandler = async (req, res, config) => {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
line_items: [parseProduct(product)],
|
||||
line_items: [parseItem(item)],
|
||||
}),
|
||||
}
|
||||
const { data } = cartId
|
||||
? await config.storeApiFetch(`/v3/carts/${cartId}/items`, options)
|
||||
: await config.storeApiFetch('/v3/carts', options)
|
||||
|
||||
console.log('API DATA', data)
|
||||
|
||||
// Create or update the cart cookie
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
getCartCookie(name, data.id, config.cartCookieMaxAge)
|
||||
getCartCookie(config.cartCookie, data.id, config.cartCookieMaxAge)
|
||||
)
|
||||
|
||||
return res.status(200).json({ done: { data } })
|
||||
return res.status(200).json({ data })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
const message =
|
||||
error instanceof BigcommerceApiError
|
||||
? 'An unexpected error ocurred with the Bigcommerce API'
|
||||
: 'An unexpected error ocurred'
|
||||
|
||||
res.status(500).json({ errors: [{ message }] })
|
||||
res.status(500).json({ data: null, errors: [{ message }] })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +101,10 @@ function getCartCookie(name: string, cartId?: string, maxAge?: number) {
|
||||
return serialize(name, cartId || '', options)
|
||||
}
|
||||
|
||||
const parseProduct = (product: any) => ({
|
||||
quantity: product.quantity,
|
||||
product_id: product.productId,
|
||||
variant_id: product.variantId,
|
||||
const parseItem = (item: Item) => ({
|
||||
quantity: item.quantity || 1,
|
||||
product_id: item.productId,
|
||||
variant_id: item.variantId,
|
||||
})
|
||||
|
||||
export default createApiHandler(cartApi)
|
||||
|
@@ -33,11 +33,14 @@ export const getProductQuery = /* GraphQL */ `
|
||||
${productInfoFragment}
|
||||
`
|
||||
|
||||
export interface GetProductResult<T> {
|
||||
product?: T extends GetProductQuery
|
||||
? Extract<T['site']['route']['node'], { __typename: 'Product' }>
|
||||
: unknown
|
||||
}
|
||||
export type Product = Extract<
|
||||
GetProductQuery['site']['route']['node'],
|
||||
{ __typename: 'Product' }
|
||||
>
|
||||
|
||||
export type GetProductResult<
|
||||
T extends { product?: any } = { product?: Product }
|
||||
> = T
|
||||
|
||||
export type ProductVariables = Images &
|
||||
({ path: string; slug?: never } | { path?: never; slug: string })
|
||||
@@ -45,7 +48,7 @@ export type ProductVariables = Images &
|
||||
async function getProduct(opts: {
|
||||
variables: ProductVariables
|
||||
config?: BigcommerceConfig
|
||||
}): Promise<GetProductResult<GetProductQuery>>
|
||||
}): Promise<GetProductResult>
|
||||
|
||||
async function getProduct<T, V = any>(opts: {
|
||||
query: string
|
||||
@@ -61,7 +64,7 @@ async function getProduct({
|
||||
query?: string
|
||||
variables: ProductVariables
|
||||
config?: BigcommerceConfig
|
||||
}): Promise<GetProductResult<GetProductQuery>> {
|
||||
}): Promise<GetProductResult> {
|
||||
config = getConfig(config)
|
||||
const variables: GetProductQueryVariables = {
|
||||
...config.imageVariables,
|
||||
|
@@ -1,12 +1,17 @@
|
||||
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
|
||||
import { BigcommerceConfig, getConfig } from '..'
|
||||
|
||||
export type BigcommerceApiHandler = (
|
||||
export type BigcommerceApiHandler<T = any> = (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
res: NextApiResponse<BigcommerceApiResponse<T>>,
|
||||
config: BigcommerceConfig
|
||||
) => void | Promise<void>
|
||||
|
||||
export type BigcommerceApiResponse<T> = {
|
||||
data: T | null
|
||||
errors?: { message: string }[]
|
||||
}
|
||||
|
||||
export default function createApiHandler(handler: BigcommerceApiHandler) {
|
||||
return function getApiHandler({
|
||||
config,
|
||||
|
@@ -30,7 +30,7 @@ export default async function fetchStoreApi<T>(
|
||||
|
||||
const contentType = res.headers.get('Content-Type')
|
||||
|
||||
if (contentType?.includes('application/json')) {
|
||||
if (!contentType?.includes('application/json')) {
|
||||
throw new BigcommerceApiError(
|
||||
`Fetch to Bigcommerce API failed, expected JSON content but found: ${contentType}`,
|
||||
res
|
||||
|
@@ -7,7 +7,11 @@ import {
|
||||
export type Cart = {}
|
||||
|
||||
export const CartProvider: FC = ({ children }) => {
|
||||
return <CommerceCartProvider url="/api/cart">{children}</CommerceCartProvider>
|
||||
return (
|
||||
<CommerceCartProvider url="/api/bigcommerce/cart">
|
||||
{children}
|
||||
</CommerceCartProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useCart() {
|
||||
|
@@ -1,11 +1,23 @@
|
||||
import type { Fetcher } from '@lib/commerce'
|
||||
import { default as useCartAddItem } from '@lib/commerce/cart/use-add-item'
|
||||
import type { Item } from '../api/cart'
|
||||
import { Cart } from '.'
|
||||
|
||||
async function fetcher(fetch: Fetcher<Cart>, { item }: { item: any }) {
|
||||
return fetch({ url: '/api/cart', method: 'POST', body: { item } })
|
||||
export type { Item }
|
||||
|
||||
function fetcher(fetch: Fetcher<Cart>, { item }: { item: Item }) {
|
||||
if (
|
||||
item.quantity &&
|
||||
(!Number.isInteger(item.quantity) || item.quantity! < 1)
|
||||
) {
|
||||
throw new Error(
|
||||
'The item quantity has to be a valid integer greater than 0'
|
||||
)
|
||||
}
|
||||
|
||||
return fetch({ url: '/api/bigcommerce/cart', method: 'POST', body: { item } })
|
||||
}
|
||||
|
||||
export default function useAddItem() {
|
||||
return useCartAddItem<Cart, { item: any }>(fetcher)
|
||||
return useCartAddItem<Cart, { item: Item }>(fetcher)
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ export const bigcommerceConfig: CommerceConfig = {
|
||||
|
||||
if (res.ok) {
|
||||
const { data } = await res.json()
|
||||
console.log('DATA', data)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -44,12 +45,11 @@ export type BigcommerceConfig = Partial<CommerceConfig>
|
||||
|
||||
export type BigcommerceProps = {
|
||||
children?: ReactNode
|
||||
config: BigcommerceConfig
|
||||
}
|
||||
} & BigcommerceConfig
|
||||
|
||||
export function CommerceProvider({ children, config }: BigcommerceProps) {
|
||||
export function CommerceProvider({ children, ...config }: BigcommerceProps) {
|
||||
return (
|
||||
<CoreCommerceProvider config={{ ...config, ...bigcommerceConfig }}>
|
||||
<CoreCommerceProvider config={{ ...bigcommerceConfig, ...config }}>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
|
Reference in New Issue
Block a user