Skip to main contentIBM Cloud-Native

Inventory UI

Develop and deploy the UI component of the inventory application

Setup

Get the initial project created and register the pipeline for automated builds. Detailed instructions for each of these steps can be found in the Deploying an App guide.

  • Create a new repository from the React UI Patterns Code Pattern into your Git org - https://github.com/IBM/template-node-react/generate

    In order to prevent naming collisions, name the repository inventory-management-ui-{your initials} replacing {your initials} with your actual initials.

  • Clone the new repository to your machine

  • Run npm install to install the project dependencies

  • Go into the repository directory cloned and execute the following

    oc sync dev-{your initials} --dev
  • Log into the cluster from the command-line then register the pipeline

    oc pipeline --tekton

    replacing {your initials} with your actual initials

Create the initial components

The React Code Pattern comes with a set UI components that implement 12 common UI Design Patterns. In the initial UI, all of the components are included to create an interactive example of how they work. The first step of building an application with the React Code Pattern is to remove those components from the menu and to create new components built from the pattern components.

Based on the requirements of this first use case, we will create a StockItemList component based on the TableList pattern.

  • Open a terminal and start the application in development mode to see the initial UI and the changes as we make them
npm run start:dev
  • Access the running service. This service runs on port 3000.

  • Make a copy of the TableList pattern component from the pattern-component directory into the components folder. Rename the file and the class inside to StockItemList.

client/src/components/StockItemList.jsx
import React, { Component } from "react";
import {
StructuredListWrapper,
StructuredListRow,
StructuredListCell,
StructuredListHead,
StructuredListBody,
StructuredListInput,
Icon
  • Update UIShell.jsx

    • Update the header variable to whatever name you want. “Big Blue Widgets” is used in the example
    • Update the menuTitle to “Inventory Management”
    • Remove all the values from menuItems
client/src/components/UIShell.jsx
class UIShell extends Component {
header = "Big Blue Widgets";
menuTitle = "Inventory Management";
menuItems = [
"Stock Items",
];
...
}
  • Update UIShellBody.jsx

    • Remove all of the pattern values from the components map and add one entry for Stock Item List. The value on the left is the label that is displayed and the value on the right is the class that should be loaded (e.g. "Stock Items": StockItemList). Note: The label value needs to match the one used on UIShell
    • Update the Use Stock Items as the default pattern name when none is given
client/src/components/UIShellBody.jsx
import React, {Component} from "react";
import "../pattern-components/patterns.scss";
import StockItemList from "./StockItemList";
class UIShellBody extends Component {
components = {
"Stock Items": StockItemList
};
  • With the application running in the first terminal, open a second terminal in the repository directory and push the changes we’ve made to the repository
git add .
git commit -m "Initial shell components"
git push
  • Refresh the browser from earlier (or follow the steps from before to use oc endpoints to open the browser). The changes we just made should be reflected in the UI.

Update StockItemList contents

Now that we’ve created the initial components, we can start to customize the StockItemList to match the data for our application.

  • Start the application in development mode (if not already running) with npm run start:dev

  • Update the title and subtitle with values for our Stock Items view.

  • Update the columns and data fields with the list of columns and sample data to match the UI. Set the formatters to {} for now.

    The value in the columns array maps to one of the attributes in our data values (e.g. name refers to the name attribute)

  • The result of these changes should look like the following:

client/src/components/StockItemList.jsx
class StockItemList extends Component {
title = 'Stock Items';
subtitle = 'This is the current inventory of items';
columns = [
"name",
"description",
"stock",
"unitPrice",
  • View the new data in your local UI: http://localhost:3000/

  • Push the changes we’ve made to the repository

git add .
git commit -m "Updates the StockItemsList view"
git push
  • Look at the Jenkins pipeline and the deployed app

Add a service component to get mock Stock Items

So far, we’ve built a UI that displays a hard-coded set of data in a table. Eventually, we want to display dynamic data provided from a database in the table. As a first step towards that goal, we need to separate the UI logic from the logic that retrieves the data. We will do that with a service component. For this first pass the service component will just return mock data.

  • Create a directory called services under the client/src folder

  • Create a file named stock-item-mock.service.js in the service directory. Our StockItem service component will have a single asynchronous function called listStockItems() that returns a list of StockItems.

client/src/services/stock-item-mock.service.js
export class StockItemMockService {
async listStockItems() {
return [];
}
}
  • Implement the service by copying the data array from StockItemList and returning it in the function. You can add a call to timer() to simulate wait time
client/src/services/stock-item-mock.service.js
import timer from '../util/timer';
export class StockItemMockService {
async listStockItems() {
// wait 1 second before returning data
await timer(1000);
return [
{
  • Update the components to pass the service in the properties
client/src/App.test.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {StockItemMockService} from "./services/stock-item-mock.service";
describe('App', () => {
test('canary verifies test infrastructure', () => {
expect(true).toEqual(true);
});
client/src/App.jsx
import React, { Component } from "react";
import UIShell from "./components/UIShell";
import "./App.scss";
import {StockItemMockService} from "./services/stock-item-mock.service";
class App extends Component {
constructor(props) {
super(props);
client/src/components/UIShell.jsx
...
class UIShell extends Component {
...
render() {
return (
<div>
<Header aria-label="IBM Platform Name">
client/src/components/UIShellBody.jsx
...
class UIShellBody extends Component {
components = {
"Stock Items": StockItemList
};
defaultComponent = "Stock Items";
render() {
  • Update StockItemList to use the provided service
src/components/StockItemList.jsx
...
class StockItemList extends Component {
...
constructor(props) {
super(props);
this.state = {
data: [],
  • Update the render UI function in StockItemList to display the values in UI
client/src/components/StockItemList.jsx
renderRow = (row, id) => {
return (
<StructuredListRow key={id} onClick={() => this.onRowClick(id)}>
<div>
<StructuredListInput
id={`row-${id}`}
value="row-0"
title="row-0"
name="row-0"
client/src/components/StockItemList.jsx
render() {
const data = this.state.data;
return (
<div className="bx--grid pattern-container">
<Header
title={this.title}
subtitle={this.subtitle}
  • View the new data in your local UI: http://localhost:3000/

  • Push the changes we’ve made to the repository

git add .
git commit -m "Adds a mock service"
git push
  • Look at the Jenkins pipeline and the deployed app

Add a service that calls the BFF

Now that we have a mock service that injects data, we can build an implementation of the service that calls our BFF. For the service, we will use a package called superagent to make the calls to the BFF.

  • With npm, install the superagent and @types/superagent dependencies
npm i -s superagent
npm i -D @types/superagent
  • Create a service implementation in the services directory called stock-item.service.js
client/src/services/stock-item.service.js
export class StockItemService {
async listStockItems() {
return [];
}
}
  • Add an implementation of listStockItems() that calls the BFF through the /api proxy
client/src/services/stock-item.service.js
import * as superagent from 'superagent';
export class StockItemService {
constructor(baseUrl) {
this.baseUrl = baseUrl || '/api';
}
async listStockItems() {
return superagent

Note: In dev mode, the proxy is configured in client/package.json. When running with the express server, the proxy is configured in server/routers/api.js. By default, the value points to localhost:3001.

  • Update App.jsx to use the new service instead of the mock service.
client/src/App.jsx
import React, { Component } from "react";
import UIShell from "./components/UIShell";
import "./App.scss";
import {StockItemService} from "./services/stock-item.service";
class App extends Component {
constructor(props) {
super(props);
  • Modify connectsTo property to the values.yaml file of the Helm chart. The value of the property should match the Kubernetes service of the microservice. (For Code Pattern projects, the service name is the same as the name of the application which is that same as the name of the repository.)
chart/base/values.yaml
...
connectsTo: inventory-management-bff-{your initials}
...
  • Add a new environment variable named API_HOST to the list of existing environment variables in deployment.yaml. The value of this environment variable should come from the connectsTo value we defined. You can add | quote to wrap the value in quotes in case the value is not formatted correctly.
chart/base/templates/deployment.yaml
...
env:
- name: INGRESS_HOST
value: ""
- name: PROTOCOLS
value: ""
- name: LOG_LEVEL
value: {{ .Values.logLevel | quote }}
- name: API_HOST
  • Push the changes we’ve made to the repository
git add .
git commit -m "Updates the StockItemsList view"
git push

Summary

You have now completed the Micro App Guide demonstrating the Inventory solution.