Build an inventory management app with Azure Static Web Apps with + React, part 2
This is part of #30DaysOfSWA.
In this part, we will keep working on our inventory management system authored in React.
Recap, from part I
What we have so far is a React we scaffolded in Snowpack. We've also managed to deploy it to Azure using Azure Static Web Apps service.
What now - let's build the actual app
Ok, we have a React app but not really an inventory management system at this point. So, what do we need?
- A header
- A menu
- A main area
- Static data
Select styling approach
I like to start with the CSS, for this I will use styled-components library, it just resonates with my way of thinking. Here's what CSS can look like expressed as a styled component:
const Menu = styled.ul`
list-style: none;
padding: 0;
`;
Instead of a ul
element, you can now type Menu
and use that in your JSX.
Routing
We need routing, to support the fact that we will use a menu with links that will show us different parts of our app.
For this we will use React Router.
- Add these lines to your package.json:
"react-router": "6.3.0",
"react-router-dom": "6.3.0",
Next, let's install these:
- Run
npm install
:
npm install
Now you will have the libraries needed to add routing to your app.
Setup routing
You need to instruct your app at high-level to use routing. We therefore start with index.jsx.
-
Change index.jsx ensure it looks like so:
import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; import "./index.css"; import App from "./App"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode>, document.getElementById("root") );
Above, we use BrowserRouter
and encapsulates the App
component.
-
Change the
App
component inApp.jsx
to look like so:
export default function App() { return ( <div> <Routes> <Route path="/" element={<Layout />}> <Route index element={<Home />} /> <Route path="about" element={<About />} /> <Route path="products/:id" element={<Product />} /> <Route path="products" element={<ProductList />} /> <Route path="dashboard" element={<Dashboard />} /> <Route path="*" element={<NoMatch />} /> </Route> </Routes> </div> ); }
Above, we have setup the routes and which elements should handle a route request. path
defines the route pattern, and element
is the component that will handle the request.
We haven't created these components yet, but we will :)
We need to create a layout component; it will constitute of our menu and an area where content is loaded as soon as we select a link.
-
Add the following component
Layout
:
function Layout() { return ( <Overview> <Header>Welcome to Inventory management system!</Header> <Navigation> <Menu> <Item> <NavLink to="/" className={({ isActive }) => (isActive ? 'active' : 'inactive')} >Home</NavLink> </Item> <Item> <NavLink to="/about" className={({ isActive }) => (isActive ? 'active' : 'inactive')} >About</NavLink> </Item> <Item> <NavLink to="/products" className={({ isActive }) => (isActive ? 'active' : 'inactive')} >Products</NavLink> </Item> <Item> <NavLink to="/dashboard" className={({ isActive }) => (isActive ? 'active' : 'inactive')} >Dashboard</NavLink> </Item> <Item> <NavLink to="/nothing-here" className={({ isActive }) => (isActive ? 'active' : 'inactive')} >Nothing here</NavLink> </Item> </Menu> </Navigation> <hr /> <Content> <Outlet /> </Content> </Overview> ); }
-
Now add some styling above all components:
const Header = styled.h1` display: block; background: DarkRed; color: Wheat; width: 100%; grid-row: 1; padding: 20px 10px; font-size: 16px; grid-column-start: 1; grid-column-end: 3; margin: 0; `; const Navigation = styled.nav` width: 200px; background: DarkSlateGrey; grid-row:2; grid-column: 1; `; const Content = styled.div` grid-row:2; grid-column: 2; padding: 20px; `; const Overview = styled.div` display: grid; grid-template-rows: 60px 100%; grid-template-columns: 200px 100%; width: 100vw; height: 100vh; `; const Menu = styled.ul` list-style: none; padding: 0; `; const Item = styled.li` margin: 10px 0px; a { padding: 10px 5px; display: block; color: white; text-decoration: none; } `;
We use a grid system for our header, left menu and main content area.
Finally, add some styling to index.css:
li>a.active {
font-weight: bold;
border-right: solid 10px red;
}
This will ensure that a selected menu item has visual indicator when a specific link has been chosen.
Create components
Now, we need to create the components we mentioned when we configured routing in App.jsx.
Create a Pages directory and create the following:
- Create About.jsx with the following content:
import * as React from "react";
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
export default About;
- Create Dashboard.jsx with the following content:
import * as React from "react";
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
export default Dashboard;
- Create Home.jsx with the following content:
import * as React from "react";
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
export default Home;
- Create NoMatch.jsx with the following content:
import * as React from "react";
import { Link } from "react-router-dom";
function NoMatch() {
return (
<div>
<h2>Nothing to see here!</h2>
<p>
<Link to="/">Go to the home page</Link>
</p>
</div>
);
}
export default NoMatch;
- Create Product.jsx with the following content:
import * as React from "react";
import { useParams, Link } from "react-router-dom";
function Product() {
let { id } = useParams();
return (
<React.Fragment>
<div>Product item {id}</div>
<div>
<Link to="/products">Back to product list</Link>
</div>
</React.Fragment>
);
}
export default Product;
- Create ProductList.jsx with the following content:
import * as React from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
const ProductsContainer = styled.div`
display:flex;
width: 100%;
flex-orientation: row;
flex-wrap: wrap;
width: 800px;
`;
const ProductQuantity = styled.div`
font-size: 40px;
grid-row:1;
color: white;
`;
const ProductName = styled.div`
grid-row:2;
color: white;
`;
const ProductItem = styled.div`
padding: 20px 10px;
box-shadow: 0 0 5px grey;
margin: 5px;
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 10%, rgba(0,212,255,1) 100%);
a {
color: white;
}
width: 200px;
height: 100px;
display: grid;
grid-template-rows: 50% 50%;
}
`;
const products = [{
id: 1,
name: "Nuts",
quantity: 30
},
{
id: 2,
name: "Bolts",
quantity: 20
},
{
id: 3,
name: "Screw drivers",
quantity: 5
},
{
id: 4,
name: "Hammers",
quantity: 5
}]
function ProductList() {
const data = products.map(p => <ProductItem>
<ProductQuantity>{p.quantity}</ProductQuantity>
<ProductName>
<Link to={`/products/${p.id}`}>
{p.name}
</Link>
</ProductName>
</ProductItem>);
return (
<ProductsContainer>{data}</ProductsContainer>
);
}
export default ProductList;
Try out the app
At this point, you have built out the app, let's look.
- Run
npm start
:
npm start
Deploy
Our app is already connected to Azure Static Web Apps, and this is the beautiful part, all we need to deploy our changes is to run some git commands.
- Run the following git commands to push your changes to Azure:
git add .
git commit -m "new changes"
git push
- Now go to your repo on GitHub and the Actions, once it finished, in 1-2 minutes, you will see your app deployed on Azure.
Inspect your changes
Go to your app online to ensure changes are there.
A way to find the URL is to go via Visual Studio Code, and the Azure extension > Azure Static Web Apps and right-click your app and select Browse Site.
Try clicking Products in the left menu, now reload the page, you got a 404 right?
This is because of how routing is setup in your app. Don't worry, we can fix this:
- Create staticwebapp.config.json and give it the following content:
"navigationFallback": {
"rewrite": "/index.html"
}
}
What you are saying here is let Azure Static Web Apps handle routes to /Products and /About etc and redirect it to index.html. Your React will now how to deal with these routes from here.
- Add and push your changes:
git add .
git commit -m "adding navigation fallback"
git push
If you verify your deploy this time, it doesn't fail if you navigate to /products and reload, does it? :)
Solution
Have a look at this repo repo
Summary
Congrats, you managed to build a React app and deploy your changes to Azure and using Azure Static Web Apps.
In the third part we will add an API, because right now we have static products list and that's great for prototyping but not for a real app.