From c8098e315fd1b17b27eb13fc0497a330365393b3 Mon Sep 17 00:00:00 2001
From: Pablets
Date: Thu, 4 Feb 2021 02:31:47 -0300
Subject: [PATCH 1/2] - Added product remove from stock after success pay. -
Added Public / protected route to update only product stock after purchase. -
Fixed Product list not updating after purchase. - Deleted CART_CLEAR_ITEMS
completely in order to correctly update the stock from each of the products
(in case they are more than 1 in the same cart.)
---
backend/controllers/productController.js | 134 +++++++++-------
backend/models/orderModel.js | 9 +-
backend/routes/productRoutes.js | 3 +
frontend/package-lock.json | 12 +-
frontend/package.json | 2 +-
frontend/src/actions/orderActions.js | 6 -
frontend/src/actions/productActions.js | 41 +++++
frontend/src/constants/cartConstants.js | 1 -
frontend/src/constants/productConstants.js | 5 +
frontend/src/reducers/cartReducers.js | 26 ++--
frontend/src/reducers/productReducers.js | 99 +++++++-----
frontend/src/screens/OrderScreen.js | 168 ++++++++++++++-------
frontend/src/screens/ProductEditScreen.js | 158 +++++++++----------
frontend/src/screens/ProductScreen.js | 133 +++++++++-------
frontend/src/store.js | 2 +
package-lock.json | 6 +-
package.json | 11 +-
17 files changed, 487 insertions(+), 329 deletions(-)
diff --git a/backend/controllers/productController.js b/backend/controllers/productController.js
index ac29dbfe0..9c2bc292b 100644
--- a/backend/controllers/productController.js
+++ b/backend/controllers/productController.js
@@ -1,12 +1,12 @@
-import asyncHandler from 'express-async-handler'
-import Product from '../models/productModel.js'
+import asyncHandler from 'express-async-handler';
+import Product from '../models/productModel.js';
// @desc Fetch all products
// @route GET /api/products
// @access Public
const getProducts = asyncHandler(async (req, res) => {
- const pageSize = 10
- const page = Number(req.query.pageNumber) || 1
+ const pageSize = 10;
+ const page = Number(req.query.pageNumber) || 1;
const keyword = req.query.keyword
? {
@@ -15,44 +15,44 @@ const getProducts = asyncHandler(async (req, res) => {
$options: 'i',
},
}
- : {}
+ : {};
- const count = await Product.countDocuments({ ...keyword })
+ const count = await Product.countDocuments({ ...keyword });
const products = await Product.find({ ...keyword })
.limit(pageSize)
- .skip(pageSize * (page - 1))
+ .skip(pageSize * (page - 1));
- res.json({ products, page, pages: Math.ceil(count / pageSize) })
-})
+ res.json({ products, page, pages: Math.ceil(count / pageSize) });
+});
// @desc Fetch single product
// @route GET /api/products/:id
// @access Public
const getProductById = asyncHandler(async (req, res) => {
- const product = await Product.findById(req.params.id)
+ const product = await Product.findById(req.params.id);
if (product) {
- res.json(product)
+ res.json(product);
} else {
- res.status(404)
- throw new Error('Product not found')
+ res.status(404);
+ throw new Error('Product not found');
}
-})
+});
// @desc Delete a product
// @route DELETE /api/products/:id
// @access Private/Admin
const deleteProduct = asyncHandler(async (req, res) => {
- const product = await Product.findById(req.params.id)
+ const product = await Product.findById(req.params.id);
if (product) {
- await product.remove()
- res.json({ message: 'Product removed' })
+ await product.remove();
+ res.json({ message: 'Product removed' });
} else {
- res.status(404)
- throw new Error('Product not found')
+ res.status(404);
+ throw new Error('Product not found');
}
-})
+});
// @desc Create a product
// @route POST /api/products
@@ -68,11 +68,11 @@ const createProduct = asyncHandler(async (req, res) => {
countInStock: 0,
numReviews: 0,
description: 'Sample description',
- })
+ });
- const createdProduct = await product.save()
- res.status(201).json(createdProduct)
-})
+ const createdProduct = await product.save();
+ res.status(201).json(createdProduct);
+});
// @desc Update a product
// @route PUT /api/products/:id
@@ -86,43 +86,62 @@ const updateProduct = asyncHandler(async (req, res) => {
brand,
category,
countInStock,
- } = req.body
+ } = req.body;
- const product = await Product.findById(req.params.id)
+ const product = await Product.findById(req.params.id);
if (product) {
- product.name = name
- product.price = price
- product.description = description
- product.image = image
- product.brand = brand
- product.category = category
- product.countInStock = countInStock
-
- const updatedProduct = await product.save()
- res.json(updatedProduct)
+ product.name = name;
+ product.price = price;
+ product.description = description;
+ product.image = image;
+ product.brand = brand;
+ product.category = category;
+ product.countInStock = countInStock;
+
+ const updatedProduct = await product.save();
+ res.json(updatedProduct);
} else {
- res.status(404)
- throw new Error('Product not found')
+ res.status(404);
+ throw new Error('Product not found');
}
-})
+});
+
+// @desc Update a product
+// @route PUT /api/products/:id
+// @access Private/Admin
+const updateProductStock = asyncHandler(async (req, res) => {
+ const { countInStock } = req.body;
+
+ const product = await Product.findById(req.params.id);
+
+ if (product) {
+ product.countInStock = countInStock;
+
+ const updatedProductStock = await product.save();
+ res.json(updatedProductStock);
+ } else {
+ res.status(404);
+ throw new Error('Product not found');
+ }
+});
// @desc Create new review
// @route POST /api/products/:id/reviews
// @access Private
const createProductReview = asyncHandler(async (req, res) => {
- const { rating, comment } = req.body
+ const { rating, comment } = req.body;
- const product = await Product.findById(req.params.id)
+ const product = await Product.findById(req.params.id);
if (product) {
const alreadyReviewed = product.reviews.find(
(r) => r.user.toString() === req.user._id.toString()
- )
+ );
if (alreadyReviewed) {
- res.status(400)
- throw new Error('Product already reviewed')
+ res.status(400);
+ throw new Error('Product already reviewed');
}
const review = {
@@ -130,32 +149,32 @@ const createProductReview = asyncHandler(async (req, res) => {
rating: Number(rating),
comment,
user: req.user._id,
- }
+ };
- product.reviews.push(review)
+ product.reviews.push(review);
- product.numReviews = product.reviews.length
+ product.numReviews = product.reviews.length;
product.rating =
product.reviews.reduce((acc, item) => item.rating + acc, 0) /
- product.reviews.length
+ product.reviews.length;
- await product.save()
- res.status(201).json({ message: 'Review added' })
+ await product.save();
+ res.status(201).json({ message: 'Review added' });
} else {
- res.status(404)
- throw new Error('Product not found')
+ res.status(404);
+ throw new Error('Product not found');
}
-})
+});
// @desc Get top rated products
// @route GET /api/products/top
// @access Public
const getTopProducts = asyncHandler(async (req, res) => {
- const products = await Product.find({}).sort({ rating: -1 }).limit(3)
+ const products = await Product.find({}).sort({ rating: -1 }).limit(3);
- res.json(products)
-})
+ res.json(products);
+});
export {
getProducts,
@@ -163,6 +182,7 @@ export {
deleteProduct,
createProduct,
updateProduct,
+ updateProductStock,
createProductReview,
getTopProducts,
-}
+};
diff --git a/backend/models/orderModel.js b/backend/models/orderModel.js
index 4d9cc68d1..eed3efb01 100644
--- a/backend/models/orderModel.js
+++ b/backend/models/orderModel.js
@@ -1,4 +1,4 @@
-import mongoose from 'mongoose'
+import mongoose from 'mongoose';
const orderSchema = mongoose.Schema(
{
@@ -13,6 +13,7 @@ const orderSchema = mongoose.Schema(
qty: { type: Number, required: true },
image: { type: String, required: true },
price: { type: Number, required: true },
+ countInStock: { type: Number, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
required: true,
@@ -71,8 +72,8 @@ const orderSchema = mongoose.Schema(
{
timestamps: true,
}
-)
+);
-const Order = mongoose.model('Order', orderSchema)
+const Order = mongoose.model('Order', orderSchema);
-export default Order
+export default Order;
diff --git a/backend/routes/productRoutes.js b/backend/routes/productRoutes.js
index 801db1cf2..e0c19eac5 100644
--- a/backend/routes/productRoutes.js
+++ b/backend/routes/productRoutes.js
@@ -6,6 +6,7 @@ import {
deleteProduct,
createProduct,
updateProduct,
+ updateProductStock,
createProductReview,
getTopProducts,
} from '../controllers/productController.js'
@@ -19,5 +20,7 @@ router
.get(getProductById)
.delete(protect, admin, deleteProduct)
.put(protect, admin, updateProduct)
+router.route('/:id/updatestock').put(protect, updateProductStock)
+router.route('/:id/stock').put(protect, updateProductStock);
export default router
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 2f70d29f5..131e71db3 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -2565,9 +2565,9 @@
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
},
"axios": {
- "version": "0.20.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz",
- "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==",
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
+ "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"requires": {
"follow-redirects": "^1.10.0"
}
@@ -6768,9 +6768,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"inquirer": {
"version": "7.3.3",
diff --git a/frontend/package.json b/frontend/package.json
index 428344856..d03b99390 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -7,7 +7,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
- "axios": "^0.20.0",
+ "axios": "^0.21.1",
"react": "^16.13.1",
"react-bootstrap": "^1.3.0",
"react-dom": "^16.13.1",
diff --git a/frontend/src/actions/orderActions.js b/frontend/src/actions/orderActions.js
index bee1f9e3a..7c1de915d 100644
--- a/frontend/src/actions/orderActions.js
+++ b/frontend/src/actions/orderActions.js
@@ -1,5 +1,4 @@
import axios from 'axios'
-import { CART_CLEAR_ITEMS } from '../constants/cartConstants'
import {
ORDER_CREATE_REQUEST,
ORDER_CREATE_SUCCESS,
@@ -45,11 +44,6 @@ export const createOrder = (order) => async (dispatch, getState) => {
type: ORDER_CREATE_SUCCESS,
payload: data,
})
- dispatch({
- type: CART_CLEAR_ITEMS,
- payload: data,
- })
- localStorage.removeItem('cartItems')
} catch (error) {
const message =
error.response && error.response.data.message
diff --git a/frontend/src/actions/productActions.js b/frontend/src/actions/productActions.js
index ad8f8173b..f9415c6a7 100644
--- a/frontend/src/actions/productActions.js
+++ b/frontend/src/actions/productActions.js
@@ -21,6 +21,9 @@ import {
PRODUCT_TOP_REQUEST,
PRODUCT_TOP_SUCCESS,
PRODUCT_TOP_FAIL,
+ PRODUCT_UPDATE_STOCK_REQUEST,
+ PRODUCT_UPDATE_STOCK_SUCCESS,
+ PRODUCT_UPDATE_STOCK_FAIL,
} from '../constants/productConstants'
import { logout } from './userActions'
@@ -186,6 +189,44 @@ export const updateProduct = (product) => async (dispatch, getState) => {
}
}
+export const updateProductStock = (product) => async (dispatch, getState) => {
+ try {
+ dispatch({
+ type: PRODUCT_UPDATE_STOCK_REQUEST,
+ });
+
+ const {
+ userLogin: { userInfo },
+ } = getState();
+
+ const config = {
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${userInfo.token}`,
+ },
+ };
+
+ const { data } = await axios.put(
+ `/api/products/${product._id}/stock`,
+ product,
+ config
+ );
+
+ dispatch({
+ type: PRODUCT_UPDATE_STOCK_SUCCESS,
+ payload: data,
+ });
+ } catch (error) {
+ dispatch({
+ type: PRODUCT_UPDATE_STOCK_FAIL,
+ payload:
+ error.response && error.response.data.message
+ ? error.response.data.message
+ : error.message,
+ });
+ }
+};
+
export const createProductReview = (productId, review) => async (
dispatch,
getState
diff --git a/frontend/src/constants/cartConstants.js b/frontend/src/constants/cartConstants.js
index d6cef833e..8d4cdc0b4 100644
--- a/frontend/src/constants/cartConstants.js
+++ b/frontend/src/constants/cartConstants.js
@@ -1,5 +1,4 @@
export const CART_ADD_ITEM = 'CART_ADD_ITEM'
-export const CART_CLEAR_ITEMS = 'CART_RESET'
export const CART_REMOVE_ITEM = 'CART_REMOVE_ITEM'
export const CART_SAVE_SHIPPING_ADDRESS = 'CART_SAVE_SHIPPING_ADDRESS'
export const CART_SAVE_PAYMENT_METHOD = 'CART_SAVE_PAYMENT_METHOD'
diff --git a/frontend/src/constants/productConstants.js b/frontend/src/constants/productConstants.js
index bdac67981..3cdefce72 100644
--- a/frontend/src/constants/productConstants.js
+++ b/frontend/src/constants/productConstants.js
@@ -20,6 +20,11 @@ export const PRODUCT_UPDATE_SUCCESS = 'PRODUCT_UPDATE_SUCCESS'
export const PRODUCT_UPDATE_FAIL = 'PRODUCT_UPDATE_FAIL'
export const PRODUCT_UPDATE_RESET = 'PRODUCT_UPDATE_RESET'
+export const PRODUCT_UPDATE_STOCK_REQUEST = 'PRODUCT_UPDATE_STOCK_REQUEST'
+export const PRODUCT_UPDATE_STOCK_SUCCESS = 'PRODUCT_UPDATE_STOCK_SUCCESS'
+export const PRODUCT_UPDATE_STOCK_FAIL = 'PRODUCT_UPDATE_STOCK_FAIL'
+export const PRODUCT_UPDATE_STOCK_RESET = 'PRODUCT_UPDATE_STOCK_RESET'
+
export const PRODUCT_CREATE_REVIEW_REQUEST = 'PRODUCT_CREATE_REVIEW_REQUEST'
export const PRODUCT_CREATE_REVIEW_SUCCESS = 'PRODUCT_CREATE_REVIEW_SUCCESS'
export const PRODUCT_CREATE_REVIEW_FAIL = 'PRODUCT_CREATE_REVIEW_FAIL'
diff --git a/frontend/src/reducers/cartReducers.js b/frontend/src/reducers/cartReducers.js
index 64815a1c3..516306148 100644
--- a/frontend/src/reducers/cartReducers.js
+++ b/frontend/src/reducers/cartReducers.js
@@ -3,8 +3,7 @@ import {
CART_REMOVE_ITEM,
CART_SAVE_SHIPPING_ADDRESS,
CART_SAVE_PAYMENT_METHOD,
- CART_CLEAR_ITEMS,
-} from '../constants/cartConstants'
+} from '../constants/cartConstants';
export const cartReducer = (
state = { cartItems: [], shippingAddress: {} },
@@ -12,9 +11,9 @@ export const cartReducer = (
) => {
switch (action.type) {
case CART_ADD_ITEM:
- const item = action.payload
+ const item = action.payload;
- const existItem = state.cartItems.find((x) => x.product === item.product)
+ const existItem = state.cartItems.find((x) => x.product === item.product);
if (existItem) {
return {
@@ -22,34 +21,29 @@ export const cartReducer = (
cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x
),
- }
+ };
} else {
return {
...state,
cartItems: [...state.cartItems, item],
- }
+ };
}
case CART_REMOVE_ITEM:
return {
...state,
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_CLEAR_ITEMS:
- return {
- ...state,
- cartItems: [],
- }
+ };
default:
- return state
+ return state;
}
-}
+};
diff --git a/frontend/src/reducers/productReducers.js b/frontend/src/reducers/productReducers.js
index 24a5bb595..0b0c5d045 100644
--- a/frontend/src/reducers/productReducers.js
+++ b/frontend/src/reducers/productReducers.js
@@ -23,25 +23,29 @@ import {
PRODUCT_TOP_REQUEST,
PRODUCT_TOP_SUCCESS,
PRODUCT_TOP_FAIL,
-} from '../constants/productConstants'
+ PRODUCT_UPDATE_STOCK_REQUEST,
+ PRODUCT_UPDATE_STOCK_SUCCESS,
+ PRODUCT_UPDATE_STOCK_FAIL,
+ PRODUCT_UPDATE_STOCK_RESET,
+} from '../constants/productConstants';
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
- return { loading: true, products: [] }
+ return { loading: true, products: [] };
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
products: action.payload.products,
pages: action.payload.pages,
page: action.payload.page,
- }
+ };
case PRODUCT_LIST_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
default:
- return state
+ return state;
}
-}
+};
export const productDetailsReducer = (
state = { product: { reviews: [] } },
@@ -49,83 +53,98 @@ export const productDetailsReducer = (
) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
- return { ...state, loading: true }
+ return { ...state, loading: true };
case PRODUCT_DETAILS_SUCCESS:
- return { loading: false, product: action.payload }
+ return { loading: false, product: action.payload };
case PRODUCT_DETAILS_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
default:
- return state
+ return state;
}
-}
+};
export const productDeleteReducer = (state = {}, action) => {
switch (action.type) {
case PRODUCT_DELETE_REQUEST:
- return { loading: true }
+ return { loading: true };
case PRODUCT_DELETE_SUCCESS:
- return { loading: false, success: true }
+ return { loading: false, success: true };
case PRODUCT_DELETE_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
default:
- return state
+ return state;
}
-}
+};
export const productCreateReducer = (state = {}, action) => {
switch (action.type) {
case PRODUCT_CREATE_REQUEST:
- return { loading: true }
+ return { loading: true };
case PRODUCT_CREATE_SUCCESS:
- return { loading: false, success: true, product: action.payload }
+ return { loading: false, success: true, product: action.payload };
case PRODUCT_CREATE_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
case PRODUCT_CREATE_RESET:
- return {}
+ return {};
default:
- return state
+ return state;
}
-}
+};
-export const productUpdateReducer = (state = { product: {} }, action) => {
+export const productUpdateStockReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_UPDATE_REQUEST:
- return { loading: true }
+ return { loading: true };
case PRODUCT_UPDATE_SUCCESS:
- return { loading: false, success: true, product: action.payload }
+ return { loading: false, success: true, product: action.payload };
case PRODUCT_UPDATE_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
case PRODUCT_UPDATE_RESET:
- return { product: {} }
+ return { product: {} };
+ default:
+ return state;
+ }
+};
+
+export const productUpdateReducer = (state = { product: {} }, action) => {
+ switch (action.type) {
+ case PRODUCT_UPDATE_STOCK_REQUEST:
+ return { loading: true };
+ case PRODUCT_UPDATE_STOCK_SUCCESS:
+ return { loading: false, success: true, product: action.payload };
+ case PRODUCT_UPDATE_STOCK_FAIL:
+ return { loading: false, error: action.payload };
+ case PRODUCT_UPDATE_STOCK_RESET:
+ return { product: {} };
default:
- return state
+ return state;
}
-}
+};
export const productReviewCreateReducer = (state = {}, action) => {
switch (action.type) {
case PRODUCT_CREATE_REVIEW_REQUEST:
- return { loading: true }
+ return { loading: true };
case PRODUCT_CREATE_REVIEW_SUCCESS:
- return { loading: false, success: true }
+ return { loading: false, success: true };
case PRODUCT_CREATE_REVIEW_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
case PRODUCT_CREATE_REVIEW_RESET:
- return {}
+ return {};
default:
- return state
+ return state;
}
-}
+};
export const productTopRatedReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_TOP_REQUEST:
- return { loading: true, products: [] }
+ return { loading: true, products: [] };
case PRODUCT_TOP_SUCCESS:
- return { loading: false, products: action.payload }
+ return { loading: false, products: action.payload };
case PRODUCT_TOP_FAIL:
- return { loading: false, error: action.payload }
+ return { loading: false, error: action.payload };
default:
- return state
+ return state;
}
-}
+};
diff --git a/frontend/src/screens/OrderScreen.js b/frontend/src/screens/OrderScreen.js
index 1b8c87ed8..8bc97c623 100644
--- a/frontend/src/screens/OrderScreen.js
+++ b/frontend/src/screens/OrderScreen.js
@@ -1,100 +1,152 @@
-import React, { useState, useEffect } from 'react'
-import axios from 'axios'
-import { PayPalButton } from 'react-paypal-button-v2'
-import { Link } from 'react-router-dom'
-import { Row, Col, ListGroup, Image, Card, Button } from 'react-bootstrap'
-import { useDispatch, useSelector } from 'react-redux'
-import Message from '../components/Message'
-import Loader from '../components/Loader'
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
+import { PayPalButton } from 'react-paypal-button-v2';
+import { Link } from 'react-router-dom';
+import { Row, Col, ListGroup, Image, Card, Button } from 'react-bootstrap';
+import { useDispatch, useSelector } from 'react-redux';
+import Message from '../components/Message';
+import Loader from '../components/Loader';
import {
getOrderDetails,
payOrder,
deliverOrder,
-} from '../actions/orderActions'
+} from '../actions/orderActions';
import {
ORDER_PAY_RESET,
ORDER_DELIVER_RESET,
-} from '../constants/orderConstants'
+ ORDER_LIST_MY_RESET,
+} from '../constants/orderConstants';
+import { PRODUCT_UPDATE_STOCK_RESET } from '../constants/productConstants';
+import { removeFromCart } from '../actions/cartActions';
+import { updateProductStock } from '../actions/productActions';
const OrderScreen = ({ match, history }) => {
- const orderId = match.params.id
+ const orderId = match.params.id;
- const [sdkReady, setSdkReady] = useState(false)
+ const [sdkReady, setSdkReady] = useState(false);
- const dispatch = useDispatch()
+ const dispatch = useDispatch();
- const orderDetails = useSelector((state) => state.orderDetails)
- const { order, loading, error } = orderDetails
+ const cart = useSelector((state) => state.cart);
+ const { cartItems } = cart;
- const orderPay = useSelector((state) => state.orderPay)
- const { loading: loadingPay, success: successPay } = orderPay
+ // console.log(cartItems);
- const orderDeliver = useSelector((state) => state.orderDeliver)
- const { loading: loadingDeliver, success: successDeliver } = orderDeliver
+ const [countInStock, setCountInStock] = useState(0);
- const userLogin = useSelector((state) => state.userLogin)
- const { userInfo } = userLogin
+ const productUpdateStock = useSelector((state) => state.productUpdateStock);
+ const { success: successStockUpdate } = productUpdateStock;
+
+ const orderDetails = useSelector((state) => state.orderDetails);
+ const { order, loading, error } = orderDetails;
+
+ const orderPay = useSelector((state) => state.orderPay);
+ const { loading: loadingPay, success: successPay } = orderPay;
+
+ const orderDeliver = useSelector((state) => state.orderDeliver);
+ const { loading: loadingDeliver, success: successDeliver } = orderDeliver;
+
+ const userLogin = useSelector((state) => state.userLogin);
+ const { userInfo } = userLogin;
if (!loading) {
// Calculate prices
const addDecimals = (num) => {
- return (Math.round(num * 100) / 100).toFixed(2)
- }
+ return (Math.round(num * 100) / 100).toFixed(2);
+ };
order.itemsPrice = addDecimals(
order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0)
- )
+ );
}
useEffect(() => {
if (!userInfo) {
- history.push('/login')
+ history.push('/login');
}
const addPayPalScript = async () => {
- const { data: clientId } = await axios.get('/api/config/paypal')
- const script = document.createElement('script')
- script.type = 'text/javascript'
- script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`
- script.async = true
+ const { data: clientId } = await axios.get('/api/config/paypal');
+ const script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`;
+ script.async = true;
script.onload = () => {
- setSdkReady(true)
- }
- document.body.appendChild(script)
- }
+ setSdkReady(true);
+ };
+ document.body.appendChild(script);
+ };
if (!order || successPay || successDeliver || order._id !== orderId) {
- dispatch({ type: ORDER_PAY_RESET })
- dispatch({ type: ORDER_DELIVER_RESET })
- dispatch(getOrderDetails(orderId))
+ dispatch({ type: ORDER_PAY_RESET });
+ dispatch({ type: ORDER_DELIVER_RESET });
+ dispatch(getOrderDetails(orderId));
} else if (!order.isPaid) {
if (!window.paypal) {
- addPayPalScript()
+ addPayPalScript();
} else {
- setSdkReady(true)
+ setSdkReady(true);
}
}
- }, [dispatch, orderId, successPay, successDeliver, order])
+
+ if (successStockUpdate) {
+ console.log('PRODUCT_UPDATE_STOCK_RESET');
+ dispatch({ type: PRODUCT_UPDATE_STOCK_RESET });
+ }
+
+ if (order && successPay) {
+ console.log('sale del loop');
+ order.orderItems.forEach((item, i) => {
+ console.log(
+ `countinstock: ${cartItems[i].countInStock}
+ -
+ qty: ${item.qty}
+ Equals: ${cartItems[i].countInStock - item.qty}`
+ );
+ const updatedStock = cartItems[i].countInStock - item.qty;
+ setCountInStock(cartItems[i].countInStock - item.qty);
+ dispatch(
+ updateProductStock({
+ _id: item.product,
+ countInStock: updatedStock,
+ })
+ );
+ dispatch(removeFromCart(item.product));
+ });
+ dispatch({ type: ORDER_LIST_MY_RESET });
+ }
+ }, [
+ history,
+ userInfo,
+ dispatch,
+ orderId,
+ successPay,
+ successDeliver,
+ order,
+ cartItems,
+ countInStock,
+ successStockUpdate,
+ ]);
const successPaymentHandler = (paymentResult) => {
- console.log(paymentResult)
- dispatch(payOrder(orderId, paymentResult))
- }
+ console.log(paymentResult);
+ dispatch(payOrder(orderId, paymentResult));
+ };
const deliverHandler = () => {
- dispatch(deliverOrder(order))
- }
+ dispatch(deliverOrder(order));
+ };
return loading ? (
) : error ? (
- {error}
+ {error}
) : (
<>
Order {order._id}
-
+
Shipping
@@ -111,11 +163,11 @@ const OrderScreen = ({ match, history }) => {
{order.shippingAddress.country}
{order.isDelivered ? (
-
+
Delivered on {order.deliveredAt}
) : (
- Not Delivered
+ Not Delivered
)}
@@ -126,9 +178,9 @@ const OrderScreen = ({ match, history }) => {
{order.paymentMethod}
{order.isPaid ? (
- Paid on {order.paidAt}
+ Paid on {order.paidAt}
) : (
- Not Paid
+ Not Paid
)}
@@ -137,7 +189,7 @@ const OrderScreen = ({ match, history }) => {
{order.orderItems.length === 0 ? (
Order is empty
) : (
-
+
{order.orderItems.map((item, index) => (
@@ -167,7 +219,7 @@ const OrderScreen = ({ match, history }) => {
-
+
Order Summary
@@ -215,8 +267,8 @@ const OrderScreen = ({ match, history }) => {
!order.isDelivered && (
>
- )
-}
+ );
+};
-export default OrderScreen
+export default OrderScreen;
diff --git a/frontend/src/screens/ProductEditScreen.js b/frontend/src/screens/ProductEditScreen.js
index b314ee705..0b9470d7b 100644
--- a/frontend/src/screens/ProductEditScreen.js
+++ b/frontend/src/screens/ProductEditScreen.js
@@ -1,82 +1,82 @@
-import axios from 'axios'
-import React, { useState, useEffect } from 'react'
-import { Link } from 'react-router-dom'
-import { Form, Button } from 'react-bootstrap'
-import { useDispatch, useSelector } from 'react-redux'
-import Message from '../components/Message'
-import Loader from '../components/Loader'
-import FormContainer from '../components/FormContainer'
-import { listProductDetails, updateProduct } from '../actions/productActions'
-import { PRODUCT_UPDATE_RESET } from '../constants/productConstants'
+import axios from 'axios';
+import React, { useState, useEffect } from 'react';
+import { Link } from 'react-router-dom';
+import { Form, Button } from 'react-bootstrap';
+import { useDispatch, useSelector } from 'react-redux';
+import Message from '../components/Message';
+import Loader from '../components/Loader';
+import FormContainer from '../components/FormContainer';
+import { listProductDetails, updateProduct } from '../actions/productActions';
+import { PRODUCT_UPDATE_RESET } from '../constants/productConstants';
const ProductEditScreen = ({ match, history }) => {
- const productId = match.params.id
+ const productId = match.params.id;
- const [name, setName] = useState('')
- const [price, setPrice] = useState(0)
- const [image, setImage] = useState('')
- const [brand, setBrand] = useState('')
- const [category, setCategory] = useState('')
- const [countInStock, setCountInStock] = useState(0)
- const [description, setDescription] = useState('')
- const [uploading, setUploading] = useState(false)
+ const [name, setName] = useState('');
+ const [price, setPrice] = useState(0);
+ const [image, setImage] = useState('');
+ const [brand, setBrand] = useState('');
+ const [category, setCategory] = useState('');
+ const [countInStock, setCountInStock] = useState(0);
+ const [description, setDescription] = useState('');
+ const [uploading, setUploading] = useState(false);
- const dispatch = useDispatch()
+ const dispatch = useDispatch();
- const productDetails = useSelector((state) => state.productDetails)
- const { loading, error, product } = productDetails
+ const productDetails = useSelector((state) => state.productDetails);
+ const { loading, error, product } = productDetails;
- const productUpdate = useSelector((state) => state.productUpdate)
+ const productUpdate = useSelector((state) => state.productUpdate);
const {
loading: loadingUpdate,
error: errorUpdate,
success: successUpdate,
- } = productUpdate
+ } = productUpdate;
useEffect(() => {
if (successUpdate) {
- dispatch({ type: PRODUCT_UPDATE_RESET })
- history.push('/admin/productlist')
+ dispatch({ type: PRODUCT_UPDATE_RESET });
+ history.push('/admin/productlist');
} else {
if (!product.name || product._id !== productId) {
- dispatch(listProductDetails(productId))
+ dispatch(listProductDetails(productId));
} else {
- setName(product.name)
- setPrice(product.price)
- setImage(product.image)
- setBrand(product.brand)
- setCategory(product.category)
- setCountInStock(product.countInStock)
- setDescription(product.description)
+ setName(product.name);
+ setPrice(product.price);
+ setImage(product.image);
+ setBrand(product.brand);
+ setCategory(product.category);
+ setCountInStock(product.countInStock);
+ setDescription(product.description);
}
}
- }, [dispatch, history, productId, product, successUpdate])
+ }, [dispatch, history, productId, product, successUpdate]);
const uploadFileHandler = async (e) => {
- const file = e.target.files[0]
- const formData = new FormData()
- formData.append('image', file)
- setUploading(true)
+ const file = e.target.files[0];
+ const formData = new FormData();
+ formData.append('image', file);
+ setUploading(true);
try {
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
- }
+ };
- const { data } = await axios.post('/api/upload', formData, config)
+ const { data } = await axios.post('/api/upload', formData, config);
- setImage(data)
- setUploading(false)
+ setImage(data);
+ setUploading(false);
} catch (error) {
- console.error(error)
- setUploading(false)
+ console.error(error);
+ setUploading(false);
}
- }
+ };
const submitHandler = (e) => {
- e.preventDefault()
+ e.preventDefault();
dispatch(
updateProduct({
_id: productId,
@@ -88,109 +88,109 @@ const ProductEditScreen = ({ match, history }) => {
description,
countInStock,
})
- )
- }
+ );
+ };
return (
<>
-
+
Go Back
Edit Product
{loadingUpdate && }
- {errorUpdate && {errorUpdate}}
+ {errorUpdate && {errorUpdate}}
{loading ? (
) : error ? (
- {error}
+ {error}
) : (
+
Name
setName(e.target.value)}
>
-
+
Price
setPrice(e.target.value)}
>
-
+
Image
setImage(e.target.value)}
>
{uploading && }
-
+
Brand
setBrand(e.target.value)}
>
-
+
Count In Stock
setCountInStock(e.target.value)}
>
-
+
Category
setCategory(e.target.value)}
>
-
+
Description
setDescription(e.target.value)}
>
-
>
- )
-}
+ );
+};
-export default ProductEditScreen
+export default ProductEditScreen;
diff --git a/frontend/src/screens/ProductScreen.js b/frontend/src/screens/ProductScreen.js
index f7375a61d..a1829ea9d 100644
--- a/frontend/src/screens/ProductScreen.js
+++ b/frontend/src/screens/ProductScreen.js
@@ -1,71 +1,90 @@
-import React, { useState, useEffect } from 'react'
-import { Link } from 'react-router-dom'
-import { useDispatch, useSelector } from 'react-redux'
-import { Row, Col, Image, ListGroup, Card, Button, Form } from 'react-bootstrap'
-import Rating from '../components/Rating'
-import Message from '../components/Message'
-import Loader from '../components/Loader'
-import Meta from '../components/Meta'
+import React, { useState, useEffect } from 'react';
+import { Link } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import {
+ Row,
+ Col,
+ Image,
+ ListGroup,
+ Card,
+ Button,
+ Form,
+} from 'react-bootstrap';
+import Rating from '../components/Rating';
+import Message from '../components/Message';
+import Loader from '../components/Loader';
+import Meta from '../components/Meta';
import {
listProductDetails,
createProductReview,
-} from '../actions/productActions'
-import { PRODUCT_CREATE_REVIEW_RESET } from '../constants/productConstants'
+} from '../actions/productActions';
+import {
+ PRODUCT_CREATE_REVIEW_RESET,
+ PRODUCT_UPDATE_STOCK_RESET,
+} from '../constants/productConstants';
const ProductScreen = ({ history, match }) => {
- const [qty, setQty] = useState(1)
- const [rating, setRating] = useState(0)
- const [comment, setComment] = useState('')
+ const [qty, setQty] = useState(1);
+ const [rating, setRating] = useState(0);
+ const [comment, setComment] = useState('');
+
+ const dispatch = useDispatch();
- const dispatch = useDispatch()
+ const productUpdateStock = useSelector((state) => state.productUpdateStock);
+ const { success: successStockUpdate } = productUpdateStock;
- const productDetails = useSelector((state) => state.productDetails)
- const { loading, error, product } = productDetails
+ const productDetails = useSelector((state) => state.productDetails);
+ const { loading, error, product } = productDetails;
- const userLogin = useSelector((state) => state.userLogin)
- const { userInfo } = userLogin
+ const userLogin = useSelector((state) => state.userLogin);
+ const { userInfo } = userLogin;
- const productReviewCreate = useSelector((state) => state.productReviewCreate)
+ const productReviewCreate = useSelector((state) => state.productReviewCreate);
const {
success: successProductReview,
loading: loadingProductReview,
error: errorProductReview,
- } = productReviewCreate
+ } = productReviewCreate;
useEffect(() => {
+ if (successStockUpdate) {
+ dispatch({ type: PRODUCT_UPDATE_STOCK_RESET });
+ }
+
if (successProductReview) {
- setRating(0)
- setComment('')
+ setRating(0);
+ setComment('');
}
if (!product._id || product._id !== match.params.id) {
- dispatch(listProductDetails(match.params.id))
- dispatch({ type: PRODUCT_CREATE_REVIEW_RESET })
+ dispatch({ type: PRODUCT_CREATE_REVIEW_RESET });
}
- }, [dispatch, match, successProductReview])
+ dispatch(listProductDetails(match.params.id));
+ }, [dispatch, match, successProductReview, product._id,
+ successStockUpdate,]);
const addToCartHandler = () => {
- history.push(`/cart/${match.params.id}?qty=${qty}`)
- }
+ history.push(`/cart/${match.params.id}?qty=${qty}`);
+ };
const submitHandler = (e) => {
- e.preventDefault()
+ e.preventDefault();
dispatch(
createProductReview(match.params.id, {
rating,
comment,
})
- )
- }
+ );
+ };
return (
<>
-
+
Go Back
{loading ? (
) : error ? (
- {error}
+ {error}
) : (
<>
@@ -74,7 +93,7 @@ const ProductScreen = ({ history, match }) => {
-
+
{product.name}
@@ -92,7 +111,7 @@ const ProductScreen = ({ history, match }) => {
-
+
Price:
@@ -117,7 +136,7 @@ const ProductScreen = ({ history, match }) => {
Qty
setQty(e.target.value)}
>
@@ -137,8 +156,8 @@ const ProductScreen = ({ history, match }) => {
Add To Cart
@@ -152,7 +171,7 @@ const ProductScreen = ({ history, match }) => {
Reviews
{product.reviews.length === 0 && No Reviews}
-
+
{product.reviews.map((review) => (
{review.name}
@@ -164,51 +183,51 @@ const ProductScreen = ({ history, match }) => {
Write a Customer Review
{successProductReview && (
-
+
Review submitted successfully
)}
{loadingProductReview && }
{errorProductReview && (
- {errorProductReview}
+ {errorProductReview}
)}
{userInfo ? (
+
Rating
setRating(e.target.value)}
>
-
-
-
-
-
-
+
+
+
+
+
+
-
+
Comment
setComment(e.target.value)}
>
Submit
) : (
- Please sign in to write a review{' '}
+ Please sign in to write a review{' '}
)}
@@ -218,7 +237,7 @@ const ProductScreen = ({ history, match }) => {
>
)}
>
- )
-}
+ );
+};
-export default ProductScreen
+export default ProductScreen;
diff --git a/frontend/src/store.js b/frontend/src/store.js
index 80b27484e..9dadae48d 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.js
@@ -7,6 +7,7 @@ import {
productDeleteReducer,
productCreateReducer,
productUpdateReducer,
+ productUpdateStockReducer,
productReviewCreateReducer,
productTopRatedReducer,
} from './reducers/productReducers'
@@ -35,6 +36,7 @@ const reducer = combineReducers({
productDelete: productDeleteReducer,
productCreate: productCreateReducer,
productUpdate: productUpdateReducer,
+ productUpdateStock:productUpdateStockReducer,
productReviewCreate: productReviewCreateReducer,
productTopRated: productTopRatedReducer,
cart: cartReducer,
diff --git a/package-lock.json b/package-lock.json
index c4131961f..43fbc029a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -916,9 +916,9 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
"ipaddr.js": {
diff --git a/package.json b/package.json
index 536e5600e..3c498d7f2 100644
--- a/package.json
+++ b/package.json
@@ -29,5 +29,14 @@
"devDependencies": {
"concurrently": "^5.3.0",
"nodemon": "^2.0.4"
- }
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/Pablets/proshop_mern.git"
+ },
+ "keywords": [],
+ "bugs": {
+ "url": "https://github.com/Pablets/proshop_mern/issues"
+ },
+ "homepage": "https://github.com/Pablets/proshop_mern#readme"
}
From 76ea248e0d8b03763ccf9e3c25f2059d1012a750 Mon Sep 17 00:00:00 2001
From: Pablets
Date: Sun, 7 Feb 2021 00:29:51 -0300
Subject: [PATCH 2/2] Fixed code format issue, now aligned with main repo.
---
backend/controllers/productController.js | 130 +++++++++---------
backend/models/orderModel.js | 8 +-
backend/routes/productRoutes.js | 2 +-
frontend/package.json | 2 +-
frontend/src/actions/productActions.js | 14 +-
frontend/src/reducers/cartReducers.js | 20 +--
frontend/src/reducers/productReducers.js | 90 ++++++------
frontend/src/screens/OrderScreen.js | 152 ++++++++++-----------
frontend/src/screens/ProductEditScreen.js | 158 +++++++++++-----------
frontend/src/screens/ProductScreen.js | 129 ++++++++----------
frontend/src/store.js | 2 +-
11 files changed, 349 insertions(+), 358 deletions(-)
diff --git a/backend/controllers/productController.js b/backend/controllers/productController.js
index 9c2bc292b..c96607dd4 100644
--- a/backend/controllers/productController.js
+++ b/backend/controllers/productController.js
@@ -1,12 +1,12 @@
-import asyncHandler from 'express-async-handler';
-import Product from '../models/productModel.js';
+import asyncHandler from 'express-async-handler'
+import Product from '../models/productModel.js'
// @desc Fetch all products
// @route GET /api/products
// @access Public
const getProducts = asyncHandler(async (req, res) => {
- const pageSize = 10;
- const page = Number(req.query.pageNumber) || 1;
+ const pageSize = 10
+ const page = Number(req.query.pageNumber) || 1
const keyword = req.query.keyword
? {
@@ -15,44 +15,44 @@ const getProducts = asyncHandler(async (req, res) => {
$options: 'i',
},
}
- : {};
+ : {}
- const count = await Product.countDocuments({ ...keyword });
+ const count = await Product.countDocuments({ ...keyword })
const products = await Product.find({ ...keyword })
.limit(pageSize)
- .skip(pageSize * (page - 1));
+ .skip(pageSize * (page - 1))
- res.json({ products, page, pages: Math.ceil(count / pageSize) });
-});
+ res.json({ products, page, pages: Math.ceil(count / pageSize) })
+})
// @desc Fetch single product
// @route GET /api/products/:id
// @access Public
const getProductById = asyncHandler(async (req, res) => {
- const product = await Product.findById(req.params.id);
+ const product = await Product.findById(req.params.id)
if (product) {
- res.json(product);
+ res.json(product)
} else {
- res.status(404);
- throw new Error('Product not found');
+ res.status(404)
+ throw new Error('Product not found')
}
-});
+})
// @desc Delete a product
// @route DELETE /api/products/:id
// @access Private/Admin
const deleteProduct = asyncHandler(async (req, res) => {
- const product = await Product.findById(req.params.id);
+ const product = await Product.findById(req.params.id)
if (product) {
- await product.remove();
- res.json({ message: 'Product removed' });
+ await product.remove()
+ res.json({ message: 'Product removed' })
} else {
- res.status(404);
- throw new Error('Product not found');
+ res.status(404)
+ throw new Error('Product not found')
}
-});
+})
// @desc Create a product
// @route POST /api/products
@@ -68,11 +68,11 @@ const createProduct = asyncHandler(async (req, res) => {
countInStock: 0,
numReviews: 0,
description: 'Sample description',
- });
+ })
- const createdProduct = await product.save();
- res.status(201).json(createdProduct);
-});
+ const createdProduct = await product.save()
+ res.status(201).json(createdProduct)
+})
// @desc Update a product
// @route PUT /api/products/:id
@@ -86,62 +86,62 @@ const updateProduct = asyncHandler(async (req, res) => {
brand,
category,
countInStock,
- } = req.body;
+ } = req.body
- const product = await Product.findById(req.params.id);
+ const product = await Product.findById(req.params.id)
if (product) {
- product.name = name;
- product.price = price;
- product.description = description;
- product.image = image;
- product.brand = brand;
- product.category = category;
- product.countInStock = countInStock;
-
- const updatedProduct = await product.save();
- res.json(updatedProduct);
+ product.name = name
+ product.price = price
+ product.description = description
+ product.image = image
+ product.brand = brand
+ product.category = category
+ product.countInStock = countInStock
+
+ const updatedProduct = await product.save()
+ res.json(updatedProduct)
} else {
- res.status(404);
- throw new Error('Product not found');
+ res.status(404)
+ throw new Error('Product not found')
}
-});
+})
// @desc Update a product
// @route PUT /api/products/:id
// @access Private/Admin
const updateProductStock = asyncHandler(async (req, res) => {
- const { countInStock } = req.body;
+ const { countInStock } = req.body
- const product = await Product.findById(req.params.id);
+ const product = await Product.findById(req.params.id)
if (product) {
- product.countInStock = countInStock;
+ product.countInStock = countInStock
- const updatedProductStock = await product.save();
- res.json(updatedProductStock);
+ const updatedProductStock = await product.save()
+ res.json(updatedProductStock)
} else {
- res.status(404);
- throw new Error('Product not found');
+ res.status(404)
+ throw new Error('Product not found')
}
-});
+})
// @desc Create new review
// @route POST /api/products/:id/reviews
// @access Private
const createProductReview = asyncHandler(async (req, res) => {
- const { rating, comment } = req.body;
+ const { rating, comment } = req.body
- const product = await Product.findById(req.params.id);
+ const product = await Product.findById(req.params.id)
if (product) {
const alreadyReviewed = product.reviews.find(
(r) => r.user.toString() === req.user._id.toString()
- );
+ )
if (alreadyReviewed) {
- res.status(400);
- throw new Error('Product already reviewed');
+ res.status(400)
+ throw new Error('Product already reviewed')
}
const review = {
@@ -149,32 +149,32 @@ const createProductReview = asyncHandler(async (req, res) => {
rating: Number(rating),
comment,
user: req.user._id,
- };
+ }
- product.reviews.push(review);
+ product.reviews.push(review)
- product.numReviews = product.reviews.length;
+ product.numReviews = product.reviews.length
product.rating =
product.reviews.reduce((acc, item) => item.rating + acc, 0) /
- product.reviews.length;
+ product.reviews.length
- await product.save();
- res.status(201).json({ message: 'Review added' });
+ await product.save()
+ res.status(201).json({ message: 'Review added' })
} else {
- res.status(404);
- throw new Error('Product not found');
+ res.status(404)
+ throw new Error('Product not found')
}
-});
+})
// @desc Get top rated products
// @route GET /api/products/top
// @access Public
const getTopProducts = asyncHandler(async (req, res) => {
- const products = await Product.find({}).sort({ rating: -1 }).limit(3);
+ const products = await Product.find({}).sort({ rating: -1 }).limit(3)
- res.json(products);
-});
+ res.json(products)
+})
export {
getProducts,
@@ -185,4 +185,4 @@ export {
updateProductStock,
createProductReview,
getTopProducts,
-};
+}
diff --git a/backend/models/orderModel.js b/backend/models/orderModel.js
index eed3efb01..ecfd01371 100644
--- a/backend/models/orderModel.js
+++ b/backend/models/orderModel.js
@@ -1,4 +1,4 @@
-import mongoose from 'mongoose';
+import mongoose from 'mongoose'
const orderSchema = mongoose.Schema(
{
@@ -72,8 +72,8 @@ const orderSchema = mongoose.Schema(
{
timestamps: true,
}
-);
+)
-const Order = mongoose.model('Order', orderSchema);
+const Order = mongoose.model('Order', orderSchema)
-export default Order;
+export default Order
diff --git a/backend/routes/productRoutes.js b/backend/routes/productRoutes.js
index e0c19eac5..16efc6f61 100644
--- a/backend/routes/productRoutes.js
+++ b/backend/routes/productRoutes.js
@@ -21,6 +21,6 @@ router
.delete(protect, admin, deleteProduct)
.put(protect, admin, updateProduct)
router.route('/:id/updatestock').put(protect, updateProductStock)
-router.route('/:id/stock').put(protect, updateProductStock);
+router.route('/:id/stock').put(protect, updateProductStock)
export default router
diff --git a/frontend/package.json b/frontend/package.json
index d03b99390..428344856 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -7,7 +7,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
- "axios": "^0.21.1",
+ "axios": "^0.20.0",
"react": "^16.13.1",
"react-bootstrap": "^1.3.0",
"react-dom": "^16.13.1",
diff --git a/frontend/src/actions/productActions.js b/frontend/src/actions/productActions.js
index f9415c6a7..cfa928bca 100644
--- a/frontend/src/actions/productActions.js
+++ b/frontend/src/actions/productActions.js
@@ -193,29 +193,29 @@ export const updateProductStock = (product) => async (dispatch, getState) => {
try {
dispatch({
type: PRODUCT_UPDATE_STOCK_REQUEST,
- });
+ })
const {
userLogin: { userInfo },
- } = getState();
+ } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`,
},
- };
+ }
const { data } = await axios.put(
`/api/products/${product._id}/stock`,
product,
config
- );
+ )
dispatch({
type: PRODUCT_UPDATE_STOCK_SUCCESS,
payload: data,
- });
+ })
} catch (error) {
dispatch({
type: PRODUCT_UPDATE_STOCK_FAIL,
@@ -223,9 +223,9 @@ export const updateProductStock = (product) => async (dispatch, getState) => {
error.response && error.response.data.message
? error.response.data.message
: error.message,
- });
+ })
}
-};
+}
export const createProductReview = (productId, review) => async (
dispatch,
diff --git a/frontend/src/reducers/cartReducers.js b/frontend/src/reducers/cartReducers.js
index 516306148..78dc505bc 100644
--- a/frontend/src/reducers/cartReducers.js
+++ b/frontend/src/reducers/cartReducers.js
@@ -3,7 +3,7 @@ import {
CART_REMOVE_ITEM,
CART_SAVE_SHIPPING_ADDRESS,
CART_SAVE_PAYMENT_METHOD,
-} from '../constants/cartConstants';
+} from '../constants/cartConstants'
export const cartReducer = (
state = { cartItems: [], shippingAddress: {} },
@@ -11,9 +11,9 @@ export const cartReducer = (
) => {
switch (action.type) {
case CART_ADD_ITEM:
- const item = action.payload;
+ const item = action.payload
- const existItem = state.cartItems.find((x) => x.product === item.product);
+ const existItem = state.cartItems.find((x) => x.product === item.product)
if (existItem) {
return {
@@ -21,29 +21,29 @@ export const cartReducer = (
cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x
),
- };
+ }
} else {
return {
...state,
cartItems: [...state.cartItems, item],
- };
+ }
}
case CART_REMOVE_ITEM:
return {
...state,
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,
- };
+ }
default:
- return state;
+ return state
}
-};
+}
diff --git a/frontend/src/reducers/productReducers.js b/frontend/src/reducers/productReducers.js
index 0b0c5d045..fb6672afb 100644
--- a/frontend/src/reducers/productReducers.js
+++ b/frontend/src/reducers/productReducers.js
@@ -27,25 +27,25 @@ import {
PRODUCT_UPDATE_STOCK_SUCCESS,
PRODUCT_UPDATE_STOCK_FAIL,
PRODUCT_UPDATE_STOCK_RESET,
-} from '../constants/productConstants';
+} from '../constants/productConstants'
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
- return { loading: true, products: [] };
+ return { loading: true, products: [] }
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
products: action.payload.products,
pages: action.payload.pages,
page: action.payload.page,
- };
+ }
case PRODUCT_LIST_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
default:
- return state;
+ return state
}
-};
+}
export const productDetailsReducer = (
state = { product: { reviews: [] } },
@@ -53,98 +53,98 @@ export const productDetailsReducer = (
) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
- return { ...state, loading: true };
+ return { ...state, loading: true }
case PRODUCT_DETAILS_SUCCESS:
- return { loading: false, product: action.payload };
+ return { loading: false, product: action.payload }
case PRODUCT_DETAILS_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
default:
- return state;
+ return state
}
-};
+}
export const productDeleteReducer = (state = {}, action) => {
switch (action.type) {
case PRODUCT_DELETE_REQUEST:
- return { loading: true };
+ return { loading: true }
case PRODUCT_DELETE_SUCCESS:
- return { loading: false, success: true };
+ return { loading: false, success: true }
case PRODUCT_DELETE_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
default:
- return state;
+ return state
}
-};
+}
export const productCreateReducer = (state = {}, action) => {
switch (action.type) {
case PRODUCT_CREATE_REQUEST:
- return { loading: true };
+ return { loading: true }
case PRODUCT_CREATE_SUCCESS:
- return { loading: false, success: true, product: action.payload };
+ return { loading: false, success: true, product: action.payload }
case PRODUCT_CREATE_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
case PRODUCT_CREATE_RESET:
- return {};
+ return {}
default:
- return state;
+ return state
}
-};
+}
export const productUpdateStockReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_UPDATE_REQUEST:
- return { loading: true };
+ return { loading: true }
case PRODUCT_UPDATE_SUCCESS:
- return { loading: false, success: true, product: action.payload };
+ return { loading: false, success: true, product: action.payload }
case PRODUCT_UPDATE_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
case PRODUCT_UPDATE_RESET:
- return { product: {} };
+ return { product: {} }
default:
- return state;
+ return state
}
-};
+}
export const productUpdateReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_UPDATE_STOCK_REQUEST:
- return { loading: true };
+ return { loading: true }
case PRODUCT_UPDATE_STOCK_SUCCESS:
- return { loading: false, success: true, product: action.payload };
+ return { loading: false, success: true, product: action.payload }
case PRODUCT_UPDATE_STOCK_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
case PRODUCT_UPDATE_STOCK_RESET:
- return { product: {} };
+ return { product: {} }
default:
- return state;
+ return state
}
-};
+}
export const productReviewCreateReducer = (state = {}, action) => {
switch (action.type) {
case PRODUCT_CREATE_REVIEW_REQUEST:
- return { loading: true };
+ return { loading: true }
case PRODUCT_CREATE_REVIEW_SUCCESS:
- return { loading: false, success: true };
+ return { loading: false, success: true }
case PRODUCT_CREATE_REVIEW_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
case PRODUCT_CREATE_REVIEW_RESET:
- return {};
+ return {}
default:
- return state;
+ return state
}
-};
+}
export const productTopRatedReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_TOP_REQUEST:
- return { loading: true, products: [] };
+ return { loading: true, products: [] }
case PRODUCT_TOP_SUCCESS:
- return { loading: false, products: action.payload };
+ return { loading: false, products: action.payload }
case PRODUCT_TOP_FAIL:
- return { loading: false, error: action.payload };
+ return { loading: false, error: action.payload }
default:
- return state;
+ return state
}
-};
+}
diff --git a/frontend/src/screens/OrderScreen.js b/frontend/src/screens/OrderScreen.js
index 8bc97c623..7d31b6836 100644
--- a/frontend/src/screens/OrderScreen.js
+++ b/frontend/src/screens/OrderScreen.js
@@ -1,119 +1,119 @@
-import React, { useState, useEffect } from 'react';
-import axios from 'axios';
-import { PayPalButton } from 'react-paypal-button-v2';
-import { Link } from 'react-router-dom';
-import { Row, Col, ListGroup, Image, Card, Button } from 'react-bootstrap';
-import { useDispatch, useSelector } from 'react-redux';
-import Message from '../components/Message';
-import Loader from '../components/Loader';
+import React, { useState, useEffect } from 'react'
+import axios from 'axios'
+import { PayPalButton } from 'react-paypal-button-v2'
+import { Link } from 'react-router-dom'
+import { Row, Col, ListGroup, Image, Card, Button } from 'react-bootstrap'
+import { useDispatch, useSelector } from 'react-redux'
+import Message from '../components/Message'
+import Loader from '../components/Loader'
import {
getOrderDetails,
payOrder,
deliverOrder,
-} from '../actions/orderActions';
+} from '../actions/orderActions'
import {
ORDER_PAY_RESET,
ORDER_DELIVER_RESET,
ORDER_LIST_MY_RESET,
-} from '../constants/orderConstants';
-import { PRODUCT_UPDATE_STOCK_RESET } from '../constants/productConstants';
-import { removeFromCart } from '../actions/cartActions';
-import { updateProductStock } from '../actions/productActions';
+} from '../constants/orderConstants'
+import { PRODUCT_UPDATE_STOCK_RESET } from '../constants/productConstants'
+import { removeFromCart } from '../actions/cartActions'
+import { updateProductStock } from '../actions/productActions'
const OrderScreen = ({ match, history }) => {
- const orderId = match.params.id;
+ const orderId = match.params.id
- const [sdkReady, setSdkReady] = useState(false);
+ const [sdkReady, setSdkReady] = useState(false)
- const dispatch = useDispatch();
+ const dispatch = useDispatch()
- const cart = useSelector((state) => state.cart);
- const { cartItems } = cart;
+ const cart = useSelector((state) => state.cart)
+ const { cartItems } = cart
// console.log(cartItems);
- const [countInStock, setCountInStock] = useState(0);
+ const [countInStock, setCountInStock] = useState(0)
- const productUpdateStock = useSelector((state) => state.productUpdateStock);
- const { success: successStockUpdate } = productUpdateStock;
+ const productUpdateStock = useSelector((state) => state.productUpdateStock)
+ const { success: successStockUpdate } = productUpdateStock
- const orderDetails = useSelector((state) => state.orderDetails);
- const { order, loading, error } = orderDetails;
+ const orderDetails = useSelector((state) => state.orderDetails)
+ const { order, loading, error } = orderDetails
- const orderPay = useSelector((state) => state.orderPay);
- const { loading: loadingPay, success: successPay } = orderPay;
+ const orderPay = useSelector((state) => state.orderPay)
+ const { loading: loadingPay, success: successPay } = orderPay
- const orderDeliver = useSelector((state) => state.orderDeliver);
- const { loading: loadingDeliver, success: successDeliver } = orderDeliver;
+ const orderDeliver = useSelector((state) => state.orderDeliver)
+ const { loading: loadingDeliver, success: successDeliver } = orderDeliver
- const userLogin = useSelector((state) => state.userLogin);
- const { userInfo } = userLogin;
+ const userLogin = useSelector((state) => state.userLogin)
+ const { userInfo } = userLogin
if (!loading) {
// Calculate prices
const addDecimals = (num) => {
- return (Math.round(num * 100) / 100).toFixed(2);
- };
+ return (Math.round(num * 100) / 100).toFixed(2)
+ }
order.itemsPrice = addDecimals(
order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0)
- );
+ )
}
useEffect(() => {
if (!userInfo) {
- history.push('/login');
+ history.push('/login')
}
const addPayPalScript = async () => {
- const { data: clientId } = await axios.get('/api/config/paypal');
- const script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`;
- script.async = true;
+ const { data: clientId } = await axios.get('/api/config/paypal')
+ const script = document.createElement('script')
+ script.type = 'text/javascript'
+ script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`
+ script.async = true
script.onload = () => {
- setSdkReady(true);
- };
- document.body.appendChild(script);
- };
+ setSdkReady(true)
+ }
+ document.body.appendChild(script)
+ }
if (!order || successPay || successDeliver || order._id !== orderId) {
- dispatch({ type: ORDER_PAY_RESET });
- dispatch({ type: ORDER_DELIVER_RESET });
- dispatch(getOrderDetails(orderId));
+ dispatch({ type: ORDER_PAY_RESET })
+ dispatch({ type: ORDER_DELIVER_RESET })
+ dispatch(getOrderDetails(orderId))
} else if (!order.isPaid) {
if (!window.paypal) {
- addPayPalScript();
+ addPayPalScript()
} else {
- setSdkReady(true);
+ setSdkReady(true)
}
}
if (successStockUpdate) {
- console.log('PRODUCT_UPDATE_STOCK_RESET');
- dispatch({ type: PRODUCT_UPDATE_STOCK_RESET });
+ console.log('PRODUCT_UPDATE_STOCK_RESET')
+ dispatch({ type: PRODUCT_UPDATE_STOCK_RESET })
}
if (order && successPay) {
- console.log('sale del loop');
+ console.log('sale del loop')
order.orderItems.forEach((item, i) => {
console.log(
`countinstock: ${cartItems[i].countInStock}
-
qty: ${item.qty}
Equals: ${cartItems[i].countInStock - item.qty}`
- );
- const updatedStock = cartItems[i].countInStock - item.qty;
- setCountInStock(cartItems[i].countInStock - item.qty);
+ )
+ const updatedStock = cartItems[i].countInStock - item.qty
+ setCountInStock(cartItems[i].countInStock - item.qty)
dispatch(
updateProductStock({
_id: item.product,
countInStock: updatedStock,
})
- );
- dispatch(removeFromCart(item.product));
- });
- dispatch({ type: ORDER_LIST_MY_RESET });
+ )
+ dispatch(removeFromCart(item.product))
+ })
+ dispatch({ type: ORDER_LIST_MY_RESET })
}
}, [
history,
@@ -126,27 +126,27 @@ const OrderScreen = ({ match, history }) => {
cartItems,
countInStock,
successStockUpdate,
- ]);
+ ])
const successPaymentHandler = (paymentResult) => {
- console.log(paymentResult);
- dispatch(payOrder(orderId, paymentResult));
- };
+ console.log(paymentResult)
+ dispatch(payOrder(orderId, paymentResult))
+ }
const deliverHandler = () => {
- dispatch(deliverOrder(order));
- };
+ dispatch(deliverOrder(order))
+ }
return loading ? (
) : error ? (
- {error}
+ {error}
) : (
<>
Order {order._id}
-
+
Shipping
@@ -163,11 +163,11 @@ const OrderScreen = ({ match, history }) => {
{order.shippingAddress.country}
{order.isDelivered ? (
-
+
Delivered on {order.deliveredAt}
) : (
- Not Delivered
+ Not Delivered
)}
@@ -178,9 +178,9 @@ const OrderScreen = ({ match, history }) => {
{order.paymentMethod}
{order.isPaid ? (
- Paid on {order.paidAt}
+ Paid on {order.paidAt}
) : (
- Not Paid
+ Not Paid
)}
@@ -189,7 +189,7 @@ const OrderScreen = ({ match, history }) => {
{order.orderItems.length === 0 ? (
Order is empty
) : (
-
+
{order.orderItems.map((item, index) => (
@@ -219,7 +219,7 @@ const OrderScreen = ({ match, history }) => {
-
+
Order Summary
@@ -267,8 +267,8 @@ const OrderScreen = ({ match, history }) => {
!order.isDelivered && (
Mark As Delivered
@@ -280,7 +280,7 @@ const OrderScreen = ({ match, history }) => {
>
- );
-};
+ )
+}
-export default OrderScreen;
+export default OrderScreen
diff --git a/frontend/src/screens/ProductEditScreen.js b/frontend/src/screens/ProductEditScreen.js
index 0b9470d7b..b314ee705 100644
--- a/frontend/src/screens/ProductEditScreen.js
+++ b/frontend/src/screens/ProductEditScreen.js
@@ -1,82 +1,82 @@
-import axios from 'axios';
-import React, { useState, useEffect } from 'react';
-import { Link } from 'react-router-dom';
-import { Form, Button } from 'react-bootstrap';
-import { useDispatch, useSelector } from 'react-redux';
-import Message from '../components/Message';
-import Loader from '../components/Loader';
-import FormContainer from '../components/FormContainer';
-import { listProductDetails, updateProduct } from '../actions/productActions';
-import { PRODUCT_UPDATE_RESET } from '../constants/productConstants';
+import axios from 'axios'
+import React, { useState, useEffect } from 'react'
+import { Link } from 'react-router-dom'
+import { Form, Button } from 'react-bootstrap'
+import { useDispatch, useSelector } from 'react-redux'
+import Message from '../components/Message'
+import Loader from '../components/Loader'
+import FormContainer from '../components/FormContainer'
+import { listProductDetails, updateProduct } from '../actions/productActions'
+import { PRODUCT_UPDATE_RESET } from '../constants/productConstants'
const ProductEditScreen = ({ match, history }) => {
- const productId = match.params.id;
+ const productId = match.params.id
- const [name, setName] = useState('');
- const [price, setPrice] = useState(0);
- const [image, setImage] = useState('');
- const [brand, setBrand] = useState('');
- const [category, setCategory] = useState('');
- const [countInStock, setCountInStock] = useState(0);
- const [description, setDescription] = useState('');
- const [uploading, setUploading] = useState(false);
+ const [name, setName] = useState('')
+ const [price, setPrice] = useState(0)
+ const [image, setImage] = useState('')
+ const [brand, setBrand] = useState('')
+ const [category, setCategory] = useState('')
+ const [countInStock, setCountInStock] = useState(0)
+ const [description, setDescription] = useState('')
+ const [uploading, setUploading] = useState(false)
- const dispatch = useDispatch();
+ const dispatch = useDispatch()
- const productDetails = useSelector((state) => state.productDetails);
- const { loading, error, product } = productDetails;
+ const productDetails = useSelector((state) => state.productDetails)
+ const { loading, error, product } = productDetails
- const productUpdate = useSelector((state) => state.productUpdate);
+ const productUpdate = useSelector((state) => state.productUpdate)
const {
loading: loadingUpdate,
error: errorUpdate,
success: successUpdate,
- } = productUpdate;
+ } = productUpdate
useEffect(() => {
if (successUpdate) {
- dispatch({ type: PRODUCT_UPDATE_RESET });
- history.push('/admin/productlist');
+ dispatch({ type: PRODUCT_UPDATE_RESET })
+ history.push('/admin/productlist')
} else {
if (!product.name || product._id !== productId) {
- dispatch(listProductDetails(productId));
+ dispatch(listProductDetails(productId))
} else {
- setName(product.name);
- setPrice(product.price);
- setImage(product.image);
- setBrand(product.brand);
- setCategory(product.category);
- setCountInStock(product.countInStock);
- setDescription(product.description);
+ setName(product.name)
+ setPrice(product.price)
+ setImage(product.image)
+ setBrand(product.brand)
+ setCategory(product.category)
+ setCountInStock(product.countInStock)
+ setDescription(product.description)
}
}
- }, [dispatch, history, productId, product, successUpdate]);
+ }, [dispatch, history, productId, product, successUpdate])
const uploadFileHandler = async (e) => {
- const file = e.target.files[0];
- const formData = new FormData();
- formData.append('image', file);
- setUploading(true);
+ const file = e.target.files[0]
+ const formData = new FormData()
+ formData.append('image', file)
+ setUploading(true)
try {
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
- };
+ }
- const { data } = await axios.post('/api/upload', formData, config);
+ const { data } = await axios.post('/api/upload', formData, config)
- setImage(data);
- setUploading(false);
+ setImage(data)
+ setUploading(false)
} catch (error) {
- console.error(error);
- setUploading(false);
+ console.error(error)
+ setUploading(false)
}
- };
+ }
const submitHandler = (e) => {
- e.preventDefault();
+ e.preventDefault()
dispatch(
updateProduct({
_id: productId,
@@ -88,109 +88,109 @@ const ProductEditScreen = ({ match, history }) => {
description,
countInStock,
})
- );
- };
+ )
+ }
return (
<>
-
+
Go Back
Edit Product
{loadingUpdate && }
- {errorUpdate && {errorUpdate}}
+ {errorUpdate && {errorUpdate}}
{loading ? (
) : error ? (
- {error}
+ {error}
) : (
+
Name
setName(e.target.value)}
>
-
+
Price
setPrice(e.target.value)}
>
-
+
Image
setImage(e.target.value)}
>
{uploading && }
-
+
Brand
setBrand(e.target.value)}
>
-
+
Count In Stock
setCountInStock(e.target.value)}
>
-
+
Category
setCategory(e.target.value)}
>
-
+
Description
setDescription(e.target.value)}
>
-
+
Update
)}
>
- );
-};
+ )
+}
-export default ProductEditScreen;
+export default ProductEditScreen
diff --git a/frontend/src/screens/ProductScreen.js b/frontend/src/screens/ProductScreen.js
index a1829ea9d..18c1c146f 100644
--- a/frontend/src/screens/ProductScreen.js
+++ b/frontend/src/screens/ProductScreen.js
@@ -1,90 +1,81 @@
-import React, { useState, useEffect } from 'react';
-import { Link } from 'react-router-dom';
-import { useDispatch, useSelector } from 'react-redux';
-import {
- Row,
- Col,
- Image,
- ListGroup,
- Card,
- Button,
- Form,
-} from 'react-bootstrap';
-import Rating from '../components/Rating';
-import Message from '../components/Message';
-import Loader from '../components/Loader';
-import Meta from '../components/Meta';
+import React, { useState, useEffect } from 'react'
+import { Link } from 'react-router-dom'
+import { useDispatch, useSelector } from 'react-redux'
+import { Row, Col, Image, ListGroup, Card, Button, Form } from 'react-bootstrap'
+import Rating from '../components/Rating'
+import Message from '../components/Message'
+import Loader from '../components/Loader'
+import Meta from '../components/Meta'
import {
listProductDetails,
createProductReview,
-} from '../actions/productActions';
+} from '../actions/productActions'
import {
PRODUCT_CREATE_REVIEW_RESET,
PRODUCT_UPDATE_STOCK_RESET,
-} from '../constants/productConstants';
+} from '../constants/productConstants'
const ProductScreen = ({ history, match }) => {
- const [qty, setQty] = useState(1);
- const [rating, setRating] = useState(0);
- const [comment, setComment] = useState('');
+ const [qty, setQty] = useState(1)
+ const [rating, setRating] = useState(0)
+ const [comment, setComment] = useState('')
- const dispatch = useDispatch();
+ const dispatch = useDispatch()
- const productUpdateStock = useSelector((state) => state.productUpdateStock);
- const { success: successStockUpdate } = productUpdateStock;
+ const productUpdateStock = useSelector((state) => state.productUpdateStock)
+ const { success: successStockUpdate } = productUpdateStock
- const productDetails = useSelector((state) => state.productDetails);
- const { loading, error, product } = productDetails;
+ const productDetails = useSelector((state) => state.productDetails)
+ const { loading, error, product } = productDetails
- const userLogin = useSelector((state) => state.userLogin);
- const { userInfo } = userLogin;
+ const userLogin = useSelector((state) => state.userLogin)
+ const { userInfo } = userLogin
- const productReviewCreate = useSelector((state) => state.productReviewCreate);
+ const productReviewCreate = useSelector((state) => state.productReviewCreate)
const {
success: successProductReview,
loading: loadingProductReview,
error: errorProductReview,
- } = productReviewCreate;
+ } = productReviewCreate
useEffect(() => {
if (successStockUpdate) {
- dispatch({ type: PRODUCT_UPDATE_STOCK_RESET });
+ dispatch({ type: PRODUCT_UPDATE_STOCK_RESET })
}
if (successProductReview) {
- setRating(0);
- setComment('');
+ setRating(0)
+ setComment('')
}
if (!product._id || product._id !== match.params.id) {
- dispatch({ type: PRODUCT_CREATE_REVIEW_RESET });
+ dispatch({ type: PRODUCT_CREATE_REVIEW_RESET })
}
- dispatch(listProductDetails(match.params.id));
- }, [dispatch, match, successProductReview, product._id,
- successStockUpdate,]);
+ dispatch(listProductDetails(match.params.id))
+ }, [dispatch, match, successProductReview, product._id, successStockUpdate])
const addToCartHandler = () => {
- history.push(`/cart/${match.params.id}?qty=${qty}`);
- };
+ history.push(`/cart/${match.params.id}?qty=${qty}`)
+ }
const submitHandler = (e) => {
- e.preventDefault();
+ e.preventDefault()
dispatch(
createProductReview(match.params.id, {
rating,
comment,
})
- );
- };
+ )
+ }
return (
<>
-
+
Go Back
{loading ? (
) : error ? (
- {error}
+ {error}
) : (
<>
@@ -93,7 +84,7 @@ const ProductScreen = ({ history, match }) => {
-
+
{product.name}
@@ -111,7 +102,7 @@ const ProductScreen = ({ history, match }) => {
-
+
Price:
@@ -136,7 +127,7 @@ const ProductScreen = ({ history, match }) => {
Qty
setQty(e.target.value)}
>
@@ -156,8 +147,8 @@ const ProductScreen = ({ history, match }) => {
Add To Cart
@@ -171,7 +162,7 @@ const ProductScreen = ({ history, match }) => {
Reviews
{product.reviews.length === 0 && No Reviews}
-
+
{product.reviews.map((review) => (
{review.name}
@@ -183,51 +174,51 @@ const ProductScreen = ({ history, match }) => {
Write a Customer Review
{successProductReview && (
-
+
Review submitted successfully
)}
{loadingProductReview && }
{errorProductReview && (
- {errorProductReview}
+ {errorProductReview}
)}
{userInfo ? (
+
Rating
setRating(e.target.value)}
>
-
-
-
-
-
-
+
+
+
+
+
+
-
+
Comment
setComment(e.target.value)}
>
Submit
) : (
- Please sign in to write a review{' '}
+ Please sign in to write a review{' '}
)}
@@ -237,7 +228,7 @@ const ProductScreen = ({ history, match }) => {
>
)}
>
- );
-};
+ )
+}
-export default ProductScreen;
+export default ProductScreen
diff --git a/frontend/src/store.js b/frontend/src/store.js
index 9dadae48d..3d8d60e0a 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.js
@@ -36,7 +36,7 @@ const reducer = combineReducers({
productDelete: productDeleteReducer,
productCreate: productCreateReducer,
productUpdate: productUpdateReducer,
- productUpdateStock:productUpdateStockReducer,
+ productUpdateStock: productUpdateStockReducer,
productReviewCreate: productReviewCreateReducer,
productTopRated: productTopRatedReducer,
cart: cartReducer,