From f161c915d9e4246f987d54786dba2913b556782f Mon Sep 17 00:00:00 2001 From: JaberHPranto Date: Tue, 13 Jul 2021 01:04:17 +0600 Subject: [PATCH] admin can now update/create a product --- .../Ecommerce/Screen/ProductEditScreen.js | 142 ++++++++++++++++++ .../Ecommerce/Screen/ProductListScreen.js | 37 +++-- client/src/constants/productConstants.js | 12 +- client/src/pages/EcommercePage.js | 2 + client/src/redux/actions/productActions.js | 76 +++++++++- client/src/redux/reducers/productReducers.js | 40 ++++- client/src/store.js | 6 +- 7 files changed, 299 insertions(+), 16 deletions(-) create mode 100644 client/src/components/Ecommerce/Screen/ProductEditScreen.js diff --git a/client/src/components/Ecommerce/Screen/ProductEditScreen.js b/client/src/components/Ecommerce/Screen/ProductEditScreen.js new file mode 100644 index 0000000..13e9270 --- /dev/null +++ b/client/src/components/Ecommerce/Screen/ProductEditScreen.js @@ -0,0 +1,142 @@ +import React, { useEffect, useState } from 'react' +import { Button, Form } from 'react-bootstrap' +import { useDispatch, useSelector } from 'react-redux' +import { Link } from 'react-router-dom' +import { PRODUCT_UPDATE_RESET } from '../../../constants/productConstants' +import { fetchProductById, updateProduct } from '../../../redux/actions/productActions' +import FormContainer from '../FormContainer' +import Loader from '../Loader' +import Message from '../Message' +import { toastSuccessMessage } from '../ToastMessage' + +const ProductEditScreen = ({ match, history }) => { + const productId = match.params.id + + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [category, setCategory] = useState('') + const [price, setPrice] = useState(0) + const [countInStock, setCountInStock] = useState(0) + const [image, setImage] = useState('') + + const dispatch = useDispatch() + + const productDetails = useSelector((state) => state.productDetails) + const { loading, error, product } = productDetails + const productUpdate = useSelector((state) => state.productUpdate) + const { loading:loadingUpdate, error:errorUpdate, success:successUpdate } = productUpdate + + + useEffect(() => { + if (successUpdate) { + dispatch({ type: PRODUCT_UPDATE_RESET }) + toastSuccessMessage("Product Updated") + history.push("/admin/productlist") + + } else { + if (!product.name || product._id !== productId) { + dispatch(fetchProductById(productId)) + } else { + setName(product.name) + setDescription(product.description) + setCategory(product.category) + setImage(product.image) + setPrice(product.price) + setCountInStock(product.countInStock) + } + } + + }, [dispatch,product,productId,successUpdate,history]) + + const submitHandler = (e) => { + e.preventDefault() + dispatch(updateProduct({ + _id: product._id,name,description,image,category,price,countInStock + })) + } + + return ( + <> + + Go Back + + +

Edit Product

+ {loadingUpdate && } + {errorUpdate && {errorUpdate}} + {loading ? ( + + ) : error ? ( + {error} + ) : ( +
+ + Name + setName(e.target.value)} + > + + + + Description + setDescription(e.target.value)} + > + + + + Category + setCategory(e.target.value)} + > + + + + Image + setImage(e.target.value)} + > + + + + Price + setPrice(e.target.value)} + > + + + + Count In Stock + setCountInStock(e.target.value)} + > + + +
+ )} +
+ + ) +} + +export default ProductEditScreen \ No newline at end of file diff --git a/client/src/components/Ecommerce/Screen/ProductListScreen.js b/client/src/components/Ecommerce/Screen/ProductListScreen.js index 593134d..3aff84e 100644 --- a/client/src/components/Ecommerce/Screen/ProductListScreen.js +++ b/client/src/components/Ecommerce/Screen/ProductListScreen.js @@ -2,7 +2,8 @@ import React, { useEffect } from 'react' import { Button, Col, Row, Table } from 'react-bootstrap' import { useDispatch, useSelector } from 'react-redux' import { LinkContainer } from 'react-router-bootstrap' -import { deleteProduct, fetchProducts } from '../../../redux/actions/productActions' +import { PRODUCT_CREATE_RESET } from '../../../constants/productConstants' +import { createProduct, deleteProduct, fetchProducts } from '../../../redux/actions/productActions' import Loader from '../Loader' import Message from '../Message' import { toastErrorMessage } from '../ToastMessage' @@ -15,25 +16,37 @@ function UserList({history}) { const productList = useSelector(state => state.productList) const { loading, error, products } = productList + + const productCreate = useSelector(state => state.productCreate) + const { loading: loadingCreate, error: errorCreate, success: successCreate, product:createdProduct } = productCreate const productDelete = useSelector(state => state.productDelete) const { loading:loadingDelete, error:errorDelete, success:successDelete } = productDelete + useEffect(() => { - if (user && user.isAdmin) { + dispatch({ type: PRODUCT_CREATE_RESET }) + + if (!user.isAdmin) + history.push('/login') + + if (successCreate) { + history.push(`/admin/product/${createdProduct._id}/edit`) + } + else { dispatch(fetchProducts()) - } else { - history.push("/login") } - }, [dispatch,history,user,successDelete]) + }, [dispatch,history,user,successDelete,successCreate,createdProduct]) const handleDelete = (id) => { - if (window.confirm('Are you sure you want to delete this user ?')) { + if (window.confirm('Are you sure you want to delete this product ?')) { dispatch(deleteProduct(id)) if(!errorDelete) - toastErrorMessage("User deleted") + toastErrorMessage("Product deleted") } } - const handleCreateProduct= () => {} + const handleCreateProduct = () => { + dispatch(createProduct()) + } return ( <> @@ -42,10 +55,12 @@ function UserList({history}) {

Products

- + + {loadingCreate && } + {errorCreate && {errorCreate}} {loadingDelete && } {errorDelete && {errorDelete}} @@ -58,7 +73,7 @@ function UserList({history}) { Name Price Category - Count In Stock + In Stock @@ -70,7 +85,7 @@ function UserList({history}) { {product.category} {product.countInStock} - + diff --git a/client/src/constants/productConstants.js b/client/src/constants/productConstants.js index b119a9d..fb18552 100644 --- a/client/src/constants/productConstants.js +++ b/client/src/constants/productConstants.js @@ -13,4 +13,14 @@ export const PRODUCT_CREATE_REVIEW_RESET = "PRODUCT_CREATE_REVIEW_RESET" export const PRODUCT_DELETE_REQUEST = "PRODUCT_DELETE_REQUEST" export const PRODUCT_DELETE_SUCCESS = "PRODUCT_DELETE_SUCCESS" -export const PRODUCT_DELETE_FAIL = "PRODUCT_DELETE_FAIL" \ No newline at end of file +export const PRODUCT_DELETE_FAIL = "PRODUCT_DELETE_FAIL" + +export const PRODUCT_CREATE_REQUEST = "PRODUCT_CREATE_REQUEST" +export const PRODUCT_CREATE_SUCCESS = "PRODUCT_CREATE_SUCCESS" +export const PRODUCT_CREATE_FAIL = "PRODUCT_CREATE_FAIL" +export const PRODUCT_CREATE_RESET = "PRODUCT_CREATE_RESET" + +export const PRODUCT_UPDATE_REQUEST = "PRODUCT_UPDATE_REQUEST" +export const PRODUCT_UPDATE_SUCCESS = "PRODUCT_UPDATE_SUCCESS" +export const PRODUCT_UPDATE_FAIL = "PRODUCT_UPDATE_FAIL" +export const PRODUCT_UPDATE_RESET = "PRODUCT_UPDATE_RESET" \ No newline at end of file diff --git a/client/src/pages/EcommercePage.js b/client/src/pages/EcommercePage.js index f0710af..f95f4cd 100644 --- a/client/src/pages/EcommercePage.js +++ b/client/src/pages/EcommercePage.js @@ -10,6 +10,7 @@ import LoginScreen from '../components/Ecommerce/Screen/LoginScreen'; import PasswordResetScreen from '../components/Ecommerce/Screen/PasswordResetScreen'; import PaymentScreen from "../components/Ecommerce/Screen/PaymentScreen"; import PlaceOrderScreen from "../components/Ecommerce/Screen/PlaceOrderScreen"; +import ProductEditScreen from "../components/Ecommerce/Screen/ProductEditScreen"; import ProductListScreen from "../components/Ecommerce/Screen/ProductListScreen"; import ProductScreen from '../components/Ecommerce/Screen/ProductScreen'; import ProfileScreen from '../components/Ecommerce/Screen/ProfileScreen'; @@ -35,6 +36,7 @@ function EcommercePage() { + diff --git a/client/src/redux/actions/productActions.js b/client/src/redux/actions/productActions.js index 5dd033c..3f31cc9 100644 --- a/client/src/redux/actions/productActions.js +++ b/client/src/redux/actions/productActions.js @@ -1,5 +1,5 @@ import axios from 'axios'; -import { PRODUCT_CREATE_REVIEW_FAIL, PRODUCT_CREATE_REVIEW_REQUEST, PRODUCT_CREATE_REVIEW_SUCCESS, PRODUCT_DELETE_FAIL, PRODUCT_DELETE_REQUEST, PRODUCT_DELETE_SUCCESS, PRODUCT_DETAILS_FAIL, PRODUCT_DETAILS_REQUEST, PRODUCT_DETAILS_SUCCESS, PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from '../../constants/productConstants'; +import { PRODUCT_CREATE_FAIL, PRODUCT_CREATE_REQUEST, PRODUCT_CREATE_REVIEW_FAIL, PRODUCT_CREATE_REVIEW_REQUEST, PRODUCT_CREATE_REVIEW_SUCCESS, PRODUCT_CREATE_SUCCESS, PRODUCT_DELETE_FAIL, PRODUCT_DELETE_REQUEST, PRODUCT_DELETE_SUCCESS, PRODUCT_DETAILS_FAIL, PRODUCT_DETAILS_REQUEST, PRODUCT_DETAILS_SUCCESS, PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_UPDATE_FAIL, PRODUCT_UPDATE_REQUEST, PRODUCT_UPDATE_SUCCESS } from '../../constants/productConstants'; // @ GET products export const fetchProducts = (keyword='',pageNumber='') => async (dispatch) => { @@ -109,4 +109,78 @@ export const deleteProduct = (id) => async (dispatch,getState) => { } +} + +// Create products +export const createProduct = () => async (dispatch,getState) => { + + try { + dispatch({ + type:PRODUCT_CREATE_REQUEST + }) + + const { userLogin: { userInfo } } = getState() + + + const config = { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${userInfo.token}` + } + } + + const { data } = await axios.post(`/api/products`,{},config) + + dispatch({ + type: PRODUCT_CREATE_SUCCESS, + payload:data + }) + + } catch (error) { + console.log(error); + dispatch({ + type: PRODUCT_CREATE_FAIL, + payload: error.response && error.response.data.message ? error.response.data.message : error.message + + }) + + } + +} + +// Update Products +export const updateProduct = (product) => async (dispatch,getState) => { + + try { + dispatch({ + type:PRODUCT_UPDATE_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}`,product,config) + + dispatch({ + type: PRODUCT_UPDATE_SUCCESS, + payload:data + }) + + } catch (error) { + console.log(error); + dispatch({ + type: PRODUCT_UPDATE_FAIL, + payload: error.response && error.response.data.message ? error.response.data.message : error.message + + }) + + } + } \ No newline at end of file diff --git a/client/src/redux/reducers/productReducers.js b/client/src/redux/reducers/productReducers.js index b02c8b6..95095b4 100644 --- a/client/src/redux/reducers/productReducers.js +++ b/client/src/redux/reducers/productReducers.js @@ -1,4 +1,4 @@ -import { PRODUCT_CREATE_REVIEW_FAIL, PRODUCT_CREATE_REVIEW_REQUEST, PRODUCT_CREATE_REVIEW_RESET, PRODUCT_CREATE_REVIEW_SUCCESS, PRODUCT_DELETE_FAIL, PRODUCT_DELETE_REQUEST, PRODUCT_DELETE_SUCCESS, PRODUCT_DETAILS_FAIL, PRODUCT_DETAILS_REQUEST, PRODUCT_DETAILS_SUCCESS, PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from '../../constants/productConstants' +import { PRODUCT_CREATE_FAIL, PRODUCT_CREATE_REQUEST, PRODUCT_CREATE_RESET, PRODUCT_CREATE_REVIEW_FAIL, PRODUCT_CREATE_REVIEW_REQUEST, PRODUCT_CREATE_REVIEW_RESET, PRODUCT_CREATE_REVIEW_SUCCESS, PRODUCT_CREATE_SUCCESS, PRODUCT_DELETE_FAIL, PRODUCT_DELETE_REQUEST, PRODUCT_DELETE_SUCCESS, PRODUCT_DETAILS_FAIL, PRODUCT_DETAILS_REQUEST, PRODUCT_DETAILS_SUCCESS, PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_UPDATE_FAIL, PRODUCT_UPDATE_REQUEST, PRODUCT_UPDATE_RESET, PRODUCT_UPDATE_SUCCESS } from '../../constants/productConstants' export const productListReducer = (state = { products: [] }, action) => { switch (action.type) { @@ -62,6 +62,44 @@ export const productDeleteReducer = (state = {}, action) => { case PRODUCT_DELETE_FAIL: return { loading: false, error: action.payload } + default: + return state + } +} + +export const productCreateReducer = (state = {}, action) => { + switch (action.type) { + case PRODUCT_CREATE_REQUEST: + return { loading: true } + + case PRODUCT_CREATE_SUCCESS: + return { loading: false,success:true,product:action.payload} + + case PRODUCT_CREATE_FAIL: + return { loading: false, error: action.payload } + + case PRODUCT_CREATE_RESET: + return {} + + default: + return state + } +} + +export const productUpdateReducer = (state = {product:{}}, action) => { + switch (action.type) { + case PRODUCT_UPDATE_REQUEST: + return { loading: true } + + case PRODUCT_UPDATE_SUCCESS: + return { loading: false,success:true,product:action.payload} + + case PRODUCT_UPDATE_FAIL: + return { loading: false, error: action.payload } + + case PRODUCT_UPDATE_RESET: + return { product: {} } + default: return state } diff --git a/client/src/store.js b/client/src/store.js index cfa55af..3390037 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -4,8 +4,8 @@ import thunk from "redux-thunk"; import { cartReducer } from "./redux/reducers/cartReducers"; import { orderCreateReducer } from "./redux/reducers/orderReducers"; import { - productCreateReviewReducer, productDeleteReducer, productDetailsReducer, - productListReducer + productCreateReducer, productCreateReviewReducer, productDeleteReducer, productDetailsReducer, + productListReducer, productUpdateReducer } from "./redux/reducers/productReducers"; import { userDeleteReducer, userDetailsReducer, userListReducer, userLoginReducer, @@ -18,6 +18,8 @@ const reducer = combineReducers({ productDetails: productDetailsReducer, productCreateReview: productCreateReviewReducer, productDelete: productDeleteReducer, + productCreate: productCreateReducer, + productUpdate: productUpdateReducer, userLogin: userLoginReducer, userRegister: userRegisterReducer, userDetails: userDetailsReducer,