diff --git a/codegen.json b/codegen.json index cce0f4f8b..e08292091 100644 --- a/codegen.json +++ b/codegen.json @@ -8,7 +8,7 @@ }, "documents": [ { - "./lib/bigcommerce/api/{operations,fragments}/**/*.ts": { + "./lib/bigcommerce/api/**/*.ts": { "noRequire": true } } diff --git a/components/core/Navbar/Navbar.tsx b/components/core/Navbar/Navbar.tsx index 5b1172754..39e22eb17 100644 --- a/components/core/Navbar/Navbar.tsx +++ b/components/core/Navbar/Navbar.tsx @@ -20,7 +20,7 @@ const Navbar: FC = ({ className }) => { - ) } diff --git a/components/product/ProductCard/ProductCard.module.css b/components/product/ProductCard/ProductCard.module.css index 7846da50e..d0ea6d80c 100644 --- a/components/product/ProductCard/ProductCard.module.css +++ b/components/product/ProductCard/ProductCard.module.css @@ -65,23 +65,40 @@ @apply transform absolute inset-0 z-0 bg-secondary; } -.squareBg.gray { - @apply bg-gray-300 !important; +.simple { + & .squareBg { + @apply bg-gray-300 !important; + } + + & .productTitle { + width: 18vw; + margin-top: -7px; + font-size: 1rem; + } + + & .productPrice { + @apply text-sm; + } } .productTitle { - line-height: 40px; - width: 18vw; + @apply pt-4 leading-8; + width: 400px; + font-size: 2rem; + letter-spacing: 0.4px; & span { - @apply inline text-2xl leading-6 p-4 bg-primary text-primary font-bold; + @apply inline p-4 bg-primary text-primary font-bold; + font-size: inherit; + letter-spacing: inherit; box-decoration-break: clone; -webkit-box-decoration-break: clone; } } .productPrice { - @apply px-3 py-1 pb-2 bg-primary text-base font-semibold inline-block text-sm leading-6; + @apply py-4 px-4 bg-primary text-base font-semibold inline-block text-sm leading-6; + letter-spacing: 0.4px; } .wishlistButton { diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index cae070fbd..dc04715c2 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -31,19 +31,21 @@ const ProductCard: FC = ({ className, product: p, variant }) => { return ( - +
-
+
-
-

+

+

{p.name} -

+

${p.prices?.price.value}
diff --git a/components/product/ProductView/ProductView.module.css b/components/product/ProductView/ProductView.module.css index e86769c44..99346db11 100644 --- a/components/product/ProductView/ProductView.module.css +++ b/components/product/ProductView/ProductView.module.css @@ -37,7 +37,9 @@ @apply absolute top-6 left-0 z-50; & .name { - @apply px-6 py-2 bg-primary text-primary font-bold text-3xl; + @apply px-6 py-2 bg-primary text-primary font-bold; + font-size: 2rem; + letter-spacing: 0.4px; } & .price { diff --git a/components/ui/Container/Container.tsx b/components/ui/Container/Container.tsx index fa3c725f0..b8e07ee07 100644 --- a/components/ui/Container/Container.tsx +++ b/components/ui/Container/Container.tsx @@ -8,7 +8,7 @@ interface Props { } const Container: FC = ({ children, className, el = 'div' }) => { - const rootClassName = cn('mx-auto max-w-7xl px-6 md:px-12', className) + const rootClassName = cn('mx-auto max-w-7xl px-3 md:px-6', className) let Component: React.ComponentType * { min-height: 325px; diff --git a/lib/bigcommerce/api/cart/index.ts b/lib/bigcommerce/api/cart/index.ts index f2f174b84..4f6062674 100644 --- a/lib/bigcommerce/api/cart/index.ts +++ b/lib/bigcommerce/api/cart/index.ts @@ -80,19 +80,19 @@ const cartApi: BigcommerceApiHandler = async ( // Create or add an item to the cart if (req.method === 'POST') { - const body = { cartId, ...req.body } + const body = { ...req.body, cartId } return await handlers['addItem']({ req, res, config, body }) } // Update item in cart if (req.method === 'PUT') { - const body = { cartId, ...req.body } + const body = { ...req.body, cartId } return await handlers['updateItem']({ req, res, config, body }) } // Remove an item from the cart if (req.method === 'DELETE') { - const body = { cartId, ...req.body } + const body = { ...req.body, cartId } return await handlers['removeItem']({ req, res, config, body }) } } catch (error) { diff --git a/lib/bigcommerce/api/customers/handlers/create-customer.ts b/lib/bigcommerce/api/customers/handlers/create-customer.ts deleted file mode 100644 index a666467ab..000000000 --- a/lib/bigcommerce/api/customers/handlers/create-customer.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { BigcommerceApiError } from '../../utils/errors' -import login from '../../operations/login' -import type { CustomersHandlers } from '..' - -const createCustomer: CustomersHandlers['createCustomer'] = async ({ - res, - body: { firstName, lastName, email, password }, - config, -}) => { - // TODO: Add proper validations with something like Ajv - if (!(firstName && lastName && email && password)) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - // TODO: validate the password and email - // Passwords must be at least 7 characters and contain both alphabetic - // and numeric characters. - - let result: { data?: any } = {} - - try { - result = await config.storeApiFetch('/v3/customers', { - method: 'POST', - body: JSON.stringify([ - { - first_name: firstName, - last_name: lastName, - email, - authentication: { - new_password: password, - }, - }, - ]), - }) - } catch (error) { - if (error instanceof BigcommerceApiError && error.status === 422) { - const hasEmailError = '0.email' in error.data?.errors - - // If there's an error with the email, it most likely means it's duplicated - if (hasEmailError) { - return res.status(400).json({ - data: null, - errors: [ - { - message: 'The email is already in use', - code: 'duplicated_email', - }, - ], - }) - } - } - - throw error - } - - console.log('DATA', result.data) - - const loginData = await login({ variables: { email, password }, config }) - - console.log('LOGIN DATA', loginData) - - res.status(200).json({ data: result.data ?? null }) -} - -export default createCustomer diff --git a/lib/bigcommerce/api/customers/handlers/get-logged-in-customer.ts b/lib/bigcommerce/api/customers/handlers/get-logged-in-customer.ts new file mode 100644 index 000000000..99e9191f5 --- /dev/null +++ b/lib/bigcommerce/api/customers/handlers/get-logged-in-customer.ts @@ -0,0 +1,44 @@ +import { GetLoggedInCustomerQuery } from '@lib/bigcommerce/schema' +import type { CustomersHandlers } from '..' + +export const getLoggedInCustomerQuery = /* GraphQL */ ` + query getLoggedInCustomer { + customer { + entityId + firstName + lastName + email + company + customerGroupId + notes + phone + addressCount + attributeCount + storeCredit { + value + currencyCode + } + } + } +` + +const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({ + res, + config, +}) => { + const data = await config.fetch( + getLoggedInCustomerQuery + ) + const { customer } = data + + if (!customer) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Customer not found', code: 'not_found' }], + }) + } + + res.status(200).json({ data: { customer } }) +} + +export default getLoggedInCustomer diff --git a/lib/bigcommerce/api/customers/handlers/login.ts b/lib/bigcommerce/api/customers/handlers/login.ts new file mode 100644 index 000000000..787c055f8 --- /dev/null +++ b/lib/bigcommerce/api/customers/handlers/login.ts @@ -0,0 +1,26 @@ +import login from '../../operations/login' +import type { LoginHandlers } from '../login' + +const loginHandler: LoginHandlers['login'] = async ({ + res, + body: { email, password }, + config, +}) => { + // TODO: Add proper validations with something like Ajv + if (!(email && password)) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + // TODO: validate the password and email + // Passwords must be at least 7 characters and contain both alphabetic + // and numeric characters. + + // TODO: Currently not working, fix this asap. + const loginData = await login({ variables: { email, password }, config }) + + res.status(200).json({ data: null }) +} + +export default loginHandler diff --git a/lib/bigcommerce/api/customers/handlers/signup.ts b/lib/bigcommerce/api/customers/handlers/signup.ts new file mode 100644 index 000000000..911919088 --- /dev/null +++ b/lib/bigcommerce/api/customers/handlers/signup.ts @@ -0,0 +1,68 @@ +import { BigcommerceApiError } from '../../utils/errors' +import login from '../../operations/login' +import { SignupHandlers } from '../signup' + +const signup: SignupHandlers['signup'] = async ({ + res, + body: { firstName, lastName, email, password }, + config, +}) => { + // TODO: Add proper validations with something like Ajv + if (!(firstName && lastName && email && password)) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + // TODO: validate the password and email + // Passwords must be at least 7 characters and contain both alphabetic + // and numeric characters. + + let result: { data?: any } = {} + + // try { + // result = await config.storeApiFetch('/v3/customers', { + // method: 'POST', + // body: JSON.stringify([ + // { + // first_name: firstName, + // last_name: lastName, + // email, + // authentication: { + // new_password: password, + // }, + // }, + // ]), + // }) + // } catch (error) { + // if (error instanceof BigcommerceApiError && error.status === 422) { + // const hasEmailError = '0.email' in error.data?.errors + + // // If there's an error with the email, it most likely means it's duplicated + // if (hasEmailError) { + // return res.status(400).json({ + // data: null, + // errors: [ + // { + // message: 'The email is already in use', + // code: 'duplicated_email', + // }, + // ], + // }) + // } + // } + + // throw error + // } + + console.log('DATA', result.data) + + // TODO: Currently not working, fix this asap. + const loginData = await login({ variables: { email, password }, config }) + + console.log('LOGIN DATA', loginData) + + res.status(200).json({ data: result.data ?? null }) +} + +export default signup diff --git a/lib/bigcommerce/api/customers/index.ts b/lib/bigcommerce/api/customers/index.ts index c39f55f6a..228e882e7 100644 --- a/lib/bigcommerce/api/customers/index.ts +++ b/lib/bigcommerce/api/customers/index.ts @@ -4,42 +4,30 @@ import createApiHandler, { } from '../utils/create-api-handler' import isAllowedMethod from '../utils/is-allowed-method' import { BigcommerceApiError } from '../utils/errors' -import createCustomer from './handlers/create-customer' +import getLoggedInCustomer from './handlers/get-logged-in-customer' -type Body = Partial | undefined +export type Customer = any -export type Customer = null - -export type CreateCustomerBody = { - firstName: string - lastName: string - email: string - password: string +export type CustomerData = { + customer: Customer } export type CustomersHandlers = { - createCustomer: BigcommerceHandler< - Customer, - { cartId?: string } & Body - > + getLoggedInCustomer: BigcommerceHandler } -const METHODS = ['POST'] +const METHODS = ['GET'] -const customersApi: BigcommerceApiHandler = async ( - req, - res, - config -) => { +const customersApi: BigcommerceApiHandler< + CustomerData, + CustomersHandlers +> = async (req, res, config, handlers) => { if (!isAllowedMethod(req, res, METHODS)) return - const { cookies } = req - const cartId = cookies[config.cartCookie] - try { - if (req.method === 'POST') { - const body = { cartId, ...req.body } - return await handlers['createCustomer']({ req, res, config, body }) + if (req.method === 'GET') { + const body = null + return await handlers['getLoggedInCustomer']({ req, res, config, body }) } } catch (error) { console.error(error) @@ -53,6 +41,6 @@ const customersApi: BigcommerceApiHandler = async ( } } -const handlers = { createCustomer } +const handlers = { getLoggedInCustomer } export default createApiHandler(customersApi, handlers, {}) diff --git a/lib/bigcommerce/api/customers/login.ts b/lib/bigcommerce/api/customers/login.ts new file mode 100644 index 000000000..e8f24a92d --- /dev/null +++ b/lib/bigcommerce/api/customers/login.ts @@ -0,0 +1,45 @@ +import createApiHandler, { + BigcommerceApiHandler, + BigcommerceHandler, +} from '../utils/create-api-handler' +import isAllowedMethod from '../utils/is-allowed-method' +import { BigcommerceApiError } from '../utils/errors' +import login from './handlers/login' + +export type LoginBody = { + email: string + password: string +} + +export type LoginHandlers = { + login: BigcommerceHandler> +} + +const METHODS = ['POST'] + +const loginApi: BigcommerceApiHandler = async ( + req, + res, + config, + handlers +) => { + if (!isAllowedMethod(req, res, METHODS)) return + + try { + const body = req.body ?? {} + return await handlers['login']({ 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 }] }) + } +} + +const handlers = { login } + +export default createApiHandler(loginApi, handlers, {}) diff --git a/lib/bigcommerce/api/customers/signup.ts b/lib/bigcommerce/api/customers/signup.ts new file mode 100644 index 000000000..aa26f78cf --- /dev/null +++ b/lib/bigcommerce/api/customers/signup.ts @@ -0,0 +1,50 @@ +import createApiHandler, { + BigcommerceApiHandler, + BigcommerceHandler, +} from '../utils/create-api-handler' +import isAllowedMethod from '../utils/is-allowed-method' +import { BigcommerceApiError } from '../utils/errors' +import signup from './handlers/signup' + +export type SignupBody = { + firstName: string + lastName: string + email: string + password: string +} + +export type SignupHandlers = { + signup: BigcommerceHandler> +} + +const METHODS = ['POST'] + +const signupApi: BigcommerceApiHandler = async ( + req, + res, + config, + handlers +) => { + if (!isAllowedMethod(req, res, METHODS)) return + + const { cookies } = req + const cartId = cookies[config.cartCookie] + + try { + const body = { ...req.body, cartId } + return await handlers['signup']({ 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 }] }) + } +} + +const handlers = { signup } + +export default createApiHandler(signupApi, handlers, {}) diff --git a/lib/bigcommerce/api/utils/fetch-graphql-api.ts b/lib/bigcommerce/api/utils/fetch-graphql-api.ts index 1e79f7c2f..1ab323832 100644 --- a/lib/bigcommerce/api/utils/fetch-graphql-api.ts +++ b/lib/bigcommerce/api/utils/fetch-graphql-api.ts @@ -20,6 +20,8 @@ export default async function fetchGraphqlApi( }), }) + // console.log('HEADERS', getRawHeaders(res)) + const json = await res.json() if (json.errors) { console.error(json.errors) @@ -27,3 +29,13 @@ export default async function fetchGraphqlApi( } return json.data } + +function getRawHeaders(res: Response) { + const headers: { [key: string]: string } = {} + + res.headers.forEach((value, key) => { + headers[key] = value + }) + + return headers +} diff --git a/lib/bigcommerce/api/wishlist/handlers/get-all-wishlists.ts b/lib/bigcommerce/api/wishlist/handlers/get-all-wishlists.ts index 310c7f41d..30fb83c2f 100644 --- a/lib/bigcommerce/api/wishlist/handlers/get-all-wishlists.ts +++ b/lib/bigcommerce/api/wishlist/handlers/get-all-wishlists.ts @@ -1,5 +1,4 @@ -import { BigcommerceApiError } from '../../utils/errors' -import type { WishlistList, WishlistHandlers } from '..' +import type { Wishlist, WishlistHandlers } from '..' // Return all wishlists const getAllWishlists: WishlistHandlers['getAllWishlists'] = async ({ @@ -7,10 +6,12 @@ const getAllWishlists: WishlistHandlers['getAllWishlists'] = async ({ body: { customerId }, config, }) => { - let result: { data?: WishlistList } = {} + let result: { data?: Wishlist[] } = {} try { - result = await config.storeApiFetch(`/v3/wishlists/customer_id=${customerId}`) + result = await config.storeApiFetch( + `/v3/wishlists/customer_id=${customerId}` + ) } catch (error) { throw error } diff --git a/lib/bigcommerce/api/wishlist/index.ts b/lib/bigcommerce/api/wishlist/index.ts index 912d59491..3bcba106b 100644 --- a/lib/bigcommerce/api/wishlist/index.ts +++ b/lib/bigcommerce/api/wishlist/index.ts @@ -44,10 +44,8 @@ export type Wishlist = { // TODO: add missing fields } -export type WishlistList = Wishlist[] - export type WishlistHandlers = { - getAllWishlists: BigcommerceHandler + getAllWishlists: BigcommerceHandler getWishlist: BigcommerceHandler addWishlist: BigcommerceHandler< Wishlist, @@ -57,7 +55,10 @@ export type WishlistHandlers = { Wishlist, { wishlistId: string } & Body > - addItem: BigcommerceHandler> + addItem: BigcommerceHandler< + Wishlist, + { wishlistId: string } & Body + > removeItem: BigcommerceHandler< Wishlist, { wishlistId: string } & Body @@ -86,13 +87,13 @@ const wishlistApi: BigcommerceApiHandler = async ( // Add an item to the wishlist if (req.method === 'POST' && wishlistId) { - const body = { wishlistId, ...req.body } + const body = { ...req.body, wishlistId } return await handlers['addItem']({ req, res, config, body }) } // Update a wishlist if (req.method === 'PUT' && wishlistId) { - const body = { wishlistId, ...req.body } + const body = { ...req.body, wishlistId } return await handlers['updateWishlist']({ req, res, config, body }) } diff --git a/lib/bigcommerce/cart/use-add-item.tsx b/lib/bigcommerce/cart/use-add-item.tsx index a18a7b651..fa8bb8b46 100644 --- a/lib/bigcommerce/cart/use-add-item.tsx +++ b/lib/bigcommerce/cart/use-add-item.tsx @@ -26,8 +26,8 @@ export const fetcher: HookFetcher = ( } return fetch({ - url: options?.url ?? defaultOpts.url, - method: options?.method ?? defaultOpts.method, + ...defaultOpts, + ...options, body: { item }, }) } diff --git a/lib/bigcommerce/cart/use-cart.tsx b/lib/bigcommerce/cart/use-cart.tsx index 8fe80747a..7ed6ba7d8 100644 --- a/lib/bigcommerce/cart/use-cart.tsx +++ b/lib/bigcommerce/cart/use-cart.tsx @@ -5,6 +5,7 @@ import type { Cart } from '../api/cart' const defaultOpts = { url: '/api/bigcommerce/cart', + method: 'GET', } export type { Cart } @@ -14,12 +15,7 @@ export const fetcher: HookFetcher = ( { cartId }, fetch ) => { - return cartId - ? fetch({ - url: options?.url, - query: options?.query, - }) - : null + return cartId ? fetch({ ...defaultOpts, ...options }) : null } export function extendHook( diff --git a/lib/bigcommerce/cart/use-remove-item.tsx b/lib/bigcommerce/cart/use-remove-item.tsx index 386462c7f..c07aa718e 100644 --- a/lib/bigcommerce/cart/use-remove-item.tsx +++ b/lib/bigcommerce/cart/use-remove-item.tsx @@ -19,8 +19,8 @@ export const fetcher: HookFetcher = ( fetch ) => { return fetch({ - url: options?.url ?? defaultOpts.url, - method: options?.method ?? defaultOpts.method, + ...defaultOpts, + ...options, body: { itemId }, }) } diff --git a/lib/bigcommerce/cart/use-update-item.tsx b/lib/bigcommerce/cart/use-update-item.tsx index 78dd1bb84..bcad7ab3a 100644 --- a/lib/bigcommerce/cart/use-update-item.tsx +++ b/lib/bigcommerce/cart/use-update-item.tsx @@ -28,8 +28,8 @@ export const fetcher: HookFetcher = ( } return fetch({ - url: options?.url ?? defaultOpts.url, - method: options?.method ?? defaultOpts.method, + ...defaultOpts, + ...options, body: { itemId, item }, }) } diff --git a/lib/bigcommerce/schema.d.ts b/lib/bigcommerce/schema.d.ts index eae1c8187..361d45a30 100644 --- a/lib/bigcommerce/schema.d.ts +++ b/lib/bigcommerce/schema.d.ts @@ -1653,6 +1653,30 @@ export enum CurrencyCode { Zwr = 'ZWR', } +export type GetLoggedInCustomerQueryVariables = Exact<{ [key: string]: never }> + +export type GetLoggedInCustomerQuery = { __typename?: 'Query' } & { + customer?: Maybe< + { __typename?: 'Customer' } & Pick< + Customer, + | 'entityId' + | 'firstName' + | 'lastName' + | 'email' + | 'company' + | 'customerGroupId' + | 'notes' + | 'phone' + | 'addressCount' + | 'attributeCount' + > & { + storeCredit: Array< + { __typename?: 'Money' } & Pick + > + } + > +} + export type CategoryTreeItemFragment = { __typename?: 'CategoryTreeItem' } & Pick< diff --git a/lib/bigcommerce/use-customer.tsx b/lib/bigcommerce/use-customer.tsx new file mode 100644 index 000000000..549fd3fa1 --- /dev/null +++ b/lib/bigcommerce/use-customer.tsx @@ -0,0 +1,41 @@ +import { ConfigInterface } from 'swr' +import { HookFetcher } from '@lib/commerce/utils/types' +import useCommerceCustomer, { CustomerInput } from '@lib/commerce/use-customer' +import type { Customer, CustomerData } from './api/customers' + +const defaultOpts = { + url: '/api/bigcommerce/customer', + method: 'GET', +} + +export type { Customer } + +export const fetcher: HookFetcher = ( + options, + { cartId }, + fetch +) => { + return cartId ? fetch({ ...defaultOpts, ...options }) : null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: ConfigInterface +) { + const useCustomer = () => { + const cart = useCommerceCustomer( + defaultOpts, + [], + customFetcher, + { revalidateOnFocus: false, ...swrOptions } + ) + + return cart + } + + useCustomer.extend = extendHook + + return useCustomer +} + +export default extendHook(fetcher) diff --git a/lib/bigcommerce/use-login.tsx b/lib/bigcommerce/use-login.tsx new file mode 100644 index 000000000..d2b86a827 --- /dev/null +++ b/lib/bigcommerce/use-login.tsx @@ -0,0 +1,49 @@ +import { useCallback } from 'react' +import { HookFetcher } from '@lib/commerce/utils/types' +import useCommerceLogin from '@lib/commerce/use-login' +import type { LoginBody } from './api/customers/login' + +const defaultOpts = { + url: '/api/bigcommerce/customers/login', + method: 'POST', +} + +export type LoginInput = LoginBody + +export const fetcher: HookFetcher = ( + options, + { email, password }, + fetch +) => { + if (!(email && password)) { + throw new Error( + 'A first name, last name, email and password are required to login' + ) + } + + return fetch({ + ...defaultOpts, + ...options, + body: { email, password }, + }) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useLogin = () => { + const fn = useCommerceLogin(defaultOpts, customFetcher) + + return useCallback( + async function login(input: LoginInput) { + const data = await fn(input) + return data + }, + [fn] + ) + } + + useLogin.extend = extendHook + + return useLogin +} + +export default extendHook(fetcher) diff --git a/lib/bigcommerce/use-signup.tsx b/lib/bigcommerce/use-signup.tsx new file mode 100644 index 000000000..68183aa6c --- /dev/null +++ b/lib/bigcommerce/use-signup.tsx @@ -0,0 +1,49 @@ +import { useCallback } from 'react' +import { HookFetcher } from '@lib/commerce/utils/types' +import useCommerceSignup from '@lib/commerce/use-signup' +import type { SignupBody } from './api/customers/signup' + +const defaultOpts = { + url: '/api/bigcommerce/customers/signup', + method: 'POST', +} + +export type SignupInput = SignupBody + +export const fetcher: HookFetcher = ( + options, + { firstName, lastName, email, password }, + fetch +) => { + if (!(firstName && lastName && email && password)) { + throw new Error( + 'A first name, last name, email and password are required to signup' + ) + } + + return fetch({ + ...defaultOpts, + ...options, + body: { firstName, lastName, email, password }, + }) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useSignup = () => { + const fn = useCommerceSignup(defaultOpts, customFetcher) + + return useCallback( + async function signup(input: SignupInput) { + const data = await fn(input) + return data + }, + [fn] + ) + } + + useSignup.extend = extendHook + + return useSignup +} + +export default extendHook(fetcher) diff --git a/lib/bigcommerce/wishlist/use-add-item.tsx b/lib/bigcommerce/wishlist/use-add-item.tsx index 862041bbd..9bbbe009e 100644 --- a/lib/bigcommerce/wishlist/use-add-item.tsx +++ b/lib/bigcommerce/wishlist/use-add-item.tsx @@ -17,8 +17,8 @@ export const fetcher: HookFetcher = ( fetch ) => { return fetch({ - url: options?.url ?? defaultOpts.url, - method: options?.method ?? defaultOpts.method, + ...defaultOpts, + ...options, body: { wishlistId, item }, }) } diff --git a/lib/bigcommerce/wishlist/use-remove-item.tsx b/lib/bigcommerce/wishlist/use-remove-item.tsx index ca9648d35..05f9744cb 100644 --- a/lib/bigcommerce/wishlist/use-remove-item.tsx +++ b/lib/bigcommerce/wishlist/use-remove-item.tsx @@ -19,8 +19,8 @@ export const fetcher: HookFetcher = ( fetch ) => { return fetch({ - url: options?.url ?? defaultOpts.url, - method: options?.method ?? defaultOpts.method, + ...defaultOpts, + ...options, body: { wishlistId, itemId }, }) } diff --git a/lib/bigcommerce/wishlist/use-wishlist.tsx b/lib/bigcommerce/wishlist/use-wishlist.tsx index 1d93609f0..d60660c05 100644 --- a/lib/bigcommerce/wishlist/use-wishlist.tsx +++ b/lib/bigcommerce/wishlist/use-wishlist.tsx @@ -4,6 +4,7 @@ import type { Wishlist } from '../api/wishlist' const defaultOpts = { url: '/api/bigcommerce/wishlists', + method: 'GET', } export type { Wishlist } @@ -18,7 +19,8 @@ export const fetcher: HookFetcher = ( fetch ) => { return fetch({ - url: options?.url, + ...defaultOpts, + ...options, body: { wishlistId }, }) } diff --git a/lib/commerce/api/index.ts b/lib/commerce/api/index.ts index 6c4bf0544..38ed70d4a 100644 --- a/lib/commerce/api/index.ts +++ b/lib/commerce/api/index.ts @@ -14,8 +14,4 @@ export interface CommerceAPIFetchOptions { preview?: boolean } -// TODO: define interfaces for all the available operations - -// export interface CommerceAPI { -// getAllProducts(options?: { query: string }): Promise; -// } +// TODO: define interfaces for all the available operations and API endpoints diff --git a/lib/commerce/use-customer.tsx b/lib/commerce/use-customer.tsx new file mode 100644 index 000000000..e33587dcf --- /dev/null +++ b/lib/commerce/use-customer.tsx @@ -0,0 +1,30 @@ +import { responseInterface, ConfigInterface } from 'swr' +import Cookies from 'js-cookie' +import type { HookInput, HookFetcher, HookFetcherOptions } from './utils/types' +import useData from './utils/use-data' +import { useCommerce } from '.' + +export type CustomerResponse = responseInterface + +export type CustomerInput = { + cartId: string | undefined +} + +export default function useCustomer( + options: HookFetcherOptions, + input: HookInput, + fetcherFn: HookFetcher, + swrOptions?: ConfigInterface +) { + // TODO: Replace this with the login cookie + const { cartCookie } = useCommerce() + + const fetcher: typeof fetcherFn = (options, input, fetch) => { + input.cartId = Cookies.get(cartCookie) + return fetcherFn(options, input, fetch) + } + + const response = useData(options, input, fetcher, swrOptions) + + return response as CustomerResponse +} diff --git a/lib/commerce/use-login.tsx b/lib/commerce/use-login.tsx new file mode 100644 index 000000000..2a251fea3 --- /dev/null +++ b/lib/commerce/use-login.tsx @@ -0,0 +1,5 @@ +import useAction from './utils/use-action' + +const useLogin = useAction + +export default useLogin diff --git a/lib/commerce/use-signup.tsx b/lib/commerce/use-signup.tsx new file mode 100644 index 000000000..08ddb22c0 --- /dev/null +++ b/lib/commerce/use-signup.tsx @@ -0,0 +1,5 @@ +import useAction from './utils/use-action' + +const useSignup = useAction + +export default useSignup diff --git a/lib/commerce/utils/use-action.tsx b/lib/commerce/utils/use-action.tsx index ef68a7641..e60cae06b 100644 --- a/lib/commerce/utils/use-action.tsx +++ b/lib/commerce/utils/use-action.tsx @@ -9,9 +9,7 @@ export default function useAction( const { fetcherRef } = useCommerce() return useCallback( - function addItem(input: Input) { - return fetcher(options, input, fetcherRef.current) - }, + (input: Input) => fetcher(options, input, fetcherRef.current), [fetcher] ) } diff --git a/lib/commerce/utils/use-data.tsx b/lib/commerce/utils/use-data.tsx index fa2b6175c..c8e31657f 100644 --- a/lib/commerce/utils/use-data.tsx +++ b/lib/commerce/utils/use-data.tsx @@ -9,9 +9,15 @@ export default function useData( swrOptions?: ConfigInterface ) { const { fetcherRef } = useCommerce() - const fetcher = (url?: string, query?: string, ...args: any[]) => { + const fetcher = ( + url?: string, + query?: string, + method?: string, + ...args: any[] + ) => { return fetcherFn( - { url, query }, + { url, query, method }, + // Transform the input array into an object args.reduce((obj, val, i) => { obj[input[i][0]!] = val return obj @@ -23,7 +29,9 @@ export default function useData( const response = useSWR( () => { const opts = typeof options === 'function' ? options() : options - return opts ? [opts.url, opts.query, ...input.map((e) => e[1])] : null + return opts + ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] + : null }, fetcher, swrOptions diff --git a/package.json b/package.json index ed4c562c5..9365500a3 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "js-cookie": "^2.2.1", "lodash.debounce": "^4.0.8", "lodash.random": "^3.2.0", - "next": "^9.5.6-canary.4", + "next": "^9.5.6-canary.9", "next-seo": "^4.11.0", "next-themes": "^0.0.4", "nextjs-progressbar": "^0.0.6", diff --git a/pages/api/bigcommerce/customers.ts b/pages/api/bigcommerce/customers/index.ts similarity index 100% rename from pages/api/bigcommerce/customers.ts rename to pages/api/bigcommerce/customers/index.ts diff --git a/pages/api/bigcommerce/customers/login.ts b/pages/api/bigcommerce/customers/login.ts new file mode 100644 index 000000000..b64b2252a --- /dev/null +++ b/pages/api/bigcommerce/customers/login.ts @@ -0,0 +1,3 @@ +import loginApi from '@lib/bigcommerce/api/customers/login' + +export default loginApi() diff --git a/pages/api/bigcommerce/customers/signup.ts b/pages/api/bigcommerce/customers/signup.ts new file mode 100644 index 000000000..d8be5512f --- /dev/null +++ b/pages/api/bigcommerce/customers/signup.ts @@ -0,0 +1,3 @@ +import signupApi from '@lib/bigcommerce/api/customers/signup' + +export default signupApi() diff --git a/pages/search.tsx b/pages/search.tsx index be2b28ee2..15f1b1ba0 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -59,7 +59,7 @@ export default function Search({ return ( -
+
  • @@ -143,6 +143,7 @@ export default function Search({ {data.products.map(({ node }) => (