diff --git a/client/src/components/Ecommerce/CheckoutSteps.js b/client/src/components/Ecommerce/CheckoutSteps.js new file mode 100644 index 0000000..2739e5c --- /dev/null +++ b/client/src/components/Ecommerce/CheckoutSteps.js @@ -0,0 +1,51 @@ +import React from "react"; +import { Nav } from "react-bootstrap"; +import { LinkContainer } from "react-router-bootstrap"; + +const CheckoutSteps = ({ step1, step2, step3, step4 }) => { + return ( + + + {step1 ? ( + + Sign In + + ) : ( + Sign In + )} + + + + {step2 ? ( + + Shipping + + ) : ( + Shipping + )} + + + + {step3 ? ( + + Payment + + ) : ( + Payment + )} + + + + {step4 ? ( + + Place Order + + ) : ( + Place Order + )} + + + ); +}; + +export default CheckoutSteps; diff --git a/client/src/components/Ecommerce/Screen/PaymentScreen.js b/client/src/components/Ecommerce/Screen/PaymentScreen.js new file mode 100644 index 0000000..eb7c343 --- /dev/null +++ b/client/src/components/Ecommerce/Screen/PaymentScreen.js @@ -0,0 +1,73 @@ +import React, { useState } from "react"; +import { Form, Button, Col } from "react-bootstrap"; +import { useDispatch, useSelector } from "react-redux"; +import FormContainer from "../FormContainer"; +import CheckoutSteps from "../CheckoutSteps"; +import { savePaymentMethod } from "../../../redux/actions/cartActions"; + +const PaymentScreen = ({ history }) => { + const cart = useSelector((state) => state.cart); + const { shippingAddress } = cart; + + if (!shippingAddress.address) { + history.push("/shipping"); + } + + const [paymentMethod, setPaymentMethod] = useState("PayPal"); + + const dispatch = useDispatch(); + + const submitHandler = (e) => { + e.preventDefault(); + dispatch(savePaymentMethod(paymentMethod)); + history.push("/placeorder"); + }; + + return ( + + + Payment Method + + + Select Method + + setPaymentMethod(e.target.value)} + > + setPaymentMethod(e.target.value)} + > + + setPaymentMethod(e.target.value)} + > + + + + + Continue + + + + ); +}; + +export default PaymentScreen; diff --git a/client/src/components/Ecommerce/Screen/PlaceOrderScreen.js b/client/src/components/Ecommerce/Screen/PlaceOrderScreen.js new file mode 100644 index 0000000..7204c41 --- /dev/null +++ b/client/src/components/Ecommerce/Screen/PlaceOrderScreen.js @@ -0,0 +1,158 @@ +import React from "react"; +import { Button, Card, Col, Image, ListGroup, Row } from "react-bootstrap"; +import { useDispatch, useSelector } from "react-redux"; +import { Link } from "react-router-dom"; +import { createOrder } from "../../../redux/actions/orderActions"; +import CheckoutSteps from "../CheckoutSteps"; +import Message from "../Message"; +import { toastSuccessMessage } from "../ToastMessage"; + +const PlaceOrderScreen = ({ history }) => { + const dispatch = useDispatch(); + + const cart = useSelector((state) => state.cart); + + const orderCreate = useSelector((state) => state.orderCreate); + const { order, success, error } = orderCreate; + + // useEffect(() => { + // if (success) { + // // history.push(`/order/${order._id}`); + // // history.push("/order") + // } + // // eslint-disable-next-line + // }, [history, success]); + + const placeOrderHandler = () => { + console.log(cart.cartItems); + + dispatch( + createOrder({ + orderItems: cart.cartItems, + shippingAddress: cart.shippingAddress, + paymentMethod: cart.paymentMethod, + itemsPrice: cart.itemsPrice, + shippingPrice: cart.shippingPrice, + totalPrice: cart.totalPrice, + }) + ); + toastSuccessMessage("You're order has been placed") + + }; + //Calculate Price + const addDecimals = (num) => { + return (Math.round(num * 100) / 100).toFixed(2); + }; + + cart.itemsPrice = addDecimals( + cart.cartItems.reduce((acc, item) => acc + item.price * item.qty, 0) + ); + cart.shippingPrice = 30; + + cart.totalPrice = Number(cart.itemsPrice) + Number(cart.shippingPrice); + + return ( + <> + + + + + + Delivery Location + + Address: + {cart.shippingAddress.address}, {cart.shippingAddress.city}{" "} + {cart.shippingAddress.thana}, {cart.shippingAddress.houseNumber} + + + + + Payment Method + Method: + {cart.paymentMethod} + + + Order Items + {cart.cartItems.length === 0 ? ( + Your cart is empty + ) : ( + + {cart.cartItems.map((item, index) => ( + + + + + + + + {item.name} + + + + {item.qty} x ৳{item.price} = ৳{item.qty * item.price} + + + + ))} + + )} + + + + + + + + Order Summary + + + + Items + ৳{cart.itemsPrice} + + + + + Delivery Charge + ৳{cart.shippingPrice} + + + {/* + + Tax + ${cart.taxPrice} + + */} + + + Total + ৳{cart.totalPrice} + + + + {error && {error}} + + + + Place Order + + + + + + + > + ); +}; + +export default PlaceOrderScreen; diff --git a/client/src/components/Ecommerce/Screen/ShippingScreen.js b/client/src/components/Ecommerce/Screen/ShippingScreen.js new file mode 100644 index 0000000..7a1ee17 --- /dev/null +++ b/client/src/components/Ecommerce/Screen/ShippingScreen.js @@ -0,0 +1,84 @@ +import React, { useState } from "react"; +import { Button, Form } from "react-bootstrap"; +import { useDispatch, useSelector } from "react-redux"; +import FormContainer from "../FormContainer"; +import CheckoutSteps from "../CheckoutSteps"; +import { saveShippingAddress } from "../../../redux/actions/cartActions"; + +const ShippingScreen = ({ history }) => { + const cart = useSelector((state) => state.cart); + const { shippingAddress } = cart; + + const [address, setAddress] = useState(shippingAddress.address || ""); + const [city, setCity] = useState(shippingAddress.city || ""); + const [thana, setThana] = useState(shippingAddress.thana || ""); + const [houseNumber, setHouseNumber] = useState( + shippingAddress.houseNumber || "" + ); + const dispatch = useDispatch(); + + const submitHandler = (e) => { + e.preventDefault(); + console.log("submit"); + dispatch(saveShippingAddress({ address, city, thana, houseNumber })); + history.push("/payment"); + }; + + return ( + + + Shipping + + + Address + setAddress(e.target.value)} + > + + + + City + setCity(e.target.value)} + > + + + + Thana + setThana(e.target.value)} + > + + + + House Number + setHouseNumber(e.target.value)} + > + + + + Continue + + + + ); +}; + +export default ShippingScreen; diff --git a/client/src/constants/cartConstants.js b/client/src/constants/cartConstants.js index 04f3db8..418150f 100644 --- a/client/src/constants/cartConstants.js +++ b/client/src/constants/cartConstants.js @@ -1,3 +1,5 @@ -export const CART_ADD_ITEM = 'CART_ADD_ITEM' -export const CART_REMOVE_ITEM = 'CART_REMOVE_ITEM' +export const CART_ADD_ITEM = "CART_ADD_ITEM"; +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"; export const CART_RESET = 'CART_RESET' diff --git a/client/src/constants/orderConstants.js b/client/src/constants/orderConstants.js new file mode 100644 index 0000000..eb4c81f --- /dev/null +++ b/client/src/constants/orderConstants.js @@ -0,0 +1,3 @@ +export const ORDER_CREATE_REQUEST = "ORDER_CREATE_REQUEST"; +export const ORDER_CREATE_SUCCESS = "ORDER_CREATE_SUCCESS"; +export const ORDER_CREATE_FAIL = "ORDER_CREATE_FAIL"; diff --git a/client/src/pages/EcommercePage.js b/client/src/pages/EcommercePage.js index ac8d218..5f2fabd 100644 --- a/client/src/pages/EcommercePage.js +++ b/client/src/pages/EcommercePage.js @@ -1,6 +1,6 @@ -import React from 'react'; -import { Container } from 'react-bootstrap'; -import { BrowserRouter as Router, Route } from 'react-router-dom'; +import React from "react"; +import { Container } from "react-bootstrap"; +import { BrowserRouter as Router, Route } from "react-router-dom"; import Footer from "../components/Ecommerce/Footer"; import Header from "../components/Ecommerce/Header"; import CartScreen from '../components/Ecommerce/Screen/CartScreen'; @@ -8,33 +8,38 @@ import ForgetPasswordScreen from '../components/Ecommerce/Screen/ForgetPasswordS import HomeScreen from '../components/Ecommerce/Screen/HomeScreen'; 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 ProductScreen from '../components/Ecommerce/Screen/ProductScreen'; import ProfileScreen from '../components/Ecommerce/Screen/ProfileScreen'; import RegisterScreen from '../components/Ecommerce/Screen/RegisterScreen'; +import ShippingScreen from "../components/Ecommerce/Screen/ShippingScreen"; import '../styles/ecommerce.css'; function EcommercePage() { - return ( - - - - - - - - - - - - - - - - - - - - ) + return ( + + + + + + + + + + + + + + + + + + + + + + + ) } - -export default EcommercePage +export default EcommercePage; diff --git a/client/src/redux/actions/cartActions.js b/client/src/redux/actions/cartActions.js index 2041da1..ce011ad 100644 --- a/client/src/redux/actions/cartActions.js +++ b/client/src/redux/actions/cartActions.js @@ -1,43 +1,66 @@ -import axios from 'axios'; -import { CART_ADD_ITEM, CART_REMOVE_ITEM } from '../../constants/cartConstants'; - -export const addToCart = (id,qty) => async (dispatch,getState) => { - try { - const { data } = await axios.get(`/api/products/${id}`) - - dispatch({ - type: CART_ADD_ITEM, - payload: { - productId: data._id, - name: data.name, - image: data.image, - price: data.price, - countInStock: data.countInStock, - qty - } - }) - - localStorage.setItem("cartItems",JSON.stringify(getState().cart.cartItems)) - - } catch (error) { - console.log("Gg",error); - } -} - - -export const removeFromCart = (id) => async (dispatch,getState) => { - try { - - dispatch({ - type: CART_REMOVE_ITEM, - payload: id - }) - - localStorage.setItem("cartItems",JSON.stringify(getState().cart.cartItems)) - - } catch (error) { - console.log("Gg",error); - } -} +import axios from "axios"; +import { + CART_ADD_ITEM, + CART_REMOVE_ITEM, + CART_SAVE_SHIPPING_ADDRESS, + CART_SAVE_PAYMENT_METHOD, +} from "../../constants/cartConstants"; +export const addToCart = (id, qty) => async (dispatch, getState) => { + try { + const { data } = await axios.get(`/api/products/${id}`); + dispatch({ + type: CART_ADD_ITEM, + payload: { + productId: data._id, + name: data.name, + image: data.image, + price: data.price, + countInStock: data.countInStock, + qty, + }, + }); + + localStorage.setItem( + "cartItems", + JSON.stringify(getState().cart.cartItems) + ); + } catch (error) { + console.log("Gg", error); + } +}; + +export const removeFromCart = (id) => async (dispatch, getState) => { + try { + dispatch({ + type: CART_REMOVE_ITEM, + payload: id, + }); + + localStorage.setItem( + "cartItems", + JSON.stringify(getState().cart.cartItems) + ); + } catch (error) { + console.log("Gg", error); + } +}; + +export const saveShippingAddress = (data) => (dispatch) => { + dispatch({ + type: CART_SAVE_SHIPPING_ADDRESS, + payload: data, + }); + + localStorage.setItem("shippingAddress", JSON.stringify(data)); +}; + +export const savePaymentMethod = (data) => (dispatch) => { + dispatch({ + type: CART_SAVE_PAYMENT_METHOD, + payload: data, + }); + + localStorage.setItem("paymentMethod", JSON.stringify(data)); +}; diff --git a/client/src/redux/actions/orderActions.js b/client/src/redux/actions/orderActions.js new file mode 100644 index 0000000..50e725f --- /dev/null +++ b/client/src/redux/actions/orderActions.js @@ -0,0 +1,52 @@ +import axios from "axios"; +import { + ORDER_CREATE_FAIL, ORDER_CREATE_REQUEST, ORDER_CREATE_SUCCESS +} from "../../constants/orderConstants"; + + +export const createOrder = (order) => async (dispatch, getState) => { + try { + dispatch({ + type: ORDER_CREATE_REQUEST, + }); + + const { + userLogin: { userInfo }, + } = getState(); + + const config = { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${userInfo.token}`, + }, + }; + + const { data } = await axios.post(`/api/orders`, order, config); + + dispatch({ + type: ORDER_CREATE_SUCCESS, + payload: data, + }); + /* dispatch({ + type: CART_CLEAR_ITEMS, + payload: data, + }) */ + /* localStorage.removeItem('cartItems') */ + } catch (error) { + console.log(error); + dispatch({ + type: ORDER_CREATE_FAIL, + payload: + error.response && error.response.data.message + ? error.response.data.message + : error.message, + }); + /* if (message === 'Not authorized, token failed') { + dispatch(logout()) + } + dispatch({ + type: ORDER_CREATE_FAIL, + payload: message, + }) */ + } +}; diff --git a/client/src/redux/reducers/cartReducers.js b/client/src/redux/reducers/cartReducers.js index 8ac2dcb..2448305 100644 --- a/client/src/redux/reducers/cartReducers.js +++ b/client/src/redux/reducers/cartReducers.js @@ -1,39 +1,51 @@ -import { CART_ADD_ITEM, CART_REMOVE_ITEM, CART_RESET } from '../../constants/cartConstants'; +import { + CART_ADD_ITEM, + CART_REMOVE_ITEM, CART_SAVE_PAYMENT_METHOD, CART_SAVE_SHIPPING_ADDRESS +} from "../../constants/cartConstants"; -export const cartReducer = (state = { cartItems: [] }, action) => { - switch (action.type) { - case CART_ADD_ITEM: - const item = action.payload +export const cartReducer = ( + state = { cartItems: [], shippingAddress: {} }, + action +) => { + switch (action.type) { + case CART_ADD_ITEM: + const item = action.payload; - const isItemExist = state.cartItems.find(p => p.productId === item.productId) - if (isItemExist) { - return { - ...state, - cartItems: state.cartItems.map(p => p.productId === item.productId ? item : p) - } - - } else { - return { - ...state, - cartItems: [...state.cartItems, item] - - } - } - case CART_REMOVE_ITEM: - return { - ...state, - cartItems: state.cartItems.filter(p=>p.productId !== action.payload) - } - - case CART_RESET: - return { - ...state, - cartItems:[] - } - - default: - return state - } - -} + const isItemExist = state.cartItems.find( + (p) => p.productId === item.productId + ); + if (isItemExist) { + return { + ...state, + cartItems: state.cartItems.map((p) => + p.productId === item.productId ? item : p + ), + }; + } else { + return { + ...state, + cartItems: [...state.cartItems, item], + }; + } + case CART_REMOVE_ITEM: + return { + ...state, + cartItems: state.cartItems.filter( + (p) => p.productId !== 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; + } +}; diff --git a/client/src/redux/reducers/orderReducers.js b/client/src/redux/reducers/orderReducers.js new file mode 100644 index 0000000..febd60c --- /dev/null +++ b/client/src/redux/reducers/orderReducers.js @@ -0,0 +1,31 @@ +import { + ORDER_CREATE_SUCCESS, + ORDER_CREATE_REQUEST, + ORDER_CREATE_FAIL, +} from "../../constants/orderConstants"; + +export const orderCreateReducer = (state = {}, action) => { + switch (action.type) { + case ORDER_CREATE_REQUEST: + return { + loading: true, + }; + case ORDER_CREATE_SUCCESS: + return { + loading: false, + success: true, + order: action.payload, + }; + case ORDER_CREATE_FAIL: + return { + loading: false, + error: action.payload, + }; + default: + return state; + /* case ORDER_CREATE_RESET: + return {} + default: + return state */ + } +}; diff --git a/client/src/store.js b/client/src/store.js index 68f064d..fbc9b51 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -1,32 +1,56 @@ -import { applyMiddleware, combineReducers, createStore } from 'redux' -import { composeWithDevTools } from 'redux-devtools-extension' -import thunk from 'redux-thunk' -import { cartReducer } from './redux/reducers/cartReducers' -import { productCreateReviewReducer, productDetailsReducer, productListReducer } from './redux/reducers/productReducers' -import { userDetailsReducer, userLoginReducer, userProfileUpdateReducer, userRegisterReducer } from './redux/reducers/userReducers' +import { applyMiddleware, combineReducers, createStore } from "redux"; +import { composeWithDevTools } from "redux-devtools-extension"; +import thunk from "redux-thunk"; +import { cartReducer } from "./redux/reducers/cartReducers"; +import { + productCreateReviewReducer, + productDetailsReducer, + productListReducer, +} from "./redux/reducers/productReducers"; +import { + userDetailsReducer, + userLoginReducer, + userProfileUpdateReducer, + userRegisterReducer, +} from "./redux/reducers/userReducers"; +import { orderCreateReducer } from "./redux/reducers/orderReducers"; const reducer = combineReducers({ - productList: productListReducer, - productDetails: productDetailsReducer, - cart: cartReducer, - userLogin: userLoginReducer, - userRegister: userRegisterReducer, - userDetails: userDetailsReducer, - userProfileUpdate: userProfileUpdateReducer, - productCreateReview: productCreateReviewReducer -}) + productList: productListReducer, + productDetails: productDetailsReducer, + cart: cartReducer, + userLogin: userLoginReducer, + userRegister: userRegisterReducer, + userDetails: userDetailsReducer, + userProfileUpdate: userProfileUpdateReducer, + productCreateReview: productCreateReviewReducer, + orderCreate: orderCreateReducer, +}); -const cartItemsFromStorage = localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : [] - -const userInfoFromStorage = localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : null +const cartItemsFromStorage = localStorage.getItem("cartItems") + ? JSON.parse(localStorage.getItem("cartItems")) + : []; +const userInfoFromStorage = localStorage.getItem("userInfo") + ? JSON.parse(localStorage.getItem("userInfo")) + : null; +const shippingAddressFromStorage = localStorage.getItem("shippingAddress") + ? JSON.parse(localStorage.getItem("shippingAddress")) + : {}; const initialState = { - cart: { cartItems: cartItemsFromStorage }, - userLogin: { userInfo: userInfoFromStorage } -} + cart: { + cartItems: cartItemsFromStorage, + shippingAddress: shippingAddressFromStorage, + }, + userLogin: { userInfo: userInfoFromStorage }, +}; -const middlewares = [thunk] +const middlewares = [thunk]; -const store = createStore(reducer, initialState, composeWithDevTools(applyMiddleware(...middlewares))) +const store = createStore( + reducer, + initialState, + composeWithDevTools(applyMiddleware(...middlewares)) +); -export default store \ No newline at end of file +export default store; diff --git a/server/controller/orderController.js b/server/controller/orderController.js new file mode 100644 index 0000000..dda9402 --- /dev/null +++ b/server/controller/orderController.js @@ -0,0 +1,42 @@ +import asyncHandler from "express-async-handler"; +import Order from "../models/orderModel.js"; + +// @desc Create new order +// @route POST /api/orders +// @access Private +const addOrderItems = asyncHandler(async (req, res) => { + const { + orderItems, + shippingAddress, + paymentMethod, + itemsPrice, + shippingPrice, + totalPrice, + } = req.body; + + + if (orderItems && orderItems.length === 0) { + res.status(400); + throw new Error("No ordered items"); + return; + } else { + const order = new Order({ + orderedItems:orderItems, + user: req.userId, + shippingAddress, + paymentMethod, + itemsPrice, + shippingPrice, + totalPrice, + }); + + console.log(order.orderItems); + + const createdOrder = await order.save(); + + res.status(201).json(createdOrder); + } +}); + +export { addOrderItems }; + diff --git a/server/controller/productController.js b/server/controller/productController.js index 8ab10cd..d0de046 100644 --- a/server/controller/productController.js +++ b/server/controller/productController.js @@ -6,7 +6,7 @@ import User from '../models/userModel.js' export const getProducts = asyncHandler(async (req, res) => { // for pagination - const pageSize = 10 + const pageSize = 8 const page = req.query.pageNumber || 1 const keyword = req.query.keyword ? { diff --git a/server/controller/userController.js b/server/controller/userController.js index 9aa6a23..a2b341b 100644 --- a/server/controller/userController.js +++ b/server/controller/userController.js @@ -8,101 +8,111 @@ import { sendEmail } from '../utils/sendEmail.js'; // @ User Login export const authUser = async (req, res) => { - // need data from frontend - const { email, password } = req.body; - - try { - // check for whether the user exist - const existingUser = await User.findOne({ email }) - if (!existingUser) return res.status(404).json({ message: "User not found" }) - - // if user exist, check for password - const isValidPassword = await bcrypt.compare(password, existingUser.password) - if (!isValidPassword) return res.status(404).json({ message: "Invalid Credentials" }) - - // if both user and password is valid => generate a jwt - const token = jwt.sign({ email: existingUser.email, id: existingUser._id }, - process.env.JWT_SECRET, - {expiresIn:'1h'} - ) - - res.status(200).json({ user:existingUser, token:token }) - - } catch (error) { - res.status(500).json({ message: "Something went wrong" }) + // need data from frontend + const { email, password } = req.body; + + try { + // check for whether the user exist + const existingUser = await User.findOne({ email }); + if (!existingUser) + return res.status(404).json({ message: "Invalid Credentials" }); + + // if user exist, check for password + const isValidPassword = await bcrypt.compare( + password, + existingUser.password + ); + if (!isValidPassword) + return res.status(404).json({ message: "Invalid Credentials" }); + + // if both user and password is valid => generate a jwt + const token = jwt.sign( + { email: existingUser.email, id: existingUser._id }, + process.env.JWT_SECRET, + { expiresIn: "1h" } + ); + + res.status(200).json({ user: existingUser, token: token }); + } catch (error) { + res.status(500).json({ message: "Something went wrong" }); + } +}; + +// @ User Registration +export const registerUser = async (req, res) => { + const { name, email, password, confirmPassword } = req.body; + + try { + // check whether this email already exist or not + const existingUser = await User.findOne({ email }); + if (existingUser) + return res.status(404).json({ message: "User already exist" }); + + // check the password + if (password !== confirmPassword) + return res.status(404).json({ message: "Passwords don't match" }); + + //password length check + if (password.length < 6) { + return res + .status(404) + .json({ message: "Passwords must be greater than 6 characters" }); } -} - - -// @ User Registration -export const registerUser = async (req,res) => { - const { name, email, password, confirmPassword } = req.body; - - try { - // check whether this email already exist or not - const existingUser = await User.findOne({ email }) - if (existingUser) return res.status(404).json({ message: "User already exist" }) - - // check the password - if (password !== confirmPassword) return res.status(404).json({ message: "Passwords don't match" }) - - // password is hashed from the user model - // Now create the user - const result = await User.create({name,email,password}) - - // generate the token - const token = jwt.sign({ email: result.email, id: result._id }, - process.env.JWT_SECRET) - - res.status(201).json({user:result,token}) - - } catch (error) { - console.log(error); - res.status(500).json({message:"Something went wrong"}) + //check for empty fields + if (!name || !email || !password || !confirmPassword) { + return res.status(404).json({ message: "Cannot Have Empty Fields" }); } -} + // password is hashed from the user model + // Now create the user + const result = await User.create({ name, email, password }); -// @ User Profile -export const getUserProfile = asyncHandler(async (req, res) => { - const user_id = req.userId - if (user_id) { - const user = await User.findById({_id:user_id}) - res.status(200).json(user) - - } else { - res.status(401) - throw new Error("Not authorized to view profile") - } + // generate the token + const token = jwt.sign( + { email: result.email, id: result._id }, + process.env.JWT_SECRET + ); -}) + res.status(201).json({ user: result, token }); + } catch (error) { + console.log(error); + res.status(500).json({ message: "Something went wrong" }); + } +}; +// @ User Profile +export const getUserProfile = asyncHandler(async (req, res) => { + const user_id = req.userId; + if (user_id) { + const user = await User.findById({ _id: user_id }); + res.status(200).json(user); + } else { + res.status(401); + throw new Error("Not authorized to view profile"); + } +}); // @ Update User Profile export const updateUserProfile = asyncHandler(async (req, res) => { - const user_id = req.userId - if (user_id) { - const user = await User.findById({_id:user_id}) - user.name = req.body.name || user.name - user.email = req.body.email || user.email - - if (req.body.password) { - user.password = req.body.password - } - - const updatedUser = await user.save() - - res.status(200).json(updatedUser) - - } else { - res.status(401) - throw new Error("Not authorized to view profile") + const user_id = req.userId; + if (user_id) { + const user = await User.findById({ _id: user_id }); + user.name = req.body.name || user.name; + user.email = req.body.email || user.email; + + if (req.body.password) { + user.password = req.body.password; } -}) - + const updatedUser = await user.save(); + res.status(200).json(updatedUser); + } else { + res.status(401); + throw new Error("Not authorized to view profile"); + } +}); // @ forget password @@ -178,4 +188,5 @@ export const resetPassword = async (req, res) => { console.log(error); res.status(500).json({ message: "Failed to reset password" }) } -} \ No newline at end of file +} + diff --git a/server/index.js b/server/index.js index bac94e2..cc457c5 100644 --- a/server/index.js +++ b/server/index.js @@ -1,18 +1,18 @@ -import cors from 'cors'; -import dotenv from 'dotenv'; -import express from 'express'; -import morgan from 'morgan'; -import connectDB from './config/db.js'; -import { errorHandler, notFound } from './middlewares/errorMiddleware.js'; -import productRoutes from './routes/productRoutes.js'; -import userRoutes from './routes/userRoutes.js'; - +import cors from "cors"; +import dotenv from "dotenv"; +import express from "express"; +import morgan from "morgan"; +import connectDB from "./config/db.js"; +import { errorHandler, notFound } from "./middlewares/errorMiddleware.js"; +import productRoutes from "./routes/productRoutes.js"; +import userRoutes from "./routes/userRoutes.js"; +import orderRoutes from "./routes/orderRoutes.js"; const app = express(); dotenv.config(); -if (process.env.NODE_ENV === 'development') { - app.use(morgan('dev')) +if (process.env.NODE_ENV === "development") { + app.use(morgan("dev")); } // middlewares @@ -25,8 +25,9 @@ app.get("/", (req, res) => { }); //routes -app.use("/api/products", productRoutes) -app.use("/api/users", userRoutes) +app.use("/api/products", productRoutes); +app.use("/api/users", userRoutes); +app.use("/api/orders", orderRoutes); // error handler app.use(notFound); diff --git a/server/middlewares/errorMiddleware.js b/server/middlewares/errorMiddleware.js index 67d5ee9..6dce95d 100644 --- a/server/middlewares/errorMiddleware.js +++ b/server/middlewares/errorMiddleware.js @@ -6,6 +6,7 @@ export const notFound = (req, res, next) => { export const errorHandler = (err, req, res, next) => { const statusCode = res.statusCode === 200 ? 500 : res.statusCode + console.log(err); res.status(statusCode).json({ message: err.message, stack: process.env.NODE_ENV === 'development' ? err.stack : null diff --git a/server/models/orderModel.js b/server/models/orderModel.js index df679a6..e218384 100644 --- a/server/models/orderModel.js +++ b/server/models/orderModel.js @@ -1,78 +1,76 @@ -import mongoose from 'mongoose' +import mongoose from "mongoose"; -const orderSchema = mongoose.Schema({ +const orderSchema = mongoose.Schema( + { user: { - type: mongoose.Schema.Types.ObjectId, - required: true, - ref:'User' + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: "User", }, orderedItems: [ - { - name: { type: String, required: true }, - qty: { type: Number, required: true }, - image: { type: String, required: true }, - price: { type: Number, required: true }, - product: { - type: mongoose.Schema.Types.ObjectId, - required: true, - ref:'Product' - } - } + { + name: { type: String, required: true }, + qty: { type: Number, required: true }, + image: { type: String, required: true }, + price: { type: Number, required: true }, + countInStock:{type:Number,required:true}, + productId: { + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: "Product", + }, + }, ], shippingAddress: { - address: { type: String, required: true }, - city: { type: String, required: true }, - postalCode: { type: String, required: true }, - district: { type: String, required: true }, + address: { type: String, required: true }, + city: { type: String, required: true }, + thana: { type: String, required: true }, + houseNumber: { type: String, required: true }, }, paymentMethod: { - type: String, - required:true + type: String, + required: true, }, paymentResult: { - id: { type: String }, - status: { type: String }, - updateTime: { type: String }, - emailAddress: { type: String }, + id: { type: String }, + status: { type: String }, + updateTime: { type: String }, + emailAddress: { type: String }, }, - taxPrice: { - type: Number, - required: true, - default:0.0 - }, shippingPrice: { - type: Number, - required: true, - default:0.0 + type: Number, + required: true, + default: 0.0, }, totalPrice: { - type: Number, - required: true, - default:0.0 + type: Number, + required: true, + default: 0.0, }, - + isPaid: { - type: Boolean, - required: true, - default:false + type: Boolean, + required: true, + default: false, }, paidAt: { - type:Date + type: Date, }, isDelivered: { - type: Boolean, - required: true, - default:false + type: Boolean, + required: true, + default: false, }, deliverAt: { - type:Date + type: Date, }, + }, + { + timestamps: true, + } +); -}, { - timestamps:true -}) - -const Order = mongoose.model("Order", orderSchema) -export default Order +const Order = mongoose.model("Order", orderSchema); +export default Order; diff --git a/server/routes/orderRoutes.js b/server/routes/orderRoutes.js new file mode 100644 index 0000000..1646443 --- /dev/null +++ b/server/routes/orderRoutes.js @@ -0,0 +1,8 @@ +import express from "express"; +import { addOrderItems } from "../controller/orderController.js"; +import { isLoggedIn } from "../middlewares/authMiddleware.js"; +const router = express.Router(); + +router.route("/").post(isLoggedIn, addOrderItems); + +export default router;
+ Address: + {cart.shippingAddress.address}, {cart.shippingAddress.city}{" "} + {cart.shippingAddress.thana}, {cart.shippingAddress.houseNumber} +