mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
Moved auth & cart hooks + several fixes
This commit is contained in:
parent
005fe9d6c9
commit
528d7556a8
@ -3,7 +3,6 @@
|
||||
--primary-2: #f1f3f5;
|
||||
--secondary: #000000;
|
||||
--secondary-2: #111;
|
||||
|
||||
--selection: var(--cyan);
|
||||
|
||||
--text-base: #000000;
|
||||
@ -13,18 +12,14 @@
|
||||
--hover: rgba(0, 0, 0, 0.075);
|
||||
--hover-1: rgba(0, 0, 0, 0.15);
|
||||
--hover-2: rgba(0, 0, 0, 0.25);
|
||||
|
||||
--cyan: #22b8cf;
|
||||
--green: #37b679;
|
||||
--red: #da3c3c;
|
||||
--pink: #e64980;
|
||||
--purple: #f81ce5;
|
||||
|
||||
--blue: #0070f3;
|
||||
|
||||
--violet-light: #7048e8;
|
||||
--violet: #5f3dc4;
|
||||
|
||||
--violet-light: #7048e8;
|
||||
--accents-0: #f8f9fa;
|
||||
--accents-1: #f1f3f5;
|
||||
--accents-2: #e9ecef;
|
||||
@ -132,3 +127,4 @@ a {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
.fit {
|
||||
min-height: calc(100vh - 88px);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { FC, useState, useMemo, useRef, useEffect } from 'react'
|
||||
import { getRandomPairOfColors } from '@lib/colors'
|
||||
import { FC, useRef, useEffect } from 'react'
|
||||
import { useUserAvatar } from '@lib/hooks/useUserAvatar'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@ -7,18 +7,13 @@ interface Props {
|
||||
}
|
||||
|
||||
const Avatar: FC<Props> = ({}) => {
|
||||
const [bg] = useState(useMemo(() => getRandomPairOfColors, []))
|
||||
let ref = useRef() as React.MutableRefObject<HTMLInputElement>
|
||||
|
||||
useEffect(() => {
|
||||
if (ref && ref.current) {
|
||||
ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)`
|
||||
}
|
||||
}, [bg])
|
||||
let { userAvatar } = useUserAvatar()
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={{ backgroundImage: userAvatar }}
|
||||
className="inline-block h-8 w-8 rounded-full border-2 border-primary hover:border-secondary focus:border-secondary transition linear-out duration-150"
|
||||
>
|
||||
{/* Add an image - We're generating a gradient as placeholder <img></img> */}
|
||||
|
@ -27,7 +27,7 @@ const HomeAllProductsGrid: FC<Props> = ({
|
||||
<ul className="mb-10">
|
||||
<li className="py-1 text-base font-bold tracking-wide">
|
||||
<Link href={getCategoryPath('')}>
|
||||
<a>All Collections</a>
|
||||
<a>All Categories</a>
|
||||
</Link>
|
||||
</li>
|
||||
{categories.map((cat: any) => (
|
||||
@ -38,6 +38,20 @@ const HomeAllProductsGrid: FC<Props> = ({
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="">
|
||||
<li className="py-1 text-base font-bold tracking-wide">
|
||||
<Link href={getDesignerPath('')}>
|
||||
<a>All Designers</a>
|
||||
</Link>
|
||||
</li>
|
||||
{brands.flatMap(({ node }: any) => (
|
||||
<li key={node.path} className="py-1 text-accents-8 text-base">
|
||||
<Link href={getDesignerPath(node.path)}>
|
||||
<a>{node.name}</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
|
@ -58,7 +58,7 @@ const Layout: FC<Props> = ({
|
||||
} = useUI()
|
||||
const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
|
||||
const { locale = 'en-US' } = useRouter()
|
||||
const isWishlistEnabled = commerceFeatures.wishlist
|
||||
const isWishlistEnabled = commerceFeatures?.wishlist
|
||||
return (
|
||||
<CommerceProvider locale={locale}>
|
||||
<div className={cn(s.root)}>
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
@apply outline-none shadow-outline-2;
|
||||
@apply outline-none shadow-outline-normal;
|
||||
}
|
||||
|
||||
.iconContainer {
|
||||
|
@ -24,7 +24,11 @@
|
||||
}
|
||||
|
||||
.bagCount {
|
||||
@apply border border-accents-1 bg-secondary text-secondary h-4 w-4 absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs;
|
||||
@apply border border-accents-1 bg-secondary text-secondary absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs;
|
||||
padding-left: 2.5px;
|
||||
padding-right: 2.5px;
|
||||
min-width: 1.25rem;
|
||||
min-height: 1.25rem;
|
||||
}
|
||||
|
||||
.avatarButton {
|
||||
|
@ -132,5 +132,5 @@
|
||||
}
|
||||
|
||||
.productImage {
|
||||
@apply transform transition-transform duration-500 object-cover;
|
||||
@apply transform transition-transform duration-500 object-cover scale-120;
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ const ProductCard: FC<Props> = ({
|
||||
<a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}>
|
||||
{variant === 'slim' ? (
|
||||
<div className="relative overflow-hidden box-border">
|
||||
<div className="absolute inset-0 flex items-start justify-end m-1 z-20">
|
||||
<span className="text-black inline-block p-3 font-bold text-xl break-words">
|
||||
<div className="absolute inset-0 flex items-center justify-end mr-8 z-20">
|
||||
<span className="bg-black text-white inline-block p-3 font-bold text-xl break-words">
|
||||
{product.name}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
.leftControl:hover,
|
||||
.rightControl:hover {
|
||||
@apply outline-none shadow-outline-blue;
|
||||
@apply outline-none shadow-outline-normal;
|
||||
}
|
||||
|
||||
.leftControl {
|
||||
@ -70,7 +70,7 @@
|
||||
}
|
||||
|
||||
.positionIndicator:focus .dot {
|
||||
@apply shadow-outline-blue;
|
||||
@apply shadow-outline-normal;
|
||||
}
|
||||
|
||||
.positionIndicatorActive .dot {
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.root:focus {
|
||||
@apply shadow-outline outline-none;
|
||||
@apply shadow-outline-normal outline-none;
|
||||
}
|
||||
|
||||
.root[data-active] {
|
||||
|
@ -3,5 +3,5 @@
|
||||
}
|
||||
|
||||
.root:focus {
|
||||
@apply outline-none shadow-outline-gray;
|
||||
@apply outline-none shadow-outline-normal;
|
||||
}
|
||||
|
@ -127,6 +127,12 @@ function uiReducer(state: State, action: Action) {
|
||||
toastText: action.text,
|
||||
}
|
||||
}
|
||||
case 'SET_USER_AVATAR': {
|
||||
return {
|
||||
...state,
|
||||
userAvatar: action.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
{ variables, preview } = {},
|
||||
fetchOptions
|
||||
) => {
|
||||
// log.warn(query)
|
||||
const config = getConfig()
|
||||
const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
|
||||
...fetchOptions,
|
||||
|
@ -2,7 +2,7 @@ import type { GetProductQuery, GetProductQueryVariables } from '../schema'
|
||||
import setProductLocaleMeta from '../api/utils/set-product-locale-meta'
|
||||
import { productInfoFragment } from '../api/fragments/product'
|
||||
import { BigcommerceConfig, getConfig } from '../api'
|
||||
import { normalizeProduct } from '@framework/utils/normalize'
|
||||
import { normalizeProduct } from '@framework/lib/normalize'
|
||||
import type { Product } from '@commerce/types'
|
||||
|
||||
export const getProductQuery = /* GraphQL */ `
|
||||
|
@ -18,8 +18,8 @@ const fetchAllProducts = async ({
|
||||
variables: { ...variables, cursor },
|
||||
})
|
||||
|
||||
const edges: ProductEdge[] = data?.products?.edges ?? []
|
||||
const hasNextPage = data?.products?.pageInfo?.hasNextPage
|
||||
const edges: ProductEdge[] = data.products?.edges ?? []
|
||||
const hasNextPage = data.products?.pageInfo?.hasNextPage
|
||||
acc = acc.concat(edges)
|
||||
|
||||
if (hasNextPage) {
|
||||
|
@ -29,6 +29,6 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
throw getError(errors, status)
|
||||
}
|
||||
|
||||
return { data: data, res }
|
||||
return { data, res }
|
||||
}
|
||||
export default fetchGraphqlApi
|
||||
|
@ -1,56 +1,80 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError, ValidationError } from '@commerce/utils/errors'
|
||||
import useCommerceLogin from '@commerce/use-login'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create'
|
||||
import { CustomerAccessTokenCreateInput } from '@framework/schema'
|
||||
import handleLogin from '@framework/utils/handle-login'
|
||||
import {
|
||||
CustomerAccessToken,
|
||||
CustomerAccessTokenCreateInput,
|
||||
CustomerAccessTokenCreatePayload,
|
||||
CustomerUserError,
|
||||
Mutation,
|
||||
MutationCheckoutCreateArgs,
|
||||
} from '@framework/schema'
|
||||
import useLogin, { UseLogin } from '@commerce/use-login'
|
||||
import { setCustomerToken } from '@framework/utils'
|
||||
|
||||
const defaultOpts = {
|
||||
query: createCustomerAccessTokenMutation,
|
||||
}
|
||||
export default useLogin as UseLogin<typeof handler>
|
||||
|
||||
export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = (
|
||||
options,
|
||||
input,
|
||||
fetch
|
||||
) => {
|
||||
if (!(input.email && input.password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to login',
|
||||
})
|
||||
const getErrorMessage = ({ code, message }: CustomerUserError) => {
|
||||
console.log(code)
|
||||
|
||||
switch (code) {
|
||||
case 'UNIDENTIFIED_CUSTOMER':
|
||||
message = 'Cannot find an account that matches the provided credentials'
|
||||
break
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
variables: { input },
|
||||
}).then(handleLogin)
|
||||
return message
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useLogin = () => {
|
||||
export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
|
||||
fetchOptions: {
|
||||
query: createCustomerAccessTokenMutation,
|
||||
},
|
||||
async fetcher({ input: { email, password }, options, fetch }) {
|
||||
if (!(email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to login',
|
||||
})
|
||||
}
|
||||
|
||||
const { customerAccessTokenCreate } = await fetch<
|
||||
Mutation,
|
||||
MutationCheckoutCreateArgs
|
||||
>({
|
||||
...options,
|
||||
variables: {
|
||||
input: { email, password },
|
||||
},
|
||||
})
|
||||
|
||||
const errors = customerAccessTokenCreate?.customerUserErrors
|
||||
|
||||
if (errors && errors.length) {
|
||||
throw new ValidationError({
|
||||
message: getErrorMessage(errors[0]),
|
||||
})
|
||||
}
|
||||
const customerAccessToken = customerAccessTokenCreate?.customerAccessToken
|
||||
const accessToken = customerAccessToken?.accessToken
|
||||
|
||||
if (accessToken) {
|
||||
setCustomerToken(accessToken)
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { revalidate } = useCustomer()
|
||||
const fn = useCommerceLogin<null, CustomerAccessTokenCreateInput>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function login(input: CustomerAccessTokenCreateInput) {
|
||||
const data = await fn(input)
|
||||
async function login(input) {
|
||||
const data = await fetch({ input })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
[fetch, revalidate]
|
||||
)
|
||||
}
|
||||
|
||||
useLogin.extend = extendHook
|
||||
|
||||
return useLogin
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import useCommerceLogout from '@commerce/use-logout'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import useLogout, { UseLogout } from '@commerce/use-logout'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import customerAccessTokenDeleteMutation from '@framework/utils/mutations/customer-access-token-delete'
|
||||
import {
|
||||
@ -8,38 +8,32 @@ import {
|
||||
setCustomerToken,
|
||||
} from '@framework/utils/customer-token'
|
||||
|
||||
const defaultOpts = {
|
||||
query: customerAccessTokenDeleteMutation,
|
||||
}
|
||||
export default useLogout as UseLogout<typeof handler>
|
||||
|
||||
export const fetcher: HookFetcher<null> = (options, _, fetch) => {
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
variables: {
|
||||
customerAccessToken: getCustomerToken(),
|
||||
},
|
||||
}).then((d) => setCustomerToken(null))
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useLogout = () => {
|
||||
export const handler: MutationHook<null> = {
|
||||
fetchOptions: {
|
||||
query: customerAccessTokenDeleteMutation,
|
||||
},
|
||||
async fetcher({ options, fetch }) {
|
||||
await fetch({
|
||||
...options,
|
||||
variables: {
|
||||
customerAccessToken: getCustomerToken(),
|
||||
},
|
||||
})
|
||||
setCustomerToken(null)
|
||||
return null
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { mutate } = useCustomer()
|
||||
const fn = useCommerceLogout<null>(defaultOpts, customFetcher)
|
||||
|
||||
return useCallback(
|
||||
async function login() {
|
||||
const data = await fn(null)
|
||||
async function logout() {
|
||||
const data = await fetch()
|
||||
await mutate(null, false)
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
[fetch, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useLogout.extend = extendHook
|
||||
|
||||
return useLogout
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCommerceSignup from '@commerce/use-signup'
|
||||
import useSignup, { UseSignup } from '@commerce/use-signup'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import { CustomerCreateInput } from '@framework/schema'
|
||||
|
||||
@ -11,63 +11,64 @@ import {
|
||||
} from '@framework/utils/mutations'
|
||||
import handleLogin from '@framework/utils/handle-login'
|
||||
|
||||
const defaultOpts = {
|
||||
query: customerCreateMutation,
|
||||
}
|
||||
export default useSignup as UseSignup<typeof handler>
|
||||
|
||||
export const fetcher: HookFetcher<null, CustomerCreateInput> = (
|
||||
options,
|
||||
input,
|
||||
fetch
|
||||
) => {
|
||||
if (!(input.firstName && input.lastName && input.email && input.password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to signup',
|
||||
export const handler: MutationHook<
|
||||
null,
|
||||
{},
|
||||
CustomerCreateInput,
|
||||
CustomerCreateInput
|
||||
> = {
|
||||
fetchOptions: {
|
||||
query: customerCreateMutation,
|
||||
},
|
||||
async fetcher({
|
||||
input: { firstName, lastName, email, password },
|
||||
options,
|
||||
fetch,
|
||||
}) {
|
||||
if (!(firstName && lastName && email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to signup',
|
||||
})
|
||||
}
|
||||
const data = await fetch({
|
||||
...options,
|
||||
variables: {
|
||||
input: {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
variables: { input },
|
||||
}).then(async (data) => {
|
||||
|
||||
try {
|
||||
const loginData = await fetch({
|
||||
query: customerAccessTokenCreateMutation,
|
||||
variables: {
|
||||
input: {
|
||||
email: input.email,
|
||||
password: input.password,
|
||||
email,
|
||||
password,
|
||||
},
|
||||
},
|
||||
})
|
||||
handleLogin(loginData)
|
||||
} catch (error) {}
|
||||
return data
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useSignup = () => {
|
||||
},
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { revalidate } = useCustomer()
|
||||
const fn = useCommerceSignup<null, CustomerCreateInput>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function signup(input: CustomerCreateInput) {
|
||||
const data = await fn(input)
|
||||
async function signup(input) {
|
||||
const data = await fetch({ input })
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
[fetch, revalidate]
|
||||
)
|
||||
}
|
||||
|
||||
useSignup.extend = extendHook
|
||||
|
||||
return useSignup
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -1,22 +1,20 @@
|
||||
import type { MutationHandler } from '@commerce/utils/types'
|
||||
import type { MutationHook } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
||||
import useCart from './use-cart'
|
||||
import { ShopifyProvider } from '..'
|
||||
import { Cart, AddCartItemBody, CartItemBody } from '../types'
|
||||
import { Cart, CartItemBody } from '../types'
|
||||
import { checkoutLineItemAddMutation, getCheckoutId } from '../utils'
|
||||
import { checkoutToCart } from './utils'
|
||||
import { Mutation } from '../schema'
|
||||
import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export default useAddItem as UseAddItem<ShopifyProvider, CartItemBody>
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHandler<Cart, {}, AddCartItemBody> = {
|
||||
export const handler: MutationHook<Cart, {}, CartItemBody> = {
|
||||
fetchOptions: {
|
||||
query: checkoutLineItemAddMutation,
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {
|
||||
const item = input?.item ?? input
|
||||
|
||||
async fetcher({ input: item, options, fetch }) {
|
||||
if (
|
||||
item.quantity &&
|
||||
(!Number.isInteger(item.quantity) || item.quantity! < 1)
|
||||
@ -26,27 +24,34 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = {
|
||||
})
|
||||
}
|
||||
|
||||
const { checkoutLineItemsAdd }: Mutation = await fetch<any, any>({
|
||||
const { checkoutLineItemsAdd } = await fetch<
|
||||
Mutation,
|
||||
MutationCheckoutLineItemsAddArgs
|
||||
>({
|
||||
...options,
|
||||
variables: {
|
||||
checkoutId: getCheckoutId(),
|
||||
lineItems: [
|
||||
{
|
||||
variantId: item.variantId,
|
||||
quantity: item.quantity ?? 1,
|
||||
},
|
||||
],
|
||||
checkoutId: getCheckoutId(),
|
||||
},
|
||||
})
|
||||
|
||||
return checkoutToCart(checkoutLineItemsAdd)
|
||||
},
|
||||
useHook() {
|
||||
useHook: ({ fetch }) => () => {
|
||||
const { mutate } = useCart()
|
||||
return async function addItem({ input, fetch }) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
|
||||
return useCallback(
|
||||
async function addItem(input) {
|
||||
const data = await fetch({ input })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
},
|
||||
[fetch, mutate]
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -7,14 +7,13 @@ import useCommerceCart, {
|
||||
} from '@commerce/cart/use-cart'
|
||||
|
||||
import { Cart } from '@commerce/types'
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
|
||||
import fetcher from './utils/fetcher'
|
||||
import getCheckoutQuery from '@framework/utils/queries/get-checkout-query'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import { checkoutCreate, checkoutToCart } from './utils'
|
||||
import getCheckoutQuery from '../utils/queries/get-checkout-query'
|
||||
|
||||
export default useCommerceCart as UseCart<ShopifyProvider>
|
||||
|
||||
export const handler: HookHandler<
|
||||
export const handler: SWRHook<
|
||||
Cart | null,
|
||||
{},
|
||||
FetchCartInput,
|
||||
@ -23,10 +22,27 @@ export const handler: HookHandler<
|
||||
fetchOptions: {
|
||||
query: getCheckoutQuery,
|
||||
},
|
||||
fetcher,
|
||||
useHook({ input, useData }) {
|
||||
async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
|
||||
let checkout
|
||||
if (checkoutId) {
|
||||
const data = await fetch({
|
||||
...options,
|
||||
variables: {
|
||||
checkoutId,
|
||||
},
|
||||
})
|
||||
checkout = data.node
|
||||
}
|
||||
|
||||
if (checkout?.completedAt || !checkoutId) {
|
||||
checkout = await checkoutCreate(fetch)
|
||||
}
|
||||
|
||||
return checkoutToCart({ checkout })
|
||||
},
|
||||
useHook: ({ useData }) => (input) => {
|
||||
const response = useData({
|
||||
swrOptions: { revalidateOnFocus: false, ...input.swrOptions },
|
||||
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
|
||||
})
|
||||
return useMemo(
|
||||
() =>
|
||||
|
@ -1,48 +1,61 @@
|
||||
import { useCallback } from 'react'
|
||||
import { HookFetcher } from '@commerce/utils/types'
|
||||
|
||||
import type {
|
||||
MutationHookContext,
|
||||
HookFetcherContext,
|
||||
} from '@commerce/utils/types'
|
||||
|
||||
import { ValidationError } from '@commerce/utils/errors'
|
||||
import useCartRemoveItem, {
|
||||
RemoveItemInput as UseRemoveItemInput,
|
||||
|
||||
import useRemoveItem, {
|
||||
RemoveItemInput as RemoveItemInputBase,
|
||||
UseRemoveItem,
|
||||
} from '@commerce/cart/use-remove-item'
|
||||
|
||||
import useCart from './use-cart'
|
||||
|
||||
import type { Cart, LineItem, RemoveCartItemBody } from '@commerce/types'
|
||||
import { checkoutLineItemRemoveMutation } from '@framework/utils/mutations'
|
||||
import getCheckoutId from '@framework/utils/get-checkout-id'
|
||||
import { checkoutLineItemRemoveMutation, getCheckoutId } from '@framework/utils'
|
||||
import { checkoutToCart } from './utils'
|
||||
|
||||
const defaultOpts = {
|
||||
query: checkoutLineItemRemoveMutation,
|
||||
}
|
||||
import { Cart, LineItem } from '@framework/types'
|
||||
import {
|
||||
Mutation,
|
||||
MutationCheckoutLineItemsRemoveArgs,
|
||||
} from '@framework/schema'
|
||||
import { RemoveCartItemBody } from '@commerce/types'
|
||||
|
||||
export type RemoveItemFn<T = any> = T extends LineItem
|
||||
? (input?: RemoveItemInput<T>) => Promise<Cart | null>
|
||||
: (input: RemoveItemInput<T>) => Promise<Cart | null>
|
||||
|
||||
export type RemoveItemInput<T = any> = T extends LineItem
|
||||
? Partial<UseRemoveItemInput>
|
||||
: UseRemoveItemInput
|
||||
? Partial<RemoveItemInputBase>
|
||||
: RemoveItemInputBase
|
||||
|
||||
export const fetcher: HookFetcher<Cart | null, any> = async (
|
||||
options,
|
||||
{ itemId, checkoutId },
|
||||
fetch
|
||||
) => {
|
||||
const data = await fetch<any>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
variables: { lineItemIds: [itemId], checkoutId },
|
||||
})
|
||||
return checkoutToCart(data.checkoutLineItemsRemove)
|
||||
}
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useRemoveItem = <T extends LineItem | undefined = undefined>(
|
||||
item?: T
|
||||
export const handler = {
|
||||
fetchOptions: {
|
||||
query: checkoutLineItemRemoveMutation,
|
||||
},
|
||||
async fetcher({
|
||||
input: { itemId },
|
||||
options,
|
||||
fetch,
|
||||
}: HookFetcherContext<RemoveCartItemBody>) {
|
||||
const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({
|
||||
...options,
|
||||
variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] },
|
||||
})
|
||||
return checkoutToCart(data.checkoutLineItemsRemove)
|
||||
},
|
||||
useHook: ({
|
||||
fetch,
|
||||
}: MutationHookContext<Cart | null, RemoveCartItemBody>) => <
|
||||
T extends LineItem | undefined = undefined
|
||||
>(
|
||||
ctx: { item?: T } = {}
|
||||
) => {
|
||||
const { mutate, data: cart } = useCart()
|
||||
const fn = useCartRemoveItem<Cart | null, any>(defaultOpts, customFetcher)
|
||||
const { item } = ctx
|
||||
const { mutate } = useCart()
|
||||
const removeItem: RemoveItemFn<LineItem> = async (input) => {
|
||||
const itemId = input?.id ?? item?.id
|
||||
|
||||
@ -52,21 +65,11 @@ export function extendHook(customFetcher: typeof fetcher) {
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fn({
|
||||
checkoutId: getCheckoutId(cart?.id),
|
||||
itemId,
|
||||
})
|
||||
|
||||
const data = await fetch({ input: { itemId } })
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}
|
||||
|
||||
return useCallback(removeItem as RemoveItemFn<T>, [fn, mutate])
|
||||
}
|
||||
|
||||
useRemoveItem.extend = extendHook
|
||||
|
||||
return useRemoveItem
|
||||
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -1,85 +1,110 @@
|
||||
import { useCallback } from 'react'
|
||||
import debounce from 'lodash.debounce'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type {
|
||||
HookFetcherContext,
|
||||
MutationHookContext,
|
||||
} from '@commerce/utils/types'
|
||||
import { ValidationError } from '@commerce/utils/errors'
|
||||
import useCartUpdateItem, {
|
||||
UpdateItemInput as UseUpdateItemInput,
|
||||
import useUpdateItem, {
|
||||
UpdateItemInput as UpdateItemInputBase,
|
||||
UseUpdateItem,
|
||||
} from '@commerce/cart/use-update-item'
|
||||
|
||||
import { fetcher as removeFetcher } from './use-remove-item'
|
||||
|
||||
import useCart from './use-cart'
|
||||
|
||||
import type { Cart, LineItem, UpdateCartItemBody } from '@commerce/types'
|
||||
import { handler as removeItemHandler } from './use-remove-item'
|
||||
import type { Cart, LineItem, UpdateCartItemBody } from '../types'
|
||||
import { checkoutToCart } from './utils'
|
||||
import checkoutLineItemUpdateMutation from '@framework/utils/mutations/checkout-line-item-update'
|
||||
import getCheckoutId from '@framework/utils/get-checkout-id'
|
||||
|
||||
const defaultOpts = {
|
||||
query: checkoutLineItemUpdateMutation,
|
||||
}
|
||||
import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils'
|
||||
import {
|
||||
Mutation,
|
||||
MutationCheckoutLineItemsUpdateArgs,
|
||||
} from '@framework/schema'
|
||||
|
||||
export type UpdateItemInput<T = any> = T extends LineItem
|
||||
? Partial<UseUpdateItemInput<LineItem>>
|
||||
: UseUpdateItemInput<LineItem>
|
||||
? Partial<UpdateItemInputBase<LineItem>>
|
||||
: UpdateItemInputBase<LineItem>
|
||||
|
||||
export const fetcher: HookFetcher<Cart | null, any> = async (
|
||||
options,
|
||||
{ item, checkoutId },
|
||||
fetch
|
||||
) => {
|
||||
if (Number.isInteger(item.quantity)) {
|
||||
// Also allow the update hook to remove an item if the quantity is lower than 1
|
||||
if (item.quantity! < 1) {
|
||||
return removeFetcher(null, { itemId: item.id, checkoutId }, fetch)
|
||||
export default useUpdateItem as UseUpdateItem<typeof handler>
|
||||
|
||||
export const handler = {
|
||||
fetchOptions: {
|
||||
query: checkoutLineItemUpdateMutation,
|
||||
},
|
||||
async fetcher({
|
||||
input: { itemId, item },
|
||||
options,
|
||||
fetch,
|
||||
}: HookFetcherContext<UpdateCartItemBody>) {
|
||||
if (Number.isInteger(item.quantity)) {
|
||||
// Also allow the update hook to remove an item if the quantity is lower than 1
|
||||
if (item.quantity! < 1) {
|
||||
return removeItemHandler.fetcher({
|
||||
options: removeItemHandler.fetchOptions,
|
||||
input: { itemId },
|
||||
fetch,
|
||||
})
|
||||
}
|
||||
} else if (item.quantity) {
|
||||
throw new ValidationError({
|
||||
message: 'The item quantity has to be a valid integer',
|
||||
})
|
||||
}
|
||||
} else if (item.quantity) {
|
||||
throw new ValidationError({
|
||||
message: 'The item quantity has to be a valid integer',
|
||||
const { checkoutLineItemsUpdate } = await fetch<
|
||||
Mutation,
|
||||
MutationCheckoutLineItemsUpdateArgs
|
||||
>({
|
||||
...options,
|
||||
variables: {
|
||||
checkoutId: getCheckoutId(),
|
||||
lineItems: [
|
||||
{
|
||||
id: itemId,
|
||||
quantity: item.quantity,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
const data = await fetch<any, any>({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
variables: { checkoutId, lineItems: [item] },
|
||||
})
|
||||
|
||||
return checkoutToCart(data.checkoutLineItemsUpdate)
|
||||
}
|
||||
|
||||
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
|
||||
const useUpdateItem = <T extends LineItem | undefined = undefined>(
|
||||
item?: T
|
||||
return checkoutToCart(checkoutLineItemsUpdate)
|
||||
},
|
||||
useHook: ({
|
||||
fetch,
|
||||
}: MutationHookContext<Cart | null, UpdateCartItemBody>) => <
|
||||
T extends LineItem | undefined = undefined
|
||||
>(
|
||||
ctx: {
|
||||
item?: T
|
||||
wait?: number
|
||||
} = {}
|
||||
) => {
|
||||
const { mutate, data: cart } = useCart()
|
||||
const fn = useCartUpdateItem<Cart | null, any>(defaultOpts, customFetcher)
|
||||
const { item } = ctx
|
||||
const { mutate } = useCart() as any
|
||||
|
||||
return useCallback(
|
||||
debounce(async (input: UpdateItemInput<T>) => {
|
||||
const itemId = input.id ?? item?.id
|
||||
const productId = input.productId ?? item?.productId
|
||||
const variantId = input.productId ?? item?.variantId
|
||||
|
||||
if (!itemId || !variantId) {
|
||||
if (!itemId || !productId || !variantId) {
|
||||
throw new ValidationError({
|
||||
message: 'Invalid input used for this operation',
|
||||
})
|
||||
}
|
||||
|
||||
const data = await fn({
|
||||
item: { id: itemId, variantId, quantity: input.quantity },
|
||||
checkoutId: getCheckoutId(cart?.id),
|
||||
const data = await fetch({
|
||||
input: {
|
||||
item: {
|
||||
productId,
|
||||
variantId,
|
||||
quantity: input.quantity,
|
||||
},
|
||||
itemId,
|
||||
},
|
||||
})
|
||||
|
||||
await mutate(data, false)
|
||||
return data
|
||||
}, cfg?.wait ?? 500),
|
||||
[fn, mutate]
|
||||
}, ctx.wait ?? 500),
|
||||
[fetch, mutate]
|
||||
)
|
||||
}
|
||||
|
||||
useUpdateItem.extend = extendHook
|
||||
|
||||
return useUpdateItem
|
||||
},
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -11,7 +11,7 @@ export const checkoutCreate = async (fetch: any) => {
|
||||
query: checkoutCreateMutation,
|
||||
})
|
||||
|
||||
const checkout = data?.checkoutCreate?.checkout
|
||||
const checkout = data.checkoutCreate?.checkout
|
||||
const checkoutId = checkout?.id
|
||||
|
||||
if (checkoutId) {
|
||||
|
@ -3,6 +3,7 @@ import { CommerceError, ValidationError } from '@commerce/utils/errors'
|
||||
|
||||
import {
|
||||
CheckoutLineItemsAddPayload,
|
||||
CheckoutLineItemsRemovePayload,
|
||||
CheckoutLineItemsUpdatePayload,
|
||||
Maybe,
|
||||
} from '@framework/schema'
|
||||
@ -11,9 +12,10 @@ import { normalizeCart } from '@framework/utils'
|
||||
export type CheckoutPayload =
|
||||
| CheckoutLineItemsAddPayload
|
||||
| CheckoutLineItemsUpdatePayload
|
||||
| CheckoutLineItemsRemovePayload
|
||||
|
||||
const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
|
||||
if (!checkoutPayload || !checkoutPayload?.checkout) {
|
||||
if (!checkoutPayload) {
|
||||
throw new CommerceError({
|
||||
message: 'Invalid response from Shopify',
|
||||
})
|
||||
@ -28,6 +30,12 @@ const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
|
||||
})
|
||||
}
|
||||
|
||||
if (!checkout) {
|
||||
throw new CommerceError({
|
||||
message: 'Invalid response from Shopify',
|
||||
})
|
||||
}
|
||||
|
||||
return normalizeCart(checkout)
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({
|
||||
checkoutId,
|
||||
},
|
||||
})
|
||||
checkout = data?.node
|
||||
checkout = data.node
|
||||
}
|
||||
|
||||
if (checkout?.completedAt || !checkoutId) {
|
||||
|
@ -1,3 +1,2 @@
|
||||
export { default as checkoutToCart } from './checkout-to-cart'
|
||||
export { default as checkoutCreate } from './checkout-create'
|
||||
export { default as fetcher } from './fetcher'
|
||||
|
@ -19,7 +19,7 @@ const getAllPages = async (options?: {
|
||||
config = getConfig(config)
|
||||
|
||||
const { data } = await config.fetch(getAllPagesQuery, { variables })
|
||||
const edges = data?.pages?.edges
|
||||
const edges = data.pages?.edges
|
||||
|
||||
const pages = edges?.map(({ node }: PageEdge) => ({
|
||||
...node,
|
||||
|
@ -24,7 +24,7 @@ const getPage = async (options: {
|
||||
variables,
|
||||
})
|
||||
|
||||
const page: Page = data?.pageByHandle
|
||||
const page: Page = data.pageByHandle
|
||||
|
||||
return {
|
||||
page: page
|
||||
|
5
framework/shopify/config.json
Normal file
5
framework/shopify/config.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"features": {
|
||||
"wishlist": false
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ async function getCustomerId({
|
||||
},
|
||||
})
|
||||
|
||||
return data?.customer?.id
|
||||
return data.customer?.id
|
||||
}
|
||||
|
||||
export default getCustomerId
|
||||
|
@ -1,24 +1,26 @@
|
||||
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
|
||||
import { Customer } from '@commerce/types'
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
import { getCustomerQuery } from '@framework/utils'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import { getCustomerQuery, getCustomerToken } from '../utils'
|
||||
import type { ShopifyProvider } from '..'
|
||||
|
||||
export default useCustomer as UseCustomer<ShopifyProvider>
|
||||
|
||||
export const handler: HookHandler<Customer | null> = {
|
||||
export const handler: SWRHook<Customer | null> = {
|
||||
fetchOptions: {
|
||||
query: getCustomerQuery,
|
||||
},
|
||||
async fetcher({ options, fetch }) {
|
||||
const data = await fetch<any | null>(options)
|
||||
return data?.customer ?? null
|
||||
const data = await fetch<any | null>({
|
||||
...options,
|
||||
variables: { customerAccessToken: getCustomerToken() },
|
||||
})
|
||||
return data.customer ?? null
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
useHook: ({ useData }) => (input) => {
|
||||
return useData({
|
||||
swrOptions: {
|
||||
revalidateOnFocus: false,
|
||||
...input.swrOptions,
|
||||
...input?.swrOptions,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@ -11,7 +11,7 @@ const getAllCollections = async (options?: {
|
||||
config = getConfig(config)
|
||||
|
||||
const { data } = await config.fetch(getAllCollectionsQuery, { variables })
|
||||
const edges = data?.collections?.edges ?? []
|
||||
const edges = data.collections?.edges ?? []
|
||||
|
||||
const categories = edges.map(
|
||||
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({
|
||||
|
@ -28,7 +28,7 @@ const getAllProducts = async (options: {
|
||||
{ variables }
|
||||
)
|
||||
|
||||
const products = data?.products?.edges?.map(({ node: p }: ProductEdge) =>
|
||||
const products = data.products?.edges?.map(({ node: p }: ProductEdge) =>
|
||||
normalizeProduct(p)
|
||||
)
|
||||
|
||||
|
@ -27,7 +27,7 @@ const getProduct = async (options: {
|
||||
variables,
|
||||
})
|
||||
|
||||
const product = data?.productByHandle
|
||||
const { productByHandle: product } = data
|
||||
|
||||
return {
|
||||
product: product ? normalizeProduct(product) : null,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import useSearch, { UseSearch } from '@commerce/products/use-search'
|
||||
import { SearchProductsData } from '@commerce/types'
|
||||
import { HookHandler } from '@commerce/utils/types'
|
||||
import { SWRHook } from '@commerce/utils/types'
|
||||
import useSearch, { UseSearch } from '@commerce/product/use-search'
|
||||
|
||||
import { ProductEdge } from '@framework/schema'
|
||||
import {
|
||||
getAllProductsQuery,
|
||||
@ -9,6 +9,8 @@ import {
|
||||
} from '@framework/utils'
|
||||
import type { ShopifyProvider } from '..'
|
||||
|
||||
import { Product } from '@commerce/types'
|
||||
|
||||
export default useSearch as UseSearch<ShopifyProvider>
|
||||
|
||||
export type SearchProductsInput = {
|
||||
@ -18,7 +20,11 @@ export type SearchProductsInput = {
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const handler: HookHandler<
|
||||
export type SearchProductsData = {
|
||||
products: Product[]
|
||||
found: boolean
|
||||
}
|
||||
export const handler: SWRHook<
|
||||
SearchProductsData,
|
||||
SearchProductsInput,
|
||||
SearchProductsInput
|
||||
@ -38,7 +44,7 @@ export const handler: HookHandler<
|
||||
found: !!edges?.length,
|
||||
}
|
||||
},
|
||||
useHook({ input, useData }) {
|
||||
useHook: ({ useData }) => (input = {}) => {
|
||||
return useData({
|
||||
input: [
|
||||
['search', input.search],
|
||||
|
@ -2,8 +2,16 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const'
|
||||
|
||||
import { handler as useCart } from './cart/use-cart'
|
||||
import { handler as useAddItem } from './cart/use-add-item'
|
||||
import { handler as useSearch } from './product/use-search'
|
||||
import { handler as useUpdateItem } from './cart/use-update-item'
|
||||
import { handler as useRemoveItem } from './cart/use-remove-item'
|
||||
|
||||
import { handler as useCustomer } from './customer/use-customer'
|
||||
import { handler as useSearch } from './product/use-search'
|
||||
|
||||
import { handler as useLogin } from './auth/use-login'
|
||||
import { handler as useLogout } from './auth/use-logout'
|
||||
import { handler as useSignup } from './auth/use-signup'
|
||||
|
||||
import fetcher from './fetcher'
|
||||
|
||||
export const shopifyProvider = {
|
||||
@ -11,9 +19,13 @@ export const shopifyProvider = {
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
storeDomain: STORE_DOMAIN,
|
||||
fetcher,
|
||||
cart: { useCart, useAddItem },
|
||||
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
||||
customer: { useCustomer },
|
||||
products: { useSearch },
|
||||
auth: { useLogin, useLogout, useSignup },
|
||||
features: {
|
||||
wishlist: false,
|
||||
},
|
||||
}
|
||||
|
||||
export type ShopifyProvider = typeof shopifyProvider
|
||||
|
@ -16,7 +16,7 @@ const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
|
||||
})
|
||||
|
||||
return (
|
||||
data?.collections?.edges?.map(
|
||||
data.collections?.edges?.map(
|
||||
({ node: { title: name, handle } }: CollectionEdge) => ({
|
||||
entityId: handle,
|
||||
name,
|
||||
|
@ -17,7 +17,7 @@ const getErrorMessage = ({
|
||||
}
|
||||
|
||||
const handleLogin = (data: any) => {
|
||||
const response = data?.customerAccessTokenCreate
|
||||
const response = data.customerAccessTokenCreate
|
||||
const errors = response?.customerUserErrors
|
||||
|
||||
if (errors && errors.length) {
|
||||
|
@ -4,7 +4,6 @@ export { default as getSortVariables } from './get-sort-variables'
|
||||
export { default as getVendors } from './get-vendors'
|
||||
export { default as getCategories } from './get-categories'
|
||||
export { default as getCheckoutId } from './get-checkout-id'
|
||||
|
||||
export * from './queries'
|
||||
export * from './mutations'
|
||||
export * from './normalize'
|
||||
|
@ -1,10 +1,7 @@
|
||||
export { default as createCustomerMutation } from './customer-create'
|
||||
export { default as checkoutCreateMutation } from './checkout-create'
|
||||
|
||||
export { default as checkoutLineItemAddMutation } from './checkout-line-item-add'
|
||||
export { default as checkoutLineItemUpdateMutation } from './checkout-create'
|
||||
export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove'
|
||||
|
||||
export { default as customerCreateMutation } from './customer-create'
|
||||
export { default as checkoutCreateMutation } from './checkout-create'
|
||||
export { default as checkoutLineItemAddMutation } from './checkout-line-item-add'
|
||||
export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-update'
|
||||
export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove'
|
||||
export { default as customerAccessTokenCreateMutation } from './customer-access-token-create'
|
||||
export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete'
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const checkoutDetailsFragment = /* GraphQL */ `
|
||||
export const checkoutDetailsFragment = `
|
||||
id
|
||||
webUrl
|
||||
subtotalPrice
|
||||
|
@ -7,4 +7,4 @@ export { default as getCollectionProductsQuery } from './get-collection-products
|
||||
export { default as getCheckoutQuery } from './get-checkout-query'
|
||||
export { default as getAllPagesQuery } from './get-all-pages-query'
|
||||
export { default as getPageQuery } from './get-page-query'
|
||||
export { default as getCustomerQuery } from './get-checkout-query'
|
||||
export { default as getCustomerQuery } from './get-customer-query'
|
||||
|
26
lib/hooks/useUserAvatar.ts
Normal file
26
lib/hooks/useUserAvatar.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import { getRandomPairOfColors } from '@lib/colors'
|
||||
|
||||
export const useUserAvatar = (name = 'userAvatar') => {
|
||||
const { userAvatar, setUserAvatar } = useUI()
|
||||
|
||||
useEffect(() => {
|
||||
if (!userAvatar && localStorage.getItem(name)) {
|
||||
// Get bg from localStorage and push it to the context.
|
||||
setUserAvatar(localStorage.getItem(name))
|
||||
}
|
||||
if (!localStorage.getItem(name)) {
|
||||
// bg not set locally, generating one, setting localStorage and context to persist.
|
||||
const bg = getRandomPairOfColors()
|
||||
const value = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)`
|
||||
localStorage.setItem(name, value)
|
||||
setUserAvatar(value)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
userAvatar,
|
||||
setUserAvatar,
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import bunyan from 'bunyan'
|
||||
import PrettyStream from 'bunyan-prettystream'
|
||||
|
||||
const prettyStdOut = new PrettyStream()
|
||||
|
||||
const log = bunyan.createLogger({
|
||||
name: 'Next.js - Commerce',
|
||||
level: 'debug',
|
||||
streams: [
|
||||
{
|
||||
level: 'debug',
|
||||
type: 'raw',
|
||||
stream: prettyStdOut,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default log
|
@ -34,7 +34,4 @@ module.exports = {
|
||||
},
|
||||
]
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
import customersApi from '@framework/api/customer'
|
||||
import customersApi from '@framework/api/customers'
|
||||
|
||||
export default customersApi()
|
||||
|
@ -36,7 +36,7 @@ export async function getStaticProps({
|
||||
wishlist: isWishlistEnabled,
|
||||
},
|
||||
},
|
||||
revalidate: 1440,
|
||||
revalidate: 14400,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ export default function Search({
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleClick(e, 'categories')}
|
||||
className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150"
|
||||
className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
@ -205,7 +205,7 @@ export default function Search({
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleClick(e, 'brands')}
|
||||
className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-900 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150"
|
||||
className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-900 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
@ -383,7 +383,7 @@ export default function Search({
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleClick(e, 'sort')}
|
||||
className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150"
|
||||
className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
|
@ -51,7 +51,7 @@ module.exports = {
|
||||
secondary: 'var(--text-secondary)',
|
||||
},
|
||||
boxShadow: {
|
||||
'outline-2': '0 0 0 2px var(--accents-2)',
|
||||
'outline-normal': '0 0 0 2px var(--accents-2)',
|
||||
magical:
|
||||
'rgba(0, 0, 0, 0.02) 0px 30px 30px, rgba(0, 0, 0, 0.03) 0px 0px 8px, rgba(0, 0, 0, 0.05) 0px 1px 0px',
|
||||
},
|
||||
@ -63,5 +63,4 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('@tailwindcss/ui')],
|
||||
}
|
||||
|
@ -22,8 +22,8 @@
|
||||
"@utils/*": ["utils/*"],
|
||||
"@commerce/*": ["framework/commerce/*"],
|
||||
"@commerce": ["framework/commerce"],
|
||||
"@framework/*": ["framework/shopify/*"],
|
||||
"@framework": ["framework/shopify"]
|
||||
"@framework/*": ["framework/bigcommerce/*"],
|
||||
"@framework": ["framework/bigcommerce"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
||||
|
@ -7257,4 +7257,4 @@ yn@3.1.1:
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
Loading…
x
Reference in New Issue
Block a user