From 027c4aca7034db8ba82cc874136e98e18c241330 Mon Sep 17 00:00:00 2001 From: mohammadou1 <47993969+mohammadou1@users.noreply.github.com> Date: Fri, 22 Jan 2021 19:59:25 +0300 Subject: [PATCH 01/10] @hotfix/fixing brand id in use-search (#174) to set searchParam brand, should check brandId and not categoryId Co-authored-by: mohux --- framework/bigcommerce/products/use-search.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/bigcommerce/products/use-search.tsx b/framework/bigcommerce/products/use-search.tsx index ade0bbca2..2b4ee7593 100644 --- a/framework/bigcommerce/products/use-search.tsx +++ b/framework/bigcommerce/products/use-search.tsx @@ -26,8 +26,7 @@ export const fetcher: HookFetcher = ( if (search) url.searchParams.set('search', search) if (Number.isInteger(categoryId)) url.searchParams.set('category', String(categoryId)) - if (Number.isInteger(categoryId)) - url.searchParams.set('brand', String(brandId)) + if (Number.isInteger(brandId)) url.searchParams.set('brand', String(brandId)) if (sort) url.searchParams.set('sort', sort) return fetch({ From 08a6b2efcfde665cea35cbcde38f2bd127ca8045 Mon Sep 17 00:00:00 2001 From: Leah Wagner Date: Thu, 28 Jan 2021 09:09:28 -0800 Subject: [PATCH 02/10] Issue/79 fallback image (#179) * Filter products from the homepage grid display when they do not have and image. Provide a fallback/placeholder image for the ProductCard for when a product does not have an image. * Avoid repeating placeholder image reference and move to a variable * Remove filter to avoid additional computations as per https://github.com/vercel/commerce/pull/80#discussion_r531142893. Placeholder images will display as fallback. * Filter products from the homepage grid display when they do not have and image. Provide a fallback/placeholder image for the ProductCard for when a product does not have an image. * Avoid repeating placeholder image reference and move to a variable * Remove filter to avoid additional computations as per https://github.com/vercel/commerce/pull/80#discussion_r531142893. Placeholder images will display as fallback. * Remove comment about filter. Code removed so no longer needed. Co-authored-by: B --- components/product/ProductCard/ProductCard.tsx | 5 +++-- pages/index.tsx | 1 + public/product-img-placeholder.svg | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 public/product-img-placeholder.svg diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 85e69b3f2..a63aacc6e 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -32,6 +32,7 @@ const ProductCard: FC = ({ imgLayout = 'responsive', }) => { const src = p.images.edges?.[0]?.node?.urlOriginal! + const placeholderImg = '/product-img-placeholder.svg'; const { price } = usePrice({ amount: p.prices?.price?.value, baseAmount: p.prices?.retailPrice?.value, @@ -58,7 +59,7 @@ const ProductCard: FC = ({ layout={imgLayout} loading={imgLoading} priority={imgPriority} - src={p.images.edges?.[0]?.node.urlOriginal!} + src={p.images.edges?.[0]?.node.urlOriginal! || placeholderImg} alt={p.images.edges?.[0]?.node.altText || 'Product Image'} /> @@ -81,7 +82,7 @@ const ProductCard: FC = ({
{p.name} { // Create a copy of products that we can mutate + // Filter products that do not have images const products = [...newestProducts] // If the lists of featured and best selling products don't have enough // products, then fill them with products from the products list, this diff --git a/public/product-img-placeholder.svg b/public/product-img-placeholder.svg new file mode 100644 index 000000000..fbb43bb62 --- /dev/null +++ b/public/product-img-placeholder.svg @@ -0,0 +1,7 @@ + + + + + + + From 3a7d5e24898465f9c2c26d7d1c131bf6be895851 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 29 Jan 2021 12:00:16 -0300 Subject: [PATCH 03/10] Latest Release (#187) * Remove duplicated css rules. (#185) * Fix typo in the Marquee component (#176) Co-authored-by: Hugo Lopes * Remove duplicated css rules. Fix invalid JSX props. Co-authored-by: Hugo Lopes * Fix the body scroll when the sidebar is open (#184) * Fix typo in the Marquee component (#176) Co-authored-by: Hugo Lopes * Fix the body scroll when the sidebar is open Co-authored-by: Hugo Lopes * Remove duplicate class in the I18nWidget comp (#183) * Fix typo in the Marquee component (#176) Co-authored-by: Hugo Lopes * Remove duplicate class name in the I18nWidget comp This PR removes a duplicate class name in the I18nWidget component. Co-authored-by: Hugo Lopes Co-authored-by: Hugo Lopes * add horizontal margin to pages when mobile screen (#180) * Add cart item options like color and size (#177) Co-authored-by: hlopes * Changes Co-authored-by: Hugo Lopes Co-authored-by: Hugo Lopes Co-authored-by: Jamie Isaksen Co-authored-by: Vinicius Zucatti <51221635+vczb@users.noreply.github.com> --- .gitignore | 1 + assets/base.css | 2 -- components/cart/CartItem/CartItem.tsx | 21 ++++++++++++++++--- .../common/I18nWidget/I18nWidget.module.css | 2 +- components/common/Layout/Layout.tsx | 8 +++---- .../product/ProductView/ProductView.tsx | 7 +++---- components/product/helpers.ts | 13 ++++++++---- components/ui/Marquee/Marquee.tsx | 4 ++-- components/ui/context.tsx | 1 + .../bigcommerce/api/cart/handlers/add-item.ts | 4 ++-- .../bigcommerce/api/cart/handlers/get-cart.ts | 2 +- .../api/cart/handlers/remove-item.ts | 2 +- .../api/cart/handlers/update-item.ts | 2 +- pages/[...pages].tsx | 2 +- pages/search.tsx | 12 +++++------ 15 files changed, 51 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index bcbf6047a..50d4285ba 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env.local .env.development.local .env.test.local diff --git a/assets/base.css b/assets/base.css index 781ebe0ac..f854065ba 100644 --- a/assets/base.css +++ b/assets/base.css @@ -102,8 +102,6 @@ a { } .animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-fill-mode: both; diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 283f3fa40..2769ac715 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -8,6 +8,13 @@ import useUpdateItem from '@framework/cart/use-update-item' import useRemoveItem from '@framework/cart/use-remove-item' import s from './CartItem.module.css' +type ItemOption = { + name: string, + nameId: number, + value: string, + valueId: number +} + const CartItem = ({ item, currencyCode, @@ -88,12 +95,20 @@ const CartItem = ({
{/** TODO: Replace this. No `path` found at Cart */} - + {item.name} - -
+ {item.options && item.options.length > 0 ? ( +
+ {item.options.map((option:ItemOption, i: number) => + + {option.value}{ i === item.options.length -1 ? "" : ", " } + + )} +
+ ) : null} +
diff --git a/components/common/I18nWidget/I18nWidget.module.css b/components/common/I18nWidget/I18nWidget.module.css index 07a1aeba7..b216f5706 100644 --- a/components/common/I18nWidget/I18nWidget.module.css +++ b/components/common/I18nWidget/I18nWidget.module.css @@ -29,7 +29,7 @@ } .item { - @apply flex cursor-pointer px-6 py-3 flex transition ease-in-out duration-150 text-primary leading-6 font-medium items-center; + @apply flex cursor-pointer px-6 py-3 transition ease-in-out duration-150 text-primary leading-6 font-medium items-center; text-transform: capitalize; } diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index ff8467a23..375473c04 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -61,16 +61,16 @@ const Layout: FC = ({ children, pageProps }) => {
{children}
- - - - {modalView === 'LOGIN_VIEW' && } {modalView === 'SIGNUP_VIEW' && } {modalView === 'FORGOT_VIEW' && } + + + + = ({ product }) => { size: null, color: null, }) - const variant = - getCurrentVariant(product, choices) || product.variants.edges?.[0] + const variant = getCurrentVariant(product, choices) const addToCart = async () => { setLoading(true) try { await addItem({ productId: product.entityId, - variantId: product.variants.edges?.[0]?.node.entityId!, + variantId: variant?.node.entityId!, }) openSidebar() setLoading(false) @@ -156,7 +155,7 @@ const ProductView: FC = ({ product }) => {
diff --git a/components/product/helpers.ts b/components/product/helpers.ts index eda8d434a..bcc90b9a8 100644 --- a/components/product/helpers.ts +++ b/components/product/helpers.ts @@ -32,8 +32,10 @@ export function getProductOptions(product: ProductNode) { export function getCurrentVariant(product: ProductNode, opts: SelectedOptions) { const variant = product.variants.edges?.find((edge) => { const { node } = edge ?? {} + const numberOfDefinedOpts = Object.values(opts).filter(value => value !== null).length; + const numberOfEdges = node?.productOptions?.edges?.length; - return Object.entries(opts).every(([key, value]) => + const isEdgeEqualToOption = ([key, value]:[string, string | null]) => node?.productOptions.edges?.find((edge) => { if ( edge?.node.__typename === 'MultipleChoiceOption' && @@ -43,9 +45,12 @@ export function getCurrentVariant(product: ProductNode, opts: SelectedOptions) { (valueEdge) => valueEdge?.node.label === value ) } - }) - ) + }); + + return numberOfDefinedOpts === numberOfEdges ? + Object.entries(opts).every(isEdgeEqualToOption) + : Object.entries(opts).some(isEdgeEqualToOption) }) - return variant + return variant ?? product.variants.edges?.[0] } diff --git a/components/ui/Marquee/Marquee.tsx b/components/ui/Marquee/Marquee.tsx index 09f562011..163f29a34 100644 --- a/components/ui/Marquee/Marquee.tsx +++ b/components/ui/Marquee/Marquee.tsx @@ -9,7 +9,7 @@ interface Props { variant?: 'primary' | 'secondary' } -const Maquee: FC = ({ +const Marquee: FC = ({ className = '', children, variant = 'primary', @@ -32,4 +32,4 @@ const Maquee: FC = ({ ) } -export default Maquee +export default Marquee diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 096e2f865..206573858 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -90,6 +90,7 @@ function uiReducer(state: State, action: Action) { return { ...state, displayModal: true, + displaySidebar: false, } } case 'CLOSE_MODAL': { diff --git a/framework/bigcommerce/api/cart/handlers/add-item.ts b/framework/bigcommerce/api/cart/handlers/add-item.ts index 25ae3880e..ca9de1ed5 100644 --- a/framework/bigcommerce/api/cart/handlers/add-item.ts +++ b/framework/bigcommerce/api/cart/handlers/add-item.ts @@ -26,8 +26,8 @@ const addItem: CartHandlers['addItem'] = async ({ }), } const { data } = cartId - ? await config.storeApiFetch(`/v3/carts/${cartId}/items`, options) - : await config.storeApiFetch('/v3/carts', options) + ? await config.storeApiFetch(`/v3/carts/${cartId}/items?include=line_items.physical_items.options`, options) + : await config.storeApiFetch('/v3/carts?include=line_items.physical_items.options', options) // Create or update the cart cookie res.setHeader( diff --git a/framework/bigcommerce/api/cart/handlers/get-cart.ts b/framework/bigcommerce/api/cart/handlers/get-cart.ts index 9fb42d730..703601cd8 100644 --- a/framework/bigcommerce/api/cart/handlers/get-cart.ts +++ b/framework/bigcommerce/api/cart/handlers/get-cart.ts @@ -12,7 +12,7 @@ const getCart: CartHandlers['getCart'] = async ({ if (cartId) { try { - result = await config.storeApiFetch(`/v3/carts/${cartId}`) + result = await config.storeApiFetch(`/v3/carts/${cartId}?include=line_items.physical_items.options`) } catch (error) { if (error instanceof BigcommerceApiError && error.status === 404) { // Remove the cookie if it exists but the cart wasn't found diff --git a/framework/bigcommerce/api/cart/handlers/remove-item.ts b/framework/bigcommerce/api/cart/handlers/remove-item.ts index 2ee5dbe16..e4b57a01b 100644 --- a/framework/bigcommerce/api/cart/handlers/remove-item.ts +++ b/framework/bigcommerce/api/cart/handlers/remove-item.ts @@ -15,7 +15,7 @@ const removeItem: CartHandlers['removeItem'] = async ({ } const result = await config.storeApiFetch<{ data: any } | null>( - `/v3/carts/${cartId}/items/${itemId}`, + `/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`, { method: 'DELETE' } ) const data = result?.data ?? null diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/cart/handlers/update-item.ts index c64c111df..73776ccd9 100644 --- a/framework/bigcommerce/api/cart/handlers/update-item.ts +++ b/framework/bigcommerce/api/cart/handlers/update-item.ts @@ -16,7 +16,7 @@ const updateItem: CartHandlers['updateItem'] = async ({ } const { data } = await config.storeApiFetch( - `/v3/carts/${cartId}/items/${itemId}`, + `/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`, { method: 'PUT', body: JSON.stringify({ diff --git a/pages/[...pages].tsx b/pages/[...pages].tsx index 6caac1720..66a6be90e 100644 --- a/pages/[...pages].tsx +++ b/pages/[...pages].tsx @@ -65,7 +65,7 @@ export default function Pages({ page, }: InferGetStaticPropsType) { return ( -
+
{page?.body && }
) diff --git a/pages/search.tsx b/pages/search.tsx index 5110aba55..88a6354cd 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -106,9 +106,9 @@ export default function Search({ fill="currentColor" > @@ -205,9 +205,9 @@ export default function Search({ fill="currentColor" > @@ -378,9 +378,9 @@ export default function Search({ fill="currentColor" > From ec54b79d7a6cbecb6c6227913242ceab6491810a Mon Sep 17 00:00:00 2001 From: B Date: Fri, 29 Jan 2021 12:01:03 -0300 Subject: [PATCH 04/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e73a9747e..228434fd3 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss). 5. Duplicate `.env.template` and rename it to `.env.local`. 6. Add proper store values to `.env.local`. 7. Run `yarn dev` to build and watch for code changes -8. The development branch is `development` (this is the branch pull requests should be made against). +8. The development branch is `canary` (this is the branch pull requests should be made against). On a release, `develop` branch is rebased into `master`. From b7fbdc4e90c9967a9d119b1bbbfb17e85652bfed Mon Sep 17 00:00:00 2001 From: B Date: Fri, 29 Jan 2021 12:01:56 -0300 Subject: [PATCH 05/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 228434fd3..8eb69383c 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss). 6. Add proper store values to `.env.local`. 7. Run `yarn dev` to build and watch for code changes 8. The development branch is `canary` (this is the branch pull requests should be made against). - On a release, `develop` branch is rebased into `master`. + On a release, `canary` branch is rebased into `master`. From 8b70794036b7a22b2cb98afeefc43fc813aa7e2e Mon Sep 17 00:00:00 2001 From: Marcelo Fortunato Date: Mon, 1 Feb 2021 23:32:28 +0000 Subject: [PATCH 06/10] chore: Remove import never used (#189) --- components/common/Avatar/Avatar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index 6ea72abb2..351a117ec 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -1,4 +1,3 @@ -import cn from 'classnames' import { FC, useState, useMemo, useRef, useEffect } from 'react' import { getRandomPairOfColors } from '@lib/colors' From 7c70f645cc54885583c8d3cd8734d3cdaf1c55d7 Mon Sep 17 00:00:00 2001 From: Jamie Isaksen Date: Tue, 2 Feb 2021 00:32:57 +0100 Subject: [PATCH 07/10] Improve import statements (#191) Co-authored-by: B --- pages/cart.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pages/cart.tsx b/pages/cart.tsx index 7e6d17b21..5444f0321 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -4,10 +4,9 @@ import getAllPages from '@framework/api/operations/get-all-pages' import useCart from '@framework/cart/use-cart' import usePrice from '@framework/use-price' import { Layout } from '@components/common' -import { Button } from '@components/ui' +import { Button, Text } from '@components/ui' import { Bag, Cross, Check } from '@components/icons' import { CartItem } from '@components/cart' -import { Text } from '@components/ui' export async function getStaticProps({ preview, From 023058dc0ca72cf5e47dd2dd7e1b7dd5f8cc63b4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 2 Feb 2021 17:49:05 -0500 Subject: [PATCH 08/10] Moved and updated cart types --- .../bigcommerce/api/cart/handlers/get-cart.ts | 5 +- .../api/cart/handlers/update-item.ts | 3 -- framework/bigcommerce/api/cart/index.ts | 17 +++++-- framework/bigcommerce/cart/use-cart.tsx | 2 +- .../bigcommerce/cart/use-update-item.tsx | 48 +++++++++---------- framework/bigcommerce/types.ts | 10 ++-- framework/commerce/cart/use-cart.tsx | 1 + framework/commerce/cart/use-update-item.tsx | 6 +++ framework/commerce/types.ts | 17 +++---- 9 files changed, 59 insertions(+), 50 deletions(-) diff --git a/framework/bigcommerce/api/cart/handlers/get-cart.ts b/framework/bigcommerce/api/cart/handlers/get-cart.ts index 9fb42d730..031bbb63f 100644 --- a/framework/bigcommerce/api/cart/handlers/get-cart.ts +++ b/framework/bigcommerce/api/cart/handlers/get-cart.ts @@ -1,6 +1,7 @@ +import type { BigcommerceCart } from '../../../types' import { BigcommerceApiError } from '../../utils/errors' import getCartCookie from '../../utils/get-cart-cookie' -import type { Cart, CartHandlers } from '..' +import type { CartHandlers } from '../' // Return current cart info const getCart: CartHandlers['getCart'] = async ({ @@ -8,7 +9,7 @@ const getCart: CartHandlers['getCart'] = async ({ body: { cartId }, config, }) => { - let result: { data?: Cart } = {} + let result: { data?: BigcommerceCart } = {} if (cartId) { try { diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/cart/handlers/update-item.ts index b0ccc710b..df9ccaee8 100644 --- a/framework/bigcommerce/api/cart/handlers/update-item.ts +++ b/framework/bigcommerce/api/cart/handlers/update-item.ts @@ -14,9 +14,6 @@ const updateItem: CartHandlers['updateItem'] = async ({ }) } - console.log('ITEM', item) - console.log('AFTER', parseCartItem(item)) - const { data } = await config.storeApiFetch( `/v3/carts/${cartId}/items/${itemId}`, { diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 0f0365cc3..7b5be31c3 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -8,7 +8,11 @@ import getCart from './handlers/get-cart' import addItem from './handlers/add-item' import updateItem from './handlers/update-item' import removeItem from './handlers/remove-item' -import type { Cart, UpdateCartItemHandlerBody } from '../../types' +import type { + BigcommerceCart, + GetCartHandlerBody, + UpdateCartItemHandlerBody, +} from '../../types' type OptionSelections = { option_id: Number @@ -27,11 +31,14 @@ export type AddItemBody = { item: ItemBody } export type RemoveItemBody = { itemId: string } export type CartHandlers = { - getCart: BigcommerceHandler - addItem: BigcommerceHandler> - updateItem: BigcommerceHandler + getCart: BigcommerceHandler + addItem: BigcommerceHandler< + BigcommerceCart, + { cartId?: string } & Partial + > + updateItem: BigcommerceHandler removeItem: BigcommerceHandler< - Cart, + BigcommerceCart, { cartId?: string } & Partial > } diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 8e008c65a..afa37ec98 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -3,7 +3,7 @@ import type { SwrOptions } from '@commerce/utils/use-data' import useResponse from '@commerce/utils/use-response' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import { normalizeCart } from '../lib/normalize' -import type { Cart as BigcommerceCart } from '../api/cart' +import type { Cart, BigcommerceCart } from '../types' const defaultOpts = { url: '/api/bigcommerce/cart', diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index 6592b090f..d1870c818 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -2,11 +2,12 @@ import { useCallback } from 'react' import debounce from 'lodash.debounce' import type { HookFetcher } from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' -import useCartUpdateItem from '@commerce/cart/use-update-item' +import useCartUpdateItem, { + UpdateItemInput as UseUpdateItemInput, +} from '@commerce/cart/use-update-item' import { normalizeCart } from '../lib/normalize' import type { UpdateCartItemBody, - UpdateCartItemInput, Cart, BigcommerceCart, LineItem, @@ -19,6 +20,10 @@ const defaultOpts = { method: 'PUT', } +export type UpdateItemInput = T extends LineItem + ? Partial> + : UseUpdateItemInput + export const fetcher: HookFetcher = async ( options, { itemId, item }, @@ -55,31 +60,24 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { ) return useCallback( - debounce( - async ( - input: T extends LineItem - ? Partial - : UpdateCartItemInput - ) => { - const itemId = input.id ?? item?.id - const productId = input.productId ?? item?.productId - const variantId = input.productId ?? item?.variantId + debounce(async (input: UpdateItemInput) => { + const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId + const variantId = input.productId ?? item?.variantId - if (!itemId || !productId || !variantId) { - throw new ValidationError({ - message: 'Invalid input used for this operation', - }) - } - - const data = await fn({ - itemId, - item: { productId, variantId, quantity: input.quantity }, + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', }) - await mutate(data, false) - return data - }, - cfg?.wait ?? 500 - ), + } + + const data = await fn({ + itemId, + item: { productId, variantId, quantity: input.quantity }, + }) + await mutate(data, false) + return data + }, cfg?.wait ?? 500), [fn, mutate] ) } diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index 766f2dbe9..26d093bfe 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -43,12 +43,10 @@ export interface CartItemBody extends Core.CartItemBody { optionSelections?: OptionSelections } -export interface UpdateCartItemBody extends Core.UpdateCartItemBody { - item: CartItemBody -} +export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} -export interface UpdateCartItemInput - extends Core.UpdateCartItemInput {} +export interface UpdateCartItemBody + extends Core.UpdateCartItemBody {} export interface UpdateCartItemHandlerBody - extends Core.UpdateCartItemHandlerBody {} + extends Core.UpdateCartItemHandlerBody {} diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 94ccb73fc..0a7ba49ee 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -6,6 +6,7 @@ import { useCommerce } from '..' export type CartResponse = ResponseState & { isEmpty?: boolean } +// Input expected by the `useCart` hook export type CartInput = { cartId?: Cart['id'] } diff --git a/framework/commerce/cart/use-update-item.tsx b/framework/commerce/cart/use-update-item.tsx index 1c6261054..e1adcb5fb 100644 --- a/framework/commerce/cart/use-update-item.tsx +++ b/framework/commerce/cart/use-update-item.tsx @@ -1,4 +1,10 @@ import useAction from '../utils/use-action' +import type { CartItemBody } from '../types' + +// Input expected by the action returned by the `useUpdateItem` hook +export type UpdateItemInput = T & { + id: string +} const useUpdateItem = useAction diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index c662077a1..61bf0d5e7 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -94,18 +94,19 @@ export interface CartItemBody { quantity?: number } -// Body by the update operation -export interface UpdateCartItemBody { - itemId: string - item: CartItemBody +// Body used by the `getCart` operation handler +export interface GetCartHandlerBody { + cartId?: string } -// Input expected by the `useUpdateItem` hook -export type UpdateCartItemInput = T & { - id: string +// Body used by the update operation +export interface UpdateCartItemBody { + itemId: string + item: T } // Body expected by the update operation handler -export interface UpdateCartItemHandlerBody extends Partial { +export interface UpdateCartItemHandlerBody + extends Partial> { cartId?: string } From 5cfa8241f66e6b952e3e6601e76a375c34a2257f Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 2 Feb 2021 20:16:47 -0500 Subject: [PATCH 09/10] Updated the useAddItem and useRemoveItem hooks --- framework/bigcommerce/api/cart/index.ts | 30 ++--------- framework/bigcommerce/cart/use-add-item.tsx | 19 ++++--- .../bigcommerce/cart/use-remove-item.tsx | 52 +++++++++++++------ framework/bigcommerce/types.ts | 10 ++++ framework/commerce/cart/use-add-item.tsx | 4 ++ framework/commerce/cart/use-remove-item.tsx | 5 ++ framework/commerce/types.ts | 29 ++++++++++- 7 files changed, 98 insertions(+), 51 deletions(-) diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 7b5be31c3..4ee668895 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -11,42 +11,22 @@ import removeItem from './handlers/remove-item' import type { BigcommerceCart, GetCartHandlerBody, + AddCartItemHandlerBody, UpdateCartItemHandlerBody, + RemoveCartItemHandlerBody, } from '../../types' -type OptionSelections = { - option_id: Number - option_value: Number | String -} - -export type ItemBody = { - productId: number - variantId: number - quantity?: number - optionSelections?: OptionSelections -} - -export type AddItemBody = { item: ItemBody } - -export type RemoveItemBody = { itemId: string } - export type CartHandlers = { getCart: BigcommerceHandler - addItem: BigcommerceHandler< - BigcommerceCart, - { cartId?: string } & Partial - > + addItem: BigcommerceHandler updateItem: BigcommerceHandler - removeItem: BigcommerceHandler< - BigcommerceCart, - { cartId?: string } & Partial - > + removeItem: BigcommerceHandler } const METHODS = ['GET', 'POST', 'PUT', 'DELETE'] // TODO: a complete implementation should have schema validation for `req.body` -const cartApi: BigcommerceApiHandler = async ( +const cartApi: BigcommerceApiHandler = async ( req, res, config, diff --git a/framework/bigcommerce/cart/use-add-item.tsx b/framework/bigcommerce/cart/use-add-item.tsx index 38ac69650..c66ee462a 100644 --- a/framework/bigcommerce/cart/use-add-item.tsx +++ b/framework/bigcommerce/cart/use-add-item.tsx @@ -1,13 +1,16 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useCartAddItem from '@commerce/cart/use-add-item' +import useCartAddItem, { + AddItemInput as UseAddItemInput, +} from '@commerce/cart/use-add-item' import { normalizeCart } from '../lib/normalize' import type { - ItemBody, - AddItemBody, - Cart as BigcommerceCart, -} from '../api/cart' + AddCartItemBody, + Cart, + BigcommerceCart, + CartItemBody, +} from '../types' import useCart from './use-cart' const defaultOpts = { @@ -15,9 +18,9 @@ const defaultOpts = { method: 'POST', } -export type AddItemInput = ItemBody +export type AddItemInput = UseAddItemInput -export const fetcher: HookFetcher = async ( +export const fetcher: HookFetcher = async ( options, { item }, fetch @@ -31,7 +34,7 @@ export const fetcher: HookFetcher = async ( }) } - const data = await fetch({ + const data = await fetch({ ...defaultOpts, ...options, body: { item }, diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx index 6f25c0c9e..c8cdaeb0d 100644 --- a/framework/bigcommerce/cart/use-remove-item.tsx +++ b/framework/bigcommerce/cart/use-remove-item.tsx @@ -1,8 +1,16 @@ import { useCallback } from 'react' import { HookFetcher } from '@commerce/utils/types' -import useCartRemoveItem from '@commerce/cart/use-remove-item' +import { ValidationError } from '@commerce/utils/errors' +import useCartRemoveItem, { + RemoveItemInput as UseRemoveItemInput, +} from '@commerce/cart/use-remove-item' import { normalizeCart } from '../lib/normalize' -import type { RemoveItemBody, Cart as BigcommerceCart } from '../api/cart' +import type { + RemoveCartItemBody, + Cart, + BigcommerceCart, + LineItem, +} from '../types' import useCart from './use-cart' const defaultOpts = { @@ -10,11 +18,15 @@ const defaultOpts = { method: 'DELETE', } -export type RemoveItemInput = { - id: string -} +export type RemoveItemFn = T extends LineItem + ? (input?: RemoveItemInput) => Promise + : (input: RemoveItemInput) => Promise -export const fetcher: HookFetcher = async ( +export type RemoveItemInput = T extends LineItem + ? Partial + : UseRemoveItemInput + +export const fetcher: HookFetcher = async ( options, { itemId }, fetch @@ -28,21 +40,29 @@ export const fetcher: HookFetcher = async ( } export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = (item?: any) => { + const useRemoveItem = ( + item?: T + ) => { const { mutate } = useCart() - const fn = useCartRemoveItem( + const fn = useCartRemoveItem( defaultOpts, customFetcher ) + const removeItem: RemoveItemFn = async (input) => { + const itemId = input?.id ?? item?.id - return useCallback( - async function removeItem(input: RemoveItemInput) { - const data = await fn({ itemId: input.id ?? item?.id }) - await mutate(data, false) - return data - }, - [fn, mutate] - ) + if (!itemId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fn({ itemId }) + await mutate(data, false) + return data + } + + return useCallback(removeItem as RemoveItemFn, [fn, mutate]) } useRemoveItem.extend = extendHook diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index 26d093bfe..90afb425d 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -45,8 +45,18 @@ export interface CartItemBody extends Core.CartItemBody { export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} +export interface AddCartItemBody extends Core.AddCartItemBody {} + +export interface AddCartItemHandlerBody + extends Core.AddCartItemHandlerBody {} + export interface UpdateCartItemBody extends Core.UpdateCartItemBody {} export interface UpdateCartItemHandlerBody extends Core.UpdateCartItemHandlerBody {} + +export interface RemoveCartItemBody extends Core.RemoveCartItemBody {} + +export interface RemoveCartItemHandlerBody + extends Core.RemoveCartItemHandlerBody {} diff --git a/framework/commerce/cart/use-add-item.tsx b/framework/commerce/cart/use-add-item.tsx index f6c069f2b..2f6422ab3 100644 --- a/framework/commerce/cart/use-add-item.tsx +++ b/framework/commerce/cart/use-add-item.tsx @@ -1,4 +1,8 @@ import useAction from '../utils/use-action' +import type { CartItemBody } from '../types' + +// Input expected by the action returned by the `useAddItem` hook +export type AddItemInput = T const useAddItem = useAction diff --git a/framework/commerce/cart/use-remove-item.tsx b/framework/commerce/cart/use-remove-item.tsx index dfa60c363..8a63b1b73 100644 --- a/framework/commerce/cart/use-remove-item.tsx +++ b/framework/commerce/cart/use-remove-item.tsx @@ -1,5 +1,10 @@ import useAction from '../utils/use-action' +// Input expected by the action returned by the `useRemoveItem` hook +export interface RemoveItemInput { + id: string +} + const useRemoveItem = useAction export default useRemoveItem diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 61bf0d5e7..cc04f52e1 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -87,6 +87,10 @@ export interface Cart { discounts?: Discount[] } +/** + * Cart mutations + */ + // Base cart item body used for cart mutations export interface CartItemBody { variantId: string @@ -99,14 +103,35 @@ export interface GetCartHandlerBody { cartId?: string } -// Body used by the update operation +// Body used by the add item to cart operation +export interface AddCartItemBody { + item: T +} + +// Body expected by the add item to cart operation handler +export interface AddCartItemHandlerBody + extends Partial> { + cartId?: string +} + +// Body used by the update cart item operation export interface UpdateCartItemBody { itemId: string item: T } -// Body expected by the update operation handler +// Body expected by the update cart item operation handler export interface UpdateCartItemHandlerBody extends Partial> { cartId?: string } + +// Body used by the remove cart item operation +export interface RemoveCartItemBody { + itemId: string +} + +// Body expected by the remove cart item operation handler +export interface RemoveCartItemHandlerBody extends Partial { + cartId?: string +} From 56ff15c91d66a5c7de9c433789281117850c1003 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Tue, 2 Feb 2021 20:18:31 -0500 Subject: [PATCH 10/10] Minor life improvement --- components/cart/CartItem/CartItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 1e9062e91..3f6a43103 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -63,7 +63,7 @@ const Item = ({ try { // If this action succeeds then there's no need to do `setRemoving(true)` // because the component will be removed from the view - await removeItem({ id: String(item.id) }) + await removeItem(item) } catch (error) { setRemoving(false) }