Files
cbsnexora/src/app/home/dashboard/client-trends-bar/client-trends-bar.component.ts
T

266 lines
8.5 KiB
TypeScript

/** Angular Imports */
import { Component, OnInit } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
/** rxjs Imports */
import { forkJoin, merge } from 'rxjs';
import { skip } from 'rxjs/operators';
/** Custom Services */
import { HomeService } from '../../home.service';
/** Charting Imports */
import { Dates } from 'app/core/utils/dates';
import Chart from 'chart.js';
/**
* Client Trends Bar Chart Component.
*/
@Component({
selector: 'mifosx-client-trends-bar',
templateUrl: './client-trends-bar.component.html',
styleUrls: ['./client-trends-bar.component.scss']
})
export class ClientTrendsBarComponent implements OnInit {
/** Static Form control for office Id */
officeId = new UntypedFormControl();
/** Static Form control for time scale */
timescale = new UntypedFormControl();
/** Office Data */
officeData: any;
/** Chart.js chart */
chart: any;
/** Substitute for resolver */
hideOutput = true;
/**
* Fetches offices data from `resolve`
* @param {HomeService} homeService Home Service
* @param {ActivatedRoute} route Activated Route
* @param {Dates} dateUtils Date Utils
*/
constructor(private homeService: HomeService,
private route: ActivatedRoute,
private dateUtils: Dates) {
this.route.data.subscribe( (data: { offices: any }) => {
this.officeData = data.offices;
});
}
ngOnInit() {
this.getChartData();
this.initializeControls();
}
/**
* Initialize the form controls for better UX.
*/
initializeControls() {
this.officeId.patchValue(1);
this.timescale.patchValue('Day');
}
/**
* Subscribes to value changes of officeID and timescale controls,
* Fetches data accordingly and sets charts based on fetched data.
*/
getChartData() {
merge(this.officeId.valueChanges, this.timescale.valueChanges).pipe(skip(1))
.subscribe(() => {
const officeId = this.officeId.value;
const timescale = this.timescale.value;
switch (timescale) {
case 'Day':
const clientsByDay = this.homeService.getClientTrendsByDay(officeId);
const loansByDay = this.homeService.getLoanTrendsByDay(officeId);
forkJoin([clientsByDay, loansByDay]).subscribe((data: any[]) => {
const dayLabels = this.getLabels(timescale);
const clientCounts = this.getCounts(data[0], dayLabels, timescale, 'client');
const loanCounts = this.getCounts(data[1], dayLabels, timescale, 'loan');
this.setChart(dayLabels, clientCounts, loanCounts);
this.hideOutput = false;
});
break;
case 'Week':
const clientsByWeek = this.homeService.getClientTrendsByWeek(officeId);
const loansByWeek = this.homeService.getLoanTrendsByWeek(officeId);
forkJoin([clientsByWeek, loansByWeek]).subscribe((data: any[]) => {
const weekLabels = this.getLabels(timescale);
const clientCounts = this.getCounts(data[0], weekLabels, timescale, 'client');
const loanCounts = this.getCounts(data[1], weekLabels, timescale, 'loan');
this.setChart(weekLabels, clientCounts, loanCounts);
this.hideOutput = false;
});
break;
case 'Month':
const clientsByMonth = this.homeService.getClientTrendsByMonth(officeId);
const loansByMonth = this.homeService.getLoanTrendsByMonth(officeId);
forkJoin([clientsByMonth, loansByMonth]).subscribe((data: any[]) => {
const monthLabels = this.getLabels(timescale);
const clientCounts = this.getCounts(data[0], monthLabels, timescale, 'client');
const loanCounts = this.getCounts(data[1], monthLabels, timescale, 'loan');
this.setChart(monthLabels, clientCounts, loanCounts);
this.hideOutput = false;
});
break;
}
});
}
/**
* Gets Abscissa Labels.
* @param {string} timescale User's timescale choice.
*/
getLabels(timescale: string) {
const date = new Date();
const labelsArray = [];
switch (timescale) {
case 'Day':
while (labelsArray.length < 12) {
date.setDate(date.getDate() - 1);
const transformedDate = this.dateUtils.formatDate(date, 'd/M');
labelsArray.push(transformedDate);
}
break;
case 'Week':
/** 1st January of present year */
const onejan = new Date(date.getFullYear(), 0, 1);
while (labelsArray.length < 12) {
date.setDate(date.getDate() - 7);
/** Gets current week number */
const weekNumber = Math.ceil(
(((date.getTime() - onejan.getTime()) / 86400000) + onejan.getDay() + 1) / 7
);
labelsArray.push(weekNumber);
}
break;
case 'Month':
while (labelsArray.length < 12) {
const transformedDate = this.dateUtils.formatDate(date, 'MMMM');
labelsArray.push(transformedDate);
date.setMonth(date.getMonth() - 1);
}
break;
}
return labelsArray.reverse();
}
/**
* Get bar heights for clients/loans trends.
* @param {any[]} response API response array.
* @param {any[]} labels Abscissa Labels.
* @param {string} timescale User's timescale choice.
* @param {string} type 'client' or 'loan'.
*/
getCounts(response: any[], labels: any[], timescale: string, type: string) {
let counts: number[] = [];
switch (timescale) {
case 'Day':
labels.forEach((label: any) => {
const day = response.find((entry: any) => {
const transformedDate = this.dateUtils.formatDate(entry.days, 'd/M');
return transformedDate === label;
});
counts = this.updateCount(day, counts, type);
});
break;
case 'Week':
labels.forEach((label: any) => {
const week = response.find((entry: any) => {
return entry.Weeks === label;
});
counts = this.updateCount(week, counts, type);
});
break;
case 'Month':
labels.forEach((label: any) => {
const month = response.find((entry: any) => {
return entry.Months === label;
});
counts = this.updateCount(month, counts, type);
});
break;
}
return counts;
}
/**
* Updates the counts array.
* @param {any} span Time span.
* @param {any[]} counts Counts.
* @param {string} type 'client' or 'loan'
*/
updateCount(span: any, counts: any[], type: string) {
if (span) {
switch (type) {
case 'client':
counts.push(span.count);
break;
case 'loan':
counts.push(span.lcount);
break;
}
} else {
counts.push(0);
}
return counts;
}
/**
* Creates an instance of Chart.js multi-bar chart.
* Refer: https://www.chartjs.org/docs/latest/charts/bar.html for configuration details.
* @param {any[]} labels Abscissa Labels.
* @param {number[]} clientCounts Clients Ordinate.
* @param {number[]} loanCounts Loans Ordinate.
*/
setChart(labels: any[], clientCounts: number[], loanCounts: number[]) {
if (!this.chart) {
this.chart = new Chart('client-trends-bar', {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'New Clients',
data: clientCounts,
backgroundColor: 'dodgerblue',
borderColor: 'dodgerblue',
borderWidth: 2,
fill: false
},
{
label: 'Loans Disbursed',
data: loanCounts,
backgroundColor: 'red',
borderColor: 'red',
borderWidth: 2,
fill: false,
}
]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
scaleLabel: {
display: true,
labelString: 'Values',
fontColor: '#1074B9'
}
},
}
}
});
} else {
this.chart.data.labels = labels;
this.chart.data.datasets[0].data = clientCounts;
this.chart.data.datasets[1].data = loanCounts;
this.chart.update();
}
}
}