mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic
This commit is contained in:
commit
7f0c1eb66c
@ -1,9 +1,6 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHandler } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCartAddItem, {
|
||||
AddItemInput as UseAddItemInput,
|
||||
} from '@commerce/cart/use-add-item'
|
||||
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import type {
|
||||
AddCartItemBody,
|
||||
@ -12,55 +9,45 @@ import type {
|
||||
CartItemBody,
|
||||
} from '../types'
|
||||
import useCart from './use-cart'
|
||||
import { BigcommerceProvider } from '..'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'POST',
|
||||
}
|
||||
|
||||
export type AddItemInput = UseAddItemInput<CartItemBody>
|
||||
export default useAddItem as UseAddItem<BigcommerceProvider, CartItemBody>
|
||||
|
||||
export const fetcher: HookFetcher<Cart, AddCartItemBody> = async (
|
||||
options,
|
||||
{ item },
|
||||
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',
|
||||
export const handler: MutationHandler<Cart, {}, AddCartItemBody> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'GET',
|
||||
},
|
||||
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 data = await fetch<BigcommerceCart, AddCartItemBody>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { item },
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fetch<BigcommerceCart, AddCartItemBody>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { item },
|
||||
})
|
||||
|
||||
return normalizeCart(data)
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useAddItem = () => {
|
||||
return normalizeCart(data)
|
||||
},
|
||||
useHook() {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartAddItem(defaultOpts, customFetcher)
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input: AddItemInput) {
|
||||
const data = await fn({ item: input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fn, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useAddItem.extend = extendHook
|
||||
|
||||
return useAddItem
|
||||
return async function addItem({ input, fetch }) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { handler as useCart } from './cart/use-cart'
|
||||
import { handler as useAddItem } from './cart/use-add-item'
|
||||
import { handler as useWishlist } from './wishlist/use-wishlist'
|
||||
import { handler as useCustomer } from './customer/use-customer'
|
||||
import { handler as useSearch } from './product/use-search'
|
||||
@ -8,7 +9,7 @@ export const bigcommerceProvider = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'bc_cartId',
|
||||
fetcher,
|
||||
cart: { useCart },
|
||||
cart: { useCart, useAddItem },
|
||||
wishlist: { useWishlist },
|
||||
customer: { useCustomer },
|
||||
products: { useSearch },
|
||||
|
@ -23,11 +23,11 @@ export type BigcommerceCart = {
|
||||
// TODO: add missing fields
|
||||
}
|
||||
|
||||
export interface Cart extends Core.Cart {
|
||||
export type Cart = Core.Cart & {
|
||||
lineItems: LineItem[]
|
||||
}
|
||||
|
||||
export interface LineItem extends Core.LineItem {}
|
||||
export type LineItem = Core.LineItem
|
||||
|
||||
/**
|
||||
* Cart mutations
|
||||
@ -38,25 +38,24 @@ export type OptionSelections = {
|
||||
option_value: number | string
|
||||
}
|
||||
|
||||
export interface CartItemBody extends Core.CartItemBody {
|
||||
export type CartItemBody = Core.CartItemBody & {
|
||||
productId: string // The product id is always required for BC
|
||||
optionSelections?: OptionSelections
|
||||
}
|
||||
|
||||
export interface GetCartHandlerBody extends Core.GetCartHandlerBody {}
|
||||
type X = Core.CartItemBody extends CartItemBody ? any : never
|
||||
type Y = CartItemBody extends Core.CartItemBody ? any : never
|
||||
|
||||
export interface AddCartItemBody extends Core.AddCartItemBody<CartItemBody> {}
|
||||
export type GetCartHandlerBody = Core.GetCartHandlerBody
|
||||
|
||||
export interface AddCartItemHandlerBody
|
||||
extends Core.AddCartItemHandlerBody<CartItemBody> {}
|
||||
export type AddCartItemBody = Core.AddCartItemBody<CartItemBody>
|
||||
|
||||
export interface UpdateCartItemBody
|
||||
extends Core.UpdateCartItemBody<CartItemBody> {}
|
||||
export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody>
|
||||
|
||||
export interface UpdateCartItemHandlerBody
|
||||
extends Core.UpdateCartItemHandlerBody<CartItemBody> {}
|
||||
export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody>
|
||||
|
||||
export interface RemoveCartItemBody extends Core.RemoveCartItemBody {}
|
||||
export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody>
|
||||
|
||||
export interface RemoveCartItemHandlerBody
|
||||
extends Core.RemoveCartItemHandlerBody {}
|
||||
export type RemoveCartItemBody = Core.RemoveCartItemBody
|
||||
|
||||
export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody
|
||||
|
@ -1,9 +1,69 @@
|
||||
import useAction from '../utils/use-action'
|
||||
import type { CartItemBody } from '../types'
|
||||
import { useCallback } from 'react'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import type { Cart, CartItemBody, AddCartItemBody } from '../types'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import { BigcommerceProvider } from '@framework'
|
||||
|
||||
export type UseAddItemHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'cart'>,
|
||||
'useAddItem'
|
||||
>
|
||||
|
||||
// Input expected by the action returned by the `useAddItem` hook
|
||||
export type AddItemInput<T extends CartItemBody> = T
|
||||
export type UseAddItemInput<P extends Provider> = UseHookInput<
|
||||
UseAddItemHandler<P>
|
||||
>
|
||||
|
||||
const useAddItem = useAction
|
||||
export type UseAddItemResult<P extends Provider> = ReturnType<
|
||||
UseHookResponse<UseAddItemHandler<P>>
|
||||
>
|
||||
|
||||
export default useAddItem
|
||||
export type UseAddItem<P extends Provider, Input> = Partial<
|
||||
UseAddItemInput<P>
|
||||
> extends UseAddItemInput<P>
|
||||
? (input?: UseAddItemInput<P>) => (input: Input) => UseAddItemResult<P>
|
||||
: (input: UseAddItemInput<P>) => (input: Input) => UseAddItemResult<P>
|
||||
|
||||
export const fetcher: HookFetcherFn<
|
||||
Cart,
|
||||
AddCartItemBody<CartItemBody>
|
||||
> = async ({ options, input, fetch }) => {
|
||||
return fetch({ ...options, body: input })
|
||||
}
|
||||
|
||||
type X = UseAddItemResult<BigcommerceProvider>
|
||||
|
||||
export default function useAddItem<P extends Provider, Input>(
|
||||
input: UseAddItemInput<P>
|
||||
) {
|
||||
const { providerRef, fetcherRef } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.cart?.useAddItem
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? (() => () => {})
|
||||
const fetchFn = provider.fetcher ?? fetcherRef.current
|
||||
const action = useHook({ input })
|
||||
|
||||
return useCallback(
|
||||
function addItem(input: Input) {
|
||||
return action({
|
||||
input,
|
||||
fetch({ input }) {
|
||||
return fetcherFn({
|
||||
input,
|
||||
options: opts!.fetchOptions,
|
||||
fetch: fetchFn,
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
[input, fetchFn, opts?.fetchOptions]
|
||||
)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { Fetcher, HookHandler } from './utils/types'
|
||||
import { Fetcher, HookHandler, MutationHandler } from './utils/types'
|
||||
import type { FetchCartInput } from './cart/use-cart'
|
||||
import type { Cart, Wishlist, Customer, SearchProductsData } from './types'
|
||||
|
||||
@ -16,6 +16,7 @@ export type Provider = CommerceConfig & {
|
||||
fetcher: Fetcher
|
||||
cart?: {
|
||||
useCart?: HookHandler<Cart | null, any, FetchCartInput>
|
||||
useAddItem?: MutationHandler<Cart, any, any>
|
||||
}
|
||||
wishlist?: {
|
||||
useWishlist?: HookHandler<Wishlist | null, any, any>
|
||||
|
@ -2,12 +2,12 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist'
|
||||
import type { Customer as BCCustomer } from '@framework/api/customers'
|
||||
import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products'
|
||||
|
||||
export interface Discount {
|
||||
export type Discount = {
|
||||
// The value of the discount, can be an amount or percentage
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface LineItem {
|
||||
export type LineItem = {
|
||||
id: string
|
||||
variantId: string
|
||||
productId: string
|
||||
@ -19,19 +19,19 @@ export interface LineItem {
|
||||
variant: ProductVariant
|
||||
}
|
||||
|
||||
export interface Measurement {
|
||||
export type Measurement = {
|
||||
value: number
|
||||
unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES'
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
export type Image = {
|
||||
url: string
|
||||
altText?: string
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
export interface ProductVariant {
|
||||
export type ProductVariant = {
|
||||
id: string
|
||||
// The SKU (stock keeping unit) associated with the product variant.
|
||||
sku: string
|
||||
@ -66,7 +66,7 @@ export interface ProductVariant {
|
||||
}
|
||||
|
||||
// Shopping cart, a.k.a Checkout
|
||||
export interface Cart {
|
||||
export type Cart = {
|
||||
id: string
|
||||
// ID of the customer to which the cart belongs.
|
||||
customerId?: string
|
||||
@ -105,47 +105,49 @@ export interface SearchProductsData extends BCSearchProductsData {}
|
||||
*/
|
||||
|
||||
// Base cart item body used for cart mutations
|
||||
export interface CartItemBody {
|
||||
export type CartItemBody = {
|
||||
variantId: string
|
||||
productId?: string
|
||||
quantity?: number
|
||||
}
|
||||
|
||||
// Body used by the `getCart` operation handler
|
||||
export interface GetCartHandlerBody {
|
||||
export type GetCartHandlerBody = {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
// Body used by the add item to cart operation
|
||||
export interface AddCartItemBody<T extends CartItemBody> {
|
||||
export type AddCartItemBody<T extends CartItemBody> = {
|
||||
item: T
|
||||
}
|
||||
|
||||
// Body expected by the add item to cart operation handler
|
||||
export interface AddCartItemHandlerBody<T extends CartItemBody>
|
||||
extends Partial<AddCartItemBody<T>> {
|
||||
export type AddCartItemHandlerBody<T extends CartItemBody> = Partial<
|
||||
AddCartItemBody<T>
|
||||
> & {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
// Body used by the update cart item operation
|
||||
export interface UpdateCartItemBody<T extends CartItemBody> {
|
||||
export type UpdateCartItemBody<T extends CartItemBody> = {
|
||||
itemId: string
|
||||
item: T
|
||||
}
|
||||
|
||||
// Body expected by the update cart item operation handler
|
||||
export interface UpdateCartItemHandlerBody<T extends CartItemBody>
|
||||
extends Partial<UpdateCartItemBody<T>> {
|
||||
export type UpdateCartItemHandlerBody<T extends CartItemBody> = Partial<
|
||||
UpdateCartItemBody<T>
|
||||
> & {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
// Body used by the remove cart item operation
|
||||
export interface RemoveCartItemBody {
|
||||
export type RemoveCartItemBody = {
|
||||
itemId: string
|
||||
}
|
||||
|
||||
// Body expected by the remove cart item operation handler
|
||||
export interface RemoveCartItemHandlerBody extends Partial<RemoveCartItemBody> {
|
||||
export type RemoveCartItemHandlerBody = Partial<RemoveCartItemBody> & {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
|
@ -82,19 +82,14 @@ export type MutationHandler<
|
||||
// Input expected by the hook
|
||||
Input extends { [k: string]: unknown } = {},
|
||||
// Input expected before doing a fetch operation
|
||||
FetchInput extends HookFetchInput = {},
|
||||
// Custom state added to the response object of SWR
|
||||
State = {}
|
||||
FetchInput extends { [k: string]: unknown } = {}
|
||||
> = {
|
||||
useHook?(context: {
|
||||
input: Input & { swrOptions?: SwrOptions<Data, FetchInput> }
|
||||
useCallback(
|
||||
fn: (context?: {
|
||||
input?: HookFetchInput | HookSwrInput
|
||||
swrOptions?: SwrOptions<Data, FetchInput>
|
||||
}) => Data
|
||||
): ResponseState<Data>
|
||||
}): ResponseState<Data> & State
|
||||
input: Input
|
||||
}): (context: {
|
||||
input: FetchInput
|
||||
fetch: (context: { input: FetchInput }) => Data | Promise<Data>
|
||||
}) => Data | Promise<Data>
|
||||
fetchOptions: HookFetcherOptions
|
||||
fetcher?: HookFetcherFn<Data, FetchInput>
|
||||
}
|
||||
@ -110,14 +105,18 @@ export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
|
||||
*/
|
||||
export type Prop<T, K extends keyof T> = NonNullable<T[K]>
|
||||
|
||||
export type UseHookParameters<H extends HookHandler<any>> = Parameters<
|
||||
export type HookHandlerType =
|
||||
| HookHandler<any, any, any>
|
||||
| MutationHandler<any, any, any>
|
||||
|
||||
export type UseHookParameters<H extends HookHandlerType> = Parameters<
|
||||
Prop<H, 'useHook'>
|
||||
>
|
||||
|
||||
export type UseHookResponse<H extends HookHandler<any>> = ReturnType<
|
||||
export type UseHookResponse<H extends HookHandlerType> = ReturnType<
|
||||
Prop<H, 'useHook'>
|
||||
>
|
||||
|
||||
export type UseHookInput<
|
||||
H extends HookHandler<any>
|
||||
H extends HookHandlerType
|
||||
> = UseHookParameters<H>[0]['input']
|
||||
|
Loading…
x
Reference in New Issue
Block a user