[AAE-3469] New Service Task List in Task List Cloud Component (#6131)

* [AAE-3469] New Service Task Lisst Component

* Fix unit tests

* Add documentation

* Add public methods to Data Table Schema
This commit is contained in:
davidcanonieto 2020-09-22 21:41:53 +02:00 committed by GitHub
parent 7bead426ce
commit 472e112b71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 869 additions and 137 deletions

View File

@ -105,7 +105,8 @@
"PEOPLE_CLOUD": "People Cloud Component", "PEOPLE_CLOUD": "People Cloud Component",
"GROUPS_CLOUD": "Groups Cloud Component", "GROUPS_CLOUD": "Groups Cloud Component",
"CONFIRM-DIALOG": "Confirmation Dialog", "CONFIRM-DIALOG": "Confirmation Dialog",
"COMMUNITY": "Community" "COMMUNITY": "Community",
"SERVICE_TASK_LIST": "Service Task List"
}, },
"TRASHCAN": { "TRASHCAN": {
"ACTIONS": { "ACTIONS": {

View File

@ -969,6 +969,35 @@
"delete" "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": { "adf-edit-process-filter": {
"filterProperties": [ "filterProperties": [
"status", "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": { "adf-cloud-process-list": {
"presets": { "presets": {
"default": [ "default": [

View File

@ -65,6 +65,7 @@ import { TasksCloudDemoComponent } from './components/cloud/tasks-cloud-demo.com
import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component'; import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component';
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component'; import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { TaskHeaderCloudDemoComponent } from './components/cloud/task-header-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 { CloudViewerComponent } from './components/cloud/cloud-viewer.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component'; import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component'; import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component';
@ -189,7 +190,8 @@ registerLocaleData(localeSv);
FormCloudDemoComponent, FormCloudDemoComponent,
ConfirmDialogExampleComponent, ConfirmDialogExampleComponent,
SampleWidgetComponent, SampleWidgetComponent,
ProcessCloudLayoutComponent ProcessCloudLayoutComponent,
ServiceTaskListCloudDemoComponent
], ],
providers: [ providers: [
{ {

View File

@ -54,6 +54,7 @@ import { DemoErrorComponent } from './components/error/demo-error.component';
import { TaskHeaderCloudDemoComponent } from './components/cloud/task-header-cloud-demo.component'; import { TaskHeaderCloudDemoComponent } from './components/cloud/task-header-cloud-demo.component';
import { FilteredSearchComponent } from './components/files/filtered-search.component'; import { FilteredSearchComponent } from './components/files/filtered-search.component';
import { ProcessCloudLayoutComponent } from './components/cloud/process-cloud-layout.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 = [ export const appRoutes: Routes = [
{ path: 'login', loadChildren: () => import('./components/login/login.module').then(m => m.AppLoginModule) }, { path: 'login', loadChildren: () => import('./components/login/login.module').then(m => m.AppLoginModule) },
@ -176,22 +177,31 @@ export const appRoutes: Routes = [
{ {
path: 'cloud', path: 'cloud',
canActivate: [AuthGuardSsoRoleService], canActivate: [AuthGuardSsoRoleService],
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' }, data: { roles: ['ACTIVITI_ADMIN', 'ACTIVITI_USER'], redirectUrl: '/error/403' },
children: [ children: [
{ {
path: '', path: '',
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: AppsCloudDemoComponent component: AppsCloudDemoComponent
}, },
{ {
path: 'people-group-cloud', path: 'people-group-cloud',
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: PeopleGroupCloudDemoComponent component: PeopleGroupCloudDemoComponent
}, },
{ {
path: 'task-header-cloud', path: 'task-header-cloud',
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: TaskHeaderCloudDemoComponent component: TaskHeaderCloudDemoComponent
}, },
{
path: 'service-task-list',
data: { roles: ['ACTIVITI_ADMIN'], redirectUrl: '/error/403' },
component: ServiceTaskListCloudDemoComponent
},
{ {
path: 'community', path: 'community',
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
loadChildren: () => import('./components/cloud/community/community.module').then(m => m.AppCommunityModule) loadChildren: () => import('./components/cloud/community/community.module').then(m => m.AppCommunityModule)
}, },
{ {

View File

@ -55,7 +55,8 @@ export class AppLayoutComponent implements OnInit, OnDestroy {
{ href: '/cloud/community', icon: 'cloud', title: 'APP_LAYOUT.COMMUNITY' }, { href: '/cloud/community', icon: 'cloud', title: 'APP_LAYOUT.COMMUNITY' },
{ href: '/form-cloud', icon: 'poll', title: 'APP_LAYOUT.FORM' }, { href: '/form-cloud', icon: 'poll', title: 'APP_LAYOUT.FORM' },
{ href: '/cloud/people-group-cloud', icon: 'group', title: 'APP_LAYOUT.PEOPLE_GROUPS_CLOUD' }, { 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: [ { href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES', children: [

View File

@ -0,0 +1,32 @@
<div fxLayout="column" fxFill fxLayoutGap="2px">
<adf-cloud-edit-task-filter
[id]="'myFilter'"
[taskType]="'serviceTask'"
[filterProperties]="taskFilterProperties.filterProperties"
[sortProperties]="taskFilterProperties.sortProperties"
[actions]="taskFilterProperties.actions"
(filterChange)="onFilterChange($event)">
</adf-cloud-edit-task-filter>
<div fxLayout="column" fxFlex fxLayoutAlign="space-between" *ngIf="editedFilter">
<adf-cloud-task-list #taskCloud
fxFlex
[taskType]="'serviceTask'"
[queryParams]="editedFilter"
class="app-cloud-layout-overflow"
[appName]="editedFilter.appName"
[sorting]="sortArray"
[multiselect]="multiselect"
[selectionMode]="selectionMode"
[stickyHeader]="true"
[showActions]="actionMenu"
[showContextMenu]="contextMenu"
#taskCloud>
</adf-cloud-task-list>
<adf-pagination
[target]="taskCloud"
(changePageSize)="onChangePageSize($event)"
(nextPage)="resetSelectedRows()"
(prevPage)="resetSelectedRows()">
</adf-pagination>
</div>
</div>

View File

@ -0,0 +1,98 @@
/*!
* @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 } from '@angular/core';
import { TaskListCloudSortingModel, TaskFilterCloudModel } from '@alfresco/adf-process-services-cloud';
import { UserPreferencesService, AppConfigService } from '@alfresco/adf-core';
import { CloudLayoutService } 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';
isFilterLoaded = false;
selectedRow: any;
sortArray: TaskListCloudSortingModel[];
editedFilter: TaskFilterCloudModel;
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<boolean>();
constructor(
private cloudLayoutService: CloudLayoutService,
private userPreference: UserPreferencesService,
private appConfig: AppConfigService) {
const properties = this.appConfig.get<Array<any>>(ServiceTaskListCloudDemoComponent.TASK_FILTER_PROPERTY_KEYS);
if (properties) {
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) {
if (settings) {
this.multiselect = settings.multiselect;
this.selectionMode = settings.selectionMode;
this.actionMenu = settings.actionMenu;
this.contextMenu = settings.contextMenu;
this.actions = settings.actions;
}
}
onChangePageSize(event) {
this.userPreference.paginationSize = event.maxItems;
}
resetSelectedRows() {
this.selectedRows = [];
}
onFilterChange(filter: any) {
this.editedFilter = Object.assign({}, filter);
this.sortArray = [new TaskListCloudSortingModel({ orderBy: this.editedFilter.sort, direction: this.editedFilter.order })];
}
}

View File

@ -52,13 +52,14 @@ Edits task filter details.
| showTaskFilterName | `boolean` | true | Toggles display of task filter name | | showTaskFilterName | `boolean` | true | Toggles display of task filter name |
| showTitle | `boolean` | true | Toggles the title. | | showTitle | `boolean` | true | Toggles the title. |
| sortProperties | `string[]` | | List of sort properties to display. | | sortProperties | `string[]` | | List of sort properties to display. |
| TaskType | `string` | userTask | The type of tasks to be listed. `userTask | serviceTask` |
### Events ### Events
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| action | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`TaskFilterAction`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)`>` | Emitted when a filter action occurs (i.e Save, Save As, Delete). | | action | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`TaskFilterAction`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)`>` | Emitted when a filter action occurs (i.e Save, Save As, Delete). |
| filterChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`TaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)`>` | Emitted when a task filter property changes. | | filterChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`TaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)` | `[`ServiceTaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)`>` | Emitted when a task filter property changes. |
## Details ## Details
@ -211,6 +212,18 @@ Users can save a filter if they make any changes to it in an application using t
- An Activiti Enterprise version uses the preference service to store saved filters. This allows for user's custom filters to be available between sessions and between devices. - An Activiti Enterprise version uses the preference service to store saved filters. This allows for user's custom filters to be available between sessions and between devices.
### Filtering Service Tasks
Use the `taskType` to choose which task type to edit:
```html
<adf-cloud-edit-task-filter
[id]="taskFilterId"
[appName]="appName"
[taskType]="'serviceTask'">
</adf-cloud-edit-task-filter>
```
## See also ## See also
- [Edit process filter cloud component](edit-process-filter-cloud.component.md) - [Edit process filter cloud component](edit-process-filter-cloud.component.md)

View File

@ -76,6 +76,8 @@ when the task list is empty:
| standalone | `boolean` | false | Filter the tasks. Display only the tasks that belong to a process in case is false or tasks that doesn't belong to a process in case of true. | | standalone | `boolean` | false | Filter the tasks. Display only the tasks that belong to a process in case is false or tasks that doesn't belong to a process in case of true. |
| status | `string` | "" | Filter the tasks. Display only tasks with status equal to the supplied value. | | status | `string` | "" | Filter the tasks. Display only tasks with status equal to the supplied value. |
| stickyHeader | `boolean` | false | Toggles the sticky header mode. | | stickyHeader | `boolean` | false | Toggles the sticky header mode. |
| TaskType | `string` | userTask | The type of tasks to be listed. `userTask | serviceTask` |
| queryFilter | `Object` | {} | An object containing a key/value for each of the properties that a service task will be filtered by. |
### Events ### Events
@ -334,6 +336,31 @@ When an action is selected in the dropdown menu, the [`TaskListCloudComponent`](
Use this to handle the response, inspect the action payload (and all custom properties defined Use this to handle the response, inspect the action payload (and all custom properties defined
earlier), and perform the corresponding actions. earlier), and perform the corresponding actions.
#### Listing service tasks
Emitted when the user executes a row action.
This usually accompanies a `showRowActionsMenu` event.
The [`TaskListCloudComponent`](../../process-services-cloud/components/task-list-cloud.component.md) itself does not execute actions but provides support for external
integration. If actions are provided using the `showRowActionsMenu` event
then `executeRowAction` will be automatically executed when the user clicks a
corresponding menu item.
```ts
const queryParams = {
activityName: "serviceTask1",
maxItems: 10,
skipCount: 0
}
```
```html
<adf-cloud-task-list
[taskType]="'serviceTask'"
[queryParams]="queryParams">
</adf-cloud-task-list>
```
## See also ## See also
- [Data column component](../../core/components/data-column.component.md) - [Data column component](../../core/components/data-column.component.md)

View File

@ -79,4 +79,12 @@ export abstract class DataTableSchema {
private getDefaultLayoutPreset(): DataColumn[] { private getDefaultLayoutPreset(): DataColumn[] {
return (this.layoutPresets['default']).map((col) => new ObjectDataColumn(col)); return (this.layoutPresets['default']).map((col) => new ObjectDataColumn(col));
} }
public setPresetKey(presetKey: string) {
this.presetKey = presetKey;
}
public setPresetsModel(presetsModel: any) {
this.presetsModel = presetsModel;
}
} }

View File

@ -98,6 +98,16 @@
} }
} }
}, },
"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": { "ADF_CLOUD_TASK_FILTERS": {
"MY_TASKS": "My Tasks", "MY_TASKS": "My Tasks",
"QUEUED_TASKS": "Queued Tasks", "QUEUED_TASKS": "Queued Tasks",
@ -149,6 +159,24 @@
"CANCEL": "CANCEL" "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": { "ADF_CLOUD_EDIT_PROCESS_FILTER": {
"TITLE": "Customize your filter", "TITLE": "Customize your filter",
"LABEL": { "LABEL": {

View File

@ -37,6 +37,7 @@ import { fakeFilter } from '../mock/task-filters-cloud.mock';
import { AbstractControl } from '@angular/forms'; import { AbstractControl } from '@angular/forms';
import moment from 'moment-es6'; import moment from 'moment-es6';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { TaskFilterCloudModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
describe('EditTaskFilterCloudComponent', () => { describe('EditTaskFilterCloudComponent', () => {
let component: EditTaskFilterCloudComponent; let component: EditTaskFilterCloudComponent;
@ -67,11 +68,13 @@ describe('EditTaskFilterCloudComponent', () => {
appsService = TestBed.inject(AppsProcessCloudService); appsService = TestBed.inject(AppsProcessCloudService);
taskService = TestBed.inject(TaskCloudService); taskService = TestBed.inject(TaskCloudService);
dialog = TestBed.inject(MatDialog); dialog = TestBed.inject(MatDialog);
spyOn(dialog, 'open').and.returnValue({ afterClosed: of({ spyOn(dialog, 'open').and.returnValue({
action: TaskFilterDialogCloudComponent.ACTION_SAVE, afterClosed: of({
icon: 'icon', action: TaskFilterDialogCloudComponent.ACTION_SAVE,
name: 'fake-name' icon: 'icon',
}) }); name: 'fake-name'
})
});
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter)); getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance)); getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
fixture.detectChanges(); fixture.detectChanges();
@ -171,7 +174,7 @@ describe('EditTaskFilterCloudComponent', () => {
expect(component.editTaskFilterForm).toBeDefined(); 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.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
const stateController = component.editTaskFilterForm.get('status'); const stateController = component.editTaskFilterForm.get('status');
@ -187,6 +190,24 @@ describe('EditTaskFilterCloudComponent', () => {
}); });
})); }));
it('should emit ServiceTaskFilterCloudModel when the taskType is set to serviceTask', (done) => {
component.appName = 'fake';
component.taskType = 'serviceTask';
component.filterProperties = ['appName', 'status'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIdChange });
fixture.detectChanges();
const statusControl: AbstractControl = component.editTaskFilterForm.get('status');
statusControl.setValue('COMPLETED');
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter instanceof ServiceTaskFilterCloudModel).toBeTruthy();
done();
});
component.onFilterChange();
});
describe('Save & Delete buttons', () => { describe('Save & Delete buttons', () => {
it('should disable save and delete button for default task filters', async(() => { it('should disable save and delete button for default task filters', async(() => {
getTaskFilterSpy.and.returnValue(of({ getTaskFilterSpy.and.returnValue(of({
@ -243,13 +264,13 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
component.editTaskFilterForm.valueChanges component.editTaskFilterForm.valueChanges
.pipe(debounceTime(300)) .pipe(debounceTime(300))
.subscribe(() => { .subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]');
fixture.detectChanges(); fixture.detectChanges();
expect(saveButton.disabled).toBe(false); expect(saveButton.disabled).toBe(false);
done(); done();
}); });
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
stateElement.click(); stateElement.click();
@ -329,13 +350,13 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
component.editTaskFilterForm.valueChanges component.editTaskFilterForm.valueChanges
.pipe(debounceTime(300)) .pipe(debounceTime(300))
.subscribe(() => { .subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
fixture.detectChanges(); fixture.detectChanges();
expect(saveButton.disabled).toEqual(false); expect(saveButton.disabled).toEqual(false);
done(); done();
}); });
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
stateElement.click(); stateElement.click();
@ -353,13 +374,13 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
component.editTaskFilterForm.valueChanges component.editTaskFilterForm.valueChanges
.pipe(debounceTime(300)) .pipe(debounceTime(300))
.subscribe(() => { .subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
fixture.detectChanges(); fixture.detectChanges();
expect(saveButton.disabled).toEqual(false); expect(saveButton.disabled).toEqual(false);
done(); done();
}); });
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger');
stateElement.click(); stateElement.click();
@ -598,7 +619,9 @@ describe('EditTaskFilterCloudComponent', () => {
}); });
component.filterChange.subscribe(() => { 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(); done();
}); });
component.onFilterChange(); component.onFilterChange();
@ -705,7 +728,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should not call restore default filters service on deletion of first filter', async(() => { it('should not call restore default filters service on deletion of first filter', async(() => {
component.toggleFilterActions = true; 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([])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([]));
fixture.detectChanges(); fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');

View File

@ -24,7 +24,7 @@ import { Subject, Observable } from 'rxjs';
import moment from 'moment-es6'; import moment from 'moment-es6';
import { Moment } from 'moment'; import { Moment } from 'moment';
import { TaskFilterCloudModel, TaskFilterProperties, FilterOptions, TaskFilterAction } from './../models/filter-cloud.model'; import { TaskFilterCloudModel, TaskFilterProperties, FilterOptions, TaskFilterAction, ServiceTaskFilterCloudModel, TaskType } from './../models/filter-cloud.model';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service'; import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component'; import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
@ -51,7 +51,9 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
public static SORT: string = 'sort'; public static SORT: string = 'sort';
public static ORDER: string = 'order'; public static ORDER: string = 'order';
public static DEFAULT_TASK_FILTER_PROPERTIES = ['status', 'assignee', 'sort', 'order']; public static DEFAULT_TASK_FILTER_PROPERTIES = ['status', 'assignee', 'sort', 'order'];
public static DEFAULT_SORT_PROPERTIES = ['id', 'name', 'createdDate', 'priority']; public static DEFAULT_SERVICE_TASK_FILTER_PROPERTIES = ['appName', 'activityName', 'status', 'sort', 'order'];
public static DEFAULT_USER_TASK_SORT_PROPERTIES = ['id', 'name', 'createdDate', 'priority'];
public static DEFAULT_SERVICE_TASK_SORT_PROPERTIES = ['id', 'name', 'startedDate', 'completedDate'];
public static DEFAULT_ACTIONS = ['save', 'saveAs', 'delete']; public static DEFAULT_ACTIONS = ['save', 'saveAs', 'delete'];
public FORMAT_DATE: string = 'DD/MM/YYYY'; public FORMAT_DATE: string = 'DD/MM/YYYY';
@ -69,11 +71,11 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
/** List of task filter properties to display. */ /** List of task filter properties to display. */
@Input() @Input()
filterProperties: string[] = EditTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES; filterProperties: string[] = [];
/** List of sort properties to display. */ /** List of sort properties to display. */
@Input() @Input()
sortProperties: string[] = EditTaskFilterCloudComponent.DEFAULT_SORT_PROPERTIES; sortProperties: string[] = [];
/** List of task filter actions. */ /** List of task filter actions. */
@Input() @Input()
@ -91,18 +93,22 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
@Input() @Input()
showTaskFilterName = true; showTaskFilterName = true;
/** Task type: userTask | serviceTask */
@Input()
taskType = 'userTask';
/** Emitted when a task filter property changes. */ /** Emitted when a task filter property changes. */
@Output() @Output()
filterChange: EventEmitter<TaskFilterCloudModel> = new EventEmitter(); filterChange: EventEmitter<TaskFilterCloudModel | ServiceTaskFilterCloudModel> = new EventEmitter();
/** Emitted when a filter action occurs (i.e Save, Save As, Delete). */ /** Emitted when a filter action occurs (i.e Save, Save As, Delete). */
@Output() @Output()
action: EventEmitter<TaskFilterAction> = new EventEmitter(); action: EventEmitter<TaskFilterAction> = new EventEmitter();
taskFilter: TaskFilterCloudModel; taskFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel;
changedTaskFilter: TaskFilterCloudModel; changedTaskFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel;
status = [ userTaskStatus = [
{ label: 'ALL', value: '' }, { label: 'ALL', value: '' },
{ label: 'CREATED', value: 'CREATED' }, { label: 'CREATED', value: 'CREATED' },
{ label: 'ASSIGNED', value: 'ASSIGNED' }, { label: 'ASSIGNED', value: 'ASSIGNED' },
@ -111,6 +117,14 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
{ label: 'COMPLETED', value: 'COMPLETED' } { label: 'COMPLETED', value: 'COMPLETED' }
]; ];
serviceTaskStatus = [
{ label: 'ALL', value: '' },
{ label: 'STARTED', value: 'STARTED' },
{ label: 'COMPLETED', value: 'COMPLETED' },
{ label: 'CANCELLED', value: 'CANCELLED' },
{ label: 'ERROR', value: 'ERROR' }
];
directions = [ directions = [
{ label: 'ASC', value: 'ASC' }, { label: 'ASC', value: 'ASC' },
{ label: 'DESC', value: 'DESC' } { label: 'DESC', value: 'DESC' }
@ -192,9 +206,14 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
filter(() => this.isFormValid()), filter(() => this.isFormValid()),
takeUntil(this.onDestroy$) takeUntil(this.onDestroy$)
) )
.subscribe((formValues: TaskFilterCloudModel) => { .subscribe((formValues: TaskFilterCloudModel | ServiceTaskFilterCloudModel) => {
this.setLastModifiedToFilter(formValues); if (this.taskType === TaskType.UserTask) {
this.changedTaskFilter = new TaskFilterCloudModel(Object.assign({}, this.taskFilter, formValues)); this.setLastModifiedToFilter(<TaskFilterCloudModel> formValues);
this.changedTaskFilter = new TaskFilterCloudModel(Object.assign({}, this.taskFilter, formValues));
} else {
this.changedTaskFilter = new ServiceTaskFilterCloudModel(Object.assign({}, this.taskFilter, formValues));
}
this.formHasBeenChanged = !this.compareFilters(this.changedTaskFilter, this.taskFilter); this.formHasBeenChanged = !this.compareFilters(this.changedTaskFilter, this.taskFilter);
this.filterChange.emit(this.changedTaskFilter); this.filterChange.emit(this.changedTaskFilter);
}); });
@ -218,16 +237,20 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
retrieveTaskFilterAndBuildForm() { retrieveTaskFilterAndBuildForm() {
this.isLoading = true; this.isLoading = true;
this.taskFilterCloudService.getTaskFilterById(this.appName, this.id) this.taskFilterCloudService.getTaskFilterById(this.appName, this.id)
.pipe( .pipe(
finalize(() => this.isLoading = false), finalize(() => this.isLoading = false),
takeUntil(this.onDestroy$) takeUntil(this.onDestroy$)
) )
.subscribe(response => { .subscribe(response => {
this.taskFilter = new TaskFilterCloudModel(response); if (this.taskType === TaskType.ServiceTask) {
this.taskFilterProperties = this.createAndFilterProperties(); this.taskFilter = new ServiceTaskFilterCloudModel(response);
this.taskFilterActions = this.createAndFilterActions(); } else {
this.buildForm(this.taskFilterProperties); this.taskFilter = new TaskFilterCloudModel(response);
}); }
this.taskFilterProperties = this.createAndFilterProperties();
this.taskFilterActions = this.createAndFilterActions();
this.buildForm(this.taskFilterProperties);
});
} }
createAndFilterProperties() { createAndFilterProperties() {
@ -257,7 +280,11 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
checkMandatoryFilterProperties() { checkMandatoryFilterProperties() {
if (this.filterProperties === undefined || this.filterProperties.length === 0) { if (this.filterProperties === undefined || this.filterProperties.length === 0) {
this.filterProperties = EditTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES; if (this.taskType === TaskType.ServiceTask) {
this.filterProperties = EditTaskFilterCloudComponent.DEFAULT_SERVICE_TASK_FILTER_PROPERTIES;
} else {
this.filterProperties = EditTaskFilterCloudComponent.DEFAULT_TASK_FILTER_PROPERTIES;
}
} }
} }
@ -294,7 +321,11 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
checkMandatorySortProperties(): void { checkMandatorySortProperties(): void {
if (this.sortProperties === undefined || this.sortProperties.length === 0) { if (this.sortProperties === undefined || this.sortProperties.length === 0) {
this.sortProperties = EditTaskFilterCloudComponent.DEFAULT_SORT_PROPERTIES; if (this.taskType === TaskType.ServiceTask) {
this.sortProperties = EditTaskFilterCloudComponent.DEFAULT_SERVICE_TASK_SORT_PROPERTIES;
} else {
this.sortProperties = EditTaskFilterCloudComponent.DEFAULT_USER_TASK_SORT_PROPERTIES;
}
} }
} }
@ -330,7 +361,7 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
this.getPropertyController(dateProperty).setValue(momentDate.toDate()); this.getPropertyController(dateProperty).setValue(momentDate.toDate());
this.getPropertyController(dateProperty).setErrors(null); this.getPropertyController(dateProperty).setErrors(null);
} else { } else {
this.getPropertyController(dateProperty).setErrors({invalid: true}); this.getPropertyController(dateProperty).setErrors({ invalid: true });
} }
} }
} }
@ -352,7 +383,10 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
* Return true if both filters are same * Return true if both filters are same
* @param editedQuery, @param currentQuery * @param editedQuery, @param currentQuery
*/ */
compareFilters(editedQuery: TaskFilterCloudModel, currentQuery: TaskFilterCloudModel): boolean { compareFilters(
editedQuery: TaskFilterCloudModel | ServiceTaskFilterCloudModel,
currentQuery: TaskFilterCloudModel | ServiceTaskFilterCloudModel
): boolean {
return JSON.stringify(editedQuery).toLowerCase() === JSON.stringify(currentQuery).toLowerCase(); return JSON.stringify(editedQuery).toLowerCase() === JSON.stringify(currentQuery).toLowerCase();
} }
@ -414,7 +448,7 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
}), }),
switchMap(() => this.restoreDefaultTaskFilters()), switchMap(() => this.restoreDefaultTaskFilters()),
takeUntil(this.onDestroy$)) takeUntil(this.onDestroy$))
.subscribe(() => {}); .subscribe(() => { });
} }
saveAs(saveAsAction: TaskFilterAction): void { saveAs(saveAsAction: TaskFilterAction): void {
@ -435,12 +469,12 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
id: filterId, id: filterId,
key: 'custom-' + filterKey key: 'custom-' + filterKey
}; };
const resultFilter: TaskFilterCloudModel = Object.assign({}, this.changedTaskFilter, newFilter); const resultFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel = Object.assign({}, this.changedTaskFilter, newFilter);
this.taskFilterCloudService.addFilter(resultFilter) this.taskFilterCloudService.addFilter(resultFilter)
.pipe(takeUntil(this.onDestroy$)).subscribe(() => { .pipe(takeUntil(this.onDestroy$)).subscribe(() => {
saveAsAction.filter = resultFilter; saveAsAction.filter = resultFilter;
this.action.emit(saveAsAction); this.action.emit(saveAsAction);
}); });
} }
}); });
} }
@ -463,7 +497,7 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
return name.replace(regExt, '-'); return name.replace(regExt, '-');
} }
restoreDefaultTaskFilters(): Observable<TaskFilterCloudModel[]> { restoreDefaultTaskFilters(): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
return this.taskFilterCloudService.getTaskListFilters(this.appName); return this.taskFilterCloudService.getTaskListFilters(this.appName);
} }
@ -562,7 +596,94 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
]; ];
} }
createTaskFilterProperties(currentTaskFilter: TaskFilterCloudModel): TaskFilterProperties[] { createServiceTaskFilterProperties(currentTaskFilter: ServiceTaskFilterCloudModel): TaskFilterProperties[] {
return [
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.APP_NAME',
type: 'select',
key: 'appName',
value: currentTaskFilter.appName || '',
options: this.applicationNames
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.SERVICE_TASK_ID',
type: 'text',
key: 'serviceTaskId',
value: currentTaskFilter.serviceTaskId || ''
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.ELEMENT_ID',
type: 'text',
key: 'elementId',
value: currentTaskFilter.elementId || ''
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.ACTIVITY_NAME',
type: 'text',
key: 'activityName',
value: currentTaskFilter.activityName || ''
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.ACTIVITY_TYPE',
type: 'text',
key: 'activityType',
value: currentTaskFilter.activityType || ''
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.SORT',
type: 'select',
key: 'sort',
value: currentTaskFilter.sort || this.createSortProperties[0].value,
options: this.createSortProperties
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.DIRECTION',
type: 'select',
key: 'order',
value: currentTaskFilter.order || this.directions[0].value,
options: this.directions
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.STATUS',
type: 'select',
key: 'status',
value: currentTaskFilter.status || this.serviceTaskStatus[0].value,
options: this.serviceTaskStatus
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.STARTED_DATE',
type: 'date',
key: 'startedDate',
value: currentTaskFilter.completedDate || false
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.COMPLETED_DATE',
type: 'date',
key: 'completedDate',
value: currentTaskFilter.completedDate || false
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.PROCESS_INSTANCE_ID',
type: 'text',
key: 'processInstanceId',
value: currentTaskFilter.processInstanceId || ''
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.PROCESS_DEF_ID',
type: 'text',
key: 'processDefinitionId',
value: currentTaskFilter.processDefinitionId || ''
}),
new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_SERVICE_TASK_FILTER.LABEL.SERVICE_NAME',
type: 'text',
key: 'serviceName',
value: currentTaskFilter.serviceName || ''
})
];
}
createUserTaskFilterProperties(currentTaskFilter: TaskFilterCloudModel): TaskFilterProperties[] {
return [ return [
new TaskFilterProperties({ new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.APP_NAME', label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.APP_NAME',
@ -581,8 +702,8 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.STATUS', label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.STATUS',
type: 'select', type: 'select',
key: 'status', key: 'status',
value: currentTaskFilter.status || this.status[0].value, value: currentTaskFilter.status || this.userTaskStatus[0].value,
options: this.status options: this.userTaskStatus
}), }),
new TaskFilterProperties({ new TaskFilterProperties({
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.ASSIGNMENT', label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.ASSIGNMENT',
@ -675,4 +796,12 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestro
}) })
]; ];
} }
createTaskFilterProperties(currentTaskFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel): TaskFilterProperties[] {
if (this.taskType === TaskType.ServiceTask) {
return this.createServiceTaskFilterProperties(<ServiceTaskFilterCloudModel> currentTaskFilter);
} else {
return this.createUserTaskFilterProperties(<TaskFilterCloudModel> currentTaskFilter);
}
}
} }

View File

@ -18,7 +18,7 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit } from '@angular/core';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service'; import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFilterCloudModel, FilterParamsModel } from '../models/filter-cloud.model'; import { TaskFilterCloudModel, FilterParamsModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
import { TranslationService } from '@alfresco/adf-core'; import { TranslationService } from '@alfresco/adf-core';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
@ -45,7 +45,7 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
/** Emitted when a filter in the list is clicked. */ /** Emitted when a filter in the list is clicked. */
@Output() @Output()
filterClick: EventEmitter<TaskFilterCloudModel> = new EventEmitter<TaskFilterCloudModel>(); filterClick: EventEmitter<TaskFilterCloudModel | ServiceTaskFilterCloudModel> = new EventEmitter<TaskFilterCloudModel | ServiceTaskFilterCloudModel>();
/** Emitted when the list is loaded. */ /** Emitted when the list is loaded. */
@Output() @Output()
@ -55,11 +55,11 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
@Output() @Output()
error: EventEmitter<any> = new EventEmitter<any>(); error: EventEmitter<any> = new EventEmitter<any>();
filters$: Observable<TaskFilterCloudModel[]>; filters$: Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]>;
currentFilter: TaskFilterCloudModel; filters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[] = [];
filters: TaskFilterCloudModel [] = []; currentFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel;
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
@ -92,7 +92,7 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName); this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName);
this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe( this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe(
(res: TaskFilterCloudModel[]) => { (res: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]) => {
this.resetFilter(); this.resetFilter();
this.filters = Object.assign([], res); this.filters = Object.assign([], res);
this.selectFilterAndEmit(this.filterParam); this.selectFilterAndEmit(this.filterParam);
@ -106,7 +106,7 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
public selectFilter(paramFilter: FilterParamsModel) { public selectFilter(paramFilter: FilterParamsModel) {
if (paramFilter) { if (paramFilter) {
this.currentFilter = this.filters.find( (filter: TaskFilterCloudModel, index) => this.currentFilter = (this.filters as Array<TaskFilterCloudModel | ServiceTaskFilterCloudModel>).find((filter: any, index) =>
paramFilter.index === index || paramFilter.index === index ||
paramFilter.key === filter.key || paramFilter.key === filter.key ||
paramFilter.id === filter.id || paramFilter.id === filter.id ||
@ -137,7 +137,7 @@ export class TaskFiltersCloudComponent implements OnInit, OnChanges, OnDestroy {
/** /**
* Return the current task * Return the current task
*/ */
getCurrentFilter(): TaskFilterCloudModel { getCurrentFilter(): TaskFilterCloudModel | ServiceTaskFilterCloudModel {
return this.currentFilter; return this.currentFilter;
} }

View File

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { TaskFilterCloudModel } from '../models/filter-cloud.model'; import { TaskFilterCloudModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
export let fakeGlobalFilter = [ export let fakeGlobalFilter = [
new TaskFilterCloudModel({ new TaskFilterCloudModel({
@ -56,6 +56,18 @@ export let fakeFilter = new TaskFilterCloudModel({
sort: 'id' sort: 'id'
}); });
export let fakeServiceFilter = new ServiceTaskFilterCloudModel({
name: 'FakeInvolvedTasks',
icon: 'adjust',
id: 'mock-task-filter-id',
status: 'CREATED',
appName: 'mock-app-name',
processDefinitionId: 'process-def-id',
activityName: 'fake-activity',
order: 'ASC',
sort: 'id'
});
export let fakeAllTaskFilter = new TaskFilterCloudModel({ export let fakeAllTaskFilter = new TaskFilterCloudModel({
name: 'AllTasks', name: 'AllTasks',
icon: 'adjust', icon: 'adjust',

View File

@ -17,7 +17,7 @@
import { DateCloudFilterType } from '../../../models/date-cloud-filter.model'; import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
export class TaskFilterCloudModel { export class TaskFilterCloudModel {
id: string; id: string;
name: string; name: string;
key: string; key: string;
@ -74,6 +74,66 @@ export class TaskFilterCloudModel {
} }
} }
} }
export class 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;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || Math.random().toString(36).substr(2, 9);
this.name = obj.name || null;
this.key = obj.key || null;
this.icon = obj.icon || null;
this.index = obj.index || null;
this.appName = obj.appName || obj.appName === '' ? obj.appName : null;
this.status = obj.status || null;
this.sort = obj.sort || null;
this.order = obj.order || null;
this.activityName = obj.activityName || null;
this.activityType = obj.activityType || null;
this.elementId = obj.elementId || null;
this.executionId = obj.executionId || null;
this.processDefinitionId = obj.processDefinitionId || null;
this.processDefinitionKey = obj.processDefinitionKey || null;
this.processDefinitionVersion = obj.processDefinitionVersion || null;
this.processInstanceId = obj.processInstanceId || null;
this.completedDate = obj.completedDate || null;
this.startedDate = obj.startedDate || null;
this.serviceVersion = obj.serviceVersion || null;
this.serviceTaskId = obj.serviceTaskId || null;
this.serviceName = obj.serviceName || null;
this.serviceFullName = obj.serviceFullName || null;
}
}
}
export enum TaskType {
UserTask = 'userTask',
ServiceTask = 'serviceTask'
}
export class FilterParamsModel { export class FilterParamsModel {
id?: string; id?: string;
@ -95,7 +155,7 @@ export class TaskFilterAction {
actionType: string; actionType: string;
icon: string; icon: string;
tooltip: string; tooltip: string;
filter: TaskFilterCloudModel; filter: TaskFilterCloudModel | ServiceTaskFilterCloudModel;
constructor(obj?: any) { constructor(obj?: any) {
if (obj) { if (obj) {

View File

@ -38,6 +38,7 @@ import {
import { UserPreferenceCloudService } from '../../../services/user-preference-cloud.service'; import { UserPreferenceCloudService } from '../../../services/user-preference-cloud.service';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { HttpClientTestingModule } from '@angular/common/http/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TaskFilterCloudModel } from '../models/filter-cloud.model';
describe('TaskFilterCloudService', () => { describe('TaskFilterCloudService', () => {
let service: 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) => { it('should create default task filters if there are no task filter preferences', (done) => {
const appName = 'fakeAppName'; const appName = 'fakeAppName';
service.getTaskListFilters(appName).subscribe((res) => { service.getTaskListFilters(appName).subscribe((res: TaskFilterCloudModel[]) => {
expect(res.length).toEqual(3); expect(res.length).toEqual(3);
expect(res[0].name).toEqual('ADF_CLOUD_TASK_FILTERS.MY_TASKS'); expect(res[0].name).toEqual('ADF_CLOUD_TASK_FILTERS.MY_TASKS');

View File

@ -18,7 +18,7 @@
import { IdentityUserService } from '@alfresco/adf-core'; import { IdentityUserService } from '@alfresco/adf-core';
import { Injectable, Inject } from '@angular/core'; import { Injectable, Inject } from '@angular/core';
import { Observable, of, BehaviorSubject, throwError } from 'rxjs'; import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
import { TaskFilterCloudModel } from '../models/filter-cloud.model'; import { TaskFilterCloudModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
import { switchMap, map, catchError } from 'rxjs/operators'; import { switchMap, map, catchError } from 'rxjs/operators';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
@ -27,8 +27,8 @@ import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.servic
providedIn: 'root' providedIn: 'root'
}) })
export class TaskFilterCloudService { export class TaskFilterCloudService {
private filtersSubject: BehaviorSubject<TaskFilterCloudModel[]>; private filtersSubject: BehaviorSubject<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]>;
filters$: Observable<TaskFilterCloudModel[]>; filters$: Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]>;
constructor( constructor(
private identityUserService: IdentityUserService, private identityUserService: IdentityUserService,
@ -49,9 +49,7 @@ export class TaskFilterCloudService {
this.preferenceService.getPreferences(appName, key).pipe( this.preferenceService.getPreferences(appName, key).pipe(
switchMap((response: any) => { switchMap((response: any) => {
const preferences = (response && response.list && response.list.entries) ? response.list.entries : []; const preferences = (response && response.list && response.list.entries) ? response.list.entries : [];
if (!this.hasPreferences(preferences)) { if (!this.hasPreferences(preferences) || !this.hasTaskFilters(preferences, key)) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else if (!this.hasTaskFilters(preferences, key)) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName)); return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else { } else {
return of(this.findFiltersByKeyInPreferences(preferences, key)); return of(this.findFiltersByKeyInPreferences(preferences, key));
@ -91,7 +89,7 @@ export class TaskFilterCloudService {
* @param filters Details of new task filter * @param filters Details of new task filter
* @returns Observable of created task filters * @returns Observable of created task filters
*/ */
private createTaskFilters(appName: string, key: string, filters: TaskFilterCloudModel[]): Observable<TaskFilterCloudModel[]> { private createTaskFilters(appName: string, key: string, filters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
return this.preferenceService.createPreference(appName, key, filters); return this.preferenceService.createPreference(appName, key, filters);
} }
@ -101,7 +99,7 @@ export class TaskFilterCloudService {
* @param key Key of the task filters * @param key Key of the task filters
* @returns Observable of task filters * @returns Observable of task filters
*/ */
private getTaskFiltersByKey(appName: string, key: string): Observable<TaskFilterCloudModel[]> { private getTaskFiltersByKey(appName: string, key: string): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
return this.preferenceService.getPreferenceByKey(appName, key); return this.preferenceService.getPreferenceByKey(appName, key);
} }
@ -110,7 +108,7 @@ export class TaskFilterCloudService {
* @param appName Name of the target app * @param appName Name of the target app
* @returns Observable of task filter details * @returns Observable of task filter details
*/ */
getTaskListFilters(appName?: string): Observable<TaskFilterCloudModel[]> { getTaskListFilters(appName?: string): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
this.createDefaultFilters(appName); this.createDefaultFilters(appName);
return this.filters$; return this.filters$;
} }
@ -121,23 +119,23 @@ export class TaskFilterCloudService {
* @param id ID of the task * @param id ID of the task
* @returns Details of the task filter * @returns Details of the task filter
*/ */
getTaskFilterById(appName: string, id: string): Observable<TaskFilterCloudModel> { getTaskFilterById(appName: string, id: string): Observable<TaskFilterCloudModel | ServiceTaskFilterCloudModel> {
const key: string = this.prepareKey(appName); const key: string = this.prepareKey(appName);
return this.getTaskFiltersByKey(appName, key).pipe( return this.getTaskFiltersByKey(appName, key).pipe(
switchMap((filters: TaskFilterCloudModel[]) => { switchMap((filters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]) => {
if (filters && filters.length === 0) { if (filters && filters.length === 0) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName)); return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else { } else {
return of(filters); return of(filters);
} }
}), }),
map((filters: TaskFilterCloudModel[]) => { map((filters: any) => {
return filters.filter((filter: TaskFilterCloudModel) => { return filters.filter((filter: TaskFilterCloudModel | ServiceTaskFilterCloudModel) => {
return filter.id === id; return filter.id === id;
})[0]; })[0];
}), }),
catchError((err) => this.handleTaskError(err)) catchError((err) => this.handleTaskError(err))
); );
} }
/** /**
@ -145,18 +143,18 @@ export class TaskFilterCloudService {
* @param filter The new filter to add * @param filter The new filter to add
* @returns Observable of task instance filters with newly added filter * @returns Observable of task instance filters with newly added filter
*/ */
addFilter(newFilter: TaskFilterCloudModel): Observable<TaskFilterCloudModel[]> { addFilter(newFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
const key: string = this.prepareKey(newFilter.appName); const key: string = this.prepareKey(newFilter.appName);
return this.getTaskFiltersByKey(newFilter.appName, key).pipe( return this.getTaskFiltersByKey(newFilter.appName, key).pipe(
switchMap((filters: TaskFilterCloudModel[]) => { switchMap((filters: any) => {
if (filters && filters.length === 0) { if (filters && filters.length === 0) {
return this.createTaskFilters(newFilter.appName, key, [newFilter]); return this.createTaskFilters(newFilter.appName, key, <TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> [newFilter]);
} else { } else {
filters.push(newFilter); filters.push(newFilter);
return this.preferenceService.updatePreference(newFilter.appName, key, filters); return this.preferenceService.updatePreference(newFilter.appName, key, filters);
} }
}), }),
map((filters: TaskFilterCloudModel[]) => { map((filters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]) => {
this.addFiltersToStream(filters); this.addFiltersToStream(filters);
return filters; return filters;
}), }),
@ -164,7 +162,7 @@ export class TaskFilterCloudService {
); );
} }
private addFiltersToStream(filters: TaskFilterCloudModel[]) { private addFiltersToStream(filters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]) {
this.filtersSubject.next(filters); this.filtersSubject.next(filters);
} }
@ -173,19 +171,19 @@ export class TaskFilterCloudService {
* @param filter The filter to update * @param filter The filter to update
* @returns Observable of task instance filters with updated filter * @returns Observable of task instance filters with updated filter
*/ */
updateFilter(updatedFilter: TaskFilterCloudModel): Observable<TaskFilterCloudModel[]> { updateFilter(updatedFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
const key: string = this.prepareKey(updatedFilter.appName); const key: string = this.prepareKey(updatedFilter.appName);
return this.getTaskFiltersByKey(updatedFilter.appName, key).pipe( return this.getTaskFiltersByKey(updatedFilter.appName, key).pipe(
switchMap((filters: any) => { switchMap((filters: any) => {
if (filters && filters.length === 0) { if (filters && filters.length === 0) {
return this.createTaskFilters(updatedFilter.appName, key, [updatedFilter]); return this.createTaskFilters(updatedFilter.appName, key, <TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> [updatedFilter]);
} else { } else {
const itemIndex = filters.findIndex((filter: TaskFilterCloudModel) => filter.id === updatedFilter.id); const itemIndex = filters.findIndex((filter: TaskFilterCloudModel | ServiceTaskFilterCloudModel) => filter.id === updatedFilter.id);
filters[itemIndex] = updatedFilter; filters[itemIndex] = updatedFilter;
return this.updateTaskFilters(updatedFilter.appName, key, filters); return this.updateTaskFilters(updatedFilter.appName, key, filters);
} }
}), }),
map((updatedFilters: TaskFilterCloudModel[]) => { map((updatedFilters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]) => {
this.addFiltersToStream(updatedFilters); this.addFiltersToStream(updatedFilters);
return updatedFilters; return updatedFilters;
}), }),
@ -198,10 +196,10 @@ export class TaskFilterCloudService {
* @param filter The filter to delete * @param filter The filter to delete
* @returns Observable of task instance filters without deleted filter * @returns Observable of task instance filters without deleted filter
*/ */
deleteFilter(deletedFilter: TaskFilterCloudModel): Observable<TaskFilterCloudModel[]> { deleteFilter(deletedFilter: TaskFilterCloudModel | ServiceTaskFilterCloudModel): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
const key = this.prepareKey(deletedFilter.appName); const key = this.prepareKey(deletedFilter.appName);
return this.getTaskFiltersByKey(deletedFilter.appName, key).pipe( return this.getTaskFiltersByKey(deletedFilter.appName, key).pipe(
switchMap(filters => { switchMap((filters: any) => {
if (filters && filters.length > 0) { if (filters && filters.length > 0) {
filters = filters.filter(filter => filter.id !== deletedFilter.id); filters = filters.filter(filter => filter.id !== deletedFilter.id);
return this.updateTaskFilters(deletedFilter.appName, key, filters); return this.updateTaskFilters(deletedFilter.appName, key, filters);
@ -233,7 +231,7 @@ export class TaskFilterCloudService {
* @param filters Details of update filter * @param filters Details of update filter
* @returns Observable of updated task filters * @returns Observable of updated task filters
*/ */
private updateTaskFilters(appName: string, key: string, filters: TaskFilterCloudModel[]): Observable<TaskFilterCloudModel[]> { private updateTaskFilters(appName: string, key: string, filters: TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]): Observable<TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[]> {
return this.preferenceService.updatePreference(appName, key, filters); return this.preferenceService.updatePreference(appName, key, filters);
} }
@ -260,7 +258,7 @@ export class TaskFilterCloudService {
* @param appName Name of the target app * @param appName Name of the target app
* @returns Array of TaskFilterCloudModel * @returns Array of TaskFilterCloudModel
*/ */
private findFiltersByKeyInPreferences(preferences: any, key: string): TaskFilterCloudModel[] { private findFiltersByKeyInPreferences(preferences: any, key: string): TaskFilterCloudModel[] | ServiceTaskFilterCloudModel[] {
const result = preferences.find((filter: any) => { return filter.entry.key === key; }); const result = preferences.find((filter: any) => { return filter.entry.key === key; });
return result && result.entry ? JSON.parse(result.entry.value) : []; return result && result.entry ? JSON.parse(result.entry.value) : [];
} }

View File

@ -21,7 +21,7 @@ import { By } from '@angular/platform-browser';
import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow } from '@alfresco/adf-core'; import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
import { TaskListCloudService } from '../services/task-list-cloud.service'; import { TaskListCloudService } from '../services/task-list-cloud.service';
import { TaskListCloudComponent } from './task-list-cloud.component'; import { TaskListCloudComponent } from './task-list-cloud.component';
import { fakeGlobalTask, fakeCustomSchema } from '../mock/fake-task-response.mock'; import { fakeGlobalTask, fakeCustomSchema, fakeServiceTask } from '../mock/fake-task-response.mock';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { Person } from '@alfresco/js-api'; import { Person } from '@alfresco/js-api';
@ -50,7 +50,7 @@ class CustomTaskListComponent {
getFullName(person: Person): string { getFullName(person: Person): string {
return `${person.firstName} ${person.lastName}`; return `${person.firstName} ${person.lastName}`;
} }
} }
@Component({ @Component({
template: ` template: `
<adf-cloud-task-list> <adf-cloud-task-list>
@ -130,7 +130,7 @@ describe('TaskListCloudComponent', () => {
}); });
it('should display empty content when process list is empty', () => { 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)); spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
fixture.detectChanges(); fixture.detectChanges();
@ -254,6 +254,34 @@ describe('TaskListCloudComponent', () => {
component.onRowClick(rowEvent); component.onRowClick(rowEvent);
}); });
it('should display service task list when typeTask is set to serviceTask', () => {
component.taskType = 'serviceTask';
component.ngOnInit();
fixture.detectChanges();
expect(component.columns).toBeDefined();
expect(component.columns.length).toEqual(4);
});
it('should load the service task list when input parameters changed', () => {
const getServiceTaskByRequestSpy = spyOn(taskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
component.appName = 'mock-app-name';
component.priority = 1;
component.status = 'mock-status';
component.lastModifiedFrom = 'mock-lastmodified-date';
component.owner = 'mock-owner-name';
component.taskType = 'serviceTask';
component.ngOnInit();
const queryParams = new SimpleChange(undefined, {
activityName: 'service1'
}, true);
component.ngOnChanges({ queryParams });
fixture.detectChanges();
expect(component.isListEmpty()).toBeFalsy();
expect(getServiceTaskByRequestSpy).toHaveBeenCalled();
});
describe('component changes', () => { describe('component changes', () => {
beforeEach(() => { beforeEach(() => {
@ -341,14 +369,14 @@ describe('TaskListCloudComponent', () => {
const size = component.size; const size = component.size;
const skipCount = component.skipCount; const skipCount = component.skipCount;
component.pagination.pipe(skip(3)) component.pagination.pipe(skip(3))
.subscribe((updatedPagination) => { .subscribe((updatedPagination) => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.size).toBe(size); expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount); expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size); expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount); expect(updatedPagination.skipCount).toEqual(skipCount);
done(); done();
}); });
const pagination = { const pagination = {
maxItems: 250, maxItems: 250,
@ -371,14 +399,14 @@ describe('TaskListCloudComponent', () => {
skipCount: 200 skipCount: 200
}; };
component.pagination.pipe(skip(1)) component.pagination.pipe(skip(1))
.subscribe((updatedPagination) => { .subscribe((updatedPagination) => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems); expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount); expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems); expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount); expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done(); done();
}); });
component.updatePagination(pagination); component.updatePagination(pagination);
}); });
@ -442,7 +470,7 @@ describe('TaskListCloudComponent', () => {
it('it should not show copy tooltip when key is not present in data-column', (done) => { 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); const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
customCopyComponent.taskList.success.subscribe( () => { customCopyComponent.taskList.success.subscribe(() => {
copyFixture.whenStable().then(() => { copyFixture.whenStable().then(() => {
copyFixture.detectChanges(); copyFixture.detectChanges();
const spanHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('span[title="standalone-subtask"]'); const spanHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('span[title="standalone-subtask"]');
@ -456,13 +484,13 @@ describe('TaskListCloudComponent', () => {
customCopyComponent.taskList.ngOnChanges({ 'appName': appName }); customCopyComponent.taskList.ngOnChanges({ 'appName': appName });
copyFixture.detectChanges(); copyFixture.detectChanges();
}); });
}); });
describe('Creating an empty custom template - EmptyTemplateComponent', () => { describe('Creating an empty custom template - EmptyTemplateComponent', () => {
let fixtureEmpty: ComponentFixture<EmptyTemplateComponent>; let fixtureEmpty: ComponentFixture<EmptyTemplateComponent>;
beforeEach(() => { beforeEach(() => {
const emptyList = {list: {entries: []}}; const emptyList = { list: { entries: [] } };
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList)); spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
fixtureEmpty = TestBed.createComponent(EmptyTemplateComponent); fixtureEmpty = TestBed.createComponent(EmptyTemplateComponent);
@ -498,7 +526,7 @@ describe('TaskListCloudComponent', () => {
] ]
}); });
beforeEach( () => { beforeEach(() => {
appConfig = TestBed.inject(AppConfigService); appConfig = TestBed.inject(AppConfigService);
taskListCloudService = TestBed.inject(TaskListCloudService); taskListCloudService = TestBed.inject(TaskListCloudService);
appConfig.config = Object.assign(appConfig.config, { appConfig.config = Object.assign(appConfig.config, {
@ -536,7 +564,7 @@ describe('TaskListCloudComponent', () => {
taskSpy.and.returnValue(of(fakeGlobalTask)); taskSpy.and.returnValue(of(fakeGlobalTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe( () => { component.success.subscribe(() => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const spanHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'); const spanHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]');
@ -555,7 +583,7 @@ describe('TaskListCloudComponent', () => {
it('shoud not show tooltip if config copyContent flag is true', async(() => { it('shoud not show tooltip if config copyContent flag is true', async(() => {
taskSpy.and.returnValue(of(fakeGlobalTask)); taskSpy.and.returnValue(of(fakeGlobalTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe( () => { component.success.subscribe(() => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const spanHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('span[title="standalone-subtask"]'); const spanHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('span[title="standalone-subtask"]');

View File

@ -16,27 +16,31 @@
*/ */
import { Component, ViewEncapsulation, OnChanges, Input, SimpleChanges, Output, EventEmitter, ContentChild, AfterContentInit, OnDestroy, OnInit } from '@angular/core'; import { Component, ViewEncapsulation, OnChanges, Input, SimpleChanges, Output, EventEmitter, ContentChild, AfterContentInit, OnDestroy, OnInit } from '@angular/core';
import { AppConfigService, UserPreferencesService, import {
DataTableSchema, UserPreferenceValues, AppConfigService, UserPreferencesService,
PaginatedComponent, PaginationModel, DataTableSchema, UserPreferenceValues,
DataRowEvent, CustomEmptyContentTemplateDirective, DataCellEvent, DataRowActionEvent } from '@alfresco/adf-core'; PaginatedComponent, PaginationModel,
import { taskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model'; DataRowEvent, CustomEmptyContentTemplateDirective, DataCellEvent, DataRowActionEvent
import { TaskQueryCloudRequestModel } from '../models/filter-cloud-model'; } from '@alfresco/adf-core';
import { BehaviorSubject, Subject } from 'rxjs'; import { taskPresetsCloudDefaultModel, serviceTaskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model';
import { BehaviorSubject, Subject, Observable } from 'rxjs';
import { TaskQueryCloudRequestModel, ServiceTaskQueryCloudRequestModel } from '../models/filter-cloud-model';
import { TaskListCloudService } from '../services/task-list-cloud.service'; import { TaskListCloudService } from '../services/task-list-cloud.service';
import { TaskListCloudSortingModel } from '../models/task-list-sorting.model'; import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { TaskType } from '../../task-filters/models/filter-cloud.model';
@Component({ @Component({
selector: 'adf-cloud-task-list', selector: 'adf-cloud-task-list',
templateUrl: './task-list-cloud.component.html', templateUrl: './task-list-cloud.component.html',
styleUrls: ['./task-list-cloud.component.scss'], styleUrls: ['./task-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class TaskListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy, OnInit { export class TaskListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy, OnInit {
static PRESET_KEY = 'adf-cloud-task-list.presets'; static USER_TASKS_PRESET_KEY = 'adf-cloud-task-list.presets';
static SERVICE_TASKS_PRESET_KEY = 'adf-cloud-service-task-list.presets';
static ENTRY_PREFIX = 'entry.'; static ENTRY_PREFIX = 'entry.';
@ContentChild(CustomEmptyContentTemplateDirective) @ContentChild(CustomEmptyContentTemplateDirective)
@ -70,7 +74,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
@Input() @Input()
lastModifiedTo: string = ''; 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() @Input()
dueDateFrom: string = ''; dueDateFrom: string = '';
@ -152,6 +156,14 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
@Input() @Input()
showContextMenu: boolean = false; showContextMenu: boolean = false;
/** Task type: userTask | serviceTask */
@Input()
taskType: string = TaskType.UserTask;
/** An object that contains properties used to query the service task list */
@Input()
queryParams: any = {};
/** Emitted before the context menu is displayed for a row. */ /** Emitted before the context menu is displayed for a row. */
@Output() @Output()
showRowContextMenu = new EventEmitter<DataCellEvent>(); showRowContextMenu = new EventEmitter<DataCellEvent>();
@ -197,7 +209,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
constructor(private taskListCloudService: TaskListCloudService, constructor(private taskListCloudService: TaskListCloudService,
appConfigService: AppConfigService, appConfigService: AppConfigService,
private userPreferences: UserPreferencesService) { private userPreferences: UserPreferencesService) {
super(appConfigService, TaskListCloudComponent.PRESET_KEY, taskPresetsCloudDefaultModel); super(appConfigService, TaskListCloudComponent.USER_TASKS_PRESET_KEY, taskPresetsCloudDefaultModel);
this.size = userPreferences.paginationSize; this.size = userPreferences.paginationSize;
this.pagination = new BehaviorSubject<PaginationModel>(<PaginationModel> { this.pagination = new BehaviorSubject<PaginationModel>(<PaginationModel> {
@ -213,6 +225,12 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(pageSize => this.size = pageSize); .subscribe(pageSize => this.size = pageSize);
if (this.taskType === TaskType.ServiceTask) {
super.setPresetKey(TaskListCloudComponent.SERVICE_TASKS_PRESET_KEY);
super.setPresetsModel(serviceTaskPresetsCloudDefaultModel);
super.loadLayoutPresets();
}
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@ -261,7 +279,14 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
private load(requestNode: TaskQueryCloudRequestModel) { private load(requestNode: TaskQueryCloudRequestModel) {
this.isLoading = true; this.isLoading = true;
this.taskListCloudService.getTaskByRequest(requestNode).subscribe( let taskRequest: Observable<any>;
if (this.taskType === TaskType.UserTask) {
taskRequest = this.taskListCloudService.getTaskByRequest(<TaskQueryCloudRequestModel> requestNode);
} else {
taskRequest = this.taskListCloudService.getServiceTaskByRequest(<ServiceTaskQueryCloudRequestModel> requestNode);
}
taskRequest.subscribe(
(tasks) => { (tasks) => {
this.rows = tasks.list.entries; this.rows = tasks.list.entries;
this.success.emit(tasks); this.success.emit(tasks);
@ -343,7 +368,14 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
} }
private createRequestNode() { private createRequestNode() {
if (this.taskType === TaskType.UserTask) {
return this.createUserTaskRequest();
} else {
return this.createServiceTaskRequest();
}
}
createUserTaskRequest() {
const requestNode = { const requestNode = {
appName: this.appName, appName: this.appName,
assignee: this.assignee, assignee: this.assignee,
@ -370,6 +402,31 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
return new TaskQueryCloudRequestModel(requestNode); return new TaskQueryCloudRequestModel(requestNode);
} }
createServiceTaskRequest() {
const requestNode = {
appName: this.appName,
id: this.queryParams.serviceTaskId,
activityName: this.queryParams.activityName,
activityType: this.queryParams.activityType,
completedDate: this.queryParams.completedDate,
elementId: this.queryParams.elementId,
executionId: this.queryParams.executionId,
processDefinitionId: this.queryParams.processDefinitionId,
processDefinitionKey: this.queryParams.processDefinitionKey,
processDefinitionVersion: this.queryParams.processDefinitionVersion,
processInstanceId: this.queryParams.processInstanceId,
serviceFullName: this.queryParams.serviceFullName,
serviceName: this.queryParams.serviceName,
serviceVersion: this.queryParams.serviceVersion,
startedDate: this.queryParams.startedDate,
status: this.queryParams.status,
maxItems: this.size,
skipCount: this.skipCount,
sorting: this.sorting
};
return new ServiceTaskQueryCloudRequestModel(requestNode);
}
setSorting(sortDetail) { setSorting(sortDetail) {
const sorting = sortDetail ? { const sorting = sortDetail ? {
orderBy: sortDetail.key.replace(TaskListCloudComponent.ENTRY_PREFIX, ''), orderBy: sortDetail.key.replace(TaskListCloudComponent.ENTRY_PREFIX, ''),

View File

@ -117,6 +117,44 @@ export let fakeGlobalTask = {
} }
}; };
export let fakeServiceTask = {
list: {
entries: [
{
entry: {
appName: 'test-ciprian2',
appVersion: '',
id: '11fe013d-c263-11e8-b75b-0a5864600540',
assignee: null,
name: 'standalone-subtask',
description: null,
createdDate: 1538059139420,
dueDate: null,
claimedDate: null,
priority: 0,
category: null,
processDefinitionId: null,
processInstanceId: null,
status: 'CREATED',
owner: 'devopsuser',
parentTaskId: '71fda20b-c25b-11e8-b75b-0a5864600540',
lastModified: 1538059139420,
lastModifiedTo: null,
lastModifiedFrom: null,
standalone: true
}
}
],
pagination: {
skipCount: 0,
maxItems: 100,
count: 1,
hasMoreItems: false,
totalItems: 1
}
}
};
export let fakeCustomSchema = export let fakeCustomSchema =
[ [
new ObjectDataColumn({ new ObjectDataColumn({

View File

@ -72,3 +72,51 @@ export class TaskQueryCloudRequestModel {
} }
} }
} }
export class 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;
constructor(obj?: any) {
if (obj) {
this.appName = obj.appName;
this.appVersion = obj.appVersion;
this.id = obj.id;
this.status = obj.status;
this.maxItems = obj.maxItems;
this.skipCount = obj.skipCount;
this.sorting = obj.sorting;
this.activityName = obj.activityName;
this.activityType = obj.activityType;
this.elementId = obj.elementId;
this.executionId = obj.executionId;
this.processDefinitionKey = obj.processDefinitionKey;
this.processDefinitionVersion = obj.processDefinitionVersion;
this.processInstanceId = obj.processInstanceId;
this.completedDate = obj.completedDate;
this.startedDate = obj.startedDate;
this.serviceVersion = obj.serviceVersion;
this.serviceName = obj.serviceName;
this.serviceFullName = obj.serviceFullName;
}
}
}

View File

@ -39,3 +39,34 @@ export let taskPresetsCloudDefaultModel = {
} }
] ]
}; };
export let 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
}
]
};

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-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 { Observable, throwError } from 'rxjs';
import { TaskListCloudSortingModel } from '../models/task-list-sorting.model'; import { TaskListCloudSortingModel } from '../models/task-list-sorting.model';
import { BaseCloudService } from '../../../services/base-cloud.service'; 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<any> {
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 { private buildQueryParams(requestNode: TaskQueryCloudRequestModel): Object {
const queryParam: Object = {}; const queryParam: Object = {};
for (const property in requestNode) { for (const property in requestNode) {