diff --git a/assets/base.css b/assets/base.css index f854065ba..dfdaf1475 100644 --- a/assets/base.css +++ b/assets/base.css @@ -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; } } + diff --git a/assets/components.css b/assets/components.css index ebebcc238..8c4c5a357 100644 --- a/assets/components.css +++ b/assets/components.css @@ -1,3 +1,3 @@ .fit { min-height: calc(100vh - 88px); -} +} \ No newline at end of file diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index 351a117ec..f78aa1d01 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -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 = ({}) => { - const [bg] = useState(useMemo(() => getRandomPairOfColors, [])) let ref = useRef() as React.MutableRefObject - - useEffect(() => { - if (ref && ref.current) { - ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` - } - }, [bg]) + let { userAvatar } = useUserAvatar() return (
{/* Add an image - We're generating a gradient as placeholder */} diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index cafcdd1d3..4b838e1a4 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -27,7 +27,7 @@ const HomeAllProductsGrid: FC = ({ +
diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index f4376bbf3..2542e2b28 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -58,7 +58,7 @@ const Layout: FC = ({ } = useUI() const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { locale = 'en-US' } = useRouter() - const isWishlistEnabled = commerceFeatures.wishlist + const isWishlistEnabled = commerceFeatures?.wishlist return (
diff --git a/components/common/Searchbar/Searchbar.module.css b/components/common/Searchbar/Searchbar.module.css index 500483195..071a14ef0 100644 --- a/components/common/Searchbar/Searchbar.module.css +++ b/components/common/Searchbar/Searchbar.module.css @@ -7,7 +7,7 @@ } .input:focus { - @apply outline-none shadow-outline-2; + @apply outline-none shadow-outline-normal; } .iconContainer { diff --git a/components/common/UserNav/UserNav.module.css b/components/common/UserNav/UserNav.module.css index a319e3dac..cd1a6ce1f 100644 --- a/components/common/UserNav/UserNav.module.css +++ b/components/common/UserNav/UserNav.module.css @@ -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 { diff --git a/components/product/ProductCard/ProductCard.module.css b/components/product/ProductCard/ProductCard.module.css index 082b22e56..1484cfaa4 100644 --- a/components/product/ProductCard/ProductCard.module.css +++ b/components/product/ProductCard/ProductCard.module.css @@ -132,5 +132,5 @@ } .productImage { - @apply transform transition-transform duration-500 object-cover; + @apply transform transition-transform duration-500 object-cover scale-120; } diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index b97937cd6..a9eaf8568 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -28,8 +28,8 @@ const ProductCard: FC = ({ {variant === 'slim' ? (
-
- +
+ {product.name}
diff --git a/components/product/ProductSlider/ProductSlider.module.css b/components/product/ProductSlider/ProductSlider.module.css index 5ad48cc3d..259d15801 100644 --- a/components/product/ProductSlider/ProductSlider.module.css +++ b/components/product/ProductSlider/ProductSlider.module.css @@ -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 { diff --git a/components/ui/Button/Button.module.css b/components/ui/Button/Button.module.css index df2be8802..5b563f496 100644 --- a/components/ui/Button/Button.module.css +++ b/components/ui/Button/Button.module.css @@ -7,7 +7,7 @@ } .root:focus { - @apply shadow-outline outline-none; + @apply shadow-outline-normal outline-none; } .root[data-active] { diff --git a/components/ui/Input/Input.module.css b/components/ui/Input/Input.module.css index 9ace85277..9daee1418 100644 --- a/components/ui/Input/Input.module.css +++ b/components/ui/Input/Input.module.css @@ -3,5 +3,5 @@ } .root:focus { - @apply outline-none shadow-outline-gray; + @apply outline-none shadow-outline-normal; } diff --git a/components/ui/context.tsx b/components/ui/context.tsx index b5321ae50..013589941 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -127,6 +127,12 @@ function uiReducer(state: State, action: Action) { toastText: action.text, } } + case 'SET_USER_AVATAR': { + return { + ...state, + userAvatar: action.value, + } + } } } diff --git a/framework/bigcommerce/api/utils/fetch-graphql-api.ts b/framework/bigcommerce/api/utils/fetch-graphql-api.ts index aa211dd88..a449b81e0 100644 --- a/framework/bigcommerce/api/utils/fetch-graphql-api.ts +++ b/framework/bigcommerce/api/utils/fetch-graphql-api.ts @@ -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, diff --git a/framework/bigcommerce/product/get-product.ts b/framework/bigcommerce/product/get-product.ts index 794d89bdf..7d77eb194 100644 --- a/framework/bigcommerce/product/get-product.ts +++ b/framework/bigcommerce/product/get-product.ts @@ -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 */ ` diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts index 0f6660cd5..efeb809f1 100644 --- a/framework/shopify/api/utils/fetch-all-products.ts +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -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) { diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index a78eeed74..92d4f2cf6 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -29,6 +29,6 @@ const fetchGraphqlApi: GraphQLFetcher = async ( throw getError(errors, status) } - return { data: data, res } + return { data, res } } export default fetchGraphqlApi diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 4811bbe8d..9ed8d404f 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -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 -export const fetcher: HookFetcher = ( - 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 = { + 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( - 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) diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx index f8bd55579..d5bf5f6b0 100644 --- a/framework/shopify/auth/use-logout.tsx +++ b/framework/shopify/auth/use-logout.tsx @@ -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 -export const fetcher: HookFetcher = (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 = { + 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(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) diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index c1b40e60c..7ad9e996d 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -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 -export const fetcher: HookFetcher = ( - 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( - 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) diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 11771f1b3..36f02847b 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -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 +export default useAddItem as UseAddItem -export const handler: MutationHandler = { +export const handler: MutationHook = { 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 = { }) } - const { checkoutLineItemsAdd }: Mutation = await fetch({ + 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] + ) }, } diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index f5749731f..2cf3a3e95 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -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 -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( () => diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx index 5c2c2dbee..1963176c8 100644 --- a/framework/shopify/cart/use-remove-item.tsx +++ b/framework/shopify/cart/use-remove-item.tsx @@ -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 extends LineItem ? (input?: RemoveItemInput) => Promise : (input: RemoveItemInput) => Promise export type RemoveItemInput = T extends LineItem - ? Partial - : UseRemoveItemInput + ? Partial + : RemoveItemInputBase -export const fetcher: HookFetcher = async ( - options, - { itemId, checkoutId }, - fetch -) => { - const data = await fetch({ - ...defaultOpts, - ...options, - variables: { lineItemIds: [itemId], checkoutId }, - }) - return checkoutToCart(data.checkoutLineItemsRemove) -} +export default useRemoveItem as UseRemoveItem -export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = ( - item?: T +export const handler = { + fetchOptions: { + query: checkoutLineItemRemoveMutation, + }, + async fetcher({ + input: { itemId }, + options, + fetch, + }: HookFetcherContext) { + const data = await fetch({ + ...options, + variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] }, + }) + return checkoutToCart(data.checkoutLineItemsRemove) + }, + useHook: ({ + fetch, + }: MutationHookContext) => < + T extends LineItem | undefined = undefined + >( + ctx: { item?: T } = {} ) => { - const { mutate, data: cart } = useCart() - const fn = useCartRemoveItem(defaultOpts, customFetcher) + const { item } = ctx + const { mutate } = useCart() const removeItem: RemoveItemFn = 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, [fn, mutate]) - } - - useRemoveItem.extend = extendHook - - return useRemoveItem + return useCallback(removeItem as RemoveItemFn, [fetch, mutate]) + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx index 2b5e7c776..9e89d0aca 100644 --- a/framework/shopify/cart/use-update-item.tsx +++ b/framework/shopify/cart/use-update-item.tsx @@ -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 extends LineItem - ? Partial> - : UseUpdateItemInput + ? Partial> + : UpdateItemInputBase -export const fetcher: HookFetcher = 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 + +export const handler = { + fetchOptions: { + query: checkoutLineItemUpdateMutation, + }, + async fetcher({ + input: { itemId, item }, + options, + fetch, + }: HookFetcherContext) { + 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({ - ...defaultOpts, - ...options, - variables: { checkoutId, lineItems: [item] }, - }) - return checkoutToCart(data.checkoutLineItemsUpdate) -} - -function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { - const useUpdateItem = ( - item?: T + return checkoutToCart(checkoutLineItemsUpdate) + }, + useHook: ({ + fetch, + }: MutationHookContext) => < + T extends LineItem | undefined = undefined + >( + ctx: { + item?: T + wait?: number + } = {} ) => { - const { mutate, data: cart } = useCart() - const fn = useCartUpdateItem(defaultOpts, customFetcher) + const { item } = ctx + const { mutate } = useCart() as any return useCallback( debounce(async (input: UpdateItemInput) => { 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) diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 9975befe8..0e71be62f 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -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) { diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index 42d797652..662db1c45 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -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): Cart => { - if (!checkoutPayload || !checkoutPayload?.checkout) { + if (!checkoutPayload) { throw new CommerceError({ message: 'Invalid response from Shopify', }) @@ -28,6 +30,12 @@ const checkoutToCart = (checkoutPayload?: Maybe): Cart => { }) } + if (!checkout) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + return normalizeCart(checkout) } diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts index 372860734..a69492f0d 100644 --- a/framework/shopify/cart/utils/fetcher.ts +++ b/framework/shopify/cart/utils/fetcher.ts @@ -17,7 +17,7 @@ const fetcher: HookFetcherFn = async ({ checkoutId, }, }) - checkout = data?.node + checkout = data.node } if (checkout?.completedAt || !checkoutId) { diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts index 0f2b4a6ca..20d04955d 100644 --- a/framework/shopify/cart/utils/index.ts +++ b/framework/shopify/cart/utils/index.ts @@ -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' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 6cec2ef73..082950634 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -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, diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index 803272918..524cece77 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -24,7 +24,7 @@ const getPage = async (options: { variables, }) - const page: Page = data?.pageByHandle + const page: Page = data.pageByHandle return { page: page diff --git a/framework/shopify/config.json b/framework/shopify/config.json new file mode 100644 index 000000000..17ef37e25 --- /dev/null +++ b/framework/shopify/config.json @@ -0,0 +1,5 @@ +{ + "features": { + "wishlist": false + } +} diff --git a/framework/shopify/customer/get-customer-id.ts b/framework/shopify/customer/get-customer-id.ts index 39a9e2572..78309a8ec 100644 --- a/framework/shopify/customer/get-customer-id.ts +++ b/framework/shopify/customer/get-customer-id.ts @@ -18,7 +18,7 @@ async function getCustomerId({ }, }) - return data?.customer?.id + return data.customer?.id } export default getCustomerId diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index 55151801b..91b7281af 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -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 - -export const handler: HookHandler = { +export const handler: SWRHook = { fetchOptions: { query: getCustomerQuery, }, async fetcher({ options, fetch }) { - const data = await fetch(options) - return data?.customer ?? null + const data = await fetch({ + ...options, + variables: { customerAccessToken: getCustomerToken() }, + }) + return data.customer ?? null }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input) => { return useData({ swrOptions: { revalidateOnFocus: false, - ...input.swrOptions, + ...input?.swrOptions, }, }) }, diff --git a/framework/shopify/product/get-all-collections.ts b/framework/shopify/product/get-all-collections.ts index b63adf159..bf3fee392 100644 --- a/framework/shopify/product/get-all-collections.ts +++ b/framework/shopify/product/get-all-collections.ts @@ -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) => ({ diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index 34480e90a..b777efc10 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -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) ) diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 191706123..33f38a427 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -27,7 +27,7 @@ const getProduct = async (options: { variables, }) - const product = data?.productByHandle + const { productByHandle: product } = data return { product: product ? normalizeProduct(product) : null, diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 174466fdb..7ca37916b 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -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 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], diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts index f11e9ff3f..383822baa 100644 --- a/framework/shopify/provider.ts +++ b/framework/shopify/provider.ts @@ -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 diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts index 3319d827d..942ec9c62 100644 --- a/framework/shopify/utils/get-categories.ts +++ b/framework/shopify/utils/get-categories.ts @@ -16,7 +16,7 @@ const getCategories = async (config: ShopifyConfig): Promise => { }) return ( - data?.collections?.edges?.map( + data.collections?.edges?.map( ({ node: { title: name, handle } }: CollectionEdge) => ({ entityId: handle, name, diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts index 70725b75b..77b6873e3 100644 --- a/framework/shopify/utils/handle-login.ts +++ b/framework/shopify/utils/handle-login.ts @@ -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) { diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts index 99aa9be68..2d59aa506 100644 --- a/framework/shopify/utils/index.ts +++ b/framework/shopify/utils/index.ts @@ -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' diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index c9c6ee100..3a16d7cec 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -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' diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index b582ca3bb..f25542329 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -1,4 +1,4 @@ -export const checkoutDetailsFragment = /* GraphQL */ ` +export const checkoutDetailsFragment = ` id webUrl subtotalPrice diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts index f41cb2797..4f10d7b65 100644 --- a/framework/shopify/utils/queries/index.ts +++ b/framework/shopify/utils/queries/index.ts @@ -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' diff --git a/lib/hooks/useUserAvatar.ts b/lib/hooks/useUserAvatar.ts new file mode 100644 index 000000000..840daae6d --- /dev/null +++ b/lib/hooks/useUserAvatar.ts @@ -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, + } +} diff --git a/lib/logger.ts b/lib/logger.ts deleted file mode 100644 index eeda2c325..000000000 --- a/lib/logger.ts +++ /dev/null @@ -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 diff --git a/next.config.js b/next.config.js index abdfe251c..3c9e37210 100644 --- a/next.config.js +++ b/next.config.js @@ -34,7 +34,4 @@ module.exports = { }, ] }, - typescript: { - ignoreBuildErrors: true, - }, } diff --git a/pages/api/bigcommerce/customers/index.ts b/pages/api/bigcommerce/customers/index.ts index 911a4521a..7b55d3aa8 100644 --- a/pages/api/bigcommerce/customers/index.ts +++ b/pages/api/bigcommerce/customers/index.ts @@ -1,3 +1,3 @@ -import customersApi from '@framework/api/customer' +import customersApi from '@framework/api/customers' export default customersApi() diff --git a/pages/index.tsx b/pages/index.tsx index 8f6ce5f2a..acb1474be 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -36,7 +36,7 @@ export async function getStaticProps({ wishlist: isWishlistEnabled, }, }, - revalidate: 1440, + revalidate: 14400, } } diff --git a/pages/search.tsx b/pages/search.tsx index 7c0a4e140..c9958a9f8 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -106,7 +106,7 @@ export default function Search({