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:


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:

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

Test out adding new stores to get a bigger list!

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

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


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!