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 menu
  • MainContainer β€” renders whatever page the user is on
  • TopFlashbar β€” 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:

  1. pageReducer β€” which page is currently shown
  2. tutorialActiveIdReducer β€” which tutorial is active
  3. isTutorialActiveReducer β€” is a tutorial running
  4. toolsOpenReducer β€” is the right tools panel open/closed
  5. serviceNameReducer β€” name of currently selected service
  6. regionNameReducer β€” currently selected region
  7. eventObjectReducer β€” the full event object currently being viewed
  8. serviceObjectReducer β€” the full service object
  9. fleetObjectReducer β€” 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 fleet
  • orderHostsModal.jsx β€” place a hardware order
  • submitModalForOverridingInputTPM.jsx β€” override TPM projection
  • submitModalForApproval.jsx β€” submit for approval workflow
  • escalateTicketModal.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)} />