Skip to main content

Build a Custom Backoffice

Introduction

The platform backoffice is capable of running custom made apps that supports your organization unique needs. There's no limit to the number of these apps except specified by you payment plan or service license. Contact your Administrator to learn more.

info

Backoffice apps are built using React Components.

Audience

The target audience of this tutorial is for frontend and backend developers.It can also be of interest to people learning javascript, web-development and reactjs framework.

Context

Backoffice apps are restricted applications that are available as the name implies at the 'back' to authorized and specific users such as as Administrators, Managers and rightfull owners within an organization, company, team, department, etc.

The nature of backoffice apps are such that they need to be constantly updated, ameliorated and requires constant development of new funcionalities to keep up with demanding needs from the business.

To speed up provisioning and accelerate time to development of theses applications, Enterstarts have developed a react powered, high performance development platform to build a multitude of these applications.

The only requirement of these applications is that the teams developing them have a solid grasp of the React Framework, in order to be able to take full advantage and develop world class backoffice applications.

Top Install Component on App J1N Inventory is a backoffice application that handles inventory, catalog and transaction management for commerce.

React Backoffice SDK (BSDK)

React Backoffice SDK or just Backoffice SDK is a suite or kit of standard, reusable and useful react components aimed at simplifying development of react components inside the Enterstarts Platform. The SDK is opt-in but provides a good foundation to build consistent and efficient components that will power whole funcionalities.

List of components/service available

  • LoadingSpinner
  • JustSpinner
  • ErrorResponse
  • SucessResponse
  • RequestManager
  • RenderSystemComponent
  • LazyLoader
  • RecordField

And many more...

SDK Examples

LoadingSpinner

Here's for instance how you would render a loading spinner

component.js
import {LoadingSpinner} from '@core/components/loading-spinner'

export function MyComponent(props) {
return (
<LoadingSpinner />
);
}

JustSpinner

JustSpinner is just like LoadingSpinner, but it only contains the spinner.

component.js
import {JustSpinner} from '@core/components/just-spinner'

export function MyComponent(props) {
return (
<JustSpinner />
);
}

ErrorResponse

How to work with errors

component.js
import {ErrorResponse} from '@core/components/error-response'

export function MyComponent(props) {
return (
<ErrorResponse error={'Ooops, failed'} />
);
}

RenderSystemComponent

RenderSystemComponent as the name implies, is a special component which work consists solely on rendering another component inside the platform. It's most important attribute is the props which forwards the props to the rendered component .

component.js
import {RenderSystemComponent} from '@core/components/render-system-component';

export function MyComponent(props) {
return (
<RenderSystemComponent
url={"https://..."}
component={"My_Component"}
cache={true|false}
props={{
...
}}
/>
);
}

RecordField

RecordFields allows rendering custom input controls in order to build standard form editors that need to operate on fields, validations, etc.

component/js
import {RecordField} from '@core/components/zrecord/RecordField'

export function MyComponent(props) {
return (
<RecordField
field={{"fieldName":"title","type":"string","label":"title","description":null,"fieldTypeConf":"{}","defaultVal":"","readonly":false,"val":null,"col":"col1","orderX":1,"orderY":0,"hidden":false,"precision":0,"scale":0,"min":null,"max":null,"pattern":"","required":true}}
onUpdate={props.onFieldUpdate}
record={{fields: this.state.fields}}
/>

);
}

LazyLoader

LazyLoader renders the children component only when it becomes visible on the browser. The LazyLoader component is generally used with the RenderSystemComponent. The OtherComp bellow will only be rendered when 15% (0.15) of its size has been "seen" by the user.

component/js
import {LazyLoader} from '@core/components/lazy-loader'

export function MyComponent(props) {
return (
<LazyLoader height={400}
width={"100%"}
threshold={0.15}
onContentVisible={() => {console.log('Loaded Render System Component!')}}>
<OtherComp />
</LazyLoader>
);
}

Putting it all together

Lets build a demo to showcase how to use this SDK.

First head to Developer Cloud - Component System and open a module to create the component.

Show Petstore module

In this picture, we're using a PetStore module created beforehand, but the module can be of your own choosing. Please head to component-template docs to learn more about component modules.

Click the New Template button to create a new template. In this example, change Runtime option to NextJs React if isnt already so.

caution

For React based Components the naming rules should obey camel-case, kebab case or pascal case. We recommend using the pascal case, in this sense MyNewComponent , RenderEngine or EcommerceList.

Learn more here

Create the component, here named MyNewComponent Create React Component

Then just open the builder on the component details page. React Component Details

After opening the builder, you can see the system has created a boilerplate component for us React Component Editor

React Component Editor

Lets start customizing a bit React Component Editor

info

The compiler button allows you to verify the correctness of your code by compiling the file and returning all errors/warnings. It's good practice to compile your components before adding them to pages and applications.

Here's the code

import {JustSpinner} from '@core/components/just-spinner'

export class MyNewComponent extends React.Component {
constructor(props) {
super(props);
var state={};
this.state=state;
}

render() {
return (
<div className="row rs-margin"
style={{background:"#fff", height:"200px"}}>
<div className="col-12">
<JustSpinner />
<h1 className="text-bold">I am loading now</h1>
</div>
</div>
);
}
}

If your prefer funcional components, it could just have been:

import {JustSpinner} from '@core/components/just-spinner'

export function MyNewComponent(props) {
return (
<div className="row rs-margin"
style={{background:"#fff", height:"200px"}}>
<div className="col-12">
<JustSpinner />
<h1 className="text-bold">I am loading now</h1>
</div>
</div>
);
}

Hit the refresh button, now it looks like this: React Component Editor

Building a Form Component

Here's a simple but robust template for a form.

Preview Form Component

import {JustSpinner} from '@core/components/just-spinner'
import {SuccessResponse} from '@core/components/success-response'
import {ErrorResponse} from '@core/components/error-response'
import {RecordField} from '@core/components/zrecord/RecordField'

export class MyNewComponent extends React.Component {
constructor(props) {
super(props);
var state={
fields: {
name: ''
}
};

this.state=state;
this.onFieldUpdate = this.onFieldUpdate.bind(this);
this.submitRequest = this.submitRequest.bind(this);
}

onFieldUpdate(conf) {
var fieldName=conf.field;
var fieldValue=conf.value;
var update = Object.assign(this.state.fields);
update[fieldName] = fieldValue;
this.setState({fields: update});
}

submitRequest(event) {
event.preventDefault();
this.setState({
is_loading: true,
});

// replace with fetch(...)
// see example bellow
setTimeout(()=>{
this.setState({
is_loading: false,
is_success: true
});
}, 550);
}

render() {
return (
<form onSubmit={this.submitRequest} style={{marginTop:"17px"}}>
<div className="row rs-margin" style={{background:"#fff", minHeight:"200px"}}>
<div className="col-12 rs-padding text-center" style={{}}>
<h3>Enter your name</h3>
{this.state.is_loading ? <JustSpinner /> : ''}
{this.state.is_success ? <SuccessResponse message={'Hi there ' + this.state.fields.name} /> : ''}
{this.state.error ? <ErrorResponse error={this.state.error} /> : ''}
</div>

<div className="col-12">
<RecordField
field={{"fieldName":"name", "type":"string", "label":"Your Name", "required":true}}
onUpdate={this.onFieldUpdate}
record={{fields: this.state.fields}}
/>
</div>

<div className="col-12">
<button className="slds-button slds-button_brand"
disabled={this.state.is_loading}
type="submit">
Greet me
</button>
</div>
</div>
</form>
);
}
}

Making Http Requests

The following component shows how can you can make http request by using the standard fetch api. A request is made to the fornite-api, which loads shopping items and renders them.

This is how it looks. Preview Http Component

The code:

caution

For brevity purposes this code does not contain a error handling strategy. You should always handle errors and unforseen circunstances in your code. Maybe we should wrap the await code inside a try-catch or use a promise based handling facade. The choice is always up to you. But remember be consistent.

import {JustSpinner} from '@core/components/just-spinner'

export class MyNewComponent extends React.Component {
constructor(props) {
super(props);
var state={
is_loading: true
};

this.state=state;
}

componentDidMount() {
this.loadData();
}

async loadData() {
const req = await fetch('https://fortnite-api.com/v2/shop/br');
const resp = await req.json();

this.setState({
is_loading: false,
data: resp.data.featured.entries
});
}

render() {
return (
<div className="row rs-margin" style={{background:"#fff", minHeight:"200px"}}>
<div className="col-12 rs-padding" style={{}}>
{this.state.is_loading ? <JustSpinner /> : ''}

{this.state.data ? this.state.data.map((E,I)=>
<div key={I}>
<img src={E.newDisplayAsset.materialInstances[0].images.OfferImage}
width="85px"
style={{float:"left", paddingRight:"10px"}}
/>
<h3> {E.devName} </h3>
<p> {E.regularPrice} </p>
<hr/>
</div>) : null}
</div>
</div>
);
}
}

Render System Component

Here's for instance, how you would render the platform file uploader/browser inside your component.

import {RenderSystemComponent} from '@core/components/render-system-component'

export function MyComponent(props) {
return (
<RenderSystemComponent
url={"https://on-boarding.api-gateway.ens-prd.enterstarts.com/render-system-component/0B483KA3YEXB9?name=ETX_FILE_BROWSER"}
component={"ETX_FILE_BROWSER"}
cache={true}
singleton={true}
props={{
user: this.props.user,
requestManager: this.requestManager,
onSelect: this.onImageSelect
}}
/>
);
}

LazyLoader

import {LazyLoader} from '@core/components/lazy-loader'

export function MyComponent(props) {
return (
<LazyLoader height={400}
width={"100%"}
threshold={0.15}
onContentVisible={() => {console.log('loaded!')}}>
<h3>Hey there</h3>
</LazyLoader>
);
}

Request Manager

Request Manager is responsible for requesting temporary tokens in order to make authenticated requests with correct tokens types required by different api's.

import {RequestManager} from '@sdk/requests';

export class MyNewComponent extends React.Component {
constructor(props) {
super(props);
var state={
is_loading: true
};

this.state=state;
this.requestData=this.requestData.bind(this);
this.requestManager = new RequestManager({
all: {
expiry: 10,
scope: {
scope_name: '* | read | write | delete'
},
tokenType: 'TOKEN_TYPE'
}
});
}

componentDidMount() {
}

requestData() {
this.requestManager.post('all', url, data)
// this.requestManager.delete('all', url, data)
// this.requestManager.put('all', url, data)
// this.requestManager.get('all', url)
}

render() {
return (
<>
<h3>Content here</h3>
</>
);
}
}