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.
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
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.
import {JustSpinner} from '@core/components/just-spinner'
export function MyComponent(props) {
return (
<JustSpinner />
);
}
ErrorResponse
How to work with errors
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 .
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.
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.
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.
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.
Create the component, here named MyNewComponent
Then just open the builder on the component details page.
After opening the builder, you can see the system has created a boilerplate
component for us
Lets start customizing a bit
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:
Building a Form Component
Here's a simple but robust template for a form.
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.
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>
</>
);
}
}