From d82ea6fc87e33522a03863e261bcf658c8e72973 Mon Sep 17 00:00:00 2001 From: Stephen Rugh Date: Tue, 26 Nov 2019 15:10:21 -0600 Subject: [PATCH] Rebase and use graphql invoker in add item to cart --- .../lib/store/actions/cart/asyncActions.js | 45 ++++++-------- .../ProductFullDetail/useProductFullDetail.js | 62 ++++++++++++++----- .../ProductFullDetail/productFullDetail.js | 8 ++- .../addConfigurableProductsToCart.graphql | 29 +++++++++ .../queries/addSimpleProductsToCart.graphql | 23 +++++++ 5 files changed, 123 insertions(+), 44 deletions(-) create mode 100644 packages/venia-ui/lib/queries/addConfigurableProductsToCart.graphql create mode 100644 packages/venia-ui/lib/queries/addSimpleProductsToCart.graphql diff --git a/packages/peregrine/lib/store/actions/cart/asyncActions.js b/packages/peregrine/lib/store/actions/cart/asyncActions.js index fb2de4918f..bf4eb65b92 100644 --- a/packages/peregrine/lib/store/actions/cart/asyncActions.js +++ b/packages/peregrine/lib/store/actions/cart/asyncActions.js @@ -44,39 +44,32 @@ export const createCart = payload => }; export const addItemToCart = (payload = {}) => { - const { item, fetchCartId } = payload; - const writingImageToCache = writeImageToCache(item); + const { + addItemQuery, + fetchCartId, + parentSku, + product, + quantity, + sku + } = payload; + const writingImageToCache = writeImageToCache(product); return async function thunk(dispatch, getState) { await writingImageToCache; dispatch(actions.addItem.request(payload)); try { - const { cart, user } = getState(); - const { isSignedIn } = user; - let cartEndpoint; - - if (!isSignedIn) { - const { cartId } = cart; + const { cart } = getState(); + const { cartId } = cart; - if (!cartId) { - const missingCartIdError = new Error( - 'Missing required information: cartId' - ); - missingCartIdError.noCartId = true; - throw missingCartIdError; + await addItemQuery({ + variables: { + cartId, + parentSku, + product, + quantity, + sku } - - cartEndpoint = `/rest/V1/guest-carts/${cartId}/items`; - } else { - cartEndpoint = '/rest/V1/carts/mine/items'; - } - - const quoteId = getQuoteIdForRest(cart, user); - const cartItem = toRESTCartItem(quoteId, payload); - await request(cartEndpoint, { - method: 'POST', - body: JSON.stringify({ cartItem }) }); // 2019-02-07 Moved these dispatches to the success clause of @@ -348,7 +341,7 @@ export const getCartDetails = (payload = {}) => { // TODO: If we don't have the image in cache we should probably try // to find it some other way otherwise we have no image to display // in the cart and will have to fall back to a placeholder. - if (imageCache && Array.isArray(items) && items.length) { + if (Array.isArray(items) && items.length) { const validTotals = totals && totals.items; items.forEach(item => { item.image = item.image || imageCache[item.sku] || {}; diff --git a/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js b/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js index 17b9904aba..e8c90d996c 100644 --- a/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +++ b/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js @@ -2,7 +2,6 @@ import { useCallback, useState, useMemo } from 'react'; import { useMutation } from '@apollo/react-hooks'; import { useCartContext } from '@magento/peregrine/lib/context/cart'; -import { appendOptionsToPayload } from '@magento/peregrine/lib/util/appendOptionsToPayload'; import { findMatchingVariant } from '@magento/peregrine/lib/util/findMatchingProductVariant'; import { isProductConfigurable } from '@magento/peregrine/lib/util/isProductConfigurable'; @@ -142,10 +141,22 @@ const getConfigPrice = (product, optionCodes, optionSelections) => { }; export const useProductFullDetail = props => { - const { product, createCartMutation } = props; + const { + addConfigurableProductToCartMutation, + addSimpleProductToCartMutation, + createCartMutation, + product + } = props; const [fetchCartId] = useMutation(createCartMutation); const [{ isAddingItem }, { addItemToCart }] = useCartContext(); + const [addConfigurableProductToCart] = useMutation( + addConfigurableProductToCartMutation + ); + const [addSimpleProductToCart] = useMutation( + addSimpleProductToCartMutation + ); + const [quantity, setQuantity] = useState(INITIAL_QUANTITY); const breadcrumbCategoryId = useMemo( @@ -177,23 +188,42 @@ export const useProductFullDetail = props => { [product, optionCodes, optionSelections] ); - const handleAddToCart = useCallback(() => { - const payload = { - item: product, - productType: product.__typename, - quantity - }; - - if (isProductConfigurable(product)) { - appendOptionsToPayload(payload, optionSelections, optionCodes); + const handleAddToCart = useCallback(async () => { + // Prepare the params to add and use the proper mutation. + if (product.__typename === 'SimpleProduct') { + addItemToCart({ + addItemQuery: addSimpleProductToCart, + fetchCartId, + product, + quantity, + sku: product.sku + }); + } else if (product.__typename === 'ConfigurableProduct') { + // GQL expects the sku for the variant rather than the option + // key/value pair. + const variant = findMatchingVariant({ + optionCodes, + optionSelections, + variants: product.variants + }); + + const { sku } = variant.product; + + addItemToCart({ + addItemQuery: addConfigurableProductToCart, + fetchCartId, + parentSku: product.sku, + product: variant.product, + quantity, + sku + }); + } else { + throw new Error('Unsupported product type. Cannot add to cart.'); } - - addItemToCart({ - ...payload, - fetchCartId - }); }, [ + addConfigurableProductToCart, addItemToCart, + addSimpleProductToCart, fetchCartId, optionCodes, optionSelections, diff --git a/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js b/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js index 9d3946b103..9a9efe3817 100644 --- a/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +++ b/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js @@ -16,14 +16,18 @@ import CREATE_CART_MUTATION from '../../queries/createCart.graphql'; import { useProductFullDetail } from '@magento/peregrine/lib/talons/ProductFullDetail/useProductFullDetail'; import { isProductConfigurable } from '@magento/peregrine/lib/util/isProductConfigurable'; +import ADD_CONFIGURABLE_MUTATION from '../../queries/addConfigurableProductsToCart.graphql'; +import ADD_SIMPLE_MUTATION from '../../queries/addSimpleProductsToCart.graphql'; const Options = React.lazy(() => import('../ProductOptions')); const ProductFullDetail = props => { const { product } = props; const talonProps = useProductFullDetail({ - product, - createCartMutation: CREATE_CART_MUTATION + addConfigurableProductToCartMutation: ADD_CONFIGURABLE_MUTATION, + addSimpleProductToCartMutation: ADD_SIMPLE_MUTATION, + createCartMutation: CREATE_CART_MUTATION, + product }); const { diff --git a/packages/venia-ui/lib/queries/addConfigurableProductsToCart.graphql b/packages/venia-ui/lib/queries/addConfigurableProductsToCart.graphql new file mode 100644 index 0000000000..d5cf36605e --- /dev/null +++ b/packages/venia-ui/lib/queries/addConfigurableProductsToCart.graphql @@ -0,0 +1,29 @@ +mutation addConfigurableProductToCart( + $cartId: String! + $quantity: Float! + $sku: String! + $parentSku: String! +) { + addConfigurableProductsToCart( + input: { + cart_id: $cartId + cart_items: [ + { + data: { quantity: $quantity, sku: $sku } + parent_sku: $parentSku + } + ] + } + ) { + cart { + items { + id + product { + name + sku + } + quantity + } + } + } +} diff --git a/packages/venia-ui/lib/queries/addSimpleProductsToCart.graphql b/packages/venia-ui/lib/queries/addSimpleProductsToCart.graphql new file mode 100644 index 0000000000..0f0d506a01 --- /dev/null +++ b/packages/venia-ui/lib/queries/addSimpleProductsToCart.graphql @@ -0,0 +1,23 @@ +mutation addSimpleProductToCart( + $cartId: String! + $quantity: Float! + $sku: String! +) { + addSimpleProductsToCart( + input: { + cart_id: $cartId + cart_items: [{ data: { quantity: $quantity, sku: $sku } }] + } + ) { + cart { + items { + id + product { + name + sku + } + quantity + } + } + } +}