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/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..e69de29bb2
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..a674d0fe5d
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
\ 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..d1821d4251
--- /dev/null
+++ b/ng2-components/ng2-activiti-analytics/src/components/widgets/date-range/date-range.widget.ts
@@ -0,0 +1,49 @@
+/*!
+ * @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 * as moment from 'moment';
+
+@Component({
+ moduleId: module.id,
+ selector: 'date-range-widget',
+ templateUrl: './date-range.widget.html',
+ styleUrls: ['./date-range.widget.css']
+})
+export class DateRangeWidget {
+
+ @Input()
+ field: any;
+
+ @Output()
+ dateRangeChanged: EventEmitter = new EventEmitter();
+
+ constructor() {}
+
+ public onDateRangeChanged(startDate: HTMLInputElement, endDate: HTMLInputElement) {
+ if (startDate.validity.valid && endDate.validity.valid) {
+ let dateStart = this.convertMomentDate(startDate.value);
+ let endStart = this.convertMomentDate(endDate.value);
+ this.dateRangeChanged.emit({startDate: dateStart, endDate: endStart});
+ }
+ }
+
+ public convertMomentDate(date: string) {
+ return moment(date, 'DD/MM/YYYY', true).format('YYYY-MM-DD') + '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..9d9cf9aa42 100644
--- a/ng2-components/ng2-activiti-analytics/src/i18n/en.json
+++ b/ng2-components/ng2-activiti-analytics/src/i18n/en.json
@@ -1,5 +1,38 @@
{
"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": {
+ "PROCESS-DEFINITION": "Process definition",
+ "DATE-RANGE": "Date range"
+ }
+ },
+ "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');
+ }
+
+
+
+}