266 lines
8.5 KiB
TypeScript
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();
|
|
}
|
|
}
|
|
|
|
}
|