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.filters.push(filter);
}); });
this.load(); this.getFilters(this.appId, this.appName);
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
let appId = changes['appId']; let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) { 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; return;
} }
} }
/** /**
* The method call the adapter data table component for render the task list * Return the task list filtered by appId or by appName
* @param tasks * @param appId
* @param appName
*/ */
private load() { getFilters(appId?: string, appName?: string) {
if (this.appName) { if (appName) {
this.filterByAppName(); this.getFiltersByAppName(appName);
} else { } 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( this.activiti.getProcessFilters(appId).subscribe(
(res: FilterRepresentationModel[]) => { (res: FilterRepresentationModel[]) => {
this.resetFilter(); 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 => { application => {
this.filterByAppId(application.id); this.getFiltersByAppId(application.id);
this.selectFirstFilter(); this.selectFirstFilter();
}, },
(err) => { (err) => {

View File

@ -106,8 +106,13 @@ The component shows the details of the task id passed in input
#### Options #### Options
**taskId**: { string } required) The id of the task details that we **taskId**: { string } required) The id of the task details that we are asking for.
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 ### Custom 'empty Activiti Task Details' template

View File

@ -21,7 +21,7 @@ import {
expect, expect,
beforeEach beforeEach
} from '@angular/core/testing'; } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { ActivitiFilters } from './activiti-filters.component'; import { ActivitiFilters } from './activiti-filters.component';
import { ActivitiTaskListService } from '../services/activiti-tasklist.service'; import { ActivitiTaskListService } from '../services/activiti-tasklist.service';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
@ -89,6 +89,7 @@ describe('ActivitiFilters', () => {
}); });
it('should emit an error with a bad response', (done) => { it('should emit an error with a bad response', (done) => {
filterList.appId = '1';
spyOn(filterList.activiti, 'getTaskListFilters').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise)); spyOn(filterList.activiti, 'getTaskListFilters').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise));
filterList.onError.subscribe((err) => { filterList.onError.subscribe((err) => {
@ -99,6 +100,18 @@ describe('ActivitiFilters', () => {
filterList.ngOnInit(); 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) => { it('should emit an event when a filter is selected', (done) => {
let currentFilter = new FilterRepresentationModel({filter: { state: 'open', assignment: 'fake-involved'}}); let currentFilter = new FilterRepresentationModel({filter: { state: 'open', assignment: 'fake-involved'}});
@ -112,4 +125,41 @@ describe('ActivitiFilters', () => {
filterList.selectFilter(currentFilter); 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.filters.push(filter);
}); });
this.load(); this.getFilters(this.appId, this.appName);
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
let appId = changes['appId']; let appId = changes['appId'];
if (appId && (appId.currentValue || appId.currentValue === null)) { 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; return;
} }
} }
/** /**
* The method call the adapter data table component for render the task list * Return the task list filtered by appId or by appName
* @param tasks * @param appId
* @param appName
*/ */
private load() { getFilters(appId?: string, appName?: string) {
if (this.appName) { if (appName) {
this.filterByAppName(); this.getFiltersByAppName(appName);
} else { } 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( this.activiti.getTaskListFilters(appId).subscribe(
(res: FilterRepresentationModel[]) => { (res: FilterRepresentationModel[]) => {
this.resetFilter(); this.resetFilter();
res.forEach((filter) => { res.forEach((filter) => {
this.filterObserver.next(filter); this.filterObserver.next(filter);
this.selectFirstFilter();
}); });
this.selectFirstFilter();
this.onSuccess.emit(res); this.onSuccess.emit(res);
}, },
(err) => { (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 => { application => {
this.filterByAppId(application.id); this.getFiltersByAppId(application.id);
this.selectFirstFilter(); this.selectFirstFilter();
}, },
(err) => { (err) => {

View File

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

View File

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

View File

@ -21,7 +21,7 @@ import {
expect, expect,
beforeEach beforeEach
} from '@angular/core/testing'; } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { ActivitiTaskList } from './activiti-tasklist.component'; import { ActivitiTaskList } from './activiti-tasklist.component';
import { ActivitiTaskListService } from '../services/activiti-tasklist.service'; import { ActivitiTaskListService } from '../services/activiti-tasklist.service';
import { UserTaskFilterRepresentationModel } from '../models/filter.model'; import { UserTaskFilterRepresentationModel } from '../models/filter.model';
@ -120,6 +120,11 @@ describe('ActivitiTaskList', () => {
taskList.ngOnInit(); 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) => { it('should throw an exception when the response is wrong', (done) => {
spyOn(taskList.activiti, 'getTotalTasks').and.returnValue(Observable.fromPromise(fakeErrorTaskPromise)); spyOn(taskList.activiti, 'getTotalTasks').and.returnValue(Observable.fromPromise(fakeErrorTaskPromise));
taskList.taskFilter = new UserTaskFilterRepresentationModel({filter: { state: 'open', assignment: 'fake-assignee'}}); taskList.taskFilter = new UserTaskFilterRepresentationModel({filter: { state: 'open', assignment: 'fake-assignee'}});
@ -157,10 +162,21 @@ describe('ActivitiTaskList', () => {
taskList.rowClick.subscribe(taskId => { taskList.rowClick.subscribe(taskId => {
expect(taskId).toEqual(999); expect(taskId).toEqual(999);
expect(taskList.getCurrentTaskId()).toEqual(999);
done(); done();
}); });
taskList.onRowClick(rowEvent); 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 * Select the first task of a tasklist if present
*/ */
private selectFirstTask() { selectFirstTask() {
if (!this.isTaskListEmpty()) { if (!this.isTaskListEmpty()) {
this.currentTaskId = this.data.getRows()[0].getValue('id'); this.currentTaskId = this.data.getRows()[0].getValue('id');
} else { } else {