Build an inventory management app with Azure Static Web Apps with + React, part 2

Chris Noring - May 11 '22 - - Dev Community

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;
`;
Enter fullscreen mode Exit fullscreen mode

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.

  1. Add these lines to your package.json:
   "react-router": "6.3.0",
   "react-router-dom": "6.3.0", 
Enter fullscreen mode Exit fullscreen mode

Next, let's install these:

  1. Run npm install:
   npm install
Enter fullscreen mode Exit fullscreen mode

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.

  1. 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.

  1. Change the App component in App.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.

  1. 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>
      );
    }
    
  2. 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.

  3. Finally, add some styling to index.css:

   li>a.active {
      font-weight: bold;
      border-right: solid 10px red;
    }
Enter fullscreen mode Exit fullscreen mode

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:

  1. Create About.jsx with the following content:
    import * as React from "react";

    function About() {
      return (
        <div>
          <h2>About</h2>
        </div>
      );
    }

    export default About;
Enter fullscreen mode Exit fullscreen mode
  1. Create Dashboard.jsx with the following content:
    import * as React from "react";

    function Dashboard() {
      return (
        <div>
          <h2>Dashboard</h2>
        </div>
      );
    }

    export default Dashboard;
Enter fullscreen mode Exit fullscreen mode
  1. Create Home.jsx with the following content:
    import * as React from "react";

    function Home() {
      return (
        <div>
          <h2>Home</h2>
        </div>
      );
    }

    export default Home;
Enter fullscreen mode Exit fullscreen mode
  1. 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;
Enter fullscreen mode Exit fullscreen mode
  1. 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;
Enter fullscreen mode Exit fullscreen mode
  1. 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;
Enter fullscreen mode Exit fullscreen mode

Try out the app

At this point, you have built out the app, let's look.

  1. Run npm start:
   npm start
Enter fullscreen mode Exit fullscreen mode

an app with a left menu

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.

  1. Run the following git commands to push your changes to Azure:
   git add .
   git commit -m "new changes"
   git push
Enter fullscreen mode Exit fullscreen mode
  1. 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.

  1. 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.

  2. 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:

  1. Create staticwebapp.config.json and give it the following content:

      "navigationFallback": {
        "rewrite": "/index.html"
      }
    }
Enter fullscreen mode Exit fullscreen mode

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.

  1. Add and push your changes:
   git add .
   git commit -m "adding navigation fallback"
   git push
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player