This is How we Build a CRM Application using Angular!

crm app

Customer Relationship Management (CRM) helps startups, SMEs, and big brands in managing data, business relationships; this ultimately improves business sales growth.

CRM application and software generally works with different sources, including business website analytics, phone history, social media analytics tools, and email services. Mainly sales managers, marketers, support agents, and brand analytics make use of CRM apps.

If I talk about CRM software related stats, then, the custom and non-custom CRM software market are expected to grow up to $40.26 billion by 2023. If I talk about companies using CRM, then 47% of corporations have 90% ready-made CRM adoption rate.

Img Src: Statista

Here, I have covered the following pointers:

  • What Are The Benefits of Custom CRM Solutions?
  • How do we Build a CRM Application?
  • How To Build a CRM Application By Using AngularJS?

What Are The Benefits of Custom CRM Solutions?

CRM software is mainly built to help enterprises, attract new customers and retain current ones. Let’s read a few benefits of CRM Solutions:

Introducing Features Useful for Your Business

It is better to build a custom CRM app as this helps in saving monthly wages and also allows you to introduce features that are beneficial for your business. When we use ready-made CRM software or apps then we have to pay for the features that we are not utilizing so somewhere it will be beneficial to build your own CRM application.

Workflow Automation

Usually, Custom CRM apps automate marketing, sales, analytics, customer support, and billing tasks save money. The businesses can freely utilize customized CRM apps as it does not hold any criteria of using software like ready-made apps. Generally, in specific sort applications automation is not always possible.

Centralized data collection and editing

Custom-built CRM app provides the full power of observing what is happening inside the organization (on the digital level). Ready-made software and applications also offer the monitoring function, but it does not provide full control of visualizing the data.

Customer Related Analytics

Most of the time, ready-made apps or software does not allow you to explore customer insights, then later this issue generally becomes the primary concern, but this sort of problem has not seemed in custom CRM apps.

How do we Build a CRM Application?

We have built and worked on several custom CRM applications. Custom-built CRM apps usually handle the sales process and customer interaction. In this type of application, numerous advanced features like voice calling, chat, report generation are introduced; this finally simplifies the organization task.

For building the CRM Application, we mainly use AngularJS. We have also used various other languages such as MYSQL, EXTJS, FLEX, GIT, HTML5, JAVA, LASSO, MYSQL, SPRING for developing CRM Application.

Leading Solutions that We Use To Build CRM App

  • We have built custom reports, and in that section, we also united VOIP calling API & chat API for VOIP Calling & Group Chat.
  • We develop the application in which sales administrators can access their personalized accounts; for this, we add a login system in an app.
  • To represent the data in an application, we produced a reports segment that formed multiple types of charts like area chart, bar graph, timelines.

Key Challenges That We Faced While Developing CRM Application

  • During the development of CRM applications generating Graphical representation, reports were the major challenge. 
  • Working on VOIP Calling & Group Chat features was also quite challenging.

Final Outcome That We Produced

In the end, we produced the user-friendly CRM Application that included the following features.

  • One-One Chat
  • Group Chat
  • VOIP Call
  • VOIP Contacts

Let’s View Some Images of Our Developed CRM Application:




Other CRM Application that we Created

Chat App Dashboard


Business Process Management Custom CRM Application


Database Searching App


How To Build a CRM Application By Using AngularJS?

In order to Build a CRM Application By Using AngularJS follow the procedure stated below, this will support you when you start developing your app. 

What you need to Install and why?

  1. You need to install the AngularJS framework to make a Single-page application.
  2. For creating multi-views, you require to install angular-ui-router.
  3. You need to use gulp for building all JS and CSS files into an individual file.

Firstly create a new directory to build a CRM conferred based project and then after that, create a package.json file.

The coding for package.json will look like:

mkdir conference-app
conference-app$ touch package.json
{
"name": "conf-app",
"version": "1.0.0",
"main": "app-server.js",
"engines": {
"node": "4.1.2",
"npm": "3.5.2"
},
"description": "",
"dependencies": {
"body-parser": "^1.17.2",
"bower": "^1.7.7",
"buffer-to-vinyl": "^1.1.0",
"express": "^4.13.3",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
"gulp-concat": "^2.6.0",
"gulp-concat-css": "^2.2.0",
"gulp-env": "^0.4.0",
"gulp-minify-css": "^1.2.4",
"gulp-ng-config": "^1.4.0",
"gulp-npm-script-sync": "^1.1.0",
"gulp-webserver": "^0.9.1",
"http-server": "^0.9.0",
"stripe": "^4.22.0",
"wiredep": "^3.0.0"
},

Read More:Top 10 Mobile Application Development companies startups can partner with in 2020

Secondly, you need to build bower.json file;

conference-app$ touch bower.json

We will build this feature for the CRM app project; its coding will look like:

{
"name": "conference-app",
"description": "Conference App Vinove",
"version": "0.0.0",
"homepage": "",
"license": "",
"private": true,
"dependencies": {
"angular": "~1.4.x",
"angular-mocks": "~1.4.x",
"angular-bootstrap": "~1.1.x",
"angular-cookies": "~1.4.x",
"angular-route": "~1.4.x",
"angular-ui-router": "0.2.x",
"angular-resource": "1.4.x",
"angular-animate": "~1.4.x",
"ng-dialog": "0.6.1",
"bootstrap": "3.3.x",
"cr-acl": "",
"angular-chosen-localytics": "*",
"bootstrap-chosen": "*",
"ng-flow": "^2.7.4",
"angular-mask": "*",
"checklist-model": "0.9.0",
"angular-ui-notification": "^0.2.0",
"angular-ui-calendar": "^1.0.2",
"angular-ui-switch": "^0.1.1",
"ng-scrollbars": "^0.0.11",
"jquery.scrollbar": "*",
"angular-nvd3": "*",
"infinity-angular-chosen": "^0.2.0",
"angular-flash-alert": "^2.4.0",
"components-font-awesome": "^4.7.0",
"textAngular": "^1.5.16",
"angular-loading-bar": "^0.9.0",
"angular-environment": "^1.0.8",
"angular-sticky": "angular-sticky-plugin#^0.3.0",
"smooth-scroll": ""
},
"resolutions": {
"angular": "~1.4.x"
},
"devDependencies": {
"cr-acl": "^0.5.0"
}
}

Configure server:

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.set('port', process.env.PORT || 3000)
app.use(express.static(__dirname))
app.use(bodyParser.json())
var http = require('http').Server(app)
// Route
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
})
http.listen(app.get('port'), () => {
console.log('Conference App listening on ' + app.get('port'))
})

Let’s Start Developing Conference CRM App

Now it’s time to create the project file structure that will help in organizing the angular modules and js files.

Now it’s time to set up index.html:

<!DOCTYPE html>
<html lang="en" ng-app="app"><head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title></title>

<!-- bower:css -->
<!-- endbower -->

<!-- Bootstrap Core CSS -->
<link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">

<!-- Custom CSS -->

<link href="dist/css/main.min.css" rel="stylesheet">

<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->

</head>

<body>

<div ui-view></div>

<!-- bower:js -->
<!-- endbower -->
<script src="bower_components/angular-stripe-checkout/angular-stripe-checkout.js"></script>

<!--<script src="https://checkout.stripe.com/checkout.js"></script>-->

<script src="dist/js/main.js"></script>
<script src="dist/js/conference.templ.js"></script>

</body>

</html>

Here, we are going to target the “root” view to place our angular modules in later. After bundling all the angular modules; the foremost.js file placed in the dist directory will spit out gulpfile.js file. If you are facing the issue in handling the app coding part, then you can hire a programmer from the recognized software outsourcing company.

Set up the gulpfile.js. This feature will be created for the application:

'use strict';var gulp = require('gulp'),
webserver = require('gulp-webserver'),
minifyCSS = require('gulp-minify-css'),
concat = require('gulp-concat'),
wiredep = require('wiredep').stream,
gulpNgConfig = require('gulp-ng-config'),
autoprefixer = require('gulp-autoprefixer'),
b2v = require('buffer-to-vinyl'),
sync = require('gulp-npm-script-sync');

sync(gulp);

gulp.task('css', function () {
return gulp.src('css/**/*.css')
.pipe(minifyCSS())
.pipe(concat('main.min.css'))
.pipe(autoprefixer())
.pipe(gulp.dest('dist/css'));
});

gulp.task('js', function() {
return gulp.src('app/**/**/*.js')
.pipe(concat('main.js'))
.pipe(gulp.dest('dist/js/'));
});

gulp.task('config', function () {
const json = JSON.stringify({
BUCKET_SLUG: process.env.COSMIC_BUCKET,
MEDIA_URL: 'https://api.cosmicjs.com/v1/' + process.env.COSMIC_BUCKET + '/media',
URL: 'https://api.cosmicjs.com/v1/',
READ_KEY: process.env.COSMIC_READ_KEY || '',
WRITE_KEY: process.env.COSMIC_WRITE_KEY || ''
});
return b2v.stream(new Buffer(json), 'config.js')
.pipe(gulpNgConfig('config'))
.pipe(gulp.dest('app'));
});

gulp.task('default', function () {
gulp.watch('css/**/*.css', ['css']);
gulp.watch('app/**/**/*.js', ['js']);
gulp.watch('bower.json', ['bower']);
});

gulp.task('bower', function () {
gulp.src('index.html')
.pipe(wiredep({
directory: 'bower_components'
}))
.pipe(gulp.dest(''));
});

What did You need to Do?

  • Use UI-router for config routes.
  • For asynchronous calls to Cosmic JS API create Auth Service.
  • For examining, credentials create the Auth Controller.

After this, create the main module. Code for main.mdl.js file:

(function () {
'use strict';
angular
.module('app', [
'ui.router',
'ui.bootstrap',
'ngMask',
'ngCookies',
'ngRoute',
'ngDialog',
'ngAnimate',
'cr.acl',
'ui-notification',
'ngFlash',
'textAngular',
'flow',
'angular-loading-bar',
'hl.sticky',
'about',
'speakers',
'schedule',
'partner',
'register',
'admin',
'config'
])
.config(config)
.run(run);config.$inject = ['$stateProvider', '$urlRouterProvider', 'cfpLoadingBarProvider', 'NotificationProvider'];
function config($stateProvider, $urlRouterProvider, cfpLoadingBarProvider, NotificationProvider) {
cfpLoadingBarProvider.includeSpinner = false;

NotificationProvider.setOptions({
startTop: 25,
startRight: 25,
verticalSpacing: 20,
horizontalSpacing: 20,
positionX: 'right',
positionY: 'top'
});

$urlRouterProvider.otherwise(function ($injector) {
var $state = $injector.get("$state");
var $location = $injector.get("$location");
var crAcl = $injector.get("crAcl");

var state = "";

switch (crAcl.getRole()) {
case 'ROLE_ADMIN':
state = 'admin.pages';
break;After this, create the main module. Code for main.mdl.js file:
default : state = 'app.about';
}

if (state) $state.go(state);
else $location.path('/');
});

$stateProvider
.state('app', {
url: '/',
abstract: true,
templateUrl: '../views/main.html',
controller: 'GlobalCtrl as vm',
data: {
is_granted: ['ROLE_GUEST']
}
})
.state('blog', {
url: '/blog',
templateUrl: '../blog.html'
})
.state('auth', {
url: '/login',
templateUrl: '../views/auth/login.html',
controller: 'AutherizationCtrl as auth',
onEnter: ['AutherizationService', 'crAcl', function(AutherizationService, crAcl) {
AutherizationService.clearCredentials();
crAcl.setRole();
}],
data: {
is_granted: ['ROLE_GUEST']
}
});
}

run.$inject = ['$rootScope', '$cookieStore', '$state', 'crAcl', '$window'];
function run($rootScope, $cookieStore, $state, crAcl, $window) {
// keeping user logged in after refresh
$rootScope.globals = $cookieStore.get('globals') || {};

crAcl
.setInheritanceRoles({
'ROLE_ADMIN': ['ROLE_ADMIN', 'ROLE_GUEST'],
'ROLE_GUEST': ['ROLE_GUEST']
});

crAcl
.setRedirect('auth');

if ($rootScope.globals.currentUser) {
crAcl.setRole('ROLE_ADMIN');
}
else {
crAcl.setRole();
}

$rootScope.$on('$stateChangeSuccess', function (event, current, previous) {
$window.document.title = current.title ? current.title + ' - ANGULAR CONFERENCE' : 'ANGULAR CONFERENCE';
});

}

})();

Also Read: BEST APP DEVELOPMENT PRACTICES TO FOLLOW IN 2020

After building main.mdl.js file set up Auth Controller. Code of auth.ctrl.js file will look like:

(function () {
'use strict';angular
.module('app')
.controller('AutherizationCtrl', AutherizationCtrl);

function AutherizationCtrl(crAcl, $state, AutherizationService, Flash, $log) {
var vm = this;

vm.login = login;

vm.showRegisterForm = false;

vm.loginForm = null;

vm.credentials = {};
vm.user = {};

function login(credentials) {
function success(response) {
function success(response) {
if (response.data.status !== 'empty') {
var currentUser = response.data.objects[0];

crAcl.setRole('ROLE_ADMIN');
AutherizationService.setCredentials(currentUser);
$state.go('admin.pages');
}
else
Flash.create('danger', 'Username or password is incorrect');
}

function failed(response) {
$log.error(response);
}

if (response.data.status !== 'empty')
AutherizationService
.checkPassword(credentials)
.then(success, failed);
else
Flash.create('danger', 'Username or password is incorrect');

$log.info(response);
}

function failed(response) {
$log.error(response);
}

if (vm.loginForm.$valid)
AutherizationService
.checkUsername(credentials)
.then(success, failed);
}

}
})();

Now create the Auth Service for the app:

(function () {
'use strict';angular
.module('app')
.service('AutherizationService', function ($http,
$cookieStore,
$q,
$rootScope,
URL, BUCKET_SLUG, READ_KEY, WRITE_KEY) {
var autherizationService = this;
$http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

autherizationService.checkUsername = function (credentials) {
return $http.get(URL + BUCKET_SLUG + '/object-type/users/search', {
params: {
metafield_key: 'email',
metafield_value_has: credentials.email,
limit: 1,
read_key: READ_KEY
}
});
};
autherizationService.checkPassword = function (credentials) {
return $http.get(URL + BUCKET_SLUG + '/object-type/users/search', {
ignoreLoadingBar: true,
params: {
metafield_key: 'password',
metafield_value: credentials.password,
limit: 1,
read_key: READ_KEY
}
});
};
autherizationService.setCredentials = function (user) {
$rootScope.globals = {
currentUser: user
};

$cookieStore.put('globals', $rootScope.globals);
};
autherizationService.clearCredentials = function () {
var deferred = $q.defer();
$cookieStore.remove('globals');

if (!$cookieStore.get('globals')) {
$rootScope.globals = {};
deferred.resolve('Credentials clear success');
} else {
deferred.reject('Can\'t clear credentials');
}

return deferred.promise;
};
});
})();

Now You Might Be Thinking What Will Be The Next Step?

  1. For asynchronous calls to our Cosmic JS API, it’s time to develop About Service.
  2. For getting content and updating its time build About Controller.
  3. Create an About Module.

Build User Service to update User; it’s coding part will look like:

(function () {
'use strict';angular
.module('app')
.service('UserService', function ($http,
$cookieStore,
$q,
$rootScope,
URL, BUCKET_SLUG, READ_KEY, WRITE_KEY) {
$http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

this.getCurrentUser = function (ignoreLoadingBar) {
return $http.get(URL + BUCKET_SLUG + '/object/' + $rootScope.globals.currentUser.slug, {
ignoreLoadingBar: ignoreLoadingBar,
params: {
read_key: READ_KEY
}
});
};
this.getUser = function (slug, ignoreLoadingBar) {
return $http.get(URL + BUCKET_SLUG + '/object/' + slug, {
ignoreLoadingBar: ignoreLoadingBar,
params: {
read_key: READ_KEY
}
});
};
this.updateUser = function (user) {
user.write_key = WRITE_KEY;

return $http.put(URL + BUCKET_SLUG + '/edit-object', user, {
ignoreLoadingBar: false
});
};

});
})();

Build About Service to get  updated content from Cosmic JS API; it’s coding part will look like:

(function () {
'use strict';angular
.module('app')
.service('AboutService', function ($http,
$cookieStore,
$q,
$rootScope,
URL, BUCKET_SLUG, READ_KEY, WRITE_KEY, MEDIA_URL) {

$http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

this.getPages = function (params) {
return $http.get(URL + BUCKET_SLUG + '/object-type/pages', {
params: {
limit: 100,
read_key: READ_KEY
}
});
};
this.updatePages = function (page) {
page.write_key = WRITE_KEY;

return $http.put(URL + BUCKET_SLUG + '/edit-object', page);
};
});
})();

Build About Controller to get  updated content; it’s coding part will look like:

(function () {
'use strict';angular
.module('app')
.controller('AboutController', AboutController);

function AboutController($stateParams, AboutService, Notification, $log, MEDIA_URL, $state) {
var vm = this;

init();

vm.toolbarEditor = [
['h1', 'h2', 'h3', 'h4', 'h5', 'p', 'bold', 'italics', 'underline', 'justifyLeft', 'justifyCenter', 'justifyRight', 'html']
];

vm.save = save;

function init() {
getPages();
}

function getPages() {
function success(response) {
vm.pages = response.data.objects;
}

function error(response) {
$log.error(response.data);
}

AboutService
.getPages()
.then(success, error);
}

function save(index) {
function success() {
Notification.primary('Update Page "' + vm.pages[index].title + '" success!');
}

function error(response) {
$log.error(response.data);
}

AboutService
.updatePages(vm.pages[index])
.then(success, error);
}
}
})();

Build About Module, it’s coding part will look like:

(function () {
'use strict';angular
.module('about', [])
.config(config);

config.$inject = ['$stateProvider', '$urlRouterProvider'];
function config($stateProvider, $urlRouterProvider) {

$stateProvider
.state('app.about', {
url: '',
templateUrl: '../views/about/about.html',
controller: 'AboutController as vm'
});
}
})();

Now What To Do?

Now it’s time to focus on:

  1. Information about speakers
  2. CRUD speakers
  3. Avatar to speaker upload 

For CRUD Speakers Design Speakers Controller, this feature you can use in the app

(function () {
'use strict';angular
.module('app')
.controller('SpeakersController', SpeakersController);

function SpeakersController($rootScope, SpeakersService, Notification, $log, MEDIA_URL, $state) {
var vm = this;

init();

vm.removeSpeaker = removeSpeaker;

function init() {
getSpeakers();
}

function getSpeakers() {
function success(response) {
vm.speakers = response.data.objects;
}

function error(response) {
$log.error(response.data);
}

SpeakersService
.getSpeakers()
.then(success, error);
}

function removeSpeaker(slug) {
function success() {
getSpeakers();
Notification.primary('Removed!');
}

function error(response) {
$log.error(response.data);
}

SpeakersService
.removeSpeaker(slug)
.then(success, error);
}

}
})();

For introducing Speakers Module feature in the app:

(function () {
'use strict';

angular
.module('speakers', [])
.config(config);

config.$inject = ['$stateProvider', '$urlRouterProvider'];
function config($stateProvider, $urlRouterProvider) {

$stateProvider
.state('app.speakers', {
url: 'speakers',
title: 'Speakers',
templateUrl: '../views/speakers/speakers.html',
controller: 'SpeakersController as vm'
});
}
})();

Build Speakers Service for CRUD from Cosmic JS API, it’s coding will look like:

(function () {
'use strict';angular
.module('app')
.service('SpeakersService', function ($http,
$cookieStore,
$q,
$rootScope,
URL, BUCKET_SLUG, READ_KEY, WRITE_KEY, MEDIA_URL) {

$http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

this.speaker = {
"title": null,
"type_slug": "speakers",
"metafields": [
{
"value":null,
"key": "profession",
"title": "Profession",
"type": "text"
},
{
"value": null,
"key": "avatar",
"title": "Avatar",
"type": "file"
}
]
};

this.getSpeakers = function () {
return $http.get(URL + BUCKET_SLUG + '/object-type/speakers', {
params: {
limit: 100,
read_key: READ_KEY
}
});
};
this.getSpeakerBySlug = function (slug) {
return $http.get(URL + BUCKET_SLUG + '/object/' + slug, {
params: {
read_key: READ_KEY
}
});
};
this.updateSpeaker = function (speaker) {
speaker.write_key = WRITE_KEY;

return $http.put(URL + BUCKET_SLUG + '/edit-object', speaker);
};
this.removeSpeaker = function (slug) {
return $http.delete(URL + BUCKET_SLUG + '/' + slug, {
ignoreLoadingBar: true,
headers:{
'Content-Type': 'application/json'
},
data: {
write_key: WRITE_KEY
}
});
};
this.createSpeaker = function (speaker) {
speaker.write_key = WRITE_KEY;

return $http.post(URL + BUCKET_SLUG + '/add-object', speaker);
};
this.upload = function (file) {
var fd = new FormData();

fd.append('media', file);
fd.append('write_key', WRITE_KEY);

var defer = $q.defer();

var xhttp = new XMLHttpRequest();

xhttp.upload.addEventListener("progress",function (e) {
defer.notify(parseInt(e.loaded * 100 / e.total));
});
xhttp.upload.addEventListener("error",function (e) {
defer.reject(e);
});

xhttp.onreadystatechange = function() {
if (xhttp.readyState === 4) {
defer.resolve(JSON.parse(xhttp.response)); //Outputs a DOMString by default
}
};

xhttp.open("post", MEDIA_URL, true);

xhttp.send(fd);

return defer.promise;
}
});
})();

Now it’s time to celebrate as our coding part, and CRM app building with AngularJS get completed. In this way, you can also easily create a single page application.

Wrapping Up

This piece will help you in building the AngularJS based Lead Management CRM Application. If in case, you are having any sort of doubt or you are facing issues while working with the coding part, then by not wasting time avail the Custom CRM App service from the reputed Software Outsourcing Company, i.e. ValueCoders. This will help you to Hire AngularJS developers for your next project. 

By taking help from the experts, you will be able to develop the perfect application, and even you will be able to implement new features in your custom CRM Application.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.