forked from crowetic/commerce
Merge pull request #5 from okbel/initial-wishlist-hooks
Add in initial wishlist api functions and hooks
This commit is contained in:
commit
9c1372acb7
@ -22,7 +22,7 @@ export type ProductsHandlers = {
|
||||
const METHODS = ['GET']
|
||||
|
||||
// TODO: a complete implementation should have schema validation for `req.body`
|
||||
const cartApi: BigcommerceApiHandler<
|
||||
const productApi: BigcommerceApiHandler<
|
||||
SearchProductsData,
|
||||
ProductsHandlers
|
||||
> = async (req, res, config, handlers) => {
|
||||
@ -45,4 +45,4 @@ const cartApi: BigcommerceApiHandler<
|
||||
|
||||
export const handlers = { getProducts }
|
||||
|
||||
export default createApiHandler(cartApi, handlers, {})
|
||||
export default createApiHandler(productApi, handlers, {})
|
||||
|
30
lib/bigcommerce/api/wishlist/handlers/add-item.ts
Normal file
30
lib/bigcommerce/api/wishlist/handlers/add-item.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { WishlistHandlers } from '..'
|
||||
|
||||
// Return current wishlist info
|
||||
const addItem: WishlistHandlers['addItem'] = async ({
|
||||
res,
|
||||
body: { wishlistId, item },
|
||||
config,
|
||||
}) => {
|
||||
if (!item) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Missing item' }],
|
||||
})
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
items: [item],
|
||||
}),
|
||||
}
|
||||
const { data } = await config.storeApiFetch(
|
||||
`/v3/wishlists/${wishlistId}/items`,
|
||||
options
|
||||
)
|
||||
|
||||
res.status(200).json({ data })
|
||||
}
|
||||
|
||||
export default addItem
|
25
lib/bigcommerce/api/wishlist/handlers/add-wishlist.ts
Normal file
25
lib/bigcommerce/api/wishlist/handlers/add-wishlist.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { WishlistHandlers } from '..'
|
||||
|
||||
// Return current wishlist info
|
||||
const addWishlist: WishlistHandlers['addWishlist'] = async ({
|
||||
res,
|
||||
body: { wishlist },
|
||||
config,
|
||||
}) => {
|
||||
if (!wishlist) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Missing wishlist data' }],
|
||||
})
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(wishlist),
|
||||
}
|
||||
const { data } = await config.storeApiFetch(`/v3/wishlists/`, options)
|
||||
|
||||
res.status(200).json({ data })
|
||||
}
|
||||
|
||||
export default addWishlist
|
22
lib/bigcommerce/api/wishlist/handlers/get-all-wishlists.ts
Normal file
22
lib/bigcommerce/api/wishlist/handlers/get-all-wishlists.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { BigcommerceApiError } from '../../utils/errors'
|
||||
import type { WishlistList, WishlistHandlers } from '..'
|
||||
|
||||
// Return all wishlists
|
||||
const getAllWishlists: WishlistHandlers['getAllWishlists'] = async ({
|
||||
res,
|
||||
body: { customerId },
|
||||
config,
|
||||
}) => {
|
||||
let result: { data?: WishlistList } = {}
|
||||
|
||||
try {
|
||||
result = await config.storeApiFetch(`/v3/wishlists/customer_id=${customerId}`)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const data = (result.data ?? []) as any
|
||||
res.status(200).json({ data })
|
||||
}
|
||||
|
||||
export default getAllWishlists
|
21
lib/bigcommerce/api/wishlist/handlers/get-wishlist.ts
Normal file
21
lib/bigcommerce/api/wishlist/handlers/get-wishlist.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { BigcommerceApiError } from '../../utils/errors'
|
||||
import type { Wishlist, WishlistHandlers } from '..'
|
||||
|
||||
// Return wishlist info
|
||||
const getWishlist: WishlistHandlers['getWishlist'] = async ({
|
||||
res,
|
||||
body: { wishlistId },
|
||||
config,
|
||||
}) => {
|
||||
let result: { data?: Wishlist } = {}
|
||||
|
||||
try {
|
||||
result = await config.storeApiFetch(`/v3/wishlists/${wishlistId}`)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
res.status(200).json({ data: result.data ?? null })
|
||||
}
|
||||
|
||||
export default getWishlist
|
25
lib/bigcommerce/api/wishlist/handlers/remove-item.ts
Normal file
25
lib/bigcommerce/api/wishlist/handlers/remove-item.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { WishlistHandlers } from '..'
|
||||
|
||||
// Return current wishlist info
|
||||
const removeItem: WishlistHandlers['removeItem'] = async ({
|
||||
res,
|
||||
body: { wishlistId, itemId },
|
||||
config,
|
||||
}) => {
|
||||
if (!wishlistId || !itemId) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
const result = await config.storeApiFetch<{ data: any } | null>(
|
||||
`/v3/wishlists/${wishlistId}/items/${itemId}`,
|
||||
{ method: 'DELETE' }
|
||||
)
|
||||
const data = result?.data ?? null
|
||||
|
||||
res.status(200).json({ data })
|
||||
}
|
||||
|
||||
export default removeItem
|
25
lib/bigcommerce/api/wishlist/handlers/remove-wishlist.ts
Normal file
25
lib/bigcommerce/api/wishlist/handlers/remove-wishlist.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { WishlistHandlers } from '..'
|
||||
|
||||
// Return current wishlist info
|
||||
const removeWishlist: WishlistHandlers['removeWishlist'] = async ({
|
||||
res,
|
||||
body: { wishlistId },
|
||||
config,
|
||||
}) => {
|
||||
if (!wishlistId) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
const result = await config.storeApiFetch<{ data: any } | null>(
|
||||
`/v3/wishlists/${wishlistId}/`,
|
||||
{ method: 'DELETE' }
|
||||
)
|
||||
const data = result?.data ?? null
|
||||
|
||||
res.status(200).json({ data })
|
||||
}
|
||||
|
||||
export default removeWishlist
|
27
lib/bigcommerce/api/wishlist/handlers/update-wishlist.ts
Normal file
27
lib/bigcommerce/api/wishlist/handlers/update-wishlist.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { WishlistHandlers } from '..'
|
||||
|
||||
// Update wish info
|
||||
const updateWishlist: WishlistHandlers['updateWishlist'] = async ({
|
||||
res,
|
||||
body: { wishlistId, wishlist },
|
||||
config,
|
||||
}) => {
|
||||
if (!wishlistId || !wishlist) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
const { data } = await config.storeApiFetch(
|
||||
`/v3/wishlists/${wishlistId}/`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(wishlist),
|
||||
}
|
||||
)
|
||||
|
||||
res.status(200).json({ data })
|
||||
}
|
||||
|
||||
export default updateWishlist
|
152
lib/bigcommerce/api/wishlist/index.ts
Normal file
152
lib/bigcommerce/api/wishlist/index.ts
Normal file
@ -0,0 +1,152 @@
|
||||
import isAllowedMethod from '../utils/is-allowed-method'
|
||||
import createApiHandler, {
|
||||
BigcommerceApiHandler,
|
||||
BigcommerceHandler,
|
||||
} from '../utils/create-api-handler'
|
||||
import { BigcommerceApiError } from '../utils/errors'
|
||||
import getWishlist from './handlers/get-wishlist'
|
||||
import getAllWishlists from './handlers/get-all-wishlists'
|
||||
import addItem from './handlers/add-item'
|
||||
import removeItem from './handlers/remove-item'
|
||||
import updateWishlist from './handlers/update-wishlist'
|
||||
import removeWishlist from './handlers/remove-wishlist'
|
||||
import addWishlist from './handlers/add-wishlist'
|
||||
|
||||
type Body<T> = Partial<T> | undefined
|
||||
|
||||
export type ItemBody = {
|
||||
product_id: number
|
||||
variant_id: number
|
||||
}
|
||||
|
||||
export type AddItemBody = { wishlistId: string; item: ItemBody }
|
||||
|
||||
export type RemoveItemBody = { wishlistId: string; itemId: string }
|
||||
|
||||
export type WishlistBody = {
|
||||
customer_id: number
|
||||
is_public: number
|
||||
name: string
|
||||
items: any[]
|
||||
}
|
||||
|
||||
export type AddWishlistBody = { wishlist: WishlistBody }
|
||||
|
||||
// TODO: this type should match:
|
||||
// https://developer.bigcommerce.com/api-reference/store-management/wishlists/wishlists/wishlistsbyidget
|
||||
export type Wishlist = {
|
||||
id: string
|
||||
customer_id: number
|
||||
name: string
|
||||
is_public: boolean
|
||||
token: string
|
||||
items: any[]
|
||||
// TODO: add missing fields
|
||||
}
|
||||
|
||||
export type WishlistList = Wishlist[]
|
||||
|
||||
export type WishlistHandlers = {
|
||||
getAllWishlists: BigcommerceHandler<WishlistList, { customerId?: string }>
|
||||
getWishlist: BigcommerceHandler<Wishlist, { wishlistId?: string }>
|
||||
addWishlist: BigcommerceHandler<
|
||||
Wishlist,
|
||||
{ wishlistId: string } & Body<AddWishlistBody>
|
||||
>
|
||||
updateWishlist: BigcommerceHandler<
|
||||
Wishlist,
|
||||
{ wishlistId: string } & Body<AddWishlistBody>
|
||||
>
|
||||
addItem: BigcommerceHandler<Wishlist, { wishlistId: string } & Body<AddItemBody>>
|
||||
removeItem: BigcommerceHandler<
|
||||
Wishlist,
|
||||
{ wishlistId: string } & Body<RemoveItemBody>
|
||||
>
|
||||
removeWishlist: BigcommerceHandler<Wishlist, { wishlistId: string }>
|
||||
}
|
||||
|
||||
const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
|
||||
|
||||
// TODO: a complete implementation should have schema validation for `req.body`
|
||||
const wishlistApi: BigcommerceApiHandler<Wishlist, WishlistHandlers> = async (
|
||||
req,
|
||||
res,
|
||||
config,
|
||||
handlers
|
||||
) => {
|
||||
if (!isAllowedMethod(req, res, METHODS)) return
|
||||
|
||||
try {
|
||||
const { wishlistId, itemId, customerId } = req.body
|
||||
// Return current wishlist info
|
||||
if (req.method === 'GET' && wishlistId) {
|
||||
const body = { wishlistId: wishlistId as string }
|
||||
return await handlers['getWishlist']({ req, res, config, body })
|
||||
}
|
||||
|
||||
// Add an item to the wishlist
|
||||
if (req.method === 'POST' && wishlistId) {
|
||||
const body = { wishlistId, ...req.body }
|
||||
return await handlers['addItem']({ req, res, config, body })
|
||||
}
|
||||
|
||||
// Update a wishlist
|
||||
if (req.method === 'PUT' && wishlistId) {
|
||||
const body = { wishlistId, ...req.body }
|
||||
return await handlers['updateWishlist']({ req, res, config, body })
|
||||
}
|
||||
|
||||
// Remove an item from the wishlist
|
||||
if (req.method === 'DELETE' && wishlistId && itemId) {
|
||||
const body = {
|
||||
wishlistId: wishlistId as string,
|
||||
itemId: itemId as string,
|
||||
}
|
||||
return await handlers['removeItem']({ req, res, config, body })
|
||||
}
|
||||
|
||||
// Remove the wishlist
|
||||
if (req.method === 'DELETE' && wishlistId && !itemId) {
|
||||
const body = { wishlistId: wishlistId as string }
|
||||
return await handlers['removeWishlist']({ req, res, config, body })
|
||||
}
|
||||
|
||||
// Get all the wishlists
|
||||
if (req.method === 'GET' && !wishlistId) {
|
||||
const body = { customerId: customerId as string }
|
||||
return await handlers['getAllWishlists']({
|
||||
req,
|
||||
res: res as any,
|
||||
config,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
// Create a wishlist
|
||||
if (req.method === 'POST' && !wishlistId) {
|
||||
const { body } = req
|
||||
return await handlers['addWishlist']({ req, res, config, body })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
const message =
|
||||
error instanceof BigcommerceApiError
|
||||
? 'An unexpected error ocurred with the Bigcommerce API'
|
||||
: 'An unexpected error ocurred'
|
||||
|
||||
res.status(500).json({ data: null, errors: [{ message }] })
|
||||
}
|
||||
}
|
||||
|
||||
export const handlers = {
|
||||
getWishlist,
|
||||
addItem,
|
||||
updateWishlist,
|
||||
removeItem,
|
||||
removeWishlist,
|
||||
getAllWishlists,
|
||||
addWishlist,
|
||||
}
|
||||
|
||||
export default createApiHandler(wishlistApi, handlers, {})
|
46
lib/bigcommerce/wishlist/use-add-item.tsx
Normal file
46
lib/bigcommerce/wishlist/use-add-item.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@lib/commerce/utils/types'
|
||||
import useAction from '@lib/commerce/utils/use-action'
|
||||
import type { ItemBody, AddItemBody } from '../api/wishlist'
|
||||
import useWishlist, { Wishlist } from './use-wishlist'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/wishlist',
|
||||
method: 'POST',
|
||||
}
|
||||
|
||||
export type AddItemInput = ItemBody
|
||||
|
||||
export const fetcher: HookFetcher<Wishlist, AddItemBody> = (
|
||||
options,
|
||||
{ wishlistId, item },
|
||||
fetch
|
||||
) => {
|
||||
return fetch({
|
||||
url: options?.url ?? defaultOpts.url,
|
||||
method: options?.method ?? defaultOpts.method,
|
||||
body: { wishlistId, item },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useAddItem = (wishlistId: string) => {
|
||||
const { mutate } = useWishlist(wishlistId)
|
||||
const fn = useAction<Wishlist, AddItemBody>(defaultOpts, customFetcher)
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input: AddItemInput) {
|
||||
const data = await fn({ wishlistId, item: input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fn, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useAddItem.extend = extendHook
|
||||
|
||||
return useAddItem
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
51
lib/bigcommerce/wishlist/use-remove-item.tsx
Normal file
51
lib/bigcommerce/wishlist/use-remove-item.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@lib/commerce/utils/types'
|
||||
import useAction from '@lib/commerce/utils/use-action'
|
||||
import type { RemoveItemBody } from '../api/wishlist'
|
||||
import useWishlist, { Wishlist } from './use-wishlist'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/wishlists',
|
||||
method: 'DELETE',
|
||||
}
|
||||
|
||||
export type RemoveItemInput = {
|
||||
id: string
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<Wishlist | null, RemoveItemBody> = (
|
||||
options,
|
||||
{ wishlistId, itemId },
|
||||
fetch
|
||||
) => {
|
||||
return fetch({
|
||||
url: options?.url ?? defaultOpts.url,
|
||||
method: options?.method ?? defaultOpts.method,
|
||||
body: { wishlistId, itemId },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useRemoveItem = (wishlistId: string, item?: any) => {
|
||||
const { mutate } = useWishlist(wishlistId)
|
||||
const fn = useAction<Wishlist | null, RemoveItemBody>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function removeItem(input: RemoveItemInput) {
|
||||
const data = await fn({ wishlistId, itemId: input.id ?? item?.id })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fn, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useRemoveItem.extend = extendHook
|
||||
|
||||
return useRemoveItem
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
11
lib/bigcommerce/wishlist/use-wishlist-actions.tsx
Normal file
11
lib/bigcommerce/wishlist/use-wishlist-actions.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import useAddItem from './use-add-item'
|
||||
import useRemoveItem from './use-remove-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 useWishlistActions(wishlistId: string) {
|
||||
const addItem = useAddItem(wishlistId)
|
||||
const removeItem = useRemoveItem(wishlistId)
|
||||
|
||||
return { addItem, removeItem }
|
||||
}
|
41
lib/bigcommerce/wishlist/use-wishlist.tsx
Normal file
41
lib/bigcommerce/wishlist/use-wishlist.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { HookFetcher } from '@lib/commerce/utils/types'
|
||||
import useData from '@lib/commerce/utils/use-data'
|
||||
import type { Wishlist } from '../api/wishlist'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/wishlists',
|
||||
}
|
||||
|
||||
export type { Wishlist }
|
||||
|
||||
export type WishlistInput = {
|
||||
wishlistId: string | undefined
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<Wishlist | null, WishlistInput> = (
|
||||
options,
|
||||
{ wishlistId },
|
||||
fetch
|
||||
) => {
|
||||
return fetch({
|
||||
url: options?.url,
|
||||
body: { wishlistId },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useWishlists = (wishlistId: string) => {
|
||||
const fetchFn: typeof fetcher = (options, input, fetch) => {
|
||||
return customFetcher(options, input, fetch)
|
||||
}
|
||||
const response = useData(defaultOpts, [['wishlistId', wishlistId]], fetchFn)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
useWishlists.extend = extendHook
|
||||
|
||||
return useWishlists
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
Loading…
x
Reference in New Issue
Block a user