ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -26) Top Seller Carousel 추가하고, 장바구니 설정하기(동일 주문자의 상품만 장바구니에 추가가능)
    NODE.JS 2021. 5. 29. 22:05

     

    홈스크린에 topseller들을 보여주는 carousel을 추가할 것이다. 

    frontent 에 react carousel을 추가하자 

    npm install react-responsive-carousel

     

    홈스크린에 carousel을 넣어주자.

    import {Carousel} from 'react-responsive-carousel';
    import 'react-responsive-carousel/lib/styles/carousel.min.css'

     

    useRouter로 가서 탑셀러들을 보여주는 라우트를 생성한다. 

    내림차순으로 정렬하고 그중에서 탑3만 가지고 온다. 

    userRouter.get('/top-sellers', expressAsyncHandler(async(req, res)=>{
      const topSellers = await User.find({isSeller:true}).sort({'seller.rating':-1}).limit(3)
      res.send(topSellers)
    }))
    

     

    이제 userConstants.js

    export const USER_TOPSELLERS_LIST_REQUEST = 'USER_TOPSELLERS_LIST_REQUEST'
    export const USER_TOPSELLERS_LIST_SUCCESS = 'USER_TOPSELLERS_LIST_SUCCESS'
    export const USER_TOPSELLERS_LIST_FAIL = 'USER_TOPSELLERS_LIST_FAIL'
    

     

    userActions.js

    유저리스트를 가져오는 액션과 매우비슷하기 때문에 복사해서 일부수정해준다. 

    export const listTopSellers = () =>async(dispatch)=>{
        dispatch({
            type: USER_TOPSELLERS_LIST_REQUEST
        })
        try{
            const {data} = await axios.get(`/api/users/top-sellers`)
            dispatch({
                type: USER_TOPSELLERS_LIST_SUCCESS,
                payload:data
            })
        }catch(error){
            const message = error.response && error.response.data.message
            ? error.response.data.message
            : error.message
            dispatch({
                type: USER_TOPSELLERS_LIST_FAIL,
                payload:message
            })
        }
    }

     

     

    userReducers.js

    export const userTopSellerListReducer = (state = {loading:true} ,action)=>{
        switch(action.type){
            case USER_TOPSELLERS_LIST_REQUEST:
                return {loading:true}
            case USER_TOPSELLERS_LIST_SUCCESS:
                return {loading:false, users:action.payload}
            case USER_TOPSELLERS_LIST_FAIL:
                return {loading:false, error:action.payload}
            default:
                return state;
        }
    }

     

    store.js

    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        userSignin: userSigninReducer,
        userRegister: userRegisterReducer,
        orderCreate:orderCreateReducer,
        orderDetails:orderDetailsReducer,
        orderPay: orderPayReducer,
        orderMineList:orderMineListReducer,
        userDetails:userDetailsReducer,
        userUpdateProfile:userUpdateProfileReducer,
        productCreate:productCreateReducer,
        productUpdate:productUpdateReducer,
        productDelete:productDeleteReducer,
        orderList:orderListReducer,
        orderDelete:orderDeleteReducer,
        orderDeliver:orderDeliverReducer,
        userList:userListReducer,
        userDelete:userDeleteReducer,
        userUpdate:userUpdateReducer,
        userTopSellerList:userTopSellerListReducer
    })

     

    HomeScreen.js

    import {Carousel} from 'react-responsive-carousel';
    import 'react-responsive-carousel/lib/styles/carousel.min.css'
    
    import { listTopSellers } from '../actions/userActions';
    
    function HomeScreen() {
        const dispatch = useDispatch();
        
        const userTopSellersList = useSelector(state => state.userTopSellersList)
        const{loading:loadingSellers, error:errorSellers, users:sellers} = userTopSellersList
    
        useEffect(() => {
            dispatch(listProducts({}));
            dispatch(listTopSellers());
        }, [dispatch])
    
        return (
            <div>
                <h2>Top Sellers</h2>
                {loadingSellers ?(
                <LoadingBox></LoadingBox>
                ): errorSellers?(
                <MessageBox variant="danger">{errorSellers}</MessageBox>
                ): (
                    <>
                    {sellers.length===0 &&  <MessageBox>No Seller Found</MessageBox>}
                    </>
                )}
                <h2>Featured Products</h2>
                {loading ?(
                <LoadingBox></LoadingBox>
                ): error?(
                <MessageBox variant="danger">{error}</MessageBox>
                ): (
                    <>
                    {products.length===0 &&  <MessageBox>No Product Found</MessageBox>}
                    <div className="row center">
                        {products.map(product=>(
                            <Product key={product._id}  product = {product}/> 
                        ))}
                    </div>
                  </>
                )}
            </div>
        )
    }
    
    export default HomeScreen

     

    이제 carousel을 이용해보자. 

                <h2>Top Sellers</h2>
                {loadingSellers ?(
                <LoadingBox></LoadingBox>
                ): errorSellers?(
                <MessageBox variant="danger">{errorSellers}</MessageBox>
                ): (
                    <>
                    {sellers.length===0 &&  <MessageBox>No Seller Found</MessageBox>}
                    <Carousel showArrows autoPlay showThumbs={false}>
                        {sellers.map((seller)=>(
                            <div key={seller._id}>
                                <Link to = {`/seller/${seller._id}`}>
                                    <img src={seller.seller.logo} alt={seller.seller.name} />
                                    <p className="legend">{seller.seller.name}</p>
                                </Link>
                            </div>
                        ))}
                    </Carousel>
                    </>
                )}

     

     

    스타일링을 주자

    /* Carousel */
    .carousel .slide img{
      max-width: 30rem;
    }

    지금은 셀러가 2명밖에 없고 레이팅도 안 매겨져서 두칸으로 나온다. 

     

    캡션 누르면 상세페이지로 이동한다. 

     

    이제 장바구니에 동일 판매자의 제품만 넣을 수 있도록 설정하자.

     

     

    constants추가하자. 

    export const CART_ADD_ITEM_FAIL = 'CART_ADD_ITEM_FAIL'
    

    cartActions을 수정하자. (카트아이템이 있는데, 추가하려는 상품과 이미 있는 상품의 판매자가 다르면 추가할 수 없다) 

    export const addToCart = (productId, qty)=> async(dispatch, getState)=>{
        const {data} =await Axios.get(`/api/products/${productId}`);
        const {cart:{cartItems}} = getState();
        if(cartItems.length>0 && data.seller._id !==cartItems[0].seller._id){
            dispatch({
                type:CART_ADD_ITEM_FAIL,
                payload:"Can't Add To Cart. Buy from one seller at a time."
            })
        }
        dispatch({
            type:CART_ADD_ITEM,
            payload:{
                name:data.name,
                image:data.image,
                price:data.price,
                countInStock:data.countInStock,
                product:data._id,
                seller:data.seller,
                qty,
            }
        });
        localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
    }

     

     

    cartreducers.추가하고 

    removeitem과 cartempty에는 error:'' 으로 추가해주는데 그 이유는 error가 카트스크린에 영향을 미칠 수도 있기 때문이다. 

    export const cartReducer = (state={cartItems:[]}, action)=>{
        switch(action.type){
            case CART_ADD_ITEM:
                const item = action.payload;
                const existItem = state.cartItems.find(x=>x.product===item.product)
                if(existItem){
                    return {
                        ...state,
                        cartItems:state.cartItems.map(x=>x.product===existItem.product? item:x)
                    }
                }else{
                    return {...state, cartItems:[...state.cartItems, item]};
                }
            case CART_ADD_ITEM_FAIL:
                return {...state, error:action.payload}
            case CART_REMOVE_ITEM:
                return {...state, error:'', cartItems:state.cartItems.filter(x=>x.product!==action.payload)};
            case CART_SAVE_SHIPPING_ADDRESS:
                return {...state,shippingAddress:action.payload }
            case CART_SAVE_PAYMENT_METHOD:
                return {...state, paymentMethod:action.payload}
            case CART_EMPTY:
                return {...state,error:'', cartItems:[]}
            default: 
                return state;
        }
    }

     

     

    카트 스크린으로 가보자. 

    위에서 action에 에러를 추가했으니 만약 에러가 발생한다면, 처리하는 곳을 넣어준다. (메세지 나타내기)

     

        const cart = useSelector(state=>state.cart);
        const {cartItems, error} = cart;    
    
    			<div className="row top">
               <div className="col-2">
                   <h1>Shopping Cart</h1>
                   {error && <MessageBox variant='danger'>{error}</MessageBox>}
                   {cartItems.length ===0? 
                        (<MessageBox>
                            Cart is Empty. 
                            <Link to ="/">Go Shopping</Link>
                        </MessageBox>

    판매자가 다른 상품을 넣으려고하면 경고메세지가 뜨기는 하지만 들어간다!

     

    상품 추가가 안되게 막아보자. 

    cartActions

    export const addToCart = (productId, qty)=> async(dispatch, getState)=>{
        const {data} =await Axios.get(`/api/products/${productId}`);
        const {cart:{cartItems}} = getState();
        if(cartItems.length>0 && data.seller._id !==cartItems[0].seller._id){
            dispatch({
                type:CART_ADD_ITEM_FAIL,
                payload:"Can't Add To Cart. Buy from one seller at a time."
            })
        }else{
            dispatch({
                type:CART_ADD_ITEM,
                payload:{
                    name:data.name,
                    image:data.image,
                    price:data.price,
                    countInStock:data.countInStock,
                    product:data._id,
                    seller:data.seller,
                    qty,
                }
            });
            localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
        }
        
    }
    

     

     

    상품 추가 안되는 것을 확인할 수 있다. 

     

    보이는 문구를 조금 바꿔보자. 

    export const addToCart = (productId, qty)=> async(dispatch, getState)=>{
        const {data} =await Axios.get(`/api/products/${productId}`);
        const {cart:{cartItems}} = getState();
        if(cartItems.length>0 && data.seller._id !==cartItems[0].seller._id){
            dispatch({
                type:CART_ADD_ITEM_FAIL,
                payload:`Can't Add To Cart. Buy only from ${cartItems[0].seller.seller.name} in this order.`
            })

     

    여기서 카트를 새로 고침해서 들어가도 저 에러메세지가 사라지지 않는데 그 이슈를 고쳐보자. 

     

    cartReducer의 나머지 항목에서 error:''를 추가해준다. 

    export const cartReducer = (state={cartItems:[]}, action)=>{
        switch(action.type){
            case CART_ADD_ITEM:
                const item = action.payload;
                const existItem = state.cartItems.find(x=>x.product===item.product)
                if(existItem){
                    return {
                        ...state,
                        error:'',
                        cartItems:state.cartItems.map(x=>x.product===existItem.product? item:x)
                    }
                }else{
                    return {...state, error:'',cartItems:[...state.cartItems, item]};
                }
            case CART_ADD_ITEM_FAIL:
                return {...state, error:action.payload}
            case CART_REMOVE_ITEM:
                return {...state, error:'', cartItems:state.cartItems.filter(x=>x.product!==action.payload)};
            case CART_SAVE_SHIPPING_ADDRESS:
                return {...state,shippingAddress:action.payload }
            case CART_SAVE_PAYMENT_METHOD:
                return {...state, paymentMethod:action.payload}
            case CART_EMPTY:
                return {...state,error:'', cartItems:[]}
            default: 
                return state;
        }
    }

     

    이슈를 해결했다. 

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.