@@ -0,0 +1,10 @@
|
||||
<div class="m-b-20" fxLayoutAlign="end">
|
||||
<mat-button-toggle-group aria-label="Select Chart Type">
|
||||
<mat-button-toggle value="Bar" (click) = "setBarChart(inputData)">Bar Chart</mat-button-toggle>
|
||||
<mat-button-toggle value="Pie" (click) = "setPieChart(inputData)">Pie Chart</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
|
||||
<div [ngStyle]="{'display': (hideOutput) ? 'none' : 'block'}">
|
||||
<canvas id="output"></canvas>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ChartComponent } from './chart.component';
|
||||
|
||||
describe('ChartComponent', () => {
|
||||
let component: ChartComponent;
|
||||
let fixture: ComponentFixture<ChartComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ChartComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ChartComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnChanges, Input } from '@angular/core';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from '../../reports.service';
|
||||
|
||||
/** Custom Models */
|
||||
import { ChartData } from '../../common-models/chart-data.model';
|
||||
|
||||
/** Charting Imports */
|
||||
import Chart from 'chart.js';
|
||||
|
||||
/**
|
||||
* Chart Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-chart',
|
||||
templateUrl: './chart.component.html',
|
||||
styleUrls: ['./chart.component.scss' ]
|
||||
})
|
||||
export class ChartComponent implements OnChanges {
|
||||
|
||||
/** Run Report Data */
|
||||
@Input() dataObject: any;
|
||||
|
||||
/** chart data object */
|
||||
chart: any;
|
||||
/** substitute for resolver */
|
||||
hideOutput = true;
|
||||
/** Data object for witching charts in view. */
|
||||
inputData: ChartData;
|
||||
|
||||
/**
|
||||
* @param {ReportsService} reportsService Reports Service
|
||||
*/
|
||||
constructor(private reportsService: ReportsService) { }
|
||||
|
||||
/**
|
||||
* Fetches run report data post changes in run report form.
|
||||
*/
|
||||
ngOnChanges() {
|
||||
this.getRunReportData();
|
||||
}
|
||||
|
||||
getRunReportData() {
|
||||
this.reportsService.getChartRunReportData(this.dataObject.reportData, this.dataObject.formData)
|
||||
.subscribe((response: ChartData) => {
|
||||
this.inputData = response;
|
||||
this.setPieChart(this.inputData);
|
||||
this.hideOutput = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance of chart.js pie chart.
|
||||
* Refer: https://www.chartjs.org/docs/latest/charts/doughnut.html for configuration details.
|
||||
*/
|
||||
setPieChart(inputData: ChartData) {
|
||||
if (this.chart) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
this.chart = new Chart('output', {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: inputData.keys,
|
||||
datasets: [{
|
||||
label: inputData.valuesLabel,
|
||||
data: inputData.values,
|
||||
backgroundColor: this.randomColorArray(inputData.values.length)
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: true,
|
||||
text: inputData.keysLabel
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance of chart.js bar chart.
|
||||
* Refer: https://www.chartjs.org/docs/latest/charts/bar.html for configuration details.
|
||||
*/
|
||||
setBarChart(inputData: ChartData) {
|
||||
if (this.chart) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
this.chart = new Chart('output', {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: inputData.keys,
|
||||
datasets: [{
|
||||
label: inputData.valuesLabel,
|
||||
data: inputData.values,
|
||||
backgroundColor: this.randomColorArray(inputData.values.length)
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
legend: { display: false },
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: inputData.keysLabel
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates bar/pie-slice colors array for dynamic charts.
|
||||
* @param {number} length Length of dataset array.
|
||||
*/
|
||||
randomColorArray(length: number) {
|
||||
const colorArray: any[] = [];
|
||||
while (length--) {
|
||||
const color = this.randomColor();
|
||||
colorArray.push(color);
|
||||
}
|
||||
return colorArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random rgb color.
|
||||
*/
|
||||
randomColor() {
|
||||
const r = Math.floor(Math.random() * 255);
|
||||
const g = Math.floor(Math.random() * 255);
|
||||
const b = Math.floor(Math.random() * 255);
|
||||
return `rgb(${r},${g},${b},0.6)`;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<iframe *ngIf = "!hideOutput" [src]="pentahoUrl" frameborder="0" width=100% height="750px;"></iframe>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PentahoComponent } from './pentaho.component';
|
||||
|
||||
describe('PentahoComponent', () => {
|
||||
let component: PentahoComponent;
|
||||
let fixture: ComponentFixture<PentahoComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ PentahoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PentahoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnChanges, Input } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from '../../reports.service';
|
||||
|
||||
/**
|
||||
* Pentaho Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-pentaho',
|
||||
templateUrl: './pentaho.component.html',
|
||||
styleUrls: ['./pentaho.component.scss']
|
||||
})
|
||||
export class PentahoComponent implements OnChanges {
|
||||
|
||||
/** Run Report Data */
|
||||
@Input() dataObject: any;
|
||||
|
||||
/** substitute for resolver */
|
||||
hideOutput = true;
|
||||
/** trusted resource url for pentaho output */
|
||||
pentahoUrl: any;
|
||||
|
||||
/**
|
||||
* @param {DomSanitizer} sanitizer DOM Sanitizer
|
||||
* @param {ReportsService} reportsService Reports Service
|
||||
*/
|
||||
constructor(private sanitizer: DomSanitizer,
|
||||
private reportsService: ReportsService) { }
|
||||
|
||||
/**
|
||||
* Fetches run report data post changes in run report form.
|
||||
*/
|
||||
ngOnChanges() {
|
||||
this.hideOutput = true;
|
||||
this.getRunReportData();
|
||||
}
|
||||
|
||||
getRunReportData() {
|
||||
this.reportsService.getPentahoRunReportData(this.dataObject.reportData, this.dataObject.formData, 'default', 'en', 'dd MMMM yyyy')
|
||||
.subscribe( (res: any) => {
|
||||
const contentType = res.headers.get('Content-Type');
|
||||
const file = new Blob([res.body], {type: contentType});
|
||||
const filecontent = URL.createObjectURL(file);
|
||||
this.pentahoUrl = this.sanitizer.bypassSecurityTrustResourceUrl(filecontent);
|
||||
this.hideOutput = false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<div class="container input">
|
||||
|
||||
<mat-card *ngIf="!isCollapsed">
|
||||
|
||||
<form [formGroup]="ReportForm" (ngSubmit)="run()">
|
||||
|
||||
<mat-card-content>
|
||||
|
||||
<div fxLayout="column" *ngFor="let param of paramData">
|
||||
|
||||
<div [ngSwitch]="param.displayType">
|
||||
|
||||
<div *ngIf="ReportForm.contains(param.name)">
|
||||
<mat-form-field fxFlex="100%" *ngSwitchCase="'text'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<input matInput required [formControlName]="param.name">
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="ReportForm.contains(param.name)">
|
||||
<mat-form-field fxFlex="100%" *ngSwitchCase="'date'">
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="runReportDatePicker" required [formControlName]="param.name">
|
||||
<mat-datepicker-toggle matSuffix [for]="runReportDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #runReportDatePicker></mat-datepicker>
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="ReportForm.contains(param.name) ">
|
||||
<mat-form-field fxFlex="100%" *ngSwitchCase="'select'" >
|
||||
<mat-label>{{param.label}}</mat-label>
|
||||
<mat-select required [formControlName]="param.name">
|
||||
<mat-option *ngFor="let option of param.selectOptions" [value]="option" >
|
||||
{{option.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="ReportForm.controls[param.name].hasError('required')">
|
||||
{{param.label}} is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field fxFlex="100%">
|
||||
<mat-label>Decimal Places</mat-label>
|
||||
<mat-select [formControl]="decimalChoice">
|
||||
<mat-option value="0">0</mat-option>
|
||||
<mat-option value="1">1</mat-option>
|
||||
<mat-option value="2">2</mat-option>
|
||||
<mat-option value="3">3</mat-option>
|
||||
<mat-option value="4">4</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="report.type === 'Pentaho' && ReportForm.contains('outputType')">
|
||||
<mat-form-field fxFlex="100%">
|
||||
<mat-label>Output Type</mat-label>
|
||||
<mat-select formControlName="outputType" value="HTML">
|
||||
<mat-option value="HTML">Normal format</mat-option>
|
||||
<mat-option value="XLS">Excel format</mat-option>
|
||||
<mat-option value="XLSX">Excel 2007 format</mat-option>
|
||||
<mat-option value="CSV">CSV format</mat-option>
|
||||
<mat-option value="PDF">PDF format</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['/reports']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!ReportForm.valid">
|
||||
<fa-icon icon="cogs"></fa-icon>
|
||||
Run Report
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</form>
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container output" *ngIf="isCollapsed">
|
||||
|
||||
<mat-card>
|
||||
|
||||
<div class="m-b-20">
|
||||
<button mat-raised-button color="primary" (click)="isCollapsed = false">
|
||||
Parameters
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mifosx-table-and-sms *ngIf="!hideTable"[dataObject]="dataObject"></mifosx-table-and-sms>
|
||||
<mifosx-chart *ngIf="!hideChart" [dataObject]="dataObject"></mifosx-chart>
|
||||
<mifosx-pentaho *ngIf="!hidePentaho" [dataObject]="dataObject"></mifosx-pentaho>
|
||||
</div>
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.input {
|
||||
max-width: 37rem;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RunReportComponent } from './run-report.component';
|
||||
|
||||
describe('RunReportComponent', () => {
|
||||
let component: RunReportComponent;
|
||||
let fixture: ComponentFixture<RunReportComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ RunReportComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RunReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,201 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from '../reports.service';
|
||||
|
||||
/** Custom Models */
|
||||
import { ReportParameter } from '../common-models/report-parameter.model';
|
||||
import { SelectOption } from '../common-models/select-option.model';
|
||||
|
||||
/**
|
||||
* Run report component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-run-report',
|
||||
templateUrl: './run-report.component.html',
|
||||
styleUrls: ['./run-report.component.scss']
|
||||
})
|
||||
export class RunReportComponent implements OnInit {
|
||||
|
||||
/** Minimum date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
/** Maximum date allowed. */
|
||||
maxDate = new Date();
|
||||
/** Contains report specifications i.e: name, type and id */
|
||||
report: any = {};
|
||||
/** Formatted data post labeling of report parameters fetched from API */
|
||||
paramData: any = [];
|
||||
/** Array of all parent parameters */
|
||||
parentParameters: any[] = [];
|
||||
/** Parameter data to configure pentaho output */
|
||||
pentahoReportParameters: any[] = [];
|
||||
/** Data to be passed on to component selectors */
|
||||
dataObject: any;
|
||||
/** Initializes new form group 'ReportForm */
|
||||
ReportForm = new FormGroup({});
|
||||
/** Static Form control for decimal places in output */
|
||||
decimalChoice = new FormControl();
|
||||
/** Toggles Report form */
|
||||
isCollapsed = false;
|
||||
/** Toggles Table output. */
|
||||
hideTable = true;
|
||||
/** Toggles Chart output */
|
||||
hideChart = true;
|
||||
/** Toggles Pentaho output */
|
||||
hidePentaho = true;
|
||||
|
||||
/**
|
||||
* Fetches report specifications from route params and retrieves report parameters data from `resolve`.
|
||||
* @param {ActivatedRoute} route ActivatedRoute.
|
||||
* @param {DomSanitizer} sanitizer DomSanitizer.
|
||||
* @param {ReportsService} reportsService ReportsService
|
||||
* @param {DatePipe} datePipe Date Pipe
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private reportsService: ReportsService,
|
||||
private datePipe: DatePipe) {
|
||||
this.report.name = this.route.snapshot.params['report-name'];
|
||||
this.report.type = this.route.snapshot.params['report-type'];
|
||||
this.report.id = this.route.snapshot.params['report-id'];
|
||||
this.route.data.subscribe((data: any) => {
|
||||
this.paramData = data.params;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets the run report form.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.createRunReportForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes form controls for Report Parameter's name attribute,
|
||||
* Fetches dropdown options and builds child dependencies.
|
||||
*/
|
||||
createRunReportForm() {
|
||||
this.paramData.forEach(
|
||||
(param: any) => {
|
||||
if (!param.parentParameterName) {
|
||||
this.ReportForm.addControl(param.name, new FormControl('', Validators.required));
|
||||
if (param.displayType === 'select') {
|
||||
this.fetchSelectOptions(param, param.name);
|
||||
}
|
||||
} else {
|
||||
const parent: ReportParameter = this.paramData
|
||||
.find((entry: any) => entry.name === param.parentParameterName);
|
||||
parent.childParameter = param;
|
||||
this.parentParameters.push(parent);
|
||||
}
|
||||
});
|
||||
if (this.report.type === 'Pentaho') {
|
||||
this.ReportForm.addControl('outputType', new FormControl(''));
|
||||
this.mapPentahoParams();
|
||||
}
|
||||
this.decimalChoice.patchValue('0');
|
||||
this.setChildControls();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Select Dropdown options for param type "Select".
|
||||
* @param {ReportParameter} param Parameter for which dropdown options are required.
|
||||
* @param {string} inputstring url substring for API call.
|
||||
*/
|
||||
fetchSelectOptions(param: ReportParameter, inputstring: string) {
|
||||
this.reportsService.getSelectOptions(inputstring).subscribe((options: SelectOption[]) => {
|
||||
param.selectOptions = options;
|
||||
if (param.selectAll === 'Y') {
|
||||
param.selectOptions.push({id: '-1', name: 'All'});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps pentaho specific names to form-control names.
|
||||
*/
|
||||
mapPentahoParams() {
|
||||
this.reportsService.getPentahoParams(this.report.id).subscribe((data: any) => {
|
||||
data.forEach((entry: any) => {
|
||||
const param: ReportParameter = this.paramData
|
||||
.find((_entry: any) => _entry.name === entry.parameterName);
|
||||
param.pentahoName = `R_${entry.reportParameterName}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to changes in parent parameters value, builds child parameter vis-a-vis parent's value.
|
||||
*/
|
||||
setChildControls() {
|
||||
this.parentParameters.forEach((param: ReportParameter) => {
|
||||
this.ReportForm.get(param.name).valueChanges.subscribe((option: any) => {
|
||||
this.ReportForm.addControl(param.childParameter.name, new FormControl('', Validators.required));
|
||||
if (param.childParameter.displayType === 'select') {
|
||||
const inputstring = `${param.childParameter.name}?${param.inputName}=${option.id}`;
|
||||
this.fetchSelectOptions(param.childParameter, inputstring);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats user response and readies it for utilization by run report function.
|
||||
* @param {any} response Object containing formcontrol values.
|
||||
*/
|
||||
formatUserResponse(response: any) {
|
||||
const formattedResponse: any = {};
|
||||
let newKey: string;
|
||||
for (const [key, value] of Object.entries(response)) {
|
||||
if (key === 'outputType') {
|
||||
formattedResponse['output-type'] = value;
|
||||
continue;
|
||||
}
|
||||
const param: ReportParameter = this.paramData
|
||||
.find((_entry: any) => _entry.name === key);
|
||||
newKey = this.report.type === 'Pentaho' ? param.pentahoName : param.inputName;
|
||||
switch (param.displayType) {
|
||||
case 'text':
|
||||
formattedResponse[newKey] = value;
|
||||
break;
|
||||
case 'select':
|
||||
formattedResponse[newKey] = value['id'];
|
||||
break;
|
||||
case 'date':
|
||||
const dateFormat = 'yyyy-MM-dd';
|
||||
formattedResponse[newKey] = this.datePipe.transform(value, dateFormat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return formattedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core run report functionality.
|
||||
*/
|
||||
run() {
|
||||
this.isCollapsed = true;
|
||||
const userResponse = this.formatUserResponse(this.ReportForm.value);
|
||||
this.dataObject = {
|
||||
formData: userResponse,
|
||||
reportData: this.report,
|
||||
decimalChoice: this.decimalChoice.value
|
||||
};
|
||||
switch (this.report.type) {
|
||||
case 'SMS':
|
||||
case 'Table':
|
||||
this.hideTable = false;
|
||||
break;
|
||||
case 'Chart':
|
||||
this.hideChart = false;
|
||||
break;
|
||||
case 'Pentaho':
|
||||
this.hidePentaho = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/** Angular Imports */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/** Custom Services */
|
||||
import { ReportsService } from '../reports.service';
|
||||
|
||||
/**
|
||||
* Reports data resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RunReportResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {ReportsService} reportsService Reports service.
|
||||
*/
|
||||
constructor(private reportsService: ReportsService) {}
|
||||
|
||||
/**
|
||||
* Returns the reports data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
return this.reportsService.getReportParams(route.params['report-name']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<div *ngIf = "!hideOutput">
|
||||
|
||||
<div class="m-b-10" *ngIf = "dataObject.reportData.type === 'Table'" fxLayoutAlign="end">
|
||||
<button mat-raised-button color="primary" (click)="downloadCSV()" >
|
||||
Export CSV
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-output">
|
||||
<table mat-table [dataSource]="dataSource">
|
||||
<div *ngFor= "let column of displayedColumns index as i">
|
||||
<ng-container [matColumnDef]="column">
|
||||
<th mat-header-cell *matHeaderCellDef>{{column}}</th>
|
||||
<td mat-cell *matCellDef="let param" >
|
||||
<span *ngIf="isDecimal(i)">{{toDecimal(param.row[i])}}</span>
|
||||
<span *ngIf="!isDecimal(i)">{{param.row[i]}}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
</div>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="select-row" ></tr>
|
||||
</table>
|
||||
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
.table-output {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.select-row:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mat-header-cell, .mat-cell {
|
||||
min-width: 150px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TableAndSmsComponent } from './table-and-sms.component';
|
||||
|
||||
describe('TableAndSmsComponent', () => {
|
||||
let component: TableAndSmsComponent;
|
||||
let fixture: ComponentFixture<TableAndSmsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ TableAndSmsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TableAndSmsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
/** Angular Imports */
|
||||
import { Component, Input, ViewChild, OnChanges } from '@angular/core';
|
||||
import { MatPaginator, MatTableDataSource} from '@angular/material';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
|
||||
/** Custom Servies */
|
||||
import { ReportsService } from '../../reports.service';
|
||||
|
||||
/**
|
||||
* Table and SMS Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-table-and-sms',
|
||||
templateUrl: './table-and-sms.component.html',
|
||||
styleUrls: ['./table-and-sms.component.scss']
|
||||
})
|
||||
export class TableAndSmsComponent implements OnChanges {
|
||||
|
||||
/** Run Report Data */
|
||||
@Input() dataObject: any;
|
||||
|
||||
/** Columns to be displayed in mat-table */
|
||||
displayedColumns: string[] = [];
|
||||
/** Data source for run-report table. */
|
||||
dataSource = new MatTableDataSource();
|
||||
/** Maps column name to type */
|
||||
columnTypes: any[] = [];
|
||||
/** substitute for resolver */
|
||||
hideOutput = true;
|
||||
/** Data to be converted into CSV file */
|
||||
csvData: any;
|
||||
|
||||
/** Paginator for run-report table. */
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
|
||||
/**
|
||||
* @param {ReportsService} reportsService Reports Service
|
||||
* @param {DecimalPipe} decimalPipe Decimal Pipe
|
||||
*/
|
||||
constructor(private reportsService: ReportsService,
|
||||
private decimalPipe: DecimalPipe ) { }
|
||||
|
||||
/**
|
||||
* Fetches run report data post changes in run report form.
|
||||
*/
|
||||
ngOnChanges() {
|
||||
this.hideOutput = true;
|
||||
this.columnTypes = [];
|
||||
this.displayedColumns = [];
|
||||
this.getRunReportData();
|
||||
}
|
||||
|
||||
getRunReportData() {
|
||||
this.reportsService.getRunReportData(this.dataObject.reportData, this.dataObject.formData)
|
||||
.subscribe( (res: any) => {
|
||||
this.csvData = res.data;
|
||||
this.setOutputTable(res.data);
|
||||
res.columnHeaders.forEach((header: any) => {
|
||||
this.columnTypes.push(header.columnDisplayType);
|
||||
this.displayedColumns.push(header.columnName);
|
||||
});
|
||||
this.hideOutput = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a dynamic Mat Table.
|
||||
* @param {any} data Mat Table data
|
||||
*/
|
||||
setOutputTable(data: any) {
|
||||
this.dataSource = new MatTableDataSource(data);
|
||||
setTimeout(() => {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the CSV file dynamically for run report data.
|
||||
*/
|
||||
downloadCSV() {
|
||||
const headers = this.displayedColumns;
|
||||
let csv = this.csvData.map((object: any) => object.row.join());
|
||||
csv.unshift(`data:text/csv;charset=utf-8,${headers.join()}`);
|
||||
csv = csv.join('\r\n');
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', encodeURI(csv));
|
||||
link.setAttribute('download', `${this.dataObject.reportData.name}.csv`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number formatted as per user's decimal choice.
|
||||
* @param {number} value Value to be formatted as per decimal choice.
|
||||
*/
|
||||
toDecimal(value: number) {
|
||||
const decimalChoice = this.dataObject.decimalChoice;
|
||||
return this.decimalPipe.transform(value, `1.${decimalChoice}-${decimalChoice}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the weather Mat-Table column has decimal display type.
|
||||
* @param {number} index Index of column.
|
||||
*/
|
||||
isDecimal(index: number) {
|
||||
return this.columnTypes[index] === 'DECIMAL';
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user