From a8aa755b792d77e583d906570ac20cb4c93028b2 Mon Sep 17 00:00:00 2001 From: DatNguyen Date: Wed, 6 Oct 2021 16:59:37 +0700 Subject: [PATCH] add product to cart, update product in cart, remove product form cart --- framework/commerce/types/cart.ts | 1 + framework/commerce/types/product.ts | 1 + framework/vendure/schema.d.ts | 2 +- .../utils/fragments/search-result-fragment.ts | 1 + framework/vendure/utils/normalize.ts | 3 +- pages/index.tsx | 1 + .../common/CartDrawer/CartDrawer.tsx | 5 +- .../ProductCartItem/ProductCartItem.tsx | 89 ++++++++++++++----- .../ProductsInCart/ProductsInCart.tsx | 16 ++-- .../common/ModalConfirm/ModalConfirm.tsx | 4 +- .../common/ProductCard/ProductCard.tsx | 20 ++++- .../common/QuanittyInput/QuanittyInput.tsx | 11 ++- src/components/hooks/cart/index.ts | 1 + .../hooks/cart/useGetActiveOrder.tsx | 5 +- .../hooks/cart/useRemoveProductInCart.tsx | 41 +++++++++ .../hooks/cart/useUpdateProductInCart.tsx | 40 +++++++++ src/domains/enums/Message.ts | 2 + 17 files changed, 195 insertions(+), 48 deletions(-) create mode 100644 src/components/hooks/cart/useRemoveProductInCart.tsx create mode 100644 src/components/hooks/cart/useUpdateProductInCart.tsx create mode 100644 src/domains/enums/Message.ts diff --git a/framework/commerce/types/cart.ts b/framework/commerce/types/cart.ts index e4af878de..b3d3b1e59 100644 --- a/framework/commerce/types/cart.ts +++ b/framework/commerce/types/cart.ts @@ -17,6 +17,7 @@ export type LineItem = { quantity: number discounts: Discount[] // A human-friendly unique string automatically generated from the product’s name + slug: string path: string variant: ProductVariant options?: SelectedOption[] diff --git a/framework/commerce/types/product.ts b/framework/commerce/types/product.ts index 39f594a7a..6d01b8c2d 100644 --- a/framework/commerce/types/product.ts +++ b/framework/commerce/types/product.ts @@ -60,6 +60,7 @@ export type ProductCard = { // TODO: collection category?: string, isNotSell?: boolean + productVariantId?:string } export type SearchProductsBody = { diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index a817d6799..7095ce02f 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -3038,7 +3038,7 @@ export type SearchResultFragment = { __typename?: 'SearchResult' } & Pick< SearchResult, 'productId' | 'sku' | 'productName' | 'description' | 'slug' | 'sku' | 'currencyCode' | 'productAsset' | 'price' | 'priceWithTax' | 'currencyCode' - | 'collectionIds' + | 'collectionIds' | 'productVariantId' > & { productAsset?: Maybe< { __typename?: 'SearchResultAsset' } & Pick< diff --git a/framework/vendure/utils/fragments/search-result-fragment.ts b/framework/vendure/utils/fragments/search-result-fragment.ts index 6155b5b47..b3fc4d561 100644 --- a/framework/vendure/utils/fragments/search-result-fragment.ts +++ b/framework/vendure/utils/fragments/search-result-fragment.ts @@ -7,6 +7,7 @@ export const searchResultFragment = /* GraphQL */ ` slug sku currencyCode + productVariantId productAsset { id preview diff --git a/framework/vendure/utils/normalize.ts b/framework/vendure/utils/normalize.ts index a5cb96c8a..939460930 100644 --- a/framework/vendure/utils/normalize.ts +++ b/framework/vendure/utils/normalize.ts @@ -11,6 +11,7 @@ export function normalizeSearchResult(item: SearchResultFragment): ProductCard { imageSrc: item.productAsset?.preview ? item.productAsset?.preview + '?w=800&mode=crop' : '', price: (item.priceWithTax as any).min / 100, currencyCode: item.currencyCode, + productVariantId: item.productVariantId?item.productVariantId:"", // TODO: // oldPrice: item.price @@ -35,7 +36,7 @@ export function normalizeCart(order: CartFragment): Cart { id: l.id, name: l.productVariant.name, quantity: l.quantity, - url: l.productVariant.product.slug, + slug: l.productVariant.product.slug, variantId: l.productVariant.id, productId: l.productVariant.productId, images: [{ url: l.featuredAsset?.preview + '?preset=thumb' || '' }], diff --git a/pages/index.tsx b/pages/index.tsx index 4fc797c26..cf279d399 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -17,6 +17,7 @@ interface Props { featuredProducts: ProductCard[], } export default function Home({ freshProducts, featuredProducts, veggie }: Props) { + // console.log("veggie",veggie) return ( <> diff --git a/src/components/common/CartDrawer/CartDrawer.tsx b/src/components/common/CartDrawer/CartDrawer.tsx index daeae0513..089e8b620 100644 --- a/src/components/common/CartDrawer/CartDrawer.tsx +++ b/src/components/common/CartDrawer/CartDrawer.tsx @@ -1,3 +1,4 @@ +import { normalizeCart } from '@framework/utils/normalize'; import React from 'react'; import { useCartDrawer } from 'src/components/contexts'; import useGetActiveOrder from 'src/components/hooks/cart/useGetActiveOrder'; @@ -18,12 +19,12 @@ const CartDrawer = ({ }: Props) => { const {order} = useGetActiveOrder() return (
- +
diff --git a/src/components/common/CartDrawer/components/ProductCartItem/ProductCartItem.tsx b/src/components/common/CartDrawer/components/ProductCartItem/ProductCartItem.tsx index 7ec3ecbdb..ef219d31d 100644 --- a/src/components/common/CartDrawer/components/ProductCartItem/ProductCartItem.tsx +++ b/src/components/common/CartDrawer/components/ProductCartItem/ProductCartItem.tsx @@ -1,25 +1,64 @@ -import React from 'react'; +import React, { useCallback, useState } from 'react' import Link from 'next/link' -import { QuanittyInput } from 'src/components/common'; -import { IconDelete } from 'src/components/icons'; -import { ROUTE } from 'src/utils/constanst.utils'; -import { ProductProps } from 'src/utils/types.utils'; -import ImgWithLink from '../../../ImgWithLink/ImgWithLink'; -import LabelCommon from '../../../LabelCommon/LabelCommon'; -import s from './ProductCartItem.module.scss'; +import { ModalConfirm, QuanittyInput } from 'src/components/common' +import { IconDelete } from 'src/components/icons' +import { ROUTE } from 'src/utils/constanst.utils' +import ImgWithLink from '../../../ImgWithLink/ImgWithLink' +import LabelCommon from '../../../LabelCommon/LabelCommon' +import s from './ProductCartItem.module.scss' +import { LineItem } from '@commerce/types/cart' +import { useUpdateProductInCart } from 'src/components/hooks/cart' +import { debounce } from 'lodash' +import useRemoveProductInCart from 'src/components/hooks/cart/useRemoveProductInCart' -export interface ProductCartItempProps extends ProductProps { - quantity: number, +export interface ProductCartItempProps extends LineItem { + currency: { code: string } } -const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageSrc, quantity }: ProductCartItempProps) => { +const ProductCartItem = ({ + slug, + discounts, + quantity, + variant, + name, + currency, + id +}: ProductCartItempProps) => { + const [visible, setVisible] = useState(false) + const {updateProduct} = useUpdateProductInCart() + const {removeProduct, loading} = useRemoveProductInCart() + const handleQuantityChangeCallback = (isSuccess:boolean,mess?:string) => { + if(!isSuccess){ + console.log(mess) + } + } + const handleRemoveCallback = (isSuccess:boolean,mess?:string) => { + if(!isSuccess){ + console.log(mess) + }else{ + setVisible(false) + } + } + const handleQuantityChange = (value:number) => { + updateProduct({orderLineId:id,quantity:value},handleQuantityChangeCallback) + } + const debounceFn = useCallback(debounce(handleQuantityChange, 500), []); + const handleCancel = () => { + setVisible(false) + } + const handleOpen = () => { + setVisible(true) + } + const handleConfirm = () => { + removeProduct({orderLineId:id},handleRemoveCallback) + } return (
- +
@@ -27,30 +66,32 @@ const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageS
- {name} {weight ? `(${weight})` : ''} + {name} {variant?.weight ? `(${variant.weight})` : ''}
- { - oldPrice && -
- {oldPrice} - {discount} -
- } -
{price}
+ {discounts.length > 0 && ( +
+ {/* {oldPrice} */} + {discounts[0]} +
+ )} +
{variant?.price} {currency?.code}
-
+
- +
+ + Are you sure want to remove {name} form your cart +
) } -export default ProductCartItem; \ No newline at end of file +export default ProductCartItem diff --git a/src/components/common/CartDrawer/components/ProductsInCart/ProductsInCart.tsx b/src/components/common/CartDrawer/components/ProductsInCart/ProductsInCart.tsx index b455d4d73..7d6432a9d 100644 --- a/src/components/common/CartDrawer/components/ProductsInCart/ProductsInCart.tsx +++ b/src/components/common/CartDrawer/components/ProductsInCart/ProductsInCart.tsx @@ -1,25 +1,21 @@ +import { LineItem } from '@commerce/types/cart'; import React from 'react'; import ProductCartItem, { ProductCartItempProps } from '../ProductCartItem/ProductCartItem'; import s from './ProductsInCart.module.scss'; interface Props { - data: ProductCartItempProps[] + data: LineItem[] + currency: { code: string } } -const ProductsInCart = ({ data }: Props) => { +const ProductsInCart = ({ data, currency }: Props) => { return (
    { data.map(item =>
  • ) } diff --git a/src/components/common/ModalConfirm/ModalConfirm.tsx b/src/components/common/ModalConfirm/ModalConfirm.tsx index 1e425482f..a41dd41d7 100644 --- a/src/components/common/ModalConfirm/ModalConfirm.tsx +++ b/src/components/common/ModalConfirm/ModalConfirm.tsx @@ -5,6 +5,7 @@ import s from './ModalConfirm.module.scss' interface ModalConfirmProps extends ModalCommonProps { okText?: String cancelText?: String + loading?:boolean onOk?: () => void onCancel?: () => void } @@ -16,6 +17,7 @@ const ModalConfirm = ({ onCancel, children, title = 'Confirm', + loading, ...props }: ModalConfirmProps) => { return ( @@ -25,7 +27,7 @@ const ModalConfirm = ({
    {cancelText}
    - {okText} + {okText}
) diff --git a/src/components/common/ProductCard/ProductCard.tsx b/src/components/common/ProductCard/ProductCard.tsx index b9c351aab..19cbdc6da 100644 --- a/src/components/common/ProductCard/ProductCard.tsx +++ b/src/components/common/ProductCard/ProductCard.tsx @@ -10,13 +10,14 @@ import ItemWishList from '../ItemWishList/ItemWishList' import LabelCommon from '../LabelCommon/LabelCommon' import s from './ProductCard.module.scss' import ProductNotSell from './ProductNotSell/ProductNotSell' - +import {useAddProductToCart} from "../../hooks/cart" export interface ProductCardProps extends ProductCard { buttonText?: string isSingleButton?: boolean, } const ProductCardComponent = ({ + id, category, name, slug, @@ -27,7 +28,18 @@ const ProductCardComponent = ({ imageSrc, isNotSell, isSingleButton, + productVariantId, }: ProductCardProps) => { + + const {addProduct,loading,error} = useAddProductToCart() + const handleAddToCart = () => { + if(productVariantId){ + addProduct({variantId:productVariantId,quantity:1},handleAddToCartCallback) + } + } + const handleAddToCartCallback = () => { + + } if (isNotSell) { return
@@ -71,12 +83,12 @@ const ProductCardComponent = ({ { isSingleButton ?
- } size='small'>Add to cart + } size='small' >Add to cart
: <> -
- +
+
{buttonText} diff --git a/src/components/common/QuanittyInput/QuanittyInput.tsx b/src/components/common/QuanittyInput/QuanittyInput.tsx index e31abb880..e2229830f 100644 --- a/src/components/common/QuanittyInput/QuanittyInput.tsx +++ b/src/components/common/QuanittyInput/QuanittyInput.tsx @@ -26,10 +26,6 @@ const QuanittyInput = ({ }: QuanittyInputProps) => { const [value, setValue] = useState(0) - useEffect(() => { - onChange && onChange(value) - }, [value]) - useEffect(() => { initValue && setValue(initValue) }, [initValue]) @@ -37,16 +33,20 @@ const QuanittyInput = ({ const onPlusClick = () => { if (max && value + step > max) { setValue(max) + onChange && onChange(max) } else { setValue(value + step) + onChange && onChange(value + step) } } const onMinusClick = () => { if (min && value - step < min) { setValue(min) + onChange && onChange(min) } else { setValue(value - step) + onChange && onChange(value - step) } } @@ -54,10 +54,13 @@ const QuanittyInput = ({ let value = Number(e.target.value) || 0 if (min && value < min) { setValue(min) + onChange && onChange(min) } else if (max && value > max) { setValue(max) + onChange && onChange(max) } else { setValue(value) + onChange && onChange(value) } } diff --git a/src/components/hooks/cart/index.ts b/src/components/hooks/cart/index.ts index c0235c81c..950b32e7d 100644 --- a/src/components/hooks/cart/index.ts +++ b/src/components/hooks/cart/index.ts @@ -1,2 +1,3 @@ export { default as useAddProductToCart } from './useAddProductToCart' +export { default as useUpdateProductInCart } from './useUpdateProductInCart' export { default as useGetActiveOrder } from './useGetActiveOrder' \ No newline at end of file diff --git a/src/components/hooks/cart/useGetActiveOrder.tsx b/src/components/hooks/cart/useGetActiveOrder.tsx index 818ca9e9a..4c26a1786 100644 --- a/src/components/hooks/cart/useGetActiveOrder.tsx +++ b/src/components/hooks/cart/useGetActiveOrder.tsx @@ -1,5 +1,7 @@ +import { Cart } from '@commerce/types/cart' import { ActiveOrderQuery } from '@framework/schema' import { cartFragment } from '@framework/utils/fragments/cart-fragment' +import { normalizeCart } from '@framework/utils/normalize' import { gql } from 'graphql-request' import gglFetcher from 'src/utils/gglFetcher' import useSWR from 'swr' @@ -14,7 +16,8 @@ const query = gql` const useGetActiveOrder = () => { const { data, ...rest } = useSWR([query], gglFetcher) - return { order: data?.activeOrder, ...rest } + return { order: data?.activeOrder ? normalizeCart(data!.activeOrder) : null, ...rest } + } export default useGetActiveOrder diff --git a/src/components/hooks/cart/useRemoveProductInCart.tsx b/src/components/hooks/cart/useRemoveProductInCart.tsx new file mode 100644 index 000000000..d66fd4306 --- /dev/null +++ b/src/components/hooks/cart/useRemoveProductInCart.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react' +import { CommonError } from 'src/domains/interfaces/CommonError' +import rawFetcher from 'src/utils/rawFetcher' +import { AdjustOrderLineMutationVariables,AdjustOrderLineMutation, RemoveOrderLineMutation, RemoveOrderLineMutationVariables } from '@framework/schema' +import { errorMapping } from 'src/utils/errrorMapping' +import { useGetActiveOrder } from '.' +import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation' +import { removeOrderLineMutation } from '@framework/utils/mutations/remove-order-line-mutation' + +const useRemoveProductInCart = () => { + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + const { mutate } = useGetActiveOrder() + + const removeProduct = (options:RemoveOrderLineMutationVariables, + fCallBack: (isSuccess: boolean, message?: string) => void + ) => { + setError(null) + setLoading(true) + rawFetcher({ + query: removeOrderLineMutation , + variables: options, + }) + .then(({ data }) => { + if (data.removeOrderLine.__typename !== "Order") { + throw CommonError.create(errorMapping(data.removeOrderLine.message), data.removeOrderLine.errorCode) + } + mutate() + fCallBack(true) + }) + .catch((error) => { + setError(error) + fCallBack(false, error.message) + }) + .finally(() => setLoading(false)) + } + + return { loading, removeProduct, error } +} + +export default useRemoveProductInCart diff --git a/src/components/hooks/cart/useUpdateProductInCart.tsx b/src/components/hooks/cart/useUpdateProductInCart.tsx new file mode 100644 index 000000000..b2a0423bb --- /dev/null +++ b/src/components/hooks/cart/useUpdateProductInCart.tsx @@ -0,0 +1,40 @@ +import { useState } from 'react' +import { CommonError } from 'src/domains/interfaces/CommonError' +import rawFetcher from 'src/utils/rawFetcher' +import { AdjustOrderLineMutationVariables,AdjustOrderLineMutation } from '@framework/schema' +import { errorMapping } from 'src/utils/errrorMapping' +import { useGetActiveOrder } from '.' +import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation' + +const useUpdateProductInCart = () => { + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + const { mutate } = useGetActiveOrder() + + const updateProduct = (options:AdjustOrderLineMutationVariables, + fCallBack: (isSuccess: boolean, message?: string) => void + ) => { + setError(null) + setLoading(true) + rawFetcher({ + query: adjustOrderLineMutation , + variables: options, + }) + .then(({ data }) => { + if (data.adjustOrderLine.__typename !== "Order") { + throw CommonError.create(errorMapping(data.adjustOrderLine.message), data.adjustOrderLine.errorCode) + } + mutate() + fCallBack(true) + }) + .catch((error) => { + setError(error) + fCallBack(false, error.message) + }) + .finally(() => setLoading(false)) + } + + return { loading, updateProduct, error } +} + +export default useUpdateProductInCart diff --git a/src/domains/enums/Message.ts b/src/domains/enums/Message.ts new file mode 100644 index 000000000..3d8b4e288 --- /dev/null +++ b/src/domains/enums/Message.ts @@ -0,0 +1,2 @@ + + \ No newline at end of file