[ADF-3237] Change and remove form from a task (#3597)

* button added for task standalone

* rebase fixed

* review fix

* test added & fixes

* refresh task details

* change and remove form added

* localize & documentation work

* tests added

* tests fixes

* tests fixes
This commit is contained in:
bbcodrin 2018-07-18 12:14:54 +03:00 committed by Eugenio Romano
parent c6c573bcfb
commit 54380fd693
13 changed files with 160 additions and 65 deletions

View File

@ -33,3 +33,4 @@ This component can be used when there is no form attached to a task.
| -- | -- | -- | | -- | -- | -- |
| cancel | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the "Cancel" button is clicked. | | cancel | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the "Cancel" button is clicked. |
| complete | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the form associated with the task is completed. | | complete | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the form associated with the task is completed. |
| showAttachForm | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the "Show Form" button is clicked. |

View File

@ -154,7 +154,9 @@
"COMPLETE_TASK_SUB_MESSAGE": "No forms to be added" "COMPLETE_TASK_SUB_MESSAGE": "No forms to be added"
}, },
"ATTACH_FORM":{ "ATTACH_FORM":{
"SELECT_FORM": "Select Form To Attach" "SELECT_FORM": "Select Form To Attach",
"REMOVE_FORM": "Remove Form",
"SELECT_OPTION": "Please select option"
} }
}, },
"ADF_PROCESS_LIST": { "ADF_PROCESS_LIST": {

View File

@ -7,24 +7,30 @@
</mat-card-title> </mat-card-title>
<div class="adf-attach-form-row"> <div class="adf-attach-form-row">
<mat-form-field class="adf-grid-full-width"> <mat-form-field class="adf-grid-full-width">
<mat-select id="form_id" [(ngModel)]="formKey"> <mat-select placeholder="{{ 'ADF_TASK_LIST.ATTACH_FORM.SELECT_OPTION' | translate }}" id="form_id" [(ngModel)]="formId">
<mat-option *ngFor="let form of forms" [value]="form.id">{{ form.name }}</mat-option> <mat-option *ngFor="let form of forms" [value]="form.id">{{ form.name }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<adf-form <adf-form
[formId]="formKey" [formId]="formId"
[readOnly]="true" [readOnly]="true"
[disableCompleteButton]="true" [showCompleteButton]="false"
[showRefreshButton]="false"> [showRefreshButton]="false"
[showValidationIcon]="false">
</adf-form> </adf-form>
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions class="adf-no-form-mat-card-actions"> <mat-card-actions class="adf-no-form-mat-card-actions">
<div>
<button mat-button id="adf-no-form-remove-button" color="warn" *ngIf="formKey" (click)="onRemoveButtonClick()">{{ 'ADF_TASK_LIST.ATTACH_FORM.REMOVE_FORM' | translate }}</button>
</div>
<div>
<button mat-button id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button> <button mat-button id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
<button mat-button id="adf-no-form-attach-form-button" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button> <button mat-button id="adf-no-form-attach-form-button" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button>
</div>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </div>

View File

@ -10,7 +10,7 @@
} }
.adf-no-form-mat-card-actions { .adf-no-form-mat-card-actions {
justify-content: flex-end; justify-content: space-between;
margin-top: 30px; margin-top: 30px;
text-align: right; text-align: right;
} }

View File

@ -54,7 +54,7 @@ describe('AttachFormComponent', () => {
it('should call attachFormToATask if clicked on Complete Button', async(() => { it('should call attachFormToATask if clicked on Complete Button', async(() => {
component.taskId = 1; component.taskId = 1;
component.formKey = 2; component.formId = 2;
spyOn(taskService, 'attachFormToATask').and.returnValue(Observable.of(true)); spyOn(taskService, 'attachFormToATask').and.returnValue(Observable.of(true));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
@ -75,4 +75,52 @@ describe('AttachFormComponent', () => {
expect(formContainer).not.toBeNull(); expect(formContainer).not.toBeNull();
}); });
})); }));
it('should show the formPreview of the selected form', async(() => {
component.formKey = 12;
fixture.detectChanges();
const formContainer = fixture.debugElement.nativeElement.querySelector('.adf-form-container');
fixture.whenStable().then(() => {
expect(formContainer).toBeDefined();
expect(formContainer).toBeNull();
});
}));
it('should remove form if it is present', (done) => {
component.taskId = 1;
component.formId = 10;
component.formKey = 12;
spyOn(taskService, 'deleteForm').and.returnValue(Observable.of({}));
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-remove-button')).toBeDefined();
const el = fixture.nativeElement.querySelector('#adf-no-form-remove-button');
el.click();
expect(component.formId).toBeNull();
done();
});
});
it('should emit success when form is changed', async(() => {
component.taskId = 1;
component.formId = 10;
spyOn(taskService, 'attachFormToATask').and.returnValue(Observable.of(
{
id: 91,
name: 'fakeName',
formKey: 1204,
assignee: null
}
));
fixture.detectChanges();
fixture.whenStable().then(() => {
const emitSpy = spyOn(component.success, 'emit');
const el = fixture.nativeElement.querySelector('#adf-no-form-attach-form-button');
el.click();
expect(emitSpy).toHaveBeenCalled();
});
}));
}); });

View File

@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { LogService } from '@alfresco/adf-core'; import { FormService, LogService } from '@alfresco/adf-core';
import { Component, EventEmitter, Input, OnInit, OnChanges, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { Form } from '../models/form.model'; import { Form } from '../models/form.model';
import { TaskListService } from './../services/tasklist.service'; import { TaskListService } from './../services/tasklist.service';
@ -26,44 +26,53 @@ import { TaskListService } from './../services/tasklist.service';
styleUrls: ['./attach-form.component.scss'] styleUrls: ['./attach-form.component.scss']
}) })
export class AttachFormComponent implements OnInit, OnChanges { export class AttachFormComponent implements OnChanges {
constructor(private taskService: TaskListService, constructor(private taskService: TaskListService,
private logService: LogService) { } private logService: LogService,
private formService: FormService) { }
/** The id of the task whose details we are asking for. */
@Input() @Input()
taskId; taskId;
/** Emitted when the "Cancel" button is clicked. */ @Input()
formKey;
@Output() @Output()
cancelAttachForm: EventEmitter<void> = new EventEmitter<void>(); cancelAttachForm: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when the form associated with the form task is attached. */
@Output() @Output()
completeAttachForm: EventEmitter<void> = new EventEmitter<void>(); success: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when an error occurs. */
@Output() @Output()
error: EventEmitter<any> = new EventEmitter<any>(); error: EventEmitter<any> = new EventEmitter<any>();
forms: Form[]; forms: Form[];
formKey: number; formId: number;
ngOnInit() {
this.loadFormsTask();
}
ngOnChanges() { ngOnChanges() {
this.loadFormsTask(); this.loadFormsTask();
this.onFormAttached();
} }
onCancelButtonClick(): void { onCancelButtonClick(): void {
this.cancelAttachForm.emit(); this.cancelAttachForm.emit();
} }
onRemoveButtonClick(): void {
this.taskService.deleteForm(this.taskId).subscribe(
() => {
this.formId = null;
this.success.emit();
},
(err) => {
this.error.emit(err);
this.logService.error('An error occurred while trying to delete the form');
});
}
onAttachFormButtonClick(): void { onAttachFormButtonClick(): void {
this.attachForm(this.taskId, this.formKey); this.attachForm(this.taskId, this.formId);
} }
private loadFormsTask(): void { private loadFormsTask(): void {
@ -76,11 +85,26 @@ export class AttachFormComponent implements OnInit, OnChanges {
}); });
} }
private attachForm(taskId: string, formKey: number) { private onFormAttached() {
if (taskId && formKey) { this.formService.getTaskForm(this.taskId)
this.taskService.attachFormToATask(taskId, formKey)
.subscribe((res) => { .subscribe((res) => {
this.completeAttachForm.emit(); this.formService.getFormDefinitionByName(res.name).subscribe((formDef) => {
this.formId = formDef;
});
}, (err) => {
this.error.emit(err);
this.logService.error('Could not load forms');
});
}
private attachForm(taskId: string, formId: number) {
if (taskId && formId) {
this.taskService.attachFormToATask(taskId, formId)
.subscribe(() => {
this.success.emit();
}, (err) => {
this.error.emit(err);
this.logService.error('Could not attach form');
}); });
} }
} }

View File

@ -22,7 +22,7 @@
<div class="adf-task-details-core-form"> <div class="adf-task-details-core-form">
<div *ngIf="isAssigned()"> <div *ngIf="isAssigned()">
<adf-form *ngIf="hasFormKey()" #activitiForm <adf-form *ngIf="hasFormKey() && !showAttachForm" #activitiForm
[showDebugButton]="debugMode" [showDebugButton]="debugMode"
[taskId]="taskDetails.id" [taskId]="taskDetails.id"
[showTitle]="showFormTitle" [showTitle]="showFormTitle"
@ -39,15 +39,21 @@
(error)='onFormError($event)' (error)='onFormError($event)'
(executeOutcome)='onFormExecuteOutcome($event)'> (executeOutcome)='onFormExecuteOutcome($event)'>
</adf-form> </adf-form>
<adf-task-standalone *ngIf="!hasFormKey()" <adf-task-standalone *ngIf="!hasFormKey() && !showAttachForm"
[taskName]="taskDetails.name" [taskName]="taskDetails.name"
[taskId]="taskDetails.id" [taskId]="taskDetails.id"
[isCompleted]="isCompletedTask()" [isCompleted]="isCompletedTask()"
[hasCompletePermission]="isCompleteButtonEnabled()" [hasCompletePermission]="isCompleteButtonEnabled()"
[hideCancelButton]="true" [hideCancelButton]="true"
(complete)="onComplete()" (complete)="onComplete()"
(formAttached)="onFormAttached()"> (showAttachForm)="onShowAttachForm()">
</adf-task-standalone> </adf-task-standalone>
<adf-attach-form *ngIf="showAttachForm"
[taskId]="taskDetails.id"
[formKey]="taskDetails.formKey"
(cancelAttachForm)="onCancelAttachForm()"
(success)="onCompleteAttachForm()">
</adf-attach-form>
</div> </div>
<div *ngIf="!isAssigned()" id="claim-message-id"> <div *ngIf="!isAssigned()" id="claim-message-id">
{{ 'ADF_TASK_LIST.DETAILS.MESSAGES.CLAIM' | translate }} {{ 'ADF_TASK_LIST.DETAILS.MESSAGES.CLAIM' | translate }}

View File

@ -21,7 +21,6 @@ import {
CardViewUpdateService, CardViewUpdateService,
ClickNotification, ClickNotification,
LogService, LogService,
FormService,
UpdateNotification, UpdateNotification,
FormRenderingService, FormRenderingService,
CommentsComponent CommentsComponent
@ -97,7 +96,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
/** Toggles rendering of the form title. */ /** Toggles rendering of the form title. */
@Input() @Input()
showFormTitle: boolean = true; showFormTitle: boolean = false;
/** Toggles rendering of the `Complete` outcome button. */ /** Toggles rendering of the `Complete` outcome button. */
@Input() @Input()
@ -175,6 +174,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
noTaskDetailsTemplateComponent: TemplateRef<any>; noTaskDetailsTemplateComponent: TemplateRef<any>;
showAssignee: boolean = false; showAssignee: boolean = false;
showAttachForm: boolean = false;
private peopleSearchObserver: Observer<UserProcessModel[]>; private peopleSearchObserver: Observer<UserProcessModel[]>;
public errorDialogRef: MatDialogRef<TemplateRef<any>>; public errorDialogRef: MatDialogRef<TemplateRef<any>>;
@ -187,7 +187,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
private authService: AuthenticationService, private authService: AuthenticationService,
private peopleProcessService: PeopleProcessService, private peopleProcessService: PeopleProcessService,
private formRenderingService: FormRenderingService, private formRenderingService: FormRenderingService,
private formService: FormService,
private logService: LogService, private logService: LogService,
private cardViewUpdateService: CardViewUpdateService, private cardViewUpdateService: CardViewUpdateService,
private dialog: MatDialog) { private dialog: MatDialog) {
@ -259,6 +258,9 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
if (clickNotification.target.key === 'assignee') { if (clickNotification.target.key === 'assignee') {
this.showAssignee = true; this.showAssignee = true;
} }
if (clickNotification.target.key === 'formName') {
this.showAttachForm = true;
}
} }
/** /**
@ -290,7 +292,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
} }
isAssigned(): boolean { isAssigned(): boolean {
return this.taskDetails.assignee ? true : false; return !!this.taskDetails.assignee;
} }
private hasEmailAddress(): boolean { private hasEmailAddress(): boolean {
@ -364,11 +366,17 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
); );
} }
onFormAttached() { onShowAttachForm() {
this.formService.getTaskForm(this.taskId) this.showAttachForm = true;
.subscribe((res) => { }
onCancelAttachForm() {
this.showAttachForm = false;
}
onCompleteAttachForm() {
this.showAttachForm = false;
this.loadDetails(this.taskId); this.loadDetails(this.taskId);
}, error => this.logService.error('Could not load forms'));
} }
onFormContentClick(content: ContentLinkModel): void { onFormContentClick(content: ContentLinkModel): void {

View File

@ -301,7 +301,7 @@ describe('TaskHeaderComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
let valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] .adf-property-value')); let valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] .adf-textitem-clickable-value'));
expect(valueEl.nativeElement.innerText).toBe('test form'); expect(valueEl.nativeElement.innerText).toBe('test form');
}); });
})); }));

View File

@ -153,7 +153,9 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
label: 'ADF_TASK_LIST.PROPERTIES.FORM_NAME', label: 'ADF_TASK_LIST.PROPERTIES.FORM_NAME',
value: this.formName, value: this.formName,
key: 'formName', key: 'formName',
default: this.translationService.instant('ADF_TASK_LIST.PROPERTIES.FORM_NAME_DEFAULT') default: this.translationService.instant('ADF_TASK_LIST.PROPERTIES.FORM_NAME_DEFAULT'),
clickable: !!this.formName,
icon: 'create'
} }
) )
]; ];

View File

@ -1,4 +1,4 @@
<mat-card class="adf-message-card" *ngIf="!showAttachForm"> <mat-card class="adf-message-card">
<mat-card-content> <mat-card-content>
<div class="adf-no-form-message-container"> <div class="adf-no-form-message-container">
<div class="adf-no-form-message-list"> <div class="adf-no-form-message-list">
@ -25,9 +25,3 @@
</div> </div>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
<adf-attach-form *ngIf="showAttachForm"
[taskId]="taskId"
(cancelAttachForm)="onCancelAttachForm()"
(completeAttachForm)="onCompleteAttachForm()">
</adf-attach-form>

View File

@ -54,10 +54,9 @@ export class TaskStandaloneComponent {
@Output() @Output()
complete: EventEmitter<void> = new EventEmitter<void>(); complete: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when the form associated with the form task is attached. */
@Output() @Output()
formAttached: EventEmitter<void> = new EventEmitter<void>(); showAttachForm: EventEmitter<void> = new EventEmitter<void>();
showAttachForm: boolean = false;
constructor() { } constructor() { }
@ -82,15 +81,6 @@ export class TaskStandaloneComponent {
} }
onShowAttachForm() { onShowAttachForm() {
this.showAttachForm = true; this.showAttachForm.emit();
}
onCancelAttachForm() {
this.showAttachForm = false;
}
onCompleteAttachForm() {
this.showAttachForm = false;
this.formAttached.emit();
} }
} }

View File

@ -219,6 +219,16 @@ export class TaskListService {
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
/**
* Deletes a form from a task.
* @param taskId Task id related to form
* @returns Null response notifying when the operation is complete
*/
deleteForm(taskId: string): Observable<TaskDetailsModel> {
return Observable.fromPromise(this.callApiDeleteForm(taskId))
.catch(err => this.handleError(err));
}
/** /**
* Gives completed status to a task. * Gives completed status to a task.
* @param taskId ID of the target task * @param taskId ID of the target task
@ -353,6 +363,10 @@ export class TaskListService {
return this.apiService.taskApi.deleteTask(taskId); return this.apiService.taskApi.deleteTask(taskId);
} }
private callApiDeleteForm(taskId: string) {
return this.apiService.taskApi.removeForm(taskId);
}
private callApiTaskChecklist(taskId: string) { private callApiTaskChecklist(taskId: string) {
return this.apiService.taskApi.getChecklist(taskId); return this.apiService.taskApi.getChecklist(taskId);
} }