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