Merge pull request #800 from Alfresco/dev-mvitale-776

Automatically open the new task when the task is completed
This commit is contained in:
Mario Romano 2016-09-23 10:54:24 +02:00 committed by GitHub
commit 11d4ac4427
8 changed files with 196 additions and 45 deletions

View File

@ -77,30 +77,40 @@ export class ActivitiProcessFilters implements OnInit, OnChanges {
this.filters.push(filter);
});
this.load();
this.getFilters(this.appId, this.appName);
}
ngOnChanges(changes: SimpleChanges) {
let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) {
this.load();
this.getFiltersByAppId(appId.currentValue);
return;
}
let appName = changes['appName'];
if (appName && appName.currentValue) {
this.getFiltersByAppName(appName.currentValue);
return;
}
}
/**
* The method call the adapter data table component for render the task list
* @param tasks
* Return the task list filtered by appId or by appName
* @param appId
* @param appName
*/
private load() {
if (this.appName) {
this.filterByAppName();
getFilters(appId?: string, appName?: string) {
if (appName) {
this.getFiltersByAppName(appName);
} else {
this.filterByAppId(this.appId);
this.getFiltersByAppId(appId);
}
}
private filterByAppId(appId) {
/**
* Return the filter list filtered by appId
* @param appId - optional
*/
getFiltersByAppId(appId?: string) {
this.activiti.getProcessFilters(appId).subscribe(
(res: FilterRepresentationModel[]) => {
this.resetFilter();
@ -117,10 +127,14 @@ export class ActivitiProcessFilters implements OnInit, OnChanges {
);
}
private filterByAppName() {
this.activiti.getDeployedApplications(this.appName).subscribe(
/**
* Return the filter list filtered by appName
* @param appName
*/
getFiltersByAppName(appName: string) {
this.activiti.getDeployedApplications(appName).subscribe(
application => {
this.filterByAppId(application.id);
this.getFiltersByAppId(application.id);
this.selectFirstFilter();
},
(err) => {

View File

@ -106,8 +106,13 @@ The component shows the details of the task id passed in input
#### Options
**taskId**: { string } required) The id of the task details that we
are asking for.
**taskId**: { string } required) The id of the task details that we are asking for.
**showNextTask**: { boolean } optional) Automatically render the next one, when the task is completed.
**showFormTitle**: { boolean } optional) Toggle rendering of the form title.
**readOnlyForm**: { boolean } optional) Toggle readonly state of the form. Enforces all form widgets render readonly if enabled.
**showFormRefreshButton**: { boolean } optional) Toggle rendering of the `Refresh` button.
**showFormSaveButton**: { boolean } optional) Toggle rendering of the `Save` outcome button.
**showFormCompleteButton**: { boolean } optional) Toggle rendering of the Form `Complete` outcome button
### Custom 'empty Activiti Task Details' template

View File

@ -21,7 +21,7 @@ import {
expect,
beforeEach
} from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { ActivitiFilters } from './activiti-filters.component';
import { ActivitiTaskListService } from '../services/activiti-tasklist.service';
import { Observable } from 'rxjs/Rx';
@ -89,6 +89,7 @@ describe('ActivitiFilters', () => {
});
it('should emit an error with a bad response', (done) => {
filterList.appId = '1';
spyOn(filterList.activiti, 'getTaskListFilters').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise));
filterList.onError.subscribe((err) => {
@ -99,6 +100,18 @@ describe('ActivitiFilters', () => {
filterList.ngOnInit();
});
it('should emit an error with a bad response', (done) => {
filterList.appName = 'fake-app';
spyOn(filterList.activiti, 'getDeployedApplications').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise));
filterList.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
filterList.ngOnInit();
});
it('should emit an event when a filter is selected', (done) => {
let currentFilter = new FilterRepresentationModel({filter: { state: 'open', assignment: 'fake-involved'}});
@ -112,4 +125,41 @@ describe('ActivitiFilters', () => {
filterList.selectFilter(currentFilter);
});
it('should reload filters by appId on binding changes', () => {
spyOn(filterList, 'getFiltersByAppId').and.stub();
const appId = '1';
let change = new SimpleChange(null, appId);
filterList.ngOnChanges({ 'appId': change });
expect(filterList.getFiltersByAppId).toHaveBeenCalledWith(appId);
});
it('should reload filters by appId null on binding changes', () => {
spyOn(filterList, 'getFiltersByAppId').and.stub();
const appId = null;
let change = new SimpleChange(null, appId);
filterList.ngOnChanges({ 'appId': change });
expect(filterList.getFiltersByAppId).toHaveBeenCalledWith(appId);
});
it('should reload filters by app name on binding changes', () => {
spyOn(filterList, 'getFiltersByAppName').and.stub();
const appName = 'fake-app-name';
let change = new SimpleChange(null, appName);
filterList.ngOnChanges({ 'appName': change });
expect(filterList.getFiltersByAppName).toHaveBeenCalledWith(appName);
});
it('should return the current filter after one is selected', () => {
let filter = new FilterRepresentationModel({name: 'FakeMyTasks', filter: { state: 'open', assignment: 'fake-assignee'}});
expect(filterList.currentFilter).toBeUndefined();
filterList.selectFilter(filter);
expect(filterList.getCurrentFilter()).toBe(filter);
});
});

View File

@ -77,37 +77,47 @@ export class ActivitiFilters implements OnInit, OnChanges {
this.filters.push(filter);
});
this.load();
this.getFilters(this.appId, this.appName);
}
ngOnChanges(changes: SimpleChanges) {
let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) {
this.load();
this.getFiltersByAppId(appId.currentValue);
return;
}
let appName = changes['appName'];
if (appName && appName.currentValue) {
this.getFiltersByAppName(appName.currentValue);
return;
}
}
/**
* The method call the adapter data table component for render the task list
* @param tasks
* Return the task list filtered by appId or by appName
* @param appId
* @param appName
*/
private load() {
if (this.appName) {
this.filterByAppName();
getFilters(appId?: string, appName?: string) {
if (appName) {
this.getFiltersByAppName(appName);
} else {
this.filterByAppId(this.appId);
this.getFiltersByAppId(appId);
}
}
private filterByAppId(appId) {
/**
* Return the filter list filtered by appId
* @param appId - optional
*/
getFiltersByAppId(appId?: string) {
this.activiti.getTaskListFilters(appId).subscribe(
(res: FilterRepresentationModel[]) => {
this.resetFilter();
res.forEach((filter) => {
this.filterObserver.next(filter);
this.selectFirstFilter();
});
this.selectFirstFilter();
this.onSuccess.emit(res);
},
(err) => {
@ -117,10 +127,14 @@ export class ActivitiFilters implements OnInit, OnChanges {
);
}
private filterByAppName() {
this.activiti.getDeployedApplications(this.appName).subscribe(
/**
* Return the filter list filtered by appName
* @param appName
*/
getFiltersByAppName(appName: string) {
this.activiti.getDeployedApplications(appName).subscribe(
application => {
this.filterByAppId(application.id);
this.getFiltersByAppId(application.id);
this.selectFirstFilter();
},
(err) => {

View File

@ -22,11 +22,11 @@
</div>
</div>
<activiti-form *ngIf="hasFormKey()" [taskId]="taskDetails.id"
[showTitle]="showTitle"
[showTitle]="showFormTitle"
[showRefreshButton]="showRefreshButton"
[showCompleteButton]="showCompleteButton"
[showSaveButton]="showSaveButton"
[readOnly]="readOnly"
[showCompleteButton]="showFormCompleteButton"
[showSaveButton]="showFormSaveButton"
[readOnly]="readOnlyForm"
(formSaved)='formSavedEmitter($event)'
(formCompleted)='formCompletedEmitter($event)'
(formLoaded)='formLoadedEmitter($event)'

View File

@ -25,6 +25,7 @@ import { ActivitiPeople } from './activiti-people.component';
import { TaskDetailsModel } from '../models/task-details.model';
import { User } from '../models/user.model';
import { ActivitiForm, FormModel, FormService } from 'ng2-activiti-form';
import { TaskQueryRequestRepresentationModel } from '../models/filter.model';
declare let componentHandler: any;
@ -40,9 +41,6 @@ declare let __moduleName: string;
})
export class ActivitiTaskDetails implements OnInit, OnChanges {
@Input()
taskId: string;
@ViewChild('activiticomments')
activiticomments: any;
@ -50,19 +48,25 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
activitichecklist: any;
@Input()
showTitle: boolean = true;
taskId: string;
@Input()
showCompleteButton: boolean = true;
showNextTask: boolean = true;
@Input()
showSaveButton: boolean = true;
showFormTitle: boolean = true;
@Input()
readOnly: boolean = false;
showFormCompleteButton: boolean = true;
@Input()
showRefreshButton: boolean = true;
showFormSaveButton: boolean = true;
@Input()
readOnlyForm: boolean = false;
@Input()
showFormRefreshButton: boolean = true;
@Output()
formSaved = new EventEmitter();
@ -127,10 +131,14 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
this.taskDetails = null;
}
/**
* Check if the task has a form
* @returns {TaskDetailsModel|string|boolean}
*/
hasFormKey() {
return this.taskDetails
return (this.taskDetails
&& this.taskDetails.formKey
&& this.taskDetails.formKey !== 'null';
&& this.taskDetails.formKey !== 'null');
}
isTaskActive() {
@ -150,7 +158,7 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
this.taskDetails = res;
let endDate: any = res.endDate;
this.readOnly = !!(endDate && !isNaN(endDate.getTime()));
this.readOnlyForm = !!(endDate && !isNaN(endDate.getTime()));
if (this.taskDetails && this.taskDetails.involvedPeople) {
this.taskDetails.involvedPeople.forEach((user) => {
@ -172,6 +180,31 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
}
}
/**
* Retrieve the next open task
* @param processInstanceId
* @param processDefinitionId
*/
loadNextTask(processInstanceId: string, processDefinitionId: string) {
let requestNode = new TaskQueryRequestRepresentationModel(
{
processInstanceId: processInstanceId,
processDefinitionId: processDefinitionId
}
);
this.activitiTaskList.getTasks(requestNode).subscribe(
(response) => {
if (response.data && response.data.length > 0) {
this.taskDetails = response.data[0];
} else {
this.reset();
}
}, (error) => {
console.error(error);
this.onError.emit(error);
});
}
/**
* Complete the activiti task
*/
@ -198,6 +231,9 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
*/
formCompletedEmitter(data: any) {
this.formCompleted.emit(data);
if (this.isShowNextTask()) {
this.loadNextTask(this.taskDetails.processInstanceId, this.taskDetails.processDefinitionId);
}
}
/**
@ -208,11 +244,27 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
this.formLoaded.emit(data);
}
/**
* Emit the error event of the form
* @param data
*/
onErrorEmitter(err: any) {
this.onError.emit(err);
}
/**
* Emit the execute outcome of the form
* @param data
*/
executeOutcomeEmitter(data: any) {
this.executeOutcome.emit(data);
}
/**
* Return the showNexTask value
* @returns {boolean}
*/
isShowNextTask(): boolean {
return this.showNextTask;
}
}

View File

@ -21,7 +21,7 @@ import {
expect,
beforeEach
} from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { ActivitiTaskList } from './activiti-tasklist.component';
import { ActivitiTaskListService } from '../services/activiti-tasklist.service';
import { UserTaskFilterRepresentationModel } from '../models/filter.model';
@ -120,6 +120,11 @@ describe('ActivitiTaskList', () => {
taskList.ngOnInit();
});
it('should return a currentId null when the taskList is empty', () => {
taskList.selectFirstTask();
expect(taskList.getCurrentTaskId()).toBeNull();
});
it('should throw an exception when the response is wrong', (done) => {
spyOn(taskList.activiti, 'getTotalTasks').and.returnValue(Observable.fromPromise(fakeErrorTaskPromise));
taskList.taskFilter = new UserTaskFilterRepresentationModel({filter: { state: 'open', assignment: 'fake-assignee'}});
@ -157,10 +162,21 @@ describe('ActivitiTaskList', () => {
taskList.rowClick.subscribe(taskId => {
expect(taskId).toEqual(999);
expect(taskList.getCurrentTaskId()).toEqual(999);
done();
});
taskList.onRowClick(rowEvent);
});
it('should reload task list by filter on binding changes', () => {
spyOn(taskList, 'load').and.stub();
const taskFilter = new UserTaskFilterRepresentationModel({filter: { state: 'open', assignment: 'fake-assignee'}});
let change = new SimpleChange(null, taskFilter);
taskList.ngOnChanges({ 'taskFilter': change });
expect(taskList.load).toHaveBeenCalled();
});
});

View File

@ -153,7 +153,7 @@ export class ActivitiTaskList implements OnInit, OnChanges {
/**
* Select the first task of a tasklist if present
*/
private selectFirstTask() {
selectFirstTask() {
if (!this.isTaskListEmpty()) {
this.currentTaskId = this.data.getRows()[0].getValue('id');
} else {