[ADF-226] Add requeue button to task header (#2366)

* Add requeue button to task header

* Fixing typescript declaration
This commit is contained in:
Popovics András
2017-09-21 18:37:27 +01:00
committed by Eugenio Romano
parent 417071db01
commit fbaf901462
10 changed files with 121 additions and 10 deletions

View File

@@ -37,6 +37,7 @@ Shows all the information related to a task.
| Name | Description | | Name | Description |
| --- | --- | | --- | --- |
| claim | Raised when the task is claimed. | | claim | Raised when the task is claimed. |
| unclaim | Raised when the task is unclaimed (requeued). |
## Details ## Details

View File

@@ -478,6 +478,7 @@ Shows all the information related to a task.
| Name | Description | | Name | Description |
| --- | --- | | --- | --- |
| claim | Raised when the task is claimed. | | claim | Raised when the task is claimed. |
| unclaim | Raised when the task is unclaimed (requeued). |
### Details ### Details

View File

@@ -69,7 +69,8 @@
[class]="getTaskHeaderViewClass()" [class]="getTaskHeaderViewClass()"
[taskDetails]="taskDetails" [taskDetails]="taskDetails"
[formName]="taskFormName" [formName]="taskFormName"
(claim)="onClaimTask($event)"> (claim)="onClaimAction($event)"
(unclaim)="onClaimAction($event)">
</adf-task-header> </adf-task-header>
<adf-people *ngIf="showInvolvePeople" #people <adf-people *ngIf="showInvolvePeople" #people
[iconImageUrl]="peopleIconImageUrl" [iconImageUrl]="peopleIconImageUrl"

View File

@@ -324,7 +324,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
this.dialog.closeAll(); this.dialog.closeAll();
} }
onClaimTask(taskId: string): void { onClaimAction(taskId: string): void {
this.loadDetails(taskId); this.loadDetails(taskId);
} }

View File

@@ -3,8 +3,10 @@
<adf-card-view [properties]="properties" [editable]="!isCompleted()"></adf-card-view> <adf-card-view [properties]="properties" [editable]="!isCompleted()"></adf-card-view>
</md-card-content> </md-card-content>
<md-card-actions *ngIf="!isAssignedToMe()" class="adf-controls"> <md-card-actions class="adf-controls">
<button md-button data-automation-id="header-claim-button" id="claim-task" (click)="claimTask(taskDetails.id)" class="adf-claim-controls">{{ 'TASK_DETAILS.BUTTON.CLAIM' | translate }} <button *ngIf="isAssignedToMe()" md-button data-automation-id="header-unclaim-button" id="unclaim-task" (click)="unclaimTask(taskDetails.id)" class="adf-claim-controls">{{ 'TASK_DETAILS.BUTTON.UNCLAIM' | translate }}
</button>
<button *ngIf="!isAssignedToMe()" md-button data-automation-id="header-claim-button" id="claim-task" (click)="claimTask(taskDetails.id)" class="adf-claim-controls">{{ 'TASK_DETAILS.BUTTON.CLAIM' | translate }}
</button> </button>
</md-card-actions> </md-card-actions>
</md-card> </md-card>

View File

@@ -19,6 +19,7 @@ import { DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { AppConfigService, CardViewUpdateService, CoreModule, TranslationService } from 'ng2-alfresco-core'; import { AppConfigService, CardViewUpdateService, CoreModule, TranslationService } from 'ng2-alfresco-core';
import { Observable } from 'rxjs/Rx';
import { AppConfigServiceMock } from '../assets/app-config.service.mock'; import { AppConfigServiceMock } from '../assets/app-config.service.mock';
import { TranslationMock } from '../assets/translation.service.mock'; import { TranslationMock } from '../assets/translation.service.mock';
@@ -122,12 +123,55 @@ describe('TaskHeaderComponent', () => {
expect(datePicker).not.toBeNull('Datepicker should be in DOM'); expect(datePicker).not.toBeNull('Datepicker should be in DOM');
}); });
it('should display the claim button if no assignee', () => { describe('Claiming', () => {
component.taskDetails.assignee = null;
component.ngOnChanges({}); it('should display the claim button if no assignee', () => {
fixture.detectChanges(); component.taskDetails.assignee = null;
let valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]'));
expect(valueEl.nativeElement.innerText).toBe('TASK_DETAILS.BUTTON.CLAIM'); component.ngOnChanges({});
fixture.detectChanges();
let claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]'));
expect(claimButton.nativeElement.innerText).toBe('TASK_DETAILS.BUTTON.CLAIM');
});
});
describe('Unclaim', () => {
const batman = { id : 1, email: 'bruce.wayne@gotham.com', firstName: 'Bruce', lastName: 'Wayne', userImage: 'batman.jpg' };
let taskListService;
beforeEach(() => {
taskListService = TestBed.get(TaskListService);
component.taskDetails.assignee = batman;
component.taskDetails.id = 'repair-batmobile';
fixture.detectChanges();
});
it('should display the requeue button if there is assignee', () => {
let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]'));
expect(unclaimButton.nativeElement.innerText).toBe('TASK_DETAILS.BUTTON.UNCLAIM');
});
it('should call the service\'s unclaim method on unclaiming' , () => {
spyOn(taskListService, 'unclaimTask');
let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]'));
unclaimButton.triggerEventHandler('click', {});
expect(taskListService.unclaimTask).toHaveBeenCalledWith('repair-batmobile');
});
it('should trigger the unclaim event on successfull unclaiming', () => {
let unclaimed: boolean = false;
component.unclaim.subscribe(() => { unclaimed = true; });
spyOn(taskListService, 'unclaimTask').and.returnValue(Observable.of(true));
let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]'));
unclaimButton.triggerEventHandler('click', {});
expect(unclaimed).toBeTruthy();
});
}); });
it('should display due date', () => { it('should display due date', () => {

View File

@@ -36,6 +36,9 @@ export class TaskHeaderComponent implements OnChanges {
@Output() @Output()
claim: EventEmitter<any> = new EventEmitter<any>(); claim: EventEmitter<any> = new EventEmitter<any>();
@Output()
unclaim: EventEmitter<any> = new EventEmitter<any>();
properties: CardViewItem []; properties: CardViewItem [];
inEdit: boolean = false; inEdit: boolean = false;
@@ -47,6 +50,9 @@ export class TaskHeaderComponent implements OnChanges {
this.refreshData(); this.refreshData();
} }
/**
* Refresh the card data
*/
refreshData() { refreshData() {
if (this.taskDetails) { if (this.taskDetails) {
let valueMap = new Map([[this.taskDetails.processInstanceId, this.taskDetails.processDefinitionName]]); let valueMap = new Map([[this.taskDetails.processInstanceId, this.taskDetails.processDefinitionName]]);
@@ -72,18 +78,32 @@ export class TaskHeaderComponent implements OnChanges {
} }
} }
/**
* Does the task have an assignee
*/
public hasAssignee(): boolean { public hasAssignee(): boolean {
return (this.taskDetails && this.taskDetails.assignee) ? true : false; return (this.taskDetails && this.taskDetails.assignee) ? true : false;
} }
/**
* Is the task assigned to the currently loggedin user
*/
isAssignedToMe(): boolean { isAssignedToMe(): boolean {
return this.taskDetails.assignee ? true : false; return this.taskDetails.assignee ? true : false;
} }
/**
* Returns task's status
*/
getTaskStatus(): string { getTaskStatus(): string {
return this.isCompleted() ? 'Completed' : 'Running'; return this.isCompleted() ? 'Completed' : 'Running';
} }
/**
* Claim task
*
* @param taskId
*/
claimTask(taskId: string) { claimTask(taskId: string) {
this.activitiTaskService.claimTask(taskId).subscribe( this.activitiTaskService.claimTask(taskId).subscribe(
(res: any) => { (res: any) => {
@@ -92,6 +112,22 @@ export class TaskHeaderComponent implements OnChanges {
}); });
} }
/**
* Unclaim task
*
* @param taskId
*/
unclaimTask(taskId: string) {
this.activitiTaskService.unclaimTask(taskId).subscribe(
(res: any) => {
this.logService.info('Task unclaimed');
this.unclaim.emit(taskId);
});
}
/**
* Returns true if the task is completed
*/
isCompleted() { isCompleted() {
return !!this.taskDetails.endDate; return !!this.taskDetails.endDate;
} }

View File

@@ -22,6 +22,7 @@
"BUTTON": { "BUTTON": {
"COMPLETE": "Complete", "COMPLETE": "Complete",
"CLAIM": "Claim", "CLAIM": "Claim",
"UNCLAIM": "Requeue",
"DRAG-ATTACHMENT": "Drop Files Here...", "DRAG-ATTACHMENT": "Drop Files Here...",
"UPLOAD-ATTACHMENT": "Upload Attachment" "UPLOAD-ATTACHMENT": "Upload Attachment"
}, },

View File

@@ -750,6 +750,22 @@ describe('Activiti TaskList Service', () => {
}); });
}); });
it('should unclaim a task', (done) => {
let taskId = '111';
service.unclaimTask(taskId).subscribe(
(res: any) => {
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify({})
});
});
it('should update a task', (done) => { it('should update a task', (done) => {
let taskId = '111'; let taskId = '111';

View File

@@ -446,6 +446,15 @@ export class TaskListService {
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
/**
* Unclaim a task
* @param id - taskId
*/
unclaimTask(taskId: string): Observable<TaskDetailsModel> {
return Observable.fromPromise(this.apiService.getInstance().activiti.taskApi.unclaimTask(taskId))
.catch(err => this.handleError(err));
}
/** /**
* Update due date * Update due date
* @param dueDate - the new due date * @param dueDate - the new due date