Beejartha

Categories
Oracle APEX

Setting UserAccess Control

In this blog post, I will explain how to assign a role at Application Access Control in Oracle APEX. Application Access Control is a feature that allows you to define and manage user roles and permissions for your APEX applications. By assigning roles to users, you can control what they can see and do in your application.

To assign a role to a user, you need to follow these steps:

    1. In to your APEX workspace and open the application that you want to manage. In the App Builder tab in your browser, select your application, in our case DEMO, you will be navigated to your Application page.

    1. Shared Components on your application home page. Shared Components in Oracle APEX are components that can be shared across multiple pages or applications. These components include templates (themes), list of values, navigation bar entries, shortcuts, plug-ins, authentication schemes and authorization schemes.You can access the Shared Components page by selecting an application and then clicking Shared Components

    1. At the Security section, Click Application Access Control.

      You’ll navigate to another page where you will set user roles and role assignments,
    2. Click on Add Role, and create a role for your application, then enter the name
      • Name: Administrator
      • Click Apply changes

    3. Then click Add User Role Assignment to assign a user to the role created.
      • Name: Test@gamil.com — this should be your email address that will be used at login page.
      • Then apply changes.

    4. 6. Then head back to shared Components page Click Authorization Scheme.
      Go to Authorization Scheme section:

      • Scheme Type: Is In Role Group
      • Type: Application Role
      • Name: Select the role create, in our case Administrator.
      • Click apply changes.

    5. Then head back to shared Components page Click List Values
      • Name: ACCESS_ROLES
      • Source Type: SQL QUERY


      select role_name d, role_id r
      from APEX_APPL_ACL_ROLES where application_id = :APP_ID
      order by 1

Then apply changes, and your good to go.I hope this was helpful and informative.

Categories
Oracle APEX

Progressive Web Application

A Progressive Web Application (PWA) is a type of application software delivered over the web and built using web technologies such as HTML, CSS, JavaScript and Web Assembly. It is designed to run on any platform with a standards-compliant browser, including desktop and mobile devices. A PWA can look and feel like a native application, with features such as a home screen icon, notifications and offline access. A PWA is a type of web application that does not require separate bundling or distribution.

Progressive Web Applications (PWA) have several advantages;

  • They run full screen on a mobile device or in a separate window on a computer, providing a better look and feel.
  • Device integration provides access to device features such as geolocation, camera and sharing capabilities. Increased page rendering performance due to advanced caching provided by the installed device.
  • PWAs have a small footprint and take up very little space compared to native applications.
  • It also has the advantage that the application can be updated without the end user having to update the native application.
  • PWAs can become active engagement tools over traditional passive web browsing experiences, based on their ability to leverage device capabilities available to end users and create more compelling and engaging user experiences than traditional browser-based applications.

Oracle APEX enables developers to build PWAs with minimal effort and configuration. In this blog post, I will show you how to set up a PWA in Oracle APEX using the following steps:

Creating a PWA in Oracle Apex

At oracle apex its merely a simple step in creating PWA. Sign In to your apex account, at the dashboard page you will several action buttons.

Click at App Builder button which it will direct you to a new page where you will create a new application. Click Create

Here you will be directed to a new page called create application.


Click New Application, head over to this page where you highlight the details of your application and set Progressive Web Application (PWA) feature.


After the application has been created it will have all the necessary features to allow a user to install the application in their device.

Enable Progressive Web Application Feature

In the App Builder tab in your browser, click your application, then click Edit Application Definition on your application home page.

  1. Click on Progressive Web App.
  2. Enable Progressive Web App.

Test and install the PWA

To test your PWA, you need to run your application in a browser that supports PWAs, such as Chrome, Edge, Firefox, or Safari. You can also use a mobile device or an emulator to test your PWA.

To test your PWA:

  • Navigate to App Builder => Your Application => Run Application.
  • Log in to your application if prompted.
  • Observe how your application behaves and looks like a native app.
  • If your browser supports PWAs, you should see a prompt or an icon to install your PWA to the home screen. For example, in Chrome, you should see a + icon in the address bar. In Safari, you should see a Share icon and then an Add to Home Screen option.
  • Click the prompt or the icon to install your PWA to the home screen.
  • Follow the instructions to complete the installation.

To install your PWA:

  • Go to the home screen of your device and look for the icon of your PWA.
  • Tap the icon to launch your PWA.
  • Enjoy using your PWA offline, with push notifications, and without any browser UI.

In this blog post, I showed you how to set up a PWA in Oracle APEX. You can use this feature to create web applications that offer a native app experience to your users. I hope this was helpful and informative.

Categories
Node & React Js

ReactJS CRUD Application

In this blog post, I will show you how to build a simple to-do application using React.js. React.js is a popular JavaScript library for creating user interfaces with reusable components. You will learn how to use React hooks, state management, and event handling to create a functional and interactive app.
The app will have the following features:
– Display a list of tasks that the user can add, edit, delete, or mark as completed.
– Filter the tasks by all, active, or completed status.
– Store the tasks in our local storage so that they persist across sessions. To do that we are going to use a package called json-server that’s going to create a localized database.
To follow along, you will need some basic knowledge of HTML, CSS, and JavaScript, as well as Node.js and npm installed on your machine. You will also need a code editor of your choice.
Let’s get started by creating a new React project using the create-react-app tool:
First you need to create a react application. You do this by adding this bash script to your terminal;

npx create-react-app react-todo

This will create a new folder called react-todo with all the necessary files and dependencies for a React app. Navigate to the project folder and start the development server:

cd react-todo
npm start

You should see a default React app running on http://localhost:3000 in your browser.
Next, let’s clean up some of the files that we don’t need for this project. Open the src folder and delete the following files:

- App.test.js
- index.css
- logo.svg
- reportWebVitals.js
- setupTests.js

Also, remove the corresponding import statements from App.js and index.js.
Add the following packages to use in the application.


yarn add moment node-sass redux redux-thunk uuid react-loading
Yarn add -D autoprefixer json-server concurrently postcss postcss-cli tailwindcss sass

Now, let’s add some basic styling to our app. At style.css in the src folder and paste the following code:


@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--primary-color: #17a2b8;
--dark-color: #343a40;
--light-color: #f4f4f4;
--danger-color: #dc3545;
--success-color: #28a745;
}
html {
font-size: 12px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
body {
margin: 0;
padding: 0;
font-family: "Open Sans", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
overflow-y: auto;
color: #333;
}
ul {
list-style-type: none;
}
a {
text-decoration: none;
}
h1 {
@apply text-2xl;
}
h2 {
@apply text-xl;
}

This will add some basic styles to our app components. Don’t forget to import


import './App.css';

Now that we have set up our project and added some styles, let’s start building our React components. In the src folder lets create these folders and files


-Actions
Alert.js
Todo.js
-Components
AddTodo.js
Alert.js
EditTodo.js
Footer.js
Todo.js
NavBar.js
-Reducers
Alert.js
Index.js
Todo.js
-Constants
Types.js
App.js
Index.js
store.js

Lets create our action types that we are going to use in this project. At types.js add these;


export const SET_ALERT = "SET_ALERT";
export const REMOVE_ALERT = "REMOVE_ALERT";
export const ADD_TODO = "ADD_TODO";
export const GET_TODO = "GET_TODO";
export const DELETE_TODO = "DELETE_TODO";
export const UPDATE_TODO = "UPDATE_TODO";
export const CLEAR_TODO = "CLEAR_TODO";
export const SEARCH_TODO = "SEARCH_TODO";
export const TODO_ERROR = "TODO_ERROR";
export const SET_CURRENT = "SET_CURRENT";
export const CLEAR_CURRENT = "CLEAR_CURRENT";
export const SET_LOADING = "SET_LOADING";

In our reducers folder at todo.js file you created lets use our action types to manage all states that are going to be used for our to-do application. Also not forgeting to import our action types that we are going to use in our switch case statement. Write the following code:


import {
ADD_TODO,
UPDATE_TODO,
CLEAR_TODO,
CLEAR_CURRENT,
GET_TODO,
SEARCH_TODO,
SET_CURRENT,
SET_LOADING,
TODO_ERROR,
DELETE_TODO,
} from "../constants/types";
const initialState = {
todos: null,
current: null,
loading: false,
error: null,
};
export default (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case GET_TODO:
return {
...state,
todos: payload,
loading: false,
};
case ADD_TODO:
return {
...state,
todos: [...state.todos, payload],
loading: false,
};
case UPDATE_TODO:
return {
...state,
todos: state.todos.map((todo) =>
todo.id === payload.id ? payload : todo
),
};
case DELETE_TODO:
return {
...state,
todos: state.todos.filter((todo) => todo.id !== payload),
loading: false,
};
case SEARCH_TODO:
return {
...state,
todos: payload,
};
case SET_CURRENT:
return {
...state,
current: payload,
};
case CLEAR_CURRENT:
return {
...state,
current: null,
};
case SET_LOADING:
return {
...state,
loading: true,
};
case TODO_ERROR:
console.error(payload);
return {
...state,
error: payload,
};
default:
return state;
}
};

At alert.js in reducers folder write the following code:


import { SET_ALERT, REMOVE_ALERT } from "../constants/types";
const initialState = [];
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case SET_ALERT:
return [...state, payload];
case REMOVE_ALERT:
return state.filter((alert) => alert.id !== payload);
default:
return state;
}
}

Then we need to combine all the reducers created. Here it is best we use one of redux’s methods:

CombineReducer


import { combineReducers } from "redux";

This is going to combine all the reducers created so that all the states can be recognized in our application through store.

Since i’ve mentioned a store, let me explain what it’s functionality;
A Store in Redux is like a placeholder for all the states, and states mutations. If any change of state occurs in our application the store will recognize it and stores. Lets continue;
Create a file index.js and write the code below;


import { combineReducers } from "redux";
import alert from "./alert";
import todo from "./todo";
export default combineReducers({
todo,
alert,
});


Redux Core principles

Lets make our Actions, Actions in redux are responsible for state change. This is done by emitting or dispatch an action. Create a file called alert.js write the code below;


import { v4 as uuidv4 } from "uuid";
import { SET_ALERT, REMOVE_ALERT } from "../constants/types";
export const setAlert = (msg, alertType, timeout = 3000) => (dispatch) => {
const id = uuidv4();
dispatch({
type: SET_ALERT,
payload: { msg, alertType, id },
});
setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout);
};

The above code will be responsible for managing alert notification action. Also note we’ve used a package called uuid. This will set unique ID identifiers for every alert shown in our application. Next Create a file called todo.js.

Todo.js


import { setAlert } from "./alert";
import {
ADD_TODO,
UPDATE_TODO,
CLEAR_TODO,
CLEAR_CURRENT,
GET_TODO,
SEARCH_TODO,
SET_CURRENT,
SET_LOADING,
TODO_ERROR,
DELETE_TODO,
} from "../constants/types";
//ADD TODO
export const addTodo = (todo) => async (dispatch) => {
try {
setLoading();
const res = await fetch("/todos", {
method: "POST",
body: JSON.stringify(todo),
headers: { "Content-Type": "application/json" },
});
const data = await res.json();
dispatch({
type: ADD_TODO,
payload: data,
});
} catch (err) {
dispatch({
type: TODO_ERROR,
payload: err.response.statusText,
});
}
};
//GET TODO
export const getTodo = () => async (dispatch) => {
try {
setLoading();
const res = await fetch("/todos");
const data = await res.json();
dispatch({
type: GET_TODO,
payload: data,
});
} catch (err) {
dispatch({
type: TODO_ERROR,
payload: err.response.statusText,
});
}
};
//UPDATE TODO
export const updateTodo = (todo) => async (dispatch) => {
try {
setLoading();
const res = await fetch(`/todos/${todo.id}`, {
method: "PUT",
body: JSON.stringify(todo),
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
dispatch({
type: UPDATE_TODO,
payload: data,
});
} catch (err) {
dispatch({
type: TODO_ERROR,
payload: err.response.statusText,
});
}
};
//DELETE TODO
export const deleteTodo = (id) => async (dispatch) => {
try {
setLoading();
await fetch(`/todos/${id}`, {
method: "DELETE",
});
dispatch({
type: DELETE_TODO,
payload: id,
});
} catch (err) {
dispatch({
type: TODO_ERROR,
payload: err.response.statusText,
});
}
};
//SEARCH TODO
export const searchTodo = (text) => async (dispatch) => {
try {
setLoading();
const res = await fetch(`/todos?q=${text}`);
const data = await res.json();
dispatch({
type: SEARCH_TODO,
payload: data,
});
} catch (err) {
dispatch({
type: TODO_ERROR,
payload: err.response.statusText,
});
}
};
//SET LOADING
export const setLoading = () => {
return {
type: SET_LOADING,
};
};
//SET CURRENT TODO
export const setCurrent = (todo) => {
return {
type: SET_CURRENT,
payload: todo,
};
};
//CLEAR CURRENT TODO
export const clearCurrent = () => {
return {
type: CLEAR_CURRENT,
};
};

This file will manage all of our to-do action requests, i.e. Creation, Deletion, Update and Reading of our To-do applications. You can note how we are reusing the same action types used in our reducers.
Now lets create our components that we are going to use in our application.

AddTodo.js

This is responsible for the creation of our task, We’ll trigger addTodo action, to create our application.


import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodo } from "../actions/todo";
import { setAlert } from "../actions/alert";
const required = (value) => {
if (!value) { return
This field is required!;}};
const AddTodo = ({ addTodo, setAlert }) => {
const [message, setMessage] = useState("");
const onSubmit = (e) => {
e.preventDefault();
if (message === "" || null) {
setAlert("This field is required!", "danger");
} else {
const newTodo = {
message,
date: new Date(),
};
addTodo(newTodo);
setAlert("Item successfully added", "success");
setMessage("");
}
};
return (
<section readonly className="mx-auto flex w-full max-w-7xl flex-col gap-2 rounded-md bg-white px-4 py-2 shadow-md transition-all duration-300 hover:scale-105 hover:border-l-2 hover:border-r-2 hover:border-solid hover:border-green-500">
<h1 readonly className="my-2 text-xl font-bold">Add</h1> <div readonly className="w-full"> <input readonly type="text" name="message" placeholder="Add Task" value={message} onChange={(e) => setMessage(e.target.value)}
className={`h-10 w-full rounded-md px-4 py-2 text-gray-800 ring-1 ring-gray-200 focus:border-gray-300 focus:outline-none focus:ring-1 focus:ring-gray-300 `}
/>
</div>
<div className="flex-end flex justify-end">
<button
type="submit"
form="add-todo"
onClick={onSubmit}
className={`flex h-10 w-20 items-center justify-center rounded-md bg-green-500 px-4 py-3 font-semibold text-white duration-200 ease-in hover:scale-105 hover:cursor-pointer `}
disabled={message === ""}
>
Add
</button>
</div>
</section>
);
};
export default connect(null, { setAlert, addTodo })(AddTodo);

Alert.js

This is going to manage all of our alert being triggered.


import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
const Alert = ({ alerts }) =>
alerts !== null &&
alerts.length > 0 &&
alerts.map((alert) => (
<div
key={alert.id}
className={`alert absolute right-4 top-0 z-50 rounded-md alert-${alert.alertType}`}
>
{alert.msg}
</div>
));
Alert.propTypes = {
alerts: PropTypes.array.isRequired,
};
const mapStateToProps = (state) => ({
alerts: state.alert,
});
export default connect(mapStateToProps)(Alert);

EditTodo.js

This is responsible for updating any item created in our list.


import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { setAlert } from "../actions/alert";
import { updateTodo } from "../actions/todo";
const required = (value) => {
if (!value) {
return <p className="text-danger">This field is required!</p>;
}
};
const EditTodo = ({ current, updateTodo, setAlert }) => {
const [message, setMessage] = useState("");
const [attention, setAttention] = useState(false);
useEffect(() => {
if (current) {
setMessage(current.message);
setAttention(current.attention);
}
}, [current]);
const Clear = () => {
setMessage("");
setAttention(false);
};
const onSubmit = (e) => {
e.preventDefault();
if (message === "") {
setAlert("This field is required!", "danger");
} else {
const editTodo = {
id: current.id,
message,
attention,
date: new Date(),
};
updateTodo(editTodo);
setAlert(`${message} Updated Successfully`, "success");
setMessage("");
setAttention(false);
}
};
return (
<section
className={`mx-auto flex w-full max-w-7xl flex-col gap-2 rounded-md bg-white px-4 py-2 shadow-md transition-all duration-300 hover:scale-105 hover:border-l-2 hover:border-r-2 hover:border-solid hover:border-blue-400 ${
message
? " scale-105 border-l-2 border-r-2 border-solid border-blue-400 transition-all duration-300"
: "transition-all duration-300"
}`}
>
<h1 className="text-xl font-bold">Edit </h1>
<div className="w-full">
<input
type="text"
name="message"
placeholder="Edit Task"
value={message}
onChange={(e) => setMessage(e.target.value)}
className={`h-10 w-full rounded-md px-4 py-2 text-gray-800 ring-1 ring-gray-200 focus:border-gray-300 focus:outline-none focus:ring-1 focus:ring-gray-300 `}
/>
</div>
<div className="flex-end flex items-center justify-between">
<label className="flex items-center space-x-2 font-medium text-gray-600">
<input
type="checkbox"
className="filled-in"
checked={attention}
value={attention}
onChange={(e) => setAttention(!attention)}
/>{" "}
<span>Check</span>
</label>
<div className="flex items-center space-x-2">
<button
type="button"
onClick={Clear}
className={`flex h-10 w-full items-center justify-center rounded-md bg-red-300 px-4 py-3 font-semibold text-white duration-200 ease-in hover:scale-105 hover:cursor-pointer `}
>
Cancel
</button>
<button
type="submit"
form="add-todo"
onClick={onSubmit}
className={`flex h-10 w-full items-center justify-center rounded-md bg-cyan-400 px-4 py-3 font-semibold text-white duration-200 ease-in hover:scale-105 hover:cursor-pointer `}
>
Submit
</button>
</div>
</div>
</section>
);
};
const mapStateToProps = (state) => ({
current: state.todo.current,
});
export default connect(mapStateToProps, { setAlert, updateTodo })(EditTodo);

Todo.js

This is responsible for showing all items in our list and filtering any item available.


import React, { useEffect, useRef } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Moment from "react-moment";
import moment from "moment";
import { setAlert } from "../actions/alert";
import { getTodo, deleteTodo, setCurrent, searchTodo } from "../actions/todo";
import ReactLoading from "react-loading";
const Loading = () => ;
const Todo = ({
getTodo,
setAlert,
searchTodo,
deleteTodo,
setCurrent,
todo: { todos, loading },
}) => {
const text = useRef("");
useEffect(() => {
getTodo();
}, []);
const onChange = (e) => {
searchTodo(text.current.value);
};
const OnDelete = async (todo) => {
deleteTodo(todo.id);
setAlert("Item Removed", "success");
};
if (loading || todos === null) {
return (
<div className="loading mx-auto w-full">
<Loading />
</div>
);
}
return (
<div>
<div className="mx-auto w-full max-w-7xl rounded-lg border border-gray-200 bg-white shadow-lg">
<header className="flex w-full items-center justify-between border-b border-gray-100 px-5 py-4">
<h2 className="font-semibold text-gray-800">Task(s)</h2>
<section className="flex w-full max-w-lg flex-row space-x-2">
<button
title="refresh"
type="button"
onClick={getTodo}
className="items-center rounded-md bg-gray-400 px-3 py-2 font-semibold text-white"
><i className="fas fa-sync-alt"/>
</button>
<div className="w-full">
<input
id="search"
type="search"
placeholder="Search..."
ref={text}
onChange={onChange}
className={`h-10 w-full rounded-md px-4 py-2 text-gray-800 ring-1 ring-gray-200 focus:border-gray-300 focus:outline-none focus:ring-1 focus:ring-gray-300 `}
/>
</div>
</section>
</header>
<div className="p-3">
<div className="overflow-x-auto">
{!loading && todos.length <= 0 ? ( <p className="mx-auto flex w-full max-w-lg flex-col text-lg font-bold text-blue-400"> Task list is empty, please add some info </p> ) : ( <table className="table w-full table-auto"> <thead className="bg-gray-100 text-base font-semibold uppercase leading-normal text-gray-400"> <tr><th className="whitespace-nowrap p-2"> <div className="text-left font-semibold">#</div>
</th>
<th className="whitespace-nowrap p-2">
<div className="text-left font-semibold">To-Do</div>
</th>
<th className="whitespace-nowrap p-2">
<div className="text-left font-semibold">Time</div>
</th>
<th className="whitespace-nowrap p-2">
<div className="text-left font-semibold">Action</div>
</th>
</tr>
</thead>
{todos.map((todo) => (
<tbody className=" font-light text-gray-600">
<tr className="border-b border-gray-200 hover:bg-gray-50">
<td className="whitespace-nowrap p-2">
<div className="text-left">{todo.id}</div>
</td>
<td className="whitespace-nowrap p-2">
<div
className={`text text-left ${
todo.attention
? "text-success line-through"
: "text-gray-600 "
}`}
>
{todo.message}
</div>
</td>
<td className="whitespace-nowrap p-2">
<div
className={`text text-left ${
todo.attention
? "text-success line-through"
: "text-gray-600 "
}`}
>
<Moment format="DD-MMM-YYYY hh:mm a">
{moment.utc(todo.date)}
</Moment>
</div>
</td>
<td className="whitespace-nowrap p-2">
<div className="flex items-center gap-2 text-left">
<button
title="Edit"
type="button"
onClick={() => setCurrent(todo)}
className="items-center rounded-md bg-cyan-400 px-3 py-3 font-semibold text-white"
>
<i className="fa fa-edit" />
</button>
<button
title="Delete"
type="button"
onClick={() => OnDelete(todo)}
className="items-center rounded-md bg-red-300 px-3 py-3 font-semibold text-white"
>
<i className="fa fa-trash" />
</button>
</div>
</td>
</tr>
</tbody>
))}
</table>
)}
</div>
</div>
</div>
</div>
);
};
Todo.propTypes = {
getTodo: PropTypes.func.isRequired,
todo: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
todo: state.todo,
});
export default connect(mapStateToProps, {
getTodo,
setAlert,
deleteTodo,
setCurrent,
searchTodo,
})(Todo);

NavBar.js


import React from "react";
const NavBar = () => {
return (
<nav className="flex w-full flex-col bg-white px-6 py-4 text-center font-sans shadow sm:flex-row sm:items-baseline sm:justify-between sm:text-left">
<div className="mb-2 sm:mb-0">
<a href="/"className="text-grey-darkest hover:text-blue-dark text-2xl font-semibold no-underline">
To-Do Application
</a>
</div>
</nav>
);
};
export default NavBar;

Footer.js


import React from "react";
const Footer = () => {
return (
<footer className="absolute bottom-2 mt-10 flex flex-1 flex-col px-6 py-2">
<p className="themeText text-start text-lg font-normal md:text-left">
(c) {new Date().getFullYear()}{" "}
<a
href="/"
className={`text-xl font-normal text-cyan-600 hover:text-blue-600`}
rel="noreferrer"
>
To-do Application
</a>
</p>
</footer>
);
};
export default Footer;

App.js

Here we are going to import all of our components.


import React from "react";
import NavBar from "./components/NavBar";
import Footer from "./components/Footer";
import Alert from "./components/Alert";
import AddTodo from "./components/AddTodo";
import EditTodo from "./components/EditTodo";
import Todo from "./components/Todo";
function App() {
return (
<main className="relative mx-auto h-screen w-full bg-gray-100 ">
<NavBar />
<div
className={`flex w-full flex-1 flex-col gap-6 overflow-y-auto overflow-x-hidden px-4 py-4`}
>
<Alert />
<AddTodo />
<EditTodo />
<Todo />
</div>
<Footer />
</main>
);
}
export default App;

Index.js

And finally here we are going to wrap our App.js with one of redux methods tag called a Provider


import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store";
import App from "./App";
import "./style.css";
// import "./style.output.css";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);

Categories
Node & React Js

Build an USSD Savings Account Application with NodeJS & Express

Introduction

Many of you or probably some, I guess, have been met in a situation where your mobile phone application is not operational or it’s experiencing some down time. Most probably when you want to do a certain transaction operation. And for some reason you get SMS notification from the company e.g. “Banks/Insurance company etc.” that goes like this;

Dear Customer, we wish to inform you that we shall be undertaking a system maintenance on our App platform. Services will be unavailable from a certain time to a certain. Mean-while continue to access our services through our USSD platform. Dial *000*0#. We regret the inconvenience caused.

Well, in this tutorial I am going to take you through how to build a simple Cash Saving USSD Application with NodeJS. Although, it’s a bit challenging to come up with a USSD Application, because mainly you have to have a great logic behind it, especially when navigating through the menu. So basically USSD is a technology that allows users to access services without the need of an internet.

So here are the steps;

Getting Started

First, we have to create our project folder using our terminal:

mkdir ussd-cashSaving
cd ussd-cashSaving

Assuming you are using VScode, enter the following command to open the project folder.

code .

Next, we create the package.json file.

npm init -y

Adding .gitignore file to hide dependencies in node_modules folder

# dependencies
node_modules
node_modules/# misc
.env

Then we install the libraries;

npm install –save express ussd-builder bcryptjs dotenv nodemon lodash mongoose colors morgan

Note: nodemon package allows you to reload the server each time our code is modified.

In the root directory of the app, create the following files

  • USSD-CASHSAVING
  • └─── node_modules
  • └─── _data
    • └─── users.json
  • └─── config
    • └─── db.js
  • └─── controllers
    • └─── users.js
  • └─── menu
    • └─── dashboard.js
    • └─── index.js
    • └─── register.js
    • └─── savings.js
    • └─── sendMoney.js
    • └─── settings.js
    • └─── statements.js
  • └─── middleware
    • └─── async.js
    • └─── auth.js
    • └─── errors.js
  • └─── models
    • └─── Users.js
  • └─── routes
    • └─── users.js
  • └─── utils
    • └─── errorResponse.js
    • └─── sessionHandler.js
    • └─── url.js
  • └─── package.json
  • └─── package-lock.json
  • └─── server.js
  • └─── .env

At package.json file rename “main”: “index.js” to “main”: “server.js”

{
“name”: “ussd-cashsaving”,
“version”: “1.0.0”,
“description”: “”,
“main”: “server.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1″,
“dev”: “nodemon server”,
},
“keywords”: [],
“author”: “”,
“license”: “ISC”,
“dependencies”: {
“bcryptjs”: “^2.4.3”,
“colors”: “^1.4.0”,
“dotenv”: “^16.0.3”,
“express”: “^4.18.2”,
“lodash”: “^4.17.21”,
“mongoose”: “^6.8.4”,
“morgan”: “^1.10.0”,
“nodemon”: “^2.0.20”,
“ussd-builder”: “^1.2.5”
}
}

Add a .env file;

NODE_ENV=development
PORT=5000JWT_SECRET=
JWT_EXPIRE=
JWT_COOKIE_EXPIRE=X_SERVER_KEY=
MONGO_URI=

Creating our Database

At config/db.js add the following code

const mongoose = require(“mongoose”);const connectDB = async () => {
const conn = await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log(`MongoDB Connected: ${conn.connection.host}`.cyan.underline);
};
mongoose.set(“strictQuery”, false);module.exports = connectDB;

We’ll be creating our database on MongoDB. The code above is for connecting to our database through Mongoose.

Creating the Data Model

At models/User.js add the following code:

const crypto = require(“crypto”);
const mongoose = require(“mongoose”);
const bcrypt = require(“bcryptjs”);
const jwt = require(“jsonwebtoken”);const UserSchema = new mongoose.Schema(
{
Name: {
type: String,
required: true,
},
phone: {
type: String,
required: true,
},
pin: {
type: String,
required: true,
minlength: 4,
select: false,
},amount: {
required: true,
type: Number,
},
},
{
timestamps: true, // mongoose creates time fields (createdAt & updatedAt) automatically
}
);

// Encrypt pin using bcrypt
UserSchema.pre(“save”, async function (next) {
if (!this.isModified(“pin”)) {
next();
}

const salt = await bcrypt.genSalt(10);
this.pin = await bcrypt.hash(this.pin, salt);
});

// Match user entered pin to hashed pin in database
UserSchema.methods.matchPassword = async function (enteredPin) {
return await bcrypt.compare(enteredPin, this.pin);
};

module.exports = mongoose.model(“User”, UserSchema);

The code above is our mongoose Schema. Generally, what is done here is creating fields where it will be captured by our database i.e name, phone, pin & amount. Also note that there is a middleware which will be used to encrypt the user’s pin.

At server.js add the following code:

const path = require(“path”);
const express = require(“express”);
const colors = require(“colors”);
const dotenv = require(“dotenv”);
const morgan = require(“morgan”);
const _ = require(“lodash”);
const UssdMenu = require(“ussd-builder”);
const session = require(“./utils/sessionHandler”);
const { notFound, errorHandler } = require(“./middleware/error”);
const connectDB = require(“./config/db”);// Load env vars
dotenv.config();let menu = new UssdMenu();

// Connect to database
connectDB();

// Route files
const List = require(“./menu”);

// Initialize express
const app = express();

// Body-parser –responsible for parsing the incoming request bodies in a middleware before you handle it
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Dev logging middleware
if (process.env.NODE_ENV === “development”) {
app.use(morgan(“dev”));
}

// Set static folder
const publicDirPath = path.join(__dirname, “public”);
app.use(express.static(publicDirPath));

// File extensions
app.use(
express.static(publicDirPath, {
extensions: [“html”, “css”, “js”, “png”, “jpg”, “json”, “ico”],
})
);

// Registering USSD handler with Express
_.over([session, List])(menu);

app.post(“/”, async (req, res, next) => {
try {
await menu.run(req.body, (ussdResult) => {
res.send(ussdResult);

This is where you call all of your library files and run the application server.

User data

Paste the following code in your_data/user.json file:

[
{
“_id”: “5d7a514b5d2c12c7449be042”,
“Name”: “John Doe”,
“phone”: “254700000001”,
“pin”: 2541,
“amount”: 1000000
},
{
“_id”: “5d7a514b5d2c12c7449be043”,
“Name”: “Kevin Smith”,
“phone”: “254700000002”,
“pin”: 2542,
“amount”: 650000
},
{
“_id”: “5d7a514b5d2c12c7449be044”,
“Name”: “Sasha Ryan”,
“phone”: “254700000003”,
“pin”: 2543,
“amount”: 470000
}
]

Export data to MongoDB

Here you can add the json file above to mongoDb and also delete all.

node seeder -i // Send data to mongo-db
node seeder -d // Destroy all the data in mongo-db

Paste the following code in your seeder.js file:

const fs = require(“fs”);
const mongoose = require(“mongoose”);
const colors = require(“colors”);
const dotenv = require(“dotenv”);
const connectDB = require(“./config/db”);// Load env vars
dotenv.config();

// Load models
const User = require(“./models/User”);

// Connect to DB
connectDB();

// Read JSON files
const users = JSON.parse(
fs.readFileSync(`${__dirname}/_data/users.json`, “utf-8”)
);

// Import into DB
const importData = async () => {
try {
await User.create(users);
console.log(“Data Imported…”.green.inverse);
process.exit();
} catch (err) {
console.error(err);
}
};

// Delete data
const deleteData = async () => {
try {
await User.deleteMany();
console.log(“Data Destroyed…”.red.inverse);
process.exit();
} catch (err) {
console.error(err);
}
};

if (process.argv[2] === “-i”) {
importData();
} else if (process.argv[2] === “-d”) {
deleteData();
}

The code above allows you to add or delete the json payload to your mongo-db.

Controllers

Paste the following code in your controllers/users.js file:

const ErrorResponse = require(“../utils/errorResponse”);
const asyncHandler = require(“../middleware/async”);
const User = require(“../models/User”);// @desc Get single user
exports.getUser = async (phone) => {
try {
const user = await User.findOne({ phone: phone }).exec();
if (!user) return null;

return user;
} catch (error) {
return undefined;
}
};

// @desc Create user
exports.createUser = async (data) => {
try {
const user = await User.create(data);
Promise.resolve(user);
return user;
} catch (error) {
return error;
}
};

// @desc update Balance
exports.updateBalance = async (amount, phone) => {
try {
const user = await User.where(“phone”, phone).update({
$set: { amount: amount },
});

return user;
} catch (error) {
return error;
}
};

// @desc login
exports.login = async (pin, phone) => {
try {
const user = await User.findOne({ phone }).select(“+pin”).exec();
if (!user) return null;

// Check if pin matches
const isMatch = await user.matchPassword(pin);
if (!isMatch) return null;

sendTokenResponse(user, 200, res);
Promise.resolve(isMatch);
} catch (error) {
return error;
}
};

Here is where CRUD, (Create, Read, Update & Delete ) methods are

Creating USSD Menu

Here the users will be able to register an account to the cash saving App, enter the amount to his/her wallet and then have the ability to check the wallet balance and send cash to an existing user in the database.

We are going to use the ussd-builder library to create our menus.
Paste the following code in your Menu/index.js file:

const dotenv = require(“dotenv”);
const _ = require(“lodash”);
const { getUser } = require(“../controllers/users”);const dashboard = require(“./dashboard”);
const register = require(“./register”);

// Load env vars
dotenv.config();

module.exports = (menu) => {
// Define menu states
menu.startState({
run: async () => {
// use menu.con() to send response without terminating session

const { phoneNumber } = menu.args;
const user = await getUser(phoneNumber);

if (user) {
menu.con(
`Welcome back ${user.Name} to Cash savings:` +
“\nEnter your 4-digit PIN to continue:”
);
} else {
menu.con(“Welcome to Cash savings:” + “\n1. Get started” + “\n0. Exit”);
}
},
// next object links to next state based on user input
next: {
“*\\d{4}”: “dashboard”,
1: “register”,
0: “Exit”,
},

defaultNext: “invalidOption”,
});

menu.state(“Exit”, {
run: () => {
menu.end(“Goodbye :)”);
},
});

menu.state(“invalidOption”, {
run: () => {
menu.end(`Invalid option`);
},
});

menu.state(“IncorrectInput”, {
run: () => {
menu.end(`Incorrect input`);

The code above will check if there’s an already existing user in the database, if not, the user will be prompted to register his/her self. If the pin is accepted the user will be redirected to the dashboard menu.

Paste the following code in your Menu/dashboard.js file

const _ = require(“lodash”);
const { login } = require(“../controllers/users”);
const { session } = require(“../utils/sessionHandler”);const savings = require(“./savings”);
const sendMoney = require(“./sendMoney”);
const settings = require(“./settings”);
const statement = require(“./statement”);

module.exports = (menu) => {
// Define menu states

menu.state(“dashboard”, {
run: async () => {
const {
val,
args: { phoneNumber },
} = menu;
session[“newPin”] = val;

if (session.newPin) {
// Save the data to Mongo
await login(session.newPin, phoneNumber).then((res) => {
if (res) {
menu.con(
`Choose a service to proceed:` +
“\n1. Send Money” +
“\n2. Check savings” +
“\n3. View Mini-statement” +
“\n4. Change PIN” +
“\n0. Exit”
);
} else {
menu.go(“dashboard.WrongPin”);
}
});
} else {
menu.end(`invalid`);
}
},
// next object links to next state based on user input
next: {
1: “dashboard.sendMoney”,
2: “dashboard.savings”,
3: “dashboard.statement”,
4: “dashboard.settings”,
0: “Exit”,
},

defaultNext: “invalidOption”,
});

menu.state(“dashboard.WrongPin”, {

In the dashboard menu, the user will see a list of items

Paste the following code in your Menu/register.js file

const _ = require(“lodash”);
const { createUser } = require(“../controllers/users”);
const { session } = require(“../utils/sessionHandler”);

module.exports = (menu) => {
// Define menu states
menu.state(“register”, {
run: () => {
menu.con(“Enter your Name”);
},
// using regex to match user input to next state
next: {
“*[a-zA-Z]+”: “register.amount”,
},
});

menu.state(“register.amount”, {
run: async () => {
const { val } = menu;
session[“name”] = val;
menu.con(“Enter amount”);
},

// using regex to match user input to next state
next: {
“*\\d+”: “register.pin”,
},
});

menu.state(“register.pin”, {
run: async () => {
const { val } = menu;
session[“amount”] = val;
menu.con(“Enter your pin”);
},

// using regex to match user input to next state
next: {
“*\\d+”: “end”,
},
});

menu.state(“end”, {
run: async () => {
const {
val,
args: { phoneNumber },
} = menu;
session[“pin”] = val;

// Save the data to Mongo
await createUser({
Name: session.name,

Paste the following code in your Menu/savings.js file

const { getUser } = require(“../controllers/users”);module.exports = (menu) => {
// Define menu states
menu.state(“dashboard.savings”, {
run: async () => {
// use menu.con() to send response without terminating session

const { phoneNumber } = menu.args;
const user = await getUser(phoneNumber);

menu.con(`Your savings are: ${user.amount} \n0. Dashboard`);
},
// next object links to next state based on user input
next: {
0: “dashboard”,
},
});

return menu;
};

Paste the following code in your Menu/sendMoney.js file

const { getUser, updateBalance } = require(“../controllers/users”);
const { session } = require(“../utils/sessionHandler”);module.exports = (menu) => {
// Define menu states
menu.state(“dashboard.sendMoney”, {
run: async () => {
const { val } = menu;
menu.con(`Enter amount to send`);
},
// next object links to next state based on user input
next: {
“*\\d”: “dashboard.sendMoney.receiver”,
},
defaultNext: “IncorrectInput”,
});

menu.state(“dashboard.sendMoney.receiver”, {
run: async () => {
const {
val,
args: { phoneNumber },
} = menu;
session[“amount”] = val;
const user = await getUser(phoneNumber);
const enteredAmount = JSON.parse(val);

console.log(enteredAmount, user.amount);

if (val > user.amount) {
menu.end(“Sorry, you don’t have sufficient amount to send!”);
} else {
menu.con(`Enter phone number to send to`);
}
},
// next object links to next state based on user input
next: {
“*\\d{10}”: “dashboard.sendMoney.send”,
},
defaultNext: “IncorrectInput”,
});

menu.state(“dashboard.sendMoney.send”, {
run: async () => {
const {
val,
args: { phoneNumber },
} = menu;

const sender = await getUser(phoneNumber);
const reciever = await getUser(val);

if (reciever) {

Paste the following code in your Menu/settings.js file

const { changePin } = require(“../controllers/users”);
const { session } = require(“../utils/sessionHandler”);module.exports = (menu) => {
// Define menu states
menu.state(“dashboard.settings”, {
run: async () => {
menu.con(`Enter your 4-digit new PIN`);
},
// next object links to next state based on user input
next: {
“*\\d{4}”: “dashboard.settings.newPin”,
},
});

menu.state(“dashboard.settings.newPin”, {
run: async () => {
const { val } = menu;
session[“newPin”] = val;
menu.con(`Confirm your 4-digit new pin`);
},
// next object links to next state based on user input
next: {
“*\\d{4}”: “dashboard.settings.updatePin”,
},
defaultNext: “invalidOption”,
});

menu.state(“dashboard.settings.updatePin”, {
run: async () => {
const {
val,
args: { phoneNumber },
} = menu;

if (session.newPin === val) {
await changePin(val, phoneNumber);
menu.end(`You have successfully changed your PIN! `);
} else {
menu.end(`Your new PIN don’t match`);
}
},
});

menu.state(“invalidOption”, {
run: () => {
menu.end(`Invalid option`);
},
});

return menu;
};

Paste the following code in your Menu/statement.js file

module.exports = (menu) => {
// Define menu states
menu.state(“dashboard.statement”, {
run: async () => {
menu.end(
`Sorry, this service is unavailable. Please try again later.`
);
},
});return menu;
};

Add session Handler

This is where you can store temporary user data that persists through an entire session.
Paste the following code in your utils/sessionHandler.js file

const UssdMenu = require(“ussd-builder”);

let sessions = {};
let menu = new UssdMenu();

menu.sessionConfig({
start: (sessionId, callback) => {
// initialize current session if it doesn’t exist
// this is called by menu.run()
if (!(sessionId in sessions)) sessions[sessionId] = {};
callback();
},
end: (sessionId, callback) => {
// clear current session
// this is called by menu.end()
delete sessions[sessionId];
callback();
},
set: (sessionId, key, value, callback) => {
// store key-value pair in current session
sessions[sessionId][key] = value;
callback();
},
get: (sessionId, key, callback) => {
// retrieve value by key in current session
let value = sessions[sessionId][key];
callback(null, value);
},
});

module.exports = menu;

The code above is illustrated by the flow chart diagram below;

Create a folder called Public and add an html file inside

mkdir public
cd public
touch index.html

This is where you put static assets that can be viewed by everybody. Paste the following code to your index.html file

Testing the App

Go to your terminal and run

npm run dev

Your browser should open the project at http://localhost:5000/
Go to your postman application, create a new collection and add a new POST Request. Run the project on your local machine, then copy the url on your browser, paste the url on postman,
Add the following request body below then send the request to test your application.

{
“phoneNumber”: “254700000002”,
“sessionId”: “abcdef123456”,
“serviceCode”: “”,
“operator”: “”,
“text”: “”
}

Note: A string containing the input at each hop, is separated by the asterisk symbol (*). E.g

“text”:”1*2*3″

for menu navigation. This is parsed by the UssdMenu to find the appropriate state to run at each hop.