[AAE-9186] Filter tasks by assignee (#7784)

* [AAE-9186] Filter tasks by assignee

* add status filter enum

* remove useless case

* remove useless unit test

* improve task assignment filter unit tests

* remove useless imports

* rebase

* fix lint

* run the copydist only for affected

* Build always cli and testing to use local

* Add back test and cli

Co-authored-by: Maurizio Vitale <maurizio.vitale@alfresco.com>
This commit is contained in:
Tomasz Gnyp
2022-08-26 13:53:06 +02:00
committed by GitHub
parent 2ef40b150f
commit c7d769510d
17 changed files with 598 additions and 322 deletions

View File

@@ -381,9 +381,11 @@
"ADF_CLOUD_FORM_COMPONENT": {
"RETRIEVE_METADATA": "Autofill Form"
},
"ADF_CLOUD_TASK_ASSIGNEMNT_FILTER": {
"ADF_CLOUD_TASK_ASSIGNMENT_FILTER": {
"ASSIGNED_TO": "Assigned to",
"ASSIGNED_TO_ME": "Assigned to me",
"UNASSIGNED": "Unassigned",
"NONE": "None",
"LIST_OF_CANDIDATE_GROUPS": "List of candidate groups",
"ASSIGNEE": "Assignee",
"ASSIGNMENT_TYPE": "Assignment type"

View File

@@ -28,7 +28,7 @@
</mat-expansion-panel-header>
<ng-container *ngIf="!isLoading;">
<form *ngIf="editTaskFilterForm" [formGroup]="editTaskFilterForm">
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutGap="10px" fxLayoutAlign="start center">
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutGap="10px" fxLayoutAlign="start">
<ng-container *ngFor="let taskFilterProperty of taskFilterProperties">
<mat-form-field fxFlex="23%"
[floatLabel]="'auto'"
@@ -36,7 +36,8 @@
[attr.data-automation-id]="taskFilterProperty.key">
<mat-select placeholder="{{taskFilterProperty.label | translate}}"
[formControlName]="taskFilterProperty.key"
[attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key">
[attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key"
(selectionChange)="onStatusChange($event)">
<mat-option *ngFor="let propertyOption of taskFilterProperty.options"
[value]="propertyOption.value"
[attr.data-automation-id]="'adf-cloud-edit-task-property-options-' + taskFilterProperty.key">
@@ -109,9 +110,13 @@
<adf-cloud-task-assignment-filter fxFlex="23%"
*ngIf="taskFilterProperty.type === 'assignment'"
[taskFilterProperty]="taskFilterProperty"
(assignedChange)="onAssignedChange($event)"
(assignedGroupChange)="onAssignedGroupsChange($event)">
[status]="selectedStatus"
[appName]="appName"
(assignedUsersChange)="onAssignedUsersChange($event)"
(assignedGroupsChange)="onAssignedGroupsChange($event)"
(assignmentTypeChange)="onAssignmentTypeChange($event)">
</adf-cloud-task-assignment-filter>
</ng-container>
</div>
</form>

View File

@@ -16,7 +16,7 @@
*/
import { OnChanges, SimpleChanges, OnInit, OnDestroy, Directive, Input, Output, EventEmitter } from '@angular/core';
import { FilterOptions, TaskFilterAction, TaskFilterProperties } from '../../models/filter-cloud.model';
import { AssignmentType, FilterOptions, TaskFilterAction, TaskFilterProperties, TaskStatusFilter } from '../../models/filter-cloud.model';
import { TaskCloudService } from './../../../services/task-cloud.service';
import { AppsProcessCloudService } from './../../../../app/services/apps-process-cloud.service';
import { DateCloudFilterType, DateRangeFilter } from '../../../../models/date-cloud-filter.model';
@@ -30,6 +30,7 @@ import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filte
import { MatDialog } from '@angular/material/dialog';
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
import { MatSelectChange } from '@angular/material/select';
/* eslint-disable @typescript-eslint/naming-convention */
@@ -106,6 +107,7 @@ export abstract class BaseEditTaskFilterCloudComponent<T> implements OnInit, OnC
taskFilterProperties: TaskFilterProperties[] = [];
taskFilterActions: TaskFilterAction[] = [];
toggleFilterActions: boolean = false;
selectedStatus: TaskStatusFilter;
sortDirections: DropdownOption[] = [
{ value: 'ASC', label: 'ADF_CLOUD_TASK_FILTERS.DIRECTION.ASCENDING' },
{ value: 'DESC', label: 'ADF_CLOUD_TASK_FILTERS.DIRECTION.DESCENDING' }
@@ -305,16 +307,49 @@ export abstract class BaseEditTaskFilterCloudComponent<T> implements OnInit, OnC
this.getPropertyController(userProperty).setValue(selectedUsers);
}
onAssignedChange(assignedValue: IdentityUserModel) {
this.editTaskFilterForm.get('candidateGroups').setValue([]);
this.editTaskFilterForm.get('assignee').setValue(assignedValue?.username);
onAssignedUsersChange(assignedUsers: IdentityUserModel[]) {
this.editTaskFilterForm.get('candidateGroups').setValue(undefined);
this.editTaskFilterForm.get('assignedUsers').setValue(assignedUsers);
}
onAssignedGroupsChange(groups: IdentityGroupModel[]) {
this.editTaskFilterForm.get('assignee').setValue(null);
this.editTaskFilterForm.get('assignedUsers').setValue(undefined);
this.editTaskFilterForm.get('candidateGroups').setValue(groups);
}
onAssignmentTypeChange(assignmentType: AssignmentType) {
switch (assignmentType) {
case AssignmentType.UNASSIGNED:
this.editTaskFilterForm.get('status').setValue(TaskStatusFilter.CREATED);
this.resetAssignmentTypeValues();
break;
case AssignmentType.NONE:
this.editTaskFilterForm.get('status').setValue(TaskStatusFilter.ALL);
this.resetAssignmentTypeValues();
break;
case AssignmentType.ASSIGNED_TO:
case AssignmentType.CANDIDATE_GROUPS:
this.editTaskFilterForm.get('status').setValue(TaskStatusFilter.ASSIGNED);
this.resetAssignmentTypeValues();
break;
default:
this.editTaskFilterForm.get('status').setValue(TaskStatusFilter.ASSIGNED);
}
}
onStatusChange(status: MatSelectChange) {
if (status.value === TaskStatusFilter.CREATED) {
this.resetAssignmentTypeValues();
}
this.selectedStatus = status.value;
}
private resetAssignmentTypeValues() {
this.editTaskFilterForm.get('assignedUsers').setValue(undefined);
this.editTaskFilterForm.get('candidateGroups').setValue(undefined);
}
hasError(property: TaskFilterProperties): boolean {
const controller = this.getPropertyController(property);
return controller.errors && controller.errors.invalid;

View File

@@ -16,7 +16,6 @@
*/
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
@@ -37,11 +36,22 @@ import { AbstractControl } from '@angular/forms';
import moment from 'moment';
import { TranslateModule } from '@ngx-translate/core';
import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model';
import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
import { AssignmentType, TaskFilterCloudModel, TaskStatusFilter } from '../../models/filter-cloud.model';
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
import { MatIconTestingModule } from '@angular/material/icon/testing';
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
import {
mockAlfrescoApi,
mockCompletedDateFilter,
mockCreatedDateFilter,
mockDateFilterFromTo,
mockDateFilterStartEnd,
mockDefaultTaskFilter,
mockDueDateFilter,
mockTaskFilterIdChange
} from '../../mock/edit-task-filter-cloud.mock';
import { mockFoodUsers } from '../../../../people/mock/people-cloud.mock';
import { mockFoodGroups } from '../../../../group/mock/group-cloud.mock';
describe('EditTaskFilterCloudComponent', () => {
let component: EditTaskFilterCloudComponent;
@@ -54,14 +64,6 @@ describe('EditTaskFilterCloudComponent', () => {
let getRunningApplicationsSpy: jasmine.Spy;
let taskService: TaskCloudService;
const mock: any = {
oauth2Auth: {
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
},
isEcmLoggedIn: () => false,
reply: jasmine.createSpy('reply')
};
setupTestBed({
imports: [
TranslateModule.forRoot(),
@@ -91,7 +93,7 @@ describe('EditTaskFilterCloudComponent', () => {
name: 'fake-name'
})
} as any);
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mockAlfrescoApi);
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
fixture.detectChanges();
@@ -100,14 +102,13 @@ describe('EditTaskFilterCloudComponent', () => {
afterEach(() => fixture.destroy());
it('should fetch task filter by taskId', () => {
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
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.status).toEqual(TaskStatusFilter.CREATED);
expect(component.taskFilter.order).toEqual('ASC');
expect(component.taskFilter.sort).toEqual('id');
});
@@ -118,8 +119,7 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges();
component.filterProperties = ['processDefinitionName'];
fixture.detectChanges();
const taskFilterIdChange = new SimpleChange(null, 'mock-process-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const controller = component.editTaskFilterForm.get('processDefinitionName');
@@ -131,8 +131,7 @@ describe('EditTaskFilterCloudComponent', () => {
});
it('should display filter name as title', async () => {
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
@@ -144,9 +143,8 @@ describe('EditTaskFilterCloudComponent', () => {
});
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
@@ -156,8 +154,7 @@ describe('EditTaskFilterCloudComponent', () => {
});
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
@@ -173,8 +170,7 @@ describe('EditTaskFilterCloudComponent', () => {
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
@@ -186,8 +182,7 @@ describe('EditTaskFilterCloudComponent', () => {
describe('EditTaskFilter form', () => {
beforeEach(() => {
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
});
@@ -201,7 +196,7 @@ describe('EditTaskFilterCloudComponent', () => {
const assigneeController = component.editTaskFilterForm.get('assignee');
expect(component.editTaskFilterForm).toBeDefined();
expect(assigneeController).toBeDefined();
expect(stateController.value).toBe('CREATED');
expect(stateController.value).toBe(TaskStatusFilter.CREATED);
expect(sortController.value).toBe('id');
expect(orderController.value).toBe('ASC');
expect(assigneeController.value).toBe('fake-involved');
@@ -209,18 +204,9 @@ describe('EditTaskFilterCloudComponent', () => {
describe('Save & Delete buttons', () => {
it('should disable save and delete button for default task filters', 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'
}));
getTaskFilterSpy.and.returnValue(of(mockDefaultTaskFilter));
const taskFilterIdChange = new SimpleChange(null, 'filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.toggleFilterActions = true;
@@ -237,8 +223,7 @@ describe('EditTaskFilterCloudComponent', () => {
});
it('should enable delete button for custom task filters', async () => {
const taskFilterIdChange = new SimpleChange(null, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.toggleFilterActions = true;
@@ -255,8 +240,7 @@ describe('EditTaskFilterCloudComponent', () => {
});
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.toggleFilterActions = true;
@@ -296,18 +280,8 @@ describe('EditTaskFilterCloudComponent', () => {
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 });
getTaskFilterSpy.and.returnValue(of(mockDefaultTaskFilter));
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.toggleFilterActions = true;
@@ -334,18 +308,9 @@ describe('EditTaskFilterCloudComponent', () => {
});
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'
}));
getTaskFilterSpy.and.returnValue(of(mockDefaultTaskFilter));
const taskFilterIdChange = new SimpleChange(null, 'filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.toggleFilterActions = true;
@@ -467,11 +432,10 @@ describe('EditTaskFilterCloudComponent', () => {
});
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.ngOnChanges({ id: mockTaskFilterIdChange });
component.filterProperties = [];
fixture.detectChanges();
const stateController = component.editTaskFilterForm.get('status');
const statusController = component.editTaskFilterForm.get('status');
const sortController = component.editTaskFilterForm.get('sort');
const orderController = component.editTaskFilterForm.get('order');
@@ -479,7 +443,7 @@ describe('EditTaskFilterCloudComponent', () => {
expect(component.taskFilterProperties.length).toBe(4);
expect(component.editTaskFilterForm).toBeDefined();
expect(stateController.value).toBe('CREATED');
expect(statusController.value).toBe(TaskStatusFilter.CREATED);
expect(sortController.value).toBe('id');
expect(orderController.value).toBe('ASC');
});
@@ -487,8 +451,7 @@ describe('EditTaskFilterCloudComponent', () => {
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
const appController = component.editTaskFilterForm.get('appName');
fixture.detectChanges();
@@ -502,8 +465,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should fetch data in completedBy filter', async () => {
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'completedBy'];
fixture.detectChanges();
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
const appController = component.editTaskFilterForm.get('completedBy');
fixture.detectChanges();
@@ -519,8 +481,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should show completedBy filter', async () => {
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'completedBy'];
fixture.detectChanges();
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
@@ -532,22 +493,13 @@ describe('EditTaskFilterCloudComponent', () => {
it('should update form on completed by user is updated', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'completedBy'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const mockUser: IdentityUserModel[] = [{
id: 'id',
username: 'test',
firstName: 'first-name',
lastName: 'last-name',
email: 'email@fake.com'
}];
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('completedBy');
startedDateTypeControl.setValue('hruser');
component.onChangedUser(mockUser, {
component.onChangedUser(mockFoodUsers, {
key: 'completedBy',
label: '',
type: 'people',
@@ -557,7 +509,7 @@ describe('EditTaskFilterCloudComponent', () => {
fixture.detectChanges();
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.completedBy).toEqual(mockUser[0]);
expect(component.changedTaskFilter.completedBy).toEqual(mockFoodUsers[0]);
done();
});
component.onFilterChange();
@@ -566,20 +518,15 @@ describe('EditTaskFilterCloudComponent', () => {
it('should set the correct started date range when date range option is changed', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'dueDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('dueDateType');
startedDateTypeControl.setValue(DateCloudFilterType.TODAY);
const dateFilter = {
startFrom: moment().startOf('day').toISOString(true),
startTo: moment().endOf('day').toISOString(true)
};
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.dueDateFrom).toEqual(dateFilter.startFrom);
expect(component.changedTaskFilter.dueDateTo).toEqual(dateFilter.startTo);
expect(component.changedTaskFilter.dueDateFrom).toEqual(mockDateFilterFromTo.startFrom);
expect(component.changedTaskFilter.dueDateTo).toEqual(mockDateFilterFromTo.startTo);
done();
});
component.onFilterChange();
@@ -588,8 +535,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should have correct options on dueDate filters', () => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'dueDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-dueDateRange"] .mat-select-trigger');
@@ -607,34 +553,18 @@ describe('EditTaskFilterCloudComponent', () => {
it('should update form on date range value is updated', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'dueDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const dateFilter = {
startDate: moment().startOf('day').toISOString(true),
endDate: moment().endOf('day').toISOString(true)
};
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('dueDateType');
startedDateTypeControl.setValue(DateCloudFilterType.RANGE);
component.onDateRangeFilterChanged(dateFilter, {
key: 'dueDateRange',
label: '',
type: 'date-range',
value: '',
attributes: {
dateType: 'dueDateType',
from: '_dueDateFrom',
to: '_dueDateTo'
}
});
component.onDateRangeFilterChanged(mockDateFilterStartEnd, mockDueDateFilter);
fixture.detectChanges();
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.dueDateFrom).toEqual(dateFilter.startDate);
expect(component.changedTaskFilter.dueDateTo).toEqual(dateFilter.endDate);
expect(component.changedTaskFilter.dueDateFrom).toEqual(mockDateFilterStartEnd.startDate);
expect(component.changedTaskFilter.dueDateTo).toEqual(mockDateFilterStartEnd.endDate);
expect(component.changedTaskFilter.dueDateType).toEqual(DateCloudFilterType.RANGE);
done();
});
@@ -644,20 +574,15 @@ describe('EditTaskFilterCloudComponent', () => {
it('should set the correct completed date range when date range option is changed', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'completedDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('completedDateType');
startedDateTypeControl.setValue(DateCloudFilterType.TODAY);
const dateFilter = {
startFrom: moment().startOf('day').toISOString(true),
startTo: moment().endOf('day').toISOString(true)
};
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.completedFrom).toEqual(dateFilter.startFrom);
expect(component.changedTaskFilter.completedTo).toEqual(dateFilter.startTo);
expect(component.changedTaskFilter.completedFrom).toEqual(mockDateFilterFromTo.startFrom);
expect(component.changedTaskFilter.completedTo).toEqual(mockDateFilterFromTo.startTo);
done();
});
component.onFilterChange();
@@ -666,34 +591,18 @@ describe('EditTaskFilterCloudComponent', () => {
it('should update form on date range when completed value is updated', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'completedDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const dateFilter = {
startDate: moment().startOf('day').toISOString(true),
endDate: moment().endOf('day').toISOString(true)
};
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('completedDateType');
startedDateTypeControl.setValue(DateCloudFilterType.RANGE);
component.onDateRangeFilterChanged(dateFilter, {
key: 'completedDateType',
label: '',
type: 'date-range',
value: '',
attributes: {
dateType: 'completedDateType',
from: '_completedFrom',
to: '_completedTo'
}
});
component.onDateRangeFilterChanged(mockDateFilterStartEnd, mockCompletedDateFilter);
fixture.detectChanges();
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.completedFrom).toEqual(dateFilter.startDate);
expect(component.changedTaskFilter.completedTo).toEqual(dateFilter.endDate);
expect(component.changedTaskFilter.completedFrom).toEqual(mockDateFilterStartEnd.startDate);
expect(component.changedTaskFilter.completedTo).toEqual(mockDateFilterStartEnd.endDate);
done();
});
component.onFilterChange();
@@ -702,20 +611,15 @@ describe('EditTaskFilterCloudComponent', () => {
it('should set the correct created date range when date range option is changed', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'createdDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('createdDateType');
startedDateTypeControl.setValue(DateCloudFilterType.TODAY);
const dateFilter = {
startDate: moment().startOf('day').toISOString(true),
endDate: moment().endOf('day').toISOString(true)
};
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.createdFrom).toEqual(dateFilter.startDate);
expect(component.changedTaskFilter.createdTo).toEqual(dateFilter.endDate);
expect(component.changedTaskFilter.createdFrom).toEqual(mockDateFilterStartEnd.startDate);
expect(component.changedTaskFilter.createdTo).toEqual(mockDateFilterStartEnd.endDate);
done();
});
@@ -725,23 +629,21 @@ describe('EditTaskFilterCloudComponent', () => {
it('should show the task assignment filter', () => {
component.appName = 'fake';
component.filterProperties = ['assignment'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const assignmentComponent = fixture.debugElement.nativeElement.querySelector('adf-cloud-task-assignment-filter');
expect(assignmentComponent).toBeTruthy();
});
it('should filter by user assignment', (done) => {
const identityUserMock = { firstName: 'fake-identity-first-name', username: 'username', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
component.appName = 'fake';
component.filterProperties = ['assignment'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.onAssignedChange(identityUserMock);
component.ngOnChanges({ id: mockTaskFilterIdChange });
component.onAssignedUsersChange(mockFoodUsers);
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.assignee).toEqual(identityUserMock.username);
expect(component.changedTaskFilter.assignedUsers).toEqual(mockFoodUsers);
expect(component.changedTaskFilter.candidateGroups).toBeNull();
done();
});
component.onFilterChange();
@@ -750,34 +652,18 @@ describe('EditTaskFilterCloudComponent', () => {
it('should update form on date range when createdDate value is updated', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'createdDateRange'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const dateFilter = {
startDate: moment().startOf('day').toISOString(true),
endDate: moment().endOf('day').toISOString(true)
};
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('createdDateType');
startedDateTypeControl.setValue(DateCloudFilterType.RANGE);
component.onDateRangeFilterChanged(dateFilter, {
key: 'createdDateType',
label: '',
type: 'date-range',
value: '',
attributes: {
dateType: 'createdDateType',
from: '_createdFrom',
to: '_createdTo'
}
});
component.onDateRangeFilterChanged(mockDateFilterStartEnd, mockCreatedDateFilter);
fixture.detectChanges();
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.createdFrom).toEqual(dateFilter.startDate);
expect(component.changedTaskFilter.createdTo).toEqual(dateFilter.endDate);
expect(component.changedTaskFilter.createdFrom).toEqual(mockDateFilterStartEnd.startDate);
expect(component.changedTaskFilter.createdTo).toEqual(mockDateFilterStartEnd.endDate);
done();
});
@@ -785,19 +671,60 @@ describe('EditTaskFilterCloudComponent', () => {
});
it('should filter by candidateGroups assignment', (done) => {
const identityGroupsMock = [
{ name: 'group1'},
{ name: 'group2'}
];
component.appName = 'fake';
component.filterProperties = ['assignment'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.onAssignedGroupsChange(identityGroupsMock);
component.onAssignedGroupsChange(mockFoodGroups);
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.candidateGroups).toEqual(identityGroupsMock);
expect(component.changedTaskFilter.candidateGroups).toEqual(mockFoodGroups);
expect(component.changedTaskFilter.assignedUsers).toBeNull();
done();
});
component.onFilterChange();
});
});
describe('assignment type change', () => {
beforeEach(() => {
component.appName = 'fake';
component.filterProperties = ['assignment', 'status'];
component.ngOnChanges({ id: mockTaskFilterIdChange });
});
it('should UNASSIGNED assignment type set status to CREATED', (done) => {
component.onAssignmentTypeChange(AssignmentType.UNASSIGNED);
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.status).toEqual(TaskStatusFilter.CREATED);
expect(component.changedTaskFilter.candidateGroups).toBeNull();
expect(component.changedTaskFilter.candidateGroups).toBeNull();
done();
});
component.onFilterChange();
});
it('should NONE assignment type set status to ALL', (done) => {
component.onAssignmentTypeChange(AssignmentType.NONE);
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.status).toEqual(null);
expect(component.changedTaskFilter.candidateGroups).toBeNull();
expect(component.changedTaskFilter.candidateGroups).toBeNull();
done();
});
component.onFilterChange();
});
it('should ASSIGNED_TO status set assignment type to ASSIGNED', (done) => {
component.onAssignmentTypeChange(AssignmentType.ASSIGNED_TO);
component.filterChange.subscribe(() => {
expect(component.changedTaskFilter.status).toEqual(TaskStatusFilter.ASSIGNED);
expect(component.changedTaskFilter.candidateGroups).toBeNull();
expect(component.changedTaskFilter.candidateGroups).toBeNull();
done();
});
component.onFilterChange();
@@ -807,8 +734,7 @@ describe('EditTaskFilterCloudComponent', () => {
describe('sort properties', () => {
it('should display default sort properties', async () => {
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
expansionPanel.click();
@@ -833,9 +759,10 @@ describe('EditTaskFilterCloudComponent', () => {
priority: '12'
}));
fixture.detectChanges();
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
expansionPanel.click();
fixture.detectChanges();
@@ -853,8 +780,7 @@ describe('EditTaskFilterCloudComponent', () => {
});
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.sortProperties = [];
fixture.detectChanges();
@@ -878,8 +804,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should display default filter actions', async () => {
component.toggleFilterActions = true;
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
expansionPanel.click();
@@ -900,9 +825,10 @@ describe('EditTaskFilterCloudComponent', () => {
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 });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
component.toggleFilterActions = true;
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
@@ -924,8 +850,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should set the correct lastModifiedTo date', (done) => {
component.appName = 'fake';
component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified'];
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
const lastModifiedToControl: AbstractControl = component.editTaskFilterForm.get('lastModifiedTo');
@@ -950,8 +875,7 @@ describe('EditTaskFilterCloudComponent', () => {
describe('edit filter actions', () => {
beforeEach(() => {
const taskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ id: taskFilterIdChange });
component.ngOnChanges({ id: mockTaskFilterIdChange });
fixture.detectChanges();
spyOn(component.action, 'emit').and.callThrough();
});

View File

@@ -23,7 +23,7 @@ import { takeUntil, map } from 'rxjs/operators';
import { Observable } from 'rxjs';
import moment, { Moment } from 'moment';
import { TaskFilterCloudModel, TaskFilterProperties, TaskFilterAction } from '../../models/filter-cloud.model';
import { TaskFilterCloudModel, TaskFilterProperties, TaskFilterAction, TaskStatusFilter } from '../../models/filter-cloud.model';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { TranslationService, UserPreferencesService } from '@alfresco/adf-core';
import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
@@ -142,19 +142,18 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
private getStatusOptions(): DropdownOption[] {
return [
{ value: '', label: 'ADF_CLOUD_TASK_FILTERS.STATUS.ALL' },
{ value: 'CREATED', label: 'ADF_CLOUD_TASK_FILTERS.STATUS.CREATED' },
{ value: 'ASSIGNED', label: 'ADF_CLOUD_TASK_FILTERS.STATUS.ASSIGNED' },
{ value: 'SUSPENDED', label: 'ADF_CLOUD_TASK_FILTERS.STATUS.SUSPENDED' },
{ value: 'CANCELLED', label: 'ADF_CLOUD_TASK_FILTERS.STATUS.CANCELLED' },
{ value: 'COMPLETED', label: 'ADF_CLOUD_TASK_FILTERS.STATUS.COMPLETED' }
{ value: TaskStatusFilter.ALL, label: 'ADF_CLOUD_TASK_FILTERS.STATUS.ALL' },
{ value: TaskStatusFilter.CREATED, label: 'ADF_CLOUD_TASK_FILTERS.STATUS.CREATED' },
{ value: TaskStatusFilter.ASSIGNED, label: 'ADF_CLOUD_TASK_FILTERS.STATUS.ASSIGNED' },
{ value: TaskStatusFilter.SUSPENDED, label: 'ADF_CLOUD_TASK_FILTERS.STATUS.SUSPENDED' },
{ value: TaskStatusFilter.CANCELLED, label: 'ADF_CLOUD_TASK_FILTERS.STATUS.CANCELLED' },
{ value: TaskStatusFilter.COMPLETED, label: 'ADF_CLOUD_TASK_FILTERS.STATUS.COMPLETED' }
];
}
createTaskFilterProperties(): TaskFilterProperties[] {
const statusOptions = this.getStatusOptions();
const sortProperties = this.createSortProperties;
return [
{
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.APP_NAME',
@@ -303,11 +302,12 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.ASSIGNMENT',
type: 'assignment',
key: 'assignment',
attributes: { assignee: 'assignee', candidateGroups: 'candidateGroups'},
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups'},
value: {
assignee: this.taskFilter.assignee || null,
assignedUsers: this.taskFilter.assignedUsers || [],
candidateGroups: this.taskFilter.candidateGroups || []
}
},
selectionMode: 'multiple'
}
];
}

View File

@@ -1,20 +1,35 @@
<div class="adf-cloud-assignment-container" fxLayout="row">
<div class="adf-cloud-assignment-container">
<mat-form-field [floatLabel]="'auto'">
<mat-select class="adf-task-assignment-filter"
placeholder="{{ 'ADF_CLOUD_TASK_ASSIGNEMNT_FILTER.ASSIGNMENT_TYPE' | translate }}"
[(ngModel)]="assignmentType"
(ngModelChange)="onAssignmentTypeChange(assignmentType)">
<mat-option [value]="assignmentTypeList.currentUser">{{ 'ADF_CLOUD_TASK_ASSIGNEMNT_FILTER.ASSIGNED_TO_ME' | translate }} </mat-option>
<mat-option [value]="assignmentTypeList.unassigned">{{ 'ADF_CLOUD_TASK_ASSIGNEMNT_FILTER.UNASSIGNED' | translate }} </mat-option>
<mat-option [value]="assignmentTypeList.candidateGroups">{{ 'ADF_CLOUD_TASK_ASSIGNEMNT_FILTER.LIST_OF_CANDIDATE_GROUPS' | translate }} </mat-option>
[attr.data-automation-id]="'adf-task-assignment-filter-select'"
(selectionChange)="onAssignmentTypeChange($event)">
<mat-option *ngFor="let assignmentType of assignmentTypeOptions"
[value]="assignmentType.value"
[attr.data-automation-id]="'adf-task-assignment-filter-' + assignmentType.value">
{{ assignmentType.label | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<adf-cloud-group class="adf-group-cloud-filter" *ngIf="isCandidateGroupsType()"
<adf-cloud-group *ngIf="isCandidateGroupsType()"
class="adf-group-cloud-filter"
data-automation-id="adf-group-cloud-candidate-groups-filter"
[mode]="'multiple'"
[appName]="appName"
[preSelectGroups]="candidateGroups"
[searchGroupsControl]="groupForm"
(changedGroups)="onChangedGroups($event)"
[title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.CANDIDATE_GROUP'"></adf-cloud-group>
[mode]="taskFilterProperty.selectionMode"
[title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.CANDIDATE_GROUP'"
(changedGroups)="onChangedGroups($event)">
</adf-cloud-group>
<adf-cloud-people *ngIf="isAssignedToType()"
class="adf-group-cloud-filter"
data-automation-id="adf-group-cloud-assigned-to-filter"
[preSelectUsers]="assignedUsers"
[title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
[appName]="appName"
(changedUsers)="onChangedAssignedUsers($event)">
</adf-cloud-people>
</div>

View File

@@ -1,5 +1,6 @@
.adf-cloud-assignment-container {
align-items: center;
display: flex;
flex-wrap: wrap;
mat-form-field {
width: 100%;
@@ -11,7 +12,6 @@
}
.adf-group-cloud-filter {
margin-left: 15px;
flex: 1;
width: 100%;
}

View File

@@ -21,19 +21,32 @@ import { TranslateModule } from '@ngx-translate/core';
import { TaskAssignmentFilterCloudComponent } from './task-assignment-filter.component';
import { GroupCloudModule } from '../../../../group/group-cloud.module';
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { AssignmentType } from '../../models/filter-cloud.model';
import { AssignmentType, TaskStatusFilter } from '../../models/filter-cloud.model';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { By } from '@angular/platform-browser';
import { DebugElement, SimpleChange } from '@angular/core';
import { mockFoodUsers } from '../../../../people/mock/people-cloud.mock';
import { mockFoodGroups } from '../../../../group/mock/group-cloud.mock';
describe('EditTaskFilterCloudComponent', () => {
describe('TaskAssignmentFilterComponent', () => {
let component: TaskAssignmentFilterCloudComponent;
let fixture: ComponentFixture<TaskAssignmentFilterCloudComponent>;
let identityUserService: IdentityUserService;
const identityUserMock = {
firstName: 'fake-identity-first-name',
lastName: 'fake-identity-last-name',
email: 'fakeIdentity@email.com'
};
function selectAssignmentType(type: AssignmentType) {
const assignmentTypeChangeSpy = spyOn(component.assignmentTypeChange, 'emit');
const assignmentTypeSelect: DebugElement = fixture.debugElement.query(By.css(`[data-automation-id="adf-task-assignment-filter-select"]`));
assignmentTypeSelect.nativeElement.click();
fixture.detectChanges();
const assignmentOption: DebugElement = fixture.debugElement.query(By.css(`[data-automation-id="adf-task-assignment-filter-${type}"]`));
assignmentOption.nativeElement.click();
fixture.detectChanges();
expect(assignmentTypeChangeSpy).toHaveBeenCalledWith(type);
}
setupTestBed({
imports: [
@@ -47,43 +60,147 @@ describe('EditTaskFilterCloudComponent', () => {
]
});
beforeEach(() => {
fixture = TestBed.createComponent(TaskAssignmentFilterCloudComponent);
component = fixture.componentInstance;
identityUserService = TestBed.inject(IdentityUserService);
component.taskFilterProperty = {
key: 'assignment',
label: 'mock-filter',
value: null,
type: 'dateRange',
attributes: null,
options: null
};
fixture.detectChanges();
describe('inputs', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TaskAssignmentFilterCloudComponent);
component = fixture.componentInstance;
identityUserService = TestBed.inject(IdentityUserService);
component.taskFilterProperty = {
key: 'assignment',
label: 'mock-filter',
value: {},
type: 'assignment',
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups' }
};
fixture.detectChanges();
});
afterEach(() => fixture.destroy());
it('should display all available assignment types', () => {
const assignmentTypeSelect: DebugElement = fixture.debugElement.query(By.css(`[data-automation-id="adf-task-assignment-filter-select"]`));
assignmentTypeSelect.nativeElement.click();
fixture.detectChanges();
const assignmentTypeOptions: DebugElement[] = fixture.debugElement.queryAll(By.css('mat-option'));
expect(assignmentTypeOptions.length).toEqual(5);
expect(assignmentTypeOptions[0].nativeElement.innerText).toEqual('ADF_CLOUD_TASK_ASSIGNMENT_FILTER.NONE');
expect(assignmentTypeOptions[1].nativeElement.innerText).toEqual('ADF_CLOUD_TASK_ASSIGNMENT_FILTER.UNASSIGNED');
expect(assignmentTypeOptions[2].nativeElement.innerText).toEqual('ADF_CLOUD_TASK_ASSIGNMENT_FILTER.ASSIGNED_TO');
expect(assignmentTypeOptions[3].nativeElement.innerText).toEqual('ADF_CLOUD_TASK_ASSIGNMENT_FILTER.CURRENT_USER');
expect(assignmentTypeOptions[4].nativeElement.innerText).toEqual('ADF_CLOUD_TASK_ASSIGNMENT_FILTER.CANDIDATE_GROUPS');
});
it('should emit the current user info when assignment is the current user', () => {
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(mockFoodUsers[0]);
spyOn(component.assignedUsersChange, 'emit');
selectAssignmentType(AssignmentType.CURRENT_USER);
expect(component.assignedUsersChange.emit).toHaveBeenCalledWith([mockFoodUsers[0]]);
});
it('should show the CANDIDATE_GROUPS input', () => {
selectAssignmentType(AssignmentType.CANDIDATE_GROUPS);
const candidateGroups = fixture.debugElement.query(By.css('[data-automation-id="adf-group-cloud-candidate-groups-filter"]'));
expect(component.candidateGroups.length).toEqual(0);
expect(candidateGroups).toBeTruthy();
});
it('should show the ASSIGNED_TO input', () => {
selectAssignmentType(AssignmentType.ASSIGNED_TO);
const candidateGroups = fixture.debugElement.query(By.css('[data-automation-id="adf-group-cloud-assigned-to-filter"]'));
expect(component.assignedUsers.length).toEqual(0);
expect(candidateGroups).toBeTruthy();
});
it('should have floating labels when values are present', () => {
const inputLabelsNodes = document.querySelectorAll('mat-form-field');
inputLabelsNodes.forEach(labelNode => {
expect(labelNode.getAttribute('ng-reflect-float-label')).toBe('auto');
});
});
});
afterEach(() => fixture.destroy());
describe('status input change', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TaskAssignmentFilterCloudComponent);
component = fixture.componentInstance;
});
it('should emit the current user info when assignment is the current user', () => {
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock as any);
spyOn(component.assignedChange, 'emit');
component.onAssignmentTypeChange(AssignmentType.CURRENT_USER);
fixture.detectChanges();
expect(component.assignedChange.emit).toHaveBeenCalledWith(identityUserMock);
afterEach(() => fixture.destroy());
it('should CREATED status set assignment type to UNASSIGNED', () => {
const createdStatusChange = new SimpleChange(null, TaskStatusFilter.CREATED, true);
component.ngOnChanges({status: createdStatusChange});
expect(component.assignmentType).toEqual(AssignmentType.UNASSIGNED);
});
it('should ASSIGNED status set assignment type to ASSIGNED_TO', () => {
const createdStatusChange = new SimpleChange(null, TaskStatusFilter.ASSIGNED, true);
component.ngOnChanges({status: createdStatusChange});
expect(component.assignmentType).toEqual(AssignmentType.ASSIGNED_TO);
});
it('should ALL status set assignment type to NONE', () => {
const createdStatusChange = new SimpleChange(null, TaskStatusFilter.ALL, true);
component.ngOnChanges({status: createdStatusChange});
expect(component.assignmentType).toEqual(AssignmentType.NONE);
});
});
it('should show the candidate groups', () => {
component.assignmentType = AssignmentType.CANDIDATE_GROUPS;
fixture.detectChanges();
const candidateGroups = fixture.debugElement.nativeElement.querySelector('.adf-group-cloud-filter');
expect(candidateGroups).toBeTruthy();
});
describe('set initial assignment type', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TaskAssignmentFilterCloudComponent);
component = fixture.componentInstance;
});
it('should have floating labels when values are present', () => {
fixture.detectChanges();
const inputLabelsNodes = document.querySelectorAll('mat-form-field');
inputLabelsNodes.forEach(labelNode => {
expect(labelNode.getAttribute('ng-reflect-float-label')).toBe('auto');
afterEach(() => fixture.destroy());
it('should set assignment type to ASSIGNED_TO if initial assignedUsers exists', () => {
component.taskFilterProperty = {
key: 'assignment',
label: 'mock-filter',
value: { assignedUsers: mockFoodUsers },
type: 'assignment',
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups'}
};
fixture.detectChanges();
expect(component.assignmentType).toEqual(AssignmentType.ASSIGNED_TO);
});
it('should set assignment type to CANDIDATE_GROUPS if initial candidateGroups exists', () => {
component.taskFilterProperty = {
key: 'assignment',
label: 'mock-filter',
value: { candidateGroups: mockFoodGroups },
type: 'assignment',
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups'}
};
fixture.detectChanges();
expect(component.assignmentType).toEqual(AssignmentType.CANDIDATE_GROUPS);
});
it('should set assignment type to NONE if initial value is empty', () => {
component.taskFilterProperty = {
key: 'assignment',
label: 'mock-filter',
value: {},
type: 'assignment',
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups'}
};
fixture.detectChanges();
expect(component.assignmentType).toEqual(AssignmentType.NONE);
});
});
});

View File

@@ -15,42 +15,54 @@
* limitations under the License.
*/
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { AssignmentType, TaskFilterProperties } from '../../models/filter-cloud.model';
import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
import { MatSelectChange } from '@angular/material/select';
import { AssignmentType, TaskFilterProperties, TaskStatusFilter } from '../../models/filter-cloud.model';
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
import { DropdownOption } from '../edit-task-filters/base-edit-task-filter-cloud.component';
import { FormControl } from '@angular/forms';
@Component({
selector: 'adf-cloud-task-assignment-filter',
templateUrl: './task-assignment-filter.component.html',
styleUrls: ['./task-assignment-filter.component.scss']
})
export class TaskAssignmentFilterCloudComponent implements OnInit {
export class TaskAssignmentFilterCloudComponent implements OnInit, OnChanges {
@Input() appName: string;
@Input() taskFilterProperty: TaskFilterProperties;
@Output() assignedChange = new EventEmitter<IdentityUserModel>();
@Input() status: TaskStatusFilter;
@Output() assignedGroupChange = new EventEmitter<IdentityGroupModel[]>();
@Output() assignedUsersChange = new EventEmitter<IdentityUserModel[]>();
@Output() assignedGroupsChange = new EventEmitter<IdentityGroupModel[]>();
@Output() assignmentTypeChange = new EventEmitter<AssignmentType>();
assignmentType: AssignmentType;
assignedUsers: IdentityUserModel[] = [];
candidateGroups: IdentityGroupModel[] = [];
groupForm = new UntypedFormControl('');
assignmentTypeList = {
unassigned: AssignmentType.UNASSIGNED,
currentUser: AssignmentType.CURRENT_USER,
candidateGroups: AssignmentType.CANDIDATE_GROUPS
};
groupForm = new FormControl('');
assignmentTypeOptions: DropdownOption[];
constructor(private identityUserService: IdentityUserService) {}
ngOnChanges(changes: SimpleChanges): void {
if (changes?.status?.currentValue !== changes?.status?.previousValue) {
this.changeAssignmentTypeByStatus(changes?.status?.currentValue);
}
}
ngOnInit() {
this.assignmentTypeOptions = this.getAssignmentTypeOptions();
if (this.isFilterPropertyDefined()) {
this.setDefaultAssignedGroups();
this.setInitialCandidateGroups();
this.setInitialAssignedUsers();
this.setDefaultAssignmentType();
}
}
@@ -59,35 +71,73 @@ export class TaskAssignmentFilterCloudComponent implements OnInit {
return this.assignmentType === AssignmentType.CANDIDATE_GROUPS;
}
onAssignmentTypeChange(type: any) {
isAssignedToType(): boolean {
return this.assignmentType === AssignmentType.ASSIGNED_TO;
}
onAssignmentTypeChange(assignmentChange: MatSelectChange) {
this.candidateGroups = [];
if (type === AssignmentType.CURRENT_USER) {
this.assignedChange.emit(this.identityUserService.getCurrentUserInfo());
} else if (type === AssignmentType.UNASSIGNED) {
this.assignedChange.emit(null);
this.assignedUsers = [];
if (assignmentChange.value === AssignmentType.CURRENT_USER) {
this.assignedUsersChange.emit([this.identityUserService.getCurrentUserInfo()]);
} else if (assignmentChange.value === AssignmentType.NONE) {
this.assignedUsersChange.emit([]);
}
this.assignmentType = assignmentChange.value;
this.assignmentTypeChange.emit(this.assignmentType);
}
onChangedGroups(groups: IdentityGroupModel[]) {
this.assignedGroupChange.emit(groups);
this.assignedGroupsChange.emit(groups);
}
private setDefaultAssignmentType() {
const assignmentAttr = this.taskFilterProperty.attributes['assignee'];
const assignee = this.taskFilterProperty.value[assignmentAttr];
onChangedAssignedUsers(users: IdentityUserModel[]) {
this.assignedUsersChange.emit(users);
}
if (this.candidateGroups.length > 0) {
this.assignmentType = AssignmentType.CANDIDATE_GROUPS;
} else if (assignee) {
this.assignmentType = AssignmentType.CURRENT_USER;
} else {
this.assignmentType = AssignmentType.UNASSIGNED;
private changeAssignmentTypeByStatus(status: TaskStatusFilter) {
switch (status) {
case TaskStatusFilter.CREATED:
this.assignmentType = AssignmentType.UNASSIGNED;
break;
case TaskStatusFilter.ASSIGNED:
this.assignmentType = AssignmentType.ASSIGNED_TO;
break;
default:
this.assignmentType = AssignmentType.NONE;
}
}
private setDefaultAssignedGroups() {
const assignmentGroupsAttr = this.taskFilterProperty.attributes['candidateGroups'];
this.candidateGroups = this.taskFilterProperty.value[assignmentGroupsAttr];
private getAssignmentTypeOptions(): DropdownOption[] {
return [
{ value: AssignmentType.NONE, label: `ADF_CLOUD_TASK_ASSIGNMENT_FILTER.${AssignmentType.NONE}` },
{ value: AssignmentType.UNASSIGNED, label: `ADF_CLOUD_TASK_ASSIGNMENT_FILTER.${AssignmentType.UNASSIGNED}` },
{ value: AssignmentType.ASSIGNED_TO, label: `ADF_CLOUD_TASK_ASSIGNMENT_FILTER.${AssignmentType.ASSIGNED_TO}` },
{ value: AssignmentType.CURRENT_USER, label: `ADF_CLOUD_TASK_ASSIGNMENT_FILTER.${AssignmentType.CURRENT_USER}` },
{ value: AssignmentType.CANDIDATE_GROUPS, label: `ADF_CLOUD_TASK_ASSIGNMENT_FILTER.${AssignmentType.CANDIDATE_GROUPS}` }
];
}
private setDefaultAssignmentType() {
if (this.candidateGroups?.length) {
this.assignmentType = AssignmentType.CANDIDATE_GROUPS;
} else if (this.assignedUsers?.length) {
this.assignmentType = AssignmentType.ASSIGNED_TO;
} else {
this.assignmentType = AssignmentType.NONE;
}
}
private setInitialCandidateGroups() {
const candidateGroupsAttr = this.taskFilterProperty.attributes['candidateGroups'];
this.candidateGroups = this.taskFilterProperty.value[candidateGroupsAttr];
}
private setInitialAssignedUsers() {
const assignedUsersAttr = this.taskFilterProperty.attributes['assignedUsers'];
this.assignedUsers = this.taskFilterProperty.value[assignedUsersAttr];
}
private isFilterPropertyDefined(): boolean {

View File

@@ -0,0 +1,86 @@
/*!
* @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 moment from 'moment';
import { fakeApplicationInstance } from '../../../app/mock/app-model.mock';
export const mockAlfrescoApi: any = {
oauth2Auth: {
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
},
isEcmLoggedIn: () => false,
reply: jasmine.createSpy('reply')
};
export const mockTaskFilterIdChange = new SimpleChange(undefined, 'mock-task-filter-id', true);
export const mockDefaultTaskFilter = {
name: 'ADF_CLOUD_TASK_FILTERS.MY_TASKS',
id: 'filter-id',
key: 'all-fake-task',
icon: 'adjust',
sort: 'startDate',
status: 'ALL',
order: 'DESC'
};
export const mockDateFilterFromTo = {
startFrom: moment().startOf('day').toISOString(true),
startTo: moment().endOf('day').toISOString(true)
};
export const mockDateFilterStartEnd = {
startDate: moment().startOf('day').toISOString(true),
endDate: moment().endOf('day').toISOString(true)
};
export const mockDueDateFilter = {
key: 'dueDateRange',
label: '',
type: 'date-range',
value: '',
attributes: {
dateType: 'dueDateType',
from: '_dueDateFrom',
to: '_dueDateTo'
}
};
export const mockCompletedDateFilter = {
key: 'completedDateType',
label: '',
type: 'date-range',
value: '',
attributes: {
dateType: 'completedDateType',
from: '_completedFrom',
to: '_completedTo'
}
};
export const mockCreatedDateFilter = {
key: 'createdDateType',
label: '',
type: 'date-range',
value: '',
attributes: {
dateType: 'createdDateType',
from: '_createdFrom',
to: '_createdTo'
}
};

View File

@@ -16,7 +16,7 @@
*/
import { assignedTaskDetailsCloudMock } from '../../task-header/mocks/task-details-cloud.mock';
import { TaskFilterCloudModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
import { TaskFilterCloudModel, ServiceTaskFilterCloudModel, AssignmentType, TaskStatusFilter } from '../models/filter-cloud.model';
export const fakeGlobalFilter: any[] = [
{
@@ -75,10 +75,11 @@ export const fakeFilter = new TaskFilterCloudModel({
name: 'FakeInvolvedTasks',
icon: 'adjust',
id: 'mock-task-filter-id',
status: 'CREATED',
status: TaskStatusFilter.CREATED,
appName: 'mock-app-name',
processDefinitionId: 'process-def-id',
assignee: 'fake-involved',
assignment: AssignmentType.NONE,
order: 'ASC',
sort: 'id',
completedBy: {
@@ -103,7 +104,7 @@ export const fakeAllTaskFilter = new TaskFilterCloudModel({
name: 'AllTasks',
icon: 'adjust',
id: 'mock-task-filter-id',
status: '',
status: TaskStatusFilter.ALL,
appName: 'mock-app-name',
processDefinitionId: 'process-def-id',
assignee: 'fake-involved',
@@ -296,7 +297,7 @@ export const defaultTaskFiltersMock = [
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'CREATED',
status: TaskStatusFilter.CREATED,
order: 'DESC'
},
{
@@ -306,7 +307,7 @@ export const defaultTaskFiltersMock = [
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'ASSIGNED',
status: TaskStatusFilter.ASSIGNED,
order: 'DESC'
},
{
@@ -316,7 +317,7 @@ export const defaultTaskFiltersMock = [
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'COMPLETED',
status: TaskStatusFilter.COMPLETED,
order: 'DESC'
}
];

View File

@@ -32,9 +32,10 @@ export class TaskFilterCloudModel {
icon: string;
index: number;
appName: string;
status: string;
status: TaskStatusFilter;
sort: string;
assignee: string;
assignedUsers: IdentityUserModel[];
candidateGroups: IdentityGroupModel[];
order: string;
owner: string;
@@ -77,6 +78,7 @@ export class TaskFilterCloudModel {
this.status = obj.status || null;
this.sort = obj.sort || null;
this.assignee = obj.assignee || null;
this.assignedUsers = obj.assignedUsers || null;
this.order = obj.order || null;
this.owner = obj.owner || null;
this.processDefinitionName = obj.processDefinitionName || null;
@@ -234,7 +236,18 @@ export interface FilterOptions {
export enum AssignmentType {
CURRENT_USER = 'CURRENT_USER',
UNASSIGNED = 'UNASSIGNED',
CANDIDATE_GROUPS = 'CANDIDATE_GROUPS'
NONE = 'NONE',
CANDIDATE_GROUPS = 'CANDIDATE_GROUPS',
ASSIGNED_TO = 'ASSIGNED_TO'
}
export enum TaskStatusFilter {
ALL = '',
CREATED = 'CREATED',
ASSIGNED = 'ASSIGNED',
SUSPENDED = 'SUSPENDED',
CANCELLED = 'CANCELLED',
COMPLETED = 'COMPLETED'
}
export interface TaskFilterProperties {