Build Your Own UI
This guide discusses how to use @securityside/rtas-base
to build your own UI around it.
Disclosure
We have already gone through this process during the development of the @securityside/rtas-ui
library. In this documentation, we share our experience to help you build your own UI. While we present one way to implement this, please note that there are multiple valid approaches.
Any approach following the concepts mentioned here will be valid and should work without issues, even if you use a different tech stack such as React, Angular, etc.
Tech Stack
In our case, we decided to build the library using TypeScript and Vite. This combination helps prevent bugs and allows us to generate the library (Build) in JavaScript in an optimized manner. It also generates type definitions (files that enable the IDE to understand the properties of all methods, intellisense
).
We chose to use JavaScript (the final code generated by the build process) to ensure that any implementation does not depend on other libraries, making the implementation as universal and straightforward as possible.
Step-by-Step Guide
Step 1: Import the Library
First, import the @securityside/rtas-base
library into your project. (here)
import { RTASBase } from '@securityside/rtas-base';
Step 2: Instantiate the Base Class
Create an instance of the base class. (here)
const connection = new RTASBase();
Step 3: Design and Implement Screens
Design your screens and implement them in JavaScript within your project. We used the state machine design pattern to manage state transitions and prevent unexpected changes.
For example, map out all scenarios where a state transition could occur:
When the state machine is in the INIT
or PENDING
state, it can transition directly to a final state of ACCEPTED
or EXPIRED
. This direct transition may occur depending on the speed of event propagation and the connection to the SignalR Server. Alternatively, it can happen if the connection to the SignalR Server is established after a final state has already been reached.
- Diagram
- Example Code
const STATES = {
INIT: 'init', // when everyting is initilized (before stablish connection SignalR Server)
PENDING: 'pending', // when event pending is recived
ACCEPTED: 'accepted', // when recive accepted event (final state)
EXPIRED: 'expired', // when recive expired event (final state)
};
// Example transition logic
function handleStateTransition(connection, currentState, state) {
switch(state) {
case STATES.INIT:
// Handle pending state
if(currentState != null) return; //could throw an error or log error;
currentState = state;
renderUI(currentState)
break;
case STATES.PENDING:
// Handle pending state
if(currentState != STATES.INIT) return; //could throw an error or log error;
currentState = state;
renderUI(currentState)
break;
case STATES.ACCEPTED:
// Handle accepted state
if(currentState != STATES.INIT && currentState != STATES.PENDING) return; //could throw an error or log error;
currentState = state;
renderUI(currentState)
// Should close connection
connection.closeConnection()
break;
case STATES.EXPIRED:
// Handle expired state
if(currentState != STATES.INIT && currentState != STATES.PENDING) return; //could throw an error or log error;
currentState = state;
renderUI(currentState)
// Should close connection
connection.closeConnection()
break;
}
}
Step 4: Map Screens to Events
Map the screens to the corresponding events from the RTASBase
instance.
import {
errorScreen,
registrationPendingScreen,
registrationExpiredScreen,
registrationSuccessScreen,
} from "./ui"
const connection = new RTASBase()
.setUrl("https://rtas.example.url")
.setToken("eyJhbGciOiJIUzI1NiIs....4ap76BW8KWv42GZqMuDNSauI77nZtU")
.setConnectionRetries(10)
.setOnErrorCallback((errorData) => errorScreen(errorData) )
.setOnConnectionReconnectedCallback(() => console.log("Connection Reconnected"))
.setOnConnectionReconnectingCallback(() => console.log("Connection Reconnecting"))
.setOnConnectionEstablishedCallback(() => console.log("Connection Established"))
.setOnConnectionCloseCallback((error) => console.log("Connection Close"))
.setRegistrationType()
.setOnRegistrationPendingCallback((data) => registrationPendingScreen(data))
.setOnRegistrationExpiredCallback((data) => registrationExpiredScreen(data))
.setOnRegistrationSuccessCallback((data) => registrationSuccessScreen(data))
.build();
Step 5: Testing
Finally, test the workflows to ensure everything works as planned.