From c48524e83fbff54e7a72b8051b8a4e85094a68c7 Mon Sep 17 00:00:00 2001 From: Apoorva Singh Date: Tue, 31 Aug 2021 16:47:32 +0530 Subject: [PATCH] address functionality --- backend/changeLog.txt | 3 + backend/models/addressModel.js | 21 +++ backend/routers/addressRouter.js | 182 +++++++++++++++++++++ backend/routers/orderRouter.js | 1 + backend/server.js | 2 + frontend/changeLog.txt | 6 + frontend/src/App.js | 45 +++-- frontend/src/actions/addressActions.js | 21 +++ frontend/src/actions/userActions.js | 1 + frontend/src/components/SearchBox.js | 3 +- frontend/src/constants/addressConstants.js | 8 + frontend/src/index.css | 52 +++++- frontend/src/reducers/addressReducers.js | 86 ++++++++++ frontend/src/screens/AddressScreen.js | 80 +++++++++ frontend/src/screens/HomeScreen.js | 7 +- frontend/src/store.js | 4 + package.json | 1 - 17 files changed, 504 insertions(+), 19 deletions(-) create mode 100644 backend/changeLog.txt create mode 100644 backend/models/addressModel.js create mode 100644 backend/routers/addressRouter.js create mode 100644 frontend/changeLog.txt create mode 100644 frontend/src/actions/addressActions.js create mode 100644 frontend/src/constants/addressConstants.js create mode 100644 frontend/src/reducers/addressReducers.js create mode 100644 frontend/src/screens/AddressScreen.js diff --git a/backend/changeLog.txt b/backend/changeLog.txt new file mode 100644 index 00000000..aca695eb --- /dev/null +++ b/backend/changeLog.txt @@ -0,0 +1,3 @@ +addressModel: Built the address model to store the addresses for customers, sellers and admins together based on their user ID. +server.js: Changed the server.js file to add the routes for Address creation and fetch +addressRouter: Created this file to perform all the logic of address posting and fetching with the correct routes. (Post on Postman, Fetch on App) \ No newline at end of file diff --git a/backend/models/addressModel.js b/backend/models/addressModel.js new file mode 100644 index 00000000..0a9ae480 --- /dev/null +++ b/backend/models/addressModel.js @@ -0,0 +1,21 @@ +import mongoose from 'mongoose'; + +const addressSchema = new mongoose.Schema( + { + name: { type: String, required: true }, + phone: { type: Number, required: true, unique: true }, + pincode: { type: Number, required: true}, + addressLine: { type: String, required: true}, + landmark: { type: String, required: true}, + city: { type: String, required: true }, + state: { type: String, required: true }, + addressType: { type: String}, + user: {type : mongoose.Schema.Types.ObjectId, ref: 'User', required: true} +}, + { + timestamps: true, + } +); +const Address = mongoose.model('Address', addressSchema); +export default Address; + diff --git a/backend/routers/addressRouter.js b/backend/routers/addressRouter.js new file mode 100644 index 00000000..2dcb8689 --- /dev/null +++ b/backend/routers/addressRouter.js @@ -0,0 +1,182 @@ +import express from 'express'; +import expressAsyncHandler from 'express-async-handler'; +import bcrypt from 'bcryptjs'; +import data from '../data.js'; +import Address from '../models/addressModel.js'; +import { generateToken, isAdmin, isAuth } from '../utils.js'; + +const addressRouter = express.Router(); + +addressRouter.get( + '/', + expressAsyncHandler(async (req, res) => { + console.log("address") + const address = await Address.find() + res.send({address}); + }) +); + +addressRouter.post( + '/', + isAuth, + expressAsyncHandler(async (req, res) => { + console.log("Address req ", req) + const address = new Address({ + name: req.body.name, + phone: req.body.phone, + pincode: req.body.pincode, + addressLine: req.body.addressLine, + landmark: req.body.landmark, + city: req.body.city, + state: req.body.state, + addressType: req.body.addressType, + user: req.body.user + }); + const createAddress = await address.save(); + res + .status(201) + .send({ message: 'New Address Created', address: createAddress }); + }) + ); + +// userRouter.get( +// '/seed', +// expressAsyncHandler(async (req, res) => { +// // await User.remove({}); +// const createdUsers = await User.insertMany(data.users); +// res.send({ createdUsers }); +// }) +// ); + +// userRouter.post( +// '/signin', +// expressAsyncHandler(async (req, res) => { +// const user = await User.findOne({ email: req.body.email }); +// if (user) { +// if (bcrypt.compareSync(req.body.password, user.password)) { +// res.send({ +// _id: user._id, +// name: user.name, +// email: user.email, +// isAdmin: user.isAdmin, +// isSeller: user.isSeller, +// token: generateToken(user), +// }); +// return; +// } +// } +// res.status(401).send({ message: 'Invalid email or password' }); +// }) +// ); + +// userRouter.post( +// '/register', +// expressAsyncHandler(async (req, res) => { +// const user = new User({ +// name: req.body.name, +// email: req.body.email, +// password: bcrypt.hashSync(req.body.password, 8), +// }); +// const createdUser = await user.save(); +// res.send({ +// _id: createdUser._id, +// name: createdUser.name, +// email: createdUser.email, +// isAdmin: createdUser.isAdmin, +// isSeller: user.isSeller, +// token: generateToken(createdUser), +// }); +// }) +// ); + +// userRouter.get( +// '/:id', +// expressAsyncHandler(async (req, res) => { +// const user = await User.findById(req.params.id); +// if (user) { +// res.send(user); +// } else { +// res.status(404).send({ message: 'User Not Found' }); +// } +// }) +// ); +// userRouter.put( +// '/profile', +// isAuth, +// expressAsyncHandler(async (req, res) => { +// const user = await User.findById(req.user._id); +// if (user) { +// user.name = req.body.name || user.name; +// user.email = req.body.email || user.email; +// if (user.isSeller) { +// user.seller.name = req.body.sellerName || user.seller.name; +// user.seller.logo = req.body.sellerLogo || user.seller.logo; +// user.seller.description = +// req.body.sellerDescription || user.seller.description; +// } +// if (req.body.password) { +// user.password = bcrypt.hashSync(req.body.password, 8); +// } +// const updatedUser = await user.save(); +// res.send({ +// _id: updatedUser._id, +// name: updatedUser.name, +// email: updatedUser.email, +// isAdmin: updatedUser.isAdmin, +// isSeller: user.isSeller, +// token: generateToken(updatedUser), +// }); +// } +// }) +// ); + +// userRouter.get( +// '/', +// isAuth, +// isAdmin, +// expressAsyncHandler(async (req, res) => { +// const users = await User.find({}); +// res.send(users); +// }) +// ); + +// userRouter.delete( +// '/:id', +// isAuth, +// isAdmin, +// expressAsyncHandler(async (req, res) => { +// const user = await User.findById(req.params.id); +// if (user) { +// if (user.email === 'admin@example.com') { +// res.status(400).send({ message: 'Can Not Delete Admin User' }); +// return; +// } +// const deleteUser = await user.remove(); +// res.send({ message: 'User Deleted', user: deleteUser }); +// } else { +// res.status(404).send({ message: 'User Not Found' }); +// } +// }) +// ); + +// userRouter.put( +// '/:id', +// isAuth, +// isAdmin, +// expressAsyncHandler(async (req, res) => { +// const user = await User.findById(req.params.id); +// if (user) { +// user.name = req.body.name || user.name; +// user.email = req.body.email || user.email; +// user.isSeller = Boolean(req.body.isSeller); +// user.isAdmin = Boolean(req.body.isAdmin); +// // user.isAdmin = req.body.isAdmin || user.isAdmin; +// const updatedUser = await user.save(); +// res.send({ message: 'User Updated', user: updatedUser }); +// } else { +// res.status(404).send({ message: 'User Not Found' }); +// } +// }) +// ); + +export default addressRouter; diff --git a/backend/routers/orderRouter.js b/backend/routers/orderRouter.js index 224e4bb1..91c220f4 100644 --- a/backend/routers/orderRouter.js +++ b/backend/routers/orderRouter.js @@ -85,6 +85,7 @@ orderRouter.post( '/', isAuth, expressAsyncHandler(async (req, res) => { + console.log("Order req ", req) if (req.body.orderItems.length === 0) { res.status(400).send({ message: 'Cart is empty' }); } else { diff --git a/backend/server.js b/backend/server.js index 68d19edc..54c073f8 100644 --- a/backend/server.js +++ b/backend/server.js @@ -8,6 +8,7 @@ import productRouter from './routers/productRouter.js'; import userRouter from './routers/userRouter.js'; import orderRouter from './routers/orderRouter.js'; import uploadRouter from './routers/uploadRouter.js'; +import addressRouter from './routers/addressRouter.js'; dotenv.config(); @@ -24,6 +25,7 @@ app.use('/api/uploads', uploadRouter); app.use('/api/users', userRouter); app.use('/api/products', productRouter); app.use('/api/orders', orderRouter); +app.use('/api/address', addressRouter); app.get('/api/config/paypal', (req, res) => { res.send(process.env.PAYPAL_CLIENT_ID || 'sb'); }); diff --git a/frontend/changeLog.txt b/frontend/changeLog.txt new file mode 100644 index 00000000..5020d2d2 --- /dev/null +++ b/frontend/changeLog.txt @@ -0,0 +1,6 @@ +addressReducers: Created addressReducers file (support file) +addressConstants: Created addressConstants file (support file) +AddressActions: Created AddressActions file (support file) +AddressScreen: Created AddressScreen file to let the users add and see their saved addresses. +App.js: Redesigned the navbar to match that of Amazon. Introduced the address feature. +store.js: (support file) \ No newline at end of file diff --git a/frontend/src/App.js b/frontend/src/App.js index 910f0237..e54d782f 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -5,7 +5,9 @@ import { signout } from './actions/userActions'; import AdminRoute from './components/AdminRoute'; import PrivateRoute from './components/PrivateRoute'; import CartScreen from './screens/CartScreen'; +import ReturnScreen from './screens/ReturnScreen'; import HomeScreen from './screens/HomeScreen'; +import AddressScreen from './screens/AddressScreen'; import OrderHistoryScreen from './screens/OrderHistoryScreen'; import OrderScreen from './screens/OrderScreen'; import PaymentMethodScreen from './screens/PaymentMethodScreen'; @@ -55,18 +57,25 @@ function App() { return (
-
+
+
- - - amazona + } + amazona + + + + Your Addresses +
- - Cart - {cartItems.length > 0 && ( - {cartItems.length} - )} - {userInfo ? (
- - {userInfo.name} {' '} + Hello {userInfo.name} +

Accounts & Lists
  • @@ -143,12 +146,25 @@ function App() {
)} + + Returns & Orders + {cartItems.length > 0 && ( + {cartItems.length} + )} + + + Cart + {cartItems.length > 0 && ( + {cartItems.length} + )} +
+
+ + All right reserved
{' '} +
); } diff --git a/frontend/src/actions/addressActions.js b/frontend/src/actions/addressActions.js new file mode 100644 index 00000000..21fdd75a --- /dev/null +++ b/frontend/src/actions/addressActions.js @@ -0,0 +1,21 @@ +import Axios from 'axios'; +import { + ADDRESS_CREATE_REQUEST, + ADDRESS_CREATE_SUCCESS, + ADDRESS_CREATE_FAIL, + ADDRESS_LIST_REQUEST, + ADDRESS_LIST_SUCCESS, + ADDRESS_LIST_FAIL + } from '../constants/addressConstants'; + +export const listAddresses = () => async (dispatch) => { +dispatch({ type: ADDRESS_LIST_REQUEST }); +try { + const { data } = await Axios.get('/api/address', { + }); + console.log(data) + dispatch({ type: ADDRESS_LIST_SUCCESS, payload: data }); +} catch (error) { + dispatch({ type: ADDRESS_LIST_FAIL, payload: error.message }); +} +}; \ No newline at end of file diff --git a/frontend/src/actions/userActions.js b/frontend/src/actions/userActions.js index 1c58c5c5..7ca2bca4 100644 --- a/frontend/src/actions/userActions.js +++ b/frontend/src/actions/userActions.js @@ -52,6 +52,7 @@ export const signin = (email, password) => async (dispatch) => { dispatch({ type: USER_SIGNIN_REQUEST, payload: { email, password } }); try { const { data } = await Axios.post('/api/users/signin', { email, password }); + console.log("TOKEN ", data) dispatch({ type: USER_SIGNIN_SUCCESS, payload: data }); localStorage.setItem('userInfo', JSON.stringify(data)); } catch (error) { diff --git a/frontend/src/components/SearchBox.js b/frontend/src/components/SearchBox.js index 331e66b3..fd2a56ca 100644 --- a/frontend/src/components/SearchBox.js +++ b/frontend/src/components/SearchBox.js @@ -8,8 +8,9 @@ export default function SearchBox(props) { }; return (
-
+
{ + switch (action.type) { + case ADDRESS_LIST_REQUEST: + return { loading: true }; + case ADDRESS_LIST_SUCCESS: + return { + loading: false, + addressList: action.payload.address, + }; + case ADDRESS_LIST_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 = {}, action) => { +// switch (action.type) { +// case PRODUCT_UPDATE_REQUEST: +// return { loading: true }; +// case PRODUCT_UPDATE_SUCCESS: +// return { loading: false, success: true }; +// case PRODUCT_UPDATE_FAIL: +// return { loading: false, error: action.payload }; +// case PRODUCT_UPDATE_RESET: +// return {}; +// default: +// return state; +// } +// }; +// export const productDeleteReducer = (state = {}, action) => { +// switch (action.type) { +// case PRODUCT_DELETE_REQUEST: +// return { loading: true }; +// case PRODUCT_DELETE_SUCCESS: +// return { loading: false, success: true }; +// case PRODUCT_DELETE_FAIL: +// return { loading: false, error: action.payload }; +// case PRODUCT_DELETE_RESET: +// return {}; +// default: +// return state; +// } +// }; +// export const productReviewCreateReducer = (state = {}, action) => { +// switch (action.type) { +// case PRODUCT_REVIEW_CREATE_REQUEST: +// return { loading: true }; +// case PRODUCT_REVIEW_CREATE_SUCCESS: +// return { loading: false, success: true, review: action.payload }; +// case PRODUCT_REVIEW_CREATE_FAIL: +// return { loading: false, error: action.payload }; +// case PRODUCT_REVIEW_CREATE_RESET: +// return {}; +// default: +// return state; +// } +// }; + \ No newline at end of file diff --git a/frontend/src/screens/AddressScreen.js b/frontend/src/screens/AddressScreen.js new file mode 100644 index 00000000..dccfbf90 --- /dev/null +++ b/frontend/src/screens/AddressScreen.js @@ -0,0 +1,80 @@ +import React, { useEffect } from 'react'; +import { BrowserRouter, Link, Route } from 'react-router-dom'; +import { useDispatch, useSelector } from 'react-redux'; +import { listAddresses } from '../actions/addressActions'; +import LoadingBox from '../components/LoadingBox'; +import MessageBox from '../components/MessageBox'; +import { ADDRESS_DETAILS_RESET } from '../constants/addressConstants'; + +export default function AddressListScreen(props) { + const addressesList = useSelector((state) => state.addressesList); + const { loading, error, addressList } = addressesList; + + console.log(addressList) + const userDelete = useSelector((state) => state.userDelete); + const { + loading: loadingDelete, + error: errorDelete, + success: successDelete, + } = userDelete; + + const dispatch = useDispatch(); + useEffect(() => { + dispatch(listAddresses()); + dispatch({ + type: ADDRESS_DETAILS_RESET, + }); + }, [dispatch, successDelete]); + const deleteHandler = (user) => { + + }; + + return ( +
+

Your Addresses

+ Add Address + {loading ? ( + + ) : error ? ( + {error} + ) : ( + <> + + + {addressList.map((address) => ( + + + + + + + + + + + + ))} + +
{address.name}{address.phone}{address.pincode}{address.addressLine}{address.landmark}{address.city}{address.state}{address.addressType} + + +
+ + )} +
+ ); +} diff --git a/frontend/src/screens/HomeScreen.js b/frontend/src/screens/HomeScreen.js index cc66d123..f19c91af 100644 --- a/frontend/src/screens/HomeScreen.js +++ b/frontend/src/screens/HomeScreen.js @@ -12,6 +12,7 @@ import { Link } from 'react-router-dom'; export default function HomeScreen() { const dispatch = useDispatch(); const productList = useSelector((state) => state.productList); + console.log(productList) const { loading, error, products } = productList; const userTopSellersList = useSelector((state) => state.userTopSellersList); @@ -27,13 +28,13 @@ export default function HomeScreen() { }, [dispatch]); return (
-

Top Sellers

+

Top Sellers

{loadingSellers ? ( ) : errorSellers ? ( {errorSellers} ) : ( - <> +
{sellers.length === 0 && No Seller Found} {sellers.map((seller) => ( @@ -45,7 +46,7 @@ export default function HomeScreen() {
))} - +
)}

Featured Products

{loading ? ( diff --git a/frontend/src/store.js b/frontend/src/store.js index e02ed04a..6232bb5f 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -20,6 +20,9 @@ import { productReviewCreateReducer, productUpdateReducer, } from './reducers/productReducers'; +import { + addressListReducer, +} from './reducers/addressReducers'; import { userAddressMapReducer, userDeleteReducer, @@ -49,6 +52,7 @@ const initialState = { }, }; const reducer = combineReducers({ + addressesList: addressListReducer, productList: productListReducer, productDetails: productDetailsReducer, cart: cartReducer, diff --git a/package.json b/package.json index f34cc148..5015edf9 100644 --- a/package.json +++ b/package.json @@ -34,5 +34,4 @@ "eslint-plugin-react": "^7.21.2", "nodemon": "^2.0.4" } - }