Added framework folder

This commit is contained in:
Luis Alvarez
2020-12-29 19:14:49 -05:00
parent 5b5c8702a3
commit ed0783cfb3
88 changed files with 11839 additions and 3 deletions

View File

@@ -0,0 +1,56 @@
import { useCallback } from 'react'
import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useCartAddItem from '@commerce/cart/use-add-item'
import type { ItemBody, AddItemBody } from '../api/cart'
import useCart, { Cart } from './use-cart'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'POST',
}
export type AddItemInput = ItemBody
export const fetcher: HookFetcher<Cart, AddItemBody> = (
options,
{ item },
fetch
) => {
if (
item.quantity &&
(!Number.isInteger(item.quantity) || item.quantity! < 1)
) {
throw new CommerceError({
message: 'The item quantity has to be a valid integer greater than 0',
})
}
return fetch({
...defaultOpts,
...options,
body: { item },
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useAddItem = () => {
const { mutate } = useCart()
const fn = useCartAddItem(defaultOpts, customFetcher)
return useCallback(
async function addItem(input: AddItemInput) {
const data = await fn({ item: input })
await mutate(data, false)
return data
},
[fn, mutate]
)
}
useAddItem.extend = extendHook
return useAddItem
}
export default extendHook(fetcher)

View File

@@ -0,0 +1,13 @@
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 }
}

View File

@@ -0,0 +1,50 @@
import type { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data'
import useCommerceCart, { CartInput } from '@commerce/cart/use-cart'
import type { Cart } from '../api/cart'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'GET',
}
export type { Cart }
export const fetcher: HookFetcher<Cart | null, CartInput> = (
options,
{ cartId },
fetch
) => {
return cartId ? fetch({ ...defaultOpts, ...options }) : null
}
export function extendHook(
customFetcher: typeof fetcher,
swrOptions?: SwrOptions<Cart | null, CartInput>
) {
const useCart = () => {
const response = useCommerceCart(defaultOpts, [], customFetcher, {
revalidateOnFocus: false,
...swrOptions,
})
// Uses a getter to only calculate the prop when required
// response.data is also a getter and it's better to not trigger it early
Object.defineProperty(response, 'isEmpty', {
get() {
return Object.values(response.data?.line_items ?? {}).every(
(items) => !items.length
)
},
set: (x) => x,
})
return response
}
useCart.extend = extendHook
return useCart
}
export default extendHook(fetcher)

View File

@@ -0,0 +1,51 @@
import { useCallback } from 'react'
import { HookFetcher } from '@commerce/utils/types'
import useCartRemoveItem from '@commerce/cart/use-remove-item'
import type { RemoveItemBody } from '../api/cart'
import useCart, { Cart } from './use-cart'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'DELETE',
}
export type RemoveItemInput = {
id: string
}
export const fetcher: HookFetcher<Cart | null, RemoveItemBody> = (
options,
{ itemId },
fetch
) => {
return fetch({
...defaultOpts,
...options,
body: { itemId },
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useRemoveItem = (item?: any) => {
const { mutate } = useCart()
const fn = useCartRemoveItem<Cart | null, RemoveItemBody>(
defaultOpts,
customFetcher
)
return useCallback(
async function removeItem(input: RemoveItemInput) {
const data = await fn({ itemId: input.id ?? item?.id })
await mutate(data, false)
return data
},
[fn, mutate]
)
}
useRemoveItem.extend = extendHook
return useRemoveItem
}
export default extendHook(fetcher)

View File

@@ -0,0 +1,70 @@
import { useCallback } from 'react'
import debounce from 'lodash.debounce'
import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useCartUpdateItem from '@commerce/cart/use-update-item'
import type { ItemBody, UpdateItemBody } from '../api/cart'
import { fetcher as removeFetcher } from './use-remove-item'
import useCart, { Cart } from './use-cart'
const defaultOpts = {
url: '/api/bigcommerce/cart',
method: 'PUT',
}
export type UpdateItemInput = Partial<{ id: string } & ItemBody>
export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = (
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)
}
} else if (item.quantity) {
throw new CommerceError({
message: 'The item quantity has to be a valid integer',
})
}
return fetch({
...defaultOpts,
...options,
body: { itemId, item },
})
}
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
const useUpdateItem = (item?: any) => {
const { mutate } = useCart()
const fn = useCartUpdateItem<Cart | null, UpdateItemBody>(
defaultOpts,
customFetcher
)
return useCallback(
debounce(async (input: UpdateItemInput) => {
const data = await fn({
itemId: input.id ?? item?.id,
item: {
productId: input.productId ?? item?.product_id,
variantId: input.productId ?? item?.variant_id,
quantity: input.quantity,
},
})
await mutate(data, false)
return data
}, cfg?.wait ?? 500),
[fn, mutate]
)
}
useUpdateItem.extend = extendHook
return useUpdateItem
}
export default extendHook(fetcher)