Add Code to our App

Add code to interact with the API

Add this code to the src\components\stores.jsx file and save it:

import React, { Component, Fragment  } from "react";
import API, { graphqlOperation } from '@aws-amplify/api';
import { Button, Container, Row, Col, Input } from "reactstrap";

class Stores extends Component {

        state = {
            isLoaded: false,
            allowed: false,
            storesData: null,
            currentStoreCode: "",
            currentName: "",
            currentCity: "",
            currentState: "",
            currentStoreCodeToDelete: "",
        }

 updateField(event) {
        var txtField = event.target.id;
        var txtValue = event.target.value;
        switch (txtField)
        {
            case "storeCode":
                this.setState( { currentStoreCode: txtValue });
                break;
            case "name":
                this.setState( { currentName: txtValue });
                break;
            case "city":
                this.setState( { currentCity: txtValue });
                break;
            case "state":
                this.setState( { currentState: txtValue });
                break;
            case "delStoreCode":
                this.setState( { currentStoreCodeToDelete: txtValue });
                break;
        }
  }

    deleteStore = () => {
      console.log("Removing Store")
      const apiName = 'storeInfoApi';
      const path = '/stores';
      const myInit = { // OPTIONAL
        headers: {
        }, // OPTIONAL
        response: true, // OPTIONAL (return the entire Axios response object instead of only response.data)
        body: {
            "storeCode": this.state.currentStoreCodeToDelete
        }
      };
      API
        .del(apiName, path, myInit)
        .then(response => {
            console.log("Deleted: " + response)
            this.getLatestStores();
        })
        .catch(error => {
            console.log(error.response);
        });
    }


  updateStore = () => {
      console.log("Store updated")
    const apiName = 'storeInfoApi';
    const path = '/stores';
    const myInit = { // OPTIONAL
        headers: {
        }, // OPTIONAL
        response: true, // OPTIONAL (return the entire Axios response object instead of only response.data)
        body: {
            "storeCode": this.state.currentStoreCode,
            "name": this.state.currentName,
            "city": this.state.currentCity,
            "state": this.state.currentState
        }
    };
      API
  .post(apiName, path, myInit)
  .then(response => {
    console.log("Posted: " + response)
    this.getLatestStores();
  })
  .catch(error => {
    console.log(error.response);
  });
  };

  getLatestStores() {

    const apiName = 'storeInfoApi';
    const path = '/stores';
    const myInit = { // OPTIONAL
        headers: {
        }, // OPTIONAL
        response: true, // OPTIONAL (return the entire Axios response object instead of only response.data)

    };

    API.get(apiName, path, myInit)
  .then(response => {
      this.setState({allowed: true});
        console.log(response);
        this.setState({
            storesData: response.data
        });
  })
  .catch(error => {
      this.setState({allowed: false});
    console.log(error.response);
 });

  }

  componentDidMount() {
    console.log("Stores component loaded")
    this.getLatestStores();
  }

    render() {
    return (
      <Fragment>
       {this.state.allowed ?

       <Fragment>
        <b>Stores Administration</b>
        <Container>
          <Row className="font-weight-bold">
            <Col>Store Code</Col>
            <Col>Store Name</Col>
            <Col>City</Col>
          </Row>
          {this.state.storesData
            ? this.state.storesData.map(storeInfo => (
                <Row key={storeInfo.storeCode}>
                  <Col>{storeInfo.storeCode}</Col>
                  <Col>{storeInfo.name}</Col>
                  <Col>{storeInfo.city}, {storeInfo.state}</Col>
                </Row>
              ))
            : "NO CURRENT STORES"}
        </Container>
        <hr/>
        <br/>
        Store Code:
        <Input
                      type="text"
                      id="storeCode"
                      columns="10"
                      onChange={this.updateField.bind(this)}
                    ></Input>
        <br/>
        Store Name:
        <Input
                      type="text"
                      id="name"
                      cols="10"
                      onChange={this.updateField.bind(this)}
                    ></Input>
        <br/>
        City:
        <Input
                      type="text"
                      id="city"
                      cols="10"
                      onChange={this.updateField.bind(this)}
                    ></Input>
        <br/>
        State:
        <Input
                      type="text"
                      id="state"
                      cols="10"
                      onChange={this.updateField.bind(this)}
                    ></Input>
        <br/>
        <Button onClick={this.updateStore}>Add / Update Store Data</Button>
        <hr/>
        Delete a Store by Code:
        <Input
                      type="text"
                      id="delStoreCode"
                      cols="10"
                      onChange={this.updateField.bind(this)}
                    ></Input>
        <br/>
        <Button onClick={this.deleteStore}>Remove Store</Button>

      </Fragment>
       : "Not allowed to view stores data"}

        </Fragment>
)
}
}
export default Stores;

This code calls the get, post or delete APIs and automatically incudes the appropriate authentication header for the logged in user. Any API call will fail unless the user is a member of the storesadmin group.

Next, replace the src\components\header.jsx file with the following contents and save:

import React from "react";

import {
  Container,
  Row,
  Col,
  Button,
  Navbar,
  Nav,
  NavbarBrand,
  NavLink,
  NavItem,
  UncontrolledDropdown,
  DropdownToggle,
  DropdownMenu,
  DropdownItem,
} from "reactstrap";

const Header = (props) => {
  const currentUser = props.userName;
  return (
    <header>
      <Navbar
        fixed="top"
        color="light"
        light
        expand="xs"
        className="border-bottom border-gray bg-white"
        style={{ height: 80 }}
      >
        <Container>
          <Row noGutters className="position-relative w-100 align-items-center">
            <Col className="d-none d-lg-flex justify-content-start">
              <Nav className="mrx-auto" navbar>
                {currentUser ? (
                  <NavItem className="d-flex align-items-center">
                    <NavLink className="font-weight-bold" href="/">
                      Welcome, {currentUser}!
                    </NavLink>
                  </NavItem>
                ) : null}

                <NavItem className="d-flex align-items-center">
                  <NavLink className="font-weight-bold" href="/">
                    Home
                  </NavLink>
                </NavItem>

                <NavItem
                  className="d-flex align-items-center"
                  onClick={() => props.onHandleOrder()}
                >
                  <NavLink className="font-weight-bold btn">Menu</NavLink>
                </NavItem>

                <UncontrolledDropdown
                  className="d-flex align-items-center"
                  nav
                  inNavbar
                >
                  <DropdownToggle className="font-weight-bold" nav caret>
                    Account
                  </DropdownToggle>
                  <DropdownMenu right>
                    <DropdownItem
                      className="font-weight-bold text-secondary text-uppercase"
                      header
                      disabled
                    >
                      My Account
                    </DropdownItem>
                    <DropdownItem divider />
                    {currentUser ? <DropdownItem>Profile</DropdownItem> : null}
                    {currentUser ? (
                      <DropdownItem onClick={() => props.onHandleStores()}>
                        Store Admin
                      </DropdownItem>
                    ) : null}
                    {currentUser ? (
                      <DropdownItem onClick={() => props.onHandleHistory()}>
                        Order History
                      </DropdownItem>
                    ) : null}
                    {currentUser ? (
                      <DropdownItem
                        onClick={() =>
                          props.onHandleLogout ? props.onHandleLogout() : null
                        }
                      >
                        Logout
                      </DropdownItem>
                    ) : (
                      <DropdownItem onClick={() => props.onHandleLogin()}>
                        Login
                      </DropdownItem>
                    )}
                  </DropdownMenu>
                </UncontrolledDropdown>
              </Nav>
            </Col>

            <Col className="d-flex justify-content-xs-start justify-content-lg-center">
              <NavbarBrand
                className="d-inline-block p-0"
                href="/"
                style={{ width: 80 }}
              >
                <img
                  src="https://jah-lex-workshop-2018.s3.amazonaws.com/mob302/images/0001.png"
                  alt="logo"
                  className="position-relative img-fluid"
                />
              </NavbarBrand>
            </Col>

            <Col className="d-none d-lg-flex justify-content-end">
              <Button
                className="info"
                onClick={() =>
                  props.onHandleReview ? props.onHandleReview() : null
                }
              >
                Tell us how we're doing
              </Button>
            </Col>
          </Row>
        </Container>
      </Navbar>
    </header>
  );
};
export default Header;

And finally, update the contents of src\App.js with the following and save.

import React, { Fragment, Component } from "react";
import "./App.css";
import { Container, Row, Col } from "reactstrap";
import Header from "./components/header";
import SideCard from "./components/sideCard";
import Stores from "./components/stores";
import Amplify, { Auth, Hub } from "aws-amplify";
import { AmplifyAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";
import awsconfig from "./aws-exports";

Amplify.configure(awsconfig);

const signUpConfig = {
  header: "Welcome!",
  signUpFields: [
    {
      label: "First Name",
      key: "given_name",
      placeholder: "First Name",
      required: true,
      displayOrder: 5,
    },
    {
      label: "Last Name",
      key: "family_name",
      placeholder: "Last Name",
      required: true,
      displayOrder: 6,
    },
    {
      label: "Address",
      key: "address",
      placeholder: "Address",
      required: true,
      displayOrder: 7,
    },
  ],
};

class App extends Component {
  state = {
    showType: "",
    loggedIn: false,
    currentUser: null,
  };

  loadCurrentUser() {
    Auth.currentAuthenticatedUser().then((userInfo) => {
      this.setState({
        loggedIn: true,
        currentUser: userInfo.username,
        currentUserData: userInfo,
      });
    });
  }
  componentDidMount = () => {
    Hub.listen("auth", ({ payload: { event, data } }) => {
      switch (event) {
        case "signIn":
          this.setState({
            currentUser: data.username,
            currentUserData: data,
            loggedIn: true,
          });
          break;
        case "signOut":
          this.setState({
            currentUser: null,
            loggedIn: false,
          });
          break;
        default:
          break;
      }
    });
    this.loadCurrentUser();
  };

  handleLogin = () => {
    this.setState({
      showType: "login",
    });
  };

  handleLogout = () => {
    this.setState({
      showType: "login",
    });
  };

  handleStores = () => {
    this.setState({
      showType: "stores",
    });
  };

  render() {
    return (
      <Fragment>
        <Header
          onHandleLogin={this.handleLogin}
          onHandleLogout={this.handleLogout}
          onHandleStores={this.handleStores}
          loggedIn={this.state.loggedIn}
          userName={this.state.currentUser}
        />
        <div className="my-5 py-5">
          <Container className="px-0">
            <Row
              noGutters
              className="pt-2 pt-md-5 w-100 px-4 px-xl-0 position-relative"
            >
              <Col
                xs={{ order: 2 }}
                md={{ size: 4, order: 1 }}
                tag="aside"
                className="pb-5 mb-5 pb-md-0 mb-md-0 mx-auto mx-md-0"
              >
                <SideCard />
              </Col>

              <Col
                xs={{ order: 1 }}
                md={{ size: 7, offset: 1 }}
                tag="section"
                className="py-5 mb-5 py-md-0 mb-md-0"
              >
                {this.state.showType === "" ? "This is the main content" : null}
                {this.state.showType === "stores" ? <Stores /> : null}
                {this.state.showType === "login" ? (
                  <AmplifyAuthenticator signUpConfig={signUpConfig}>
                    <div>
                      You are logged in.
                      <AmplifySignOut />
                    </div>
                  </AmplifyAuthenticator>
                ) : null}
              </Col>
            </Row>
          </Container>
        </div>
      </Fragment>
    );
  }
}

export default App;

The code is intended to only allow members of the Cognito group storeadmins to manage stores. So before we test this, we will add the previously registered user to the storeadmins group using Amazon Cognito:

  1. Navigate to the Amazon Cognito console.
  2. Click Manage User Pools
  3. Select the User Pool (should only be one)
  4. Select Users and Groups and then the Groups tab. You should see the storeadmins group. Create Environment
  5. Click on the group name.
  6. Click Add Users
  7. Click the + sign next to the user you want to add. The user is now able to administrate the stores! Create Environment

Now, ensure your application is running (use npm start if its not), and then login to your web application. Once authenticated, select Store Admin from the Account menu:

Create Environment

You should now see a list of the initial store that you loaded into the DynamoDB table.

Create Environment

Test out adding new stores to get a bigger list!

Create Environment

And then remove some stores by entering in a valid store code and clicking Remove Store

Create Environment

As a final test, we want to ensure authenticated user who are not members of storeadmins cannot manage the store information. Do this by:

  1. Clicking Logout from the Account menu.
  2. Click Sign Out Create Environment
  3. Click Create Account and go through registration of a new user. (Remember, you can use the same email address, just not the same username).
  4. Enter the confirmation code that you receive in email.
  5. You should now be logged in as the new user (who is not a member of storeadmins)
  6. Select Store Admin from the Account menu.
  7. Verify that you receive the message: Not allowed to view stores data Create Environment

At this point, we have completed this section. However, let’s update our hosted website by running the following command: (If the web server is currently running, press CTRL-C to stop the current running server.)

amplify publish -c

Once complete, click on the CloudFront endpoint and verify the site functions as expected. Congratulations!