\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics-report-list.component.spec.ts b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-list.component.spec.ts
new file mode 100644
index 0000000000..702d31d9e8
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-list.component.spec.ts
@@ -0,0 +1,131 @@
+/*!
+ * @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 { AnalyticsService } from '../services/analytics.service';
+import { DebugElement } from '@angular/core';
+
+declare let jasmine: any;
+
+describe('Test ng2-activiti-analytics Report list', () => {
+
+ let reportList = [
+ {'id': 2002, 'name': 'Fake Test Process definition heat map'},
+ {'id': 2003, 'name': 'Fake Test Process definition overview'},
+ {'id': 2004, 'name': 'Fake Test Process instances overview'},
+ {'id': 2005, 'name': 'Fake Test Task overview'},
+ {'id': 2006, 'name': 'Fake Test Task service level agreement'}
+ ];
+
+ let reportSelected = {'id': 2003, 'name': 'Fake Test Process definition overview'};
+
+ let component: any;
+ let fixture: ComponentFixture;
+ let debug: DebugElement;
+ let element: HTMLElement;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ CoreModule
+ ],
+ declarations: [
+ AnalyticsReportListComponent
+ ],
+ providers: [
+ AnalyticsService
+ ]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AnalyticsReportListComponent);
+ component = fixture.componentInstance;
+ debug = fixture.debugElement;
+ element = fixture.nativeElement;
+ });
+
+ describe('Rendering tests', () => {
+
+ beforeEach(() => {
+ jasmine.Ajax.install();
+ });
+
+ afterEach(() => {
+ jasmine.Ajax.uninstall();
+ });
+
+ it('Report return true with undefined reports', () => {
+ expect(component.isReportsEmpty()).toBeTruthy();
+
+ });
+
+ it('Report return true with an empty reports', () => {
+ component.reports = [];
+ expect(component.isReportsEmpty()).toBeTruthy();
+ });
+
+ it('Report render the report list relative to a single app', (done) => {
+ fixture.detectChanges();
+
+ component.onSuccess.subscribe(() => {
+ fixture.detectChanges();
+ expect(element.querySelector('#report-list-0 > i').innerHTML).toBe('assignment');
+ expect(element.querySelector('#report-list-0 > span').innerHTML).toBe('Fake Test Process definition heat map');
+ expect(element.querySelector('#report-list-1 > span').innerHTML).toBe('Fake Test Process definition overview');
+ expect(element.querySelector('#report-list-2 > span').innerHTML).toBe('Fake Test Process instances overview');
+ expect(element.querySelector('#report-list-3 > span').innerHTML).toBe('Fake Test Task overview');
+ expect(element.querySelector('#report-list-4 > span').innerHTML).toBe('Fake Test Task service level agreement');
+ expect(component.isReportsEmpty()).toBeFalsy();
+ done();
+ });
+
+ jasmine.Ajax.requests.mostRecent().respondWith({
+ status: 200,
+ contentType: 'json',
+ responseText: reportList
+ });
+ });
+
+ it('Report emit an error with a empty response', (done) => {
+ fixture.detectChanges();
+
+ component.onError.subscribe((err) => {
+ expect(err).toBeDefined();
+ done();
+ });
+
+ jasmine.Ajax.requests.mostRecent().respondWith({
+ status: 404,
+ contentType: 'json',
+ responseText: []
+ });
+ });
+
+ it('Should return the current report when one report is selected', () => {
+ component.reportClick.subscribe(() => {
+ expect(component.currentReport).toEqual(reportSelected);
+ });
+
+ component.selectReport(reportSelected);
+ });
+
+ });
+
+});
diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics-report-list.component.ts b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-list.component.ts
new file mode 100644
index 0000000000..266f4be5aa
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/analytics-report-list.component.ts
@@ -0,0 +1,93 @@
+/*!
+ * @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, Output } from '@angular/core';
+import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
+import { AnalyticsService } from '../services/analytics.service';
+import { ReportModel } from '../models/report.model';
+import { Observer } from 'rxjs/Observer';
+import { Observable } from 'rxjs/Observable';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'analytics-report-list',
+ templateUrl: './analytics-report-list.component.html',
+ styleUrls: ['./analytics-report-list.component.css']
+})
+export class AnalyticsReportListComponent implements OnInit {
+
+ @Output()
+ reportClick: EventEmitter = new EventEmitter();
+
+ @Output()
+ onSuccess = new EventEmitter();
+
+ @Output()
+ onError = new EventEmitter();
+
+ private reportObserver: Observer;
+ report$: Observable;
+
+ currentReport: any;
+
+ reports: ReportModel[] = [];
+
+ constructor(private auth: AlfrescoAuthenticationService,
+ private analyticsService: AnalyticsService) {
+
+ this.report$ = new Observable(observer => this.reportObserver = observer).share();
+ }
+
+ ngOnInit() {
+ this.report$.subscribe((report: ReportModel) => {
+ this.reports.push(report);
+ });
+
+ this.getReportListByAppId();
+ }
+
+
+ getReportListByAppId() {
+ this.analyticsService.getReportList().subscribe(
+ (res: ReportModel[]) => {
+ res.forEach((report) => {
+ this.reportObserver.next(report);
+ });
+ this.onSuccess.emit(res);
+ },
+ (err: any) => {
+ this.onError.emit(err);
+ console.log(err);
+ },
+ () => console.log('Reports loaded')
+ );
+ }
+
+ isReportsEmpty(): boolean {
+ return this.reports === undefined || (this.reports && this.reports.length === 0);
+ }
+
+ /**
+ * Select the current report
+ * @param report
+ */
+ public selectReport(report: any) {
+ this.currentReport = report;
+ this.reportClick.emit(report);
+ }
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.css b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.css
new file mode 100644
index 0000000000..096757598a
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.css
@@ -0,0 +1,25 @@
+.chart {display: block; width: 100%;}
+
+.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;
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.html b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.html
index 5bb81bc756..9409f13586 100644
--- a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.html
+++ b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.html
@@ -1,21 +1,113 @@
+
+
-
-
-
-
-
-
-
-
+
+
{{report.title}}
+
+
+
+
+
+
+
+
+
+
{{label | translate}}
+
+
+
{{row | translate }}
+
+
+
+
+
+
+
{{label | translate}}
+
+
+
{{row | translate }}
+
+
+
+
+
+
+
+
+
+ UNKNOWN WIDGET TYPE: {{report.type}}
+
+
+
+
+
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.spec.ts b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.spec.ts
index 7f61019dd0..52ff7da466 100644
--- a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.spec.ts
+++ b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.spec.ts
@@ -16,16 +16,30 @@
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
-import { AnalyticsComponent } from './analytics.component';
-import { DebugElement } from '@angular/core';
import {
- AlfrescoAuthenticationService,
- AlfrescoSettingsService,
- AlfrescoApiService,
CoreModule
} from 'ng2-alfresco-core';
-describe('Test ng2-alfresco-analytics analytics component ', () => {
+import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
+import { AnalyticsComponent } from '../components/analytics.component';
+import { WIDGET_DIRECTIVES } from '../components/widgets/index';
+import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
+
+import { AnalyticsService } from '../services/analytics.service';
+
+import { DebugElement } from '@angular/core';
+
+export const ANALYTICS_DIRECTIVES: any[] = [
+ AnalyticsComponent,
+ AnalyticsReportListComponent,
+ WIDGET_DIRECTIVES
+];
+export const ANALYTICS_PROVIDERS: any[] = [
+ AnalyticsService
+];
+
+
+describe('Show component HTML', () => {
let component: any;
let fixture: ComponentFixture;
@@ -37,25 +51,26 @@ describe('Test ng2-alfresco-analytics analytics component ', () => {
imports: [
CoreModule
],
- declarations: [AnalyticsComponent],
+ declarations: [
+ ...ANALYTICS_DIRECTIVES,
+ ...CHART_DIRECTIVES
+ ],
providers: [
- AlfrescoSettingsService,
- AlfrescoAuthenticationService,
- AlfrescoApiService
+ ...ANALYTICS_PROVIDERS
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsComponent);
-
+ component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
- component = fixture.componentInstance;
fixture.detectChanges();
});
- xit('No test', () => {
+ it('Display component tag base-chart', () => {
+ expect(true).toBe(true);
});
-});
+});
diff --git a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.ts b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.ts
index 92f4aa7c40..cb897fe774 100644
--- a/ng2-components/ng2-activiti-analytics/src/components/analytics.component.ts
+++ b/ng2-components/ng2-activiti-analytics/src/components/analytics.component.ts
@@ -15,35 +15,170 @@
* limitations under the License.
*/
-import { Component } from '@angular/core';
+import { Component, EventEmitter, OnInit, OnChanges, Input, Output, SimpleChanges, ViewChild } from '@angular/core';
+import { AlfrescoTranslationService } from 'ng2-alfresco-core';
+import { AnalyticsService } from '../services/analytics.service';
+import { ReportModel, ReportQuery, ParameterValueModel, ReportParameterModel } from '../models/report.model';
+import { Chart } from '../models/chart.model';
+import { FormGroup, FormBuilder, Validators } from '@angular/forms';
+import * as moment from 'moment';
@Component({
moduleId: module.id,
selector: 'activiti-analytics',
- templateUrl: './analytics.component.html'
+ templateUrl: './analytics.component.html',
+ styleUrls: ['./analytics.component.css']
})
-export class AnalyticsComponent {
+export class AnalyticsComponent implements OnInit, OnChanges {
- constructor() {
+ @ViewChild('processDefinition')
+ processDefinition: any;
+
+ @Input()
+ reportId: string;
+
+ @Output()
+ onSuccess = new EventEmitter();
+
+ @Output()
+ onError = new EventEmitter();
+
+ reportDetails: ReportModel;
+
+ reportParamQuery = new ReportQuery();
+
+ reports: any[];
+
+ reportForm: FormGroup;
+
+ debug: boolean = true;
+
+ constructor(private translate: AlfrescoTranslationService,
+ private analyticsService: AnalyticsService,
+ private formBuilder: FormBuilder ) {
console.log('AnalyticsComponent');
+ if (translate) {
+ translate.addTranslationFolder('node_modules/ng2-activiti-analytics/src');
+ }
}
- // lineChart
- public lineChartData: Array = [
- [65, 59, 80, 81, 56, 55, 40],
- [28, 48, 40, 19, 86, 27, 90]
- ];
- public lineChartLabels: Array = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
- public lineChartType: string = 'line';
- public pieChartType: string = 'pie';
+ ngOnInit() {
+ let today = moment().format('YYYY-MM-DD');
+ this.reportForm = this.formBuilder.group({
+ dateRange: this.formBuilder.group({
+ startDate: [today, Validators.required],
+ endDate: [today, Validators.required]
+ })
+ });
+ }
- // Pie
- public pieChartLabels: string[] = ['Download Sales', 'In-Store Sales', 'Mail Sales'];
- public pieChartData: number[] = [300, 500, 100];
+ ngOnChanges(changes: SimpleChanges) {
+ let reportId = changes['reportId'];
+ if (reportId && reportId.currentValue) {
+ this.getParamsReports(reportId.currentValue);
+ return;
+ }
+ }
- public randomizeType(): void {
- this.lineChartType = this.lineChartType === 'line' ? 'bar' : 'line';
- this.pieChartType = this.pieChartType === 'doughnut' ? 'pie' : 'doughnut';
+ public getParamsReports(reportId: string) {
+ this.reset();
+ this.analyticsService.getParamsReports(reportId).subscribe(
+ (res: ReportModel) => {
+ this.reportDetails = res;
+ this.retriveParameterOptions();
+ this.onSuccess.emit(res);
+ },
+ (err: any) => {
+ this.onError.emit(err);
+ console.log(err);
+ },
+ () => console.log('Login done')
+ );
+ }
+
+ private retriveParameterOptions() {
+ this.reportDetails.definition.parameters.forEach((param) => {
+ this.analyticsService.getParamValuesByType(param.type).subscribe(
+ (opts: ParameterValueModel[]) => {
+ param.options = opts;
+ },
+ (err: any) => {
+ console.log(err);
+ },
+ () => console.log(`${param.type} options loaded`)
+ );
+ });
+ }
+
+ public createReport() {
+ this.analyticsService.getReportsByParams(this.reportDetails.id, this.reportParamQuery).subscribe(
+ (res: Chart[]) => {
+ this.reports = res;
+ this.onSuccess.emit(res);
+ },
+ (err: any) => {
+ this.onError.emit(err);
+ console.log(err);
+ },
+ () => console.log('Login done')
+ );
+ }
+
+ 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.analyticsService.getTasksByProcessDefinitionId(this.reportId, this.reportParamQuery.processDefinitionId).subscribe(
+ (res: any) => {
+ let paramTask: ReportParameterModel = this.reportDetails.definition.parameters.find(p => p.type === 'task');
+ if (paramTask) {
+ paramTask.options = res;
+ }
+ });
+ }
+ }
+
+ 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() {
+ this.reports = null;
}
public chartClicked(e: any): void {
@@ -53,4 +188,8 @@ export class AnalyticsComponent {
public chartHovered(e: any): void {
console.log(e);
}
+
+ public convertNumber(value: string): number {
+ return parseInt(value, 10);
+ }
}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/checkbox/checkbox.widget.html b/ng2-components/ng2-activiti-analytics/src/components/widgets/checkbox/checkbox.widget.html
new file mode 100644
index 0000000000..b9841dae1b
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/checkbox/checkbox.widget.html
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/checkbox/checkbox.widget.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/checkbox/checkbox.widget.ts
new file mode 100644
index 0000000000..67a6081e1c
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/checkbox/checkbox.widget.ts
@@ -0,0 +1,34 @@
+/*!
+ * @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 } from '@angular/core';
+import { WidgetComponent } from './../widget.component';
+
+declare var componentHandler;
+
+@Component({
+ moduleId: module.id,
+ selector: 'checkbox-widget',
+ templateUrl: './checkbox.widget.html'
+})
+export class CheckboxWidget extends WidgetComponent {
+
+ constructor() {
+ super();
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.css b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.css
new file mode 100644
index 0000000000..384c81f215
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.css
@@ -0,0 +1,3 @@
+.date-picker-mdl {
+ margin-left: 20px;
+}
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.html b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.html
new file mode 100644
index 0000000000..bade2bc528
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.html
@@ -0,0 +1,48 @@
+
+
+
+ Start date must be less than End date
+
+
+
+
+
+
+
+ Start is required
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
FormGroup : {{ dateRange.value | json }}
+
FormGroup valid : {{ dateRange.valid }}
+
FormGroup status : {{ dateRange.errors | json }}
+
FormGroup start status : {{ dateRange.controls.startDate.errors | json }}
+
FormGroup end status: {{ dateRange.controls.endDate.errors | json }}
+
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.ts
new file mode 100644
index 0000000000..315c2ca109
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.ts
@@ -0,0 +1,137 @@
+/*!
+ * @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, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
+import { AbstractControl, FormGroup, FormBuilder } from '@angular/forms';
+import { WidgetComponent } from './../widget.component';
+import * as moment from 'moment';
+
+declare let mdDateTimePicker: any;
+
+function dateCheck(c: AbstractControl) {
+ let startDate = moment(c.get('startDate').value);
+ let endDate = moment(c.get('endDate').value);
+ let result = startDate.isAfter(endDate);
+ return result ? {'greaterThan': true} : null;
+}
+
+@Component({
+ moduleId: module.id,
+ selector: 'date-range-widget',
+ templateUrl: './date-range.widget.html',
+ styleUrls: ['./date-range.widget.css']
+})
+export class DateRangeWidget extends WidgetComponent {
+
+ public static FORMAT_DATE_ACTIVITI: string = 'YYYY-MM-DD';
+
+ @ViewChild('startElement')
+ startElement: any;
+
+ @ViewChild('endElement')
+ endElement: any;
+
+ @Input('group')
+ public dateRange: FormGroup;
+
+ @Input()
+ field: any;
+
+ @Output()
+ dateRangeChanged: EventEmitter = new EventEmitter();
+
+ debug: boolean = true;
+
+ dialogStart: any = new mdDateTimePicker.default({
+ type: 'date',
+ future: moment().add(21, 'years')
+ });
+
+ dialogEnd: any = new mdDateTimePicker.default({
+ type: 'date',
+ future: moment().add(21, 'years')
+ });
+
+ constructor(public elementRef: ElementRef,
+ private formBuilder: FormBuilder) {
+ super();
+ }
+
+ ngOnInit() {
+ this.initForm();
+ this.initSartDateDialog();
+ this.initEndDateDialog();
+ }
+
+ initForm() {
+ this.dateRange.setValidators(dateCheck);
+ this.dateRange.valueChanges.subscribe(data => this.onGroupValueChanged(data));
+ }
+
+ initSartDateDialog() {
+ this.dialogStart.trigger = this.startElement.nativeElement;
+
+ let startDateButton = document.getElementById('startDateButton');
+ startDateButton.addEventListener('click', () => {
+ this.dialogStart.toggle();
+ });
+ }
+
+ initEndDateDialog() {
+ this.dialogEnd.trigger = this.endElement.nativeElement;
+
+ let endDateButton = document.getElementById('endDateButton');
+ endDateButton.addEventListener('click', () => {
+ this.dialogEnd.toggle();
+ });
+ }
+
+ onOkStart(inputEl: HTMLInputElement) {
+ let date = this.dialogStart.time.format(DateRangeWidget.FORMAT_DATE_ACTIVITI);
+ this.dateRange.patchValue({
+ startDate: date
+ });
+ let materialElemen: any = inputEl.parentElement;
+ if (materialElemen) {
+ materialElemen.MaterialTextfield.change(date);
+ }
+ }
+
+ onOkEnd(inputEl: HTMLInputElement) {
+ let date = this.dialogEnd.time.format(DateRangeWidget.FORMAT_DATE_ACTIVITI);
+ this.dateRange.patchValue({
+ endDate: date
+ });
+
+ let materialElemen: any = inputEl.parentElement;
+ if (materialElemen) {
+ materialElemen.MaterialTextfield.change(date);
+ }
+ }
+
+ onGroupValueChanged(data: any) {
+ if (this.dateRange.valid) {
+ let dateStart = this.convertMomentDate(this.dateRange.controls['startDate'].value);
+ let endStart = this.convertMomentDate(this.dateRange.controls['endDate'].value);
+ this.dateRangeChanged.emit({startDate: dateStart, endDate: endStart});
+ }
+ }
+
+ public convertMomentDate(date: string) {
+ return moment(date, DateRangeWidget.FORMAT_DATE_ACTIVITI, true).format(DateRangeWidget.FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z';
+ }
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.css b/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.css
new file mode 100644
index 0000000000..48a6b82b45
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.css
@@ -0,0 +1,19 @@
+.date-widget {
+ width: 100%;
+}
+
+.date-widget__invalid .mdl-textfield__input {
+ border-color: #d50000;
+}
+
+.date-widget__invalid .mdl-textfield__label {
+ color: #d50000;
+}
+
+.date-widget__invalid .mdl-textfield__label:after {
+ background-color: #d50000;
+}
+
+.date-widget__invalid .mdl-textfield__error {
+ visibility: visible !important;
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.html b/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.html
new file mode 100644
index 0000000000..1312f7ebc8
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.html
@@ -0,0 +1,10 @@
+
+
+
+ {{field.validationSummary}}
+
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.ts
new file mode 100644
index 0000000000..08447f456a
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date/date.widget.ts
@@ -0,0 +1,51 @@
+/*!
+ * @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, ElementRef } from '@angular/core';
+import { WidgetComponent } from './../widget.component';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'date-widget',
+ templateUrl: './date.widget.html',
+ styleUrls: ['./date.widget.css']
+})
+export class DateWidget extends WidgetComponent {
+
+ constructor(private elementRef: ElementRef) {
+ super();
+ }
+
+ setupMaterialComponents(componentHandler: any): boolean {
+ // workaround for MDL issues with dynamic components
+ if (componentHandler) {
+ componentHandler.upgradeAllRegistered();
+ if (this.elementRef && this.hasValue()) {
+ let el = this.elementRef.nativeElement;
+ let container = el.querySelector('.mdl-textfield');
+ if (container) {
+ container.MaterialTextfield.change(this.field.value);
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.css b/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.css
new file mode 100644
index 0000000000..ae995ca854
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.css
@@ -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;
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.html b/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.html
new file mode 100644
index 0000000000..0066152f30
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.html
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.ts
new file mode 100644
index 0000000000..b22edf556e
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/dropdown/dropdown.widget.ts
@@ -0,0 +1,50 @@
+/*!
+ * @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, Input, Output, EventEmitter } from '@angular/core';
+import { WidgetComponent } from './../widget.component';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'dropdown-widget',
+ templateUrl: './dropdown.widget.html',
+ styleUrls: ['./dropdown.widget.css']
+})
+export class DropdownWidget extends WidgetComponent {
+
+ @Input()
+ field: any;
+
+ @Output()
+ fieldChanged: EventEmitter = new EventEmitter();
+
+ @Input()
+ showDefaultOption: boolean = true;
+
+ @Input()
+ defaultOptionText: string = 'Choose One';
+
+ constructor() {
+ super();
+ }
+
+ handleError(error: any) {
+ console.error(error);
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.css b/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.css
new file mode 100644
index 0000000000..3e4f6952f3
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.css
@@ -0,0 +1,19 @@
+.number-widget {
+ width: 100%;
+}
+
+.number-widget__invalid .mdl-textfield__input {
+ border-color: #d50000;
+}
+
+.number-widget__invalid .mdl-textfield__label {
+ color: #d50000;
+}
+
+.number-widget__invalid .mdl-textfield__label:after {
+ background-color: #d50000;
+}
+
+.number-widget__invalid .mdl-textfield__error {
+ visibility: visible !important;
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.html b/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.html
new file mode 100644
index 0000000000..2db7992288
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.ts
new file mode 100644
index 0000000000..11ab9ccb46
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/duration/duration.widget.ts
@@ -0,0 +1,59 @@
+/*!
+ * @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, ElementRef, OnInit } from '@angular/core';
+import { NumberWidget } from './../number/number.widget';
+import { ReportParameterModel, ParameterValueModel } from './../../../models/report.model';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'duration-widget',
+ templateUrl: './duration.widget.html',
+ styleUrls: ['./duration.widget.css']
+})
+export class DurationWidget extends NumberWidget implements OnInit {
+ duration: ReportParameterModel;
+ currentValue: number;
+
+ constructor(public elementRef: ElementRef) {
+ super(elementRef);
+ }
+
+ ngOnInit() {
+ if (this.field.value === null) {
+ this.field.value = 0;
+ }
+
+ let paramOptions: ParameterValueModel[] = [];
+ paramOptions.push(new ParameterValueModel({id: '1', name: 'Seconds'}));
+ paramOptions.push(new ParameterValueModel({id: '60', name: 'Minutes'}));
+ paramOptions.push(new ParameterValueModel({id: '3600', name: 'Hours'}));
+ paramOptions.push(new ParameterValueModel({id: '86400', name: 'Days', selected: true}));
+
+ this.duration = new ReportParameterModel({id: 'duration', name: 'duration', options: paramOptions});
+ this.duration.value = paramOptions[0].id;
+ }
+
+ public calculateDuration() {
+ if (this.field && this.duration.value ) {
+ this.currentValue = parseInt(this.field.value, 10) * parseInt(this.duration.value, 10);
+ this.fieldChanged.emit({value: this.currentValue});
+ }
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/index.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/index.ts
new file mode 100644
index 0000000000..efc2d86d5f
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/index.ts
@@ -0,0 +1,40 @@
+/*!
+ * @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 { DropdownWidget } from './dropdown/dropdown.widget';
+import { NumberWidget } from './number/number.widget';
+import { DurationWidget } from './duration/duration.widget';
+import { CheckboxWidget } from './checkbox/checkbox.widget';
+import { DateWidget } from './date/date.widget';
+import { DateRangeWidget } from './date-range/date-range.widget';
+
+// primitives
+export * from './dropdown/dropdown.widget';
+export * from './number/number.widget';
+export * from './duration/duration.widget';
+export * from './checkbox/checkbox.widget';
+export * from './date/date.widget';
+export * from './date-range/date-range.widget';
+
+export const WIDGET_DIRECTIVES: any[] = [
+ DropdownWidget,
+ NumberWidget,
+ DurationWidget,
+ CheckboxWidget,
+ DateWidget,
+ DateRangeWidget
+];
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.css b/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.css
new file mode 100644
index 0000000000..3e4f6952f3
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.css
@@ -0,0 +1,19 @@
+.number-widget {
+ width: 100%;
+}
+
+.number-widget__invalid .mdl-textfield__input {
+ border-color: #d50000;
+}
+
+.number-widget__invalid .mdl-textfield__label {
+ color: #d50000;
+}
+
+.number-widget__invalid .mdl-textfield__label:after {
+ background-color: #d50000;
+}
+
+.number-widget__invalid .mdl-textfield__error {
+ visibility: visible !important;
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.html b/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.html
new file mode 100644
index 0000000000..64acbf818e
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.html
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.ts
new file mode 100644
index 0000000000..ca5dab97d3
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/number/number.widget.ts
@@ -0,0 +1,48 @@
+/*!
+ * @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, ElementRef } from '@angular/core';
+import { WidgetComponent } from './../widget.component';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'number-widget',
+ templateUrl: './number.widget.html',
+ styleUrls: ['./number.widget.css']
+})
+export class NumberWidget extends WidgetComponent {
+
+ constructor(public elementRef: ElementRef) {
+ super();
+ }
+
+ setupMaterialComponents(handler: any): boolean {
+ // workaround for MDL issues with dynamic components
+ if (handler) {
+ handler.upgradeAllRegistered();
+ if (this.elementRef && this.hasValue()) {
+ let container = this.elementRef.nativeElement.querySelector('.mdl-textfield');
+ if (container) {
+ container.MaterialTextfield.change(this.field.value.toString());
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/components/widgets/widget.component.ts b/ng2-components/ng2-activiti-analytics/src/components/widgets/widget.component.ts
new file mode 100644
index 0000000000..eafd8cdca2
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/widget.component.ts
@@ -0,0 +1,68 @@
+/*!
+ * @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 { Input, AfterViewInit, Output, EventEmitter, SimpleChanges, OnChanges } from '@angular/core';
+
+declare var componentHandler;
+
+/**
+ * Base widget component.
+ */
+export class WidgetComponent implements AfterViewInit, OnChanges {
+
+ @Input()
+ field: any;
+
+ @Output()
+ fieldChanged: EventEmitter = new EventEmitter();
+
+ ngOnChanges(changes: SimpleChanges) {
+ let field = changes['field'];
+ if (field && field.currentValue) {
+ this.fieldChanged.emit(field.currentValue.value);
+ return;
+ }
+ }
+
+ hasField() {
+ return this.field ? true : false;
+ }
+
+ hasValue(): boolean {
+ return this.field &&
+ this.field.value !== null &&
+ this.field.value !== undefined;
+ }
+
+ changeValue(field: any) {
+ this.fieldChanged.emit(field);
+ }
+
+ ngAfterViewInit() {
+ this.setupMaterialComponents(componentHandler);
+ }
+
+ setupMaterialComponents(handler?: any): boolean {
+ // workaround for MDL issues with dynamic components
+ if (handler) {
+ handler.upgradeAllRegistered();
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/i18n/en.json b/ng2-components/ng2-activiti-analytics/src/i18n/en.json
index 3afc878e36..fd01ebcd42 100644
--- a/ng2-components/ng2-activiti-analytics/src/i18n/en.json
+++ b/ng2-components/ng2-activiti-analytics/src/i18n/en.json
@@ -1,5 +1,40 @@
{
"ANALYTICS": {
"TTILE": "ANALYTICS"
+ },
+ "__KEY_REPORTING": {
+ "DEFAULT-REPORTS": {
+ "PROCESS-DEFINITION-OVERVIEW": {
+ "GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS": "Total number of process definitions",
+ "GENERAL-TABLE-TOTAL-PROCESS-INSTANCES": "Total number of process instances",
+ "GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES": "Total number of active process instances",
+ "GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES": "Total number of completed process instances"
+ }
+ }
+ },
+ "REPORTING": {
+ "DEFAULT-REPORTS": {
+ "PROCESS-HEAT-MAP": {
+ "TYPE-FILTERING": "Include all process steps (Unchecking this, will remove pass through steps like start events, gateways, etc.)?"
+ },
+ "PROCESS-INSTANCES-OVERVIEW": {
+ "PROCESS-DEFINITION": "Process definition",
+ "DATE-RANGE": "Date range",
+ "SLOW-PROC-INST-NUMBER": "How many of the slowest process instances should be displayed?"
+ },
+ "TASK-OVERVIEW": {
+ "PROCESS-DEFINITION": "Process definition",
+ "DATE-RANGE": "Date range",
+ "DATE-RANGE-INTERVAL": "Aggregate dates by"
+ },
+ "TASK-SLA": {
+ "TASK": "Task",
+ "PROCESS-DEFINITION": "Process definition",
+ "DATE-RANGE": "Date range",
+ "SLA-DURATION": "What is the time this task needs to be completed in to be within the SLA?"
+ }
+ },
+ "PROCESS-STATUS": "Process status",
+ "TASK-STATUS": "Task status"
}
}
diff --git a/ng2-components/ng2-activiti-analytics/src/models/chart.model.ts b/ng2-components/ng2-activiti-analytics/src/models/chart.model.ts
new file mode 100644
index 0000000000..30f7db5ba9
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/models/chart.model.ts
@@ -0,0 +1,160 @@
+/*!
+ * @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.
+ */
+
+export class Chart {
+ id: string;
+ type: string;
+
+ constructor(obj?: any) {
+ this.id = obj && obj.id || null;
+ if (obj && obj.type) {
+ this.type = this.convertType(obj.type);
+ }
+ }
+
+ private convertType(type: string) {
+ let chartType = '';
+ switch (type) {
+ case 'pieChart':
+ chartType = 'pie';
+ break;
+ case 'table':
+ chartType = 'table';
+ break;
+ case 'line':
+ chartType = 'line';
+ break;
+ case 'barChart':
+ chartType = 'bar';
+ break;
+ default:
+ chartType = 'table';
+ break;
+ }
+ return chartType;
+ }
+}
+
+export class LineChart extends Chart {
+ title: string;
+ titleKey: string;
+ labels: string[] = [];
+ datasets: any[] = [];
+
+ constructor(obj?: any) {
+ super(obj);
+ this.title = obj && obj.title || null;
+ this.titleKey = obj && obj.titleKey || null;
+ this.labels = obj && obj.columnNames.slice(1, obj.columnNames.length);
+
+ obj.rows.forEach((value: any) => {
+ this.datasets.push({data: value.slice(1, value.length), label: value[0]});
+ });
+ }
+}
+
+export class BarChart extends Chart {
+ title: string;
+ titleKey: string;
+ labels: string[] = [];
+ datasets: any[] = [];
+ data: any[] = [];
+ options: any = {
+ scales: {
+ yAxes: [{
+ ticks: {
+ beginAtZero: true,
+ stepSize: 1
+ }
+ }]
+ }
+ };
+
+ constructor(obj?: any) {
+ super(obj);
+ this.title = obj && obj.title || null;
+ this.titleKey = obj && obj.titleKey || null;
+ obj.values.forEach((params: any) => {
+ let dataValue = [];
+ params.values.forEach((info: any) => {
+ info.forEach((value: any, index: any) => {
+ if (index % 2 === 0) {
+ this.labels.push(value);
+ } else {
+ dataValue.push(value);
+ }
+ });
+ });
+ this.datasets.push({data: dataValue, label: params.key});
+
+ });
+ }
+}
+
+export class TableChart extends Chart {
+ title: string;
+ titleKey: string;
+ labels: string[] = [];
+ datasets: any[] = [];
+
+ constructor(obj?: any) {
+ super(obj);
+ this.title = obj && obj.title || null;
+ this.titleKey = obj && obj.titleKey || null;
+ this.labels = obj && obj.columnNames;
+ this.datasets = obj && obj.rows;
+ }
+}
+
+
+export class HeatMapChart extends Chart {
+ title: string;
+ titleKey: string;
+ labels: string[] = [];
+ datasets: any[] = [];
+
+ constructor(obj?: any) {
+ super(obj);
+ this.title = obj && obj.title || null;
+ this.titleKey = obj && obj.titleKey || null;
+ this.labels = obj && obj.columnNames;
+ this.datasets = obj && obj.rows;
+ }
+}
+
+export class PieChart extends Chart {
+ title: string;
+ titleKey: string;
+ labels: string[] = [];
+ data: string[] = [];
+
+ constructor(obj?: any) {
+ super(obj);
+ this.title = obj && obj.title || null;
+ this.titleKey = obj && obj.titleKey || null;
+ if (obj.values) {
+ obj.values.forEach((value: any) => {
+ this.add(value.key, value.y);
+ });
+ }
+ }
+
+ add(label: string, data: string) {
+ this.labels.push(label);
+ this.data.push(data);
+ }
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/models/report.model.ts b/ng2-components/ng2-activiti-analytics/src/models/report.model.ts
new file mode 100644
index 0000000000..d13f61bbcb
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/models/report.model.ts
@@ -0,0 +1,136 @@
+/*!
+ * @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.
+ */
+
+/**
+ *
+ * This object represent the report definition.
+ *
+ *
+ * @returns {ReportModel} .
+ */
+export class ReportModel {
+ id: number;
+ name: string;
+ definition: ReportParametersModel;
+ created: string;
+
+ constructor(obj?: any) {
+ this.id = obj && obj.id;
+ this.name = obj && obj.name || null;
+ if (obj && obj.definition) {
+ this.definition = new ReportParametersModel(JSON.parse(obj.definition));
+ }
+ this.created = obj && obj.created || null;
+ }
+}
+
+export class ReportParametersModel {
+ parameters: ReportParameterModel[] = [];
+
+ constructor(obj?: any) {
+ obj.parameters.forEach((params: any) => {
+ let reportParamsModel = new ReportParameterModel(params);
+ this.parameters.push(reportParamsModel);
+ });
+ }
+
+ findParam(name: string): ReportParameterModel {
+ this.parameters.forEach((param) => {
+ return param.type === name ? param : null;
+ });
+ return null;
+ }
+}
+
+/**
+ *
+ * This object represent the report parameter definition.
+ *
+ *
+ * @returns {ReportParameterModel} .
+ */
+export class ReportParameterModel {
+ id: string;
+ name: string;
+ nameKey: string;
+ type: string;
+ value: string;
+ options: ParameterValueModel[];
+ dependsOn: string;
+
+ constructor(obj?: any) {
+ this.id = obj && obj.id;
+ this.name = obj && obj.name || null;
+ this.nameKey = obj && obj.nameKey || null;
+ this.type = obj && obj.type || null;
+ this.value = obj && obj.value || null;
+ this.options = obj && obj.options || null;
+ this.dependsOn = obj && obj.dependsOn || null;
+ }
+}
+
+export class ParameterValueModel {
+ id: string;
+ name: string;
+ version: string;
+ value: string;
+
+ constructor(obj?: any) {
+ this.id = obj && obj.id;
+ this.name = obj && obj.name || null;
+ this.value = obj && obj.value || null;
+ this.version = obj && obj.version || null;
+ }
+
+ get label () {
+ return this.version ? `${this.name} (v ${this.version}) ` : this.name;
+ }
+}
+
+
+export class ReportQuery {
+ processDefinitionId: string;
+ status: string;
+ taskName: string;
+ typeFiltering: boolean;
+ dateRange: ReportDateRange;
+ dateRangeInterval: string;
+ slowProcessInstanceInteger: number;
+ duration: number;
+
+ constructor(obj?: any) {
+ this.processDefinitionId = obj && obj.processDefinitionId || null;
+ this.status = obj && obj.status || null;
+ this.taskName = obj && obj.taskName || null;
+ this.dateRangeInterval = obj && obj.dateRangeInterval || null;
+ this.typeFiltering = obj && obj.typeFiltering || false;
+ this.slowProcessInstanceInteger = obj && obj.slowProcessInstanceInteger || 0;
+ this.duration = obj && obj.duration || 0;
+ this.dateRange = new ReportDateRange(obj);
+ }
+}
+
+export class ReportDateRange {
+ startDate: string;
+ endDate: string;
+
+ constructor(obj?: any) {
+ this.startDate = obj && obj.startDate || null;
+ this.endDate = obj && obj.endDate || null;
+ }
+
+}
diff --git a/ng2-components/ng2-activiti-analytics/src/services/analytics.service.ts b/ng2-components/ng2-activiti-analytics/src/services/analytics.service.ts
new file mode 100644
index 0000000000..01f6b161ad
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/services/analytics.service.ts
@@ -0,0 +1,211 @@
+/*!
+ * @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 { Injectable } from '@angular/core';
+import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
+import { Observable } from 'rxjs/Rx';
+import { Response, Http, Headers, RequestOptions, URLSearchParams } from '@angular/http';
+import { ReportModel, ParameterValueModel } from '../models/report.model';
+import { Chart, PieChart, TableChart, BarChart } from '../models/chart.model';
+
+@Injectable()
+export class AnalyticsService {
+
+ constructor(private authService: AlfrescoAuthenticationService,
+ private http: Http,
+ private alfrescoSettingsService: AlfrescoSettingsService) {
+ }
+
+ /**
+ * Retrive all the Deployed app
+ * @returns {Observable}
+ */
+ getReportList(): Observable {
+ let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/reports`;
+ let options = this.getRequestOptions();
+ return this.http
+ .get(url, options)
+ .map((res: any) => {
+ let reports: ReportModel[] = [];
+ let body = res.json();
+ body.forEach((report: ReportModel) => {
+ let reportModel = new ReportModel(report);
+ reports.push(reportModel);
+ });
+ if (body && body.length === 0) {
+ return this.createDefaultReports();
+ }
+ return reports;
+ }).catch(this.handleError);
+ }
+
+ getParamsReports(reportId: string): Observable {
+ let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/report-params/${reportId}`;
+ let options = this.getRequestOptions();
+ return this.http
+ .get(url, options)
+ .map((res: any) => {
+ let body = res.json();
+ return new ReportModel(body);
+ }).catch(this.handleError);
+ }
+
+ getParamValuesByType(type: string, reportId?: string, processDefinitionId?: string) {
+ if (type === 'status') {
+ return this.getProcessStatusValues();
+ } else if (type === 'processDefinition') {
+ return this.getProcessDefinitionsValues();
+ } else if (type === 'dateInterval') {
+ return this.getDateIntervalValues();
+ } else if (type === 'task') {
+ return this.getTasksByProcessDefinitionId(reportId, processDefinitionId);
+ } else {
+ return Observable.create(observer => {
+ observer.next(null);
+ observer.complete();
+ });
+ }
+ }
+
+ getProcessStatusValues(): Observable {
+ let paramOptions: ParameterValueModel[] = [];
+
+ paramOptions.push(new ParameterValueModel({id: 'All', name: 'All'}));
+ paramOptions.push(new ParameterValueModel({id: 'Active', name: 'Active'}));
+ paramOptions.push(new ParameterValueModel({id: 'Complete', name: 'Complete'}));
+
+ return Observable.create(observer => {
+ observer.next(paramOptions);
+ observer.complete();
+ });
+ }
+
+ getDateIntervalValues(): Observable {
+ let paramOptions: ParameterValueModel[] = [];
+
+ paramOptions.push(new ParameterValueModel({id: 'byHour', name: 'By hour'}));
+ paramOptions.push(new ParameterValueModel({id: 'byDay', name: 'By day'}));
+ paramOptions.push(new ParameterValueModel({id: 'byWeek', name: 'By week'}));
+ paramOptions.push(new ParameterValueModel({id: 'byMonth', name: 'By month'}));
+ paramOptions.push(new ParameterValueModel({id: 'byYear', name: 'By year'}));
+
+ return Observable.create(observer => {
+ observer.next(paramOptions);
+ observer.complete();
+ });
+ }
+
+ getProcessDefinitionsValues(appId?: string): Observable {
+ let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/process-definitions`;
+ let params: URLSearchParams;
+ if (appId) {
+ params = new URLSearchParams();
+ params.set('appDefinitionId', appId);
+ }
+ let options = this.getRequestOptions(params);
+ return this.http
+ .get(url, options)
+ .map((res: any) => {
+ let paramOptions: ParameterValueModel[] = [];
+ let body = res.json();
+ body.forEach((opt) => {
+ paramOptions.push(new ParameterValueModel(opt));
+ });
+ return paramOptions;
+ }).catch(this.handleError);
+ }
+
+ getTasksByProcessDefinitionId(reportId: string, processDefinitionId: string): Observable {
+ if (processDefinitionId) {
+ let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/report-params/${reportId}/tasks`;
+ let params: URLSearchParams;
+ if (processDefinitionId) {
+ params = new URLSearchParams();
+ params.set('processDefinitionId', processDefinitionId);
+ }
+ let options = this.getRequestOptions(params);
+ return this.http
+ .get(url, options)
+ .map((res: any) => {
+ let paramOptions: ParameterValueModel[] = [];
+ let body = res.json();
+ body.forEach((opt) => {
+ paramOptions.push(new ParameterValueModel({id: opt, name: opt}));
+ });
+ return paramOptions;
+ }).catch(this.handleError);
+ } else {
+ return Observable.create(observer => {
+ observer.next(null);
+ observer.complete();
+ });
+ }
+ }
+
+ getReportsByParams(reportId: number, paramsQuery: any): Observable {
+ let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/reporting/report-params/${reportId}`;
+ let body = JSON.stringify(paramsQuery);
+ let options = this.getRequestOptions();
+ return this.http
+ .post(url, body, options)
+ .map((res: any) => {
+ let elements: Chart[] = [];
+ let bodyRes = res.json();
+ bodyRes.elements.forEach((chartData) => {
+ if (chartData.type === 'pieChart') {
+ elements.push(new PieChart(chartData));
+ } else if (chartData.type === 'table') {
+ elements.push(new TableChart(chartData));
+ } else if (chartData.type === 'processDefinitionHeatMap') {
+ elements.push(new TableChart(chartData));
+ } else if (chartData.type === 'masterDetailTable') {
+ elements.push(new TableChart(chartData));
+ } else if (chartData.type === 'barChart') {
+ elements.push(new BarChart(chartData));
+ }
+ });
+
+ return elements;
+ }).catch(this.handleError);
+ }
+
+ public createDefaultReports(): ReportModel[] {
+ let reports: ReportModel[] = [];
+ return reports;
+ }
+
+ public getHeaders(): Headers {
+ return new Headers({
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': this.authService.getTicketBpm()
+ });
+ }
+
+ public getRequestOptions(param?: any): RequestOptions {
+ let headers = this.getHeaders();
+ return new RequestOptions({headers: headers, withCredentials: true, search: param});
+ }
+
+ private handleError(error: Response) {
+ console.error(error);
+ return Observable.throw(error.json().error || 'Server error');
+ }
+
+
+
+}
diff --git a/ng2-components/ng2-alfresco-login/demo/package.json b/ng2-components/ng2-alfresco-login/demo/package.json
index 95ca477852..091fdfc9cd 100644
--- a/ng2-components/ng2-alfresco-login/demo/package.json
+++ b/ng2-components/ng2-alfresco-login/demo/package.json
@@ -60,7 +60,8 @@
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27",
"zone.js": "^0.6.23",
-
+ "md-date-time-picker": "^2.2.0",
+ "moment": "2.15.1",
"material-design-icons": "2.2.3",
"material-design-lite": "1.2.1",
"ng2-translate": "2.5.0",