WDD 330: Web Frontend Development II

W04 Team Activity: Checkout

Overview

The next step for our site is to build out the checkout process.

Activity Instructions

Complete the following assignment as a team. Designate one team member as the "main driver" and collaborate on their copy of the code. Everyone on the team should be actively engaged in writing the code and contributing to the solution. Once the solution is working, make sure that everyone on the team gets a copy of the code.

There are many spots where code examples have been given. To get the most out of this activity, do not look at the examples until your group has given that section a try. Then after you look at the example, resist the temptation to copy/paste. Use the examples to get correction, or help you get unstuck.

Core Requirements

Trello

  1. In the synchronous meeting, the driver should visit the team's copy of the project Trello board.
  2. Add each of the attending team members to the Team Activity 04: Checkout card.
  3. Move it to the "Doing" list in Trello.
  4. Read the details of the card together.

GitHub Branch

  1. Pull any changes from the team GitHub project before proceeding.
  2. Create a new branch named driverinitials--team4.

The Cart

Check your cart page to make sure that it is correctly displaying a total. If it is not then someone in your group should really complete that card in Trello.

  1. Add a Checkout button/link that will take us to the checkout page.
  2. Next we need to build a form on the checkout page to gather the required information from our users. We will need the following:
    1. Customer Name
      1. First Name
      2. Last Name
    2. Address (we will assume billing and shipping are the same)
      1. Street Address
      2. City
      3. State
      4. Zip Code
    3. Payment
      1. Credit Card Number
      2. Expiration Date
      3. Security Code
    4. Checkout button
  3. The form should not submit unless all of the fields are filled out.
  4. Include an order summary somewhere near the form that includes:
    • Subtotal
    • Shipping Estimate
    • Tax
    • Order Total

    On a real site the server should verify the totals as well to make sure that the user doesn't try anything funny.

Order Summary

  1. We need to fill in the information in the summary section. The item subtotal will come from the items in the cart, and then we need to add tax and shipping costs and calculate the order total. Once the user supplies a zip code you should calculate both shipping and tax and display it in the order summary to the user.
    Normally tax and shipping would be calculated according to the destination address. There would be a look up process that would figure out the right values to use. To keep ours simple we will use the same formulas for all orders.
    Tax: Use 6% sales tax.
    Shipping: Use $10 for the first item plus $2 for each additional item after that.
    Create a CheckoutProcess module, and add a method to calculate and display the item subtotal. This should get called when the page loads.
  2. It that same module, create a second method to calculate and display shipping, tax, and the order total. This method should get called after the user fills in the zip code.
    Example: CheckoutProcess
    export default class CheckoutProcess {
      constructor(key, outputSelector) {
        this.key = key;
        this.outputSelector = outputSelector;
        this.list = [];
        this.itemTotal = 0;
        this.shipping = 0;
        this.tax = 0;
        this.orderTotal = 0;
      }
    
      init() {
        this.list = getLocalStorage(this.key);
        this.calculateItemSummary();
      }
    
      calculateItemSummary() {
        // calculate and display the total amount of the items in the cart, and the number of items.
    
      }
    
      calculateOrdertotal() {
        // calculate the shipping and tax amounts. Then use them to along with the cart total to figure out the order total
    
        // display the totals.
        this.displayOrderTotals();
      }
    
      displayOrderTotals() {
        // once the totals are all calculated display them in the order summary page
    
      }
    }

Refactor ProductData

We will use the same server we used last week to pull the products from for submitting our orders, but a different endpoint. The URL should be:

http://wdd330-backend.onrender.com/checkout

Because we need to send the order information we will need to make a POST request to the server instead of the GET request we have been using.

We are already making requests to this server for product data through our ProductData module. This would be the logical place to add the functionality to submit an order. The name "ProductData" becomes less descriptive of what is going on in the module now however. It would more descriptive with the name ExternalServices.

Refactor your code to make this change.

If you panicked a bit when you read that you are paying attention. Renaming something that you have already used in multiple places is not something to do lightly, but sometimes it is necessary. Because of this, good development tools will have built-in ways to help you. VS Code is a good tool.
  1. Commit and push your changes before proceeding. That will make it easy to revert if something goes wrong.
  2. Open ProductData.mjs and select the word ProductData in the class definition. Right click on it and select Rename symbol in the menu.
  3. Enter ExternalServices and hold down the shift key while hitting enter/return. This will preview the changes. It should open up a new panel in the editor where it will show you everywhere it found the name that you want to change. If you left-click on one of the lines it will open the file side by side with how it was before the changes and how it will be after. Review each change. If the changes looks good then right-click on the same line in the Preview panel and select apply Refactoring. Then go File->Save All
  4. Next we need to adjust the filename of the module so that it reflects the change. If you right-click on the file in the file explorer in VS Code and select rename there you should get a pop-up message that asks if you want to rename the imports as well. Say yes! File->Save All again.
  5. Finally just to make sure nothing was missed, go to the Activity Bar on the side of your VS Code window and click on the magnifying glass icon (ctrl/cmd + shift + F). Type ProductData and if there are any more instances, change them.
  6. Test the product list and product detail pages to make sure they still work.

Submit the Order

Our checkout process will involve a server. It is expecting us to send it an object with all the information about the order in a specific format. The object should look like the following (order does not matter):

{
  orderDate: '2021-01-27T18:18:26.095Z',
  fname: "John",
  lname: "Doe",
  street: "123 Main",
  city: "Rexburg",
  state: "ID",
  zip: "83440",
  cardNumber: "1234123412341234",
  expiration: "8/21",
  code: "123",
  items: [{
    id: "20CXG"
    name: "The North Face Pivoter 27 L Backpack"
    price: 39.99,
    quantity: 1
  }, {
    id: "14GVF",
    name: "Marmot 5°F Rampart Down Sleeping Bag - 650 Fill, Mummy (For Men and Women)",
    price: 229.99,
    quantity: 1
  }],
  orderTotal: "298.18",
  shipping: 12,
  tax: "16.20"
}

Create a couple of functions in your CheckoutProcess module.

  1. One will be used to prepare the items list part of this object for us.
  2. The other will get called when the form is submitted and will get the data object ready and send it to ExternalServices.
    // takes the items currently stored in the cart (localstorage) and returns them in a simplified form.
    function packageItems(items) {
      // convert the list of products from localStorage to the simpler form required for the checkout process. Array.map would be perfect for this.
    
    }
    
    export default class CheckoutProcess {
      ...
    
      async checkout(form) {
        // build the data object from the calculated fields, the items in the cart, and the information entered into the form
    
        // call the checkout method in our ExternalServices module and send it our data object.
      }
    }
    Note that the keys in your object MUST match the keys shown above. You cannot send "firstName". It has to be "fname". That is what the server will be expecting. Check your names in the html form if you have differences.

    If you are struggling to convert your form into the object required take a look at the function below.

    HINT
    // takes a form element and returns an object where the key is the "name" of the form input.
    function formDataToJSON(formElement) {
      const formData = new FormData(formElement),
        convertedJSON = {};
    
      formData.forEach(function (value, key) {
        convertedJSON[key] = value;
      });
    
      return convertedJSON;
    }
  3. Use 1234123412341234 for the card number, 123 for the security code, and any expiration in the future if you want your order to succeed.
    For testing to save you time and typing remember that you can set the value="" property of an HTML input element. Remember to remove your defaults once everything is working.
  4. Add a new method to our newly renamed ExternalServices class. Call it checkout. It should accept the object that represents our order.
  5. The method should POST that object. You will need to create a custom options object and pass it in with your fetch. (Remember to stringify your object before sending it!) to the address above (https://wdd330-backend.onrender.com:3000/checkout) and return the server response. The response will be JSON.
    All of the requests we have made thus far have been fairly simple and the default settings for fetch have been sufficient. The defaults will not work for us here. Fetch has an optional second argument that is for options. An options object that will work for use here is below:
    const options = {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(payload}

    We would use it like this:

    fetch(url, options);
    The choices include watching for a click event on the form button, or we can watch for a submit event on the form.
  6. Whichever you choose, you will want to keep the form from doing what it would normally do on submit. You can do this by calling event.preventDefault() in the listener function.
  7. You can consider this activity completed when you receive a response from your POST to the server. The next activity will continue this by handling the success and failure responses appropriately.

Instructor's Solution

As a part of this team activity, you are expected to look over a solution from the instructor, to compare your approach to that one.

Do NOT open the solution until you have worked through this activity as a team for at least one hour. At the end of the hour, if you are still struggling with some of the core requirements, you are welcome to view the instructor's solution and use it to help you complete your own code.

Example Instructor Solution

Finally - Make a Pull Request

After you have completed what you can, reviewed the instructor's solution, and gotten your code working:

  1. the driver should commit and push the changes,
  2. the driver should submit a pull request for this branch,
  3. the team reviews the pull request, closes it, and merges the branch back into the Main, and
  4. finally, someone moves the Trello card to "Done".

Submission

Return to I-Learn to report on your work.