[ADF-3778] TaskFilterCloud - should render only My Task and Completed task (#4000)

* Change the MyTask with assignee and generate an unique id

* save the taskfilter by user

* Add taskFilters value on the query params

* Fix the unit tests

* use js api beta version

* Rebase against development

* Filter the filters by id name or index

* Add the ability to translate and filter by key

* make properties optionals

* Remove import duplication
This commit is contained in:
Maurizio Vitale
2018-11-23 14:06:54 +00:00
committed by GitHub
parent 776d25820b
commit 91e5251bd0
12 changed files with 183 additions and 127 deletions

View File

@@ -1,10 +1,13 @@
{
"TEST_KEY": "MY TEST",
"ADF-PROCESS-LIST-CLOUD": {
"ADF_CLOUD_PROCESS_LIST": {
"MESSAGES": {
"TITLE": "No Processes Found",
"SUBTITLE":"Create a new process that you want to easily find later",
"NONE": "No process instance filter selected."
}
},
"ADF_CLOUD_TASK_FILTERS": {
"MY_TASKS": "My Tasks",
"COMPLETED_TASKS": "Completed Tasks"
}
}

View File

@@ -0,0 +1,13 @@
{
"ADF_CLOUD_TASK_PROCESS_LIST": {
"MESSAGES": {
"TITLE": "Nessun processo trovato",
"SUBTITLE":"Crea un nuovo processo",
"NONE": "Nessun filtro per processo selezionato."
}
},
"ADF_CLOUD_TASK_FILTERS": {
"MY_TASKS": "Miei compiti",
"COMPLETED_TASKS": "Compiti completati"
}
}

View File

@@ -19,8 +19,8 @@
<ng-template>
<adf-empty-content *ngIf="!emptyCustomContent"
icon="assessment"
[title]="'ADF-PROCESS-LIST-CLOUD.MESSAGES.TITLE' | translate"
[subtitle]="'ADF-PROCESS-LIST-CLOUD.MESSAGES.SUBTITLE'| translate">
[title]="'ADF_CLOUD_TASK_PROCESS_LIST.MESSAGES.TITLE' | translate"
[subtitle]="'ADF_CLOUD_TASK_PROCESS_LIST.MESSAGES.SUBTITLE'| translate">
</adf-empty-content>
<ng-content select="adf-empty-custom-content"></ng-content>
</ng-template>

View File

@@ -33,15 +33,19 @@ export class QueryModel {
}
}
}
export class FilterRepresentationModel {
export class TaskFilterCloudRepresentationModel {
id: string;
name: string;
key: string;
icon: string;
query: QueryModel;
constructor(obj?: any) {
if (obj) {
this.name = obj.name || null;
this.icon = obj.icon || null;
this.id = obj.id;
this.name = obj.name;
this.key = obj.key;
this.icon = obj.icon;
this.query = new QueryModel(obj.query);
}
}
@@ -50,3 +54,18 @@ export class FilterRepresentationModel {
return !!this.query;
}
}
export class FilterParamsModel {
id?: string;
name?: string;
key?: string;
index?: number;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || null;
this.name = obj.name || null;
this.key = obj.key || null;
this.index = obj.index;
}
}
}

View File

@@ -15,16 +15,15 @@
* limitations under the License.
*/
import { LogService, StorageService } from '@alfresco/adf-core';
import { StorageService, JwtHelperService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
import { Observable, forkJoin } from 'rxjs';
import { FilterRepresentationModel, QueryModel } from '../models/filter-cloud.model';
import { Observable } from 'rxjs';
import { TaskFilterCloudRepresentationModel, QueryModel } from '../models/filter-cloud.model';
@Injectable()
export class TaskFilterCloudService {
constructor(private logService: LogService,
private storage: StorageService) {
constructor(private storage: StorageService, private jwtService: JwtHelperService) {
}
/**
@@ -32,34 +31,14 @@ export class TaskFilterCloudService {
* @param appName Name of the target app
* @returns Observable of default filters just created
*/
public createDefaultFilters(appName: string): Observable<FilterRepresentationModel[]> {
let involvedTasksFilter = this.getInvolvedTasksFilterInstance(appName);
let involvedObservable = this.addFilter(involvedTasksFilter);
public createDefaultFilters(appName: string): Observable<TaskFilterCloudRepresentationModel[]> {
let myTasksFilter = this.getMyTasksFilterInstance(appName);
let myTaskObservable = this.addFilter(myTasksFilter);
let queuedTasksFilter = this.getQueuedTasksFilterInstance(appName);
let queuedObservable = this.addFilter(queuedTasksFilter);
this.addFilter(myTasksFilter);
let completedTasksFilter = this.getCompletedTasksFilterInstance(appName);
let completeObservable = this.addFilter(completedTasksFilter);
this.addFilter(completedTasksFilter);
return new Observable((observer) => {
forkJoin(
involvedObservable,
myTaskObservable,
queuedObservable,
completeObservable
).subscribe(
(filters) => {
observer.next(filters);
observer.complete();
},
(err: any) => {
this.logService.error(err);
});
});
return this.getTaskListFilters(appName);
}
/**
@@ -67,8 +46,9 @@ export class TaskFilterCloudService {
* @param appName Name of the target app
* @returns Observable of task filter details
*/
getTaskListFilters(appName?: string): Observable<FilterRepresentationModel[]> {
let key = 'task-filters-' + appName;
getTaskListFilters(appName?: string): Observable<TaskFilterCloudRepresentationModel[]> {
const username = this.getUsername();
let key = `task-filters-${appName}-${username}`;
const filters = JSON.parse(this.storage.getItem(key) || '[]');
return new Observable(function(observer) {
observer.next(filters);
@@ -81,8 +61,9 @@ export class TaskFilterCloudService {
* @param filter The new filter to add
* @returns Details of task filter just added
*/
addFilter(filter: FilterRepresentationModel): Observable<FilterRepresentationModel> {
const key = 'task-filters-' + filter.query.appName || '0';
addFilter(filter: TaskFilterCloudRepresentationModel): Observable<TaskFilterCloudRepresentationModel> {
const username = this.getUsername();
const key = `task-filters-${filter.query.appName}-${username}`;
let filters = JSON.parse(this.storage.getItem(key) || '[]');
filters.push(filter);
@@ -95,25 +76,18 @@ export class TaskFilterCloudService {
});
}
/**
* Creates and returns a filter for "Involved" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getInvolvedTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'Cancelled Tasks',
icon: 'view_headline',
query: new QueryModel(
{
appName: appName,
sort: 'id',
state: 'CANCELLED',
assignment: 'involved',
order: 'DESC'
}
)
});
getUsername(): string {
return this.getValueFromToken<string>('preferred_username');
}
getValueFromToken<T>(key: string): T {
let value;
const token = localStorage.getItem('access_token');
if (token) {
const tokenPayload = this.jwtService.decodeToken(token);
value = tokenPayload[key];
}
return <T> value;
}
/**
@@ -121,58 +95,42 @@ export class TaskFilterCloudService {
* @param appName Name of the target app
* @returns The newly created filter
*/
getMyTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'My Tasks',
getMyTasksFilterInstance(appName: string): TaskFilterCloudRepresentationModel {
const username = this.getUsername();
return new TaskFilterCloudRepresentationModel({
id: Math.random().toString(36).substr(2, 9),
name: 'ADF_CLOUD_TASK_FILTERS.MY_TASKS',
key: 'my-tasks',
icon: 'inbox',
query: new QueryModel(
{
appName: appName,
state: 'ASSIGNED',
assignment: username,
sort: 'id',
state: 'CREATED',
assignment: 'assignee',
order: 'ASC'
}
)
});
}
/**
* Creates and returns a filter for "Queued Tasks" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getQueuedTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'Suspended Tasks',
icon: 'adjust',
query: new QueryModel(
{
appName: appName,
sort: 'createdDate',
state: 'SUSPENDED',
assignment: 'candidate',
order: 'DESC'
}
)
});
}
/**
* Creates and returns a filter for "Completed" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getCompletedTasksFilterInstance(appName: string): FilterRepresentationModel {
return new FilterRepresentationModel({
name: 'Completed Tasks',
getCompletedTasksFilterInstance(appName: string): TaskFilterCloudRepresentationModel {
return new TaskFilterCloudRepresentationModel({
id: Math.random().toString(36).substr(2, 9),
name: 'ADF_CLOUD_TASK_FILTERS.COMPLETED_TASKS',
key: 'completed-tasks',
icon: 'done',
query: new QueryModel(
{
appName: appName,
sort: 'createdDate',
state: 'COMPLETED',
assignment: 'involved',
assignment: '',
sort: 'id',
order: 'ASC'
}
)

View File

@@ -1,10 +1,10 @@
<div class="menu-container">
<mat-list class="adf-menu-list" *ngIf="filters$ | async; else loading">
<mat-list-item (click)="selectFilterAndEmit(filter)" *ngFor="let filter of filters$ | async"
<mat-list class="adf-menu-list" *ngIf="filters$ | async as filterList; else loading">
<mat-list-item (click)="selectFilterAndEmit({id: filter.id})" *ngFor="let filter of filterList"
class="adf-filters__entry" [class.active]="currentFilter === filter">
<mat-icon *ngIf="showIcons && filter.icon" matListIcon class="adf-filters__entry-icon">{{filter.icon}}
</mat-icon>
<span matLine [attr.data-automation-id]="filter.name + '_filter'">{{filter.name}}</span>
<span matLine [attr.data-automation-id]="filter.name + '_filter'">{{filter.name | translate}}</span>
</mat-list-item>
</mat-list>
<ng-template #loading>

View File

@@ -19,7 +19,7 @@ import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { from, Observable } from 'rxjs';
import { FilterRepresentationModel } from '../models/filter-cloud.model';
import { TaskFilterCloudRepresentationModel, FilterParamsModel } from '../models/filter-cloud.model';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFiltersCloudComponent } from './task-filters-cloud.component';
import { By } from '@angular/platform-browser';
@@ -31,19 +31,19 @@ describe('TaskFiltersCloudComponent', () => {
let taskFilterService: TaskFilterCloudService;
let fakeGlobalFilter = [
new FilterRepresentationModel({
new TaskFilterCloudRepresentationModel({
name: 'FakeInvolvedTasks',
icon: 'adjust',
id: 10,
filter: {state: 'open', assignment: 'fake-involved'}
}),
new FilterRepresentationModel({
new TaskFilterCloudRepresentationModel({
name: 'FakeMyTasks1',
icon: 'done',
id: 11,
filter: {state: 'open', assignment: 'fake-assignee'}
}),
new FilterRepresentationModel({
new TaskFilterCloudRepresentationModel({
name: 'FakeMyTasks2',
icon: 'inbox',
id: 12,
@@ -218,7 +218,7 @@ describe('TaskFiltersCloudComponent', () => {
it('should select the task filter based on the input by name param', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ name: 'FakeMyTasks1' });
component.filterParam = new FilterParamsModel({ name: 'FakeMyTasks1' });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
@@ -236,7 +236,7 @@ describe('TaskFiltersCloudComponent', () => {
it('should select the default task filter if filter input does not exist', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ name: 'UnexistableFilter' });
component.filterParam = new FilterParamsModel({ name: 'UnexistableFilter' });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
@@ -255,7 +255,7 @@ describe('TaskFiltersCloudComponent', () => {
it('should select the task filter based on the input by index param', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ index: 2 });
component.filterParam = new FilterParamsModel({ index: 2 });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
@@ -274,7 +274,7 @@ describe('TaskFiltersCloudComponent', () => {
it('should select the task filter based on the input by id param', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ id: 12 });
component.filterParam = new FilterParamsModel({ id: 12 });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
@@ -293,7 +293,7 @@ describe('TaskFiltersCloudComponent', () => {
it('should emit an event when a filter is selected', async(() => {
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
component.filterParam = new FilterRepresentationModel({ id: 12 });
component.filterParam = new FilterParamsModel({ id: 12 });
const appName = 'my-app-1';
let change = new SimpleChange(null, appName, true);
@@ -303,7 +303,7 @@ describe('TaskFiltersCloudComponent', () => {
spyOn(component, 'selectFilterAndEmit').and.stub();
let filterButton = fixture.debugElement.nativeElement.querySelector('span[data-automation-id="FakeMyTasks1_filter"]');
filterButton.click();
expect(component.selectFilterAndEmit).toHaveBeenCalledWith(fakeGlobalFilter[1]);
expect(component.selectFilterAndEmit).toHaveBeenCalledWith({id: fakeGlobalFilter[1].id});
}));
it('should reload filters by appName on binding changes', () => {
@@ -349,11 +349,11 @@ describe('TaskFiltersCloudComponent', () => {
});
it('should return the current filter after one is selected', () => {
let filter = fakeGlobalFilter[1];
let filter = new FilterParamsModel({ name: 'FakeInvolvedTasks' });
component.filters = fakeGlobalFilter;
expect(component.currentFilter).toBeUndefined();
component.selectFilter(filter);
expect(component.getCurrentFilter()).toBe(filter);
expect(component.getCurrentFilter()).toBe(fakeGlobalFilter[1]);
});
});

View File

@@ -18,7 +18,8 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { FilterRepresentationModel } from '../models/filter-cloud.model';
import { TaskFilterCloudRepresentationModel, FilterParamsModel } from '../models/filter-cloud.model';
import { TranslationService } from '@alfresco/adf-core';
@Component({
selector: 'adf-cloud-task-filters',
templateUrl: './task-filters-cloud.component.html',
@@ -30,13 +31,13 @@ export class TaskFiltersCloudComponent implements OnChanges {
appName: string;
@Input()
filterParam: FilterRepresentationModel;
filterParam: FilterParamsModel;
@Input()
showIcons: boolean = false;
@Output()
filterClick: EventEmitter<FilterRepresentationModel> = new EventEmitter<FilterRepresentationModel>();
filterClick: EventEmitter<TaskFilterCloudRepresentationModel> = new EventEmitter<TaskFilterCloudRepresentationModel>();
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
@@ -44,19 +45,19 @@ export class TaskFiltersCloudComponent implements OnChanges {
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
filters$: Observable<FilterRepresentationModel[]>;
filters$: Observable<TaskFilterCloudRepresentationModel[]>;
currentFilter: FilterRepresentationModel;
currentFilter: TaskFilterCloudRepresentationModel;
filters: FilterRepresentationModel [] = [];
filters: TaskFilterCloudRepresentationModel [] = [];
constructor(private taskFilterCloudService: TaskFilterCloudService) {
constructor(private taskFilterCloudService: TaskFilterCloudService, private translationService: TranslationService) {
}
ngOnChanges(changes: SimpleChanges) {
const appName = changes['appName'];
const filter = changes['filterParam'];
if (appName && appName.currentValue) {
if (appName && appName.currentValue !== appName.previousValue) {
this.getFilters(appName.currentValue);
} else if (filter && filter.currentValue !== filter.previousValue) {
this.selectFilter(filter.currentValue);
@@ -70,7 +71,7 @@ export class TaskFiltersCloudComponent implements OnChanges {
this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName);
this.filters$.subscribe(
(res: FilterRepresentationModel[]) => {
(res: TaskFilterCloudRepresentationModel[]) => {
if (res.length === 0) {
this.createFilters(appName);
} else {
@@ -93,7 +94,7 @@ export class TaskFiltersCloudComponent implements OnChanges {
this.filters$ = this.taskFilterCloudService.createDefaultFilters(appName);
this.filters$.subscribe(
(resDefault: FilterRepresentationModel[]) => {
(resDefault: TaskFilterCloudRepresentationModel[]) => {
this.resetFilter();
this.filters = resDefault;
},
@@ -103,14 +104,14 @@ export class TaskFiltersCloudComponent implements OnChanges {
);
}
/**
* Pass the selected filter as next
*/
public selectFilter(newFilter: FilterRepresentationModel) {
public selectFilter(newFilter: FilterParamsModel) {
if (newFilter) {
this.currentFilter = this.filters.find((filter) =>
this.currentFilter = this.filters.find( (filter: TaskFilterCloudRepresentationModel, index) =>
newFilter.index === index ||
newFilter.key === filter.key ||
newFilter.id === filter.id ||
(newFilter.name &&
(newFilter.name.toLocaleLowerCase() === filter.name.toLocaleLowerCase())
(newFilter.name.toLocaleLowerCase() === this.translationService.instant(filter.name).toLocaleLowerCase())
));
}
if (!this.currentFilter) {
@@ -118,7 +119,7 @@ export class TaskFiltersCloudComponent implements OnChanges {
}
}
public selectFilterAndEmit(newFilter: FilterRepresentationModel) {
public selectFilterAndEmit(newFilter: FilterParamsModel) {
this.selectFilter(newFilter);
this.filterClick.emit(this.currentFilter);
}
@@ -135,7 +136,7 @@ export class TaskFiltersCloudComponent implements OnChanges {
/**
* Return the current task
*/
getCurrentFilter(): FilterRepresentationModel {
getCurrentFilter(): TaskFilterCloudRepresentationModel {
return this.currentFilter;
}