forked from crowetic/commerce
Improved types for operations
This commit is contained in:
parent
172b413521
commit
e5f0809070
@ -1,14 +1,15 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
import type { HookFetcher } from '@commerce/utils/types'
|
import type { HookFetcher } from '@commerce/utils/types'
|
||||||
import { CommerceError } from '@commerce/utils/errors'
|
import { ValidationError } from '@commerce/utils/errors'
|
||||||
import useCartUpdateItem from '@commerce/cart/use-update-item'
|
import useCartUpdateItem from '@commerce/cart/use-update-item'
|
||||||
import { normalizeCart } from '../lib/normalize'
|
import { normalizeCart } from '../lib/normalize'
|
||||||
import type {
|
import type {
|
||||||
Cart,
|
|
||||||
BigcommerceCart,
|
|
||||||
UpdateCartItemBody,
|
UpdateCartItemBody,
|
||||||
UpdateCartItemInput,
|
UpdateCartItemInput,
|
||||||
|
Cart,
|
||||||
|
BigcommerceCart,
|
||||||
|
LineItem,
|
||||||
} from '../types'
|
} from '../types'
|
||||||
import { fetcher as removeFetcher } from './use-remove-item'
|
import { fetcher as removeFetcher } from './use-remove-item'
|
||||||
import useCart from './use-cart'
|
import useCart from './use-cart'
|
||||||
@ -29,12 +30,12 @@ export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async (
|
|||||||
return removeFetcher(null, { itemId }, fetch)
|
return removeFetcher(null, { itemId }, fetch)
|
||||||
}
|
}
|
||||||
} else if (item.quantity) {
|
} else if (item.quantity) {
|
||||||
throw new CommerceError({
|
throw new ValidationError({
|
||||||
message: 'The item quantity has to be a valid integer',
|
message: 'The item quantity has to be a valid integer',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fetch<BigcommerceCart>({
|
const data = await fetch<BigcommerceCart, UpdateCartItemBody>({
|
||||||
...defaultOpts,
|
...defaultOpts,
|
||||||
...options,
|
...options,
|
||||||
body: { itemId, item },
|
body: { itemId, item },
|
||||||
@ -44,7 +45,9 @@ export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
||||||
const useUpdateItem = (item?: any) => {
|
const useUpdateItem = <T extends LineItem | undefined = undefined>(
|
||||||
|
item?: T
|
||||||
|
) => {
|
||||||
const { mutate } = useCart()
|
const { mutate } = useCart()
|
||||||
const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>(
|
const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>(
|
||||||
defaultOpts,
|
defaultOpts,
|
||||||
@ -52,26 +55,31 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
debounce(async (input: UpdateCartItemInput) => {
|
debounce(
|
||||||
console.log('INPUT', input, {
|
async (
|
||||||
itemId: input.id ?? item?.id,
|
input: T extends LineItem
|
||||||
item: {
|
? Partial<UpdateCartItemInput>
|
||||||
productId: input.productId ?? item?.product_id,
|
: UpdateCartItemInput
|
||||||
variantId: input.productId ?? item?.variant_id,
|
) => {
|
||||||
quantity: input.quantity,
|
const itemId = input.id ?? item?.id
|
||||||
},
|
const productId = input.productId ?? item?.productId
|
||||||
})
|
const variantId = input.productId ?? item?.variantId
|
||||||
const data = await fn({
|
|
||||||
itemId: input.id ?? item?.id,
|
if (!itemId || !productId || !variantId) {
|
||||||
item: {
|
throw new ValidationError({
|
||||||
productId: input.productId ?? item?.product_id,
|
message: 'Invalid input used for this operation',
|
||||||
variantId: input.productId ?? item?.variant_id,
|
})
|
||||||
quantity: input.quantity,
|
}
|
||||||
},
|
|
||||||
})
|
const data = await fn({
|
||||||
await mutate(data, false)
|
itemId,
|
||||||
return data
|
item: { productId, variantId, quantity: input.quantity },
|
||||||
}, cfg?.wait ?? 500),
|
})
|
||||||
|
await mutate(data, false)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
cfg?.wait ?? 500
|
||||||
|
),
|
||||||
[fn, mutate]
|
[fn, mutate]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Cart as BigcommerceCart } from '../api/cart'
|
import type { Cart, BigcommerceCart, LineItem } from '../types'
|
||||||
import update from './immutability'
|
import update from './immutability'
|
||||||
|
|
||||||
function normalizeProductOption(productOption: any) {
|
function normalizeProductOption(productOption: any) {
|
||||||
@ -90,6 +90,7 @@ function normalizeLineItem(item: any): LineItem {
|
|||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
variantId: String(item.variant_id),
|
variantId: String(item.variant_id),
|
||||||
|
productId: String(item.product_id),
|
||||||
name: item.name,
|
name: item.name,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
variant: {
|
variant: {
|
||||||
|
@ -39,6 +39,7 @@ export type OptionSelections = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CartItemBody extends Core.CartItemBody {
|
export interface CartItemBody extends Core.CartItemBody {
|
||||||
|
productId: string // The product id is always required for BC
|
||||||
optionSelections?: OptionSelections
|
optionSelections?: OptionSelections
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,8 @@ export interface UpdateCartItemBody extends Core.UpdateCartItemBody {
|
|||||||
item: CartItemBody
|
item: CartItemBody
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateCartItemInput extends Core.UpdateCartItemInput {}
|
export interface UpdateCartItemInput
|
||||||
|
extends Core.UpdateCartItemInput<CartItemBody> {}
|
||||||
|
|
||||||
export interface UpdateCartItemHandlerBody
|
export interface UpdateCartItemHandlerBody
|
||||||
extends Core.UpdateCartItemHandlerBody {}
|
extends Core.UpdateCartItemHandlerBody {}
|
||||||
|
@ -6,6 +6,7 @@ export interface Discount {
|
|||||||
export interface LineItem {
|
export interface LineItem {
|
||||||
id: string
|
id: string
|
||||||
variantId: string
|
variantId: string
|
||||||
|
productId: string
|
||||||
name: string
|
name: string
|
||||||
quantity: number
|
quantity: number
|
||||||
discounts: Discount[]
|
discounts: Discount[]
|
||||||
@ -88,8 +89,8 @@ export interface Cart {
|
|||||||
|
|
||||||
// Base cart item body used for cart mutations
|
// Base cart item body used for cart mutations
|
||||||
export interface CartItemBody {
|
export interface CartItemBody {
|
||||||
productId: string
|
|
||||||
variantId: string
|
variantId: string
|
||||||
|
productId?: string
|
||||||
quantity?: number
|
quantity?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +101,8 @@ export interface UpdateCartItemBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Input expected by the `useUpdateItem` hook
|
// Input expected by the `useUpdateItem` hook
|
||||||
export interface UpdateCartItemInput extends Partial<CartItemBody> {
|
export type UpdateCartItemInput<T extends CartItemBody> = T & {
|
||||||
id?: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body expected by the update operation handler
|
// Body expected by the update operation handler
|
||||||
|
@ -26,6 +26,14 @@ export class CommerceError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for errors that come from a bad implementation of the hooks
|
||||||
|
export class ValidationError extends CommerceError {
|
||||||
|
constructor(options: ErrorProps) {
|
||||||
|
super(options)
|
||||||
|
this.code = 'validation_error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FetcherError extends CommerceError {
|
export class FetcherError extends CommerceError {
|
||||||
status: number
|
status: number
|
||||||
|
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
// Core fetcher added by CommerceProvider
|
// Core fetcher added by CommerceProvider
|
||||||
export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T>
|
export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T>
|
||||||
|
|
||||||
export type FetcherOptions = {
|
export type FetcherOptions<Body = any> = {
|
||||||
url?: string
|
url?: string
|
||||||
query?: string
|
query?: string
|
||||||
method?: string
|
method?: string
|
||||||
variables?: any
|
variables?: any
|
||||||
body?: any
|
body?: Body
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HookFetcher<Data, Input = null, Result = any> = (
|
export type HookFetcher<Data, Input = null, Result = any> = (
|
||||||
options: HookFetcherOptions | null,
|
options: HookFetcherOptions | null,
|
||||||
input: Input,
|
input: Input,
|
||||||
fetch: <T = Result>(options: FetcherOptions) => Promise<T>
|
fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T>
|
||||||
) => Data | Promise<Data>
|
) => Data | Promise<Data>
|
||||||
|
|
||||||
export type HookFetcherOptions = {
|
export type HookFetcherOptions = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user