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'); + } + + + +}