4
0
forked from crowetic/commerce

Updated types, hooks, useCustomer sample

This commit is contained in:
Luis Alvarez 2020-10-23 19:21:37 -05:00
parent 40049b27a1
commit 444456d376
9 changed files with 83 additions and 78 deletions

View File

@ -10,14 +10,16 @@ const getCart: CartHandlers['getCart'] = async ({
}) => { }) => {
let result: { data?: Cart } = {} let result: { data?: Cart } = {}
try { if (cartId) {
result = await config.storeApiFetch(`/v3/carts/${cartId}`) try {
} catch (error) { result = await config.storeApiFetch(`/v3/carts/${cartId}`)
if (error instanceof BigcommerceApiError && error.status === 404) { } catch (error) {
// Remove the cookie if it exists but the cart wasn't found if (error instanceof BigcommerceApiError && error.status === 404) {
res.setHeader('Set-Cookie', getCartCookie(config.cartCookie)) // Remove the cookie if it exists but the cart wasn't found
} else { res.setHeader('Set-Cookie', getCartCookie(config.cartCookie))
throw error } else {
throw error
}
} }
} }

View File

@ -1,4 +1,4 @@
import { GetLoggedInCustomerQuery } from '@lib/bigcommerce/schema' import type { GetLoggedInCustomerQuery } from '@lib/bigcommerce/schema'
import type { CustomersHandlers } from '..' import type { CustomersHandlers } from '..'
export const getLoggedInCustomerQuery = /* GraphQL */ ` export const getLoggedInCustomerQuery = /* GraphQL */ `
@ -22,23 +22,40 @@ export const getLoggedInCustomerQuery = /* GraphQL */ `
} }
` `
export type Customer = NonNullable<GetLoggedInCustomerQuery['customer']>
const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({ const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({
req,
res, res,
config, config,
}) => { }) => {
const { data } = await config.fetch<GetLoggedInCustomerQuery>( const token = req.cookies[config.customerCookie]
getLoggedInCustomerQuery
)
const { customer } = data
if (!customer) { if (token) {
return res.status(400).json({ const { data } = await config.fetch<GetLoggedInCustomerQuery>(
data: null, getLoggedInCustomerQuery,
errors: [{ message: 'Customer not found', code: 'not_found' }], undefined,
}) {
headers: {
cookie: `${config.customerCookie}=${token}`,
},
}
)
const { customer } = data
console.log('CUSTOMER', customer)
if (!customer) {
return res.status(400).json({
data: null,
errors: [{ message: 'Customer not found', code: 'not_found' }],
})
}
return res.status(200).json({ data: { customer } })
} }
res.status(200).json({ data: { customer } }) res.status(200).json({ data: null })
} }
export default getLoggedInCustomer export default getLoggedInCustomer

View File

@ -4,16 +4,18 @@ import createApiHandler, {
} from '../utils/create-api-handler' } from '../utils/create-api-handler'
import isAllowedMethod from '../utils/is-allowed-method' import isAllowedMethod from '../utils/is-allowed-method'
import { BigcommerceApiError } from '../utils/errors' import { BigcommerceApiError } from '../utils/errors'
import getLoggedInCustomer from './handlers/get-logged-in-customer' import getLoggedInCustomer, {
Customer,
} from './handlers/get-logged-in-customer'
export type Customer = any export type { Customer }
export type CustomerData = { export type CustomerData = {
customer: Customer customer: Customer
} }
export type CustomersHandlers = { export type CustomersHandlers = {
getLoggedInCustomer: BigcommerceHandler<CustomerData, null> getLoggedInCustomer: BigcommerceHandler<CustomerData>
} }
const METHODS = ['GET'] const METHODS = ['GET']
@ -25,10 +27,8 @@ const customersApi: BigcommerceApiHandler<
if (!isAllowedMethod(req, res, METHODS)) return if (!isAllowedMethod(req, res, METHODS)) return
try { try {
if (req.method === 'GET') { const body = null
const body = null return await handlers['getLoggedInCustomer']({ req, res, config, body })
return await handlers['getLoggedInCustomer']({ req, res, config, body })
}
} catch (error) { } catch (error) {
console.error(error) console.error(error)

View File

@ -1,36 +1,33 @@
import { ConfigInterface } from 'swr'
import { HookFetcher } from '@lib/commerce/utils/types' import { HookFetcher } from '@lib/commerce/utils/types'
import useCommerceCustomer, { CustomerInput } from '@lib/commerce/use-customer' import type { SwrOptions } from '@lib/commerce/utils/use-data'
import useCommerceCustomer from '@lib/commerce/use-customer'
import type { Customer, CustomerData } from './api/customers' import type { Customer, CustomerData } from './api/customers'
const defaultOpts = { const defaultOpts = {
url: '/api/bigcommerce/customer', url: '/api/bigcommerce/customers',
method: 'GET', method: 'GET',
} }
export type { Customer } export type { Customer }
export const fetcher: HookFetcher<CustomerData | null, CustomerInput> = ( export const fetcher: HookFetcher<Customer | null> = async (
options, options,
{ cartId }, _,
fetch fetch
) => { ) => {
return cartId ? fetch({ ...defaultOpts, ...options }) : null const data = await fetch<CustomerData>({ ...defaultOpts, ...options })
return data.customer
} }
export function extendHook( export function extendHook(
customFetcher: typeof fetcher, customFetcher: typeof fetcher,
swrOptions?: ConfigInterface swrOptions?: SwrOptions<Customer | null>
) { ) {
const useCustomer = () => { const useCustomer = () => {
const cart = useCommerceCustomer<CustomerData | null>( return useCommerceCustomer(defaultOpts, [], customFetcher, {
defaultOpts, revalidateOnFocus: false,
[], ...swrOptions,
customFetcher, })
{ revalidateOnFocus: false, ...swrOptions }
)
return cart
} }
useCustomer.extend = extendHook useCustomer.extend = extendHook

View File

@ -1,4 +1,4 @@
import { responseInterface, ConfigInterface } from 'swr' import type { responseInterface, ConfigInterface } from 'swr'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types'
import useData from '../utils/use-data' import useData from '../utils/use-data'

View File

@ -1,30 +1,5 @@
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 useData from './utils/use-data'
import { useCommerce } from '.'
export type CustomerResponse<T> = responseInterface<T, Error> const useCustomer = useData
export type CustomerInput = { export default useCustomer
cartId: string | undefined
}
export default function useCustomer<T>(
options: HookFetcherOptions,
input: HookInput,
fetcherFn: HookFetcher<T | null, CustomerInput>,
swrOptions?: ConfigInterface<T | null>
) {
// 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<T>
}

View File

@ -9,11 +9,11 @@ export type FetcherOptions = {
body?: any body?: any
} }
export type HookFetcher<T, Input = null> = ( export type HookFetcher<Result, Input = null> = (
options: HookFetcherOptions | null, options: HookFetcherOptions | null,
input: Input, input: Input,
fetch: Fetcher<T> fetch: <T = Result>(options: FetcherOptions) => Promise<T>
) => T | Promise<T> ) => Result | Promise<Result>
export type HookFetcherOptions = { export type HookFetcherOptions = {
query?: string query?: string

View File

@ -1,13 +1,22 @@
import useSWR, { ConfigInterface } from 'swr' import useSWR, { ConfigInterface, responseInterface } from 'swr'
import type { HookInput, HookFetcher, HookFetcherOptions } from './types' import type { HookInput, HookFetcher, HookFetcherOptions } from './types'
import { CommerceError } from './errors'
import { useCommerce } from '..' import { useCommerce } from '..'
export default function useData<T, Input = any>( export type SwrOptions<Result, Input = null> = ConfigInterface<
Result,
CommerceError,
HookFetcher<Result, Input>
>
export type UseData = <Result = any, Input = null>(
options: HookFetcherOptions | (() => HookFetcherOptions | null), options: HookFetcherOptions | (() => HookFetcherOptions | null),
input: HookInput, input: HookInput,
fetcherFn: HookFetcher<T, Input>, fetcherFn: HookFetcher<Result, Input>,
swrOptions?: ConfigInterface<T> swrOptions?: SwrOptions<Result, Input>
) { ) => responseInterface<Result, CommerceError>
const useData: UseData = (options, input, fetcherFn, swrOptions) => {
const { fetcherRef } = useCommerce() const { fetcherRef } = useCommerce()
const fetcher = ( const fetcher = (
url?: string, url?: string,
@ -25,7 +34,6 @@ export default function useData<T, Input = any>(
fetcherRef.current fetcherRef.current
) )
} }
const response = useSWR( const response = useSWR(
() => { () => {
const opts = typeof options === 'function' ? options() : options const opts = typeof options === 'function' ? options() : options
@ -39,3 +47,5 @@ export default function useData<T, Input = any>(
return response return response
} }
export default useData

View File

@ -3,11 +3,15 @@ import { Layout } from '@components/core'
import { Logo, Modal, Button } from '@components/ui' import { Logo, Modal, Button } from '@components/ui'
import useLogin from '@lib/bigcommerce/use-login' import useLogin from '@lib/bigcommerce/use-login'
import useLogout from '@lib/bigcommerce/use-logout' import useLogout from '@lib/bigcommerce/use-logout'
import useCustomer from '@lib/bigcommerce/use-customer'
export default function Login() { export default function Login() {
const signup = useSignup() const signup = useSignup()
const login = useLogin() const login = useLogin()
const logout = useLogout() const logout = useLogout()
// Data about the currently logged in customer, it will update
// automatically after a signup/login/logout
const { data } = useCustomer()
// TODO: use this method. It can take more than 5 seconds to do a signup // TODO: use this method. It can take more than 5 seconds to do a signup
const handleSignup = async () => { const handleSignup = async () => {
// TODO: validate the password and email before calling the signup // TODO: validate the password and email before calling the signup