diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 4f8a5cbcd..b5cc0cccf 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,4 +1,42 @@ -import useCart, { UseCart } from '@commerce/cart/use-cart' +import { useMemo } from 'react' +import { HookHandler } 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 + +export const handler: HookHandler< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + async fetcher({ input: { cartId }, options, fetch }) { + const data = cartId ? await fetch(options) : null + return data && normalizeCart(data) + }, + useHook({ input, useData }) { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} diff --git a/framework/bigcommerce/customer/use-customer.tsx b/framework/bigcommerce/customer/use-customer.tsx index 95adb6fb3..3929002f7 100644 --- a/framework/bigcommerce/customer/use-customer.tsx +++ b/framework/bigcommerce/customer/use-customer.tsx @@ -1,4 +1,25 @@ +import { HookHandler } 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 + +export const handler: HookHandler = { + fetchOptions: { + url: '/api/bigcommerce/customers', + method: 'GET', + }, + async fetcher({ options, fetch }) { + const data = await fetch(options) + return data?.customer ?? null + }, + useHook({ input, useData }) { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 52db6a72d..393a8c0b9 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,4 +1,54 @@ +import { HookHandler } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/products/use-search' +import type { SearchProductsData } from '../api/catalog/products' import type { BigcommerceProvider } from '..' export default useSearch as UseSearch + +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +export const handler: HookHandler< + SearchProductsData, + SearchProductsInput, + SearchProductsInput +> = { + fetchOptions: { + url: '/api/bigcommerce/catalog/products', + method: 'GET', + }, + fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { + // Use a dummy base as we only care about the relative path + const url = new URL(options.url!, 'http://a') + + if (search) url.searchParams.set('search', search) + if (Number.isInteger(categoryId)) + url.searchParams.set('category', String(categoryId)) + if (Number.isInteger(brandId)) + url.searchParams.set('brand', String(brandId)) + if (sort) url.searchParams.set('sort', sort) + + return fetch({ + url: url.pathname + url.search, + method: options.method, + }) + }, + useHook({ input, useData }) { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index e8ac29cf5..a54fab0bb 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -1,13 +1,10 @@ -import { useMemo } from 'react' import { FetcherError } from '@commerce/utils/errors' -import type { Fetcher, HookHandler } from '@commerce/utils/types' -import type { FetchCartInput } from '@commerce/cart/use-cart' +import type { Fetcher } from '@commerce/utils/types' import { normalizeCart } from './lib/normalize' -import type { Wishlist } from './api/wishlist' -import type { Customer, CustomerData } from './api/customers' -import type { SearchProductsData } from './api/catalog/products' -import useCustomer from './customer/use-customer' -import type { Cart } from './types' +import { handler as useCart } from './cart/use-cart' +import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' async function getText(res: Response) { try { @@ -46,158 +43,6 @@ const fetcher: Fetcher = async ({ throw await getError(res) } -const useCart: HookHandler< - Cart | null, - {}, - FetchCartInput, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/cart', - method: 'GET', - }, - async fetcher({ input: { cartId }, options, fetch }) { - const data = cartId ? await fetch(options) : null - return data && normalizeCart(data) - }, - useHook({ input, useData }) { - const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, - }) - - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }), - [response] - ) - }, -} - -const useWishlist: HookHandler< - Wishlist | null, - { includeProducts?: boolean }, - { customerId?: number; includeProducts: boolean }, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/wishlist', - method: 'GET', - }, - fetcher({ input: { customerId, includeProducts }, options, fetch }) { - if (!customerId) return null - - // Use a dummy base as we only care about the relative path - const url = new URL(options.url!, 'http://a') - - if (includeProducts) url.searchParams.set('products', '1') - - return fetch({ - url: url.pathname + url.search, - method: options.method, - }) - }, - useHook({ input, useData }) { - const { data: customer } = useCustomer() - const response = useData({ - input: [ - ['customerId', (customer as any)?.id], - ['includeProducts', input.includeProducts], - ], - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.items?.length || 0) <= 0 - }, - enumerable: true, - }, - }), - [response] - ) - }, -} - -const useCustomerHandler: HookHandler = { - fetchOptions: { - url: '/api/bigcommerce/customers', - method: 'GET', - }, - async fetcher({ options, fetch }) { - const data = await fetch(options) - return data?.customer ?? null - }, - useHook({ input, useData }) { - return useData({ - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - }, -} - -export type SearchProductsInput = { - search?: string - categoryId?: number - brandId?: number - sort?: string -} - -const useSearch: HookHandler< - SearchProductsData, - SearchProductsInput, - SearchProductsInput -> = { - fetchOptions: { - url: '/api/bigcommerce/catalog/products', - method: 'GET', - }, - fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { - // Use a dummy base as we only care about the relative path - const url = new URL(options.url!, 'http://a') - - if (search) url.searchParams.set('search', search) - if (Number.isInteger(categoryId)) - url.searchParams.set('category', String(categoryId)) - if (Number.isInteger(brandId)) - url.searchParams.set('brand', String(brandId)) - if (sort) url.searchParams.set('sort', sort) - - return fetch({ - url: url.pathname + url.search, - method: options.method, - }) - }, - useHook({ input, useData }) { - return useData({ - input: [ - ['search', input.search], - ['categoryId', input.categoryId], - ['brandId', input.brandId], - ['sort', input.sort], - ], - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - }, -} - export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', @@ -205,7 +50,7 @@ export const bigcommerceProvider = { cartNormalizer: normalizeCart, cart: { useCart }, wishlist: { useWishlist }, - customer: { useCustomer: useCustomerHandler }, + customer: { useCustomer }, products: { useSearch }, } diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index dfa3d9dbc..a93f0f6a4 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -1,4 +1,59 @@ +import { useMemo } from 'react' +import { HookHandler } 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 + +export const handler: HookHandler< + Wishlist | null, + { includeProducts?: boolean }, + { customerId?: number; includeProducts: boolean }, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/wishlist', + method: 'GET', + }, + fetcher({ input: { customerId, includeProducts }, options, fetch }) { + if (!customerId) return null + + // Use a dummy base as we only care about the relative path + const url = new URL(options.url!, 'http://a') + + if (includeProducts) url.searchParams.set('products', '1') + + return fetch({ + url: url.pathname + url.search, + method: options.method, + }) + }, + useHook({ input, useData }) { + const { data: customer } = useCustomer() + const response = useData({ + input: [ + ['customerId', (customer as any)?.id], + ['includeProducts', input.includeProducts], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +}