Online Coding Bootcamp

Build ECommerce Website Like Amazon

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

  • 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
  • Code Editor: VS Code
  • Web Browser: Google Chrome
 
  • 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
  • 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}
  • 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

  • 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 git@github.com:/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
  • 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”)}
  • 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 …
  • 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
  • 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

  • What is Hook?
  • Update HomeScreen.js
  • [productList, setProductList] = useState([])
  • useEffect(()=> {setProductList(data.products), []}
  • 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>
  • 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
  • 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

  • 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 });
  • 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
  • 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));
  • 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

  • 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)
  • 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”
  • 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-import
    eslint-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
  • What is epxressjs
  • npm install express
  •  create productRoute
  • move data.js to backend
  • create /api/products api
  • create /api/product/:id api
  • 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))
  • 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

  • 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>}
  • 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 }) }
  • 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.’ })}
  • 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.’ })
  • 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>
  • 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

  • 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 })
  • 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 })
  • 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 })
  • 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}`); }); });
  • 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() }
  • 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

  • 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;}
  • 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 };
  • 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 } 
  • 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}
  • 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 }
  • 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

  • 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)
  • 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)); }
  • 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}
  • 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

  • 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
  • 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}
  • 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);
  •  
  • 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

  • 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
  • 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

  • Code quality
  • Done features
  • Coding errors
  • Using Clouds
  • Using third-party services like Paypal, Heroku, …

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.

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.

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.

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.

You can e-Transfer the fee to basir.jafarzadeh@gmail.com

Also there are other options like Paypal.

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?)