Merge pull request #902 from Alfresco/dev-mvitale-analytics-refactor

Refactoring analitycs-param-report
This commit is contained in:
Mario Romano 2016-10-14 19:11:52 +01:00 committed by GitHub
commit a4cf5a1f5c
24 changed files with 1019 additions and 800 deletions

View File

@ -5,7 +5,7 @@
<a id="apps-header" href="#apps" class="mdl-layout__tab is-active">APPS</a> <a id="apps-header" href="#apps" class="mdl-layout__tab is-active">APPS</a>
<a id="tasks-header" href="#tasks" class="mdl-layout__tab">TASK LIST</a> <a id="tasks-header" href="#tasks" class="mdl-layout__tab">TASK LIST</a>
<a id="processes-header" href="#processes" class="mdl-layout__tab">PROCESS LIST</a> <a id="processes-header" href="#processes" class="mdl-layout__tab">PROCESS LIST</a>
<a id="report-header" href="#report" class="mdl-layout__tab">REPORT</a> <a id="report-header" href="#report" class="mdl-layout__tab">ANALYTICS</a>
</div> </div>
</header> </header>
<main class="mdl-layout__content activiti" #tabmain> <main class="mdl-layout__content activiti" #tabmain>

View File

@ -19,6 +19,7 @@ import { NgModule, ModuleWithProviders } from '@angular/core';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
import { AnalyticsReportListComponent } from './src/components/analytics-report-list.component'; import { AnalyticsReportListComponent } from './src/components/analytics-report-list.component';
import { AnalyticsReportParametersComponent } from './src/components/analytics-report-parameters.component';
import { AnalyticsComponent } from './src/components/analytics.component'; import { AnalyticsComponent } from './src/components/analytics.component';
import { AnalyticsService } from './src/services/analytics.service'; import { AnalyticsService } from './src/services/analytics.service';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts'; import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
@ -27,12 +28,14 @@ import { WIDGET_DIRECTIVES } from './src/components/widgets/index';
export * from './src/components/analytics.component'; export * from './src/components/analytics.component';
export * from './src/components/analytics-report-list.component'; export * from './src/components/analytics-report-list.component';
export * from './src/components/analytics-report-parameters.component';
export * from './src/services/analytics.service'; export * from './src/services/analytics.service';
export * from './src/components/widgets/index'; export * from './src/components/widgets/index';
export const ANALYTICS_DIRECTIVES: any[] = [ export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent, AnalyticsComponent,
AnalyticsReportListComponent, AnalyticsReportListComponent,
AnalyticsReportParametersComponent,
WIDGET_DIRECTIVES WIDGET_DIRECTIVES
]; ];

View File

@ -15,109 +15,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { ReportParameterModel } from '../models/report.model';
export var reportDefParamStatus = {
'id': 2005,
'name': 'Fake Task overview status',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"status","name":null,"nameKey":null,"type":"status","value":null,"dependsOn":null}]}'
};
export var reportDefParamNumber = {
'id': 2005,
'name': 'Fake Process instances overview',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"slowProcessInstanceInteger","name":null,"nameKey":null,"type":"integer","value":10,"dependsOn":null}]}'
};
export var reportDefParamDuration = {
'id': 2005,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"duration","name":null,"nameKey":null,"type":"duration","value":null,"dependsOn":null}]}'
};
export var reportDefParamCheck = {
'id': 2005,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"typeFiltering","name":null,"nameKey":null,"type":"boolean","value":true,"dependsOn":null}]}'
};
export var reportDefParamDateRange = {
'id': 2005,
'name': 'Fake Process instances overview',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"dateRange","name":null,"nameKey":null,"type":"dateRange","value":null,"dependsOn":null}]}'
};
export var reportDefParamRangeInterval = {
'id': 2006,
'name': 'Fake Task overview RangeInterval',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"dateRangeInterval","name":null,"nameKey":null,"type":"dateInterval","value":null,"dependsOn":null}]}'
};
export var reportDefParamProcessDef = {
'id': 2006,
'name': 'Fake Task overview ProcessDefinition',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"processDefinitionId","name":null,"nameKey":null,"type":"processDefinition","value":null,"dependsOn":null}]}'
};
export var reportDefParamProcessDefOptions = {
'size': 4, 'total': 4, 'start': 0, 'data': [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
},
{
'id': 'FakeProcessTest 2:1:1',
'name': 'Fake Process Test 2 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 3:1:1',
'name': 'Fake Process Test 3 Name ',
'version': 1
}
]
};
export var reportDefParamProcessDefOptionsApp = {
'size': 2, 'total': 2, 'start': 2, 'data': [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
}
]
};
export var reportDefParamTask = {
'id': 2006,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"taskName","name":null,"nameKey":null,"type":"task","value":null,"dependsOn":"processDefinitionId"}]}'
};
export var reportDefParamTaskOptions = ['Fake task name 1', 'Fake task name 2'];
export var chartProcessDefOverview = { export var chartProcessDefOverview = {
'elements': [{ 'elements': [{
'id': 'id1585876275153', 'id': 'id1585876275153',
@ -198,58 +95,3 @@ export var chartTaskOverview = {
] ]
}] }]
}; };
export var fieldNumber = new ReportParameterModel(
{
id: 'slowProcessInstanceInteger',
type: 'integer',
value: '102'
}
);
export var fieldStatus = new ReportParameterModel(
{
id: 'status',
type: 'status',
value: 'fake-value'
}
);
export var fieldTypeFiltering = new ReportParameterModel(
{
id: 'typeFiltering',
type: 'boolean',
value: false
}
);
export var fieldTask = new ReportParameterModel(
{
id: 'taskName',
type: 'task',
value: 'fake-task-name'
}
);
export var fieldDateRange = {
startDate: '2016-10-12T00:00:00.000Z',
endDate: '2016-10-14T00:00:00.000Z'
};
export var fieldDateRangeInterval = new ReportParameterModel(
{
id: 'dateRangeInterval',
type: 'dateInterval',
value: 'fake-date-interval'
}
);
export var fieldProcessDef = new ReportParameterModel(
{
id: 'processDefinitionId',
type: 'processDefinition',
value: 'fake-process-name:1:15027'
}
);
export var fieldDuration = {value: 30};

View File

@ -0,0 +1,127 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ReportParameterDetailsModel } from '../models/report.model';
export var reportDefParamStatus = {
'id': 2005,
'name': 'Fake Task overview status',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"status","name":null,"nameKey":null,"type":"status","value":null,"dependsOn":null}]}'
};
export var reportDefParamNumber = {
'id': 2005,
'name': 'Fake Process instances overview',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"slowProcessInstanceInteger","name":null,"nameKey":null,"type":"integer","value":10,"dependsOn":null}]}'
};
export var reportDefParamDuration = {
'id': 2005,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"duration","name":null,"nameKey":null,"type":"duration","value":null,"dependsOn":null}]}'
};
export var reportDefParamCheck = {
'id': 2005,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters"' +
' :[{"id":"typeFiltering","name":null,"nameKey":null,"type":"boolean","value":true,"dependsOn":null}]}'
};
export var reportDefParamDateRange = {
'id': 2005,
'name': 'Fake Process instances overview',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"dateRange","name":null,"nameKey":null,"type":"dateRange","value":null,"dependsOn":null}]}'
};
export var reportDefParamRangeInterval = {
'id': 2006,
'name': 'Fake Task overview RangeInterval',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"dateRangeInterval","name":null,"nameKey":null,"type":"dateInterval","value":null,"dependsOn":null}]}'
};
export var reportDefParamProcessDef = {
'id': 2006,
'name': 'Fake Task overview ProcessDefinition',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"processDefinitionId","name":null,"nameKey":null,"type":"processDefinition","value":null,"dependsOn":null}]}'
};
export var reportDefParamProcessDefOptions = {
'size': 4, 'total': 4, 'start': 0, 'data': [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
},
{
'id': 'FakeProcessTest 2:1:1',
'name': 'Fake Process Test 2 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 3:1:1',
'name': 'Fake Process Test 3 Name ',
'version': 1
}
]
};
export var reportDefParamProcessDefOptionsApp = {
'size': 2, 'total': 2, 'start': 2, 'data': [
{
'id': 'FakeProcessTest 1:1:1',
'name': 'Fake Process Test 1 Name ',
'version': 1
},
{
'id': 'FakeProcessTest 1:2:1',
'name': 'Fake Process Test 1 Name ',
'version': 2
}
]
};
export var reportDefParamTask = {
'id': 2006,
'name': 'Fake Task service level agreement',
'created': '2016-10-05T15:39:40.222+0000',
'definition': '{ "parameters" :[{"id":"taskName","name":null,"nameKey":null,"type":"task","value":null,"dependsOn":"processDefinitionId"}]}'
};
export var reportDefParamTaskOptions = ['Fake task name 1', 'Fake task name 2'];
export var fieldProcessDef = new ReportParameterDetailsModel(
{
id: 'processDefinitionId',
type: 'processDefinition',
value: 'fake-process-name:1:15027'
}
);

View File

@ -18,7 +18,7 @@
import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core'; import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { ReportModel } from '../models/report.model'; import { ReportParametersModel } from '../models/report.model';
import { Observer } from 'rxjs/Observer'; import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
@ -31,7 +31,7 @@ import { Observable } from 'rxjs/Observable';
export class AnalyticsReportListComponent implements OnInit { export class AnalyticsReportListComponent implements OnInit {
@Output() @Output()
reportClick: EventEmitter<ReportModel> = new EventEmitter<ReportModel>(); reportClick: EventEmitter<ReportParametersModel> = new EventEmitter<ReportParametersModel>();
@Output() @Output()
onSuccess = new EventEmitter(); onSuccess = new EventEmitter();
@ -44,16 +44,16 @@ export class AnalyticsReportListComponent implements OnInit {
currentReport: any; currentReport: any;
reports: ReportModel[] = []; reports: ReportParametersModel[] = [];
constructor(private auth: AlfrescoAuthenticationService, constructor(private auth: AlfrescoAuthenticationService,
private analyticsService: AnalyticsService) { private analyticsService: AnalyticsService) {
this.report$ = new Observable<ReportModel>(observer => this.reportObserver = observer).share(); this.report$ = new Observable<ReportParametersModel>(observer => this.reportObserver = observer).share();
} }
ngOnInit() { ngOnInit() {
this.report$.subscribe((report: ReportModel) => { this.report$.subscribe((report: ReportParametersModel) => {
this.reports.push(report); this.reports.push(report);
}); });
@ -62,7 +62,7 @@ export class AnalyticsReportListComponent implements OnInit {
getReportListByAppId() { getReportListByAppId() {
this.analyticsService.getReportList().subscribe( this.analyticsService.getReportList().subscribe(
(res: ReportModel[]) => { (res: ReportParametersModel[]) => {
res.forEach((report) => { res.forEach((report) => {
this.reportObserver.next(report); this.reportObserver.next(report);
}); });

View File

@ -0,0 +1,23 @@
.dropdown-widget {
width: 100%;
}
.dropdown-widget__select {
width: 100%;
}
.dropdown-widget__invalid .dropdown-widget__select {
border-color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label {
color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label:after {
background-color: #d50000;
}
.dropdown-widget__invalid .mdl-textfield__error {
visibility: visible !important;
}

View File

@ -0,0 +1,57 @@
<div class="col-md-6">
<div *ngIf="reportParameters">
<form [formGroup]="reportForm" novalidate>
<h1>{{reportParameters.name}}</h1>
<div *ngFor="let field of reportParameters.definition.parameters">
<div [ngSwitch]="field.type">
<div *ngSwitchCase="'integer'">
<br>
<number-widget [field]="field" [group]="reportForm.controls.processInstanceGroup" [controllerName]="'slowProcessInstanceInteger'"
[required]="true"></number-widget>
</div>
<div *ngSwitchCase="'duration'">
<br>
<duration-widget [field]="field" [group]="reportForm.controls.durationGroup"
[controllerName]="'duration'"></duration-widget>
</div>
<div *ngSwitchCase="'boolean'">
<br>
<checkbox-widget [field]="field"></checkbox-widget>
</div>
<div *ngSwitchCase="'status'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.statusGroup" [controllerName]="'status'"
[required]="true"></dropdown-widget>
</div>
<div *ngSwitchCase="'processDefinition'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.processDefGroup" [controllerName]="'processDefinitionId'"
[required]="true" (fieldChanged)="onProcessDefinitionChanges(field)"></dropdown-widget>
</div>
<div *ngSwitchCase="'task'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.taskGroup" [controllerName]="'taskName'"
[required]="true"></dropdown-widget>
</div>
<div *ngSwitchCase="'dateRange'">
<br>
<date-range-widget [field]="field" [group]="reportForm.controls.dateRange"></date-range-widget>
</div>
<div *ngSwitchCase="'dateInterval'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.dateIntervalGroup" [controllerName]="'dateRangeInterval'"
[required]="true" [showDefaultOption]="false"></dropdown-widget>
</div>
<div *ngSwitchDefault>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{field.type}}</span>
</div>
</div>
</div>
<div *ngIf="debug">
<p>ReportForm valid : {{ reportForm.valid }}</p>
<p>ReportForm status : {{ reportForm.errors | json }}</p>
<p>ReportForm FormGroup valid : {{reportForm && reportForm.controls.dateRange.valid | json }}</p>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,406 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import {
CoreModule
} from 'ng2-alfresco-core';
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsComponent } from '../components/analytics.component';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel } from '../models/report.model';
import * as moment from 'moment';
import { DebugElement, SimpleChange } from '@angular/core';
import * as analyticParamsMock from '../assets/analyticsParamsReportComponent.mock';
export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent,
AnalyticsReportParametersComponent,
AnalyticsReportListComponent,
WIDGET_DIRECTIVES
];
export const ANALYTICS_PROVIDERS: any[] = [
AnalyticsService
];
declare let jasmine: any;
declare let mdDateTimePicker: any;
describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
let component: any;
let fixture: ComponentFixture<AnalyticsReportParametersComponent>;
let debug: DebugElement;
let element: HTMLElement;
let componentHandler: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule
],
declarations: [
...ANALYTICS_DIRECTIVES,
...CHART_DIRECTIVES
],
providers: [
...ANALYTICS_PROVIDERS
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsReportParametersComponent);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
fixture.detectChanges();
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
});
describe('Rendering tests', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Should initialize the Report form with a Form Group ', () => {
expect(component.reportForm.get('dateRange')).toBeDefined();
expect(component.reportForm.get('dateRange').get('startDate')).toBeDefined();
expect(component.reportForm.get('dateRange').get('endDate')).toBeDefined();
});
it('Should render a dropdown with all the status when the definition parameter type is \'status\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-status');
expect(element.querySelector('h1').innerHTML).toEqual('Fake Task overview status');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(4);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('All');
expect(dropDown[2].innerHTML).toEqual('Active');
expect(dropDown[3].innerHTML).toEqual('Complete');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamStatus
});
});
it('Should render a number with the default value when the definition parameter type is \'integer\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let numberElement: any = element.querySelector('#slowProcessInstanceInteger');
expect(numberElement.value).toEqual('10');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamNumber
});
});
it('Should render a duration component when the definition parameter type is \'duration\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let numberElement: any = element.querySelector('#duration');
expect(numberElement.value).toEqual('0');
let dropDown: any = element.querySelector('#select-duration');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(4);
expect(dropDown[0].innerHTML).toEqual('Seconds');
expect(dropDown[1].innerHTML).toEqual('Minutes');
expect(dropDown[2].innerHTML).toEqual('Hours');
expect(dropDown[3].innerHTML).toEqual('Days');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamDuration
});
});
it('Should save an Params object when the submit is performed', () => {
component.onSuccess.subscribe((res) => {
expect(res.dateRange.startDate).toEqual('2016-09-01T00:00:00.000Z');
expect(res.dateRange.endDate).toEqual('2016-10-05T00:00:00.000Z');
expect(res.status).toEqual('All');
expect(res.processDefinitionId).toEqual('FakeProcess:1:22');
expect(res.taskName).toEqual('FakeTaskName');
expect(res.duration).toEqual(22);
expect(res.dateRangeInterval).toEqual(120);
expect(res.slowProcessInstanceInteger).toEqual(2);
});
let values: any = {
dateRange: {
startDate: '2016-09-01', endDate: '2016-10-05'
},
statusGroup: {
status: 'All'
},
processDefGroup: {
processDefinitionId: 'FakeProcess:1:22'
},
taskGroup: {
taskName: 'FakeTaskName'
},
durationGroup: {
duration: 22
},
dateIntervalGroup: {
dateRangeInterval: 120
},
processInstanceGroup: {
slowProcessInstanceInteger: 2
}
};
component.submit(values);
});
it('Should render a checkbox with the value true when the definition parameter type is \'boolean\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let checkElement: any = element.querySelector('#typeFiltering');
expect(checkElement.checked).toBeTruthy();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamCheck
});
});
it('Should render a date range components when the definition parameter type is \'dateRange\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let today = moment().format('YYYY-MM-DD');
const startDate: any = element.querySelector('#startDateInput');
const endDate: any = element.querySelector('#endDateInput');
expect(startDate.value).toEqual(today);
expect(endDate.value).toEqual(today);
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamDateRange
});
});
it('Should render a dropdown with all the RangeInterval when the definition parameter type is \'dateRangeInterval\' ', (done) => {
component.onSuccessReportParams.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-dateRangeInterval');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(5);
expect(dropDown[0].innerHTML).toEqual('By hour');
expect(dropDown[1].innerHTML).toEqual('By day');
expect(dropDown[2].innerHTML).toEqual('By week');
expect(dropDown[3].innerHTML).toEqual('By month');
expect(dropDown[4].innerHTML).toEqual('By year');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamRangeInterval
});
});
it('Should render a dropdown with all the process definition when the definition parameter type is \'processDefinition\' and the' +
' reportId change', (done) => {
component.onSuccessParamOpt.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-processDefinitionId');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(5);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('Fake Process Test 1 Name (v 1) ');
expect(dropDown[2].innerHTML).toEqual('Fake Process Test 1 Name (v 2) ');
expect(dropDown[3].innerHTML).toEqual('Fake Process Test 2 Name (v 1) ');
expect(dropDown[4].innerHTML).toEqual('Fake Process Test 3 Name (v 1) ');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDefOptions
});
});
it('Should render a dropdown with all the process definition when the definition parameter type is \'processDefinition\' and the' +
' appId change', (done) => {
component.onSuccessParamOpt.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-processDefinitionId');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(3);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('Fake Process Test 1 Name (v 1) ');
expect(dropDown[2].innerHTML).toEqual('Fake Process Test 1 Name (v 2) ');
done();
});
let appId = 1;
component.appId = appId;
let change = new SimpleChange(null, appId);
component.ngOnChanges({ 'appId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDefOptionsApp
});
});
it('Should load the task list when a process definition is selected', () => {
component.onSuccessReportParams.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(2);
expect(res[0].id).toEqual('Fake task name 1');
expect(res[0].name).toEqual('Fake task name 1');
expect(res[1].id).toEqual('Fake task name 2');
expect(res[1].name).toEqual('Fake task name 2');
});
component.reportId = 100;
component.reportParameters = new ReportParametersModel(analyticParamsMock.reportDefParamTask);
component.onProcessDefinitionChanges(analyticParamsMock.fieldProcessDef);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamTaskOptions
});
});
it('Should emit an error with a 404 response when the options response is not found', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticParamsMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
it('Should emit an error with a 404 response when the report parameters response is not found', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
});
it('Should convert a string in number', () => {
let numberConvert = component.convertNumber('2');
expect(numberConvert).toEqual(2);
});
});

View File

@ -0,0 +1,205 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, EventEmitter, OnInit, OnChanges, Input, Output, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel, ReportQuery, ParameterValueModel, ReportParameterDetailsModel } from '../models/report.model';
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
import * as moment from 'moment';
@Component({
moduleId: module.id,
selector: 'analytics-report-parameters',
templateUrl: './analytics-report-parameters.component.html',
styleUrls: ['./analytics-report-parameters.component.css']
})
export class AnalyticsReportParametersComponent implements OnInit, OnChanges {
public static FORMAT_DATE_ACTIVITI: string = 'YYYY-MM-DD';
@Input()
appId: string;
@Input()
reportId: string;
@Input()
debug: boolean = false;
@Output()
onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
@Output()
onFormValueChanged = new EventEmitter();
onDropdownChanged = new EventEmitter();
onSuccessReportParams = new EventEmitter();
onSuccessParamOpt = new EventEmitter();
reportParameters: ReportParametersModel;
reportForm: FormGroup;
private dropDownSub;
private reportParamsSub;
private paramOpts;
constructor(private translate: AlfrescoTranslationService,
private analyticsService: AnalyticsService,
private formBuilder: FormBuilder ) {
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src');
}
}
ngOnInit() {
this.initForm();
this.dropDownSub = this.onDropdownChanged.subscribe((field) => {
let paramDependOn: ReportParameterDetailsModel = this.reportParameters.definition.parameters.find(p => p.dependsOn === field.id);
if (paramDependOn) {
this.retrieveParameterOptions(this.reportParameters.definition.parameters, this.appId, this.reportId, field.value);
}
});
this.paramOpts = this.onSuccessReportParams.subscribe((report: ReportParametersModel) => {
if (report.hasParameters()) {
this.retrieveParameterOptions(report.definition.parameters, this.appId);
}
});
this.reportForm.valueChanges.subscribe(data => this.onValueChanged(data));
}
ngOnChanges(changes: SimpleChanges) {
let reportId = changes['reportId'];
if (reportId && reportId.currentValue) {
this.getReportParams(reportId.currentValue);
}
let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) {
this.getReportParams(this.reportId);
}
}
initForm() {
this.reportForm = this.formBuilder.group({
dateRange: new FormGroup({}),
statusGroup: new FormGroup({
status: new FormControl()
}),
processInstanceGroup: new FormGroup({
slowProcessInstanceInteger: new FormControl()
}),
taskGroup: new FormGroup({
taskName: new FormControl()
}),
dateIntervalGroup: new FormGroup({
dateRangeInterval: new FormControl()
}),
durationGroup: new FormGroup({
duration: new FormControl()
}),
processDefGroup: new FormGroup({
processDefinitionId: new FormControl()
})
});
}
public getReportParams(reportId: string) {
this.reportParamsSub = this.analyticsService.getReportParams(reportId).subscribe(
(res: ReportParametersModel) => {
this.reportParameters = res;
this.onSuccessReportParams.emit(res);
},
(err: any) => {
console.log(err);
this.onError.emit(err);
}
);
}
private retrieveParameterOptions(parameters: ReportParameterDetailsModel[], appId: string, reportId?: string, processDefinitionId?: string) {
parameters.forEach((param) => {
this.analyticsService.getParamValuesByType(param.type, appId, reportId, processDefinitionId).subscribe(
(opts: ParameterValueModel[]) => {
param.options = opts;
this.onSuccessParamOpt.emit(opts);
},
(err: any) => {
console.log(err);
this.onError.emit(err);
}
);
});
}
onProcessDefinitionChanges(field: any) {
if (field.value) {
this.onDropdownChanged.emit(field);
}
}
public submit(values: any) {
let reportParamQuery = this.convertFormValuesToReportParamQuery(values);
this.onSuccess.emit(reportParamQuery);
}
onValueChanged(values: any) {
this.onFormValueChanged.emit(values);
if (this.reportForm && this.reportForm.valid) {
this.submit(values);
}
}
public convertMomentDate(date: string) {
return moment(date, AnalyticsReportParametersComponent.FORMAT_DATE_ACTIVITI, true)
.format(AnalyticsReportParametersComponent.FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z';
}
public convertNumber(value: string): number {
return parseInt(value, 10);
}
convertFormValuesToReportParamQuery(values: any): ReportQuery {
let reportParamQuery: ReportQuery = new ReportQuery();
reportParamQuery.dateRange.startDate = this.convertMomentDate(values.dateRange.startDate);
reportParamQuery.dateRange.endDate = this.convertMomentDate(values.dateRange.endDate);
reportParamQuery.status = values.statusGroup.status;
reportParamQuery.processDefinitionId = values.processDefGroup.processDefinitionId;
reportParamQuery.taskName = values.taskGroup.taskName;
reportParamQuery.duration = values.durationGroup.duration;
reportParamQuery.dateRangeInterval = values.dateIntervalGroup.dateRangeInterval;
reportParamQuery.slowProcessInstanceInteger = this.convertNumber(values.processInstanceGroup.slowProcessInstanceInteger);
return reportParamQuery;
}
ngOnDestroy() {
this.dropDownSub.unsubscribe();
this.paramOpts.unsubscribe();
if (this.reportParamsSub) {
this.reportParamsSub.unsubscribe();
}
}
}

View File

@ -1,102 +1,50 @@
<div class="col-md-6"> <div class="col-md-6">
<div *ngIf="reportDetails"> <analytics-report-parameters [appId]="appId" [reportId]="reportId"
<form [formGroup]="reportForm" novalidate> (onFormValueChanged)="reset()" (onSuccess)="showReport($event)"></analytics-report-parameters>
<h1>{{reportDetails.name}}</h1>
<div *ngFor="let field of reportDetails.definition.parameters">
<div [ngSwitch]="field.type">
<div *ngSwitchCase="'integer'">
<br>
<number-widget [field]="field"
(fieldChanged)="onNumberChanges(field)"></number-widget>
</div>
<div *ngSwitchCase="'duration'">
<br>
<duration-widget [field]="field"
(fieldChanged)="onDurationChanges($event)"></duration-widget>
</div>
<div *ngSwitchCase="'boolean'">
<br>
<checkbox-widget [field]="field"
(fieldChanged)="onTypeFilteringChanges(field)"></checkbox-widget>
</div>
<div *ngSwitchCase="'status'">
<br>
<dropdown-widget [field]="field"
(fieldChanged)="onStatusChanges(field)"></dropdown-widget>
</div>
<div *ngSwitchCase="'processDefinition'">
<br>
<dropdown-widget [field]="field"
(fieldChanged)="onProcessDefinitionChanges(field)" #processDefinition></dropdown-widget>
</div>
<div *ngSwitchCase="'task'">
<br>
<dropdown-widget [field]="field"
(fieldChanged)="onTaskChanges(field)"></dropdown-widget>
</div>
<div *ngSwitchCase="'dateRange'">
<br>
<date-range-widget [field]="field" [group]="reportForm.controls.dateRange"
(dateRangeChanged)="onDateRangeChange($event)"></date-range-widget>
</div>
<div *ngSwitchCase="'dateInterval'">
<br>
<dropdown-widget [field]="field" [showDefaultOption]="false"
(fieldChanged)="onDateRangeIntervalChange(field)"></dropdown-widget>
</div>
<div *ngSwitchDefault>
<span>UNKNOWN WIDGET TYPE: {{field.type}}</span>
</div>
</div>
</div>
<br><br>
<button type="submit" class="mdl-button mdl-js-button mdl-button--fab" (click)="showReport()" >
<i class="material-icons">refresh</i>
</button>
<div *ngIf="debug">
<p>ReportForm valid : {{ reportForm.valid }}</p>
<p>ReportForm status : {{ reportForm.errors | json }}</p>
<p>ReportForm FormGroup valid : {{ reportForm.controls.dateRange.valid | json }}</p>
</div>
</form>
<div *ngIf="reports">
<div *ngFor="let report of reports"> <div *ngFor="let report of reports">
<h2>{{report.title}}</h2> <h2>{{report.title}}</h2>
<div [ngSwitch]="report.type"> <div [ngSwitch]="report.type">
<div *ngSwitchCase="'pie'"> <div *ngSwitchCase="'pie'">
<div class="col-md-6"> <div class="col-md-6">
<base-chart class="chart" <div *ngIf="!report.hasData()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasData()" class="chart"
[data]="report.data" [data]="report.data"
[datasets]="report.datasets"
[labels]="report.labels" [labels]="report.labels"
[chartType]="report.type"></base-chart> [chartType]="report.type"></base-chart>
</div> </div>
</div> </div>
<div *ngSwitchCase="'table'"> <div *ngSwitchCase="'table'">
<div [attr.id]="'chart-table-' + report.id"> <div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<table class="table table-responsive table-condensed" style="width: 100%"> <div [attr.id]="'chart-table-' + report.id" *ngIf="report.hasDatasets()">
<tr> <table class="table table-responsive table-condensed" style="width: 100%">
<th *ngFor="let label of report.labels">{{label | translate}}</th> <tr>
</tr> <th *ngFor="let label of report.labels">{{label | translate}}</th>
<tr *ngFor="let rows of report.datasets" style="text-align: center;"> </tr>
<td *ngFor="let row of rows">{{row | translate }}</td> <tr *ngFor="let rows of report.datasets" style="text-align: center;">
</tr> <td *ngFor="let row of rows">{{row | translate }}</td>
</table> </tr>
</table>
</div> </div>
</div> </div>
<div *ngSwitchCase="'masterDetailTable'"> <div *ngSwitchCase="'masterDetailTable'">
<table class="table table-responsive table-condensed" style="width: 100%"> <div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<tr> <div [attr.id]="'chart-master-detail-table-' + report.id" *ngIf="report.hasDatasets()">
<th *ngFor="let label of report.labels">{{label | translate}}</th> <table class="table table-responsive table-condensed" style="width: 100%">
</tr> <tr>
<tr *ngFor="let rows of report.datasets" style="text-align: center;"> <th *ngFor="let label of report.labels">{{label | translate}}</th>
<td *ngFor="let row of rows">{{row | translate }}</td> </tr>
</tr> <tr *ngFor="let rows of report.datasets" style="text-align: center;">
</table> <td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div> </div>
<div *ngSwitchCase="'bar'"> <div *ngSwitchCase="'bar'">
<div class="col-md-6"> <div class="col-md-6">
<base-chart class="chart" <div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasDatasets()" class="chart"
[datasets]="report.datasets" [datasets]="report.datasets"
[labels]="report.labels" [labels]="report.labels"
[options]="report.options" [options]="report.options"
@ -104,9 +52,11 @@
</div> </div>
</div> </div>
<div *ngSwitchDefault> <div *ngSwitchDefault>
<span>UNKNOWN WIDGET TYPE: {{report.type}}</span> <span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{report.type}}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<br><br><br>
<div *ngIf="!reports">{{'ANALYTICS.MESSAGES.FILL-PARAMETER' | translate}}</div>
</div> </div>

View File

@ -22,18 +22,18 @@ import {
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component'; import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsComponent } from '../components/analytics.component'; import { AnalyticsComponent } from '../components/analytics.component';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index'; import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts'; import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { AnalyticsService } from '../services/analytics.service';
import { ReportModel, ReportQuery } from '../models/report.model';
import { Chart } from '../models/chart.model'; import { Chart } from '../models/chart.model';
import * as moment from 'moment'; import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model';
import { DebugElement, SimpleChange } from '@angular/core'; import { DebugElement, SimpleChange } from '@angular/core';
import * as analyticMock from '../assets/analyticsComponent.mock'; import * as analyticMock from '../assets/analyticsComponent.mock';
export const ANALYTICS_DIRECTIVES: any[] = [ export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent, AnalyticsComponent,
AnalyticsReportParametersComponent,
AnalyticsReportListComponent, AnalyticsReportListComponent,
WIDGET_DIRECTIVES WIDGET_DIRECTIVES
]; ];
@ -89,215 +89,6 @@ describe('Test ng2-activiti-analytics Report ', () => {
jasmine.Ajax.uninstall(); jasmine.Ajax.uninstall();
}); });
it('Should initialize the Report form with a Form Group ', () => {
expect(component.reportForm.get('dateRange')).toBeDefined();
expect(component.reportForm.get('dateRange').get('startDate')).toBeDefined();
expect(component.reportForm.get('dateRange').get('endDate')).toBeDefined();
});
it('Should render a dropdown with all the status when the definition parameter type is \'status\' ', (done) => {
component.onSuccessParamsReport.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-status');
expect(element.querySelector('h1').innerHTML).toEqual('Fake Task overview status');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(4);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('All');
expect(dropDown[2].innerHTML).toEqual('Active');
expect(dropDown[3].innerHTML).toEqual('Complete');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamStatus
});
});
it('Should render a number with the default value when the definition parameter type is \'integer\' ', (done) => {
component.onSuccessParamsReport.subscribe(() => {
fixture.detectChanges();
let numberElement: any = element.querySelector('#slowProcessInstanceInteger');
expect(numberElement.value).toEqual('10');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamNumber
});
});
it('Should render a duration component when the definition parameter type is \'duration\' ', (done) => {
component.onSuccessParamsReport.subscribe(() => {
fixture.detectChanges();
let numberElement: any = element.querySelector('#duration');
expect(numberElement.value).toEqual('0');
let dropDown: any = element.querySelector('#select-duration');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(4);
expect(dropDown[0].innerHTML).toEqual('Seconds');
expect(dropDown[1].innerHTML).toEqual('Minutes');
expect(dropDown[2].innerHTML).toEqual('Hours');
expect(dropDown[3].innerHTML).toEqual('Days');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamDuration
});
});
it('Should render a checkbox with the value true when the definition parameter type is \'boolean\' ', (done) => {
component.onSuccessParamsReport.subscribe(() => {
fixture.detectChanges();
let checkElement: any = element.querySelector('#typeFiltering');
expect(checkElement.checked).toBeTruthy();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamCheck
});
});
it('Should render a date range components when the definition parameter type is \'dateRange\' ', (done) => {
component.onSuccessParamsReport.subscribe(() => {
fixture.detectChanges();
let today = moment().format('YYYY-MM-DD');
const startDate: any = element.querySelector('#startDateInput');
const endDate: any = element.querySelector('#endDateInput');
expect(startDate.value).toEqual(today);
expect(endDate.value).toEqual(today);
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamDateRange
});
});
it('Should render a dropdown with all the RangeInterval when the definition parameter type is \'dateRangeInterval\' ', (done) => {
component.onSuccessParamsReport.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-dateRangeInterval');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(5);
expect(dropDown[0].innerHTML).toEqual('By hour');
expect(dropDown[1].innerHTML).toEqual('By day');
expect(dropDown[2].innerHTML).toEqual('By week');
expect(dropDown[3].innerHTML).toEqual('By month');
expect(dropDown[4].innerHTML).toEqual('By year');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamRangeInterval
});
});
it('Should render a dropdown with all the process definition when the definition parameter type is \'processDefinition\' and the' +
' reportId change', (done) => {
component.onSuccessParamOpt.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-processDefinitionId');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(5);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('Fake Process Test 1 Name (v 1) ');
expect(dropDown[2].innerHTML).toEqual('Fake Process Test 1 Name (v 2) ');
expect(dropDown[3].innerHTML).toEqual('Fake Process Test 2 Name (v 1) ');
expect(dropDown[4].innerHTML).toEqual('Fake Process Test 3 Name (v 1) ');
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamProcessDefOptions
});
});
it('Should render a dropdown with all the process definition when the definition parameter type is \'processDefinition\' and the' +
' appId change', (done) => {
component.onSuccessParamOpt.subscribe(() => {
fixture.detectChanges();
let dropDown: any = element.querySelector('#select-processDefinitionId');
expect(dropDown).toBeDefined();
expect(dropDown.length).toEqual(3);
expect(dropDown[0].innerHTML).toEqual('Choose One');
expect(dropDown[1].innerHTML).toEqual('Fake Process Test 1 Name (v 1) ');
expect(dropDown[2].innerHTML).toEqual('Fake Process Test 1 Name (v 2) ');
done();
});
let appId = 1;
component.appId = appId;
let change = new SimpleChange(null, appId);
component.ngOnChanges({ 'appId': change });
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamProcessDefOptionsApp
});
});
it('Should render the Process definition overview report ', (done) => { it('Should render the Process definition overview report ', (done) => {
component.onShowReport.subscribe((res) => { component.onShowReport.subscribe((res) => {
expect(res).toBeDefined(); expect(res).toBeDefined();
@ -325,14 +116,10 @@ describe('Test ng2-activiti-analytics Report ', () => {
done(); done();
}); });
component.reportDetails = new ReportModel({ let reportParamQuery = new ReportQuery({status: 'All'});
id: 1, component.appId = 1;
definition: component.reportId = 1001;
'{ "parameters" :[{"id":"status","type":"status", "options": [{"id": "all", "name" :"all"}],"value":null}]}' component.showReport(reportParamQuery);
});
component.reportParamQuery = new ReportQuery({status: 'All'});
component.showReport();
jasmine.Ajax.requests.mostRecent().respondWith({ jasmine.Ajax.requests.mostRecent().respondWith({
status: 200, status: 200,
@ -378,14 +165,8 @@ describe('Test ng2-activiti-analytics Report ', () => {
done(); done();
}); });
component.reportDetails = new ReportModel({ let reportParamQuery = new ReportQuery({status: 'All'});
id: 1, component.showReport(reportParamQuery);
definition:
'{ "parameters" :[{"id":"status","type":"status", "options": [{"id": "all", "name" :"all"}],"value":null}]}'
});
component.reportParamQuery = new ReportQuery({status: 'All'});
component.showReport();
jasmine.Ajax.requests.mostRecent().respondWith({ jasmine.Ajax.requests.mostRecent().respondWith({
status: 200, status: 200,
@ -394,157 +175,22 @@ describe('Test ng2-activiti-analytics Report ', () => {
}); });
}); });
it('Should reset the report and save the number value onNumberChanges method', () => { it('Should reset the reports when the onChanged is call', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onNumberChanges(analyticMock.fieldNumber);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.slowProcessInstanceInteger).toEqual(102);
});
it('Should reset the report and save the duration value onDurationChanges method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onDurationChanges(analyticMock.fieldDuration);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.duration).toEqual(30);
});
it('Should reset the report and save the status value onStatusChanges method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onStatusChanges(analyticMock.fieldStatus);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.status).toEqual('fake-value');
});
it('Should reset the report and save the typeFiltering value onTypeFilteringChanges method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onTypeFilteringChanges(analyticMock.fieldTypeFiltering);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.typeFiltering).toBeFalsy();
});
it('Should reset the report and save the taskName value onTaskChanges method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onTaskChanges(analyticMock.fieldTask);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.taskName).toEqual('fake-task-name');
});
it('Should reset the report and save the dateRange value onDateRangeChange method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onDateRangeChange(analyticMock.fieldDateRange);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.dateRange.startDate).toEqual('2016-10-12T00:00:00.000Z');
expect(component.reportParamQuery.dateRange.endDate).toEqual('2016-10-14T00:00:00.000Z');
});
it('Should reset the report and save the dateRangeInterval value onDateRangeIntervalChange method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.onDateRangeIntervalChange(analyticMock.fieldDateRangeInterval);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.dateRangeInterval).toEqual('fake-date-interval');
});
it('Should reset the report and save the processDefinitionId value onProcessDefinitionChanges method', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.reportDetails = new ReportModel({
id: 1,
definition:
'{ "parameters" :[{"id":"processDefinitionId","type":"processDefinition","value":null}]}'
});
component.onProcessDefinitionChanges(analyticMock.fieldProcessDef);
expect(component.reports).toBeNull();
expect(component.reportParamQuery.processDefinitionId).toEqual('fake-process-name:1:15027');
});
it('Should load the task list when a process definition is selected', () => {
component.onSuccessParamsReport.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(2);
expect(res[0].id).toEqual('Fake task name 1');
expect(res[0].name).toEqual('Fake task name 1');
expect(res[1].id).toEqual('Fake task name 2');
expect(res[1].name).toEqual('Fake task name 2');
});
component.reportId = 100;
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.reportDetails = new ReportModel(analyticMock.reportDefParamTask);
component.onProcessDefinitionChanges(analyticMock.fieldProcessDef);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamTaskOptions
});
});
it('Should convert a string in number', () => {
let numberConvert = component.convertNumber('2');
expect(numberConvert).toEqual(2);
});
it('Should emit an error with a 404 response when the options response is not found', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportId = 1; let reportId = 1;
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
let change = new SimpleChange(null, reportId); let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change }); component.ngOnChanges({ 'reportId': change });
expect(component.reports).toBeUndefined();
jasmine.Ajax.requests.first().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.reportDefParamProcessDef
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
}); });
it('Should emit an error with a 404 response when the Process definition overview response is not found ', (done) => { it('Should emit onError event with a 404 response ', (done) => {
component.onError.subscribe((err) => { component.onError.subscribe((err) => {
expect(err).toBeDefined(); expect(err).toBeDefined();
done(); done();
}); });
component.reportDetails = new ReportModel({ let reportParamQuery = new ReportQuery({status: 'All'});
id: 1, component.showReport(reportParamQuery);
definition:
'{ "parameters" :[{"id":"status","type":"status", "options": [{"id": "all", "name" :"all"}],"value":null}]}'
});
component.reportParamQuery = new ReportQuery({status: 'All'});
component.showReport();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
it('Should emit an error with a 404 response when the report parameters response is not found', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportId = 1;
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
jasmine.Ajax.requests.mostRecent().respondWith({ jasmine.Ajax.requests.mostRecent().respondWith({
status: 404, status: 404,

View File

@ -15,12 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, OnInit, OnChanges, Input, Output, SimpleChanges, ViewChild } from '@angular/core'; import { Component, EventEmitter, OnInit, OnChanges, Input, Output, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { ReportModel, ReportQuery, ParameterValueModel, ReportParameterModel } from '../models/report.model'; import { ReportQuery } from '../models/report.model';
import { Chart } from '../models/chart.model'; import { Chart } from '../models/chart.model';
import { FormGroup, FormBuilder } from '@angular/forms';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@ -30,50 +29,30 @@ import { FormGroup, FormBuilder } from '@angular/forms';
}) })
export class AnalyticsComponent implements OnInit, OnChanges { export class AnalyticsComponent implements OnInit, OnChanges {
@ViewChild('processDefinition')
processDefinition: any;
@Input() @Input()
appId: string; appId: string;
@Input() @Input()
reportId: string; reportId: number;
@Input()
debug: boolean = false;
@Output() @Output()
onSuccess = new EventEmitter(); onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
@Output()
onDropdownChanged = new EventEmitter();
@Output() @Output()
onShowReport = new EventEmitter(); onShowReport = new EventEmitter();
@Output() @Output()
onSuccessParamsReport = new EventEmitter(); onError = new EventEmitter();
@Output()
onSuccessParamOpt = new EventEmitter();
reportDetails: ReportModel;
reportParamQuery = new ReportQuery(); reportParamQuery = new ReportQuery();
reports: any[]; reports: any[];
reportForm: FormGroup;
debug: boolean = false;
private dropDownSub;
private paramsReportSub;
private paramOpts;
constructor(private translate: AlfrescoTranslationService, constructor(private translate: AlfrescoTranslationService,
private analyticsService: AnalyticsService, private analyticsService: AnalyticsService) {
private formBuilder: FormBuilder ) {
console.log('AnalyticsComponent'); console.log('AnalyticsComponent');
if (translate) { if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src'); translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src');
@ -81,67 +60,15 @@ export class AnalyticsComponent implements OnInit, OnChanges {
} }
ngOnInit() { ngOnInit() {
this.reportForm = this.formBuilder.group({
dateRange: new FormGroup({})
});
this.dropDownSub = this.onDropdownChanged.subscribe((field) => {
let paramDependOn: ReportParameterModel = this.reportDetails.definition.parameters.find(p => p.dependsOn === field.id);
if (paramDependOn) {
this.retrieveParameterOptions(this.reportDetails.definition.parameters, this.appId, this.reportId, field.value);
}
});
this.paramOpts = this.onSuccessParamsReport.subscribe((report: ReportModel) => {
if (report.hasParameters()) {
this.retrieveParameterOptions(report.definition.parameters, this.appId);
}
});
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
let reportId = changes['reportId'];
if (reportId && reportId.currentValue) {
this.getParamsReports(reportId.currentValue);
}
let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) {
this.getParamsReports(this.reportId);
}
}
public getParamsReports(reportId: string) {
this.reset(); this.reset();
this.paramsReportSub = this.analyticsService.getParamsReports(reportId).subscribe(
(res: ReportModel) => {
this.reportDetails = res;
this.onSuccessParamsReport.emit(res);
},
(err: any) => {
console.log(err);
this.onError.emit(err);
}
);
} }
private retrieveParameterOptions(parameters: ReportParameterModel[], appId: string, reportId?: string, processDefinitionId?: string) { public showReport($event) {
parameters.forEach((param) => { this.reportParamQuery = $event;
this.analyticsService.getParamValuesByType(param.type, appId, reportId, processDefinitionId).subscribe( this.analyticsService.getReportsByParams(this.reportId, this.reportParamQuery).subscribe(
(opts: ParameterValueModel[]) => {
param.options = opts;
this.onSuccessParamOpt.emit(opts);
},
(err: any) => {
console.log(err);
this.onError.emit(err);
}
);
});
}
public showReport() {
this.analyticsService.getReportsByParams(this.reportDetails.id, this.reportParamQuery).subscribe(
(res: Chart[]) => { (res: Chart[]) => {
this.reports = res; this.reports = res;
this.onShowReport.emit(res); this.onShowReport.emit(res);
@ -153,65 +80,9 @@ export class AnalyticsComponent implements OnInit, OnChanges {
); );
} }
onNumberChanges(field: any) {
this.reset();
this.reportParamQuery.slowProcessInstanceInteger = parseInt(field.value, 10);
}
onDurationChanges(field: any) {
this.reset();
if (field && field.value) {
this.reportParamQuery.duration = parseInt(field.value, 10);
}
}
onTypeFilteringChanges(field: any) {
this.reset();
this.reportParamQuery.typeFiltering = field.value;
}
onStatusChanges(field: any) {
this.reset();
this.reportParamQuery.status = field.value;
}
onProcessDefinitionChanges(field: any) {
this.reset();
if (field.value) {
this.reportParamQuery.processDefinitionId = field.value;
this.onDropdownChanged.emit(field);
}
}
onTaskChanges(field: any) {
this.reset();
this.reportParamQuery.taskName = field.value;
}
onDateRangeChange(dateRange: any) {
this.reset();
this.reportParamQuery.dateRange.startDate = dateRange.startDate;
this.reportParamQuery.dateRange.endDate = dateRange.endDate;
}
onDateRangeIntervalChange(field: any) {
this.reset();
this.reportParamQuery.dateRangeInterval = field.value;
}
public reset() { public reset() {
this.reports = null; if (this.reports) {
} this.reports = undefined;
public convertNumber(value: string): number {
return parseInt(value, 10);
}
ngOnDestroy() {
this.dropDownSub.unsubscribe();
this.paramOpts.unsubscribe();
if (this.paramsReportSub) {
this.paramsReportSub.unsubscribe();
} }
} }
} }

View File

@ -1,6 +1,6 @@
<label>{{field.nameKey | translate}}</label><br> <label>{{field.nameKey | translate}}</label><br>
<div [formGroup]="dateRange"> <div [formGroup]="dateRange">
<small *ngIf="dateRange.errors && dateRange.errors.greaterThan" [hidden]="!dateRange.errors" class="text-danger"> <small *ngIf="dateRange && dateRange.errors && dateRange.errors.greaterThan" [hidden]="!dateRange.errors" class="text-danger">
Start date must be less than End date Start date must be less than End date
</small> </small>
<div class="mdl-grid"> <div class="mdl-grid">
@ -12,7 +12,7 @@
(onOk)="onOkStart(startElement)" (onOk)="onOkStart(startElement)"
id="startDateInput" #startElement> id="startDateInput" #startElement>
<label class="mdl-textfield__label" for="startDateInput">Start Date</label> <label class="mdl-textfield__label" for="startDateInput">Start Date</label>
<small [hidden]="dateRange.controls.startDate.valid" class="text-danger"> <small [hidden]="dateRange && dateRange.controls.startDate && dateRange.controls.startDate.valid" class="text-danger">
Start is required Start is required
</small> </small>
</div> </div>
@ -40,9 +40,9 @@
</div> </div>
</div> </div>
<div *ngIf="debug"> <div *ngIf="debug">
<p>FormGroup : {{ dateRange.value | json }}</p> <p>FormGroup : {{dateRange && dateRange.value | json }}</p>
<p>FormGroup valid : {{ dateRange.valid }}</p> <p>FormGroup valid : {{dateRange && dateRange.valid }}</p>
<p>FormGroup status : {{ dateRange.errors | json }}</p> <p>FormGroup status : {{dateRange && dateRange.errors | json }}</p>
<p>FormGroup start status : {{ dateRange.controls.startDate.errors | json }}</p> <p>FormGroup start status : {{dateRange && dateRange.controls.startDate && dateRange.controls.startDate.errors | json }}</p>
<p>FormGroup end status: {{ dateRange.controls.endDate.errors | json }}</p> <p>FormGroup end status: {{dateRange && dateRange.controls.endDate.errors | json }}</p>
</div> </div>

View File

@ -144,4 +144,8 @@ export class DateRangeWidget extends WidgetComponent {
public convertMomentDate(date: string) { public convertMomentDate(date: string) {
return moment(date, DateRangeWidget.FORMAT_DATE_ACTIVITI, true).format(DateRangeWidget.FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z'; return moment(date, DateRangeWidget.FORMAT_DATE_ACTIVITI, true).format(DateRangeWidget.FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z';
} }
ngOnDestroy() {
}
} }

View File

@ -1,8 +1,8 @@
<div class="dropdown-widget"> <div class="dropdown-widget" [formGroup]="formGroup">
<label class="dropdown-widget__label" [attr.for]="field.id">{{field.nameKey | translate}}</label> <label class="dropdown-widget__label" [attr.for]="field.id">{{field.nameKey | translate}}</label>
<select [attr.id]="'select-' + field.id" class="dropdown-widget__select" <select [formControlName]="controllerName" [attr.id]="'select-' + field.id" class="dropdown-widget__select"
[(ngModel)]="field.value" (ngModelChange)="changeValue($event)" required> [(ngModel)]="field.value" (ngModelChange)="changeValue($event)">
<option *ngIf="showDefaultOption">{{defaultOptionText}}</option> <option *ngIf="showDefaultOption" value="">{{defaultOptionText}}</option>
<option *ngFor="let opt of field.options" [value]="opt.id">{{opt.label}}</option> <option *ngFor="let opt of field.options" [value]="opt.id">{{opt.label}}</option>
</select> </select>
</div> </div>

View File

@ -16,6 +16,7 @@
*/ */
import { Component, Input, Output, EventEmitter } from '@angular/core'; import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, Validators } from '@angular/forms';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
@Component({ @Component({
@ -29,16 +30,31 @@ export class DropdownWidget extends WidgetComponent {
@Input() @Input()
field: any; field: any;
@Input('group')
public formGroup: FormGroup;
@Input('controllerName')
public controllerName: string;
@Output() @Output()
fieldChanged: EventEmitter<any> = new EventEmitter<any>(); fieldChanged: EventEmitter<any> = new EventEmitter<any>();
@Input() @Input()
showDefaultOption: boolean = true; showDefaultOption: boolean = true;
@Input()
required: boolean = false;
@Input() @Input()
defaultOptionText: string = 'Choose One'; defaultOptionText: string = 'Choose One';
constructor() { constructor() {
super(); super();
} }
ngOnInit() {
if (this.required) {
this.formGroup.get(this.controllerName).setValidators(Validators.required);
}
}
} }

View File

@ -13,7 +13,8 @@
</div> </div>
<div class="mdl-cell mdl-cell--6-col"> <div class="mdl-cell mdl-cell--6-col">
<div style="margin-top: 30px"> <div style="margin-top: 30px">
<dropdown-widget [field]="duration" [showDefaultOption]="false" <dropdown-widget [field]="duration" [group]="formGroup" [controllerName]="'timeType'"
[showDefaultOption]="false"
(fieldChanged)="calculateDuration()"></dropdown-widget> (fieldChanged)="calculateDuration()"></dropdown-widget>
</div> </div>
</div> </div>

View File

@ -15,9 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ElementRef, OnInit } from '@angular/core'; import { Component, ElementRef, OnInit, Input } from '@angular/core';
import { NumberWidget } from './../number/number.widget'; import { NumberWidget } from './../number/number.widget';
import { ReportParameterModel, ParameterValueModel } from './../../../models/report.model'; import { ReportParameterDetailsModel, ParameterValueModel } from './../../../models/report.model';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@ -26,14 +27,35 @@ import { ReportParameterModel, ParameterValueModel } from './../../../models/rep
styleUrls: ['./duration.widget.css'] styleUrls: ['./duration.widget.css']
}) })
export class DurationWidget extends NumberWidget implements OnInit { export class DurationWidget extends NumberWidget implements OnInit {
duration: ReportParameterModel;
@Input()
field: any;
@Input('group')
public formGroup: FormGroup;
@Input('controllerName')
public controllerName: string;
@Input()
required: boolean = false;
duration: ReportParameterDetailsModel;
currentValue: number; currentValue: number;
public selectionGroup: FormGroup;
constructor(public elementRef: ElementRef) { constructor(public elementRef: ElementRef) {
super(elementRef); super(elementRef);
} }
ngOnInit() { ngOnInit() {
let timeType = new FormControl();
this.formGroup.addControl('timeType', timeType);
if (this.required) {
this.formGroup.get(this.controllerName).setValidators(Validators.required);
}
if (this.field.value === null) { if (this.field.value === null) {
this.field.value = 0; this.field.value = 0;
} }
@ -44,13 +66,14 @@ export class DurationWidget extends NumberWidget implements OnInit {
paramOptions.push(new ParameterValueModel({id: '3600', name: 'Hours'})); paramOptions.push(new ParameterValueModel({id: '3600', name: 'Hours'}));
paramOptions.push(new ParameterValueModel({id: '86400', name: 'Days', selected: true})); paramOptions.push(new ParameterValueModel({id: '86400', name: 'Days', selected: true}));
this.duration = new ReportParameterModel({id: 'duration', name: 'duration', options: paramOptions}); this.duration = new ReportParameterDetailsModel({id: 'duration', name: 'duration', options: paramOptions});
this.duration.value = paramOptions[0].id; this.duration.value = paramOptions[0].id;
} }
public calculateDuration() { public calculateDuration() {
if (this.field && this.duration.value ) { if (this.field && this.duration.value ) {
this.currentValue = parseInt(this.field.value, 10) * parseInt(this.duration.value, 10); this.currentValue = parseInt(this.field.value, 10) * parseInt(this.duration.value, 10);
this.formGroup.get(this.controllerName).setValue(this.currentValue);
this.fieldChanged.emit({value: this.currentValue}); this.fieldChanged.emit({value: this.currentValue});
} }
} }

View File

@ -1,5 +1,5 @@
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label number-widget"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label number-widget" [formGroup]="formGroup">
<input class="mdl-textfield__input" <input formControlName="{{controllerName}}" class="mdl-textfield__input"
type="text" type="text"
pattern="-?[0-9]*(\.[0-9]+)?" pattern="-?[0-9]*(\.[0-9]+)?"
[attr.id]="field.id" [attr.id]="field.id"

View File

@ -15,8 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ElementRef } from '@angular/core'; import { Component, ElementRef, Input } from '@angular/core';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
import { FormGroup, Validators } from '@angular/forms';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@ -26,10 +27,28 @@ import { WidgetComponent } from './../widget.component';
}) })
export class NumberWidget extends WidgetComponent { export class NumberWidget extends WidgetComponent {
@Input()
field: any;
@Input('group')
public formGroup: FormGroup;
@Input('controllerName')
public controllerName: string;
@Input()
required: boolean = false;
constructor(public elementRef: ElementRef) { constructor(public elementRef: ElementRef) {
super(); super();
} }
ngOnInit() {
if (this.required) {
this.formGroup.get(this.controllerName).setValidators(Validators.required);
}
}
setupMaterialComponents(handler: any): boolean { setupMaterialComponents(handler: any): boolean {
// workaround for MDL issues with dynamic components // workaround for MDL issues with dynamic components
if (handler) { if (handler) {

View File

@ -1,6 +1,11 @@
{ {
"ANALYTICS": { "ANALYTICS": {
"TTILE": "ANALYTICS" "TTILE": "ANALYTICS",
"MESSAGES": {
"UNKNOWN-WIDGET-TYPE": "UNKNOWN WIDGET TYPE",
"FILL-PARAMETER": "Fill in the parameters to generate your report",
"NO-DATA-FOUND": "No data found"
}
}, },
"__KEY_REPORTING": { "__KEY_REPORTING": {
"DEFAULT-REPORTS": { "DEFAULT-REPORTS": {

View File

@ -99,10 +99,15 @@ export class BarChart extends Chart {
} }
}); });
}); });
this.datasets.push({data: dataValue, label: params.key}); if (dataValue && dataValue.length > 0) {
this.datasets.push({data: dataValue, label: params.key});
}
}); });
} }
hasDatasets() {
return this.datasets && this.datasets.length > 0 ? true : false;
}
} }
export class TableChart extends Chart { export class TableChart extends Chart {
@ -116,7 +121,13 @@ export class TableChart extends Chart {
this.title = obj && obj.title || null; this.title = obj && obj.title || null;
this.titleKey = obj && obj.titleKey || null; this.titleKey = obj && obj.titleKey || null;
this.labels = obj && obj.columnNames; this.labels = obj && obj.columnNames;
this.datasets = obj && obj.rows; if (obj.rows) {
this.datasets = obj && obj.rows;
}
}
hasDatasets() {
return this.datasets && this.datasets.length > 0 ? true : false;
} }
} }
@ -131,7 +142,13 @@ export class HeatMapChart extends Chart {
this.title = obj && obj.title || null; this.title = obj && obj.title || null;
this.titleKey = obj && obj.titleKey || null; this.titleKey = obj && obj.titleKey || null;
this.labels = obj && obj.columnNames; this.labels = obj && obj.columnNames;
this.datasets = obj && obj.rows; if (obj.rows) {
this.datasets = obj && obj.rows;
}
}
hasDatasets() {
return this.datasets && this.datasets.length > 0 ? true : false;
} }
} }
@ -156,4 +173,8 @@ export class PieChart extends Chart {
this.labels.push(label); this.labels.push(label);
this.data.push(data); this.data.push(data);
} }
hasData() {
return this.data && this.data.length > 0 ? true : false;
}
} }

View File

@ -20,19 +20,19 @@
* This object represent the report definition. * This object represent the report definition.
* *
* *
* @returns {ReportModel} . * @returns {ReportParametersModel} .
*/ */
export class ReportModel { export class ReportParametersModel {
id: number; id: number;
name: string; name: string;
definition: ReportParametersModel; definition: ReportDefinitionModel;
created: string; created: string;
constructor(obj?: any) { constructor(obj?: any) {
this.id = obj && obj.id; this.id = obj && obj.id;
this.name = obj && obj.name || null; this.name = obj && obj.name || null;
if (obj && obj.definition) { if (obj && obj.definition) {
this.definition = new ReportParametersModel(JSON.parse(obj.definition)); this.definition = new ReportDefinitionModel(JSON.parse(obj.definition));
} }
this.created = obj && obj.created || null; this.created = obj && obj.created || null;
} }
@ -42,17 +42,17 @@ export class ReportModel {
} }
} }
export class ReportParametersModel { export class ReportDefinitionModel {
parameters: ReportParameterModel[] = []; parameters: ReportParameterDetailsModel[] = [];
constructor(obj?: any) { constructor(obj?: any) {
obj.parameters.forEach((params: any) => { obj.parameters.forEach((params: any) => {
let reportParamsModel = new ReportParameterModel(params); let reportParamsModel = new ReportParameterDetailsModel(params);
this.parameters.push(reportParamsModel); this.parameters.push(reportParamsModel);
}); });
} }
findParam(name: string): ReportParameterModel { findParam(name: string): ReportParameterDetailsModel {
this.parameters.forEach((param) => { this.parameters.forEach((param) => {
return param.type === name ? param : null; return param.type === name ? param : null;
}); });
@ -65,9 +65,9 @@ export class ReportParametersModel {
* This object represent the report parameter definition. * This object represent the report parameter definition.
* *
* *
* @returns {ReportParameterModel} . * @returns {ReportParameterDetailsModel} .
*/ */
export class ReportParameterModel { export class ReportParameterDetailsModel {
id: string; id: string;
name: string; name: string;
nameKey: string; nameKey: string;

View File

@ -19,7 +19,7 @@ import { Injectable } from '@angular/core';
import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core'; import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { Response, Http, Headers, RequestOptions, URLSearchParams } from '@angular/http'; import { Response, Http, Headers, RequestOptions, URLSearchParams } from '@angular/http';
import { ReportModel, ParameterValueModel } from '../models/report.model'; import { ReportParametersModel, ParameterValueModel } from '../models/report.model';
import { Chart, PieChart, TableChart, BarChart } from '../models/chart.model'; import { Chart, PieChart, TableChart, BarChart } from '../models/chart.model';
@Injectable() @Injectable()
@ -40,10 +40,10 @@ export class AnalyticsService {
return this.http return this.http
.get(url, options) .get(url, options)
.map((res: any) => { .map((res: any) => {
let reports: ReportModel[] = []; let reports: ReportParametersModel[] = [];
let body = res.json(); let body = res.json();
body.forEach((report: ReportModel) => { body.forEach((report: ReportParametersModel) => {
let reportModel = new ReportModel(report); let reportModel = new ReportParametersModel(report);
reports.push(reportModel); reports.push(reportModel);
}); });
if (body && body.length === 0) { if (body && body.length === 0) {
@ -53,14 +53,14 @@ export class AnalyticsService {
}).catch(this.handleError); }).catch(this.handleError);
} }
getParamsReports(reportId: string): Observable<any> { getReportParams(reportId: string): Observable<any> {
let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/report-params/${reportId}`; let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/report-params/${reportId}`;
let options = this.getRequestOptions(); let options = this.getRequestOptions();
return this.http return this.http
.get(url, options) .get(url, options)
.map((res: any) => { .map((res: any) => {
let body = res.json(); let body = res.json();
return new ReportModel(body); return new ReportParametersModel(body);
}).catch(this.handleError); }).catch(this.handleError);
} }
@ -183,8 +183,8 @@ export class AnalyticsService {
}).catch(this.handleError); }).catch(this.handleError);
} }
public createDefaultReports(): ReportModel[] { public createDefaultReports(): ReportParametersModel[] {
let reports: ReportModel[] = []; let reports: ReportParametersModel[] = [];
return reports; return reports;
} }