Online Coding Bootcamp

Build ECommerce Website Like Amazon


Join Bootcamp


View Demo


Download Source


Contact Instructor

By the numbers

  • 12 Sessions
  • 24 Teaching Hours
  • 8 Languages & Frameworks
  • 3 GB Videos & Resources

What you’ll learn

  • HTML, CSS & JavaScript
  • React & Redux
  • Node, Express & MongoDB
  • Git, Github & VS Code

Requirements

  • Computer Algorithms
  • Basic HTML, CSS
  • Basic JavaScript
  • Code Editor & Web Browser

Instructor

  • 5 years of tutoring
  • 17 years of coding
  • 5 coding ebooks
  • 300 students

Session #1

HTML, CSS & JavaScript

Step 1: Introduction to Amazona

  • Coding course to build an eCommerce website like Amazon
  • Web design using HTML and CSS
  • User interactivity by JavaScript
  • Build frontend using React and Redux
  • Create backend by Node and MongoDB
  • Connect to Payment Gateways
  • Publish on the clouds like Heroku
  • Audiences from none-coders to middles

Step 2: Install development tools

  • Code Editor: VS Code
  • Web Browser: Google Chrome

 
Step 3: Create Home Page

  • Create amazona folder and open it in VS Code
  • Add template folder
  • Add template/index.html
  • Add template/style.css
  • Update index.html
  • Add html:5 tab
  • link href=style.css
  • h1 Amazona, p Welcome to Amazona
  • Update style.css
  • html {font-size: 62.5; box-sizing: border-box}
  • body { height: 100vh, font: 1.6rem Helvetica }
  • div.container {grid-template-area: “header” “main” “footer”, height: 100%; columns: 1fr; rows: 5rem 1fr 5rem}
  • div.header header {grid-area: header, flex, space-between, bgColor: #203040}
  • >div.header-brand a {color:#fff, font:3rem} a:hover{#ff8000}
  • >div.header-links > a Cart & a.Sign-In
  • div.main {grid-area: main } List of Products
  • div.footer {grid-area: footer, flex, center, bgColor, colot} All Right Reserved

Step 4: Add Sidebar

  • Update index.html
  • aside.sidebar {position: fixed, transform:translate(-30rem}; width:30rem;bgColor:#fafafa}
  • div.header-brand > button.open-button 9776 {color:white, border:none, font-size: 3rem}
  • div.sidebar-header {flex, space-between} Categories button.close-button
  • Add script
  • .open-button, .close-button, .sidebar
  • open-button.addEventListener(“click”) sidebar.classList.add(“open”)
  • close-button.addEventListener(“click”) sidebar.classList.remove(“open”)
  • .sidebar.open {transform:translate(0)}
  • .sidebar {transition: all .3s}

Step 5: Add Products

  • Add 3 product images to template/images
  • Update index.html
  • div.main > div.content > ul.products {flex, center, wrap}
  • Update style.css
  • li> div.product {flex, column, height: 50, flex: 0 1 34,border-bottom, margin:1}
  • .product-name, product-price {bold, 2rem}
  • .product-brand {#808080, 1.2rem}
  • .product-image {max-width:34, max-height: 34}
  •  .product-rating {marign-bottom:1rem}

Session #2

React

Step 1: Working with Git and GitHub

  • Install Node and Git
  • Open VS Code
  • Create amazona folder and Add it to the workspace
  • $ git config –global user.name “your name”
  • $ git config –global user.email “your email”
  • $ git init
  • Create ReadMe.md
  • Enter “# Amazona Project” in ReadMe.md and save it
  • $ git add . 
  • $ git commit -m “First Commit”
  • Create a Github account
  • Create a new GitHub repository and name it amazona
  • $ git remote add origin [email protected]:/amazona-final.git
  • git push origin master
  • Create a new branch for each session and merge with master at the end of the session
  • $ git branch -b session-1-html-css
  • Write your code
  • $ git add . && git commit -m “session-1” && git push
  • Open Github repository and Create Pull Request
  • Merge Pull Request
  • Repeat creating branches for next sessions

Step 2: Create React App

  • What is React?
  • npx create-react-app frontend
  • Copy div.container to App.js
  • Change class= to className=
  • Copy style.css content to index.css
  • Update App.js
  • const openMenu = () => {classList.add(“open”)}
  • const closeMenu = () => {classList.remove(“open”)}

Step 3: Create Home Screen

  • Create data.js
  • export default { products:[{_id:1, name:’Fit Shirt’, …}, {}, … ]
  • Update App.js
  • Remove all li except first
  • Create screens/HomeScreen.js
  • div.content > ul.products data.products.map (product => <li><Product product={product}/></li>)
  • Create components/Product.js
  • div.product> img.product-image …

Step 4: Add Page Navigation

  • What is SPA?
  • npm install react-router-dom
  • Update App.js
  • return <BroswerRouter> …</BroswerRouter>
  • div.main > div.content <Route path=”/” exact component={HomeScreen} />
  • Create screens/HomeScreen.js
  • Move div.content to HomeScreen.js
  • div.main > div.content > <Route path=”/product/:id” exact component={ProductScreen} />
  • Create screens/ProductScreen.js

Step 5: Create Product Page

  • Update ProductScreen
  • const product = data.products.find(x=>x.id == props.match.params.id)
  • div > div.back-to-result {padding:1rem}
  •  div.details {flex, space-beteen, wrap, padding: 1}
  • div.details-image {flex: 2 1 60} img {max-width:60, width: 100%}
  • div.details-info {flex: 1 1 30}
  • div.details-action {flex: 1 1 30, border, radius, bgColor:#f8f8f8}
  • div.details-info > ul > li> h4 {product.name}, rating, price, description
  • div.details-action > price, status, qty (select option 5), button.primary Add To Cart

Session #3

React Hooks & Redux

Step 1: Use Hooks

  • What is Hook?
  • Update HomeScreen.js
  • [productList, setProductList] = useState([])
  • useEffect(()=> {setProductList(data.products), []}

Step 2: Connect To Redux

  • What is Redux
  • npm install redux react-redux
  • Create store.js
  • initState= {products:[]}
  • reducer = (state, action) => switch LOAD_PRODUCTS: {products: action.payload}
  • export default createStore(reducer, initState)
  • Edit HomeScreen.js
  • shopName = useSelector(state=>state.products)
  • const dispatch = useDispatch()
  • useEffect(()=>dispatch({type: LOAD_PRODUCTS, payload: data})
  • Edit index.js
  • <Provider store={store}>…</Provider>

Step 3: Chrome Redux Dev Tools

  • What is Redux Dev Tools?
  • Install Redux Dev Tools Chrome Extension
  • Edit store.js
  • window.__REDUX_DEVTOOLS_EXTENSION__ &&
  • Check state change based on triggering the action

Step 4: Handle Asynchronous Actions

  • What is async actions and redux-thunk?
  • npm install redux-thunk
  • Edit store.js
  • import thunk from ‘redux-thunk’
  •  import {compose, applyMiddleware} from redux
  • composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__() || compose
  • createStore(…, …, composeEnhancer(applyMiddleware(thunk)))

Session #4

Handle Shopping Cart

Step 1: Create Reducers and Actions

  • Create constants/cartConstants.js
  • CART_ADD_ITEM, CART_REMOVE_ITEM
  • Create reducers/cartReducers.js
  • cartReducer(state = { cartItems: [] }, action)
  • switch(action.type) case CART_ADD_ITEM:
  • if(exist) state.cartItems.map(x=>x.product === product.product ? item : x)}
  • else { cartItems: […state.cartItems, item] }
  • case CART_REMOVE_ITEM:
  • cartItems: state.cartItems.filter(x => x.product != action.payload)
  • Edit store.js
  • import {combineReducers} from ‘redux’
  • reducers = combineReducers({cart: cartReducers, products: reducer});
  • Create actions/cartActions.js
  • addToCart = (productId, qty) => async (dispatch, getState)
  • const product = await new Promise(res, rej =>{setTimeout(()=>res(data.products.find(x=>x._id==productId), 1000)})
  • dispatch({type: CART_ADD_ITEM, payload: {product: data._id, …, qty}})
  • removeFromCart(productId) => (dispatch, getState)
  • dispatch({ type: CART_REMOVE_ITEM, payload: productId });

Step 2: Create Shopping Cart Screen

  • create screens/cartScreen.js
  • productId = props.match.params.id
  • qty = Number(props.location.search.split(“=”)[1])
  • const cart = useSelector(state=>state.cart)
  • const {cartItems} = cart
  • useEffect(() => {if (productId) {dispatch(addToCart(productId, qty))}},[])
  • const removeFromCartHandler = (productId) => {dispatch(removeFromCart(productId))}
  • const checkoutHandler = () =>/signin?redirect=shipping
  • div.cart {flex, wrap, align-items: flex-start}
  • div.cart-list {flex: 3 1 60rem}
  • ul.cart-list-container
  • div.cart-action { flex:1 1 20rem,bgColor, border,radius}
  • h3 subtotal:$X & button Procees To Checkout

Step 3: Remember Shopping Cart By Cookie

  • npm install js-cookie
  • Edit store.js
  • const cartItems = Cookie.getJSON(“cartItems”) || [];
  • const initialState = { cart: { cartItems}};
  • Update cartAction.js
  • addToCart() & removeFromCart
  • const { cart: { cartItems } } = getState();
  • Cookie.set(“cartItems”, JSON.stringify(cartItems));

Step 4: Show Badge on Cart Menu

  • Update App.js
  • const cart = useSelector((state) => state.cart);
  • const { cartItems } = cart;
  • {cartItems.length !== 0 && <div className=”badge”>{cartItems.length}</div>}
  • .badge {: inline-block;absolute;top: 5px; 1X1; p: .3rem;radius: 50%;size: 1rem; center; #ff4400;}

 

Session #5

Node & MongoDB

Step 1: Create Node Application

  • What is node?
  • npm init
  • Create backend/server.js
  • console.log(“Amazona Backend”)
  • Move /frontend/src/data.js to backend
  • const data = require(“./data.js”)
  • console.log(data.products)

Step 2: Add Babel Tanspiler

  • What is Babel tanspiler
  • npm install @babel/core, cli, node, preset-env
  • Create .babelrc
  • { presets: [[“@babel/preset-env”]]}
  • Edit package.json
  • “start”:”nodemon –exec babel-node server.js”

Step 3: Enable Code Linting

  • What is Code Linter
  • Install ESLint extension in VS Code 
  • Edit setting.json
  • “editor.codeActionsOnSave”: {“source.fixAll.eslint”: true},
  • npm install eslint eslint-config-airbnb eslint-plugin-importeslint-plugin-jsx-a11y eslint-plugin-react  eslint-plugin-react-hooks –save-dev
  • Create .eslintrc.js
  • module.exports = { env: {browser: true,es6: true,node: true},
  • extends: [‘plugin:react/recommended’,’airbnb’,],
  • parserOptions: {ecmaFeatures: {jsx: true}, ecmaVersion: 2018, sourceType: ‘module’,},
  • plugins: [‘react’,],rules: {“no-underscore-dangle”: 0, …}
  • Check Ctrl+Shift+P > ESLint Show Output for debug

Step 4: Use Express.JS

  • What is epxressjs
  • npm install express
  •  create productRoute
  • move data.js to backend
  • create /api/products api
  • create /api/product/:id api

Step 5: Connect to MongoDB

  • npm install mongoose dotenv
  • Create config.js 
  • dotenv.config()
  • export default { MONGODB_URL: process.env.MONGODB_URL ||  ‘mongodb://localhost/amazona’}
  • Edit server.js 
  • mongoose.connect(mongodbUrl, {useNewUrlParser, useCreateIndex, useUnifiedTopology }).catch(e => console.log(err))

Step 6: Create User Model

  • Create models/userModel.js
  • new schema{name, email, password, isAdmin}, {timestamps: true}
  • email:{ required: true, unique: true, dropDups: true }
  • userModel = model.create(“User”, userSchema)
  • Create routes/userRoute.js
  • get /createadmin, async (req, res) => 
  • new User({email:’admin’, password:’admin’,isAdmin:true})
  • await user.save()
  • Edit server.js
  • app.use(“/api/users”, userRoute)

Session #6

User Authentication

Step 1: Create Register Screen

  • Create screen/RegisterScreen.js
  • const [name, setName] = useState(”) , email, pass
  • const userRegister = useSelector(state => state.userRegister)
  • const { loading, userInfo, error } = userRegister
  • const submitHandler = (e) => {dispatch(register(name, email, password));}
  • div.form { flex ,center, center, height: 100%;}
  • form> ul.form-container {flex, column, width: 32, border, list:none}
  • .form-container > li { flex,  column, marign: 1rem}
  • .form-container > li > input{padding: 1, border, border-radius: .5}
  • .form-container > li > button.primary {color: #f0c000}
  • const redirect = props.location.search.split(“=”)[1]
  • useEffect(()=>{ if(userInfo) props.history.push(redirect)}, [success, error])
  • .form-container > li >{loading && <div>Loading…</div>} {error && <div>{error}</div>}

Step 2: Create Register Reducers and Actions

  • Create reducers/userReducers.js
  • case USER_REGISTER_REQUEST: { loading: true }
  • case USER_REGISTER_SUCCESS: { loading: false, userInfo: action.payload }
  • case USER_REGISTER_FAIL:{ loading: false, error: action.payload } default: return state
  • Create actions/userActions.js
  • register = (name, email, password) => async (dispatch) => {}
  • dispatch({ type: USER_REGISTER_REQUEST, payload: { name, email, password } })
  • try {  await Axios.post(“/api/users/register”, { name, email, password })
  • dispatch({ type: USER_REGISTER_SUCCESS, payload: data })
  • catch(e){ dispatch({ type: USER_REGISTER_FAIL, payload: error.message }) }

Step 3: Build Register API

  • npm install bodyParser
  • Edit server.js
  • app.use(bodyParser.json())
  • Edit userRoute.js
  • router.post(“register”)
  • user = new User({name: req.body.name, email: req.body.email,password: req.body.password})
    try{newUser = await user.save()
  • res.send({ _id: newUser.id, …, token: “jsonwebtoken”}
  • catch(e) {res.status(401).send({ msg: ‘Invalid User Data.’ })}

Step 4: Generate JSON Web Token

  • npm install jsonwebtoken
  • Update config.js
  • JWT_SECRET: process.env.JWT_SECRET || ‘somethingsecret’
  • Create util.js
  • function generateToken(user)
  • jwt.sign({_id: user._id, …}, config.JWT_SECRET, {expiresIn: ’48h’})
  • function isAuth (req, res, next)
  • const token = req.headers.authorization
  • if (token) {const onlyToken = token.slice(7, token.length)}
  • jwt.verify(onlyToken, config.JWT_SECRET, (err, decode)
  • if (err) {return res.status(401).send({ msg: ‘Invalid Token’ })}
  • req.user = decode; next(); return
  • return res.status(401).send({ msg: “Token is not supplied.”})
  • function isAdmin (req, res, next)
  • if (req.user && req.user.isAdmin) {return next();}
  • else res.status(401).send({ msg: ‘Admin Token is not valid.’ })

Step 5: Remember User by Cookie

  • Update store.js
  • const userInfo = Cookie.getJSON(“userInfo”) || null
  • const initialState = { userSignin: { userInfo }}
  • register & signin = (name, email, password) => {Cookie.set(‘userInfo’, JSON.stringify(data))}
  • Edit App.js
  • const userSignin = useSelector(state => state.userSignin);
  • const { userInfo } = userSignin;
  • div.header-links > {userInfo ? <Link to=”/profile”>{userInfo.name}/Link> : <Link to=”/signin”>Sign In</Link>}
  • {userInfo && userInfo.isAdmin && (div.dropdown > a.dropdown-content Admin > ul > li ><Link to=”/products”>Products</Link>

Step 6: Add Authenticated Routes

  • Edit App.js
  • window.isAuth = !!userInfo;
  • Create PrivateRoute.js
  • const PrivateRoute = ({ component: Component, …rest }) => (
  • <Route {…rest} render={(props) => ( window.isAuth === true ? <Component {…props} /> : <Redirect to=”/signin” /> )} />
  • Edit App.js
  • <PrivateRoute path=”/products” component={ProductsScreen} />

Session #7

Manage Products

Step 1: List Products

  • Create ProductsScreen.js
  • const productList = useSelector(state => state.productList)
  • const { loading, products, error } = productList
  • const dispatch = useDispatch()
  • useEffect(() => {dispatch(listProducts()), []}
  • div > div.product-header { flex, space-between, align-items: flex-start}
  • div.product-list > table.table {width: 100%;}
  • thead > tr > th > ID, Name, Price, Category, Action
  • tbody > {products.map(product => (
  • button onClick={() => openModal(product)}
  • button onClick={() => deleteHandler(product)}
  • Create constants/productConstants.js
  • PRODUCT_LIST_REQUEST, SUCCESS, FAIL
  • Create reducers/productReducers.js
  • case REQUEST: {loading:true} case SUCCESS: {products:action.payload}
  • Create actions/productActions.js
  • function listProducts() {dispatch({ type: PRODUCT_LIST_REQUEST })const { data } = await axios.get(“/api/products”)} dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data })

Step 2: Build List/Details Product API

  • Create models/productModels.js
  • prodctSchema = new mongoose.Schema({name: { type: String, required: true }, …})
  • productModel = mongoose.model(“Product”, prodctSchema)
  • Create routes/productRoute.js
  • router.get(“/”, async (req, res) => {
  • products = await Product.find({ }) res.send(products)
  • Edit server.js
  • app.use(“/api/products”, productRoute)
  • router.get(“/:id”, async (req, res) => {
  • const product = await Product.findOne({ _id: req.params.id })

Step 3: Add/Edit Products

  • Edit ProductsScreen.js
  • const [modalVisible, setModalVisible] = useState(false)
    const [name, setName] = useState(”), …
  • productSave = useSelector(state => state.productSave)
  • { loading: loadingSave, success: successSave, error: errorSave } = productSave
  • openModal = (product) => {setModalVisible(true) setId(product._id)}
  • submitHandler = (e) => {e.preventDefault(); dispatch(saveProduct({_id: id,name}));
  • useEffect(()=>{if (successSave) {setModalVisible(false)}}
  • button.button primary onClick={() => openModal({})} Create Product
  • modalVisible && div.form > form > onSubmit={submitHandler} 
  • ul.form-container > li > h2> Create Product
  • li > {loadingSave &&  Loading…} {errorSave &&  errorSave} 
  • li > label > Name & input value={name}
  • Edit constants/productConstants.js
  • PRODUCT_SAVE_REQUEST, SUCCESS, FAIL
  • Edit reducers/productReducers.js
  • case REQUEST: {loading:true} case SUCCESS: {success: true, product: action.payload}
  • Edit actions/productActions.js
  • function saveProduct(product) {dispatch({ type: PRODUCT_SAVE_REQUEST })const { data } = product._id? await put(“/api/products”): axios.post(“/api/products”)}
  • dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data })

Step 4: Upload Image

  • Edit ProductsScreen.js
  • const uploadImageFile = (e) => { const file = e.target.files[0];
    const bodyFormData = new FormData(); bodyFormData.append(‘image’, file);
  • axios.post(‘/upload’, bodyFormData, {headers: {
    headers: { ‘Content-Type’: ‘multipart/form-data’ },}
  • setImage(response.data)
  • input type=”file” name=”imageFile” onChange={uploadImageFile}
  • Edit server.js
  • import fileUpload from ‘express-fileupload’
  • app.use(fileUpload())
  • app.post(‘/upload’, (req, res) => {
    if (!req.files || Object.keys(req.files).length === 0) {return res.status(400).send(‘No files were uploaded.’);}
  • const { image } = req.files; const filename = `${new Date().getTime()}.jpg`; image.mv(`${uploads}/${filename}`, (err) => {
    if (err) return res.status(500).send(err); res.send(`/uploads/${filename}`); }); });

Step 5: Build Add/Edit Product API

  • Edit routes/productRoute.js
  • router.post(“/”, isAuth, isAdmin, async (req, res) => {
  • const product = new Product({name: req.body.name, … }
  • try{const newProduct = await product.save()} catch(e){}
  • router.put(“/:id”, isAuth, isAdmin, async (req, res) => {
  • const product = await Product.findById(req.params.id)
  • if (product) { product.name = req.body.name; const updatedProduct = await product.save() }

Step 6: Load Products From Backend

  • Update HomeScreen.js
  • const productList = useSelector(state => state.productList)
  • const { loading, products, error } = productList
  • const dispatch = useDispatch()
  • useEffect(() => {dispatch(listProducts()), []}
  • Update ProductScreen.js
  • const productDetails = useSelector(state => state.productDetails)
  • const { loading, products, error } = productList
  • const dispatch = useDispatch()
  • useEffect(() => {dispatch(listDetails(props.match.params.id)), []}

Session #8

Checkout Order

Step 1: Create Checkout Wizard

  • Create components/CheckoutSteps.js
  • div.checkout-steps {flex, space-between, 40rem, m: 1rem auto}
  • div.props.step1 ? ‘active’ : ”} Signin { border-top: .3rem #c0c0c0 solid;color: #c0c0c0;flex: 1 1;padding-top: 1rem;
    }}
  • .active {border-top: .3rem #f08000 solid; olor: #f08000;}

Step 2: Get Shipping Address

  • Add ShippingScreen.js
  • const [address, setAddress] = useState(”), city, postalCode, country
  • submitHandler = (e)=>{ dispatch(saveShipping({ address, city, postalCode, country })); props.history.push(‘payment’);}
  • div >
  • div.form > form > ul.form-container
  • li > h2 > shipping
  • li >label Address & input name=”address” 
  • li > button Continue
  • Update cartActions.js
  • saveShipping = (data) => (dispatch) => {dispatch({ type: CART_SAVE_SHIPPING, payload: data });}
  • Update cartReducers.js
  • case CART_SAVE_SHIPPING: return { …state, shipping: action.payload };

Step 3: Get Payment Method

  • Add PaymentScreen.js
  • const [paymentMethod, setPaymentMethod] = useState(”);
  • submitHandler = (e)=>{ dispatch(savePayment({ paymentMethod })); props.history.push(‘placeorder’);}
  • div.form > form > ul.form-container
  • li > h2 > Payment
  • li > input type=”radio” value=”paypal”onChange={(e) => setPaymentMethod(e.target.value)}> label Paypal
  • li > button Continue
  • Update cartActions.js
  • saveShipping = (data) => (dispatch) => {dispatch({ type: CART_SAVE_PAYMENT, payload: data });}
  • Update cartReducers.js
  • case CART_SAVE_PAYMENT: { …state, payment: action.payload } 

Step 4: Preview Order

  • Add PlaceOrderScreen.js
  • const cart = useSelector(state => state.cart);
  • const { cartItems, shipping, payment } = cart;
  • if (!shipping.address) {props.history.push(“/shipping”);} else if (!payment.paymentMethod) {props.history.push(“/payment”);}
  • const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0)
  • const shippingPrice = itemsPrice > 100 ? 0 : 10
  • const taxPrice = 0.15 * itemsPrice
  • const totalPrice = itemsPrice + shippingPrice + taxPrice
  • div > <CheckoutSteps step1 step2 step3 step4 ></CheckoutSteps>
  • div.placeorder {flex, wrap, space-between, p:1}
  • > div.placeorder-info {flex: 3 1 60rem}
  • > div.placeorder-action {flex: 1 1 20rem, border, radius, p:1, bgColor}
  • placeorder-info > div { border, radius, bgColor:fcfcfc, p:1, m:1 }
  • placeorder-action > ul {padding:0;list:none}
  • placeorder-action > ul > li { flex, space-between, m-button:1}
  • placeorder-action > ul > li:last-child { f:2rem, color:c04000}

Step 5: Place Order

  • Edit PlaceOrderScreen.js
  • const { loading, success, error, order } = orderCreate
  • const placeOrderHandler = () => {}
  • dispatch(createOrder({ orderItems: cartItems, shipping, payment, itemsPrice, shippingPrice, taxPrice, totalPrice}))
  • useEffect(() => { if (success) {props.history.push(“/order/” + order._id)}}, [success]);

  • Create orderConstants.js
  • const ORDER_CREATE_REQUEST
  • Create orderActions.js
  • createOrder = (order) => async (dispatch, getState)
  • dispatch({ type: ORDER_CREATE_REQUEST, payload: order })
  • const { userSignin: { userInfo } } = getState()
  • const { data: { data: newOrder } } = await Axios.post(“/api/orders”, order, {headers: {Authorization: ‘ Bearer ‘ + userInfo.token}})
  • Create orderReducers.js
  • function orderCreateReducer(state = {}, action)
  • case ORDER_CREATE_SUCCESS: return { loading: false, order: action.payload, success: true }

Step 6: Build Create Order Backend

  • Create orderModel.js
  • const shippingSchema = {
    address: { type: String, required: true }, … }
  • const orderItemSchema = new mongoose.Schema({
    name, qty, image, price, product: { type: mongoose.Schema.Types.ObjectId, ref: ‘Product’, required: true},
  • const orderSchema = new mongoose.Schema({ user: { type: mongoose.Schema.Types.ObjectId, ref: ‘User’, required: true }, orderItems: [orderItemSchema], shipping: shippingSchema,
    payment: paymentSchema, itemsPrice: { type: Number }, … }, {
    timestamps: true}}
  • const orderModel = mongoose.model(“Order”, orderSchema)
  • Create orderRoute.js
  • router.post(“/”, isAuth, async (req, res) => { const newOrder = new Order({ orderItems: req.body.orderItems, user: req.user._id, shipping: req.body.shipping,…})
    const newOrderCreated = await newOrder.save();
    res.status(201).send({ message: “New Order Created”, data: newOrderCreated })})
  • Edit server.js
  • app.use(“/api/orders”, orderRoutes)

Session #9

Manage Orders

Step 1: Order Details Screen

  • Create OrderScreen.js
  • const orderDetails = useSelector(state => state.orderDetails);
  • const { loading, order, error } = orderDetails;
  • div.placeorder > div.placeorder-info > div > h3 Shipping
  • div {order.shipping.address}, {order.shipping.city},
    {order.shipping.postalCode}, {order.shipping.country},
  • div {order.isDelivered ? “Delivered at ” + order.deliveredAt : “Not Delivered.”}
  • div.placeorder > div.placeorder-info > div > h3 Payment
  • Payment Method: {order.payment.paymentMethod}
  • {order.isPaid ? “Paid at ” + order.paidAt : “Not Paid.”}
  • div.cart-list-container 
  • order.orderItems.map(item => {item.name}
  • div.placeorder-action > ul > li.placeorder-actions-payment
  • li > h3 > Order Summary
  • li> div Items & div ${order.itemsPrice} & ${order.shippingPrice} ${order.taxPrice} ${order.totalPrice}
  • Edit orderConstants.js
  • export const ORDER_DETAILS_REQUEST = ‘ORDER_DETAILS_REQUEST’
  • Edit orderActions.js
  • detailsOrder = (orderId) => async (dispatch, getState)

Step 2: Connect to PayPal

  • Create components/PaypalButton.js
  • const [sdkReady, setSdkReady] = useState(false)
  • const addPaypalSdk = async () => { const result = await axios.get(“/api/config/paypal”)
  • const clientID = result.data; 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);
  • useEffect(() => { if (!window.paypal) { addPaypalSdk();} }, []);
  • if (!sdkReady) {return <div>Loading…</div>}
  • const Button = window.paypal.Buttons.driver(‘react’, { React, ReactDOM });
  • const onApprove = (data, actions) => actions.order.capture().then(details => props.onSuccess(data, details)).catch(err => console.log(err));
  • const createOrder = (data, actions) => actions.order.create({
    purchase_units: [ {amount: {currency_code: ‘USD’,value: props.amount
    } }]});
  • const Button = window.paypal.Buttons.driver(‘react’, { React, ReactDOM });
  • return <Button {…props} createOrder={(data, actions) => createOrder(data, actions)} onApprove={(data, actions) => onApprove(data, actions)} />
  • export default PaypalButton
  • Edit OrderScreen.js
  • div.placeorder-action > ul > li.placeorder-actions-payment
  • {loadingPay && <div>Finishing Payment…</div>}
    {!order.isPaid && <PaypalButton amount={order.totalPrice} onSuccess={handleSuccessPayment} />
  • const handleSuccessPayment = (paymentResult) => {
    dispatch(payOrder(order, paymentResult)); }

Step 3: Pay Order

  • Edit orderActions.js
  • payOrder = (order, paymentResult) => async (dispatch, getState)
  • dispatch({ type: ORDER_PAY_REQUEST, payload: paymentResult })
  • const { userSignin: { userInfo } } = getState()
  • const { data } = await Axios.put(“/api/orders/” + order._id + “/pay”, paymentResult, { headers: { Authorization: ‘Bearer ‘ + userInfo.token }});
  • dispatch({ type: ORDER_PAY_SUCCESS, payload: data })
  • function orderPayReducer(state = {order: { orderItems: [],shipping: {},payment: {}}}, action)
  • switch (action.type) { case ORDER_PAY_REQUEST:return { loading: true }; case ORDER_PAY_SUCCESS:return { loading: false, success:true };
  • Edit store.js
  • { orderPay : orderPayReducer }
  • Edit config.js
  • PAYPAL_CLIENT_ID: process.env.PAYPAL_CLIENT_ID || ‘sb’
  • Edit server.js
  • app.get(“/api/config/paypal”, (req, res) => {
    res.send(config.PAYPAL_CLIENT_ID); })
  • Edit orderRoute.js
  • router.put(“/:id/pay”, isAuth, async (req, res) => {
  • order.payment = {paymentMethod: ‘paypal’,paymentResult: {payerID: req.body.payerID,orderID: req.body.orderID,paymentID: req.body.paymentID}

Step 4: Order List Screen

  • Create OrdersScreen.js
  • const orderList = useSelector(state => state.orderList)
  • const { loading, orders, error } = orderList
  • useEffect(() => { dispatch(listOrders());}, [successDelete]);
  • return loading ? <div>Loading…</div> : 
  • div.content > div.order-header  Orders
  • div.order-list > table.table
  • ID, DATE, TOTAL, USER, PAID, DELIVERED
  • {orders.map(order => (<tr key={order._id}> <td>{order._id}</td>… }
  • td <Link to={“/order/” + order._id} className=”button secondary” >Details</Link> {‘ ‘}
  • <button type=”button” onClick={() => deleteHandler(order)} className=”button secondary”>Delete</button>
  • Edit userRoute.js
  • router.get(“/”, isAuth, async (req, res) => {
    const orders = await Order.find({}).populate(‘user’);
    res.send(orders); });

Session #10

User Profile & Product Review

Step 1: User Profile

  • Create ProfileScreen.js
  • const [name, setName] = useState(”); password, email
  • div.profile > div.profile-info > form > ul.form-container > li > h2 > User Profile
  • li {loading && Loading… } {error && {error}}
    {success && Profile Saved Successfully. }
  • li > button  onClick={handleSignout}  Signout
  • div.profile-orders
  • {loadingOrders ? Loading… : errorOrders ? {errorOrders}: }
  • table.table > thead > tr > th ID, DATE, TOTAL, STATE
  • {orders.map(order =>
    {order._id}…
  • handleLogout = () => { dispatch(signout()) ; props.history.push(“/signin”)
  • submitHandler = (e) => { e.preventDefault(); dispatch(update({ userId: userInfo._id, email, name, password })) }
  • const userUpdate = useSelector(state => state.userUpdate)
  • const { loading, success, error } = userUpdate
  • const myOrderList = useSelector(state => state.myOrderList)
  • const { loading: loadingOrders, orders, error: errorOrders } = myOrderList
  • useEffect(() => { if (userInfo) { setEmail(userInfo.email);setName(userInfo.name);setPassword(userInfo.password);} dispatch(listMyOrders()) } , [userInfo]}
  • Create Action and Reducer fot listMyOrder
  • Create Action and Reducer fot updateProfile

Step 2: Review Products

  • Edit ProductScreen.js
  • const [comment, setComment] = useState(”);
  • const [rating, setRating] = useState(”);
  • const { loading: loadingSaveReview, error: errorSaveReview, success: successSaveReview } = productReviewSave;
  • useEffect(() => { if (successSaveReview) { setComment(”); setRating(”); alert(‘Review Submitted’); dispatch({ type: PRODUCT_REVIEW_SAVE_RESET }); }
  • else {dispatch(detailsProduct(props.match.params.id));}
    }, [successSaveReview]);
  • div.details-info > li >
  • div.details & div > h2 Review
  • {product.reviews.length === 0 &&There is no review.}
  • ul.review{list-style-type: none;padding: 0;}
  • product.reviews.map((review) => ({review.name} {review.createdAt.substring(0, 10)} {review.comment}))
  • li > h3 Write a customer reviews
  • {window.isAuth? (form ul.form-container
  • li > label Rating > select required value={rating} name=”rating” id=”rating” onChange={(e) => setRating(e.target.value)}>Select
  • li >  label Comment textarea required value={comment} onChange={(e) => setComment(e.target.value)} />
  • const submitHandler = (e) => { e.preventDefault(); dispatch(saveProductReview(props.match.params.id, { comment, rating })); };
  • Create components/Rating.js
  • = 1 ? ‘active’ : ”}>☆ …
  • Update index.css
  • .rating-container {display: flex;align-items: center;}
  • .rating-container > div:last-child{margin-top: .5rem;margin-left: .5rem;}
  • .rating{margin: .5rem 0;}
  • .rating > span {display: inline-block;position: relative;font-size: 2.5rem;color: #c0c0c0;line-height: 2rem;}
  • .rating > span.active:before{content: “2605”;position: absolute; color:#F0C000}

Step 3: Load Categories From Backend

  • Update App.js
  • const productCategoryList = useSelector((state) => state.productCategoryList);
  • const { categories, loading, error } = productCategoryList;
  • useEffect(() => {dispatch(listProductCategories());}, []);
  • {loading? Loading… : error ?{error} : categories.length === 0 ? (There is no categories.) 
  • categories.map((x) => ( {x}))}
  • Edit productActions.js
  • listProductCategories() => {}
  • dispatch({ type: PRODUCT_CATEGORY_LIST_REQUEST, loading: true })
  • const result = await axios.get(‘/api/products/categories’)
  • Edit reducers/productReducers.js
  • function productCategoryRegucer(){}
  • Edit store.js
  • {productCategoryList: productCategoryListReducer}
  • Edit routes/productRoute.js
  • router.get(‘/categories’, async (req, res) => {
  • const categories = await Product.find().distinct(‘category’) res.send(categories);
  •  

Step 4: Search and Sort Products

  • Edit HomePage.js
  • const [searchKeyword, setSearchKeyword] = useState(”);
  • const [sortOrder, setSortOrder] = useState(”);
  • const productList = useSelector((state) => state.productList)
  • useEffect(() => {dispatch(listProducts(category, searchKeyword, sortOrder)); }, [dispatch, category]);
  • const searchHandler = (e) => {
    e.preventDefault(); dispatch(listProducts(category, searchKeyword, sortOrder));};
  • const sortHandler = (e) => {
    setSortOrder(e.target.value);dispatch(listProducts(category, searchKeyword, e.target.value));};
  • div.conent > {category && ({category})}
  • ul.filter { display: flex;flex-wrap: wrap;}
  • li> form onSubmit={searchHandler}
  • input value={searchKeyword}
  • button type=”submit”>Search
  • li> Sort by {‘ ‘} select newest highest lowest
  • Edit productActions.js
  • const listProducts = (category = ”, searchKeyword = ”, sortOrder = ”) => async (dispatch) => {
  • dispatch({ type: PRODUCT_LIST_REQUEST, payload: { category, searchKeyword, sortOrder } })
  • try { const result = await axios(`/api/products?category=${category}&search=${searchKeyword}&sort=${sortOrder}`)
  • Edit productRoute.js
  • router.get(‘/’, async (req, res) => {
    const category = req.query.category ? { category: req.query.category } :{};
  • const search = req.query.search ? { name: { $regex: req.query.search, $options: ‘i’, }, } : {};

  • const order = req.query.sort ? (req.query.sort === ‘lowest’
    ? { price: 1 } : { price: -1 }) : { _id: -1 };
  • const products = await Product.find({ …category, …search }).sort(order); 
  •  

Session #11

Deploy Website

Step 1: Connect To Cloud MongoDB

  • Create an account on https://www.mongodb.com/cloud
  • Login to https://cloud.mongodb.com/
  •  Add database user
  • Left sidebar> Select Security > Database Access
  • Select Add New User button
  • Enter user name and password and click Add User
  • Add IP whitelist
  • Left sidebar > Select Security > Network Access
  • Select Add IP Address
  • Enter 0.0.0.0/0 in Whitelist Entry and click Confirm
  • Get connection string
  • Left sidebar > Select Altas > Cluster
  • Click Connect
  • Click Connect to your application
  • Click Copy button

Step 2: Deploy on Heroku

  • Create git repository in amazona folder using git init
  • Create Heroku Account at heroku.com
  • Install Heroku CLI
  • $ heroku login
  • $ heroku apps:create amazonaapp
  • Edit package.json
  • “build”: “rm -rf dist && babel backend -d dist”,
  • “heroku-postbuild”: “npm run build && cd frontend && npm install && npm run build”
  • “engines”: { “node”: “12.4.0”, “npm”: “6.9.0”}
  • Create Procfile
  • web: node dist/server.js
  • Set MongoDB connection string in Heroku
  • Open Heroku apps https://dashboard.heroku.com/apps/
  • Select your apps, open Setting Tab and click Reveal Config Vars
  • Add key MONGODB_URL
  • Enter copied connection string from the previous step
  • Edit config.js
  • PORT: process.env.PORT || 5000
  • Edit server.js
  • app.use(express.static(path.join(__dirname, ‘/../frontend/build’)));
    app.get(‘*’, (req, res) => res.sendFile(path.join(`${__dirname}/../frontend/build/index.html`));
  • app.listen(config.PORT, …)
  • Edit .babelrc
  • [“@babel/preset-env”,,{“targets”: {“node”: “current”}}]
  • git add . && git commit -m “publish”
  • git push heroku

Session #12

Project Delivery and Answer To Questions

Step 1: Project Qualification

  • Code quality
  • Done features

Step 2: Answer to Questions

  • Coding errors
  • Using Clouds
  • Using third-party services like Paypal, Heroku, …


Join Bootcamp


View Demo


Download Source


Contact Instructor

How does it all work?

All classes will be held on Zoom. Zoom is a video conferencing tool to hold online classes with screen sharing. Create a Zoom account at zoom.us. Then click the “Join Bootcamp” button and fill the form. I will send you the payment link and Zoom invitation link to join the classes.

what should I prepare for the class?

You need to download Zoom and install it on your computer. Also, install VS Code and Google Chrome to write code and check the result.

I will give you learning materials like 6 ebooks and other resources throughout the class.

What if I am not happy?

My courses are helping hundreds of non-coders become developers at small and big companies. If for any reason, you’re feeling blue and not satisfied then email me and I’ll refund you. Seriously, I want you to learn. That’s simple. No question about it.

how many classes are available?

Most of my coding bootcamp takes 12 weeks. But If you need more classes we can plan them.

Also there is intensive Bootcamp option that you can attend, It has 2 session per week and takes 6 weeks.

How can I pay the class fee?

You can e-Transfer the fee to [email protected]

Also there are other options like Paypal.

I have more questions?

Sure. For quick questions tweet @BasirJD. You can also send me your questions via email if that’s your jam. What else?!
(let’s follow each other on Twitter @BasirJD— become friends?)