Menu

Full Stack Morning

Full Stack Morning

  1. Given 3 variables, money = 57 (us dollars), distance = 122 (miles) and fuel = 20 (gallons). You need to console log three lines. Create three new variables, name them anything you want. The first variable should be the money, but the value should be in rupees, the second one the distance should be in kilometers and the third new variable’s value should be in liters. Use the original variable values to do the conversion. In the end, write three console logs which shows the original and the new converted values.
  2. Make a variable named choice that will have a random number value between 1 – 10. Use the Math.random() method to do this. Then, write a simple conditional to check if choice is 7. If choice is 7 console log ‘LUCKY’, otherwise console log ‘UNLUCKY’.
  3. Create a variable called num and give it a random number value. Then write a conditional to check if the number is odd. If num is odd, console log ‘ODD’, otherwise console log ‘EVEN’.
  4. Create a variable named food. Create another variable named randomNum which should be a random number between 1 – 6.  Then write a switch statement which sets each number case to a particular food. Case 1 should make food equal to ‘Apple’, case 2 should make food equal to grape and so on. Here is the list, (apple, grape, cabage, carrot, biryani, pulav).
    Next, write normal if, else if, else statements to do the following:
    1. If food is set to either ‘apple’ or ‘grape’, your code should console log ‘fruit’.
    2. If food is set to either ‘cabbage’ or ‘carrot’, your code should console log ‘vegetable’
    3. If food is set to either ‘biryani’ or ‘pulav’ your code should console log ‘rice’
  5. Create a variable named message and make it equal to 2-3 lines of lorem ipsum. Demonstrate all the string methods on this variable.
// // let message = `Hello World and
// // once again hello world`

// // console.log(message)

// // message = 'Aloha World and \nonce again hello world'

// // console.log(message)

// let name = 'John Doe'
// let city = 'Delhi'
// // message = `Aloha ${name + ' from ' + city}, and hello to you.`
// message = `Aloha ${name} from ${city}, and hello to you.`

// console.log(message)

// let age = 65

// if (age >= 18) {
//   console.log('You are allowed to come in, but you cannot drink.')
// } else if (age >= 21) {
//   console.log('You can come and you can drink as well.')
// } else if (age >= 65) {
//   console.log('You can come in, and drinks are free.')
// }

// let john = 'delhite'

// if (john === 'admin') {
//   console.log('You are allowed to access this page')
// } else if (john === 'moderator') {
//   console.log('You are allowed to see some other content')
// } else if (john === 'subscriber') {
//   console.log('You are allowed to pay me some money')
// } else {
//   console.log("You aren't welcome here!")
// }

// if (age >= 21) {
//   console.log('You can come and you can drink as well.')
// }

// if (age < 18) {
//   console.log('Buzz off')
// }

// let password = 'hell'

// if (password.length >= 6) {
//   // inner conditional
//   if (password.indexOf(' ') !== -1) {
//     console.log('Password is not valid')
//   } else {
//     console.log('Password is valid')
//   }
// } else {
//   console.log('Password is too short')
// }

// let loggedInUser = 'john'

// // form -loggedin -> username

// if (loggedInUser) {
//   console.log(`Welcome ${loggedInUser}`)
// } else {
//   console.log('SHOW CONTENT')
// }

// let age = 20
// // let city = 'delhi'

// if (age >= 18 && age < 21) {
//   console.log('You are allowed to enter, but you cannot drink.')
// } else if (age >= 21 && age <= 65) {
//   console.log('You are allowed to enter, and drink.')
// } else if (age > 65) {
//   console.log('Drinks are free')
// } else {
//   console.log('Buzz off')
// }

// // let loggedInUser = 'john'
// let loggedIn = null

// if (!loggedIn) {
//   console.log('PLEASE LOG IN')
// } else {
//   console.log(`Welcome ${loggedInUser}`)
// }

// let day = 40

// switch (day) {
//   case 1:
//     console.log('MONDAY')
//     break
//     case 2:
//       console.log('TUESDAY')
//       break
//       case 3:
//         console.log('WED')
//         break
//         case 4:
//           console.log('THU')
//           break
//           case 5:
//             console.log('FRI')
//     break
//     case 6:
//       console.log('SAT')
//       break
//       case 7:
//     console.log('SUN')
//     break
//     default:
//     console.log('INVALID DAY')
//   }

// let password = '2134'

// switch (password) {
//   case '1234':
//     console.log('TOO SIMPLE PASSWORD')
//     break
//   case 'password':
//     console.log('TOO COMMON')
//     break
//   default:
//     console.log('This is OK')
// }

// let num = 19

// num === 2 ? console.log('LUCKY') : console.log('UNLUCKY')

// let status = 'offline'

// let color = status === 'online' ? 'green' : 'red'

// console.log(color)

// let food
// let randomNum = Math.floor(Math.random() * 6) + 1

// console.log(food)

// switch (randomNum) {
//   case 1:
//     food = 'Apple'
//     break
//   case 2:
//     food = 'Grapes'
//     break
//   case 3:
//     food = 'Cabbage'
//     break
//   case 4:
//     food = 'Carrot'
//     break
//   case 5:
//     food = 'Biryani'
//     break
//   case 6:
//     food = 'Pulav'
//     break
//   default:
//     console.log('Incorrect food number')
// }

// console.log(food)

// let message = 'Some value'
// console.log(message.toUpperCase())

// // console.log(upperMessage)

// Arrays

// let names = ['rakesh', 'hafiza', 'akshay', 'aditya']

// if (names.length === 4) {
//   console.log('This is a good array')
// } else if (names.length < 4 || names.length > 4) {
//   console.log('Bad array')
// }

// HW
// let message = 'This is a crap message'

// console.log(`Original Message: ${message}`)

// if (message.split(' ').indexOf('crap') !== -1) {
//   let censoredIdx = message.split(' ').indexOf('crap')
//   let censoredMsg = message.split(' ').pop(censoredIdx)
//   console.log(`Censored Message: ${censoredMsg}`)
// }

// Objects

// const userDetails = ['John', 'Doe', 25, '[email protected]']

// const user = {
//   email: '[email protected]',
//   lastName: 'Doe',
//   age: 25,
//   firstName: 'John',
//   80: "User's age"
// }

// console.log(user.email)
// console.log(user.age)
// console.log(user['80'])
// console.log(user['firstName'])
// console.log(user.middleName)

// const pallette = {
//   red: '#eb4d4b',
//   yellow: '#f9ca24',
//   blue: '#30336b'
// }

// let userSelection = 'yellow'

// console.log(pallette[userSelection])

// console.log(pallette['yellow'])

// console.log(`The color selected is ${pallette['userSelection']}`)

// const user = {
//   email: '[email protected]',
//   lastName: 'Doe',
//   age: 25,
//   firstName: 'John'
// }

// const products = [
//   {
//     name: 'Samsung Galaxy 20',
//     skew: '21821sjh2187',
//     details: 'This does so and so......',
//     price: 90000,
//     images: [
//       'https://rstforum.net/image/1',
//       'https://rstforum.net/image/2',
//       'https://rstforum.net/image/3'
//     ],
//     brand: 'Samsung'
//   },
//   {
//     name: 'Apple iPhone',
//     skew: '2134324324',
//     details: 'This does so and so......',
//     price: 120000,
//     images: [
//       'https://rstforum.net/image/apple/1',
//       'https://rstforum.net/image/apple/2',
//       'https://rstforum.net/image/apple/3'
//     ],
//     brand: 'Apple'
//   }
// ]

// // console.log(products[1].details)
// console.log(products[1]['details'])

// const myPlaylist = {
//   song1: {
//     name: 'Song1',
//     duration: '3:09',
//     price: 1000000 // 100,00,00
//   },
//   song2: {
//     name: 'Song1',
//     duration: '3:09'
//   },
//   song3: {
//     name: 'Song1',
//     duration: '3:09'
//   }
// }

// console.log(myPlaylist.song1.name)

// Object literal
const fitnessData = {
  totalSteps: 400000,
  totalKms: 253.32,
  avgCalorieBurn: 2300,
  workoutsThisWeek: '5 of 7',
  avgSleep: '5:45',
  80: 'Work week',
  'Full Name': 'John Doe'
}

// console.log(fitnessData.avgSleep)
// console.log(fitnessData['totalKms'])
// console.log(fitnessData['80'])

// console.log(fitnessData['Full Name'])

// console.log(fitnessData)
// fitnessData.BPM = 98
// console.log(fitnessData)
// fitnessData.BPM++
// fitnessData.totalKms -= 100
// console.log(fitnessData)

const products = [
  {
    name: 'iPhone 12',
    price: 80000,
    brand: 'Apple'
  },
  {
    name: 'Galaxy 20',
    price: 100000,
    brand: 'Samsung'
  },
  {
    name: 'MI 11',
    price: 40000,
    brand: 'Xiaomi'
  }
]

// products[0].price = 100000
// console.log(products[0].price)

// const nums = [21, 21, 23, 452, 5, 2, 2121, 212, 2]

// for (let i = 1; i <= 100; i++) {
//   // console.log(i ** 2)
//   // console.log('The value of i is ' + i)
//   // console.log(`The value of i is ${i}`)
//   console.log('HELLO WORLD!')
// }

// for (let i = 50; i > 0; i /= 5) {
//   console.log(i)
// }

// let times = 10

// for (let i = 0; i < times; i++) {
//   console.log(i)
// }

// for (let num = 1; num <= 10; num++) {
//   console.log(`${num}*${num}=${num * num}`)
// }

// const nums = [12, 34, 56, 34, 78, 54, 23, 12]

// for (let count = 0; count <= nums.length; count++) {
//   console.log(nums[count])
// }

// const movies = [
//   { movieName: 'Inception', rating: 3.8 },
//   { movieName: 'Avengers', rating: 3.4 },
//   { movieName: 'Iron Man', rating: 2.9 }
// ]

// for (let i = 0; i < movies.length; i++) {
//   let movie = movies[i]
//   console.log(`${movie['movieName']} has a rating of ${movie.rating}`)
// }

// for (let char = 0; char < word.length; char++) {
//   console.log(word[char].toUpperCase())
// }

// const word = 'Hello World'

// let reversedWord = ''

// for (let char = word.length - 1; char >= 0; char--) {
//   // console.log(word[char])
//   reversedWord = reversedWord + word[char]
// }

// console.log(reversedWord)

// for (let i = 1; i <= 10; i++) {
//   for (let j = 0; j < 5; j++) {
//     for (let k = 0; k < 3; k++) {
//       console.log(i, j, k)
//     }
//   }
// }

// const arr = [
//   [4, 64],
//   [128, 32, 4, 16],
//   [16, 4, 4, 32],
//   [2, 16, 16, 2]
// ]

// // for (let i = 0; i < gameBoard.length; i++) {
// //   for (let j = 0; j < gameBoard[i].length; j++) {
// //     console.log(gameBoard[i][j])
// //   }
// // }

// const arr = [
//   ['A', 'B', 'C', 'D'],
//   ['E', 'F', 'G'],
//   ['hello', 'world', 'cool'],
//   [23, 45, 64, 212, 34, 565, 32]
// ]

// for (let i = 0; i < arr.length; i++) {
//   for (let j = 0; j < arr[i].length; j++) {
//     console.log(arr[i][j])
//   }
// }

// // const wordsArr = // split words
// const selectedWordsA = []

// const allSelectedWords = [
//   ['amet', 'accusamus'],
//   ['lorem', '']
// ]

// let words =
//   'Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus illum ipsam minus rem tempora aliquam reprehenderit ea adipisci nisi quisquam.'

// const wordsArr = words.toLowerCase().split(' ')
// const selectedWordsA = []
// const selectedWordsL = []

// console.log(wordsArr)

// for (let i = 0; i < wordsArr.length; i++) {
//   if (wordsArr[i][0] === 'a') {
//     selectedWordsA.push(wordsArr[i])
//   } else if (wordsArr[i][0] === 'l') {
//     selectedWordsL.push(wordsArr[i])
//   }
// }

// console.log(selectedWordsA)
// console.log(selectedWordsL)

// // For loop
// for (let i = 0; i < 100; i++) {
//   console.log(`${i}: Hello World`)
// }

// for (let num = 0; num <= 20; num++) {
//   console.log(num)
// }

// let str = 'b' + 'a' + +'a' + 'a'
// console.log(str)

// let i = 0

// while (i <= 20) {
//   console.log(i)
//   i++
// }

// const target = Math.floor(Math.random() * 9999) + 1
// // // console.log(target)
// let guess = Math.floor(Math.random() * 9999) + 1
// // // console.log(guess)

// while (guess !== target) {
//   console.log(`Target: ${target} | Guess: ${guess}`)
//   guess = Math.floor(Math.random() * 9999) + 1
// }

// // console.log(`The final result: Target: ${target} | Guess: ${guess}`)

// const target = Math.floor(Math.random() * 100) + 1
// let guess = Math.floor(Math.random() * 100) + 1

// while (guess !== target) {
//   if (guess === 13) {
//     console.log('13 is an unlucky number.\nBreaking out of this loop.')
//     break
//   }

//   console.log(`Target: ${target} | Guess: ${guess}`)
//   guess = Math.floor(Math.random() * 100) + 1
// }

// console.log(`The final result: Target: ${target} | Guess: ${guess}`)

// const target = Math.floor(Math.random() * 100) + 1
// let guess = Math.floor(Math.random() * 100) + 1

// // true -> loop forever
// while (true) {
//   console.log(`Target: ${target} | Guess: ${guess}`)

//   if (target === guess) {
//     break // break out of the loop when condition is true
//   }

//   guess = Math.floor(Math.random() * 100) + 1
// }

// console.log(`\nGame over!\nTarget: ${target} | Guess: ${guess}.`)

const names = ['John', 'Jack', 'Jim', 'Jane', 'James', 'Janardhan']
// // for loop
// for (let i = 0; i < names.length; i++) {
//   console.log(names[i])
// }

// for...of loop
// for (let name of names) {
//   console.log(`Hello ${name}!`)
//   console.log(name)
// }

// console.log(name)

// let name = 'John Doe'
// let reversedName = []

// for (let char of name) {
//   // console.log(char.toUpperCase( ))
//   reversedName.unshift(char)
// }

// console.log(reversedName.join(''))

// const matrix = [
//   [1, 4, 7],
//   [9, 7, 2],
//   [9, 4, 16]
// ]

// let total = 0

// for (let row of matrix) {
//   for (let num of row) {
//     total += num
//   }
// }

// console.log(total)

// const cats = ['fashion', 'mobiles', 'books']
// const prods = ['tshirt', 'samsung', '1984', 'bro']

// for (let i = 0; i < prods.length; i++) {
//   console.log(cats[i], prods[i])
// }

// const productPrices = {
//   apple: 80000,
//   onePlus: 50000,
//   samsung: 90000
// }

// for (let brand of Object.keys(productPrices)) {
//   console.log(`${brand} is going to cost you ${productPrices[brand]}`)
// }

// for (let price of Object.values(productPrices)) {
//   console.log(price)
// }

// const movieRating = {
//   pursuitOfHappiness: 4.8,
//   satya: 4.8,
//   gangsOfWasepur: 4,
//   robot: -3
// }

// for (let movie in movieRating) {
//   // console.log(movie)
//   console.log(`${movie} has a rating of ${movieRating[movie]}`)
// }

const data = [
  {
    id: 1,
    first_name: 'Mozelle',
    last_name: 'Beesley',
    email: '[email protected]',
    gender: 'Female',
    ip_address: '204.93.93.196',
    bank_balance: '$641.25'
  },
  {
    id: 2,
    first_name: 'Marjie',
    last_name: 'Kinge',
    email: '[email protected]',
    gender: 'Female',
    ip_address: '19.175.126.65',
    bank_balance: '$937.11'
  },
  {
    id: 3,
    first_name: 'Gage',
    last_name: 'Renish',
    email: '[email protected]',
    gender: 'Male',
    ip_address: '219.51.80.158',
    bank_balance: '$819.20'
  },
  {
    id: 4,
    first_name: 'Alika',
    last_name: 'Skeffington',
    email: '[email protected]',
    gender: 'Female',
    ip_address: '158.35.238.167',
    bank_balance: '$172.63'
  },
  {
    id: 5,
    first_name: 'Noel',
    last_name: 'Crudginton',
    email: '[email protected]',
    gender: 'Male',
    ip_address: '250.158.31.16',
    bank_balance: '$565.80'
  },
]

const upperClass = [] // >800
const middleClass = [] // 500 || 800
const lowerClass = [] // <500

for (let customer of data) {
  let balance = +customer.bank_balance.split('$')[1]

  if (balance >= 800) {
    upperClass.push(customer)
  } else if (balance > 400 && balance < 800) {
    middleClass.push(customer)
  } else {
    lowerClass.push(customer)
  }
}

console.log(upperClass)

// if price is less than 5000 -> push it to CHEAP array
// if price is between than 5000 and 12000 -> push it to AFFORDABLE array
// if price is over 12000 -> push it to EXPENSIVE array
// Create 4 arrays -> SUZUKI, TESLA, FORD, TOYOTA, OTHER
// function validatePassword(password, username) {
//   //   if (
//   //     password.indexOf(' ') === -1 &&
//   //     password.indexOf(username) === -1 &&
//   //     password.length > 6
//   //   ) {
//   //     return true
//   //   } else {
//   //     return false
//   //   }

//   return (
//     password.indexOf(' ') === -1 &&
//     password.indexOf(username) === -1 &&
//     password.length > 6
//   )
// }

// // console.log(validatePassword('jdoe2021', 'doe.20'))

// let rstUser = 'johndoe9883292'
// let rstPassword = 'johndoe'

// if (validatePassword(rstPassword, rstUser)) {
//   console.log('Welcome to RST Forum')
// } else {
//   console.log('Please check your username or password')
// }

// function isValidPassword(password, username) {
//   const tooShort = password.length < 8
//   const hasSpace = password.indexOf(' ') !== -1
//   const tooSimilar = password.indexOf(username) !== -1

//   return tooShort || hasSpace || tooSimilar
// }

// function isPangram(sentence) {
//   sentence = sentence.toLowerCase()

//   for (let char of 'abcdefghifklmnopqrstuvwxyz') {
//     if (sentence.indexOf(char) === -1) {
//       return false
//     }
//   }

//   return true
// }

// console.log(isPangram('Two driven'))

let msg = 'Hello Mars'

function greet() {
  var msg = 'Hello World'
  return msg + '!'
}

function great() {
  var msg = 'Hello World!!!!!!!!!!!!!!!'
  return msg + '!'
}

console.log(msg)
console.log(greet())
// // Function validator (approach 1)
// function isValidPassword(password, username) {
//   if (
//     password.length < 8 ||
//     password.indexOf(' ') !== -1 ||
//     password.indexOf(username) !== -1
//   ) {
//     return false
//   } else {
//     return true
//   }
// }

// // Function validator (approach 2)
// function isValidPassword(password, username) {
//   const tooShort = password.length < 8
//   const hasSpace = password.indexOf(' ') !== -1
//   const tooSimilar = password.indexOf(username) !== -1

//   if (tooShort || hasSpace || tooSimilar) return false
//   return true
// }

// // Function validator (approach 3)
// function isValidPassword(password, username) {
//   const tooShort = password.length < 8
//   const hasSpace = password.indexOf(' ') !== -1
//   const tooSimilar = password.indexOf(username) !== -1

//   return tooShort || hasSpace || tooSimilar
// }

// // console.log(isValidPassword('helloworld', 'hello'))
// // console.log(isValidPassword('kasmd21sdda89', 'hello'))
// // console.log(isValidPassword('asd as', 'johndoe'))

// // Function Average
// function avg(arr) {
//   let total = 0
//   for (let num of arr) {
//     total += num
//   }
//   return total / arr.length
// }

// // console.log(avg([12, 34, 5, 2, 13, 45]))

// // Function Pangram (a sentence that contains every letter of the alphabet)
// function isPangram(sentence) {
//   sentence = sentence.toLowerCase()
//   for (let char of 'abcdefghifklmnopqrstuvwxyz') {
//     if (sentence.indexOf(char) === -1) {
//       return false
//     }
//   }
//   return true
// }

// // console.log(isPangram('The quick brown fox jumps over the lazy dog.'))
// // console.log(isPangram('Kay re tichaya'))


// Exercises
// 1. Write a simple function which accepts an array returns a new arr
// where all the values are doubled.

// 2. Write a simple function called coinFlip and when called should
// either return HEADS or TAILS

// 3. Write a function called generateEvens which should take an argument
// of the number upto which it wants even numbers. So if I pass 100 to the
// function, I want to get back an array with even numbers starting from 0
// all the way to 100, i.e. [2,4,6,8,10,12,...,100]

// 4. Write a function called returnDay which should accept a number between
// 1 and 7 and return the corresponding day (monday, tuesday, wednesday... etc)

// 5. Write a function called capitalized which should accept a string and
// return the same string with it's first letter capitalized.
// ex. capitalized("john") // "John"

// 6. Write a function isPalindrome which accepts a string and checks if the
// string is a palindrome, if it is, then it should return true or else false.
// A palindrome is a word or sentence which reads the same backward or forward.
// isPalindrome("racecar") // true
// radar, level, rotor, kayak, madam, refer, mom, wow,
function isPalindrome(word) {
  return word === word.split('').reverse().join('')
}

// function capitalize(words) {
//   let wordArr = words.split(' ')
//   let capitalArr = []
//   for (word of wordArr) {
//     capitalArr.push(word[0].toUpperCase() + word.substring(1))
//   }
//   return capitalArr.join(' ')
// }

// console.log(capitalize('hello world hello universe'))

// let name = 'Jack'

// var first_name = 'Jack'
// console.log(first_name)

// if (true) {
//   var first_name = 'John'
//   var last_name = 'Doe'
//   console.log(first_name + ' ' + last_name)
// }

// console.log(first_name, last_name)

// for (var i = 0; i < 10; i++) {
//   console.log(i)
// }

// console.log(i)

// // console.log(first_name, last_name) // ERROR

// function outer() {
//   let movie = 'Avengers'

//   function inner() {
//     let otherMovie = 'John Wick'
//     let ratings = `${movie} has a rating of 4.2`
//     console.log(ratings)
//   }
//   inner()
// }

// // outer()

// function square(num) {
//   return num * num
// }

// const square = function (num) {
//   return num * num
// }

// // Named function
// function add(x, y) {
//   return x + y
// }

// // Function expression
// const sum = function (x, y) {
//   return x + y
// }

// // Function expressions also point to named functions
// const product = function multiply(x, y) {
//   return x * y
// }

// console.log(sum(10, 50))
// console.log(product(10, 4))
// console.log(multiply(10, 10)) // ERROR!

// function add(x, y) {
//   return x + y
// }

// const sub = function (x, y) {
//   return x - y
// }

// const mul = function (x, y) {
//   return x * y
// }

// function div(x, y) {
//   return x / y
// }

// const operations = [add, sub, mul, div]

// for (let op of operations) {
//   let result = op(10, 2)
//   console.log(result)
// }

// console.log(operations[1](10, 30))

// const customMath = {
//   sub: function (x, y) {
//     return x - y
//   },
//   div: div
// }

// console.log(customMath.sub(10, 5))

// Higher Order Functions (HOF)
// function multipleGreets(f) {
//   f()
//   f()
//   f()
// }

// function sayHello() {
//   console.log('Hello World')
// }
// function printNums() {
//   for (let i = 0; i < 10; i++) {
//     console.log(i)
//   }
// }

// multipleGreets(sayHello)
// multipleGreets(printNums)

// multipleGreets(function () {
//   console.log("I'm a FUNCTION!!!!")
// })

// // multipleGreets('Hello Bro')

// function sayHello() {
//   console.log('Hello World')
// }
// function sayGoodbye() {
//   console.log('Goodbye World')
// }

// function repeat(func, num) {
//   for (let i = 0; i < num; i++) {
//     func()
//   }
// }

// // repeat(sayHello, 10)
// repeat(sayGoodbye, 2)

// // HOF
// function randomPick(f1, f2) {
//   let randNum = Math.random()
//   if (randNum < 0.5) {
//     f1()
//   } else {
//     f2()
//   }
// }

// randomPick(
//   function () {
//     console.log('Hello World!')
//   },
//   function () {
//     console.log('Goodbye World!')
//   }
// )

// const square = function (x) {
//   return x ** 2
// }

// console.log(square(10))

function raiseBy(num) {
  return function (x) {
    return x ** num
  }
}

let square = raiseBy(2)
let cube = raiseBy(3)

console.log(square(20))
console.log(cube(20))
  1. Write a function named sqArr that accepts an array. The function return an array with all the numbers in the argument array squared. Use a function expression instead of a regular function.
    Ex. sqArr([2,5,6,4]) -> [4,25,36,16]
  2. Create a HOF factory function named multiplyBy that accepts a number and return a new function. Very similar to the raise
    Ex. const fiveTimes = multiplyBy(5)
    fiveTimes(10) -> 50
  3. Write a function named maths, which accepts three arguments. The first two will be two numbers and the third argument will be a function. This will will use the two arguments and do whatever arithmetic operations on them.
    Ex. maths(5, 5, add) -> 10
    maths(10, 3, sub) -> 7
    Make four maths function calls. Pass function references in the first two and the next two pass anonymous functions.
// function raiseBy(num) {
//   return function (x) {
//     return x ** num
//   }
// }

// const square = raiseBy(2)
// const cube = raiseBy(3)

// // console.log(square(10))

// const myMaths = {
//   add: function (x, y) {
//     return x + y
//   },
//   sub: function (x, y) {
//     return x - y
//   },
//   mul: function (x, y) {
//     return x * y
//   }
// }

// console.log(myMaths.add(10, 40))

// function isBetween(x, y) {
//   return function (num) {
//     return num >= x && num <= y
//   }
// }

// const isChild = isBetween(0, 15)
// const isTeen = isBetween(16, 18)
// const isAdult = isBetween(18, 65)
// const isSenior = isBetween(65, 100)

// let userAge = 99

// if (isChild(userAge)) {
//   console.log('You are not welcome here')
// } else if (isTeen(userAge)) {
//   console.log('You are welcome but you cannot drink')
// } else if (isAdult(userAge)) {
//   console.log('You are welcome')
// } else if (isSenior(userAge)) {
//   console.log('Drinks are free')
// } else {
//   console.log("I don't believe it")
// }

// maths

// function maths(x, y, fn) {
//   return fn(x, y)
// }

// function add(x, y) {
//   return x + y
// }

// let result = maths(10, 10, function (x, y) {
//   return x - y
// })

// let addResult = maths(10, 10, add)

// console.log(result)
// console.log(addResult)

// setTimeout(function () {
//   console.log('Hello World')
// }, 1000)

// var name
// var last_name

// console.log(name)
// name = 'john doe'

// console.log(person)
// let person = 'John Doe'

// const nums = [9, 2, 4, 6, 2, 3, 7, 6]

// // nums.forEach(function (x) {
// //   console.log(x)
// // })

// nums.forEach(function (x) {
//   if (x % 2 === 1) {
//     console.log(x)
//   }
// })

// // console.log(nums)

// const movies = [
//   {
//     title: 'Avengers',
//     rating: 4.1
//   },
//   {
//     title: 'Dr. Strange',
//     rating: 3.9
//   },
//   {
//     title: 'Tenet',
//     rating: 4.3
//   },
//   {
//     title: 'Joker',
//     rating: 4.7
//   }
// ]

// movies.forEach(function (item) {
//   console.log(item['title'].toUpperCase())
// })

// movies.forEach(function (movie, i) {
//   console.log(i + 1, movie.title.toUpperCase())
// })

// let nums = [1, 2, 3, 4, 5, 6]

// // const newNums = []

// nums.forEach(function (num, idx) {
//   console.log(`${idx + 1}: ${num * 10}`)
// })

// const names = ['john', 'jack', 'jane', 'james']

// // let capitalNames = names.map(function (name) {
// //   return name.toUpperCase()
// // })

// console.log(
//   names.map(function (name) {
//     return name.toUpperCase()
//   })
// )

// console.log(capitalNames)

const nums = [2, 3, 4, 7, 6, 8, 13, 10, 19, 12, 14, 22, 21, 16]

// const doubles = nums.map(function (num) {
//   return num * 2 // we have to return a value
// })
// console.log(doubles)

const numDetails = nums.map(function (num) {
  return {
    number: num,
    isEven: num % 2 === 0
  }
})

console.log(numDetails)
// function doubleArr(arr) {
//   return arr.map(function (item) {
//     return item * 2
//   })
// }

// console.log(doubleArr([1, 2, 3, 4]))

// // HOF
// function maths(num1, num2, func) {
//   return func(num1, num2)
// }

// function divide(a, b) {
//   return a / b
// }

// console.log(
//   maths(10, 10, function (a, b) {
//     return a + b
//   })
// )

// console.log(maths(10, 10, divide))

// // Function Expression
// const square = function (x) {
//   return x * x
// }

// // Arrow functions
// const cube = x => {
//   return x * x * x
// }

// const add = (x, y) => {
//   return x + y
// }

// const randomNum = () => {
//   return Math.random()
// }

// const square = (x) => {
//   return x * x
// }

// // Implicit return
// const square2 = x => (
//   x * x
// )

// const square3 = x => x * x

// console.log(square(20))
// console.log(cube(20))
// console.log(add(20, 10))
// console.log(randomNum())

// console.log(square2(100))

// function maths(num1, num2, fn) {
//   return fn(num1, num2)
// }

// const maths = (num1, num2, fn) => (
//   fn(num1, num2)
// )

// HOF
// const maths = (num1, num2, fn) => fn(num1, num2)

// console.log(maths(10, 50, (x, y) => x / y))

// const nums = [1, 2, 3, 4, 5, 6]

// nums.map(num => num * 2)

// let movies = ['The Terminator', 'The Avengers', 'Jurassic Park', 'Titanic']

// // console.log(
// //   movies.find(movie => {
// //     return movie.includes('The')
// //   })
// // )

// console.log(movies.find(movie => movie.indexOf('tan') !== -1))

// const books = [
//   {
//     title: 'The Shining',
//     author: 'Stephen King',
//     rating: 4.1
//   },
//   {
//     title: 'Sacred Games',
//     author: 'Vikram Chandra',
//     rating: 4.5
//   },
//   {
//     title: '1984',
//     author: 'George Orwell',
//     rating: 4.9
//   },
//   {
//     title: 'The Alchemist',
//     author: 'Paulo Coelho',
//     rating: 3.5
//   },
//   {
//     title: 'The Great Gatsby',
//     author: 'F. Scott Fitzgerald',
//     rating: 3.8
//   }
// ]

// books.sort((a, b) => a.rating - b.rating)

// console.log(books)

// console.log(books.find(book => book.rating > 4))

// const nums = [9, 8, 3, 4, 6, 12, 17, 23, 0]

// console.log(nums.filter(num => num % 2 === 0))
// console.log(nums.filter(n => n > 10))

// console.log(books.filter(book => book.rating > 4))

// const names = ['jack', 'james', 'john', 'jane', 'josh', 'brad']

// console.log(names.every(name => name[0] === 'j'))
// console.log(names.some(name => name[0] === 'b'))

// console.log(names.every(name => name.length >= 4))

// console.log(names.every(name => name[name.length - 1] !== 'n'))

// const prices = [500.4, 211, 23, 5, 4, 22.2, -23.2, 9233]

// // const result = [211,  23, 500.4, 5, 4, 22.2, -23.2, 9233]

// // prices.sort((n1, n2) => n1 - n2)
// prices.sort((n1, n2) => n2 - n1)

// console.log(prices)

let nums = [21, 221, 2000000, 1, 34, 123, 4342, 56, 4]

const maxVal = nums.reduce((max, currentVal) => {
  if (currentVal > max) {
    return currentVal
  }
  return max
}, 40000)

console.log(maxVal) // 2000000

// const nums = [3, 2, 4, 6, 9]

let total = nums.reduce((accumulator, currentValue) => {
  return accumulator + currentValue
}, 1000)

console.log(total)
// const multiply = (x = 1, y = 1) => {
//   // if (typeof y === 'undefined') {
//   //   y = 1
//   // }

//   // y = typeof y === 'undefined' ? 1 : y

//   return x * y
// }

// console.log(multiply())

// const greet = (name, msg = 'Hi') => {
//   return `${msg}, ${name}`
// }

// console.log(greet('john'))

// const numberArr = [5, 10, 11, 2, 1]

// Math.max(nums)

// function add(a, b, c, d, e) {
//   return a + b + c + d + e
// }

// function add2(...nums) {
//   let total = 0
//   // nums.forEach(num => (total += num))

//   for (num of nums) {
//     total += num
//   }

//   return total
// }

// console.log(add(...numberArr))
// console.log(add(...numberArr))

// function printNames(name1, name2, ...others) {
//   console.log(name1)
//   console.log(name2)
//   console.log(others)
// }

// function add(x, y, ...others) {

// }

// printNames('john', 'jane', 'jack', 'josh', 'brad')

const fullName = ({ firstName, lastName }) => {
  return `${firstName} ${lastName}`
}
const data = [
  {
    id: 1,
    title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops',
    price: 109.95,
    description:
      'Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday',
    category: 'men clothing',
    image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg'
  },
  {
    id: 2,
    title: 'Mens Casual Premium Slim Fit T-Shirts ',
    price: 22.3,
    description:
      'Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket.',
    category: 'men clothing',
    image:
      'https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg'
  },
  {
    id: 3,
    title: 'Mens Cotton Jacket',
    price: 55.99,
    description:
      'great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling, traveling or other outdoors. Good gift choice for you or your family member. A warm hearted love to Father, husband or son in this thanksgiving or Christmas Day.',
    category: 'men clothing',
    image: 'https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg'
  },
  {
    id: 4,
    title: 'Mens Casual Slim Fit',
    price: 15.99,
    description:
      'The color could be slightly different between on the screen and in practice. / Please note that body builds vary by person, therefore, detailed size information should be reviewed below on the product description.',
    category: 'men clothing',
    image: 'https://fakestoreapi.com/img/71YXzeOuslL._AC_UY879_.jpg'
  },
  {
    id: 5,
    title:
      "John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet",
    price: 695,
    description:
      "From our Legends Collection, the Naga was inspired by the mythical water dragon that protects the ocean's pearl. Wear facing inward to be bestowed with love and abundance, or outward for protection.",
    category: 'jewelery',
    image: 'https://fakestoreapi.com/img/71pWzhdJNwL._AC_UL640_QL65_ML3_.jpg'
  },
  {
    id: 6,
    title: 'Solid Gold Petite Micropave ',
    price: 168,
    description:
      'Satisfaction Guaranteed. Return or exchange any order within 30 days.Designed and sold by Hafeez Center in the United States. Satisfaction Guaranteed. Return or exchange any order within 30 days.',
    category: 'jewelery',
    image: 'https://fakestoreapi.com/img/61sbMiUnoGL._AC_UL640_QL65_ML3_.jpg'
  },
  {
    id: 7,
    title: 'White Gold Plated Princess',
    price: 9.99,
    description:
      "Classic Created Wedding Engagement Solitaire Diamond Promise Ring for Her. Gifts to spoil your love more for Engagement, Wedding, Anniversary, Valentine's Day...",
    category: 'jewelery',
    image: 'https://fakestoreapi.com/img/71YAIFU48IL._AC_UL640_QL65_ML3_.jpg'
  },
  {
    id: 8,
    title: 'Pierced Owl Rose Gold Plated Stainless Steel Double',
    price: 10.99,
    description:
      'Rose Gold Plated Double Flared Tunnel Plug Earrings. Made of 316L Stainless Steel',
    category: 'jewelery',
    image: 'https://fakestoreapi.com/img/51UDEzMJVpL._AC_UL640_QL65_ML3_.jpg'
  },
  {
    id: 9,
    title: 'WD 2TB Elements Portable External Hard Drive - USB 3.0 ',
    price: 64,
    description:
      'USB 3.0 and USB 2.0 Compatibility Fast data transfers Improve PC Performance High Capacity; Compatibility Formatted NTFS for Windows 10, Windows 8.1, Windows 7; Reformatting may be required for other operating systems; Compatibility may vary depending on user’s hardware configuration and operating system',
    category: 'electronics',
    image: 'https://fakestoreapi.com/img/61IBBVJvSDL._AC_SY879_.jpg'
  },
  {
    id: 10,
    title: 'SanDisk SSD PLUS 1TB Internal SSD - SATA III 6 Gb/s',
    price: 109,
    description:
      'Easy upgrade for faster boot up, shutdown, application load and response (As compared to 5400 RPM SATA 2.5” hard drive; Based on published specifications and internal benchmarking tests using PCMark vantage scores) Boosts burst write performance, making it ideal for typical PC workloads The perfect balance of performance and reliability Read/write speeds of up to 535MB/s/450MB/s (Based on internal testing; Performance may vary depending upon drive capacity, host device, OS and application.)',
    category: 'electronics',
    image: 'https://fakestoreapi.com/img/61U7T1koQqL._AC_SX679_.jpg'
  },
  {
    id: 11,
    title:
      'Silicon Power 256GB SSD 3D NAND A55 SLC Cache Performance Boost SATA III 2.5',
    price: 109,
    description:
      '3D NAND flash are applied to deliver high transfer speeds Remarkable transfer speeds that enable faster bootup and improved overall system performance. The advanced SLC Cache Technology allows performance boost and longer lifespan 7mm slim design suitable for Ultrabooks and Ultra-slim notebooks. Supports TRIM command, Garbage Collection technology, RAID, and ECC (Error Checking & Correction) to provide the optimized performance and enhanced reliability.',
    category: 'electronics',
    image: 'https://fakestoreapi.com/img/71kWymZ+c+L._AC_SX679_.jpg'
  },
  {
    id: 12,
    title:
      'WD 4TB Gaming Drive Works with Playstation 4 Portable External Hard Drive',
    price: 114,
    description:
      "Expand your PS4 gaming experience, Play anywhere Fast and easy, setup Sleek design with high capacity, 3-year manufacturer's limited warranty",
    category: 'electronics',
    image: 'https://fakestoreapi.com/img/61mtL65D4cL._AC_SX679_.jpg'
  },
  {
    id: 13,
    title: 'Acer SB220Q bi 21.5 inches Full HD (1920 x 1080) IPS Ultra-Thin',
    price: 599,
    description:
      '21. 5 inches Full HD (1920 x 1080) widescreen IPS display And Radeon free Sync technology. No compatibility for VESA Mount Refresh Rate: 75Hz - Using HDMI port Zero-frame design | ultra-thin | 4ms response time | IPS panel Aspect ratio - 16: 9. Color Supported - 16. 7 million colors. Brightness - 250 nit Tilt angle -5 degree to 15 degree. Horizontal viewing angle-178 degree. Vertical viewing angle-178 degree 75 hertz',
    category: 'electronics',
    image: 'https://fakestoreapi.com/img/81QpkIctqPL._AC_SX679_.jpg'
  },
  {
    id: 14,
    title:
      'Samsung 49-Inch CHG90 144Hz Curved Gaming Monitor (LC49HG90DMNXZA) – Super Ultrawide Screen QLED ',
    price: 999.99,
    description:
      '49 INCH SUPER ULTRAWIDE 32:9 CURVED GAMING MONITOR with dual 27 inch screen side by side QUANTUM DOT (QLED) TECHNOLOGY, HDR support and factory calibration provides stunningly realistic and accurate color and contrast 144HZ HIGH REFRESH RATE and 1ms ultra fast response time work to eliminate motion blur, ghosting, and reduce input lag',
    category: 'electronics',
    image: 'https://fakestoreapi.com/img/81Zt42ioCgL._AC_SX679_.jpg'
  },
  {
    id: 15,
    title: "BIYLACLESEN Women's 3-in-1 Snowboard Jacket Winter Coats",
    price: 56.99,
    description:
      'Note:The Jackets is US standard size, Please choose size as your usual wear Material: 100% Polyester; Detachable Liner Fabric: Warm Fleece. Detachable Functional Liner: Skin Friendly, Lightweigt and Warm.Stand Collar Liner jacket, keep you warm in cold weather. Zippered Pockets: 2 Zippered Hand Pockets, 2 Zippered Pockets on Chest (enough to keep cards or keys)and 1 Hidden Pocket Inside.Zippered Hand Pockets and Hidden Pocket keep your things secure. Humanized Design: Adjustable and Detachable Hood and Adjustable cuff to prevent the wind and water,for a comfortable fit. 3 in 1 Detachable Design provide more convenience, you can separate the coat and inner as needed, or wear it together. It is suitable for different season and help you adapt to different climates',
    category: 'women clothing',
    image: 'https://fakestoreapi.com/img/51Y5NI-I5jL._AC_UX679_.jpg'
  },
  {
    id: 16,
    title:
      "Lock and Love Women's Removable Hooded Faux Leather Moto Biker Jacket",
    price: 29.95,
    description:
      '100% POLYURETHANE(shell) 100% POLYESTER(lining) 75% POLYESTER 25% COTTON (SWEATER), Faux leather material for style and comfort / 2 pockets of front, 2-For-One Hooded denim style faux leather jacket, Button detail on waist / Detail stitching at sides, HAND WASH ONLY / DO NOT BLEACH / LINE DRY / DO NOT IRON',
    category: 'women clothing',
    image: 'https://fakestoreapi.com/img/81XH0e8fefL._AC_UY879_.jpg'
  },
  {
    id: 17,
    title: 'Rain Jacket Women Windbreaker Striped Climbing Raincoats',
    price: 39.99,
    description:
      "Lightweight perfet for trip or casual wear---Long sleeve with hooded, adjustable drawstring waist design. Button and zipper front closure raincoat, fully stripes Lined and The Raincoat has 2 side pockets are a good size to hold all kinds of things, it covers the hips, and the hood is generous but doesn't overdo it.Attached Cotton Lined Hood with Adjustable Drawstrings give it a real styled look.",
    category: 'women clothing',
    image: 'https://fakestoreapi.com/img/71HblAHs5xL._AC_UY879_-2.jpg'
  },
  {
    id: 18,
    title: "MBJ Women's Solid Short Sleeve Boat Neck V ",
    price: 9.85,
    description:
      '95% RAYON 5% SPANDEX, Made in USA or Imported, Do Not Bleach, Lightweight fabric with great stretch for comfort, Ribbed on sleeves and neckline / Double stitching on bottom hem',
    category: 'women clothing',
    image: 'https://fakestoreapi.com/img/71z3kpMAYsL._AC_UY879_.jpg'
  },
  {
    id: 19,
    title: "Opna Women's Short Sleeve Moisture",
    price: 7.95,
    description:
      '100% Polyester, Machine wash, 100% cationic polyester interlock, Machine Wash & Pre Shrunk for a Great Fit, Lightweight, roomy and highly breathable with moisture wicking fabric which helps to keep moisture away, Soft Lightweight Fabric with comfortable V-neck collar and a slimmer fit, delivers a sleek, more feminine silhouette and Added Comfort',
    category: 'women clothing',
    image: 'https://fakestoreapi.com/img/51eg55uWmdL._AC_UX679_.jpg'
  },
  {
    id: 20,
    title: 'DANVOUY Womens T Shirt Casual Cotton Short',
    price: 12.99,
    description:
      '95%Cotton,5%Spandex, Features: Casual, Short Sleeve, Letter Print,V-Neck,Fashion Tees, The fabric is soft and has some stretch., Occasion: Casual/Office/Beach/School/Home/Street. Season: Spring,Summer,Autumn,Winter.',
    category: 'women clothing',
    image: 'https://fakestoreapi.com/img/61pHAEJ4NML._AC_UX679_.jpg'
  }
]

// Write a function (arrow function) that accepts a product array, priceKey (optional)
// It will map over all the items in the array and find the price key
// return a new array of objects with the price converted to rupees

// const currencyConv = arrObj => {
//   return arrObj.map(item => {
//     return { ...item, price: item.price * 75 }
//   })
// }

// const currencyConv = arrObj =>
//   arrObj.map(item => ({ ...item, price: item.price * 75 }))

// console.log(currencyConv(data))

// const currencyConv = arr => {
//   const updatedArr = []
//   arr.forEach(obj => updatedArr.push({ ...obj, price: obj.price * 75 }))
//   return updatedArr
// }

// console.log(currencyConv(data))

const currencyConv = (arrObj, tarKey = 'price') => {
  return arrObj.map(item => {
    return { ...item, [tarKey]: item[tarKey] * 75 }
  })
}

console.log(currencyConv(data))

// Write a function that accepts a dataset, key, value
// return all the objects that have this value

customFilter(data, 'price', 12.99)
// const maths = {
//   add: function (x, y) {
//     return x + y
//   },
//   sub: function (x, y) {
//     return x - y
//   },
//   info: 'Maths function'
// }

// console.log(maths.add(10, 20))
// console.log(maths.info)

// const movieReviews = [4.5, 5.0, 3.2, 2.1, 4.7, 3.8, 3.1, 3.9, 4.4]

// const highest = Math.max(...movieReviews)
// const lowest = Math.min(...movieReviews)

// // let total = 0
// // movieReviews.forEach(rating => (total += rating))

// const total = movieReviews.reduce((acc, nextVal) => acc + nextVal)
// const average = total / movieReviews.length

// const movieReviewInfo = {
//   highest: highest,
//   lowest: lowest,
//   total: total,
//   average: average
// }

// const movieReviewInfo = {
//   highest,
//   lowest,
//   total,
//   average
// }

// const movieReviewInfo = { highest, lowest, total, average }

// console.log(movieReviewInfo)

// console.log(movieReviews)
// console.log(highest)
// console.log(lowest)
// console.log(total)
// console.log(average)

// const getReviewDetails = arr => {
//   const highest = Math.max(...arr)
//   const lowest = Math.min(...arr)
//   const total = arr.reduce((accumulator, nextVal) => accumulator + nextVal)
//   const average = total / arr.length

//   return {
//     highest,
//     lowest,
//     acc: total,
//     average
//   }
// }

// console.log(getReviewDetails([4.5, 5.0, 3.2]))

// const username = 'janedoe'
// const role = 'admin'

// const user1 = {
//   [role]: username,
//   [username.toUpperCase()]: username
// }

// console.log(user1)

// const addProperty = (obj, k, v) => {
//   return { ...obj, [k]: v }
// }

// const addProperty = (obj, k, v) => ({ ...obj, [k]: v })

// const user = {
//   firstName: 'John',
//   lastName: 'Doe'
// }

// console.log(addProperty(user, 'age', 25))
// console.log(addProperty(user, 'lastName', 'cool'))

// const math = {
//   multiply: function (x, y) {
//     return x * y
//   },
//   divide: function (x, y) {
//     return x / y
//   },
//   subtract: function (x) {
//     return x - x
//   },
//   info: 'The math object'
// }

// function square(x) {
//   return x * x
// }

// const power = {
//   square,
//   cube: function (x) {
//     return x * x * x
//   },
//   hypercube: function (x) {
//     return x * x * x * x
//   }
// }

// console.log(square(20))
// console.log(power.cube(20))
// console.log(power.hypercube(20))

// const math2 = {
//   multiply(x, y) {
//     return x * y
//   },
//   divide(x, y) {
//     return x / y
//   },
//   subtract(x, y) {
//     return x - y
//   }
// }

// console.log(math2.multiply())

// function greet() {
//   console.log('Hello World!')
//   // console.log(this)
// }

// // greet()

// // console.log(this)

// greet()
// this.greet()

// function greet() {
//   console.log(this)
// }

// const user = {
//   firstName: 'John',
//   lastName: 'Doe',
//   role: 'admin',
//   fullName() {
//     console.log(this)
//   }
// }

// // console.log(user.firstName)
// // console.log(user.lastName)
// // console.log(user.role)
// user.fullName()
// greet()

// const user = {
//   firstName: 'John',
//   lastName: 'Doe',
//   role: 'admin',
//   fullName() {
//     console.log(`${this.firstName} ${this.lastName} is ${this.role}`)
//   }
// }

// user.fullName()

// const user = {
//   firstName: 'John',
//   lastName: 'Doe',
//   role: 'admin',
//   fullName() {
//     const { firstName, lastName, role } = this // Object Destructuring
//     console.log(`${firstName} ${lastName} is an ${role}`)
//   }
// }

// user.fullName()

const user = {
  firstName: 'John',
  lastName: 'Doe',
  role: 'admin',
  greet() {
    console.log('hello world')
  },
  fullName() {
    return `${this.firstName} ${this.lastName} is an ${this.role}`
  },
  logDetails() {
    console.log(this)
    console.log(`${this.fullName()} and is cool!`)
  }
}

const coolUser = {
  firstName: 'Jane',
  lastName: 'Chan',
  role: 'superadmin',
  fullName: user.fullName,
  logDetails: user.logDetails
}

// user.logDetails()

// const outsideLogDetails = user.logDetails()
// console.log(outsideLogDetails)

// const outsideGreet = user.greet
// outsideGreet()

const logDetails = user.logDetails

// logDetails()
// user.logDetails()

// console.log(coolUser.fullName())
coolUser.logDetails()
// const hellos = {
//   messages: [
//     'hello world',
//     'hello universe',
//     'hello darkness',
//     'hello hello',
//     'heylo'
//   ],
//   pickMsg() {
//     console.log(this)
//     const index = Math.floor(Math.random() * this.messages.length)
//     return this.messages[index]
//   },
//   start() {
//     setInterval(() => console.log(this.pickMsg()), 1000)
//   }
// }

// // setInterval(function () {
// //   console.log('HELLO WORLD')
// // }, 500)

// hellos.start()

// const h1 = document.getElementById('pagetitle')
// console.dir(h1)

// const li = document.getElementsByTagName('li')
// console.log(li)

// const p = document.getElementsByTagName('p')
// console.log(p.length)

// const special = document.getElementsByClassName('special')
// console.log(special)

const someUl = document.getElementsByTagName('ul')[0]
// console.log(someUl[0])

const specials = someUl.getElementsByClassName('special')
console.log(specials)
// // const ul = document.querySelector('ul')
// // const allLis = ul.querySelectorAll('li')
// // allLis[0].innerText = 'This is the modified version.'

// // console.log(ul.innerText)

// const ul = document.querySelector('ul')
// const allLis = ul.querySelectorAll('li')

// // console.log(allLis)

// for (let li of allLis) {
//   li.innerHTML += '<b class="bold-text">Added to it via JS</b>'
// }

// const lis = document.querySelectorAll('li')
// const colors = ['red', 'yellow', 'green', 'orange', 'teal']

// for (li of lis) {
//   li.style.color = colors[Math.floor(Math.random() * colors.length)]
//   li.style.fontSize = '20'
//   li.style.fontWeight = 'bold'
//   // li.style.backgroundColor = 'black'
//   li.style.fontFamily = 'Helvetica, sans-serif'
// }

// const subtitle = document.createElement('h2')
// subtitle.innerText = 'This is a subtitle'
// subtitle.style.fontSize = '35px'

// const section = document.querySelector('section')
// section.appendChild(subtitle)

// const photo = document.createElement('img')
// photo.setAttribute('src', 'https://4.img-dpreview.com/files/ph1.jpeg')
// photo.style.width = '400px'

// const photoLink = document.createElement('a')
// photoLink.setAttribute('href', 'https://4.img-dpreview.com/files/ph1.jpeg')
// photoLink.setAttribute('target', '_blank')

// photoLink.appendChild(photo)
// section.appendChild(photolink)

// const todos = document.querySelector('#todos')
// const newTask = document.createElement('li')
// newTask.innerText = 'Brand new task to complete'
// // newTask.innerHTML = 'Brand new task to complete <button>X</button>'
// newTask.classList.add('todo')
// newTask.style.color = 'purple'
// newTask.style.fontWeight = 'bold'

// todos.appendChild(newTask) // append to the end

// const firstTask = todos.querySelector('li')
// todos.insertBefore(newTask, firstTask) // insert before a certain element

// const b = document.createElement('b')
// b.innerText = 'HELLO WORLD'
// const p = document.querySelector('p')

// p.insertAdjacentElement('beforebegin', b)
// p.insertAdjacentElement('afterbegin', b)
// p.insertAdjacentElement('beforeend', b)
// p.insertAdjacentElement('afterend', b)

// const b = document.createElement('b')
// b.innerText = 'HELLO WORLD'
// const i = document.createElement('i')
// i.innerText = 'GOODBYE GALAXY'

// const p = document.querySelector('p')

// p.append(b, i) // append both b and i to the end of p
// p.append(b) // just append b to the end of p
// p.prepend(i) // prepend i to the start of p
// const btn = document.querySelector('#btn1')

// btn.onclick = function () {
//   console.log('Incorrect EVENT')
// }

// btn.onclick = function () {
//   console.log('Log this as well')
// }

// btn.addEventListener('click', function () {
//   console.log('Button was clicked!')
// })

// // btn.addEventListener('click', function () {
// //   alert('This button was click and it trigged this!')
// // })

// btn.addEventListener('mouseover', function () {
//   btn.innerText = 'Button was hovered'
//   // console.log('The mouse was hovered over this button')
// })

// btn.addEventListener('mouseout', function () {
//   btn.innerText = 'Button 1'
//   // console.log('The mouse was hovered over this button')
// })

// window.addEventListener('scroll', function () {
//   console.log('Stop scrolling')
// })

// btn.addEventListener('mouseover', function () {
//   const height = Math.floor(Math.random() * window.innerHeight)
//   const width = Math.floor(Math.random() * window.innerWidth)
//   btn.style.top = `${height}px`
//   btn.style.left = `${width}px`
// })

// btn.addEventListener('click', function () {
//   btn.innerText = 'You won!'
//   document.body.style.backgroundColor = 'green'
// })

// const colors = [
//   'red',
//   'orange',
//   'yellow',
//   'green',
//   'blue',
//   'purple',
//   'indigo',
//   'violet',
//   'teal'
// ]

// const container = document.querySelector('#boxes')

// const printColor = function (e) {
//   console.log(this.style.backgroundColor)
// }

// const changeColor = function (e) {
//   console.log(e)
//   const h1 = document.querySelector('h1')
//   h1.innerText = this.style.backgroundColor + ' selected'
//   h1.style.color = this.style.backgroundColor
// }

// for (color of colors) {
//   const box = document.createElement('div')
//   box.style.backgroundColor = color
//   box.classList.add('box')
//   container.append(box)

//   box.addEventListener('click', printColor)
//   box.addEventListener('click', changeColor)
// }

// document.body.addEventListener('keypress', function (e) {
//   if (e.code === 'Space') {
//     console.log('You clicked the space bar')
//   }
// })

// const input = document.querySelector('#username')

// input.addEventListener('keydown', function (e) {
//   console.log('KEY DOWN')
// })

// input.addEventListener('keyup', function (e) {
//   console.log('KEY UP')
// })

// input.addEventListener('keypress', function (e) {
//   console.log('KEY PRESS')
// })
taskAdd.querySelector('#addtask')
const todos = document.querySelector('#todos')

taskAdd.addEventListener('keypress', function (event) {
  if (event.key === 'Enter') {
    const newTaskText = this.value
    const newTask = document.createElement('li')
    newTask.innerText = newTaskText
    todos.append(newTask)
    this.value = ''
  }
})
// // console.log('The first log')
// // alert('An interruption in between')
// // console.log('The last log')

// // console.log('The first log')
// // setTimeout(() => {
// //   console.log('The line that takes time to complete')
// // }, 3000)

// // console.log('The last log')

// const btn = document.querySelector('button')

// // setTimeout(() => {
// //   btn.style.transform = `translateX(100px)`
// //   setTimeout(() => {
// //     btn.style.transform = `translateX(200px)`
// //     setTimeout(() => {
// //       btn.style.transform = `translateX(300px)`
// //       setTimeout(() => {
// //         btn.style.transform = `translateX(400px)`
// //         setTimeout(() => {
// //           btn.style.transform = `translateX(500px)`
// //           setTimeout(() => {
// //             btn.style.transform = `translateX(600px)`
// //           }, 1000)
// //         }, 1000)
// //       }, 1000)
// //     }, 1000)
// //   }, 1000)
// // }, 1000)

// const moveX = (element, amount, delay, successCb, failureCb) => {
//   setTimeout(() => {
//     element.style.transform = `translateX(${amount}px)`
//     if (callback) {
//       successCb()
//     }
//   }, delay)
// }

// moveX(btn, 100, 500, () => {
//   // Success
//   console.log('Success!')
//   moveX(btn, 100, 500, () => {
//     // Success
//     console.log('Success!')
//       moveX(btn, 100, 500, () => {
//         // Success
//         console.log('Success!')
//           moveX(btn, 100, 500, () => {
//             // Success
//             console.log('Success!')
//               moveX(btn, 100, 500, () => {
//                 // Success
//                 console.log('Success!')
//               }, () => {
//                 // Failure
//                 alert('FAILED!')
//               })
//           }, () => {
//             // Failure
//             alert('FAILED!')
//           })
//       }, () => {
//         // Failure
//         alert('FAILED!')
//       })
//   }, () => {
//     // Failure
//     alert('FAILED!')
//   })
// }, () => {
//   // Failure
//   alert('FAILED!')
// })

// const willGetAPlayStation = new Promise((resolve, reject) => {
//   const rand = Math.random()

//   if (rand < 0.5) {
//     resolve()
//   } else {
//     reject()
//   }
// })

// // console.log(willGetAPlayStation)

// // on promise fulfillment
// willGetAPlayStation
//   .then(() => {
//     console.log('I got a playstation!')
//   })
//   .catch(() => {
//     console.log("$@#$% I didn't get anything!")
//   })

// const makePlayStationPromise = () => {
//   return new Promise((resolve, reject) => {
//     setTimeout(() => {
//       const rand = Math.random()
//       if (rand < 0.5) {
//         resolve()
//       } else {
//         reject()
//       }
//     }, 5000)
//   })
// }

// makePlayStationPromise()
//   .then(() => {
//     console.log('I got a playstation!')
//   })
//   .catch(() => {
//     console.log("$@#$% I didn't get anything!")
//   })

// const fakeRequest = url => {
//   return new Promise((resolve, reject) => {
//     setTimeout(() => {
//       const pages = {
//         '/users': [
//           { id: 1, username: 'john' },
//           { id: 2, username: 'jane' }
//         ],
//         '/about': 'This is the about page'
//       }
//       const data = pages[url]

//       if (data) {
//         resolve({ status: 200, data })
//       } else {
//         reject({ status: 404 })
//       }
//     }, 3000)
//   })
// }

// fakeRequest('/kaand')
//   .then(() => console.log('DONE SUCCESS'))
//   .catch(() => console.log('FAILED!'))

const fakeRequest = url => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const pages = {
        '/users': [
          { id: 1, username: 'john' },
          { id: 2, username: 'jane' }
        ],
        '/users/1': {
          id: 1,
          username: 'johndoe',
          topPostId: 53231,
          city: 'mumbai'
        },
        '/users/5': {
          id: 1,
          username: 'janedoe',
          topPostId: 32443,
          city: 'pune'
        },
        '/posts/53231': {
          id: 1,
          title: 'Really amazing post',
          slug: 'really-amazing-post'
        }
      }
      const data = pages[url]

      if (data) {
        resolve({ status: 200, data })
      } else {
        reject({ status: 404 })
      }
    }, 1000)
  })
}

// fakeRequest('/users')
//   .then(res => {
//     const id = res.data[0].id
//     fakeRequest(`/users/${id}`).then(res => {
//       const postId = res.data.topPostId
//       fakeRequest(`/posts/${postId}`).then(res => {
//         console.log(res)
//       })
//     })
//   })
//   .catch(err => console.log(err))

fakeRequest('/users')
  .then(res => {
    console.log(res)
    const id = res.data[0].id
    return fakeRequest(`/chubaka/${id}`)
  })
  .then(res => {
    console.log(res)
    const postId = res.data.topPostId
    return fakeRequest(`/posts/${postId}`)
  })
  .then(res => {
    console.log(res)
  })
  .catch(err => console.log(err))
<!-- HTML FILE -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JavaScript Events</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <!-- <h1>JavaScript Events</h1> -->

    <!-- <button onclick="console.log('Hello World')">Say Hello</button> -->

    <!-- <button id="btn1">Try to click me</button> -->

    <!-- DEMO BTN MOVE -->
    <button>Try to click this</button>

    <!-- MULTIPLE ITEM EVENTS -->
    <!-- ADD THIS H1 LATER -->
    <!-- <h1 style="text-align: center">Pick a color</h1>
    <section id="boxes"></section> -->

    <!-- KEY EVENTS -->
    <!-- <input type="text" id="username" placeholder="username" /> -->
    <!-- <input type="text" id="addtask" placeholder="Create a task" /> -->
    <!-- <ul id="todos"></ul> -->

    <!-- FORM EVENTS -->
    <!-- <form id="payment-form">
      <input name="cc" type="text" placeholder="credit card no." id="cc" />
      <label>
        <input
          name="terms"
          type="checkbox"
          placeholder="credit card no."
          id="terms"
        />
        I agree to terms and conditions
      </label>
      <select name="brand" id="brand">
        <option selected value="Rupay Card">Rupay Card</option>
        <option value="Visa Card">Visa Card</option>
        <option value="Master Card">Master Card</option>
      </select>
      <input type="submit" value="Submit Form" />
    </form> -->
    <script src="scratchpad_12.js"></script>
  </body>
</html>
// const request = new XMLHttpRequest()

// request.onload = function () {
//   console.log('DATA LOADED')
// }

// request.onerror = function () {
//   console.log('ERROR!')
// }

// request.open('GET', 'https://swapi.dev/api/planets')
// request.send()

// const req = new XMLHttpRequest()

// req.addEventListener('load', function () {
//   console.log('DATA LOADED')
//   const data = JSON.parse(this.responseText)
//   const planet = data.results[0]
//   const filmUrl = planet.films[0]

//   // Second request
//   const film = new XMLHttpRequest()
//   film.addEventListener('load', function () {
//     const filmData = JSON.parse(this.responseText)
//     console.log(filmData)
//   })
//   film.open('get', filmUrl, true)
//   film.send()
// })
// req.addEventListener('error', function () {
//   console.log('ERROR')
// })

// req.open('get', 'https://swapi.dev/api/planets', true)
// req.send()

// console.log(req)

const parseData = res => {
  if (res.status !== 200) {
    throw new Error(`Status HELLO code error: ${res.status}`)
  } else {
    return res.json()
  }
}

const printData = data => {
  // console.log(data)
  data.results.forEach(planet => {
    console.log(planet.name)
  })
  // return new Promise((resolve, reject) => {
  //   resolve(data.next)
  // })
  return Promise.resolve(data.next)
}

const fetchNext = url => {
  return fetch(url)
}

fetch('https://swapi.dev/api/plasanets')
  .then(parseData)
  .then(printData)
  .then(fetchNext)
  .then(parseData)
  .then(printData)
  // .then(parseData)
  // .then(data => {
  //   printData(data)
  //   return fetchNext(data.next)
  // })
  // .then(res => {
  //   return parseData(res)
  // })
  // .then(data => {
  //   printData(data)
  // })
  .catch(err => {
    console.log('SOMETHING WENT WRONG\n\n', err)
  })

console.log('HELLLLO WORLDDD!')

// SLIDE EXAMPLES

// Fetch basic
fetch('https://swapi.dev/api/planets', {
  headers: { Accept: 'application/json' }
})
  .then(response => {
    if (response.status !== 200) {
      console.log('Problem', response.status)
      return
    }
    // Have to use response.json as we get back a stream
    // and not the json directly.
    response.json().then(data => {
      // itself is a promise
      console.log(data)
    })
  })
  .catch(function (err) {
    console.log('Fetch err', err)
  })

//  -------------------------

// XHR example using fetch instead

fetch('https://swapi.dev/api/plannets')
  .then(response => {
    if (!response.ok) {
      console.log('Something went wrong!', response.status)
      // throw new Error(`Status code error: ${response.status}`)
    } else {
      response.json().then(data => {
        for (let planet of data.results) {
          console.log(planet.name)
        }
      })
    }
  })
  .catch(err => {
    console.log('ERROR!', err)
  })

// CHAINING FETCH
fetch('https://swapi.dev/api/planets')
  .then(response => {
    if (!response.ok) {
      throw new Error(`Status code error: ${response.status}`)
    }
    return response.json()
  })
  .then(data => {
    console.log('Fetched all planets')
    const filmUrl = data.results[0].films[0]
    return fetch(filmUrl)
  })
  .then(response => {
    if (!response.ok) {
      throw new Error(`Status code error: ${response.status}`)
    }
    return response.json()
  })
  .then(data => {
    console.log('Fetched first film')
    console.log(data.title)
  })
  .catch(err => {
    console.log('ERROR!', err)
  })

// Chaining FETCH -> function abstraction

// Custom function to extract checking and parsing logic
const checkStatusAndParse = res => {
  if (!res.ok) {
    throw new Error(`Status code error: ${res.status}`)
  }
  return res.json()
}

fetch('https://swapi.dev/api/planets')
  .then(response => {
    return checkStatusAndParse(response)
  })
  .then(data => {
    console.log('Fetched all planets')
    const filmUrl = data.results[0].films[0]
    return fetch(filmUrl)
  })
  .then(response => {
    return checkStatusAndParse(response)
  })
  .then(data => {
    console.log('Fetched first film')
    console.log(data.title)
  })
  .catch(err => {
    console.log('ERROR!', err)
  })

// Chaining FETCH -> function abstraction - cleanup

// Custom function to extract checking and parsing logic
const checkStatusAndParse = res => {
  if (!res.ok) {
    throw new Error(`Status code error: ${res.status}`)
  }
  return res.json()
}

fetch('https://swapi.dev/api/planets')
  .then(checkStatusAndParse)
  .then(data => {
    console.log('Fetched all planets')
    const filmUrl = data.results[0].films[0]
    return fetch(filmUrl)
  })
  .then(checkStatusAndParse)
  .then(data => {
    console.log('Fetched first film')
    console.log(data.title)
  })
  .catch(err => {
    console.log('ERROR!', err)
  })
// fetch('https://swapi.dev/api/planets')
//   .then(response => {
//     if (response.status !== 200) {
//       throw new Error('ERRORRRR!')
//     } else {
//       console.log(response)
//       // return response.json()
//     }
//   })
//   // .then(data => console.log(data.results))
//   .catch(err => console.log(err))

// axios
//   .get('https://swapi.dev/api/planets')
//   .then(({ data }) => {
//     // console.log(res.data.results)
//     const tatooine = data.results[0]
//     console.log('PLANET NAME: ', tatooine.name)
//     return axios.get(tatooine.residents[0])
//   })
//   .then(({ data }) => {
//     console.log('PILOT NAME: ', data.name)
//     return axios.get(data.starships[0])
//   })
//   .then(({ data }) => console.log('STARSHIP: ', data.name))
//   .catch(err => console.log('SOMETHING WENT WRONG', err))

// const user = {
//   name: 'John',
//   job: 'FullStack Dev',
//   age: 25,
//   details: {
//     father: 'Jack',
//     mother: 'Jane',
//     planet: {
//       name: 'Tatooine',
//       galaxy: 'Far far away',
//       boss: 'Lord Vader'
//     }
//   },
//   sayName() {
//     console.log(this.name)
//   }
// }

// const { details, age, sayName } = user

// console.log(details)
// console.log(age)

// sayName()

// Get a reference the planets div
const planets = document.querySelector('#planets')
const pageTitle = document.querySelector('#title')

const getPlanets = () => {
  axios.get('https://swapi.dev/api/planets').then(({ data }) => {
    pageTitle.innerHTML = `Total Planets in the galaxy: ${data.count}`
    data.results.forEach(planet => {
      const planetCard = document.createElement('div')
      const planetName = document.createElement('h2')
      const planetPop = document.createElement('span')
      const planetTerr = document.createElement('span')

      planetName.innerText = planet.name
      planetPop.innerHTML = `Population: <b>${planet.population}</b><br>`
      planetTerr.innerHTML = `Terrain: <b>${planet.terrain}</b>`

      planetCard.append(planetName, planetPop, planetTerr)
      planetName.classList.add('text-2xl')
      planetCard.classList.add('p-4')
      planetCard.classList.add('bg-yellow-200')
      planets.append(planetCard)
    })
  })
}

// getPlanets()
// const getPlanets = () => {
//   const planetData = []

//   axios.get('https://swapi.dev/api/planets/').then(({ data }) => {
//     const { results } = data

//     results.forEach(planet => {
//       const singlePlanetData = {
//         name: planet.name,
//         residents: []
//       }

//       planet.residents.forEach(residentLink => {
//         axios.get(residentLink).then(res => {
//           singlePlanetData.residents.push(res.data.name)
//         })
//       })

//       planetData.push(singlePlanetData)
//     })
//   })
//   return planetData
// }

// console.log(getPlanets())

// async function getData() {
//   // const data = axios.get('https://swapi.dev/api/planets')
//   throw new Error('Some Error')
//   // return 'Hello World'
// }

// getData()
//   .then(res => console.log(res))
//   .catch(err => console.log(err))

// async function add(x, y) {
//   if (typeof x !== 'number' || typeof y !== 'number') {
//     throw new Error('X and Y must be numbers')
//   }
//   return x + y
// }

// add(10, 10)
//   .then(res => {
//     console.log(res)
//   })
//   .catch(res => {
//     console.log(res)
//   })

// async function getData() {
//   const response = await axios.get('https://swapi.dev/api/plsdsdanets')
//   console.log(response.data)
// }

// // getData()

// getData().catch(err => console.log(err))

// const planetaryDetails = []

// async function getData() {
//   try {
//     const res = await axios.get('https://swapi.dev/api/planets')
//     const residents = []
//     // console.log(res.data)

//     for (let planet of res.data.results) {
//       let planetDetails = {
//         name: planet.name,
//         residents: []
//       }
//       for (let resident of planet.residents) {
//         let residentData = await axios.get(resident)
//         planetDetails.residents.push(residentData.data.name)
//       }
//       residents.push(planetDetails)
//     }

//     // console.log(residents)

//     // for (let resident of res.data.results[0].residents) {
//     //   let resData = await axios.get(resident)
//     //   residents.push(resData.data)
//     // }

//     // console.log(residents)
//   } catch (error) {
//     console.log(error)
//   }
// }

// getData()

// console.log(planetaryDetails)

// // getData().catch(err => console.log(err))

// async function getPokemon() {
//   const pokemon1 = await axios.get('https://pokeapi.co/api/v2/pokemon/1')
//   const pokemon2 = await axios.get('https://pokeapi.co/api/v2/pokemon/2')
//   const pokemon3 = await axios.get('https://pokeapi.co/api/v2/pokemon/3')

//   console.log(pokemon1.data)
//   console.log(pokemon2.data)
//   console.log(pokemon3.data)
// }

// getPokemon()

// async function getPokemon() {
//   const prom1 = axios.get('https://pokeapi.co/api/v2/pokemon/1')
//   const prom2 = axios.get('https://pokeapi.co/api/v2/pokemon/2')
//   const prom3 = axios.get('https://pokeapi.co/api/v2/pokemon/3')

//   const poke1 = await prom1
//   const poke2 = await prom2
//   const poke3 = await prom3

//   console.log(poke1.data) // undefined
//   console.log(poke2.data) // undefined
//   console.log(poke3.data) // undefined
// }

// getPokemon()

// function printPokemon(results) {
//   for (let pokemon of results) {
//     console.log(pokemon.data.name)
//   }
// }

// async function getPokemon() {
//   const prom1 = axios.get('https://pokeapi.co/api/v2/pokemon/1')
//   const prom2 = axios.get('https://pokeapi.co/api/v2/pokemon/2')
//   const prom3 = axios.get('https://pokeapi.co/api/v2/pokemon/3')

//   const results = await Promise.all([prom1, prom2, prom3])
//   return { data: results.data, final: 'DONE' }
//   // console.log(results)
//   // printPokemon(results)
// }

const getPokemon = async () => {
  const prom1 = axios.get('https://pokeapi.co/api/v2/pokemon/1')
  const prom2 = axios.get('https://pokeapi.co/api/v2/pokemon/2')
  const prom3 = axios.get('https://pokeapi.co/api/v2/pokemon/3')

  const results = await Promise.all([prom1, prom2, prom3])
  // console.log([...results])
  return [...results]
  // return { data: results.data, final: 'DONE' }
}

// const getFinalData = () => {
//   return getPokemon()
// }

// console.log(getFinalData())

const final = getPokemon()
console.log('final', final)
// // // const myArr = [1, 2, 3]
// // // const myArr2 = ['Hello', 'World', 'Galaxy']

// // // const myObj = {
// // //   firstName: 'John',
// // //   lastName: 'Doe',
// // //   greet() {
// // //     console.log('Hello World')
// // //   }
// // // }

// // const myObj2 = {
// //   productName: 'Apple watch',
// //   productPrice: 100000,
// //   message() {
// //     console.log(`${this.productName} costs ${this.productPrice}.`)
// //   }
// // }

// // myObj2.greet = () => console.log('Hello World!')

// // const myArr = [1, 2, 3, 4]

// // // console.log(myArr)
// // // console.log(myArr2)
// // // console.log(myObj)
// // console.log(myObj2)
// // // // console.log('Hello World')

// // Array.prototype.sayHello = message => console.log(message)
// // Array.prototype.pop = () => console.log('Mai nahi nikalunga!')
// // String.prototype.toUpperCase = str => str.toLowerCase()
// // String.prototype.greet = msg => console.log(`${msg} <- is the message`)

// function initializeDeck() {
//   const deck = []
//   const suits = ['hearts', 'diamonds', 'spades', 'clubs']
//   const values = '2,3,4,5,6,7,8,9,10,J,Q,K,A'
//   for (let value of values.split(',')) {
//     for (let suit of suits) {
//       deck.push({ value, suit })
//     }
//   }
//   return deck
// }

// function drawCard(deck, drawnCards) {
//   const card = deck.pop()
//   drawnCards.push(card)
//   return card
// }

// function drawMultiple(numCards, deck, drawnCards) {
//   const cards = []
//   for (let i = 0; i < numCards; i++) {
//     cards.push(drawCard(deck, drawnCards))
//   }
//   return cards
// }

// function shuffle(deck) {
//   // loop over array backwards
//   for (let i = deck.length - 1; i > 0; i--) {
//     // pick random index before current element
//     let j = Math.floor(Math.random() * (i + 1))
//     ;[deck[i], deck[j]] = [deck[j], deck[i]]
//   }
//   return deck
// }

// // const myDeck = initializeDeck()
// // const myDeck2 = initializeDeck()
// // shuffle(myDeck)
// // const hand = []

// // drawCard(myDeck, hand)
// // drawMultiple(2, myDeck, hand)

// // console.log(hand)
// // console.log(myDeck)

// // Refactoring to make the above a factory function

// OBJECT
// const makeDeck = () => {
//   return {
//     deck: [],
//     drawnCards: [],
//     suits: ['hearts', 'diamonds', 'spades', 'clubs'],
//     values: '2,3,4,5,6,7,8,9,10,J,Q,K,A',
//     initializeDeck() {
//       const { suits, values, deck } = this
//       for (let value of values.split(',')) {
//         for (let suit of suits) {
//           deck.push({ value, suit })
//         }
//       }
//       return deck
//     },
//     drawCard() {
//       const { deck, drawnCards } = this
//       const card = deck.pop()
//       drawnCards.push(card)
//       return card
//     },
//     drawMultiple(numCards) {
//       const cards = []
//       for (let i = 0; i < numCards; i++) {
//         cards.push(this.drawCard())
//       }
//       return cards
//     },
//     shuffle() {
//       const { deck } = this
//       for (let i = deck.length - 1; i > 0; i--) {
//         let j = Math.floor(Math.random() * (i + 1))
//         ;[deck[i], deck[j]] = [deck[j], deck[i]]
//       }
//     }
//   }
// }

// const myDeck1 = makeDeck()
// const myDeck2 = makeDeck()

// myDeck1.initializeDeck()
// myDeck2.initializeDeck()

// myDeck1.shuffle()

// myDeck1.drawCard()
// myDeck2.drawCard()

// myDeck1.drawMultiple(3)
// myDeck2.drawMultiple(3)

// console.log(myDeck1)
// console.log(myDeck2)

// CONSTRUCTOR FUNC
// function Person(firstName, lastName, age) {
//   this.firstName = firstName
//   this.lastName = lastName
//   this.age = age
// }

// Person.prototype.greet = function () {
//   console.log('Hello World!')
// }

// Person.prototype.sayName = function () {
//   console.log(this.firstName + ' ' + this.lastName)
// }

// // const myArr = [1, 2, 3, 4]
// // const myArr = new Array(1, 2, 3, 4)
// // const jane = { firstName: 'Jane', lastName: 'Doe', age: 25 }
// // console.log(myArr)

// const john = new Person('john', 'doe', 25)
// const jane = new Person('jane', 'doe', 27)
// console.log(john)
// console.log(jane)

// class Person {
//   constructor(firstName, lastName, age) {
//     this.firstName = firstName
//     this.lastName = lastName
//     this.age = age
//   }

//   greet() {
//     console.log('Hello World!')
//   }
//   sayName() {
//     console.log(this.firstName + ' ' + this.lastName)
//   }
// }

// const john = new Person('john', 'doe', 25)
// const jane = new Person('jane', 'doe', 27)

// console.log(john)
// console.log(jane)

// // DECK TYPE
// class Deck {
//   constructor() {
//     this.deck = []
//     this.drawnCards = []
//     this.suits = ['hearts', 'diamonds', 'spades', 'clubs']
//     this.values = '2,3,4,5,6,7,8,9,10,J,Q,K,A'
//   }

//   initializeDeck() {
//     const { suits, values, deck } = this
//     for (let value of values.split(',')) {
//       for (let suit of suits) {
//         deck.push({ value, suit })
//       }
//     }
//     return deck
//   }

//   drawCard() {
//     const card = this.deck.pop()
//     this.drawnCards.push(card)
//     return card
//   }

//   drawMultiple(numCards) {
//     const cards = []
//     for (let i = 0; i < numCards; i++) {
//       cards.push(this.drawCard())
//     }
//     return cards
//   }

//   shuffle() {
//     // const { deck } = this
//     for (let i = this.deck.length - 1; i > 0; i--) {
//       let j = Math.floor(Math.random() * (i + 1))
//       ;[this.deck[i], this.deck[j]] = [this.deck[j], this.deck[i]]
//     }
//   }
// }

// const deck1 = new Deck()
// const deck2 = new Deck()
// deck1.initializeDeck()
// deck2.initializeDeck()
// deck1.shuffle()
// deck2.shuffle()
// deck1.drawCard()
// deck2.drawCard()
// deck1.drawMultiple(2)
// deck2.drawMultiple(2)

// console.log(deck1)
// console.log(deck2)

// function shuffle() {
//   const { deck } = this
//   for (let i = deck.length - 1; i > 0; i--) {
//     let j = Math.floor(Math.random() * (i + 1))
//     ;[deck[i], deck[j]] = [deck[j], deck[i]]
//   }
// }

// CLASSES WITH INHERITANCE  (SUBCLASS CREATION)
class User {
  constructor(username, password) {
    this.username = username
    this.password = password
  }

  login(pass) {
    if (pass === this.password) {
      console.log('Welcome back')
    } else {
      console.log('Incorrect password')
    }
  }
  logout() {
    console.log('You have signed out')
  }
}

class Subscriber extends User {}

class Creator extends User {
  constructor(username, password, aadhaar) {
    super(username, password)
    this.aadhaar = aadhaar
  }
  uploadVideo(videoName) {
    console.log(`${videoName} was successfully uploaded`)
  }
}

const john = new Subscriber('johndoe', 'john@123')
const jane = new Creator('janedoe', 'jane@123', 'kmsalkd776d57q3387')

console.log(john)
console.log(jane)

// john.login('john@123')
john.login('john@123')
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Website</title>
  </head>
  <body>
    <div id="root"></div>

    <script src="https://unpkg.com/react@^16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@^16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel">
      // const rootElement = document.querySelector('#root')
      // const heading = document.createElement('h1')
      // heading.innerText = 'Hello World!'
      // rootElement.append(heading)

      // const heading = React.createElement('h1', {
      //   children: 'Hello World, Universe',
      //   className: 'title'
      // })

      // const subheading = React.createElement('h2', {}, 'This is the subtitle')

      // const para = React.createElement('p', {
      //   children: 'This is a paragraph created in ReactJS'
      // })

      // const sec = React.createElement('div', {
      //   className: 'intro',
      //   children: [heading, subheading, para]
      // })

      // const heading = <h1 className='title'>Hello World, Universe</h1>
      // const subheading = <h2>This is the subtitle</h2>
      // const para = <p>This is a paragraph created in ReactJS</p>

      // console.log(heading)

      // const children = 'Hello World. This is the page title'
      // const className = 'title'
      // const props = { children, className }

      // const heading = <h1 {...props} className='introduction' id='test' />

      // const message = 'Hello World'
      // const myClassName = 'special'

      // const heading = <h1 className={myClassName}>{message.toUpperCase()}</h1>

      // COMPONENT
      function para({ className, children }) {
        return <p className={className}>{children}</p>
      }

      const element = (
        <div className='container'>
          {para({
            children: 'This is a small one-liner paragraph.',
            className: 'special'
          })}
          <p>This is another paragraph</p>
        </div>
      )

      ReactDOM.render(element, document.querySelector('#root'))
    </script>
  </body>
</html>
//// REACT FULL COMPONENT EXAMPLE

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Website</title>
    <link
      href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
      rel="stylesheet"
    />
  </head>
  <body>
    <div id="root"></div>

    <script src="https://unpkg.com/react@^16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@^16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <!-- <script type="text/babel" src="script.js"></script> -->

    <script type="text/babel">
      // const {title, date, author, children} = {
      //   title: 'My first blog post',
      //   date: 'Tue Jan 26 2021',
      //   author: 'John Doe',
      //   children: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui atque voluptas corrupti nihil repellat eum sunt nisi, perspiciatis magni aspernatur.',
      // }

      const Header = () => {
        return (
          <header className='w-100 border-b-2 py-3'>
            <h1 className='text-3xl font-bold'>My Website</h1>
          </header>
        )
      }

      const ProductCard = () => {}

      const Post = props => {
        return (
          <article className='p-4 border-2 border-gray-200 rounded'>
            <img src={props.imageUrl} alt={props.imgAlt} />
            <h3 className='text-2xl font-bold text-gray-700'>{props.title}</h3>
            <p className='text-sm font-italic text-green-400'>
              Published on {props.date} by {props.author}
            </p>
            <p className='text-base text-gray-500'>{props.children}</p>
          </article>
        )
      }

      const PostUnstyled = props => {
        return (
          <article>
            <img src={props.imageUrl} alt={props.imgAlt} />
            <h3>{props.title.toUpperCase()}</h3>
            <p>
              Published on {props.date} by {props.author}
            </p>
            <p>{props.children}</p>
          </article>
        )
      }

      const element = (
        <div>
          <Header />

          <div className='flex m-10 align-center justify-between'>
            <Post
              title='My first blog post'
              date={new Date().toDateString()}
              author='John Doe'
              imageUrl='https://www.google.com/logos/doodles/2021/india-republic-day-2021-6753651837108846-l.png'
              imgAlt='Blog Image'>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui atque
              voluptas corrupti nihil repellat eum sunt nisi, perspiciatis magni
              aspernatur.
            </Post>
            <Post
              title='My first blog post'
              date={new Date().toDateString()}
              author='John Doe'
              test='Hello'>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui atque
              voluptas corrupti nihil repellat eum sunt nisi, perspiciatis magni
              aspernatur.
            </Post>
            <Post
              title='My first blog post'
              date={new Date().toDateString()}
              author='John Doe'
              test='Hello'>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui atque
              voluptas corrupti nihil repellat eum sunt nisi, perspiciatis magni
              aspernatur.
            </Post>
          </div>
          <PostUnstyled title='My first blog post'>
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui atque
            voluptas corrupti nihil repellat eum sunt nisi, perspiciatis magni
            aspernatur.
          </PostUnstyled>
        </div>
      )

      ReactDOM.render(element, document.querySelector('#root'))
    </script>
  </body>
</html>
//// SCRATCHPAD #2

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Website</title>
  </head>
  <body>
    <div id="root"></div>

    <script src="https://unpkg.com/react@^16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@^16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel">
      // const rootElement = document.querySelector('#root')
      // const heading = document.createElement('h1')
      // heading.innerText = 'Hello World!'
      // rootElement.append(heading)

      // const heading = React.createElement('h1', {
      //   children: 'Hello World, Universe',
      //   className: 'title'
      // })

      // const subheading = React.createElement('h2', {}, 'This is the subtitle')

      // const para = React.createElement('p', {
      //   children: 'This is a paragraph created in ReactJS'
      // })

      // const sec = React.createElement('div', {
      //   className: 'intro',
      //   children: [heading, subheading, para]
      // })

      // const heading = <h1 className='title'>Hello World, Universe</h1>
      // const subheading = <h2>This is the subtitle</h2>
      // const para = <p>This is a paragraph created in ReactJS</p>

      // console.log(heading)

      // const children = 'Hello World. This is the page title'
      // const className = 'title'
      // const props = { children, className }

      // const heading = <h1 {...props} className='introduction' id='test' />

      // const message = 'Hello World'
      // const myClassName = 'special'

      // const heading = <h1 className={myClassName}>{message.toUpperCase()}</h1>

      // const para = ({className, children}) => {
      //   return <p className={className}>{children}</p>
      // }

      // COMPONENT
      function Para({ className, children }) {
        return <p className={className}>{children}</p>
      }

      // const myPara = React.createElement(para, {
      //   children: 'Something very different',
      //   className: 'special'
      // })

      const element = (
        <div className='container'>
          {/*{para({
            children: 'This is a small one-liner paragraph.',
            className: 'special'
          })}*/}
          {/*{React.createElement(Para, {
            children: 'Something very different',
            className: 'special'
          })}*/}

          <Para className='special'>This is a small one-liner paragraph.</Para>
          <p>This is another paragraph</p>
        </div>
      )

      ReactDOM.render(element, document.querySelector('#root'))
    </script>
  </body>
</html>
///// REACT PROPTYPES

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Website</title>
  </head>
  <body>
    <div id="root"></div>

    <script src="https://unpkg.com/react@^16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@^16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/prop-types@^15/prop-types.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel">
      // const PropTypes = {
      //   string(props, propName, componentName) {
      //     const type = typeof props[propName]
      //     if (type !== 'string') {
      //       return new Error(
      //         `Prop '${propName}' should be of type 'string', not '${type}'.`
      //       )
      //     }
      //   },
      //   number(props, propName, componentName) {
      //     const type = typeof props[propName]
      //     if (type !== 'number') {
      //       return new Error(
      //         `Prop '${propName}' should be of type 'number', not '${type}'.`
      //       )
      //     }
      //   }
      // }

      // Greet Component
      function Greet({ message, person }) {
        return (
          <p>
            {message}, {person}
          </p>
        )
      }

      Greet.propTypes = {
        message: PropTypes.string.isRequired,
        person: PropTypes.string.isRequired
      }

      const element = (
        <div>
          <Greet message='Hi' person='John Doe' />
          <Greet message={1020} person='Jane Doe' />
          <Greet message='Hi' />
        </div>
      )

      ReactDOM.render(element, document.querySelector('#root'))
    </script>
  </body>
</html>
import React, { useState } from 'react'
import { Flex, Heading, Link, Box, Icon } from '@chakra-ui/react'
import { HiShoppingBag, HiUser, HiOutlineMenuAlt3 } from 'react-icons/hi'

const MenuItems = ({ children, href }) => (
  <Link
    href={href}
    mt={{ base: 4, md: 0 }}
    fontSize='sm'
    letterSpacing='wide'
    color='whiteAlpha.600'
    textTransform='uppercase'
    _hover={{ color: 'whiteAlpha.800' }}
    mr={5}
    display='block'>
    {children}
  </Link>
)

const Header = () => {
  const [show, setShow] = useState(false)

  return (
    <Flex
      as='header'
      align='center'
      justify='space-between'
      wrap='wrap'
      py='6'
      px='6'
      bgColor='gray.800'
      shadow='md'>
      <Flex align='center' mr='5'>
        <Heading
          as='h1'
          color='whiteAlpha.800'
          fontWeight='medium'
          size='md'
          letterSpacing='wider'
          mr={{ md: '1rem', base: 0 }}>
          <Link
            href='/'
            _hover={{ color: 'whiteAlpha.700', textDecor: 'none' }}>
            RST Store
          </Link>
        </Heading>
      </Flex>

      <Box
        onClick={() => setShow(!show)}
        display={{ base: 'block', md: 'none', sm: 'block' }}>
        <Icon as={HiOutlineMenuAlt3} color='white' w='6' h='6' />
        <title>Menu</title>
      </Box>

      <Box
        display={{ sm: show ? 'block' : 'none', md: 'flex' }}
        width={{ sm: 'full', md: 'auto' }}
        alignItems='center'>
        <MenuItems href='/'>
          <Flex alignItems='center'>
            <Icon as={HiShoppingBag} w='4' h='4' mr='1' />
            Cart
          </Flex>
        </MenuItems>
        <MenuItems href='/'>
          <Flex alignItems='center'>
            <Icon as={HiUser} w='4' h='4' mr='1' />
            Login
          </Flex>
        </MenuItems>
      </Box>
    </Flex>
  )
}

export default Header

FullStack Ecommerce

1. Generate some boilerplate for a basic React application.

  • Go into your workspace/projects directory using the terminal.
  • cd ~/Documents/FullStack/Workspace
  • mkdir rststore
  • npx create-react-app@latest frontend –use-npm
  • code .
  • cd frontend
  • npm start
  • Open public/index.html and clean up the file. Remove all the comments and change the title and meta content.
  • Delete all the files in the src folder. Create and add basic boilerplate code to App.js and index.js.

2. Chakra UI Installation and Setup

  • npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
  • npm install react-icons –save
  • Edit index.js file. Import ChakraProvider component and wrap the App component with it.

3. Create Header and Footer components

  • Create a folder called src/components in the src folder and add all components in that.

4. Create HomeScreen product listings

  • Create the HomeScreen component (all display/screen components will go in the /screens folder), along with the product and ratings components.

5. Install and Implement React Router

  • npm install react-router-dom
  • Complete all the React Router setup on the App page.
  • Modify Product cards, Header links etc. Use React Routers Link component instead.

6. Design and build the ProductScreen component.

7. Setting up the backend

  • Close the React server.
  • Create a folder named backend outside the frontend folder in the root of our project.
  • npm init in the root. (not inside the backend or frontend folders, but outside in the root directory)
  • During npm init setup the main will be server.js
  • npm install express (in the root folder)
  • Create a file backend/server.js in the backend folder and a folder data and copy products.js to this folder.
  • Create some routes to serve the data from the backend
  • Create a script for start in package.json
  • Create 2 basic routes for ‘/’ and ‘/api/products’
  • Convert products.js export statement to commonjs format.
  • Create another route to fetch single product by id.

8. Fetching data from our backend using React

  • Inside the frontend folder, run npm install axios
  • Modify the HomeScreen component to fetch and store data in the component.
  • To fix the issue of our backend address for now, we’ll add a proxy to the frontend’s package.json. Add this “proxy”: “http://127.0.0.1:5000”, to frontend/package.json
  • Make sure both frontend and backend are running in two terminals and test it out.
  • Modify the ProductScreen component to also make a request to our backend to fetch data.
  • Delete the projects.js file from our frontend’s src folder as we no longer need it.

9. More backend setup

  • In the root directory: run
  • npm install -D nodemon concurrently. ‘-D’ is a dev dependency, meaning we only need these modules during development.
  • These above packages will help us auto restart our server when we change our code, so we don’t have to do it manually.
  • Modify scripts in package.json (root folder) to add some scripts to use our npm command directly to work them.

10. Setup environment variables

  • npm install dotenv
  • Do the dotenv setup in server.js file and create a .env file in the root folder.

11. Convert imports to ES Modules

  • Add “type”: “module” to package.json file. Change all import and export statements to ES module style.
  • Note: You will have to add .js extension while using ES modules in the backend.

12. Install and setup MongoDB

  • After installation, setup MongoDB Compass
  • Use this as the connection string: mongodb://localhost:27017/rststore
  • Add the following in the .env
    MONGO_URI = mongodb://localhost:27017/rststore

13. Connect to the database

  • Install mongoose: npm install mongoose (in the root folder)
  • Create a folder named config in the backend folder and create a file named backend/config/db.js inside it.
  • Do all the mongoDB connection setup
  • Import db.js into server.js and run the connectDB function.
  • Start the server and check if any error.

14. Improve console log messages.

  • Run this command in the root folder: npm install colors
  • Add stylings to server and db console messages.

15. Create database models for data

  • Create a models folder inside the backend folder.
  • Create all our data model schemas.

16. Prepare sample data for the backend data seeding

  • Delete all the _id key-values from the products.js as MongoDB
    will automatically create it for us.
  • Create data/users.js file and fill it with a few user objects.
  • npm install bcryptjs – for password hashing
  • Use bcryptjs for the password fields to encrypt the password. (temporary)

17. Create database seeder for quick sample data generation (optional)

  • Create backend/seeder.js file inside the backend folder.
  • Add seeding logic and create import and destroy scripts.

18. Fetching products from the database

  • Create a folder named backend/routes in the backend folder.
  • Create a file named backend/routes/productRoutes.js
  • npm install express-async-handler – we will wrap our callbacks with this function so that we can do error handler in a better way.
  • Move the products and single product fetch routes to this file and add all the logic for now.
  • Test the routes in Postman

19. Do Postman setup to work with our API (optional)Custom error handling. Run the following in the root folder.

  • npm install express-async-handler
  • Create a folder named backend/middleware in the backend folder
  • Create an errorMiddleware.js and add logic to handle errors.

20. Introduction to Redux

  • The Redux Pattern
  • Download the Redux DevTools brower extension.
  • Go to the frontend folder. cd frontend
  • npm install redux react-redux redux-thunk redux-devtools-extension
  • In the src folder, create a file named store.js
  • Import Provider and store into the index.js and do all the setup.

21. Create our first reducer

  • In frontend/src create a folder called reducers – (frontend/src/reducers).
  • Create a file (our reducer) named productReducer.js inside the reducers folder and add all the reducer logic.
  • Next, import productListReducer to store.js and add it to the combineReducers({}) function’s argument object as a new key/value.
  • Create a folder called constants in the frontend/src folder and inside it create the productConstants.js file to store all our action names.

22. Getting Redux State in the Home Screen

  1. Clean up the file. Remove axios import, the [products, setProducts] state variables and remove everything from inside useEffect. We don’t need these anymore.
  2. Import useDispatch and useSelector from ‘react-redux’. Also import listProducts from ‘../actions/productActions’.
  3. The first hook will be used to dispatch/call an action, and the other is use to select parts of the state. Here we will need the productList part of the state.

23. Getting Redux State in the Home Screen

  • Create the dispatch object using the useDispatch hook and call it in useEffect to
    fire the listProducts action.
  • To select products from our state, we need to use the useSelector hook. This hook will take in an arrow function. This function gets state and then we can select which part of the state do we want.
  • Add a conditional to display a loading messageerror message or our product list.

24. Create Message and Loader components.

  • Use the Spinner and Alert components from ChakraUI to add these.

25. Single product details screen Reducer and Action

  • Again, we will follow the same pattern/steps as earlier.
  • Start off by adding the required constants. Since this is for the single product screen, we will add the constants once again to the productConstants.js
  • Create a new reducer named productDetailsReducer in productReducers.js.
  • Whenever we create a new reducer, we have to add it to our store. So, import productDetailsReducer in the store.js file and add a new piece of state named productDetails.
  • Next, step will be to create an action. Add a new action named listProductDetails to the actions file.
  • Next, in the ProductScreen.js, get rid of axios and clean up useEffect. Import useDispatchuseSelector and the listProductDetails action that we just created.
  • Create the dispatch object and dispatch the listProductDetails action in the useEffect hook. You will now be able to see the state in Redux Devtools.
  • Use useSelector hook and select the productDetails piece of state. De-structure the correct values and use them in the JSX to display the product details, error and loading components.

26. Cart and Quantity

  • Add a quantity select box with it’s logic to only contain the number of items in stock.
  • The add to cart button should redirect to the a cart page/screen with the product id and quantity as a query string.

27. Cart Screen and Route

  • Create screens/CartScreen.js file in the screens folder.
  • Import this new CartScreen.js component in the App.js file and create a route for the Cart screen<Route path=’/cart/:id?’ component={CartScreen} />
  • The :id? question mark here means that this id is optional in the route/address. Because if we directly go to the cart page we will not have any id.

28 Cart Functionality

  • Create constants/cartConstants.js and add CART_ADD_ITEM and CART_REMOVE_ITEM constant varaibles to it.
  • Create reducers/cartReducer.js and add the reducer logic.
  • Import cartReducer.js in store.js and add the cartReducer function to the combineReducers argument object.
  • Create actions/cartActions.js. Do the below steps for the building the cart actions.
  • Import axios. We need axios to make a request to /api/products/:id to get the data/fields for that particular product.
  • Import CART_ADD_ITEM from actions/cartActions.js.
  • Then create the addToCart function which will get a (id, qty). We will get both these from the URL params.
  • We will need to use thunk as we are making an async request. So we will return an async function from it. This async function will get (dispatch, getState)dispatch is used for dispatching as usual, but getState will allow us to get our entire state tree. So anything we want like productListproductDetailscart, we can get it using getState.
  • After dispatching, we also want to store this in localStorage.
  • getState().cart.cartItems will give us back a JavaScript object and we can only store strings in the browser localStorage. Hence we have to stringify it. And when we want to take it out and read, we will have to parse it using JSON.parse
  • So we saved it to localStorage but where do we get it to actually fill the state, whenever we reload. We do that in the store.js.
  • We will first see if there is anything in cartItems in the localStorage. If it’s there, then we will add it to the initial state, so always loaded on the app’s first load. If nothing is present in the localStorage then we will just add an empty array.

29. Completing CartScreen.js and creating ‘add to cart’ functionality

  • Build the add to cart functionality.
  • Finish the CartScreen.js component.
  • Add functionality to the Remove Item from cart button. Follow the steps below:
  • Add CART_REMOVE_ITEM to the cartReducer.js file.
  • Create an action named removeFromCart in the cartActions.js file.
  • Fire this action in the removeFromCartHandler function in the CartScreen component.

30. Clean up the backend routes by extracting their logic into controllers

  • In the backend folder, create a folder called controller and create a file called productController.js inside it.
  • Extract all the logic to the productController.js from the routes file. The routes file should now only be for routing and all logic will go in to the controllers.
  • In productRoutes.js instead of using the method router.use(), instead use router.route() and add the route inside and then chain the appropriate methods get, post, put, delete etc. to it. This way we can define different controller logic to the same route. We will see this in sometime.

31. User Authentication Endpoints (Backend Routes)

  • Create routes/userRoutes.js and controllers/userController.js.
  • Start by working on an auth route. So here we want to authenticate a user by email and password. And then we want to send back some data, a token which we can save on the client (browser), so in the frontend (browser) we can use that token to access protected pages/routes (react frontend routes).
  • Import the ../models/userModel.js as we will need it to create new users in the MongoDB database.
  • Create authUser controller function. Inside it, first thing is to get data from the body. This data is on the request object and is something that will be sent here (to the backend) by a POST request and is usually sent via a form on the frontend pages. We can also mimic this sending data using Postman.
  • Before getting the data, make a new folder in rststore Postman collection.. Add a folder called Users & Auth. Inside that create a new request. Name it POST /api/users/login with the url to {{URL}}/api/users/login and add an email & password object to Body of type JSON.
  • Now this object that we add to the body tab in Postman, what will be sent in an object (key-value) on the request object. We extract that from request.body.
  • But in order for parsing this JSON data that we get on the request object from the frontend/Postman, we need to add another middleware in server.js.
  • Add the following line after const app = express part. app.use(express.json())
  • Create the authUser controller function. Also import this into userRoutes.js and add a login route.
  • Also import userRoutes in server.js and add the routes for users..
  • app.use(‘/api/users’, userRoutes)
  • Use the findOne() method on the User model to get the user’s object from the database.
  • Password needs to be encrypted before checking as we are storing encrypted passwords in the database, so add a method on the User Model itself. Edit models/userModel.js and add a method on the userSchema – userSchema.methods.matchPassword.
  • Use this new method in userController.js and finished the implementation.
  • Don’t return the token for now. Just set it to null. Return all other details leaving the password as a json object and also add an else condition.
  • Test this new endpoint in POSTMAN. (start only the server for now)

32. Using Json Web Tokens (JWT)

  • What are JWTs and how do they work?
  • Installation: npm install jsonwebtoken
  • Create a folder called backend/utils. Put all helpers and utility functions here. Inside it create a file named generateToken.js and create the token generation function. Also add the JWT_SECRET to the env as this is needed for the token generation.
  • Import generateToken.js into userController.js and use the function in the response object’s token property (key).
  • Test in Postman. Add this request to Postman.

33. Creating custom Authentication Middleware to accessprotected routes.

  • Add a new request in Postman. GET /api/users/profile with request URL {{URL}}/api/users/profile.
  • Add getUserProfile controller method to userController.js and also export it. Also add the route for this in userRoutes.js.
  • Create a new file named middleware/authMiddleware.js. This middleware will validate the token.
  • Implement the protect middleware function. In our backend we will be getting the authorization tokens in the header object in requests (we will send it that way from React).
  • Import protect into routes/userRoutes.js. We need to add our middleware function here to the /profile endpoint.
  • Implement the complete of the protect middleware function.
  • Once done, we can now use this middleware to any endpoint (route) that we want to be protected, that is only accessible using a valid JWT.
  • Finish implementation of the getUserProfile controller function in userController.js
  • Save the token in Postman, so we don’t have to keep copying and pasting tokens to the Headers. Add Tests to login request so that the token can be set in an environment variable. After that set profile request Auth Type to Bearer Token.

34. User registration

  • Add a new request to Postman: POST /api/users with url as {{URL}}/api/users. This will be a POST request. So a GET request to the same endpoint will give us a total list of users, while a POST request will create a new user.
  • Add a new registerUser controller function to the userController.js file. After that, import this function to userRoutes.js and create a new register route.
  • #### Password Encryption ####
  • The password still isn’t encrypted as we sent it directly to the User.create() function. So to encrypt password while creating a new user and adding it to the database, create a new mongoose middleware.
  • In mongoose, we can set certain things to happen on saves or finds etc. So when we execute the User.create function, before saving to database, we can run some code to encrypt the password and then save.
  • Test the new endpoint in Postman.

35. User Login Reducer and Action

  • Add all constants for the login. Create the file constants/userConstants.js and add all the constants.
  • Create the reducers/userReducer.js file and add the userLoginReducer function. Add all the necessary actions and logic.
  • Import userLoginReducer to store.js and add it to the combineReducers function’s parameter object.
  • Create the actions file in actions/userActions.js and add the action logic. This is pretty much the same as the earlier actions. Only difference here is that we have to add some headers to the axios request, and also store the user data to localStorage.
  • Lastly, since we stored the user data in localStorage, we should load them in the initial state in store.js. Create a variable named userInfoFromStorage and set it to get it’s value from localStorage. In the initialState variable, add another key named userLogin and set it’s value to userInfouserLogin: { userInfo: userInfoFromStorage }

36. User Login Screen

  • Create a new component called components/FormContainer.js which will just be a simple wrapper for our form elements. Just a box with some styling, which we will use to add all our forms in.
  • Create a new screen in screens/LoginScreen.js and write the ui and logic.
  • Import it in App.js and create a route for it.
  • (styling changes overall if required)

37. Implement all the Login – Redux functionality in the LoginScreen component.

  • (styling changes overall if required)

38. Header modification to show User and User Logout feature

  • Modify Header.js. Import redux modules, for running the LOGOUT action and getting access to the state.
    import {useDispatch, useSelector} from ‘react-redux’
  • Import Menu, MenuButton, MenuList, MenuItem from Chakra.
  • Get the userInfo state using useSelector.
  • Also import import { IoChevronDown } from ‘react-icons/io5’
  • Create a logout action in userActions.js.
  • Implement all the logic.

39. User Register, Constants, Reducer, Action and Screen.

  • Add new REGISTER constants to the userConstants.js
  • Create userRegisterReducer in the userReducers.js
  • Import this reducer in store.js and add it to combineReducers
  • Create register action in userActions.js
  • Create the RegisterScreen component and add it to the App.js router.

40. Update User Profile endpoint in the backend

  • Create a updateUserProfile controller method and export it.
  • Import updateUserProfile in userRoutes.js and add a PUT request on the same /profile route. This route will also take the middleware protect as this is a protected endpoint.

41. User Profile Screen and Getting User Details

  • Add USER DETAILS constants to the userConstants.js file.
  • Create userDetailsReducer in userReducer.js and import and add it to the Store.
  • Create and add a getUserDetails action in userActions.js
  • Create the ProfileScreen and add it to Router in App.js

42. Add Update User Profile functionality

  • Add new UPDATE PROFILE constants to the userConstants.js
  • Create userUpdateProfileReducer in the userReducers.js
  • Import this reducer in store.js and add it to combineReducers
  • Create updateUserProfile action in userActions.js
  • Import this action in the ProfileScreen and dispatch it in the submitHandler.

43. Shipping Screen and Save Address

  • Create a new component/screen in screen/ShippingScreen.js and add it to the Router in App.js
  • Complete the ShippingScreen component. Create all the local state required and build the shipping form.
  • In the submitHandler we want to dispatch an action that will save the shipping address to the Redux store.
  • Create a new constant named CART_SAVE_SHIPPING_ADDRESS in the constants/cartConstants.js file.
  • Create a new action named saveShippingAddress in the actions/cartActions.js file.
  • Create a new case CART_SAVE_SHIPPING_ADDRESS in the reducers/cartReducers.js file. Also in the cartReducer function’s initial state object, add another key named shippingAddress and set it to an empty object.

44. Shipping Screen and Save Address

  • Since we are going to store the shippingAddress to localStorage, we also should check if it’s already present in the user’s machine and load it if present. Add the code to the store.js file.
  • Add all Redux related functionality to the ShippingScreen component and finish the implementation.

45. Checkout Steps Component

  • Create he component and then add it to the ShippingScreen component.

46. PaymentScreen – where users can choose the payment method

  • Create the PaymentScreen component in the screens folder.
  • Add CART_SAVE_PAYMENT_METHOD to the cartConstants.js file.
  • Create the savePaymentMethod action function in actions/cartActions.js file.
  • Create a new case CART_SAVE_PAYMENT_METHOD in the reducers/cartReducer.js
  • Import and add PaymentScreen to App.js router.

47. Place Order Screen

  • We will just create a basic screen/page for now. We will do all the real setup only once we have created a backend to actually accept an order.
  • Create a new screen named PlaceOrderScreen.js in the screen folder.
  • Complete the PlaceOrderScreen implementation for now. We will complete this fully when we are done with our order functionality in the backend.
  • Also calculate and set cart.itemsPrice, cart.shippingPrice, cart.taxPrice, cart.totalPrice

48. Backend: Order controller and endpoint (route)

  • Create controllers/orderController.js.
  • Create routes/orderRoutes.js. Import the orderController and connect it here to a endpoint.
  • Lastly add the main endpoint (route) to server.js

49. Create Order

  • Create an constants/orderConstants.js file and add all the constants.
  • Create a new reducer file in reducers/orderReducers.js and add a orderCreateReducer function in it. Import this in store.js and add it to combineReducers.
  • Create a new actions file in actions/orderActions.js and add a createOrder action function.
  • Import createOrder action into the PlaceOrderScreen component and complete the implementation.

50. Get Order By ID (Backend Endpoint)

  • Create the getORderById controller function in the orderController.js file.
  • Add ‘/:id’ route to the orderRoute.js file and attached it to the getOrderbyID controller function.

51. Create the order details reducer and action (frontend)

  • Add new order details related constants in the orderConstants.js file.
  • Create orderDetailsReducer to the orderReducer.js file and connect it in the store.js file.
  • Create a new getOrderDetails function in the orderActions.js file.

52. Create the Order Screen component

  • Create the OrderScreen.js file in screens.
  • Add this new screen to the App.js route.

53. Backend endpoint for updating an order to paid

  • Create a new updateOrderToPaid controller function in the backend/controllers/orderController.js file.
  • Import updateOrderToPaid in the routes/orderRoutes.js and add a route for the endpoint ‘/:id/pay’ making it a PUT request.

54. Order pay reducer and action

  • Add new ORDER_PAY_ constants to the constants/orderConstants.js file in the frontend.
  • Create a new reducer function orderPayReducer in the reducers/orderReducers.js. Import this in store.js and add it to the combineReducers function.
  • Create a new action named payOrder in the actions/orderAction.js file.

55. Adding PayPal Payments

  • Signup for a free Personal or Business account on PayPal.
  • After signup process completion, go to https://developer.paypal.com/developer/applications
  • Go to Sandbox -> Accounts and create 2 Accounts, a personal which you will use to pay and a business to which you will be paying. This will be a sandbox/mock/test environment for working with the PayPal API. You can simply use the 2 default Sandbox Accounts provided by PayPal.

56. Adding PayPal Payments

  • Go to Dashboard -> My Apps & Credentials and make sure Sandbox mode is activated. Click on Create App and follow the process to create a new application. Give the app name ‘rststore’, app type ‘Merchant’ and select the business email (do not use the personal email here). Check everything carefully and then click Create App.
  • After creation you will get your Sandbox API Credentials. We won’t add the Client ID in the frontend. We’ll add it to the backend and create a route to access it.
  • Add the client ID in the .env file and create a route/endpoint in server.js
  • In order to use PayPal, we need to add a script to our site. Visit https://developer.paypal.com/docs/checkout/reference/customize-sdk/ to see the details. We will need to add this script <script src=”https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID”></script> to the orderScreen.
  • Edit the OrderScreen.js file and do all the PayPal implementations.
  • We will use an npm package for adding the PayPal button to the screen. Install the package: npm i react-paypal-button-v2. Make sure you install this in the frontend folder.
  • Add all the paypal functionality and finish the page’s implementation.

57. Show Orders on Profile Page

  • In the backend, create a new controller function, controllers/getMyOrders.js
  • Import this controller in the routes/orderRoutes.js file and connect the route/endpoint.
  • (Optional) Test the endpoint in Postman.
  • Add new ORDER_MY_LIST constants to the constants/orderConstants.js file.
  • Create a reducer named orderMyListReducer in the reducers folder.
  • Import and add this reducer to the combineReducer function’s argument object in store.js
  • Create a new action named listMyOrders and add it to the orderAction.js.
  • Modify the ProfileScreen.js file and implement showing orders in there.

58. Clear state on logout

  • Create ORDER_MY_LIST_RESET in orderConstants.js and add that as a case to the orderMyListReducer function in orderReducer.js
  • Create USER_DETAILS_RESET in userConstants.js and add that as a case to the userDetailsReducer function in userReducer.js
  • Import USER_DETAILS_RESET and ORDER_MY_LIST_RESET and dispatch it in the logout action function.

59. Admin Middleware and Getting Users Endpoint

  • We will create routes that are admin protected and will only be accessible by admin users.
  • For testing purposed, add this as a request to Postman. {{URL}}/api/users. Make a new request for this URL and name this request GET /api/users.
  • In the backend, create a new controller named getUsers in controllers/userController.js
  • Import this controller in routes/userRoutes.js and add it as a get request.
  • Create a new admin auth middleware in middlewares/authMiddlewares.js. This will check if a user is an admin. Import this userRoutes.js and protect the required route.
  • Test route in Postman.

60. Admin User List – Frontend

  • Add new USER_LIST constants to userConstants.js
  • Import these constants and create a new reducer userListReducer in the userReducer.js file. Add it to store.js as well.
  • Create new action called listUsers in userActions.js
  • Create a component called UserListScreen.js in the screens folder. This screen will show admins the complete list of users.
  • Modify the Header.js to show the admin/manage menu and it’s links.

61. Admin Screen page security

  • Edit the UserListScreen.js. Bring in the userLogin state and read the current user login info. If the user is not an admin, then push the user to the ‘/login’ page.
  • Go to the userConstants.js file and add USER_LIST_RESET to it.
  • Import USER_LIST_RESET in the userReducers.js file and add the new case to the userListReducer function.
  • Import USER_LIST_RESET in the userActions.js file and dispatch it in the LOGOUT action. This will clear the users list from redux when an admin logs out.

62. Delete User Functionality (for Admins)

  • Create a new controller function named deleteUser in backend/controllers/userController.js.
  • Import this controller in routes/userRoutes.js and create a new /:id route and add the controller which should be protected by the protect and admin middlewares, to a new route of ‘/:id’.
  • Test this new backend route in Postman. After this, implement these features in the frontend.
  • Add new USER_DELETE_ constants in the frontend/constants/userConstants.js.
  • Add a new reducer function named userDeleteReducer in the reducers/userReducers.js file. Add this reducer to store.js.
  • Add a new action function named deleteUsers to the actions/userActions.js file. Dispatch this action in the UsersListScreen component.

63. Backend endpoints for getting and updating user by it’s ID

  • Add getUserById and updateUser controller methods to the userController.js file. Import these controllers in the userRoutes.js and add the routes.
  • Test these in Postman.

64. User Edit screen and User Details screen components

  • Update the edit link in UserListScreen.js
  • Create a new file named UserEditScreen and implement it.

65. Update user functionality

  • Add new USER_UPDATE_ constants in the userConstants.js file.
  • Create userUpdateReducer reducer function in the userReducers.js and attach it to the store.js
  • Create a new action named updateUser in the userActions.js file. Dispatch this action correctly in the screens/UserEditScreen.js

66. Admin – Product List

  • In the screens folder, create ProductListScreen.js and implement the component.
  • Import and create a route for it in App.js.

67. Admin – Delete Products

  • Create a new deleteProduct controller function in the backend/productController.js file.
  • Import this new controller in routes/productRoutes.js and create an endpoint for it. Protect this route with the product and admin middlewares.
  • (optional) Test the route in Postman.
  • Now implement the feature in the frontend. Add new PRODUCT_DELETE_ constants in the frontend/constants/productConstants.js file.
  • Import these constants in the reducers/productReducers.js file and create a new productDeleteReducer function. Import and add it to the store.js
  • Create a new deleteProduct action function in actions/productActions.js
  • Import the action back in ProductListScreen.js.
  • (for testing only) You can run npm run data:import. Remember to logout and shut down the server before doing this. Also this will reset all the data in the database, including the users and their orders.

68. Create and Update Product Backend Endpoints

  • The Create Product button will immediately add a product with some dummy data and take us to an edit page where we can edit that data.
  • In the productControllers.js file, add two new controller functions, createProduct and updateProduct.
  • Import and add those controller functions in routes/productRoutes.js
  • (optional) Test it in Postman

69. Admin – Create Product Screen

  • Add new PRODUCT_CREATE_ constants in productConstants.js
  • Add new productCreateReducer function to productReducers.js and add it to store.js
  • Add new createProduct action function in productActions.js
  • Complete the ProductListScreen and implement all the functionality.

70. Product Edit Screen

  • Create a new file named ProductEditScreen.js in the screen folder. Implement the entire component.

71. Admin – Update Product Functionality

  • Add new PRODUCT_UPDATE_ constants to the productConstants.js file.
  • Add new productUpdateReducer reducer function to the productReducers.js and add it to store.js
  • Add new updateProduct action function to the productActions.js
  • Dispatch the action in ProductEditScreen and do all required modifications.

72. Image Upload Configuration and Endpoint

  • Install multer in the root folder: npm install multer
  • In the root folder, we will create a folder called uploads. We will store all our uploads in this folder
  • Create uploadRoutes.js file in the routes folder. Complete the upload implementation.
  • In the server.js file create the main route for uploads and make /uploads folder static so we can use it to upload photos.

73. Upload images from the frontend

  • Edit the ProductEditScreen component and implement the upload button.

74. Admin – Order List

  • In controllers/orderController.js, add a new controller function named getOrders.
  • Import getOrders in the routes/orderRoutes.js file and add the route and controller.
  • Add new ORDER_LIST_ constants to the orderConstants.js file.
  • Create new orderListReducer reducer function in the reducers/orderReducers.js file. Add it to the store.js
  • Create new listOrders action function in the actions/orderActions.js file.
  • Create a new OrderListScreen.js to show the orders list to the admin. Add this new component to the App.js and it’s routes.

75. Create Review Endpoint

  • Associate User to reviewSchema. Edit models/productModel.js and add a user object id ref.
  • Add a new createProductReview controller function in controllers/productControllers.js and add it to routes/productRoutes.js and create a route for it.

76. Adding product reviews on the frontend

  • Add new PRODUCT_CREATE_REVIEW_ constants to productConstants.js
  • Create productReviewCreateReducer reducer function in productReducers.js and add it to store.js
  • Create a new createProductReview action function in productActions.js file.
  • Implement the functionality in the ProductScreen.js

DEPLOYMENT

  • Add frontend/build in the gitignore
  • Then add heroku post build script
  • Signup for heroku
  • Install the CLI. https://devcenter.heroku.com/articles/heroku-cli
  • In the root folder run: heroku login
  • heroku create rststoreapp
  • add a procfile with the command in the root folder
  • add post build script in package json
  • git add .
  • git commit -m “Ready for deploy”
  • heroku git:remote -a rststoreapp
  • git push heroku main

PLease download 7ZIP software to unzip this file. The file size is large hence used 7ZIP instead of normal ZIP archive.