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