What youβll learn: React components, JSX, hooks (useState, useEffect, useCallback), Redux state management β everything needed to understand EPICFrontend code.
Prerequisite: Read Guide 01 (JavaScript Basics) first.
Section 1 β What Is React?
React is a JavaScript library for building user interfaces. Instead of directly manipulating HTML, you write components β reusable pieces of UI.
Without React (old way):
document.getElementById('myDiv').innerHTML = '<p>Hello ' + userName + '</p>';
document.getElementById('myDiv').style.color = 'red';
// messy, error-prone, hard to maintain
With React:
function Greeting({ userName }) {
return <p style={{ color: 'red' }}>Hello {userName}</p>;
}
// clean, reusable, easy to understand
In EPIC, the entire web interface that service owners use to manage peak capacity is built in React.
Section 2 β JSX: HTML Inside JavaScript
JSX looks like HTML but itβs actually JavaScript. React converts it behind the scenes.
// This JSX:
const element = <h1>EPIC Dashboard</h1>;
// Is actually compiled to:
const element = React.createElement('h1', null, 'EPIC Dashboard');
Rules of JSX:
// 1. Always close tags (even self-closing)
<input /> // correct (note the /)
<input> // WRONG
// 2. Use className instead of class (class is a reserved word in JS)
<div className="dashboard">...</div>
// 3. JavaScript expressions use { }
const eventName = "PrimeDay2024";
<h1>Event: {eventName}</h1> // renders: Event: PrimeDay2024
<h1>Event: {2 + 2}</h1> // renders: Event: 4
// 4. Must return one root element
// WRONG:
return (
<h1>Title</h1>
<p>Paragraph</p>
);
// CORRECT: wrap in one parent
return (
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
);
Section 3 β Components: Building Blocks
A component is just a function that returns JSX. Each page and reusable element in EPIC is a component.
// Simple component
function EventCard({ eventId, eventName }) {
return (
<div>
<h2>{eventName}</h2>
<p>Event ID: {eventId}</p>
</div>
);
}
// Using it:
<EventCard eventId="PD2024" eventName="Prime Day 2024" />
Props (properties) are how you pass data INTO a component:
// Props are like function arguments
function FleetRow({ fleetId, peakTpm, status }) {
return (
<tr>
<td>{fleetId}</td>
<td>{peakTpm}</td>
<td>{status}</td>
</tr>
);
}
// Passing props:
<FleetRow fleetId="RIPE-NA" peakTpm={50000} status="Ready" />
Section 4 β State: When UI Needs to Change
Props are read-only. When you need data that can change (loading state, fetched data, user input), use state.
import React, { useState } from 'react';
function HotwDashboard() {
// useState(initialValue) returns [currentValue, setterFunction]
const [isLoading, setIsLoading] = useState(true);
const [hotwData, setHotwData] = useState([]);
const [error, setError] = useState(null);
return (
<div>
{isLoading && <p>Loading...</p>}
{!isLoading && hotwData.map(item => (
<FleetRow key={item.FleetId} data={item} />
))}
</div>
);
}
Rule: When state changes, React automatically re-renders the component with new data. This is the magic of React.
Real EPIC example from hotwDashboard.jsx:
const HOTWDashboard = (props) => {
const [hotwData, setHotwData] = useState([]); // list of fleet data
const [isLoading, setIsLoading] = useState(true); // show spinner
const [alias, setAlias] = useState( // user alias for filtering
new URLSearchParams(props.location.search).get('alias')
);
// When data is fetched:
setHotwData(fetchedData); // updates state β React re-renders
setIsLoading(false); // hide spinner
};
Section 5 β useEffect: Running Code at the Right Time
useEffect runs code after the component renders. Used for: API calls, subscriptions, side effects.
import { useState, useEffect } from 'react';
function ServiceDetails({ serviceName }) {
const [service, setService] = useState(null);
useEffect(() => {
// This runs after component first renders
// (and whenever serviceName changes, because it's in the dependency array)
backend_api.getServiceDetails(serviceName)
.then(response => {
setService(response.data);
});
}, [serviceName]); // β dependency array: re-run when serviceName changes
// [] = run only once when component first loads
// no array = run every render (usually wrong)
if (!service) return <p>Loading...</p>;
return <div>{service.ServiceId}</div>;
}
Real EPIC example from hotwDashboard.jsx:
useEffect(() => {
if (!alias) {
let loggedInAlias = UtilHelpers.getUserNameFromHarmony();
setAlias(loggedInAlias);
}
}, [eventId, alias, props.history, purpose]);
// Re-runs whenever eventId, alias, props.history, or purpose changes
Section 6 β useCallback: Avoiding Unnecessary Re-renders
useCallback memoizes a function β it doesnβt recreate the function on every render unless its dependencies change:
const getTableItems = useCallback(() => {
// This function is "remembered" and only recreated
// when its dependencies change
const buildCurrentDetails = async (fleetDetails, serviceDetails) => {
// build the table row data...
};
// fetch data and call buildCurrentDetails
}, [eventId, alias]); // only recreate when eventId or alias changes
Why use it? Performance. Without it, a new function is created on every render, causing child components to unnecessarily re-render.
Section 7 β The EPIC App Structure (Epic.js)
The main EPIC app is in Epic.js. Itβs the root component that wraps everything:
// Epic.js β simplified structure
function Epic() {
const [navigationOpen, setNavigationOpen] = React.useState(false);
const pageName = useSelector((state) => state.pageReducer.value); // from Redux
const event = useSelector((state) => state.eventObjectReducer.value);
return (
<div>
{/* Sticky header */}
<div id="containerHead" style={{ position: 'sticky', top: 0}}>
<TopFlashbar/> {/* Announcement banner */}
<AnnouncementFlashbar/> {/* Announcements */}
<TopNavigationEpic/> {/* Top nav bar */}
</div>
{/* Main layout */}
<AppLayout
navigation={<SideNav />} {/* Left sidebar */}
content={<MainContainer />} {/* Main content area */}
tools={<TutorialPanel />} {/* Right panel */}
/>
{/* Sticky footer */}
<div id="containerFoot">Have questions? Create a SIM here</div>
</div>
);
}
Key components used:
AppLayoutβ from Amazonβs internal Polaris UI library (@amzn/awsui-components-react)SideNavβ the left navigation menuMainContainerβ renders whatever page the user is onTopFlashbarβ announcement messages at the top
Section 8 β Redux: Global State Management
The Problem: In a large app, many components need the same data. Passing props down many levels (called βprop drillingβ) becomes unmanageable.
The Solution: Redux β a single global βstoreβ that any component can read from or write to.
Without Redux:
App β Page β Section β Table β Row β Cell (needs serviceName)
(pass props at each level) β messy!
With Redux:
Redux Store β any component can directly read serviceName
The EPIC Redux Store
// store/store.js
const store = configureStore({
reducer: {
pageReducer: dataReducer, // current page name
tutorialActiveIdReducer: tutIdReducer, // tutorial state
isTutorialActiveReducer: isTutActiveReducer,
toolsOpenReducer: toolsOpenReducer, // is tools panel open?
serviceNameReducer: serviceNameReducer, // selected service
regionNameReducer: regionNameReducer, // selected region
eventObjectReducer: eventObjectReducer, // current event
serviceObjectReducer: serviceObjectReducer, // current service
fleetObjectReducer: fleetObjectReducer // current fleet
},
});
8 pieces of global state:
pageReducerβ which page is currently showntutorialActiveIdReducerβ which tutorial is activeisTutorialActiveReducerβ is a tutorial runningtoolsOpenReducerβ is the right tools panel open/closedserviceNameReducerβ name of currently selected serviceregionNameReducerβ currently selected regioneventObjectReducerβ the full event object currently being viewedserviceObjectReducerβ the full service objectfleetObjectReducerβ the full fleet object
Reading From Redux Store
import { useSelector } from 'react-redux';
function MyComponent() {
// Read from global store:
const pageName = useSelector((state) => state.pageReducer.value);
const serviceName = useSelector((state) => state.serviceNameReducer.value);
const event = useSelector((state) => state.eventObjectReducer.value);
return <div>Current page: {pageName}</div>;
}
Writing To Redux Store (Dispatch)
import { useDispatch } from 'react-redux';
import { setActiveTutorialId } from './store/tutorialActiveIdSlice';
function TutorialButton() {
const dispatch = useDispatch();
return (
<button onClick={() => {
dispatch(setActiveTutorialId(5)); // updates global state
}}>
Start Tutorial 5
</button>
);
}
A Redux Slice (Example)
Each piece of state is a βsliceβ. Hereβs what a slice looks like:
// store/pageNameSlice.js
import { createSlice } from '@reduxjs/toolkit';
const pageNameSlice = createSlice({
name: 'page',
initialState: { value: '' }, // initial value
reducers: {
setPageName: (state, action) => {
state.value = action.payload; // update the value
}
}
});
export const { setPageName } = pageNameSlice.actions;
export default pageNameSlice.reducer;
Section 9 β Routing in EPIC
EPIC has many pages. React Router decides which page to show based on the URL:
| URL | Component shown |
|---|---|
/events |
events.jsx β list of all events |
/service/:serviceName |
service.jsx β service details |
/hotw/:eventName |
hotwDashboard.jsx β HOTW dashboard |
/serviceDetails/:serviceName/:fleetId/:eventId |
serviceDetails.jsx |
/hotwAsgRunDetails/:... |
hotwAsgRunDetails.jsx |
Props from URL:
// In hotwDashboard.jsx
const HOTWDashboard = (props) => {
let eventId = props.match.params.eventName;
// If URL is /hotw/PD2024, then eventId = "PD2024"
};
Section 10 β The Polaris UI Library
Amazon uses an internal UI component library called Polaris (@amzn/awsui-components-react). EPIC uses it for all UI elements:
import { Table, Button, Modal, Spinner, Alert } from '@amzn/awsui-components-react/polaris';
// Pre-built table component
<Table
columnDefinitions={[
{ header: 'Fleet ID', cell: item => item.FleetId },
{ header: 'Peak TPM', cell: item => item.PeakTPM },
]}
items={hotwData}
loading={isLoading}
loadingText="Loading HOTW data..."
/>
// Pre-built modal dialog
<Modal
visible={isModalOpen}
header="Place Hardware Order"
onDismiss={() => setIsModalOpen(false)}
>
<p>Are you sure you want to order {hostsNeeded} hosts?</p>
<Button onClick={confirmOrder}>Confirm</Button>
</Modal>
You donβt need to learn Polaris from scratch β just know that all those UI components (tables, buttons, modals, flashbars) come from this library.
Section 11 β Making API Calls (backend_api.js)
The frontend communicates with EPICBackend through the backend_api.js file:
// common/backend_api.js
let backend_api = {
async invokeBackendApi(pathParams, pathTemplate, method, additionalParams, body) {
const apigClient = await getApigClient(
AWS_CONFIG_CONSTANTS.BACKEND_API_URL,
AWS_CONFIG_CONSTANTS.BACKEND_API_HARMONY_ROLE
);
return apigClient.invokeApi(pathParams, pathTemplate, method, additionalParams, body);
},
// Get service details
getServiceDetails(serviceName) {
return this.invokeBackendApi({}, '/service/' + serviceName, 'GET', {...}, {});
},
// Get fleet details for a specific fleet + event
getFleetDetails(fleetId, eventId) {
return this.invokeBackendApi({}, '/fleet/' + fleetId + '/' + eventId, 'GET', {...}, {});
},
// Get HOTW dashboard data
getHotwDashboardDetails(eventId, purpose, alias) {
return this.invokeBackendApi({}, `/hotw/dashboard/${eventId}/${purpose}`, 'GET', {...}, {});
}
};
Using it in a component:
useEffect(() => {
backend_api.getFleetDetails(fleetId, eventId)
.then(response => {
setFleetData(response.data);
setIsLoading(false);
})
.catch(error => {
console.error("Failed to load fleet:", error);
setError(error);
});
}, [fleetId, eventId]);
Section 12 β EPIC Pages: Quick Reference
Every page in /pages/ folder corresponds to a screen users see:
| Page File | URL | What It Shows |
|---|---|---|
events.jsx |
/events | List of all peak events (PD2024, BF2024, etc.) |
service.jsx |
/service/:name | Service overview, list of fleets |
serviceDetails.jsx |
/serviceDetails/β¦ | Deep dive into one fleet for one event |
serviceReadinessDashboard.jsx |
/readiness/:event | Are all fleets ready for the event? |
hotwDashboard.jsx |
/hotw/:event | Hardware order status for all fleets |
hotwRunHistory.jsx |
/hotwRunHistory/β¦ | History of HOTW automation runs |
hotwAsgRunDetails.jsx |
/hotwAsgRunDetails/β¦ | Per-ASG details for a HOTW run |
fleetConfigurations.jsx |
/fleetConfig/β¦ | ASG configuration for a fleet |
serviceThrottling.jsx |
/throttling/β¦ | Gizmo throttling settings |
serviceOnboarding.jsx |
/onboarding | Onboard a new service to EPIC |
createEvent.jsx |
/createEvent | Create a new peak event |
dashboards.jsx |
/dashboard | Main dashboard overview |
actionDashboard.jsx |
/actions | Pending actions for the user |
bauServiceOwnerDashboard.jsx |
/bauDashboard | BAU (non-peak) dashboard |
descaleFleetConfigurations.jsx |
/descale/β¦ | Descaling configuration |
Section 13 β Component Hierarchy in EPIC
Understanding how components nest:
Epic.js (root)
βββ TopFlashbar.jsx (announcements)
βββ AnnouncementFlashbar.jsx
βββ EmergentAnnouncementFlashbar.jsx
βββ TopNavigationEpic.jsx (top nav bar)
βββ AppLayout
βββ SideNav.jsx (left sidebar)
βββ MainContainer.jsx (main content β changes per page)
βββ events.jsx
β βββ eventsTable.jsx
β βββ ... table rows
βββ hotwDashboard.jsx
β βββ HotwReadinessTable.jsx
β βββ ... per-fleet rows
β βββ atomicHotwModal.jsx (opens on button click)
βββ serviceDetails.jsx
βββ ServiceDetailsHeader.jsx
βββ Tabs/
β βββ hostProjectionTab.jsx
β βββ upstreamProjectionsTab.jsx
β βββ downstreamProjectionsTab.jsx
β βββ fleetMilestoneReadinessTab.jsx
βββ many modals (submitModalFor*.jsx)
Section 14 β Tables in EPIC Frontend
Most of EPICβs UI is tables. Understanding table components:
// components/tables/hotwReadinessTable.jsx
// Renders the main HOTW dashboard table
// Components that exist:
// - eventsTable.jsx β shows all events
// - fleetsTable.jsx β shows all fleets for a service
// - hotwReadinessTable.jsx β HOTW status per fleet
// - hotwRunHistoryTable.jsx β history of HOTW runs
// - milestonesTable.jsx β milestone progress per fleet
// - hostProjectionsTable.jsx β projected host counts
// - serviceReadinessTable.jsx β readiness status per fleet
// - throttlingTable.jsx β Gizmo throttling config
// - preferredAsgTable.jsx β preferred ASGs for HOTW
Section 15 β Modals in EPIC Frontend
Modals are popups for user actions. EPIC has 30+ modal files. Pattern:
// Generic pattern for any submit modal
function SubmitModal({ visible, onDismiss, onConfirm, data }) {
return (
<Modal
visible={visible}
onDismiss={onDismiss}
header="Confirm Action"
footer={
<div>
<Button onClick={onDismiss}>Cancel</Button>
<Button variant="primary" onClick={onConfirm}>Confirm</Button>
</div>
}
>
<p>Are you sure? {data.message}</p>
</Modal>
);
}
Important modals in EPIC:
atomicHotwModal.jsxβ manually trigger HOTW for one fleetorderHostsModal.jsxβ place a hardware ordersubmitModalForOverridingInputTPM.jsxβ override TPM projectionsubmitModalForApproval.jsxβ submit for approval workflowescalateTicketModal.jsxβ escalate a SIM ticket
Section 16 β Key Patterns Quick Reference
// 1. Fetch data on load
useEffect(() => {
setIsLoading(true);
backend_api.getSomething(id)
.then(response => { setData(response.data); setIsLoading(false); })
.catch(err => { setError(err); setIsLoading(false); });
}, [id]); // re-fetch when id changes
// 2. Read global state (Redux)
const serviceName = useSelector(state => state.serviceNameReducer.value);
const event = useSelector(state => state.eventObjectReducer.value);
// 3. Update global state (Redux)
const dispatch = useDispatch();
dispatch(setServiceName("FORTRESSService"));
// 4. Conditional rendering
{isLoading && <Spinner />}
{!isLoading && data && <DataTable items={data} />}
{error && <Alert type="error">{error.message}</Alert>}
// 5. List rendering (always need key!)
{fleets.map(fleet => (
<FleetRow key={fleet.FleetId} fleet={fleet} />
))}
// 6. Event handling
<Button onClick={() => setModalVisible(true)}>Open Modal</Button>
<Input onChange={({ detail }) => setInputValue(detail.value)} />