ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -28) Rate & Review 생성하기
    NODE.JS 2021. 5. 30. 12:45

    상품 디테일 페이지 아랫부분에 해당 상품에 대한 리뷰를 작성하는 란을 만든다. 

     

    product screen에 리뷰남기기 란을 만든다. (row-top이 끝나는 부분에)

        const userSignin = useSelector(state => state.userSignin)
        const {userInfo} = userSignin
    
        const [rating, setRating] = useState(0)
        const [comment, setComment] = useState('')
    
    
        const submitHandler = (e) =>{
            e.preventDefault();
            if(comment && rating){
                dispatch(createComment(productId, {rating, comment, name:userInfo.name}))
            }else{
                alert('Please enter comment and rating.')
            }
        }
        
        
        
         </div>
                <div>
                    <h2 id="reviews">Reviews</h2>
                    {product.reviews.length ===0 && <MesssageBox>There is no reviews.</MesssageBox> }
                    <ul>
                        {product.reviews.map(review=>(
                            <li key={review._id}>
                                <strong>{review.name}</strong>
                                <Rating rating = {review.rating} caption=" "></Rating>
                                <p>
                                    {review.createdAt.substring(0,10)}
                                </p>
                                <p>
                                    {review.comment}
                                </p>
                            </li>
                        ))}
                        <li>
                            {userInfo? (
                                <form onSubmit={submitHandler} className="form">
                                    <div>
                                        <h2>Write a customer review</h2>
                                    </div>
                                    <div>
                                        <label htmlFor="rating">Rating</label>
                                        <select id="rating" value={rating} onChange={e=>setRating(e.target.value)}>
                                            <option value="">Select...</option>
                                            <option value="1">1- Poor</option>
                                            <option value="2">2- Fair</option>
                                            <option value="3">3- Good</option>
                                            <option value="4">4- Very Good</option>
                                            <option value="5">5- Excelent</option>
                                        </select>
                                    </div>
                                    <div>
                                        <label htmlFor="comment">Comment</label>
                                        <textarea id="comment" value={comment} onChange={e=>setComment(e.target.value)}></textarea>
                                    </div>
                                    <div>
                                        <label></label>
                                        <button className="primary" type="submit">Submit</button>
                                    </div>
                                </form>
                            ): (
                                <MessageBox>Please <Link to ="/signin">Sign In</Link> to write a review.</MessageBox>
                            )}
                        </li>
                    </ul>
                </div>
            </div>
    
            )}
            </div>
        )
    }
    
    export default ProductScreen
    

     

     

    리뷰를 생성하는 라우터를 만들자. 

    그전에 productmodel 안에 review를 추가해준다. 

    import mongoose from 'mongoose'
    
    const reviewSchema = new mongoose.Schema({
        name:{
            type:String, 
            required:true,
        },    
        comment:{
            type:String, 
            required:true,
        },
        rating:{
            type:Number, 
            required:true,
        },
    }, {timestamps:true})
    
    const productSchema = new mongoose.Schema({
        name:{
            type:String, 
            required:true,
            unique:true
        },
        seller:{
            type:mongoose.Schema.Types.ObjectId,
            ref:'User',
        },
        image:{
            type:String,
            required:true
        },
        brand:{
            type:String,
            required:true
        },
        category:{
            type:String,
            required:true
        },
        description:{
            type:String,
            required:true
        },
        price:{
            type:Number,
            required:true
        },
        countInStock:{
            type:Number,
            required:true
        },
        rating:{
            type:Number,
            required:true
        },
        numReviews:{
            type:Number,
            required:true
        },
        reviews:[
            reviewSchema
        ]
    },{timestamps:true})
    
    
    const Product = mongoose.model('Product', productSchema);
    
    export default Product;

    productRouter

    push리뷰로 리뷰를 생성하고 나서는 rating을 수정해주고(최근의 레이팅 더해서 평균낸다)

    상품을 업데이트 하고나서 가장 최근에 생성한(지금생성한)리뷰를 돌려준다. 

    productRouter.post('/:id/reviews', isAuth, expressAsyncHandler(async(req, res)=>{
        const productId = req.params.id;
        const product = await Product.findById(productId)
        if(product){
    
            const review = {name:req.user.name,  rating:Number(req.body.rating), comment:req.body.comment}
            product.reviews.push(review)
            product.numReviews = product.reviews.length;
            product.rating= product.reviews.reduce((a, c)=>c.rating+a,0)/product.numReviews
    
            const updatedProduct = await product.save()
            res.status(201).send({meassge:'Review Created', review:updatedProduct.reviews[updatedProduct.reviews.length-1]})
        }else{
            res.status(404).send({message:'Product Not Found'})
        }
    }))
    
    
    export default productRouter;

    productconstants

    export const PRODUCT_REVIEW_CREATE_REQUEST = 'PRODUCT_REVIEW_CREATE_REQUEST';
    export const PRODUCT_REVIEW_CREATE_SUCCESS = 'PRODUCT_REVIEW_CREATE_SUCCESS';
    export const PRODUCT_REVIEW_CREATE_FAIL = 'PRODUCT_REVIEW_CREATE_FAIL';
    export const PRODUCT_REVIEW_CREATE_RESET = 'PRODUCT_REVIEW_CREATE_RESET';
    

     

    productactions

      export const createReview = (productId, review) =>async(dispatch, getState)=>{
        dispatch({
            type: PRODUCT_REVIEW_CREATE_REQUEST
        })
        const {userSignin:{userInfo}} = getState();
    
        try{
            const {data} = await Axios.post(`/api/products/${productId}/reviews`,review, {
                headers: {Authorization : `Bearer ${userInfo.token}`}
            } )
            dispatch({
                type:PRODUCT_REVIEW_CREATE_SUCCESS,
                payload:data.review
            })
        }catch(error){
            dispatch({type:PRODUCT_REVIEW_CREATE_FAIL, payload:error.response && error.response.data.message? error.response.data.message:error.message})
        }
    }

     

    productReducers.

    export const productReviewCreateReducer = (state={}, action)=>{
        switch(action.type){
            case PRODUCT_REVIEW_CREATE_REQUEST:
                return {loading:true}
            case PRODUCT_REVIEW_CREATE_SUCCESS:
                return {loading:false, success:true, review:action.payload}
            case PRODUCT_REVIEW_CREATE_FAIL:
                return {loading:false, error:action.payload}
            case PRODUCT_REVIEW_CREATE_RESET:
                return {};
            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,
        userTopSellersList:userTopSellerListReducer,
        productCategoryList:productCategoryListReducer,
        productReviewCreate:productReviewCreateReducer
    })

     

     

    ProductScreen.js

        const dispatch = useDispatch();
    
        const userSignin = useSelector(state => state.userSignin)
        const {userInfo} = userSignin
    
        const [rating, setRating] = useState(0)
        const [comment, setComment] = useState('')
    
        const productReviewCreate = useSelector(state => state.productReviewCreate)
        const {loading:loadingReviewCreate, error:errorReviewCreate, success:successReviewCreate} = productReviewCreate
       
        useEffect(() => {
            if(successReviewCreate){
                window.alert('Review Submitted Successfully')
                setRating('')
                setComment('')
                dispatch({
                    type:PRODUCT_REVIEW_CREATE_RESET
                })
            }
            dispatch(detailsProduct(productId));
        }, [dispatch, productId,successReviewCreate]);
    
    
     const submitHandler = (e) =>{
            e.preventDefault();
            if(comment && rating){
                dispatch(createReview(productId, {rating, comment, name:userInfo.name}))
            }else{
                alert('Please enter comment and rating.')
            }
        }
    
    
    
    
    
    
    
                    <h2 id="reviews">Reviews</h2>
                    {product.reviews.length ===0 && <MesssageBox>There is no reviews.</MesssageBox> }
                    <ul>
                        {product.reviews.map(review=>(
                            <li key={review._id}>
                                <strong>{review.name}</strong>
                                <Rating rating = {review.rating} caption=" "></Rating>
                                <p>
                                    {review.createdAt.substring(0,10)}
                                </p>
                                <p>
                                    {review.comment}
                                </p>
                            </li>
                        ))}
                        <li>
                            {userInfo? (
                                <form onSubmit={submitHandler} className="form">
                                    <div>
                                        <h2>Write a customer review</h2>
                                    </div>
                                    <div>
                                        <label htmlFor="rating">Rating</label>
                                        <select id="rating" value={rating} onChange={e=>setRating(e.target.value)}>
                                            <option value="">Select...</option>
                                            <option value="1">1- Poor</option>
                                            <option value="2">2- Fair</option>
                                            <option value="3">3- Good</option>
                                            <option value="4">4- Very Good</option>
                                            <option value="5">5- Excelent</option>
                                        </select>
                                    </div>
                                    <div>
                                        <label htmlFor="comment">Comment</label>
                                        <textarea id="comment" value={comment} onChange={e=>setComment(e.target.value)}></textarea>
                                    </div>
                                    <div>
                                        <label></label>
                                        <button className="primary" type="submit">Submit</button>
                                    </div>
                                    <div>
                                        {loadingReviewCreate && <LoadingBox></LoadingBox>}
                                        {errorReviewCreate && (<MessageBox variant='danger'>{errorReviewCreate}</MessageBox>)}
                                    </div>
                                </form>

     

     

     

     

    알림이 뜨면서 확인누르면 잘 실행된다. 

     

    같은 사람이 두개의 리뷰를 달지 못하도록 설정하자. 

    productRouter.post('/:id/reviews', isAuth, expressAsyncHandler(async(req, res)=>{
        const productId = req.params.id;
        const product = await Product.findById(productId)
        if(product){
            if (product.reviews.find((x) => x.name === req.user.name)) {
                return res
                  .status(400)
                  .send({ message: 'You already submitted a review' });
              }
            const review = {name:req.user.name,  rating:Number(req.body.rating), comment:req.body.comment}
            product.reviews.push(review)
            product.numReviews = product.reviews.length;
            product.rating= product.reviews.reduce((a, c)=>c.rating+a,0)/product.numReviews
    
            const updatedProduct = await product.save()
            res.status(201).send({meassge:'Review Created', review:updatedProduct.reviews[updatedProduct.reviews.length-1]})
        }else{
            res.status(404).send({message:'Product Not Found'})
        }
    }))
    
    
    export default productRouter;
    
    

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.