mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic
This commit is contained in:
@@ -1,54 +1,40 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCommerceLogin from '@commerce/use-login'
|
||||
import useLogin, { UseLogin } from '@commerce/use-login'
|
||||
import type { LoginBody } from '../api/customers/login'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/customers/login',
|
||||
method: 'POST',
|
||||
}
|
||||
export default useLogin as UseLogin<typeof handler>
|
||||
|
||||
export type LoginInput = LoginBody
|
||||
export const handler: MutationHook<null, {}, LoginBody> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/customers/login',
|
||||
method: 'POST',
|
||||
},
|
||||
async fetcher({ input: { email, password }, options, fetch }) {
|
||||
if (!(email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to login',
|
||||
})
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<null, LoginBody> = (
|
||||
options,
|
||||
{ email, password },
|
||||
fetch
|
||||
) => {
|
||||
if (!(email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to login',
|
||||
return fetch({
|
||||
...options,
|
||||
body: { email, password },
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { email, password },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useLogin = () => {
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { revalidate } = useCustomer()
|
||||
const fn = useCommerceLogin<null, LoginInput>(defaultOpts, customFetcher)
|
||||
|
||||
return useCallback(
|
||||
async function login(input: LoginInput) {
|
||||
const data = await fn(input)
|
||||
async function login(input) {
|
||||
const data = await fetch({ input })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
[fetch, revalidate]
|
||||
)
|
||||
}
|
||||
|
||||
useLogin.extend = extendHook
|
||||
|
||||
return useLogin
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@@ -1,38 +1,25 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import useCommerceLogout from '@commerce/use-logout'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import useLogout, { UseLogout } from '@commerce/use-logout'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/customers/logout',
|
||||
method: 'GET',
|
||||
}
|
||||
export default useLogout as UseLogout<typeof handler>
|
||||
|
||||
export const fetcher: HookFetcher<null> = (options, _, fetch) => {
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useLogout = () => {
|
||||
export const handler: MutationHook<null> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/customers/logout',
|
||||
method: 'GET',
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { mutate } = useCustomer()
|
||||
const fn = useCommerceLogout<null>(defaultOpts, customFetcher)
|
||||
|
||||
return useCallback(
|
||||
async function login() {
|
||||
const data = await fn(null)
|
||||
async function logout() {
|
||||
const data = await fetch()
|
||||
await mutate(null, false)
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
[fetch, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useLogout.extend = extendHook
|
||||
|
||||
return useLogout
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@@ -1,55 +1,44 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCommerceSignup from '@commerce/use-signup'
|
||||
import useSignup, { UseSignup } from '@commerce/use-signup'
|
||||
import type { SignupBody } from '../api/customers/signup'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import customerCreateMutation from '@framework/utils/mutations/customer-create'
|
||||
import { CustomerCreateInput } from '@framework/schema'
|
||||
|
||||
const defaultOpts = {
|
||||
query: customerCreateMutation,
|
||||
}
|
||||
export default useSignup as UseSignup<typeof handler>
|
||||
|
||||
export const fetcher: HookFetcher<null, CustomerCreateInput> = (
|
||||
options,
|
||||
{ firstName, lastName, email, password },
|
||||
fetch
|
||||
) => {
|
||||
if (!(firstName && lastName && email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to signup',
|
||||
export const handler: MutationHook<null, {}, SignupBody, SignupBody> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/customers/signup',
|
||||
method: 'POST',
|
||||
},
|
||||
async fetcher({
|
||||
input: { firstName, lastName, email, password },
|
||||
options,
|
||||
fetch,
|
||||
}) {
|
||||
if (!(firstName && lastName && email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to signup',
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...options,
|
||||
body: { firstName, lastName, email, password },
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
variables: { firstName, lastName, email, password },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useSignup = () => {
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { revalidate } = useCustomer()
|
||||
const fn = useCommerceSignup<null, CustomerCreateInput>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function signup(input: CustomerCreateInput) {
|
||||
const data = await fn(input)
|
||||
async function signup(input) {
|
||||
const data = await fetch({ input })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
[fetch, revalidate]
|
||||
)
|
||||
}
|
||||
|
||||
useSignup.extend = extendHook
|
||||
|
||||
return useSignup
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
export { default as useCart } from './use-cart'
|
||||
export { default as useAddItem } from './use-add-item'
|
||||
export { default as useRemoveItem } from './use-remove-item'
|
||||
export { default as useWishlistActions } from './use-cart-actions'
|
||||
export { default as useUpdateItem } from './use-cart-actions'
|
||||
export { default as useUpdateItem } from './use-update-item'
|
||||
|
@@ -1,29 +1,24 @@
|
||||
import type { MutationHandler } from '@commerce/utils/types'
|
||||
import { useCallback } from 'react'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import type {
|
||||
AddCartItemBody,
|
||||
Cart,
|
||||
BigcommerceCart,
|
||||
CartItemBody,
|
||||
AddCartItemBody,
|
||||
} from '../types'
|
||||
import useCart from './use-cart'
|
||||
import { BigcommerceProvider } from '..'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'POST',
|
||||
}
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export default useAddItem as UseAddItem<BigcommerceProvider, CartItemBody>
|
||||
|
||||
export const handler: MutationHandler<Cart, {}, AddCartItemBody> = {
|
||||
export const handler: MutationHook<Cart, {}, CartItemBody> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'GET',
|
||||
method: 'POST',
|
||||
},
|
||||
async fetcher({ input: { item }, options, fetch }) {
|
||||
async fetcher({ input: item, options, fetch }) {
|
||||
if (
|
||||
item.quantity &&
|
||||
(!Number.isInteger(item.quantity) || item.quantity! < 1)
|
||||
@@ -34,20 +29,22 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = {
|
||||
}
|
||||
|
||||
const data = await fetch<BigcommerceCart, AddCartItemBody>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { item },
|
||||
})
|
||||
|
||||
return normalizeCart(data)
|
||||
},
|
||||
useHook() {
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { mutate } = useCart()
|
||||
|
||||
return async function addItem({ input, fetch }) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
return useCallback(
|
||||
async function addItem(input) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fetch, mutate]
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@@ -1,13 +0,0 @@
|
||||
import useAddItem from './use-add-item'
|
||||
import useRemoveItem from './use-remove-item'
|
||||
import useUpdateItem from './use-update-item'
|
||||
|
||||
// This hook is probably not going to be used, but it's here
|
||||
// to show how a commerce should be structuring it
|
||||
export default function useCartActions() {
|
||||
const addItem = useAddItem()
|
||||
const updateItem = useUpdateItem()
|
||||
const removeItem = useRemoveItem()
|
||||
|
||||
return { addItem, updateItem, removeItem }
|
||||
}
|
@@ -1,13 +1,12 @@
|
||||
import { useMemo } from 'react'
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import type { Cart } from '../types'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
export default useCart as UseCart<BigcommerceProvider>
|
||||
export default useCart as UseCart<typeof handler>
|
||||
|
||||
export const handler: HookHandler<
|
||||
export const handler: SWRHook<
|
||||
Cart | null,
|
||||
{},
|
||||
FetchCartInput,
|
||||
@@ -21,9 +20,9 @@ export const handler: HookHandler<
|
||||
const data = cartId ? await fetch(options) : null
|
||||
return data && normalizeCart(data)
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
useHook: ({ useData }) => (input) => {
|
||||
const response = useData({
|
||||
swrOptions: { revalidateOnFocus: false, ...input.swrOptions },
|
||||
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
|
||||
})
|
||||
|
||||
return useMemo(
|
||||
|
@@ -1,8 +1,12 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@commerce/utils/types'
|
||||
import type {
|
||||
MutationHookContext,
|
||||
HookFetcherContext,
|
||||
} from '@commerce/utils/types'
|
||||
import { ValidationError } from '@commerce/utils/errors'
|
||||
import useCartRemoveItem, {
|
||||
RemoveItemInput as UseRemoveItemInput,
|
||||
import useRemoveItem, {
|
||||
RemoveItemInput as RemoveItemInputBase,
|
||||
UseRemoveItem,
|
||||
} from '@commerce/cart/use-remove-item'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import type {
|
||||
@@ -13,41 +17,41 @@ import type {
|
||||
} from '../types'
|
||||
import useCart from './use-cart'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'DELETE',
|
||||
}
|
||||
|
||||
export type RemoveItemFn<T = any> = T extends LineItem
|
||||
? (input?: RemoveItemInput<T>) => Promise<Cart | null>
|
||||
: (input: RemoveItemInput<T>) => Promise<Cart | null>
|
||||
|
||||
export type RemoveItemInput<T = any> = T extends LineItem
|
||||
? Partial<UseRemoveItemInput>
|
||||
: UseRemoveItemInput
|
||||
? Partial<RemoveItemInputBase>
|
||||
: RemoveItemInputBase
|
||||
|
||||
export const fetcher: HookFetcher<Cart | null, RemoveCartItemBody> = async (
|
||||
options,
|
||||
{ itemId },
|
||||
fetch
|
||||
) => {
|
||||
const data = await fetch<BigcommerceCart>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { itemId },
|
||||
})
|
||||
return normalizeCart(data)
|
||||
}
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useRemoveItem = <T extends LineItem | undefined = undefined>(
|
||||
item?: T
|
||||
export const handler = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'DELETE',
|
||||
},
|
||||
async fetcher({
|
||||
input: { itemId },
|
||||
options,
|
||||
fetch,
|
||||
}: HookFetcherContext<RemoveCartItemBody>) {
|
||||
const data = await fetch<BigcommerceCart>({
|
||||
...options,
|
||||
body: { itemId },
|
||||
})
|
||||
return normalizeCart(data)
|
||||
},
|
||||
useHook: ({
|
||||
fetch,
|
||||
}: MutationHookContext<Cart | null, RemoveCartItemBody>) => <
|
||||
T extends LineItem | undefined = undefined
|
||||
>(
|
||||
ctx: { item?: T } = {}
|
||||
) => {
|
||||
const { item } = ctx
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartRemoveItem<Cart | null, RemoveCartItemBody>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
const removeItem: RemoveItemFn<LineItem> = async (input) => {
|
||||
const itemId = input?.id ?? item?.id
|
||||
|
||||
@@ -57,17 +61,11 @@ export function extendHook(customFetcher: typeof fetcher) {
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fn({ itemId })
|
||||
const data = await fetch({ input: { itemId } })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
|
||||
return useCallback(removeItem as RemoveItemFn<T>, [fn, mutate])
|
||||
}
|
||||
|
||||
useRemoveItem.extend = extendHook
|
||||
|
||||
return useRemoveItem
|
||||
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@@ -1,9 +1,13 @@
|
||||
import { useCallback } from 'react'
|
||||
import debounce from 'lodash.debounce'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type {
|
||||
MutationHookContext,
|
||||
HookFetcherContext,
|
||||
} from '@commerce/utils/types'
|
||||
import { ValidationError } from '@commerce/utils/errors'
|
||||
import useCartUpdateItem, {
|
||||
UpdateItemInput as UseUpdateItemInput,
|
||||
import useUpdateItem, {
|
||||
UpdateItemInput as UpdateItemInputBase,
|
||||
UseUpdateItem,
|
||||
} from '@commerce/cart/use-update-item'
|
||||
import { normalizeCart } from '../lib/normalize'
|
||||
import type {
|
||||
@@ -12,52 +16,59 @@ import type {
|
||||
BigcommerceCart,
|
||||
LineItem,
|
||||
} from '../types'
|
||||
import { fetcher as removeFetcher } from './use-remove-item'
|
||||
import { handler as removeItemHandler } from './use-remove-item'
|
||||
import useCart from './use-cart'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'PUT',
|
||||
}
|
||||
|
||||
export type UpdateItemInput<T = any> = T extends LineItem
|
||||
? Partial<UseUpdateItemInput<LineItem>>
|
||||
: UseUpdateItemInput<LineItem>
|
||||
? Partial<UpdateItemInputBase<LineItem>>
|
||||
: UpdateItemInputBase<LineItem>
|
||||
|
||||
export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async (
|
||||
options,
|
||||
{ itemId, item },
|
||||
fetch
|
||||
) => {
|
||||
if (Number.isInteger(item.quantity)) {
|
||||
// Also allow the update hook to remove an item if the quantity is lower than 1
|
||||
if (item.quantity! < 1) {
|
||||
return removeFetcher(null, { itemId }, fetch)
|
||||
export default useUpdateItem as UseUpdateItem<typeof handler>
|
||||
|
||||
export const handler = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/cart',
|
||||
method: 'PUT',
|
||||
},
|
||||
async fetcher({
|
||||
input: { itemId, item },
|
||||
options,
|
||||
fetch,
|
||||
}: HookFetcherContext<UpdateCartItemBody>) {
|
||||
if (Number.isInteger(item.quantity)) {
|
||||
// Also allow the update hook to remove an item if the quantity is lower than 1
|
||||
if (item.quantity! < 1) {
|
||||
return removeItemHandler.fetcher({
|
||||
options: removeItemHandler.fetchOptions,
|
||||
input: { itemId },
|
||||
fetch,
|
||||
})
|
||||
}
|
||||
} else if (item.quantity) {
|
||||
throw new ValidationError({
|
||||
message: 'The item quantity has to be a valid integer',
|
||||
})
|
||||
}
|
||||
} else if (item.quantity) {
|
||||
throw new ValidationError({
|
||||
message: 'The item quantity has to be a valid integer',
|
||||
|
||||
const data = await fetch<BigcommerceCart, UpdateCartItemBody>({
|
||||
...options,
|
||||
body: { itemId, item },
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fetch<BigcommerceCart, UpdateCartItemBody>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { itemId, item },
|
||||
})
|
||||
|
||||
return normalizeCart(data)
|
||||
}
|
||||
|
||||
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
||||
const useUpdateItem = <T extends LineItem | undefined = undefined>(
|
||||
item?: T
|
||||
return normalizeCart(data)
|
||||
},
|
||||
useHook: ({
|
||||
fetch,
|
||||
}: MutationHookContext<Cart | null, UpdateCartItemBody>) => <
|
||||
T extends LineItem | undefined = undefined
|
||||
>(
|
||||
ctx: {
|
||||
item?: T
|
||||
wait?: number
|
||||
} = {}
|
||||
) => {
|
||||
const { mutate } = useCart()
|
||||
const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
const { item } = ctx
|
||||
const { mutate } = useCart() as any
|
||||
|
||||
return useCallback(
|
||||
debounce(async (input: UpdateItemInput<T>) => {
|
||||
@@ -71,20 +82,16 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fn({
|
||||
itemId,
|
||||
item: { productId, variantId, quantity: input.quantity },
|
||||
const data = await fetch({
|
||||
input: {
|
||||
itemId,
|
||||
item: { productId, variantId, quantity: input.quantity },
|
||||
},
|
||||
})
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}, cfg?.wait ?? 500),
|
||||
[fn, mutate]
|
||||
}, ctx.wait ?? 500),
|
||||
[fetch, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useUpdateItem.extend = extendHook
|
||||
|
||||
return useUpdateItem
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
5
framework/bigcommerce/config.json
Normal file
5
framework/bigcommerce/config.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"features": {
|
||||
"wishlist": false
|
||||
}
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
|
||||
import type { Customer, CustomerData } from '../api/customers'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
export default useCustomer as UseCustomer<BigcommerceProvider>
|
||||
export default useCustomer as UseCustomer<typeof handler>
|
||||
|
||||
export const handler: HookHandler<Customer | null> = {
|
||||
export const handler: SWRHook<Customer | null> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/customers',
|
||||
method: 'GET',
|
||||
@@ -14,11 +13,11 @@ export const handler: HookHandler<Customer | null> = {
|
||||
const data = await fetch<CustomerData | null>(options)
|
||||
return data?.customer ?? null
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
useHook: ({ useData }) => (input) => {
|
||||
return useData({
|
||||
swrOptions: {
|
||||
revalidateOnFocus: false,
|
||||
...input.swrOptions,
|
||||
...input?.swrOptions,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
import useSearch, { UseSearch } from '@commerce/products/use-search'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import useSearch, { UseSearch } from '@commerce/product/use-search'
|
||||
import type { SearchProductsData } from '../api/catalog/products'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
export default useSearch as UseSearch<BigcommerceProvider>
|
||||
export default useSearch as UseSearch<typeof handler>
|
||||
|
||||
export type SearchProductsInput = {
|
||||
search?: string
|
||||
@@ -12,7 +11,7 @@ export type SearchProductsInput = {
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const handler: HookHandler<
|
||||
export const handler: SWRHook<
|
||||
SearchProductsData,
|
||||
SearchProductsInput,
|
||||
SearchProductsInput
|
||||
@@ -37,7 +36,7 @@ export const handler: HookHandler<
|
||||
method: options.method,
|
||||
})
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
useHook: ({ useData }) => (input = {}) => {
|
||||
return useData({
|
||||
input: [
|
||||
['search', input.search],
|
||||
|
@@ -1,18 +1,34 @@
|
||||
import { handler as useCart } from './cart/use-cart'
|
||||
import { handler as useAddItem } from './cart/use-add-item'
|
||||
import { handler as useUpdateItem } from './cart/use-update-item'
|
||||
import { handler as useRemoveItem } from './cart/use-remove-item'
|
||||
|
||||
import { handler as useWishlist } from './wishlist/use-wishlist'
|
||||
import { handler as useWishlistAddItem } from './wishlist/use-add-item'
|
||||
import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item'
|
||||
|
||||
import { handler as useCustomer } from './customer/use-customer'
|
||||
import { handler as useSearch } from './product/use-search'
|
||||
|
||||
import { handler as useLogin } from './auth/use-login'
|
||||
import { handler as useLogout } from './auth/use-logout'
|
||||
import { handler as useSignup } from './auth/use-signup'
|
||||
|
||||
import fetcher from './fetcher'
|
||||
|
||||
export const bigcommerceProvider = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'bc_cartId',
|
||||
fetcher,
|
||||
cart: { useCart, useAddItem },
|
||||
wishlist: { useWishlist },
|
||||
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
||||
wishlist: {
|
||||
useWishlist,
|
||||
useAddItem: useWishlistAddItem,
|
||||
useRemoveItem: useWishlistRemoveItem,
|
||||
},
|
||||
customer: { useCustomer },
|
||||
products: { useSearch },
|
||||
auth: { useLogin, useLogout, useSignup },
|
||||
}
|
||||
|
||||
export type BigcommerceProvider = typeof bigcommerceProvider
|
||||
|
@@ -43,9 +43,6 @@ export type CartItemBody = Core.CartItemBody & {
|
||||
optionSelections?: OptionSelections
|
||||
}
|
||||
|
||||
type X = Core.CartItemBody extends CartItemBody ? any : never
|
||||
type Y = CartItemBody extends Core.CartItemBody ? any : never
|
||||
|
||||
export type GetCartHandlerBody = Core.GetCartHandlerBody
|
||||
|
||||
export type AddCartItemBody = Core.AddCartItemBody<CartItemBody>
|
||||
|
@@ -1,43 +1,24 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useWishlistAddItem, {
|
||||
AddItemInput,
|
||||
} from '@commerce/wishlist/use-add-item'
|
||||
import { UseWishlistInput } from '@commerce/wishlist/use-wishlist'
|
||||
import useAddItem, { UseAddItem } from '@commerce/wishlist/use-add-item'
|
||||
import type { ItemBody, AddItemBody } from '../api/wishlist'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import useWishlist from './use-wishlist'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/wishlist',
|
||||
method: 'POST',
|
||||
}
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
// export type AddItemInput = ItemBody
|
||||
|
||||
export const fetcher: HookFetcher<any, AddItemBody> = (
|
||||
options,
|
||||
{ item },
|
||||
fetch
|
||||
) => {
|
||||
// TODO: add validations before doing the fetch
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { item },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useAddItem = (opts?: UseWishlistInput<BigcommerceProvider>) => {
|
||||
export const handler: MutationHook<any, {}, ItemBody, AddItemBody> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/wishlist',
|
||||
method: 'POST',
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { data: customer } = useCustomer()
|
||||
const { revalidate } = useWishlist(opts)
|
||||
const fn = useWishlistAddItem(defaultOpts, customFetcher)
|
||||
const { revalidate } = useWishlist()
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input: AddItemInput<any>) {
|
||||
async function addItem(item) {
|
||||
if (!customer) {
|
||||
// A signed customer is required in order to have a wishlist
|
||||
throw new CommerceError({
|
||||
@@ -45,17 +26,12 @@ export function extendHook(customFetcher: typeof fetcher) {
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fn({ item: input })
|
||||
// TODO: add validations before doing the fetch
|
||||
const data = await fetch({ input: { item } })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn, revalidate, customer]
|
||||
[fetch, revalidate, customer]
|
||||
)
|
||||
}
|
||||
|
||||
useAddItem.extend = extendHook
|
||||
|
||||
return useAddItem
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@@ -1,43 +1,32 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useWishlistRemoveItem from '@commerce/wishlist/use-remove-item'
|
||||
import type { RemoveItemBody } from '../api/wishlist'
|
||||
import useRemoveItem, {
|
||||
RemoveItemInput,
|
||||
UseRemoveItem,
|
||||
} from '@commerce/wishlist/use-remove-item'
|
||||
import type { RemoveItemBody, Wishlist } from '../api/wishlist'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import useWishlist from './use-wishlist'
|
||||
import useWishlist, { UseWishlistInput } from './use-wishlist'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/wishlist',
|
||||
method: 'DELETE',
|
||||
}
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export type RemoveItemInput = {
|
||||
id: string | number
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<any | null, RemoveItemBody> = (
|
||||
options,
|
||||
{ itemId },
|
||||
fetch
|
||||
) => {
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { itemId },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useRemoveItem = (opts?: any) => {
|
||||
export const handler: MutationHook<
|
||||
Wishlist | null,
|
||||
{ wishlist?: UseWishlistInput },
|
||||
RemoveItemInput,
|
||||
RemoveItemBody
|
||||
> = {
|
||||
fetchOptions: {
|
||||
url: '/api/bigcommerce/wishlist',
|
||||
method: 'DELETE',
|
||||
},
|
||||
useHook: ({ fetch }) => ({ wishlist } = {}) => {
|
||||
const { data: customer } = useCustomer()
|
||||
const { revalidate } = useWishlist(opts)
|
||||
const fn = useWishlistRemoveItem<any | null, RemoveItemBody>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
const { revalidate } = useWishlist(wishlist)
|
||||
|
||||
return useCallback(
|
||||
async function removeItem(input: RemoveItemInput) {
|
||||
async function removeItem(input) {
|
||||
if (!customer) {
|
||||
// A signed customer is required in order to have a wishlist
|
||||
throw new CommerceError({
|
||||
@@ -45,17 +34,11 @@ export function extendHook(customFetcher: typeof fetcher) {
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fn({ itemId: String(input.id) })
|
||||
const data = await fetch({ input: { itemId: String(input.id) } })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn, revalidate, customer]
|
||||
[fetch, revalidate, customer]
|
||||
)
|
||||
}
|
||||
|
||||
useRemoveItem.extend = extendHook
|
||||
|
||||
return useRemoveItem
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@@ -1,16 +1,17 @@
|
||||
import { useMemo } from 'react'
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist'
|
||||
import type { Wishlist } from '../api/wishlist'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import type { BigcommerceProvider } from '..'
|
||||
|
||||
export default useWishlist as UseWishlist<BigcommerceProvider>
|
||||
export type UseWishlistInput = { includeProducts?: boolean }
|
||||
|
||||
export const handler: HookHandler<
|
||||
export default useWishlist as UseWishlist<typeof handler>
|
||||
|
||||
export const handler: SWRHook<
|
||||
Wishlist | null,
|
||||
{ includeProducts?: boolean },
|
||||
{ customerId?: number; includeProducts: boolean },
|
||||
UseWishlistInput,
|
||||
{ customerId?: number } & UseWishlistInput,
|
||||
{ isEmpty?: boolean }
|
||||
> = {
|
||||
fetchOptions: {
|
||||
@@ -30,16 +31,16 @@ export const handler: HookHandler<
|
||||
method: options.method,
|
||||
})
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
useHook: ({ useData }) => (input) => {
|
||||
const { data: customer } = useCustomer()
|
||||
const response = useData({
|
||||
input: [
|
||||
['customerId', (customer as any)?.id],
|
||||
['includeProducts', input.includeProducts],
|
||||
['includeProducts', input?.includeProducts],
|
||||
],
|
||||
swrOptions: {
|
||||
revalidateOnFocus: false,
|
||||
...input.swrOptions,
|
||||
...input?.swrOptions,
|
||||
},
|
||||
})
|
||||
|
||||
|
@@ -1,69 +1,23 @@
|
||||
import { useCallback } from 'react'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||
import { mutationFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, MutationHook } from '../utils/types'
|
||||
import type { Cart, CartItemBody, AddCartItemBody } from '../types'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import { BigcommerceProvider } from '@framework'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseAddItemHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'cart'>,
|
||||
'useAddItem'
|
||||
>
|
||||
|
||||
// Input expected by the action returned by the `useAddItem` hook
|
||||
export type UseAddItemInput<P extends Provider> = UseHookInput<
|
||||
UseAddItemHandler<P>
|
||||
>
|
||||
|
||||
export type UseAddItemResult<P extends Provider> = ReturnType<
|
||||
UseHookResponse<UseAddItemHandler<P>>
|
||||
>
|
||||
|
||||
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 type UseAddItem<
|
||||
H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<
|
||||
Cart,
|
||||
AddCartItemBody<CartItemBody>
|
||||
> = async ({ options, input, fetch }) => {
|
||||
return fetch({ ...options, body: input })
|
||||
> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.cart?.useAddItem!
|
||||
|
||||
const useAddItem: UseAddItem = (...args) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(...args)
|
||||
}
|
||||
|
||||
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]
|
||||
)
|
||||
}
|
||||
export default useAddItem
|
||||
|
@@ -1,17 +0,0 @@
|
||||
import type { HookFetcher, HookFetcherOptions } from '../utils/types'
|
||||
import useAddItem from './use-add-item'
|
||||
import useRemoveItem from './use-remove-item'
|
||||
import useUpdateItem from './use-update-item'
|
||||
|
||||
// This hook is probably not going to be used, but it's here
|
||||
// to show how a commerce should be structuring it
|
||||
export default function useCartActions<T, Input>(
|
||||
options: HookFetcherOptions,
|
||||
fetcher: HookFetcher<T, Input>
|
||||
) {
|
||||
const addItem = useAddItem<T, Input>(options, fetcher)
|
||||
const updateItem = useUpdateItem<T, Input>(options, fetcher)
|
||||
const removeItem = useRemoveItem<T, Input>(options, fetcher)
|
||||
|
||||
return { addItem, updateItem, removeItem }
|
||||
}
|
@@ -1,34 +1,21 @@
|
||||
import Cookies from 'js-cookie'
|
||||
import { useHook, useSWRHook } from '../utils/use-hook'
|
||||
import type { HookFetcherFn, SWRHook } from '../utils/types'
|
||||
import type { Cart } from '../types'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
|
||||
export type FetchCartInput = {
|
||||
cartId?: Cart['id']
|
||||
}
|
||||
|
||||
export type UseCartHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'cart'>,
|
||||
'useCart'
|
||||
>
|
||||
|
||||
export type UseCartInput<P extends Provider> = UseHookInput<UseCartHandler<P>>
|
||||
|
||||
export type CartResponse<P extends Provider> = UseHookResponse<
|
||||
UseCartHandler<P>
|
||||
>
|
||||
|
||||
export type UseCart<P extends Provider> = Partial<
|
||||
UseCartInput<P>
|
||||
> extends UseCartInput<P>
|
||||
? (input?: UseCartInput<P>) => CartResponse<P>
|
||||
: (input: UseCartInput<P>) => CartResponse<P>
|
||||
export type UseCart<
|
||||
H extends SWRHook<any, any, any> = SWRHook<
|
||||
Cart | null,
|
||||
{},
|
||||
FetchCartInput,
|
||||
{ isEmpty?: boolean }
|
||||
>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
|
||||
options,
|
||||
@@ -38,32 +25,17 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
|
||||
return cartId ? await fetch({ ...options }) : null
|
||||
}
|
||||
|
||||
export default function useCart<P extends Provider>(
|
||||
input: UseCartInput<P> = {}
|
||||
) {
|
||||
const { providerRef, fetcherRef, cartCookie } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.cart?.useCart
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
|
||||
const fn = (provider: Provider) => provider.cart?.useCart!
|
||||
|
||||
const useCart: UseCart = (input) => {
|
||||
const hook = useHook(fn)
|
||||
const { cartCookie } = useCommerce()
|
||||
const fetcherFn = hook.fetcher ?? fetcher
|
||||
const wrapper: typeof fetcher = (context) => {
|
||||
context.input.cartId = Cookies.get(cartCookie)
|
||||
return fetcherFn(context)
|
||||
}
|
||||
|
||||
return useHook({
|
||||
input,
|
||||
useData(ctx) {
|
||||
const response = useData(
|
||||
{ ...opts!, fetcher: wrapper },
|
||||
ctx?.input ?? [],
|
||||
provider.fetcher ?? fetcherRef.current,
|
||||
ctx?.swrOptions ?? input.swrOptions
|
||||
)
|
||||
return response
|
||||
},
|
||||
})
|
||||
return useSWRHook({ ...hook, fetcher: wrapper })(input)
|
||||
}
|
||||
|
||||
export default useCart
|
||||
|
@@ -1,10 +1,35 @@
|
||||
import useAction from '../utils/use-action'
|
||||
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||
import { mutationFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, MutationHook } from '../utils/types'
|
||||
import type { Cart, LineItem, RemoveCartItemBody } from '../types'
|
||||
import type { Provider } from '..'
|
||||
|
||||
// Input expected by the action returned by the `useRemoveItem` hook
|
||||
export interface RemoveItemInput {
|
||||
/**
|
||||
* Input expected by the action returned by the `useRemoveItem` hook
|
||||
*/
|
||||
export type RemoveItemInput = {
|
||||
id: string
|
||||
}
|
||||
|
||||
const useRemoveItem = useAction
|
||||
export type UseRemoveItem<
|
||||
H extends MutationHook<any, any, any> = MutationHook<
|
||||
Cart | null,
|
||||
{ item?: LineItem },
|
||||
RemoveItemInput,
|
||||
RemoveCartItemBody
|
||||
>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<
|
||||
Cart | null,
|
||||
RemoveCartItemBody
|
||||
> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.cart?.useRemoveItem!
|
||||
|
||||
const useRemoveItem: UseRemoveItem = (input) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(input)
|
||||
}
|
||||
|
||||
export default useRemoveItem
|
||||
|
@@ -1,11 +1,38 @@
|
||||
import useAction from '../utils/use-action'
|
||||
import type { CartItemBody } from '../types'
|
||||
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||
import { mutationFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, MutationHook } from '../utils/types'
|
||||
import type { Cart, CartItemBody, LineItem, UpdateCartItemBody } from '../types'
|
||||
import type { Provider } from '..'
|
||||
|
||||
// Input expected by the action returned by the `useUpdateItem` hook
|
||||
/**
|
||||
* Input expected by the action returned by the `useUpdateItem` hook
|
||||
*/
|
||||
export type UpdateItemInput<T extends CartItemBody> = T & {
|
||||
id: string
|
||||
}
|
||||
|
||||
const useUpdateItem = useAction
|
||||
export type UseUpdateItem<
|
||||
H extends MutationHook<any, any, any> = MutationHook<
|
||||
Cart | null,
|
||||
{
|
||||
item?: LineItem
|
||||
wait?: number
|
||||
},
|
||||
UpdateItemInput<CartItemBody>,
|
||||
UpdateCartItemBody<CartItemBody>
|
||||
>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<
|
||||
Cart | null,
|
||||
UpdateCartItemBody<CartItemBody>
|
||||
> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.cart?.useUpdateItem!
|
||||
|
||||
const useUpdateItem: UseUpdateItem = (input) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(input)
|
||||
}
|
||||
|
||||
export default useUpdateItem
|
||||
|
5
framework/commerce/config.json
Normal file
5
framework/commerce/config.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"features": {
|
||||
"wishlist": true
|
||||
}
|
||||
}
|
@@ -1,56 +1,20 @@
|
||||
import { useHook, useSWRHook } from '../utils/use-hook'
|
||||
import { SWRFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, SWRHook } from '../utils/types'
|
||||
import type { Customer } from '../types'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import defaultFetcher from '../utils/default-fetcher'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import { Provider } from '..'
|
||||
|
||||
export type UseCustomerHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'customer'>,
|
||||
'useCustomer'
|
||||
>
|
||||
export type UseCustomer<
|
||||
H extends SWRHook<any, any, any> = SWRHook<Customer | null>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export type UseCustomerInput<P extends Provider> = UseHookInput<
|
||||
UseCustomerHandler<P>
|
||||
>
|
||||
export const fetcher: HookFetcherFn<Customer | null, any> = SWRFetcher
|
||||
|
||||
export type CustomerResponse<P extends Provider> = UseHookResponse<
|
||||
UseCustomerHandler<P>
|
||||
>
|
||||
const fn = (provider: Provider) => provider.customer?.useCustomer!
|
||||
|
||||
export type UseCustomer<P extends Provider> = Partial<
|
||||
UseCustomerInput<P>
|
||||
> extends UseCustomerInput<P>
|
||||
? (input?: UseCustomerInput<P>) => CustomerResponse<P>
|
||||
: (input: UseCustomerInput<P>) => CustomerResponse<P>
|
||||
|
||||
export const fetcher = defaultFetcher as HookFetcherFn<Customer | null>
|
||||
|
||||
export default function useCustomer<P extends Provider>(
|
||||
input: UseCustomerInput<P> = {}
|
||||
) {
|
||||
const { providerRef, fetcherRef } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.customer?.useCustomer
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
|
||||
|
||||
return useHook({
|
||||
input,
|
||||
useData(ctx) {
|
||||
const response = useData(
|
||||
{ ...opts!, fetcher: fetcherFn },
|
||||
ctx?.input ?? [],
|
||||
provider.fetcher ?? fetcherRef.current,
|
||||
ctx?.swrOptions ?? input.swrOptions
|
||||
)
|
||||
return response
|
||||
},
|
||||
})
|
||||
const useCustomer: UseCustomer = (input) => {
|
||||
const hook = useHook(fn)
|
||||
return useSWRHook({ fetcher, ...hook })(input)
|
||||
}
|
||||
|
||||
export default useCustomer
|
||||
|
@@ -6,7 +6,7 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { Fetcher, HookHandler, MutationHandler } from './utils/types'
|
||||
import { Fetcher, SWRHook, MutationHook } from './utils/types'
|
||||
import type { FetchCartInput } from './cart/use-cart'
|
||||
import type { Cart, Wishlist, Customer, SearchProductsData } from './types'
|
||||
|
||||
@@ -15,17 +15,26 @@ const Commerce = createContext<CommerceContextValue<any> | {}>({})
|
||||
export type Provider = CommerceConfig & {
|
||||
fetcher: Fetcher
|
||||
cart?: {
|
||||
useCart?: HookHandler<Cart | null, any, FetchCartInput>
|
||||
useAddItem?: MutationHandler<Cart, any, any>
|
||||
useCart?: SWRHook<Cart | null, any, FetchCartInput>
|
||||
useAddItem?: MutationHook<any, any, any>
|
||||
useUpdateItem?: MutationHook<any, any, any>
|
||||
useRemoveItem?: MutationHook<any, any, any>
|
||||
}
|
||||
wishlist?: {
|
||||
useWishlist?: HookHandler<Wishlist | null, any, any>
|
||||
useWishlist?: SWRHook<Wishlist | null, any, any>
|
||||
useAddItem?: MutationHook<any, any, any>
|
||||
useRemoveItem?: MutationHook<any, any, any>
|
||||
}
|
||||
customer: {
|
||||
useCustomer?: HookHandler<Customer | null, any, any>
|
||||
customer?: {
|
||||
useCustomer?: SWRHook<Customer | null, any, any>
|
||||
}
|
||||
products: {
|
||||
useSearch?: HookHandler<SearchProductsData, any, any>
|
||||
products?: {
|
||||
useSearch?: SWRHook<SearchProductsData, any, any>
|
||||
}
|
||||
auth?: {
|
||||
useSignup?: MutationHook<any, any, any>
|
||||
useLogin?: MutationHook<any, any, any>
|
||||
useLogout?: MutationHook<any, any, any>
|
||||
}
|
||||
}
|
||||
|
||||
|
20
framework/commerce/product/use-search.tsx
Normal file
20
framework/commerce/product/use-search.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useHook, useSWRHook } from '../utils/use-hook'
|
||||
import { SWRFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, SWRHook } from '../utils/types'
|
||||
import type { SearchProductsData } from '../types'
|
||||
import { Provider } from '..'
|
||||
|
||||
export type UseSearch<
|
||||
H extends SWRHook<any, any, any> = SWRHook<SearchProductsData>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<SearchProductsData, any> = SWRFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.products?.useSearch!
|
||||
|
||||
const useSearch: UseSearch = (input) => {
|
||||
const hook = useHook(fn)
|
||||
return useSWRHook({ fetcher, ...hook })(input)
|
||||
}
|
||||
|
||||
export default useSearch
|
@@ -1,57 +0,0 @@
|
||||
import type { SearchProductsData } from '../types'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import defaultFetcher from '../utils/default-fetcher'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import { BigcommerceProvider } from '@framework'
|
||||
|
||||
export type UseSearchHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'products'>,
|
||||
'useSearch'
|
||||
>
|
||||
|
||||
export type UseSeachInput<P extends Provider> = UseHookInput<
|
||||
UseSearchHandler<P>
|
||||
>
|
||||
|
||||
export type SearchResponse<P extends Provider> = UseHookResponse<
|
||||
UseSearchHandler<P>
|
||||
>
|
||||
|
||||
export type UseSearch<P extends Provider> = Partial<
|
||||
UseSeachInput<P>
|
||||
> extends UseSeachInput<P>
|
||||
? (input?: UseSeachInput<P>) => SearchResponse<P>
|
||||
: (input: UseSeachInput<P>) => SearchResponse<P>
|
||||
|
||||
export const fetcher = defaultFetcher as HookFetcherFn<SearchProductsData>
|
||||
|
||||
export default function useSearch<P extends Provider>(
|
||||
input: UseSeachInput<P> = {}
|
||||
) {
|
||||
const { providerRef, fetcherRef } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.products?.useSearch
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
|
||||
|
||||
return useHook({
|
||||
input,
|
||||
useData(ctx) {
|
||||
const response = useData(
|
||||
{ ...opts!, fetcher: fetcherFn },
|
||||
ctx?.input ?? [],
|
||||
provider.fetcher ?? fetcherRef.current,
|
||||
ctx?.swrOptions ?? input.swrOptions
|
||||
)
|
||||
return response
|
||||
},
|
||||
})
|
||||
}
|
@@ -2,6 +2,10 @@ 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 type CommerceProviderConfig = {
|
||||
features: Record<string, boolean>
|
||||
}
|
||||
|
||||
export type Discount = {
|
||||
// The value of the discount, can be an amount or percentage
|
||||
value: number
|
||||
|
@@ -1,5 +1,19 @@
|
||||
import useAction from './utils/use-action'
|
||||
import { useHook, useMutationHook } from './utils/use-hook'
|
||||
import { mutationFetcher } from './utils/default-fetcher'
|
||||
import type { MutationHook, HookFetcherFn } from './utils/types'
|
||||
import type { Provider } from '.'
|
||||
|
||||
const useLogin = useAction
|
||||
export type UseLogin<
|
||||
H extends MutationHook<any, any, any> = MutationHook<null, {}, {}>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<null, {}> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.auth?.useLogin!
|
||||
|
||||
const useLogin: UseLogin = (...args) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(...args)
|
||||
}
|
||||
|
||||
export default useLogin
|
||||
|
@@ -1,5 +1,19 @@
|
||||
import useAction from './utils/use-action'
|
||||
import { useHook, useMutationHook } from './utils/use-hook'
|
||||
import { mutationFetcher } from './utils/default-fetcher'
|
||||
import type { HookFetcherFn, MutationHook } from './utils/types'
|
||||
import type { Provider } from '.'
|
||||
|
||||
const useLogout = useAction
|
||||
export type UseLogout<
|
||||
H extends MutationHook<any, any, any> = MutationHook<null>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<null> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.auth?.useLogout!
|
||||
|
||||
const useLogout: UseLogout = (...args) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(...args)
|
||||
}
|
||||
|
||||
export default useLogout
|
||||
|
@@ -1,5 +1,19 @@
|
||||
import useAction from './utils/use-action'
|
||||
import { useHook, useMutationHook } from './utils/use-hook'
|
||||
import { mutationFetcher } from './utils/default-fetcher'
|
||||
import type { HookFetcherFn, MutationHook } from './utils/types'
|
||||
import type { Provider } from '.'
|
||||
|
||||
const useSignup = useAction
|
||||
export type UseSignup<
|
||||
H extends MutationHook<any, any, any> = MutationHook<null>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<null> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.auth?.useSignup!
|
||||
|
||||
const useSignup: UseSignup = (...args) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(...args)
|
||||
}
|
||||
|
||||
export default useSignup
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import type { HookFetcherFn } from './types'
|
||||
|
||||
const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) =>
|
||||
export const SWRFetcher: HookFetcherFn<any, any> = ({ options, fetch }) =>
|
||||
fetch(options)
|
||||
|
||||
export default defaultFetcher
|
||||
export const mutationFetcher: HookFetcherFn<any, any> = ({
|
||||
input,
|
||||
options,
|
||||
fetch,
|
||||
}) => fetch({ ...options, body: input })
|
||||
|
||||
export default SWRFetcher
|
||||
|
37
framework/commerce/utils/features.ts
Normal file
37
framework/commerce/utils/features.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import commerceProviderConfig from '@framework/config.json'
|
||||
import type { CommerceProviderConfig } from '../types'
|
||||
import memo from 'lodash.memoize'
|
||||
|
||||
type FeaturesAPI = {
|
||||
isEnabled: (desideredFeature: string) => boolean
|
||||
}
|
||||
|
||||
function isFeatureEnabled(config: CommerceProviderConfig) {
|
||||
const features = config.features
|
||||
return (desideredFeature: string) =>
|
||||
Object.keys(features)
|
||||
.filter((k) => features[k])
|
||||
.includes(desideredFeature)
|
||||
}
|
||||
|
||||
function boostrap(): FeaturesAPI {
|
||||
const basis = {
|
||||
isEnabled: () => false,
|
||||
}
|
||||
|
||||
if (!commerceProviderConfig) {
|
||||
console.log('No config.json found - Please add a config.json')
|
||||
return basis
|
||||
}
|
||||
|
||||
if (commerceProviderConfig.features) {
|
||||
return {
|
||||
...basis,
|
||||
isEnabled: memo(isFeatureEnabled(commerceProviderConfig)),
|
||||
}
|
||||
}
|
||||
|
||||
return basis
|
||||
}
|
||||
|
||||
export default boostrap()
|
@@ -2,13 +2,18 @@ import type { ConfigInterface } from 'swr'
|
||||
import type { CommerceError } from './errors'
|
||||
import type { ResponseState } from './use-data'
|
||||
|
||||
/**
|
||||
* Returns the properties in T with the properties in type K, overriding properties defined in T
|
||||
*/
|
||||
export type Override<T, K> = Omit<T, keyof K> & K
|
||||
|
||||
/**
|
||||
* Returns the properties in T with the properties in type K changed from optional to required
|
||||
*/
|
||||
export type PickRequired<T, K extends keyof T> = Omit<T, K> &
|
||||
Required<Pick<T, K>>
|
||||
{
|
||||
[P in K]-?: NonNullable<T[P]>
|
||||
}
|
||||
|
||||
/**
|
||||
* Core fetcher added by CommerceProvider
|
||||
@@ -31,16 +36,15 @@ export type HookFetcher<Data, Input = null, Result = any> = (
|
||||
fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T>
|
||||
) => Data | Promise<Data>
|
||||
|
||||
export type HookFetcherFn<
|
||||
Data,
|
||||
Input = never,
|
||||
Result = any,
|
||||
Body = any
|
||||
> = (context: {
|
||||
export type HookFetcherFn<Data, Input = undefined, Result = any, Body = any> = (
|
||||
context: HookFetcherContext<Input, Result, Body>
|
||||
) => Data | Promise<Data>
|
||||
|
||||
export type HookFetcherContext<Input = undefined, Result = any, Body = any> = {
|
||||
options: HookFetcherOptions
|
||||
input: Input
|
||||
fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T>
|
||||
}) => Data | Promise<Data>
|
||||
}
|
||||
|
||||
export type HookFetcherOptions = { method?: string } & (
|
||||
| { query: string; url?: string }
|
||||
@@ -49,13 +53,20 @@ export type HookFetcherOptions = { method?: string } & (
|
||||
|
||||
export type HookInputValue = string | number | boolean | undefined
|
||||
|
||||
export type HookSwrInput = [string, HookInputValue][]
|
||||
export type HookSWRInput = [string, HookInputValue][]
|
||||
|
||||
export type HookFetchInput = { [k: string]: HookInputValue }
|
||||
|
||||
export type HookInput = {}
|
||||
export type HookFunction<
|
||||
Input extends { [k: string]: unknown } | null,
|
||||
T
|
||||
> = keyof Input extends never
|
||||
? () => T
|
||||
: Partial<Input> extends Input
|
||||
? (input?: Input) => T
|
||||
: (input: Input) => T
|
||||
|
||||
export type HookHandler<
|
||||
export type SWRHook<
|
||||
// Data obj returned by the hook and fetch operation
|
||||
Data,
|
||||
// Input expected by the hook
|
||||
@@ -65,58 +76,56 @@ export type HookHandler<
|
||||
// Custom state added to the response object of SWR
|
||||
State = {}
|
||||
> = {
|
||||
useHook?(context: {
|
||||
input: Input & { swrOptions?: SwrOptions<Data, FetchInput> }
|
||||
useData(context?: {
|
||||
input?: HookFetchInput | HookSwrInput
|
||||
swrOptions?: SwrOptions<Data, FetchInput>
|
||||
}): ResponseState<Data>
|
||||
}): ResponseState<Data> & State
|
||||
useHook(
|
||||
context: SWRHookContext<Data, FetchInput>
|
||||
): HookFunction<
|
||||
Input & { swrOptions?: SwrOptions<Data, FetchInput> },
|
||||
ResponseState<Data> & State
|
||||
>
|
||||
fetchOptions: HookFetcherOptions
|
||||
fetcher?: HookFetcherFn<Data, FetchInput>
|
||||
}
|
||||
|
||||
export type MutationHandler<
|
||||
export type SWRHookContext<
|
||||
Data,
|
||||
FetchInput extends { [k: string]: unknown } = {}
|
||||
> = {
|
||||
useData(context?: {
|
||||
input?: HookFetchInput | HookSWRInput
|
||||
swrOptions?: SwrOptions<Data, FetchInput>
|
||||
}): ResponseState<Data>
|
||||
}
|
||||
|
||||
export type MutationHook<
|
||||
// Data obj returned by the hook and fetch operation
|
||||
Data,
|
||||
// Input expected by the hook
|
||||
Input extends { [k: string]: unknown } = {},
|
||||
// Input expected by the action returned by the hook
|
||||
ActionInput extends { [k: string]: unknown } = {},
|
||||
// Input expected before doing a fetch operation
|
||||
FetchInput extends { [k: string]: unknown } = {}
|
||||
FetchInput extends { [k: string]: unknown } = ActionInput
|
||||
> = {
|
||||
useHook?(context: {
|
||||
input: Input
|
||||
}): (context: {
|
||||
input: FetchInput
|
||||
fetch: (context: { input: FetchInput }) => Data | Promise<Data>
|
||||
}) => Data | Promise<Data>
|
||||
useHook(
|
||||
context: MutationHookContext<Data, FetchInput>
|
||||
): HookFunction<Input, HookFunction<ActionInput, Data | Promise<Data>>>
|
||||
fetchOptions: HookFetcherOptions
|
||||
fetcher?: HookFetcherFn<Data, FetchInput>
|
||||
}
|
||||
|
||||
export type MutationHookContext<
|
||||
Data,
|
||||
FetchInput extends { [k: string]: unknown } | null = {}
|
||||
> = {
|
||||
fetch: keyof FetchInput extends never
|
||||
? () => Data | Promise<Data>
|
||||
: Partial<FetchInput> extends FetchInput
|
||||
? (context?: { input?: FetchInput }) => Data | Promise<Data>
|
||||
: (context: { input: FetchInput }) => Data | Promise<Data>
|
||||
}
|
||||
|
||||
export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
|
||||
Data,
|
||||
CommerceError,
|
||||
HookFetcher<Data, Input, Result>
|
||||
>
|
||||
|
||||
/**
|
||||
* Returns the property K from type T excluding nullables
|
||||
*/
|
||||
export type Prop<T, K extends keyof T> = NonNullable<T[K]>
|
||||
|
||||
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 HookHandlerType> = ReturnType<
|
||||
Prop<H, 'useHook'>
|
||||
>
|
||||
|
||||
export type UseHookInput<
|
||||
H extends HookHandlerType
|
||||
> = UseHookParameters<H>[0]['input']
|
||||
|
@@ -1,15 +0,0 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher, HookFetcherOptions } from './types'
|
||||
import { useCommerce } from '..'
|
||||
|
||||
export default function useAction<T, Input = null>(
|
||||
options: HookFetcherOptions,
|
||||
fetcher: HookFetcher<T, Input>
|
||||
) {
|
||||
const { fetcherRef } = useCommerce()
|
||||
|
||||
return useCallback(
|
||||
(input: Input) => fetcher(options, input, fetcherRef.current),
|
||||
[fetcher]
|
||||
)
|
||||
}
|
@@ -1,11 +1,11 @@
|
||||
import useSWR, { responseInterface } from 'swr'
|
||||
import type {
|
||||
HookHandler,
|
||||
HookSwrInput,
|
||||
HookSWRInput,
|
||||
HookFetchInput,
|
||||
PickRequired,
|
||||
Fetcher,
|
||||
SwrOptions,
|
||||
HookFetcherOptions,
|
||||
HookFetcherFn,
|
||||
} from './types'
|
||||
import defineProperty from './define-property'
|
||||
import { CommerceError } from './errors'
|
||||
@@ -14,13 +14,12 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export type UseData = <
|
||||
Data = any,
|
||||
Input extends { [k: string]: unknown } = {},
|
||||
FetchInput extends HookFetchInput = {}
|
||||
>(
|
||||
options: PickRequired<HookHandler<Data, Input, FetchInput>, 'fetcher'>,
|
||||
input: HookFetchInput | HookSwrInput,
|
||||
export type UseData = <Data = any, FetchInput extends HookFetchInput = {}>(
|
||||
options: {
|
||||
fetchOptions: HookFetcherOptions
|
||||
fetcher: HookFetcherFn<Data, FetchInput>
|
||||
},
|
||||
input: HookFetchInput | HookSWRInput,
|
||||
fetcherFn: Fetcher,
|
||||
swrOptions?: SwrOptions<Data, FetchInput>
|
||||
) => ResponseState<Data>
|
||||
|
50
framework/commerce/utils/use-hook.ts
Normal file
50
framework/commerce/utils/use-hook.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useCallback } from 'react'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import type { MutationHook, PickRequired, SWRHook } from './types'
|
||||
import useData from './use-data'
|
||||
|
||||
export function useFetcher() {
|
||||
const { providerRef, fetcherRef } = useCommerce()
|
||||
return providerRef.current.fetcher ?? fetcherRef.current
|
||||
}
|
||||
|
||||
export function useHook<
|
||||
P extends Provider,
|
||||
H extends MutationHook<any, any, any> | SWRHook<any, any, any>
|
||||
>(fn: (provider: P) => H) {
|
||||
const { providerRef } = useCommerce<P>()
|
||||
const provider = providerRef.current
|
||||
return fn(provider)
|
||||
}
|
||||
|
||||
export function useSWRHook<H extends SWRHook<any, any, any>>(
|
||||
hook: PickRequired<H, 'fetcher'>
|
||||
) {
|
||||
const fetcher = useFetcher()
|
||||
|
||||
return hook.useHook({
|
||||
useData(ctx) {
|
||||
const response = useData(hook, ctx?.input ?? [], fetcher, ctx?.swrOptions)
|
||||
return response
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useMutationHook<H extends MutationHook<any, any, any>>(
|
||||
hook: PickRequired<H, 'fetcher'>
|
||||
) {
|
||||
const fetcher = useFetcher()
|
||||
|
||||
return hook.useHook({
|
||||
fetch: useCallback(
|
||||
({ input } = {}) => {
|
||||
return hook.fetcher({
|
||||
input,
|
||||
options: hook.fetchOptions,
|
||||
fetch: fetcher,
|
||||
})
|
||||
},
|
||||
[fetcher, hook.fetchOptions]
|
||||
),
|
||||
})
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
import { useMemo } from 'react'
|
||||
import { responseInterface } from 'swr'
|
||||
import { CommerceError } from './errors'
|
||||
import { Override } from './types'
|
||||
|
||||
export type UseResponseOptions<
|
||||
D,
|
||||
R extends responseInterface<any, CommerceError>
|
||||
> = {
|
||||
descriptors?: PropertyDescriptorMap
|
||||
normalizer?: (data: R['data']) => D
|
||||
}
|
||||
|
||||
export type UseResponse = <D, R extends responseInterface<any, CommerceError>>(
|
||||
response: R,
|
||||
options: UseResponseOptions<D, R>
|
||||
) => D extends object ? Override<R, { data?: D }> : R
|
||||
|
||||
const useResponse: UseResponse = (response, { descriptors, normalizer }) => {
|
||||
const memoizedResponse = useMemo(
|
||||
() =>
|
||||
Object.create(response, {
|
||||
...descriptors,
|
||||
...(normalizer
|
||||
? {
|
||||
data: {
|
||||
get() {
|
||||
return response.data && normalizer(response.data)
|
||||
},
|
||||
enumerable: true,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
[response]
|
||||
)
|
||||
return memoizedResponse
|
||||
}
|
||||
|
||||
export default useResponse
|
@@ -1,12 +1,19 @@
|
||||
import useAction from '../utils/use-action'
|
||||
import type { CartItemBody } from '../types'
|
||||
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||
import { mutationFetcher } from '../utils/default-fetcher'
|
||||
import type { MutationHook } from '../utils/types'
|
||||
import type { Provider } from '..'
|
||||
|
||||
// Input expected by the action returned by the `useAddItem` hook
|
||||
// export interface AddItemInput {
|
||||
// includeProducts?: boolean
|
||||
// }
|
||||
export type AddItemInput<T extends CartItemBody> = T
|
||||
export type UseAddItem<
|
||||
H extends MutationHook<any, any, any> = MutationHook<any, {}, {}>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
const useAddItem = useAction
|
||||
export const fetcher = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.wishlist?.useAddItem!
|
||||
|
||||
const useAddItem: UseAddItem = (...args) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(...args)
|
||||
}
|
||||
|
||||
export default useAddItem
|
||||
|
@@ -1,5 +1,28 @@
|
||||
import useAction from '../utils/use-action'
|
||||
import { useHook, useMutationHook } from '../utils/use-hook'
|
||||
import { mutationFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, MutationHook } from '../utils/types'
|
||||
import type { Provider } from '..'
|
||||
|
||||
const useRemoveItem = useAction
|
||||
export type RemoveItemInput = {
|
||||
id: string | number
|
||||
}
|
||||
|
||||
export type UseRemoveItem<
|
||||
H extends MutationHook<any, any, any> = MutationHook<
|
||||
any | null,
|
||||
{ wishlist?: any },
|
||||
RemoveItemInput,
|
||||
{}
|
||||
>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export const fetcher: HookFetcherFn<any | null, {}> = mutationFetcher
|
||||
|
||||
const fn = (provider: Provider) => provider.wishlist?.useRemoveItem!
|
||||
|
||||
const useRemoveItem: UseRemoveItem = (input) => {
|
||||
const hook = useHook(fn)
|
||||
return useMutationHook({ fetcher, ...hook })(input)
|
||||
}
|
||||
|
||||
export default useRemoveItem
|
||||
|
@@ -1,56 +1,25 @@
|
||||
import { useHook, useSWRHook } from '../utils/use-hook'
|
||||
import { SWRFetcher } from '../utils/default-fetcher'
|
||||
import type { HookFetcherFn, SWRHook } from '../utils/types'
|
||||
import type { Wishlist } from '../types'
|
||||
import type {
|
||||
Prop,
|
||||
HookFetcherFn,
|
||||
UseHookInput,
|
||||
UseHookResponse,
|
||||
} from '../utils/types'
|
||||
import defaultFetcher from '../utils/default-fetcher'
|
||||
import useData from '../utils/use-data'
|
||||
import { Provider, useCommerce } from '..'
|
||||
import type { Provider } from '..'
|
||||
|
||||
export type UseWishlistHandler<P extends Provider> = Prop<
|
||||
Prop<P, 'wishlist'>,
|
||||
'useWishlist'
|
||||
>
|
||||
export type UseWishlist<
|
||||
H extends SWRHook<any, any, any> = SWRHook<
|
||||
Wishlist | null,
|
||||
{ includeProducts?: boolean },
|
||||
{ customerId?: number; includeProducts: boolean },
|
||||
{ isEmpty?: boolean }
|
||||
>
|
||||
> = ReturnType<H['useHook']>
|
||||
|
||||
export type UseWishlistInput<P extends Provider> = UseHookInput<
|
||||
UseWishlistHandler<P>
|
||||
>
|
||||
export const fetcher: HookFetcherFn<Wishlist | null, any> = SWRFetcher
|
||||
|
||||
export type WishlistResponse<P extends Provider> = UseHookResponse<
|
||||
UseWishlistHandler<P>
|
||||
>
|
||||
const fn = (provider: Provider) => provider.wishlist?.useWishlist!
|
||||
|
||||
export type UseWishlist<P extends Provider> = Partial<
|
||||
UseWishlistInput<P>
|
||||
> extends UseWishlistInput<P>
|
||||
? (input?: UseWishlistInput<P>) => WishlistResponse<P>
|
||||
: (input: UseWishlistInput<P>) => WishlistResponse<P>
|
||||
|
||||
export const fetcher = defaultFetcher as HookFetcherFn<Wishlist | null>
|
||||
|
||||
export default function useWishlist<P extends Provider>(
|
||||
input: UseWishlistInput<P> = {}
|
||||
) {
|
||||
const { providerRef, fetcherRef } = useCommerce<P>()
|
||||
|
||||
const provider = providerRef.current
|
||||
const opts = provider.wishlist?.useWishlist
|
||||
|
||||
const fetcherFn = opts?.fetcher ?? fetcher
|
||||
const useHook = opts?.useHook ?? ((ctx) => ctx.useData())
|
||||
|
||||
return useHook({
|
||||
input,
|
||||
useData(ctx) {
|
||||
const response = useData(
|
||||
{ ...opts!, fetcher: fetcherFn },
|
||||
ctx?.input ?? [],
|
||||
provider.fetcher ?? fetcherRef.current,
|
||||
ctx?.swrOptions ?? input.swrOptions
|
||||
)
|
||||
return response
|
||||
},
|
||||
})
|
||||
const useWishlist: UseWishlist = (input) => {
|
||||
const hook = useHook(fn)
|
||||
return useSWRHook({ fetcher, ...hook })(input)
|
||||
}
|
||||
|
||||
export default useWishlist
|
||||
|
Reference in New Issue
Block a user