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 } = {}
try {
result = await config.storeApiFetch(`/v3/carts/${cartId}`)
} catch (error) {
if (error instanceof BigcommerceApiError && error.status === 404) {
// Remove the cookie if it exists but the cart wasn't found
res.setHeader('Set-Cookie', getCartCookie(config.cartCookie))
} else {
throw error
if (cartId) {
try {
result = await config.storeApiFetch(`/v3/carts/${cartId}`)
} catch (error) {
if (error instanceof BigcommerceApiError && error.status === 404) {
// Remove the cookie if it exists but the cart wasn't found
res.setHeader('Set-Cookie', getCartCookie(config.cartCookie))
} 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 '..'
export const getLoggedInCustomerQuery = /* GraphQL */ `
@ -22,23 +22,40 @@ export const getLoggedInCustomerQuery = /* GraphQL */ `
}
`
export type Customer = NonNullable<GetLoggedInCustomerQuery['customer']>
const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({
req,
res,
config,
}) => {
const { data } = await config.fetch<GetLoggedInCustomerQuery>(
getLoggedInCustomerQuery
)
const { customer } = data
const token = req.cookies[config.customerCookie]
if (!customer) {
return res.status(400).json({
data: null,
errors: [{ message: 'Customer not found', code: 'not_found' }],
})
if (token) {
const { data } = await config.fetch<GetLoggedInCustomerQuery>(
getLoggedInCustomerQuery,
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

View File

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

View File

@ -1,36 +1,33 @@
import { ConfigInterface } from 'swr'
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'
const defaultOpts = {
url: '/api/bigcommerce/customer',
url: '/api/bigcommerce/customers',
method: 'GET',
}
export type { Customer }
export const fetcher: HookFetcher<CustomerData | null, CustomerInput> = (
export const fetcher: HookFetcher<Customer | null> = async (
options,
{ cartId },
_,
fetch
) => {
return cartId ? fetch({ ...defaultOpts, ...options }) : null
const data = await fetch<CustomerData>({ ...defaultOpts, ...options })
return data.customer
}
export function extendHook(
customFetcher: typeof fetcher,
swrOptions?: ConfigInterface
swrOptions?: SwrOptions<Customer | null>
) {
const useCustomer = () => {
const cart = useCommerceCustomer<CustomerData | null>(
defaultOpts,
[],
customFetcher,
{ revalidateOnFocus: false, ...swrOptions }
)
return cart
return useCommerceCustomer(defaultOpts, [], customFetcher, {
revalidateOnFocus: false,
...swrOptions,
})
}
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 type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types'
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 { useCommerce } from '.'
export type CustomerResponse<T> = responseInterface<T, Error>
const useCustomer = useData
export type CustomerInput = {
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>
}
export default useCustomer

View File

@ -9,11 +9,11 @@ export type FetcherOptions = {
body?: any
}
export type HookFetcher<T, Input = null> = (
export type HookFetcher<Result, Input = null> = (
options: HookFetcherOptions | null,
input: Input,
fetch: Fetcher<T>
) => T | Promise<T>
fetch: <T = Result>(options: FetcherOptions) => Promise<T>
) => Result | Promise<Result>
export type HookFetcherOptions = {
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 { CommerceError } from './errors'
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),
input: HookInput,
fetcherFn: HookFetcher<T, Input>,
swrOptions?: ConfigInterface<T>
) {
fetcherFn: HookFetcher<Result, Input>,
swrOptions?: SwrOptions<Result, Input>
) => responseInterface<Result, CommerceError>
const useData: UseData = (options, input, fetcherFn, swrOptions) => {
const { fetcherRef } = useCommerce()
const fetcher = (
url?: string,
@ -25,7 +34,6 @@ export default function useData<T, Input = any>(
fetcherRef.current
)
}
const response = useSWR(
() => {
const opts = typeof options === 'function' ? options() : options
@ -39,3 +47,5 @@ export default function useData<T, Input = any>(
return response
}
export default useData

View File

@ -3,11 +3,15 @@ import { Layout } from '@components/core'
import { Logo, Modal, Button } from '@components/ui'
import useLogin from '@lib/bigcommerce/use-login'
import useLogout from '@lib/bigcommerce/use-logout'
import useCustomer from '@lib/bigcommerce/use-customer'
export default function Login() {
const signup = useSignup()
const login = useLogin()
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
const handleSignup = async () => {
// TODO: validate the password and email before calling the signup