-
아마존 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;
'NODE.JS' 카테고리의 다른 글
아마존 E-commerce 클론 -30) Pagination적용하기 (0) 2021.05.30 아마존 E-commerce 클론 -29) Google Map적용하기 (0) 2021.05.30 아마존 E-commerce 클론 -27) Search Box ,Search Filter기능 , Side bar, sort & Filter 만들기 (0) 2021.05.29 아마존 E-commerce 클론 -26) Top Seller Carousel 추가하고, 장바구니 설정하기(동일 주문자의 상품만 장바구니에 추가가능) (0) 2021.05.29 아마존 E-commerce 클론 -25) Seller 정보페이지 만들기 (0) 2021.05.29