├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── client ├── .gitignore ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.js │ ├── Routes.js │ ├── admin │ ├── AddCategory.js │ ├── AddProduct.js │ ├── CategoryList.js │ ├── ManageProducts.js │ ├── Orders.js │ ├── ProductList.js │ ├── UpdateProduct.js │ └── apiAdmin.js │ ├── auth │ ├── AdminRoute.js │ ├── PrivateRoute.js │ └── index.js │ ├── config.js │ ├── core │ ├── Card.js │ ├── Cart.js │ ├── Checkbox.js │ ├── Checkout.js │ ├── Copyright.js │ ├── Home.js │ ├── Layout.js │ ├── Menu.js │ ├── NotFound.js │ ├── Product.js │ ├── RadioBox.js │ ├── Search.js │ ├── Shop.js │ ├── ShowImage.js │ ├── apiCore.js │ ├── cartHelpers.js │ └── fixedPrices.js │ ├── index.js │ ├── styles.css │ └── user │ ├── AdminDashboard.js │ ├── Profile.js │ ├── Signin.js │ ├── Signup.js │ ├── UserDashboard.js │ └── apiUser.js ├── controllers ├── auth.js ├── braintree.js ├── category.js ├── order.js ├── product.js └── user.js ├── helpers └── dbErrorHandler.js ├── models ├── category.js ├── order.js ├── product.js └── user.js ├── package.json ├── routes ├── auth.js ├── braintree.js ├── category.js ├── order.js ├── product.js └── user.js ├── server.js └── validator └── index.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | buy_me_a_coffee: ashrafkabir 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env 3 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ashraf Kabir 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mern-ecommerce 2 | 3 | > Frontend-> React JS 4 | 5 | > Backend-> Node JS & Express JS 6 | 7 | > Database-> MongoDB 8 | 9 | ## Installation process 10 | 1. #### clone the repo using this command 11 | ```bash 12 | git clone https://github.com/ashraf-kabir/mern-ecommerce.git 13 | ``` 14 | 2. #### install npm packages 15 | 1. install backend packages 16 | ```bash 17 | cd mern-ecommerce 18 | npm install 19 | ``` 20 | 2. install frontend packages 21 | ```bash 22 | cd client 23 | npm install 24 | ``` 25 | 3. go to the parent folder of mern-ecommerce & create .env for connection, JWT_SECRET, BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY and BRAINTREE_PRIVATE_KEY. 26 | 27 | ```bash 28 | cd mern-ecommerce 29 | sudo nano .env 30 | ``` 31 | (ctrl+x to save & nano follow instruction there) 32 | 33 | ##### sample code for backend .env 34 | ```env 35 | MONGODB_URI=YOUR_MONGODB_URI 36 | JWT_SECRET=YOUR_JWT_SECRET 37 | BRAINTREE_MERCHANT_ID=YOUR_BRAINTREE_MERCHANT_ID 38 | BRAINTREE_PUBLIC_KEY=YOUR_BRAINTREE_PUBLIC_KEY 39 | BRAINTREE_PRIVATE_KEY=YOUR_BRAINTREE_PRIVATE_KEY 40 | ``` 41 | 4. create another .env file inside client directory for REACT_APP_API_URL. 42 | 43 | ```bash 44 | cd mern-ecommerce/client 45 | sudo nano .env 46 | ``` 47 | ##### sample code for frontend .env 48 | ```env 49 | REACT_APP_API_URL=YOUR_API_URL 50 | ``` 51 | ##### Instructions: 52 | 1. for mongodb atlas database creation follow this tutorial->https://www.youtube.com/watch?v=KKyag6t98g8 53 | 2. you can use any random string as JWTSECRET 54 | 3. for localhost REACT_APP_API_URL is http://localhost:5000/api 55 | but for heroku (server deployment) it will be different 56 | 4. #### note: add .env on .gitignore 57 | 5. for server deployment use secrets directly 58 | 59 | 5. deploy this project on your local server by using this command 60 | ```bash 61 | cd mern-ecommerce 62 | npm run dev 63 | ``` 64 | #### note: both backend & frontend server will start at once with the above command. 65 | 66 | 6. #### Database Structure: (Table: columns) 67 | 1. categories: _id, name, createdAt, updatedAt; 68 | 2. orders: _id, status, products (Array), transaction_id, amount, address, user (Object), createdAt, updatedAt 69 | 3. products: _id, photo (Object), sold, name, description, price, category, shipping, quantity, createdAt, updatedAt 70 | 4. users: _id, role, history (Array), name, email, salt, hashed_password, createdAt, updatedAt 71 | 72 | ### App Description: 73 | 1. user can view all products 74 | 2. user can view single product 75 | 3. user can search products and view products by category and price range 76 | 4. user can add to cart checkout products using credit card info 77 | 5. user can register & sign in 78 | 6. admin can create, edit, update & delete products 79 | 7. admin can create categories 80 | 8. admin can view ordered products 81 | 9. admin can change the status of a product (processing, shipped, delivered, etc.) 82 | 83 | 6. Deployed on: (No longer available due to heroku free dyno plan has deprecated)
https://ecommerce-ak.herokuapp.com/ 84 | 7. raise a star to support me 85 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.0", 7 | "@material-ui/icons": "^4.9.1", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.5.0", 10 | "@testing-library/user-event": "^7.2.1", 11 | "braintree-web-drop-in-react": "^1.1.1", 12 | "config": "^3.3.1", 13 | "fontsource-roboto": "^3.0.3", 14 | "moment": "^2.27.0", 15 | "query-string": "^6.13.1", 16 | "react": "^16.13.1", 17 | "react-dom": "^16.13.1", 18 | "react-router-dom": "^5.2.0", 19 | "react-scripts": "5.0.1" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": "react-app" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashraf-kabir/mern-ecommerce/152bd41eab387997fed84e8e4f1ff0e151a15066/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 21 | 22 | 26 | 27 | 31 | 32 | MERN ECOMMERCE 33 | 34 | 35 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashraf-kabir/mern-ecommerce/152bd41eab387997fed84e8e4f1ff0e151a15066/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashraf-kabir/mern-ecommerce/152bd41eab387997fed84e8e4f1ff0e151a15066/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const App = () =>
Hello from ashraf kabir
; 4 | 5 | export default App; 6 | -------------------------------------------------------------------------------- /client/src/Routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter, Switch, Route } from 'react-router-dom'; 3 | import Signup from './user/Signup'; 4 | import Signin from './user/Signin'; 5 | import Home from './core/Home'; 6 | import PrivateRoute from './auth/PrivateRoute'; 7 | import Dashboard from './user/UserDashboard'; 8 | import AdminRoute from './auth/AdminRoute'; 9 | import AdminDashboard from './user/AdminDashboard'; 10 | import AddCategory from './admin/AddCategory'; 11 | import AddProduct from './admin/AddProduct'; 12 | import Shop from './core/Shop'; 13 | import Product from './core/Product'; 14 | import Cart from './core/Cart'; 15 | import Orders from './admin/Orders'; 16 | import Profile from './user/Profile'; 17 | import ManageProducts from './admin/ManageProducts'; 18 | import UpdateProduct from './admin/UpdateProduct'; 19 | import CategoryList from './admin/CategoryList'; 20 | import NotFound from './core/NotFound'; 21 | 22 | const Routes = () => { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | export default Routes; 52 | -------------------------------------------------------------------------------- /client/src/admin/AddCategory.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Layout from '../core/Layout'; 3 | import { isAuthenticated } from '../auth'; 4 | import { Link } from 'react-router-dom'; 5 | import { createCategory } from './apiAdmin'; 6 | 7 | const AddCategory = () => { 8 | const [name, setName] = useState(''); 9 | const [error, setError] = useState(false); 10 | const [success, setSuccess] = useState(false); 11 | 12 | // destructure user and token from localstorage 13 | const { user, token } = isAuthenticated(); 14 | 15 | const handleChange = (e) => { 16 | setError(''); 17 | setName(e.target.value); 18 | }; 19 | 20 | const clickSubmit = (e) => { 21 | e.preventDefault(); 22 | setError(''); 23 | setSuccess(false); 24 | // make request to api to create category 25 | createCategory(user._id, token, { name }).then((data) => { 26 | if (data.error) { 27 | setError(data.error); 28 | } else { 29 | setError(''); 30 | setSuccess(true); 31 | } 32 | }); 33 | }; 34 | 35 | const newCategoryForm = () => ( 36 |
37 |
38 | 39 | 47 |
48 | 49 |
50 | ); 51 | 52 | const showSuccess = () => { 53 | if (success) { 54 | return

{name} is created

; 55 | } 56 | }; 57 | 58 | const showError = () => { 59 | if (error) { 60 | return

Category should be unique

; 61 | } 62 | }; 63 | 64 | const goBack = () => ( 65 |
66 | 67 | Back to Dashboard 68 | 69 |
70 | ); 71 | 72 | return ( 73 | 77 |
78 |
79 | {showSuccess()} 80 | {showError()} 81 | {newCategoryForm()} 82 | {goBack()} 83 |
84 |
85 |
86 | ); 87 | }; 88 | 89 | export default AddCategory; 90 | -------------------------------------------------------------------------------- /client/src/admin/AddProduct.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import Layout from '../core/Layout'; 3 | import { isAuthenticated } from '../auth'; 4 | import { createProduct, getCategories } from './apiAdmin'; 5 | 6 | const AddProduct = () => { 7 | const [values, setValues] = useState({ 8 | name: '', 9 | description: '', 10 | price: '', 11 | categories: [], 12 | category: '', 13 | shipping: '', 14 | quantity: '', 15 | photo: '', 16 | loading: false, 17 | error: '', 18 | createdProduct: '', 19 | redirectToProfile: false, 20 | formData: '', 21 | }); 22 | 23 | const { user, token } = isAuthenticated(); 24 | 25 | const { 26 | name, 27 | description, 28 | price, 29 | categories, 30 | category, 31 | shipping, 32 | quantity, 33 | photo, 34 | loading, 35 | error, 36 | createdProduct, 37 | redirectToProfile, 38 | formData, 39 | } = values; 40 | 41 | // load categories and set form data 42 | const init = () => { 43 | getCategories().then((data) => { 44 | if (data.error) { 45 | setValues({ ...values, error: data.error }); 46 | } else { 47 | setValues({ 48 | ...values, 49 | categories: data, 50 | formData: new FormData(), 51 | }); 52 | } 53 | }); 54 | }; 55 | 56 | useEffect(() => { 57 | init(); 58 | }, []); 59 | 60 | const handleChange = (name) => (event) => { 61 | const value = name === 'photo' ? event.target.files[0] : event.target.value; 62 | formData.set(name, value); 63 | setValues({ ...values, [name]: value }); 64 | }; 65 | 66 | const clickSubmit = (event) => { 67 | event.preventDefault(); 68 | setValues({ ...values, error: '', loading: true }); 69 | 70 | createProduct(user._id, token, formData).then((data) => { 71 | if (data.error) { 72 | setValues({ ...values, error: data.error }); 73 | } else { 74 | setValues({ 75 | ...values, 76 | name: '', 77 | description: '', 78 | photo: '', 79 | price: '', 80 | quantity: '', 81 | loading: false, 82 | createdProduct: data.name, 83 | }); 84 | } 85 | }); 86 | }; 87 | 88 | const newPostForm = () => ( 89 |
90 |

Post Photo

91 |
92 | 100 |
101 | 102 |
103 | 104 | 110 |
111 | 112 |
113 | 114 |