diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json
index ae460cccb4..647904c1b0 100644
--- a/demo-shell/resources/i18n/en.json
+++ b/demo-shell/resources/i18n/en.json
@@ -105,7 +105,8 @@
"PEOPLE_CLOUD": "People Cloud Component",
"GROUPS_CLOUD": "Groups Cloud Component",
"CONFIRM-DIALOG": "Confirmation Dialog",
- "COMMUNITY": "Community"
+ "COMMUNITY": "Community",
+ "SERVICE_TASK_LIST": "Service Task List"
},
"TRASHCAN": {
"ACTIONS": {
diff --git a/demo-shell/src/app.config.json b/demo-shell/src/app.config.json
index 0f3ecbdc8f..cf55983c35 100644
--- a/demo-shell/src/app.config.json
+++ b/demo-shell/src/app.config.json
@@ -969,6 +969,35 @@
"delete"
]
},
+ "adf-edit-service-task-filter": {
+ "filterProperties": [
+ "appName",
+ "serviceTaskId",
+ "activityName",
+ "activityType",
+ "completedDate",
+ "startedDate",
+ "sort",
+ "order",
+ "status",
+ "elementId",
+ "serviceName",
+ "processInstanceId",
+ "processDefinitionId",
+ "serviceName"
+ ],
+ "sortProperties": [
+ "id",
+ "activityName",
+ "completedDate",
+ "startedDate"
+ ],
+ "actions": [
+ "save",
+ "saveAs",
+ "delete"
+ ]
+ },
"adf-edit-process-filter": {
"filterProperties": [
"status",
@@ -1066,6 +1095,44 @@
]
}
},
+ "adf-cloud-service-task-list": {
+ "presets": {
+ "default": [
+ {
+ "key": "entry.id",
+ "type": "text",
+ "title": "ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.ID",
+ "sortable": true
+ },
+ {
+ "key": "entry.activityName",
+ "type": "text",
+ "title": "ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.ACTIVITY_NAME",
+ "sortable": true
+ },
+ {
+ "key": "entry.status",
+ "type": "text",
+ "title": "ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.STATUS",
+ "sortable": true
+ },
+ {
+ "key": "entry.startedDate",
+ "type": "date",
+ "title": "ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.STARTED_DATE",
+ "sortable": true,
+ "format": "timeAgo"
+ },
+ {
+ "key": "entry.completedDate",
+ "type": "date",
+ "title": "ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.COMPLETED_DATE",
+ "sortable": true,
+ "format": "timeAgo"
+ }
+ ]
+ }
+ },
"adf-cloud-process-list": {
"presets": {
"default": [
diff --git a/demo-shell/src/app/app.module.ts b/demo-shell/src/app/app.module.ts
index 3e4434206f..38eed6b3ed 100644
--- a/demo-shell/src/app/app.module.ts
+++ b/demo-shell/src/app/app.module.ts
@@ -65,6 +65,7 @@ import { TasksCloudDemoComponent } from './components/cloud/tasks-cloud-demo.com
import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component';
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { TaskHeaderCloudDemoComponent } from './components/cloud/task-header-cloud-demo.component';
+import { ServiceTaskListCloudDemoComponent } from './components/cloud/service-task-list-cloud-demo.component';
import { CloudViewerComponent } from './components/cloud/cloud-viewer.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component';
@@ -193,7 +194,8 @@ registerLocaleData(localeSv);
ConfirmDialogExampleComponent,
CustomEditorComponent,
CustomWidgetComponent,
- ProcessCloudLayoutComponent
+ ProcessCloudLayoutComponent,
+ ServiceTaskListCloudDemoComponent
],
providers: [
{
diff --git a/demo-shell/src/app/app.routes.ts b/demo-shell/src/app/app.routes.ts
index c98c513e92..6bd5e74a23 100644
--- a/demo-shell/src/app/app.routes.ts
+++ b/demo-shell/src/app/app.routes.ts
@@ -54,6 +54,7 @@ import { DemoErrorComponent } from './components/error/demo-error.component';
import { TaskHeaderCloudDemoComponent } from './components/cloud/task-header-cloud-demo.component';
import { FilteredSearchComponent } from './components/files/filtered-search.component';
import { ProcessCloudLayoutComponent } from './components/cloud/process-cloud-layout.component';
+import { ServiceTaskListCloudDemoComponent } from './components/cloud/service-task-list-cloud-demo.component';
export const appRoutes: Routes = [
{ path: 'login', loadChildren: () => import('./components/login/login.module').then(m => m.AppLoginModule) },
@@ -176,22 +177,31 @@ export const appRoutes: Routes = [
{
path: 'cloud',
canActivate: [AuthGuardSsoRoleService],
- data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
+ data: { roles: ['ACTIVITI_ADMIN', 'ACTIVITI_USER'], redirectUrl: '/error/403' },
children: [
{
path: '',
+ data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: AppsCloudDemoComponent
},
{
path: 'people-group-cloud',
+ data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: PeopleGroupCloudDemoComponent
},
{
path: 'task-header-cloud',
+ data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: TaskHeaderCloudDemoComponent
},
+ {
+ path: 'service-task-list',
+ data: { roles: ['ACTIVITI_ADMIN'], redirectUrl: '/error/403' },
+ component: ServiceTaskListCloudDemoComponent
+ },
{
path: 'community',
+ data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
loadChildren: () => import('./components/cloud/community/community.module').then(m => m.AppCommunityModule)
},
{
diff --git a/demo-shell/src/app/components/app-layout/app-layout.component.ts b/demo-shell/src/app/components/app-layout/app-layout.component.ts
index 334c159f47..76faedde67 100644
--- a/demo-shell/src/app/components/app-layout/app-layout.component.ts
+++ b/demo-shell/src/app/components/app-layout/app-layout.component.ts
@@ -55,7 +55,8 @@ export class AppLayoutComponent implements OnInit, OnDestroy {
{ href: '/cloud/community', icon: 'cloud', title: 'APP_LAYOUT.COMMUNITY' },
{ href: '/form-cloud', icon: 'poll', title: 'APP_LAYOUT.FORM' },
{ href: '/cloud/people-group-cloud', icon: 'group', title: 'APP_LAYOUT.PEOPLE_GROUPS_CLOUD' },
- { href: '/cloud/task-header-cloud', icon: 'cloud', title: 'APP_LAYOUT.TASK_HEADER_CLOUD.COMPONENT_NAME' }
+ { href: '/cloud/task-header-cloud', icon: 'cloud', title: 'APP_LAYOUT.TASK_HEADER_CLOUD.COMPONENT_NAME' },
+ { href: '/cloud/service-task-list', icon: 'cloud', title: 'APP_LAYOUT.SERVICE_TASK_LIST' }
]
},
{ href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES', children: [
diff --git a/demo-shell/src/app/components/cloud/service-task-list-cloud-demo.component.html b/demo-shell/src/app/components/cloud/service-task-list-cloud-demo.component.html
new file mode 100644
index 0000000000..e95cb3aecb
--- /dev/null
+++ b/demo-shell/src/app/components/cloud/service-task-list-cloud-demo.component.html
@@ -0,0 +1,29 @@
+
diff --git a/demo-shell/src/app/components/cloud/service-task-list-cloud-demo.component.ts b/demo-shell/src/app/components/cloud/service-task-list-cloud-demo.component.ts
new file mode 100644
index 0000000000..747ec48113
--- /dev/null
+++ b/demo-shell/src/app/components/cloud/service-task-list-cloud-demo.component.ts
@@ -0,0 +1,101 @@
+/*!
+ * @license
+ * Copyright 2019 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, OnInit, OnDestroy, ViewChild } from '@angular/core';
+import { TaskListCloudSortingModel, ServiceTaskListCloudComponent, ServiceTaskFilterCloudModel } from '@alfresco/adf-process-services-cloud';
+import { UserPreferencesService, AppConfigService, PaginationModel } from '@alfresco/adf-core';
+import { CloudLayoutService, CloudServiceSettings } from './services/cloud-layout.service';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+@Component({
+ templateUrl: 'service-task-list-cloud-demo.component.html'
+})
+export class ServiceTaskListCloudDemoComponent implements OnInit, OnDestroy {
+
+ public static ACTION_SAVE_AS = 'saveAs';
+ public static ACTION_DELETE = 'delete';
+ static TASK_FILTER_PROPERTY_KEYS = 'adf-edit-service-task-filter';
+
+ @ViewChild('taskCloud')
+ taskCloud: ServiceTaskListCloudComponent;
+
+ isFilterLoaded = false;
+
+ selectedRow: any;
+
+ sortArray: TaskListCloudSortingModel[];
+ editedFilter: ServiceTaskFilterCloudModel;
+ taskFilterProperties: any = { filterProperties: [], sortProperties: [], actions: [] };
+
+ multiselect: boolean;
+ selectedRows: string[] = [];
+ actionMenu: boolean;
+ contextMenu: boolean;
+ actions: any[] = [];
+ selectedAction: { id: number, name: string, actionType: string};
+ selectedContextAction: { id: number, name: string, actionType: string};
+ selectionMode: string;
+
+ private onDestroy$ = new Subject();
+
+ constructor(
+ private cloudLayoutService: CloudLayoutService,
+ private userPreference: UserPreferencesService,
+ private appConfig: AppConfigService) {
+
+ const properties = this.appConfig.get>(ServiceTaskListCloudDemoComponent.TASK_FILTER_PROPERTY_KEYS);
+ if (properties === this.taskFilterProperties) {
+ this.taskFilterProperties = properties;
+ }
+ }
+
+ ngOnInit() {
+ this.isFilterLoaded = false;
+ this.cloudLayoutService.settings$
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe(settings => this.setCurrentSettings(settings));
+ }
+
+ ngOnDestroy() {
+ this.onDestroy$.next(true);
+ this.onDestroy$.complete();
+ }
+
+ setCurrentSettings(settings: CloudServiceSettings) {
+ if (settings) {
+ this.multiselect = settings.multiselect;
+ this.selectionMode = settings.selectionMode;
+ this.actionMenu = settings.actionMenu;
+ this.contextMenu = settings.contextMenu;
+ this.actions = settings.actions;
+ }
+ }
+
+ onChangePageSize(event: PaginationModel) {
+ this.userPreference.paginationSize = event.maxItems;
+ }
+
+ resetSelectedRows() {
+ this.selectedRows = [];
+ }
+
+ onFilterChange(filter: ServiceTaskFilterCloudModel) {
+ this.editedFilter = Object.assign({}, filter);
+ this.sortArray = [new TaskListCloudSortingModel({ orderBy: this.editedFilter.sort, direction: this.editedFilter.order })];
+ }
+}
diff --git a/lib/core/datatable/data/data-table.schema.ts b/lib/core/datatable/data/data-table.schema.ts
index ad0198286d..219bb2bc4e 100644
--- a/lib/core/datatable/data/data-table.schema.ts
+++ b/lib/core/datatable/data/data-table.schema.ts
@@ -79,4 +79,12 @@ export abstract class DataTableSchema {
private getDefaultLayoutPreset(): DataColumn[] {
return (this.layoutPresets['default']).map((col) => new ObjectDataColumn(col));
}
+
+ public setPresetKey(presetKey: string) {
+ this.presetKey = presetKey;
+ }
+
+ public setPresetsModel(presetsModel: any) {
+ this.presetsModel = presetsModel;
+ }
}
diff --git a/lib/process-services-cloud/src/lib/i18n/en.json b/lib/process-services-cloud/src/lib/i18n/en.json
index 3038e0f7a2..4a127240b9 100644
--- a/lib/process-services-cloud/src/lib/i18n/en.json
+++ b/lib/process-services-cloud/src/lib/i18n/en.json
@@ -101,11 +101,26 @@
}
}
},
+ "ADF_CLOUD_SERVICE_TASK_LIST": {
+ "PROPERTIES": {
+ "ACTIVITY_NAME": "Activity name",
+ "ACTIVITY_TYPE": "Status",
+ "ID": "Id",
+ "COMPLETED_DATE": "Completed date",
+ "STARTED_DATE": "Started date",
+ "STATUS": "Status"
+ }
+ },
"ADF_CLOUD_TASK_FILTERS": {
"MY_TASKS": "My Tasks",
"QUEUED_TASKS": "Queued Tasks",
"COMPLETED_TASKS": "Completed Tasks"
},
+ "ADF_CLOUD_SERVICE_TASK_FILTERS": {
+ "ALL_SERVICE_TASKS": "All Service Tasks",
+ "ERRORED_TASKS": "Errored Tasks",
+ "COMPLETED_TASKS": "Completed Tasks"
+ },
"ADF_CLOUD_PROCESS_FILTERS": {
"ALL_PROCESSES": "All",
"RUNNING_PROCESSES": "Running",
@@ -153,6 +168,24 @@
"CANCEL": "CANCEL"
}
},
+ "ADF_CLOUD_EDIT_SERVICE_TASK_FILTER": {
+ "LABEL": {
+ "APP_NAME": "ApplicationName",
+ "SERVICE_TASK_ID": "ServiceTaskId",
+ "SERVICE_NAME": "ServiceName",
+ "ELEMENT_ID": "ElementId",
+ "PROCESS_DEF_NAME": "ProcessDefinitionName",
+ "PROCESS_DEF_ID": "ProcessDefinitionId",
+ "PROCESS_INSTANCE_ID": "ProcessInstanceId",
+ "STATUS": "Status",
+ "DIRECTION": "Direction",
+ "ACTIVITY_NAME": "ActivityName",
+ "ACTIVITY_TYPE": "ActivityType",
+ "SORT": "Sort",
+ "STARTED_DATE": "StartedDate",
+ "COMPLETED_DATE": "CompletedDate"
+ }
+ },
"ADF_CLOUD_EDIT_PROCESS_FILTER": {
"TITLE": "Customize your filter",
"LABEL": {
diff --git a/lib/process-services-cloud/src/lib/styles/_index.scss b/lib/process-services-cloud/src/lib/styles/_index.scss
index 6f59178c9d..9acaec92d5 100644
--- a/lib/process-services-cloud/src/lib/styles/_index.scss
+++ b/lib/process-services-cloud/src/lib/styles/_index.scss
@@ -6,8 +6,8 @@
@import './../process/process-filters/components/edit-process-filter-cloud.component.scss';
@import './../task/task-form/components/task-form-cloud.component';
@import './../task/start-task/components/start-task-cloud.component.scss';
-@import './../task/task-filters/components/edit-task-filter-cloud.component.scss';
-@import './../task/task-filters/components/task-filters-cloud.component.scss';
+@import './../task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.scss';
+@import './../task/task-filters/components/base-task-filters-cloud.component.scss';
@import './../process/start-process/components/start-process-cloud.component';
@import './../form/components/widgets/attach-file/attach-file-cloud-widget.component.scss';
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.html
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.html
rename to lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.html
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.scss
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.scss
rename to lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.scss
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts
new file mode 100644
index 0000000000..7b4fd646a1
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts
@@ -0,0 +1,54 @@
+/*!
+ * @license
+ * Copyright 2019 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 { EventEmitter, Input, Output, OnDestroy, Directive } from '@angular/core';
+import { Subject } from 'rxjs';
+import { FilterParamsModel } from '../models/filter-cloud.model';
+
+@Directive()
+// tslint:disable-next-line: directive-class-suffix
+export abstract class BaseTaskFiltersCloudComponent implements OnDestroy {
+ /** Display filters available to the current user for the application with the specified name. */
+ @Input()
+ appName: string = '';
+
+ /**
+ * Parameters to use for the task filter cloud. If there is no match then the default filter
+ * (the first one in the list) is selected.
+ */
+ @Input()
+ filterParam: FilterParamsModel;
+
+ /** Toggles display of the filter's icons. */
+ @Input()
+ showIcons: boolean = false;
+
+ /** Emitted when the list is loaded. */
+ @Output()
+ success: EventEmitter = new EventEmitter();
+
+ /** Emitted when an error occurs during loading. */
+ @Output()
+ error: EventEmitter = new EventEmitter();
+
+ protected onDestroy$ = new Subject();
+
+ ngOnDestroy() {
+ this.onDestroy$.next(true);
+ this.onDestroy$.complete();
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html
deleted file mode 100644
index e55abfdb1a..0000000000
--- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.html
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts
deleted file mode 100644
index 30ba4310e4..0000000000
--- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.ts
+++ /dev/null
@@ -1,719 +0,0 @@
-/*!
- * @license
- * Copyright 2019 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, OnChanges, Input, Output, EventEmitter, SimpleChanges, OnInit, OnDestroy } from '@angular/core';
-import { AbstractControl, FormGroup, FormBuilder } from '@angular/forms';
-import { DateAdapter } from '@angular/material/core';
-import { MatDialog } from '@angular/material/dialog';
-import { debounceTime, filter, takeUntil, finalize, switchMap } from 'rxjs/operators';
-import { Subject, Observable, of } from 'rxjs';
-import moment from 'moment-es6';
-import { Moment } from 'moment';
-
-import { TaskFilterCloudModel, TaskFilterProperties, FilterOptions, TaskFilterAction } from './../models/filter-cloud.model';
-import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
-import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
-import { TranslationService, UserPreferencesService, UserPreferenceValues, IdentityUserModel, IdentityUserService } from '@alfresco/adf-core';
-import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
-import { ApplicationInstanceModel } from '../../../app/models/application-instance.model';
-import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud-filter.model';
-import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
-import { TaskCloudService } from '../../services/task-cloud.service';
-
-@Component({
- selector: 'adf-cloud-edit-task-filter',
- templateUrl: './edit-task-filter-cloud.component.html',
- styleUrls: ['./edit-task-filter-cloud.component.scss']
-})
-export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestroy {
-
- public static ACTION_SAVE = 'save';
- public static ACTION_SAVE_AS = 'saveAs';
- public static ACTION_DELETE = 'delete';
- public static APP_RUNNING_STATUS: string = 'RUNNING';
- public static APPLICATION_NAME: string = 'appName';
- public static PROCESS_DEFINITION_NAME: string = 'processDefinitionName';
- public static LAST_MODIFIED: string = 'lastModified';
- public static SORT: string = 'sort';
- public static ORDER: string = 'order';
- public static DEFAULT_TASK_FILTER_PROPERTIES = ['status', 'assignee', 'sort', 'order'];
- public static DEFAULT_SORT_PROPERTIES = ['id', 'name', 'createdDate', 'priority'];
- public static DEFAULT_ACTIONS = ['save', 'saveAs', 'delete'];
- public FORMAT_DATE: string = 'DD/MM/YYYY';
-
- /** (required) Name of the app. */
- @Input()
- appName: string = '';
-
- /** user role. */
- @Input()
- role: string = '';
-
- /** (required) ID of the task filter. */
- @Input()
- id: string;
-
- /** List of task filter properties to display. */
- @Input()
- filterProperties: string[] = EditTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES;
-
- /** List of sort properties to display. */
- @Input()
- sortProperties: string[] = EditTaskFilterCloudComponent.DEFAULT_SORT_PROPERTIES;
-
- /** List of task filter actions. */
- @Input()
- actions: string[] = EditTaskFilterCloudComponent.DEFAULT_ACTIONS;
-
- /** Toggles the filter actions. */
- @Input()
- showFilterActions = true;
-
- /** Toggles the title. */
- @Input()
- showTitle = true;
-
- /** Toggles display of task filter name */
- @Input()
- showTaskFilterName = true;
-
- /** Emitted when a task filter property changes. */
- @Output()
- filterChange: EventEmitter = new EventEmitter();
-
- /** Emitted when a filter action occurs (i.e Save, Save As, Delete). */
- @Output()
- action: EventEmitter = new EventEmitter();
-
- taskFilter: TaskFilterCloudModel;
- changedTaskFilter: TaskFilterCloudModel;
-
- status = [
- { label: 'ALL', value: '' },
- { label: 'CREATED', value: 'CREATED' },
- { label: 'ASSIGNED', value: 'ASSIGNED' },
- { label: 'SUSPENDED', value: 'SUSPENDED' },
- { label: 'CANCELLED', value: 'CANCELLED' },
- { label: 'COMPLETED', value: 'COMPLETED' }
- ];
-
- directions = [
- { label: 'ASC', value: 'ASC' },
- { label: 'DESC', value: 'DESC' }
- ];
- actionDisabledForDefault = [
- EditTaskFilterCloudComponent.ACTION_SAVE,
- EditTaskFilterCloudComponent.ACTION_DELETE
- ];
- allProcessDefinitionNamesOption = { label: 'All', value: '' };
-
- private applicationNames: any[] = [];
- private processDefinitionNames: any[] = [];
- private formHasBeenChanged = false;
- editTaskFilterForm: FormGroup;
- taskFilterProperties: TaskFilterProperties[] = [];
- taskFilterActions: TaskFilterAction[] = [];
- toggleFilterActions: boolean = false;
-
- private onDestroy$ = new Subject();
- isLoading: boolean = false;
-
- constructor(
- private formBuilder: FormBuilder,
- public dialog: MatDialog,
- private translateService: TranslationService,
- private taskFilterCloudService: TaskFilterCloudService,
- private dateAdapter: DateAdapter,
- private userPreferencesService: UserPreferencesService,
- private appsProcessCloudService: AppsProcessCloudService,
- private identityUserService: IdentityUserService,
- private taskCloudService: TaskCloudService) {
- }
-
- ngOnInit() {
- this.userPreferencesService
- .select(UserPreferenceValues.Locale)
- .pipe(takeUntil(this.onDestroy$))
- .subscribe(locale => this.dateAdapter.setLocale(locale));
- }
-
- ngOnChanges(changes: SimpleChanges) {
- const id = changes['id'];
- if (id && id.currentValue !== id.previousValue) {
- this.retrieveTaskFilterAndBuildForm();
- }
- }
-
- ngOnDestroy() {
- this.onDestroy$.next(true);
- this.onDestroy$.complete();
- }
-
- buildForm(taskFilterProperties: TaskFilterProperties[]) {
- this.formHasBeenChanged = false;
- this.editTaskFilterForm = this.formBuilder.group(this.getFormControlsConfig(taskFilterProperties));
- this.onFilterChange();
- }
-
- getFormControlsConfig(taskFilterProperties: TaskFilterProperties[]): any {
- const properties = taskFilterProperties.map((property: TaskFilterProperties) => {
- if (!!property.attributes) {
- return this.getAttributesControlConfig(property);
- } else {
- return { [property.key]: property.value };
- }
- });
- return properties.reduce(((result, current) => Object.assign(result, current)), {});
- }
-
- private getAttributesControlConfig(property: TaskFilterProperties) {
- return Object.values(property.attributes).reduce((result, key) => {
- result[key] = property.value[key];
- return result;
- }, {});
- }
-
- /**
- * Check for edit task filter form changes
- */
- onFilterChange() {
- this.editTaskFilterForm.valueChanges
- .pipe(
- debounceTime(200),
- filter(() => this.isFormValid()),
- takeUntil(this.onDestroy$)
- )
- .subscribe((formValues: TaskFilterCloudModel) => {
- this.setLastModifiedToFilter(formValues);
- this.changedTaskFilter = new TaskFilterCloudModel(Object.assign({}, this.taskFilter, formValues));
- this.formHasBeenChanged = !this.compareFilters(this.changedTaskFilter, this.taskFilter);
- this.filterChange.emit(this.changedTaskFilter);
- });
- }
-
- private setLastModifiedToFilter(formValues: TaskFilterCloudModel) {
- if (formValues.lastModifiedTo && Date.parse(formValues.lastModifiedTo.toString())) {
- const lastModifiedToFilterValue = moment(formValues.lastModifiedTo);
- lastModifiedToFilterValue.set({
- hour: 23,
- minute: 59,
- second: 59
- });
- formValues.lastModifiedTo = lastModifiedToFilterValue.toDate();
- }
- }
-
- /**
- * Fetches task filter by application name and filter id and creates filter properties, build form
- */
- retrieveTaskFilterAndBuildForm() {
- this.isLoading = true;
- this.taskFilterCloudService.getTaskFilterById(this.appName, this.id)
- .pipe(
- finalize(() => this.isLoading = false),
- takeUntil(this.onDestroy$)
- )
- .subscribe(response => {
- this.taskFilter = new TaskFilterCloudModel(response);
- this.taskFilterProperties = this.createAndFilterProperties();
- this.taskFilterActions = this.createAndFilterActions();
- this.buildForm(this.taskFilterProperties);
- });
- }
-
- createAndFilterProperties() {
- this.checkMandatoryFilterProperties();
-
- if (this.checkForProperty(EditTaskFilterCloudComponent.APPLICATION_NAME)) {
- this.applicationNames = [];
- this.getRunningApplications();
- }
- if (this.checkForProperty(EditTaskFilterCloudComponent.PROCESS_DEFINITION_NAME)) {
- this.processDefinitionNames = [];
- this.getProcessDefinitions();
- }
-
- const defaultProperties = this.createTaskFilterProperties(this.taskFilter);
- let filteredProperties = defaultProperties.filter((filterProperty: TaskFilterProperties) => this.isValidProperty(this.filterProperties, filterProperty));
-
- if (!this.hasSortProperty()) {
- filteredProperties = this.removeOrderProperty(filteredProperties);
- }
-
- if (this.hasLastModifiedProperty()) {
- filteredProperties = [...filteredProperties, ...this.createLastModifiedProperty()];
- }
- return filteredProperties;
- }
-
- checkMandatoryFilterProperties() {
- if (this.filterProperties === undefined || this.filterProperties.length === 0) {
- this.filterProperties = EditTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES;
- }
- }
-
- private isValidProperty(filterProperties: string[], filterProperty: any): boolean {
- return filterProperties ? filterProperties.indexOf(filterProperty.key) >= 0 : true;
- }
-
- checkForProperty(property: string): boolean {
- return this.filterProperties ? this.filterProperties.indexOf(property) >= 0 : false;
- }
-
- hasSortProperty(): boolean {
- return this.filterProperties.indexOf(EditTaskFilterCloudComponent.SORT) >= 0;
- }
-
- removeOrderProperty(filteredProperties: TaskFilterProperties[]): TaskFilterProperties[] {
- if (filteredProperties && filteredProperties.length > 0) {
- return filteredProperties.filter(property => property.key !== EditTaskFilterCloudComponent.ORDER);
- }
- return [];
- }
-
- hasLastModifiedProperty(): boolean {
- return this.filterProperties.indexOf(EditTaskFilterCloudComponent.LAST_MODIFIED) >= 0;
- }
-
- get createSortProperties(): any {
- this.checkMandatorySortProperties();
- const sortProperties = this.sortProperties.map((property: string) => {
- return { label: property.charAt(0).toUpperCase() + property.slice(1), value: property };
- });
- return sortProperties;
- }
-
- checkMandatorySortProperties(): void {
- if (this.sortProperties === undefined || this.sortProperties.length === 0) {
- this.sortProperties = EditTaskFilterCloudComponent.DEFAULT_SORT_PROPERTIES;
- }
- }
-
- createAndFilterActions(): TaskFilterAction[] {
- this.checkMandatoryActions();
- return this.createFilterActions()
- .filter(action => this.isValidAction(this.actions, action));
- }
-
- checkMandatoryActions(): void {
- if (this.actions === undefined || this.actions.length === 0) {
- this.actions = EditTaskFilterCloudComponent.DEFAULT_ACTIONS;
- }
- }
-
- private isValidAction(actions: string[], action: any): boolean {
- return actions ? actions.indexOf(action.actionType) >= 0 : true;
- }
-
- isFormValid(): boolean {
- return this.editTaskFilterForm.valid;
- }
-
- getPropertyController(property: TaskFilterProperties): AbstractControl {
- return this.editTaskFilterForm.get(property.key);
- }
-
- onDateChanged(newDateValue: any, dateProperty: TaskFilterProperties) {
- if (newDateValue) {
- const momentDate = moment(newDateValue, this.FORMAT_DATE, true);
-
- if (momentDate.isValid()) {
- this.getPropertyController(dateProperty).setValue(momentDate.toDate());
- this.getPropertyController(dateProperty).setErrors(null);
- } else {
- this.getPropertyController(dateProperty).setErrors({invalid: true});
- }
- }
- }
-
- onDateTypeChange(dateType: DateCloudFilterType, property: TaskFilterProperties) {
- this.editTaskFilterForm.get(property.attributes.dateType).setValue(dateType);
- }
-
- onDateRangeFilterChanged(dateRange: DateRangeFilter, property: TaskFilterProperties) {
- this.editTaskFilterForm.get(property.attributes?.from).setValue(
- dateRange.startDate ? dateRange.startDate.toISOString() : null
- );
- this.editTaskFilterForm.get(property.attributes?.to).setValue(
- dateRange.endDate ? dateRange.endDate.toISOString() : null
- );
- }
-
- onChangedUser(users: IdentityUserModel[], userProperty: TaskFilterProperties) {
- this.getPropertyController(userProperty).setValue(users[0]?.username);
- }
-
- hasError(property: TaskFilterProperties): boolean {
- return this.getPropertyController(property).errors && this.getPropertyController(property).errors.invalid;
- }
-
- /**
- * Return true if both filters are same
- * @param editedQuery, @param currentQuery
- */
- compareFilters(editedQuery: TaskFilterCloudModel, currentQuery: TaskFilterCloudModel): boolean {
- return JSON.stringify(editedQuery).toLowerCase() === JSON.stringify(currentQuery).toLowerCase();
- }
-
- getRunningApplications() {
- this.appsProcessCloudService
- .getDeployedApplicationsByStatus(EditTaskFilterCloudComponent.APP_RUNNING_STATUS, this.role)
- .pipe(takeUntil(this.onDestroy$))
- .subscribe((applications: ApplicationInstanceModel[]) => {
- if (applications && applications.length > 0) {
- applications.map((application) => {
- this.applicationNames.push({ label: application.name, value: application.name });
- });
- }
- });
- }
-
- getProcessDefinitions() {
- this.taskCloudService.getProcessDefinitions(this.appName)
- .pipe(takeUntil(this.onDestroy$))
- .subscribe((processDefinitions: ProcessDefinitionCloud[]) => {
- if (processDefinitions && processDefinitions.length > 0) {
- this.processDefinitionNames.push(this.allProcessDefinitionNamesOption);
- processDefinitions.map((processDefinition) => {
- this.processDefinitionNames.push({ label: processDefinition.name, value: processDefinition.name });
- });
- }
- });
- }
-
- executeFilterActions(action: TaskFilterAction): void {
- if (action.actionType === EditTaskFilterCloudComponent.ACTION_SAVE) {
- this.save(action);
- } else if (action.actionType === EditTaskFilterCloudComponent.ACTION_SAVE_AS) {
- this.saveAs(action);
- } else if (action.actionType === EditTaskFilterCloudComponent.ACTION_DELETE) {
- this.delete(action);
- }
- }
-
- save(saveAction: TaskFilterAction): void {
- this.taskFilterCloudService
- .updateFilter(this.changedTaskFilter)
- .pipe(takeUntil(this.onDestroy$))
- .subscribe(() => {
- saveAction.filter = this.changedTaskFilter;
- this.action.emit(saveAction);
- this.formHasBeenChanged = this.compareFilters(this.changedTaskFilter, this.taskFilter);
- });
- }
-
- delete(deleteAction: TaskFilterAction): void {
- this.taskFilterCloudService
- .deleteFilter(this.taskFilter)
- .pipe(
- filter((filters) => {
- deleteAction.filter = this.taskFilter;
- this.action.emit(deleteAction);
- return filters.length === 0;
- }),
- switchMap(() => this.restoreDefaultTaskFilters()),
- takeUntil(this.onDestroy$))
- .subscribe(() => {});
- }
-
- saveAs(saveAsAction: TaskFilterAction): void {
- const dialogRef = this.dialog.open(TaskFilterDialogCloudComponent, {
- data: {
- name: this.translateService.instant(this.taskFilter.name)
- },
- height: 'auto',
- minWidth: '30%'
- });
- dialogRef.afterClosed().subscribe((result) => {
- if (result && result.action === TaskFilterDialogCloudComponent.ACTION_SAVE) {
- const filterId = Math.random().toString(36).substr(2, 9);
- const filterKey = this.getSanitizeFilterName(result.name);
- const newFilter = {
- name: result.name,
- icon: result.icon,
- id: filterId,
- key: 'custom-' + filterKey
- };
- const resultFilter: TaskFilterCloudModel = Object.assign({}, this.changedTaskFilter, newFilter);
- this.taskFilterCloudService.addFilter(resultFilter)
- .pipe(takeUntil(this.onDestroy$)).subscribe(() => {
- saveAsAction.filter = resultFilter;
- this.action.emit(saveAsAction);
- });
- }
- });
- }
-
- /**
- * Return filter name
- * @param filterName
- */
- getSanitizeFilterName(filterName: string): string {
- const nameWithHyphen = this.replaceSpaceWithHyphen(filterName.trim());
- return nameWithHyphen.toLowerCase();
- }
-
- /**
- * Return name with hyphen
- * @param name
- */
- replaceSpaceWithHyphen(name: string): string {
- const regExt = new RegExp(' ', 'g');
- return name.replace(regExt, '-');
- }
-
- restoreDefaultTaskFilters(): Observable {
- return this.taskFilterCloudService.getTaskListFilters(this.appName);
- }
-
- showActions(): boolean {
- return this.showFilterActions;
- }
-
- onExpand(): void {
- this.toggleFilterActions = true;
- }
-
- onClose(): void {
- this.toggleFilterActions = false;
- }
-
- isDateType(property: TaskFilterProperties): boolean {
- return property.type === 'date';
- }
-
- isDateRangeType(property: TaskFilterProperties): boolean {
- return property.type === 'date-range';
- }
-
- isSelectType(property: TaskFilterProperties): boolean {
- return property.type === 'select';
- }
-
- isTextType(property: TaskFilterProperties): boolean {
- return property.type === 'text';
- }
-
- isCheckBoxType(property: TaskFilterProperties): boolean {
- return property.type === 'checkbox';
- }
-
- isUserSelectType(property: TaskFilterProperties): boolean {
- return property.type === 'people';
- }
-
- isDisabledAction(action: TaskFilterAction): boolean {
- return this.isDisabledForDefaultFilters(action) ? true : this.hasFormChanged(action);
- }
-
- isDisabledForDefaultFilters(action: TaskFilterAction): boolean {
- return (
- this.taskFilterCloudService.isDefaultFilter(this.taskFilter.name) &&
- this.actionDisabledForDefault.includes(action.actionType)
- );
- }
-
- hasFormChanged(action: TaskFilterAction): boolean {
- if (action.actionType === EditTaskFilterCloudComponent.ACTION_SAVE) {
- return !this.formHasBeenChanged;
- }
- if (action.actionType === EditTaskFilterCloudComponent.ACTION_SAVE_AS) {
- return !this.formHasBeenChanged;
- }
- if (action.actionType === EditTaskFilterCloudComponent.ACTION_DELETE) {
- return false;
- }
-
- return false;
- }
-
- getUserByUsername(username: string): Observable {
- if (username) {
- return this.identityUserService.findUserByUsername(username);
- }
- return of([]);
- }
-
- createFilterActions(): TaskFilterAction[] {
- return [
- new TaskFilterAction({
- actionType: EditTaskFilterCloudComponent.ACTION_SAVE,
- icon: 'save',
- tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.SAVE'
- }),
- new TaskFilterAction({
- actionType: EditTaskFilterCloudComponent.ACTION_SAVE_AS,
- icon: 'unarchive',
- tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.SAVE_AS'
- }),
- new TaskFilterAction({
- actionType: EditTaskFilterCloudComponent.ACTION_DELETE,
- icon: 'delete',
- tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.DELETE'
- })
- ];
- }
-
- createLastModifiedProperty(): TaskFilterProperties[] {
- return [
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.LAST_MODIFIED_FROM',
- type: 'date',
- key: 'lastModifiedFrom',
- value: ''
- }),
-
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.LAST_MODIFIED_TO',
- type: 'date',
- key: 'lastModifiedTo',
- value: ''
- })
- ];
- }
-
- createTaskFilterProperties(currentTaskFilter: TaskFilterCloudModel): TaskFilterProperties[] {
- return [
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.APP_NAME',
- type: 'select',
- key: 'appName',
- value: currentTaskFilter.appName || '',
- options: this.applicationNames
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.TASK_ID',
- type: 'text',
- key: 'taskId',
- value: ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.STATUS',
- type: 'select',
- key: 'status',
- value: currentTaskFilter.status || this.status[0].value,
- options: this.status
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.ASSIGNMENT',
- type: 'text',
- key: 'assignee',
- value: currentTaskFilter.assignee || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_DEF_NAME',
- type: 'select',
- key: 'processDefinitionName',
- value: currentTaskFilter.processDefinitionName || '',
- options: this.processDefinitionNames
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_INSTANCE_ID',
- type: 'text',
- key: 'processInstanceId',
- value: currentTaskFilter.processInstanceId || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_DEF_ID',
- type: 'text',
- key: 'processDefinitionId',
- value: currentTaskFilter.processDefinitionId || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.TASK_NAME',
- type: 'text',
- key: 'taskName',
- value: currentTaskFilter.taskName || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PARENT_TASK_ID',
- type: 'text',
- key: 'parentTaskId',
- value: currentTaskFilter.parentTaskId || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PRIORITY',
- type: 'text',
- key: 'priority',
- value: currentTaskFilter.priority || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.OWNER',
- type: 'text',
- key: 'owner',
- value: currentTaskFilter.owner || ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.CREATED_DATE',
- type: 'date',
- key: 'createdDate',
- value: ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.DUE_DATE',
- type: 'date',
- key: 'dueDate',
- value: ''
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.SORT',
- type: 'select',
- key: 'sort',
- value: currentTaskFilter.sort || this.createSortProperties[0].value,
- options: this.createSortProperties
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.DIRECTION',
- type: 'select',
- key: 'order',
- value: currentTaskFilter.order || this.directions[0].value,
- options: this.directions
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.STAND_ALONE',
- type: 'checkbox',
- key: 'standalone',
- value: currentTaskFilter.standalone || false
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.DUE_DATE',
- type: 'date-range',
- key: 'dueDateRange',
- attributes: { dateType: 'dueDateType', from: '_dueDateFrom', to: '_dueDateTo'},
- value: {
- dueDateType: currentTaskFilter.dueDateType || null,
- _dueDateFrom: currentTaskFilter.dueDateFrom || null,
- _dueDateTo: currentTaskFilter.dueDateTo || null
- },
- dateFilterOptions: [
- DateCloudFilterType.NO_DATE,
- DateCloudFilterType.TODAY,
- DateCloudFilterType.TOMORROW,
- DateCloudFilterType.NEXT_7_DAYS,
- DateCloudFilterType.RANGE
- ]
- }),
- new TaskFilterProperties({
- label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.COMPLETED_BY',
- type: 'people',
- key: 'completedBy',
- value: this.getUserByUsername(currentTaskFilter.completedBy),
- selectionMode: 'single'
- })
- ];
- }
-}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html
new file mode 100644
index 0000000000..48c6bff6e0
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.scss
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.scss
rename to lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.scss
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts
new file mode 100644
index 0000000000..9ce699e491
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts
@@ -0,0 +1,410 @@
+/*!
+ * @license
+ * Copyright 2019 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 { OnChanges, SimpleChanges, OnInit, OnDestroy, Directive, Input, Output, EventEmitter } from '@angular/core';
+import { FilterOptions, TaskFilterAction, TaskFilterProperties } from '../../models/filter-cloud.model';
+import { TaskCloudService } from './../../../services/task-cloud.service';
+import { AppsProcessCloudService } from './../../../../app/services/apps-process-cloud.service';
+import { ApplicationInstanceModel } from './../../../../app/models/application-instance.model';
+import { ProcessDefinitionCloud } from './../../../../models/process-definition-cloud.model';
+import { DateCloudFilterType, DateRangeFilter } from '../../../../models/date-cloud-filter.model';
+import moment, { Moment } from 'moment';
+import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';
+import { debounceTime, filter, takeUntil } from 'rxjs/operators';
+import { Subject } from 'rxjs';
+import { DateAdapter } from '@angular/material/core';
+import { UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
+
+@Directive()
+// tslint:disable-next-line: directive-class-suffix
+export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestroy {
+
+ public static ACTION_SAVE = 'save';
+ public static ACTION_SAVE_AS = 'saveAs';
+ public static ACTION_DELETE = 'delete';
+ public static APP_RUNNING_STATUS: string = 'RUNNING';
+ public static APPLICATION_NAME: string = 'appName';
+ public static PROCESS_DEFINITION_NAME: string = 'processDefinitionName';
+ public static LAST_MODIFIED: string = 'lastModified';
+ public static SORT: string = 'sort';
+ public static ORDER: string = 'order';
+ public static DEFAULT_ACTIONS = ['save', 'saveAs', 'delete'];
+ public static FORMAT_DATE: string = 'DD/MM/YYYY';
+ public static DIRECTIONS = [
+ { label: 'ASC', value: 'ASC' },
+ { label: 'DESC', value: 'DESC' }
+ ];
+ public static ACTIONS_DISABLED_BY_DEFAULT = [
+ BaseEditTaskFilterCloudComponent.ACTION_SAVE,
+ BaseEditTaskFilterCloudComponent.ACTION_DELETE
+ ];
+
+ /** (required) Name of the app. */
+ @Input()
+ appName: string = '';
+
+ /** (required) ID of the task filter. */
+ @Input()
+ id: string;
+
+ /** Toggles the title. */
+ @Input()
+ showTitle = true;
+
+ /** Toggles display of task filter name */
+ @Input()
+ showTaskFilterName = true;
+
+ /** List of task filter properties to display. */
+ @Input()
+ filterProperties: string[] = [];
+
+ /** user role. */
+ @Input()
+ role: string = '';
+
+ /** Toggles the filter actions. */
+ @Input()
+ showFilterActions = true;
+
+ /** List of task filter actions. */
+ @Input()
+ actions: string[] = BaseEditTaskFilterCloudComponent.DEFAULT_ACTIONS;
+
+ /** List of sort properties to display. */
+ @Input()
+ sortProperties: string[] = [];
+
+ /** Emitted when a filter action occurs (i.e Save, Save As, Delete). */
+ @Output()
+ action: EventEmitter = new EventEmitter();
+
+ protected applicationNames: any[] = [];
+ protected processDefinitionNames: any[] = [];
+ protected formHasBeenChanged = false;
+ editTaskFilterForm: FormGroup;
+ taskFilterProperties: TaskFilterProperties[] = [];
+ taskFilterActions: TaskFilterAction[] = [];
+ toggleFilterActions: boolean = false;
+ allProcessDefinitionNamesOption = { label: 'All', value: '' };
+
+ protected onDestroy$ = new Subject();
+ isLoading: boolean = false;
+
+ constructor(
+ protected formBuilder: FormBuilder,
+ protected dateAdapter: DateAdapter,
+ protected userPreferencesService: UserPreferencesService,
+ protected appsProcessCloudService: AppsProcessCloudService,
+ protected taskCloudService: TaskCloudService) {
+ }
+
+ ngOnInit() {
+ this.userPreferencesService
+ .select(UserPreferenceValues.Locale)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe(locale => this.dateAdapter.setLocale(locale));
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const id = changes['id'];
+ if (id && id.currentValue !== id.previousValue) {
+ this.retrieveTaskFilterAndBuildForm();
+ }
+ }
+
+ ngOnDestroy() {
+ this.onDestroy$.next(true);
+ this.onDestroy$.complete();
+ }
+
+ createFilterActions(): TaskFilterAction[] {
+ return [
+ new TaskFilterAction({
+ actionType: BaseEditTaskFilterCloudComponent.ACTION_SAVE,
+ icon: 'save',
+ tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.SAVE'
+ }),
+ new TaskFilterAction({
+ actionType: BaseEditTaskFilterCloudComponent.ACTION_SAVE_AS,
+ icon: 'unarchive',
+ tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.SAVE_AS'
+ }),
+ new TaskFilterAction({
+ actionType: BaseEditTaskFilterCloudComponent.ACTION_DELETE,
+ icon: 'delete',
+ tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.DELETE'
+ })
+ ];
+ }
+
+ hasFormChanged(action: TaskFilterAction): boolean {
+ if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE) {
+ return !this.formHasBeenChanged;
+ }
+ if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE_AS) {
+ return !this.formHasBeenChanged;
+ }
+ if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_DELETE) {
+ return false;
+ }
+
+ return false;
+ }
+
+ onExpand(): void {
+ this.toggleFilterActions = true;
+ }
+
+ onClose(): void {
+ this.toggleFilterActions = false;
+ }
+
+ isDateType(property: TaskFilterProperties): boolean {
+ return property.type === 'date';
+ }
+
+ isDateRangeType(property: TaskFilterProperties): boolean {
+ return property.type === 'date-range';
+ }
+
+ isSelectType(property: TaskFilterProperties): boolean {
+ return property.type === 'select';
+ }
+
+ isTextType(property: TaskFilterProperties): boolean {
+ return property.type === 'text';
+ }
+
+ isCheckBoxType(property: TaskFilterProperties): boolean {
+ return property.type === 'checkbox';
+ }
+
+ isDisabledAction(action: TaskFilterAction): boolean {
+ return this.isDisabledForDefaultFilters(action) ? true : this.hasFormChanged(action);
+ }
+
+ /**
+ * Return filter name
+ * @param filterName
+ */
+ getSanitizeFilterName(filterName: string): string {
+ const nameWithHyphen = this.replaceSpaceWithHyphen(filterName.trim());
+ return nameWithHyphen.toLowerCase();
+ }
+
+ /**
+ * Return name with hyphen
+ * @param name
+ */
+ replaceSpaceWithHyphen(name: string): string {
+ const regExt = new RegExp(' ', 'g');
+ return name.replace(regExt, '-');
+ }
+
+ executeFilterActions(action: TaskFilterAction): void {
+ if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE) {
+ this.save(action);
+ } else if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE_AS) {
+ this.saveAs(action);
+ } else if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_DELETE) {
+ this.delete(action);
+ }
+ }
+
+ getRunningApplications() {
+ this.appsProcessCloudService
+ .getDeployedApplicationsByStatus(BaseEditTaskFilterCloudComponent.APP_RUNNING_STATUS, this.role)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe((applications: ApplicationInstanceModel[]) => {
+ if (applications && applications.length > 0) {
+ applications.map((application) => {
+ this.applicationNames.push({ label: application.name, value: application.name });
+ });
+ }
+ });
+ }
+
+ getProcessDefinitions() {
+ this.taskCloudService.getProcessDefinitions(this.appName)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe((processDefinitions: ProcessDefinitionCloud[]) => {
+ if (processDefinitions && processDefinitions.length > 0) {
+ this.processDefinitionNames.push(this.allProcessDefinitionNamesOption);
+ processDefinitions.map((processDefinition) => {
+ this.processDefinitionNames.push({ label: processDefinition.name, value: processDefinition.name });
+ });
+ }
+ });
+ }
+
+ checkMandatoryActions(): void {
+ if (this.actions === undefined || this.actions.length === 0) {
+ this.actions = BaseEditTaskFilterCloudComponent.DEFAULT_ACTIONS;
+ }
+ }
+
+ private isValidAction(actions: string[], action: any): boolean {
+ return actions ? actions.indexOf(action.actionType) >= 0 : true;
+ }
+
+ isFormValid(): boolean {
+ return this.editTaskFilterForm.valid;
+ }
+
+ getPropertyController(property: TaskFilterProperties): AbstractControl {
+ return this.editTaskFilterForm.get(property.key);
+ }
+
+ onDateChanged(newDateValue: any, dateProperty: TaskFilterProperties) {
+ if (newDateValue) {
+ const momentDate = moment(newDateValue, BaseEditTaskFilterCloudComponent.FORMAT_DATE, true);
+
+ if (momentDate.isValid()) {
+ this.getPropertyController(dateProperty).setValue(momentDate.toDate());
+ this.getPropertyController(dateProperty).setErrors(null);
+ } else {
+ this.getPropertyController(dateProperty).setErrors({ invalid: true });
+ }
+ }
+ }
+
+ onDateRangeFilterChanged(dateRange: DateRangeFilter, property: TaskFilterProperties) {
+ this.editTaskFilterForm.get(property.attributes?.from).setValue(
+ dateRange.startDate ? dateRange.startDate.toISOString() : null
+ );
+ this.editTaskFilterForm.get(property.attributes?.to).setValue(
+ dateRange.endDate ? dateRange.endDate.toISOString() : null
+ );
+ }
+
+ hasError(property: TaskFilterProperties): boolean {
+ return this.getPropertyController(property).errors && this.getPropertyController(property).errors.invalid;
+ }
+
+ hasLastModifiedProperty(): boolean {
+ return this.filterProperties.indexOf(BaseEditTaskFilterCloudComponent.LAST_MODIFIED) >= 0;
+ }
+
+ get createSortProperties(): FilterOptions[] {
+ this.checkMandatorySortProperties();
+ const sortProperties = this.sortProperties.map((property: string) => {
+ return { label: property.charAt(0).toUpperCase() + property.slice(1), value: property };
+ });
+ return sortProperties;
+ }
+
+ createAndFilterActions(): TaskFilterAction[] {
+ this.checkMandatoryActions();
+ return this.createFilterActions().filter(action => this.isValidAction(this.actions, action));
+ }
+
+ isValidProperty(filterProperties: string[], filterProperty: any): boolean {
+ return filterProperties ? filterProperties.indexOf(filterProperty.key) >= 0 : true;
+ }
+
+ checkForProperty(property: string): boolean {
+ return this.filterProperties ? this.filterProperties.indexOf(property) >= 0 : false;
+ }
+
+ hasSortProperty(): boolean {
+ return this.filterProperties.indexOf(BaseEditTaskFilterCloudComponent.SORT) >= 0;
+ }
+
+ removeOrderProperty(filteredProperties: TaskFilterProperties[]): TaskFilterProperties[] {
+ if (filteredProperties && filteredProperties.length > 0) {
+ return filteredProperties.filter(property => property.key !== BaseEditTaskFilterCloudComponent.ORDER);
+ }
+ return [];
+ }
+
+ createAndFilterProperties() {
+ this.checkMandatoryFilterProperties();
+
+ if (this.checkForProperty(BaseEditTaskFilterCloudComponent.APPLICATION_NAME)) {
+ this.applicationNames = [];
+ this.getRunningApplications();
+ }
+ if (this.checkForProperty(BaseEditTaskFilterCloudComponent.PROCESS_DEFINITION_NAME)) {
+ this.processDefinitionNames = [];
+ this.getProcessDefinitions();
+ }
+
+ const defaultProperties = this.createTaskFilterProperties();
+ let filteredProperties = defaultProperties.filter((filterProperty: TaskFilterProperties) => this.isValidProperty(this.filterProperties, filterProperty));
+
+ if (!this.hasSortProperty()) {
+ filteredProperties = this.removeOrderProperty(filteredProperties);
+ }
+
+ return filteredProperties;
+ }
+
+ /**
+ * Check for edit task filter form changes
+ */
+ onFilterChange() {
+ this.editTaskFilterForm.valueChanges
+ .pipe(
+ debounceTime(200),
+ filter(() => this.isFormValid()),
+ takeUntil(this.onDestroy$)
+ )
+ .subscribe((formValues) => {
+ this.assignNewFilter(formValues);
+ });
+ }
+
+ getFormControlsConfig(taskFilterProperties: TaskFilterProperties[]): any {
+ const properties = taskFilterProperties.map((property: TaskFilterProperties) => {
+ if (!!property.attributes) {
+ return this.getAttributesControlConfig(property);
+ } else {
+ return { [property.key]: property.value };
+ }
+ });
+ return properties.reduce(((result, current) => Object.assign(result, current)), {});
+ }
+
+ private getAttributesControlConfig(property: TaskFilterProperties) {
+ return Object.values(property.attributes).reduce((result, key) => {
+ result[key] = property.value[key];
+ return result;
+ }, {});
+ }
+
+ buildForm(taskFilterProperties: TaskFilterProperties[]) {
+ this.formHasBeenChanged = false;
+ this.editTaskFilterForm = this.formBuilder.group(this.getFormControlsConfig(taskFilterProperties));
+ this.onFilterChange();
+ }
+
+ onDateTypeChange(dateType: DateCloudFilterType, property: TaskFilterProperties) {
+ this.editTaskFilterForm.get(property.attributes.dateType).setValue(dateType);
+ }
+
+ abstract save(action: TaskFilterAction): void;
+ abstract saveAs(action: TaskFilterAction): void;
+ abstract delete(action: TaskFilterAction): void;
+ abstract checkMandatorySortProperties(): void;
+ abstract checkMandatoryFilterProperties(): void;
+ abstract isDisabledForDefaultFilters(action: TaskFilterAction): boolean;
+ abstract createTaskFilterProperties(): TaskFilterProperties[];
+ abstract retrieveTaskFilterAndBuildForm(): void;
+ abstract assignNewFilter(formValues): void;
+
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts
new file mode 100644
index 0000000000..1e4d79ffa8
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.spec.ts
@@ -0,0 +1,706 @@
+/*!
+ * @license
+ * Copyright 2019 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { SimpleChange } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { setupTestBed } from '@alfresco/adf-core';
+import { MatDialog } from '@angular/material/dialog';
+import { of } from 'rxjs';
+import { debounceTime } from 'rxjs/operators';
+import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
+import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
+import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
+import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
+import { fakeApplicationInstance } from '../../../../app/mock/app-model.mock';
+import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
+import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
+import { TaskCloudService } from '../../../services/task-cloud.service';
+import { fakeServiceFilter } from '../../mock/task-filters-cloud.mock';
+import { TranslateModule } from '@ngx-translate/core';
+import { EditServiceTaskFilterCloudComponent } from './edit-service-task-filter-cloud.component';
+
+describe('EditServiceTaskFilterCloudComponent', () => {
+ let component: EditServiceTaskFilterCloudComponent;
+ let service: ServiceTaskFilterCloudService;
+ let appsService: AppsProcessCloudService;
+ let fixture: ComponentFixture;
+ let dialog: MatDialog;
+ let getTaskFilterSpy: jasmine.Spy;
+ let getRunningApplicationsSpy: jasmine.Spy;
+ let taskService: TaskCloudService;
+
+ setupTestBed({
+ imports: [
+ TranslateModule.forRoot(),
+ ProcessServiceCloudTestingModule,
+ TaskFiltersCloudModule
+ ],
+ providers: [
+ MatDialog,
+ { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent);
+ component = fixture.componentInstance;
+ service = TestBed.inject(ServiceTaskFilterCloudService);
+ appsService = TestBed.inject(AppsProcessCloudService);
+ taskService = TestBed.inject(TaskCloudService);
+ dialog = TestBed.inject(MatDialog);
+ spyOn(dialog, 'open').and.returnValue({
+ afterClosed: of({
+ action: EditServiceTaskFilterCloudComponent.ACTION_SAVE,
+ icon: 'icon',
+ name: 'fake-name'
+ })
+ });
+ getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeServiceFilter));
+ getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
+ fixture.detectChanges();
+ });
+
+ afterEach(() => fixture.destroy());
+
+ it('should fetch task filter by taskId', () => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(getTaskFilterSpy).toHaveBeenCalled();
+ expect(component.taskFilter.name).toEqual('FakeInvolvedTasks');
+ expect(component.taskFilter.icon).toEqual('adjust');
+ expect(component.taskFilter.status).toEqual('CREATED');
+ expect(component.taskFilter.order).toEqual('ASC');
+ expect(component.taskFilter.sort).toEqual('id');
+ });
+ });
+
+ it('should fetch process definitions when processDefinitionName filter property is set', async(() => {
+ const processSpy = spyOn(taskService, 'getProcessDefinitions').and.returnValue(of([{ id: 'fake-id', name: 'fake-name' }]));
+ fixture.detectChanges();
+ component.filterProperties = ['processDefinitionName'];
+ fixture.detectChanges();
+ const taskFilterIdChange = new SimpleChange(null, 'mock-process-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const controller = component.editTaskFilterForm.get('processDefinitionName');
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(processSpy).toHaveBeenCalled();
+ expect(controller).toBeDefined();
+ });
+ }));
+
+ it('should display filter name as title', async(() => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id');
+ const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-sub-title-id');
+ expect(title.innerText).toEqual('FakeInvolvedTasks');
+ expect(subTitle.innerText.trim()).toEqual('ADF_CLOUD_EDIT_TASK_FILTER.TITLE');
+ }));
+
+ it('should not display filter name if showFilterName is false', async(() => {
+ const taskFilterIdChange = new SimpleChange(null, 'mock-task-filter-id', true);
+ component.showTaskFilterName = false;
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id');
+
+ fixture.whenStable().then(() => {
+ expect(title).toBeNull();
+ });
+ }));
+
+ it('should not display mat-spinner if isloading set to false', async(() => {
+ const taskFilterIdChange = new SimpleChange(null, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id');
+ const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-sub-title-id');
+ const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin');
+
+ fixture.whenStable().then(() => {
+ expect(matSpinnerElement).toBeNull();
+ expect(title.innerText).toEqual('FakeInvolvedTasks');
+ expect(subTitle.innerText.trim()).toEqual('ADF_CLOUD_EDIT_TASK_FILTER.TITLE');
+ });
+ }));
+
+ it('should display mat-spinner if isloading set to true', async(() => {
+ component.isLoading = true;
+ const taskFilterIdChange = new SimpleChange(null, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+
+ const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin');
+
+ fixture.whenStable().then(() => {
+ expect(matSpinnerElement).toBeDefined();
+ });
+ }));
+
+ describe('EditServiceTaskFilter form', () => {
+
+ beforeEach(() => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ });
+
+ it('should defined editTaskFilter form ', () => {
+ expect(component.editTaskFilterForm).toBeDefined();
+ });
+
+ it('should create editTaskFilter form with default user task properties', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const appNameController = component.editTaskFilterForm.get('appName');
+ const statusController = component.editTaskFilterForm.get('status');
+ const sortController = component.editTaskFilterForm.get('sort');
+ const orderController = component.editTaskFilterForm.get('order');
+ const activityNameController = component.editTaskFilterForm.get('activityName');
+ expect(component.editTaskFilterForm).toBeDefined();
+ expect(appNameController.value).toBe('mock-app-name');
+ expect(statusController.value).toBe('COMPLETED');
+ expect(sortController.value).toBe('id');
+ expect(orderController.value).toBe('ASC');
+ expect(activityNameController.value).toBe('fake-activity');
+ });
+ }));
+
+ describe('Save & Delete buttons', () => {
+ it('should disable save and delete button for default task filters', async(() => {
+ getTaskFilterSpy.and.returnValue(of({
+ name: 'ADF_CLOUD_SERVICE_TASK_FILTERS.ALL_SERVICE_TASKS',
+ id: 'filter-id',
+ key: 'all-fake-task',
+ icon: 'adjust',
+ sort: 'startDate',
+ status: 'ALL',
+ order: 'DESC'
+ }));
+
+ const taskFilterIdChange = new SimpleChange(null, 'filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ expect(saveButton.disabled).toBe(true);
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ expect(deleteButton.disabled).toBe(true);
+ });
+ }));
+
+ it('should enable delete button for custom task filters', async(() => {
+ const taskFilterIdChange = new SimpleChange(null, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ expect(saveButton.disabled).toBe(true);
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ expect(deleteButton.disabled).toBe(false);
+ });
+ }));
+
+ it('should enable save button if the filter is changed for custom task filters', (done) => {
+ const taskFilterIdChange = new SimpleChange(null, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+
+ component.editTaskFilterForm.valueChanges
+ .pipe(debounceTime(300))
+ .subscribe(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ fixture.detectChanges();
+ expect(saveButton.disabled).toBe(false);
+ done();
+ });
+
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ sortOptions[3].nativeElement.click();
+ fixture.detectChanges();
+ });
+
+ it('should disable save button if the filter is not changed for custom filter', async(() => {
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ expect(saveButton.disabled).toBe(true);
+ });
+ }));
+ });
+
+ describe('SaveAs button', () => {
+ it('should disable saveAs button if the process filter is not changed for default filter', async(() => {
+ getTaskFilterSpy.and.returnValue(of({
+ name: 'ADF_CLOUD_TASK_FILTERS.MY_TASKS',
+ id: 'filter-id',
+ key: 'all-fake-task',
+ icon: 'adjust',
+ sort: 'startDate',
+ status: 'ALL',
+ order: 'DESC'
+ }));
+
+ const taskFilterIdChange = new SimpleChange(null, 'filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ expect(saveButton.disabled).toEqual(true);
+ });
+ }));
+
+ it('should disable saveAs button if the process filter is not changed for custom filter', async(() => {
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ expect(saveButton.disabled).toEqual(true);
+ });
+ }));
+
+ it('should enable saveAs button if the filter values are changed for default filter', (done) => {
+ getTaskFilterSpy.and.returnValue(of({
+ name: 'ADF_CLOUD_TASK_FILTERS.MY_TASKS',
+ id: 'filter-id',
+ key: 'all-fake-task',
+ icon: 'adjust',
+ sort: 'startDate',
+ status: 'ALL',
+ order: 'DESC'
+ }));
+
+ const taskFilterIdChange = new SimpleChange(null, 'filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+
+ component.editTaskFilterForm.valueChanges
+ .pipe(debounceTime(300))
+ .subscribe(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ fixture.detectChanges();
+ expect(saveButton.disabled).toEqual(false);
+ done();
+ });
+
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ sortOptions[3].nativeElement.click();
+ fixture.detectChanges();
+ });
+
+ it('should enable saveAs button if the filter values are changed for custom filter', (done) => {
+ component.toggleFilterActions = true;
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+
+ component.editTaskFilterForm.valueChanges
+ .pipe(debounceTime(300))
+ .subscribe(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ fixture.detectChanges();
+ expect(saveButton.disabled).toEqual(false);
+ done();
+ });
+
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ sortOptions[3].nativeElement.click();
+ fixture.detectChanges();
+ });
+ });
+
+ it('should display current task filter details', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-status"]');
+ const assigneeElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-assignee"]');
+ const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]');
+ const orderElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-order"]');
+ expect(assigneeElement).toBeDefined();
+ expect(stateElement.textContent.trim()).toBe('COMPLETED');
+ expect(sortElement.textContent.trim()).toBe('Id');
+ expect(orderElement.textContent.trim()).toBe('ASC');
+ });
+ }));
+
+ it('should display all the statuses that are defined in the task filter', async(() => {
+
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-status"]');
+ stateElement.click();
+ fixture.detectChanges();
+
+ const statusOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-status"]'));
+
+ expect(statusOptions[0].nativeElement.textContent.trim()).toBe('ALL');
+ expect(statusOptions[1].nativeElement.textContent.trim()).toBe('STARTED');
+ expect(statusOptions[2].nativeElement.textContent.trim()).toBe('COMPLETED');
+ expect(statusOptions[3].nativeElement.textContent.trim()).toBe('CANCELLED');
+ expect(statusOptions[4].nativeElement.textContent.trim()).toBe('ERROR');
+ }));
+
+ it('should display sort drop down', async(() => {
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]');
+ sortElement.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ expect(sortOptions.length).toEqual(4);
+ });
+ }));
+
+ it('should display order drop down', async(() => {
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const orderElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-order"]');
+ orderElement.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const orderOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ expect(orderOptions.length).toEqual(2);
+ });
+ }));
+
+ it('should able to build a editTaskFilter form with default properties if input is empty', async(() => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ component.filterProperties = [];
+ fixture.detectChanges();
+ const stateController = component.editTaskFilterForm.get('status');
+ const sortController = component.editTaskFilterForm.get('sort');
+ const orderController = component.editTaskFilterForm.get('order');
+ const activityNameController = component.editTaskFilterForm.get('activityName');
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(component.taskFilterProperties.length).toBe(5);
+ expect(component.editTaskFilterForm).toBeDefined();
+ expect(stateController.value).toBe('COMPLETED');
+ expect(sortController.value).toBe('id');
+ expect(orderController.value).toBe('ASC');
+ expect(activityNameController.value).toBe('fake-activity');
+ });
+ }));
+
+ it('should able to fetch running applications when appName property defined in the input', async(() => {
+ component.filterProperties = ['appName', 'processInstanceId', 'priority'];
+ fixture.detectChanges();
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ const appController = component.editTaskFilterForm.get('appName');
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(getRunningApplicationsSpy).toHaveBeenCalled();
+ expect(appController).toBeDefined();
+ expect(appController.value).toBe('mock-app-name');
+ });
+ }));
+ });
+
+ describe('sort properties', () => {
+
+ it('should display default sort properties', async(() => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]');
+ sortElement.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const sortController = component.editTaskFilterForm.get('sort');
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ expect(sortController.value).toBe('id');
+ expect(sortOptions.length).toEqual(4);
+ });
+ }));
+
+ it('should display sort properties when sort properties are specified', async(() => {
+ component.sortProperties = ['id', 'name', 'processInstanceId'];
+ getTaskFilterSpy.and.returnValue(of({
+ sort: 'my-custom-sort',
+ processInstanceId: 'process-instance-id',
+ priority: '12'
+ }));
+ fixture.detectChanges();
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]');
+ sortElement.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const sortController = component.editTaskFilterForm.get('sort');
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ expect(component.sortProperties.length).toBe(3);
+ expect(sortController.value).toBe('my-custom-sort');
+ expect(sortOptions.length).toEqual(3);
+ });
+ }));
+
+ it('should display default sort properties if input is empty', async(() => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ component.sortProperties = [];
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]');
+ sortElement.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const sortController = component.editTaskFilterForm.get('sort');
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ expect(sortController.value).toBe('id');
+ expect(sortOptions.length).toEqual(4);
+ });
+ }));
+ });
+
+ describe('filter actions', () => {
+
+ it('should display default filter actions', async(() => {
+ component.toggleFilterActions = true;
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ expect(component.taskFilterActions.map(action => action.actionType)).toEqual(['save', 'saveAs', 'delete']);
+ expect(component.taskFilterActions.length).toBe(3);
+ expect(saveButton.disabled).toBe(true);
+ expect(saveAsButton.disabled).toBe(true);
+ expect(deleteButton.disabled).toBe(false);
+ });
+ }));
+
+ it('should display filter actions when input actions are specified', async(() => {
+ component.actions = ['save'];
+ fixture.detectChanges();
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ component.toggleFilterActions = true;
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ expect(component.taskFilterActions.map(action => action.actionType)).toEqual(['save']);
+ expect(component.taskFilterActions.length).toBe(1);
+ expect(saveButton.disabled).toBeTruthy();
+ const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ expect(saveAsButton).toBeFalsy();
+ expect(deleteButton).toBeFalsy();
+ });
+ }));
+
+ });
+
+ describe('edit filter actions', () => {
+
+ beforeEach(() => {
+ const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
+ component.ngOnChanges({ 'id': taskFilterIdChange });
+ fixture.detectChanges();
+ spyOn(component.action, 'emit').and.callThrough();
+ });
+
+ it('should emit save event and save the filter on click save button', async(() => {
+ component.toggleFilterActions = true;
+ spyOn(service, 'updateFilter').and.returnValue(of({}));
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ sortOptions[3].nativeElement.click();
+ fixture.detectChanges();
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(saveButton.disabled).toBe(false);
+ saveButton.click();
+ expect(service.updateFilter).toHaveBeenCalled();
+ expect(component.action.emit).toHaveBeenCalled();
+ });
+ }));
+
+ it('should emit delete event and delete the filter on click of delete button', async(() => {
+ component.toggleFilterActions = true;
+ spyOn(service, 'deleteFilter').and.returnValue(of({}));
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(deleteButton.disabled).toBe(false);
+ deleteButton.click();
+ expect(service.deleteFilter).toHaveBeenCalled();
+ expect(component.action.emit).toHaveBeenCalled();
+ });
+ }));
+
+ it('should emit saveAs event and add filter on click saveAs button', async(() => {
+ component.toggleFilterActions = true;
+ spyOn(service, 'addFilter').and.returnValue(of({}));
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ sortElement.click();
+ fixture.detectChanges();
+ const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text'));
+ sortOptions[2].nativeElement.click();
+ fixture.detectChanges();
+ const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(saveAsButton.disabled).toBe(false);
+ saveAsButton.click();
+ expect(service.addFilter).toHaveBeenCalled();
+ expect(component.action.emit).toHaveBeenCalled();
+ expect(dialog.open).toHaveBeenCalled();
+ });
+ }));
+
+ it('should call restore default filters service on deletion of last filter', async(() => {
+ component.toggleFilterActions = true;
+ spyOn(service, 'deleteFilter').and.returnValue(of([]));
+ const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(deleteButton.disabled).toBe(false);
+ deleteButton.click();
+ expect(service.deleteFilter).toHaveBeenCalled();
+ expect(component.action.emit).toHaveBeenCalled();
+ expect(restoreDefaultFiltersSpy).toHaveBeenCalled();
+ });
+ }));
+
+ it('should not call restore default filters service on deletion of first filter', async(() => {
+ component.toggleFilterActions = true;
+ spyOn(service, 'deleteFilter').and.returnValue(of([{ name: 'mock-filter-name' }]));
+ const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
+ fixture.detectChanges();
+ const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
+ expansionPanel.click();
+ fixture.detectChanges();
+ const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
+ stateElement.click();
+ fixture.detectChanges();
+ const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]');
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(deleteButton.disabled).toBe(false);
+ deleteButton.click();
+ expect(service.deleteFilter).toHaveBeenCalled();
+ expect(component.action.emit).toHaveBeenCalled();
+ expect(restoreDefaultFiltersSpy).not.toHaveBeenCalled();
+ });
+ }));
+ });
+});
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts
new file mode 100644
index 0000000000..6875626a9c
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts
@@ -0,0 +1,267 @@
+/*!
+ * @license
+ * Copyright 2019 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, Output, EventEmitter } from '@angular/core';
+import { FormBuilder } from '@angular/forms';
+import { DateAdapter } from '@angular/material/core';
+import { MatDialog } from '@angular/material/dialog';
+import { filter, takeUntil, finalize, switchMap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
+import { Moment } from 'moment';
+
+import { TaskFilterProperties, TaskFilterAction, ServiceTaskFilterCloudModel } from '../../models/filter-cloud.model';
+import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
+import { TranslationService, UserPreferencesService } from '@alfresco/adf-core';
+import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
+import { TaskCloudService } from '../../../services/task-cloud.service';
+import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
+import { BaseEditTaskFilterCloudComponent } from './base-edit-task-filter-cloud.component';
+
+@Component({
+ selector: 'adf-cloud-edit-service-task-filter',
+ templateUrl: './base-edit-task-filter-cloud.component.html',
+ styleUrls: ['./base-edit-task-filter-cloud.component.scss']
+})
+export class EditServiceTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent {
+
+ public static DEFAULT_TASK_FILTER_PROPERTIES = ['appName', 'activityName', 'status', 'sort', 'order'];
+ public static DEFAULT_TASK_SORT_PROPERTIES = ['id', 'name', 'startedDate', 'completedDate'];
+ public static DEFAULT_TASK_STATUS_PROPERTIES = [
+ { label: 'ALL', value: '' },
+ { label: 'STARTED', value: 'STARTED' },
+ { label: 'COMPLETED', value: 'COMPLETED' },
+ { label: 'CANCELLED', value: 'CANCELLED' },
+ { label: 'ERROR', value: 'ERROR' }
+ ];
+
+ /** Emitted when a task filter property changes. */
+ @Output()
+ filterChange: EventEmitter = new EventEmitter();
+
+ taskFilter: ServiceTaskFilterCloudModel;
+ changedTaskFilter: ServiceTaskFilterCloudModel;
+
+ constructor(
+ protected formBuilder: FormBuilder,
+ public dialog: MatDialog,
+ private translateService: TranslationService,
+ private serviceTaskFilterCloudService: ServiceTaskFilterCloudService,
+ protected dateAdapter: DateAdapter,
+ protected userPreferencesService: UserPreferencesService,
+ protected appsProcessCloudService: AppsProcessCloudService,
+ protected taskCloudService: TaskCloudService) {
+ super(formBuilder, dateAdapter, userPreferencesService, appsProcessCloudService, taskCloudService);
+ }
+
+ assignNewFilter(formValues: ServiceTaskFilterCloudModel) {
+ this.changedTaskFilter = Object.assign({}, this.taskFilter, formValues) as ServiceTaskFilterCloudModel;
+ this.formHasBeenChanged = !this.compareFilters(this.changedTaskFilter, this.taskFilter);
+ this.filterChange.emit(this.changedTaskFilter);
+ }
+
+ /**
+ * Fetches task filter by application name and filter id and creates filter properties, build form
+ */
+ retrieveTaskFilterAndBuildForm() {
+ this.isLoading = true;
+ this.serviceTaskFilterCloudService.getTaskFilterById(this.appName, this.id)
+ .pipe(
+ finalize(() => this.isLoading = false),
+ takeUntil(this.onDestroy$)
+ )
+ .subscribe(response => {
+ this.taskFilter = response as ServiceTaskFilterCloudModel;
+ this.taskFilterProperties = this.createAndFilterProperties();
+ this.taskFilterActions = this.createAndFilterActions();
+ this.buildForm(this.taskFilterProperties);
+ });
+ }
+
+ checkMandatoryFilterProperties() {
+ if (this.filterProperties === undefined || this.filterProperties.length === 0) {
+ this.filterProperties = EditServiceTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES;
+ }
+ }
+
+ checkMandatorySortProperties(): void {
+ if (this.sortProperties === undefined || this.sortProperties.length === 0) {
+ this.sortProperties = EditServiceTaskFilterCloudComponent.DEFAULT_TASK_SORT_PROPERTIES;
+ }
+ }
+
+ /**
+ * Return true if both filters are same
+ * @param editedQuery, @param currentQuery
+ */
+ compareFilters(
+ editedQuery: ServiceTaskFilterCloudModel,
+ currentQuery: ServiceTaskFilterCloudModel
+ ): boolean {
+ return JSON.stringify(editedQuery).toLowerCase() === JSON.stringify(currentQuery).toLowerCase();
+ }
+
+ save(saveAction: TaskFilterAction): void {
+ this.serviceTaskFilterCloudService
+ .updateFilter(this.changedTaskFilter)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe(() => {
+ saveAction.filter = this.changedTaskFilter;
+ this.action.emit(saveAction);
+ this.formHasBeenChanged = this.compareFilters(this.changedTaskFilter, this.taskFilter);
+ });
+ }
+
+ delete(deleteAction: TaskFilterAction): void {
+ this.serviceTaskFilterCloudService
+ .deleteFilter(this.taskFilter)
+ .pipe(
+ filter((filters) => {
+ deleteAction.filter = this.taskFilter;
+ this.action.emit(deleteAction);
+ return filters.length === 0;
+ }),
+ switchMap(() => this.restoreDefaultTaskFilters()),
+ takeUntil(this.onDestroy$))
+ .subscribe(() => { });
+ }
+
+ saveAs(saveAsAction: TaskFilterAction): void {
+ const dialogRef = this.dialog.open(TaskFilterDialogCloudComponent, {
+ data: {
+ name: this.translateService.instant(this.taskFilter.name)
+ },
+ height: 'auto',
+ minWidth: '30%'
+ });
+ dialogRef.afterClosed().subscribe((result) => {
+ if (result && result.action === TaskFilterDialogCloudComponent.ACTION_SAVE) {
+ const filterId = Math.random().toString(36).substr(2, 9);
+ const filterKey = this.getSanitizeFilterName(result.name);
+ const newFilter = {
+ name: result.name,
+ icon: result.icon,
+ id: filterId,
+ key: 'custom-' + filterKey
+ };
+ const resultFilter: ServiceTaskFilterCloudModel = Object.assign({}, this.changedTaskFilter, newFilter);
+ this.serviceTaskFilterCloudService.addFilter(resultFilter)
+ .pipe(takeUntil(this.onDestroy$)).subscribe(() => {
+ saveAsAction.filter = resultFilter;
+ this.action.emit(saveAsAction);
+ });
+ }
+ });
+ }
+
+ isDisabledForDefaultFilters(action: TaskFilterAction): boolean {
+ return (
+ this.serviceTaskFilterCloudService.isDefaultFilter(this.taskFilter.name) &&
+ EditServiceTaskFilterCloudComponent.ACTIONS_DISABLED_BY_DEFAULT.includes(action.actionType)
+ );
+ }
+
+ restoreDefaultTaskFilters(): Observable {
+ return this.serviceTaskFilterCloudService.getTaskListFilters(this.appName);
+ }
+
+ createTaskFilterProperties(): TaskFilterProperties[] {
+ return [
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.APP_NAME',
+ type: 'select',
+ key: 'appName',
+ value: this.taskFilter.appName || '',
+ options: this.applicationNames
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.SERVICE_TASK_ID',
+ type: 'text',
+ key: 'serviceTaskId',
+ value: this.taskFilter.serviceTaskId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.ELEMENT_ID',
+ type: 'text',
+ key: 'elementId',
+ value: this.taskFilter.elementId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.ACTIVITY_NAME',
+ type: 'text',
+ key: 'activityName',
+ value: this.taskFilter.activityName || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.ACTIVITY_TYPE',
+ type: 'text',
+ key: 'activityType',
+ value: this.taskFilter.activityType || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.SORT',
+ type: 'select',
+ key: 'sort',
+ value: this.taskFilter.sort || this.createSortProperties[0].value,
+ options: this.createSortProperties
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.DIRECTION',
+ type: 'select',
+ key: 'order',
+ value: this.taskFilter.order || EditServiceTaskFilterCloudComponent.DIRECTIONS[0].value,
+ options: EditServiceTaskFilterCloudComponent.DIRECTIONS
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.STATUS',
+ type: 'select',
+ key: 'status',
+ value: this.taskFilter.status || EditServiceTaskFilterCloudComponent.DEFAULT_TASK_STATUS_PROPERTIES[0].value,
+ options: EditServiceTaskFilterCloudComponent.DEFAULT_TASK_STATUS_PROPERTIES
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.STARTED_DATE',
+ type: 'date',
+ key: 'startedDate',
+ value: this.taskFilter.completedDate || false
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.COMPLETED_DATE',
+ type: 'date',
+ key: 'completedDate',
+ value: this.taskFilter.completedDate || false
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.PROCESS_INSTANCE_ID',
+ type: 'text',
+ key: 'processInstanceId',
+ value: this.taskFilter.processInstanceId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.PROCESS_DEF_ID',
+ type: 'text',
+ key: 'processDefinitionId',
+ value: this.taskFilter.processDefinitionId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.SERVICE_NAME',
+ type: 'text',
+ key: 'serviceName',
+ value: this.taskFilter.serviceName || ''
+ })
+ ];
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts
similarity index 93%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.spec.ts
rename to lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts
index 6a7db9bc3a..b2a842e1e5 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filter-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts
@@ -23,21 +23,21 @@ import { setupTestBed } from '@alfresco/adf-core';
import { MatDialog } from '@angular/material/dialog';
import { of } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
-import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
-import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
-import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
-import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
-import { fakeApplicationInstance } from '../../../app/mock/app-model.mock';
-import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
+import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
+import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
+import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
+import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
+import { fakeApplicationInstance } from '../../../../app/mock/app-model.mock';
+import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { EditTaskFilterCloudComponent } from './edit-task-filter-cloud.component';
-import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
-import { TaskCloudService } from '../../services/task-cloud.service';
-import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
-import { fakeFilter } from '../mock/task-filters-cloud.mock';
+import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
+import { TaskCloudService } from '../../../services/task-cloud.service';
+import { fakeFilter } from '../../mock/task-filters-cloud.mock';
import { AbstractControl } from '@angular/forms';
import moment from 'moment-es6';
import { TranslateModule } from '@ngx-translate/core';
-import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
+import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model';
+import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
describe('EditTaskFilterCloudComponent', () => {
let component: EditTaskFilterCloudComponent;
@@ -68,11 +68,13 @@ describe('EditTaskFilterCloudComponent', () => {
appsService = TestBed.inject(AppsProcessCloudService);
taskService = TestBed.inject(TaskCloudService);
dialog = TestBed.inject(MatDialog);
- spyOn(dialog, 'open').and.returnValue({ afterClosed: of({
- action: TaskFilterDialogCloudComponent.ACTION_SAVE,
- icon: 'icon',
- name: 'fake-name'
- }) });
+ spyOn(dialog, 'open').and.returnValue({
+ afterClosed: of({
+ action: EditTaskFilterCloudComponent.ACTION_SAVE,
+ icon: 'icon',
+ name: 'fake-name'
+ })
+ });
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
fixture.detectChanges();
@@ -172,7 +174,7 @@ describe('EditTaskFilterCloudComponent', () => {
expect(component.editTaskFilterForm).toBeDefined();
});
- it('should create editTaskFilter form with default properties', async(() => {
+ it('should create editTaskFilter form with default user task properties', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const stateController = component.editTaskFilterForm.get('status');
@@ -244,13 +246,13 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges();
component.editTaskFilterForm.valueChanges
- .pipe(debounceTime(300))
- .subscribe(() => {
- const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
- fixture.detectChanges();
- expect(saveButton.disabled).toBe(false);
- done();
- });
+ .pipe(debounceTime(300))
+ .subscribe(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
+ fixture.detectChanges();
+ expect(saveButton.disabled).toBe(false);
+ done();
+ });
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
stateElement.click();
@@ -330,13 +332,13 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges();
component.editTaskFilterForm.valueChanges
- .pipe(debounceTime(300))
- .subscribe(() => {
- const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
- fixture.detectChanges();
- expect(saveButton.disabled).toEqual(false);
- done();
- });
+ .pipe(debounceTime(300))
+ .subscribe(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ fixture.detectChanges();
+ expect(saveButton.disabled).toEqual(false);
+ done();
+ });
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
stateElement.click();
@@ -354,13 +356,13 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges();
component.editTaskFilterForm.valueChanges
- .pipe(debounceTime(300))
- .subscribe(() => {
- const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
- fixture.detectChanges();
- expect(saveButton.disabled).toEqual(false);
- done();
- });
+ .pipe(debounceTime(300))
+ .subscribe(() => {
+ const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
+ fixture.detectChanges();
+ expect(saveButton.disabled).toEqual(false);
+ done();
+ });
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
stateElement.click();
@@ -657,7 +659,9 @@ describe('EditTaskFilterCloudComponent', () => {
});
component.filterChange.subscribe(() => {
- expect(component.changedTaskFilter.lastModifiedTo.toISOString()).toEqual(lastModifiedToFilter.toISOString());
+ if (component.changedTaskFilter instanceof TaskFilterCloudModel) {
+ expect(component.changedTaskFilter.lastModifiedTo.toISOString()).toEqual(lastModifiedToFilter.toISOString());
+ }
done();
});
component.onFilterChange();
@@ -764,7 +768,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should not call restore default filters service on deletion of first filter', async(() => {
component.toggleFilterActions = true;
- spyOn(service, 'deleteFilter').and.returnValue(of([{ name: 'mock-filter-name'}]));
+ spyOn(service, 'deleteFilter').and.returnValue(of([{ name: 'mock-filter-name' }]));
const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts
new file mode 100644
index 0000000000..d3fcdec248
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts
@@ -0,0 +1,334 @@
+/*!
+ * @license
+ * Copyright 2019 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, Output, EventEmitter } from '@angular/core';
+import { FormBuilder } from '@angular/forms';
+import { DateAdapter } from '@angular/material/core';
+import { MatDialog } from '@angular/material/dialog';
+import { filter, takeUntil, finalize, switchMap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
+import moment from 'moment-es6';
+import { Moment } from 'moment';
+
+import { TaskFilterCloudModel, TaskFilterProperties, TaskFilterAction } from '../../models/filter-cloud.model';
+import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
+import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
+import { TranslationService, UserPreferencesService } from '@alfresco/adf-core';
+import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
+import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model';
+import { TaskCloudService } from '../../../services/task-cloud.service';
+import { BaseEditTaskFilterCloudComponent } from './base-edit-task-filter-cloud.component';
+
+@Component({
+ selector: 'adf-cloud-edit-task-filter',
+ templateUrl: './base-edit-task-filter-cloud.component.html',
+ styleUrls: ['./base-edit-task-filter-cloud.component.scss']
+})
+export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent {
+
+ public static DEFAULT_TASK_FILTER_PROPERTIES = ['status', 'assignee', 'sort', 'order'];
+ public static DEFAULT_TASK_SORT_PROPERTIES = ['id', 'name', 'createdDate', 'priority'];
+ public static DEFAULT_TASK_STATUS_PROPERTIES = [
+ { label: 'ALL', value: '' },
+ { label: 'CREATED', value: 'CREATED' },
+ { label: 'ASSIGNED', value: 'ASSIGNED' },
+ { label: 'SUSPENDED', value: 'SUSPENDED' },
+ { label: 'CANCELLED', value: 'CANCELLED' },
+ { label: 'COMPLETED', value: 'COMPLETED' }
+ ];
+
+ /** Emitted when a task filter property changes. */
+ @Output()
+ filterChange: EventEmitter = new EventEmitter();
+
+ taskFilter: TaskFilterCloudModel;
+ changedTaskFilter: TaskFilterCloudModel;
+
+ constructor(
+ protected formBuilder: FormBuilder,
+ public dialog: MatDialog,
+ private translateService: TranslationService,
+ private taskFilterCloudService: TaskFilterCloudService,
+ protected dateAdapter: DateAdapter,
+ protected userPreferencesService: UserPreferencesService,
+ protected appsProcessCloudService: AppsProcessCloudService,
+ protected taskCloudService: TaskCloudService) {
+ super(formBuilder, dateAdapter, userPreferencesService, appsProcessCloudService, taskCloudService);
+ }
+
+ assignNewFilter(formValues: TaskFilterCloudModel) {
+ this.setLastModifiedToFilter( formValues);
+ this.changedTaskFilter = new TaskFilterCloudModel(Object.assign({}, this.taskFilter, formValues));
+ this.formHasBeenChanged = !this.compareFilters(this.changedTaskFilter, this.taskFilter);
+ this.filterChange.emit(this.changedTaskFilter);
+ }
+
+ /**
+ * Fetches task filter by application name and filter id and creates filter properties, build form
+ */
+ retrieveTaskFilterAndBuildForm() {
+ this.isLoading = true;
+ this.taskFilterCloudService.getTaskFilterById(this.appName, this.id)
+ .pipe(
+ finalize(() => this.isLoading = false),
+ takeUntil(this.onDestroy$)
+ )
+ .subscribe(response => {
+ this.taskFilter = new TaskFilterCloudModel(response);
+ this.taskFilterProperties = this.createAndFilterProperties();
+ if (this.hasLastModifiedProperty()) {
+ this.taskFilterProperties = [...this.taskFilterProperties, ...this.createLastModifiedProperty()];
+ }
+ this.taskFilterActions = this.createAndFilterActions();
+ this.buildForm(this.taskFilterProperties);
+ });
+ }
+
+ checkMandatoryFilterProperties() {
+ if (this.filterProperties === undefined || this.filterProperties.length === 0) {
+ this.filterProperties = EditTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES;
+ }
+ }
+
+ checkMandatorySortProperties(): void {
+ if (this.sortProperties === undefined || this.sortProperties.length === 0) {
+ this.sortProperties = EditTaskFilterCloudComponent.DEFAULT_TASK_SORT_PROPERTIES;
+ }
+ }
+
+ private setLastModifiedToFilter(formValues: TaskFilterCloudModel) {
+ if (formValues.lastModifiedTo && Date.parse(formValues.lastModifiedTo.toString())) {
+ const lastModifiedToFilterValue = moment(formValues.lastModifiedTo);
+ lastModifiedToFilterValue.set({
+ hour: 23,
+ minute: 59,
+ second: 59
+ });
+ formValues.lastModifiedTo = lastModifiedToFilterValue.toDate();
+ }
+ }
+
+ /**
+ * Return true if both filters are same
+ * @param editedQuery, @param currentQuery
+ */
+ compareFilters(
+ editedQuery: TaskFilterCloudModel,
+ currentQuery: TaskFilterCloudModel
+ ): boolean {
+ return JSON.stringify(editedQuery).toLowerCase() === JSON.stringify(currentQuery).toLowerCase();
+ }
+
+ save(saveAction: TaskFilterAction): void {
+ this.taskFilterCloudService
+ .updateFilter(this.changedTaskFilter)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe(() => {
+ saveAction.filter = this.changedTaskFilter;
+ this.action.emit(saveAction);
+ this.formHasBeenChanged = this.compareFilters(this.changedTaskFilter, this.taskFilter);
+ });
+ }
+
+ delete(deleteAction: TaskFilterAction): void {
+ this.taskFilterCloudService
+ .deleteFilter(this.taskFilter)
+ .pipe(
+ filter((filters) => {
+ deleteAction.filter = this.taskFilter;
+ this.action.emit(deleteAction);
+ return filters.length === 0;
+ }),
+ switchMap(() => this.restoreDefaultTaskFilters()),
+ takeUntil(this.onDestroy$))
+ .subscribe(() => { });
+ }
+
+ saveAs(saveAsAction: TaskFilterAction): void {
+ const dialogRef = this.dialog.open(TaskFilterDialogCloudComponent, {
+ data: {
+ name: this.translateService.instant(this.taskFilter.name)
+ },
+ height: 'auto',
+ minWidth: '30%'
+ });
+ dialogRef.afterClosed().subscribe((result) => {
+ if (result && result.action === TaskFilterDialogCloudComponent.ACTION_SAVE) {
+ const filterId = Math.random().toString(36).substr(2, 9);
+ const filterKey = this.getSanitizeFilterName(result.name);
+ const newFilter = {
+ name: result.name,
+ icon: result.icon,
+ id: filterId,
+ key: 'custom-' + filterKey
+ };
+ const resultFilter: TaskFilterCloudModel = Object.assign({}, this.changedTaskFilter, newFilter);
+ this.taskFilterCloudService.addFilter(resultFilter)
+ .pipe(takeUntil(this.onDestroy$)).subscribe(() => {
+ saveAsAction.filter = resultFilter;
+ this.action.emit(saveAsAction);
+ });
+ }
+ });
+ }
+
+ isDisabledForDefaultFilters(action: TaskFilterAction): boolean {
+ return (
+ this.taskFilterCloudService.isDefaultFilter(this.taskFilter.name) &&
+ EditTaskFilterCloudComponent.ACTIONS_DISABLED_BY_DEFAULT.includes(action.actionType)
+ );
+ }
+
+ restoreDefaultTaskFilters(): Observable {
+ return this.taskFilterCloudService.getTaskListFilters(this.appName);
+ }
+
+ createLastModifiedProperty(): TaskFilterProperties[] {
+ return [
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.LAST_MODIFIED_FROM',
+ type: 'date',
+ key: 'lastModifiedFrom',
+ value: ''
+ }),
+
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.LAST_MODIFIED_TO',
+ type: 'date',
+ key: 'lastModifiedTo',
+ value: ''
+ })
+ ];
+ }
+
+ createTaskFilterProperties(): TaskFilterProperties[] {
+ return [
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.APP_NAME',
+ type: 'select',
+ key: 'appName',
+ value: this.taskFilter.appName || '',
+ options: this.applicationNames
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.TASK_ID',
+ type: 'text',
+ key: 'taskId',
+ value: ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.STATUS',
+ type: 'select',
+ key: 'status',
+ value: this.taskFilter.status || EditTaskFilterCloudComponent.DEFAULT_TASK_STATUS_PROPERTIES[0].value,
+ options: EditTaskFilterCloudComponent.DEFAULT_TASK_STATUS_PROPERTIES
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.ASSIGNMENT',
+ type: 'text',
+ key: 'assignee',
+ value: this.taskFilter.assignee || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_DEF_NAME',
+ type: 'select',
+ key: 'processDefinitionName',
+ value: this.taskFilter.processDefinitionName || '',
+ options: this.processDefinitionNames
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_INSTANCE_ID',
+ type: 'text',
+ key: 'processInstanceId',
+ value: this.taskFilter.processInstanceId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PROCESS_DEF_ID',
+ type: 'text',
+ key: 'processDefinitionId',
+ value: this.taskFilter.processDefinitionId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.TASK_NAME',
+ type: 'text',
+ key: 'taskName',
+ value: this.taskFilter.taskName || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PARENT_TASK_ID',
+ type: 'text',
+ key: 'parentTaskId',
+ value: this.taskFilter.parentTaskId || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.PRIORITY',
+ type: 'text',
+ key: 'priority',
+ value: this.taskFilter.priority || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.OWNER',
+ type: 'text',
+ key: 'owner',
+ value: this.taskFilter.owner || ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.CREATED_DATE',
+ type: 'date',
+ key: 'createdDate',
+ value: ''
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.SORT',
+ type: 'select',
+ key: 'sort',
+ value: this.taskFilter.sort || this.createSortProperties[0].value,
+ options: this.createSortProperties
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.DIRECTION',
+ type: 'select',
+ key: 'order',
+ value: this.taskFilter.order || EditTaskFilterCloudComponent.DIRECTIONS[0].value,
+ options: EditTaskFilterCloudComponent.DIRECTIONS
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.STAND_ALONE',
+ type: 'checkbox',
+ key: 'standalone',
+ value: this.taskFilter.standalone || false
+ }),
+ new TaskFilterProperties({
+ label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.DUE_DATE',
+ type: 'date-range',
+ key: 'dueDateRange',
+ attributes: { dateType: 'dueDateType', from: '_dueDateFrom', to: '_dueDateTo'},
+ value: {
+ dueDateType: this.taskFilter.dueDateType || null,
+ _dueDateFrom: this.taskFilter.dueDateFrom || null,
+ _dueDateTo: this.taskFilter.dueDateTo || null
+ },
+ dateFilterOptions: [
+ DateCloudFilterType.NO_DATE,
+ DateCloudFilterType.TOMORROW,
+ DateCloudFilterType.NEXT_7_DAYS,
+ DateCloudFilterType.RANGE
+ ]
+ })
+ ];
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.spec.ts
new file mode 100644
index 0000000000..c55dbdeaf0
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.spec.ts
@@ -0,0 +1,354 @@
+/*!
+ * @license
+ * Copyright 2019 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 { SimpleChange } from '@angular/core';
+import { ComponentFixture, TestBed, async } from '@angular/core/testing';
+import { setupTestBed } from '@alfresco/adf-core';
+import { from, Observable } from 'rxjs';
+import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
+import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
+import { FilterParamsModel } from '../models/filter-cloud.model';
+import { By } from '@angular/platform-browser';
+import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
+import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
+import { fakeGlobalServiceFilters } from '../mock/task-filters-cloud.mock';
+import { TranslateModule } from '@ngx-translate/core';
+import { ServiceTaskFilterCloudService } from '../services/service-task-filter-cloud.service';
+import { ServiceTaskFiltersCloudComponent } from './service-task-filters-cloud.component';
+
+describe('ServiceTaskFiltersCloudComponent', () => {
+
+ let serviceTaskFilterCloudService: ServiceTaskFilterCloudService;
+
+ const fakeGlobalFilterObservable =
+ new Observable(function(observer) {
+ observer.next(fakeGlobalServiceFilters);
+ observer.complete();
+ });
+
+ const fakeGlobalFilterPromise = new Promise(function (resolve) {
+ resolve(fakeGlobalServiceFilters);
+ });
+
+ const mockErrorFilterList = {
+ error: 'wrong request'
+ };
+
+ const mockErrorFilterPromise = Promise.reject(mockErrorFilterList);
+
+ let component: ServiceTaskFiltersCloudComponent;
+ let fixture: ComponentFixture;
+
+ setupTestBed({
+ imports: [
+ TranslateModule.forRoot(),
+ ProcessServiceCloudTestingModule,
+ TaskFiltersCloudModule
+ ],
+ providers: [
+ { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ServiceTaskFiltersCloudComponent);
+ component = fixture.componentInstance;
+
+ serviceTaskFilterCloudService = TestBed.inject(ServiceTaskFilterCloudService);
+ });
+
+ it('should attach specific icon for each filter if hasIcon is true', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+ const change = new SimpleChange(undefined, 'my-app-1', true);
+ component.ngOnChanges({'appName': change});
+ fixture.detectChanges();
+ component.showIcons = true;
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(component.filters.length).toBe(3);
+ const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
+ expect(filters.length).toBe(3);
+ expect(filters[0].innerText).toContain('adjust');
+ expect(filters[1].innerText).toContain('done');
+ expect(filters[2].innerText).toContain('inbox');
+ });
+ }));
+
+ it('should not attach icons for each filter if hasIcon is false', (done) => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(from(fakeGlobalFilterPromise));
+
+ component.showIcons = false;
+ const change = new SimpleChange(undefined, 'my-app-1', true);
+ component.ngOnChanges({'appName': change});
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
+ expect(filters.length).toBe(0);
+ done();
+ });
+ });
+
+ it('should display the filters', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+ const change = new SimpleChange(undefined, 'my-app-1', true);
+ component.ngOnChanges({'appName': change});
+ fixture.detectChanges();
+ component.showIcons = true;
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ const filters = fixture.debugElement.queryAll(By.css('.adf-filters__entry'));
+ expect(component.filters.length).toBe(3);
+ expect(filters.length).toBe(3);
+ expect(filters[0].nativeElement.innerText).toContain('FakeServiceTasks');
+ expect(filters[1].nativeElement.innerText).toContain('FakeMyServiceTasks1');
+ expect(filters[2].nativeElement.innerText).toContain('FakeMyServiceTasks2');
+ });
+ }));
+
+ it('should emit an error with a bad response', (done) => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(from(mockErrorFilterPromise));
+
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+ component.ngOnChanges({'appName': change});
+
+ component.error.subscribe((err) => {
+ expect(err).toBeDefined();
+ done();
+ });
+ });
+
+ it('should return the filter task list', (done) => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(from(fakeGlobalFilterPromise));
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.filters).toBeDefined();
+ expect(component.filters.length).toEqual(3);
+ done();
+ });
+ });
+
+ it('should return the filter task list, filtered By Name', (done) => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(from(fakeGlobalFilterPromise));
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.filters).toBeDefined();
+ expect(component.filters[0].name).toEqual('FakeServiceTasks');
+ expect(component.filters[1].name).toEqual('FakeMyServiceTasks1');
+ expect(component.filters[2].name).toEqual('FakeMyServiceTasks2');
+ done();
+ });
+ });
+
+ it('should select the first filter as default', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+
+ fixture.detectChanges();
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.currentFilter).toBeDefined();
+ expect(component.currentFilter.name).toEqual('FakeServiceTasks');
+ });
+
+ }));
+
+ it('should select the task filter based on the input by name param', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+
+ component.filterParam = new FilterParamsModel({ name: 'FakeMyServiceTasks1' });
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+
+ fixture.detectChanges();
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.currentFilter).toBeDefined();
+ expect(component.currentFilter.name).toEqual('FakeMyServiceTasks1');
+ });
+
+ }));
+
+ it('should select the default task filter if filter input does not exist', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+
+ component.filterParam = new FilterParamsModel({ name: 'UnexistableFilter' });
+
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+
+ fixture.detectChanges();
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.currentFilter).toBeDefined();
+ expect(component.currentFilter.name).toEqual('FakeServiceTasks');
+ });
+
+ }));
+
+ it('should select the task filter based on the input by index param', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+
+ component.filterParam = new FilterParamsModel({ index: 2 });
+
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+
+ fixture.detectChanges();
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.currentFilter).toBeDefined();
+ expect(component.currentFilter.name).toEqual('FakeMyServiceTasks2');
+ });
+
+ }));
+
+ it('should select the task filter based on the input by id param', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+
+ component.filterParam = new FilterParamsModel({ id: 12 });
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+
+ fixture.detectChanges();
+ component.ngOnChanges({ 'appName': change });
+
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.currentFilter).toBeDefined();
+ expect(component.currentFilter.name).toEqual('FakeMyServiceTasks2');
+ });
+
+ }));
+
+ it('should emit an event when a filter is selected', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+
+ component.filterParam = new FilterParamsModel({ id: 12 });
+
+ const appName = 'my-app-1';
+ const change = new SimpleChange(null, appName, true);
+ component.ngOnChanges({ 'appName': change });
+
+ fixture.detectChanges();
+ spyOn(component, 'selectFilterAndEmit').and.stub();
+ const filterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${fakeGlobalServiceFilters[1].key}_filter"]`);
+
+ filterButton.click();
+ expect(component.selectFilterAndEmit).toHaveBeenCalledWith(fakeGlobalServiceFilters[1]);
+ }));
+
+ it('should reset the filter when the param is undefined', async(() => {
+ spyOn(serviceTaskFilterCloudService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
+ spyOn(component, 'selectFilterAndEmit');
+ component.currentFilter = null;
+
+ const filterName = undefined;
+ const change = new SimpleChange(null, filterName, false);
+ component.ngOnChanges({ 'filterParam': change });
+
+ fixture.detectChanges();
+ expect(component.selectFilterAndEmit).toHaveBeenCalledWith(undefined);
+ expect(component.currentFilter).toEqual(undefined);
+ }));
+
+ it('should reload filters by appName on binding changes', () => {
+ spyOn(component, 'getFilters').and.stub();
+ const appName = 'my-app-1';
+
+ const change = new SimpleChange(null, appName, true);
+ component.ngOnChanges({ 'appName': change });
+
+ expect(component.getFilters).toHaveBeenCalledWith(appName);
+ });
+
+ it('should change current filter when filterParam (name) changes', () => {
+ component.filters = fakeGlobalServiceFilters;
+ component.currentFilter = null;
+
+ const change = new SimpleChange(null, { name: fakeGlobalServiceFilters[1].name }, true);
+ component.ngOnChanges({ 'filterParam': change });
+
+ fixture.whenStable().then(() => {
+ expect(component.currentFilter.name).toEqual(fakeGlobalServiceFilters[1].name);
+ });
+ });
+
+ it('should change current filter when filterParam (key) changes', () => {
+ component.filters = fakeGlobalServiceFilters;
+ component.currentFilter = null;
+
+ const change = new SimpleChange(null, { key: fakeGlobalServiceFilters[2].key }, true);
+ component.ngOnChanges({ 'filterParam': change });
+
+ fixture.whenStable().then(() => {
+ expect(component.currentFilter.key).toEqual(fakeGlobalServiceFilters[2].key);
+ });
+ });
+
+ it('should change current filter when filterParam (index) changes', () => {
+ component.filters = fakeGlobalServiceFilters;
+ component.currentFilter = null;
+ const position = 1;
+
+ const change = new SimpleChange(null, { index: position }, true);
+ component.ngOnChanges({ 'filterParam': change });
+
+ fixture.whenStable().then(() => {
+ expect(component.currentFilter.name).toEqual(fakeGlobalServiceFilters[position].name);
+ });
+ });
+
+ it('should reload filters by app name on binding changes', () => {
+ spyOn(component, 'getFilters').and.stub();
+ const appName = 'fake-app-name';
+
+ const change = new SimpleChange(null, appName, true);
+ component.ngOnChanges({ 'appName': change });
+
+ expect(component.getFilters).toHaveBeenCalledWith(appName);
+ });
+
+ it('should return the current filter after one is selected', () => {
+ const filter = new FilterParamsModel({ name: 'FakeMyServiceTasks2' });
+ component.filters = fakeGlobalServiceFilters;
+
+ expect(component.currentFilter).toBeUndefined();
+ component.selectFilter(filter);
+ expect(component.currentFilter).toBe(fakeGlobalServiceFilters[2]);
+ });
+});
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts
new file mode 100644
index 0000000000..04a9a50b08
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts
@@ -0,0 +1,123 @@
+/*!
+ * @license
+ * Copyright 2019 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, OnChanges, Output, SimpleChanges, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { FilterParamsModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
+import { TranslationService } from '@alfresco/adf-core';
+import { takeUntil } from 'rxjs/operators';
+import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component';
+import { ServiceTaskFilterCloudService } from '../services/service-task-filter-cloud.service';
+
+@Component({
+ selector: 'adf-cloud-service-task-filters',
+ templateUrl: './base-task-filters-cloud.component.html',
+ styleUrls: ['base-task-filters-cloud.component.scss']
+})
+export class ServiceTaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent implements OnInit, OnChanges {
+
+ /** Emitted when a filter in the list is clicked. */
+ @Output()
+ filterClick: EventEmitter = new EventEmitter();
+
+ filters$: Observable;
+ filters: ServiceTaskFilterCloudModel[] = [];
+ currentFilter: ServiceTaskFilterCloudModel;
+
+ constructor(private serviceTaskFilterCloudService: ServiceTaskFilterCloudService,
+ private translationService: TranslationService) {
+ super();
+ }
+
+ ngOnInit() {
+ this.getFilters(this.appName);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const appName = changes['appName'];
+ const filter = changes['filterParam'];
+ if (appName && appName.currentValue !== appName.previousValue) {
+ this.getFilters(appName.currentValue);
+ } else if (filter && filter.currentValue !== filter.previousValue) {
+ this.selectFilterAndEmit(filter.currentValue);
+ }
+ }
+
+ /**
+ * Return the filter list filtered by appName
+ */
+ getFilters(appName: string) {
+ this.filters$ = this.serviceTaskFilterCloudService.getTaskListFilters(appName);
+
+ this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe(
+ (res: ServiceTaskFilterCloudModel[]) => {
+ this.resetFilter();
+ this.filters = Object.assign([], res);
+ this.selectFilterAndEmit(this.filterParam);
+ this.success.emit(res);
+ },
+ (err: any) => {
+ this.error.emit(err);
+ }
+ );
+ }
+
+ public selectFilter(paramFilter: FilterParamsModel) {
+ if (paramFilter) {
+ this.currentFilter = (this.filters as Array).find((filter: any, index) =>
+ paramFilter.index === index ||
+ paramFilter.key === filter.key ||
+ paramFilter.id === filter.id ||
+ (paramFilter.name &&
+ (paramFilter.name.toLocaleLowerCase() === this.translationService.instant(filter.name).toLocaleLowerCase())
+ ));
+ }
+ }
+
+ public selectFilterAndEmit(newParamFilter: FilterParamsModel) {
+ if (newParamFilter) {
+ this.selectFilter(newParamFilter);
+ this.filterClick.emit(this.currentFilter);
+ } else {
+ this.currentFilter = undefined;
+ }
+ }
+
+ /**
+ * Select as default task filter the first in the list
+ */
+ public selectDefaultTaskFilter() {
+ if (!this.isFilterListEmpty()) {
+ this.currentFilter = this.filters[0];
+ }
+ }
+
+ /**
+ * Check if the filter list is empty
+ */
+ isFilterListEmpty(): boolean {
+ return this.filters === undefined || (this.filters && this.filters.length === 0);
+ }
+
+ /**
+ * Reset the filters properties
+ */
+ private resetFilter() {
+ this.filters = [];
+ this.currentFilter = undefined;
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.html
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.html
rename to lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.html
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.scss
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.scss
rename to lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.scss
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.spec.ts
similarity index 96%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.spec.ts
rename to lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.spec.ts
index 0677bdafee..1e414b5a7e 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.spec.ts
@@ -18,9 +18,9 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
-import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
+import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { setupTestBed } from '@alfresco/adf-core';
-import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
+import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { TranslateModule } from '@ngx-translate/core';
describe('TaskFilterDialogCloudComponent', () => {
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.ts
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog-cloud.component.ts
rename to lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.ts
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts
index 5c31cf83ba..0362491151 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts
@@ -349,6 +349,6 @@ describe('TaskFiltersCloudComponent', () => {
expect(component.currentFilter).toBeUndefined();
component.selectFilter(filter);
- expect(component.getCurrentFilter()).toBe(fakeGlobalFilter[0]);
+ expect(component.currentFilter).toBe(fakeGlobalFilter[0]);
});
});
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts
index 97970c0a6c..d63d2a7819 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts
@@ -15,55 +15,31 @@
* limitations under the License.
*/
-import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit } from '@angular/core';
-import { Observable, Subject } from 'rxjs';
+import { Component, EventEmitter, OnChanges, Output, SimpleChanges, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFilterCloudModel, FilterParamsModel } from '../models/filter-cloud.model';
import { TranslationService } from '@alfresco/adf-core';
import { takeUntil } from 'rxjs/operators';
+import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component';
@Component({
selector: 'adf-cloud-task-filters',
- templateUrl: './task-filters-cloud.component.html',
- styleUrls: ['task-filters-cloud.component.scss']
+ templateUrl: './base-task-filters-cloud.component.html',
+ styleUrls: ['base-task-filters-cloud.component.scss']
})
-export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
- /** Display filters available to the current user for the application with the specified name. */
- @Input()
- appName: string = '';
-
- /**
- * Parameters to use for the task filter cloud. If there is no match then the default filter
- * (the first one in the list) is selected.
- */
- @Input()
- filterParam: FilterParamsModel;
-
- /** Toggles display of the filter's icons. */
- @Input()
- showIcons: boolean = false;
-
+export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent implements OnInit, OnChanges {
/** Emitted when a filter in the list is clicked. */
@Output()
filterClick: EventEmitter = new EventEmitter();
- /** Emitted when the list is loaded. */
- @Output()
- success: EventEmitter = new EventEmitter();
-
- /** Emitted when an error occurs during loading. */
- @Output()
- error: EventEmitter = new EventEmitter();
-
filters$: Observable;
-
+ filters: TaskFilterCloudModel[] = [];
currentFilter: TaskFilterCloudModel;
- filters: TaskFilterCloudModel [] = [];
-
- private onDestroy$ = new Subject();
-
- constructor(private taskFilterCloudService: TaskFilterCloudService, private translationService: TranslationService) {
+ constructor(private taskFilterCloudService: TaskFilterCloudService,
+ private translationService: TranslationService) {
+ super();
}
ngOnInit() {
@@ -80,11 +56,6 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
}
}
- ngOnDestroy() {
- this.onDestroy$.next(true);
- this.onDestroy$.complete();
- }
-
/**
* Return the filter list filtered by appName
*/
@@ -106,7 +77,7 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
public selectFilter(paramFilter: FilterParamsModel) {
if (paramFilter) {
- this.currentFilter = this.filters.find( (filter: TaskFilterCloudModel, index) =>
+ this.currentFilter = this.filters.find((filter: any, index) =>
paramFilter.index === index ||
paramFilter.key === filter.key ||
paramFilter.id === filter.id ||
@@ -134,13 +105,6 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
}
}
- /**
- * Return the current task
- */
- getCurrentFilter(): TaskFilterCloudModel {
- return this.currentFilter;
- }
-
/**
* Check if the filter list is empty
*/
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts b/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts
index b8a70bd993..84b074d78d 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-import { TaskFilterCloudModel } from '../models/filter-cloud.model';
+import { TaskFilterCloudModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
-export let fakeGlobalFilter = [
+export const fakeGlobalFilter = [
new TaskFilterCloudModel({
name: 'FakeInvolvedTasks',
key: 'fake-involved-tasks',
@@ -44,7 +44,31 @@ export let fakeGlobalFilter = [
})
];
-export let fakeFilter = new TaskFilterCloudModel({
+export const fakeGlobalServiceFilters = [
+ {
+ name: 'FakeServiceTasks',
+ key: 'fake-involved-tasks',
+ icon: 'adjust',
+ id: '10',
+ status: 'open'
+ } as ServiceTaskFilterCloudModel,
+ {
+ name: 'FakeMyServiceTasks1',
+ key: 'fake-my-tast1',
+ icon: 'done',
+ id: '11',
+ status: 'open'
+ } as ServiceTaskFilterCloudModel,
+ {
+ name: 'FakeMyServiceTasks2',
+ key: 'fake-my-tast2',
+ icon: 'inbox',
+ id: '12',
+ status: 'open'
+ } as ServiceTaskFilterCloudModel
+];
+
+export const fakeFilter = new TaskFilterCloudModel({
name: 'FakeInvolvedTasks',
icon: 'adjust',
id: 'mock-task-filter-id',
@@ -56,7 +80,19 @@ export let fakeFilter = new TaskFilterCloudModel({
sort: 'id'
});
-export let fakeAllTaskFilter = new TaskFilterCloudModel({
+export const fakeServiceFilter = {
+ name: 'FakeInvolvedTasks',
+ icon: 'adjust',
+ id: 'mock-task-filter-id',
+ status: 'COMPLETED',
+ appName: 'mock-app-name',
+ processDefinitionId: 'process-def-id',
+ activityName: 'fake-activity',
+ order: 'ASC',
+ sort: 'id'
+} as ServiceTaskFilterCloudModel;
+
+export const fakeAllTaskFilter = new TaskFilterCloudModel({
name: 'AllTasks',
icon: 'adjust',
id: 'mock-task-filter-id',
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts b/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts
index 6420149d0c..534da7a3c8 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts
@@ -19,7 +19,7 @@ import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
import { DateRangeFilterService } from '../../../common/date-range-filter/date-range-filter.service';
import { ComponentSelectionMode } from '../../../types';
-export class TaskFilterCloudModel {
+export class TaskFilterCloudModel {
id: string;
name: string;
key: string;
@@ -116,6 +116,38 @@ export class TaskFilterCloudModel {
return !!this.dateRangeFilterService.isDateRangeType(type);
}
}
+
+export interface ServiceTaskFilterCloudModel {
+ id?: string;
+ name?: string;
+ key?: string;
+ icon?: string;
+ index?: number;
+ appName?: string;
+ status?: string;
+ sort?: string;
+ order?: string;
+ activityName?: string;
+ activityType?: string;
+ completedDate?: Date;
+ elementId?: string;
+ executionId?: string;
+ processDefinitionId?: string;
+ processDefinitionKey?: string;
+ processDefinitionVersion?: number;
+ processInstanceId?: string;
+ serviceTaskId?: string;
+ serviceFullName?: string;
+ serviceName?: string;
+ serviceVersion?: string;
+ startedDate?: Date;
+}
+
+export enum TaskType {
+ UserTask = 'userTask',
+ ServiceTask = 'serviceTask'
+}
+
export class FilterParamsModel {
id?: string;
@@ -137,7 +169,7 @@ export class TaskFilterAction {
actionType: string;
icon: string;
tooltip: string;
- filter: TaskFilterCloudModel;
+ filter: TaskFilterCloudModel | ServiceTaskFilterCloudModel;
constructor(obj?: any) {
if (obj) {
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/public-api.ts b/lib/process-services-cloud/src/lib/task/task-filters/public-api.ts
index c19bbea7f8..0712740a68 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/public-api.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/public-api.ts
@@ -16,7 +16,8 @@
*/
export * from './components/task-filters-cloud.component';
-export * from './components/edit-task-filter-cloud.component';
+export * from './components/edit-task-filters/edit-task-filter-cloud.component';
+export * from './components/edit-task-filters/edit-service-task-filter-cloud.component';
export * from './models/filter-cloud.model';
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/service-task-filter-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/service-task-filter-cloud.service.ts
new file mode 100644
index 0000000000..71433cb029
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-filters/services/service-task-filter-cloud.service.ts
@@ -0,0 +1,295 @@
+/*!
+ * @license
+ * Copyright 2019 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 { IdentityUserService } from '@alfresco/adf-core';
+import { Injectable, Inject } from '@angular/core';
+import { Observable, of, BehaviorSubject } from 'rxjs';
+import { ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
+import { switchMap, map } from 'rxjs/operators';
+import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
+import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ServiceTaskFilterCloudService {
+ private filtersSubject: BehaviorSubject;
+ filters$: Observable;
+
+ constructor(
+ private identityUserService: IdentityUserService,
+ @Inject(TASK_FILTERS_SERVICE_TOKEN)
+ public preferenceService: PreferenceCloudServiceInterface
+ ) {
+ this.filtersSubject = new BehaviorSubject([]);
+ this.filters$ = this.filtersSubject.asObservable();
+ }
+
+ /**
+ * Creates and returns the default task filters for an app.
+ * @param appName Name of the target app
+ * @returns Observable of default filters task filters just created or created filters
+ */
+ private createDefaultFilters(appName: string) {
+ const key: string = this.prepareKey(appName);
+ this.preferenceService.getPreferences(appName, key).pipe(
+ switchMap((response: any) => {
+ const preferences = (response && response.list && response.list.entries) ? response.list.entries : [];
+ if (!this.hasPreferences(preferences) || !this.hasTaskFilters(preferences, key)) {
+ return this.createTaskFilters(appName, key, this.defaultServiceTaskFilters(appName));
+ } else {
+ return of(this.findFiltersByKeyInPreferences(preferences, key));
+ }
+ })
+ ).subscribe((filters) => {
+ this.addFiltersToStream(filters);
+ });
+ }
+
+ /**
+ * Checks user preference are empty or not
+ * @param preferences User preferences of the target app
+ * @returns Boolean value if the preferences are not empty
+ */
+ private hasPreferences(preferences: any): boolean {
+ return preferences && preferences.length > 0;
+ }
+
+ /**
+ * Checks for task filters in given user preferences
+ * @param preferences User preferences of the target app
+ * @param key Key of the task filters
+ * @param filters Details of create filter
+ * @returns Boolean value if the preference has task filters
+ */
+ private hasTaskFilters(preferences: any, key: string): boolean {
+ const filters = preferences.find((filter: any) => { return filter.entry.key === key; });
+ return (filters && filters.entry) ? JSON.parse(filters.entry.value).length > 0 : false;
+ }
+
+ /**
+ * Calls create preference api to create task filters
+ * @param appName Name of the target app
+ * @param key Key of the task instance filters
+ * @param filters Details of new task filter
+ * @returns Observable of created task filters
+ */
+ private createTaskFilters(appName: string, key: string, filters: ServiceTaskFilterCloudModel[]): Observable {
+ return this.preferenceService.createPreference(appName, key, filters);
+ }
+
+ /**
+ * Calls get preference api to get task filter by preference key
+ * @param appName Name of the target app
+ * @param key Key of the task filters
+ * @returns Observable of task filters
+ */
+ private getTaskFiltersByKey(appName: string, key: string): Observable {
+ return this.preferenceService.getPreferenceByKey(appName, key);
+ }
+
+ /**
+ * Gets all task filters for a task app.
+ * @param appName Name of the target app
+ * @returns Observable of task filter details
+ */
+ getTaskListFilters(appName?: string): Observable {
+ this.createDefaultFilters(appName);
+ return this.filters$;
+ }
+
+ /**
+ * Gets a task filter.
+ * @param appName Name of the target app
+ * @param id ID of the task
+ * @returns Details of the task filter
+ */
+ getTaskFilterById(appName: string, id: string): Observable {
+ const key: string = this.prepareKey(appName);
+ return this.getTaskFiltersByKey(appName, key).pipe(
+ switchMap((filters: ServiceTaskFilterCloudModel[]) => {
+ if (filters && filters.length === 0) {
+ return this.createTaskFilters(appName, key, this.defaultServiceTaskFilters(appName));
+ } else {
+ return of(filters);
+ }
+ }),
+ map((filters: any) => {
+ return filters.filter((filter: ServiceTaskFilterCloudModel) => {
+ return filter.id === id;
+ })[0];
+ })
+ );
+ }
+
+ /**
+ * Adds a new task filter.
+ * @param filter The new filter to add
+ * @returns Observable of task instance filters with newly added filter
+ */
+ addFilter(newFilter: ServiceTaskFilterCloudModel): Observable {
+ const key: string = this.prepareKey(newFilter.appName);
+ return this.getTaskFiltersByKey(newFilter.appName, key).pipe(
+ switchMap((filters: ServiceTaskFilterCloudModel[]) => {
+ if (filters && filters.length === 0) {
+ return this.createTaskFilters(newFilter.appName, key, [newFilter]);
+ } else {
+ filters.push(newFilter);
+ return this.preferenceService.updatePreference(newFilter.appName, key, filters);
+ }
+ }),
+ map((filters: ServiceTaskFilterCloudModel[]) => {
+ this.addFiltersToStream(filters);
+ return filters;
+ })
+ );
+ }
+
+ private addFiltersToStream(filters: ServiceTaskFilterCloudModel[]) {
+ this.filtersSubject.next(filters);
+ }
+
+ /**
+ * Updates a task filter.
+ * @param filter The filter to update
+ * @returns Observable of task instance filters with updated filter
+ */
+ updateFilter(updatedFilter: ServiceTaskFilterCloudModel): Observable {
+ const key: string = this.prepareKey(updatedFilter.appName);
+ return this.getTaskFiltersByKey(updatedFilter.appName, key).pipe(
+ switchMap((filters: ServiceTaskFilterCloudModel[]) => {
+ if (filters && filters.length === 0) {
+ return this.createTaskFilters(updatedFilter.appName, key, [updatedFilter]);
+ } else {
+ const itemIndex = filters.findIndex((filter: ServiceTaskFilterCloudModel) => filter.id === updatedFilter.id);
+ filters[itemIndex] = updatedFilter;
+ return this.updateTaskFilters(updatedFilter.appName, key, filters);
+ }
+ }),
+ map((updatedFilters: ServiceTaskFilterCloudModel[]) => {
+ this.addFiltersToStream(updatedFilters);
+ return updatedFilters;
+ })
+ );
+ }
+
+ /**
+ * Deletes a task filter
+ * @param filter The filter to delete
+ * @returns Observable of task instance filters without deleted filter
+ */
+ deleteFilter(deletedFilter: ServiceTaskFilterCloudModel): Observable {
+ const key = this.prepareKey(deletedFilter.appName);
+ return this.getTaskFiltersByKey(deletedFilter.appName, key).pipe(
+ switchMap((filters: ServiceTaskFilterCloudModel[]) => {
+ if (filters && filters.length > 0) {
+ filters = filters.filter(filter => filter.id !== deletedFilter.id);
+ return this.updateTaskFilters(deletedFilter.appName, key, filters);
+ }
+ return of([]);
+ }),
+ map(filters => {
+ this.addFiltersToStream(filters);
+ return filters;
+ })
+ );
+ }
+
+ /**
+ * Checks if given filter is a default filter
+ * @param filterName Name of the target task filter
+ * @returns Boolean value for whether the filter is a default filter
+ */
+ isDefaultFilter(filterName: string): boolean {
+ const defaultFilters = this.defaultServiceTaskFilters();
+ return defaultFilters.findIndex((filter) => filterName === filter.name) !== -1;
+ }
+
+ /**
+ * Calls update preference api to update task filter
+ * @param appName Name of the target app
+ * @param key Key of the task filters
+ * @param filters Details of update filter
+ * @returns Observable of updated task filters
+ */
+ private updateTaskFilters(appName: string, key: string, filters: ServiceTaskFilterCloudModel[]): Observable {
+ return this.preferenceService.updatePreference(appName, key, filters);
+ }
+
+ /**
+ * Creates a uniq key with appName and username
+ * @param appName Name of the target app
+ * @returns String of task filters preference key
+ */
+ private prepareKey(appName: string): string {
+ return `task-filters-${appName}-${this.identityUserService.getCurrentUserInfo().username}`;
+ }
+
+ /**
+ * Finds and returns the task filters from preferences
+ * @param appName Name of the target app
+ * @returns Array of TaskFilterCloudModel
+ */
+ private findFiltersByKeyInPreferences(preferences: any, key: string): ServiceTaskFilterCloudModel[] {
+ const result = preferences.find((filter: any) => { return filter.entry.key === key; });
+ return result && result.entry ? JSON.parse(result.entry.value) : [];
+ }
+
+ /**
+ * Creates and returns the default filters for a task app.
+ * @param appName Name of the target app
+ * @returns Array of TaskFilterCloudModel
+ */
+ private defaultServiceTaskFilters(appName?: string): ServiceTaskFilterCloudModel[] {
+ return [
+ {
+ id: this.generateRandomId(),
+ name: 'ADF_CLOUD_SERVICE_TASK_FILTERS.ALL_SERVICE_TASKS',
+ key: 'my-service-tasks',
+ icon: 'inbox',
+ appName,
+ status: 'ALL',
+ sort: 'startedDate',
+ order: 'DESC'
+ } as ServiceTaskFilterCloudModel,
+ {
+ id: this.generateRandomId(),
+ name: 'ADF_CLOUD_SERVICE_TASK_FILTERS.COMPLETED_TASKS',
+ key: 'completed-tasks',
+ icon: 'done',
+ appName,
+ status: 'COMPLETED',
+ sort: 'completedDate',
+ order: 'DESC'
+ } as ServiceTaskFilterCloudModel,
+ {
+ id: this.generateRandomId(),
+ name: 'ADF_CLOUD_SERVICE_TASK_FILTERS.ERRORED_TASKS',
+ key: 'errored-service-tasks',
+ icon: 'error',
+ appName,
+ status: 'ERROR',
+ sort: 'startedDate',
+ order: 'DESC'
+ } as ServiceTaskFilterCloudModel
+ ];
+ }
+
+ generateRandomId(): string {
+ return Math.random().toString(36).substr(2, 9);
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts
index c4bf692ab0..72d5a9fcd2 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts
@@ -38,6 +38,7 @@ import {
import { UserPreferenceCloudService } from '../../../services/user-preference-cloud.service';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TaskFilterCloudModel } from '../models/filter-cloud.model';
describe('TaskFilterCloudService', () => {
let service: TaskFilterCloudService;
@@ -257,7 +258,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
it('should create default task filters if there are no task filter preferences', (done) => {
const appName = 'fakeAppName';
- service.getTaskListFilters(appName).subscribe((res) => {
+ service.getTaskListFilters(appName).subscribe((res: TaskFilterCloudModel[]) => {
expect(res.length).toEqual(3);
expect(res[0].name).toEqual('ADF_CLOUD_TASK_FILTERS.MY_TASKS');
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts
index e680365539..da2ed40748 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts
@@ -17,9 +17,9 @@
import { IdentityUserService } from '@alfresco/adf-core';
import { Injectable, Inject } from '@angular/core';
-import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
+import { Observable, of, BehaviorSubject } from 'rxjs';
import { TaskFilterCloudModel } from '../models/filter-cloud.model';
-import { switchMap, map, catchError } from 'rxjs/operators';
+import { switchMap, map } from 'rxjs/operators';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
@@ -49,15 +49,12 @@ export class TaskFilterCloudService {
this.preferenceService.getPreferences(appName, key).pipe(
switchMap((response: any) => {
const preferences = (response && response.list && response.list.entries) ? response.list.entries : [];
- if (!this.hasPreferences(preferences)) {
- return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
- } else if (!this.hasTaskFilters(preferences, key)) {
+ if (!this.hasPreferences(preferences) || !this.hasTaskFilters(preferences, key)) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else {
return of(this.findFiltersByKeyInPreferences(preferences, key));
}
- }),
- catchError((err) => this.handleTaskError(err))
+ })
).subscribe((filters) => {
this.addFiltersToStream(filters);
});
@@ -131,13 +128,12 @@ export class TaskFilterCloudService {
return of(filters);
}
}),
- map((filters: TaskFilterCloudModel[]) => {
+ map((filters: any) => {
return filters.filter((filter: TaskFilterCloudModel) => {
return filter.id === id;
})[0];
- }),
- catchError((err) => this.handleTaskError(err))
- );
+ })
+ );
}
/**
@@ -148,9 +144,9 @@ export class TaskFilterCloudService {
addFilter(newFilter: TaskFilterCloudModel): Observable {
const key: string = this.prepareKey(newFilter.appName);
return this.getTaskFiltersByKey(newFilter.appName, key).pipe(
- switchMap((filters: TaskFilterCloudModel[]) => {
+ switchMap((filters: any) => {
if (filters && filters.length === 0) {
- return this.createTaskFilters(newFilter.appName, key, [newFilter]);
+ return this.createTaskFilters(newFilter.appName, key, [newFilter]);
} else {
filters.push(newFilter);
return this.preferenceService.updatePreference(newFilter.appName, key, filters);
@@ -159,8 +155,7 @@ export class TaskFilterCloudService {
map((filters: TaskFilterCloudModel[]) => {
this.addFiltersToStream(filters);
return filters;
- }),
- catchError((err) => this.handleTaskError(err))
+ })
);
}
@@ -176,9 +171,9 @@ export class TaskFilterCloudService {
updateFilter(updatedFilter: TaskFilterCloudModel): Observable {
const key: string = this.prepareKey(updatedFilter.appName);
return this.getTaskFiltersByKey(updatedFilter.appName, key).pipe(
- switchMap((filters: any) => {
+ switchMap((filters: TaskFilterCloudModel[]) => {
if (filters && filters.length === 0) {
- return this.createTaskFilters(updatedFilter.appName, key, [updatedFilter]);
+ return this.createTaskFilters(updatedFilter.appName, key, [updatedFilter]);
} else {
const itemIndex = filters.findIndex((filter: TaskFilterCloudModel) => filter.id === updatedFilter.id);
filters[itemIndex] = updatedFilter;
@@ -188,8 +183,7 @@ export class TaskFilterCloudService {
map((updatedFilters: TaskFilterCloudModel[]) => {
this.addFiltersToStream(updatedFilters);
return updatedFilters;
- }),
- catchError((err) => this.handleTaskError(err))
+ })
);
}
@@ -201,7 +195,7 @@ export class TaskFilterCloudService {
deleteFilter(deletedFilter: TaskFilterCloudModel): Observable {
const key = this.prepareKey(deletedFilter.appName);
return this.getTaskFiltersByKey(deletedFilter.appName, key).pipe(
- switchMap(filters => {
+ switchMap((filters: TaskFilterCloudModel[]) => {
if (filters && filters.length > 0) {
filters = filters.filter(filter => filter.id !== deletedFilter.id);
return this.updateTaskFilters(deletedFilter.appName, key, filters);
@@ -211,8 +205,7 @@ export class TaskFilterCloudService {
map(filters => {
this.addFiltersToStream(filters);
return filters;
- }),
- catchError((err) => this.handleTaskError(err))
+ })
);
}
@@ -237,22 +230,13 @@ export class TaskFilterCloudService {
return this.preferenceService.updatePreference(appName, key, filters);
}
- /**
- * Gets the username field from the access token.
- * @returns Username string
- */
- getUsername(): string {
- const user = this.identityUserService.getCurrentUserInfo();
- return user.username;
- }
-
/**
* Creates a uniq key with appName and username
* @param appName Name of the target app
* @returns String of task filters preference key
*/
private prepareKey(appName: string): string {
- return `task-filters-${appName}-${this.getUsername()}`;
+ return `task-filters-${appName}-${this.identityUserService.getCurrentUserInfo().username}`;
}
/**
@@ -265,10 +249,6 @@ export class TaskFilterCloudService {
return result && result.entry ? JSON.parse(result.entry.value) : [];
}
- private handleTaskError(error: any) {
- return throwError(error || 'Server error');
- }
-
/**
* Creates and returns the default filters for a task app.
* @param appName Name of the target app
@@ -282,7 +262,7 @@ export class TaskFilterCloudService {
icon: 'inbox',
appName,
status: 'ASSIGNED',
- assignee: this.getUsername(),
+ assignee: this.identityUserService.getCurrentUserInfo().username,
sort: 'createdDate',
order: 'DESC'
}),
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts
index 8016a51076..9d189b2d8a 100644
--- a/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts
+++ b/lib/process-services-cloud/src/lib/task/task-filters/task-filters-cloud.module.ts
@@ -23,12 +23,14 @@ import { TaskFiltersCloudComponent } from './components/task-filters-cloud.compo
import { MaterialModule } from '../../material.module';
import { CoreModule, MomentDateAdapter, MOMENT_DATE_FORMATS } from '@alfresco/adf-core';
import { HttpClientModule } from '@angular/common/http';
-import { EditTaskFilterCloudComponent } from './components/edit-task-filter-cloud.component';
-import { TaskFilterDialogCloudComponent } from './components/task-filter-dialog-cloud.component';
import { AppListCloudModule } from './../../app/app-list-cloud.module';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { ProcessCommonModule } from '../../common/process-common.module';
import { PeopleCloudModule } from '../../people/people-cloud.module';
+import { EditServiceTaskFilterCloudComponent } from './components/edit-task-filters/edit-service-task-filter-cloud.component';
+import { EditTaskFilterCloudComponent } from './components/edit-task-filters/edit-task-filter-cloud.component';
+import { TaskFilterDialogCloudComponent } from './components/task-filter-dialog/task-filter-dialog-cloud.component';
+import { ServiceTaskFiltersCloudComponent } from './components/service-task-filters-cloud.component';
@NgModule({
imports: [
@@ -43,8 +45,19 @@ import { PeopleCloudModule } from '../../people/people-cloud.module';
ProcessCommonModule,
PeopleCloudModule
],
- declarations: [TaskFiltersCloudComponent, EditTaskFilterCloudComponent, TaskFilterDialogCloudComponent],
- exports: [TaskFiltersCloudComponent, EditTaskFilterCloudComponent],
+ declarations: [
+ TaskFiltersCloudComponent,
+ ServiceTaskFiltersCloudComponent,
+ EditTaskFilterCloudComponent,
+ EditServiceTaskFilterCloudComponent,
+ TaskFilterDialogCloudComponent
+ ],
+ exports: [
+ TaskFiltersCloudComponent,
+ ServiceTaskFiltersCloudComponent,
+ EditTaskFilterCloudComponent,
+ EditServiceTaskFilterCloudComponent
+ ],
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.html
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.html
rename to lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.html
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.scss
similarity index 100%
rename from lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.scss
rename to lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.scss
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts
new file mode 100644
index 0000000000..9bd9680a3f
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts
@@ -0,0 +1,256 @@
+/*!
+ * @license
+ * Copyright 2019 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 { OnChanges, Input, SimpleChanges, Output, EventEmitter, ContentChild, AfterContentInit, OnDestroy, OnInit, Directive } from '@angular/core';
+import {
+ AppConfigService, UserPreferencesService,
+ DataTableSchema, UserPreferenceValues,
+ PaginatedComponent, PaginationModel,
+ DataRowEvent, CustomEmptyContentTemplateDirective, DataCellEvent, DataRowActionEvent
+} from '@alfresco/adf-core';
+import { taskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model';
+import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model';
+import { BehaviorSubject, Subject } from 'rxjs';
+import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
+import { takeUntil } from 'rxjs/operators';
+
+@Directive()
+// tslint:disable-next-line: directive-class-suffix
+export abstract class BaseTaskListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy, OnInit {
+
+ static ENTRY_PREFIX = 'entry.';
+
+ @ContentChild(CustomEmptyContentTemplateDirective)
+ emptyCustomContent: CustomEmptyContentTemplateDirective;
+
+ /** The name of the application. */
+ @Input()
+ appName: string = '';
+
+ /**
+ * Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode,
+ * you can use the Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for
+ * multiple rows.
+ */
+ @Input()
+ selectionMode: string = 'single'; // none|single|multiple
+
+ /** Toggles multiple row selection, rendering a checkbox at the beginning of each row. */
+ @Input()
+ multiselect: boolean = false;
+
+ /** Toggles the sticky header mode. */
+ @Input()
+ stickyHeader: boolean = false;
+
+ /**
+ * Specifies how the table should be sorted. The parameters are for BE sorting.
+ */
+ @Input()
+ sorting: TaskListCloudSortingModel[];
+
+ /** Toggles the data actions column. */
+ @Input()
+ showActions: boolean = false;
+
+ /** Position of the actions dropdown menu. Can be "left" or "right". */
+ @Input()
+ actionsPosition: string = 'right'; // left|right
+
+ /** Toggles custom context menu for the component. */
+ @Input()
+ showContextMenu: boolean = false;
+
+ /** Emitted before the context menu is displayed for a row. */
+ @Output()
+ showRowContextMenu = new EventEmitter();
+
+ /** Emitted before the actions menu is displayed for a row. */
+ @Output()
+ showRowActionsMenu = new EventEmitter();
+
+ /** Emitted when the user executes a row action. */
+ @Output()
+ executeRowAction = new EventEmitter();
+
+ /** Emitted when a task in the list is clicked */
+ @Output()
+ rowClick: EventEmitter = new EventEmitter();
+
+ /** Emitted when rows are selected/unselected */
+ @Output()
+ rowsSelected: EventEmitter = new EventEmitter();
+
+ /** Emitted when the task list is loaded */
+ @Output()
+ success: EventEmitter = new EventEmitter();
+
+ /** Emitted when an error occurs. */
+ @Output()
+ error: EventEmitter = new EventEmitter();
+
+ pagination: BehaviorSubject;
+
+ requestNode: TaskQueryCloudRequestModel;
+ rows: any[] = [];
+ size: number;
+ skipCount: number = 0;
+ currentInstanceId: any;
+ isLoading = true;
+ selectedInstances: any[];
+ formattedSorting: any[];
+ private defaultSorting = { key: 'startDate', direction: 'desc' };
+
+ private onDestroy$ = new Subject();
+
+ constructor(appConfigService: AppConfigService,
+ private userPreferences: UserPreferencesService,
+ presetKey: string) {
+ super(appConfigService, presetKey, taskPresetsCloudDefaultModel);
+ this.size = userPreferences.paginationSize;
+
+ this.pagination = new BehaviorSubject( {
+ maxItems: this.size,
+ skipCount: 0,
+ totalItems: 0
+ });
+
+ }
+
+ ngOnInit() {
+ this.userPreferences
+ .select(UserPreferenceValues.PaginationSize)
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe(pageSize => this.size = pageSize);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (changes['sorting']) {
+ this.formatSorting(changes['sorting'].currentValue);
+ }
+ this.reload();
+ }
+
+ ngOnDestroy() {
+ this.onDestroy$.next(true);
+ this.onDestroy$.complete();
+ }
+
+ ngAfterContentInit() {
+ this.createDatatableSchema();
+ }
+
+ reload() {
+ this.requestNode = this.createRequestNode();
+ if (this.requestNode.appName || this.requestNode.appName === '') {
+ this.load(this.requestNode);
+ } else {
+ this.rows = [];
+ }
+ }
+
+ isListEmpty(): boolean {
+ return !this.rows || this.rows.length === 0;
+ }
+
+ /**
+ * Resets the pagination values
+ */
+ resetPagination() {
+ this.skipCount = 0;
+ this.size = this.userPreferences.paginationSize;
+ this.pagination.next({
+ skipCount: 0,
+ maxItems: this.size
+ });
+ }
+
+ /**
+ * Resets the pagination values and
+ * Reloads the task list
+ * @param pagination Pagination values to be set
+ */
+ updatePagination(pagination: PaginationModel) {
+ this.size = pagination.maxItems;
+ this.skipCount = pagination.skipCount;
+ this.pagination.next(pagination);
+ this.reload();
+ }
+
+ onSortingChanged(event: CustomEvent) {
+ this.setSorting(event.detail);
+ this.formatSorting(this.sorting);
+ this.reload();
+ }
+
+ onRowClick(item: DataRowEvent) {
+ this.currentInstanceId = item.value.getValue('entry.id');
+ this.rowClick.emit(this.currentInstanceId);
+ }
+
+ onRowSelect(event: CustomEvent) {
+ this.selectedInstances = [...event.detail.selection];
+ this.rowsSelected.emit(this.selectedInstances);
+ }
+
+ onRowUnselect(event: CustomEvent) {
+ this.selectedInstances = [...event.detail.selection];
+ this.rowsSelected.emit(this.selectedInstances);
+ }
+
+ onRowKeyUp(event: CustomEvent) {
+ if (event.detail.keyboardEvent.key === 'Enter') {
+ event.preventDefault();
+ this.currentInstanceId = event.detail.row.getValue('entry.id');
+ this.rowClick.emit(this.currentInstanceId);
+ }
+ }
+
+ onShowRowActionsMenu(event: DataCellEvent) {
+ this.showRowActionsMenu.emit(event);
+ }
+
+ onShowRowContextMenu(event: DataCellEvent) {
+ this.showRowContextMenu.emit(event);
+ }
+
+ onExecuteRowAction(row: DataRowActionEvent) {
+ this.executeRowAction.emit(row);
+ }
+
+ setSorting(sortDetail) {
+ const sorting = sortDetail ? {
+ orderBy: sortDetail.key.replace(BaseTaskListCloudComponent.ENTRY_PREFIX, ''),
+ direction: sortDetail.direction.toUpperCase()
+ } : { ... this.defaultSorting };
+ this.sorting = [new TaskListCloudSortingModel(sorting)];
+ }
+
+ formatSorting(sorting: TaskListCloudSortingModel[]) {
+ this.formattedSorting = this.isValidSorting(sorting) ? [
+ BaseTaskListCloudComponent.ENTRY_PREFIX + sorting[0].orderBy,
+ sorting[0].direction.toLocaleLowerCase()
+ ] : null;
+ }
+
+ isValidSorting(sorting: TaskListCloudSortingModel[]) {
+ return sorting.length && sorting[0].orderBy && sorting[0].direction;
+ }
+
+ abstract load(requestNode);
+ abstract createRequestNode();
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts
new file mode 100644
index 0000000000..6398fbca62
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts
@@ -0,0 +1,524 @@
+/*!
+ * @license
+ * Copyright 2019 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, SimpleChange, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed, async } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
+import { TaskListCloudService } from '../services/task-list-cloud.service';
+import { ServiceTaskListCloudComponent } from './service-task-list-cloud.component';
+import { fakeServiceTask, fakeCustomSchema } from '../mock/fake-task-response.mock';
+import { of } from 'rxjs';
+import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
+import { Person } from '@alfresco/js-api';
+import { TranslateModule } from '@ngx-translate/core';
+import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
+import { skip } from 'rxjs/operators';
+
+@Component({
+ template: `
+
+
+
+
+
+ `
+})
+class CustomTaskListComponent {
+ @ViewChild(ServiceTaskListCloudComponent)
+ taskList: ServiceTaskListCloudComponent;
+
+ getFullName(person: Person): string {
+ return `${person.firstName} ${person.lastName}`;
+ }
+}
+@Component({
+ template: `
+
+
+
+
+
+ `
+})
+class EmptyTemplateComponent {
+}
+@Component({
+ template: `
+
+
+
+
+
+ `
+})
+class CustomCopyContentTaskListComponent {
+ @ViewChild(ServiceTaskListCloudComponent, { static: true })
+ taskList: ServiceTaskListCloudComponent;
+}
+
+describe('ServiceTaskListCloudComponent', () => {
+ let component: ServiceTaskListCloudComponent;
+ let fixture: ComponentFixture;
+ let appConfig: AppConfigService;
+ let taskListCloudService: TaskListCloudService;
+
+ setupTestBed({
+ imports: [
+ TranslateModule.forRoot(),
+ ProcessServiceCloudTestingModule
+ ],
+ declarations: [
+ EmptyTemplateComponent
+ ]
+ });
+
+ beforeEach(() => {
+ appConfig = TestBed.inject(AppConfigService);
+ taskListCloudService = TestBed.inject(TaskListCloudService);
+ fixture = TestBed.createComponent(ServiceTaskListCloudComponent);
+ component = fixture.componentInstance;
+ appConfig.config = Object.assign(appConfig.config, {
+ 'adf-cloud-service-task-list': {
+ 'presets': {
+ 'fakeCustomSchema': [
+ {
+ 'key': 'fakeName',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.FAKE',
+ 'sortable': true
+ },
+ {
+ 'key': 'fakeTaskName',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.TASK_FAKE',
+ 'sortable': true
+ }
+ ]
+ }
+ }
+ });
+ });
+
+ afterEach(() => {
+ fixture.destroy();
+ });
+
+ it('should use the default schemaColumn as default', () => {
+ component.ngAfterContentInit();
+ expect(component.columns).toBeDefined();
+ expect(component.columns.length).toEqual(3);
+ });
+
+ it('should display empty content when process list is empty', () => {
+ const emptyList = { list: { entries: [] } };
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(emptyList));
+
+ fixture.detectChanges();
+ expect(component.isLoading).toBe(true);
+ let loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
+ expect(loadingContent.nativeElement).toBeDefined();
+
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ component.ngOnChanges({ appName });
+ fixture.detectChanges();
+
+ loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
+ expect(loadingContent).toBeFalsy();
+
+ const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
+ expect(emptyContent.nativeElement).toBeDefined();
+ });
+
+ it('should load spinner and show the content', () => {
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+
+ fixture.detectChanges();
+ expect(component.isLoading).toBe(true);
+ let loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
+ expect(loadingContent.nativeElement).toBeDefined();
+
+ component.ngOnChanges({ appName });
+ fixture.detectChanges();
+
+ expect(component.isLoading).toBe(false);
+ loadingContent = fixture.debugElement.query(By.css('mat-progress-spinner'));
+ expect(loadingContent).toBeFalsy();
+
+ const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
+ expect(emptyContent).toBeFalsy();
+
+ expect(component.rows.length).toEqual(1);
+ });
+
+ it('should use the custom schemaColumn from app.config.json', () => {
+ component.presetColumn = 'fakeCustomSchema';
+ component.ngAfterContentInit();
+ fixture.detectChanges();
+ expect(component.columns).toEqual(fakeCustomSchema);
+ });
+
+ it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
+ component.presetColumn = 'fakeCustomSchema';
+ fixture.detectChanges();
+ expect(component.columns).toBeDefined();
+ expect(component.columns.length).toEqual(2);
+ });
+
+ it('should return an empty task list when no input parameters are passed', () => {
+ component.ngAfterContentInit();
+ expect(component.rows).toBeDefined();
+ expect(component.isListEmpty()).toBeTruthy();
+ });
+
+ it('should return the results if an application name is given', (done) => {
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.rows).toBeDefined();
+ expect(component.isListEmpty()).not.toBeTruthy();
+ expect(component.rows.length).toEqual(1);
+ expect(component.rows[0].entry['appName']).toBe('simpleapp');
+ expect(component.rows[0].entry['activityType']).toBe('serviceTask');
+ expect(component.rows[0].entry['id']).toBe('04fdf69f-4ddd-48ab-9563-da776c9b163c');
+ expect(component.rows[0].entry['elementId']).toBe('ServiceTask_0lszm0x');
+ expect(component.rows[0].entry['executionId']).toBe('2023b099-fced-11ea-b116-62141048995a');
+ expect(component.rows[0].entry['startedDate']).toBe('2020-09-22T16:03:37.444+0000');
+ expect(component.rows[0].entry['completedDate']).toBe('2020-09-22T16:03:37.482+0000');
+ expect(component.rows[0].entry['processDefinitionVersion']).toBe(1);
+ expect(component.rows[0].entry['processDefinitionId']).toBe('Process_24rkVVSR:1:0db78dcd-fc14-11ea-bce0-62141048995a');
+ expect(component.rows[0].entry['processInstanceId']).toBe('2023b097-fced-11ea-b116-62141048995a');
+ expect(component.rows[0].entry['status']).toBe('COMPLETED');
+ expect(component.rows[0].entry['serviceFullName']).toBe('simpleapp-rb');
+ expect(component.rows[0].entry['serviceName']).toBe('simpleapp-rb');
+ done();
+ });
+ component.appName = appName.currentValue;
+ component.ngOnChanges({ 'appName': appName });
+ fixture.detectChanges();
+ });
+
+ it('should reload tasks when reload() is called', (done) => {
+ component.appName = 'fake';
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ component.success.subscribe((res) => {
+ expect(res).toBeDefined();
+ expect(component.rows).toBeDefined();
+ expect(component.isListEmpty()).not.toBeTruthy();
+ done();
+ });
+ fixture.detectChanges();
+ component.reload();
+ });
+
+ it('should emit row click event', (done) => {
+ const row = new ObjectDataRow({
+ entry: {
+ id: '999'
+ }
+ });
+ const rowEvent = new DataRowEvent(row, null);
+ component.rowClick.subscribe((taskId) => {
+ expect(taskId).toEqual('999');
+ expect(component.currentInstanceId).toEqual('999');
+ done();
+ });
+ component.onRowClick(rowEvent);
+ });
+
+ describe('component changes', () => {
+
+ beforeEach(() => {
+ component.rows = fakeServiceTask.list.entries;
+ fixture.detectChanges();
+ });
+
+ it('should NOT reload the task list when no parameters changed', () => {
+ spyOn(taskListCloudService, 'getTaskByRequest');
+ component.rows = null;
+ fixture.detectChanges();
+ expect(component.isListEmpty()).toBeTruthy();
+ });
+
+ it('should reload the task list when input parameters changed', () => {
+ const getServiceTaskByRequestSpy = spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ component.appName = 'mock-app-name';
+ component.queryParams.status = 'mock-status';
+ const queryParams = new SimpleChange(undefined, { status: 'mock-status' }, true);
+ component.ngOnChanges({
+ 'queryParams': queryParams
+ });
+ fixture.detectChanges();
+ expect(component.isListEmpty()).toBeFalsy();
+ expect(getServiceTaskByRequestSpy).toHaveBeenCalled();
+ });
+
+ it('should set formattedSorting if sorting input changes', () => {
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ spyOn(component, 'formatSorting').and.callThrough();
+
+ component.appName = 'mock-app-name';
+ const mockSort = [
+ new TaskListCloudSortingModel({
+ orderBy: 'startDate',
+ direction: 'DESC'
+ })
+ ];
+ const sortChange = new SimpleChange(undefined, mockSort, true);
+ component.ngOnChanges({
+ 'sorting': sortChange
+ });
+ fixture.detectChanges();
+ expect(component.formatSorting).toHaveBeenCalledWith(mockSort);
+ expect(component.formattedSorting).toEqual([ServiceTaskListCloudComponent.ENTRY_PREFIX + 'startDate', 'desc']);
+ });
+
+ it('should reload task list when sorting on a column changes', () => {
+ const getServiceTaskByRequestSpy = spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ component.onSortingChanged(new CustomEvent('sorting-changed', {
+ detail: {
+ key: 'fakeName',
+ direction: 'asc'
+ },
+ bubbles: true
+ }));
+ fixture.detectChanges();
+ expect(component.sorting).toEqual([
+ new TaskListCloudSortingModel({
+ orderBy: 'fakeName',
+ direction: 'ASC'
+ })
+ ]);
+ expect(component.formattedSorting).toEqual(['entry.fakeName', 'asc']);
+ expect(component.isListEmpty()).toBeFalsy();
+ expect(getServiceTaskByRequestSpy).toHaveBeenCalled();
+ });
+
+ it('should reset pagination when resetPaginationValues is called', async (done) => {
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ component.ngOnChanges({ appName });
+ fixture.detectChanges();
+
+ const size = component.size;
+ const skipCount = component.skipCount;
+ component.pagination.pipe(skip(3))
+ .subscribe((updatedPagination) => {
+ fixture.detectChanges();
+ expect(component.size).toBe(size);
+ expect(component.skipCount).toBe(skipCount);
+ expect(updatedPagination.maxItems).toEqual(size);
+ expect(updatedPagination.skipCount).toEqual(skipCount);
+ done();
+ });
+
+ const pagination = {
+ maxItems: 250,
+ skipCount: 200
+ };
+ component.updatePagination(pagination);
+ await fixture.whenStable();
+ component.resetPagination();
+ });
+
+ it('should set pagination and reload when updatePagination is called', (done) => {
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ spyOn(component, 'reload').and.stub();
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ component.ngOnChanges({ appName });
+ fixture.detectChanges();
+
+ const pagination = {
+ maxItems: 250,
+ skipCount: 200
+ };
+ component.pagination.pipe(skip(1))
+ .subscribe((updatedPagination) => {
+ fixture.detectChanges();
+ expect(component.size).toBe(pagination.maxItems);
+ expect(component.skipCount).toBe(pagination.skipCount);
+ expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
+ expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
+ done();
+ });
+
+ component.updatePagination(pagination);
+ });
+ });
+
+ describe('Injecting custom colums for tasklist - CustomTaskListComponent', () => {
+ let fixtureCustom: ComponentFixture;
+ let componentCustom: CustomTaskListComponent;
+ let customCopyComponent: CustomCopyContentTaskListComponent;
+ let element: any;
+ let copyFixture: ComponentFixture;
+
+ setupTestBed({
+ imports: [
+ TranslateModule.forRoot(),
+ ProcessServiceCloudTestingModule
+ ],
+ declarations: [
+ CustomTaskListComponent,
+ CustomCopyContentTaskListComponent
+ ]
+ });
+
+ beforeEach(() => {
+ spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+ fixtureCustom = TestBed.createComponent(CustomTaskListComponent);
+ copyFixture = TestBed.createComponent(CustomCopyContentTaskListComponent);
+ fixtureCustom.detectChanges();
+ componentCustom = fixtureCustom.componentInstance;
+ customCopyComponent = copyFixture.componentInstance;
+ element = copyFixture.debugElement.nativeElement;
+ });
+
+ afterEach(() => {
+ fixtureCustom.destroy();
+ copyFixture.destroy();
+ });
+
+ it('should fetch custom schemaColumn from html', () => {
+ fixture.detectChanges();
+ expect(componentCustom.taskList.columnList).toBeDefined();
+ expect(componentCustom.taskList.columns[0]['title']).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.NAME');
+ expect(componentCustom.taskList.columns[1]['title']).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED');
+ expect(componentCustom.taskList.columns.length).toEqual(2);
+ });
+
+ it('it should show copy tooltip when key is present in data-colunn', async(() => {
+ copyFixture.detectChanges();
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ copyFixture.whenStable().then(() => {
+ copyFixture.detectChanges();
+ const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]');
+ spanHTMLElement.dispatchEvent(new Event('mouseenter'));
+ copyFixture.detectChanges();
+ expect(copyFixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
+ });
+ customCopyComponent.taskList.appName = appName.currentValue;
+ customCopyComponent.taskList.ngOnChanges({ 'appName': appName });
+ copyFixture.detectChanges();
+ }));
+
+ it('it should not show copy tooltip when key is not present in data-column', (done) => {
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ customCopyComponent.taskList.success.subscribe(() => {
+ copyFixture.whenStable().then(() => {
+ copyFixture.detectChanges();
+ const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="serviceTaskName"]');
+ spanHTMLElement.dispatchEvent(new Event('mouseenter'));
+ copyFixture.detectChanges();
+ expect(copyFixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
+ done();
+ });
+ });
+ customCopyComponent.taskList.appName = appName.currentValue;
+ customCopyComponent.taskList.ngOnChanges({ 'appName': appName });
+ copyFixture.detectChanges();
+ });
+ });
+
+ describe('Copy cell content directive from app.config specifications', () => {
+
+ let element: any;
+ let taskSpy: jasmine.Spy;
+
+ setupTestBed({
+ imports: [
+ TranslateModule.forRoot(),
+ ProcessServiceCloudTestingModule
+ ]
+ });
+
+ beforeEach(() => {
+ appConfig = TestBed.inject(AppConfigService);
+ taskListCloudService = TestBed.inject(TaskListCloudService);
+ appConfig.config = Object.assign(appConfig.config, {
+ 'adf-cloud-service-task-list': {
+ 'presets': {
+ 'fakeCustomSchema': [
+ {
+ 'key': 'entry.id',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.FAKE',
+ 'sortable': true,
+ 'copyContent': true
+ },
+ {
+ 'key': 'entry.activityName',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_TASK_LIST.PROPERTIES.TASK_FAKE',
+ 'sortable': true
+ }
+ ]
+ }
+ }
+ });
+ fixture = TestBed.createComponent(ServiceTaskListCloudComponent);
+ component = fixture.componentInstance;
+ element = fixture.debugElement.nativeElement;
+ taskSpy = spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
+
+ });
+ afterEach(() => {
+ fixture.destroy();
+ });
+
+ it('shoud show tooltip if config copyContent flag is true', async(() => {
+ taskSpy.and.returnValue(of(fakeServiceTask));
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+
+ component.success.subscribe(() => {
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]');
+ spanHTMLElement.dispatchEvent(new Event('mouseenter'));
+ fixture.detectChanges();
+ expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
+ });
+ });
+
+ component.presetColumn = 'fakeCustomSchema';
+ component.appName = appName.currentValue;
+ component.ngOnChanges({ 'appName': appName });
+ component.ngAfterContentInit();
+ }));
+
+ it('shoud not show tooltip if config copyContent flag is true', async(() => {
+ taskSpy.and.returnValue(of(fakeServiceTask));
+ const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
+ component.success.subscribe(() => {
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="serviceTaskName"]');
+ spanHTMLElement.dispatchEvent(new Event('mouseenter'));
+ fixture.detectChanges();
+ expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
+ });
+ });
+ component.presetColumn = 'fakeCustomSchema';
+ component.appName = appName.currentValue;
+ component.ngOnChanges({ 'appName': appName });
+ component.ngAfterContentInit();
+ }));
+ });
+});
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts
new file mode 100644
index 0000000000..d944162088
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts
@@ -0,0 +1,69 @@
+/*!
+ * @license
+ * Copyright 2019 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, ViewEncapsulation, Input } from '@angular/core';
+import {
+ AppConfigService, UserPreferencesService
+} from '@alfresco/adf-core';
+import { ServiceTaskQueryCloudRequestModel } from '../models/filter-cloud-model';
+import { TaskListCloudService } from '../services/task-list-cloud.service';
+import { BaseTaskListCloudComponent } from './base-task-list-cloud.component';
+
+@Component({
+ selector: 'adf-cloud-service-task-list',
+ templateUrl: './base-task-list-cloud.component.html',
+ styleUrls: ['./base-task-list-cloud.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent {
+
+ static PRESET_KEY = 'adf-cloud-service-task-list.presets';
+
+ @Input()
+ queryParams: { [key: string]: string } = {};
+
+ constructor(private taskListCloudService: TaskListCloudService,
+ appConfigService: AppConfigService,
+ userPreferences: UserPreferencesService) {
+ super(appConfigService, userPreferences, ServiceTaskListCloudComponent.PRESET_KEY);
+ }
+
+ load(requestNode: ServiceTaskQueryCloudRequestModel) {
+ this.isLoading = true;
+ this.taskListCloudService.getServiceTaskByRequest(requestNode).subscribe(
+ (tasks) => {
+ this.rows = tasks.list.entries;
+ this.success.emit(tasks);
+ this.isLoading = false;
+ this.pagination.next(tasks.list.pagination);
+ }, (error) => {
+ this.error.emit(error);
+ this.isLoading = false;
+ });
+ }
+
+ createRequestNode(): ServiceTaskQueryCloudRequestModel {
+ const requestNode = {
+ ...this.queryParams,
+ appName: this.appName,
+ maxItems: this.size,
+ skipCount: this.skipCount,
+ sorting: this.sorting
+ };
+ return requestNode;
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts
index 655f0a261f..5cd91b4a9f 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts
@@ -50,7 +50,7 @@ class CustomTaskListComponent {
getFullName(person: Person): string {
return `${person.firstName} ${person.lastName}`;
}
- }
+}
@Component({
template: `
@@ -130,7 +130,7 @@ describe('TaskListCloudComponent', () => {
});
it('should display empty content when process list is empty', () => {
- const emptyList = {list: {entries: []}};
+ const emptyList = { list: { entries: [] } };
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
fixture.detectChanges();
@@ -248,7 +248,7 @@ describe('TaskListCloudComponent', () => {
const rowEvent = new DataRowEvent(row, null);
component.rowClick.subscribe((taskId) => {
expect(taskId).toEqual('999');
- expect(component.getCurrentId()).toEqual('999');
+ expect(component.currentInstanceId).toEqual('999');
done();
});
component.onRowClick(rowEvent);
@@ -262,8 +262,8 @@ describe('TaskListCloudComponent', () => {
});
it('should NOT reload the task list when no parameters changed', () => {
+ spyOn(taskListCloudService, 'getTaskByRequest');
component.rows = null;
- component.ngOnChanges({});
fixture.detectChanges();
expect(component.isListEmpty()).toBeTruthy();
});
@@ -341,14 +341,14 @@ describe('TaskListCloudComponent', () => {
const size = component.size;
const skipCount = component.skipCount;
component.pagination.pipe(skip(3))
- .subscribe((updatedPagination) => {
+ .subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount);
done();
- });
+ });
const pagination = {
maxItems: 250,
@@ -371,14 +371,14 @@ describe('TaskListCloudComponent', () => {
skipCount: 200
};
component.pagination.pipe(skip(1))
- .subscribe((updatedPagination) => {
+ .subscribe((updatedPagination) => {
fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done();
- });
+ });
component.updatePagination(pagination);
});
@@ -442,7 +442,7 @@ describe('TaskListCloudComponent', () => {
it('it should not show copy tooltip when key is not present in data-column', (done) => {
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
- customCopyComponent.taskList.success.subscribe( () => {
+ customCopyComponent.taskList.success.subscribe(() => {
copyFixture.whenStable().then(() => {
copyFixture.detectChanges();
const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="standalone-subtask"]');
@@ -456,13 +456,13 @@ describe('TaskListCloudComponent', () => {
customCopyComponent.taskList.ngOnChanges({ 'appName': appName });
copyFixture.detectChanges();
});
- });
+ });
describe('Creating an empty custom template - EmptyTemplateComponent', () => {
let fixtureEmpty: ComponentFixture;
beforeEach(() => {
- const emptyList = {list: {entries: []}};
+ const emptyList = { list: { entries: [] } };
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
fixtureEmpty = TestBed.createComponent(EmptyTemplateComponent);
@@ -498,7 +498,7 @@ describe('TaskListCloudComponent', () => {
]
});
- beforeEach( () => {
+ beforeEach(() => {
appConfig = TestBed.inject(AppConfigService);
taskListCloudService = TestBed.inject(TaskListCloudService);
appConfig.config = Object.assign(appConfig.config, {
@@ -536,7 +536,7 @@ describe('TaskListCloudComponent', () => {
taskSpy.and.returnValue(of(fakeGlobalTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
- component.success.subscribe( () => {
+ component.success.subscribe(() => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]');
@@ -555,7 +555,7 @@ describe('TaskListCloudComponent', () => {
it('shoud not show tooltip if config copyContent flag is true', async(() => {
taskSpy.and.returnValue(of(fakeGlobalTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
- component.success.subscribe( () => {
+ component.success.subscribe(() => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const spanHTMLElement: HTMLInputElement = element.querySelector('span[title="standalone-subtask"]');
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts
index 215c890acb..d064ca3abe 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts
@@ -15,36 +15,21 @@
* limitations under the License.
*/
-import { Component, ViewEncapsulation, OnChanges, Input, SimpleChanges, Output, EventEmitter, ContentChild, AfterContentInit, OnDestroy, OnInit } from '@angular/core';
-import { AppConfigService, UserPreferencesService,
- DataTableSchema, UserPreferenceValues,
- PaginatedComponent, PaginationModel,
- DataRowEvent, CustomEmptyContentTemplateDirective, DataCellEvent, DataRowActionEvent } from '@alfresco/adf-core';
-import { taskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model';
+import { Component, ViewEncapsulation, Input } from '@angular/core';
+import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core';
import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model';
-import { BehaviorSubject, Subject } from 'rxjs';
import { TaskListCloudService } from '../services/task-list-cloud.service';
-import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
-import { takeUntil } from 'rxjs/operators';
+import { BaseTaskListCloudComponent } from './base-task-list-cloud.component';
@Component({
- selector: 'adf-cloud-task-list',
- templateUrl: './task-list-cloud.component.html',
- styleUrls: ['./task-list-cloud.component.scss'],
- encapsulation: ViewEncapsulation.None
+ selector: 'adf-cloud-task-list',
+ templateUrl: './base-task-list-cloud.component.html',
+ styleUrls: ['./base-task-list-cloud.component.scss'],
+ encapsulation: ViewEncapsulation.None
})
-
-export class TaskListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy, OnInit {
+export class TaskListCloudComponent extends BaseTaskListCloudComponent {
static PRESET_KEY = 'adf-cloud-task-list.presets';
- static ENTRY_PREFIX = 'entry.';
-
- @ContentChild(CustomEmptyContentTemplateDirective)
- emptyCustomContent: CustomEmptyContentTemplateDirective;
-
- /** The name of the application. */
- @Input()
- appName: string = '';
/**
* The assignee of the process. Possible values are: "assignee" (the current user is the assignee),
@@ -70,7 +55,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
@Input()
lastModifiedTo: string = '';
- /** Filter the tasks. Display only tasks with dueDate greater or equal than the supplied date. */
+ /** Filter the tasks. Display only tasks with dueDate greater or equal than the supplied date. */
@Input()
dueDateFrom: string = '';
@@ -122,150 +107,15 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
@Input()
standalone: boolean = false;
- /**
- * Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode,
- * you can use the Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for
- * multiple rows.
- */
- @Input()
- selectionMode: string = 'single'; // none|single|multiple
-
- /** Toggles multiple row selection, rendering a checkbox at the beginning of each row. */
- @Input()
- multiselect: boolean = false;
-
- /** Toggles the sticky header mode. */
- @Input()
- stickyHeader: boolean = false;
-
- /**
- * Specifies how the table should be sorted. The parameters are for BE sorting.
- */
- @Input()
- sorting: TaskListCloudSortingModel[];
-
- /** Toggles the data actions column. */
- @Input()
- showActions: boolean = false;
-
- /** Position of the actions dropdown menu. Can be "left" or "right". */
- @Input()
- actionsPosition: string = 'right'; // left|right
-
- /** Toggles custom context menu for the component. */
- @Input()
- showContextMenu: boolean = false;
-
- /** Emitted before the context menu is displayed for a row. */
- @Output()
- showRowContextMenu = new EventEmitter();
-
- /** Emitted before the actions menu is displayed for a row. */
- @Output()
- showRowActionsMenu = new EventEmitter();
-
- /** Emitted when the user executes a row action. */
- @Output()
- executeRowAction = new EventEmitter();
-
- /** Emitted when a task in the list is clicked */
- @Output()
- rowClick: EventEmitter = new EventEmitter();
-
- /** Emitted when rows are selected/unselected */
- @Output()
- rowsSelected: EventEmitter = new EventEmitter();
-
- /** Emitted when the task list is loaded */
- @Output()
- success: EventEmitter = new EventEmitter();
-
- /** Emitted when an error occurs. */
- @Output()
- error: EventEmitter = new EventEmitter();
-
- pagination: BehaviorSubject;
-
- requestNode: TaskQueryCloudRequestModel;
- rows: any[] = [];
- size: number;
- skipCount: number = 0;
- currentInstanceId: any;
- isLoading = true;
- selectedInstances: any[];
- formattedSorting: any[];
- private defaultSorting = { key: 'startDate', direction: 'desc' };
-
- private onDestroy$ = new Subject();
-
constructor(private taskListCloudService: TaskListCloudService,
appConfigService: AppConfigService,
- private userPreferences: UserPreferencesService) {
- super(appConfigService, TaskListCloudComponent.PRESET_KEY, taskPresetsCloudDefaultModel);
- this.size = userPreferences.paginationSize;
-
- this.pagination = new BehaviorSubject( {
- maxItems: this.size,
- skipCount: 0,
- totalItems: 0
- });
-
+ userPreferences: UserPreferencesService) {
+ super(appConfigService, userPreferences, TaskListCloudComponent.PRESET_KEY);
}
- ngOnInit() {
- this.userPreferences
- .select(UserPreferenceValues.PaginationSize)
- .pipe(takeUntil(this.onDestroy$))
- .subscribe(pageSize => this.size = pageSize);
- }
-
- ngOnChanges(changes: SimpleChanges) {
- if (this.isPropertyChanged(changes, 'sorting')) {
- this.formatSorting(changes['sorting'].currentValue);
- }
- if (this.isAnyPropertyChanged(changes)) {
- this.reload();
- }
- }
-
- ngOnDestroy() {
- this.onDestroy$.next(true);
- this.onDestroy$.complete();
- }
-
- ngAfterContentInit() {
- this.createDatatableSchema();
- }
-
- getCurrentId(): string {
- return this.currentInstanceId;
- }
-
- private isAnyPropertyChanged(changes: SimpleChanges): boolean {
- for (const property in changes) {
- if (this.isPropertyChanged(changes, property)) {
- return true;
- }
- }
- return false;
- }
-
- private isPropertyChanged(changes: SimpleChanges, property: string): boolean {
- return changes.hasOwnProperty(property);
- }
-
- reload() {
- this.requestNode = this.createRequestNode();
- if (this.requestNode.appName || this.requestNode.appName === '') {
- this.load(this.requestNode);
- } else {
- this.rows = [];
- }
- }
-
- private load(requestNode: TaskQueryCloudRequestModel) {
+ load(requestNode: TaskQueryCloudRequestModel) {
this.isLoading = true;
- this.taskListCloudService.getTaskByRequest(requestNode).subscribe(
+ this.taskListCloudService.getTaskByRequest( requestNode).subscribe(
(tasks) => {
this.rows = tasks.list.entries;
this.success.emit(tasks);
@@ -277,77 +127,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
});
}
- isListEmpty(): boolean {
- return !this.rows || this.rows.length === 0;
- }
-
- /**
- * Resets the pagination values
- */
- resetPagination() {
- this.skipCount = 0;
- this.size = this.userPreferences.paginationSize;
- this.pagination.next({
- skipCount: 0,
- maxItems: this.size
- });
- }
-
- /**
- * Resets the pagination values and
- * Reloads the task list
- * @param pagination Pagination values to be set
- */
- updatePagination(pagination: PaginationModel) {
- this.size = pagination.maxItems;
- this.skipCount = pagination.skipCount;
- this.pagination.next(pagination);
- this.reload();
- }
-
- onSortingChanged(event: CustomEvent) {
- this.setSorting(event.detail);
- this.formatSorting(this.sorting);
- this.reload();
- }
-
- onRowClick(item: DataRowEvent) {
- this.currentInstanceId = item.value.getValue('entry.id');
- this.rowClick.emit(this.currentInstanceId);
- }
-
- onRowSelect(event: CustomEvent) {
- this.selectedInstances = [...event.detail.selection];
- this.rowsSelected.emit(this.selectedInstances);
- }
-
- onRowUnselect(event: CustomEvent) {
- this.selectedInstances = [...event.detail.selection];
- this.rowsSelected.emit(this.selectedInstances);
- }
-
- onRowKeyUp(event: CustomEvent) {
- if (event.detail.keyboardEvent.key === 'Enter') {
- event.preventDefault();
- this.currentInstanceId = event.detail.row.getValue('entry.id');
- this.rowClick.emit(this.currentInstanceId);
- }
- }
-
- onShowRowActionsMenu(event: DataCellEvent) {
- this.showRowActionsMenu.emit(event);
- }
-
- onShowRowContextMenu(event: DataCellEvent) {
- this.showRowContextMenu.emit(event);
- }
-
- onExecuteRowAction(row: DataRowActionEvent) {
- this.executeRowAction.emit(row);
- }
-
- private createRequestNode() {
-
+ createRequestNode(): TaskQueryCloudRequestModel {
const requestNode = {
appName: this.appName,
assignee: this.assignee,
@@ -374,23 +154,4 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
};
return new TaskQueryCloudRequestModel(requestNode);
}
-
- setSorting(sortDetail) {
- const sorting = sortDetail ? {
- orderBy: sortDetail.key.replace(TaskListCloudComponent.ENTRY_PREFIX, ''),
- direction: sortDetail.direction.toUpperCase()
- } : { ... this.defaultSorting };
- this.sorting = [new TaskListCloudSortingModel(sorting)];
- }
-
- formatSorting(sorting: TaskListCloudSortingModel[]) {
- this.formattedSorting = this.isValidSorting(sorting) ? [
- TaskListCloudComponent.ENTRY_PREFIX + sorting[0].orderBy,
- sorting[0].direction.toLocaleLowerCase()
- ] : null;
- }
-
- isValidSorting(sorting: TaskListCloudSortingModel[]) {
- return sorting.length && sorting[0].orderBy && sorting[0].direction;
- }
}
diff --git a/lib/process-services-cloud/src/lib/task/task-list/mock/fake-task-response.mock.ts b/lib/process-services-cloud/src/lib/task/task-list/mock/fake-task-response.mock.ts
index 217591cecb..50e93a6b35 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/mock/fake-task-response.mock.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/mock/fake-task-response.mock.ts
@@ -79,7 +79,7 @@ export const fakeTaskCloudList = {
}
};
-export let fakeGlobalTask = {
+export const fakeGlobalTask = {
list: {
entries: [
{
@@ -117,7 +117,41 @@ export let fakeGlobalTask = {
}
};
-export let fakeCustomSchema =
+export const fakeServiceTask = {
+ list: {
+ entries: [
+ {
+ entry: {
+ activityType: 'serviceTask',
+ activityName: 'serviceTaskName',
+ appName: 'simpleapp',
+ completedDate: '2020-09-22T16:03:37.482+0000',
+ elementId: 'ServiceTask_0lszm0x',
+ executionId: '2023b099-fced-11ea-b116-62141048995a',
+ id: '04fdf69f-4ddd-48ab-9563-da776c9b163c',
+ processDefinitionId: 'Process_24rkVVSR:1:0db78dcd-fc14-11ea-bce0-62141048995a',
+ processDefinitionKey: 'Process_24rkVVSR',
+ processDefinitionVersion: 1,
+ processInstanceId: '2023b097-fced-11ea-b116-62141048995a',
+ serviceFullName: 'simpleapp-rb',
+ serviceName: 'simpleapp-rb',
+ serviceVersion: '',
+ startedDate: '2020-09-22T16:03:37.444+0000',
+ status: 'COMPLETED'
+ }
+ }
+ ],
+ pagination: {
+ skipCount: 0,
+ maxItems: 100,
+ count: 1,
+ hasMoreItems: false,
+ totalItems: 1
+ }
+ }
+};
+
+export const fakeCustomSchema =
[
new ObjectDataColumn({
'key': 'fakeName',
diff --git a/lib/process-services-cloud/src/lib/task/task-list/models/filter-cloud-model.ts b/lib/process-services-cloud/src/lib/task/task-list/models/filter-cloud-model.ts
index 876d589bf8..f099a0ff77 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/models/filter-cloud-model.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/models/filter-cloud-model.ts
@@ -74,3 +74,26 @@ export class TaskQueryCloudRequestModel {
}
}
}
+
+export interface ServiceTaskQueryCloudRequestModel {
+ appName: string;
+ appVersion?: string;
+ id?: string;
+ status?: string;
+ maxItems: number;
+ skipCount: number;
+ sorting?: TaskListCloudSortingModel[];
+ activityName?: string;
+ activityType?: string;
+ completedDate?: Date;
+ elementId?: string;
+ executionId?: string;
+ processDefinitionId?: string;
+ processDefinitionKey?: string;
+ processDefinitionVersion?: number;
+ processInstanceId?: string;
+ serviceFullName?: string;
+ serviceName?: string;
+ serviceVersion?: string;
+ startedDate?: Date;
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-list/models/task-preset-cloud.model.ts b/lib/process-services-cloud/src/lib/task/task-list/models/task-preset-cloud.model.ts
index 48f0f7babd..d67964e93b 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/models/task-preset-cloud.model.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/models/task-preset-cloud.model.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-export let taskPresetsCloudDefaultModel = {
+export const taskPresetsCloudDefaultModel = {
'default': [
{
'key': 'entry.name',
@@ -39,3 +39,34 @@ export let taskPresetsCloudDefaultModel = {
}
]
};
+
+export const serviceTaskPresetsCloudDefaultModel = {
+ 'default': [
+ {
+ 'key': 'entry.activityName',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.ACTIVITY_NAME',
+ 'sortable': true
+ },
+ {
+ 'key': 'entry.status',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.STATUS',
+ 'sortable': true
+ },
+ {
+ 'key': 'entry.startedDate',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.STARTED_DATE',
+ 'cssClass': 'hidden',
+ 'sortable': true
+ },
+ {
+ 'key': 'entry.completedDate',
+ 'type': 'text',
+ 'title': 'ADF_CLOUD_SERVICE_TASK_LIST.PROPERTIES.COMPLETED_DATE',
+ 'cssClass': 'hidden',
+ 'sortable': true
+ }
+ ]
+};
diff --git a/lib/process-services-cloud/src/lib/task/task-list/public-api.ts b/lib/process-services-cloud/src/lib/task/task-list/public-api.ts
index cda7578d63..7f17dc60c8 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/public-api.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/public-api.ts
@@ -16,6 +16,7 @@
*/
export * from './components/task-list-cloud.component';
+export * from './components/service-task-list-cloud.component';
export * from './models/filter-cloud-model';
export * from './models/task-list-sorting.model';
diff --git a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts
index 809431fc56..a64351ab0b 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts
@@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
-import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model';
+import { TaskQueryCloudRequestModel, ServiceTaskQueryCloudRequestModel } from '../models/filter-cloud-model';
import { Observable, throwError } from 'rxjs';
import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
import { BaseCloudService } from '../../../services/base-cloud.service';
@@ -51,6 +51,26 @@ export class TaskListCloudService extends BaseCloudService {
}
}
+ /**
+ * Finds a task using an object with optional query properties.
+ * @param requestNode Query object
+ * @returns Task information
+ */
+ getServiceTaskByRequest(requestNode: ServiceTaskQueryCloudRequestModel): Observable {
+ if (requestNode.appName || requestNode.appName === '') {
+ const queryUrl = `${this.getBasePath(requestNode.appName)}/query/admin/v1/service-tasks`;
+ const queryParams = this.buildQueryParams(requestNode);
+ const sortingParams = this.buildSortingParam(requestNode.sorting);
+ if (sortingParams) {
+ queryParams['sort'] = sortingParams;
+ }
+ return this.get(queryUrl, queryParams);
+ } else {
+ this.logService.error('Appname is mandatory for querying task');
+ return throwError('Appname not configured');
+ }
+ }
+
private buildQueryParams(requestNode: TaskQueryCloudRequestModel): Object {
const queryParam: Object = {};
for (const property in requestNode) {
diff --git a/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts b/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts
index 439921c4a6..376207690e 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/task-list-cloud.module.ts
@@ -19,6 +19,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MaterialModule } from '../../material.module';
import { TaskListCloudComponent } from './components/task-list-cloud.component';
+import { ServiceTaskListCloudComponent } from './components/service-task-list-cloud.component';
import { CoreModule } from '@alfresco/adf-core';
@NgModule({
@@ -27,7 +28,13 @@ import { CoreModule } from '@alfresco/adf-core';
MaterialModule,
CoreModule
],
- declarations: [TaskListCloudComponent],
- exports: [TaskListCloudComponent]
+ declarations: [
+ TaskListCloudComponent,
+ ServiceTaskListCloudComponent
+ ],
+ exports: [
+ TaskListCloudComponent,
+ ServiceTaskListCloudComponent
+ ]
})
export class TaskListCloudModule { }